This allows adding new transports to an existing multipath switch. This is similar to what the `nconnect` mount parameter does, but instead we can do this while the NFS mount is live. For example: echo 'dstaddr 192.168.40.8 kind rdma' \ > /sys/kernel/sunrpc/client/0/multipath/add Signed-off-by: Dan Aloni <dan@xxxxxxxxxxxx> --- net/sunrpc/sysfs.c | 104 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/sysfs.c b/net/sunrpc/sysfs.c index 3592f3b862b2..d745dfb7cef4 100644 --- a/net/sunrpc/sysfs.c +++ b/net/sunrpc/sysfs.c @@ -315,18 +315,116 @@ static ssize_t rpc_netns_multipath_list_show(struct kobject *kobj, return pos; } -static ssize_t rpc_netns_multipath_list_store(struct kobject *kobj, +static ssize_t rpc_netns_multipath_add_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { - return -EINVAL; + struct rpc_netns_multipath *c = + container_of(kobj, struct rpc_netns_multipath, kobject); + struct rpc_xprt_switch *xps = c->xps; + struct rpc_xprt_iter xpi; + struct sockaddr_storage addr; + struct xprt_create xprt_args = { + .ident = XPRT_TRANSPORT_RDMA, + .net = c->net, + .dstaddr = (struct sockaddr *)&addr, + }; + struct rpc_xprt *existing_xprt; + char *opt, *str, *arg; + bool has_dstaddr = false; + bool has_kind = false; + int err; + + opt = kstrndup(buf, count, GFP_KERNEL); + if (!opt) + return -ENOMEM; + + str = strstrip(opt); + + while (1) { + arg = strsep(&str, " "); + if (!arg) + break; + + if (sysfs_streq(arg, "kind")) { + arg = strsep(&str, " "); + if (!arg) { + err = -EINVAL; + goto out; + } + if (has_kind) { + err = -EINVAL; + goto out; + } + + if (sysfs_streq(arg, "rdma")) { + xprt_args.ident = XPRT_TRANSPORT_RDMA; + } else if (sysfs_streq(arg, "tcp")) { + xprt_args.ident = XPRT_TRANSPORT_TCP; + } else { + err = -EINVAL; + goto out; + } + + has_kind = true; + } else if (sysfs_streq(arg, "dstaddr")) { + arg = strsep(&str, " "); + if (!arg) { + err = -EINVAL; + goto out; + } + xprt_args.addrlen = rpc_pton(c->net, + arg, strlen(arg), (struct sockaddr *)&addr, + sizeof(addr)); + if (has_dstaddr || !xprt_args.addrlen) { + err = -EINVAL; + goto out; + } + + has_dstaddr = true; + } else { + break; + } + } + + if (!has_dstaddr || !has_kind) { + err = -EINVAL; + goto out; + } + + + /* Discover an existing xprt */ + xprt_iter_init_listall(&xpi, xps); + existing_xprt = xprt_iter_get_next(&xpi); + xprt_iter_destroy(&xpi); + + if (!existing_xprt) { + err = -ENOENT; + goto out; + } + + xprt_args.servername = existing_xprt->servername; + xprt_iter_init_listall(&xpi, xps); + rpc_add_xprt(&xpi, NULL, &xprt_args, NULL, NULL); + xprt_iter_destroy(&xpi); + + xprt_put(existing_xprt); + + err = count; +out: + kfree(opt); + + return err; } static struct kobj_attribute rpc_netns_multipath_list = __ATTR(list, - 0644, rpc_netns_multipath_list_show, rpc_netns_multipath_list_store); + 0444, rpc_netns_multipath_list_show, NULL); +static struct kobj_attribute rpc_netns_multipath_add = __ATTR(add, + 0200, NULL, rpc_netns_multipath_add_store); static struct attribute *rpc_netns_multipath_attrs[] = { &rpc_netns_multipath_list.attr, + &rpc_netns_multipath_add.attr, NULL, }; -- 2.26.2