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