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 + -
显示快捷键?