liquidio CN23XX: VF link status
authorRaghu Vatsavayi <rvatsavayi@caviumnetworks.com>
Wed, 7 Dec 2016 16:54:32 +0000 (08:54 -0800)
committerDavid S. Miller <davem@davemloft.net>
Thu, 8 Dec 2016 18:21:39 +0000 (13:21 -0500)
Adds support for VF link status related changes.

Signed-off-by: Raghu Vatsavayi <raghu.vatsavayi@caviumnetworks.com>
Signed-off-by: Derek Chickles <derek.chickles@caviumnetworks.com>
Signed-off-by: Satanand Burla <satananda.burla@caviumnetworks.com>
Signed-off-by: Felix Manlunas <felix.manlunas@caviumnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/cavium/liquidio/lio_vf_main.c

index f05a7c3c7b7f094535105f3bca780e824d60fa31..07e4864d7ab601f3771d6a01981b2b4368747b11 100644 (file)
@@ -163,6 +163,186 @@ static void ifstate_reset(struct lio *lio, int state_flag)
        atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) & ~(state_flag)));
 }
 
+/**
+ * \brief Stop Tx queues
+ * @param netdev network device
+ */
+static void txqs_stop(struct net_device *netdev)
+{
+       if (netif_is_multiqueue(netdev)) {
+               int i;
+
+               for (i = 0; i < netdev->num_tx_queues; i++)
+                       netif_stop_subqueue(netdev, i);
+       } else {
+               netif_stop_queue(netdev);
+       }
+}
+
+/**
+ * \brief Start Tx queues
+ * @param netdev network device
+ */
+static void txqs_start(struct net_device *netdev)
+{
+       if (netif_is_multiqueue(netdev)) {
+               int i;
+
+               for (i = 0; i < netdev->num_tx_queues; i++)
+                       netif_start_subqueue(netdev, i);
+       } else {
+               netif_start_queue(netdev);
+       }
+}
+
+/**
+ * \brief Wake Tx queues
+ * @param netdev network device
+ */
+static void txqs_wake(struct net_device *netdev)
+{
+       struct lio *lio = GET_LIO(netdev);
+
+       if (netif_is_multiqueue(netdev)) {
+               int i;
+
+               for (i = 0; i < netdev->num_tx_queues; i++) {
+                       int qno = lio->linfo.txpciq[i % (lio->linfo.num_txpciq)]
+                                     .s.q_no;
+                       if (__netif_subqueue_stopped(netdev, i)) {
+                               INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, qno,
+                                                         tx_restart, 1);
+                               netif_wake_subqueue(netdev, i);
+                       }
+               }
+       } else {
+               INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, lio->txq,
+                                         tx_restart, 1);
+               netif_wake_queue(netdev);
+       }
+}
+
+/**
+ * \brief Start Tx queue
+ * @param netdev network device
+ */
+static void start_txq(struct net_device *netdev)
+{
+       struct lio *lio = GET_LIO(netdev);
+
+       if (lio->linfo.link.s.link_up) {
+               txqs_start(netdev);
+               return;
+       }
+}
+
+/**
+ * \brief Print link information
+ * @param netdev network device
+ */
+static void print_link_info(struct net_device *netdev)
+{
+       struct lio *lio = GET_LIO(netdev);
+
+       if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) {
+               struct oct_link_info *linfo = &lio->linfo;
+
+               if (linfo->link.s.link_up) {
+                       netif_info(lio, link, lio->netdev, "%d Mbps %s Duplex UP\n",
+                                  linfo->link.s.speed,
+                                  (linfo->link.s.duplex) ? "Full" : "Half");
+               } else {
+                       netif_info(lio, link, lio->netdev, "Link Down\n");
+               }
+       }
+}
+
+/**
+ * \brief Routine to notify MTU change
+ * @param work work_struct data structure
+ */
+static void octnet_link_status_change(struct work_struct *work)
+{
+       struct cavium_wk *wk = (struct cavium_wk *)work;
+       struct lio *lio = (struct lio *)wk->ctxptr;
+
+       rtnl_lock();
+       call_netdevice_notifiers(NETDEV_CHANGEMTU, lio->netdev);
+       rtnl_unlock();
+}
+
+/**
+ * \brief Sets up the mtu status change work
+ * @param netdev network device
+ */
+static int setup_link_status_change_wq(struct net_device *netdev)
+{
+       struct lio *lio = GET_LIO(netdev);
+       struct octeon_device *oct = lio->oct_dev;
+
+       lio->link_status_wq.wq = alloc_workqueue("link-status",
+                                                WQ_MEM_RECLAIM, 0);
+       if (!lio->link_status_wq.wq) {
+               dev_err(&oct->pci_dev->dev, "unable to create cavium link status wq\n");
+               return -1;
+       }
+       INIT_DELAYED_WORK(&lio->link_status_wq.wk.work,
+                         octnet_link_status_change);
+       lio->link_status_wq.wk.ctxptr = lio;
+
+       return 0;
+}
+
+static void cleanup_link_status_change_wq(struct net_device *netdev)
+{
+       struct lio *lio = GET_LIO(netdev);
+
+       if (lio->link_status_wq.wq) {
+               cancel_delayed_work_sync(&lio->link_status_wq.wk.work);
+               destroy_workqueue(lio->link_status_wq.wq);
+       }
+}
+
+/**
+ * \brief Update link status
+ * @param netdev network device
+ * @param ls link status structure
+ *
+ * Called on receipt of a link status response from the core application to
+ * update each interface's link status.
+ */
+static void update_link_status(struct net_device *netdev,
+                              union oct_link_status *ls)
+{
+       struct lio *lio = GET_LIO(netdev);
+       struct octeon_device *oct = lio->oct_dev;
+
+       if ((lio->intf_open) && (lio->linfo.link.u64 != ls->u64)) {
+               lio->linfo.link.u64 = ls->u64;
+
+               print_link_info(netdev);
+               lio->link_changes++;
+
+               if (lio->linfo.link.s.link_up) {
+                       netif_carrier_on(netdev);
+                       txqs_wake(netdev);
+               } else {
+                       netif_carrier_off(netdev);
+                       txqs_stop(netdev);
+               }
+
+               if (lio->linfo.link.s.mtu < netdev->mtu) {
+                       dev_warn(&oct->pci_dev->dev,
+                                "PF has changed the MTU for gmx port. Reducing the mtu from %d to %d\n",
+                                netdev->mtu, lio->linfo.link.s.mtu);
+                       lio->mtu = lio->linfo.link.s.mtu;
+                       netdev->mtu = lio->linfo.link.s.mtu;
+                       queue_delayed_work(lio->link_status_wq.wq,
+                                          &lio->link_status_wq.wk.work, 0);
+               }
+       }
+}
+
 static
 int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret)
 {
@@ -499,6 +679,8 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx)
        if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED)
                unregister_netdev(netdev);
 
