📄 lpfc_hbadisc.c
字号:
/******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2004-2005 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){ 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; } 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 NPort x%x " "Data: x%x x%x x%x\n", phba->brd_no, 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 NPort x%x " "Data: x%x x%x x%x\n", phba->brd_no, 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: *(int *)(evtp->evt_arg1) = lpfc_online(phba); complete((struct completion *)(evtp->evt_arg2)); break; case LPFC_EVT_OFFLINE: *(int *)(evtp->evt_arg1) = lpfc_offline(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; list_add_tail(&evtp->evt_listp, &phba->work_list); spin_lock_irq(phba->host->host_lock); 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; struct list_head *node_list[7]; LPFC_MBOXQ_t *mb; int rc, i; psli = &phba->sli; 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) { /* Fabric nodes are not handled thru state machine for link down */ if (ndlp->nlp_type & NLP_FABRIC) { /* Remove ALL Fabric nodes except Fabric_DID */ if (ndlp->nlp_DID != Fabric_DID) { /* Take it off current list and free */ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); } } else { 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; 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); /* * Clean up old Fabric NLP_FABRIC logins. */ list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpunmap_list, nlp_listp) { if (ndlp->nlp_DID == Fabric_DID) { /* Take it off current list and free */ lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); } } /* 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; mb = &pmb->mb; /* Since we don't do discovery right now, turn these off here */ psli->ring[psli->ip_ring].flag &= ~LPFC_STOP_IOCB_EVENT; psli->ring[psli->fcp_ring].flag &= ~LPFC_STOP_IOCB_EVENT; psli->ring[psli->next_ring].flag &= ~LPFC_STOP_IOCB_EVENT; /* Check for error */ if ((mb->mbxStatus) && (mb->mbxStatus != 0x1601)) { /* CLEAR_LA mbox error <mbxStatus> state <hba_state> */ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, "%d:0320 CLEAR_LA mbxStatus error x%x hba " "state x%x\n", phba->brd_no, mb->mbxStatus, phba->hba_state); phba->hba_state = LPFC_HBA_ERROR; goto out; } if (phba->fc_flag & FC_ABORT_DISCOVERY) goto out; phba->num_disc_nodes = 0; /* go thru NPR list and issue ELS PLOGIs */ if (phba->fc_npr_cnt) { lpfc_els_disc_plogi(phba); } if(!phba->num_disc_nodes) { spin_lock_irq(phba->host->host_lock); phba->fc_flag &= ~FC_NDISC_ACTIVE; spin_unlock_irq(phba->host->host_lock); } phba->hba_state = LPFC_HBA_READY;out: /* Device Discovery completes */ lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, "%d:0225 Device Discovery completes\n", phba->brd_no); mempool_free( pmb, phba->mbox_mem_pool); spin_lock_irq(phba->host->host_lock); phba->fc_flag &= ~FC_ABORT_DISCOVERY; if (phba->fc_flag & FC_ESTABLISH_LINK) { phba->fc_flag &= ~FC_ESTABLISH_LINK; } spin_unlock_irq(phba->host->host_lock); del_timer_sync(&phba->fc_estabtmo); lpfc_can_disctmo(phba); /* turn on Link Attention interrupts */ spin_lock_irq(phba->host->host_lock);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -