xref: /openbmc/linux/fs/nfs/sysfs.c (revision 943aef2dbcf75f81c4574903131bd9559cee4fd1)
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 
20d5082aceSBenjamin Coddington struct kobject *nfs_net_kobj;
218b18a2edSBenjamin 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 
28*943aef2dSBenjamin Coddington static void nfs_kset_release(struct kobject *kobj)
29*943aef2dSBenjamin Coddington {
30*943aef2dSBenjamin Coddington 	struct kset *kset = container_of(kobj, struct kset, kobj);
31*943aef2dSBenjamin Coddington 	kfree(kset);
32*943aef2dSBenjamin Coddington }
33*943aef2dSBenjamin Coddington 
34996bc4f4STrond Myklebust static const struct kobj_ns_type_operations *nfs_netns_object_child_ns_type(
3502a476d9SGreg Kroah-Hartman 		const struct kobject *kobj)
36996bc4f4STrond Myklebust {
37996bc4f4STrond Myklebust 	return &net_ns_type_operations;
38996bc4f4STrond Myklebust }
39996bc4f4STrond Myklebust 
40*943aef2dSBenjamin Coddington static struct kobj_type nfs_kset_type = {
41*943aef2dSBenjamin Coddington 	.release = nfs_kset_release,
42*943aef2dSBenjamin Coddington 	.sysfs_ops = &kobj_sysfs_ops,
43*943aef2dSBenjamin Coddington };
44*943aef2dSBenjamin Coddington 
45996bc4f4STrond Myklebust static struct kobj_type nfs_netns_object_type = {
46996bc4f4STrond Myklebust 	.release = nfs_netns_object_release,
47996bc4f4STrond Myklebust 	.sysfs_ops = &kobj_sysfs_ops,
48996bc4f4STrond Myklebust 	.child_ns_type = nfs_netns_object_child_ns_type,
49996bc4f4STrond Myklebust };
50996bc4f4STrond Myklebust 
51996bc4f4STrond Myklebust static struct kobject *nfs_netns_object_alloc(const char *name,
52996bc4f4STrond Myklebust 		struct kset *kset, struct kobject *parent)
53996bc4f4STrond Myklebust {
54996bc4f4STrond Myklebust 	struct kobject *kobj;
55996bc4f4STrond Myklebust 
56996bc4f4STrond Myklebust 	kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
57996bc4f4STrond Myklebust 	if (kobj) {
58996bc4f4STrond Myklebust 		kobj->kset = kset;
59996bc4f4STrond Myklebust 		if (kobject_init_and_add(kobj, &nfs_netns_object_type,
60996bc4f4STrond Myklebust 					parent, "%s", name) == 0)
61996bc4f4STrond Myklebust 			return kobj;
62996bc4f4STrond Myklebust 		kobject_put(kobj);
63996bc4f4STrond Myklebust 	}
64996bc4f4STrond Myklebust 	return NULL;
65996bc4f4STrond Myklebust }
66996bc4f4STrond Myklebust 
67996bc4f4STrond Myklebust int nfs_sysfs_init(void)
68996bc4f4STrond Myklebust {
69*943aef2dSBenjamin Coddington 	int ret;
70*943aef2dSBenjamin Coddington 
71*943aef2dSBenjamin Coddington 	nfs_kset = kzalloc(sizeof(*nfs_kset), GFP_KERNEL);
728b18a2edSBenjamin Coddington 	if (!nfs_kset)
73996bc4f4STrond Myklebust 		return -ENOMEM;
74*943aef2dSBenjamin Coddington 
75*943aef2dSBenjamin Coddington 	ret = kobject_set_name(&nfs_kset->kobj, "nfs");
76*943aef2dSBenjamin Coddington 	if (ret) {
77*943aef2dSBenjamin Coddington 		kfree(nfs_kset);
78*943aef2dSBenjamin Coddington 		return ret;
79*943aef2dSBenjamin Coddington 	}
80*943aef2dSBenjamin Coddington 
81*943aef2dSBenjamin Coddington 	nfs_kset->kobj.parent = fs_kobj;
82*943aef2dSBenjamin Coddington 	nfs_kset->kobj.ktype = &nfs_kset_type;
83*943aef2dSBenjamin Coddington 	nfs_kset->kobj.kset = NULL;
84*943aef2dSBenjamin Coddington 
85*943aef2dSBenjamin Coddington 	ret = kset_register(nfs_kset);
86*943aef2dSBenjamin Coddington 	if (ret) {
87*943aef2dSBenjamin Coddington 		kfree(nfs_kset);
88*943aef2dSBenjamin Coddington 		return ret;
89*943aef2dSBenjamin Coddington 	}
90*943aef2dSBenjamin Coddington 
91d5082aceSBenjamin Coddington 	nfs_net_kobj = nfs_netns_object_alloc("net", nfs_kset, NULL);
92d5082aceSBenjamin Coddington 	if (!nfs_net_kobj) {
938b18a2edSBenjamin Coddington 		kset_unregister(nfs_kset);
94*943aef2dSBenjamin Coddington 		kfree(nfs_kset);
95996bc4f4STrond Myklebust 		return -ENOMEM;
96996bc4f4STrond Myklebust 	}
97996bc4f4STrond Myklebust 	return 0;
98996bc4f4STrond Myklebust }
99996bc4f4STrond Myklebust 
100996bc4f4STrond Myklebust void nfs_sysfs_exit(void)
101996bc4f4STrond Myklebust {
102d5082aceSBenjamin Coddington 	kobject_put(nfs_net_kobj);
1038b18a2edSBenjamin Coddington 	kset_unregister(nfs_kset);
104996bc4f4STrond Myklebust }
105bf11fbdbSTrond Myklebust 
106bf11fbdbSTrond Myklebust static ssize_t nfs_netns_identifier_show(struct kobject *kobj,
107bf11fbdbSTrond Myklebust 		struct kobj_attribute *attr, char *buf)
108bf11fbdbSTrond Myklebust {
109bf11fbdbSTrond Myklebust 	struct nfs_netns_client *c = container_of(kobj,
110bf11fbdbSTrond Myklebust 			struct nfs_netns_client,
111bf11fbdbSTrond Myklebust 			kobject);
112094eca37STrond Myklebust 	ssize_t ret;
113094eca37STrond Myklebust 
114094eca37STrond Myklebust 	rcu_read_lock();
11519cdc8faSye xingchen 	ret = sysfs_emit(buf, "%s\n", rcu_dereference(c->identifier));
116094eca37STrond Myklebust 	rcu_read_unlock();
117094eca37STrond Myklebust 	return ret;
118bf11fbdbSTrond Myklebust }
119bf11fbdbSTrond Myklebust 
120bf11fbdbSTrond Myklebust /* Strip trailing '\n' */
121bf11fbdbSTrond Myklebust static size_t nfs_string_strip(const char *c, size_t len)
122bf11fbdbSTrond Myklebust {
123bf11fbdbSTrond Myklebust 	while (len > 0 && c[len-1] == '\n')
124bf11fbdbSTrond Myklebust 		--len;
125bf11fbdbSTrond Myklebust 	return len;
126bf11fbdbSTrond Myklebust }
127bf11fbdbSTrond Myklebust 
128bf11fbdbSTrond Myklebust static ssize_t nfs_netns_identifier_store(struct kobject *kobj,
129bf11fbdbSTrond Myklebust 		struct kobj_attribute *attr,
130bf11fbdbSTrond Myklebust 		const char *buf, size_t count)
131bf11fbdbSTrond Myklebust {
132bf11fbdbSTrond Myklebust 	struct nfs_netns_client *c = container_of(kobj,
133bf11fbdbSTrond Myklebust 			struct nfs_netns_client,
134bf11fbdbSTrond Myklebust 			kobject);
135bf11fbdbSTrond Myklebust 	const char *old;
136bf11fbdbSTrond Myklebust 	char *p;
137bf11fbdbSTrond Myklebust 	size_t len;
138bf11fbdbSTrond Myklebust 
139bf11fbdbSTrond Myklebust 	len = nfs_string_strip(buf, min_t(size_t, count, CONTAINER_ID_MAXLEN));
140bf11fbdbSTrond Myklebust 	if (!len)
141bf11fbdbSTrond Myklebust 		return 0;
142bf11fbdbSTrond Myklebust 	p = kmemdup_nul(buf, len, GFP_KERNEL);
143bf11fbdbSTrond Myklebust 	if (!p)
144bf11fbdbSTrond Myklebust 		return -ENOMEM;
145094eca37STrond Myklebust 	old = rcu_dereference_protected(xchg(&c->identifier, (char __rcu *)p), 1);
146bf11fbdbSTrond Myklebust 	if (old) {
147bf11fbdbSTrond Myklebust 		synchronize_rcu();
148bf11fbdbSTrond Myklebust 		kfree(old);
149bf11fbdbSTrond Myklebust 	}
150bf11fbdbSTrond Myklebust 	return count;
151bf11fbdbSTrond Myklebust }
152bf11fbdbSTrond Myklebust 
153bf11fbdbSTrond Myklebust static void nfs_netns_client_release(struct kobject *kobj)
154bf11fbdbSTrond Myklebust {
155bf11fbdbSTrond Myklebust 	struct nfs_netns_client *c = container_of(kobj,
156bf11fbdbSTrond Myklebust 			struct nfs_netns_client,
157bf11fbdbSTrond Myklebust 			kobject);
158bf11fbdbSTrond Myklebust 
159094eca37STrond Myklebust 	kfree(rcu_dereference_raw(c->identifier));
160bf11fbdbSTrond Myklebust 	kfree(c);
161bf11fbdbSTrond Myklebust }
162bf11fbdbSTrond Myklebust 
16302a476d9SGreg Kroah-Hartman static const void *nfs_netns_client_namespace(const struct kobject *kobj)
164bf11fbdbSTrond Myklebust {
165bf11fbdbSTrond Myklebust 	return container_of(kobj, struct nfs_netns_client, kobject)->net;
166bf11fbdbSTrond Myklebust }
167bf11fbdbSTrond Myklebust 
168bf11fbdbSTrond Myklebust static struct kobj_attribute nfs_netns_client_id = __ATTR(identifier,
169bf11fbdbSTrond Myklebust 		0644, nfs_netns_identifier_show, nfs_netns_identifier_store);
170bf11fbdbSTrond Myklebust 
171bf11fbdbSTrond Myklebust static struct attribute *nfs_netns_client_attrs[] = {
172bf11fbdbSTrond Myklebust 	&nfs_netns_client_id.attr,
173bf11fbdbSTrond Myklebust 	NULL,
174bf11fbdbSTrond Myklebust };
17501f34245SGreg Kroah-Hartman ATTRIBUTE_GROUPS(nfs_netns_client);
176bf11fbdbSTrond Myklebust 
177bf11fbdbSTrond Myklebust static struct kobj_type nfs_netns_client_type = {
178bf11fbdbSTrond Myklebust 	.release = nfs_netns_client_release,
17901f34245SGreg Kroah-Hartman 	.default_groups = nfs_netns_client_groups,
180bf11fbdbSTrond Myklebust 	.sysfs_ops = &kobj_sysfs_ops,
181bf11fbdbSTrond Myklebust 	.namespace = nfs_netns_client_namespace,
182bf11fbdbSTrond Myklebust };
183bf11fbdbSTrond Myklebust 
184bf11fbdbSTrond Myklebust static struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent,
185bf11fbdbSTrond Myklebust 		struct net *net)
186bf11fbdbSTrond Myklebust {
187bf11fbdbSTrond Myklebust 	struct nfs_netns_client *p;
188bf11fbdbSTrond Myklebust 
189bf11fbdbSTrond Myklebust 	p = kzalloc(sizeof(*p), GFP_KERNEL);
190bf11fbdbSTrond Myklebust 	if (p) {
191bf11fbdbSTrond Myklebust 		p->net = net;
1928b18a2edSBenjamin Coddington 		p->kobject.kset = nfs_kset;
193bf11fbdbSTrond Myklebust 		if (kobject_init_and_add(&p->kobject, &nfs_netns_client_type,
194bf11fbdbSTrond Myklebust 					parent, "nfs_client") == 0)
195bf11fbdbSTrond Myklebust 			return p;
196bf11fbdbSTrond Myklebust 		kobject_put(&p->kobject);
197bf11fbdbSTrond Myklebust 	}
198bf11fbdbSTrond Myklebust 	return NULL;
199bf11fbdbSTrond Myklebust }
200bf11fbdbSTrond Myklebust 
201bf11fbdbSTrond Myklebust void nfs_netns_sysfs_setup(struct nfs_net *netns, struct net *net)
202bf11fbdbSTrond Myklebust {
203bf11fbdbSTrond Myklebust 	struct nfs_netns_client *clp;
204bf11fbdbSTrond Myklebust 
205d5082aceSBenjamin Coddington 	clp = nfs_netns_client_alloc(nfs_net_kobj, net);
206bf11fbdbSTrond Myklebust 	if (clp) {
207bf11fbdbSTrond Myklebust 		netns->nfs_client = clp;
208bf11fbdbSTrond Myklebust 		kobject_uevent(&clp->kobject, KOBJ_ADD);
209bf11fbdbSTrond Myklebust 	}
210bf11fbdbSTrond Myklebust }
211bf11fbdbSTrond Myklebust 
212bf11fbdbSTrond Myklebust void nfs_netns_sysfs_destroy(struct nfs_net *netns)
213bf11fbdbSTrond Myklebust {
214bf11fbdbSTrond Myklebust 	struct nfs_netns_client *clp = netns->nfs_client;
215bf11fbdbSTrond Myklebust 
216bf11fbdbSTrond Myklebust 	if (clp) {
217bf11fbdbSTrond Myklebust 		kobject_uevent(&clp->kobject, KOBJ_REMOVE);
218bf11fbdbSTrond Myklebust 		kobject_del(&clp->kobject);
219bf11fbdbSTrond Myklebust 		kobject_put(&clp->kobject);
220bf11fbdbSTrond Myklebust 		netns->nfs_client = NULL;
221bf11fbdbSTrond Myklebust 	}
222bf11fbdbSTrond Myklebust }
223