diff options
author | Maciej Sosnowski <maciej.sosnowski@intel.com> | 2010-11-24 12:29:46 -0500 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2011-01-16 16:23:34 -0500 |
commit | 5f61b2c6939bb6d26393df15765bc3cb260db063 (patch) | |
tree | 0a243de60e121a3bfc06c86d7dc76ca3540fe8c4 /drivers/infiniband | |
parent | ea623455b736d82f476460647e8b5fe5dc36f4f2 (diff) |
RDMA/nes: Fix SFP+ link down detection issue with switch port disable
In case of SFP+ PHY, link status check at interrupt processing can
give false results. For proper link status change detection a delayed
recheck is needed to give nes registers time to settle. Add a
periodic link status recheck scheduled at interrupt to detect
potential delayed registers state changes.
Addresses: http://bugs.openfabrics.org/bugzilla/show_bug.cgi?id=2117
Signed-off-by: Maciej Sosnowski <maciej.sosnowski@intel.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r-- | drivers/infiniband/hw/nes/nes.c | 11 | ||||
-rw-r--r-- | drivers/infiniband/hw/nes/nes.h | 4 | ||||
-rw-r--r-- | drivers/infiniband/hw/nes/nes_hw.c | 81 | ||||
-rw-r--r-- | drivers/infiniband/hw/nes/nes_hw.h | 4 |
4 files changed, 100 insertions, 0 deletions
diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c index e17f52c2194..3b4ec3238ce 100644 --- a/drivers/infiniband/hw/nes/nes.c +++ b/drivers/infiniband/hw/nes/nes.c | |||
@@ -674,6 +674,8 @@ static int __devinit nes_probe(struct pci_dev *pcidev, const struct pci_device_i | |||
674 | } | 674 | } |
675 | nes_notifiers_registered++; | 675 | nes_notifiers_registered++; |
676 | 676 | ||
677 | INIT_DELAYED_WORK(&nesdev->work, nes_recheck_link_status); | ||
678 | |||
677 | /* Initialize network devices */ | 679 | /* Initialize network devices */ |
678 | if ((netdev = nes_netdev_init(nesdev, mmio_regs)) == NULL) | 680 | if ((netdev = nes_netdev_init(nesdev, mmio_regs)) == NULL) |
679 | goto bail7; | 681 | goto bail7; |
@@ -756,6 +758,7 @@ static void __devexit nes_remove(struct pci_dev *pcidev) | |||
756 | struct nes_device *nesdev = pci_get_drvdata(pcidev); | 758 | struct nes_device *nesdev = pci_get_drvdata(pcidev); |
757 | struct net_device *netdev; | 759 | struct net_device *netdev; |
758 | int netdev_index = 0; | 760 | int netdev_index = 0; |
761 | unsigned long flags; | ||
759 | 762 | ||
760 | if (nesdev->netdev_count) { | 763 | if (nesdev->netdev_count) { |
761 | netdev = nesdev->netdev[netdev_index]; | 764 | netdev = nesdev->netdev[netdev_index]; |
@@ -782,6 +785,14 @@ static void __devexit nes_remove(struct pci_dev *pcidev) | |||
782 | free_irq(pcidev->irq, nesdev); | 785 | free_irq(pcidev->irq, nesdev); |
783 | tasklet_kill(&nesdev->dpc_tasklet); | 786 | tasklet_kill(&nesdev->dpc_tasklet); |
784 | 787 | ||
788 | spin_lock_irqsave(&nesdev->nesadapter->phy_lock, flags); | ||
789 | if (nesdev->link_recheck) { | ||
790 | spin_unlock_irqrestore(&nesdev->nesadapter->phy_lock, flags); | ||
791 | cancel_delayed_work_sync(&nesdev->work); | ||
792 | } else { | ||
793 | spin_unlock_irqrestore(&nesdev->nesadapter->phy_lock, flags); | ||
794 | } | ||
795 | |||
785 | /* Deallocate the Adapter Structure */ | 796 | /* Deallocate the Adapter Structure */ |
786 | nes_destroy_adapter(nesdev->nesadapter); | 797 | nes_destroy_adapter(nesdev->nesadapter); |
787 | 798 | ||
diff --git a/drivers/infiniband/hw/nes/nes.h b/drivers/infiniband/hw/nes/nes.h index b3d145e82b4..6fe79876009 100644 --- a/drivers/infiniband/hw/nes/nes.h +++ b/drivers/infiniband/hw/nes/nes.h | |||
@@ -268,6 +268,9 @@ struct nes_device { | |||
268 | u8 napi_isr_ran; | 268 | u8 napi_isr_ran; |
269 | u8 disable_rx_flow_control; | 269 | u8 disable_rx_flow_control; |
270 | u8 disable_tx_flow_control; | 270 | u8 disable_tx_flow_control; |
271 | |||
272 | struct delayed_work work; | ||
273 | u8 link_recheck; | ||
271 | }; | 274 | }; |
272 | 275 | ||
273 | 276 | ||
@@ -507,6 +510,7 @@ void nes_nic_ce_handler(struct nes_device *, struct nes_hw_nic_cq *); | |||
507 | void nes_iwarp_ce_handler(struct nes_device *, struct nes_hw_cq *); | 510 | void nes_iwarp_ce_handler(struct nes_device *, struct nes_hw_cq *); |
508 | int nes_destroy_cqp(struct nes_device *); | 511 | int nes_destroy_cqp(struct nes_device *); |
509 | int nes_nic_cm_xmit(struct sk_buff *, struct net_device *); | 512 | int nes_nic_cm_xmit(struct sk_buff *, struct net_device *); |
513 | void nes_recheck_link_status(struct work_struct *work); | ||
510 | 514 | ||
511 | /* nes_nic.c */ | 515 | /* nes_nic.c */ |
512 | struct net_device *nes_netdev_init(struct nes_device *, void __iomem *); | 516 | struct net_device *nes_netdev_init(struct nes_device *, void __iomem *); |
diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c index 2b89b06ca7c..8b606fd6402 100644 --- a/drivers/infiniband/hw/nes/nes_hw.c +++ b/drivers/infiniband/hw/nes/nes_hw.c | |||
@@ -2650,6 +2650,13 @@ static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number) | |||
2650 | } | 2650 | } |
2651 | } | 2651 | } |
2652 | } | 2652 | } |
2653 | if (nesadapter->phy_type[mac_index] == NES_PHY_TYPE_SFP_D) { | ||
2654 | if (nesdev->link_recheck) | ||
2655 | cancel_delayed_work(&nesdev->work); | ||
2656 | nesdev->link_recheck = 1; | ||
2657 | schedule_delayed_work(&nesdev->work, | ||
2658 | NES_LINK_RECHECK_DELAY); | ||
2659 | } | ||
2653 | } | 2660 | } |
2654 | 2661 | ||
2655 | spin_unlock_irqrestore(&nesadapter->phy_lock, flags); | 2662 | spin_unlock_irqrestore(&nesadapter->phy_lock, flags); |
@@ -2657,6 +2664,80 @@ static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number) | |||
2657 | nesadapter->mac_sw_state[mac_number] = NES_MAC_SW_IDLE; | 2664 | nesadapter->mac_sw_state[mac_number] = NES_MAC_SW_IDLE; |
2658 | } | 2665 | } |
2659 | 2666 | ||
2667 | void nes_recheck_link_status(struct work_struct *work) | ||
2668 | { | ||
2669 | unsigned long flags; | ||
2670 | struct nes_device *nesdev = container_of(work, struct nes_device, work.work); | ||
2671 | struct nes_adapter *nesadapter = nesdev->nesadapter; | ||
2672 | struct nes_vnic *nesvnic; | ||
2673 | u32 mac_index = nesdev->mac_index; | ||
2674 | u16 phy_data; | ||
2675 | u16 temp_phy_data; | ||
2676 | |||
2677 | spin_lock_irqsave(&nesadapter->phy_lock, flags); | ||
2678 | |||
2679 | /* check link status */ | ||
2680 | nes_read_10G_phy_reg(nesdev, nesadapter->phy_index[mac_index], 1, 0x9003); | ||
2681 | temp_phy_data = (u16)nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL); | ||
2682 | |||
2683 | nes_read_10G_phy_reg(nesdev, nesadapter->phy_index[mac_index], 3, 0x0021); | ||
2684 | nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL); | ||
2685 | nes_read_10G_phy_reg(nesdev, nesadapter->phy_index[mac_index], 3, 0x0021); | ||
2686 | phy_data = (u16)nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL); | ||
2687 | |||
2688 | phy_data = (!temp_phy_data && (phy_data == 0x8000)) ? 0x4 : 0x0; | ||
2689 | |||
2690 | nes_debug(NES_DBG_PHY, "%s: Phy data = 0x%04X, link was %s.\n", | ||
2691 | __func__, phy_data, | ||
2692 | nesadapter->mac_link_down[mac_index] ? "DOWN" : "UP"); | ||
2693 | |||
2694 | if (phy_data & 0x0004) { | ||
2695 | nesadapter->mac_link_down[mac_index] = 0; | ||
2696 | list_for_each_entry(nesvnic, &nesadapter->nesvnic_list[mac_index], list) { | ||
2697 | if (nesvnic->linkup == 0) { | ||
2698 | printk(PFX "The Link is now up for port %s, netdev %p.\n", | ||
2699 | nesvnic->netdev->name, nesvnic->netdev); | ||
2700 | if (netif_queue_stopped(nesvnic->netdev)) | ||
2701 | netif_start_queue(nesvnic->netdev); | ||
2702 | nesvnic->linkup = 1; | ||
2703 | netif_carrier_on(nesvnic->netdev); | ||
2704 | |||
2705 | spin_lock(&nesvnic->port_ibevent_lock); | ||
2706 | if (nesdev->iw_status == 0) { | ||
2707 | nesdev->iw_status = 1; | ||
2708 | nes_port_ibevent(nesvnic); | ||
2709 | } | ||
2710 | spin_unlock(&nesvnic->port_ibevent_lock); | ||
2711 | } | ||
2712 | } | ||
2713 | |||
2714 | } else { | ||
2715 | nesadapter->mac_link_down[mac_index] = 1; | ||
2716 | list_for_each_entry(nesvnic, &nesadapter->nesvnic_list[mac_index], list) { | ||
2717 | if (nesvnic->linkup == 1) { | ||
2718 | printk(PFX "The Link is now down for port %s, netdev %p.\n", | ||
2719 | nesvnic->netdev->name, nesvnic->netdev); | ||
2720 | if (!(netif_queue_stopped(nesvnic->netdev))) | ||
2721 | netif_stop_queue(nesvnic->netdev); | ||
2722 | nesvnic->linkup = 0; | ||
2723 | netif_carrier_off(nesvnic->netdev); | ||
2724 | |||
2725 | spin_lock(&nesvnic->port_ibevent_lock); | ||
2726 | if (nesdev->iw_status == 1) { | ||
2727 | nesdev->iw_status = 0; | ||
2728 | nes_port_ibevent(nesvnic); | ||
2729 | } | ||
2730 | spin_unlock(&nesvnic->port_ibevent_lock); | ||
2731 | } | ||
2732 | } | ||
2733 | } | ||
2734 | if (nesdev->link_recheck++ < NES_LINK_RECHECK_MAX) | ||
2735 | schedule_delayed_work(&nesdev->work, NES_LINK_RECHECK_DELAY); | ||
2736 | else | ||
2737 | nesdev->link_recheck = 0; | ||
2738 | |||
2739 | spin_unlock_irqrestore(&nesadapter->phy_lock, flags); | ||
2740 | } | ||
2660 | 2741 | ||
2661 | 2742 | ||
2662 | static void nes_nic_napi_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq) | 2743 | static void nes_nic_napi_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq) |
diff --git a/drivers/infiniband/hw/nes/nes_hw.h b/drivers/infiniband/hw/nes/nes_hw.h index 8a9ea9a4dc5..d2abe07133a 100644 --- a/drivers/infiniband/hw/nes/nes_hw.h +++ b/drivers/infiniband/hw/nes/nes_hw.h | |||
@@ -1354,6 +1354,10 @@ struct nes_terminate_hdr { | |||
1354 | #define BAD_FRAME_OFFSET 64 | 1354 | #define BAD_FRAME_OFFSET 64 |
1355 | #define CQE_MAJOR_DRV 0x8000 | 1355 | #define CQE_MAJOR_DRV 0x8000 |
1356 | 1356 | ||
1357 | /* Used for link status recheck after interrupt processing */ | ||
1358 | #define NES_LINK_RECHECK_DELAY msecs_to_jiffies(50) | ||
1359 | #define NES_LINK_RECHECK_MAX 60 | ||
1360 | |||
1357 | #define nes_vlan_rx vlan_hwaccel_receive_skb | 1361 | #define nes_vlan_rx vlan_hwaccel_receive_skb |
1358 | #define nes_netif_rx netif_receive_skb | 1362 | #define nes_netif_rx netif_receive_skb |
1359 | 1363 | ||