diff options
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_init.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_init.c | 154 |
1 files changed, 132 insertions, 22 deletions
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 6cfeba7454d4..22843751c2ca 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /******************************************************************* | 1 | /******************************************************************* |
2 | * This file is part of the Emulex Linux Device Driver for * | 2 | * This file is part of the Emulex Linux Device Driver for * |
3 | * Fibre Channel Host Bus Adapters. * | 3 | * Fibre Channel Host Bus Adapters. * |
4 | * Copyright (C) 2004-2007 Emulex. All rights reserved. * | 4 | * Copyright (C) 2004-2008 Emulex. All rights reserved. * |
5 | * EMULEX and SLI are trademarks of Emulex. * | 5 | * EMULEX and SLI are trademarks of Emulex. * |
6 | * www.emulex.com * | 6 | * www.emulex.com * |
7 | * Portions Copyright (C) 2004-2005 Christoph Hellwig * | 7 | * Portions Copyright (C) 2004-2005 Christoph Hellwig * |
@@ -461,11 +461,21 @@ lpfc_config_port_post(struct lpfc_hba *phba) | |||
461 | int | 461 | int |
462 | lpfc_hba_down_prep(struct lpfc_hba *phba) | 462 | lpfc_hba_down_prep(struct lpfc_hba *phba) |
463 | { | 463 | { |
464 | struct lpfc_vport **vports; | ||
465 | int i; | ||
464 | /* Disable interrupts */ | 466 | /* Disable interrupts */ |
465 | writel(0, phba->HCregaddr); | 467 | writel(0, phba->HCregaddr); |
466 | readl(phba->HCregaddr); /* flush */ | 468 | readl(phba->HCregaddr); /* flush */ |
467 | 469 | ||
468 | lpfc_cleanup_discovery_resources(phba->pport); | 470 | if (phba->pport->load_flag & FC_UNLOADING) |
471 | lpfc_cleanup_discovery_resources(phba->pport); | ||
472 | else { | ||
473 | vports = lpfc_create_vport_work_array(phba); | ||
474 | if (vports != NULL) | ||
475 | for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) | ||
476 | lpfc_cleanup_discovery_resources(vports[i]); | ||
477 | lpfc_destroy_vport_work_array(phba, vports); | ||
478 | } | ||
469 | return 0; | 479 | return 0; |
470 | } | 480 | } |
471 | 481 | ||
@@ -1422,9 +1432,32 @@ lpfc_cleanup(struct lpfc_vport *vport) | |||
1422 | lpfc_port_link_failure(vport); | 1432 | lpfc_port_link_failure(vport); |
1423 | 1433 | ||
1424 | list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { | 1434 | list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { |
1435 | if (!NLP_CHK_NODE_ACT(ndlp)) { | ||
1436 | ndlp = lpfc_enable_node(vport, ndlp, | ||
1437 | NLP_STE_UNUSED_NODE); | ||
1438 | if (!ndlp) | ||
1439 | continue; | ||
1440 | spin_lock_irq(&phba->ndlp_lock); | ||
1441 | NLP_SET_FREE_REQ(ndlp); | ||
1442 | spin_unlock_irq(&phba->ndlp_lock); | ||
1443 | /* Trigger the release of the ndlp memory */ | ||
1444 | lpfc_nlp_put(ndlp); | ||
1445 | continue; | ||
1446 | } | ||
1447 | spin_lock_irq(&phba->ndlp_lock); | ||
1448 | if (NLP_CHK_FREE_REQ(ndlp)) { | ||
1449 | /* The ndlp should not be in memory free mode already */ | ||
1450 | spin_unlock_irq(&phba->ndlp_lock); | ||
1451 | continue; | ||
1452 | } else | ||
1453 | /* Indicate request for freeing ndlp memory */ | ||
1454 | NLP_SET_FREE_REQ(ndlp); | ||
1455 | spin_unlock_irq(&phba->ndlp_lock); | ||
1456 | |||
1425 | if (ndlp->nlp_type & NLP_FABRIC) | 1457 | if (ndlp->nlp_type & NLP_FABRIC) |
1426 | lpfc_disc_state_machine(vport, ndlp, NULL, | 1458 | lpfc_disc_state_machine(vport, ndlp, NULL, |
1427 | NLP_EVT_DEVICE_RECOVERY); | 1459 | NLP_EVT_DEVICE_RECOVERY); |
1460 | |||
1428 | lpfc_disc_state_machine(vport, ndlp, NULL, | 1461 | lpfc_disc_state_machine(vport, ndlp, NULL, |
1429 | NLP_EVT_DEVICE_RM); | 1462 | NLP_EVT_DEVICE_RM); |
1430 | } | 1463 | } |
@@ -1438,6 +1471,17 @@ lpfc_cleanup(struct lpfc_vport *vport) | |||
1438 | if (i++ > 3000) { | 1471 | if (i++ > 3000) { |
1439 | lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY, | 1472 | lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY, |
1440 | "0233 Nodelist not empty\n"); | 1473 | "0233 Nodelist not empty\n"); |
1474 | list_for_each_entry_safe(ndlp, next_ndlp, | ||
1475 | &vport->fc_nodes, nlp_listp) { | ||
1476 | lpfc_printf_vlog(ndlp->vport, KERN_ERR, | ||
1477 | LOG_NODE, | ||
1478 | "0282: did:x%x ndlp:x%p " | ||
1479 | "usgmap:x%x refcnt:%d\n", | ||
1480 | ndlp->nlp_DID, (void *)ndlp, | ||
1481 | ndlp->nlp_usg_map, | ||
1482 | atomic_read( | ||
1483 | &ndlp->kref.refcount)); | ||
1484 | } | ||
1441 | break; | 1485 | break; |
1442 | } | 1486 | } |
1443 | 1487 | ||
@@ -1586,6 +1630,8 @@ lpfc_offline_prep(struct lpfc_hba * phba) | |||
1586 | list_for_each_entry_safe(ndlp, next_ndlp, | 1630 | list_for_each_entry_safe(ndlp, next_ndlp, |
1587 | &vports[i]->fc_nodes, | 1631 | &vports[i]->fc_nodes, |
1588 | nlp_listp) { | 1632 | nlp_listp) { |
1633 | if (!NLP_CHK_NODE_ACT(ndlp)) | ||
1634 | continue; | ||
1589 | if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) | 1635 | if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) |
1590 | continue; | 1636 | continue; |
1591 | if (ndlp->nlp_type & NLP_FABRIC) { | 1637 | if (ndlp->nlp_type & NLP_FABRIC) { |
@@ -1695,9 +1741,9 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) | |||
1695 | 1741 | ||
1696 | vport = (struct lpfc_vport *) shost->hostdata; | 1742 | vport = (struct lpfc_vport *) shost->hostdata; |
1697 | vport->phba = phba; | 1743 | vport->phba = phba; |
1698 | |||
1699 | vport->load_flag |= FC_LOADING; | 1744 | vport->load_flag |= FC_LOADING; |
1700 | vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; | 1745 | vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; |
1746 | vport->fc_rscn_flush = 0; | ||
1701 | 1747 | ||
1702 | lpfc_get_vport_cfgparam(vport); | 1748 | lpfc_get_vport_cfgparam(vport); |
1703 | shost->unique_id = instance; | 1749 | shost->unique_id = instance; |
@@ -1879,6 +1925,42 @@ void lpfc_host_attrib_init(struct Scsi_Host *shost) | |||
1879 | spin_unlock_irq(shost->host_lock); | 1925 | spin_unlock_irq(shost->host_lock); |
1880 | } | 1926 | } |
1881 | 1927 | ||
1928 | static int | ||
1929 | lpfc_enable_msix(struct lpfc_hba *phba) | ||
1930 | { | ||
1931 | int error; | ||
1932 | |||
1933 | phba->msix_entries[0].entry = 0; | ||
1934 | phba->msix_entries[0].vector = 0; | ||
1935 | |||
1936 | error = pci_enable_msix(phba->pcidev, phba->msix_entries, | ||
1937 | ARRAY_SIZE(phba->msix_entries)); | ||
1938 | if (error) { | ||
1939 | lpfc_printf_log(phba, KERN_INFO, LOG_INIT, | ||
1940 | "0420 Enable MSI-X failed (%d), continuing " | ||
1941 | "with MSI\n", error); | ||
1942 | pci_disable_msix(phba->pcidev); | ||
1943 | return error; | ||
1944 | } | ||
1945 | |||
1946 | error = request_irq(phba->msix_entries[0].vector, lpfc_intr_handler, 0, | ||
1947 | LPFC_DRIVER_NAME, phba); | ||
1948 | if (error) { | ||
1949 | lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | ||
1950 | "0421 MSI-X request_irq failed (%d), " | ||
1951 | "continuing with MSI\n", error); | ||
1952 | pci_disable_msix(phba->pcidev); | ||
1953 | } | ||
1954 | return error; | ||
1955 | } | ||
1956 | |||
1957 | static void | ||
1958 | lpfc_disable_msix(struct lpfc_hba *phba) | ||
1959 | { | ||
1960 | free_irq(phba->msix_entries[0].vector, phba); | ||
1961 | pci_disable_msix(phba->pcidev); | ||
1962 | } | ||
1963 | |||
1882 | static int __devinit | 1964 | static int __devinit |
1883 | lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) | 1965 | lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) |
1884 | { | 1966 | { |
@@ -1905,6 +1987,9 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) | |||
1905 | 1987 | ||
1906 | spin_lock_init(&phba->hbalock); | 1988 | spin_lock_init(&phba->hbalock); |
1907 | 1989 | ||
1990 | /* Initialize ndlp management spinlock */ | ||
1991 | spin_lock_init(&phba->ndlp_lock); | ||
1992 | |||
1908 | phba->pcidev = pdev; | 1993 | phba->pcidev = pdev; |
1909 | 1994 | ||
1910 | /* Assign an unused board number */ | 1995 | /* Assign an unused board number */ |
@@ -2002,6 +2087,8 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) | |||
2002 | 2087 | ||
2003 | memset(phba->hbqslimp.virt, 0, lpfc_sli_hbq_size()); | 2088 | memset(phba->hbqslimp.virt, 0, lpfc_sli_hbq_size()); |
2004 | 2089 | ||
2090 | INIT_LIST_HEAD(&phba->hbqbuf_in_list); | ||
2091 | |||
2005 | /* Initialize the SLI Layer to run with lpfc HBAs. */ | 2092 | /* Initialize the SLI Layer to run with lpfc HBAs. */ |
2006 | lpfc_sli_setup(phba); | 2093 | lpfc_sli_setup(phba); |
2007 | lpfc_sli_queue_setup(phba); | 2094 | lpfc_sli_queue_setup(phba); |
@@ -2077,24 +2164,36 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) | |||
2077 | lpfc_debugfs_initialize(vport); | 2164 | lpfc_debugfs_initialize(vport); |
2078 | 2165 | ||
2079 | pci_set_drvdata(pdev, shost); | 2166 | pci_set_drvdata(pdev, shost); |
2167 | phba->intr_type = NONE; | ||
2080 | 2168 | ||
2081 | if (phba->cfg_use_msi) { | 2169 | if (phba->cfg_use_msi == 2) { |
2170 | error = lpfc_enable_msix(phba); | ||
2171 | if (!error) | ||
2172 | phba->intr_type = MSIX; | ||
2173 | } | ||
2174 | |||
2175 | /* Fallback to MSI if MSI-X initialization failed */ | ||
2176 | if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) { | ||
2082 | retval = pci_enable_msi(phba->pcidev); | 2177 | retval = pci_enable_msi(phba->pcidev); |
2083 | if (!retval) | 2178 | if (!retval) |
2084 | phba->using_msi = 1; | 2179 | phba->intr_type = MSI; |
2085 | else | 2180 | else |
2086 | lpfc_printf_log(phba, KERN_INFO, LOG_INIT, | 2181 | lpfc_printf_log(phba, KERN_INFO, LOG_INIT, |
2087 | "0452 Enable MSI failed, continuing " | 2182 | "0452 Enable MSI failed, continuing " |
2088 | "with IRQ\n"); | 2183 | "with IRQ\n"); |
2089 | } | 2184 | } |
2090 | 2185 | ||
2091 | retval = request_irq(phba->pcidev->irq, lpfc_intr_handler, IRQF_SHARED, | 2186 | /* MSI-X is the only case the doesn't need to call request_irq */ |
2092 | LPFC_DRIVER_NAME, phba); | 2187 | if (phba->intr_type != MSIX) { |
2093 | if (retval) { | 2188 | retval = request_irq(phba->pcidev->irq, lpfc_intr_handler, |
2094 | lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | 2189 | IRQF_SHARED, LPFC_DRIVER_NAME, phba); |
2095 | "0451 Enable interrupt handler failed\n"); | 2190 | if (retval) { |
2096 | error = retval; | 2191 | lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0451 Enable " |
2097 | goto out_disable_msi; | 2192 | "interrupt handler failed\n"); |
2193 | error = retval; | ||
2194 | goto out_disable_msi; | ||
2195 | } else if (phba->intr_type != MSI) | ||
2196 | phba->intr_type = INTx; | ||
2098 | } | 2197 | } |
2099 | 2198 | ||
2100 | phba->MBslimaddr = phba->slim_memmap_p; | 2199 | phba->MBslimaddr = phba->slim_memmap_p; |
@@ -2139,9 +2238,14 @@ out_remove_device: | |||
2139 | out_free_irq: | 2238 | out_free_irq: |
2140 | lpfc_stop_phba_timers(phba); | 2239 | lpfc_stop_phba_timers(phba); |
2141 | phba->pport->work_port_events = 0; | 2240 | phba->pport->work_port_events = 0; |
2142 | free_irq(phba->pcidev->irq, phba); | 2241 | |
2242 | if (phba->intr_type == MSIX) | ||
2243 | lpfc_disable_msix(phba); | ||
2244 | else | ||
2245 | free_irq(phba->pcidev->irq, phba); | ||
2246 | |||
2143 | out_disable_msi: | 2247 | out_disable_msi: |
2144 | if (phba->using_msi) | 2248 | if (phba->intr_type == MSI) |
2145 | pci_disable_msi(phba->pcidev); | 2249 | pci_disable_msi(phba->pcidev); |
2146 | destroy_port(vport); | 2250 | destroy_port(vport); |
2147 | out_kthread_stop: | 2251 | out_kthread_stop: |
@@ -2214,10 +2318,13 @@ lpfc_pci_remove_one(struct pci_dev *pdev) | |||
2214 | 2318 | ||
2215 | lpfc_debugfs_terminate(vport); | 2319 | lpfc_debugfs_terminate(vport); |
2216 | 2320 | ||
2217 | /* Release the irq reservation */ | 2321 | if (phba->intr_type == MSIX) |
2218 | free_irq(phba->pcidev->irq, phba); | 2322 | lpfc_disable_msix(phba); |
2219 | if (phba->using_msi) | 2323 | else { |
2220 | pci_disable_msi(phba->pcidev); | 2324 | free_irq(phba->pcidev->irq, phba); |
2325 | if (phba->intr_type == MSI) | ||
2326 | pci_disable_msi(phba->pcidev); | ||
2327 | } | ||
2221 | 2328 | ||
2222 | pci_set_drvdata(pdev, NULL); | 2329 | pci_set_drvdata(pdev, NULL); |
2223 | scsi_host_put(shost); | 2330 | scsi_host_put(shost); |
@@ -2276,10 +2383,13 @@ static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev, | |||
2276 | pring = &psli->ring[psli->fcp_ring]; | 2383 | pring = &psli->ring[psli->fcp_ring]; |
2277 | lpfc_sli_abort_iocb_ring(phba, pring); | 2384 | lpfc_sli_abort_iocb_ring(phba, pring); |
2278 | 2385 | ||
2279 | /* Release the irq reservation */ | 2386 | if (phba->intr_type == MSIX) |
2280 | free_irq(phba->pcidev->irq, phba); | 2387 | lpfc_disable_msix(phba); |
2281 | if (phba->using_msi) | 2388 | else { |
2282 | pci_disable_msi(phba->pcidev); | 2389 | free_irq(phba->pcidev->irq, phba); |
2390 | if (phba->intr_type == MSI) | ||
2391 | pci_disable_msi(phba->pcidev); | ||
2392 | } | ||
2283 | 2393 | ||
2284 | /* Request a slot reset. */ | 2394 | /* Request a slot reset. */ |
2285 | return PCI_ERS_RESULT_NEED_RESET; | 2395 | return PCI_ERS_RESULT_NEED_RESET; |