Skip site navigation (1)Skip section navigation (2)
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>