at master 19 kB view raw
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 27static DEFINE_SPINLOCK(v9fs_sessionlist_lock); 28static LIST_HEAD(v9fs_sessionlist); 29struct 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 36enum { 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 68static 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 */ 79const 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 */ 119static 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 */ 148int 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 */ 211int 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 399static 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 434struct 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 519err_clnt: 520#ifdef CONFIG_9P_FSCACHE 521 kfree(v9ses->cachetag); 522#endif 523 p9_client_destroy(v9ses->clnt); 524err_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 536void 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 562void 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 575void 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 581static struct kobject *v9fs_kobj; 582 583#ifdef CONFIG_9P_FSCACHE 584/* 585 * List caches associated with a session 586 */ 587static 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 612static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches); 613#endif /* CONFIG_9P_FSCACHE */ 614 615static struct attribute *v9fs_attrs[] = { 616#ifdef CONFIG_9P_FSCACHE 617 &v9fs_attr_cache.attr, 618#endif 619 NULL, 620}; 621 622static 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 631static 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 653static void v9fs_sysfs_cleanup(void) 654{ 655 sysfs_remove_group(v9fs_kobj, &v9fs_attr_group); 656 kobject_put(v9fs_kobj); 657} 658 659static 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 */ 671static 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 */ 688static 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 703static 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 729out_sysfs_cleanup: 730 v9fs_sysfs_cleanup(); 731 732out_cache: 733 v9fs_destroy_inode_cache(); 734 735 return err; 736} 737 738/** 739 * exit_v9fs - shutdown module 740 * 741 */ 742 743static void __exit exit_v9fs(void) 744{ 745 v9fs_sysfs_cleanup(); 746 v9fs_destroy_inode_cache(); 747 unregister_filesystem(&v9fs_fs_type); 748} 749 750module_init(init_v9fs) 751module_exit(exit_v9fs) 752 753MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>"); 754MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>"); 755MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>"); 756MODULE_DESCRIPTION("9P Client File System"); 757MODULE_LICENSE("GPL");