aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband
diff options
context:
space:
mode:
authorMaciej Sosnowski <maciej.sosnowski@intel.com>2010-11-24 12:29:46 -0500
committerRoland Dreier <rolandd@cisco.com>2011-01-16 16:23:34 -0500
commit5f61b2c6939bb6d26393df15765bc3cb260db063 (patch)
tree0a243de60e121a3bfc06c86d7dc76ca3540fe8c4 /drivers/infiniband
parentea623455b736d82f476460647e8b5fe5dc36f4f2 (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.c11
-rw-r--r--drivers/infiniband/hw/nes/nes.h4
-rw-r--r--drivers/infiniband/hw/nes/nes_hw.c81
-rw-r--r--drivers/infiniband/hw/nes/nes_hw.h4
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 *);
507void nes_iwarp_ce_handler(struct nes_device *, struct nes_hw_cq *); 510void nes_iwarp_ce_handler(struct nes_device *, struct nes_hw_cq *);
508int nes_destroy_cqp(struct nes_device *); 511int nes_destroy_cqp(struct nes_device *);
509int nes_nic_cm_xmit(struct sk_buff *, struct net_device *); 512int nes_nic_cm_xmit(struct sk_buff *, struct net_device *);
513void nes_recheck_link_status(struct work_struct *work);
510 514
511/* nes_nic.c */ 515/* nes_nic.c */
512struct net_device *nes_netdev_init(struct nes_device *, void __iomem *); 516struct 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
2667void 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
2662static void nes_nic_napi_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq) 2743static 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