832 lines · 21.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Copyright (c) 2020 Anna Schumaker <Anna.Schumaker@Netapp.com> */#include <linux/sunrpc/clnt.h>#include <linux/kobject.h>#include <linux/sunrpc/addr.h>#include <linux/sunrpc/xprtsock.h>#include <net/net_namespace.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; static void rpc_sysfs_object_release(struct kobject *kobj){	kfree(kobj);} static const struct kobj_ns_type_operations *rpc_sysfs_object_child_ns_type(const struct kobject *kobj){	return &net_ns_type_operations;} static const struct kobj_type rpc_sysfs_object_type = {	.release = rpc_sysfs_object_release,	.sysfs_ops = &kobj_sysfs_ops,	.child_ns_type = rpc_sysfs_object_child_ns_type,}; static struct kobject *rpc_sysfs_object_alloc(const char *name,					      struct kset *kset,					      struct kobject *parent){	struct kobject *kobj; 	kobj = kzalloc_obj(*kobj);	if (kobj) {		kobj->kset = kset;		if (kobject_init_and_add(kobj, &rpc_sysfs_object_type,					 parent, "%s", name) == 0)			return kobj;		kobject_put(kobj);	}	return NULL;} static inline struct rpc_clnt *rpc_sysfs_client_kobj_get_clnt(struct kobject *kobj){	struct rpc_sysfs_client *c = container_of(kobj,		struct rpc_sysfs_client, kobject);	struct rpc_clnt *ret = c->clnt; 	return refcount_inc_not_zero(&ret->cl_count) ? ret : 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 inline struct rpc_xprt_switch *rpc_sysfs_xprt_kobj_get_xprt_switch(struct kobject *kobj){	struct rpc_sysfs_xprt *x = container_of(kobj,		struct rpc_sysfs_xprt, kobject); 	return xprt_switch_get(x->xprt_switch);} static inline struct rpc_xprt_switch *rpc_sysfs_xprt_switch_kobj_get_xprt(struct kobject *kobj){	struct rpc_sysfs_xprt_switch *x = container_of(kobj,		struct rpc_sysfs_xprt_switch, kobject); 	return xprt_switch_get(x->xprt_switch);} static ssize_t rpc_sysfs_clnt_version_show(struct kobject *kobj,					   struct kobj_attribute *attr,					   char *buf){	struct rpc_clnt *clnt = rpc_sysfs_client_kobj_get_clnt(kobj);	ssize_t ret; 	if (!clnt)		return sprintf(buf, "<closed>\n"); 	ret = sprintf(buf, "%u", clnt->cl_vers);	refcount_dec(&clnt->cl_count);	return ret;} static ssize_t rpc_sysfs_clnt_program_show(struct kobject *kobj,					   struct kobj_attribute *attr,					   char *buf){	struct rpc_clnt *clnt = rpc_sysfs_client_kobj_get_clnt(kobj);	ssize_t ret; 	if (!clnt)		return sprintf(buf, "<closed>\n"); 	ret = sprintf(buf, "%s", clnt->cl_program->name);	refcount_dec(&clnt->cl_count);	return ret;} static ssize_t rpc_sysfs_clnt_max_connect_show(struct kobject *kobj,					       struct kobj_attribute *attr,					       char *buf){	struct rpc_clnt *clnt = rpc_sysfs_client_kobj_get_clnt(kobj);	ssize_t ret; 	if (!clnt)		return sprintf(buf, "<closed>\n"); 	ret = sprintf(buf, "%u\n", clnt->cl_max_connect);	refcount_dec(&clnt->cl_count);	return ret;} 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) {		ret = sprintf(buf, "<closed>\n");		goto out;	}	ret = sprintf(buf, "%s\n", xprt->address_strings[RPC_DISPLAY_ADDR]);	xprt_put(xprt);out:	return ret;} static ssize_t rpc_sysfs_xprt_srcaddr_show(struct kobject *kobj,					   struct kobj_attribute *attr,					   char *buf){	struct rpc_xprt *xprt = rpc_sysfs_xprt_kobj_get_xprt(kobj);	size_t buflen = PAGE_SIZE;	ssize_t ret; 	if (!xprt || !xprt_connected(xprt)) {		ret = sprintf(buf, "<closed>\n");	} else if (xprt->ops->get_srcaddr) {		ret = xprt->ops->get_srcaddr(xprt, buf, buflen);		if (ret > 0) {			if (ret < buflen - 1) {				buf[ret] = '\n';				ret++;				buf[ret] = '\0';			}		} else			ret = sprintf(buf, "<closed>\n");	} else		ret = sprintf(buf, "<not a socket>\n");	xprt_put(xprt);	return ret;} static const char *xprtsec_strings[] = {	[RPC_XPRTSEC_NONE] = "none",	[RPC_XPRTSEC_TLS_ANON] = "tls-anon",	[RPC_XPRTSEC_TLS_X509] = "tls-x509",}; static ssize_t rpc_sysfs_xprt_xprtsec_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) {		ret = sprintf(buf, "<closed>\n");		goto out;	} 	ret = sprintf(buf, "%s\n", xprtsec_strings[xprt->xprtsec.policy]);	xprt_put(xprt);out:	return ret; } static ssize_t rpc_sysfs_xprt_info_show(struct kobject *kobj,					struct kobj_attribute *attr, char *buf){	struct rpc_xprt *xprt = rpc_sysfs_xprt_kobj_get_xprt(kobj);	unsigned short srcport = 0;	size_t buflen = PAGE_SIZE;	ssize_t ret; 	if (!xprt || !xprt_connected(xprt)) {		ret = sprintf(buf, "<closed>\n");		goto out;	} 	if (xprt->ops->get_srcport)		srcport = xprt->ops->get_srcport(xprt); 	ret = snprintf(buf, buflen,		       "last_used=%lu\ncur_cong=%lu\ncong_win=%lu\n"		       "max_num_slots=%u\nmin_num_slots=%u\nnum_reqs=%u\n"		       "binding_q_len=%u\nsending_q_len=%u\npending_q_len=%u\n"		       "backlog_q_len=%u\nmain_xprt=%d\nsrc_port=%u\n"		       "tasks_queuelen=%ld\ndst_port=%s\n",		       xprt->last_used, xprt->cong, xprt->cwnd, xprt->max_reqs,		       xprt->min_reqs, xprt->num_reqs, xprt->binding.qlen,		       xprt->sending.qlen, xprt->pending.qlen,		       xprt->backlog.qlen, xprt->main, srcport,		       atomic_long_read(&xprt->queuelen),		       xprt->address_strings[RPC_DISPLAY_PORT]);out:	xprt_put(xprt);	return ret;} static ssize_t rpc_sysfs_xprt_state_show(struct kobject *kobj,					 struct kobj_attribute *attr,					 char *buf){	struct rpc_xprt *xprt = rpc_sysfs_xprt_kobj_get_xprt(kobj);	ssize_t ret;	int locked, connected, connecting, close_wait, bound, binding,	    closing, congested, cwnd_wait, write_space, offline, remove; 	if (!(xprt && xprt->state)) {		ret = sprintf(buf, "state=CLOSED\n");	} else {		locked = test_bit(XPRT_LOCKED, &xprt->state);		connected = test_bit(XPRT_CONNECTED, &xprt->state);		connecting = test_bit(XPRT_CONNECTING, &xprt->state);		close_wait = test_bit(XPRT_CLOSE_WAIT, &xprt->state);		bound = test_bit(XPRT_BOUND, &xprt->state);		binding = test_bit(XPRT_BINDING, &xprt->state);		closing = test_bit(XPRT_CLOSING, &xprt->state);		congested = test_bit(XPRT_CONGESTED, &xprt->state);		cwnd_wait = test_bit(XPRT_CWND_WAIT, &xprt->state);		write_space = test_bit(XPRT_WRITE_SPACE, &xprt->state);		offline = test_bit(XPRT_OFFLINE, &xprt->state);		remove = test_bit(XPRT_REMOVE, &xprt->state); 		ret = sprintf(buf, "state=%s %s %s %s %s %s %s %s %s %s %s %s\n",			      locked ? "LOCKED" : "",			      connected ? "CONNECTED" : "",			      connecting ? "CONNECTING" : "",			      close_wait ? "CLOSE_WAIT" : "",			      bound ? "BOUND" : "",			      binding ? "BOUNDING" : "",			      closing ? "CLOSING" : "",			      congested ? "CONGESTED" : "",			      cwnd_wait ? "CWND_WAIT" : "",			      write_space ? "WRITE_SPACE" : "",			      offline ? "OFFLINE" : "",			      remove ? "REMOVE" : "");	} 	xprt_put(xprt);	return ret;} static ssize_t rpc_sysfs_xprt_del_xprt_show(struct kobject *kobj,					    struct kobj_attribute *attr,					    char *buf){	return sprintf(buf, "# delete this xprt\n");}  static ssize_t rpc_sysfs_xprt_switch_info_show(struct kobject *kobj,					       struct kobj_attribute *attr,					       char *buf){	struct rpc_xprt_switch *xprt_switch =		rpc_sysfs_xprt_switch_kobj_get_xprt(kobj);	ssize_t ret; 	if (!xprt_switch)		return 0;	ret = sprintf(buf, "num_xprts=%u\nnum_active=%u\n"		      "num_unique_destaddr=%u\nqueue_len=%ld\n",		      xprt_switch->xps_nxprts, xprt_switch->xps_nactive,		      xprt_switch->xps_nunique_destaddr_xprts,		      atomic_long_read(&xprt_switch->xps_queuelen));	xprt_switch_put(xprt_switch);	return ret;} static ssize_t rpc_sysfs_xprt_switch_add_xprt_show(struct kobject *kobj,						   struct kobj_attribute *attr,						   char *buf){	return sprintf(buf, "# add one xprt to this xprt_switch\n");} static ssize_t rpc_sysfs_xprt_switch_add_xprt_store(struct kobject *kobj,						    struct kobj_attribute *attr,						    const char *buf, size_t count){	struct rpc_xprt_switch *xprt_switch =		rpc_sysfs_xprt_switch_kobj_get_xprt(kobj);	struct xprt_create xprt_create_args = {};	struct rpc_xprt *xprt, *new; 	if (!xprt_switch)		return 0; 	xprt = rpc_xprt_switch_get_main_xprt(xprt_switch);	if (!xprt)		goto out; 	xprt_create_args.ident = xprt->xprt_class->ident;	xprt_create_args.net = xprt->xprt_net;	xprt_create_args.dstaddr = (struct sockaddr *)&xprt->addr;	xprt_create_args.addrlen = xprt->addrlen;	xprt_create_args.servername = xprt->servername;	xprt_create_args.bc_xprt = xprt->bc_xprt;	xprt_create_args.xprtsec = xprt->xprtsec;	xprt_create_args.connect_timeout = xprt->connect_timeout;	xprt_create_args.reconnect_timeout = xprt->max_reconnect_timeout; 	new = xprt_create_transport(&xprt_create_args);	if (IS_ERR(new)) {		count = PTR_ERR(new);		goto out_put_xprt;	} 	rpc_xprt_switch_add_xprt(xprt_switch, new);	xprt_put(new); out_put_xprt:	xprt_put(xprt);out:	xprt_switch_put(xprt_switch);	return count;} 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_TCP_TLS ||	      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 occurrence 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_obj(*saved_addr);	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;} static ssize_t rpc_sysfs_xprt_state_change(struct kobject *kobj,					   struct kobj_attribute *attr,					   const char *buf, size_t count){	struct rpc_xprt *xprt = rpc_sysfs_xprt_kobj_get_xprt(kobj);	int offline = 0, online = 0, remove = 0;	struct rpc_xprt_switch *xps = rpc_sysfs_xprt_kobj_get_xprt_switch(kobj); 	if (!xprt || !xps) {		count = 0;		goto out_put;	} 	if (!strncmp(buf, "offline", 7))		offline = 1;	else if (!strncmp(buf, "online", 6))		online = 1;	else if (!strncmp(buf, "remove", 6))		remove = 1;	else {		count = -EINVAL;		goto out_put;	} 	if (wait_on_bit_lock(&xprt->state, XPRT_LOCKED, TASK_KILLABLE)) {		count = -EINTR;		goto out_put;	}	if (xprt->main) {		count = -EINVAL;		goto release_tasks;	}	if (offline) {		xprt_set_offline_locked(xprt, xps);	} else if (online) {		xprt_set_online_locked(xprt, xps);	} else if (remove) {		if (test_bit(XPRT_OFFLINE, &xprt->state))			xprt_delete_locked(xprt, xps);		else			count = -EINVAL;	} release_tasks:	xprt_release_write(xprt, NULL);out_put:	xprt_put(xprt);	xprt_switch_put(xps);	return count;} static ssize_t rpc_sysfs_xprt_del_xprt(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 rpc_xprt_switch *xps = rpc_sysfs_xprt_kobj_get_xprt_switch(kobj); 	if (!xprt || !xps) {		count = 0;		goto out;	} 	if (xprt->main) {		count = -EINVAL;		goto release_tasks;	} 	if (wait_on_bit_lock(&xprt->state, XPRT_LOCKED, TASK_KILLABLE)) {		count = -EINTR;		goto out_put;	} 	xprt_set_offline_locked(xprt, xps);	xprt_delete_locked(xprt, xps); 

Lines 1–500 of 832.