From: Paul Durrant Date: Wed, 30 Sep 2015 15:13:59 +0000 (+0100) Subject: xen-netback: add support for multi-queue hash mapping table X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=436975a2617898dbaa91acb5b0fe5bc6a682d2a5;p=people%2Fpauldu%2Flinux.git xen-netback: add support for multi-queue hash mapping table Signed-off-by: Paul Durrant --- diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index 6dc76c1e807b..91272ca419e6 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h @@ -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; diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index e7bd63eb2876..739f3a255baa 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -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, diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 929a6e7e5ecf..1a18a79acac5 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -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;