lpfc_hbadisc.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 2,453 行 · 第 1/5 页

C
2,453
字号
/******************************************************************* * This file is part of the Emulex Linux Device Driver for         * * Fibre Channel Host Bus Adapters.                                * * Copyright (C) 2004-2006 Emulex.  All rights reserved.           * * EMULEX and SLI are trademarks of Emulex.                        * * www.emulex.com                                                  * * Portions Copyright (C) 2004-2005 Christoph Hellwig              * *                                                                 * * This program is free software; you can redistribute it and/or   * * modify it under the terms of version 2 of the GNU General       * * Public License as published by the Free Software Foundation.    * * This program is distributed in the hope that it will be useful. * * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND          * * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,  * * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE      * * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * * TO BE LEGALLY INVALID.  See the GNU General Public License for  * * more details, a copy of which can be found in the file COPYING  * * included with this package.                                     * *******************************************************************/#include <linux/blkdev.h>#include <linux/pci.h>#include <linux/kthread.h>#include <linux/interrupt.h>#include <scsi/scsi.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_transport_fc.h>#include "lpfc_hw.h"#include "lpfc_disc.h"#include "lpfc_sli.h"#include "lpfc_scsi.h"#include "lpfc.h"#include "lpfc_logmsg.h"#include "lpfc_crtn.h"/* AlpaArray for assignment of scsid for scan-down and bind_method */static uint8_t lpfcAlpaArray[] = {	0xEF, 0xE8, 0xE4, 0xE2, 0xE1, 0xE0, 0xDC, 0xDA, 0xD9, 0xD6,	0xD5, 0xD4, 0xD3, 0xD2, 0xD1, 0xCE, 0xCD, 0xCC, 0xCB, 0xCA,	0xC9, 0xC7, 0xC6, 0xC5, 0xC3, 0xBC, 0xBA, 0xB9, 0xB6, 0xB5,	0xB4, 0xB3, 0xB2, 0xB1, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA9,	0xA7, 0xA6, 0xA5, 0xA3, 0x9F, 0x9E, 0x9D, 0x9B, 0x98, 0x97,	0x90, 0x8F, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7C, 0x7A, 0x79,	0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x6E, 0x6D, 0x6C, 0x6B,	0x6A, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5C, 0x5A, 0x59, 0x56,	0x55, 0x54, 0x53, 0x52, 0x51, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A,	0x49, 0x47, 0x46, 0x45, 0x43, 0x3C, 0x3A, 0x39, 0x36, 0x35,	0x34, 0x33, 0x32, 0x31, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29,	0x27, 0x26, 0x25, 0x23, 0x1F, 0x1E, 0x1D, 0x1B, 0x18, 0x17,	0x10, 0x0F, 0x08, 0x04, 0x02, 0x01};static void lpfc_disc_timeout_handler(struct lpfc_hba *);static voidlpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp){	uint8_t *name = (uint8_t *)&ndlp->nlp_portname;	int warn_on = 0;	spin_lock_irq(phba->host->host_lock);	if (!(ndlp->nlp_flag & NLP_NODEV_TMO)) {		spin_unlock_irq(phba->host->host_lock);		return;	}	/*	 * If a discovery event readded nodev_timer after timer	 * firing and before processing the timer, cancel the	 * nlp_tmofunc.	 */	spin_unlock_irq(phba->host->host_lock);	del_timer_sync(&ndlp->nlp_tmofunc);	spin_lock_irq(phba->host->host_lock);	ndlp->nlp_flag &= ~NLP_NODEV_TMO;	if (ndlp->nlp_sid != NLP_NO_SID) {		warn_on = 1;		/* flush the target */		lpfc_sli_abort_iocb(phba, &phba->sli.ring[phba->sli.fcp_ring],			ndlp->nlp_sid, 0, 0, LPFC_CTX_TGT);	}	spin_unlock_irq(phba->host->host_lock);	if (warn_on) {		lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,				"%d:0203 Nodev timeout on "				"WWPN %x:%x:%x:%x:%x:%x:%x:%x "				"NPort x%x Data: x%x x%x x%x\n",				phba->brd_no,				*name, *(name+1), *(name+2), *(name+3),				*(name+4), *(name+5), *(name+6), *(name+7),				ndlp->nlp_DID, ndlp->nlp_flag,				ndlp->nlp_state, ndlp->nlp_rpi);	} else {		lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,				"%d:0204 Nodev timeout on "				"WWPN %x:%x:%x:%x:%x:%x:%x:%x "				"NPort x%x Data: x%x x%x x%x\n",				phba->brd_no,				*name, *(name+1), *(name+2), *(name+3),				*(name+4), *(name+5), *(name+6), *(name+7),				ndlp->nlp_DID, ndlp->nlp_flag,				ndlp->nlp_state, ndlp->nlp_rpi);	}	lpfc_disc_state_machine(phba, ndlp, NULL, NLP_EVT_DEVICE_RM);	return;}static voidlpfc_work_list_done(struct lpfc_hba * phba){	struct lpfc_work_evt  *evtp = NULL;	struct lpfc_nodelist  *ndlp;	int free_evt;	spin_lock_irq(phba->host->host_lock);	while(!list_empty(&phba->work_list)) {		list_remove_head((&phba->work_list), evtp, typeof(*evtp),				 evt_listp);		spin_unlock_irq(phba->host->host_lock);		free_evt = 1;		switch (evtp->evt) {		case LPFC_EVT_NODEV_TMO:			ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1);			lpfc_process_nodev_timeout(phba, ndlp);			free_evt = 0;			break;		case LPFC_EVT_ELS_RETRY:			ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1);			lpfc_els_retry_delay_handler(ndlp);			free_evt = 0;			break;		case LPFC_EVT_ONLINE:			if (phba->hba_state < LPFC_LINK_DOWN)				*(int *)(evtp->evt_arg1)  = lpfc_online(phba);			else				*(int *)(evtp->evt_arg1)  = 0;			complete((struct completion *)(evtp->evt_arg2));			break;		case LPFC_EVT_OFFLINE:			if (phba->hba_state >= LPFC_LINK_DOWN)				lpfc_offline(phba);			lpfc_sli_brdrestart(phba);			*(int *)(evtp->evt_arg1) =				lpfc_sli_brdready(phba,HS_FFRDY | HS_MBRDY);			complete((struct completion *)(evtp->evt_arg2));			break;		case LPFC_EVT_WARM_START:			if (phba->hba_state >= LPFC_LINK_DOWN)				lpfc_offline(phba);			lpfc_reset_barrier(phba);			lpfc_sli_brdreset(phba);			lpfc_hba_down_post(phba);			*(int *)(evtp->evt_arg1) =				lpfc_sli_brdready(phba, HS_MBRDY);			complete((struct completion *)(evtp->evt_arg2));			break;		case LPFC_EVT_KILL:			if (phba->hba_state >= LPFC_LINK_DOWN)				lpfc_offline(phba);			*(int *)(evtp->evt_arg1)				= (phba->stopped) ? 0 : lpfc_sli_brdkill(phba);			complete((struct completion *)(evtp->evt_arg2));			break;		}		if (free_evt)			kfree(evtp);		spin_lock_irq(phba->host->host_lock);	}	spin_unlock_irq(phba->host->host_lock);}static voidlpfc_work_done(struct lpfc_hba * phba){	struct lpfc_sli_ring *pring;	int i;	uint32_t ha_copy;	uint32_t control;	uint32_t work_hba_events;	spin_lock_irq(phba->host->host_lock);	ha_copy = phba->work_ha;	phba->work_ha = 0;	work_hba_events=phba->work_hba_events;	spin_unlock_irq(phba->host->host_lock);	if (ha_copy & HA_ERATT)		lpfc_handle_eratt(phba);	if (ha_copy & HA_MBATT)		lpfc_sli_handle_mb_event(phba);	if (ha_copy & HA_LATT)		lpfc_handle_latt(phba);	if (work_hba_events & WORKER_DISC_TMO)		lpfc_disc_timeout_handler(phba);	if (work_hba_events & WORKER_ELS_TMO)		lpfc_els_timeout_handler(phba);	if (work_hba_events & WORKER_MBOX_TMO)		lpfc_mbox_timeout_handler(phba);	if (work_hba_events & WORKER_FDMI_TMO)		lpfc_fdmi_tmo_handler(phba);	spin_lock_irq(phba->host->host_lock);	phba->work_hba_events &= ~work_hba_events;	spin_unlock_irq(phba->host->host_lock);	for (i = 0; i < phba->sli.num_rings; i++, ha_copy >>= 4) {		pring = &phba->sli.ring[i];		if ((ha_copy & HA_RXATT)		    || (pring->flag & LPFC_DEFERRED_RING_EVENT)) {			if (pring->flag & LPFC_STOP_IOCB_MASK) {				pring->flag |= LPFC_DEFERRED_RING_EVENT;			} else {				lpfc_sli_handle_slow_ring_event(phba, pring,								(ha_copy &								 HA_RXMASK));				pring->flag &= ~LPFC_DEFERRED_RING_EVENT;			}			/*			 * Turn on Ring interrupts			 */			spin_lock_irq(phba->host->host_lock);			control = readl(phba->HCregaddr);			control |= (HC_R0INT_ENA << i);			writel(control, phba->HCregaddr);			readl(phba->HCregaddr); /* flush */			spin_unlock_irq(phba->host->host_lock);		}	}	lpfc_work_list_done (phba);}static intcheck_work_wait_done(struct lpfc_hba *phba) {	spin_lock_irq(phba->host->host_lock);	if (phba->work_ha ||	    phba->work_hba_events ||	    (!list_empty(&phba->work_list)) ||	    kthread_should_stop()) {		spin_unlock_irq(phba->host->host_lock);		return 1;	} else {		spin_unlock_irq(phba->host->host_lock);		return 0;	}}intlpfc_do_work(void *p){	struct lpfc_hba *phba = p;	int rc;	DECLARE_WAIT_QUEUE_HEAD(work_waitq);	set_user_nice(current, -20);	phba->work_wait = &work_waitq;	while (1) {		rc = wait_event_interruptible(work_waitq,						check_work_wait_done(phba));		BUG_ON(rc);		if (kthread_should_stop())			break;		lpfc_work_done(phba);	}	phba->work_wait = NULL;	return 0;}/* * This is only called to handle FC worker events. Since this a rare * occurance, we allocate a struct lpfc_work_evt structure here instead of * embedding it in the IOCB. */intlpfc_workq_post_event(struct lpfc_hba * phba, void *arg1, void *arg2,		      uint32_t evt){	struct lpfc_work_evt  *evtp;	/*	 * All Mailbox completions and LPFC_ELS_RING rcv ring IOCB events will	 * be queued to worker thread for processing	 */	evtp = kmalloc(sizeof(struct lpfc_work_evt), GFP_KERNEL);	if (!evtp)		return 0;	evtp->evt_arg1  = arg1;	evtp->evt_arg2  = arg2;	evtp->evt       = evt;	spin_lock_irq(phba->host->host_lock);	list_add_tail(&evtp->evt_listp, &phba->work_list);	if (phba->work_wait)		wake_up(phba->work_wait);	spin_unlock_irq(phba->host->host_lock);	return 1;}intlpfc_linkdown(struct lpfc_hba * phba){	struct lpfc_sli       *psli;	struct lpfc_nodelist  *ndlp, *next_ndlp;	struct list_head *listp, *node_list[7];	LPFC_MBOXQ_t     *mb;	int               rc, i;	psli = &phba->sli;	/* sysfs or selective reset may call this routine to clean up */	if (phba->hba_state >= LPFC_LINK_DOWN) {		if (phba->hba_state == LPFC_LINK_DOWN)			return 0;		spin_lock_irq(phba->host->host_lock);		phba->hba_state = LPFC_LINK_DOWN;		spin_unlock_irq(phba->host->host_lock);	}	/* Clean up any firmware default rpi's */	if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))) {		lpfc_unreg_did(phba, 0xffffffff, mb);		mb->mbox_cmpl=lpfc_sli_def_mbox_cmpl;		if (lpfc_sli_issue_mbox(phba, mb, (MBX_NOWAIT | MBX_STOP_IOCB))		    == MBX_NOT_FINISHED) {			mempool_free( mb, phba->mbox_mem_pool);		}	}	/* Cleanup any outstanding RSCN activity */	lpfc_els_flush_rscn(phba);	/* Cleanup any outstanding ELS commands */	lpfc_els_flush_cmd(phba);	/* Issue a LINK DOWN event to all nodes */	node_list[0] = &phba->fc_npr_list;  /* MUST do this list first */	node_list[1] = &phba->fc_nlpmap_list;	node_list[2] = &phba->fc_nlpunmap_list;	node_list[3] = &phba->fc_prli_list;	node_list[4] = &phba->fc_reglogin_list;	node_list[5] = &phba->fc_adisc_list;	node_list[6] = &phba->fc_plogi_list;	for (i = 0; i < 7; i++) {		listp = node_list[i];		if (list_empty(listp))			continue;		list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) {			rc = lpfc_disc_state_machine(phba, ndlp, NULL,					     NLP_EVT_DEVICE_RECOVERY);			/* Check config parameter use-adisc or FCP-2 */			if ((rc != NLP_STE_FREED_NODE) &&				(phba->cfg_use_adisc == 0) &&				!(ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE)) {				/* We know we will have to relogin, so				 * unreglogin the rpi right now to fail				 * any outstanding I/Os quickly.				 */				lpfc_unreg_rpi(phba, ndlp);			}		}	}	/* free any ndlp's on unused list */	list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list,				nlp_listp) {		lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);	}	/* Setup myDID for link up if we are in pt2pt mode */	if (phba->fc_flag & FC_PT2PT) {		phba->fc_myDID = 0;		if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))) {			lpfc_config_link(phba, mb);			mb->mbox_cmpl=lpfc_sli_def_mbox_cmpl;			if (lpfc_sli_issue_mbox			    (phba, mb, (MBX_NOWAIT | MBX_STOP_IOCB))			    == MBX_NOT_FINISHED) {				mempool_free( mb, phba->mbox_mem_pool);			}		}		spin_lock_irq(phba->host->host_lock);		phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI);		spin_unlock_irq(phba->host->host_lock);	}	spin_lock_irq(phba->host->host_lock);	phba->fc_flag &= ~FC_LBIT;	spin_unlock_irq(phba->host->host_lock);	/* Turn off discovery timer if its running */	lpfc_can_disctmo(phba);	/* Must process IOCBs on all rings to handle ABORTed I/Os */	return 0;}static intlpfc_linkup(struct lpfc_hba * phba){	struct lpfc_nodelist *ndlp, *next_ndlp;	struct list_head *listp, *node_list[7];	int i;	spin_lock_irq(phba->host->host_lock);	phba->hba_state = LPFC_LINK_UP;	phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI | FC_ABORT_DISCOVERY |			   FC_RSCN_MODE | FC_NLP_MORE | FC_RSCN_DISCOVERY);	phba->fc_flag |= FC_NDISC_ACTIVE;	phba->fc_ns_retry = 0;	spin_unlock_irq(phba->host->host_lock);	node_list[0] = &phba->fc_plogi_list;	node_list[1] = &phba->fc_adisc_list;	node_list[2] = &phba->fc_reglogin_list;	node_list[3] = &phba->fc_prli_list;	node_list[4] = &phba->fc_nlpunmap_list;	node_list[5] = &phba->fc_nlpmap_list;	node_list[6] = &phba->fc_npr_list;	for (i = 0; i < 7; i++) {		listp = node_list[i];		if (list_empty(listp))			continue;		list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) {			if (phba->fc_flag & FC_LBIT) {				if (ndlp->nlp_type & NLP_FABRIC) {					/* On Linkup its safe to clean up the					 * ndlp from Fabric connections.					 */					lpfc_nlp_list(phba, ndlp,							NLP_UNUSED_LIST);				} else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) {					/* Fail outstanding IO now since device					 * is marked for PLOGI.					 */					lpfc_unreg_rpi(phba, ndlp);				}			}		}	}	/* free any ndlp's on unused list */	list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list,				nlp_listp) {		lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);	}	return 0;}/* * This routine handles processing a CLEAR_LA mailbox * command upon completion. It is setup in the LPFC_MBOXQ * as the completion routine when the command is * handed off to the SLI layer. */voidlpfc_mbx_cmpl_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb){	struct lpfc_sli *psli;	MAILBOX_t *mb;	uint32_t control;	psli = &phba->sli;

⌨️ 快捷键说明

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