]> xenbits.xensource.com Git - people/pauldu/linux.git/commitdiff
xen-netback: add support for multi-queue hash mapping table
authorPaul Durrant <paul.durrant@citrix.com>
Wed, 30 Sep 2015 15:13:59 +0000 (16:13 +0100)
committerPaul Durrant <paul.durrant@citrix.com>
Wed, 30 Sep 2015 15:13:59 +0000 (16:13 +0100)
Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
drivers/net/xen-netback/common.h
drivers/net/xen-netback/interface.c
drivers/net/xen-netback/xenbus.c

index 6dc76c1e807b4d0ad45ae0bbbd1ba1310ed33239..91272ca419e6404383844de94000528b9f607b37 100644 (file)
@@ -248,8 +248,10 @@ struct xenvif {
        struct xenvif_queue *queues;
        unsigned int num_queues; /* active queues, resource allocated */
        unsigned int stalled_queues;
+       int queue_mapping[128];
 
        struct xenbus_watch credit_watch;
+       struct xenbus_watch mq_watch;
 
        spinlock_t lock;
 
index e7bd63eb2876ed245a6ece5c8f24f826fb6493ed..739f3a255baaf8f092cde834be99cb6adfee940d 100644 (file)
@@ -142,6 +142,19 @@ void xenvif_wake_queue(struct xenvif_queue *queue)
        netif_tx_wake_queue(netdev_get_tx_queue(dev, id));
 }
 
+static u16 xenvif_select_queue(struct net_device *dev, struct sk_buff *skb,
+                              void *accel_priv,
+                              select_queue_fallback_t fallback)
+{
+       struct xenvif *vif = netdev_priv(dev);
+
+       if (vif->queue_mapping[0] < 0)
+               return fallback(dev, skb) % dev->real_num_tx_queues;
+
+       return vif->queue_mapping[skb_get_hash(skb) %
+                                 ARRAY_SIZE(vif->queue_mapping)];
+}
+
 static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct xenvif *vif = netdev_priv(dev);
