aic7xxx_osm.c
来自「linux 内核源代码」· C语言 代码 · 共 2,346 行 · 第 1/5 页
C
2,346 行
*/ dev->openings = 1; ahc_set_scsi_status(scb, SCSI_STATUS_BUSY); ahc_platform_set_tags(ahc, sdev, &devinfo, (dev->flags & AHC_DEV_Q_BASIC) ? AHC_QUEUE_BASIC : AHC_QUEUE_TAGGED); break; } }}static voidahc_linux_queue_cmd_complete(struct ahc_softc *ahc, 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. */ { u_int new_status; switch (ahc_cmd_get_transaction_status(cmd)) { 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; } ahc_cmd_set_transaction_status(cmd, new_status); } cmd->scsi_done(cmd);}static voidahc_linux_freeze_simq(struct ahc_softc *ahc){ unsigned long s; ahc_lock(ahc, &s); ahc->platform_data->qfrozen++; if (ahc->platform_data->qfrozen == 1) { scsi_block_requests(ahc->platform_data->host); /* XXX What about Twin channels? */ ahc_platform_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS, CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_INITIATOR, CAM_REQUEUE_REQ); } ahc_unlock(ahc, &s);}static voidahc_linux_release_simq(struct ahc_softc *ahc){ u_long s; int unblock_reqs; unblock_reqs = 0; ahc_lock(ahc, &s); if (ahc->platform_data->qfrozen > 0) ahc->platform_data->qfrozen--; if (ahc->platform_data->qfrozen == 0) unblock_reqs = 1; ahc_unlock(ahc, &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(ahc->platform_data->host);}static intahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag){ struct ahc_softc *ahc; struct ahc_linux_device *dev; struct scb *pending_scb; u_int saved_scbptr; u_int active_scb_index; u_int last_phase; u_int saved_scsiid; u_int cdb_byte; int retval; int was_paused; int paused; int wait; int disconnected; unsigned long flags; pending_scb = NULL; paused = FALSE; wait = FALSE; ahc = *(struct ahc_softc **)cmd->device->host->hostdata; scmd_printk(KERN_INFO, cmd, "Attempting to queue a%s message\n", 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"); ahc_lock(ahc, &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. */ printf("%s:%d:%d:%d: Is not an active device\n", ahc_name(ahc), cmd->device->channel, cmd->device->id, cmd->device->lun); retval = SUCCESS; goto no_cmd; } if ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED)) == 0 && ahc_search_untagged_queues(ahc, cmd, cmd->device->id, cmd->device->channel + 'A', cmd->device->lun, CAM_REQ_ABORTED, SEARCH_COMPLETE) != 0) { printf("%s:%d:%d:%d: Command found on untagged queue\n", ahc_name(ahc), cmd->device->channel, cmd->device->id, cmd->device->lun); retval = SUCCESS; goto done; } /* * See if we can find a matching cmd in the pending list. */ LIST_FOREACH(pending_scb, &ahc->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, &ahc->pending_scbs, pending_links) { if (ahc_match_scb(ahc, 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 and that we didn't "just" miss * an interrupt that would affect this cmd. */ was_paused = ahc_is_paused(ahc); ahc_pause_and_flushwork(ahc); 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", ahc_name(ahc), was_paused ? "" : "not "); ahc_dump_card_state(ahc); disconnected = TRUE; if (flag == SCB_ABORT) { if (ahc_search_qinfifo(ahc, 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", ahc_name(ahc), cmd->device->channel, cmd->device->id, cmd->device->lun); retval = SUCCESS; goto done; } } else if (ahc_search_qinfifo(ahc, cmd->device->id, cmd->device->channel + 'A', cmd->device->lun, pending_scb->hscb->tag, ROLE_INITIATOR, /*status*/0, SEARCH_COUNT) > 0) { disconnected = FALSE; } if (disconnected && (ahc_inb(ahc, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) { struct scb *bus_scb; bus_scb = ahc_lookup_scb(ahc, ahc_inb(ahc, SCB_TAG)); if (bus_scb == pending_scb) disconnected = FALSE; else if (flag != SCB_ABORT && ahc_inb(ahc, SAVED_SCSIID) == pending_scb->hscb->scsiid && ahc_inb(ahc, 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, is in the disconnected state, or we're hoping to find * a command for the same target active on the bus to abuse to * send a BDR. Queue the appropriate message based on which of * these states we are in. */ last_phase = ahc_inb(ahc, LASTPHASE); saved_scbptr = ahc_inb(ahc, SCBPTR); active_scb_index = ahc_inb(ahc, SCB_TAG); saved_scsiid = ahc_inb(ahc, SAVED_SCSIID); if (last_phase != P_BUSFREE && (pending_scb->hscb->tag == active_scb_index || (flag == SCB_DEVICE_RESET && SCSIID_TARGET(ahc, saved_scsiid) == scmd_id(cmd)))) { /* * We're active on the bus, so assert ATN * and hope that the target responds. */ pending_scb = ahc_lookup_scb(ahc, active_scb_index); pending_scb->flags |= SCB_RECOVERY_SCB|flag; ahc_outb(ahc, MSG_OUT, HOST_MSG); ahc_outb(ahc, 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. * In either case (selection or reselection), * we will now issue the approprate message * to the timed-out device. * * Set the MK_MESSAGE control bit indicating * that we desire to send a message. We * also set the disconnected flag since * in the paging case 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; pending_scb->flags |= SCB_RECOVERY_SCB|flag; /* * Remove any cached copy of this SCB in the * disconnected list in preparation for the * queuing of our abort SCB. We use the * same element in the SCB, SCB_NEXT, for * both the qinfifo and the disconnected list. */ ahc_search_disc_list(ahc, cmd->device->id, cmd->device->channel + 'A', cmd->device->lun, pending_scb->hscb->tag, /*stop_on_first*/TRUE, /*remove*/TRUE, /*save_state*/FALSE); /* * In the non-paging case, 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. */ if ((ahc->flags & AHC_PAGESCBS) == 0) { ahc_outb(ahc, SCBPTR, pending_scb->hscb->tag); ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL)|MK_MESSAGE); } /* * Clear out any entries in the QINFIFO first * so we are the next SCB for this target * to run. */ ahc_search_qinfifo(ahc, cmd->device->id, cmd->device->channel + 'A', cmd->device->lun, SCB_LIST_NULL, ROLE_INITIATOR, CAM_REQUEUE_REQ, SEARCH_COMPLETE); ahc_qinfifo_requeue_tail(ahc, pending_scb); ahc_outb(ahc, SCBPTR, saved_scbptr); ahc_print_path(ahc, 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) ahc_unpause(ahc); if (wait) { DECLARE_COMPLETION_ONSTACK(done); ahc->platform_data->eh_done = &done; ahc_unlock(ahc, &flags); printf("Recovery code sleeping\n"); if (!wait_for_completion_timeout(&done, 5 * HZ)) { ahc_lock(ahc, &flags); ahc->platform_data->eh_done = NULL; ahc_unlock(ahc, &flags); printf("Timer Expired\n"); retval = FAILED; } printf("Recovery code awake\n"); } else ahc_unlock(ahc, &flags); return (retval);}voidahc_platform_dump_card_state(struct ahc_softc *ahc){}static void ahc_linux_set_width(struct scsi_target *starget, int width){ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata); struct ahc_devinfo devinfo; unsigned long flags; ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, starget->channel + 'A', ROLE_INITIATOR); ahc_lock(ahc, &flags); ahc_set_width(ahc, &devinfo, width, AHC_TRANS_GOAL, FALSE); ahc_unlock(ahc, &flags);}static void ahc_linux_set_period(struct scsi_target *starget, int period){ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); struct ahc_softc *ahc = *((struct ahc_softc **)shost->hostdata); struct ahc_tmode_tstate *tstate; struct ahc_initiator_tinfo *tinfo = ahc_fetch_transinfo(ahc, starget->channel + 'A', shost->this_id, starget->id, &tstate); struct ahc_devinfo devinfo; unsigned int ppr_options = tinfo->goal.ppr_options; unsigned long flags; unsigned long offset = tinfo->goal.offset; struct ahc_syncrate *syncrate; if (offset == 0) offset = MAX_OFFSET; if (period < 9) period = 9; /* 12.5ns is our minimum */ if (period == 9) { if (spi_max_width(starget)) ppr_options |= MSG_EXT_PPR_DT_REQ; else /* need wide for DT and need DT for 12.5 ns */ period = 10; } ahc_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, starget->channel + 'A', ROLE_INITIATOR); /* all PPR requests apart from QAS require wide transfers */ if (ppr_options & ~MSG_EXT_PPR_QAS_REQ) { if (spi_width(starget) == 0) ppr_options &= MSG_EXT_PPR_QAS_REQ; } syncrate = ahc_find_syncrate(ahc, &period, &ppr_options, AHC_SYNCRATE_DT); ahc_lock(ahc, &flags); ahc_set_syncrate(ahc, &devinfo, syncrate, period, offset, ppr_options, AHC_TRANS_GOAL, FALSE); ahc_unlock(ahc, &flags);}static void ahc_linux_set_offset(struct scsi_target *starget, int offset)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?