mirror of https://gitee.com/openkylin/linux.git
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>
This commit is contained in:
parent
ea623455b7
commit
5f61b2c693
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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 *);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue