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
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);
}
}
+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);
}
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.
*/