linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 6/5] timers: enable irqs in __mod_timer()
@ 2005-03-21 14:19 Oleg Nesterov
  2005-03-22  5:43 ` Andrew Morton
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Oleg Nesterov @ 2005-03-21 14:19 UTC (permalink / raw)
  To: linux-kernel
  Cc: Ingo Molnar, Christoph Lameter, Chen, Kenneth W, Andrew Morton

On top of "[PATCH 5/5] timers: cleanup, kill __get_base()", see
http://marc.theaimsgroup.com/?l=linux-kernel&m=111125359121372

If the timer is currently running on another CPU, __mod_timer()
spins with interrupts disabled and timer->lock held. I think it
is better to spin_unlock_irqrestore(&timer->lock) in __mod_timer's
retry path.

This patch is unneccessary long. It is because it tries to cleanup
the code a bit. I do not like the fact that lock+test+unlock pattern
is duplicated in the code.

If you think that this patch uglifies the code or does not match
kernel's coding style - just say nack :)

Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>

--- 2.6.12-rc1/kernel/timer.c~6_LOCK	2005-03-19 23:34:23.000000000 +0300
+++ 2.6.12-rc1/kernel/timer.c	2005-03-21 18:55:25.000000000 +0300
@@ -174,64 +174,60 @@ int __mod_timer(struct timer_list *timer
 {
 	tvec_base_t *old_base, *new_base;
 	unsigned long flags;
-	int ret = 0;
-
-	BUG_ON(!timer->function);
-
-	check_timer(timer);
-
-	spin_lock_irqsave(&timer->lock, flags);
-	new_base = &__get_cpu_var(tvec_bases);
-repeat:
-	old_base = timer_base(timer);
-
-	/*
-	 * Prevent deadlocks via ordering by old_base < new_base.
-	 */
-	if (old_base && (new_base != old_base)) {
-		if (old_base < new_base) {
-			spin_lock(&new_base->lock);
-			spin_lock(&old_base->lock);
-		} else {
-			spin_lock(&old_base->lock);
-			spin_lock(&new_base->lock);
-		}
-		/*
-		 * The timer base might have been cancelled while we were
-		 * trying to take the lock(s), or can still be running on
-		 * old_base's CPU.
-		 */
-		if (timer_base(timer) != old_base
-				|| old_base->running_timer == timer) {
-			spin_unlock(&new_base->lock);
-			spin_unlock(&old_base->lock);
-			goto repeat;
-		}
-	} else {
-		spin_lock(&new_base->lock);
-		if (timer_base(timer) != old_base) {
-			spin_unlock(&new_base->lock);
-			goto repeat;
-		}
-	}
-
-	/*
-	 * Delete the previous timeout (if there was any).
-	 * We hold timer->lock, no need to check old_base != 0.
-	 */
-	if (timer_pending(timer)) {
-		list_del(&timer->entry);
-		ret = 1;
-	}
-
-	timer->expires = expires;
-	internal_add_timer(new_base, timer);
-	__set_base(timer, new_base, 1);
-
-	if (old_base && (new_base != old_base))
-		spin_unlock(&old_base->lock);
-	spin_unlock(&new_base->lock);
-	spin_unlock_irqrestore(&timer->lock, flags);
+	int new_lock, ret;
+
+	BUG_ON(!timer->function);
+	check_timer(timer);
+
+	for (ret = -1; ret < 0; ) {
+		spin_lock_irqsave(&timer->lock, flags);
+		new_base = &__get_cpu_var(tvec_bases);
+		old_base = timer_base(timer);
+
+		/* Prevent AB-BA deadlocks. */
+		new_lock = old_base < new_base;
+		if (new_lock)
+			spin_lock(&new_base->lock);
+
+		/* Note:
+		 *	(old_base == NULL)     => (new_lock == 1)
+		 *	(old_base == new_base) => (new_lock == 0)
+		 */
+		if (old_base) {
+			spin_lock(&old_base->lock);
+
+			if (timer_base(timer) != old_base)
+				goto unlock;
+
+			if (old_base != new_base) {
+				/* Ensure the timer is serialized. */
+				if (old_base->running_timer == timer)
+					goto unlock;
+
+				if (!new_lock) {
+					spin_lock(&new_base->lock);
+					new_lock = 1;
+				}
+			}
+		}
+
+		ret = 0;
+		/* We hold timer->lock, no need to check old_base != 0. */
+		if (timer_pending(timer)) {
+			list_del(&timer->entry);
+			ret = 1;
+		}
+
+		timer->expires = expires;
+		internal_add_timer(new_base, timer);
+		__set_base(timer, new_base, 1);
+unlock:
+		if (old_base)
+			spin_unlock(&old_base->lock);
+		if (new_lock)
+			spin_unlock(&new_base->lock);
+		spin_unlock_irqrestore(&timer->lock, flags);
+	}
 
 	return ret;
 }

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 6/5] timers: enable irqs in __mod_timer()
  2005-03-21 14:19 [PATCH 6/5] timers: enable irqs in __mod_timer() Oleg Nesterov
@ 2005-03-22  5:43 ` Andrew Morton
  2005-03-24 21:56 ` [PATCH 0/5] timers: description Chen, Kenneth W
  2005-03-26 19:52 ` Chen, Kenneth W
  2 siblings, 0 replies; 9+ messages in thread
