xref: /linux/fs/9p/v9fs.c (revision bbbf7f32843b5788786cd8d91e9430823c2777c9)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  This file contains functions assisting in mapping VFS to 9P2000
4  *
5  *  Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
6  *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
7  */
8 
9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10 
11 #include <linux/module.h>
12 #include <linux/errno.h>
13 #include <linux/fs.h>
14 #include <linux/sched.h>
15 #include <linux/cred.h>
16 #include <linux/fs_parser.h>
17 #include <linux/fs_context.h>
18 #include <linux/slab.h>
19 #include <linux/seq_file.h>
20 #include <net/9p/9p.h>
21 #include <net/9p/client.h>
22 #include <net/9p/transport.h>
23 #include "v9fs.h"
24 #include "v9fs_vfs.h"
25 #include "cache.h"
26 
27 static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
28 static LIST_HEAD(v9fs_sessionlist);
29 struct kmem_cache *v9fs_inode_cache;
30 
31 /*
32  * Option Parsing (code inspired by NFS code)
33  *  NOTE: each transport will parse its own options
34  */
35 
36 enum {
37 	/* Mount-point source, we need to handle this explicitly because
38 	 * the code below accepts unknown args and the vfs layer only handles
39 	 * source if we rejected it as EINVAL */
40 	Opt_source,
41 	/* Options that take integer arguments */
42 	Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid,
43 	/* String options */
44 	Opt_uname, Opt_remotename, Opt_cache, Opt_cachetag,
45 	/* Options that take no arguments */
46 	Opt_nodevmap, Opt_noxattr, Opt_directio, Opt_ignoreqv,
47 	/* Access options */
48 	Opt_access, Opt_posixacl,
49 	/* Lock timeout option */
50 	Opt_locktimeout,
51 
52 	/* Client options */
53 	Opt_msize, Opt_trans, Opt_legacy, Opt_version,
54 
55 	/* fd transport options */
56 	/* Options that take integer arguments */
57 	Opt_rfdno, Opt_wfdno,
58 	/* Options that take no arguments */
59 
60 	/* rdma transport options */
61 	/* Options that take integer arguments */
62 	Opt_rq_depth, Opt_sq_depth, Opt_timeout,
63 
64 	/* Options for both fd and rdma transports */
65 	Opt_port, Opt_privport,
66 };
67 
68 static const struct constant_table p9_versions[] = {
69 	{ "9p2000",	p9_proto_legacy },
70 	{ "9p2000.u",	p9_proto_2000u },
71 	{ "9p2000.L",	p9_proto_2000L },
72 	{}
73 };
74 
75 /*
76  * This structure contains all parameters used for the core code,
77  * the client, and all the transports.
78  */
79 const struct fs_parameter_spec v9fs_param_spec[] = {
80 	fsparam_string	("source",	Opt_source),
81 	fsparam_u32hex	("debug",	Opt_debug),
82 	fsparam_uid	("dfltuid",	Opt_dfltuid),
83 	fsparam_gid	("dfltgid",	Opt_dfltgid),
84 	fsparam_u32	("afid",	Opt_afid),
85 	fsparam_string	("uname",	Opt_uname),
86 	fsparam_string	("aname",	Opt_remotename),
87 	fsparam_flag	("nodevmap",	Opt_nodevmap),
88 	fsparam_flag	("noxattr",	Opt_noxattr),
89 	fsparam_flag	("directio",	Opt_directio),
90 	fsparam_flag	("ignoreqv",	Opt_ignoreqv),
91 	fsparam_string	("cache",	Opt_cache),
92 	fsparam_string	("cachetag",	Opt_cachetag),
93 	fsparam_string	("access",	Opt_access),
94 	fsparam_flag	("posixacl",	Opt_posixacl),
95 	fsparam_u32	("locktimeout",	Opt_locktimeout),
96 
97 	/* client options */
98 	fsparam_u32	("msize",	Opt_msize),
99 	fsparam_flag	("noextend",	Opt_legacy),
100 	fsparam_string	("trans",	Opt_trans),
101 	fsparam_enum	("version",	Opt_version, p9_versions),
102 
103 	/* fd transport options */
104 	fsparam_u32	("rfdno",	Opt_rfdno),
105 	fsparam_u32	("wfdno",	Opt_wfdno),
106 
107 	/* rdma transport options */
108 	fsparam_u32	("sq",		Opt_sq_depth),
109 	fsparam_u32	("rq",		Opt_rq_depth),
110 	fsparam_u32	("timeout",	Opt_timeout),
111 
112 	/* fd and rdma transprt options */
113 	fsparam_u32	("port",	Opt_port),
114 	fsparam_flag	("privport",	Opt_privport),
115 	{}
116 };
117 
118 /* Interpret mount options for cache mode */
get_cache_mode(char * s)119 static int get_cache_mode(char *s)
120 {
121 	int version = -EINVAL;
122 
123 	if (!strcmp(s, "loose")) {
124 		version = CACHE_SC_LOOSE;
125 		p9_debug(P9_DEBUG_9P, "Cache mode: loose\n");
126 	} else if (!strcmp(s, "fscache")) {
127 		version = CACHE_SC_FSCACHE;
128 		p9_debug(P9_DEBUG_9P, "Cache mode: fscache\n");
129 	} else if (!strcmp(s, "mmap")) {
130 		version = CACHE_SC_MMAP;
131 		p9_debug(P9_DEBUG_9P, "Cache mode: mmap\n");
132 	} else if (!strcmp(s, "readahead")) {
133 		version = CACHE_SC_READAHEAD;
134 		p9_debug(P9_DEBUG_9P, "Cache mode: readahead\n");
135 	} else if (!strcmp(s, "none")) {
136 		version = CACHE_SC_NONE;
137 		p9_debug(P9_DEBUG_9P, "Cache mode: none\n");
138 	} else if (kstrtoint(s, 0, &version) != 0) {
139 		version = -EINVAL;
140 		pr_info("Unknown Cache mode or invalid value %s\n", s);
141 	}
142 	return version;
143 }
144 
145 /*
146  * Display the mount options in /proc/mounts.
147  */
v9fs_show_options(struct seq_file * m,struct dentry * root)148 int v9fs_show_options(struct seq_file *m, struct dentry *root)
149 {
150 	struct v9fs_session_info *v9ses = root->d_sb->s_fs_info;
151 
152 	if (v9ses->debug)
153 		seq_printf(m, ",debug=%#x", v9ses->debug);
154 	if (!uid_eq(v9ses->dfltuid, V9FS_DEFUID))
155 		seq_printf(m, ",dfltuid=%u",
156 			   from_kuid_munged(&init_user_ns, v9ses->dfltuid));
157 	if (!gid_eq(v9ses->dfltgid, V9FS_DEFGID))
158 		seq_printf(m, ",dfltgid=%u",
159 			   from_kgid_munged(&init_user_ns, v9ses->dfltgid));
160 	if (v9ses->afid != ~0)
161 		seq_printf(m, ",afid=%u", v9ses->afid);
162 	if (strcmp(v9ses->uname, V9FS_DEFUSER) != 0)
163 		seq_printf(m, ",uname=%s", v9ses->uname);
164 	if (strcmp(v9ses->aname, V9FS_DEFANAME) != 0)
165 		seq_printf(m, ",aname=%s", v9ses->aname);
166 	if (v9ses->nodev)
167 		seq_puts(m, ",nodevmap");
168 	if (v9ses->cache)
169 		seq_printf(m, ",cache=%#x", v9ses->cache);
170 #ifdef CONFIG_9P_FSCACHE
171 	if (v9ses->cachetag && (v9ses->cache & CACHE_FSCACHE))
172 		seq_printf(m, ",cachetag=%s", v9ses->cachetag);
173 #endif
174 
175 	switch (v9ses->flags & V9FS_ACCESS_MASK) {
176 	case V9FS_ACCESS_USER:
177 		seq_puts(m, ",access=user");
178 		break;
179 	case V9FS_ACCESS_ANY:
180 		seq_puts(m, ",access=any");
181 		break;
182 	case V9FS_ACCESS_CLIENT:
183 		seq_puts(m, ",access=client");
184 		break;
185 	case V9FS_ACCESS_SINGLE:
186 		seq_printf(m, ",access=%u",
187 			   from_kuid_munged(&init_user_ns, v9ses->uid));
188 		break;
189 	}
190 
191 	if (v9ses->flags & V9FS_IGNORE_QV)
192 		seq_puts(m, ",ignoreqv");
193 	if (v9ses->flags & V9FS_DIRECT_IO)
194 		seq_puts(m, ",directio");
195 	if (v9ses->flags & V9FS_POSIX_ACL)
196 		seq_puts(m, ",posixacl");
197 
198 	if (v9ses->flags & V9FS_NO_XATTR)
199 		seq_puts(m, ",noxattr");
200 
201 	return p9_show_client_options(m, v9ses->clnt);
202 }
203 
204 /**
205  * v9fs_parse_param - parse a mount option into the filesystem context
206  * @fc: the filesystem context
207  * @param: the parameter to parse
208  *
209  * Return 0 upon success, -ERRNO upon failure.
210  */
v9fs_parse_param(struct fs_context * fc,struct fs_parameter * param)211 int v9fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
212 {
213 	struct v9fs_context *ctx = fc->fs_private;
214 	struct fs_parse_result result;
215 	char *s;
216 	int r;
217 	int opt;
218 	struct p9_client_opts	*clnt = &ctx->client_opts;
219 	struct p9_fd_opts	*fd_opts = &ctx->fd_opts;
220 	struct p9_rdma_opts	*rdma_opts = &ctx->rdma_opts;
221 	struct p9_session_opts	*session_opts = &ctx->session_opts;
222 
223 	opt = fs_parse(fc, v9fs_param_spec, param, &result);
224 	if (opt < 0) {
225 		/*
226 		 * We might like to report bad mount options here, but
227 		 * traditionally 9p has ignored unknown mount options
228 		 */
229 		if (opt == -ENOPARAM)
230 			return 0;
231 
232 		return opt;
233 	}
234 
235 	switch (opt) {
236 	case Opt_source:
237 		if (fc->source) {
238 			pr_info("p9: multiple sources not supported\n");
239 			return -EINVAL;
240 		}
241 		fc->source = param->string;
242 		param->string = NULL;
243 		break;
244 	case Opt_debug:
245 		session_opts->debug = result.uint_32;
246 #ifdef CONFIG_NET_9P_DEBUG
247 		p9_debug_level = result.uint_32;
248 #endif
249 		break;
250 
251 	case Opt_dfltuid:
252 		session_opts->dfltuid = result.uid;
253 		break;
254 	case Opt_dfltgid:
255 		session_opts->dfltgid = result.gid;
256 		break;
257 	case Opt_afid:
258 		session_opts->afid = result.uint_32;
259 		break;
260 	case Opt_uname:
261 		kfree(session_opts->uname);
262 		session_opts->uname = param->string;
263 		param->string = NULL;
264 		break;
265 	case Opt_remotename:
266 		kfree(session_opts->aname);
267 		session_opts->aname = param->string;
268 		param->string = NULL;
269 		break;
270 	case Opt_nodevmap:
271 		session_opts->nodev = 1;
272 		break;
273 	case Opt_noxattr:
274 		session_opts->flags |= V9FS_NO_XATTR;
275 		break;
276 	case Opt_directio:
277 		session_opts->flags |= V9FS_DIRECT_IO;
278 		break;
279 	case Opt_ignoreqv:
280 		session_opts->flags |= V9FS_IGNORE_QV;
281 		break;
282 	case Opt_cachetag:
283 #ifdef CONFIG_9P_FSCACHE
284 		kfree(session_opts->cachetag);
285 		session_opts->cachetag = param->string;
286 		param->string = NULL;
287 #endif
288 		break;
289 	case Opt_cache:
290 		r = get_cache_mode(param->string);
291 		if (r < 0)
292 			return r;
293 		session_opts->cache = r;
294 		break;
295 	case Opt_access:
296 		s = param->string;
297 		session_opts->flags &= ~V9FS_ACCESS_MASK;
298 		if (strcmp(s, "user") == 0) {
299 			session_opts->flags |= V9FS_ACCESS_USER;
300 		} else if (strcmp(s, "any") == 0) {
301 			session_opts->flags |= V9FS_ACCESS_ANY;
302 		} else if (strcmp(s, "client") == 0) {
303 			session_opts->flags |= V9FS_ACCESS_CLIENT;
304 		} else {
305 			uid_t uid;
306 
307 			session_opts->flags |= V9FS_ACCESS_SINGLE;
308 			r = kstrtouint(s, 10, &uid);
309 			if (r) {
310 				pr_info("Unknown access argument %s: %d\n",
311 					param->string, r);
312 				return r;
313 			}
314 			session_opts->uid = make_kuid(current_user_ns(), uid);
315 			if (!uid_valid(session_opts->uid)) {
316 				pr_info("Unknown uid %s\n", s);
317 				return -EINVAL;
318 			}
319 		}
320 		break;
321 
322 	case Opt_posixacl:
323 #ifdef CONFIG_9P_FS_POSIX_ACL
324 		session_opts->flags |= V9FS_POSIX_ACL;
325 #else
326 		p9_debug(P9_DEBUG_ERROR,
327 			 "Not defined CONFIG_9P_FS_POSIX_ACL. Ignoring posixacl option\n");
328 #endif
329 		break;
330 
331 	case Opt_locktimeout:
332 		if (result.uint_32 < 1) {
333 			p9_debug(P9_DEBUG_ERROR,
334 				 "locktimeout must be a greater than zero integer.\n");
335 			return -EINVAL;
336 		}
337 		session_opts->session_lock_timeout = (long)result.uint_32 * HZ;
338 		break;
339 
340 	/* Options for client */
341 	case Opt_msize:
342 		if (result.uint_32 < 4096) {
343 			p9_debug(P9_DEBUG_ERROR, "msize should be at least 4k\n");
344 			return -EINVAL;
345 		}
346 		if (result.uint_32 > INT_MAX) {
347 			p9_debug(P9_DEBUG_ERROR, "msize too big\n");
348 			return -EINVAL;
349 		}
350 		clnt->msize = result.uint_32;
351 		break;
352 	case Opt_trans:
353 		v9fs_put_trans(clnt->trans_mod);
354 		clnt->trans_mod = v9fs_get_trans_by_name(param->string);
355 		if (!clnt->trans_mod) {
356 			pr_info("Could not find request transport: %s\n",
357 				param->string);
358 			return -EINVAL;
359 		}
360 		break;
361 	case Opt_legacy:
362 		clnt->proto_version = p9_proto_legacy;
363 		break;
364 	case Opt_version:
365 		clnt->proto_version = result.uint_32;
366 		p9_debug(P9_DEBUG_9P, "Protocol version: %s\n", param->string);
367 		break;
368 	/* Options for fd transport */
369 	case Opt_rfdno:
370 		fd_opts->rfd = result.uint_32;
371 		break;
372 	case Opt_wfdno:
373 		fd_opts->wfd = result.uint_32;
374 		break;
375 	/* Options for rdma transport */
376 	case Opt_sq_depth:
377 		rdma_opts->sq_depth = result.uint_32;
378 		break;
379 	case Opt_rq_depth:
380 		rdma_opts->rq_depth = result.uint_32;
381 		break;
382 	case Opt_timeout:
383 		rdma_opts->timeout = result.uint_32;
384 		break;
385 	/* Options for both fd and rdma transports */
386 	case Opt_port:
387 		fd_opts->port = result.uint_32;
388 		rdma_opts->port = result.uint_32;
389 		break;
390 	case Opt_privport:
391 		fd_opts->privport = true;
392 		rdma_opts->port = true;
393 		break;
394 	}
395 
396 	return 0;
397 }
398 
v9fs_apply_options(struct v9fs_session_info * v9ses,struct fs_context * fc)399 static void v9fs_apply_options(struct v9fs_session_info *v9ses,
400 		  struct fs_context *fc)
401 {
402 	struct v9fs_context	*ctx = fc->fs_private;
403 
404 	v9ses->debug = ctx->session_opts.debug;
405 	v9ses->dfltuid = ctx->session_opts.dfltuid;
406 	v9ses->dfltgid = ctx->session_opts.dfltgid;
407 	v9ses->afid = ctx->session_opts.afid;
408 	v9ses->uname = ctx->session_opts.uname;
409 	ctx->session_opts.uname = NULL;
410 	v9ses->aname = ctx->session_opts.aname;
411 	ctx->session_opts.aname = NULL;
412 	v9ses->nodev = ctx->session_opts.nodev;
413 	/*
414 	 * Note that we must |= flags here as session_init already
415 	 * set basic flags. This adds in flags from parsed options.
416 	 */
417 	v9ses->flags |= ctx->session_opts.flags;
418 #ifdef CONFIG_9P_FSCACHE
419 	v9ses->cachetag = ctx->session_opts.cachetag;
420 	ctx->session_opts.cachetag = NULL;
421 #endif
422 	v9ses->cache = ctx->session_opts.cache;
423 	v9ses->uid = ctx->session_opts.uid;
424 	v9ses->session_lock_timeout = ctx->session_opts.session_lock_timeout;
425 }
426 
427 /**
428  * v9fs_session_init - initialize session
429  * @v9ses: session information structure
430  * @fc: the filesystem mount context
431  *
432  */
433 
v9fs_session_init(struct v9fs_session_info * v9ses,struct fs_context * fc)434 struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
435 		  struct fs_context *fc)
436 {
437 	struct p9_fid *fid;
438 	int rc = -ENOMEM;
439 
440 	init_rwsem(&v9ses->rename_sem);
441 
442 	v9ses->clnt = p9_client_create(fc);
443 	if (IS_ERR(v9ses->clnt)) {
444 		rc = PTR_ERR(v9ses->clnt);
445 		p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n");
446 		goto err_names;
447 	}
448 
449 	/*
450 	 * Initialize flags on the real v9ses. v9fs_apply_options below
451 	 * will |= the additional flags from parsed options.
452 	 */
453 	v9ses->flags = V9FS_ACCESS_USER;
454 
455 	if (p9_is_proto_dotl(v9ses->clnt)) {
456 		v9ses->flags = V9FS_ACCESS_CLIENT;
457 		v9ses->flags |= V9FS_PROTO_2000L;
458 	} else if (p9_is_proto_dotu(v9ses->clnt)) {
459 		v9ses->flags |= V9FS_PROTO_2000U;
460 	}
461 
462 	v9fs_apply_options(v9ses, fc);
463 
464 	v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
465 
466 	if (!v9fs_proto_dotl(v9ses) &&
467 	    ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
468 		/*
469 		 * We support ACCESS_CLIENT only for dotl.
470 		 * Fall back to ACCESS_USER
471 		 */
472 		v9ses->flags &= ~V9FS_ACCESS_MASK;
473 		v9ses->flags |= V9FS_ACCESS_USER;
474 	}
475 	/* FIXME: for legacy mode, fall back to V9FS_ACCESS_ANY */
476 	if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) &&
477 		((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
478 
479 		v9ses->flags &= ~V9FS_ACCESS_MASK;
480 		v9ses->flags |= V9FS_ACCESS_ANY;
481 		v9ses->uid = INVALID_UID;
482 	}
483 	if (!v9fs_proto_dotl(v9ses) ||
484 		!((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
485 		/*
486 		 * We support ACL checks on client only if the protocol is
487 		 * 9P2000.L and access is V9FS_ACCESS_CLIENT.
488 		 */
489 		v9ses->flags &= ~V9FS_ACL_MASK;
490 	}
491 
492 	fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID,
493 							v9ses->aname);
494 	if (IS_ERR(fid)) {
495 		rc = PTR_ERR(fid);
496 		p9_debug(P9_DEBUG_ERROR, "cannot attach\n");
497 		goto err_clnt;
498 	}
499 
500 	if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
501 		fid->uid = v9ses->uid;
502 	else
503 		fid->uid = INVALID_UID;
504 
505 #ifdef CONFIG_9P_FSCACHE
506 	/* register the session for caching */
507 	if (v9ses->cache & CACHE_FSCACHE) {
508 		rc = v9fs_cache_session_get_cookie(v9ses, fc->source);
509 		if (rc < 0)
510 			goto err_clnt;
511 	}
512 #endif
513 	spin_lock(&v9fs_sessionlist_lock);
514 	list_add(&v9ses->slist, &v9fs_sessionlist);
515 	spin_unlock(&v9fs_sessionlist_lock);
516 
517 	return fid;
518 
519 err_clnt:
520 #ifdef CONFIG_9P_FSCACHE
521 	kfree(v9ses->cachetag);
522 #endif
523 	p9_client_destroy(v9ses->clnt);
524 err_names:
525 	kfree(v9ses->uname);
526 	kfree(v9ses->aname);
527 	return ERR_PTR(rc);
528 }
529 
530 /**
531  * v9fs_session_close - shutdown a session
532  * @v9ses: session information structure
533  *
534  */
535 
v9fs_session_close(struct v9fs_session_info * v9ses)536 void v9fs_session_close(struct v9fs_session_info *v9ses)
537 {
538 	if (v9ses->clnt) {
539 		p9_client_destroy(v9ses->clnt);
540 		v9ses->clnt = NULL;
541 	}
542 
543 #ifdef CONFIG_9P_FSCACHE
544 	fscache_relinquish_volume(v9fs_session_cache(v9ses), NULL, false);
545 	kfree(v9ses->cachetag);
546 #endif
547 	kfree(v9ses->uname);
548 	kfree(v9ses->aname);
549 
550 	spin_lock(&v9fs_sessionlist_lock);
551 	list_del(&v9ses->slist);
552 	spin_unlock(&v9fs_sessionlist_lock);
553 }
554 
555 /**
556  * v9fs_session_cancel - terminate a session
557  * @v9ses: session to terminate
558  *
559  * mark transport as disconnected and cancel all pending requests.
560  */
561 
v9fs_session_cancel(struct v9fs_session_info * v9ses)562 void v9fs_session_cancel(struct v9fs_session_info *v9ses)
563 {
564 	p9_debug(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
565 	p9_client_disconnect(v9ses->clnt);
566 }
567 
568 /**
569  * v9fs_session_begin_cancel - Begin terminate of a session
570  * @v9ses: session to terminate
571  *
572  * After this call we don't allow any request other than clunk.
573  */
574 
v9fs_session_begin_cancel(struct v9fs_session_info * v9ses)575 void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses)
576 {
577 	p9_debug(P9_DEBUG_ERROR, "begin cancel session %p\n", v9ses);
578 	p9_client_begin_disconnect(v9ses->clnt);
579 }
580 
581 static struct kobject *v9fs_kobj;
582 
583 #ifdef CONFIG_9P_FSCACHE
584 /*
585  * List caches associated with a session
586  */
caches_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)587 static ssize_t caches_show(struct kobject *kobj,
588 			   struct kobj_attribute *attr,
589 			   char *buf)
590 {
591 	ssize_t n = 0, count = 0, limit = PAGE_SIZE;
592 	struct v9fs_session_info *v9ses;
593 
594 	spin_lock(&v9fs_sessionlist_lock);
595 	list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
596 		if (v9ses->cachetag) {
597 			n = snprintf(buf + count, limit, "%s\n", v9ses->cachetag);
598 			if (n < 0) {
599 				count = n;
600 				break;
601 			}
602 
603 			count += n;
604 			limit -= n;
605 		}
606 	}
607 
608 	spin_unlock(&v9fs_sessionlist_lock);
609 	return count;
610 }
611 
612 static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
613 #endif /* CONFIG_9P_FSCACHE */
614 
615 static struct attribute *v9fs_attrs[] = {
616 #ifdef CONFIG_9P_FSCACHE
617 	&v9fs_attr_cache.attr,
618 #endif
619 	NULL,
620 };
621 
622 static const struct attribute_group v9fs_attr_group = {
623 	.attrs = v9fs_attrs,
624 };
625 
626 /**
627  * v9fs_sysfs_init - Initialize the v9fs sysfs interface
628  *
629  */
630 
v9fs_sysfs_init(void)631 static int __init v9fs_sysfs_init(void)
632 {
633 	int ret;
634 
635 	v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
636 	if (!v9fs_kobj)
637 		return -ENOMEM;
638 
639 	ret = sysfs_create_group(v9fs_kobj, &v9fs_attr_group);
640 	if (ret) {
641 		kobject_put(v9fs_kobj);
642 		return ret;
643 	}
644 
645 	return 0;
646 }
647 
648 /**
649  * v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
650  *
651  */
652 
v9fs_sysfs_cleanup(void)653 static void v9fs_sysfs_cleanup(void)
654 {
655 	sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
656 	kobject_put(v9fs_kobj);
657 }
658 
v9fs_inode_init_once(void * foo)659 static void v9fs_inode_init_once(void *foo)
660 {
661 	struct v9fs_inode *v9inode = (struct v9fs_inode *)foo;
662 
663 	memset(&v9inode->qid, 0, sizeof(v9inode->qid));
664 	inode_init_once(&v9inode->netfs.inode);
665 }
666 
667 /**
668  * v9fs_init_inode_cache - initialize a cache for 9P
669  * Returns 0 on success.
670  */
v9fs_init_inode_cache(void)671 static int v9fs_init_inode_cache(void)
672 {
673 	v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache",
674 					  sizeof(struct v9fs_inode),
675 					  0, (SLAB_RECLAIM_ACCOUNT|
676 					      SLAB_ACCOUNT),
677 					  v9fs_inode_init_once);
678 	if (!v9fs_inode_cache)
679 		return -ENOMEM;
680 
681 	return 0;
682 }
683 
684 /**
685  * v9fs_destroy_inode_cache - destroy the cache of 9P inode
686  *
687  */
v9fs_destroy_inode_cache(void)688 static void v9fs_destroy_inode_cache(void)
689 {
690 	/*
691 	 * Make sure all delayed rcu free inodes are flushed before we
692 	 * destroy cache.
693 	 */
694 	rcu_barrier();
695 	kmem_cache_destroy(v9fs_inode_cache);
696 }
697 
698 /**
699  * init_v9fs - Initialize module
700  *
701  */
702 
init_v9fs(void)703 static int __init init_v9fs(void)
704 {
705 	int err;
706 
707 	pr_info("Installing v9fs 9p2000 file system support\n");
708 	/* TODO: Setup list of registered transport modules */
709 
710 	err = v9fs_init_inode_cache();
711 	if (err < 0) {
712 		pr_err("Failed to register v9fs for caching\n");
713 		return err;
714 	}
715 
716 	err = v9fs_sysfs_init();
717 	if (err < 0) {
718 		pr_err("Failed to register with sysfs\n");
719 		goto out_cache;
720 	}
721 	err = register_filesystem(&v9fs_fs_type);
722 	if (err < 0) {
723 		pr_err("Failed to register filesystem\n");
724 		goto out_sysfs_cleanup;
725 	}
726 
727 	return 0;
728 
729 out_sysfs_cleanup:
730 	v9fs_sysfs_cleanup();
731 
732 out_cache:
733 	v9fs_destroy_inode_cache();
734 
735 	return err;
736 }
737 
738 /**
739  * exit_v9fs - shutdown module
740  *
741  */
742 
exit_v9fs(void)743 static void __exit exit_v9fs(void)
744 {
745 	v9fs_sysfs_cleanup();
746 	v9fs_destroy_inode_cache();
747 	unregister_filesystem(&v9fs_fs_type);
748 }
749 
750 module_init(init_v9fs)
751 module_exit(exit_v9fs)
752 
753 MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
754 MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
755 MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
756 MODULE_DESCRIPTION("9P Client File System");
757 MODULE_LICENSE("GPL");
758