diff options
author | Yuval Mintz <yuvalmin@broadcom.com> | 2013-03-20 01:21:28 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-03-20 13:27:28 -0400 |
commit | 7fa6f34081f168975af72be51715bdc6601931f7 (patch) | |
tree | 5b69dcc7f444fee1eb678ae4b8146ac00c82d736 | |
parent | 47a5247fddf30a1c0d1f5a1afb3bd17e8715075e (diff) |
bnx2x: AER revised
Revised bnx2x implementation of PCI Express Advanced Error Recovery -
stop and free driver resources according to the AER flow (instead of the
currently implemented `hope-for-the-best' release approach), and do not make
any assumptions on the HW state after slot reset.
Signed-off-by: Yuval Mintz <yuvalmin@broadcom.com>
Signed-off-by: Ariel Elior <ariele@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 149 |
4 files changed, 130 insertions, 30 deletions
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index a4729c73ab80..c59da2d7b065 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | |||
@@ -1226,10 +1226,11 @@ enum { | |||
1226 | 1226 | ||
1227 | 1227 | ||
1228 | struct bnx2x_prev_path_list { | 1228 | struct bnx2x_prev_path_list { |
1229 | struct list_head list; | ||
1229 | u8 bus; | 1230 | u8 bus; |
1230 | u8 slot; | 1231 | u8 slot; |
1231 | u8 path; | 1232 | u8 path; |
1232 | struct list_head list; | 1233 | u8 aer; |
1233 | u8 undi; | 1234 | u8 undi; |
1234 | }; | 1235 | }; |
1235 | 1236 | ||
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index db6912b09997..3f5cd7c9f103 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | |||
@@ -2010,7 +2010,7 @@ static int bnx2x_init_hw(struct bnx2x *bp, u32 load_code) | |||
2010 | * Cleans the object that have internal lists without sending | 2010 | * Cleans the object that have internal lists without sending |
2011 | * ramrods. Should be run when interrutps are disabled. | 2011 | * ramrods. Should be run when interrutps are disabled. |
2012 | */ | 2012 | */ |
2013 | static void bnx2x_squeeze_objects(struct bnx2x *bp) | 2013 | void bnx2x_squeeze_objects(struct bnx2x *bp) |
2014 | { | 2014 | { |
2015 | int rc; | 2015 | int rc; |
2016 | unsigned long ramrod_flags = 0, vlan_mac_flags = 0; | 2016 | unsigned long ramrod_flags = 0, vlan_mac_flags = 0; |
@@ -2775,7 +2775,7 @@ load_error0: | |||
2775 | #endif /* ! BNX2X_STOP_ON_ERROR */ | 2775 | #endif /* ! BNX2X_STOP_ON_ERROR */ |
2776 | } | 2776 | } |
2777 | 2777 | ||
2778 | static int bnx2x_drain_tx_queues(struct bnx2x *bp) | 2778 | int bnx2x_drain_tx_queues(struct bnx2x *bp) |
2779 | { | 2779 | { |
2780 | u8 rc = 0, cos, i; | 2780 | u8 rc = 0, cos, i; |
2781 | 2781 | ||
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index f9098d8fc25b..54e1b149acb3 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | |||
@@ -1402,4 +1402,8 @@ static inline bool bnx2x_is_valid_ether_addr(struct bnx2x *bp, u8 *addr) | |||
1402 | * | 1402 | * |
1403 | */ | 1403 | */ |
1404 | void bnx2x_fill_fw_str(struct bnx2x *bp, char *buf, size_t buf_len); | 1404 | void bnx2x_fill_fw_str(struct bnx2x *bp, char *buf, size_t buf_len); |
1405 | |||
1406 | int bnx2x_drain_tx_queues(struct bnx2x *bp); | ||
1407 | void bnx2x_squeeze_objects(struct bnx2x *bp); | ||
1408 | |||
1405 | #endif /* BNX2X_CMN_H */ | 1409 | #endif /* BNX2X_CMN_H */ |
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 4902d1eb3d1e..10b0748a2d72 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | |||
@@ -9718,6 +9718,31 @@ static struct bnx2x_prev_path_list * | |||
9718 | return NULL; | 9718 | return NULL; |
9719 | } | 9719 | } |
9720 | 9720 | ||
9721 | static int bnx2x_prev_path_mark_eeh(struct bnx2x *bp) | ||
9722 | { | ||
9723 | struct bnx2x_prev_path_list *tmp_list; | ||
9724 | int rc; | ||
9725 | |||
9726 | rc = down_interruptible(&bnx2x_prev_sem); | ||
9727 | if (rc) { | ||
9728 | BNX2X_ERR("Received %d when tried to take lock\n", rc); | ||
9729 | return rc; | ||
9730 | } | ||
9731 | |||
9732 | tmp_list = bnx2x_prev_path_get_entry(bp); | ||
9733 | if (tmp_list) { | ||
9734 | tmp_list->aer = 1; | ||
9735 | rc = 0; | ||
9736 | } else { | ||
9737 | BNX2X_ERR("path %d: Entry does not exist for eeh; Flow occurs before initial insmod is over ?\n", | ||
9738 | BP_PATH(bp)); | ||
9739 | } | ||
9740 | |||
9741 | up(&bnx2x_prev_sem); | ||
9742 | |||
9743 | return rc; | ||
9744 | } | ||
9745 | |||
9721 | static bool bnx2x_prev_is_path_marked(struct bnx2x *bp) | 9746 | static bool bnx2x_prev_is_path_marked(struct bnx2x *bp) |
9722 | { | 9747 | { |
9723 | struct bnx2x_prev_path_list *tmp_list; | 9748 | struct bnx2x_prev_path_list *tmp_list; |
@@ -9726,14 +9751,15 @@ static bool bnx2x_prev_is_path_marked(struct bnx2x *bp) | |||
9726 | if (down_trylock(&bnx2x_prev_sem)) | 9751 | if (down_trylock(&bnx2x_prev_sem)) |
9727 | return false; | 9752 | return false; |
9728 | 9753 | ||
9729 | list_for_each_entry(tmp_list, &bnx2x_prev_list, list) { | 9754 | tmp_list = bnx2x_prev_path_get_entry(bp); |
9730 | if (PCI_SLOT(bp->pdev->devfn) == tmp_list->slot && | 9755 | if (tmp_list) { |
9731 | bp->pdev->bus->number == tmp_list->bus && | 9756 | if (tmp_list->aer) { |
9732 | BP_PATH(bp) == tmp_list->path) { | 9757 | DP(NETIF_MSG_HW, "Path %d was marked by AER\n", |
9758 | BP_PATH(bp)); | ||
9759 | } else { | ||
9733 | rc = true; | 9760 | rc = true; |
9734 | BNX2X_DEV_INFO("Path %d was already cleaned from previous drivers\n", | 9761 | BNX2X_DEV_INFO("Path %d was already cleaned from previous drivers\n", |
9735 | BP_PATH(bp)); | 9762 | BP_PATH(bp)); |
9736 | break; | ||
9737 | } | 9763 | } |
9738 | } | 9764 | } |
9739 | 9765 | ||
@@ -9747,6 +9773,28 @@ static int bnx2x_prev_mark_path(struct bnx2x *bp, bool after_undi) | |||
9747 | struct bnx2x_prev_path_list *tmp_list; | 9773 | struct bnx2x_prev_path_list *tmp_list; |
9748 | int rc; | 9774 | int rc; |
9749 | 9775 | ||
9776 | rc = down_interruptible(&bnx2x_prev_sem); | ||
9777 | if (rc) { | ||
9778 | BNX2X_ERR("Received %d when tried to take lock\n", rc); | ||
9779 | return rc; | ||
9780 | } | ||
9781 | |||
9782 | /* Check whether the entry for this path already exists */ | ||
9783 | tmp_list = bnx2x_prev_path_get_entry(bp); | ||
9784 | if (tmp_list) { | ||
9785 | if (!tmp_list->aer) { | ||
9786 | BNX2X_ERR("Re-Marking the path.\n"); | ||
9787 | } else { | ||
9788 | DP(NETIF_MSG_HW, "Removing AER indication from path %d\n", | ||
9789 | BP_PATH(bp)); | ||
9790 | tmp_list->aer = 0; | ||
9791 | } | ||
9792 | up(&bnx2x_prev_sem); | ||
9793 | return 0; | ||
9794 | } | ||
9795 | up(&bnx2x_prev_sem); | ||
9796 | |||
9797 | /* Create an entry for this path and add it */ | ||
9750 | tmp_list = kmalloc(sizeof(struct bnx2x_prev_path_list), GFP_KERNEL); | 9798 | tmp_list = kmalloc(sizeof(struct bnx2x_prev_path_list), GFP_KERNEL); |
9751 | if (!tmp_list) { | 9799 | if (!tmp_list) { |
9752 | BNX2X_ERR("Failed to allocate 'bnx2x_prev_path_list'\n"); | 9800 | BNX2X_ERR("Failed to allocate 'bnx2x_prev_path_list'\n"); |
@@ -9756,6 +9804,7 @@ static int bnx2x_prev_mark_path(struct bnx2x *bp, bool after_undi) | |||
9756 | tmp_list->bus = bp->pdev->bus->number; | 9804 | tmp_list->bus = bp->pdev->bus->number; |
9757 | tmp_list->slot = PCI_SLOT(bp->pdev->devfn); | 9805 | tmp_list->slot = PCI_SLOT(bp->pdev->devfn); |
9758 | tmp_list->path = BP_PATH(bp); | 9806 | tmp_list->path = BP_PATH(bp); |
9807 | tmp_list->aer = 0; | ||
9759 | tmp_list->undi = after_undi ? (1 << BP_PORT(bp)) : 0; | 9808 | tmp_list->undi = after_undi ? (1 << BP_PORT(bp)) : 0; |
9760 | 9809 | ||
9761 | rc = down_interruptible(&bnx2x_prev_sem); | 9810 | rc = down_interruptible(&bnx2x_prev_sem); |
@@ -9763,8 +9812,8 @@ static int bnx2x_prev_mark_path(struct bnx2x *bp, bool after_undi) | |||
9763 | BNX2X_ERR("Received %d when tried to take lock\n", rc); | 9812 | BNX2X_ERR("Received %d when tried to take lock\n", rc); |
9764 | kfree(tmp_list); | 9813 | kfree(tmp_list); |
9765 | } else { | 9814 | } else { |
9766 | BNX2X_DEV_INFO("Marked path [%d] - finished previous unload\n", | 9815 | DP(NETIF_MSG_HW, "Marked path [%d] - finished previous unload\n", |
9767 | BP_PATH(bp)); | 9816 | BP_PATH(bp)); |
9768 | list_add(&tmp_list->list, &bnx2x_prev_list); | 9817 | list_add(&tmp_list->list, &bnx2x_prev_list); |
9769 | up(&bnx2x_prev_sem); | 9818 | up(&bnx2x_prev_sem); |
9770 | } | 9819 | } |
@@ -10003,6 +10052,7 @@ static int bnx2x_prev_unload(struct bnx2x *bp) | |||
10003 | } | 10052 | } |
10004 | 10053 | ||
10005 | do { | 10054 | do { |
10055 | int aer = 0; | ||
10006 | /* Lock MCP using an unload request */ | 10056 | /* Lock MCP using an unload request */ |
10007 | fw = bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS, 0); | 10057 | fw = bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS, 0); |
10008 | if (!fw) { | 10058 | if (!fw) { |
@@ -10011,7 +10061,18 @@ static int bnx2x_prev_unload(struct bnx2x *bp) | |||
10011 | break; | 10061 | break; |
10012 | } | 10062 | } |
10013 | 10063 | ||
10014 | if (fw == FW_MSG_CODE_DRV_UNLOAD_COMMON) { | 10064 | rc = down_interruptible(&bnx2x_prev_sem); |
10065 | if (rc) { | ||
10066 | BNX2X_ERR("Cannot check for AER; Received %d when tried to take lock\n", | ||
10067 | rc); | ||
10068 | } else { | ||
10069 | /* If Path is marked by EEH, ignore unload status */ | ||
10070 | aer = !!(bnx2x_prev_path_get_entry(bp) && | ||
10071 | bnx2x_prev_path_get_entry(bp)->aer); | ||
10072 | } | ||
10073 | up(&bnx2x_prev_sem); | ||
10074 | |||
10075 | if (fw == FW_MSG_CODE_DRV_UNLOAD_COMMON || aer) { | ||
10015 | rc = bnx2x_prev_unload_common(bp); | 10076 | rc = bnx2x_prev_unload_common(bp); |
10016 | break; | 10077 | break; |
10017 | } | 10078 | } |
@@ -12632,9 +12693,7 @@ static void bnx2x_remove_one(struct pci_dev *pdev) | |||
12632 | 12693 | ||
12633 | static int bnx2x_eeh_nic_unload(struct bnx2x *bp) | 12694 | static int bnx2x_eeh_nic_unload(struct bnx2x *bp) |
12634 | { | 12695 | { |
12635 | int i; | 12696 | bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT; |
12636 | |||
12637 | bp->state = BNX2X_STATE_ERROR; | ||
12638 | 12697 | ||
12639 | bp->rx_mode = BNX2X_RX_MODE_NONE; | 12698 | bp->rx_mode = BNX2X_RX_MODE_NONE; |
12640 | 12699 | ||
@@ -12643,29 +12702,21 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp) | |||
12643 | 12702 | ||
12644 | /* Stop Tx */ | 12703 | /* Stop Tx */ |
12645 | bnx2x_tx_disable(bp); | 12704 | bnx2x_tx_disable(bp); |
12646 | |||
12647 | bnx2x_netif_stop(bp, 0); | ||
12648 | /* Delete all NAPI objects */ | 12705 | /* Delete all NAPI objects */ |
12649 | bnx2x_del_all_napi(bp); | 12706 | bnx2x_del_all_napi(bp); |
12650 | if (CNIC_LOADED(bp)) | 12707 | if (CNIC_LOADED(bp)) |
12651 | bnx2x_del_all_napi_cnic(bp); | 12708 | bnx2x_del_all_napi_cnic(bp); |
12709 | netdev_reset_tc(bp->dev); | ||
12652 | 12710 | ||
12653 | del_timer_sync(&bp->timer); | 12711 | del_timer_sync(&bp->timer); |
12712 | cancel_delayed_work(&bp->sp_task); | ||
12713 | cancel_delayed_work(&bp->period_task); | ||
12654 | 12714 | ||
12655 | bnx2x_stats_handle(bp, STATS_EVENT_STOP); | 12715 | spin_lock_bh(&bp->stats_lock); |
12656 | 12716 | bp->stats_state = STATS_STATE_DISABLED; | |
12657 | /* Release IRQs */ | 12717 | spin_unlock_bh(&bp->stats_lock); |
12658 | bnx2x_free_irq(bp); | ||
12659 | |||
12660 | /* Free SKBs, SGEs, TPA pool and driver internals */ | ||
12661 | bnx2x_free_skbs(bp); | ||
12662 | |||
12663 | for_each_rx_queue(bp, i) | ||
12664 | bnx2x_free_rx_sge_range(bp, bp->fp + i, NUM_RX_SGE); | ||
12665 | |||
12666 | bnx2x_free_mem(bp); | ||
12667 | 12718 | ||
12668 | bp->state = BNX2X_STATE_CLOSED; | 12719 | bnx2x_save_statistics(bp); |
12669 | 12720 | ||
12670 | netif_carrier_off(bp->dev); | 12721 | netif_carrier_off(bp->dev); |
12671 | 12722 | ||
@@ -12701,6 +12752,8 @@ static pci_ers_result_t bnx2x_io_error_detected(struct pci_dev *pdev, | |||
12701 | 12752 | ||
12702 | rtnl_lock(); | 12753 | rtnl_lock(); |
12703 | 12754 | ||
12755 | BNX2X_ERR("IO error detected\n"); | ||
12756 | |||
12704 | netif_device_detach(dev); | 12757 | netif_device_detach(dev); |
12705 | 12758 | ||
12706 | if (state == pci_channel_io_perm_failure) { | 12759 | if (state == pci_channel_io_perm_failure) { |
@@ -12711,6 +12764,8 @@ static pci_ers_result_t bnx2x_io_error_detected(struct pci_dev *pdev, | |||
12711 | if (netif_running(dev)) | 12764 | if (netif_running(dev)) |
12712 | bnx2x_eeh_nic_unload(bp); | 12765 | bnx2x_eeh_nic_unload(bp); |
12713 | 12766 | ||
12767 | bnx2x_prev_path_mark_eeh(bp); | ||
12768 | |||
12714 | pci_disable_device(pdev); | 12769 | pci_disable_device(pdev); |
12715 | 12770 | ||
12716 | rtnl_unlock(); | 12771 | rtnl_unlock(); |
@@ -12729,9 +12784,10 @@ static pci_ers_result_t bnx2x_io_slot_reset(struct pci_dev *pdev) | |||
12729 | { | 12784 | { |
12730 | struct net_device *dev = pci_get_drvdata(pdev); | 12785 | struct net_device *dev = pci_get_drvdata(pdev); |
12731 | struct bnx2x *bp = netdev_priv(dev); | 12786 | struct bnx2x *bp = netdev_priv(dev); |
12787 | int i; | ||
12732 | 12788 | ||
12733 | rtnl_lock(); | 12789 | rtnl_lock(); |
12734 | 12790 | BNX2X_ERR("IO slot reset initializing...\n"); | |
12735 | if (pci_enable_device(pdev)) { | 12791 | if (pci_enable_device(pdev)) { |
12736 | dev_err(&pdev->dev, | 12792 | dev_err(&pdev->dev, |
12737 | "Cannot re-enable PCI device after reset\n"); | 12793 | "Cannot re-enable PCI device after reset\n"); |
@@ -12745,6 +12801,42 @@ static pci_ers_result_t bnx2x_io_slot_reset(struct pci_dev *pdev) | |||
12745 | if (netif_running(dev)) | 12801 | if (netif_running(dev)) |
12746 | bnx2x_set_power_state(bp, PCI_D0); | 12802 | bnx2x_set_power_state(bp, PCI_D0); |
12747 | 12803 | ||
12804 | if (netif_running(dev)) { | ||
12805 | BNX2X_ERR("IO slot reset --> driver unload\n"); | ||
12806 | if (IS_PF(bp) && SHMEM2_HAS(bp, drv_capabilities_flag)) { | ||
12807 | u32 v; | ||
12808 | |||
12809 | v = SHMEM2_RD(bp, | ||
12810 | drv_capabilities_flag[BP_FW_MB_IDX(bp)]); | ||
12811 | SHMEM2_WR(bp, drv_capabilities_flag[BP_FW_MB_IDX(bp)], | ||
12812 | v & ~DRV_FLAGS_CAPABILITIES_LOADED_L2); | ||
12813 | } | ||
12814 | bnx2x_drain_tx_queues(bp); | ||
12815 | bnx2x_send_unload_req(bp, UNLOAD_RECOVERY); | ||
12816 | bnx2x_netif_stop(bp, 1); | ||
12817 | bnx2x_free_irq(bp); | ||
12818 | |||
12819 | /* Report UNLOAD_DONE to MCP */ | ||
12820 | bnx2x_send_unload_done(bp, true); | ||
12821 | |||
12822 | bp->sp_state = 0; | ||
12823 | bp->port.pmf = 0; | ||
12824 | |||
12825 | bnx2x_prev_unload(bp); | ||
12826 | |||
12827 | /* We should have resetted the engine, so It's fair to | ||
12828 | * assume the FW will no longer write to the bnx2x driver. | ||
12829 | */ | ||
12830 | bnx2x_squeeze_objects(bp); | ||
12831 | bnx2x_free_skbs(bp); | ||
12832 | for_each_rx_queue(bp, i) | ||
12833 | bnx2x_free_rx_sge_range(bp, bp->fp + i, NUM_RX_SGE); | ||
12834 | bnx2x_free_fp_mem(bp); | ||
12835 | bnx2x_free_mem(bp); | ||
12836 | |||
12837 | bp->state = BNX2X_STATE_CLOSED; | ||
12838 | } | ||
12839 | |||
12748 | rtnl_unlock(); | 12840 | rtnl_unlock(); |
12749 | 12841 | ||
12750 | return PCI_ERS_RESULT_RECOVERED; | 12842 | return PCI_ERS_RESULT_RECOVERED; |
@@ -12771,6 +12863,9 @@ static void bnx2x_io_resume(struct pci_dev *pdev) | |||
12771 | 12863 | ||
12772 | bnx2x_eeh_recover(bp); | 12864 | bnx2x_eeh_recover(bp); |
12773 | 12865 | ||
12866 | bp->fw_seq = SHMEM_RD(bp, func_mb[BP_FW_MB_IDX(bp)].drv_mb_header) & | ||
12867 | DRV_MSG_SEQ_NUMBER_MASK; | ||
12868 | |||
12774 | if (netif_running(dev)) | 12869 | if (netif_running(dev)) |
12775 | bnx2x_nic_load(bp, LOAD_NORMAL); | 12870 | bnx2x_nic_load(bp, LOAD_NORMAL); |
12776 | 12871 | ||