Interesting anomoly with a 2940UW
Justin T. Gibbs
gibbs at plutotech.com
Tue Sep 9 09:43:39 PDT 1997
>Actually, the way I have the locking code in the kernel, there is a small
>chance of an overflow, although I haven't worked out exactly yet how it
>would occur. Instead of specifically setting the CMDOUTCNT to 0, I do the
>following:
>
>while ( qoutcnt > 0 )
>{
> for(i=0; i<qoutcnt; i++)
> {
> do our stuff here
> }
> if (p->flags & PAGE_ENABLED)
> {
> pause_sequencer(p); // In case we ever have paging on 2{7,8}4x class
>cards
> outb(qoutcnt - i, p->base + CMDOUTCNT); //In most cases, this should be 0
> unpause_sequencer(p, FALSE); // Do this before checking qoutcnt again
> } // so we might catch a blocked command
> outb(CLRCMDINT, p->base + CLRINT);
> interrupt_cleared++;
> qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
>}
This won't work. qoutcnt may have stale information which then gets
transfered to CMDOUTCNT. This is because you don't know where the
sequencer is at the time you pause it. Using the sequencer spin lock
code I wrote as an example (did you incorperate this exact bit of code???):
complete:
/* Post the SCB and issue an interrupt */
.if ( SCB_PAGING )
/*
* Spin loop until there is space
* in the QOUTFIFO.
*/
mov A, FIFODEPTH;
cmp CMDOUTCNT, A je .;
inc CMDOUTCNT;
.endif
mov QOUTFIFO,SCB_TAG; <----
mvi INTSTAT,CMDCMPLT;
So, say I pause the sequencer just before the instruction with the
arrow executes. You will now set CMDOUTCNT to 0 when it should
really be 1. If your interrupt processing gets deferred or interrupted,
you are open to an overflow.
Above and beyond this, the code you wrote is inefficient. If you have
good interrupt latency, you will pause the sequencer on every command
completion. If you use the algorithm I mentioned initially, pause and
clear the CMDOUTCNT value every fifodepth completions, you remove this
race and also pause the sequencer as little as possible. The race
is removed as you know that the sequencer is either spinning waiting
for the count to drop or executing code before the test. In general,
your interrupt latency should be low, and you have the time between two
command completions to clear the count and avoid the spin lock. In my
tests here under FreeBSD, I've never hit the spin lock, but I've also
done quite a bit with the CAM SCSI code to ensure low interrupt latency.
Here's the FreeBSD code:
while ((qoutcnt = (ahc_inb(ahc, QOUTCNT) & ahc->qcntmask)) != 0) {
ahc->cmdoutcnt += qoutcnt;
for (; qoutcnt > 0; qoutcnt--) {
scb_index = ahc_inb(ahc, QOUTFIFO);
scb = ahc->scb_data->scbarray[scb_index];
if (!scb || !(scb->flags & SCB_ACTIVE)) {
printf("%s: WARNING "
"no command for scb %d "
"(cmdcmplt)\nQOUTCNT == %d\n",
ahc_name(ahc), scb_index,
qoutcnt);
continue;
}
STAILQ_INSERT_TAIL(&ahc->cmplete_scbs, scb,
links);
}
if ((ahc->flags & AHC_PAGESCBS) != 0) {
if (ahc->cmdoutcnt >= ahc->qfullcount) {
/*
* Since paging only occurs on
* aic78X0 chips, we can use
* Auto Access Pause to clear
* the command count.
*/
ahc_outb(ahc, CMDOUTCNT, 0);
ahc->cmdoutcnt = 0;
}
}
while((scb = ahc->cmplete_scbs.stqh_first) != NULL) {
/* Process each command */
....
}
}
--
Justin T. Gibbs
===========================================
FreeBSD: Turning PCs into workstations
===========================================
More information about the aic7xxx
mailing list