[Qemu-devel] [PATCH 2/3] qemu-timer: add QEMUClock-active_timers list lock
This patch makes QEMUClock-active_timers list iteration thread-safe. With additional patches, this will allow threads to use timers without holding the QEMU global mutex. The qemu_next_alarm_deadline() function was restructured to reduce code duplication, which would have gotten worse due to the extra locking calls. The new qemu_next_clock_deadline() function does the work of updating the nearest deadline. Signed-off-by: Stefan Hajnoczi stefa...@redhat.com --- qemu-timer.c | 96 +++- 1 file changed, 69 insertions(+), 27 deletions(-) diff --git a/qemu-timer.c b/qemu-timer.c index 4740da9..c773af0 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -29,6 +29,7 @@ #include hw/hw.h #include qemu/timer.h +#include qemu/thread.h #ifdef CONFIG_POSIX #include pthread.h #endif @@ -46,6 +47,7 @@ struct QEMUClock { QEMUTimer *active_timers; +QemuMutex active_timers_lock; NotifierList reset_notifiers; int64_t last; @@ -85,31 +87,38 @@ static bool qemu_timer_expired_ns(QEMUTimer *timer_head, int64_t current_time) return timer_head (timer_head-expire_time = current_time); } -static int64_t qemu_next_alarm_deadline(void) +static int64_t qemu_next_clock_deadline(QEMUClock *clock, int64_t delta) { -int64_t delta = INT64_MAX; -int64_t rtdelta; +int64_t expire_time, next; +bool has_timer = false; -if (!use_icount vm_clock-enabled vm_clock-active_timers) { -delta = vm_clock-active_timers-expire_time - - qemu_get_clock_ns(vm_clock); +if (!clock-enabled) { +return delta; } -if (host_clock-enabled host_clock-active_timers) { -int64_t hdelta = host_clock-active_timers-expire_time - - qemu_get_clock_ns(host_clock); -if (hdelta delta) { -delta = hdelta; -} + +qemu_mutex_lock(clock-active_timers_lock); +if (clock-active_timers) { +has_timer = true; +expire_time = clock-active_timers-expire_time; } -if (rt_clock-enabled rt_clock-active_timers) { -rtdelta = (rt_clock-active_timers-expire_time - - qemu_get_clock_ns(rt_clock)); -if (rtdelta delta) { -delta = rtdelta; -} +qemu_mutex_unlock(clock-active_timers_lock); +if (!has_timer) { +return delta; } -return delta; +next = expire_time - qemu_get_clock_ns(clock); +return MIN(next, delta); +} + +static int64_t qemu_next_alarm_deadline(void) +{ +int64_t delta = INT64_MAX; + +if (!use_icount) { +delta = qemu_next_clock_deadline(vm_clock, delta); +} +delta = qemu_next_clock_deadline(host_clock, delta); +return qemu_next_clock_deadline(rt_clock, delta); } static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t) @@ -240,6 +249,7 @@ static QEMUClock *qemu_new_clock(int type) clock-enabled = true; clock-last = INT64_MIN; notifier_list_init(clock-reset_notifiers); +qemu_mutex_init(clock-active_timers_lock); return clock; } @@ -254,22 +264,41 @@ void qemu_clock_enable(QEMUClock *clock, bool enabled) int64_t qemu_clock_has_timers(QEMUClock *clock) { -return !!clock-active_timers; +bool has_timers; + +qemu_mutex_lock(clock-active_timers_lock); +has_timers = !!clock-active_timers; +qemu_mutex_unlock(clock-active_timers_lock); +return has_timers; } int64_t qemu_clock_expired(QEMUClock *clock) { -return (clock-active_timers -clock-active_timers-expire_time qemu_get_clock_ns(clock)); +bool has_timers; +int64_t expire_time; + +qemu_mutex_lock(clock-active_timers_lock); +has_timers = clock-active_timers; +expire_time = clock-active_timers-expire_time; +qemu_mutex_unlock(clock-active_timers_lock); + +return has_timers expire_time qemu_get_clock_ns(clock); } int64_t qemu_clock_deadline(QEMUClock *clock) { /* To avoid problems with overflow limit this to 2^32. */ int64_t delta = INT32_MAX; +bool has_timers; +int64_t expire_time; -if (clock-active_timers) { -delta = clock-active_timers-expire_time - qemu_get_clock_ns(clock); +qemu_mutex_lock(clock-active_timers_lock); +has_timers = clock-active_timers; +expire_time = clock-active_timers-expire_time; +qemu_mutex_unlock(clock-active_timers_lock); + +if (has_timers) { +delta = expire_time - qemu_get_clock_ns(clock); } if (delta 0) { delta = 0; @@ -298,8 +327,10 @@ void qemu_free_timer(QEMUTimer *ts) /* stop a timer, but do not dealloc it */ void qemu_del_timer(QEMUTimer *ts) { +QEMUClock *clock = ts-clock; QEMUTimer **pt, *t; +qemu_mutex_lock(clock-active_timers_lock); pt = ts-clock-active_timers; for(;;) { t = *pt; @@ -311,18 +342,21 @@ void qemu_del_timer(QEMUTimer *ts) } pt = t-next; } +
Re: [Qemu-devel] [PATCH 2/3] qemu-timer: add QEMUClock-active_timers list lock
Il 05/07/2013 14:39, Stefan Hajnoczi ha scritto: This patch makes QEMUClock-active_timers list iteration thread-safe. With additional patches, this will allow threads to use timers without holding the QEMU global mutex. The qemu_next_alarm_deadline() function was restructured to reduce code duplication, which would have gotten worse due to the extra locking calls. The new qemu_next_clock_deadline() function does the work of updating the nearest deadline. Signed-off-by: Stefan Hajnoczi stefa...@redhat.com --- qemu-timer.c | 96 +++- 1 file changed, 69 insertions(+), 27 deletions(-) diff --git a/qemu-timer.c b/qemu-timer.c index 4740da9..c773af0 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -29,6 +29,7 @@ #include hw/hw.h #include qemu/timer.h +#include qemu/thread.h #ifdef CONFIG_POSIX #include pthread.h #endif @@ -46,6 +47,7 @@ struct QEMUClock { QEMUTimer *active_timers; +QemuMutex active_timers_lock; NotifierList reset_notifiers; int64_t last; @@ -85,31 +87,38 @@ static bool qemu_timer_expired_ns(QEMUTimer *timer_head, int64_t current_time) return timer_head (timer_head-expire_time = current_time); } -static int64_t qemu_next_alarm_deadline(void) +static int64_t qemu_next_clock_deadline(QEMUClock *clock, int64_t delta) { -int64_t delta = INT64_MAX; -int64_t rtdelta; +int64_t expire_time, next; +bool has_timer = false; -if (!use_icount vm_clock-enabled vm_clock-active_timers) { -delta = vm_clock-active_timers-expire_time - - qemu_get_clock_ns(vm_clock); +if (!clock-enabled) { +return delta; } -if (host_clock-enabled host_clock-active_timers) { -int64_t hdelta = host_clock-active_timers-expire_time - - qemu_get_clock_ns(host_clock); -if (hdelta delta) { -delta = hdelta; -} + +qemu_mutex_lock(clock-active_timers_lock); +if (clock-active_timers) { +has_timer = true; +expire_time = clock-active_timers-expire_time; } Ideally, the main loop would only lock clocks that have an expired timer. We can optimize it later, but perhaps you can add a FIXME comment. @@ -254,22 +264,41 @@ void qemu_clock_enable(QEMUClock *clock, bool enabled) int64_t qemu_clock_has_timers(QEMUClock *clock) { -return !!clock-active_timers; +bool has_timers; + +qemu_mutex_lock(clock-active_timers_lock); +has_timers = !!clock-active_timers; +qemu_mutex_unlock(clock-active_timers_lock); +return has_timers; This doesn't need the lock; the result can change immediately after qemu_clock_has_timers returns. On the other hand, this is likely a sign that the resulting code is actually not thread safe. I think you can remove the call to qemu_clock_has_timers(vm_clock) from qemu_clock_warp. It will advance icount_warp_timer by INT32_MAX nsecs (a couple of seconds), which is fine. } int64_t qemu_clock_expired(QEMUClock *clock) { -return (clock-active_timers -clock-active_timers-expire_time qemu_get_clock_ns(clock)); +bool has_timers; +int64_t expire_time; + +qemu_mutex_lock(clock-active_timers_lock); +has_timers = clock-active_timers; +expire_time = clock-active_timers-expire_time; +qemu_mutex_unlock(clock-active_timers_lock); + +return has_timers expire_time qemu_get_clock_ns(clock); } int64_t qemu_clock_deadline(QEMUClock *clock) { /* To avoid problems with overflow limit this to 2^32. */ int64_t delta = INT32_MAX; +bool has_timers; +int64_t expire_time; -if (clock-active_timers) { -delta = clock-active_timers-expire_time - qemu_get_clock_ns(clock); +qemu_mutex_lock(clock-active_timers_lock); +has_timers = clock-active_timers; +expire_time = clock-active_timers-expire_time; +qemu_mutex_unlock(clock-active_timers_lock); + +if (has_timers) { +delta = expire_time - qemu_get_clock_ns(clock); } if (delta 0) { delta = 0; @@ -298,8 +327,10 @@ void qemu_free_timer(QEMUTimer *ts) /* stop a timer, but do not dealloc it */ void qemu_del_timer(QEMUTimer *ts) { +QEMUClock *clock = ts-clock; QEMUTimer **pt, *t; +qemu_mutex_lock(clock-active_timers_lock); pt = ts-clock-active_timers; for(;;) { t = *pt; @@ -311,18 +342,21 @@ void qemu_del_timer(QEMUTimer *ts) } pt = t-next; } +qemu_mutex_unlock(clock-active_timers_lock); } /* modify the current timer so that it will be fired when current_time = expire_time. The corresponding callback will be called. */ void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time) { +QEMUClock *clock = ts-clock; QEMUTimer **pt, *t; qemu_del_timer(ts); /* add the timer in