diff options
author | James Smart <James.Smart@Emulex.Com> | 2008-02-08 18:49:26 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-02-11 18:52:57 -0500 |
commit | e47c9093531d3406a8ae38acca4ce207ef70cc0e (patch) | |
tree | cb115ec0b7981a100ef39ecfc68a36aa7e3e0f2e /drivers/scsi/lpfc/lpfc_hbadisc.c | |
parent | 4660c8ed5aaed99d82785499f034a8cc9199866d (diff) |
[SCSI] lpfc 8.2.5 : Correct ndlp referencing issues
Correct ndlp referencing issues:
- Fix ndlp kref issues due to race conditions between threads
- Fix cancel els delay retry event which missed an ndlp reference count
Signed-off-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_hbadisc.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_hbadisc.c | 296 |
1 files changed, 254 insertions, 42 deletions
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index dc042bd97baa..1ee3e62c78a7 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.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 * |
@@ -272,9 +272,8 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) | |||
272 | if (!(vport->load_flag & FC_UNLOADING) && | 272 | if (!(vport->load_flag & FC_UNLOADING) && |
273 | !(ndlp->nlp_flag & NLP_DELAY_TMO) && | 273 | !(ndlp->nlp_flag & NLP_DELAY_TMO) && |
274 | !(ndlp->nlp_flag & NLP_NPR_2B_DISC) && | 274 | !(ndlp->nlp_flag & NLP_NPR_2B_DISC) && |
275 | (ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)) { | 275 | (ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)) |
276 | lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM); | 276 | lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM); |
277 | } | ||
278 | } | 277 | } |
279 | 278 | ||
280 | 279 | ||
@@ -566,9 +565,10 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove) | |||
566 | int rc; | 565 | int rc; |
567 | 566 | ||
568 | list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { | 567 | list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { |
568 | if (!NLP_CHK_NODE_ACT(ndlp)) | ||
569 | continue; | ||
569 | if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) | 570 | if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) |
570 | continue; | 571 | continue; |
571 | |||
572 | if ((phba->sli3_options & LPFC_SLI3_VPORT_TEARDOWN) || | 572 | if ((phba->sli3_options & LPFC_SLI3_VPORT_TEARDOWN) || |
573 | ((vport->port_type == LPFC_NPIV_PORT) && | 573 | ((vport->port_type == LPFC_NPIV_PORT) && |
574 | (ndlp->nlp_DID == NameServer_DID))) | 574 | (ndlp->nlp_DID == NameServer_DID))) |
@@ -684,20 +684,21 @@ lpfc_linkup_cleanup_nodes(struct lpfc_vport *vport) | |||
684 | struct lpfc_nodelist *ndlp; | 684 | struct lpfc_nodelist *ndlp; |
685 | 685 | ||
686 | list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { | 686 | list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { |
687 | if (!NLP_CHK_NODE_ACT(ndlp)) | ||
688 | continue; | ||
687 | if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) | 689 | if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) |
688 | continue; | 690 | continue; |
689 | |||
690 | if (ndlp->nlp_type & NLP_FABRIC) { | 691 | if (ndlp->nlp_type & NLP_FABRIC) { |
691 | /* On Linkup its safe to clean up the ndlp | 692 | /* On Linkup its safe to clean up the ndlp |
692 | * from Fabric connections. | 693 | * from Fabric connections. |
693 | */ | 694 | */ |
694 | if (ndlp->nlp_DID != Fabric_DID) | 695 | if (ndlp->nlp_DID != Fabric_DID) |
695 | lpfc_unreg_rpi(vport, ndlp); | 696 | lpfc_unreg_rpi(vport, ndlp); |
696 | lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); | 697 | lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); |
697 | } else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) { | 698 | } else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) { |
698 | /* Fail outstanding IO now since device is | 699 | /* Fail outstanding IO now since device is |
699 | * marked for PLOGI. | 700 | * marked for PLOGI. |
700 | */ | 701 | */ |
701 | lpfc_unreg_rpi(vport, ndlp); | 702 | lpfc_unreg_rpi(vport, ndlp); |
702 | } | 703 | } |
703 | } | 704 | } |
@@ -1305,7 +1306,6 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) | |||
1305 | lpfc_mbuf_free(phba, mp->virt, mp->phys); | 1306 | lpfc_mbuf_free(phba, mp->virt, mp->phys); |
1306 | kfree(mp); | 1307 | kfree(mp); |
1307 | mempool_free(pmb, phba->mbox_mem_pool); | 1308 | mempool_free(pmb, phba->mbox_mem_pool); |
1308 | lpfc_nlp_put(ndlp); | ||
1309 | 1309 | ||
1310 | if (phba->fc_topology == TOPOLOGY_LOOP) { | 1310 | if (phba->fc_topology == TOPOLOGY_LOOP) { |
1311 | /* FLOGI failed, use loop map to make discovery list */ | 1311 | /* FLOGI failed, use loop map to make discovery list */ |
@@ -1313,6 +1313,10 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) | |||
1313 | 1313 | ||
1314 | /* Start discovery */ | 1314 | /* Start discovery */ |
1315 | lpfc_disc_start(vport); | 1315 | lpfc_disc_start(vport); |
1316 | /* Decrement the reference count to ndlp after the | ||
1317 | * reference to the ndlp are done. | ||
1318 | */ | ||
1319 | lpfc_nlp_put(ndlp); | ||
1316 | return; | 1320 | return; |
1317 | } | 1321 | } |
1318 | 1322 | ||
@@ -1320,6 +1324,10 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) | |||
1320 | lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX, | 1324 | lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX, |
1321 | "0258 Register Fabric login error: 0x%x\n", | 1325 | "0258 Register Fabric login error: 0x%x\n", |
1322 | mb->mbxStatus); | 1326 | mb->mbxStatus); |
1327 | /* Decrement the reference count to ndlp after the reference | ||
1328 | * to the ndlp are done. | ||
1329 | */ | ||
1330 | lpfc_nlp_put(ndlp); | ||
1323 | return; | 1331 | return; |
1324 | } | 1332 | } |
1325 | 1333 | ||
@@ -1327,8 +1335,6 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) | |||
1327 | ndlp->nlp_type |= NLP_FABRIC; | 1335 | ndlp->nlp_type |= NLP_FABRIC; |
1328 | lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); | 1336 | lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); |
1329 | 1337 | ||
1330 | lpfc_nlp_put(ndlp); /* Drop the reference from the mbox */ | ||
1331 | |||
1332 | if (vport->port_state == LPFC_FABRIC_CFG_LINK) { | 1338 | if (vport->port_state == LPFC_FABRIC_CFG_LINK) { |
1333 | vports = lpfc_create_vport_work_array(phba); | 1339 | vports = lpfc_create_vport_work_array(phba); |
1334 | if (vports != NULL) | 1340 | if (vports != NULL) |
@@ -1356,6 +1362,11 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) | |||
1356 | lpfc_mbuf_free(phba, mp->virt, mp->phys); | 1362 | lpfc_mbuf_free(phba, mp->virt, mp->phys); |
1357 | kfree(mp); | 1363 | kfree(mp); |
1358 | mempool_free(pmb, phba->mbox_mem_pool); | 1364 | mempool_free(pmb, phba->mbox_mem_pool); |
1365 | |||
1366 | /* Drop the reference count from the mbox at the end after | ||
1367 | * all the current reference to the ndlp have been done. | ||
1368 | */ | ||
1369 | lpfc_nlp_put(ndlp); | ||
1359 | return; | 1370 | return; |
1360 | } | 1371 | } |
1361 | 1372 | ||
@@ -1463,9 +1474,8 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) | |||
1463 | * registered the port. | 1474 | * registered the port. |
1464 | */ | 1475 | */ |
1465 | if (ndlp->rport && ndlp->rport->dd_data && | 1476 | if (ndlp->rport && ndlp->rport->dd_data && |
1466 | ((struct lpfc_rport_data *) ndlp->rport->dd_data)->pnode == ndlp) { | 1477 | ((struct lpfc_rport_data *) ndlp->rport->dd_data)->pnode == ndlp) |
1467 | lpfc_nlp_put(ndlp); | 1478 | lpfc_nlp_put(ndlp); |
1468 | } | ||
1469 | 1479 | ||
1470 | lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT, | 1480 | lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT, |
1471 | "rport add: did:x%x flg:x%x type x%x", | 1481 | "rport add: did:x%x flg:x%x type x%x", |
@@ -1660,6 +1670,18 @@ lpfc_nlp_set_state(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, | |||
1660 | } | 1670 | } |
1661 | 1671 | ||
1662 | void | 1672 | void |
1673 | lpfc_enqueue_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) | ||
1674 | { | ||
1675 | struct Scsi_Host *shost = lpfc_shost_from_vport(vport); | ||
1676 | |||
1677 | if (list_empty(&ndlp->nlp_listp)) { | ||
1678 | spin_lock_irq(shost->host_lock); | ||
1679 | list_add_tail(&ndlp->nlp_listp, &vport->fc_nodes); | ||
1680 | spin_unlock_irq(shost->host_lock); | ||
1681 | } | ||
1682 | } | ||
1683 | |||
1684 | void | ||
1663 | lpfc_dequeue_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) | 1685 | lpfc_dequeue_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) |
1664 | { | 1686 | { |
1665 | struct Scsi_Host *shost = lpfc_shost_from_vport(vport); | 1687 | struct Scsi_Host *shost = lpfc_shost_from_vport(vport); |
@@ -1672,7 +1694,80 @@ lpfc_dequeue_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) | |||
1672 | list_del_init(&ndlp->nlp_listp); | 1694 | list_del_init(&ndlp->nlp_listp); |
1673 | spin_unlock_irq(shost->host_lock); | 1695 | spin_unlock_irq(shost->host_lock); |
1674 | lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state, | 1696 | lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state, |
1675 | NLP_STE_UNUSED_NODE); | 1697 | NLP_STE_UNUSED_NODE); |
1698 | } | ||
1699 | |||
1700 | void | ||
1701 | lpfc_disable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) | ||
1702 | { | ||
1703 | if ((ndlp->nlp_flag & NLP_DELAY_TMO) != 0) | ||
1704 | lpfc_cancel_retry_delay_tmo(vport, ndlp); | ||
1705 | if (ndlp->nlp_state && !list_empty(&ndlp->nlp_listp)) | ||
1706 | lpfc_nlp_counters(vport, ndlp->nlp_state, -1); | ||
1707 | lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state, | ||
1708 | NLP_STE_UNUSED_NODE); | ||
1709 | } | ||
1710 | |||
1711 | struct lpfc_nodelist * | ||
1712 | lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, | ||
1713 | int state) | ||
1714 | { | ||
1715 | struct lpfc_hba *phba = vport->phba; | ||
1716 | uint32_t did; | ||
1717 | unsigned long flags; | ||
1718 | |||
1719 | if (!ndlp) | ||
1720 | return NULL; | ||
1721 | |||
1722 | spin_lock_irqsave(&phba->ndlp_lock, flags); | ||
1723 | /* The ndlp should not be in memory free mode */ | ||
1724 | if (NLP_CHK_FREE_REQ(ndlp)) { | ||
1725 | spin_unlock_irqrestore(&phba->ndlp_lock, flags); | ||
1726 | lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE, | ||
1727 | "0277 lpfc_enable_node: ndlp:x%p " | ||
1728 | "usgmap:x%x refcnt:%d\n", | ||
1729 | (void *)ndlp, ndlp->nlp_usg_map, | ||
1730 | atomic_read(&ndlp->kref.refcount)); | ||
1731 | return NULL; | ||
1732 | } | ||
1733 | /* The ndlp should not already be in active mode */ | ||
1734 | if (NLP_CHK_NODE_ACT(ndlp)) { | ||
1735 | spin_unlock_irqrestore(&phba->ndlp_lock, flags); | ||
1736 | lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE, | ||
1737 | "0278 lpfc_enable_node: ndlp:x%p " | ||
1738 | "usgmap:x%x refcnt:%d\n", | ||
1739 | (void *)ndlp, ndlp->nlp_usg_map, | ||
1740 | atomic_read(&ndlp->kref.refcount)); | ||
1741 | return NULL; | ||
1742 | } | ||
1743 | |||
1744 | /* Keep the original DID */ | ||
1745 | did = ndlp->nlp_DID; | ||
1746 | |||
1747 | /* re-initialize ndlp except of ndlp linked list pointer */ | ||
1748 | memset((((char *)ndlp) + sizeof (struct list_head)), 0, | ||
1749 | sizeof (struct lpfc_nodelist) - sizeof (struct list_head)); | ||
1750 | INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp); | ||
1751 | INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp); | ||
1752 | init_timer(&ndlp->nlp_delayfunc); | ||
1753 | ndlp->nlp_delayfunc.function = lpfc_els_retry_delay; | ||
1754 | ndlp->nlp_delayfunc.data = (unsigned long)ndlp; | ||
1755 | ndlp->nlp_DID = did; | ||
1756 | ndlp->vport = vport; | ||
1757 | ndlp->nlp_sid = NLP_NO_SID; | ||
1758 | /* ndlp management re-initialize */ | ||
1759 | kref_init(&ndlp->kref); | ||
1760 | NLP_INT_NODE_ACT(ndlp); | ||
1761 | |||
1762 | spin_unlock_irqrestore(&phba->ndlp_lock, flags); | ||
1763 | |||
1764 | if (state != NLP_STE_UNUSED_NODE) | ||
1765 | lpfc_nlp_set_state(vport, ndlp, state); | ||
1766 | |||
1767 | lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE, | ||
1768 | "node enable: did:x%x", | ||
1769 | ndlp->nlp_DID, 0, 0); | ||
1770 | return ndlp; | ||
1676 | } | 1771 | } |
1677 | 1772 | ||
1678 | void | 1773 | void |
@@ -1972,7 +2067,21 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) | |||
1972 | "Data: x%x x%x x%x\n", | 2067 | "Data: x%x x%x x%x\n", |
1973 | ndlp->nlp_DID, ndlp->nlp_flag, | 2068 | ndlp->nlp_DID, ndlp->nlp_flag, |
1974 | ndlp->nlp_state, ndlp->nlp_rpi); | 2069 | ndlp->nlp_state, ndlp->nlp_rpi); |
1975 | lpfc_dequeue_node(vport, ndlp); | 2070 | if (NLP_CHK_FREE_REQ(ndlp)) { |
2071 | lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE, | ||
2072 | "0280 lpfc_cleanup_node: ndlp:x%p " | ||
2073 | "usgmap:x%x refcnt:%d\n", | ||
2074 | (void *)ndlp, ndlp->nlp_usg_map, | ||
2075 | atomic_read(&ndlp->kref.refcount)); | ||
2076 | lpfc_dequeue_node(vport, ndlp); | ||
2077 | } else { | ||
2078 | lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE, | ||
2079 | "0281 lpfc_cleanup_node: ndlp:x%p " | ||
2080 | "usgmap:x%x refcnt:%d\n", | ||
2081 | (void *)ndlp, ndlp->nlp_usg_map, | ||
2082 | atomic_read(&ndlp->kref.refcount)); | ||
2083 | lpfc_disable_node(vport, ndlp); | ||
2084 | } | ||
1976 | 2085 | ||
1977 | /* cleanup any ndlp on mbox q waiting for reglogin cmpl */ | 2086 | /* cleanup any ndlp on mbox q waiting for reglogin cmpl */ |
1978 | if ((mb = phba->sli.mbox_active)) { | 2087 | if ((mb = phba->sli.mbox_active)) { |
@@ -1994,12 +2103,16 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) | |||
1994 | } | 2103 | } |
1995 | list_del(&mb->list); | 2104 | list_del(&mb->list); |
1996 | mempool_free(mb, phba->mbox_mem_pool); | 2105 | mempool_free(mb, phba->mbox_mem_pool); |
1997 | lpfc_nlp_put(ndlp); | 2106 | /* We shall not invoke the lpfc_nlp_put to decrement |
2107 | * the ndlp reference count as we are in the process | ||
2108 | * of lpfc_nlp_release. | ||
2109 | */ | ||
1998 | } | 2110 | } |
1999 | } | 2111 | } |
2000 | spin_unlock_irq(&phba->hbalock); | 2112 | spin_unlock_irq(&phba->hbalock); |
2001 | 2113 | ||
2002 | lpfc_els_abort(phba,ndlp); | 2114 | lpfc_els_abort(phba, ndlp); |
2115 | |||
2003 | spin_lock_irq(shost->host_lock); | 2116 | spin_lock_irq(shost->host_lock); |
2004 | ndlp->nlp_flag &= ~NLP_DELAY_TMO; | 2117 | ndlp->nlp_flag &= ~NLP_DELAY_TMO; |
2005 | spin_unlock_irq(shost->host_lock); | 2118 | spin_unlock_irq(shost->host_lock); |
@@ -2057,7 +2170,6 @@ lpfc_nlp_remove(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) | |||
2057 | } | 2170 | } |
2058 | } | 2171 | } |
2059 | } | 2172 | } |
2060 | |||
2061 | lpfc_cleanup_node(vport, ndlp); | 2173 | lpfc_cleanup_node(vport, ndlp); |
2062 | 2174 | ||
2063 | /* | 2175 | /* |
@@ -2182,7 +2294,16 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) | |||
2182 | ndlp->nlp_flag |= NLP_NPR_2B_DISC; | 2294 | ndlp->nlp_flag |= NLP_NPR_2B_DISC; |
2183 | spin_unlock_irq(shost->host_lock); | 2295 | spin_unlock_irq(shost->host_lock); |
2184 | return ndlp; | 2296 | return ndlp; |
2297 | } else if (!NLP_CHK_NODE_ACT(ndlp)) { | ||
2298 | ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_NPR_NODE); | ||
2299 | if (!ndlp) | ||
2300 | return NULL; | ||
2301 | spin_lock_irq(shost->host_lock); | ||
2302 | ndlp->nlp_flag |= NLP_NPR_2B_DISC; | ||
2303 | spin_unlock_irq(shost->host_lock); | ||
2304 | return ndlp; | ||
2185 | } | 2305 | } |
2306 | |||
2186 | if (vport->fc_flag & FC_RSCN_MODE) { | 2307 | if (vport->fc_flag & FC_RSCN_MODE) { |
2187 | if (lpfc_rscn_payload_check(vport, did)) { | 2308 | if (lpfc_rscn_payload_check(vport, did)) { |
2188 | /* If we've already recieved a PLOGI from this NPort | 2309 | /* If we've already recieved a PLOGI from this NPort |
@@ -2485,6 +2606,8 @@ lpfc_disc_flush_list(struct lpfc_vport *vport) | |||
2485 | if (vport->fc_plogi_cnt || vport->fc_adisc_cnt) { | 2606 | if (vport->fc_plogi_cnt || vport->fc_adisc_cnt) { |
2486 | list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, | 2607 | list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, |
2487 | nlp_listp) { | 2608 | nlp_listp) { |
2609 | if (!NLP_CHK_NODE_ACT(ndlp)) | ||
2610 | continue; | ||
2488 | if (ndlp->nlp_state == NLP_STE_PLOGI_ISSUE || | 2611 | if (ndlp->nlp_state == NLP_STE_PLOGI_ISSUE || |
2489 | ndlp->nlp_state == NLP_STE_ADISC_ISSUE) { | 2612 | ndlp->nlp_state == NLP_STE_ADISC_ISSUE) { |
2490 | lpfc_free_tx(phba, ndlp); | 2613 | lpfc_free_tx(phba, ndlp); |
@@ -2572,6 +2695,8 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport) | |||
2572 | /* Start discovery by sending FLOGI, clean up old rpis */ | 2695 | /* Start discovery by sending FLOGI, clean up old rpis */ |
2573 | list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, | 2696 | list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, |
2574 | nlp_listp) { | 2697 | nlp_listp) { |
2698 | if (!NLP_CHK_NODE_ACT(ndlp)) | ||
2699 | continue; | ||
2575 | if (ndlp->nlp_state != NLP_STE_NPR_NODE) | 2700 | if (ndlp->nlp_state != NLP_STE_NPR_NODE) |
2576 | continue; | 2701 | continue; |
2577 | if (ndlp->nlp_type & NLP_FABRIC) { | 2702 | if (ndlp->nlp_type & NLP_FABRIC) { |
@@ -2618,7 +2743,7 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport) | |||
2618 | "NameServer login\n"); | 2743 | "NameServer login\n"); |
2619 | /* Next look for NameServer ndlp */ | 2744 | /* Next look for NameServer ndlp */ |
2620 | ndlp = lpfc_findnode_did(vport, NameServer_DID); | 2745 | ndlp = lpfc_findnode_did(vport, NameServer_DID); |
2621 | if (ndlp) | 2746 | if (ndlp && NLP_CHK_NODE_ACT(ndlp)) |
2622 | lpfc_els_abort(phba, ndlp); | 2747 | lpfc_els_abort(phba, ndlp); |
2623 | 2748 | ||
2624 | /* ReStart discovery */ | 2749 | /* ReStart discovery */ |
@@ -2897,6 +3022,7 @@ lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, | |||
2897 | ndlp->nlp_sid = NLP_NO_SID; | 3022 | ndlp->nlp_sid = NLP_NO_SID; |
2898 | INIT_LIST_HEAD(&ndlp->nlp_listp); | 3023 | INIT_LIST_HEAD(&ndlp->nlp_listp); |
2899 | kref_init(&ndlp->kref); | 3024 | kref_init(&ndlp->kref); |
3025 | NLP_INT_NODE_ACT(ndlp); | ||
2900 | 3026 | ||
2901 | lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE, | 3027 | lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE, |
2902 | "node init: did:x%x", | 3028 | "node init: did:x%x", |
@@ -2911,6 +3037,8 @@ lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, | |||
2911 | static void | 3037 | static void |
2912 | lpfc_nlp_release(struct kref *kref) | 3038 | lpfc_nlp_release(struct kref *kref) |
2913 | { | 3039 | { |
3040 | struct lpfc_hba *phba; | ||
3041 | unsigned long flags; | ||
2914 | struct lpfc_nodelist *ndlp = container_of(kref, struct lpfc_nodelist, | 3042 | struct lpfc_nodelist *ndlp = container_of(kref, struct lpfc_nodelist, |
2915 | kref); | 3043 | kref); |
2916 | 3044 | ||
@@ -2918,8 +3046,24 @@ lpfc_nlp_release(struct kref *kref) | |||
2918 | "node release: did:x%x flg:x%x type:x%x", | 3046 | "node release: did:x%x flg:x%x type:x%x", |
2919 | ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type); | 3047 | ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type); |
2920 | 3048 | ||
3049 | lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE, | ||
3050 | "0279 lpfc_nlp_release: ndlp:x%p " | ||
3051 | "usgmap:x%x refcnt:%d\n", | ||
3052 | (void *)ndlp, ndlp->nlp_usg_map, | ||
3053 | atomic_read(&ndlp->kref.refcount)); | ||
3054 | |||
3055 | /* remove ndlp from action. */ | ||
2921 | lpfc_nlp_remove(ndlp->vport, ndlp); | 3056 | lpfc_nlp_remove(ndlp->vport, ndlp); |
2922 | mempool_free(ndlp, ndlp->vport->phba->nlp_mem_pool); | 3057 | |
3058 | /* clear the ndlp active flag for all release cases */ | ||
3059 | phba = ndlp->vport->phba; | ||
3060 | spin_lock_irqsave(&phba->ndlp_lock, flags); | ||
3061 | NLP_CLR_NODE_ACT(ndlp); | ||
3062 | spin_unlock_irqrestore(&phba->ndlp_lock, flags); | ||
3063 | |||
3064 | /* free ndlp memory for final ndlp release */ | ||
3065 | if (NLP_CHK_FREE_REQ(ndlp)) | ||
3066 | mempool_free(ndlp, ndlp->vport->phba->nlp_mem_pool); | ||
2923 | } | 3067 | } |
2924 | 3068 | ||
2925 | /* This routine bumps the reference count for a ndlp structure to ensure | 3069 | /* This routine bumps the reference count for a ndlp structure to ensure |
@@ -2929,37 +3073,108 @@ lpfc_nlp_release(struct kref *kref) | |||
2929 | struct lpfc_nodelist * | 3073 | struct lpfc_nodelist * |
2930 | lpfc_nlp_get(struct lpfc_nodelist *ndlp) | 3074 | lpfc_nlp_get(struct lpfc_nodelist *ndlp) |
2931 | { | 3075 | { |
3076 | struct lpfc_hba *phba; | ||
3077 | unsigned long flags; | ||
3078 | |||
2932 | if (ndlp) { | 3079 | if (ndlp) { |
2933 | lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE, | 3080 | lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE, |
2934 | "node get: did:x%x flg:x%x refcnt:x%x", | 3081 | "node get: did:x%x flg:x%x refcnt:x%x", |
2935 | ndlp->nlp_DID, ndlp->nlp_flag, | 3082 | ndlp->nlp_DID, ndlp->nlp_flag, |
2936 | atomic_read(&ndlp->kref.refcount)); | 3083 | atomic_read(&ndlp->kref.refcount)); |
2937 | kref_get(&ndlp->kref); | 3084 | /* The check of ndlp usage to prevent incrementing the |
3085 | * ndlp reference count that is in the process of being | ||
3086 | * released. | ||
3087 | */ | ||
3088 | phba = ndlp->vport->phba; | ||
3089 | spin_lock_irqsave(&phba->ndlp_lock, flags); | ||
3090 | if (!NLP_CHK_NODE_ACT(ndlp) || NLP_CHK_FREE_ACK(ndlp)) { | ||
3091 | spin_unlock_irqrestore(&phba->ndlp_lock, flags); | ||
3092 | lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_NODE, | ||
3093 | "0276 lpfc_nlp_get: ndlp:x%p " | ||
3094 | "usgmap:x%x refcnt:%d\n", | ||
3095 | (void *)ndlp, ndlp->nlp_usg_map, | ||
3096 | atomic_read(&ndlp->kref.refcount)); | ||
3097 | return NULL; | ||
3098 | } else | ||
3099 | kref_get(&ndlp->kref); | ||
3100 | spin_unlock_irqrestore(&phba->ndlp_lock, flags); | ||
2938 | } | 3101 | } |
2939 | return ndlp; | 3102 | return ndlp; |
2940 | } | 3103 | } |
2941 | 3104 | ||
2942 | |||
2943 | /* This routine decrements the reference count for a ndlp structure. If the | 3105 | /* This routine decrements the reference count for a ndlp structure. If the |
2944 | * count goes to 0, this indicates the the associated nodelist should be freed. | 3106 | * count goes to 0, this indicates the the associated nodelist should be |
3107 | * freed. Returning 1 indicates the ndlp resource has been released; on the | ||
3108 | * other hand, returning 0 indicates the ndlp resource has not been released | ||
3109 | * yet. | ||
2945 | */ | 3110 | */ |
2946 | int | 3111 | int |
2947 | lpfc_nlp_put(struct lpfc_nodelist *ndlp) | 3112 | lpfc_nlp_put(struct lpfc_nodelist *ndlp) |
2948 | { | 3113 | { |
2949 | if (ndlp) { | 3114 | struct lpfc_hba *phba; |
2950 | lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE, | 3115 | unsigned long flags; |
2951 | "node put: did:x%x flg:x%x refcnt:x%x", | 3116 | |
2952 | ndlp->nlp_DID, ndlp->nlp_flag, | 3117 | if (!ndlp) |
2953 | atomic_read(&ndlp->kref.refcount)); | 3118 | return 1; |
3119 | |||
3120 | lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE, | ||
3121 | "node put: did:x%x flg:x%x refcnt:x%x", | ||
3122 | ndlp->nlp_DID, ndlp->nlp_flag, | ||
3123 | atomic_read(&ndlp->kref.refcount)); | ||
3124 | phba = ndlp->vport->phba; | ||
3125 | spin_lock_irqsave(&phba->ndlp_lock, flags); | ||
3126 | /* Check the ndlp memory free acknowledge flag to avoid the | ||
3127 | * possible race condition that kref_put got invoked again | ||
3128 | * after previous one has done ndlp memory free. | ||
3129 | */ | ||
3130 | if (NLP_CHK_FREE_ACK(ndlp)) { | ||
3131 | spin_unlock_irqrestore(&phba->ndlp_lock, flags); | ||
3132 | lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_NODE, | ||
3133 | "0274 lpfc_nlp_put: ndlp:x%p " | ||
3134 | "usgmap:x%x refcnt:%d\n", | ||
3135 | (void *)ndlp, ndlp->nlp_usg_map, | ||
3136 | atomic_read(&ndlp->kref.refcount)); | ||
3137 | return 1; | ||
3138 | } | ||
3139 | /* Check the ndlp inactivate log flag to avoid the possible | ||
3140 | * race condition that kref_put got invoked again after ndlp | ||
3141 | * is already in inactivating state. | ||
3142 | */ | ||
3143 | if (NLP_CHK_IACT_REQ(ndlp)) { | ||
3144 | spin_unlock_irqrestore(&phba->ndlp_lock, flags); | ||
3145 | lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_NODE, | ||
3146 | "0275 lpfc_nlp_put: ndlp:x%p " | ||
3147 | "usgmap:x%x refcnt:%d\n", | ||
3148 | (void *)ndlp, ndlp->nlp_usg_map, | ||
3149 | atomic_read(&ndlp->kref.refcount)); | ||
3150 | return 1; | ||
2954 | } | 3151 | } |
2955 | return ndlp ? kref_put(&ndlp->kref, lpfc_nlp_release) : 0; | 3152 | /* For last put, mark the ndlp usage flags to make sure no |
3153 | * other kref_get and kref_put on the same ndlp shall get | ||
3154 | * in between the process when the final kref_put has been | ||
3155 | * invoked on this ndlp. | ||
3156 | */ | ||
3157 | if (atomic_read(&ndlp->kref.refcount) == 1) { | ||
3158 | /* Indicate ndlp is put to inactive state. */ | ||
3159 | NLP_SET_IACT_REQ(ndlp); | ||
3160 | /* Acknowledge ndlp memory free has been seen. */ | ||
3161 | if (NLP_CHK_FREE_REQ(ndlp)) | ||
3162 | NLP_SET_FREE_ACK(ndlp); | ||
3163 | } | ||
3164 | spin_unlock_irqrestore(&phba->ndlp_lock, flags); | ||
3165 | /* Note, the kref_put returns 1 when decrementing a reference | ||
3166 | * count that was 1, it invokes the release callback function, | ||
3167 | * but it still left the reference count as 1 (not actually | ||
3168 | * performs the last decrementation). Otherwise, it actually | ||
3169 | * decrements the reference count and returns 0. | ||
3170 | */ | ||
3171 | return kref_put(&ndlp->kref, lpfc_nlp_release); | ||
2956 | } | 3172 | } |
2957 | 3173 | ||
2958 | /* This routine free's the specified nodelist if it is not in use | 3174 | /* This routine free's the specified nodelist if it is not in use |
2959 | * by any other discovery thread. This routine returns 1 if the ndlp | 3175 | * by any other discovery thread. This routine returns 1 if the |
2960 | * is not being used by anyone and has been freed. A return value of | 3176 | * ndlp has been freed. A return value of 0 indicates the ndlp is |
2961 | * 0 indicates it is being used by another discovery thread and the | 3177 | * not yet been released. |
2962 | * refcount is left unchanged. | ||
2963 | */ | 3178 | */ |
2964 | int | 3179 | int |
2965 | lpfc_nlp_not_used(struct lpfc_nodelist *ndlp) | 3180 | lpfc_nlp_not_used(struct lpfc_nodelist *ndlp) |
@@ -2968,11 +3183,8 @@ lpfc_nlp_not_used(struct lpfc_nodelist *ndlp) | |||
2968 | "node not used: did:x%x flg:x%x refcnt:x%x", | 3183 | "node not used: did:x%x flg:x%x refcnt:x%x", |
2969 | ndlp->nlp_DID, ndlp->nlp_flag, | 3184 | ndlp->nlp_DID, ndlp->nlp_flag, |
2970 | atomic_read(&ndlp->kref.refcount)); | 3185 | atomic_read(&ndlp->kref.refcount)); |
2971 | 3186 | if (atomic_read(&ndlp->kref.refcount) == 1) | |
2972 | if (atomic_read(&ndlp->kref.refcount) == 1) { | 3187 | if (lpfc_nlp_put(ndlp)) |
2973 | lpfc_nlp_put(ndlp); | 3188 | return 1; |
2974 | return 1; | ||
2975 | } | ||
2976 | return 0; | 3189 | return 0; |
2977 | } | 3190 | } |
2978 | |||