sunrpc: add dst_attr attributes to the sysfs xprt directory
authorOlga Kornievskaia <kolga@netapp.com>
Tue, 8 Jun 2021 19:59:19 +0000 (15:59 -0400)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Thu, 8 Jul 2021 18:03:24 +0000 (14:03 -0400)
Allow to query and set the destination's address of a transport.
Setting of the destination address is allowed only for TCP or RDMA
based connections.

Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
include/linux/sunrpc/xprt.h
net/sunrpc/sysfs.c
net/sunrpc/xprt.c
net/sunrpc/xprtmultipath.c

index 8360db6..13a4eaf 100644 (file)
@@ -414,6 +414,7 @@ void                        xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie);
 
 bool                   xprt_lock_connect(struct rpc_xprt *, struct rpc_task *, void *);
 void                   xprt_unlock_connect(struct rpc_xprt *, void *);
+void                   xprt_release_write(struct rpc_xprt *, struct rpc_task *);
 
 /*
  * Reserved bit positions in xprt->state
index 20f7570..402924b 100644 (file)
@@ -4,8 +4,23 @@
  */
 #include <linux/sunrpc/clnt.h>
 #include <linux/kobject.h>
+#include <linux/sunrpc/addr.h>
+
 #include "sysfs.h"
 
+struct xprt_addr {
+       const char *addr;
+       struct rcu_head rcu;
+};
+
+static void free_xprt_addr(struct rcu_head *head)
+{
+       struct xprt_addr *addr = container_of(head, struct xprt_addr, rcu);
+
+       kfree(addr->addr);
+       kfree(addr);
+}
+
 static struct kset *rpc_sunrpc_kset;
 static struct kobject *rpc_sunrpc_client_kobj, *rpc_sunrpc_xprt_switch_kobj;
 
@@ -43,6 +58,87 @@ static struct kobject *rpc_sysfs_object_alloc(const char *name,
        return NULL;
 }
 
+static inline struct rpc_xprt *
+rpc_sysfs_xprt_kobj_get_xprt(struct kobject *kobj)
+{
+       struct rpc_sysfs_xprt *x = container_of(kobj,
+               struct rpc_sysfs_xprt, kobject);
+
+       return xprt_get(x->xprt);
+}
+
+static ssize_t rpc_sysfs_xprt_dstaddr_show(struct kobject *kobj,
+                                          struct kobj_attribute *attr,
+                                          char *buf)
+{
+       struct rpc_xprt *xprt = rpc_sysfs_xprt_kobj_get_xprt(kobj);
+       ssize_t ret;
+
+       if (!xprt)
+               return 0;
+       ret = sprintf(buf, "%s\n", xprt->address_strings[RPC_DISPLAY_ADDR]);
+       xprt_put(xprt);
+       return ret + 1;
+}
+
+static ssize_t rpc_sysfs_xprt_dstaddr_store(struct kobject *kobj,
+                                           struct kobj_attribute *attr,
+                                           const char *buf, size_t count)
+{
+       struct rpc_xprt *xprt = rpc_sysfs_xprt_kobj_get_xprt(kobj);
+       struct sockaddr *saddr;
+       char *dst_addr;
+       int port;
+       struct xprt_addr *saved_addr;
+       size_t buf_len;
+
+       if (!xprt)
+               return 0;
+       if (!(xprt->xprt_class->ident == XPRT_TRANSPORT_TCP ||
+             xprt->xprt_class->ident == XPRT_TRANSPORT_RDMA)) {
+               xprt_put(xprt);
+               return -EOPNOTSUPP;
+       }
+
+       if (wait_on_bit_lock(&xprt->state, XPRT_LOCKED, TASK_KILLABLE)) {
+               count = -EINTR;
+               goto out_put;
+       }
+       saddr = (struct sockaddr *)&xprt->addr;
+       port = rpc_get_port(saddr);
+
+       /* buf_len is the len until the first occurence of either
+        * '\n' or '\0'
+        */
+       buf_len = strcspn(buf, "\n");
+
+       dst_addr = kstrndup(buf, buf_len, GFP_KERNEL);
+       if (!dst_addr)
+               goto out_err;
+       saved_addr = kzalloc(sizeof(*saved_addr), GFP_KERNEL);
+       if (!saved_addr)
+               goto out_err_free;
+       saved_addr->addr =
+               rcu_dereference_raw(xprt->address_strings[RPC_DISPLAY_ADDR]);
+       rcu_assign_pointer(xprt->address_strings[RPC_DISPLAY_ADDR], dst_addr);
+       call_rcu(&saved_addr->rcu, free_xprt_addr);
+       xprt->addrlen = rpc_pton(xprt->xprt_net, buf, buf_len, saddr,
+                                sizeof(*saddr));
+       rpc_set_port(saddr, port);
+
+       xprt_force_disconnect(xprt);
+out:
+       xprt_release_write(xprt, NULL);
+out_put:
+       xprt_put(xprt);
+       return count;
+out_err_free:
+       kfree(dst_addr);
+out_err:
+       count = -ENOMEM;
+       goto out;
+}
+
 int rpc_sysfs_init(void)
 {
        rpc_sunrpc_kset = kset_create_and_add("sunrpc", NULL, kernel_kobj);
@@ -106,6 +202,14 @@ static const void *rpc_sysfs_xprt_namespace(struct kobject *kobj)
                            kobject)->xprt->xprt_net;
 }
 
+static struct kobj_attribute rpc_sysfs_xprt_dstaddr = __ATTR(dstaddr,
+       0644, rpc_sysfs_xprt_dstaddr_show, rpc_sysfs_xprt_dstaddr_store);
+
+static struct attribute *rpc_sysfs_xprt_attrs[] = {
+       &rpc_sysfs_xprt_dstaddr.attr,
+       NULL,
+};
+
 static struct kobj_type rpc_sysfs_client_type = {
        .release = rpc_sysfs_client_release,
        .sysfs_ops = &kobj_sysfs_ops,
@@ -120,6 +224,7 @@ static struct kobj_type rpc_sysfs_xprt_switch_type = {
 
 static struct kobj_type rpc_sysfs_xprt_type = {
        .release = rpc_sysfs_xprt_release,
+       .default_attrs = rpc_sysfs_xprt_attrs,
        .sysfs_ops = &kobj_sysfs_ops,
        .namespace = rpc_sysfs_xprt_namespace,
 };
index 20b9bd7..fb6db09 100644 (file)
@@ -55,6 +55,7 @@
 #include <trace/events/sunrpc.h>
 
 #include "sunrpc.h"
+#include "sysfs.h"
 
 /*
  * Local variables
@@ -443,7 +444,7 @@ void xprt_release_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task)
 }
 EXPORT_SYMBOL_GPL(xprt_release_xprt_cong);
 
-static inline void xprt_release_write(struct rpc_xprt *xprt, struct rpc_task *task)
+void xprt_release_write(struct rpc_xprt *xprt, struct rpc_task *task)
 {
        if (xprt->snd_task != task)
                return;
@@ -1812,6 +1813,7 @@ void xprt_free(struct rpc_xprt *xprt)
        put_net(xprt->xprt_net);
        xprt_free_all_slots(xprt);
        xprt_free_id(xprt);
+       rpc_sysfs_xprt_destroy(xprt);
        kfree_rcu(xprt, rcu);
 }
 EXPORT_SYMBOL_GPL(xprt_free);
index e7973c1..07e76ae 100644 (file)
@@ -86,7 +86,6 @@ void rpc_xprt_switch_remove_xprt(struct rpc_xprt_switch *xps,
        spin_lock(&xps->xps_lock);
        xprt_switch_remove_xprt_locked(xps, xprt);
        spin_unlock(&xps->xps_lock);
-       rpc_sysfs_xprt_destroy(xprt);
        xprt_put(xprt);
 }
 
@@ -155,7 +154,6 @@ static void xprt_switch_free_entries(struct rpc_xprt_switch *xps)
                                struct rpc_xprt, xprt_switch);
                xprt_switch_remove_xprt_locked(xps, xprt);
                spin_unlock(&xps->xps_lock);
-               rpc_sysfs_xprt_destroy(xprt);
                xprt_put(xprt);
                spin_lock(&xps->xps_lock);
        }