diff mbox series

[v3,5/5] sunrpc: Create a per-rpc_clnt file for managing the destination IP address

Message ID 20210312211826.360959-6-Anna.Schumaker@Netapp.com (mailing list archive)
State New, archived
Headers show
Series SUNRPC: Create sysfs files for changing IP address | expand

Commit Message

Anna Schumaker March 12, 2021, 9:18 p.m. UTC
From: Anna Schumaker <Anna.Schumaker@Netapp.com>

Reading the file displays the current destination address, and writing
to it allows users to change the address.

And since we're using IP here, restrict to only creating sysfs files for
TCP and RDMA connections.

Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
---
v3: Fix suspicious RCU usage warnings
    Fix up xprt locking and reference counting
    s/netns/sysfs/
    Unconditionally create files instead of looking at protocol type
        during client setup (this makes it easier for userspace tools)
    Add a newline character to the buffer returned in "show" handler
v2: Change filename and related functions from "address" to "dstaddr"
    Combine patches for reading and writing to this file
---
 net/sunrpc/sysfs.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++
 net/sunrpc/sysfs.h |  1 +
 2 files changed, 71 insertions(+)
diff mbox series

Patch

diff --git a/net/sunrpc/sysfs.c b/net/sunrpc/sysfs.c
index abfe8c0b3108..1ae15c4729c0 100644
--- a/net/sunrpc/sysfs.c
+++ b/net/sunrpc/sysfs.c
@@ -3,6 +3,8 @@ 
  * Copyright (c) 2020 Anna Schumaker <Anna.Schumaker@Netapp.com>
  */
 #include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/addr.h>
+#include <linux/sunrpc/xprtsock.h>
 #include <linux/kobject.h>
 #include "sysfs.h"
 
@@ -55,6 +57,64 @@  int rpc_sysfs_init(void)
 	return 0;
 }
 
+static inline struct rpc_xprt *rpc_sysfs_client_kobj_get_xprt(struct kobject *kobj)
+{
+	struct rpc_sysfs_client *c = container_of(kobj,
+				struct rpc_sysfs_client, kobject);
+	struct rpc_xprt *xprt;
+
+	rcu_read_lock();
+	xprt = xprt_get(rcu_dereference(c->clnt->cl_xprt));
+	rcu_read_unlock();
+	return xprt;
+}
+
+static ssize_t rpc_sysfs_dstaddr_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct rpc_xprt *xprt = rpc_sysfs_client_kobj_get_xprt(kobj);
+	ssize_t ret;
+
+	if (!xprt)
+		return 0;
+	if (!(xprt->prot & (IPPROTO_TCP | XPRT_TRANSPORT_RDMA)))
+		ret = sprintf(buf, "Not Supported");
+	else
+		ret = rpc_ntop((struct sockaddr *)&xprt->addr, buf, PAGE_SIZE);
+	buf[ret] = '\n';
+	xprt_put(xprt);
+	return ret + 1;
+}
+
+static ssize_t rpc_sysfs_dstaddr_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct rpc_xprt *xprt = rpc_sysfs_client_kobj_get_xprt(kobj);
+	struct sockaddr *saddr;
+	int port;
+
+	if (!xprt)
+		return 0;
+	if (!(xprt->prot & (IPPROTO_TCP | XPRT_TRANSPORT_RDMA))) {
+		xprt_put(xprt);
+		return -ENOTSUPP;
+	}
+
+	wait_on_bit_lock(&xprt->state, XPRT_LOCKED, TASK_KILLABLE);
+	saddr = (struct sockaddr *)&xprt->addr;
+	port = rpc_get_port(saddr);
+
+	kfree(xprt->address_strings[RPC_DISPLAY_ADDR]);
+	xprt->address_strings[RPC_DISPLAY_ADDR] = kstrndup(buf, count - 1, GFP_KERNEL);
+	xprt->addrlen = rpc_pton(xprt->xprt_net, buf, count - 1, saddr, sizeof(*saddr));
+	rpc_set_port(saddr, port);
+
+	xprt->ops->connect(xprt, NULL);
+	clear_bit(XPRT_LOCKED, &xprt->state);
+	xprt_put(xprt);
+	return count;
+}
+
 static void rpc_sysfs_client_release(struct kobject *kobj)
 {
 	struct rpc_sysfs_client *c;
@@ -68,8 +128,17 @@  static const void *rpc_sysfs_client_namespace(struct kobject *kobj)
 	return container_of(kobj, struct rpc_sysfs_client, kobject)->net;
 }
 
+static struct kobj_attribute rpc_sysfs_client_dstaddr = __ATTR(dstaddr,
+		0644, rpc_sysfs_dstaddr_show, rpc_sysfs_dstaddr_store);
+
+static struct attribute *rpc_sysfs_client_attrs[] = {
+	&rpc_sysfs_client_dstaddr.attr,
+	NULL,
+};
+
 static struct kobj_type rpc_sysfs_client_type = {
 	.release = rpc_sysfs_client_release,
+	.default_attrs = rpc_sysfs_client_attrs,
 	.sysfs_ops = &kobj_sysfs_ops,
 	.namespace = rpc_sysfs_client_namespace,
 };
@@ -104,6 +173,7 @@  void rpc_sysfs_client_setup(struct rpc_clnt *clnt, struct net *net)
 	rpc_client = rpc_sysfs_client_alloc(rpc_sunrpc_client_kobj, net, clnt->cl_clid);
 	if (rpc_client) {
 		clnt->cl_sysfs = rpc_client;
+		rpc_client->clnt = clnt;
 		kobject_uevent(&rpc_client->kobject, KOBJ_ADD);
 	}
 }
diff --git a/net/sunrpc/sysfs.h b/net/sunrpc/sysfs.h
index 443679d71f38..5093f93a83cb 100644
--- a/net/sunrpc/sysfs.h
+++ b/net/sunrpc/sysfs.h
@@ -8,6 +8,7 @@ 
 struct rpc_sysfs_client {
 	struct kobject kobject;
 	struct net *net;
+	struct rpc_clnt *clnt;
 };
 
 extern int rpc_sysfs_init(void);