📄 aic7xxx_osm.c
字号:
} else { scb->hscb->sgptr = ahc_htole32(SG_LIST_NULL); scb->hscb->dataptr = 0; scb->hscb->datacnt = 0; scb->sg_count = 0; } ahc_sync_sglist(ahc, scb, BUS_DMASYNC_PREWRITE); LIST_INSERT_HEAD(&ahc->pending_scbs, scb, pending_links); dev->openings--; dev->active++; dev->commands_issued++; if ((dev->flags & AHC_DEV_PERIODIC_OTAG) != 0) dev->commands_since_idle_or_otag++; /* * We only allow one untagged transaction * per target in the initiator role unless * we are storing a full busy target *lun* * table in SCB space. */ if ((scb->hscb->control & (TARGET_SCB|TAG_ENB)) == 0 && (ahc->features & AHC_SCB_BTT) == 0) { struct scb_tailq *untagged_q; int target_offset; target_offset = SCB_GET_TARGET_OFFSET(ahc, scb); untagged_q = &(ahc->untagged_queues[target_offset]); TAILQ_INSERT_TAIL(untagged_q, scb, links.tqe); scb->flags |= SCB_UNTAGGEDQ; if (TAILQ_FIRST(untagged_q) != scb) continue; } scb->flags |= SCB_ACTIVE; ahc_queue_scb(ahc, scb); }}/* * SCSI controller interrupt handler. */voidahc_linux_isr(int irq, void *dev_id, struct pt_regs * regs){ struct ahc_softc *ahc; struct ahc_cmd *acmd; u_long flags; struct ahc_linux_device *next_dev; ahc = (struct ahc_softc *) dev_id; ahc_lock(ahc, &flags); ahc_intr(ahc); acmd = TAILQ_FIRST(&ahc->platform_data->completeq); TAILQ_INIT(&ahc->platform_data->completeq); next_dev = ahc_linux_next_device_to_run(ahc); ahc_unlock(ahc, &flags); if (next_dev) {#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) tasklet_schedule(&ahc->platform_data->runq_tasklet);#else ahc_runq_tasklet((unsigned long)ahc);#endif } if (acmd != NULL) ahc_linux_run_complete_queue(ahc, acmd);}voidahc_platform_flushwork(struct ahc_softc *ahc){ struct ahc_cmd *acmd; acmd = TAILQ_FIRST(&ahc->platform_data->completeq); TAILQ_INIT(&ahc->platform_data->completeq); if (acmd != NULL) ahc_linux_run_complete_queue(ahc, acmd);}static struct ahc_linux_target*ahc_linux_alloc_target(struct ahc_softc *ahc, u_int channel, u_int target){ struct ahc_linux_target *targ; u_int target_offset; targ = malloc(sizeof(*targ), M_DEVBUG, M_NOWAIT); if (targ == NULL) return (NULL); memset(targ, 0, sizeof(*targ)); targ->channel = channel; targ->target = target; targ->ahc = ahc; target_offset = target; if (channel != 0) target_offset += 8; ahc->platform_data->targets[target_offset] = targ; return (targ);}static voidahc_linux_free_target(struct ahc_softc *ahc, struct ahc_linux_target *targ){ u_int target_offset; target_offset = targ->target; if (targ->channel != 0) target_offset += 8; ahc->platform_data->targets[target_offset] = NULL; free(targ, M_DEVBUF);}static struct ahc_linux_device*ahc_linux_alloc_device(struct ahc_softc *ahc, struct ahc_linux_target *targ, u_int lun){ struct ahc_linux_device *dev; dev = malloc(sizeof(*dev), M_DEVBUG, M_NOWAIT); if (dev == NULL) return (NULL); memset(dev, 0, sizeof(*dev)); init_timer(&dev->timer); TAILQ_INIT(&dev->busyq); dev->flags = AHC_DEV_UNCONFIGURED; dev->lun = lun; dev->target = targ; /* * We start out life using untagged * transactions of which we allow one. */ dev->openings = 1; /* * Set maxtags to 0. This will be changed if we * later determine that we are dealing with * a tagged queuing capable device. */ dev->maxtags = 0; targ->refcount++; targ->devices[lun] = dev; return (dev);}static voidahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev){ struct ahc_linux_target *targ; del_timer(&dev->timer); targ = dev->target; targ->devices[dev->lun] = NULL; free(dev, M_DEVBUF); targ->refcount--; if (targ->refcount == 0) ahc_linux_free_target(ahc, targ);}/* * Return a string describing the driver. */const char *ahc_linux_info(struct Scsi_Host *host){ static char buffer[512]; char ahc_info[256]; char *bp; struct ahc_softc *ahc; bp = &buffer[0]; ahc = *(struct ahc_softc **)host->hostdata; memset(bp, 0, sizeof(buffer)); strcpy(bp, "Adaptec AIC7XXX EISA/VLB/PCI SCSI HBA DRIVER, Rev "); strcat(bp, AIC7XXX_DRIVER_VERSION); strcat(bp, "\n"); strcat(bp, " <"); strcat(bp, ahc->description); strcat(bp, ">\n"); strcat(bp, " "); ahc_controller_info(ahc, ahc_info); strcat(bp, ahc_info); strcat(bp, "\n"); return (bp);}voidahc_send_async(struct ahc_softc *ahc, char channel, u_int target, u_int lun, ac_code code, void *arg){ switch (code) { case AC_TRANSFER_NEG: { char buf[80]; struct ahc_linux_target *targ; struct info_str info; struct ahc_initiator_tinfo *tinfo; struct ahc_tmode_tstate *tstate; int target_offset; info.buffer = buf; info.length = sizeof(buf); info.offset = 0; info.pos = 0; tinfo = ahc_fetch_transinfo(ahc, channel, channel == 'A' ? ahc->our_id : ahc->our_id_b, target, &tstate); /* * Don't bother reporting results while * negotiations are still pending. */ if (tinfo->curr.period != tinfo->goal.period || tinfo->curr.width != tinfo->goal.width || tinfo->curr.offset != tinfo->goal.offset || tinfo->curr.ppr_options != tinfo->goal.ppr_options) if (bootverbose == 0) break; /* * Don't bother reporting results that * are identical to those last reported. */ target_offset = target; if (channel == 'B') target_offset += 8; targ = ahc->platform_data->targets[target_offset]; if (targ == NULL) break; if (tinfo->curr.period == targ->last_tinfo.period && tinfo->curr.width == targ->last_tinfo.width && tinfo->curr.offset == targ->last_tinfo.offset && tinfo->curr.ppr_options == targ->last_tinfo.ppr_options) if (bootverbose == 0) break; targ->last_tinfo.period = tinfo->curr.period; targ->last_tinfo.width = tinfo->curr.width; targ->last_tinfo.offset = tinfo->curr.offset; targ->last_tinfo.ppr_options = tinfo->curr.ppr_options; printf("(%s:%c:", ahc_name(ahc), channel); if (target == CAM_TARGET_WILDCARD) printf("*): "); else printf("%d): ", target); ahc_format_transinfo(&info, &tinfo->curr); if (info.pos < info.length) *info.buffer = '\0'; else buf[info.length - 1] = '\0'; printf("%s", buf); break; } case AC_SENT_BDR: break; case AC_BUS_RESET:#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) if (ahc->platform_data->host != NULL) { scsi_report_bus_reset(ahc->platform_data->host, channel - 'A'); }#endif break; default: panic("ahc_send_async: Unexpected async event"); }}/* * Calls the higher level scsi done function and frees the scb. */voidahc_done(struct ahc_softc *ahc, struct scb * scb){ Scsi_Cmnd *cmd; struct ahc_linux_device *dev; LIST_REMOVE(scb, pending_links); if ((scb->flags & SCB_UNTAGGEDQ) != 0) { struct scb_tailq *untagged_q; int target_offset; target_offset = SCB_GET_TARGET_OFFSET(ahc, scb); untagged_q = &(ahc->untagged_queues[target_offset]); TAILQ_REMOVE(untagged_q, scb, links.tqe); ahc_run_untagged_queue(ahc, untagged_q); } if ((scb->flags & SCB_ACTIVE) == 0) { printf("SCB %d done'd twice\n", scb->hscb->tag); ahc_dump_card_state(ahc); panic("Stopping for safety"); } cmd = scb->io_ctx; dev = scb->platform_data->dev; dev->active--; dev->openings++; ahc_linux_unmap_scb(ahc, scb); if (scb->flags & SCB_SENSE) { memcpy(cmd->sense_buffer, ahc_get_sense_buf(ahc, scb), MIN(sizeof(struct scsi_sense_data), sizeof(cmd->sense_buffer))); cmd->result |= (DRIVER_SENSE << 24); } else { /* * Guard against stale sense data. * The Linux mid-layer assumes that sense * was retrieved anytime the first byte of * the sense buffer looks "sane". */ cmd->sense_buffer[0] = 0; } if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) { uint32_t amount_xferred; amount_xferred = ahc_get_transfer_length(scb) - ahc_get_residual(scb); if (amount_xferred < scb->io_ctx->underflow) { printf("Saw underflow (%ld of %ld bytes). " "Treated as error\n", ahc_get_residual(scb), ahc_get_transfer_length(scb)); ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR); } else { ahc_set_transaction_status(scb, CAM_REQ_CMP); ahc_linux_sniff_command(ahc, cmd, scb); } } else if (ahc_get_transaction_status(scb) == DID_OK) { ahc_linux_handle_scsi_status(ahc, dev, scb); } else if (ahc_get_transaction_status(scb) == DID_NO_CONNECT) { /* * Should a selection timeout kill the device? * That depends on whether the selection timeout * is persistent. Since we have no guarantee that * the mid-layer will issue an inquiry for this device * again, we can't just kill it off. dev->flags |= AHC_DEV_UNCONFIGURED; */ } if (dev->openings == 1 && ahc_get_transaction_status(scb) == CAM_REQ_CMP && ahc_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL) dev->tag_success_count++; /* * Some devices deal with temporary internal resource * shortages by returning queue full. When the queue * full occurrs, we throttle back. Slowly try to get * back to our previous queue depth. */ if ((dev->openings + dev->active) < dev->maxtags && dev->tag_success_count > AHC_TAG_SUCCESS_INTERVAL) { dev->tag_success_count = 0; dev->openings++; } if (dev->active == 0) dev->commands_since_idle_or_otag = 0; if (TAILQ_EMPTY(&dev->busyq)) { if ((dev->flags & AHC_DEV_UNCONFIGURED) != 0 && dev->active == 0) ahc_linux_free_device(ahc, dev); } else if ((dev->flags & AHC_DEV_ON_RUN_LIST) == 0) { TAILQ_INSERT_TAIL(&ahc->platform_data->device_runq, dev, links); dev->flags |= AHC_DEV_ON_RUN_LIST; } if ((scb->flags & SCB_RECOVERY_SCB) != 0) { printf("Recovery SCB completes\n"); up(&ahc->platform_data->eh_sem); } ahc_free_scb(ahc, scb); ahc_linux_queue_cmd_complete(ahc, cmd);}static voidahc_linux_handle_scsi_status(struct ahc_softc *ahc, struct ahc_linux_device *dev, struct scb *scb){ /* * We don't currently trust the mid-layer to * properly deal with queue full or busy. So, * when one occurs, we tell the mid-layer to * unconditionally requeue the command to us * so that we can retry it ourselves. We also * implement our own throttling mechanism so * we don't clobber the device with too many * commands. */ switch (ahc_get_scsi_status(scb)) { default: break; case SCSI_STATUS_QUEUE_FULL: { /* * By the time the core driver has returned this * command, all other commands that were queued * to us but not the device have been returned. * This ensures that dev->active is equal to * the number of commands actually queued to * the device. */ dev->tag_success_count = 0; if (dev->active != 0) { /* * Drop our opening count to the number * of commands currently outstanding. */ dev->openings = 0;/* ahc_print_path(ahc, scb); printf("Dropping tag count to %d\n", dev->active); */ if (dev->active == dev->tags_on_last_queuefull) { dev->last_queuefull_same_count++; /* * If we repeatedly see a queue full * at the same queue depth, this * device has a fixed number of tag * slots. Lock in this tag depth * so we stop seeing queue fulls from * this device. */ if (dev->last_queuefull_same_count == AHC_LOCK_TAGS_COUNT) { dev->maxtags = dev->active; ahc_print_path(ahc, scb); printf("Locking max tag count at %d\n", dev->active); } } else { dev->tags_on_last_queuefull = dev->active; dev->last_queuefull_same_count = 0; } ahc_set_transaction_status(scb, CAM_REQUEUE_REQ); ahc_set_scsi_status(scb, SCSI_STATUS_OK); break; } /* * Drop down to a single opening, and treat this * as if the target return BUSY SCSI status. */ dev->openings = 1; /* FALLTHROUGH */ } case SCSI_STATUS_BUSY: { /* * Set a short timer to defer sending commands for * a bit since
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -