Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 24 Oct 2000 13:59:53 +0000 (GMT)
From:      Terry Lambert <tlambert@primenet.com>
To:        eischen@vigrid.com (Daniel Eischen)
Cc:        tlambert@primenet.com (Terry Lambert), jasone@canonware.com (Jason Evans), seth@pengar.com (Seth Leigh), smp@FreeBSD.ORG
Subject:   Re: SA project (was Re: SMP project status)
Message-ID:  <200010241359.GAA27114@usr05.primenet.com>
In-Reply-To: <Pine.SUN.3.91.1001024080432.1904A-100000@pcnet1.pcnet.com> from "Daniel Eischen" at Oct 24, 2000 08:24:19 AM

next in thread | previous in thread | raw e-mail | index | archive | help
> > Perhaps the biggest pain is the default signal behaviour, which
> > no longer results in system call restart.  This means that the
> > signal code must self-mask anything that it wants to pretend is
> > a system call, for every wrapping library routine.  POSIX really
> > screwed us over on that account, kind of like what they did to
> > locks when you close one of several file handles to the same file.
> 
> Hmm.  If the method used by the kernel to notify the threads library
> that a thread has blocked in the kernel can also tell the threads
> library that it is/isn't blocked in a restartable system call,
> then the threads library can DTRT WRT interrupting the thread.
> Basically, all it would need is the system call number.  It could
> probably get it from the context of the interrupted thread (which
> is passed out to the threads library under SA).

This may be one approach; it assumes a scheduler context in user
space which is updated from kernel space, and which contains the
call number.  It's doable, but potentially gross.


> There are only a few system calls that require restarting (assuming
> SA_RESTART is set), so I'm not quite sure what you mean by self-masking
> library routines.  Perhaps library routines that use read(), for
> instance, in their implementation?

OK, if I have a call that can be interrupted, e.g.:

	int
	foo( struct fooargs *args)
	{
	}

And I want to wrap it in user space so that I can do a call
conversion reschedule based on it blocking, and me getting an
activation, then I have:

	int
	foo( struct fooargs *args)	/* wrapper*/
	{
		...
		rv = realfoo( args);
		...			/* Danger, Will Robinson!*/
	}

..so I need the code that occurs before and after the call to the
realfoo() call to appear as atomic as the realfoo() call, in the
event that I either need to restart it, or, worst case, I need the
library wrapper foo() to return EINTR as if it were realfoo() in
an unwrapped library, not running threaded.

This means that I need to mask off any additional signal processing
between the time that realfoo() returns EINTR, and processing to
the end of the function is completed to the point that foo() can
return -- in other words, foo() must look like a system call to
realfoo() to the caller, even if we end up with a blocking call in
the kernel itself, coupled with an activation.  It wouldn't do to,
for example, disable SIGINT or SIGHUP, merely because I had some
background processing going on in another thread.

Under activations, this applies to everything the uses non-blocking
fd's in the current situation, coupled with a scheduler entry in
user space to convert a call that would have blocked into a call
that returns EWOULDBLK.  Under activations, this becomes once again
a blocking call, with an activation occurring at the point that
the current kernel would normally put the process to sleep (the one
exception to this would be an involuntary context switch as a result
of the quantum being used up).

Unfortunately, it gets more complicated, as Julian and Archie
could also tell you.  Since an involuntary context switch can
occurr during the processing of an activation, in other words, in
the user space scheduler, instead of the kernel, activations that
are pending delivery to user space have to be stalled until the
user space scheduler hits a "safe" place, where it can be concurrent
again, so it can safely process the pending activations.  You need
one of these per CPU on which you might be scheduled to run, or more,
if you are capable of being in the scheduler more than once on a
single CPU (this is necessary for some priority handling).

As I said before, this is really more complicated than it would
have to be, if all non-blocking scheduling events occurred in
user space.  You could do this, if you had either a non-blocking
system call interface, or a generic AST mechanism, like VMS and NT
have, since either approach will result in either all the work being
done in user space, or all the work being done in the kernel.  The
DEC MTS services use AST-based call conversion to schedule several
concurrent operations simultaneously; I used that to add the missing
interlocks into Mentat Streams on VMS back in 92 or so, since the
MTS as it shipped from DEC didn't have timers or interlocks for
doing inter-thread semaphoring.

OBTW: the signal processing following an interrupted system call
is not the only situation where blocking additional interruptions
until processing has completed will be an issue.  On any architecture
with register windows, such as SPARC, you will need an explicit
kernel assist to flush the register windows, so it will have to be
stubbed in, even it it's a NOOP on x86 architectures (at least until
the new Intel stuff becomes common, I think).

Personally, I'd probably approach the problem as if I were going
to allow multiple concurrent outstanding system calls from a
single process on a uniprocessor system (which means that you can
have more than one entry in the scheduler for a given process),
and then sort out the migration and other issues, as well as the
SMP issues, later.


					Terry Lambert
					terry@lambert.org
---
Any opinions in this posting are my own and not those of my present
or previous employers.


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-smp" in the body of the message




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