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 + -
显示快捷键?