Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5f61b2c6 authored by Maciej Sosnowski's avatar Maciej Sosnowski Committed by Roland Dreier
Browse files

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: default avatarMaciej Sosnowski <maciej.sosnowski@intel.com>
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent ea623455
Loading
Loading
Loading
Loading
+11 −0
Original line number Original line Diff line number Diff line
@@ -674,6 +674,8 @@ static int __devinit nes_probe(struct pci_dev *pcidev, const struct pci_device_i
	}
	}
	nes_notifiers_registered++;
	nes_notifiers_registered++;


	INIT_DELAYED_WORK(&nesdev->work, nes_recheck_link_status);

	/* Initialize network devices */
	/* Initialize network devices */
	if ((netdev = nes_netdev_init(nesdev, mmio_regs)) == NULL)
	if ((netdev = nes_netdev_init(nesdev, mmio_regs)) == NULL)
		goto bail7;
		goto bail7;
@@ -756,6 +758,7 @@ static void __devexit nes_remove(struct pci_dev *pcidev)
	struct nes_device *nesdev = pci_get_drvdata(pcidev);
	struct nes_device *nesdev = pci_get_drvdata(pcidev);
	struct net_device *netdev;
	struct net_device *netdev;
	int netdev_index = 0;
	int netdev_index = 0;
	unsigned long flags;


		if (nesdev->netdev_count) {
		if (nesdev->netdev_count) {
			netdev = nesdev->netdev[netdev_index];
			netdev = nesdev->netdev[netdev_index];
@@ -782,6 +785,14 @@ static void __devexit nes_remove(struct pci_dev *pcidev)
	free_irq(pcidev->irq, nesdev);
	free_irq(pcidev->irq, nesdev);
	tasklet_kill(&nesdev->dpc_tasklet);
	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 */
	/* Deallocate the Adapter Structure */
	nes_destroy_adapter(nesdev->nesadapter);
	nes_destroy_adapter(nesdev->nesadapter);


+4 −0
Original line number Original line Diff line number Diff line
@@ -268,6 +268,9 @@ struct nes_device {
	u8                     napi_isr_ran;
	u8                     napi_isr_ran;
	u8                     disable_rx_flow_control;
	u8                     disable_rx_flow_control;
	u8                     disable_tx_flow_control;
	u8                     disable_tx_flow_control;

	struct delayed_work    work;
	u8                     link_recheck;
};
};




@@ -507,6 +510,7 @@ void nes_nic_ce_handler(struct nes_device *, struct nes_hw_nic_cq *);
void nes_iwarp_ce_handler(struct nes_device *, struct nes_hw_cq *);
void nes_iwarp_ce_handler(struct nes_device *, struct nes_hw_cq *);
int nes_destroy_cqp(struct nes_device *);
int nes_destroy_cqp(struct nes_device *);
int nes_nic_cm_xmit(struct sk_buff *, struct net_device *);
int nes_nic_cm_xmit(struct sk_buff *, struct net_device *);
void nes_recheck_link_status(struct work_struct *work);


/* nes_nic.c */
/* nes_nic.c */
struct net_device *nes_netdev_init(struct nes_device *, void __iomem *);
struct net_device *nes_netdev_init(struct nes_device *, void __iomem *);
+81 −0
Original line number Original line Diff line number Diff line
@@ -2650,6 +2650,13 @@ static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number)
				}
				}
			}
			}
		}
		}
		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);
	spin_unlock_irqrestore(&nesadapter->phy_lock, flags);
@@ -2657,6 +2664,80 @@ static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number)
	nesadapter->mac_sw_state[mac_number] = NES_MAC_SW_IDLE;
	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)
static void nes_nic_napi_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
+4 −0
Original line number Original line Diff line number Diff line
@@ -1354,6 +1354,10 @@ struct nes_terminate_hdr {
#define BAD_FRAME_OFFSET	64
#define BAD_FRAME_OFFSET	64
#define CQE_MAJOR_DRV		0x8000
#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_vlan_rx vlan_hwaccel_receive_skb
#define nes_netif_rx netif_receive_skb
#define nes_netif_rx netif_receive_skb