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 * Default access flags must be cleared if session options
417 * changes them to avoid mangling the setting.
418 */
419 if (ctx->session_opts.flags & V9FS_ACCESS_MASK)
420 v9ses->flags &= ~V9FS_ACCESS_MASK;
421 v9ses->flags |= ctx->session_opts.flags;
422 #ifdef CONFIG_9P_FSCACHE
423 v9ses->cachetag = ctx->session_opts.cachetag;
424 ctx->session_opts.cachetag = NULL;
425 #endif
426 v9ses->cache = ctx->session_opts.cache;
427 v9ses->uid = ctx->session_opts.uid;
428 v9ses->session_lock_timeout = ctx->session_opts.session_lock_timeout;
429 }
430
431 /**
432 * v9fs_session_init - initialize session
433 * @v9ses: session information structure
434 * @fc: the filesystem mount context
435 *
436 */
437
v9fs_session_init(struct v9fs_session_info * v9ses,struct fs_context * fc)438 struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
439 struct fs_context *fc)
440 {
441 struct p9_fid *fid;
442 int rc = -ENOMEM;
443
444 init_rwsem(&v9ses->rename_sem);
445
446 v9ses->clnt = p9_client_create(fc);
447 if (IS_ERR(v9ses->clnt)) {
448 rc = PTR_ERR(v9ses->clnt);
449 p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n");
450 goto err_names;
451 }
452
453 /*
454 * Initialize flags on the real v9ses. v9fs_apply_options below
455 * will |= the additional flags from parsed options.
456 */
457 v9ses->flags = V9FS_ACCESS_USER;
458
459 if (p9_is_proto_dotl(v9ses->clnt)) {
460 v9ses->flags = V9FS_ACCESS_CLIENT;
461 v9ses->flags |= V9FS_PROTO_2000L;
462 } else if (p9_is_proto_dotu(v9ses->clnt)) {
463 v9ses->flags |= V9FS_PROTO_2000U;
464 }
465
466 v9fs_apply_options(v9ses, fc);
467
468 v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
469
470 if (!v9fs_proto_dotl(v9ses) &&
471 ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
472 /*
473 * We support ACCESS_CLIENT only for dotl.
474 * Fall back to ACCESS_USER
475 */
476 v9ses->flags &= ~V9FS_ACCESS_MASK;
477 v9ses->flags |= V9FS_ACCESS_USER;
478 }
479 /* FIXME: for legacy mode, fall back to V9FS_ACCESS_ANY */
480 if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) &&
481 ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
482
483 v9ses->flags &= ~V9FS_ACCESS_MASK;
484 v9ses->flags |= V9FS_ACCESS_ANY;
485 v9ses->uid = INVALID_UID;
486 }
487 if (!v9fs_proto_dotl(v9ses) ||
488 !((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
489 /*
490 * We support ACL checks on client only if the protocol is
491 * 9P2000.L and access is V9FS_ACCESS_CLIENT.
492 */
493 v9ses->flags &= ~V9FS_ACL_MASK;
494 }
495
496 fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID,
497 v9ses->aname);
498 if (IS_ERR(fid)) {
499 rc = PTR_ERR(fid);
500 p9_debug(P9_DEBUG_ERROR, "cannot attach\n");
501 goto err_clnt;
502 }
503
504 if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
505 fid->uid = v9ses->uid;
506 else
507 fid->uid = INVALID_UID;
508
509 #ifdef CONFIG_9P_FSCACHE
510 /* register the session for caching */
511 if (v9ses->cache & CACHE_FSCACHE) {
512 rc = v9fs_cache_session_get_cookie(v9ses, fc->source);
513 if (rc < 0)
514 goto err_clnt;
515 }
516 #endif
517 spin_lock(&v9fs_sessionlist_lock);
518 list_add(&v9ses->slist, &v9fs_sessionlist);
519 spin_unlock(&v9fs_sessionlist_lock);
520
521 return fid;
522
523 err_clnt:
524 #ifdef CONFIG_9P_FSCACHE
525 kfree(v9ses->cachetag);
526 #endif
527 p9_client_destroy(v9ses->clnt);
528 err_names:
529 kfree(v9ses->uname);
530 kfree(v9ses->aname);
531 return ERR_PTR(rc);
532 }
533
534 /**
535 * v9fs_session_close - shutdown a session
536 * @v9ses: session information structure
537 *
538 */
539
v9fs_session_close(struct v9fs_session_info * v9ses)540 void v9fs_session_close(struct v9fs_session_info *v9ses)
541 {
542 if (v9ses->clnt) {
543 p9_client_destroy(v9ses->clnt);
544 v9ses->clnt = NULL;
545 }
546
547 #ifdef CONFIG_9P_FSCACHE
548 fscache_relinquish_volume(v9fs_session_cache(v9ses), NULL, false);
549 kfree(v9ses->cachetag);
550 #endif
551 kfree(v9ses->uname);
552 kfree(v9ses->aname);
553
554 spin_lock(&v9fs_sessionlist_lock);
555 list_del(&v9ses->slist);
556 spin_unlock(&v9fs_sessionlist_lock);
557 }
558
559 /**
560 * v9fs_session_cancel - terminate a session
561 * @v9ses: session to terminate
562 *
563 * mark transport as disconnected and cancel all pending requests.
564 */
565
v9fs_session_cancel(struct v9fs_session_info * v9ses)566 void v9fs_session_cancel(struct v9fs_session_info *v9ses)
567 {
568 p9_debug(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
569 p9_client_disconnect(v9ses->clnt);
570 }
571
572 /**
573 * v9fs_session_begin_cancel - Begin terminate of a session
574 * @v9ses: session to terminate
575 *
576 * After this call we don't allow any request other than clunk.
577 */
578
v9fs_session_begin_cancel(struct v9fs_session_info * v9ses)579 void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses)
580 {
581 p9_debug(P9_DEBUG_ERROR, "begin cancel session %p\n", v9ses);
582 p9_client_begin_disconnect(v9ses->clnt);
583 }
584
585 static struct kobject *v9fs_kobj;
586
587 #ifdef CONFIG_9P_FSCACHE
588 /*
589 * List caches associated with a session
590 */
caches_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)591 static ssize_t caches_show(struct kobject *kobj,
592 struct kobj_attribute *attr,
593 char *buf)
594 {
595 ssize_t n = 0, count = 0, limit = PAGE_SIZE;
596 struct v9fs_session_info *v9ses;
597
598 spin_lock(&v9fs_sessionlist_lock);
599 list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
600 if (v9ses->cachetag) {
601 n = snprintf(buf + count, limit, "%s\n", v9ses->cachetag);
602 if (n < 0) {
603 count = n;
604 break;
605 }
606
607 count += n;
608 limit -= n;
609 }
610 }
611
612 spin_unlock(&v9fs_sessionlist_lock);
613 return count;
614 }
615
616 static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
617 #endif /* CONFIG_9P_FSCACHE */
618
619 static struct attribute *v9fs_attrs[] = {
620 #ifdef CONFIG_9P_FSCACHE
621 &v9fs_attr_cache.attr,
622 #endif
623 NULL,
624 };
625
626 static const struct attribute_group v9fs_attr_group = {
627 .attrs = v9fs_attrs,
628 };
629
630 /**
631 * v9fs_sysfs_init - Initialize the v9fs sysfs interface
632 *
633 */
634
v9fs_sysfs_init(void)635 static int __init v9fs_sysfs_init(void)
636 {
637 int ret;
638
639 v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
640 if (!v9fs_kobj)
641 return -ENOMEM;
642
643 ret = sysfs_create_group(v9fs_kobj, &v9fs_attr_group);
644 if (ret) {
645 kobject_put(v9fs_kobj);
646 return ret;
647 }
648
649 return 0;
650 }
651
652 /**
653 * v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
654 *
655 */
656
v9fs_sysfs_cleanup(void)657 static void v9fs_sysfs_cleanup(void)
658 {
659 sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
660 kobject_put(v9fs_kobj);
661 }
662
v9fs_inode_init_once(void * foo)663 static void v9fs_inode_init_once(void *foo)
664 {
665 struct v9fs_inode *v9inode = (struct v9fs_inode *)foo;
666
667 memset(&v9inode->qid, 0, sizeof(v9inode->qid));
668 inode_init_once(&v9inode->netfs.inode);
669 }
670
671 /**
672 * v9fs_init_inode_cache - initialize a cache for 9P
673 * Returns 0 on success.
674 */
v9fs_init_inode_cache(void)675 static int v9fs_init_inode_cache(void)
676 {
677 v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache",
678 sizeof(struct v9fs_inode),
679 0, (SLAB_RECLAIM_ACCOUNT|
680 SLAB_ACCOUNT),
681 v9fs_inode_init_once);
682 if (!v9fs_inode_cache)
683 return -ENOMEM;
684
685 return 0;
686 }
687
688 /**
689 * v9fs_destroy_inode_cache - destroy the cache of 9P inode
690 *
691 */
v9fs_destroy_inode_cache(void)692 static void v9fs_destroy_inode_cache(void)
693 {
694 /*
695 * Make sure all delayed rcu free inodes are flushed before we
696 * destroy cache.
697 */
698 rcu_barrier();
699 kmem_cache_destroy(v9fs_inode_cache);
700 }
701
702 /**
703 * init_v9fs - Initialize module
704 *
705 */
706
init_v9fs(void)707 static int __init init_v9fs(void)
708 {
709 int err;
710
711 pr_info("Installing v9fs 9p2000 file system support\n");
712 /* TODO: Setup list of registered transport modules */
713
714 err = v9fs_init_inode_cache();
715 if (err < 0) {
716 pr_err("Failed to register v9fs for caching\n");
717 return err;
718 }
719
720 err = v9fs_sysfs_init();
721 if (err < 0) {
722 pr_err("Failed to register with sysfs\n");
723 goto out_cache;
724 }
725 err = register_filesystem(&v9fs_fs_type);
726 if (err < 0) {
727 pr_err("Failed to register filesystem\n");
728 goto out_sysfs_cleanup;
729 }
730
731 return 0;
732
733 out_sysfs_cleanup:
734 v9fs_sysfs_cleanup();
735
736 out_cache:
737 v9fs_destroy_inode_cache();
738
739 return err;
740 }
741
742 /**
743 * exit_v9fs - shutdown module
744 *
745 */
746
exit_v9fs(void)747 static void __exit exit_v9fs(void)
748 {
749 v9fs_sysfs_cleanup();
750 v9fs_destroy_inode_cache();
751 unregister_filesystem(&v9fs_fs_type);
752 }
753
754 module_init(init_v9fs)
755 module_exit(exit_v9fs)
756
757 MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
758 MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
759 MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
760 MODULE_DESCRIPTION("9P Client File System");
761 MODULE_LICENSE("GPL");
762