// 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