From: Andrew Morton @ 2005-03-22  5:43 UTC (permalink / raw)
  To: Oleg Nesterov; +Cc: linux-kernel, mingo, christoph, kenneth.w.chen

Oleg Nesterov <oleg@tv-sign.ru> wrote:
>
> If the timer is currently running on another CPU, __mod_timer()
>  spins with interrupts disabled and timer->lock held. I think it
>  is better to spin_unlock_irqrestore(&timer->lock) in __mod_timer's
>  retry path.
> 
>  This patch is unneccessary long. It is because it tries to cleanup
>  the code a bit. I do not like the fact that lock+test+unlock pattern
>  is duplicated in the code.
> 
>  If you think that this patch uglifies the code or does not match
>  kernel's coding style - just say nack :)

I've seen worse ;)

I think this makes it a bit more kernel-like?

--- 25/kernel/timer.c~timers-enable-irqs-in-__mod_timer-tidy	2005-03-21 21:41:03.000000000 -0800
+++ 25-akpm/kernel/timer.c	2005-03-21 21:41:57.000000000 -0800
@@ -174,12 +174,13 @@ int __mod_timer(struct timer_list *timer
 {
 	tvec_base_t *old_base, *new_base;
 	unsigned long flags;
-	int new_lock, ret;
+	int new_lock;
+	int ret = -1;
 
 	BUG_ON(!timer->function);
 	check_timer(timer);
 
-	for (ret = -1; ret < 0; ) {
+	do {
 		spin_lock_irqsave(&timer->lock, flags);
 		new_base = &__get_cpu_var(tvec_bases);
 		old_base = timer_base(timer);
@@ -227,7 +228,7 @@ unlock:
 		if (new_lock)
 			spin_unlock(&new_base->lock);
 		spin_unlock_irqrestore(&timer->lock, flags);
-	}
+	} while (ret == -1);
 
 	return ret;
 }
_


^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: [PATCH 0/5] timers: description
  2005-03-21 14:19 [PATCH 6/5] timers: enable irqs in __mod_timer() Oleg Nesterov
  2005-03-22  5:43 ` Andrew Morton
@ 2005-03-24 21:56 ` Chen, Kenneth W
  2005-03-26 19:52 ` Chen, Kenneth W
  2 siblings, 0 replies; 9+ messages in thread
From: Chen, Kenneth W @ 2005-03-24 21:56 UTC (permalink / raw)
  To: 'Oleg Nesterov', linux-kernel
  Cc: Ingo Molnar, Christoph Lameter, Andrew Morton

Oleg Nesterov wrote on Monday, March 21, 2005 6:19 AM
> These patches are updated version of 'del_timer_sync: proof of concept'
> 2 patches.

Looks good performance wise.  Took a quick micro benchmark measurement on
a 16-node numa box (32-way). Results are pretty nice (to the expectation).

Time to execute del_timer_sync, averaged over 10,000 call:
Vanilla 2.6.11	19,313 ns
2.6.12-rc1-mm1	    81 ns
del_singleshot_timer_sync 74ns

Took another measurement on a 4-way smp box:
Vanilla 2.6.11	648 ns
2.6.12-rc1-mm1	 55 ns
del_singleshot	 41 ns

And Andrew always asks what are the impact on real-world bench, we did
not see performance regression on db transaction processing benchmark
with these set of timer patches versus using del_singleshot_timer_sync.
(del_singleshot_timer_sync is slightly faster, though 14 ns difference
falls well into noise range)

Note: I've only stress tested deleting an un-expired timer.



^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: [PATCH 0/5] timers: description
  2005-03-21 14:19 [PATCH 6/5] timers: enable irqs in __mod_timer() Oleg Nesterov
  2005-03-22  5:43 ` Andrew Morton
  2005-03-24 21:56 ` [PATCH 0/5] timers: description Chen, Kenneth W
@ 2005-03-26 19:52 ` Chen, Kenneth W
  2005-03-27 10:08   ` Oleg Nesterov
  2005-03-28 19:12   ` Christoph Lameter
  2 siblings, 2 replies; 9+ messages in thread
From: Chen, Kenneth W @ 2005-03-26 19:52 UTC (permalink / raw)
  To: 'Oleg Nesterov', linux-kernel
  Cc: Ingo Molnar, Christoph Lameter, Andrew Morton

Oleg Nesterov wrote on March 19, 2005 17:28:48
> These patches are updated version of 'del_timer_sync: proof of
> concept' 2 patches.

I changed schedule_timeout() to call the new del_timer_sync instead of
currently del_singleshot_timer_sync in attempt to stress these set of
patches a bit more and I just observed a kernel hang.

The symptom starts with lost network connectivity.  It looks like the
entire ethernet connections were gone, followed by blank screen on the
console.  I'm not sure whether it is a hard or soft hang, but system
is inaccessible (blank screen and no network connection). I'm forced
to do a reboot when that happens.



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/5] timers: description
  2005-03-26 19:52 ` Chen, Kenneth W
@ 2005-03-27 10:08   ` Oleg Nesterov
  2005-03-28 19:12   ` Christoph Lameter
  1 sibling, 0 replies; 9+ messages in thread
From: Oleg Nesterov @ 2005-03-27 10:08 UTC (permalink / raw)
  To: Chen, Kenneth W
  Cc: linux-kernel, Ingo Molnar, Christoph Lameter, Andrew Morton

"Chen, Kenneth W" wrote:
>
> Oleg Nesterov wrote on March 19, 2005 17:28:48
> > These patches are updated version of 'del_timer_sync: proof of
> > concept' 2 patches.
>
> I changed schedule_timeout() to call the new del_timer_sync instead of
> currently del_singleshot_timer_sync in attempt to stress these set of
> patches a bit more and I just observed a kernel hang.
>
> The symptom starts with lost network connectivity.  It looks like the
> entire ethernet connections were gone, followed by blank screen on the
> console.  I'm not sure whether it is a hard or soft hang, but system
> is inaccessible (blank screen and no network connection). I'm forced
> to do a reboot when that happens.

Very strange. I am running 2.6.11 + timer patches +
	#define del_singleshot_timer_sync(t) del_timer_sync(t)
without any problems.

This timer is private to schedule_timeout(), it can't change
base, so del_timer_sync() should be "obviously correct" in
that case.

What kernel version? Could you try this stupid patch?

Oleg.

--- TST/kernel/timer.c~	2005-03-27 16:47:20.000000000 +0400
+++ TST/kernel/timer.c	2005-03-27 17:16:32.000000000 +0400
@@ -352,27 +352,46 @@ EXPORT_SYMBOL(del_timer);
  */
 int del_timer_sync(struct timer_list *timer)
 {
+	unsigned long tout;
+	int running = 0, migrated = 0, done = 0;
 	int ret;
 
 	check_timer(timer);
 
+	preempt_disable();
+	tout = jiffies + 10;
+
 	ret = 0;
 	for (;;) {
 		unsigned long flags;
 		tvec_base_t *base;
 
 		base = timer_base(timer);
-		if (!base)
+		if (!base) {
+			preempt_enable();
 			return ret;
+		}
+		if (time_after(jiffies, tout)) {
+			preempt_enable();
+			printk(KERN_ERR "del_timer_sync hang: %d %d %d %d\n",
+				running, migrated, done, ret);
+			dump_stack();
+			return 0;
+		}
 
 		spin_lock_irqsave(&base->lock, flags);
 
-		if (base->running_timer == timer)
+		if (base->running_timer == timer) {
+			++running;
 			goto unlock;
+		}
 
-		if (timer_base(timer) != base)
+		if (timer_base(timer) != base) {
+			++migrated;
 			goto unlock;
+		}
 
+		++done;
 		if (timer_pending(timer)) {
 			list_del(&timer->entry);
 			ret = 1;

^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: [PATCH 0/5] timers: description
  2005-03-26 19:52 ` Chen, Kenneth W
  2005-03-27 10:08   ` Oleg Nesterov
@ 2005-03-28 19:12   ` Christoph Lameter
  2005-03-29 11:27     ` Oleg Nesterov
  1 sibling, 1 reply; 9+ messages in thread
From: Christoph Lameter @ 2005-03-28 19:12 UTC (permalink / raw)
  To: Chen, Kenneth W
  Cc: 'Oleg Nesterov', linux-kernel, Ingo Molnar, Andrew Morton

On Sat, 26 Mar 2005, Chen, Kenneth W wrote:

> Oleg Nesterov wrote on March 19, 2005 17:28:48
> > These patches are updated version of 'del_timer_sync: proof of
> > concept' 2 patches.
>
> I changed schedule_timeout() to call the new del_timer_sync instead of
> currently del_singleshot_timer_sync in attempt to stress these set of
> patches a bit more and I just observed a kernel hang.
>
> The symptom starts with lost network connectivity.  It looks like the
> entire ethernet connections were gone, followed by blank screen on the
> console.  I'm not sure whether it is a hard or soft hang, but system
> is inaccessible (blank screen and no network connection). I'm forced
> to do a reboot when that happens.

Same problems here with occasional hangs w/o changes to schedule_timeout.


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/5] timers: description
  2005-03-28 19:12   ` Christoph Lameter
