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