⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cpqfctsworker.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* 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/malloc.h>#include <linux/ioport.h>#include <linux/kernel.h>#include <linux/stat.h>#include <linux/blk.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/smp_lock.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 "sd.h"#include "hosts.h"   // struct Scsi_Host definition for T handler#include "cpqfcTSchip.h"#include "cpqfcTSstructs.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 accomodate 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 functionsstatic 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  struct fs_struct *fs;  DECLARE_MUTEX_LOCKED(fcQueReady);  DECLARE_MUTEX_LOCKED(fcTYOBcomplete);   DECLARE_MUTEX_LOCKED(TachFrozen);    DECLARE_MUTEX_LOCKED(BoardLock);    ENTER("WorkerThread");  lock_kernel();	/*	 * If we were started as result of loading a module, close all of the	 * user space pages.  We don't need them, and if we didn't close them	 * they would be locked into memory.	 */  exit_mm(current);  current->session = 1;  current->pgrp = 1;	  /* Become as one with the init task */	  exit_fs(current);	/* current->fs->count--; */  fs = init_task.fs;  // Some kernels compiled for SMP, while actually running  // on a uniproc machine, will return NULL for this call  if( !fs)  {    printk(" cpqfcTS FATAL: fs is NULL! Is this an SMP kernel on uniproc machine?\n ");  }   else  {     current->fs = fs;    atomic_inc(&fs->count);  }  siginitsetinv(&current->blocked, SHUTDOWN_SIGS);  /*   * Set the name of this process.   */  sprintf(current->comm, "cpqfcTS_wt_%d", HostAdapter->host_no);  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( &io_request_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( &io_request_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 DBGvoid 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 occured, so login frames      // are invalid

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -