cpqfctsinit.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,099 行 · 第 1/5 页
C
2,099 行
int indx; // Remember the command ptr so we can return; we'll complete when // the device comes back, causing immediate retry for( indx=0; indx < CPQFCTS_REQ_QUEUE_LEN; indx++)//, SCptr++) { if( cpqfcHBAdata->LinkDnCmnd[indx] == NULL ) // available? {#ifdef DUMMYCMND_DBG printk(" @add Cmnd %p to LnkDnCmnd[%d]@ ", Cmnd,indx);#endif cpqfcHBAdata->LinkDnCmnd[indx] = Cmnd; break; } } if( indx >= CPQFCTS_REQ_QUEUE_LEN ) // no space for Cmnd?? { // this will result in an _abort call later (with possible trouble) printk("no buffer for LinkDnCmnd!! %p\n", Cmnd); }}// The file <scsi/scsi_host.h> says not to call scsi_done from// inside _queuecommand, so we'll do it from the heartbeat timer// (clarification: Turns out it's ok to call scsi_done from queuecommand // for cases that don't go to the hardware like scsi cmds destined// for LUNs we know don't exist, so this code might be simplified...)static void QueBadTargetCmnd( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd){ int i; // printk(" can't find target %d\n", Cmnd->target); for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++) { // find spare slot if( cpqfcHBAdata->BadTargetCmnd[i] == NULL ) { cpqfcHBAdata->BadTargetCmnd[i] = Cmnd;// printk(" BadTargetCmnd[%d] %p Queued, chnl/target/lun %d/%d/%d\n",// i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun); break; } }}// This is the "main" entry point for Linux Scsi commands --// it all starts here.int cpqfcTS_queuecommand(Scsi_Cmnd *Cmnd, void (* done)(Scsi_Cmnd *)){ struct Scsi_Host *HostAdapter = Cmnd->device->host; CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; PTACHYON fcChip = &cpqfcHBAdata->fcChip; TachFCHDR_GCMND fchs; // only use for FC destination id field PFC_LOGGEDIN_PORT pLoggedInPort; ULONG ulStatus, SESTtype; LONG ExchangeID; ENTER("cpqfcTS_queuecommand"); PCI_TRACEO( (ULONG)Cmnd, 0x98) Cmnd->scsi_done = done;#ifdef DEBUG_CMND cpqfcTS_print_scsi_cmd( Cmnd);#endif // prevent board contention with kernel thread... if( cpqfcHBAdata->BoardLock ) {// printk(" @BrdLck Hld@ "); QueCmndOnBoardLock( cpqfcHBAdata, Cmnd); } else { // in the current system (2.2.12), this routine is called // after spin_lock_irqsave(), so INTs are disabled. However, // we might have something pending in the LinkQ, which // might cause the WorkerTask to run. In case that // happens, make sure we lock it out. PCI_TRACE( 0x98) CPQ_SPINLOCK_HBA( cpqfcHBAdata) PCI_TRACE( 0x98) // can we find an FC device mapping to this SCSI target? pLoggedInPort = fcFindLoggedInPort( fcChip, Cmnd, // search Scsi Nexus 0, // DON'T search linked list for FC port id NULL, // DON'T search linked list for FC WWN NULL); // DON'T care about end of list if( pLoggedInPort == NULL ) // not found! {// printk(" @Q bad targ cmnd %p@ ", Cmnd); QueBadTargetCmnd( cpqfcHBAdata, Cmnd); } else if (Cmnd->device->lun >= CPQFCTS_MAX_LUN) { printk(KERN_WARNING "cpqfc: Invalid LUN: %d\n", Cmnd->device->lun); QueBadTargetCmnd( cpqfcHBAdata, Cmnd); } else // we know what FC device to send to... { // does this device support FCP target functions? // (determined by PRLI field) if( !(pLoggedInPort->fcp_info & TARGET_FUNCTION) ) { printk(" Doesn't support TARGET functions port_id %Xh\n", pLoggedInPort->port_id ); QueBadTargetCmnd( cpqfcHBAdata, Cmnd); } // In this case (previous login OK), the device is temporarily // unavailable waiting for re-login, in which case we expect it // to be back in between 25 - 500ms. // If the FC port doesn't log back in within several seconds // (i.e. implicit "logout"), or we get an explicit logout, // we set "device_blocked" in Scsi_Device struct; in this // case 30 seconds will elapse before Linux/Scsi sends another // command to the device. else if( pLoggedInPort->prli != TRUE ) {// printk("Device (Chnl/Target %d/%d) invalid PRLI, port_id %06lXh\n",// Cmnd->channel, Cmnd->target, pLoggedInPort->port_id); QueLinkDownCmnd( cpqfcHBAdata, Cmnd);// Need to use "blocked" flag?? // Cmnd->device->device_blocked = TRUE; // just let it timeout } else // device supports TARGET functions, and is logged in... { // (context of fchs is to "reply" to...) fchs.s_id = pLoggedInPort->port_id; // destination FC address // what is the data direction? For data TO the device, // we need IWE (Intiator Write Entry). Otherwise, IRE. if( Cmnd->cmnd[0] == WRITE_10 || Cmnd->cmnd[0] == WRITE_6 || Cmnd->cmnd[0] == WRITE_BUFFER || Cmnd->cmnd[0] == VENDOR_WRITE_OPCODE || // CPQ specific Cmnd->cmnd[0] == MODE_SELECT ) { SESTtype = SCSI_IWE; // data from HBA to Device } else SESTtype = SCSI_IRE; // data from Device to HBA ulStatus = cpqfcTSBuildExchange( cpqfcHBAdata, SESTtype, // e.g. Initiator Read Entry (IRE) &fchs, // we are originator; only use d_id Cmnd, // Linux SCSI command (with scatter/gather list) &ExchangeID );// fcController->fcExchanges index, -1 if failed if( !ulStatus ) // Exchange setup? { if( cpqfcHBAdata->BoardLock ) { TriggerHBA( fcChip->Registers.ReMapMemBase, 0); printk(" @bl! %d, xID %Xh@ ", current->pid, ExchangeID); } ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); if( !ulStatus ) { PCI_TRACEO( ExchangeID, 0xB8) // submitted to Tach's Outbound Que (ERQ PI incremented) // waited for completion for ELS type (Login frames issued // synchronously) } else // check reason for Exchange not being started - we might // want to Queue and start later, or fail with error { printk("quecommand: cpqfcTSStartExchange failed: %Xh\n", ulStatus ); } } // end good BuildExchange status else // SEST table probably full -- why? hardware hang? { printk("quecommand: cpqfcTSBuildExchange faild: %Xh\n", ulStatus); } } // end can't do FCP-SCSI target functions } // end can't find target (FC device) CPQ_SPINUNLOCK_HBA( cpqfcHBAdata) } PCI_TRACEO( (ULONG)Cmnd, 0x9C) LEAVE("cpqfcTS_queuecommand"); return 0;} // Entry point for upper Scsi layer intiated abort. Typically// this is called if the command (for hard disk) fails to complete// in 30 seconds. This driver intends to complete all disk commands// within Exchange ".timeOut" seconds (now 7) with target status, or// in case of ".timeOut" expiration, a DID_SOFT_ERROR which causes// immediate retry.// If any disk commands get the _abort call, except for the case that// the physical device was removed or unavailable due to hardware// errors, it should be considered a driver error and reported to// the author.int cpqfcTS_abort(Scsi_Cmnd *Cmnd){// printk(" cpqfcTS_abort called?? \n"); return 0;} int cpqfcTS_eh_abort(Scsi_Cmnd *Cmnd){ struct Scsi_Host *HostAdapter = Cmnd->device->host; // get the pointer to our Scsi layer HBA buffer CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; PTACHYON fcChip = &cpqfcHBAdata->fcChip; FC_EXCHANGES *Exchanges = fcChip->Exchanges; int i; ENTER("cpqfcTS_eh_abort"); Cmnd->result = DID_ABORT <<16; // assume we'll find it printk(" @Linux _abort Scsi_Cmnd %p ", Cmnd); // See if we can find a Cmnd pointer that matches... // The most likely case is we accepted the command // from Linux Scsi (e.g. ceated a SEST entry) and it // got lost somehow. If we can't find any reference // to the passed pointer, we can only presume it // got completed as far as our driver is concerned. // If we found it, we will try to abort it through // common mechanism. If FC ABTS is successful (ACC) // or is rejected (RJT) by target, we will call // Scsi "done" quickly. Otherwise, the ABTS will timeout // and we'll call "done" later. // Search the SEST exchanges for a matching Cmnd ptr. for( i=0; i< TACH_SEST_LEN; i++) { if( Exchanges->fcExchange[i].Cmnd == Cmnd ) { // found it! printk(" x_ID %Xh, type %Xh\n", i, Exchanges->fcExchange[i].type); Exchanges->fcExchange[i].status = INITIATOR_ABORT; // seconds default Exchanges->fcExchange[i].timeOut = 10; // seconds default (changed later) // Since we need to immediately return the aborted Cmnd to Scsi // upper layers, we can't make future reference to any of its // fields (e.g the Nexus). cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &i); break; } } if( i >= TACH_SEST_LEN ) // didn't find Cmnd ptr in chip's SEST? { // now search our non-SEST buffers (i.e. Cmnd waiting to // start on the HBA or waiting to complete with error for retry). // first check BadTargetCmnd for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++) { if( cpqfcHBAdata->BadTargetCmnd[i] == Cmnd ) { cpqfcHBAdata->BadTargetCmnd[i] = NULL; printk("in BadTargetCmnd Q\n"); goto Done; // exit } } // if not found above... for( i=0; i < CPQFCTS_REQ_QUEUE_LEN; i++) { if( cpqfcHBAdata->LinkDnCmnd[i] == Cmnd ) { cpqfcHBAdata->LinkDnCmnd[i] = NULL; printk("in LinkDnCmnd Q\n"); goto Done; } } for( i=0; i< CPQFCTS_REQ_QUEUE_LEN; i++) { // find spare slot if( cpqfcHBAdata->BoardLockCmnd[i] == Cmnd ) { cpqfcHBAdata->BoardLockCmnd[i] = NULL; printk("in BoardLockCmnd Q\n"); goto Done; } } Cmnd->result = DID_ERROR <<16; // Hmmm... printk("Not found! ");// panic("_abort"); } Done: // panic("_abort"); LEAVE("cpqfcTS_eh_abort"); return 0; // (see scsi.h)} // FCP-SCSI Target Device Reset// See dpANS Fibre Channel Protocol for SCSI// X3.269-199X revision 12, pg 25#ifdef SUPPORT_RESETint cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev, unsigned int reset_flags){ int timeout = 10*HZ; int retries = 1; char scsi_cdb[12]; int result; Scsi_Cmnd * SCpnt; Scsi_Device * SDpnt;// FIXME, cpqfcTS_TargetDeviceReset needs to be fixed // similarly to how the passthrough ioctl was fixed // around the 2.5.30 kernel. Scsi_Cmnd replaced with // Scsi_Request, etc.// For now, so people don't fall into a hole... // printk(" ENTERING cpqfcTS_TargetDeviceReset() - flag=%d \n",reset_flags); if (ScsiDev->host->eh_active) return FAILED; memset( scsi_cdb, 0, sizeof( scsi_cdb)); scsi_cdb[0] = RELEASE; SCpnt = scsi_get_command(ScsiDev, GFP_KERNEL); { CPQFC_DECLARE_COMPLETION(wait); SCpnt->SCp.buffers_residual = FCP_TARGET_RESET; // FIXME: this would panic, SCpnt->request would be NULL. SCpnt->request->CPQFC_WAITING = &wait; scsi_do_cmd(SCpnt, scsi_cdb, NULL, 0, my_ioctl_done, timeout, retries); CPQFC_WAIT_FOR_COMPLETION(&wait); SCpnt->request->CPQFC_WAITING = NULL; } if(driver_byte(SCpnt->result) != 0) switch(SCpnt->sense_buffer[2] & 0xf) { case ILLEGAL_REQUEST: if(cmd[0] == ALLOW_MEDIUM_REMOVAL) dev->lockable = 0; else printk("SCSI device (ioctl) reports ILLEGAL REQUEST.\n"); break; case NOT_READY: // This happens if there is no disc in drive if(dev->removable && (cmd[0] != TEST_UNIT_READY)){ printk(KERN_INFO "Device not ready. Make sure there is a disc in the drive.\n"); break; } case UNIT_ATTENTION: if (dev->removable){ dev->changed = 1; SCpnt->result = 0; // This is no longer considered an error // gag this error, VFS will log it anyway /axboe // printk(KERN_INFO "Disc change detected.\n"); break; }; default: // Fall through for non-removable media printk("SCSI error: host %d id %d lun %d return code = %x\n", dev->host->host_no, dev->id, dev->lun, SCpnt->result); printk("\tSense class %x, sense error %x, extended sense %x\n", sense_class(SCpnt->sense_buffer[0]), sense_error(SCpnt->sense_buffer[0]), SCpnt->sense_buffer[2] & 0xf); }; result = SCpnt->result; SDpnt = SCpnt->device; scsi_put_command(SCpnt); SCpnt = NULL; // printk(" LEAVING cpqfcTS_TargetDeviceReset() - return SUCCESS \n"); return SUCCESS;}#elseint cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev, unsigned int reset_flags){
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?