+       cleanup_link_status_change_wq(netdev);
+
        free_netdev(netdev);
 
        oct->props[ifidx].gmxport = -1;
@@ -634,6 +816,28 @@ static u16 select_q(struct net_device *dev, struct sk_buff *skb,
        return (u16)(qindex % (lio->linfo.num_txpciq));
 }
 
+/**
+ * \brief Net device open for LiquidIO
+ * @param netdev network device
+ */
+static int liquidio_open(struct net_device *netdev)
+{
+       struct lio *lio = GET_LIO(netdev);
+       struct octeon_device *oct = lio->oct_dev;
+
+       ifstate_set(lio, LIO_IFSTATE_RUNNING);
+
+       /* Ready for link status updates */
+       lio->intf_open = 1;
+
+       netif_info(lio, ifup, lio->netdev, "Interface Open, ready for traffic\n");
+       start_txq(netdev);
+
+       dev_info(&oct->pci_dev->dev, "%s interface is opened\n", netdev->name);
+
+       return 0;
+}
+
 /**
  * \brief Net device stop for LiquidIO
  * @param netdev network device
@@ -653,6 +857,8 @@ static int liquidio_stop(struct net_device *netdev)
 
        ifstate_reset(lio, LIO_IFSTATE_RUNNING);
 
+       txqs_stop(netdev);
+
        dev_info(&oct->pci_dev->dev, "%s interface is stopped\n", netdev->name);
 
        return 0;
@@ -758,11 +964,47 @@ static int liquidio_set_features(struct net_device *netdev,
 }
 
 static const struct net_device_ops lionetdevops = {
+       .ndo_open               = liquidio_open,
+       .ndo_stop               = liquidio_stop,
        .ndo_fix_features       = liquidio_fix_features,
        .ndo_set_features       = liquidio_set_features,
        .ndo_select_queue       = select_q,
 };
 
+static int lio_nic_info(struct octeon_recv_info *recv_info, void *buf)
+{
+       struct octeon_device *oct = (struct octeon_device *)buf;
+       struct octeon_recv_pkt *recv_pkt = recv_info->recv_pkt;
+       union oct_link_status *ls;
+       int gmxport = 0;
+       int i;
+
+       if (recv_pkt->buffer_size[0] != sizeof(*ls)) {
+               dev_err(&oct->pci_dev->dev, "Malformed NIC_INFO, len=%d, ifidx=%d\n",
+                       recv_pkt->buffer_size[0],
+                       recv_pkt->rh.r_nic_info.gmxport);
+               goto nic_info_err;
+       }
+
+       gmxport = recv_pkt->rh.r_nic_info.gmxport;
+       ls = (union oct_link_status *)get_rbd(recv_pkt->buffer_ptr[0]);
+
+       octeon_swap_8B_data((u64 *)ls, (sizeof(union oct_link_status)) >> 3);
+
+       for (i = 0; i < oct->ifcount; i++) {
+               if (oct->props[i].gmxport == gmxport) {
+                       update_link_status(oct->props[i].netdev, ls);
+                       break;
+               }
+       }
+
+nic_info_err:
+       for (i = 0; i < recv_pkt->buffer_count; i++)
+               recv_buffer_free(recv_pkt->buffer_ptr[i]);
+       octeon_free_recv_info(recv_info);
+       return 0;
+}
+
 /**
  * \brief Setup network interfaces
  * @param octeon_dev  octeon device
@@ -788,6 +1030,10 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
 
        ifidx_or_pfnum = octeon_dev->pf_num;
 
+       /* This is to handle link status changes */
+       octeon_register_dispatch_fn(octeon_dev, OPCODE_NIC, OPCODE_NIC_INFO,
+                                   lio_nic_info, octeon_dev);
+
        for (i = 0; i < octeon_dev->ifcount; i++) {
                resp_size = sizeof(struct liquidio_if_cfg_resp);
                ctx_size = sizeof(struct liquidio_if_cfg_context);
@@ -946,6 +1192,9 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
                        liquidio_set_feature(netdev, OCTNET_CMD_VERBOSE_ENABLE,
                                             0);
 
+               if (setup_link_status_change_wq(netdev))
+                       goto setup_nic_dev_fail;
+
                /* Register the network device with the OS */
                if (register_netdev(netdev)) {
                        dev_err(&octeon_dev->pci_dev->dev, "Device registration failed\n");