diff options
Diffstat (limited to 'drivers/scsi/cpqfcTSworker.c')
-rw-r--r-- | drivers/scsi/cpqfcTSworker.c | 6516 |
1 files changed, 0 insertions, 6516 deletions
diff --git a/drivers/scsi/cpqfcTSworker.c b/drivers/scsi/cpqfcTSworker.c deleted file mode 100644 index d822ddcc52b..00000000000 --- a/drivers/scsi/cpqfcTSworker.c +++ /dev/null @@ -1,6516 +0,0 @@ -/* Copyright(c) 2000, Compaq Computer Corporation - * Fibre Channel Host Bus Adapter - * 64-bit, 66MHz PCI - * Originally developed and tested on: - * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ... - * SP# P225CXCBFIEL6T, Rev XC - * SP# 161290-001, Rev XD - * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5 - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * Written by Don Zimmerman -*/ - -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/ioport.h> -#include <linux/kernel.h> -#include <linux/stat.h> -#include <linux/blkdev.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/smp_lock.h> -#include <linux/pci.h> - -#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)) - -#include <asm/system.h> -#include <asm/irq.h> -#include <asm/dma.h> - -#include "scsi.h" -#include <scsi/scsi_host.h> // struct Scsi_Host definition for T handler -#include "cpqfcTSchip.h" -#include "cpqfcTSstructs.h" -#include "cpqfcTStrigger.h" - -//#define LOGIN_DBG 1 - -// REMARKS: -// Since Tachyon chips may be permitted to wait from 500ms up to 2 sec -// to empty an outgoing frame from its FIFO to the Fibre Channel stream, -// we cannot do everything we need to in the interrupt handler. Specifically, -// every time a link re-init (e.g. LIP) takes place, all SCSI I/O has to be -// suspended until the login sequences have been completed. Login commands -// are frames just like SCSI commands are frames; they are subject to the same -// timeout issues and delays. Also, various specs provide up to 2 seconds for -// devices to log back in (i.e. respond with ACC to a login frame), so I/O to -// that device has to be suspended. -// A serious problem here occurs on highly loaded FC-AL systems. If our FC port -// has a low priority (e.g. high arbitrated loop physical address, alpa), and -// some other device is hogging bandwidth (permissible under FC-AL), we might -// time out thinking the link is hung, when it's simply busy. Many such -// considerations complicate the design. Although Tachyon assumes control -// (in silicon) for many link-specific issues, the Linux driver is left with the -// rest, which turns out to be a difficult, time critical chore. - -// These "worker" functions will handle things like FC Logins; all -// processes with I/O to our device must wait for the Login to complete -// and (if successful) I/O to resume. In the event of a malfunctioning or -// very busy loop, it may take hundreds of millisecs or even seconds to complete -// a frame send. We don't want to hang up the entire server (and all -// processes which don't depend on Fibre) during this wait. - -// The Tachyon chip can have around 30,000 I/O operations ("exchanges") -// open at one time. However, each exchange must be initiated -// synchronously (i.e. each of the 30k I/O had to be started one at a -// time by sending a starting frame via Tachyon's outbound que). - -// To accommodate kernel "module" build, this driver limits the exchanges -// to 256, because of the contiguous physical memory limitation of 128M. - -// Typical FC Exchanges are opened presuming the FC frames start without errors, -// while Exchange completion is handled in the interrupt handler. This -// optimizes performance for the "everything's working" case. -// However, when we have FC related errors or hot plugging of FC ports, we pause -// I/O and handle FC-specific tasks in the worker thread. These FC-specific -// functions will handle things like FC Logins and Aborts. As the Login sequence -// completes to each and every target, I/O can resume to that target. - -// Our kernel "worker thread" must share the HBA with threads calling -// "queuecommand". We define a "BoardLock" semaphore which indicates -// to "queuecommand" that the HBA is unavailable, and Cmnds are added to a -// board lock Q. When the worker thread finishes with the board, the board -// lock Q commands are completed with status causing immediate retry. -// Typically, the board is locked while Logins are in progress after an -// FC Link Down condition. When Cmnds are re-queued after board lock, the -// particular Scsi channel/target may or may not have logged back in. When -// the device is waiting for login, the "prli" flag is clear, in which case -// commands are passed to a Link Down Q. Whenever the login finally completes, -// the LinkDown Q is completed, again with status causing immediate retry. -// When FC devices are logged in, we build and start FC commands to the -// devices. - -// NOTE!! As of May 2000, kernel 2.2.14, the error recovery logic for devices -// that never log back in (e.g. physically removed) is NOT completely -// understood. I've still seen instances of system hangs on failed Write -// commands (possibly from the ext2 layer?) on device removal. Such special -// cases need to be evaluated from a system/application view - e.g., how -// exactly does the system want me to complete commands when the device is -// physically removed?? - -// local functions - -static void SetLoginFields( - PFC_LOGGEDIN_PORT pLoggedInPort, - TachFCHDR_GCMND* fchs, - BOOLEAN PDisc, - BOOLEAN Originator); - -static void AnalyzeIncomingFrame( - CPQFCHBA *cpqfcHBAdata, - ULONG QNdx ); - -static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds ); - -static int verify_PLOGI( PTACHYON fcChip, - TachFCHDR_GCMND* fchs, ULONG* reject_explain); -static int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain); - -static void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type); -static void BuildLinkServicePayload( - PTACHYON fcChip, ULONG type, void* payload); - -static void UnblockScsiDevice( struct Scsi_Host *HostAdapter, - PFC_LOGGEDIN_PORT pLoggedInPort); - -static void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID); - -static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata); - -static void RevalidateSEST( struct Scsi_Host *HostAdapter, - PFC_LOGGEDIN_PORT pLoggedInPort); - -static void IssueReportLunsCommand( - CPQFCHBA* cpqfcHBAdata, - TachFCHDR_GCMND* fchs); - -// (see scsi_error.c comments on kernel task creation) - -void cpqfcTSWorkerThread( void *host) -{ - struct Scsi_Host *HostAdapter = (struct Scsi_Host*)host; - CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; -#ifdef PCI_KERNEL_TRACE - PTACHYON fcChip = &cpqfcHBAdata->fcChip; -#endif - DECLARE_MUTEX_LOCKED(fcQueReady); - DECLARE_MUTEX_LOCKED(fcTYOBcomplete); - DECLARE_MUTEX_LOCKED(TachFrozen); - DECLARE_MUTEX_LOCKED(BoardLock); - - ENTER("WorkerThread"); - - lock_kernel(); - daemonize("cpqfcTS_wt_%d", HostAdapter->host_no); - siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); - - - cpqfcHBAdata->fcQueReady = &fcQueReady; // primary wait point - cpqfcHBAdata->TYOBcomplete = &fcTYOBcomplete; - cpqfcHBAdata->TachFrozen = &TachFrozen; - - - cpqfcHBAdata->worker_thread = current; - - unlock_kernel(); - - if( cpqfcHBAdata->notify_wt != NULL ) - up( cpqfcHBAdata->notify_wt); // OK to continue - - while(1) - { - unsigned long flags; - - down_interruptible( &fcQueReady); // wait for something to do - - if (signal_pending(current) ) - break; - - PCI_TRACE( 0x90) - // first, take the IO lock so the SCSI upper layers can't call - // into our _quecommand function (this also disables INTs) - spin_lock_irqsave( HostAdapter->host_lock, flags); // STOP _que function - PCI_TRACE( 0x90) - - CPQ_SPINLOCK_HBA( cpqfcHBAdata) - // next, set this pointer to indicate to the _quecommand function - // that the board is in use, so it should que the command and - // immediately return (we don't actually require the semaphore function - // in this driver rev) - - cpqfcHBAdata->BoardLock = &BoardLock; - - PCI_TRACE( 0x90) - - // release the IO lock (and re-enable interrupts) - spin_unlock_irqrestore( HostAdapter->host_lock, flags); - - // disable OUR HBA interrupt (keep them off as much as possible - // during error recovery) - disable_irq( cpqfcHBAdata->HostAdapter->irq); - - // OK, let's process the Fibre Channel Link Q and do the work - cpqfcTS_WorkTask( HostAdapter); - - // hopefully, no more "work" to do; - // re-enable our INTs for "normal" completion processing - enable_irq( cpqfcHBAdata->HostAdapter->irq); - - - cpqfcHBAdata->BoardLock = NULL; // allow commands to be queued - CPQ_SPINUNLOCK_HBA( cpqfcHBAdata) - - - // Now, complete any Cmnd we Q'd up while BoardLock was held - - CompleteBoardLockCmnd( cpqfcHBAdata); - - - } - // hopefully, the signal was for our module exit... - if( cpqfcHBAdata->notify_wt != NULL ) - up( cpqfcHBAdata->notify_wt); // yep, we're outta here -} - - -// Freeze Tachyon routine. -// If Tachyon is already frozen, return FALSE -// If Tachyon is not frozen, call freeze function, return TRUE -// -static BOOLEAN FreezeTach( CPQFCHBA *cpqfcHBAdata) -{ - PTACHYON fcChip = &cpqfcHBAdata->fcChip; - BOOLEAN FrozeTach = FALSE; - // It's possible that the chip is already frozen; if so, - // "Freezing" again will NOT! generate another Freeze - // Completion Message. - - if( (fcChip->Registers.TYstatus.value & 0x70000) != 0x70000) - { // (need to freeze...) - fcChip->FreezeTachyon( fcChip, 2); // both ERQ and FCP assists - - // 2. Get Tach freeze confirmation - // (synchronize SEST manipulation with Freeze Completion Message) - // we need INTs on so semaphore can be set. - enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Semaphore - down_interruptible( cpqfcHBAdata->TachFrozen); // wait for INT handler sem. - // can we TIMEOUT semaphore wait?? TBD - disable_irq( cpqfcHBAdata->HostAdapter->irq); - - FrozeTach = TRUE; - } // (else, already frozen) - - return FrozeTach; -} - - - - -// This is the kernel worker thread task, which processes FC -// tasks which were queued by the Interrupt handler or by -// other WorkTask functions. - -#define DBG 1 -//#undef DBG -void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter) -{ - CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; - PTACHYON fcChip = &cpqfcHBAdata->fcChip; - FC_EXCHANGES *Exchanges = fcChip->Exchanges; - ULONG QconsumerNdx; - LONG ExchangeID; - ULONG ulStatus=0; - TachFCHDR_GCMND fchs; - PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ; - - ENTER("WorkTask"); - - // copy current index to work on - QconsumerNdx = fcLQ->consumer; - - PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x90) - - - // NOTE: when this switch completes, we will "consume" the Que item -// printk("Que type %Xh\n", fcLQ->Qitem[QconsumerNdx].Type); - switch( fcLQ->Qitem[QconsumerNdx].Type ) - { - // incoming frame - link service (ACC, UNSOL REQ, etc.) - // or FCP-SCSI command - case SFQ_UNKNOWN: - AnalyzeIncomingFrame( cpqfcHBAdata, QconsumerNdx ); - - break; - - - - case EXCHANGE_QUEUED: // an Exchange (i.e. FCP-SCSI) was previously - // Queued because the link was down. The - // heartbeat timer detected it and Queued it here. - // We attempt to start it again, and if - // successful we clear the EXCHANGE_Q flag. - // If the link doesn't come up, the Exchange - // will eventually time-out. - - ExchangeID = (LONG) // x_ID copied from DPC timeout function - fcLQ->Qitem[QconsumerNdx].ulBuff[0]; - - // It's possible that a Q'd exchange could have already - // been started by other logic (e.g. ABTS process) - // Don't start if already started (Q'd flag clear) - - if( Exchanges->fcExchange[ExchangeID].status & EXCHANGE_QUEUED ) - { -// printk(" *Start Q'd x_ID %Xh: type %Xh ", -// ExchangeID, Exchanges->fcExchange[ExchangeID].type); - - ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID); - if( !ulStatus ) - { -// printk("success* "); - } - else - { -#ifdef DBG - - if( ulStatus == EXCHANGE_QUEUED) - printk("Queued* "); - else - printk("failed* "); - -#endif - } - } - break; - - - case LINKDOWN: - // (lots of things already done in INT handler) future here? - break; - - - case LINKACTIVE: // Tachyon set the Lup bit in FM status - // NOTE: some misbehaving FC ports (like Tach2.1) - // can re-LIP immediately after a LIP completes. - - // if "initiator", need to verify LOGs with ports -// printk("\n*LNKUP* "); - - if( fcChip->Options.initiator ) - SendLogins( cpqfcHBAdata, NULL ); // PLOGI or PDISC, based on fcPort data - // if SendLogins successfully completes, PortDiscDone - // will be set. - - - // If SendLogins was successful, then we expect to get incoming - // ACCepts or REJECTs, which are handled below. - - break; - - // LinkService and Fabric request/reply processing - case ELS_FDISC: // need to send Fabric Discovery (Login) - case ELS_FLOGI: // need to send Fabric Login - case ELS_SCR: // need to send State Change Registration - case FCS_NSR: // need to send Name Service Request - case ELS_PLOGI: // need to send PLOGI - case ELS_ACC: // send generic ACCept - case ELS_PLOGI_ACC: // need to send ELS ACCept frame to recv'd PLOGI - case ELS_PRLI_ACC: // need to send ELS ACCept frame to recv'd PRLI - case ELS_LOGO: // need to send ELS LOGO (logout) - case ELS_LOGO_ACC: // need to send ELS ACCept frame to recv'd PLOGI - case ELS_RJT: // ReJecT reply - case ELS_PRLI: // need to send ELS PRLI - - -// printk(" *ELS %Xh* ", fcLQ->Qitem[QconsumerNdx].Type); - // if PortDiscDone is not set, it means the SendLogins routine - // failed to complete -- assume that LDn occurred, so login frames - // are invalid - if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn - { - printk("Discard Q'd ELS login frame\n"); - break; - } - - ulStatus = cpqfcTSBuildExchange( - cpqfcHBAdata, - fcLQ->Qitem[QconsumerNdx].Type, // e.g. PLOGI - (TachFCHDR_GCMND*) - fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs - NULL, // no data (no scatter/gather list) - &ExchangeID );// fcController->fcExchanges index, -1 if failed - - if( !ulStatus ) // Exchange setup? - { - ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); - if( !ulStatus ) - { - // 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 - { - - } - } - - else // Xchange setup failed... - printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus ); - - break; - - case SCSI_REPORT_LUNS: - // pass the incoming frame (actually, it's a PRLI frame) - // so we can send REPORT_LUNS, in order to determine VSA/PDU - // FCP-SCSI Lun address mode - IssueReportLunsCommand( cpqfcHBAdata, (TachFCHDR_GCMND*) - fcLQ->Qitem[QconsumerNdx].ulBuff); - - break; - - - - - case BLS_ABTS: // need to ABORT one or more exchanges - { - LONG x_ID = fcLQ->Qitem[QconsumerNdx].ulBuff[0]; - BOOLEAN FrozeTach = FALSE; - - if ( x_ID >= TACH_SEST_LEN ) // (in)sanity check - { -// printk( " cpqfcTS ERROR! BOGUS x_ID %Xh", x_ID); - break; - } - - - if( Exchanges->fcExchange[ x_ID].Cmnd == NULL ) // should be RARE - { -// printk(" ABTS %Xh Scsi Cmnd null! ", x_ID); - - break; // nothing to abort! - } - -//#define ABTS_DBG -#ifdef ABTS_DBG - printk("INV SEST[%X] ", x_ID); - if( Exchanges->fcExchange[x_ID].status & FC2_TIMEOUT) - { - printk("FC2TO"); - } - if( Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT) - { - printk("IA"); - } - if( Exchanges->fcExchange[x_ID].status & PORTID_CHANGED) - { - printk("PORTID"); - } - if( Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED) - { - printk("DEVRM"); - } - if( Exchanges->fcExchange[x_ID].status & LINKFAIL_TX) - { - printk("LKF"); - } - if( Exchanges->fcExchange[x_ID].status & FRAME_TO) - { - printk("FRMTO"); - } - if( Exchanges->fcExchange[x_ID].status & ABORTSEQ_NOTIFY) - { - printk("ABSQ"); - } - if( Exchanges->fcExchange[x_ID].status & SFQ_FRAME) - { - printk("SFQFR"); - } - - if( Exchanges->fcExchange[ x_ID].type == 0x2000) - printk(" WR"); - else if( Exchanges->fcExchange[ x_ID].type == 0x3000) - printk(" RD"); - else if( Exchanges->fcExchange[ x_ID].type == 0x10) - printk(" ABTS"); - else - printk(" %Xh", Exchanges->fcExchange[ x_ID].type); - - if( !(Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT)) - { - printk(" Cmd %p, ", - Exchanges->fcExchange[ x_ID].Cmnd); - - printk(" brd/chn/trg/lun %d/%d/%d/%d port_id %06X\n", - cpqfcHBAdata->HBAnum, - Exchanges->fcExchange[ x_ID].Cmnd->channel, - Exchanges->fcExchange[ x_ID].Cmnd->target, - Exchanges->fcExchange[ x_ID].Cmnd->lun, - Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF); - } - else // assume that Cmnd ptr is invalid on _abort() - { - printk(" Cmd ptr invalid\n"); - } - -#endif - - - // Steps to ABORT a SEST exchange: - // 1. Freeze TL SCSI assists & ERQ (everything) - // 2. Receive FROZEN inbound CM (must succeed!) - // 3. Invalidate x_ID SEST entry - // 4. Resume TL SCSI assists & ERQ (everything) - // 5. Build/start on exchange - change "type" to BLS_ABTS, - // timeout to X sec (RA_TOV from PLDA is actually 0) - // 6. Set Exchange Q'd status if ABTS cannot be started, - // or simply complete Exchange in "Terminate" condition - - PCI_TRACEO( x_ID, 0xB4) - - // 1 & 2 . Freeze Tach & get confirmation of freeze - FrozeTach = FreezeTach( cpqfcHBAdata); - - // 3. OK, Tachyon is frozen, so we can invalidate SEST exchange. - // FC2_TIMEOUT means we are originating the abort, while - // TARGET_ABORT means we are ACCepting an abort. - // LINKFAIL_TX, ABORTSEQ_NOFITY, INV_ENTRY or FRAME_TO are - // all from Tachyon: - // Exchange was corrupted by LDn or other FC physical failure - // INITIATOR_ABORT means the upper layer driver/application - // requested the abort. - - - - // clear bit 31 (VALid), to invalidate & take control from TL - fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF; - - - // examine and Tach's "Linked List" for IWEs that - // received (nearly) simultaneous transfer ready (XRDY) - // repair linked list if necessary (TBD!) - // (If we ignore the "Linked List", we will time out - // WRITE commands where we received the FCP-SCSI XFRDY - // frame (because Tachyon didn't processes it). Linked List - // management should be done as an optimization. - -// readl( fcChip->Registers.ReMapMemBase+TL_MEM_SEST_LINKED_LIST )); - - - - - // 4. Resume all Tachlite functions (for other open Exchanges) - // as quickly as possible to allow other exchanges to other ports - // to resume. Freezing Tachyon may cause cascading errors, because - // any received SEST frame cannot be processed by the SEST. - // Don't "unfreeze" unless Link is operational - if( FrozeTach ) // did we just freeze it (above)? - fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists - - - PCI_TRACEO( x_ID, 0xB4) - - // Note there is no confirmation that the chip is "unfrozen". Also, - // if the Link is down when unfreeze is called, it has no effect. - // Chip will unfreeze when the Link is back up. - - // 5. Now send out Abort commands if possible - // Some Aborts can't be "sent" (Port_id changed or gone); - // if the device is gone, there is no port_id to send the ABTS to. - - if( !(Exchanges->fcExchange[ x_ID].status & PORTID_CHANGED) - && - !(Exchanges->fcExchange[ x_ID].status & DEVICE_REMOVED) ) - { - Exchanges->fcExchange[ x_ID].type = BLS_ABTS; - fchs.s_id = Exchanges->fcExchange[ x_ID].fchs.d_id; - ulStatus = cpqfcTSBuildExchange( - cpqfcHBAdata, - BLS_ABTS, - &fchs, // (uses only s_id) - NULL, // (no scatter/gather list for ABTS) - &x_ID );// ABTS on this Exchange ID - - if( !ulStatus ) // Exchange setup build OK? - { - - // ABTS may be needed because an Exchange was corrupted - // by a Link disruption. If the Link is UP, we can - // presume that this ABTS can start immediately; otherwise, - // set Que'd status so the Login functions - // can restart it when the FC physical Link is restored - if( ((fcChip->Registers.FMstatus.value &0xF0) &0x80)) // loop init? - { -// printk(" *set Q status x_ID %Xh on LDn* ", x_ID); - Exchanges->fcExchange[ x_ID].status |= EXCHANGE_QUEUED; - } - - else // what FC device (port_id) does the Cmd belong to? - { - PFC_LOGGEDIN_PORT pLoggedInPort = - Exchanges->fcExchange[ x_ID].pLoggedInPort; - - // if Port is logged in, we might start the abort. - - if( (pLoggedInPort != NULL) - && - (pLoggedInPort->prli == TRUE) ) - { - // it's possible that an Exchange has already been Queued - // to start after Login completes. Check and don't - // start it (again) here if Q'd status set -// printk(" ABTS xchg %Xh ", x_ID); - if( Exchanges->fcExchange[x_ID].status & EXCHANGE_QUEUED) - { -// printk("already Q'd "); - } - else - { -// printk("starting "); - - fcChip->fcStats.FC2aborted++; - ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID ); - if( !ulStatus ) - { - // OK - // submitted to Tach's Outbound Que (ERQ PI incremented) - } - else - { -/* printk("ABTS exchange start failed -status %Xh, x_ID %Xh ", - ulStatus, x_ID); -*/ - } - } - } - else - { -/* printk(" ABTS NOT starting xchg %Xh, %p ", - x_ID, pLoggedInPort); - if( pLoggedInPort ) - printk("prli %d ", pLoggedInPort->prli); -*/ - } - } - } - else // what the #@! - { // how do we fail to build an Exchange for ABTS?? - printk("ABTS exchange build failed -status %Xh, x_ID %Xh\n", - ulStatus, x_ID); - } - } - else // abort without ABTS -- just complete exchange/Cmnd to Linux - { -// printk(" *Terminating x_ID %Xh on %Xh* ", -// x_ID, Exchanges->fcExchange[x_ID].status); - cpqfcTSCompleteExchange( cpqfcHBAdata->PciDev, fcChip, x_ID); - - } - } // end of ABTS case - break; - - - - case BLS_ABTS_ACC: // need to ACCept one ABTS - // (NOTE! this code not updated for Linux yet..) - - - printk(" *ABTS_ACC* "); - // 1. Freeze TL - - fcChip->FreezeTachyon( fcChip, 2); // both ERQ and FCP assists - - memcpy( // copy the incoming ABTS frame - &fchs, - fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs - sizeof( fchs)); - - // 3. OK, Tachyon is frozen so we can invalidate SEST entry - // (if necessary) - // Status FC2_TIMEOUT means we are originating the abort, while - // TARGET_ABORT means we are ACCepting an abort - - ExchangeID = fchs.ox_rx_id & 0x7FFF; // RX_ID for exchange -// printk("ABTS ACC for Target ExchangeID %Xh\n", ExchangeID); - - - // sanity check on received ExchangeID - if( Exchanges->fcExchange[ ExchangeID].status == TARGET_ABORT ) - { - // clear bit 31 (VALid), to invalidate & take control from TL -// printk("Invalidating SEST exchange %Xh\n", ExchangeID); - fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len &= 0x7FFFFFFF; - } - - - // 4. Resume all Tachlite functions (for other open Exchanges) - // as quickly as possible to allow other exchanges to other ports - // to resume. Freezing Tachyon for too long may royally screw - // up everything! - fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists - - // Note there is no confirmation that the chip is "unfrozen". Also, - // if the Link is down when unfreeze is called, it has no effect. - // Chip will unfreeze when the Link is back up. - - // 5. Now send out Abort ACC reply for this exchange - Exchanges->fcExchange[ ExchangeID].type = BLS_ABTS_ACC; - - fchs.s_id = Exchanges->fcExchange[ ExchangeID].fchs.d_id; - ulStatus = cpqfcTSBuildExchange( - cpqfcHBAdata, - BLS_ABTS_ACC, - &fchs, - NULL, // no data (no scatter/gather list) - &ExchangeID );// fcController->fcExchanges index, -1 if failed - - if( !ulStatus ) // Exchange setup? - { - ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); - if( !ulStatus ) - { - // 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 - { - - } - } - break; - - - case BLS_ABTS_RJT: // need to ReJecT one ABTS; reject implies the - // exchange doesn't exist in the TARGET context. - // ExchangeID has to come from LinkService space. - - printk(" *ABTS_RJT* "); - ulStatus = cpqfcTSBuildExchange( - cpqfcHBAdata, - BLS_ABTS_RJT, - (TachFCHDR_GCMND*) - fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs - NULL, // no data (no scatter/gather list) - &ExchangeID );// fcController->fcExchanges index, -1 if failed - - if( !ulStatus ) // Exchange setup OK? - { - ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID ); - // If it fails, we aren't required to retry. - } - if( ulStatus ) - { - printk("Failed to send BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID); - } - else - { - printk("Sent BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID); - - } - - break; - - - - default: - break; - } // end switch -//doNothing: - // done with this item - now set the NEXT index - - if( QconsumerNdx+1 >= FC_LINKQ_DEPTH ) // rollover test - { - fcLQ->consumer = 0; - } - else - { - fcLQ->consumer++; - } - - PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x94) - - LEAVE("WorkTask"); - return; -} - - - - -// When Tachyon reports link down, bad al_pa, or Link Service (e.g. Login) -// commands come in, post to the LinkQ so that action can be taken outside the -// interrupt handler. -// This circular Q works like Tachyon's que - the producer points to the next -// (unused) entry. Called by Interrupt handler, WorkerThread, Timer -// sputlinkq -void cpqfcTSPutLinkQue( CPQFCHBA *cpqfcHBAdata, - int Type, - void *QueContent) -{ - PTACHYON fcChip = &cpqfcHBAdata->fcChip; -// FC_EXCHANGES *Exchanges = fcChip->Exchanges; - PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ; - ULONG ndx; - - ENTER("cpqfcTSPutLinkQ"); - - ndx = fcLQ->producer; - - ndx += 1; // test for Que full - - - - if( ndx >= FC_LINKQ_DEPTH ) // rollover test - ndx = 0; - - if( ndx == fcLQ->consumer ) // QUE full test - { - // QUE was full! lost LK command (fatal to logic) - fcChip->fcStats.lnkQueFull++; - - printk("*LinkQ Full!*"); - TriggerHBA( fcChip->Registers.ReMapMemBase, 1); -/* - { - int i; - printk("LinkQ PI %d, CI %d\n", fcLQ->producer, - fcLQ->consumer); - - for( i=0; i< FC_LINKQ_DEPTH; ) - { - printk(" [%d]%Xh ", i, fcLQ->Qitem[i].Type); - if( (++i %8) == 0) printk("\n"); - } - - } -*/ - printk( "cpqfcTS: WARNING!! PutLinkQue - FULL!\n"); // we're hung - } - else // QUE next element - { - // Prevent certain multiple (back-to-back) requests. - // This is important in that we don't want to issue multiple - // ABTS for the same Exchange, or do multiple FM inits, etc. - // We can never be sure of the timing of events reported to - // us by Tach's IMQ, which can depend on system/bus speeds, - // FC physical link circumstances, etc. - - if( (fcLQ->producer != fcLQ->consumer) - && - (Type == FMINIT) ) - { - LONG lastNdx; // compute previous producer index - if( fcLQ->producer) - lastNdx = fcLQ->producer- 1; - else - lastNdx = FC_LINKQ_DEPTH-1; - - - if( fcLQ->Qitem[lastNdx].Type == FMINIT) - { -// printk(" *skip FMINIT Q post* "); -// goto DoneWithPutQ; - } - - } - - // OK, add the Q'd item... - - fcLQ->Qitem[fcLQ->producer].Type = Type; - - memcpy( - fcLQ->Qitem[fcLQ->producer].ulBuff, - QueContent, - sizeof(fcLQ->Qitem[fcLQ->producer].ulBuff)); - - fcLQ->producer = ndx; // increment Que producer - - // set semaphore to wake up Kernel (worker) thread - // - up( cpqfcHBAdata->fcQueReady ); - } - -//DoneWithPutQ: - - LEAVE("cpqfcTSPutLinkQ"); -} - - - - -// reset device ext FC link Q -void cpqfcTSLinkQReset( CPQFCHBA *cpqfcHBAdata) - -{ - PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ; - fcLQ->producer = 0; - fcLQ->consumer = 0; - -} - - - - - -// When Tachyon gets an unassisted FCP-SCSI frame, post here so -// an arbitrary context thread (e.g. IOCTL loopback test function) -// can process it. - -// (NOTE: Not revised for Linux) -// This Q works like Tachyon's que - the producer points to the next -// (unused) entry. -void cpqfcTSPutScsiQue( CPQFCHBA *cpqfcHBAdata, - int Type, - void *QueContent) -{ -// CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; -// PTACHYON fcChip = &cpqfcHBAdata->fcChip; - -// ULONG ndx; - -// ULONG *pExchangeID; -// LONG ExchangeID; - -/* - KeAcquireSpinLockAtDpcLevel( &pDevExt->fcScsiQueLock); - ndx = pDevExt->fcScsiQue.producer + 1; // test for Que full - - if( ndx >= FC_SCSIQ_DEPTH ) // rollover test - ndx = 0; - - if( ndx == pDevExt->fcScsiQue.consumer ) // QUE full test - { - // QUE was full! lost LK command (fatal to logic) - fcChip->fcStats.ScsiQueFull++; -#ifdef DBG - printk( "fcPutScsiQue - FULL!\n"); -#endif - - } - else // QUE next element - { - pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].Type = Type; - - if( Type == FCP_RSP ) - { - // this TL inbound message type means that a TL SEST exchange has - // copied an FCP response frame into a buffer pointed to by the SEST - // entry. That buffer is allocated in the SEST structure at ->RspHDR. - // Copy the RspHDR for use by the Que handler. - pExchangeID = (ULONG *)QueContent; - - memcpy( - pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff, - &fcChip->SEST->RspHDR[ *pExchangeID ], - sizeof(pDevExt->fcScsiQue.Qitem[0].ulBuff)); // (any element for size) - - } - else - { - memcpy( - pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff, - QueContent, - sizeof(pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff)); - } - - pDevExt->fcScsiQue.producer = ndx; // increment Que - - - KeSetEvent( &pDevExt->TYIBscsi, // signal any waiting thread - 0, // no priority boost - FALSE ); // no waiting later for this event - } - KeReleaseSpinLockFromDpcLevel( &pDevExt->fcScsiQueLock); -*/ -} - - - - - - - -static void ProcessELS_Request( CPQFCHBA*,TachFCHDR_GCMND*); - -static void ProcessELS_Reply( CPQFCHBA*,TachFCHDR_GCMND*); - -static void ProcessFCS_Reply( CPQFCHBA*,TachFCHDR_GCMND*); - -void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata, - PFC_LOGGEDIN_PORT pFcPort) -{ - PTACHYON fcChip = &cpqfcHBAdata->fcChip; - - if( pFcPort->port_id != 0xFFFC01 ) // don't care about Fabric - { - fcChip->fcStats.logouts++; - printk("cpqfcTS: Implicit logout of WWN %08X%08X, port_id %06X\n", - (ULONG)pFcPort->u.liWWN, - (ULONG)(pFcPort->u.liWWN >>32), - pFcPort->port_id); - - // Terminate I/O with this (Linux) Scsi target - cpqfcTSTerminateExchange( cpqfcHBAdata, - &pFcPort->ScsiNexus, - DEVICE_REMOVED); - } - - // Do an "implicit logout" - we can't really Logout the device - // (i.e. with LOGOut Request) because of port_id confusion - // (i.e. the Other port has no port_id). - // A new login for that WWN will have to re-write port_id (0 invalid) - pFcPort->port_id = 0; // invalid! - pFcPort->pdisc = FALSE; - pFcPort->prli = FALSE; - pFcPort->plogi = FALSE; - pFcPort->flogi = FALSE; - pFcPort->LOGO_timer = 0; - pFcPort->device_blocked = TRUE; // block Scsi Requests - pFcPort->ScsiNexus.VolumeSetAddressing=0; -} - - -// On FC-AL, there is a chance that a previously known device can -// be quietly removed (e.g. with non-managed hub), -// while a NEW device (with different WWN) took the same alpa or -// even 24-bit port_id. This chance is unlikely but we must always -// check for it. -static void TestDuplicatePortId( CPQFCHBA* cpqfcHBAdata, - PFC_LOGGEDIN_PORT pLoggedInPort) -{ - PTACHYON fcChip = &cpqfcHBAdata->fcChip; - // set "other port" at beginning of fcPorts list - PFC_LOGGEDIN_PORT pOtherPortWithPortId = fcChip->fcPorts.pNextPort; - while( pOtherPortWithPortId ) - { - if( (pOtherPortWithPortId->port_id == - pLoggedInPort->port_id) - && - (pOtherPortWithPortId != pLoggedInPort) ) - { - // trouble! (Implicitly) Log the other guy out - printk(" *port_id %Xh is duplicated!* ", - pOtherPortWithPortId->port_id); |