void fdfinish(struct fd_softc *fd, struct buf *bp) { struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev)); /* * Move this drive to the end of the queue to give others a `fair' * chance. We only force a switch if N operations are completed while * another drive is waiting to be serviced, since there is a long motor * startup delay whenever we switch. */ (void)BUFQ_GET(fd->sc_q); if (TAILQ_NEXT(fd, sc_drivechain) && ++fd->sc_ops >= 8) { fd->sc_ops = 0; TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain); if (BUFQ_PEEK(fd->sc_q) != NULL) TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain); else fd->sc_active = 0; } bp->b_resid = fd->sc_bcount; fd->sc_skip = 0; #if NRND > 0 rnd_add_uint32(&fd->rnd_source, bp->b_blkno); #endif biodone(bp); /* turn off motor 5s from now */ callout_reset(&fd->sc_motoroff_ch, 5 * hz, fd_motor_off, fd); fdc->sc_state = DEVIDLE; }
/* * ssstart looks to see if there is a buf waiting for the device * and that the device is not already busy. If both are true, * It dequeues the buf and creates a scsi command to perform the * transfer required. The transfer request will call scsipi_done * on completion, which will in turn call this routine again * so that the next queued transfer is performed. * The bufs are queued by the strategy routine (ssstrategy) * * This routine is also called after other non-queued requests * have been made of the scsi driver, to ensure that the queue * continues to be drained. * ssstart() is called at splbio */ static void ssstart(struct scsipi_periph *periph) { struct ss_softc *ss = (void *)periph->periph_dev; struct buf *bp; SC_DEBUG(periph, SCSIPI_DB2, ("ssstart ")); /* * See if there is a buf to do and we are not already * doing one */ while (periph->periph_active < periph->periph_openings) { /* if a special awaits, let it proceed first */ if (periph->periph_flags & PERIPH_WAITING) { periph->periph_flags &= ~PERIPH_WAITING; wakeup((void *)periph); return; } /* * See if there is a buf with work for us to do.. */ if ((bp = BUFQ_PEEK(ss->buf_queue)) == NULL) return; if (ss->special && ss->special->read) { (ss->special->read)(ss, bp); } else { /* generic scsi2 scanner read */ /* XXX add code for SCSI2 scanner read */ } } }
void fdcretry(struct fdc_softc *fdc) { char bits[64]; struct fd_softc *fd; struct buf *bp; fd = TAILQ_FIRST(&fdc->sc_drives); bp = BUFQ_PEEK(fd->sc_q); if (fd->sc_opts & FDOPT_NORETRY) goto fail; switch (fdc->sc_errors) { case 0: /* try again */ fdc->sc_state = DOSEEK; break; case 1: case 2: case 3: /* didn't work; try recalibrating */ fdc->sc_state = DORECAL; break; case 4: /* still no go; reset the bastard */ fdc->sc_state = DORESET; break; default: fail: if ((fd->sc_opts & FDOPT_SILENT) == 0) { diskerr(bp, "fd", "hard error", LOG_PRINTF, fd->sc_skip / FDC_BSIZE, NULL); printf(" (st0 %s", bitmask_snprintf(fdc->sc_status[0], NE7_ST0BITS, bits, sizeof(bits))); printf(" st1 %s", bitmask_snprintf(fdc->sc_status[1], NE7_ST1BITS, bits, sizeof(bits))); printf(" st2 %s", bitmask_snprintf(fdc->sc_status[2], NE7_ST2BITS, bits, sizeof(bits))); printf(" cyl %d head %d sec %d)\n", fdc->sc_status[3], fdc->sc_status[4], fdc->sc_status[5]); } bp->b_error = EIO; fdfinish(fd, bp); } fdc->sc_errors++; }
/* * Destroy a device buffer queue. */ void bufq_free(struct bufq_state *bufq) { KASSERT(bufq->bq_private != NULL); KASSERT(BUFQ_PEEK(bufq) == NULL); free(bufq->bq_private, M_DEVBUF); free(bufq, M_DEVBUF); }
void hdc_qstart(void *arg) { struct hdcsoftc * const sc = arg; inq = 0; hdcstart(sc, 0); if (BUFQ_PEEK(sc->sc_q)) { vsbus_dma_start(&sc->sc_vd); /* More to go */ inq = 1; } }
static void mtstart(void *arg) { struct mt_softc *sc = arg; struct buf *bp; short cmdcount = 1; u_char cmdbuf[2]; dlog(LOG_DEBUG, "%s start", device_xname(sc->sc_dev)); sc->sc_flags &= ~MTF_WRT; bp = BUFQ_PEEK(sc->sc_tab); if ((sc->sc_flags & MTF_ALIVE) == 0 && ((bp->b_flags & B_CMD) == 0 || bp->b_cmd != MTRESET)) goto fatalerror; if (sc->sc_flags & MTF_REW) { if (!hpibpptest(sc->sc_hpibno, sc->sc_slave)) goto stillrew; switch (mtreaddsj(sc, MTE_DSJ_FORCE|MTE_COMPLETE|MTE_IDLE)) { case 0: case 1: stillrew: if ((sc->sc_stat1 & SR1_BOT) || !(sc->sc_stat1 & SR1_ONLINE)) { sc->sc_flags &= ~MTF_REW; break; } case -2: /* * -2 means "timeout" reading DSJ, which is probably * temporary. This is considered OK when doing a NOP, * but not otherwise. */ if (sc->sc_flags & (MTF_DSJTIMEO | MTF_STATTIMEO)) { callout_reset(&sc->sc_start_ch, hz >> 5, spl_mtstart, sc); return; } case 2: if (bp->b_cmd != MTNOP || !(bp->b_flags & B_CMD)) { bp->b_error = EBUSY; goto done; } goto done; default: goto fatalerror; } }
void fdctimeout(void *arg) { struct fdc_softc *fdc = arg; struct fd_softc *fd = TAILQ_FIRST(&fdc->sc_drives); mutex_enter(&fdc->sc_mtx); #ifdef DEBUG log(LOG_ERR, "fdctimeout: state %d\n", fdc->sc_state); #endif fdcstatus(fd->sc_dev, 0, "timeout"); if (BUFQ_PEEK(fd->sc_q) != NULL) fdc->sc_state++; else fdc->sc_state = DEVIDLE; (void)fdcintr1(fdc); mutex_exit(&fdc->sc_mtx); }
static int fdcintr1(struct fdc_softc *fdc) { #define st0 fdc->sc_status[0] #define cyl fdc->sc_status[1] struct fd_softc *fd; struct buf *bp; bus_space_tag_t iot = fdc->sc_iot; bus_space_handle_t ioh = fdc->sc_ioh; int read, head, sec, i, nblks; struct fd_type *type; struct ne7_fd_formb *finfo = NULL; KASSERT(mutex_owned(&fdc->sc_mtx)); if (fdc->sc_state == PROBING) { #ifdef DEBUG printf("fdcintr: got probe interrupt\n"); #endif fdc->sc_probe++; goto out; } loop: /* Is there a drive for the controller to do a transfer with? */ fd = TAILQ_FIRST(&fdc->sc_drives); if (fd == NULL) { fdc->sc_state = DEVIDLE; goto out; } /* Is there a transfer to this drive? If not, deactivate drive. */ bp = BUFQ_PEEK(fd->sc_q); if (bp == NULL) { fd->sc_ops = 0; TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain); fd->sc_active = 0; goto loop; } if (bp->b_flags & B_FORMAT) finfo = (struct ne7_fd_formb *)bp->b_data; switch (fdc->sc_state) { case DEVIDLE: fdc->sc_errors = 0; fd->sc_skip = 0; fd->sc_bcount = bp->b_bcount; fd->sc_blkno = bp->b_blkno / (FDC_BSIZE / DEV_BSIZE); callout_stop(&fd->sc_motoroff_ch); if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) { fdc->sc_state = MOTORWAIT; return 1; } if ((fd->sc_flags & FD_MOTOR) == 0) { /* Turn on the motor, being careful about pairing. */ struct fd_softc *ofd = fdc->sc_fd[fd->sc_drive ^ 1]; if (ofd && ofd->sc_flags & FD_MOTOR) { callout_stop(&ofd->sc_motoroff_ch); ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT); } fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT; fd_set_motor(fdc, 0); fdc->sc_state = MOTORWAIT; /* Allow .25s for motor to stabilize. */ callout_reset(&fd->sc_motoron_ch, hz / 4, fd_motor_on, fd); return 1; } /* Make sure the right drive is selected. */ fd_set_motor(fdc, 0); /* fall through */ case DOSEEK: doseek: if (fd->sc_cylin == bp->b_cylinder) goto doio; out_fdc(iot, ioh, NE7CMD_SPECIFY);/* specify command */ out_fdc(iot, ioh, fd->sc_type->steprate); out_fdc(iot, ioh, 6); /* XXX head load time == 6ms */ out_fdc(iot, ioh, NE7CMD_SEEK); /* seek function */ out_fdc(iot, ioh, fd->sc_drive); /* drive number */ out_fdc(iot, ioh, bp->b_cylinder * fd->sc_type->step); fd->sc_cylin = -1; fdc->sc_state = SEEKWAIT; iostat_seek(fd->sc_dk.dk_stats); disk_busy(&fd->sc_dk); callout_reset(&fdc->sc_timo_ch, 4 * hz, fdctimeout, fdc); return 1; case DOIO: doio: type = fd->sc_type; if (finfo) fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; sec = fd->sc_blkno % type->seccyl; nblks = type->seccyl - sec; nblks = min(nblks, fd->sc_bcount / FDC_BSIZE); nblks = min(nblks, fdc->sc_maxiosize / FDC_BSIZE); fd->sc_nblks = nblks; fd->sc_nbytes = finfo ? bp->b_bcount : nblks * FDC_BSIZE; head = sec / type->sectrac; sec -= head * type->sectrac; #ifdef DIAGNOSTIC { int block; block = (fd->sc_cylin * type->heads + head) * type->sectrac + sec; if (block != fd->sc_blkno) { printf("fdcintr: block %d != blkno " "%" PRId64 "\n", block, fd->sc_blkno); #ifdef DDB Debugger(); #endif } } #endif read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE; isa_dmastart(fdc->sc_ic, fdc->sc_drq, (char *)bp->b_data + fd->sc_skip, fd->sc_nbytes, NULL, read | DMAMODE_DEMAND, BUS_DMA_NOWAIT); bus_space_write_1(iot, fdc->sc_fdctlioh, 0, type->rate); #ifdef FD_DEBUG printf("fdcintr: %s drive %d track %d head %d sec %d nblks %d\n", read ? "read" : "write", fd->sc_drive, fd->sc_cylin, head, sec, nblks); #endif if (finfo) { /* formatting */ if (out_fdc(iot, ioh, NE7CMD_FORMAT) < 0) { fdc->sc_errors = 4; fdcretry(fdc); goto loop; } out_fdc(iot, ioh, (head << 2) | fd->sc_drive); out_fdc(iot, ioh, finfo->fd_formb_secshift); out_fdc(iot, ioh, finfo->fd_formb_nsecs); out_fdc(iot, ioh, finfo->fd_formb_gaplen); out_fdc(iot, ioh, finfo->fd_formb_fillbyte); } else { if (read) out_fdc(iot, ioh, NE7CMD_READ); /* READ */ else out_fdc(iot, ioh, NE7CMD_WRITE); /* WRITE */ out_fdc(iot, ioh, (head << 2) | fd->sc_drive); out_fdc(iot, ioh, fd->sc_cylin); /* track */ out_fdc(iot, ioh, head); out_fdc(iot, ioh, sec + 1); /* sector +1 */ out_fdc(iot, ioh, type->secsize);/* sector size */ out_fdc(iot, ioh, type->sectrac);/* sectors/track */ out_fdc(iot, ioh, type->gap1); /* gap1 size */ out_fdc(iot, ioh, type->datalen);/* data length */ } fdc->sc_state = IOCOMPLETE; disk_busy(&fd->sc_dk); /* allow 2 seconds for operation */ callout_reset(&fdc->sc_timo_ch, 2 * hz, fdctimeout, fdc); return 1; /* will return later */ case SEEKWAIT: callout_stop(&fdc->sc_timo_ch); fdc->sc_state = SEEKCOMPLETE; /* allow 1/50 second for heads to settle */ callout_reset(&fdc->sc_intr_ch, hz / 50, fdcintrcb, fdc); return 1; case SEEKCOMPLETE: /* no data on seek */ disk_unbusy(&fd->sc_dk, 0, 0); /* Make sure seek really happened. */ out_fdc(iot, ioh, NE7CMD_SENSEI); if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != bp->b_cylinder * fd->sc_type->step) { #ifdef FD_DEBUG fdcstatus(fd->sc_dev, 2, "seek failed"); #endif fdcretry(fdc); goto loop; } fd->sc_cylin = bp->b_cylinder; goto doio; case IOTIMEDOUT: isa_dmaabort(fdc->sc_ic, fdc->sc_drq); case SEEKTIMEDOUT: case RECALTIMEDOUT: case RESETTIMEDOUT: fdcretry(fdc); goto loop; case IOCOMPLETE: /* IO DONE, post-analyze */ callout_stop(&fdc->sc_timo_ch); disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid), (bp->b_flags & B_READ)); if (fdcresult(fdc) != 7 || (st0 & 0xf8) != 0) { isa_dmaabort(fdc->sc_ic, fdc->sc_drq); #ifdef FD_DEBUG fdcstatus(fd->sc_dev, 7, bp->b_flags & B_READ ? "read failed" : "write failed"); printf("blkno %llu nblks %d\n", (unsigned long long)fd->sc_blkno, fd->sc_nblks); #endif fdcretry(fdc); goto loop; } isa_dmadone(fdc->sc_ic, fdc->sc_drq); if (fdc->sc_errors) { diskerr(bp, "fd", "soft error (corrected)", LOG_PRINTF, fd->sc_skip / FDC_BSIZE, NULL); printf("\n"); fdc->sc_errors = 0; } fd->sc_blkno += fd->sc_nblks; fd->sc_skip += fd->sc_nbytes; fd->sc_bcount -= fd->sc_nbytes; if (!finfo && fd->sc_bcount > 0) { bp->b_cylinder = fd->sc_blkno / fd->sc_type->seccyl; goto doseek; } fdfinish(fd, bp); goto loop; case DORESET: /* try a reset, keep motor on */ fd_set_motor(fdc, 1); delay(100); fd_set_motor(fdc, 0); fdc->sc_state = RESETCOMPLETE; callout_reset(&fdc->sc_timo_ch, hz / 2, fdctimeout, fdc); return 1; /* will return later */ case RESETCOMPLETE: callout_stop(&fdc->sc_timo_ch); /* clear the controller output buffer */ for (i = 0; i < 4; i++) { out_fdc(iot, ioh, NE7CMD_SENSEI); (void) fdcresult(fdc); } /* fall through */ case DORECAL: out_fdc(iot, ioh, NE7CMD_RECAL); /* recalibrate function */ out_fdc(iot, ioh, fd->sc_drive); fdc->sc_state = RECALWAIT; callout_reset(&fdc->sc_timo_ch, 5 * hz, fdctimeout, fdc); return 1; /* will return later */ case RECALWAIT: callout_stop(&fdc->sc_timo_ch); fdc->sc_state = RECALCOMPLETE; /* allow 1/30 second for heads to settle */ callout_reset(&fdc->sc_intr_ch, hz / 30, fdcintrcb, fdc); return 1; /* will return later */ case RECALCOMPLETE: out_fdc(iot, ioh, NE7CMD_SENSEI); if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) { #ifdef FD_DEBUG fdcstatus(fd->sc_dev, 2, "recalibrate failed"); #endif fdcretry(fdc); goto loop; } fd->sc_cylin = 0; goto doseek; case MOTORWAIT: if (fd->sc_flags & FD_MOTOR_WAIT) return 1; /* time's not up yet */ goto doseek; default: fdcstatus(fd->sc_dev, 0, "stray interrupt"); return 1; } #undef st0 #undef cyl out: cv_signal(&fdc->sc_cv); return 1; }
static int xbdstart(struct dk_softc *dksc, struct buf *bp) { struct xbd_softc *xs; struct xbdreq *pxr, *xr; struct partition *pp; daddr_t bn; int ret, runqueue; DPRINTF_FOLLOW(("xbdstart(%p, %p)\n", dksc, bp)); runqueue = 1; ret = -1; xs = getxbd_softc(bp->b_dev); if (xs == NULL || xs->sc_shutdown) { bp->b_flags |= B_ERROR; bp->b_error = EIO; biodone(bp); return 0; } dksc = &xs->sc_dksc; /* XXXrcd: * Translate partition relative blocks to absolute blocks, * this probably belongs (somehow) in dksubr.c, since it * is independant of the underlying code... This will require * that the interface be expanded slightly, though. */ bn = bp->b_blkno; if (DISKPART(bp->b_dev) != RAW_PART) { pp = &xs->sc_dksc.sc_dkdev.dk_label-> d_partitions[DISKPART(bp->b_dev)]; bn += pp->p_offset; } DPRINTF(XBDB_IO, ("xbdstart: addr %p, sector %llu, " "count %ld [%s]\n", bp->b_data, (unsigned long long)bn, bp->b_bcount, bp->b_flags & B_READ ? "read" : "write")); GET_XBDREQ(pxr); if (__predict_false(pxr == NULL)) goto out; disk_busy(&dksc->sc_dkdev); /* XXX: put in dksubr.c */ /* * We have a request slot, return 0 to make dk_start remove * the bp from the work queue. */ ret = 0; pxr->xr_bp = bp; pxr->xr_parent = pxr; pxr->xr_bn = bn; pxr->xr_bqueue = bp->b_bcount; pxr->xr_bdone = bp->b_bcount; pxr->xr_data = (vaddr_t)bp->b_data; pxr->xr_sc = xs; if (pxr->xr_data & (XEN_BSIZE - 1)) map_align(pxr); fill_ring(pxr); while (__predict_false(pxr->xr_bqueue > 0)) { GET_XBDREQ(xr); if (__predict_false(xr == NULL)) break; xr->xr_parent = pxr; fill_ring(xr); } if (__predict_false(pxr->xr_bqueue > 0)) { SIMPLEQ_INSERT_TAIL(&xbdr_suspended, pxr, xr_suspended); DPRINTF(XBDB_IO, ("xbdstart: suspended xbdreq %p " "for bp %p\n", pxr, bp)); } else if (CANGET_XBDREQ() && BUFQ_PEEK(&bufq) != NULL) { /* * We have enough resources to start another bp and * there are additional bps on the queue, dk_start * will call us again and we'll run the queue then. */ runqueue = 0; } out: if (runqueue && last_req_prod != req_prod) signal_requests_to_xen(); return ret; }