fm10k: Add support for multiple queues
authorAlexander Duyck <alexander.h.duyck@intel.com>
Sat, 20 Sep 2014 23:50:42 +0000 (19:50 -0400)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Tue, 23 Sep 2014 10:59:19 +0000 (03:59 -0700)
This patch takes the driver from supporting a single queue to supporting
multiple queues.  The upper queue limit for the PF is 128 queues and the
upper limit for the VF is (128 / num_vfs) rounded down to nearest power of 2.

Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/fm10k/fm10k.h
drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
drivers/net/ethernet/intel/fm10k/fm10k_main.c
drivers/net/ethernet/intel/fm10k/fm10k_netdev.c

index d8b0d343bf9efa38b9835e85c1238436c3dfadcc..4b6f4064c4fc71e627d77fb58027a2899b1eb9a5 100644 (file)
@@ -422,6 +422,7 @@ void fm10k_unmap_and_free_tx_resource(struct fm10k_ring *,
                                      struct fm10k_tx_buffer *);
 void fm10k_restore_rx_state(struct fm10k_intfc *);
 void fm10k_reset_rx_state(struct fm10k_intfc *);
+int fm10k_setup_tc(struct net_device *dev, u8 tc);
 int fm10k_open(struct net_device *netdev);
 int fm10k_close(struct net_device *netdev);
 
index a88c75c70b9192ef804472bdf7ec560aeff47adf..54e8ebd9fbe43dadc214d3c84e32fb89212e9db3 100644 (file)
@@ -838,6 +838,61 @@ static int fm10k_set_rssh(struct net_device *netdev, const u32 *indir,
        return 0;
 }
 
+static unsigned int fm10k_max_channels(struct net_device *dev)
+{
+       struct fm10k_intfc *interface = netdev_priv(dev);
+       unsigned int max_combined = interface->hw.mac.max_queues;
+       u8 tcs = netdev_get_num_tc(dev);
+
+       /* For QoS report channels per traffic class */
+       if (tcs > 1)
+               max_combined = 1 << (fls(max_combined / tcs) - 1);
+
+       return max_combined;
+}
+
+static void fm10k_get_channels(struct net_device *dev,
+                              struct ethtool_channels *ch)
+{
+       struct fm10k_intfc *interface = netdev_priv(dev);
+       struct fm10k_hw *hw = &interface->hw;
+
+       /* report maximum channels */
+       ch->max_combined = fm10k_max_channels(dev);
+
+       /* report info for other vector */
+       ch->max_other = NON_Q_VECTORS(hw);
+       ch->other_count = ch->max_other;
+
+       /* record RSS queues */
+       ch->combined_count = interface->ring_feature[RING_F_RSS].indices;
+}
+
+static int fm10k_set_channels(struct net_device *dev,
+                             struct ethtool_channels *ch)
+{
+       struct fm10k_intfc *interface = netdev_priv(dev);
+       unsigned int count = ch->combined_count;
+       struct fm10k_hw *hw = &interface->hw;
+
+       /* verify they are not requesting separate vectors */
+       if (!count || ch->rx_count || ch->tx_count)
+               return -EINVAL;
+
+       /* verify other_count has not changed */
+       if (ch->other_count != NON_Q_VECTORS(hw))
+               return -EINVAL;
+
+       /* verify the number of channels does not exceed hardware limits */
+       if (count > fm10k_max_channels(dev))
+               return -EINVAL;
+
+       interface->ring_feature[RING_F_RSS].limit = count;
+
+       /* use setup TC to update any traffic class queue mapping */
+       return fm10k_setup_tc(dev, netdev_get_num_tc(dev));
+}
+
 static const struct ethtool_ops fm10k_ethtool_ops = {
        .get_strings            = fm10k_get_strings,
        .get_sset_count         = fm10k_get_sset_count,
@@ -860,6 +915,8 @@ static const struct ethtool_ops fm10k_ethtool_ops = {
        .get_rxfh_key_size      = fm10k_get_rssrk_size,
        .get_rxfh               = fm10k_get_rssh,
        .set_rxfh               = fm10k_set_rssh,
+       .get_channels           = fm10k_get_channels,
+       .set_channels           = fm10k_set_channels,
 };
 
 void fm10k_set_ethtool_ops(struct net_device *dev)
index f7220d841336203e13297cc0639d17eb603bff9b..dae82d29af523ef41630c5253e02e52c7f41e351 100644 (file)
@@ -1085,6 +1085,81 @@ static int fm10k_poll(struct napi_struct *napi, int budget)
        return 0;
 }
 
