Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 10 Aug 2003 23:15:22 +1000 (EST)
From:      Bruce Evans <bde@zeta.org.au>
To:        Peter Holm <peter@holm.cc>
Cc:        freebsd-current@freebsd.org
Subject:   Re: Deadlock [PATCH]
Message-ID:  <20030810214320.A57453@delplex.bde.org>
In-Reply-To: <20030810070311.GA97363@peter.osted.lan>
References:  <20030810070311.GA97363@peter.osted.lan>

next in thread | previous in thread | raw e-mail | index | archive | help
On Sun, 10 Aug 2003, Peter Holm wrote:

> I have tracked down, what I belive to be the cause of several
> deadlock situations I have encountered, like
> http://people.freebsd.org/~pho/stress/cons40.html.
>
> The problem seems to be the 4bsd scheduler, that does not preempt correctly.
>
> I've included a patch that fixes the problem for me.

% --- sched_4bsd.c~	Sun Jun 15 16:57:17 2003
% +++ sched_4bsd.c	Sun Aug 10 08:41:06 2003
% @@ -448,7 +448,8 @@
%
%  	ke->ke_sched->ske_cpticks++;
%  	kg->kg_estcpu = ESTCPULIM(kg->kg_estcpu + 1);
% -	if ((kg->kg_estcpu % INVERSE_ESTCPU_WEIGHT) == 0) {
% +	if (((kg->kg_estcpu + 1) % INVERSE_ESTCPU_WEIGHT) == 0) {
% +		curthread->td_flags |= TDF_NEEDRESCHED;
%  		resetpriority(kg);
%  		if (td->td_priority >= PUSER)
%  			td->td_priority = kg->kg_user_pri;

I wonder how this makes much difference.  Setting of the rescheduling
flag is supposed to be handled by resetpriority(), so the above at
most works around bugs in resetpriority().  There are plenty of bugs
in resetpriority(), but they are minor and mostly overcome by larger
bugs AFAIK.

resetpriority() was first broken in rev.1.48 of kern_synch.c 1999/03/04)
and the bugs have moved around a bit since then.  The main point of
kern_synch.c rev.1.48 was to prevent excessive context switches for
non-PRI_TIMESHARE scheduling classes so that POSIX roundrobin and fifo
scheduling classes could be implemented using these clasess.  This has
been broken in -current by context switching even more excessively to
switch to interrupt tasks.  The main bugs in kern_synch.c were that
it broke the PRI_TIMESHARE case by not passing the new priority to
maybe_resched() (still broken), and it didn't get the logic for the
non-PRI_TIMESHARE case right (now fixed).  The excessive context
switching in -current makes these bugs mostly moot -- we normally
reschedule on the next (non-clock) interrupt, so rescheduling is just
delayed a bit.  The delay is normally quite small too, at least in the
case that the rescheduling is triggered by sched_clock().  roundrobin()
is supposed to cause rescheduling every quantum (default 1/10 seconds).
sched_clock() calls resetpriority() every 1/16 seconds on i386's and
rescheduling 16 times per second isn't much different from rescheduling
10 time per second.  But sched_clock() should never cause rescheduling
since it should only decrease (numerically increase) the priority of
the current thread.

I think your deadlock must be related to roundrobin() never running,
which might be caused by the bugfeature that userret() doesn't consider
rescheduling.  roundrobin() is just a timeout routine, and its current
implementation depends on excessive context switching.  All timeout
routines are called from a low priority task, and switching to this
task causes roundrobin scheduling of user processes as a side effect.
roundrobin() just ensures that there is a timeout every quantum in
case the system doesn't otherwise generate timeouts frequently enough,
The bugfeature lets processes return to user mode even though their
user priority is much lower (numerically higher) than that of other
user tasks or perhaps even some kernel tasks.  They effectively run
with a high kernel priority until they give up control or are kicked
by an interrupt.  Normally there are enough interrupts to prevent
problems.  But -current has the curious behaviour of forcibly rescheduling
on almost all interrupts _except_ the clock (scheduling) one.  Clock
interrupts are fast, so they don't cause context switches.  Hardclock
interrupts schedule timeouts every quantum, but your bug apparently
blocks timeouts.  Softclock interrupts are not supposed to cause
rescheduling (that is roundrobin()'s job).  Your change does forcible
rescheduling for statclock interrupts.

In my version of FreeBSD, clock interrupt handlers are non-fast so they
do forcible context switching as a side effect and I probably wouldn't
notice this problem.  My clock interrupt tasks have a much higher
priority than the timeout task so deadlocks are less likely and would
be more obvious.

I'll try running your stress test.

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20030810214320.A57453>