xref: /openbmc/linux/fs/nfs/sysfs.c (revision 8b18a2edecc0741b0eecf8b18fdb356a0f8682de)
1996bc4f4STrond Myklebust // SPDX-License-Identifier: GPL-2.0
2996bc4f4STrond Myklebust /*
3996bc4f4STrond Myklebust  * Copyright (c) 2019 Hammerspace Inc
4996bc4f4STrond Myklebust  */
5996bc4f4STrond Myklebust 
6996bc4f4STrond Myklebust #include <linux/module.h>
7996bc4f4STrond Myklebust #include <linux/kobject.h>
8996bc4f4STrond Myklebust #include <linux/sysfs.h>
9996bc4f4STrond Myklebust #include <linux/fs.h>
10996bc4f4STrond Myklebust #include <linux/slab.h>
11996bc4f4STrond Myklebust #include <linux/netdevice.h>
12bf11fbdbSTrond Myklebust #include <linux/string.h>
13bf11fbdbSTrond Myklebust #include <linux/nfs_fs.h>
14bf11fbdbSTrond Myklebust #include <linux/rcupdate.h>
15996bc4f4STrond Myklebust 
16bf11fbdbSTrond Myklebust #include "nfs4_fs.h"
17bf11fbdbSTrond Myklebust #include "netns.h"
18996bc4f4STrond Myklebust #include "sysfs.h"
19996bc4f4STrond Myklebust 
20996bc4f4STrond Myklebust struct kobject *nfs_client_kobj;
21*8b18a2edSBenjamin Coddington static struct kset *nfs_kset;
22996bc4f4STrond Myklebust 
23996bc4f4STrond Myklebust static void nfs_netns_object_release(struct kobject *kobj)
24996bc4f4STrond Myklebust {
25996bc4f4STrond Myklebust 	kfree(kobj);
26996bc4f4STrond Myklebust }
27996bc4f4STrond Myklebust 
28996bc4f4STrond Myklebust static const struct kobj_ns_type_operations *nfs_netns_object_child_ns_type(
2902a476d9SGreg Kroah-Hartman 		const struct kobject *kobj)
30996bc4f4STrond Myklebust {
31996bc4f4STrond Myklebust 	return &net_ns_type_operations;
32996bc4f4STrond Myklebust }
33996bc4f4STrond Myklebust 
34996bc4f4STrond Myklebust static struct kobj_type nfs_netns_object_type = {
35996bc4f4STrond Myklebust 	.release = nfs_netns_object_release,
36996bc4f4STrond Myklebust 	.sysfs_ops = &kobj_sysfs_ops,
37996bc4f4STrond Myklebust 	.child_ns_type = nfs_netns_object_child_ns_type,
38996bc4f4STrond Myklebust };
39996bc4f4STrond Myklebust 
40996bc4f4STrond Myklebust static struct kobject *nfs_netns_object_alloc(const char *name,
41996bc4f4STrond Myklebust 		struct kset *kset, struct kobject *parent)
42996bc4f4STrond Myklebust {
43996bc4f4STrond Myklebust 	struct kobject *kobj;
44996bc4f4STrond Myklebust 
45996bc4f4STrond Myklebust 	kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
46996bc4f4STrond Myklebust 	if (kobj) {
47996bc4f4STrond Myklebust 		kobj->kset = kset;
48996bc4f4STrond Myklebust 		if (kobject_init_and_add(kobj, &nfs_netns_object_type,
49996bc4f4STrond Myklebust 					parent, "%s", name) == 0)
50996bc4f4STrond Myklebust 			return kobj;
51996bc4f4STrond Myklebust 		kobject_put(kobj);
52996bc4f4STrond Myklebust 	}
53996bc4f4STrond Myklebust 	return NULL;
54996bc4f4STrond Myklebust }
55996bc4f4STrond Myklebust 
56996bc4f4STrond Myklebust int nfs_sysfs_init(void)
57996bc4f4STrond Myklebust {
58*8b18a2edSBenjamin Coddington 	nfs_kset = kset_create_and_add("nfs", NULL, fs_kobj);
59*8b18a2edSBenjamin Coddington 	if (!nfs_kset)
60996bc4f4STrond Myklebust 		return -ENOMEM;
61*8b18a2edSBenjamin Coddington 	nfs_client_kobj = nfs_netns_object_alloc("net", nfs_kset, NULL);
62996bc4f4STrond Myklebust 	if  (!nfs_client_kobj) {
63*8b18a2edSBenjamin Coddington 		kset_unregister(nfs_kset);
64*8b18a2edSBenjamin Coddington 		nfs_kset = NULL;
65996bc4f4STrond Myklebust 		return -ENOMEM;
66996bc4f4STrond Myklebust 	}
67996bc4f4STrond Myklebust 	return 0;
68996bc4f4STrond Myklebust }
69996bc4f4STrond Myklebust 
70996bc4f4STrond Myklebust void nfs_sysfs_exit(void)
71996bc4f4STrond Myklebust {
72996bc4f4STrond Myklebust 	kobject_put(nfs_client_kobj);
73*8b18a2edSBenjamin Coddington 	kset_unregister(nfs_kset);
74996bc4f4STrond Myklebust }
75bf11fbdbSTrond Myklebust 
76bf11fbdbSTrond Myklebust static ssize_t nfs_netns_identifier_show(struct kobject *kobj,
77bf11fbdbSTrond Myklebust 		struct kobj_attribute *attr, char *buf)
78bf11fbdbSTrond Myklebust {
79bf11fbdbSTrond Myklebust 	struct nfs_netns_client *c = container_of(kobj,
80bf11fbdbSTrond Myklebust 			struct nfs_netns_client,
81bf11fbdbSTrond Myklebust 			kobject);
82094eca37STrond Myklebust 	ssize_t ret;
83094eca37STrond Myklebust 
84094eca37STrond Myklebust 	rcu_read_lock();
8519cdc8faSye xingchen 	ret = sysfs_emit(buf, "%s\n", rcu_dereference(c->identifier));
86094eca37STrond Myklebust 	rcu_read_unlock();
87094eca37STrond Myklebust 	return ret;
88bf11fbdbSTrond Myklebust }
89bf11fbdbSTrond Myklebust 
90bf11fbdbSTrond Myklebust /* Strip trailing '\n' */
91bf11fbdbSTrond Myklebust static size_t nfs_string_strip(const char *c, size_t len)
92bf11fbdbSTrond Myklebust {
93bf11fbdbSTrond Myklebust 	while (len > 0 && c[len-1] == '\n')
94bf11fbdbSTrond Myklebust 		--len;
95bf11fbdbSTrond Myklebust 	return len;
96bf11fbdbSTrond Myklebust }
97bf11fbdbSTrond Myklebust 
98bf11fbdbSTrond Myklebust static ssize_t nfs_netns_identifier_store(struct kobject *kobj,
99bf11fbdbSTrond Myklebust 		struct kobj_attribute *attr,
100bf11fbdbSTrond Myklebust 		const char *buf, size_t count)
101bf11fbdbSTrond Myklebust {
102bf11fbdbSTrond Myklebust 	struct nfs_netns_client *c = container_of(kobj,
103bf11fbdbSTrond Myklebust 			struct nfs_netns_client,
104bf11fbdbSTrond Myklebust 			kobject);
105bf11fbdbSTrond Myklebust 	const char *old;
106bf11fbdbSTrond Myklebust 	char *p;
107bf11fbdbSTrond Myklebust 	size_t len;
108bf11fbdbSTrond Myklebust 
109bf11fbdbSTrond Myklebust 	len = nfs_string_strip(buf, min_t(size_t, count, CONTAINER_ID_MAXLEN));
110bf11fbdbSTrond Myklebust 	if (!len)
111bf11fbdbSTrond Myklebust 		return 0;
112bf11fbdbSTrond Myklebust 	p = kmemdup_nul(buf, len, GFP_KERNEL);
113bf11fbdbSTrond Myklebust 	if (!p)
114bf11fbdbSTrond Myklebust 		return -ENOMEM;
115094eca37STrond Myklebust 	old = rcu_dereference_protected(xchg(&c->identifier, (char __rcu *)p), 1);
116bf11fbdbSTrond Myklebust 	if (old) {
117bf11fbdbSTrond Myklebust 		synchronize_rcu();
118bf11fbdbSTrond Myklebust 		kfree(old);
119bf11fbdbSTrond Myklebust 	}
120bf11fbdbSTrond Myklebust 	return count;
121bf11fbdbSTrond Myklebust }
122bf11fbdbSTrond Myklebust 
123bf11fbdbSTrond Myklebust static void nfs_netns_client_release(struct kobject *kobj)
124bf11fbdbSTrond Myklebust {
125bf11fbdbSTrond Myklebust 	struct nfs_netns_client *c = container_of(kobj,
126bf11fbdbSTrond Myklebust 			struct nfs_netns_client,
127bf11fbdbSTrond Myklebust 			kobject);
128bf11fbdbSTrond Myklebust 
129094eca37STrond Myklebust 	kfree(rcu_dereference_raw(c->identifier));
130bf11fbdbSTrond Myklebust 	kfree(c);
131bf11fbdbSTrond Myklebust }
132bf11fbdbSTrond Myklebust 
13302a476d9SGreg Kroah-Hartman static const void *nfs_netns_client_namespace(const struct kobject *kobj)
134bf11fbdbSTrond Myklebust {
135bf11fbdbSTrond Myklebust 	return container_of(kobj, struct nfs_netns_client, kobject)->net;
136bf11fbdbSTrond Myklebust }
137bf11fbdbSTrond Myklebust 
138bf11fbdbSTrond Myklebust static struct kobj_attribute nfs_netns_client_id = __ATTR(identifier,
139bf11fbdbSTrond Myklebust 		0644, nfs_netns_identifier_show, nfs_netns_identifier_store);
140bf11fbdbSTrond Myklebust 
141bf11fbdbSTrond Myklebust static struct attribute *nfs_netns_client_attrs[] = {
142bf11fbdbSTrond Myklebust 	&nfs_netns_client_id.attr,
143bf11fbdbSTrond Myklebust 	NULL,
144bf11fbdbSTrond Myklebust };
14501f34245SGreg Kroah-Hartman ATTRIBUTE_GROUPS(nfs_netns_client);
146bf11fbdbSTrond Myklebust 
147bf11fbdbSTrond Myklebust static struct kobj_type nfs_netns_client_type = {
148bf11fbdbSTrond Myklebust 	.release = nfs_netns_client_release,
14901f34245SGreg Kroah-Hartman 	.default_groups = nfs_netns_client_groups,
150bf11fbdbSTrond Myklebust 	.sysfs_ops = &kobj_sysfs_ops,
151bf11fbdbSTrond Myklebust 	.namespace = nfs_netns_client_namespace,
152bf11fbdbSTrond Myklebust };
153bf11fbdbSTrond Myklebust 
154bf11fbdbSTrond Myklebust static struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent,
155bf11fbdbSTrond Myklebust 		struct net *net)
156bf11fbdbSTrond Myklebust {
157bf11fbdbSTrond Myklebust 	struct nfs_netns_client *p;
158bf11fbdbSTrond Myklebust 
159bf11fbdbSTrond Myklebust 	p = kzalloc(sizeof(*p), GFP_KERNEL);
160bf11fbdbSTrond Myklebust 	if (p) {
161bf11fbdbSTrond Myklebust 		p->net = net;
162*8b18a2edSBenjamin Coddington 		p->kobject.kset = nfs_kset;
163bf11fbdbSTrond Myklebust 		if (kobject_init_and_add(&p->kobject, &nfs_netns_client_type,
164bf11fbdbSTrond Myklebust 					parent, "nfs_client") == 0)
165bf11fbdbSTrond Myklebust 			return p;
166bf11fbdbSTrond Myklebust 		kobject_put(&p->kobject);
167bf11fbdbSTrond Myklebust 	}
168bf11fbdbSTrond Myklebust 	return NULL;
169bf11fbdbSTrond Myklebust }
170bf11fbdbSTrond Myklebust 
171bf11fbdbSTrond Myklebust void nfs_netns_sysfs_setup(struct nfs_net *netns, struct net *net)
172bf11fbdbSTrond Myklebust {
173bf11fbdbSTrond Myklebust 	struct nfs_netns_client *clp;
174bf11fbdbSTrond Myklebust 
175bf11fbdbSTrond Myklebust 	clp = nfs_netns_client_alloc(nfs_client_kobj, net);
176bf11fbdbSTrond Myklebust 	if (clp) {
177bf11fbdbSTrond Myklebust 		netns->nfs_client = clp;
178bf11fbdbSTrond Myklebust 		kobject_uevent(&clp->kobject, KOBJ_ADD);
179bf11fbdbSTrond Myklebust 	}
180bf11fbdbSTrond Myklebust }
181bf11fbdbSTrond Myklebust 
182bf11fbdbSTrond Myklebust void nfs_netns_sysfs_destroy(struct nfs_net *netns)
183bf11fbdbSTrond Myklebust {
184bf11fbdbSTrond Myklebust 	struct nfs_netns_client *clp = netns->nfs_client;
185bf11fbdbSTrond Myklebust 
186bf11fbdbSTrond Myklebust 	if (clp) {
187bf11fbdbSTrond Myklebust 		kobject_uevent(&clp->kobject, KOBJ_REMOVE);
188bf11fbdbSTrond Myklebust 		kobject_del(&clp->kobject);
189bf11fbdbSTrond Myklebust 		kobject_put(&clp->kobject);
190bf11fbdbSTrond Myklebust 		netns->nfs_client = NULL;
191bf11fbdbSTrond Myklebust 	}
192bf11fbdbSTrond Myklebust }
193