+/**
+ * fm10k_set_qos_queues: Allocate queues for a QOS-enabled device
+ * @interface: board private structure to initialize
+ *
+ * When QoS (Quality of Service) is enabled, allocate queues for
+ * each traffic class.  If multiqueue isn't available,then abort QoS
+ * initialization.
+ *
+ * This function handles all combinations of Qos and RSS.
+ *
+ **/
+static bool fm10k_set_qos_queues(struct fm10k_intfc *interface)
+{
+       struct net_device *dev = interface->netdev;
+       struct fm10k_ring_feature *f;
+       int rss_i, i;
+       int pcs;
+
+       /* Map queue offset and counts onto allocated tx queues */
+       pcs = netdev_get_num_tc(dev);
+
+       if (pcs <= 1)
+               return false;
+
+       /* set QoS mask and indices */
+       f = &interface->ring_feature[RING_F_QOS];
+       f->indices = pcs;
+       f->mask = (1 << fls(pcs - 1)) - 1;
+
+       /* determine the upper limit for our current DCB mode */
+       rss_i = interface->hw.mac.max_queues / pcs;
+       rss_i = 1 << (fls(rss_i) - 1);
+
+       /* set RSS mask and indices */
+       f = &interface->ring_feature[RING_F_RSS];
+       rss_i = min_t(u16, rss_i, f->limit);
+       f->indices = rss_i;
+       f->mask = (1 << fls(rss_i - 1)) - 1;
+
+       /* configure pause class to queue mapping */
+       for (i = 0; i < pcs; i++)
+               netdev_set_tc_queue(dev, i, rss_i, rss_i * i);
+
+       interface->num_rx_queues = rss_i * pcs;
+       interface->num_tx_queues = rss_i * pcs;
+
+       return true;
+}
+
+/**
+ * fm10k_set_rss_queues: Allocate queues for RSS
+ * @interface: board private structure to initialize
+ *
+ * This is our "base" multiqueue mode.  RSS (Receive Side Scaling) will try
+ * to allocate one Rx queue per CPU, and if available, one Tx queue per CPU.
+ *
+ **/
+static bool fm10k_set_rss_queues(struct fm10k_intfc *interface)
+{
+       struct fm10k_ring_feature *f;
+       u16 rss_i;
+
+       f = &interface->ring_feature[RING_F_RSS];
+       rss_i = min_t(u16, interface->hw.mac.max_queues, f->limit);
+
+       /* record indices and power of 2 mask for RSS */
+       f->indices = rss_i;
+       f->mask = (1 << fls(rss_i - 1)) - 1;
+
+       interface->num_rx_queues = rss_i;
+       interface->num_tx_queues = rss_i;
+
+       return true;
+}
+
 /**
  * fm10k_set_num_queues: Allocate queues for device, feature dependent
  * @interface: board private structure to initialize
@@ -1101,6 +1176,11 @@ static void fm10k_set_num_queues(struct fm10k_intfc *interface)
        /* Start with base case */
        interface->num_rx_queues = 1;
        interface->num_tx_queues = 1;
