📄 aic79xx_osm.c
字号:
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 == AHD_LOCK_TAGS_COUNT) { dev->maxtags = dev->active; ahd_print_path(ahd, 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; } ahd_set_transaction_status(scb, CAM_REQUEUE_REQ); ahd_set_scsi_status(scb, SCSI_STATUS_OK); ahd_platform_set_tags(ahd, &devinfo, (dev->flags & AHD_DEV_Q_BASIC) ? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED); break; } /* * Drop down to a single opening, and treat this * as if the target returned BUSY SCSI status. */ dev->openings = 1; ahd_platform_set_tags(ahd, &devinfo, (dev->flags & AHD_DEV_Q_BASIC) ? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED); ahd_set_scsi_status(scb, SCSI_STATUS_BUSY); }}static voidahd_linux_queue_cmd_complete(struct ahd_softc *ahd, struct scsi_cmnd *cmd){ /* * Map CAM error codes into Linux Error codes. We * avoid the conversion so that the DV code has the * full error information available when making * state change decisions. */ { uint32_t status; u_int new_status; status = ahd_cmd_get_transaction_status(cmd); switch (status) { case CAM_REQ_INPROG: case CAM_REQ_CMP: case CAM_SCSI_STATUS_ERROR: new_status = DID_OK; break; case CAM_REQ_ABORTED: new_status = DID_ABORT; break; case CAM_BUSY: new_status = DID_BUS_BUSY; break; case CAM_REQ_INVALID: case CAM_PATH_INVALID: new_status = DID_BAD_TARGET; break; case CAM_SEL_TIMEOUT: new_status = DID_NO_CONNECT; break; case CAM_SCSI_BUS_RESET: case CAM_BDR_SENT: new_status = DID_RESET; break; case CAM_UNCOR_PARITY: new_status = DID_PARITY; break; case CAM_CMD_TIMEOUT: new_status = DID_TIME_OUT; break; case CAM_UA_ABORT: case CAM_REQ_CMP_ERR: case CAM_AUTOSENSE_FAIL: case CAM_NO_HBA: case CAM_DATA_RUN_ERR: case CAM_UNEXP_BUSFREE: case CAM_SEQUENCE_FAIL: case CAM_CCB_LEN_ERR: case CAM_PROVIDE_FAIL: case CAM_REQ_TERMIO: case CAM_UNREC_HBA_ERROR: case CAM_REQ_TOO_BIG: new_status = DID_ERROR; break; case CAM_REQUEUE_REQ: new_status = DID_REQUEUE; break; default: /* We should never get here */ new_status = DID_ERROR; break; } ahd_cmd_set_transaction_status(cmd, new_status); } cmd->scsi_done(cmd);}static voidahd_linux_sem_timeout(u_long arg){ struct ahd_softc *ahd; u_long s; ahd = (struct ahd_softc *)arg; ahd_lock(ahd, &s); if ((ahd->platform_data->flags & AHD_SCB_UP_EH_SEM) != 0) { ahd->platform_data->flags &= ~AHD_SCB_UP_EH_SEM; up(&ahd->platform_data->eh_sem); } ahd_unlock(ahd, &s);}voidahd_freeze_simq(struct ahd_softc *ahd){ ahd->platform_data->qfrozen++; if (ahd->platform_data->qfrozen == 1) { scsi_block_requests(ahd->platform_data->host); ahd_platform_abort_scbs(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS, CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_INITIATOR, CAM_REQUEUE_REQ); }}voidahd_release_simq(struct ahd_softc *ahd){ u_long s; int unblock_reqs; unblock_reqs = 0; ahd_lock(ahd, &s); if (ahd->platform_data->qfrozen > 0) ahd->platform_data->qfrozen--; if (ahd->platform_data->qfrozen == 0) { unblock_reqs = 1; } ahd_unlock(ahd, &s); /* * There is still a race here. The mid-layer * should keep its own freeze count and use * a bottom half handler to run the queues * so we can unblock with our own lock held. */ if (unblock_reqs) scsi_unblock_requests(ahd->platform_data->host);}static intahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag){ struct ahd_softc *ahd; struct ahd_linux_device *dev; struct scb *pending_scb; u_int saved_scbptr; u_int active_scbptr; u_int last_phase; u_int saved_scsiid; u_int cdb_byte; int retval; int was_paused; int paused; int wait; int disconnected; ahd_mode_state saved_modes; unsigned long flags; pending_scb = NULL; paused = FALSE; wait = FALSE; ahd = *(struct ahd_softc **)cmd->device->host->hostdata; scmd_printk(KERN_INFO, cmd, "Attempting to queue a%s message:", flag == SCB_ABORT ? "n ABORT" : " TARGET RESET"); printf("CDB:"); for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++) printf(" 0x%x", cmd->cmnd[cdb_byte]); printf("\n"); ahd_lock(ahd, &flags); /* * First determine if we currently own this command. * Start by searching the device queue. If not found * there, check the pending_scb list. If not found * at all, and the system wanted us to just abort the * command, return success. */ dev = scsi_transport_device_data(cmd->device); if (dev == NULL) { /* * No target device for this command exists, * so we must not still own the command. */ scmd_printk(KERN_INFO, cmd, "Is not an active device\n"); retval = SUCCESS; goto no_cmd; } /* * See if we can find a matching cmd in the pending list. */ LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) { if (pending_scb->io_ctx == cmd) break; } if (pending_scb == NULL && flag == SCB_DEVICE_RESET) { /* Any SCB for this device will do for a target reset */ LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) { if (ahd_match_scb(ahd, pending_scb, scmd_id(cmd), scmd_channel(cmd) + 'A', CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_INITIATOR)) break; } } if (pending_scb == NULL) { scmd_printk(KERN_INFO, cmd, "Command not found\n"); goto no_cmd; } if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) { /* * We can't queue two recovery actions using the same SCB */ retval = FAILED; goto done; } /* * Ensure that the card doesn't do anything * behind our back. Also make sure that we * didn't "just" miss an interrupt that would * affect this cmd. */ was_paused = ahd_is_paused(ahd); ahd_pause_and_flushwork(ahd); paused = TRUE; if ((pending_scb->flags & SCB_ACTIVE) == 0) { scmd_printk(KERN_INFO, cmd, "Command already completed\n"); goto no_cmd; } printf("%s: At time of recovery, card was %spaused\n", ahd_name(ahd), was_paused ? "" : "not "); ahd_dump_card_state(ahd); disconnected = TRUE; if (flag == SCB_ABORT) { if (ahd_search_qinfifo(ahd, cmd->device->id, cmd->device->channel + 'A', cmd->device->lun, pending_scb->hscb->tag, ROLE_INITIATOR, CAM_REQ_ABORTED, SEARCH_COMPLETE) > 0) { printf("%s:%d:%d:%d: Cmd aborted from QINFIFO\n", ahd_name(ahd), cmd->device->channel, cmd->device->id, cmd->device->lun); retval = SUCCESS; goto done; } } else if (ahd_search_qinfifo(ahd, cmd->device->id, cmd->device->channel + 'A', cmd->device->lun, pending_scb->hscb->tag, ROLE_INITIATOR, /*status*/0, SEARCH_COUNT) > 0) { disconnected = FALSE; } saved_modes = ahd_save_modes(ahd); ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); last_phase = ahd_inb(ahd, LASTPHASE); saved_scbptr = ahd_get_scbptr(ahd); active_scbptr = saved_scbptr; if (disconnected && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) { struct scb *bus_scb; bus_scb = ahd_lookup_scb(ahd, active_scbptr); if (bus_scb == pending_scb) disconnected = FALSE; else if (flag != SCB_ABORT && ahd_inb(ahd, SAVED_SCSIID) == pending_scb->hscb->scsiid && ahd_inb(ahd, SAVED_LUN) == SCB_GET_LUN(pending_scb)) disconnected = FALSE; } /* * At this point, pending_scb is the scb associated with the * passed in command. That command is currently active on the * bus or is in the disconnected state. */ saved_scsiid = ahd_inb(ahd, SAVED_SCSIID); if (last_phase != P_BUSFREE && (SCB_GET_TAG(pending_scb) == active_scbptr || (flag == SCB_DEVICE_RESET && SCSIID_TARGET(ahd, saved_scsiid) == scmd_id(cmd)))) { /* * We're active on the bus, so assert ATN * and hope that the target responds. */ pending_scb = ahd_lookup_scb(ahd, active_scbptr); pending_scb->flags |= SCB_RECOVERY_SCB|flag; ahd_outb(ahd, MSG_OUT, HOST_MSG); ahd_outb(ahd, SCSISIGO, last_phase|ATNO); scmd_printk(KERN_INFO, cmd, "Device is active, asserting ATN\n"); wait = TRUE; } else if (disconnected) { /* * Actually re-queue this SCB in an attempt * to select the device before it reconnects. */ pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT; ahd_set_scbptr(ahd, SCB_GET_TAG(pending_scb)); pending_scb->hscb->cdb_len = 0; pending_scb->hscb->task_attribute = 0; pending_scb->hscb->task_management = SIU_TASKMGMT_ABORT_TASK; if ((pending_scb->flags & SCB_PACKETIZED) != 0) { /* * Mark the SCB has having an outstanding * task management function. Should the command * complete normally before the task management * function can be sent, the host will be notified * to abort our requeued SCB. */ ahd_outb(ahd, SCB_TASK_MANAGEMENT, pending_scb->hscb->task_management); } else { /* * If non-packetized, set the MK_MESSAGE control * bit indicating that we desire to send a message. * We also set the disconnected flag since there is * no guarantee that our SCB control byte matches * the version on the card. We don't want the * sequencer to abort the command thinking an * unsolicited reselection occurred. */ pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED; /* * The sequencer will never re-reference the * in-core SCB. To make sure we are notified * during reslection, set the MK_MESSAGE flag in * the card's copy of the SCB. */ ahd_outb(ahd, SCB_CONTROL, ahd_inb(ahd, SCB_CONTROL)|MK_MESSAGE); } /* * Clear out any entries in the QINFIFO first * so we are the next SCB for this target * to run. */ ahd_search_qinfifo(ahd, cmd->device->id, cmd->device->channel + 'A', cmd->device->lun, SCB_LIST_NULL, ROLE_INITIATOR, CAM_REQUEUE_REQ, SEARCH_COMPLETE); ahd_qinfifo_requeue_tail(ahd, pending_scb); ahd_set_scbptr(ahd, saved_scbptr); ahd_print_path(ahd, pending_scb); printf("Device is disconnected, re-queuing SCB\n"); wait = TRUE; } else { scmd_printk(KERN_INFO, cmd, "Unable to deliver message\n"); retval = FAILED; goto done; }no_cmd: /* * Our assumption is that if we don't have the command, no * recovery action was required, so we return success. Again, * the semantics of the mid-layer recovery engine are not * well defined, so this may change in time. */ retval = SUCCESS;done: if (paused) ahd_unpause(ahd); if (wait) { struct timer_list timer; int ret; ahd->platform_data->flags |= AHD_SCB_UP_EH_SEM; ahd_unlock(ahd, &flags); init_timer(&timer); timer.data = (u_long)ahd; timer.expires = jiffies + (5 * HZ); timer.function = ahd_linux_sem_timeout; add_timer(&timer); printf("Recovery code sleeping\n"); down(&ahd->platform_data->eh_sem); printf("Recovery code awake\n"); ret = del_timer_sync(&timer); if (ret == 0) { printf("Timer Expired\n"); retval = FAILED; } } ahd_unlock(ahd, &flags); return (retval);}static void ahd_linux_set_width(struct scsi_target *starget, int width){ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); struct ahd_devinfo devinfo; unsigned long flags; ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, starget->channel + 'A', ROLE_INITIATOR); ahd_lock(ahd, &flags); ahd_set_width(ahd, &devinfo, width, AHD_TRANS_GOAL, FALSE); ahd_unlock(ahd, &flags);}static void ahd_linux_set_period(struct scsi_target *starget, int period){ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); struct ahd_tmode_tstate *tstate; struct ahd_initiator_tinfo *tinfo = ahd_fetch_transinfo(ahd, starget->channel + 'A', shost->this_id, starget->id, &tstate); struct ahd_devinfo devinfo; unsigned int ppr_options = tinfo->goal.ppr_options; unsigned int dt; unsigned long flags; unsigned long offset = tinfo->goal.offset;#ifdef AHD_DEBUG if ((ahd_debug & AHD_SHOW_DV) != 0) printf("%s: set period to %d\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -