📄 cpqfctscontrol.c
字号:
// completes { pLoggedInPort = &fcChip->fcPorts; while( pLoggedInPort ) // for all ports which are expecting // PDISC after the next LIP, set the // logoutTimer { if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec? { pLoggedInPort->LOGO_timer = 3; // we want 2 seconds // but Timer granularity // is 1 second } // suspend any I/O in progress until // PDISC received... pLoggedInPort->prli = FALSE; // block FCP-SCSI commands pLoggedInPort = pLoggedInPort->pNextPort; } // ... all Previously known ports checked } // since any hot plugging device may NOT support LILP frames // (such as early Tachyon chips), clear this flag indicating // we shouldn't use (our copy of) a LILP map. // If we receive an LILP frame, we'll set it again. fcChip->Options.LILPin = 0; // our LILPmap is invalid cpqfcHBAdata->PortDiscDone = 0; // must re-validate FC ports! // also, we want to invalidate (i.e. INITIATOR_ABORT) any // open Login exchanges, in case the LinkDown happened in the // middle of logins. It's possible that some ports already // ACCepted login commands which we have not processed before // another LinkDown occured. Any accepted Login exhanges are // invalidated by LinkDown, even before they are acknowledged. // It's also possible for a port to have a Queued Reply or Request // for login which was interrupted by LinkDown; it may come later, // but it will be unacceptable to us. // we must scan the entire exchange space, find every Login type // originated by us, and abort it. This is NOT an abort due to // timeout, so we don't actually send abort to the other port - // we just complete it to free up the fcExchange slot. for( i=TACH_SEST_LEN; i< TACH_MAX_XID; i++) { // looking for Extended Link Serv.Exchanges if( Exchanges->fcExchange[i].type == ELS_PDISC || Exchanges->fcExchange[i].type == ELS_PLOGI || Exchanges->fcExchange[i].type == ELS_PRLI ) { // ABORT the exchange!#ifdef IMQ_DEBUG printk("Originator ABORT x_id %Xh, type %Xh, port_id %Xh on LDn\n", i, Exchanges->fcExchange[i].type, Exchanges->fcExchange[i].fchs.d_id);#endif Exchanges->fcExchange[i].status |= INITIATOR_ABORT; cpqfcTSCompleteExchange( fcChip, i); // abort on LDn } } } // ################ LINK UP ################## if( fcChip->Registers.FMstatus.value & 0x200L ) // Link Up bit { // AL_PA could have changed // We need the following code, duplicated from LinkDn condition, // because it's possible for the Tachyon to re-initialize (hard // reset) without ever getting a LinkDn indication. pLoggedInPort = &fcChip->fcPorts; while( pLoggedInPort ) // for all ports which are expecting // PDISC after the next LIP, set the // logoutTimer { if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec? { pLoggedInPort->LOGO_timer = 3; // we want 2 seconds // but Timer granularity // is 1 second // suspend any I/O in progress until // PDISC received... } pLoggedInPort = pLoggedInPort->pNextPort; } // ... all Previously known ports checked // CpqTs acquired AL_PA in register AL_PA (ACQ_ALPA) fcChip->Registers.rcv_al_pa.value = readl(fcChip->Registers.rcv_al_pa.address); // Now, if our acquired address is DIFFERENT from our // previous one, we are not allow to do PDISC - we // must go back to PLOGI, which will terminate I/O in // progress for ALL logged in FC devices... // (This is highly unlikely). if( (fcChip->Registers.my_al_pa & 0xFF) != ((fcChip->Registers.rcv_al_pa.value >> 16) &0xFF) ) {// printk(" #our HBA port_id changed!# "); // FC port_id changed!! pLoggedInPort = &fcChip->fcPorts; while( pLoggedInPort ) // for all ports which are expecting // PDISC after the next LIP, set the // logoutTimer { pLoggedInPort->pdisc = FALSE; pLoggedInPort->prli = FALSE; pLoggedInPort = pLoggedInPort->pNextPort; } // ... all Previously known ports checked // when the port_id changes, we must terminate // all open exchanges. cpqfcTSTerminateExchange( cpqfcHBAdata, NULL, PORTID_CHANGED); } // Replace the entire 24-bit port_id. We only know the // lower 8 bits (alpa) from Tachyon; if a FLOGI is done, // we'll get the upper 16-bits from the FLOGI ACC frame. // If someone plugs into Fabric switch, we'll do FLOGI and // get full 24-bit port_id; someone could then remove and // hot-plug us into a dumb hub. If we send a 24-bit PLOGI // to a "private" loop device, it might blow up. // Consequently, we force the upper 16-bits of port_id to // be re-set on every LinkUp transition fcChip->Registers.my_al_pa = (fcChip->Registers.rcv_al_pa.value >> 16) & 0xFF; // copy frame manager status to unused ULONG slot fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] = fcChip->Registers.my_al_pa; // (for debugging) // for TachLite, we need to write the acquired al_pa // back into the FMconfig register, because after // first initialization, the AQ (prev. acq.) bit gets // set, causing TL FM to use the AL_PA field in FMconfig. // (In Tachyon, FM writes the acquired AL_PA for us.) ulBuff = readl( fcChip->Registers.FMconfig.address); ulBuff &= 0x00ffffffL; // mask out current al_pa ulBuff |= ( fcChip->Registers.my_al_pa << 24 ); // or in acq. al_pa fcChip->Registers.FMconfig.value = ulBuff; // copy it back writel( fcChip->Registers.FMconfig.value, // put in TachLite fcChip->Registers.FMconfig.address); #ifdef IMQ_DEBUG printk("#LUp %Xh, FMstat 0x%08X#", fcChip->Registers.my_al_pa, fcChip->Registers.FMstatus.value);#endif // also set the WRITE-ONLY My_ID Register (for Fabric // initialization) writel( fcChip->Registers.my_al_pa, fcChip->Registers.ReMapMemBase +TL_MEM_TACH_My_ID); fcChip->fcStats.linkUp++; // reset TL statistics counters // (we ignore these error counters // while link is down) ulBuff = // just reset TL's counter readl( fcChip->Registers.FMLinkStatus1.address); ulBuff = // just reset TL's counter readl( fcChip->Registers.FMLinkStatus2.address); // for initiator, need to start verifying ports (e.g. PDISC) CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachlite, if Link OK // Tachyon creates an interesting problem for us on LILP frames. // Instead of writing the incoming LILP frame into the SFQ before // indicating LINK UP (the actual order of events), Tachyon tells // us LINK UP, and later us the LILP. So we delay, then examine the // IMQ for an Inbound CM (x04); if found, we can set // LINKACTIVE after processing the LILP. Otherwise, just proceed. // Since Tachyon imposes this time delay (and doesn't tell us // what it is), we have to impose a delay before "Peeking" the IMQ // for Tach hardware (DMA) delivery. // Processing LILP is required by SANMark udelay( 1000); // microsec delay waiting for LILP (if it comes) if( PeekIMQEntry( fcChip, ELS_LILP_FRAME) ) { // found SFQ LILP, which will post LINKACTIVE // printk("skipping LINKACTIVE post\n"); } else cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, ulFibreFrame); } // ******* Set Fabric Login indication ******** if( fcChip->Registers.FMstatus.value & 0x2000 ) { printk(" #Fabric# "); fcChip->Options.fabric = 1; } else fcChip->Options.fabric = 0; // ******* LIP(F8,x) or BAD AL_PA? ******** if( fcChip->Registers.FMstatus.value & 0x30000L ) { // copy the error AL_PAs fcChip->Registers.rcv_al_pa.value = readl(fcChip->Registers.rcv_al_pa.address); // Bad AL_PA? if( fcChip->Registers.FMstatus.value & 0x10000L ) { PFC_LOGGEDIN_PORT pLoggedInPort; // copy "BAD" al_pa field fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] = (fcChip->Registers.rcv_al_pa.value & 0xff00L) >> 8; pLoggedInPort = fcFindLoggedInPort( fcChip, NULL, // DON'T search Scsi Nexus fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1], // port id NULL, // DON'T search linked list for FC WWN NULL); // DON'T care about end of list if( pLoggedInPort ) { // Just in case we got this BAD_ALPA because a device // quietly disappeared (can happen on non-managed hubs such // as the Vixel Rapport 1000), // do an Implicit Logout. We never expect this on a Logged // in port (but do expect it on port discovery). // (As a reasonable alternative, this could be changed to // simply start the implicit logout timer, giving the device // several seconds to "come back".) // printk(" #BAD alpa %Xh# ", fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1]); cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort); } } // LIP(f8,x)? if( fcChip->Registers.FMstatus.value & 0x20000L ) { // for debugging, copy al_pa field fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] = (fcChip->Registers.rcv_al_pa.value & 0xffL); // get the other port's al_pa // (one that sent LIP(F8,?) ) } } // Elastic store err if( fcChip->Registers.FMstatus.value & 0x400L ) { // don't count e-s if loop is down! if( !(USHORT)(fcChip->Registers.FMstatus.value & 0x80) ) fcChip->fcStats.e_stores++; } } break; case INBOUND_FCP_XCHG_COMPLETION: // 0x0C // Remarks: // On Tachlite TL/TS, we get this message when the data phase // of a SEST inbound transfer is complete. For example, if a WRITE command // was received with OX_ID 0, we might respond with XFER_RDY with // RX_ID 8001. This would start the SEST controlled data phases. When // all data frames are received, we get this inbound completion. This means // we should send a status frame to complete the status phase of the // FCP-SCSI exchange, using the same OX_ID,RX_ID that we used for data // frames. // See Outbound CM discussion of x_IDs // Psuedo Code // Get SEST index (x_ID) // x_ID out of range, return (err condition) // set status bits from 2nd dword // free transactionID & SEST entry // call fcComplete with transactionID & status ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0]; x_ID = ulBuff & 0x7fffL; // lower 14 bits SEST_Index/Trans_ID // (mask out MSB "direction" bit) // Range check CM OX/RX_ID value... if( x_ID < TACH_SEST_LEN ) // don't go beyond SEST array space {//#define FCP_COMPLETION_DBG 1#ifdef FCP_COMPLETION_DBG printk(" FCP_CM x_ID %Xh, status %Xh, Cmnd %p\n", x_ID, ulBuff, Exchanges->fcExchange[x_ID].Cmnd);#endif if( ulBuff & 0x08000000L ) // RPC -Response Phase Complete - or - // time to send response frame? RPCset = 1; // (SEST transaction) else RPCset = 0; // set the status for this Inbound SCSI transaction's ID dwStatus = 0L; if( ulBuff & 0x70000000L ) // any errs? { if( ulBuff & 0x40000000L ) dwStatus |= LINKFAIL_RX; if( ulBuff & 0x20000000L ) dwStatus |= COUNT_ERROR; if( ulBuff & 0x10000000L ) dwStatus |= OVERFLOW; } // FCP transaction done - copy status Exchanges->fcExchange[ x_ID ].status = dwStatus; // Did the exchange get an FCP-RSP response frame? // (Note the little endian/big endian FC payload difference) if( RPCset ) // SEST transaction Response frame rec'd { // complete the command in our driver... cpqfcTSCompleteExchange( fcChip, x_ID); } // end "RPCset" else // ("target" logic) { // Tachlite says all data frames have been received - now it's time // to analyze data transfer (successful?), then send a response // frame for this exchange ulFibreFrame[0] = x_ID; // copy for later reference // if this was a TWE, we have to send satus response if( Exchanges->fcExchange[ x_ID].type == SCSI_TWE ) {// fcPutScsiQue( cpqfcHBAdata, // NEED_FCP_RSP, ulFibreFrame); // (ulFibreFrame not used here) } } } else // ERROR CONDITION! bogus x_ID in completion message { printk("IN FCP_XCHG: bad x_ID: %Xh\n", x_ID); } break; case INBOUND_SCSI_DATA_COMMAND: case BAD_SCSI_FRAME: case INB_SCSI_STATUS_COMPLETION: case BUFFER_PROCESSED_COMPLETION: break; } // Tachyon is producing; // we are consuming fcChip->IMQ->consumerIndex++; // increment OUR consumerIndex if( fcChip->IMQ->consumerIndex >= IMQ_LEN)// check for rollover fcChip->IMQ->consumerIndex = 0L; // reset it if( fcChip->IMQ->producerIndex == fcChip->IMQ->consumerIndex ) { // all Messages are processed - iStatus = 0; // no more messages to process } else iStatus = 1; // more messages to process // update TachLite's ConsumerIndex... (clears INTA_L)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -