[comp.bugs.sys5] Surprising fact about STREAMS service modules

larry@hcr.UUCP (Larry Philps) (12/23/88)

Well, I just learned something about the STREAMS mechanism today.  Since I
found it quite surprising, I thought I should mention it to the world before
some other poor sucker gets surprised also.

As anyone who has perused the STREAMS code at all knows, all the
non-interruptable parts of it run at "splstr", which on most systems is
spl5.  This is lower than the buffer cache, but high enough to block all
devices, most importantly the terminals and the network.  After looking
carefully at the STREAMS programming manuals I can't find any reference to
the spl at which the driver service modules are called.  Silly me, I just
assumed that it would be at splstr.  However, check out the following code
fragment from io/stream.c.

queuerun()
{
	....

	s = splstr();
	...
	if (q->q_qinfo->qi_srvp) {
		spl1();
		(*q->q_qinfo->qi_srvp)(q);
		splstr();
	}
	...
	splx(s);
}

Note the spl1()!  Anybody else out there surprised?  We were seeing some
really bizarre behaviour out of RFS when under heavy load the server would
reverse the order of a DUCOPYOUT and a DUREAD packet.  RFS produces no
diagnostics whatsoever when the out of sequence DUCOPYOUT arrives and the
only visible effect was that part of the read returned all zeros.

We tracked this down using a network analyzer to capture a packet trace and
then analyzed the RFS headers.  After much work I decided that "This can
never happen, but it did - well ... at least it can never happen if the code
is never reentered".  After that it took every little time to discover the
preceeding fragment.

So, the moral of this story is, if you have a STREAMS driver that talks to
any hardware, be sure to protect its code with an splstr() or else write that
code so that it can be reentered.

---
Larry Philps                             HCR Corporation
130 Bloor St. West, 10th floor           Toronto, Ontario.  M5S 1N5
(416) 922-1937                           {utzoo,utcsri,ihnp4}!hcr!larry

paul@taniwha.UUCP (Paul Campbell) (12/25/88)

In article <4385@hcr.UUCP> larry@hcr.UUCP (Larry Philps) writes:
>Well, I just learned something about the STREAMS mechanism today.  Since I
>found it quite surprising, I thought I should mention it to the world before
>some other poor sucker gets surprised also.
>...
>the spl at which the driver service modules are called.  Silly me, I just
>assumed that it would be at splstr.  However, check out the following code
>fragment from io/stream.c.

I've always known about this but then I taught myself from the sources
(no manuals aroud then ...). I've always assumed that there are only
two things one can assume:

	- that no other service routine can be called while one is running
	  (this should not apply in MP systems .... I always thought that
	  multiple service routines could/would/should be running but none
	  in the same stream [or its stream head code]).
	
	- that timeouts will not go off while service routines are being run
	  (again this will probably not apply to MP systems).

What you CAN'T assume is that put routines will not be called while a service
routine is being run, one might be called from an interrupt service routine.

Something else that's always assumed is that ISRs are all called at IPLs
<= splstr (most systems I've worked with had splstr == 6 which is >= spltty).
I have worked on one system where the device we were working with interrupted 
at > splstr (it had to - it had horrible real-time constraints). The games
we had to play to make putq() safe were pretty bad!

	Paul

PS:	has anyone done a 'real' MP streams implementation? What sort of
	scheduling policies did you use? How did code have to change?
	Which of the above assumptions still apply? (This would probably
	make a good Usenix paper).

-- 
Paul Campbell			..!{unisoft|mtxinu}!taniwha!paul (415)420-8179
Taniwha Systems Design, Oakland CA

 	"Read my lips .... no GNU taxes"

smb@ulysses.homer.nj.att.com (Steven M. Bellovin) (12/26/88)

A persistent bug source, in many versions on the UNIX system, is that
splN() calls, other than splx(), unconditionally set the interrupt
level to the indicated value, rather than *raising* it to that value.
It is almost never correct to lower an interrupt level, except via
splx() or spl0().  Sun has gotten some of those right; the symbolic
spl() calls (splimp(), etc.) check the current level before raising it.
4.3bsd is wrong; I haven't been able to check 4.3 Tahoe yet.