@ 2005-03-29 11:27     ` Oleg Nesterov
  2005-03-29 14:47       ` Christoph Lameter
  0 siblings, 1 reply; 9+ messages in thread
From: Oleg Nesterov @ 2005-03-29 11:27 UTC (permalink / raw)
  To: Christoph Lameter
  Cc: Chen, Kenneth W, linux-kernel, Ingo Molnar, Andrew Morton

Christoph Lameter wrote:
>
> On Sat, 26 Mar 2005, Chen, Kenneth W wrote:
>
> > I changed schedule_timeout() to call the new del_timer_sync instead of
> > currently del_singleshot_timer_sync in attempt to stress these set of
> > patches a bit more and I just observed a kernel hang.
> >
> > The symptom starts with lost network connectivity.  It looks like the
> > entire ethernet connections were gone, followed by blank screen on the
> > console.  I'm not sure whether it is a hard or soft hang, but system
> > is inaccessible (blank screen and no network connection). I'm forced
> > to do a reboot when that happens.
>
> Same problems here with occasional hangs w/o changes to schedule_timeout.

Bad. You are runnning 2.6.12-rc1-mm1 ?

Oleg.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/5] timers: description
  2005-03-29 11:27     ` Oleg Nesterov
@ 2005-03-29 14:47       ` Christoph Lameter
  0 siblings, 0 replies; 9+ messages in thread