@@ -386,6 +399,7 @@ static const struct ethtool_ops xenvif_ethtool_ops = {
 };
 
 static const struct net_device_ops xenvif_netdev_ops = {
+       .ndo_select_queue = xenvif_select_queue,
        .ndo_start_xmit = xenvif_start_xmit,
        .ndo_get_stats  = xenvif_get_stats,
        .ndo_open       = xenvif_open,
index 929a6e7e5ecfe9249569c0059516e531fb0b79eb..1a18a79acac5e37406d5679a3aad98443287b3f4 100644 (file)
@@ -359,6 +359,12 @@ static int netback_probe(struct xenbus_device *dev,
        if (err)
                pr_debug("Error writing multi-queue-max-queues\n");
 
+       /* Multi-queue mapping support: This is an optional feature. */
+       err = xenbus_printf(XBT_NIL, dev->nodename,
+                           "feature-multi-queue-mapping", "%d", 1);
+       if (err)
+               pr_debug("Error writing feature-multi-queue-mapping\n");
+
        script = xenbus_read(XBT_NIL, dev->nodename, "script", NULL);
        if (IS_ERR(script)) {
                err = PTR_ERR(script);
@@ -683,7 +689,7 @@ static void xen_net_rate_changed(struct xenbus_watch *watch,
        }
 }
 
-static int xen_register_watchers(struct xenbus_device *dev, struct xenvif *vif)
+static int xen_register_credit_watch(struct xenbus_device *dev, struct xenvif *vif)
 {
        int err = 0;
        char *node;
@@ -708,7 +714,7 @@ static int xen_register_watchers(struct xenbus_device *dev, struct xenvif *vif)
        return err;
 }
 
-static void xen_unregister_watchers(struct xenvif *vif)
+static void xen_unregister_credit_watch(struct xenvif *vif)
 {
        if (vif->credit_watch.node) {
                unregister_xenbus_watch(&vif->credit_watch);
@@ -717,6 +723,126 @@ static void xen_unregister_watchers(struct xenvif *vif)
        }
 }
 
+static void xen_net_read_multi_queue_mapping(struct xenvif *vif)
+{
+       struct xenbus_device *dev = xenvif_to_xenbus_device(vif);
+       char *str, *token;
+       unsigned int *mapping;
+       unsigned int n, i;
+
+       str = xenbus_read(XBT_NIL, dev->otherend, "multi-queue-mapping", NULL);
+       if (IS_ERR(str)) {
+               pr_info("%s: no mapping table\n", dev->nodename);
+               goto fail1;
+       }
+
+       mapping = kcalloc(ARRAY_SIZE(vif->queue_mapping), sizeof(unsigned int),
+                         GFP_KERNEL);
+       if (mapping == NULL) {
+               pr_err("%s: failed to allocate mapping table\n",
+                       dev->nodename);
+               goto fail2;
+       }
+
+       n = 0;
+       while ((token = strsep(&str, ",")) != NULL) {
+               int rc;
+
+               if (n >= ARRAY_SIZE(vif->queue_mapping)) {
+                       pr_err("%s: mapping table too big\n",
+                              dev->nodename);
+                       goto fail3;
+               }
+
+               rc = kstrtouint(token, 0, &mapping[n]);
+               if (rc < 0 || mapping[n] > vif->num_queues) {
+                       pr_err("%s: invalid mapping table value (%s at index %u)\n",
+                              dev->nodename, token, n);
+                       goto fail3;
+               }
+
+               n++;
+       }
+
+       if (n == 0 || (n & (n - 1)) != 0) {
+               pr_err("%s: invalid mapping table size (%u)\n",
+                      dev->nodename, n);
+               goto fail3;
+       }
+
+       i = ARRAY_SIZE(vif->queue_mapping);
+       while (i-- > 0)
+               vif->queue_mapping[i] = mapping[i & (n - 1)];
+
+       pr_info("%s: set mapping table\n", dev->nodename);
+
+       kfree(mapping);
+       kfree(str);
+       return;
+
+fail3:
+       kfree(mapping);
+fail2:
+       kfree(str);
+fail1:
+       vif->queue_mapping[0] = -1;
+}
+
+
+static void xen_mq_changed(struct xenbus_watch *watch,
+                          const char **vec, unsigned int len)
+{
+       struct xenvif *vif = container_of(watch, struct xenvif, mq_watch);
+
+       xen_net_read_multi_queue_mapping(vif);
+}
+
+static int xen_register_mq_watch(struct xenbus_device *dev, struct xenvif *vif)
+{
+       int err = 0;
+       char *node;
+       unsigned maxlen = strlen(dev->otherend) + sizeof("/multi-queue-mapping");
+
+       if (vif->mq_watch.node)
+               return -EADDRINUSE;
+
+       node = kmalloc(maxlen, GFP_KERNEL);
+       if (!node)
+               return -ENOMEM;
+       snprintf(node, maxlen, "%s/multi-queue-mapping", dev->otherend);
+       vif->mq_watch.node = node;
+       vif->mq_watch.callback = xen_mq_changed;
+       err = register_xenbus_watch(&vif->mq_watch);
+       if (err) {
+               pr_err("Failed to set watcher %s\n", vif->mq_watch.node);
+               kfree(node);
+               vif->mq_watch.node = NULL;
+               vif->mq_watch.callback = NULL;
+       }
+       return err;
+}
+
+static void xen_unregister_mq_watch(struct xenvif *vif)
+{
+       if (vif->mq_watch.node) {
+               unregister_xenbus_watch(&vif->mq_watch);
+               kfree(vif->mq_watch.node);
+               vif->mq_watch.node = NULL;
+       }
+}
+
+static void xen_register_watchers(struct xenbus_device *dev, struct xenvif *vif)
+{
+       xen_register_credit_watch(dev, vif);
+       xen_register_mq_watch(dev, vif);
+}
+
+static void xen_unregister_watchers(struct xenvif *vif)
+{
+       xen_unregister_mq_watch(vif);
+       xen_unregister_credit_watch(vif);
+}
+
 static void unregister_hotplug_status_watch(struct backend_info *be)
 {
        if (be->have_hotplug_status_watch) {
@@ -774,6 +900,12 @@ static void connect(struct backend_info *be)
                return;
        }
 
+       /* Use the number of queues requested by the frontend */
+       be->vif->queues = vzalloc(requested_num_queues *
+                                 sizeof(struct xenvif_queue));
+       be->vif->num_queues = requested_num_queues;
+       be->vif->stalled_queues = requested_num_queues;
+
        err = xen_net_read_mac(dev, be->vif->fe_dev_addr);
        if (err) {
                xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename);
@@ -785,12 +917,6 @@ static void connect(struct backend_info *be)
        xen_register_watchers(dev, be->vif);
        read_xenbus_vif_flags(be);
 
-       /* Use the number of queues requested by the frontend */
-       be->vif->queues = vzalloc(requested_num_queues *
-                                 sizeof(struct xenvif_queue));
-       be->vif->num_queues = requested_num_queues;
-       be->vif->stalled_queues = requested_num_queues;
-
        for (queue_index = 0; queue_index < requested_num_queues; ++queue_index) {
                queue = &be->vif->queues[queue_index];
                queue->vif = be->vif;