/drivers/scsi/cpqfcTSworker.c
C | 6520 lines | 3618 code | 1329 blank | 1573 comment | 654 complexity | fb40d0ad9b6df81c59f2f69f6b870311 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, GPL-2.0, LGPL-2.0, AGPL-1.0
Large files files are truncated, but you can click here to view the full file
- /* 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 __KERNEL_SYSCALLS__
- #define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))
- #include <linux/unistd.h>
- #include <asm/system.h>
- #include <asm/irq.h>
- #include <asm/dma.h>
- #include "scsi.h"
- #include "hosts.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;
-