aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband/hw/nes
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/infiniband/hw/nes')
-rw-r--r--drivers/infiniband/hw/nes/nes.c35
-rw-r--r--drivers/infiniband/hw/nes/nes.h4
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.c8
-rw-r--r--drivers/infiniband/hw/nes/nes_hw.c95
-rw-r--r--drivers/infiniband/hw/nes/nes_hw.h10
-rw-r--r--drivers/infiniband/hw/nes/nes_nic.c82
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.c37
7 files changed, 250 insertions, 21 deletions
diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c
index 0c9f0aa5d4ea..3b4ec3238ceb 100644
--- a/drivers/infiniband/hw/nes/nes.c
+++ b/drivers/infiniband/hw/nes/nes.c
@@ -144,6 +144,7 @@ static int nes_inetaddr_event(struct notifier_block *notifier,
struct nes_device *nesdev;
struct net_device *netdev;
struct nes_vnic *nesvnic;
+ unsigned int is_bonded;
nes_debug(NES_DBG_NETDEV, "nes_inetaddr_event: ip address %pI4, netmask %pI4.\n",
&ifa->ifa_address, &ifa->ifa_mask);
@@ -152,7 +153,8 @@ static int nes_inetaddr_event(struct notifier_block *notifier,
nesdev, nesdev->netdev[0]->name);
netdev = nesdev->netdev[0];
nesvnic = netdev_priv(netdev);
- if (netdev == event_netdev) {
+ is_bonded = (netdev->master == event_netdev);
+ if ((netdev == event_netdev) || is_bonded) {
if (nesvnic->rdma_enabled == 0) {
nes_debug(NES_DBG_NETDEV, "Returning without processing event for %s since"
" RDMA is not enabled.\n",
@@ -169,7 +171,10 @@ static int nes_inetaddr_event(struct notifier_block *notifier,
nes_manage_arp_cache(netdev, netdev->dev_addr,
ntohl(nesvnic->local_ipaddr), NES_ARP_DELETE);
nesvnic->local_ipaddr = 0;
- return NOTIFY_OK;
+ if (is_bonded)
+ continue;
+ else
+ return NOTIFY_OK;
break;
case NETDEV_UP:
nes_debug(NES_DBG_NETDEV, "event:UP\n");
@@ -178,15 +183,24 @@ static int nes_inetaddr_event(struct notifier_block *notifier,
nes_debug(NES_DBG_NETDEV, "Interface already has local_ipaddr\n");
return NOTIFY_OK;
}
+ /* fall through */
+ case NETDEV_CHANGEADDR:
/* Add the address to the IP table */
- nesvnic->local_ipaddr = ifa->ifa_address;
+ if (netdev->master)
+ nesvnic->local_ipaddr =
+ ((struct in_device *)netdev->master->ip_ptr)->ifa_list->ifa_address;
+ else
+ nesvnic->local_ipaddr = ifa->ifa_address;
nes_write_indexed(nesdev,
NES_IDX_DST_IP_ADDR+(0x10*PCI_FUNC(nesdev->pcidev->devfn)),
- ntohl(ifa->ifa_address));
+ ntohl(nesvnic->local_ipaddr));
nes_manage_arp_cache(netdev, netdev->dev_addr,
ntohl(nesvnic->local_ipaddr), NES_ARP_ADD);
- return NOTIFY_OK;
+ if (is_bonded)
+ continue;
+ else
+ return NOTIFY_OK;
break;
default:
break;
@@ -660,6 +674,8 @@ static int __devinit nes_probe(struct pci_dev *pcidev, const struct pci_device_i
}
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;
@@ -742,6 +758,7 @@ static void __devexit nes_remove(struct pci_dev *pcidev)
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];
@@ -768,6 +785,14 @@ static void __devexit nes_remove(struct pci_dev *pcidev)
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 b3d145e82b4c..6fe79876009e 100644
--- a/drivers/infiniband/hw/nes/nes.h
+++ b/drivers/infiniband/hw/nes/nes.h
@@ -268,6 +268,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;
};
@@ -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 *);
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_cm.c b/drivers/infiniband/hw/nes/nes_cm.c
index 25ad0f9944c0..009ec814d517 100644
--- a/drivers/infiniband/hw/nes/nes_cm.c
+++ b/drivers/infiniband/hw/nes/nes_cm.c
@@ -1107,6 +1107,7 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
struct flowi fl;
struct neighbour *neigh;
int rc = arpindex;
+ struct net_device *netdev;
struct nes_adapter *nesadapter = nesvnic->nesdev->nesadapter;
memset(&fl, 0, sizeof fl);
@@ -1117,7 +1118,12 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
return rc;
}
- neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, nesvnic->netdev);
+ if (nesvnic->netdev->master)
+ netdev = nesvnic->netdev->master;
+ else
+ netdev = nesvnic->netdev;
+
+ neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, netdev);
if (neigh) {
if (neigh->nud_state & NUD_VALID) {
nes_debug(NES_DBG_CM, "Neighbor MAC address for 0x%08X"
diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c
index 1980a461c499..8b606fd64022 100644
--- a/drivers/infiniband/hw/nes/nes_hw.c
+++ b/drivers/infiniband/hw/nes/nes_hw.c
@@ -2608,6 +2608,13 @@ static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number)
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 {
@@ -2633,9 +2640,23 @@ static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number)
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 (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);
@@ -2643,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;
}
+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 1204c3432b63..d2abe07133a5 100644
--- a/drivers/infiniband/hw/nes/nes_hw.h
+++ b/drivers/infiniband/hw/nes/nes_hw.h
@@ -1193,6 +1193,8 @@ struct nes_listener {
struct nes_ib_device;
+#define NES_EVENT_DELAY msecs_to_jiffies(100)
+
struct nes_vnic {
struct nes_ib_device *nesibdev;
u64 sq_full;
@@ -1247,6 +1249,10 @@ struct nes_vnic {
u32 lro_max_aggr;
struct net_lro_mgr lro_mgr;
struct net_lro_desc lro_desc[NES_MAX_LRO_DESCRIPTORS];
+ struct timer_list event_timer;
+ enum ib_event_type delayed_event;
+ enum ib_event_type last_dispatched_event;
+ spinlock_t port_ibevent_lock;
};
struct nes_ib_device {
@@ -1348,6 +1354,10 @@ struct nes_terminate_hdr {
#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
diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c
index 3892e2c0e95a..2c9c1933bbe3 100644
--- a/drivers/infiniband/hw/nes/nes_nic.c
+++ b/drivers/infiniband/hw/nes/nes_nic.c
@@ -144,6 +144,7 @@ static int nes_netdev_open(struct net_device *netdev)
u32 nic_active_bit;
u32 nic_active;
struct list_head *list_pos, *list_temp;
+ unsigned long flags;
assert(nesdev != NULL);
@@ -233,18 +234,36 @@ static int nes_netdev_open(struct net_device *netdev)
first_nesvnic = nesvnic;
}
- if (nesvnic->of_device_registered) {
- nesdev->iw_status = 1;
- nesdev->nesadapter->send_term_ok = 1;
- nes_port_ibevent(nesvnic);
- }
-
if (first_nesvnic->linkup) {
/* Enable network packets */
nesvnic->linkup = 1;
netif_start_queue(netdev);
netif_carrier_on(netdev);
}
+
+ spin_lock_irqsave(&nesdev->nesadapter->phy_lock, flags);
+ if (nesdev->nesadapter->phy_type[nesdev->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(&nesdev->nesadapter->phy_lock, flags);
+
+ spin_lock_irqsave(&nesvnic->port_ibevent_lock, flags);
+ if (nesvnic->of_device_registered) {
+ nesdev->nesadapter->send_term_ok = 1;
+ if (nesvnic->linkup == 1) {
+ if (nesdev->iw_status == 0) {
+ nesdev->iw_status = 1;
+ nes_port_ibevent(nesvnic);
+ }
+ } else {
+ nesdev->iw_status = 0;
+ }
+ }
+ spin_unlock_irqrestore(&nesvnic->port_ibevent_lock, flags);
+
napi_enable(&nesvnic->napi);
nesvnic->netdev_open = 1;
@@ -263,6 +282,7 @@ static int nes_netdev_stop(struct net_device *netdev)
u32 nic_active;
struct nes_vnic *first_nesvnic = NULL;
struct list_head *list_pos, *list_temp;
+ unsigned long flags;
nes_debug(NES_DBG_SHUTDOWN, "nesvnic=%p, nesdev=%p, netdev=%p %s\n",
nesvnic, nesdev, netdev, netdev->name);
@@ -315,12 +335,17 @@ static int nes_netdev_stop(struct net_device *netdev)
nic_active &= nic_active_mask;
nes_write_indexed(nesdev, NES_IDX_NIC_BROADCAST_ON, nic_active);
-
+ spin_lock_irqsave(&nesvnic->port_ibevent_lock, flags);
if (nesvnic->of_device_registered) {
nesdev->nesadapter->send_term_ok = 0;
nesdev->iw_status = 0;
- nes_port_ibevent(nesvnic);
+ if (nesvnic->linkup == 1)
+ nes_port_ibevent(nesvnic);
}
+ del_timer_sync(&nesvnic->event_timer);
+ nesvnic->event_timer.function = NULL;
+ spin_unlock_irqrestore(&nesvnic->port_ibevent_lock, flags);
+
nes_destroy_nic_qp(nesvnic);
nesvnic->netdev_open = 0;
@@ -908,8 +933,8 @@ static void nes_netdev_set_multicast_list(struct net_device *netdev)
nesvnic->nic_index &&
mc_index < max_pft_entries_avaiable) {
nes_debug(NES_DBG_NIC_RX,
- "mc_index=%d skipping nic_index=%d,\
- used for=%d \n", mc_index,
+ "mc_index=%d skipping nic_index=%d, "
+ "used for=%d \n", mc_index,
nesvnic->nic_index,
nesadapter->pft_mcast_map[mc_index]);
mc_index++;
@@ -1750,7 +1775,10 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev,
nesvnic->rdma_enabled = 0;
}
nesvnic->nic_cq.cq_number = nesvnic->nic.qp_id;
+ init_timer(&nesvnic->event_timer);
+ nesvnic->event_timer.function = NULL;
spin_lock_init(&nesvnic->tx_lock);
+ spin_lock_init(&nesvnic->port_ibevent_lock);
nesdev->netdev[nesdev->netdev_count] = netdev;
nes_debug(NES_DBG_INIT, "Adding nesvnic (%p) to the adapters nesvnic_list for MAC%d.\n",
@@ -1763,8 +1791,11 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev,
(((PCI_FUNC(nesdev->pcidev->devfn) == 1) && (nesdev->mac_index == 2)) ||
((PCI_FUNC(nesdev->pcidev->devfn) == 2) && (nesdev->mac_index == 1)))))) {
u32 u32temp;
- u32 link_mask;
- u32 link_val;
+ u32 link_mask = 0;
+ u32 link_val = 0;
+ u16 temp_phy_data;
+ u16 phy_data = 0;
+ unsigned long flags;
u32temp = nes_read_indexed(nesdev, NES_IDX_PHY_PCS_CONTROL_STATUS0 +
(0x200 * (nesdev->mac_index & 1)));
@@ -1786,6 +1817,23 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev,
link_val = 0x02020000;
}
break;
+ case NES_PHY_TYPE_SFP_D:
+ spin_lock_irqsave(&nesdev->nesadapter->phy_lock, flags);
+ nes_read_10G_phy_reg(nesdev,
+ nesdev->nesadapter->phy_index[nesdev->mac_index],
+ 1, 0x9003);
+ temp_phy_data = (u16)nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL);
+ nes_read_10G_phy_reg(nesdev,
+ nesdev->nesadapter->phy_index[nesdev->mac_index],
+ 3, 0x0021);
+ nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL);
+ nes_read_10G_phy_reg(nesdev,
+ nesdev->nesadapter->phy_index[nesdev->mac_index],
+ 3, 0x0021);
+ phy_data = (u16)nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL);
+ spin_unlock_irqrestore(&nesdev->nesadapter->phy_lock, flags);
+ phy_data = (!temp_phy_data && (phy_data == 0x8000)) ? 0x4 : 0x0;
+ break;
default:
link_mask = 0x0f1f0000;
link_val = 0x0f0f0000;
@@ -1795,8 +1843,14 @@ struct net_device *nes_netdev_init(struct nes_device *nesdev,
u32temp = nes_read_indexed(nesdev,
NES_IDX_PHY_PCS_CONTROL_STATUS0 +
(0x200 * (nesdev->mac_index & 1)));
- if ((u32temp & link_mask) == link_val)
- nesvnic->linkup = 1;
+
+ if (phy_type == NES_PHY_TYPE_SFP_D) {
+ if (phy_data & 0x0004)
+ nesvnic->linkup = 1;
+ } else {
+ if ((u32temp & link_mask) == link_val)
+ nesvnic->linkup = 1;
+ }
/* clear the MAC interrupt status, assumes direct logical to physical mapping */
u32temp = nes_read_indexed(nesdev, NES_IDX_MAC_INT_STATUS + (0x200 * nesdev->mac_index));
diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c
index 99933e4e48ff..26d8018c0a7c 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.c
+++ b/drivers/infiniband/hw/nes/nes_verbs.c
@@ -3936,6 +3936,30 @@ struct nes_ib_device *nes_init_ofa_device(struct net_device *netdev)
return nesibdev;
}
+
+/**
+ * nes_handle_delayed_event
+ */
+static void nes_handle_delayed_event(unsigned long data)
+{
+ struct nes_vnic *nesvnic = (void *) data;
+
+ if (nesvnic->delayed_event != nesvnic->last_dispatched_event) {
+ struct ib_event event;
+
+ event.device = &nesvnic->nesibdev->ibdev;
+ if (!event.device)
+ goto stop_timer;
+ event.event = nesvnic->delayed_event;
+ event.element.port_num = nesvnic->logical_port + 1;
+ ib_dispatch_event(&event);
+ }
+
+stop_timer:
+ nesvnic->event_timer.function = NULL;
+}
+
+
void nes_port_ibevent(struct nes_vnic *nesvnic)
{
struct nes_ib_device *nesibdev = nesvnic->nesibdev;
@@ -3944,7 +3968,18 @@ void nes_port_ibevent(struct nes_vnic *nesvnic)
event.device = &nesibdev->ibdev;
event.element.port_num = nesvnic->logical_port + 1;
event.event = nesdev->iw_status ? IB_EVENT_PORT_ACTIVE : IB_EVENT_PORT_ERR;
- ib_dispatch_event(&event);
+
+ if (!nesvnic->event_timer.function) {
+ ib_dispatch_event(&event);
+ nesvnic->last_dispatched_event = event.event;
+ nesvnic->event_timer.function = nes_handle_delayed_event;
+ nesvnic->event_timer.data = (unsigned long) nesvnic;
+ nesvnic->event_timer.expires = jiffies + NES_EVENT_DELAY;
+ add_timer(&nesvnic->event_timer);
+ } else {
+ mod_timer(&nesvnic->event_timer, jiffies + NES_EVENT_DELAY);
+ }
+ nesvnic->delayed_event = event.event;
}