Date: Tue, 09 Sep 1997 10:39:42 -0600 From: "Justin T. Gibbs" <gibbs@plutotech.com> To: Doug Ledford <dledford@dialnet.net> Cc: "Justin T. Gibbs" <gibbs@plutotech.com>, "Daniel M. Eischen" <deischen@iworks.InterWorks.org>, aic7xxx@freebsd.org Subject: Re: Interesting anomoly with a 2940UW Message-ID: <199709091643.KAA14727@pluto.plutotech.com> In-Reply-To: Your message of "Tue, 09 Sep 1997 09:17:54 CDT." <199709091417.JAA11378@dledford.dialnet.net>
next in thread | previous in thread | raw e-mail | index | archive | help
>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 ===========================================
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199709091643.KAA14727>