[ewg] [PATCH 3/4] RDMA/nes: fix SFP link down detection issue with switch port disable

Maciej Sosnowski maciej.sosnowski at intel.com
Fri Dec 3 07:53:40 PST 2010


In case of SFP phy link status check at interrupt processing
happen to give false results. For proper link status change detection
delayed recheck is needed to give nes registers time to settle.
Add periodic link status recheck scheduled at interrupt
to detect potential delayed registers state changes.

This patch fixes openfabrics bugzilla #2117:
http://bugs.openfabrics.org/bugzilla/show_bug.cgi?id=2117

Signed-off-by: Maciej Sosnowski <maciej.sosnowski at intel.com>
---

 .../fixes/nes_0049_recheck_link_status.patch       |  172 +++++++++++++++++++++++
 1 files changed, 172 insertions(+), 0 deletions(-)

diff --git a/kernel_patches/fixes/nes_0049_recheck_link_status.patch b/kernel_patches/fixes/nes_0049_recheck_link_status.patch
new file mode 100644
index 0000000..ee3230b
--- /dev/null
+++ b/kernel_patches/fixes/nes_0049_recheck_link_status.patch
@@ -0,0 +1,172 @@
+diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c
+index a7aea75..588d699 100644
+--- a/drivers/infiniband/hw/nes/nes.c
++++ b/drivers/infiniband/hw/nes/nes.c
+@@ -668,6 +668,8 @@ static int __devinit nes_probe(struct pc
+ 	}
+ 	nes_notifiers_registered++;
+ 
++	INIT_DELAYED_WORK(&nesdev->work, nes_recheck_link_status);
++
+ 	/* Initialize network devices */
+ 		if ((netdev = nes_netdev_init(nesdev, mmio_regs)) == NULL) {
+ 			goto bail7;
+@@ -753,6 +755,7 @@ static void __devexit nes_remove(struct 
+ 	struct nes_device *nesdev = pci_get_drvdata(pcidev);
+ 	struct net_device *netdev;
+ 	int netdev_index = 0;
++	unsigned long flags;
+ 
+ 		if (nesdev->netdev_count) {
+ 			netdev = nesdev->netdev[netdev_index];
+@@ -779,6 +782,14 @@ static void __devexit nes_remove(struct 
+ 	free_irq(pcidev->irq, nesdev);
+ 	tasklet_kill(&nesdev->dpc_tasklet);
+ 
++	spin_lock_irqsave(&nesdev->nesadapter->phy_lock, flags);
++	if (nesdev->link_recheck) {
++		spin_unlock_irqrestore(&nesdev->nesadapter->phy_lock, flags);
++		cancel_delayed_work_sync(&nesdev->work);
++	} else {
++		spin_unlock_irqrestore(&nesdev->nesadapter->phy_lock, flags);
++	}
++
+ 	/* Deallocate the Adapter Structure */
+ 	nes_destroy_adapter(nesdev->nesadapter);
+ 
+diff --git a/drivers/infiniband/hw/nes/nes.h b/drivers/infiniband/hw/nes/nes.h
+index bc54a27..009e39f 100644
+--- a/drivers/infiniband/hw/nes/nes.h
++++ b/drivers/infiniband/hw/nes/nes.h
+@@ -270,6 +270,9 @@ struct nes_device {
+ 	u8                     napi_isr_ran;
+ 	u8                     disable_rx_flow_control;
+ 	u8                     disable_tx_flow_control;
++
++	struct delayed_work    work;
++	u8                     link_recheck;
+ };
+ 
+ 
+@@ -509,6 +512,7 @@ void nes_nic_ce_handler(struct nes_devic
+ void nes_iwarp_ce_handler(struct nes_device *, struct nes_hw_cq *);
+ int nes_destroy_cqp(struct nes_device *);
+ int nes_nic_cm_xmit(struct sk_buff *, struct net_device *);
++void nes_recheck_link_status(struct work_struct *work);
+ 
+ /* nes_nic.c */
+ 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 d605393..098d282 100644
+--- a/drivers/infiniband/hw/nes/nes_hw.c
++++ b/drivers/infiniband/hw/nes/nes_hw.c
+@@ -2649,6 +2649,13 @@ static void nes_process_mac_intr(struct 
+ 				}
+ 			}
+ 		}
++		if (nesadapter->phy_type[mac_index] == NES_PHY_TYPE_SFP_D) {
++			if (nesdev->link_recheck)
++				cancel_delayed_work(&nesdev->work);
++			nesdev->link_recheck = 1;
++			schedule_delayed_work(&nesdev->work,
++					      NES_LINK_RECHECK_DELAY);
++		}
+ 	}
+ 
+ 	spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
+@@ -2656,6 +2663,80 @@ static void nes_process_mac_intr(struct 
+ 	nesadapter->mac_sw_state[mac_number] = NES_MAC_SW_IDLE;
+ }
+ 
++void nes_recheck_link_status(struct work_struct *work)
++{
++	unsigned long flags;
++	struct nes_device *nesdev = container_of(work, struct nes_device, work.work);
++	struct nes_adapter *nesadapter = nesdev->nesadapter;
++	struct nes_vnic *nesvnic;
++	u32 mac_index = nesdev->mac_index;
++	u16 phy_data;
++	u16 temp_phy_data;
++
++	spin_lock_irqsave(&nesadapter->phy_lock, flags);
++
++	/* check link status */
++	nes_read_10G_phy_reg(nesdev, nesadapter->phy_index[mac_index], 1, 0x9003);
++	temp_phy_data = (u16)nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL);
++
++	nes_read_10G_phy_reg(nesdev, nesadapter->phy_index[mac_index], 3, 0x0021);
++	nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL);
++	nes_read_10G_phy_reg(nesdev, nesadapter->phy_index[mac_index], 3, 0x0021);
++	phy_data = (u16)nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL);
++
++	phy_data = (!temp_phy_data && (phy_data == 0x8000)) ? 0x4 : 0x0;
++
++	nes_debug(NES_DBG_PHY, "%s: Phy data = 0x%04X, link was %s.\n",
++		__func__, phy_data,
++		nesadapter->mac_link_down[mac_index] ? "DOWN" : "UP");
++
++	if (phy_data & 0x0004) {
++		nesadapter->mac_link_down[mac_index] = 0;
++		list_for_each_entry(nesvnic, &nesadapter->nesvnic_list[mac_index], list) {
++			if (nesvnic->linkup == 0) {
++				printk(PFX "The Link is now up for port %s, netdev %p.\n",
++						nesvnic->netdev->name, nesvnic->netdev);
++				if (netif_queue_stopped(nesvnic->netdev))
++					netif_start_queue(nesvnic->netdev);
++				nesvnic->linkup = 1;
++				netif_carrier_on(nesvnic->netdev);
++
++				spin_lock(&nesvnic->port_ibevent_lock);
++				if (nesdev->iw_status == 0) {
++					nesdev->iw_status = 1;
++					nes_port_ibevent(nesvnic);
++				}
++				spin_unlock(&nesvnic->port_ibevent_lock);
++			}
++		}
++
++	} else {
++		nesadapter->mac_link_down[mac_index] = 1;
++		list_for_each_entry(nesvnic, &nesadapter->nesvnic_list[mac_index], list) {
++			if (nesvnic->linkup == 1) {
++				printk(PFX "The Link is now down for port %s, netdev %p.\n",
++						nesvnic->netdev->name, nesvnic->netdev);
++				if (!(netif_queue_stopped(nesvnic->netdev)))
++					netif_stop_queue(nesvnic->netdev);
++				nesvnic->linkup = 0;
++				netif_carrier_off(nesvnic->netdev);
++
++				spin_lock(&nesvnic->port_ibevent_lock);
++				if (nesdev->iw_status == 1) {
++					nesdev->iw_status = 0;
++					nes_port_ibevent(nesvnic);
++				}
++				spin_unlock(&nesvnic->port_ibevent_lock);
++			}
++		}
++	}
++	if (nesdev->link_recheck++ < NES_LINK_RECHECK_MAX)
++		schedule_delayed_work(&nesdev->work, NES_LINK_RECHECK_DELAY);
++	else
++		nesdev->link_recheck = 0;
++
++	spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
++}
+ 
+ 
+ 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 2a4b7e2..d8acbe8 100644
+--- a/drivers/infiniband/hw/nes/nes_hw.h
++++ b/drivers/infiniband/hw/nes/nes_hw.h
+@@ -1341,6 +1341,10 @@ #define RDMA_READ_REQ_OPCODE	1
+ #define BAD_FRAME_OFFSET	64
+ #define CQE_MAJOR_DRV		0x8000
+ 
++/* Used for link status recheck after interrupt processing */
++#define NES_LINK_RECHECK_DELAY	msecs_to_jiffies(50)
++#define NES_LINK_RECHECK_MAX	60
++
+ #define nes_vlan_rx vlan_hwaccel_receive_skb
+ #define nes_netif_rx netif_receive_skb
+ 




More information about the ewg mailing list