+
+       if (fm10k_set_qos_queues(interface))
+               return;
+
+       fm10k_set_rss_queues(interface);
 }
 
 /**
@@ -1381,6 +1461,71 @@ static int fm10k_init_msix_capability(struct fm10k_intfc *interface)
        return 0;
 }
 
+/**
+ * fm10k_cache_ring_qos - Descriptor ring to register mapping for QoS
+ * @interface: Interface structure continaining rings and devices
+ *
+ * Cache the descriptor ring offsets for Qos
+ **/
+static bool fm10k_cache_ring_qos(struct fm10k_intfc *interface)
+{
+       struct net_device *dev = interface->netdev;
+       int pc, offset, rss_i, i, q_idx;
+       u16 pc_stride = interface->ring_feature[RING_F_QOS].mask + 1;
+       u8 num_pcs = netdev_get_num_tc(dev);
+
+       if (num_pcs <= 1)
+               return false;
+
+       rss_i = interface->ring_feature[RING_F_RSS].indices;
+
+       for (pc = 0, offset = 0; pc < num_pcs; pc++, offset += rss_i) {
+               q_idx = pc;
+               for (i = 0; i < rss_i; i++) {
+                       interface->tx_ring[offset + i]->reg_idx = q_idx;
+                       interface->tx_ring[offset + i]->qos_pc = pc;
+                       interface->rx_ring[offset + i]->reg_idx = q_idx;
+                       interface->rx_ring[offset + i]->qos_pc = pc;
+                       q_idx += pc_stride;
+               }
+       }
+
+       return true;
+}
+
+/**
+ * fm10k_cache_ring_rss - Descriptor ring to register mapping for RSS
+ * @interface: Interface structure continaining rings and devices
+ *
+ * Cache the descriptor ring offsets for RSS
+ **/
+static void fm10k_cache_ring_rss(struct fm10k_intfc *interface)
+{
+       int i;
+
+       for (i = 0; i < interface->num_rx_queues; i++)
+               interface->rx_ring[i]->reg_idx = i;
+
+       for (i = 0; i < interface->num_tx_queues; i++)
+               interface->tx_ring[i]->reg_idx = i;
+}
+
+/**
+ * fm10k_assign_rings - Map rings to network devices
+ * @interface: Interface structure containing rings and devices
+ *
+ * This function is meant to go though and configure both the network
+ * devices so that they contain rings, and configure the rings so that
+ * they function with their network devices.
+ **/
+static void fm10k_assign_rings(struct fm10k_intfc *interface)
+{
+       if (fm10k_cache_ring_qos(interface))
+               return;
+
+       fm10k_cache_ring_rss(interface);
+}
+
 static void fm10k_init_reta(struct fm10k_intfc *interface)
 {
        u16 i, rss_i = interface->ring_feature[RING_F_RSS].indices;
@@ -1448,6 +1593,9 @@ int fm10k_init_queueing_scheme(struct fm10k_intfc *interface)
        if (err)
                return err;
 
+       /* Map rings to devices, and map devices to physical queues */
+       fm10k_assign_rings(interface);
+
        /* Initialize RSS redirection table */
        fm10k_init_reta(interface);
 
index b9b1459b46975e1e901287619969f72c9c3e4142..2cda9f99edc38f9d5906d3f2e4695ffc8b903203 100644 (file)
@@ -973,6 +973,46 @@ static struct rtnl_link_stats64 *fm10k_get_stats64(struct net_device *netdev,
        return stats;
 }
 
+int fm10k_setup_tc(struct net_device *dev, u8 tc)
+{
+       struct fm10k_intfc *interface = netdev_priv(dev);
+
+       /* Currently only the PF supports priority classes */
+       if (tc && (interface->hw.mac.type != fm10k_mac_pf))
+               return -EINVAL;
+
+       /* Hardware supports up to 8 traffic classes */
+       if (tc > 8)
+               return -EINVAL;
+
+       /* Hardware has to reinitialize queues to match packet
+        * buffer alignment. Unfortunately, the hardware is not
+        * flexible enough to do this dynamically.
+        */
+       if (netif_running(dev))
+               fm10k_close(dev);
+
+       fm10k_mbx_free_irq(interface);
+
+       fm10k_clear_queueing_scheme(interface);
+
+       /* we expect the prio_tc map to be repopulated later */
+       netdev_reset_tc(dev);
+       netdev_set_num_tc(dev, tc);
+
+       fm10k_init_queueing_scheme(interface);
+
+       fm10k_mbx_request_irq(interface);
+
+       if (netif_running(dev))
+               fm10k_open(dev);
+
+       /* flag to indicate SWPRI has yet to be updated */
+       interface->flags |= FM10K_FLAG_SWPRI_CONFIG;
+
+       return 0;
+}
+
 static const struct net_device_ops fm10k_netdev_ops = {
        .ndo_open               = fm10k_open,
        .ndo_stop               = fm10k_close,
@@ -985,6 +1025,7 @@ static const struct net_device_ops fm10k_netdev_ops = {
        .ndo_vlan_rx_kill_vid   = fm10k_vlan_rx_kill_vid,
        .ndo_set_rx_mode        = fm10k_set_rx_mode,
        .ndo_get_stats64        = fm10k_get_stats64,
+       .ndo_setup_tc           = fm10k_setup_tc,
 };
 
 #define DEFAULT_DEBUG_LEVEL_SHIFT 3