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