File Index
Files and directories
Sign in
FILE

sysfs.c

File preview with syntax highlighting.

linux-6.19.14/fs/nfs/sysfs.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (c) 2019 Hammerspace Inc
  4 */
  5
  6#include <linux/module.h>
  7#include <linux/kobject.h>
  8#include <linux/sysfs.h>
  9#include <linux/fs.h>
 10#include <linux/slab.h>
 11#include <linux/netdevice.h>
 12#include <linux/string.h>
 13#include <linux/nfs_fs.h>
 14#include <linux/rcupdate.h>
 15#include <linux/lockd/lockd.h>
 16
 17#include "internal.h"
 18#include "nfs4_fs.h"
 19#include "netns.h"
 20#include "sysfs.h"
 21
 22static struct kset *nfs_kset;
 23
 24static void nfs_kset_release(struct kobject *kobj)
 25{
 26	struct kset *kset = container_of(kobj, struct kset, kobj);
 27	kfree(kset);
 28}
 29
 30static const struct kobj_ns_type_operations *nfs_netns_object_child_ns_type(
 31		const struct kobject *kobj)
 32{
 33	return &net_ns_type_operations;
 34}
 35
 36static struct kobj_type nfs_kset_type = {
 37	.release = nfs_kset_release,
 38	.sysfs_ops = &kobj_sysfs_ops,
 39	.child_ns_type = nfs_netns_object_child_ns_type,
 40};
 41
 42int nfs_sysfs_init(void)
 43{
 44	int ret;
 45
 46	nfs_kset = kzalloc(sizeof(*nfs_kset), GFP_KERNEL);
 47	if (!nfs_kset)
 48		return -ENOMEM;
 49
 50	ret = kobject_set_name(&nfs_kset->kobj, "nfs");
 51	if (ret) {
 52		kfree(nfs_kset);
 53		return ret;
 54	}
 55
 56	nfs_kset->kobj.parent = fs_kobj;
 57	nfs_kset->kobj.ktype = &nfs_kset_type;
 58	nfs_kset->kobj.kset = NULL;
 59
 60	ret = kset_register(nfs_kset);
 61	if (ret) {
 62		kfree(nfs_kset);
 63		return ret;
 64	}
 65
 66	return 0;
 67}
 68
 69void nfs_sysfs_exit(void)
 70{
 71	kset_unregister(nfs_kset);
 72}
 73
 74static ssize_t nfs_netns_identifier_show(struct kobject *kobj,
 75		struct kobj_attribute *attr, char *buf)
 76{
 77	struct nfs_netns_client *c = container_of(kobj,
 78			struct nfs_netns_client,
 79			kobject);
 80	ssize_t ret;
 81
 82	rcu_read_lock();
 83	ret = sysfs_emit(buf, "%s\n", rcu_dereference(c->identifier));
 84	rcu_read_unlock();
 85	return ret;
 86}
 87
 88/* Strip trailing '\n' */
 89static size_t nfs_string_strip(const char *c, size_t len)
 90{
 91	while (len > 0 && c[len-1] == '\n')
 92		--len;
 93	return len;
 94}
 95
 96static ssize_t nfs_netns_identifier_store(struct kobject *kobj,
 97		struct kobj_attribute *attr,
 98		const char *buf, size_t count)
 99{
100	struct nfs_netns_client *c = container_of(kobj,
101			struct nfs_netns_client,
102			kobject);
103	const char *old;
104	char *p;
105	size_t len;
106
107	len = nfs_string_strip(buf, min_t(size_t, count, CONTAINER_ID_MAXLEN));
108	if (!len)
109		return 0;
110	p = kmemdup_nul(buf, len, GFP_KERNEL);
111	if (!p)
112		return -ENOMEM;
113	old = rcu_dereference_protected(xchg(&c->identifier, (char __rcu *)p), 1);
114	if (old) {
115		synchronize_rcu();
116		kfree(old);
117	}
118	return count;
119}
120
121static void nfs_netns_client_release(struct kobject *kobj)
122{
123	struct nfs_netns_client *c = container_of(kobj,
124			struct nfs_netns_client,
125			kobject);
126
127	kfree(rcu_dereference_raw(c->identifier));
128}
129
130static const void *nfs_netns_client_namespace(const struct kobject *kobj)
131{
132	return container_of(kobj, struct nfs_netns_client, kobject)->net;
133}
134
135static struct kobj_attribute nfs_netns_client_id = __ATTR(identifier,
136		0644, nfs_netns_identifier_show, nfs_netns_identifier_store);
137
138static struct attribute *nfs_netns_client_attrs[] = {
139	&nfs_netns_client_id.attr,
140	NULL,
141};
142ATTRIBUTE_GROUPS(nfs_netns_client);
143
144static struct kobj_type nfs_netns_client_type = {
145	.release = nfs_netns_client_release,
146	.default_groups = nfs_netns_client_groups,
147	.sysfs_ops = &kobj_sysfs_ops,
148	.namespace = nfs_netns_client_namespace,
149};
150
151static void nfs_netns_object_release(struct kobject *kobj)
152{
153	struct nfs_netns_client *c = container_of(kobj,
154			struct nfs_netns_client,
155			nfs_net_kobj);
156	kfree(c);
157}
158
159static const void *nfs_netns_namespace(const struct kobject *kobj)
160{
161	return container_of(kobj, struct nfs_netns_client, nfs_net_kobj)->net;
162}
163
164static struct kobj_type nfs_netns_object_type = {
165	.release = nfs_netns_object_release,
166	.sysfs_ops = &kobj_sysfs_ops,
167	.namespace =  nfs_netns_namespace,
168};
169
170static struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent,
171		struct net *net)
172{
173	struct nfs_netns_client *p;
174
175	p = kzalloc(sizeof(*p), GFP_KERNEL);
176	if (p) {
177		p->net = net;
178		p->kobject.kset = nfs_kset;
179		p->nfs_net_kobj.kset = nfs_kset;
180
181		if (kobject_init_and_add(&p->nfs_net_kobj, &nfs_netns_object_type,
182					parent, "net") != 0) {
183			kobject_put(&p->nfs_net_kobj);
184			return NULL;
185		}
186
187		if (kobject_init_and_add(&p->kobject, &nfs_netns_client_type,
188					&p->nfs_net_kobj, "nfs_client") == 0)
189			return p;
190
191		kobject_put(&p->kobject);
192		kobject_put(&p->nfs_net_kobj);
193	}
194	return NULL;
195}
196
197void nfs_netns_sysfs_setup(struct nfs_net *netns, struct net *net)
198{
199	struct nfs_netns_client *clp;
200
201	clp = nfs_netns_client_alloc(&nfs_kset->kobj, net);
202	if (clp) {
203		netns->nfs_client = clp;
204		kobject_uevent(&clp->kobject, KOBJ_ADD);
205	}
206}
207
208void nfs_netns_sysfs_destroy(struct nfs_net *netns)
209{
210	struct nfs_netns_client *clp = netns->nfs_client;
211
212	if (clp) {
213		kobject_uevent(&clp->kobject, KOBJ_REMOVE);
214		kobject_del(&clp->kobject);
215		kobject_put(&clp->kobject);
216		kobject_del(&clp->nfs_net_kobj);
217		kobject_put(&clp->nfs_net_kobj);
218		netns->nfs_client = NULL;
219	}
220}
221
222static bool shutdown_match_client(const struct rpc_task *task, const void *data)
223{
224	return true;
225}
226
227static void shutdown_client(struct rpc_clnt *clnt)
228{
229	clnt->cl_shutdown = 1;
230	rpc_cancel_tasks(clnt, -EIO, shutdown_match_client, NULL);
231}
232
233/*
234 * Shut down the nfs_client only once all the superblocks
235 * have been shut down.
236 */
237static void shutdown_nfs_client(struct nfs_client *clp)
238{
239	struct nfs_server *server;
240	rcu_read_lock();
241	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
242		if (!(server->flags & NFS_MOUNT_SHUTDOWN)) {
243			rcu_read_unlock();
244			return;
245		}
246	}
247	rcu_read_unlock();
248	nfs_mark_client_ready(clp, -EIO);
249	shutdown_client(clp->cl_rpcclient);
250}
251
252static ssize_t
253shutdown_show(struct kobject *kobj, struct kobj_attribute *attr,
254				char *buf)
255{
256	struct nfs_server *server = container_of(kobj, struct nfs_server, kobj);
257	bool shutdown = server->flags & NFS_MOUNT_SHUTDOWN;
258	return sysfs_emit(buf, "%d\n", shutdown);
259}
260
261static ssize_t
262shutdown_store(struct kobject *kobj, struct kobj_attribute *attr,
263				const char *buf, size_t count)
264{
265	struct nfs_server *server;
266	int ret, val;
267
268	server = container_of(kobj, struct nfs_server, kobj);
269
270	ret = kstrtoint(buf, 0, &val);
271	if (ret)
272		return ret;
273
274	if (val != 1)
275		return -EINVAL;
276
277	/* already shut down? */
278	if (server->flags & NFS_MOUNT_SHUTDOWN)
279		goto out;
280
281	server->flags |= NFS_MOUNT_SHUTDOWN;
282	shutdown_client(server->client);
283
284	if (!IS_ERR(server->client_acl))
285		shutdown_client(server->client_acl);
286
287	if (server->nlm_host)
288		shutdown_client(server->nlm_host->h_rpcclnt);
289out:
290	shutdown_nfs_client(server->nfs_client);
291	return count;
292}
293
294static struct kobj_attribute nfs_sysfs_attr_shutdown = __ATTR_RW(shutdown);
295
296#if IS_ENABLED(CONFIG_NFS_V4_1)
297static ssize_t
298implid_domain_show(struct kobject *kobj, struct kobj_attribute *attr,
299				char *buf)
300{
301	struct nfs_server *server = container_of(kobj, struct nfs_server, kobj);
302	struct nfs41_impl_id *impl_id = server->nfs_client->cl_implid;
303
304	if (!impl_id || strlen(impl_id->domain) == 0)
305		return 0; //sysfs_emit(buf, "");
306	return sysfs_emit(buf, "%s\n", impl_id->domain);
307}
308
309static struct kobj_attribute nfs_sysfs_attr_implid_domain = __ATTR_RO(implid_domain);
310
311
312static ssize_t
313implid_name_show(struct kobject *kobj, struct kobj_attribute *attr,
314				char *buf)
315{
316	struct nfs_server *server = container_of(kobj, struct nfs_server, kobj);
317	struct nfs41_impl_id *impl_id = server->nfs_client->cl_implid;
318
319	if (!impl_id || strlen(impl_id->name) == 0)
320		return 0; //sysfs_emit(buf, "");
321	return sysfs_emit(buf, "%s\n", impl_id->name);
322}
323
324static struct kobj_attribute nfs_sysfs_attr_implid_name = __ATTR_RO(implid_name);
325
326#endif /* IS_ENABLED(CONFIG_NFS_V4_1) */
327
328#define RPC_CLIENT_NAME_SIZE 64
329
330void nfs_sysfs_link_rpc_client(struct nfs_server *server,
331			struct rpc_clnt *clnt, const char *uniq)
332{
333	char name[RPC_CLIENT_NAME_SIZE];
334	int ret;
335
336	strscpy(name, clnt->cl_program->name, sizeof(name));
337	strncat(name, uniq ? uniq : "", sizeof(name) - strlen(name) - 1);
338	strncat(name, "_client", sizeof(name) - strlen(name) - 1);
339
340	ret = sysfs_create_link_nowarn(&server->kobj,
341						&clnt->cl_sysfs->kobject, name);
342	if (ret < 0)
343		pr_warn("NFS: can't create link to %s in sysfs (%d)\n",
344			name, ret);
345}
346EXPORT_SYMBOL_GPL(nfs_sysfs_link_rpc_client);
347
348static void nfs_sysfs_sb_release(struct kobject *kobj)
349{
350	/* no-op: why? see lib/kobject.c kobject_cleanup() */
351}
352
353static const void *nfs_netns_server_namespace(const struct kobject *kobj)
354{
355	return container_of(kobj, struct nfs_server, kobj)->nfs_client->cl_net;
356}
357
358static struct kobj_type nfs_sb_ktype = {
359	.release = nfs_sysfs_sb_release,
360	.sysfs_ops = &kobj_sysfs_ops,
361	.namespace = nfs_netns_server_namespace,
362	.child_ns_type = nfs_netns_object_child_ns_type,
363};
364
365#if IS_ENABLED(CONFIG_NFS_V4_1)
366static void nfs_sysfs_add_nfsv41_server(struct nfs_server *server)
367{
368	int ret;
369
370	if (!server->nfs_client->cl_implid)
371		return;
372
373	ret = sysfs_create_file_ns(&server->kobj, &nfs_sysfs_attr_implid_domain.attr,
374					   nfs_netns_server_namespace(&server->kobj));
375	if (ret < 0)
376		pr_warn("NFS: sysfs_create_file_ns for server-%d failed (%d)\n",
377			server->s_sysfs_id, ret);
378
379	ret = sysfs_create_file_ns(&server->kobj, &nfs_sysfs_attr_implid_name.attr,
380				   nfs_netns_server_namespace(&server->kobj));
381	if (ret < 0)
382		pr_warn("NFS: sysfs_create_file_ns for server-%d failed (%d)\n",
383			server->s_sysfs_id, ret);
384}
385#else /* CONFIG_NFS_V4_1 */
386static inline void nfs_sysfs_add_nfsv41_server(struct nfs_server *server)
387{
388}
389#endif /* CONFIG_NFS_V4_1 */
390
391#if IS_ENABLED(CONFIG_NFS_LOCALIO)
392
393static ssize_t
394localio_show(struct kobject *kobj, struct kobj_attribute *attr,
395				char *buf)
396{
397	struct nfs_server *server = container_of(kobj, struct nfs_server, kobj);
398	bool localio = nfs_server_is_local(server->nfs_client);
399	return sysfs_emit(buf, "%d\n", localio);
400}
401
402static struct kobj_attribute nfs_sysfs_attr_localio = __ATTR_RO(localio);
403
404static void nfs_sysfs_add_nfs_localio_server(struct nfs_server *server)
405{
406	int ret = sysfs_create_file_ns(&server->kobj, &nfs_sysfs_attr_localio.attr,
407				       nfs_netns_server_namespace(&server->kobj));
408	if (ret < 0)
409		pr_warn("NFS: sysfs_create_file_ns for server-%d failed (%d)\n",
410			server->s_sysfs_id, ret);
411}
412#else
413static inline void nfs_sysfs_add_nfs_localio_server(struct nfs_server *server)
414{
415}
416#endif /* IS_ENABLED(CONFIG_NFS_LOCALIO) */
417
418void nfs_sysfs_add_server(struct nfs_server *server)
419{
420	int ret;
421
422	ret = kobject_init_and_add(&server->kobj, &nfs_sb_ktype,
423				&nfs_kset->kobj, "server-%d", server->s_sysfs_id);
424	if (ret < 0) {
425		pr_warn("NFS: nfs sysfs add server-%d failed (%d)\n",
426					server->s_sysfs_id, ret);
427		return;
428	}
429	ret = sysfs_create_file_ns(&server->kobj, &nfs_sysfs_attr_shutdown.attr,
430				nfs_netns_server_namespace(&server->kobj));
431	if (ret < 0)
432		pr_warn("NFS: sysfs_create_file_ns for server-%d failed (%d)\n",
433			server->s_sysfs_id, ret);
434
435	nfs_sysfs_add_nfsv41_server(server);
436	nfs_sysfs_add_nfs_localio_server(server);
437}
438EXPORT_SYMBOL_GPL(nfs_sysfs_add_server);
439
440void nfs_sysfs_move_server_to_sb(struct super_block *s)
441{
442	struct nfs_server *server = s->s_fs_info;
443	int ret;
444
445	ret = kobject_rename(&server->kobj, s->s_id);
446	if (ret < 0)
447		pr_warn("NFS: rename sysfs %s failed (%d)\n",
448					server->kobj.name, ret);
449}
450
451void nfs_sysfs_move_sb_to_server(struct nfs_server *server)
452{
453	const char *s;
454	int ret = -ENOMEM;
455
456	s = kasprintf(GFP_KERNEL, "server-%d", server->s_sysfs_id);
457	if (s) {
458		ret = kobject_rename(&server->kobj, s);
459		kfree(s);
460	}
461	if (ret < 0)
462		pr_warn("NFS: rename sysfs %s failed (%d)\n",
463					server->kobj.name, ret);
464}
465
466/* unlink, not dec-ref */
467void nfs_sysfs_remove_server(struct nfs_server *server)
468{
469	kobject_del(&server->kobj);
470}
~/ Read-only file preview.