Date: Sat, 01 Nov 1997 22:15:40 -0800 (PST) From: Simon Shapiro <Shimon@i-Connect.Net> To: "Jamil J. Weatherbee" <jamil@trojanhorse.ml.org> Cc: Mike Smith <mike@smith.net.au>, hackers@FreeBSD.ORG, dufault@hda.com Subject: Re: Strategy Routines Message-ID: <XFMail.971101221540.Shimon@i-Connect.Net> In-Reply-To: <Pine.BSF.3.96.971101213806.671D-100000@trojanhorse.ml.org>
next in thread | previous in thread | raw e-mail | index | archive | help
Hi Jamil J. Weatherbee; On 02-Nov-97 you wrote: > > Setup the copy, shcedule software insterrupt. > > > > The software interrupt routine gets control, copies a chunk, does the > > bookkeeping. If not done, schedules another interrupt and returns. > > > > I think it has lower overhead than tslepp and the added advantage that > > it > > will not panic if called outside a user context. > > > > Wait, are you talking about doing an acquire_timer0() and then doing > work > in the routine you assign or what? That's one thing I don't understand, > it seems with that particualr facility only one routine can be active on > the timer 0 interrupt at a time, so that is why I have avoided it for > these kinds of situations. Can someone let me in on the big secret. No big secret, me thinks. What I did in the DPT driver (actually now a standard feature of CAM, is use software interrupts. Maybe I'd better outline it (it is best followed with sys/dev/dpt/dpt_scsi.c in hand); early in the driver, in the global section, there is: static void dpt_swi_register(void *); ... extern void (*ihandlers[32]) __P((void)); ... Following that is a silly little function: static void dpt_swi_register(void *unused) { ihandlers[SWI_CAMBIO] = dpt_sintr; } SYSINIT(dpt_camswi, SI_SUB_DRIVERS, SI_ORDER_FIRST, dpt_swi_register, NULL) This code informs the kernel that I wish to register a software interrupt (of the SCSI veriaty, but it is trivial to clone it for other purposes). sometimes during boot (Justin, help... :-) this function will be called and setup a vector for the function dpt_sintr (see below). This function will run at cambio priority. The reason this looks like normal interrupt registration is that it is :-) Later on in the code we find: static INLINE void dpt_sched_queue(dpt_softc_t *dpt) { if (dpt->state & DPT_HA_QUIET) { printf("dpt%d: Under Quiet Busses Condition. " "No Commands are submitted\n", dpt->unit); return; } setsoftcambio(); } This little gem will schedule an interrupt. I am not certain exactly when it will be called, but if I remember correctly, when all other interrupts are cleared, or some such (again, Justin, or someone help...). void As you remember from above, the function dpt_sintr is the one that will be called. Here is dpt_sintr: dpt_sintr(void) { dpt_softc_t *dpt; int ospl; /* Find which DPT needs help */ for (dpt = TAILQ_FIRST(&dpt_softc_list); dpt != NULL; dpt = TAILQ_NEXT(dpt, links)) { /* * Drain the completed queue, to make room for new, " waiting requests. * We change to splcam to block interrupts from mucking * with the completed queue */ ospl = splcam(); if (dpt->queue_status & DPT_SINTR_ACTIVE) { splx(ospl); continue; } dpt->queue_status |= DPT_SINTR_ACTIVE; if (!TAILQ_EMPTY(&dpt->completed_ccbs)) { splx(ospl); dpt_complete(dpt); ospl = splcam(); } /* Submit as many waiting requests as the DPT can take */ if (!TAILQ_EMPTY(&dpt->waiting_ccbs)) { dpt_run_queue(dpt, 0); } dpt->queue_status &= ~DPT_SINTR_ACTIVE; splx(ospl); } } Don't pay any attention to what is inside it. Just remember it will run sometimein the future, in priority splsoftcam(). Now we are ready to work. At any point in the driver, where I want to run dpt_sintr, I simply call dpt_sched_queue. It is very important to remember that dpt_sched_queue(0 does NOT block, nor actually call dpt_sintr! dpt_sched_queue returns immediately, and dpt_sintr will eventiually run, sometime in the future. I am selecting these vague words on purpose, as this is exactly an interrupt mechanism. An event will be generated which will cause a stimuli. A routine will react to the stimuli but your code is de-coupled from it. How do you use it? Say you have a 2MB array you want to dump out a port in 128 byte chunks, leaving the CPU a chance to do something else. Assume that dpt_sintr dumps the data. Write a loop: static int dump_it(u_int8_t data, int size) { int ndx; dpt_softc_t dpt; for (ndx = 0; ndx < size; ndx += 128 ) { dpt_sched_queue(dpt); } } This is all ugly, full of extras (I am lazy with my cut-n-pest :-) but the idea should be clear. This is not original, nor new, nor necessarily the best for your needs, but a somewhat interesting way of doing it. Simon
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?XFMail.971101221540.Shimon>