From 9d6346311418d12e90cca9384e5fbbe2ffa18efb Mon Sep 17 00:00:00 2001 From: Daniel Walker Date: Wed, 28 Feb 2007 20:12:07 -0800 Subject: [PATCH] update timekeeping_is_continuous comment Signed-off-by: Daniel Walker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel/timer.c') diff --git a/kernel/timer.c b/kernel/timer.c index cb1b86a9c52f..d033313bf53c 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -908,7 +908,7 @@ static inline void change_clocksource(void) { } #endif /** - * timeofday_is_continuous - check to see if timekeeping is free running + * timekeeping_is_continuous - check to see if timekeeping is free running */ int timekeeping_is_continuous(void) { -- cgit v1.2.3 From 05fb6bf0b29552b64dc86f405a484de2514e0ac2 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 28 Feb 2007 20:12:13 -0800 Subject: [PATCH] kernel-doc fixes for 2.6.20-git15 (non-drivers) Fix kernel-doc warnings in 2.6.20-git15 (lib/, mm/, kernel/, include/). Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hrtimer.h | 1 + kernel/relay.c | 3 +-- kernel/timer.c | 1 + lib/bitmap.c | 16 ++++++++-------- mm/slab.c | 2 +- 5 files changed, 12 insertions(+), 11 deletions(-) (limited to 'kernel/timer.c') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 37f9279192a9..3bef961b58b1 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -140,6 +140,7 @@ struct hrtimer_sleeper { /** * struct hrtimer_base - the timer base for a specific clock + * @cpu_base: per cpu clock base * @index: clock type index for per_cpu support when moving a * timer to a base on another cpu. * @active: red black tree root node for the active timers diff --git a/kernel/relay.c b/kernel/relay.c index ef8a935710a2..577f251c7e28 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -474,13 +474,12 @@ static void setup_callbacks(struct rchan *chan, } /** - * * relay_hotcpu_callback - CPU hotplug callback * @nb: notifier block * @action: hotplug action to take * @hcpu: CPU number * - * Returns the success/failure of the operation. (NOTIFY_OK, NOTIFY_BAD) + * Returns the success/failure of the operation. (%NOTIFY_OK, %NOTIFY_BAD) */ static int __cpuinit relay_hotcpu_callback(struct notifier_block *nb, unsigned long action, diff --git a/kernel/timer.c b/kernel/timer.c index d033313bf53c..6663a87f7304 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -711,6 +711,7 @@ static unsigned long cmp_next_hrtimer_event(unsigned long now, /** * next_timer_interrupt - return the jiffy of the next pending timer + * @now: current time (in jiffies) */ unsigned long get_next_timer_interrupt(unsigned long now) { diff --git a/lib/bitmap.c b/lib/bitmap.c index ee6e58fce8f7..26ebafa8c41d 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c @@ -97,10 +97,10 @@ EXPORT_SYMBOL(__bitmap_complement); /** * __bitmap_shift_right - logical right shift of the bits in a bitmap - * @dst - destination bitmap - * @src - source bitmap - * @nbits - shift by this many bits - * @bits - bitmap size, in bits + * @dst : destination bitmap + * @src : source bitmap + * @shift : shift by this many bits + * @bits : bitmap size, in bits * * Shifting right (dividing) means moving bits in the MS -> LS bit * direction. Zeros are fed into the vacated MS positions and the @@ -141,10 +141,10 @@ EXPORT_SYMBOL(__bitmap_shift_right); /** * __bitmap_shift_left - logical left shift of the bits in a bitmap - * @dst - destination bitmap - * @src - source bitmap - * @nbits - shift by this many bits - * @bits - bitmap size, in bits + * @dst : destination bitmap + * @src : source bitmap + * @shift : shift by this many bits + * @bits : bitmap size, in bits * * Shifting left (multiplying) means moving bits in the LS -> MS * direction. Zeros are fed into the vacated LS bit positions diff --git a/mm/slab.c b/mm/slab.c index 8fdaffa717ea..57f7aa420064 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -4026,7 +4026,7 @@ void drain_array(struct kmem_cache *cachep, struct kmem_list3 *l3, /** * cache_reap - Reclaim memory from caches. - * @unused: unused parameter + * @w: work descriptor * * Called from workqueue/eventd every few seconds. * Purpose: -- cgit v1.2.3 From e81ce1f7ecdaed2844c75313b09af791d44e6373 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 5 Mar 2007 00:30:51 -0800 Subject: [PATCH] timer/hrtimer: take per cpu locks in sane order Doing something like this on a two cpu system # echo 0 > /sys/devices/system/cpu/cpu0/online # echo 1 > /sys/devices/system/cpu/cpu0/online # echo 0 > /sys/devices/system/cpu/cpu1/online will give me this: ======================================================= [ INFO: possible circular locking dependency detected ] 2.6.21-rc2-g562aa1d4-dirty #7 ------------------------------------------------------- bash/1282 is trying to acquire lock: (&cpu_base->lock_key){.+..}, at: [<000000000005f17e>] hrtimer_cpu_notify+0xc6/0x240 but task is already holding lock: (&cpu_base->lock_key#2){.+..}, at: [<000000000005f174>] hrtimer_cpu_notify+0xbc/0x240 which lock already depends on the new lock. This happens because we have the following code in kernel/hrtimer.c: migrate_hrtimers(int cpu) [...] old_base = &per_cpu(hrtimer_bases, cpu); new_base = &get_cpu_var(hrtimer_bases); [...] spin_lock(&new_base->lock); spin_lock(&old_base->lock); Which means the spinlocks are taken in an order which depends on which cpu gets shut down from which other cpu. Therefore lockdep complains that there might be an ABBA deadlock. Since migrate_hrtimers() gets only called on cpu hotplug it's safe to assume that it isn't executed concurrently on a The same problem exists in kernel/timer.c: migrate_timers(). As pointed out by Christian Borntraeger one possible solution to avoid the locking order complaints would be to make sure that the locks are always taken in the same order. E.g. by taking the lock of the cpu with the lower number first. To achieve this we introduce two new spinlock functions double_spin_lock and double_spin_unlock which lock or unlock two locks in a given order. Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Roman Zippel Cc: John Stultz Cc: Christian Borntraeger Cc: Martin Schwidefsky Signed-off-by: Heiko Carstens Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/spinlock.h | 37 +++++++++++++++++++++++++++++++++++++ kernel/hrtimer.c | 9 ++++----- kernel/timer.c | 8 ++++---- 3 files changed, 45 insertions(+), 9 deletions(-) (limited to 'kernel/timer.c') diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h index 61fef376ed2e..a946176db638 100644 --- a/include/linux/spinlock.h +++ b/include/linux/spinlock.h @@ -282,6 +282,43 @@ do { \ 1 : ({ local_irq_restore(flags); 0; }); \ }) +/* + * Locks two spinlocks l1 and l2. + * l1_first indicates if spinlock l1 should be taken first. + */ +static inline void double_spin_lock(spinlock_t *l1, spinlock_t *l2, + bool l1_first) + __acquires(l1) + __acquires(l2) +{ + if (l1_first) { + spin_lock(l1); + spin_lock(l2); + } else { + spin_lock(l2); + spin_lock(l1); + } +} + +/* + * Unlocks two spinlocks l1 and l2. + * l1_taken_first indicates if spinlock l1 was taken first and therefore + * should be released after spinlock l2. + */ +static inline void double_spin_unlock(spinlock_t *l1, spinlock_t *l2, + bool l1_taken_first) + __releases(l1) + __releases(l2) +{ + if (l1_taken_first) { + spin_unlock(l2); + spin_unlock(l1); + } else { + spin_unlock(l1); + spin_unlock(l2); + } +} + /* * Pull the atomic_t declaration: * (asm-mips/atomic.h needs above definitions) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 476cb0c0b4a4..de93a8176ca6 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -1355,17 +1355,16 @@ static void migrate_hrtimers(int cpu) tick_cancel_sched_timer(cpu); local_irq_disable(); - - spin_lock(&new_base->lock); - spin_lock(&old_base->lock); + double_spin_lock(&new_base->lock, &old_base->lock, + smp_processor_id() < cpu); for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { migrate_hrtimer_list(&old_base->clock_base[i], &new_base->clock_base[i]); } - spin_unlock(&old_base->lock); - spin_unlock(&new_base->lock); + double_spin_unlock(&new_base->lock, &old_base->lock, + smp_processor_id() < cpu); local_irq_enable(); put_cpu_var(hrtimer_bases); } diff --git a/kernel/timer.c b/kernel/timer.c index 6663a87f7304..8ad384253ef2 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -1651,8 +1651,8 @@ static void __devinit migrate_timers(int cpu) new_base = get_cpu_var(tvec_bases); local_irq_disable(); - spin_lock(&new_base->lock); - spin_lock(&old_base->lock); + double_spin_lock(&new_base->lock, &old_base->lock, + smp_processor_id() < cpu); BUG_ON(old_base->running_timer); @@ -1665,8 +1665,8 @@ static void __devinit migrate_timers(int cpu) migrate_timer_list(new_base, old_base->tv5.vec + i); } - spin_unlock(&old_base->lock); - spin_unlock(&new_base->lock); + double_spin_unlock(&new_base->lock, &old_base->lock, + smp_processor_id() < cpu); local_irq_enable(); put_cpu_var(tvec_bases); } -- cgit v1.2.3 From 6321dd60c76b2e12383bc06046288b15397ed3a0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 6 Mar 2007 08:25:42 +0100 Subject: [PATCH] Save/restore periodic tick information over suspend/resume The programming of periodic tick devices needs to be saved/restored across suspend/resume - otherwise we might end up with a system coming up that relies on getting a PIT (or HPET) interrupt, while those devices default to 'no interrupts' after powerup. (To confuse things it worked to a certain degree on some systems because the lapic gets initialized as a side-effect of SMP bootup.) This suspend / resume thing was dropped unintentionally during the last-minute -mm code reshuffling. Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Signed-off-by: Linus Torvalds --- kernel/time/tick-broadcast.c | 36 ++++++++++++++++++++++++++++++++++++ kernel/time/tick-common.c | 32 ++++++++++++++++++++++++++++++++ kernel/time/tick-internal.h | 4 ++++ kernel/timer.c | 6 ++++++ 4 files changed, 78 insertions(+) (limited to 'kernel/timer.c') diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 12b3efeb9f6f..5567745470f7 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -284,6 +284,42 @@ void tick_shutdown_broadcast(unsigned int *cpup) spin_unlock_irqrestore(&tick_broadcast_lock, flags); } +void tick_suspend_broadcast(void) +{ + struct clock_event_device *bc; + unsigned long flags; + + spin_lock_irqsave(&tick_broadcast_lock, flags); + + bc = tick_broadcast_device.evtdev; + if (bc && tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) + clockevents_set_mode(bc, CLOCK_EVT_MODE_SHUTDOWN); + + spin_unlock_irqrestore(&tick_broadcast_lock, flags); +} + +int tick_resume_broadcast(void) +{ + struct clock_event_device *bc; + unsigned long flags; + int broadcast = 0; + + spin_lock_irqsave(&tick_broadcast_lock, flags); + + bc = tick_broadcast_device.evtdev; + if (bc) { + if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC && + !cpus_empty(tick_broadcast_mask)) + tick_broadcast_start_periodic(bc); + + broadcast = cpu_isset(smp_processor_id(), tick_broadcast_mask); + } + spin_unlock_irqrestore(&tick_broadcast_lock, flags); + + return broadcast; +} + + #ifdef CONFIG_TICK_ONESHOT static cpumask_t tick_broadcast_oneshot_mask; diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 0986a2bfab49..43ba1bdec14c 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -298,6 +298,28 @@ static void tick_shutdown(unsigned int *cpup) spin_unlock_irqrestore(&tick_device_lock, flags); } +static void tick_suspend_periodic(void) +{ + struct tick_device *td = &__get_cpu_var(tick_cpu_device); + unsigned long flags; + + spin_lock_irqsave(&tick_device_lock, flags); + if (td->mode == TICKDEV_MODE_PERIODIC) + clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_SHUTDOWN); + spin_unlock_irqrestore(&tick_device_lock, flags); +} + +static void tick_resume_periodic(void) +{ + struct tick_device *td = &__get_cpu_var(tick_cpu_device); + unsigned long flags; + + spin_lock_irqsave(&tick_device_lock, flags); + if (td->mode == TICKDEV_MODE_PERIODIC) + tick_setup_periodic(td->evtdev, 0); + spin_unlock_irqrestore(&tick_device_lock, flags); +} + /* * Notification about clock event devices */ @@ -325,6 +347,16 @@ static int tick_notify(struct notifier_block *nb, unsigned long reason, tick_shutdown(dev); break; + case CLOCK_EVT_NOTIFY_SUSPEND: + tick_suspend_periodic(); + tick_suspend_broadcast(); + break; + + case CLOCK_EVT_NOTIFY_RESUME: + if (!tick_resume_broadcast()) + tick_resume_periodic(); + break; + default: break; } diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h index 54861a0f29ff..75890efd24ff 100644 --- a/kernel/time/tick-internal.h +++ b/kernel/time/tick-internal.h @@ -67,6 +67,8 @@ extern int tick_check_broadcast_device(struct clock_event_device *dev); extern int tick_is_broadcast_device(struct clock_event_device *dev); extern void tick_broadcast_on_off(unsigned long reason, int *oncpu); extern void tick_shutdown_broadcast(unsigned int *cpup); +extern void tick_suspend_broadcast(void); +extern int tick_resume_broadcast(void); extern void tick_set_periodic_handler(struct clock_event_device *dev, int broadcast); @@ -90,6 +92,8 @@ static inline int tick_device_uses_broadcast(struct clock_event_device *dev, static inline void tick_do_periodic_broadcast(struct clock_event_device *d) { } static inline void tick_broadcast_on_off(unsigned long reason, int *oncpu) { } static inline void tick_shutdown_broadcast(unsigned int *cpup) { } +static inline void tick_suspend_broadcast(void) { } +static inline int tick_resume_broadcast(void) { return 0; } /* * Set the periodic handler in non broadcast mode diff --git a/kernel/timer.c b/kernel/timer.c index 8ad384253ef2..ee0a2da4aab3 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -997,6 +997,9 @@ static int timekeeping_resume(struct sys_device *dev) write_sequnlock_irqrestore(&xtime_lock, flags); touch_softlockup_watchdog(); + + clockevents_notify(CLOCK_EVT_NOTIFY_RESUME, NULL); + /* Resume hrtimers */ clock_was_set(); @@ -1011,6 +1014,9 @@ static int timekeeping_suspend(struct sys_device *dev, pm_message_t state) timekeeping_suspended = 1; timekeeping_suspend_time = read_persistent_clock(); write_sequnlock_irqrestore(&xtime_lock, flags); + + clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL); + return 0; } -- cgit v1.2.3 From 90675a27fa3eb0e97f1d040b183cceb44316e669 Mon Sep 17 00:00:00 2001 From: Daniel Walker Date: Tue, 6 Mar 2007 01:42:11 -0800 Subject: [PATCH] fix vsyscall settimeofday I've only seen this on x86_64. The vsyscall state only gets updated when a timer interrupts comes in. So if the time is set long before the next timer, there will be a period when a gettimeofday() won't reflect the correct time. I added an explicit update_vsyscall() during the settimeofday(), that way the vsyscall state doesn't get stale. Signed-off-by: Daniel Walker Cc: Thomas Gleixner Acked-by: Ingo Molnar Acked-by: John Stultz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/timer.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'kernel/timer.c') diff --git a/kernel/timer.c b/kernel/timer.c index ee0a2da4aab3..797cccb86431 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -862,6 +862,8 @@ int do_settimeofday(struct timespec *tv) clock->error = 0; ntp_clear(); + update_vsyscall(&xtime, clock); + write_sequnlock_irqrestore(&xtime_lock, flags); /* signal hrtimers about time change */ -- cgit v1.2.3 From 9501b6cf5541f0d576d566a463f1e7d3eaaab4eb Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 25 Mar 2007 14:31:17 +0200 Subject: [PATCH] dynticks: fix hrtimer rounding error in next_timer_interrupt The rework of next_timer_interrupt() fixed the timer wheel bugs, but invented a rounding error versus the next hrtimer event. This is caused by the conversion of the hrtimer internal representation to relative jiffies. This causes bug #8100: http://bugzilla.kernel.org/show_bug.cgi?id=8100 next_timer_interrupt() returns "now" in such a case and causes the code in tick_nohz_stop_sched_tick() to trigger the timer softirq, which is bogus as no timer is due for expiry. This results in an endless context switching between idle and ksoftirqd until a timer is due for expiry. Modify the hrtimer evaluation so that, it returns now + 1, when the conversion results in a delta < 1 jiffie. It's confirmed to resolve bug #8100 Reported-by: Emil Karlson Signed-off-by: Thomas Gleixner Signed-off-by: Linus Torvalds --- kernel/timer.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'kernel/timer.c') diff --git a/kernel/timer.c b/kernel/timer.c index 797cccb86431..440048acaea1 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -695,15 +695,28 @@ static unsigned long cmp_next_hrtimer_event(unsigned long now, { ktime_t hr_delta = hrtimer_get_next_event(); struct timespec tsdelta; + unsigned long delta; if (hr_delta.tv64 == KTIME_MAX) return expires; - if (hr_delta.tv64 <= TICK_NSEC) - return now; + /* + * Expired timer available, let it expire in the next tick + */ + if (hr_delta.tv64 <= 0) + return now + 1; tsdelta = ktime_to_timespec(hr_delta); - now += timespec_to_jiffies(&tsdelta); + delta = timespec_to_jiffies(&tsdelta); + /* + * Take rounding errors in to account and make sure, that it + * expires in the next tick. Otherwise we go into an endless + * ping pong due to tick_nohz_stop_sched_tick() retriggering + * the timer softirq + */ + if (delta < 1) + delta = 1; + now += delta; if (time_before(now, expires)) return now; return expires; -- cgit v1.2.3 From 995f054f2a342f8505fed4f8395d12c0f5966414 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sat, 7 Apr 2007 12:05:00 +0200 Subject: [PATCH] high-res timers: resume fix Soeren Sonnenburg reported that upon resume he is getting this backtrace: [] smp_apic_timer_interrupt+0x57/0x90 [] retrigger_next_event+0x0/0xb0 [] apic_timer_interrupt+0x28/0x30 [] retrigger_next_event+0x0/0xb0 [] __kfifo_put+0x8/0x90 [] on_each_cpu+0x35/0x60 [] clock_was_set+0x18/0x20 [] timekeeping_resume+0x7c/0xa0 [] __sysdev_resume+0x11/0x80 [] sysdev_resume+0x47/0x80 [] device_power_up+0x5/0x10 it turns out that on resume we mistakenly re-enable interrupts too early. Do the timer retrigger only on the current CPU. Signed-off-by: Ingo Molnar Acked-by: Thomas Gleixner Acked-by: Soeren Sonnenburg Signed-off-by: Linus Torvalds --- include/linux/hrtimer.h | 3 +++ kernel/hrtimer.c | 12 ++++++++++++ kernel/timer.c | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'kernel/timer.c') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 5bdbc744e773..17c29dca8354 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -206,6 +206,7 @@ struct hrtimer_cpu_base { struct clock_event_device; extern void clock_was_set(void); +extern void hres_timers_resume(void); extern void hrtimer_interrupt(struct clock_event_device *dev); /* @@ -236,6 +237,8 @@ static inline ktime_t hrtimer_cb_get_time(struct hrtimer *timer) */ static inline void clock_was_set(void) { } +static inline void hres_timers_resume(void) { } + /* * In non high resolution mode the time reference is taken from * the base softirq time variable. diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 067ba2c05328..b74860aaf5f1 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -458,6 +458,18 @@ void clock_was_set(void) on_each_cpu(retrigger_next_event, NULL, 0, 1); } +/* + * During resume we might have to reprogram the high resolution timer + * interrupt (on the local CPU): + */ +void hres_timers_resume(void) +{ + WARN_ON_ONCE(num_online_cpus() > 1); + + /* Retrigger the CPU local events: */ + retrigger_next_event(NULL); +} + /* * Check, whether the timer is on the callback pending list */ diff --git a/kernel/timer.c b/kernel/timer.c index 440048acaea1..dd6c2c1c561b 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -1016,7 +1016,7 @@ static int timekeeping_resume(struct sys_device *dev) clockevents_notify(CLOCK_EVT_NOTIFY_RESUME, NULL); /* Resume hrtimers */ - clock_was_set(); + hres_timers_resume(); return 0; } -- cgit v1.2.3 From e19dff1fdd99a25819af74cf0710e147fff4fd3a Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 26 Apr 2007 15:46:56 -0700 Subject: [AF_RXRPC]: Make it possible to merely try to cancel timers from a module Export try_to_del_timer_sync() for use by the AF_RXRPC module. Signed-off-by: David Howells Signed-off-by: David S. Miller --- kernel/timer.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'kernel/timer.c') diff --git a/kernel/timer.c b/kernel/timer.c index dd6c2c1c561b..b22bd39740dd 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -505,6 +505,8 @@ out: return ret; } +EXPORT_SYMBOL(try_to_del_timer_sync); + /** * del_timer_sync - deactivate a timer and wait for the handler to finish. * @timer: the timer to be deactivated -- cgit v1.2.3