D0430@PUCC.BITNET (Paul Lansky) (10/01/86)
In a driver we have for some D-A converters (under Ultrix 1.1) I put a DELAY(bignumber) (about 1/2 second) in at clean-up time to give the DACs a chance to reset themselves properly. This has the effect, however, of suspending all system activity for that amount of time. From the looks of the code (in machdep.c) I don't see why this would happen since there seems to be no relevant spl(). I ain't no wizard so I may be missing something fundamental. Can someone suggest an alternate approach? Thanks Paul Lansky Music Department Princeton University {seismo,ucbvax} .. princeton!winnie!paul or d0430@pucc (on bitnet)
mike@BRL.ARPA (Mike Muuss) (10/07/86)
DELAY(num) causes the kernel to loop hard, at the current ipl. If entered at spl0(), interupts will still be processed, but the kernel will just keep looping... Recall that the kernel will not context switch unless swtch() is called -- and you DON'T want to do that in this case. More likely, what you want to do is to have your driver_close() routine shut down the DACs, and then execute a bit of code something like this: driver_close() { /* Shut down the DACs here */ timeout( &driver_close, 2*HZ ); s = spl6(); /* protect against races */ dac_wait = 1; while( dac_wait ) sleep( &dac_wait, PZERO ); splx(s); /* Handle final cleanups */ } close_timeout() { dac_wait = 0; wakeup( &dac_wait ); } (Check the arguments to sleep() and timeout() carefully for the version of UNIX that you are using -- the above code is just to give you the "flavor" of this operation). The timeout() routine arranges for the UNIX clock() routine to call your time-handler after the specified number of clock ticks (2 seconds worth in this example) have passed. In the interim, execution proceeds normally. The sleep() routine causes this process to be de-selected, and a context switch to another process to occur. This will allow other users to continue executing while the timeout runs. IMPORTANT NOTE: The loop around the sleep() is generally very important -- there are no guarantees that the sleep() will not return from some other cause (event) than the one you are expecting, so be certain to wait for the intended condition to happen. In this case, the transition from 1 to 0 of the dac_wait flag. (If you know *exactly* what you are doing, and know *exactly* the semantics of sleep/wakeup on your system, this can be avoided, but my advice to you is: "don't"). Note that when sleep() context switches, it drops the high spl. When the sleeping context is resumed by wakeup(), the high spl is restored for you at the right point in the context switch. Note that the above code is impervious to multiple timeouts (which *shouldn't* ordinarily occur), because any additional timeouts will just clear the dac_wait flag. Note that if you were EXPECTING competing timeout or interupt events during this close operation, it would be best to protect the set..while..sleep loop with spl6 [for timeouts] or spl4,5 [for device interupts], to prevent race conditions on the dac_wait flag. Hope this helps all you folks out there write better drivers. Best, -Mike Muuss (301)-278-6678 AV 298-6678 FTS 939-6678 ArpaNet: <Mike @ BRL.ARPA> UUCP: decvax!brl-bmd!mike Postal: Mike Muuss Leader, Advanced Computer Systems Team Computer Science and Mathematics Branch Systems Engineering and Concepts Analysis Division U.S. Army Ballistic Research Laboratory Attn: SLCBR-SECAD (Muuss) APG, MD 21005-5066
D0430@PUCC.BITNET (Paul Lansky) (10/07/86)
howdy, I'd like to express many thanks for the dozen or so answers I've gotten to my DELAY query. They all suggest using timeout/sleep in some combination or subterfuge. I've implemented this and it seems to work beautifully. The most economical and straightforward suggestion came from Jim Lowe who suggested the following (similar to what happens in the uda.c and tmscp.c drivers in Ultrix) (void)timeout(wakeup, (caddr_t)&tp->t_dev, hz>>1) ; (void)sleep((caddr_t)&tp->t_dev, PZERO); You all are quite a resource! Paul Lansky Music Department Princeton University {seismo, ucbvax} princeton!winnie!paul bitnet == d0430@pucc
chris@umcp-cs.UUCP (Chris Torek) (10/08/86)
I never saw the original article, but I can guess at the problem being solved. Mike's advice is accurrate, but, I think, overcautious: In article <4393@brl-smoke.ARPA> mike@BRL.ARPA (Mike Muuss) writes: >driver_close() >{ > /* Shut down the DACs here */ > timeout( &driver_close, 2*HZ ); (That should be `close_timeout', not `&driver_close', and the routine must be declared above.) > s = spl6(); /* protect against races */ > dac_wait = 1; > while( dac_wait ) > sleep( &dac_wait, PZERO ); > splx(s); > /* Handle final cleanups */ >} > >close_timeout() >{ > dac_wait = 0; > wakeup( &dac_wait ); >} >(Check the arguments to sleep() and timeout() carefully for the version >of UNIX that you are using -- the above code is just to give you the >"flavor" of this operation). I might go so far as to say that every Unix will do them the same way, but no doubt someone out there has made some incompatible changes to its semantics *without* changing the name. . . . >The sleep() routine causes this process to be de-selected, and a context >switch to another process to occur. ... The loop around the sleep() is >generally very important -- there are no guarantees that the sleep() >will not return from some other cause (event) than the one you are >expecting, so be certain to wait for the intended condition to happen. There *are* some guarantees. The sleep() will return only when someone does a wakeup() on the exact same value as the first argument to sleep(), OR when a signal occurs, provided the second argument was greater than PZERO. As Mike cautioned, though, your kernel vendor just might have broken this. >Note that when sleep() context switches, it drops the high spl. >When the sleeping context is resumed by wakeup(), the high spl >is restored for you at the right point in the context switch. (`Drops the high ipl' sounds better. IPL stands for Interrupt Priority Level; spl stands for `set priority level'. At least, that is my best guess as to what it stands for . . . .) Anyway, I would use---in fact, I have already used, in similar cases---the following: /* DAC driver */ struct dac_softc { int sc_flags; /* e.g., exclusive use opens */ ... } dac_softc[NDAC]; ... #define DACPRI (PZERO - 1) /* adjust as appropriate */ extern int hz; int wakeup(); #define dacunit(dev) minor(dev) ... /* * Close a DAC. */ /*ARGSUSED*/ dacclose(dev, flag) dev_t dev; { register struct dacdevice *dac; register struct dac_softc *sc; dac = (struct dacdevice *) dacinfo[dacunit(dac)]->ui_addr; sc = &dac_softc[dacunit(dac)]; sc->sc_flags |= SC_SHUTDOWN; /* mark shutdown in progress */ dac->dac_foo = DAC_SHUTDOWN; /* shut down DAC */ timeout(wakeup, (caddr_t) sc, hz * 2); sleep((caddr_t) sc, DACPRI); /* wait two seconds */ dac->dac_foo = 0; /* then release it */ sc->sc_flags = 0; /* mark closed */ } -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1516) UUCP: seismo!umcp-cs!chris CSNet: chris@umcp-cs ARPA: chris@mimsy.umd.edu