From: Christoph Lameter @ 2005-03-29 14:47 UTC (permalink / raw)
  To: Oleg Nesterov; +Cc: Chen, Kenneth W, linux-kernel, Ingo Molnar, Andrew Morton

On Tue, 29 Mar 2005, Oleg Nesterov wrote:

> > Same problems here with occasional hangs w/o changes to schedule_timeout.
>
> Bad. You are runnning 2.6.12-rc1-mm1 ?

Not sure if this is really related to your patches. Its 2.6.11 with your
patches extracted from mm.


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 0/5] timers: description
@ 2005-03-19 18:30 Oleg Nesterov
  0 siblings, 0 replies; 9+ messages in thread
From: Oleg Nesterov @ 2005-03-19 18:30 UTC (permalink / raw)
  To: linux-kernel; +Cc: Ingo Molnar, Christoph Lameter, Andrew Morton

Hello.

These patches are updated version of 'del_timer_sync: proof of concept'
2 patches.

1/5:
	unchanded.

2/5:
	del_timer_sync() simplified. It is not neccessary to unlock and
	retry if __TIMER_PENDING has changed, it is only neccessary if
	timer's base == (timer->_base & ~1) has changed. Also, comments
	are updated.

3/5:
	The reworked del_timer_sync() can't work unless timers are
	serialized wrt to itself. They are not.
	I missed the fact that __mod_timer() can change timer's base
	while the timer is running.

4/5:
	remove memory barrier in __run_timers() and del_timer().

5/5:
	kill ugly __get_base(), it was temporal.


The del_singleshot_timer_sync function now unneeded, but it looks like
additional test for del_timer_sync(), so it will be removed later.

Btw, add_timer_on() is racy against __mod_timer(), is it worth fixing?

Oleg.

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2005-03-29 14:47 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-03-21 14:19 [PATCH 6/5] timers: enable irqs in __mod_timer() Oleg Nesterov
2005-03-22  5:43 ` Andrew Morton
2005-03-24 21:56 ` [PATCH 0/5] timers: description Chen, Kenneth W
2005-03-26 19:52 ` Chen, Kenneth W
2005-03-27 10:08   ` Oleg Nesterov
2005-03-28 19:12   ` Christoph Lameter
2005-03-29 11:27     ` Oleg Nesterov
2005-03-29 14:47       ` Christoph Lameter
  -- strict thread matches above, loose matches on Subject: below --
2005-03-19 18:30 Oleg Nesterov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).