]> xenbits.xensource.com Git - people/pauldu/linux.git/commitdiff
xen-netback: add code to negotiate toeplitz hash and parameters
authorPaul Durrant <paul.durrant@citrix.com>
Thu, 1 Oct 2015 13:40:04 +0000 (14:40 +0100)
committerPaul Durrant <paul.durrant@citrix.com>
Fri, 9 Oct 2015 14:32:26 +0000 (15:32 +0100)
Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
drivers/net/xen-netback/common.h
drivers/net/xen-netback/xenbus.c

index 2481594c077a6dc9d16491b41dd05d86361fd17e..e6d03e2d93379854c751dc0826b2f86f9e29bc6f 100644 (file)
@@ -213,6 +213,29 @@ struct xenvif_mcast_addr {
 
 #define XEN_NETBK_MCAST_MAX 64
 
+enum xenvif_hash_alg {
+       XEN_NETBK_HASH_UNSPECIFIED,
+       XEN_NETBK_HASH_TOEPLITZ,
+};
+
+struct xenvif_toeplitz_params {
+       union {
+               struct {
+                       u8 ipv4_enabled:1;
+                       u8 ipv4_tcp_enabled:1;
+                       u8 ipv6_enabled:1;
+                       u8 ipv6_tcp_enabled:1;
+               };
+               u8 types;
+       };
+
+       u8 key[40];
+};
+
+union xenvif_hash_params {
+       struct xenvif_toeplitz_params toeplitz;
+};
+
 struct xenvif {
        /* Unique identifier for this interface. */
        domid_t          domid;
@@ -245,8 +268,14 @@ struct xenvif {
        unsigned int stalled_queues;
        int queue_mapping[128];
 
+       /* Hash */
+       enum xenvif_hash_alg hash_alg;
+       union xenvif_hash_params hash_params;
+
        struct xenbus_watch credit_watch;
        struct xenbus_watch mq_watch;
+       struct xenbus_watch hash_watch;
+       struct xenbus_watch params_watch;
 
        spinlock_t lock;
 
index ce290f9e8f4fd97898500e2c8ebbae7d5f16706a..80f73bebc07c646876e3f6f0aa0381cd6121d549 100644 (file)
@@ -246,6 +246,27 @@ static int netback_remove(struct xenbus_device *dev)
        return 0;
 }
 
+static int netback_set_toeplitz_caps(struct xenbus_device *dev)
+{
+       unsigned int len = strlen(dev->nodename) + sizeof("/multi-queue-hash-caps-toeplitz");
+       char *node;
+       int err;
+
+       node = kmalloc(len, GFP_KERNEL);
+       if (!node)
+               return -ENOMEM;
+
+       snprintf(node, len, "%s/multi-queue-hash-caps-toeplitz",
+                dev->nodename);
+
+       err = xenbus_printf(XBT_NIL, node,
+                           "types", "ipv4,ipv4+tcp,ipv6,ipv6+tcp");
+       if (err)
+               pr_debug("Error writing toeplitz capabilities\n");
+
+       kfree(node);
+       return 0;
+}
 
 /**
  * Entry point to this code when a new device is created.  Allocate the basic
@@ -365,6 +386,15 @@ static int netback_probe(struct xenbus_device *dev,
        if (err)
                pr_debug("Error writing feature-multi-queue-hash-mapping\n");
 
+       /* Selectable multi-queue hash algorithms: This is an optional feature. */
+       err = netback_set_toeplitz_caps(dev);
+       if (!err) {
+               err = xenbus_printf(XBT_NIL, dev->nodename,
+                                   "multi-queue-hash-list", "toeplitz");
+               if (err)
+                       pr_debug("Error writing multi-queue-hash-list\n");
+       }
+
        script = xenbus_read(XBT_NIL, dev->nodename, "script", NULL);
        if (IS_ERR(script)) {
                err = PTR_ERR(script);
@@ -831,14 +861,263 @@ static void xen_unregister_mq_watch(struct xenvif *vif)
        }
 }
 
+static void xen_net_read_toeplitz_types(struct xenvif *vif,
+                                       const char *node)
+{
+       struct xenbus_device *dev = xenvif_to_xenbus_device(vif);
+       char *str, *token;
+
+       vif->hash_params.toeplitz.types = 0;
+
+       str = xenbus_read(XBT_NIL, node, "types", NULL);
+       if (IS_ERR(str)) {
+               pr_info("%s: no hash types enabled\n", dev->nodename);
+               return;
+       }
+
+       while ((token = strsep(&str, ",")) != NULL) {
+               if (strcmp(token, "ipv4") == 0) {
+                       vif->hash_params.toeplitz.ipv4_enabled = 1;
+               } else if (strcmp(token, "ipv4+tcp") == 0) {
+                       vif->hash_params.toeplitz.ipv4_tcp_enabled = 1;
+               } else if (strcmp(token, "ipv6") == 0) {
+                       vif->hash_params.toeplitz.ipv6_enabled = 1;
+               } else if (strcmp(token, "ipv6+tcp") == 0) {
+                       vif->hash_params.toeplitz.ipv6_tcp_enabled = 1;
+               } else {
+                       pr_err("%s: unknown hash type (%s)\n",
+                              dev->nodename, token);
+                       goto fail1;
+               }
+       }
+
+       pr_info("%s: hash types enabled: %s%s%s%s\n", dev->nodename,
+               vif->hash_params.toeplitz.ipv4_enabled ? "ipv4 " : "",
+               vif->hash_params.toeplitz.ipv4_tcp_enabled ? "ipv4+tcp " : "",
+               vif->hash_params.toeplitz.ipv6_enabled ? "ipv6 " : "",
+               vif->hash_params.toeplitz.ipv6_tcp_enabled ? "ipv6+tcp " : "");
+
+       kfree(str);
+       return;
+
+fail1:
+       vif->hash_params.toeplitz.types = 0;
+}
+
+static void xen_net_read_toeplitz_key(struct xenvif *vif,
+                                     const char *node)
+{
+       struct xenbus_device *dev = xenvif_to_xenbus_device(vif);
+       char *str, *token;
+       u8 key[40];
+       unsigned int n, i;
+
+       str = xenbus_read(XBT_NIL, node, "key", NULL);
+       if (IS_ERR(str)) {
+               pr_info("%s: no key specified\n", dev->nodename);
+               goto fail1;
+       }
+
+       memset(key, 0, sizeof(key));
+
+       n = 0;
+       while ((token = strsep(&str, ",")) != NULL) {
+               int rc;
+
+               if (n >= ARRAY_SIZE(vif->hash_params.toeplitz.key)) {
+                       pr_err("%s: key too big\n",
+                              dev->nodename);
+                       goto fail2;
+               }
+
+               rc = kstrtou8(token, 0, &key[n]);
+               if (rc < 0) {
+                       pr_err("%s: invalid key value (%s at index %u)\n",
+                              dev->nodename, token, n);
+                       goto fail2;
+               }
+
+               n++;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(vif->hash_params.toeplitz.key); i++)
+               vif->hash_params.toeplitz.key[i] = key[i];
+
+       pr_info("%s: key is set %02x:%02x:%02x:%02x...\n", dev->nodename,
+               vif->hash_params.toeplitz.key[0],
+               vif->hash_params.toeplitz.key[1],
+               vif->hash_params.toeplitz.key[2],
+               vif->hash_params.toeplitz.key[3]);
+
+       kfree(str);
+       return;
+
+fail2:
+       kfree(str);
+fail1:
+       vif->hash_params.toeplitz.types = 0;
+}
+
+static void xen_net_read_toeplitz_params(struct xenvif *vif)
+{
+       struct xenbus_device *dev = xenvif_to_xenbus_device(vif);
+       unsigned int len = strlen(dev->otherend) + sizeof("/multi-queue-hash-params-toeplitz");
+       char *node;
+
+       node = kmalloc(len, GFP_KERNEL);
+       if (!node)
+               return;
+       snprintf(node, len, "%s/multi-queue-hash-params-toeplitz",
+                dev->otherend);
+
+       xen_net_read_toeplitz_types(vif, node);
+       xen_net_read_toeplitz_key(vif, node);
+
+       kfree(node);
+}
+
+static void xen_net_read_hash_params(struct xenvif *vif)
+{
+       switch (vif->hash_alg) {
+       case XEN_NETBK_HASH_TOEPLITZ:
+               xen_net_read_toeplitz_params(vif);
+               break;
+       default:
+               break;
+       }
+}
+
+static void xen_hash_params_changed(struct xenbus_watch *watch,
+                                   const char **vec, unsigned int len)
+{
+       struct xenvif *vif = container_of(watch, struct xenvif, params_watch);
+
+       xen_net_read_hash_params(vif);
+}
+
+static int xen_register_hash_params_watch(struct xenbus_device *dev,
+                                         struct xenvif *vif, const char *alg_name)
+{
+       int err = 0;
+       char *node;
+       unsigned maxlen = strlen(dev->otherend) + sizeof("/multi-queue-hash-params-");
+
+       if (vif->params_watch.node)
+               return -EADDRINUSE;
+
+       maxlen += sizeof(alg_name);
+
+       node = kmalloc(maxlen, GFP_KERNEL);
+       if (!node)
+               return -ENOMEM;
+       snprintf(node, maxlen, "%s/multi-queue-hash-params-%s",
+                dev->otherend, alg_name);
+       vif->params_watch.node = node;
+       vif->params_watch.callback = xen_hash_params_changed;
+       err = register_xenbus_watch(&vif->params_watch);
+       if (err) {
+               pr_err("Failed to set watcher %s\n", vif->params_watch.node);
+               kfree(node);
+               vif->params_watch.node = NULL;
+               vif->params_watch.callback = NULL;
+       }
+       return err;
+}
+
+static void xen_unregister_hash_params_watch(struct xenvif *vif)
+{
+       if (vif->params_watch.node) {
+               unregister_xenbus_watch(&vif->params_watch);
+               kfree(vif->params_watch.node);
+               vif->params_watch.node = NULL;
+       }
+}
+
+static void xen_net_read_hash(struct xenvif *vif)
+{
+       struct xenbus_device *dev = xenvif_to_xenbus_device(vif);
+       char *str;
+
+       xen_unregister_hash_params_watch(vif);
+
+       str = xenbus_read(XBT_NIL, dev->otherend, "multi-queue-hash", NULL);
+       if (IS_ERR(str))
+               goto fail1;
+
+       if (strcmp(str, "toeplitz") == 0)
+               vif->hash_alg = XEN_NETBK_HASH_TOEPLITZ;
+       else
+               vif->hash_alg = XEN_NETBK_HASH_UNSPECIFIED;
+
+       if (vif->hash_alg == XEN_NETBK_HASH_UNSPECIFIED) {
+               pr_info("%s: no hash algorithm specified\n", dev->nodename);
+       } else {
+               pr_info("%s: %s hash algorithm specified\n", dev->nodename,
+                       str);
+
+               xen_register_hash_params_watch(dev, vif, str);
+       }
+
+       kfree(str);
+       return;
+
+fail1:
+       vif->hash_alg = XEN_NETBK_HASH_UNSPECIFIED;
+}
+
+static void xen_hash_changed(struct xenbus_watch *watch,
+                            const char **vec, unsigned int len)
+{
+       struct xenvif *vif = container_of(watch, struct xenvif, hash_watch);
+
+       xen_net_read_hash(vif);
+}
+
+static int xen_register_hash_watch(struct xenbus_device *dev, struct xenvif *vif)
+{
+       int err = 0;
+       char *node;
+       unsigned maxlen = strlen(dev->otherend) + sizeof("/multi-queue-hash");
+
+       if (vif->hash_watch.node)
+               return -EADDRINUSE;
+
+       node = kmalloc(maxlen, GFP_KERNEL);
+       if (!node)
+               return -ENOMEM;
+       snprintf(node, maxlen, "%s/multi-queue-hash", dev->otherend);
+       vif->hash_watch.node = node;
+       vif->hash_watch.callback = xen_hash_changed;
+       err = register_xenbus_watch(&vif->hash_watch);
+       if (err) {
+               pr_err("Failed to set watcher %s\n", vif->hash_watch.node);
+               kfree(node);
+               vif->hash_watch.node = NULL;
+               vif->hash_watch.callback = NULL;
+       }
+       return err;
+}
+
+static void xen_unregister_hash_watch(struct xenvif *vif)
+{
+       if (vif->hash_watch.node) {
+               unregister_xenbus_watch(&vif->hash_watch);
+               kfree(vif->hash_watch.node);
+               vif->hash_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);
+       xen_register_hash_watch(dev, vif);
 }
 
 static void xen_unregister_watchers(struct xenvif *vif)
 {
+       xen_unregister_hash_params_watch(vif);
+       xen_unregister_hash_watch(vif);
        xen_unregister_mq_watch(vif);
        xen_unregister_credit_watch(vif);
 }
@@ -884,6 +1163,8 @@ static void connect(struct backend_info *be)
        unsigned int requested_num_queues;
        struct xenvif_queue *queue;
 
+       be->vif->hash_alg = XEN_NETBK_HASH_UNSPECIFIED;
+
        /* Check whether the frontend requested multiple queues
         * and read the number requested.
         */