lpfc_hbadisc.c

来自「linux 内核源代码」· C语言 代码 · 共 2,407 行 · 第 1/5 页

C
2,407
字号
/******************************************************************* * This file is part of the Emulex Linux Device Driver for         * * Fibre Channel Host Bus Adapters.                                * * Copyright (C) 2004-2007 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"#include "lpfc_vport.h"#include "lpfc_debugfs.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_vport *);voidlpfc_terminate_rport_io(struct fc_rport *rport){	struct lpfc_rport_data *rdata;	struct lpfc_nodelist * ndlp;	struct lpfc_hba *phba;	rdata = rport->dd_data;	ndlp = rdata->pnode;	if (!ndlp) {		if (rport->roles & FC_RPORT_ROLE_FCP_TARGET)			printk(KERN_ERR "Cannot find remote node"			" to terminate I/O Data x%x\n",			rport->port_id);		return;	}	phba  = ndlp->vport->phba;	lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_RPORT,		"rport terminate: sid:x%x did:x%x flg:x%x",		ndlp->nlp_sid, ndlp->nlp_DID, ndlp->nlp_flag);	if (ndlp->nlp_sid != NLP_NO_SID) {		lpfc_sli_abort_iocb(ndlp->vport,			&phba->sli.ring[phba->sli.fcp_ring],			ndlp->nlp_sid, 0, LPFC_CTX_TGT);	}	/*	 * A device is normally blocked for rediscovery and unblocked when	 * devloss timeout happens.  In case a vport is removed or driver	 * unloaded before devloss timeout happens, we need to unblock here.	 */	scsi_target_unblock(&rport->dev);	return;}/* * This function will be called when dev_loss_tmo fire. */voidlpfc_dev_loss_tmo_callbk(struct fc_rport *rport){	struct lpfc_rport_data *rdata;	struct lpfc_nodelist * ndlp;	struct lpfc_vport *vport;	struct lpfc_hba   *phba;	struct completion devloss_compl;	struct lpfc_work_evt *evtp;	rdata = rport->dd_data;	ndlp = rdata->pnode;	if (!ndlp) {		if (rport->scsi_target_id != -1) {			printk(KERN_ERR "Cannot find remote node"				" for rport in dev_loss_tmo_callbk x%x\n",				rport->port_id);		}		return;	}	vport = ndlp->vport;	phba  = vport->phba;	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,		"rport devlosscb: sid:x%x did:x%x flg:x%x",		ndlp->nlp_sid, ndlp->nlp_DID, ndlp->nlp_flag);	init_completion(&devloss_compl);	evtp = &ndlp->dev_loss_evt;	if (!list_empty(&evtp->evt_listp))		return;	spin_lock_irq(&phba->hbalock);	evtp->evt_arg1  = ndlp;	evtp->evt_arg2  = &devloss_compl;	evtp->evt       = LPFC_EVT_DEV_LOSS;	list_add_tail(&evtp->evt_listp, &phba->work_list);	if (phba->work_wait)		wake_up(phba->work_wait);	spin_unlock_irq(&phba->hbalock);	wait_for_completion(&devloss_compl);	return;}/* * This function is called from the worker thread when dev_loss_tmo * expire. */voidlpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp){	struct lpfc_rport_data *rdata;	struct fc_rport   *rport;	struct lpfc_vport *vport;	struct lpfc_hba   *phba;	uint8_t *name;	int warn_on = 0;	rport = ndlp->rport;	if (!rport)		return;	rdata = rport->dd_data;	name = (uint8_t *) &ndlp->nlp_portname;	vport = ndlp->vport;	phba  = vport->phba;	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,		"rport devlosstmo:did:x%x type:x%x id:x%x",		ndlp->nlp_DID, ndlp->nlp_type, rport->scsi_target_id);	if (!(vport->load_flag & FC_UNLOADING) &&	    ndlp->nlp_state == NLP_STE_MAPPED_NODE)		return;	if (ndlp->nlp_type & NLP_FABRIC) {		int  put_node;		int  put_rport;		/* We will clean up these Nodes in linkup */		put_node = rdata->pnode != NULL;		put_rport = ndlp->rport != NULL;		rdata->pnode = NULL;		ndlp->rport = NULL;		if (put_node)			lpfc_nlp_put(ndlp);		if (put_rport)			put_device(&rport->dev);		return;	}	if (ndlp->nlp_sid != NLP_NO_SID) {		warn_on = 1;		/* flush the target */		lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],				    ndlp->nlp_sid, 0, LPFC_CTX_TGT);	}	if (vport->load_flag & FC_UNLOADING)		warn_on = 0;	if (warn_on) {		lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,				 "0203 Devloss timeout on "				 "WWPN %x:%x:%x:%x:%x:%x:%x:%x "				 "NPort x%x Data: x%x x%x x%x\n",				 *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_vlog(vport, KERN_INFO, LOG_DISCOVERY,				 "0204 Devloss timeout on "				 "WWPN %x:%x:%x:%x:%x:%x:%x:%x "				 "NPort x%x Data: x%x x%x x%x\n",				 *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);	}	if (!(vport->load_flag & FC_UNLOADING) &&	    !(ndlp->nlp_flag & NLP_DELAY_TMO) &&	    !(ndlp->nlp_flag & NLP_NPR_2B_DISC) &&	    (ndlp->nlp_state != NLP_STE_UNMAPPED_NODE))		lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);	else {		int  put_node;		int  put_rport;		put_node = rdata->pnode != NULL;		put_rport = ndlp->rport != NULL;		rdata->pnode = NULL;		ndlp->rport = NULL;		if (put_node)			lpfc_nlp_put(ndlp);		if (put_rport)			put_device(&rport->dev);	}}voidlpfc_worker_wake_up(struct lpfc_hba *phba){	wake_up(phba->work_wait);	return;}static voidlpfc_work_list_done(struct lpfc_hba *phba){	struct lpfc_work_evt  *evtp = NULL;	struct lpfc_nodelist  *ndlp;	struct lpfc_vport     *vport;	int free_evt;	spin_lock_irq(&phba->hbalock);	while (!list_empty(&phba->work_list)) {		list_remove_head((&phba->work_list), evtp, typeof(*evtp),				 evt_listp);		spin_unlock_irq(&phba->hbalock);		free_evt = 1;		switch (evtp->evt) {		case LPFC_EVT_DEV_LOSS_DELAY:			free_evt = 0; /* evt is part of ndlp */			ndlp = (struct lpfc_nodelist *) (evtp->evt_arg1);			vport = ndlp->vport;			if (!vport)				break;			lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,				"rport devlossdly:did:x%x flg:x%x",				ndlp->nlp_DID, ndlp->nlp_flag, 0);			if (!(vport->load_flag & FC_UNLOADING) &&			    !(ndlp->nlp_flag & NLP_DELAY_TMO) &&			    !(ndlp->nlp_flag & NLP_NPR_2B_DISC)) {				lpfc_disc_state_machine(vport, ndlp, NULL,					NLP_EVT_DEVICE_RM);			}			break;		case LPFC_EVT_ELS_RETRY:			ndlp = (struct lpfc_nodelist *) (evtp->evt_arg1);			lpfc_els_retry_delay_handler(ndlp);			free_evt = 0; /* evt is part of ndlp */			break;		case LPFC_EVT_DEV_LOSS:			ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1);			lpfc_nlp_get(ndlp);			lpfc_dev_loss_tmo_handler(ndlp);			free_evt = 0;			complete((struct completion *)(evtp->evt_arg2));			lpfc_nlp_put(ndlp);			break;		case LPFC_EVT_ONLINE:			if (phba->link_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_PREP:			if (phba->link_state >= LPFC_LINK_DOWN)				lpfc_offline_prep(phba);			*(int *)(evtp->evt_arg1) = 0;			complete((struct completion *)(evtp->evt_arg2));			break;		case LPFC_EVT_OFFLINE:			lpfc_offline(phba);			lpfc_sli_brdrestart(phba);			*(int *)(evtp->evt_arg1) =				lpfc_sli_brdready(phba, HS_FFRDY | HS_MBRDY);			lpfc_unblock_mgmt_io(phba);			complete((struct completion *)(evtp->evt_arg2));			break;		case LPFC_EVT_WARM_START:			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);			lpfc_unblock_mgmt_io(phba);			complete((struct completion *)(evtp->evt_arg2));			break;		case LPFC_EVT_KILL:			lpfc_offline(phba);			*(int *)(evtp->evt_arg1)				= (phba->pport->stopped)				        ? 0 : lpfc_sli_brdkill(phba);			lpfc_unblock_mgmt_io(phba);			complete((struct completion *)(evtp->evt_arg2));			break;		}		if (free_evt)			kfree(evtp);		spin_lock_irq(&phba->hbalock);	}	spin_unlock_irq(&phba->hbalock);}static voidlpfc_work_done(struct lpfc_hba *phba){	struct lpfc_sli_ring *pring;	uint32_t ha_copy, status, control, work_port_events;	struct lpfc_vport **vports;	struct lpfc_vport *vport;	int i;	spin_lock_irq(&phba->hbalock);	ha_copy = phba->work_ha;	phba->work_ha = 0;	spin_unlock_irq(&phba->hbalock);	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);	vports = lpfc_create_vport_work_array(phba);	if (vports != NULL)		for(i = 0; i < LPFC_MAX_VPORTS; i++) {			/*			 * We could have no vports in array if unloading, so if			 * this happens then just use the pport			 */			if (vports[i] == NULL && i == 0)				vport = phba->pport;			else				vport = vports[i];			if (vport == NULL)				break;			work_port_events = vport->work_port_events;			if (work_port_events & WORKER_DISC_TMO)				lpfc_disc_timeout_handler(vport);			if (work_port_events & WORKER_ELS_TMO)				lpfc_els_timeout_handler(vport);			if (work_port_events & WORKER_HB_TMO)				lpfc_hb_timeout_handler(phba);			if (work_port_events & WORKER_MBOX_TMO)				lpfc_mbox_timeout_handler(phba);			if (work_port_events & WORKER_FABRIC_BLOCK_TMO)				lpfc_unblock_fabric_iocbs(phba);			if (work_port_events & WORKER_FDMI_TMO)				lpfc_fdmi_timeout_handler(vport);			if (work_port_events & WORKER_RAMP_DOWN_QUEUE)				lpfc_ramp_down_queue_handler(phba);			if (work_port_events & WORKER_RAMP_UP_QUEUE)				lpfc_ramp_up_queue_handler(phba);			spin_lock_irq(&vport->work_port_lock);			vport->work_port_events &= ~work_port_events;			spin_unlock_irq(&vport->work_port_lock);		}	lpfc_destroy_vport_work_array(vports);	pring = &phba->sli.ring[LPFC_ELS_RING];	status = (ha_copy & (HA_RXMASK  << (4*LPFC_ELS_RING)));	status >>= (4*LPFC_ELS_RING);	if ((status & HA_RXMASK)		|| (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,							(status &							 HA_RXMASK));			pring->flag &= ~LPFC_DEFERRED_RING_EVENT;		}		/*		 * Turn on Ring interrupts		 */		spin_lock_irq(&phba->hbalock);		control = readl(phba->HCregaddr);		if (!(control & (HC_R0INT_ENA << LPFC_ELS_RING))) {			lpfc_debugfs_slow_ring_trc(phba,				"WRK Enable ring: cntl:x%x hacopy:x%x",				control, ha_copy, 0);			control |= (HC_R0INT_ENA << LPFC_ELS_RING);			writel(control, phba->HCregaddr);			readl(phba->HCregaddr); /* flush */		}		else {			lpfc_debugfs_slow_ring_trc(phba,				"WRK Ring ok:     cntl:x%x hacopy:x%x",				control, ha_copy, 0);		}		spin_unlock_irq(&phba->hbalock);	}	lpfc_work_list_done(phba);}static intcheck_work_wait_done(struct lpfc_hba *phba){	struct lpfc_vport *vport;	struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];	int rc = 0;	spin_lock_irq(&phba->hbalock);	list_for_each_entry(vport, &phba->port_list, listentry) {		if (vport->work_port_events) {			rc = 1;			break;		}	}	if (rc || phba->work_ha || (!list_empty(&phba->work_list)) ||	    kthread_should_stop() || pring->flag & LPFC_DEFERRED_RING_EVENT) {		rc = 1;		phba->work_found++;	} else		phba->work_found = 0;	spin_unlock_irq(&phba->hbalock);	return rc;}intlpfc_do_work(void *p){	struct lpfc_hba *phba = p;	int rc;	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(work_waitq);	set_user_nice(current, -20);	phba->work_wait = &work_waitq;	phba->work_found = 0;

⌨️ 快捷键说明

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