linux/net/sunrpc/sysfs.c
Greg Kroah-Hartman 02a476d932 kobject: make kobject_get_ownership() take a constant kobject *
The call, kobject_get_ownership(), does not modify the kobject passed
into it, so make it const.  This propagates down into the kobj_type
function callbacks so make the kobject passed into them also const,
ensuring that nothing in the kobject is being changed here.

This helps make it more obvious what calls and callbacks do, and do not,
modify structures passed to them.

Cc: Trond Myklebust <trond.myklebust@hammerspace.com>
Cc: Anna Schumaker <anna@kernel.org>
Cc: Roopa Prabhu <roopa@nvidia.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Chuck Lever <chuck.lever@oracle.com>
Cc: Jeff Layton <jlayton@kernel.org>
Cc: linux-nfs@vger.kernel.org
Cc: bridge@lists.linux-foundation.org
Cc: netdev@vger.kernel.org
Acked-by: Jakub Kicinski <kuba@kernel.org>
Acked-by: Rafael J. Wysocki <rafael@kernel.org>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Link: https://lore.kernel.org/r/20221121094649.1556002-1-gregkh@linuxfoundation.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-11-22 17:34:29 +01:00

627 lines
16 KiB
C

// 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 "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 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(sizeof(*kobj), GFP_KERNEL);
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_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_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 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_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_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;
}
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;
}
int rpc_sysfs_init(void)
{
rpc_sunrpc_kset = kset_create_and_add("sunrpc", NULL, kernel_kobj);
if (!rpc_sunrpc_kset)
return -ENOMEM;
rpc_sunrpc_client_kobj =
rpc_sysfs_object_alloc("rpc-clients", rpc_sunrpc_kset, NULL);
if (!rpc_sunrpc_client_kobj)
goto err_client;
rpc_sunrpc_xprt_switch_kobj =
rpc_sysfs_object_alloc("xprt-switches", rpc_sunrpc_kset, NULL);
if (!rpc_sunrpc_xprt_switch_kobj)
goto err_switch;
return 0;
err_switch:
kobject_put(rpc_sunrpc_client_kobj);
rpc_sunrpc_client_kobj = NULL;
err_client:
kset_unregister(rpc_sunrpc_kset);
rpc_sunrpc_kset = NULL;
return -ENOMEM;
}
static void rpc_sysfs_client_release(struct kobject *kobj)
{
struct rpc_sysfs_client *c;
c = container_of(kobj, struct rpc_sysfs_client, kobject);
kfree(c);
}
static void rpc_sysfs_xprt_switch_release(struct kobject *kobj)
{
struct rpc_sysfs_xprt_switch *xprt_switch;
xprt_switch = container_of(kobj, struct rpc_sysfs_xprt_switch, kobject);
kfree(xprt_switch);
}
static void rpc_sysfs_xprt_release(struct kobject *kobj)
{
struct rpc_sysfs_xprt *xprt;
xprt = container_of(kobj, struct rpc_sysfs_xprt, kobject);
kfree(xprt);
}
static const void *rpc_sysfs_client_namespace(const struct kobject *kobj)
{
return container_of(kobj, struct rpc_sysfs_client, kobject)->net;
}
static const void *rpc_sysfs_xprt_switch_namespace(const struct kobject *kobj)
{
return container_of(kobj, struct rpc_sysfs_xprt_switch, kobject)->net;
}
static const void *rpc_sysfs_xprt_namespace(const struct kobject *kobj)
{
return container_of(kobj, struct rpc_sysfs_xprt,
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 kobj_attribute rpc_sysfs_xprt_srcaddr = __ATTR(srcaddr,
0644, rpc_sysfs_xprt_srcaddr_show, NULL);
static struct kobj_attribute rpc_sysfs_xprt_info = __ATTR(xprt_info,
0444, rpc_sysfs_xprt_info_show, NULL);
static struct kobj_attribute rpc_sysfs_xprt_change_state = __ATTR(xprt_state,
0644, rpc_sysfs_xprt_state_show, rpc_sysfs_xprt_state_change);
static struct attribute *rpc_sysfs_xprt_attrs[] = {
&rpc_sysfs_xprt_dstaddr.attr,
&rpc_sysfs_xprt_srcaddr.attr,
&rpc_sysfs_xprt_info.attr,
&rpc_sysfs_xprt_change_state.attr,
NULL,
};
ATTRIBUTE_GROUPS(rpc_sysfs_xprt);
static struct kobj_attribute rpc_sysfs_xprt_switch_info =
__ATTR(xprt_switch_info, 0444, rpc_sysfs_xprt_switch_info_show, NULL);
static struct attribute *rpc_sysfs_xprt_switch_attrs[] = {
&rpc_sysfs_xprt_switch_info.attr,
NULL,
};
ATTRIBUTE_GROUPS(rpc_sysfs_xprt_switch);
static struct kobj_type rpc_sysfs_client_type = {
.release = rpc_sysfs_client_release,
.sysfs_ops = &kobj_sysfs_ops,
.namespace = rpc_sysfs_client_namespace,
};
static struct kobj_type rpc_sysfs_xprt_switch_type = {
.release = rpc_sysfs_xprt_switch_release,
.default_groups = rpc_sysfs_xprt_switch_groups,
.sysfs_ops = &kobj_sysfs_ops,
.namespace = rpc_sysfs_xprt_switch_namespace,
};
static struct kobj_type rpc_sysfs_xprt_type = {
.release = rpc_sysfs_xprt_release,
.default_groups = rpc_sysfs_xprt_groups,
.sysfs_ops = &kobj_sysfs_ops,
.namespace = rpc_sysfs_xprt_namespace,
};
void rpc_sysfs_exit(void)
{
kobject_put(rpc_sunrpc_client_kobj);
kobject_put(rpc_sunrpc_xprt_switch_kobj);
kset_unregister(rpc_sunrpc_kset);
}
static struct rpc_sysfs_client *rpc_sysfs_client_alloc(struct kobject *parent,
struct net *net,
int clid)
{
struct rpc_sysfs_client *p;
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (p) {
p->net = net;
p->kobject.kset = rpc_sunrpc_kset;
if (kobject_init_and_add(&p->kobject, &rpc_sysfs_client_type,
parent, "clnt-%d", clid) == 0)
return p;
kobject_put(&p->kobject);
}
return NULL;
}
static struct rpc_sysfs_xprt_switch *
rpc_sysfs_xprt_switch_alloc(struct kobject *parent,
struct rpc_xprt_switch *xprt_switch,
struct net *net,
gfp_t gfp_flags)
{
struct rpc_sysfs_xprt_switch *p;
p = kzalloc(sizeof(*p), gfp_flags);
if (p) {
p->net = net;
p->kobject.kset = rpc_sunrpc_kset;
if (kobject_init_and_add(&p->kobject,
&rpc_sysfs_xprt_switch_type,
parent, "switch-%d",
xprt_switch->xps_id) == 0)
return p;
kobject_put(&p->kobject);
}
return NULL;
}
static struct rpc_sysfs_xprt *rpc_sysfs_xprt_alloc(struct kobject *parent,
struct rpc_xprt *xprt,
gfp_t gfp_flags)
{
struct rpc_sysfs_xprt *p;
p = kzalloc(sizeof(*p), gfp_flags);
if (!p)
goto out;
p->kobject.kset = rpc_sunrpc_kset;
if (kobject_init_and_add(&p->kobject, &rpc_sysfs_xprt_type,
parent, "xprt-%d-%s", xprt->id,
xprt->address_strings[RPC_DISPLAY_PROTO]) == 0)
return p;
kobject_put(&p->kobject);
out:
return NULL;
}
void rpc_sysfs_client_setup(struct rpc_clnt *clnt,
struct rpc_xprt_switch *xprt_switch,
struct net *net)
{
struct rpc_sysfs_client *rpc_client;
struct rpc_sysfs_xprt_switch *xswitch =
(struct rpc_sysfs_xprt_switch *)xprt_switch->xps_sysfs;
if (!xswitch)
return;
rpc_client = rpc_sysfs_client_alloc(rpc_sunrpc_client_kobj,
net, clnt->cl_clid);
if (rpc_client) {
char name[] = "switch";
int ret;
clnt->cl_sysfs = rpc_client;
rpc_client->clnt = clnt;
rpc_client->xprt_switch = xprt_switch;
kobject_uevent(&rpc_client->kobject, KOBJ_ADD);
ret = sysfs_create_link_nowarn(&rpc_client->kobject,
&xswitch->kobject, name);
if (ret)
pr_warn("can't create link to %s in sysfs (%d)\n",
name, ret);
}
}
void rpc_sysfs_xprt_switch_setup(struct rpc_xprt_switch *xprt_switch,
struct rpc_xprt *xprt,
gfp_t gfp_flags)
{
struct rpc_sysfs_xprt_switch *rpc_xprt_switch;
struct net *net;
if (xprt_switch->xps_net)
net = xprt_switch->xps_net;
else
net = xprt->xprt_net;
rpc_xprt_switch =
rpc_sysfs_xprt_switch_alloc(rpc_sunrpc_xprt_switch_kobj,
xprt_switch, net, gfp_flags);
if (rpc_xprt_switch) {
xprt_switch->xps_sysfs = rpc_xprt_switch;
rpc_xprt_switch->xprt_switch = xprt_switch;
rpc_xprt_switch->xprt = xprt;
kobject_uevent(&rpc_xprt_switch->kobject, KOBJ_ADD);
} else {
xprt_switch->xps_sysfs = NULL;
}
}
void rpc_sysfs_xprt_setup(struct rpc_xprt_switch *xprt_switch,
struct rpc_xprt *xprt,
gfp_t gfp_flags)
{
struct rpc_sysfs_xprt *rpc_xprt;
struct rpc_sysfs_xprt_switch *switch_obj =
(struct rpc_sysfs_xprt_switch *)xprt_switch->xps_sysfs;
if (!switch_obj)
return;
rpc_xprt = rpc_sysfs_xprt_alloc(&switch_obj->kobject, xprt, gfp_flags);
if (rpc_xprt) {
xprt->xprt_sysfs = rpc_xprt;
rpc_xprt->xprt = xprt;
rpc_xprt->xprt_switch = xprt_switch;
kobject_uevent(&rpc_xprt->kobject, KOBJ_ADD);
}
}
void rpc_sysfs_client_destroy(struct rpc_clnt *clnt)
{
struct rpc_sysfs_client *rpc_client = clnt->cl_sysfs;
if (rpc_client) {
char name[] = "switch";
sysfs_remove_link(&rpc_client->kobject, name);
kobject_uevent(&rpc_client->kobject, KOBJ_REMOVE);
kobject_del(&rpc_client->kobject);
kobject_put(&rpc_client->kobject);
clnt->cl_sysfs = NULL;
}
}
void rpc_sysfs_xprt_switch_destroy(struct rpc_xprt_switch *xprt_switch)
{
struct rpc_sysfs_xprt_switch *rpc_xprt_switch = xprt_switch->xps_sysfs;
if (rpc_xprt_switch) {
kobject_uevent(&rpc_xprt_switch->kobject, KOBJ_REMOVE);
kobject_del(&rpc_xprt_switch->kobject);
kobject_put(&rpc_xprt_switch->kobject);
xprt_switch->xps_sysfs = NULL;
}
}
void rpc_sysfs_xprt_destroy(struct rpc_xprt *xprt)
{
struct rpc_sysfs_xprt *rpc_xprt = xprt->xprt_sysfs;
if (rpc_xprt) {
kobject_uevent(&rpc_xprt->kobject, KOBJ_REMOVE);
kobject_del(&rpc_xprt->kobject);
kobject_put(&rpc_xprt->kobject);
xprt->xprt_sysfs = NULL;
}
}