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