Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[linux-2.6-microblaze.git] / fs / 9p / v9fs.c
1 /*
2  *  linux/fs/9p/v9fs.c
3  *
4  *  This file contains functions assisting in mapping VFS to 9P2000
5  *
6  *  Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
7  *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License version 2
11  *  as published by the Free Software Foundation.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to:
20  *  Free Software Foundation
21  *  51 Franklin Street, Fifth Floor
22  *  Boston, MA  02111-1301  USA
23  *
24  */
25
26 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
27
28 #include <linux/module.h>
29 #include <linux/errno.h>
30 #include <linux/fs.h>
31 #include <linux/sched.h>
32 #include <linux/cred.h>
33 #include <linux/parser.h>
34 #include <linux/idr.h>
35 #include <linux/slab.h>
36 #include <linux/seq_file.h>
37 #include <net/9p/9p.h>
38 #include <net/9p/client.h>
39 #include <net/9p/transport.h>
40 #include "v9fs.h"
41 #include "v9fs_vfs.h"
42 #include "cache.h"
43
44 static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
45 static LIST_HEAD(v9fs_sessionlist);
46 struct kmem_cache *v9fs_inode_cache;
47
48 /*
49  * Option Parsing (code inspired by NFS code)
50  *  NOTE: each transport will parse its own options
51  */
52
53 enum {
54         /* Options that take integer arguments */
55         Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid,
56         /* String options */
57         Opt_uname, Opt_remotename, Opt_cache, Opt_cachetag,
58         /* Options that take no arguments */
59         Opt_nodevmap,
60         /* Cache options */
61         Opt_cache_loose, Opt_fscache, Opt_mmap,
62         /* Access options */
63         Opt_access, Opt_posixacl,
64         /* Error token */
65         Opt_err
66 };
67
68 static const match_table_t tokens = {
69         {Opt_debug, "debug=%x"},
70         {Opt_dfltuid, "dfltuid=%u"},
71         {Opt_dfltgid, "dfltgid=%u"},
72         {Opt_afid, "afid=%u"},
73         {Opt_uname, "uname=%s"},
74         {Opt_remotename, "aname=%s"},
75         {Opt_nodevmap, "nodevmap"},
76         {Opt_cache, "cache=%s"},
77         {Opt_cache_loose, "loose"},
78         {Opt_fscache, "fscache"},
79         {Opt_mmap, "mmap"},
80         {Opt_cachetag, "cachetag=%s"},
81         {Opt_access, "access=%s"},
82         {Opt_posixacl, "posixacl"},
83         {Opt_err, NULL}
84 };
85
86 static const char *const v9fs_cache_modes[nr__p9_cache_modes] = {
87         [CACHE_NONE]    = "none",
88         [CACHE_MMAP]    = "mmap",
89         [CACHE_LOOSE]   = "loose",
90         [CACHE_FSCACHE] = "fscache",
91 };
92
93 /* Interpret mount options for cache mode */
94 static int get_cache_mode(char *s)
95 {
96         int version = -EINVAL;
97
98         if (!strcmp(s, "loose")) {
99                 version = CACHE_LOOSE;
100                 p9_debug(P9_DEBUG_9P, "Cache mode: loose\n");
101         } else if (!strcmp(s, "fscache")) {
102                 version = CACHE_FSCACHE;
103                 p9_debug(P9_DEBUG_9P, "Cache mode: fscache\n");
104         } else if (!strcmp(s, "mmap")) {
105                 version = CACHE_MMAP;
106                 p9_debug(P9_DEBUG_9P, "Cache mode: mmap\n");
107         } else if (!strcmp(s, "none")) {
108                 version = CACHE_NONE;
109                 p9_debug(P9_DEBUG_9P, "Cache mode: none\n");
110         } else
111                 pr_info("Unknown Cache mode %s\n", s);
112         return version;
113 }
114
115 /*
116  * Display the mount options in /proc/mounts.
117  */
118 int v9fs_show_options(struct seq_file *m, struct dentry *root)
119 {
120         struct v9fs_session_info *v9ses = root->d_sb->s_fs_info;
121
122         if (v9ses->debug)
123                 seq_printf(m, ",debug=%x", v9ses->debug);
124         if (!uid_eq(v9ses->dfltuid, V9FS_DEFUID))
125                 seq_printf(m, ",dfltuid=%u",
126                            from_kuid_munged(&init_user_ns, v9ses->dfltuid));
127         if (!gid_eq(v9ses->dfltgid, V9FS_DEFGID))
128                 seq_printf(m, ",dfltgid=%u",
129                            from_kgid_munged(&init_user_ns, v9ses->dfltgid));
130         if (v9ses->afid != ~0)
131                 seq_printf(m, ",afid=%u", v9ses->afid);
132         if (strcmp(v9ses->uname, V9FS_DEFUSER) != 0)
133                 seq_printf(m, ",uname=%s", v9ses->uname);
134         if (strcmp(v9ses->aname, V9FS_DEFANAME) != 0)
135                 seq_printf(m, ",aname=%s", v9ses->aname);
136         if (v9ses->nodev)
137                 seq_puts(m, ",nodevmap");
138         if (v9ses->cache)
139                 seq_printf(m, ",%s", v9fs_cache_modes[v9ses->cache]);
140 #ifdef CONFIG_9P_FSCACHE
141         if (v9ses->cachetag && v9ses->cache == CACHE_FSCACHE)
142                 seq_printf(m, ",cachetag=%s", v9ses->cachetag);
143 #endif
144
145         switch (v9ses->flags & V9FS_ACCESS_MASK) {
146         case V9FS_ACCESS_USER:
147                 seq_puts(m, ",access=user");
148                 break;
149         case V9FS_ACCESS_ANY:
150                 seq_puts(m, ",access=any");
151                 break;
152         case V9FS_ACCESS_CLIENT:
153                 seq_puts(m, ",access=client");
154                 break;
155         case V9FS_ACCESS_SINGLE:
156                 seq_printf(m, ",access=%u",
157                            from_kuid_munged(&init_user_ns, v9ses->uid));
158                 break;
159         }
160
161         if (v9ses->flags & V9FS_POSIX_ACL)
162                 seq_puts(m, ",posixacl");
163
164         return p9_show_client_options(m, v9ses->clnt);
165 }
166
167 /**
168  * v9fs_parse_options - parse mount options into session structure
169  * @v9ses: existing v9fs session information
170  *
171  * Return 0 upon success, -ERRNO upon failure.
172  */
173
174 static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
175 {
176         char *options, *tmp_options;
177         substring_t args[MAX_OPT_ARGS];
178         char *p;
179         int option = 0;
180         char *s, *e;
181         int ret = 0;
182
183         /* setup defaults */
184         v9ses->afid = ~0;
185         v9ses->debug = 0;
186         v9ses->cache = CACHE_NONE;
187 #ifdef CONFIG_9P_FSCACHE
188         v9ses->cachetag = NULL;
189 #endif
190
191         if (!opts)
192                 return 0;
193
194         tmp_options = kstrdup(opts, GFP_KERNEL);
195         if (!tmp_options) {
196                 ret = -ENOMEM;
197                 goto fail_option_alloc;
198         }
199         options = tmp_options;
200
201         while ((p = strsep(&options, ",")) != NULL) {
202                 int token, r;
203                 if (!*p)
204                         continue;
205                 token = match_token(p, tokens, args);
206                 switch (token) {
207                 case Opt_debug:
208                         r = match_int(&args[0], &option);
209                         if (r < 0) {
210                                 p9_debug(P9_DEBUG_ERROR,
211                                          "integer field, but no integer?\n");
212                                 ret = r;
213                         } else {
214                                 v9ses->debug = option;
215 #ifdef CONFIG_NET_9P_DEBUG
216                                 p9_debug_level = option;
217 #endif
218                         }
219                         break;
220
221                 case Opt_dfltuid:
222                         r = match_int(&args[0], &option);
223                         if (r < 0) {
224                                 p9_debug(P9_DEBUG_ERROR,
225                                          "integer field, but no integer?\n");
226                                 ret = r;
227                                 continue;
228                         }
229                         v9ses->dfltuid = make_kuid(current_user_ns(), option);
230                         if (!uid_valid(v9ses->dfltuid)) {
231                                 p9_debug(P9_DEBUG_ERROR,
232                                          "uid field, but not a uid?\n");
233                                 ret = -EINVAL;
234                         }
235                         break;
236                 case Opt_dfltgid:
237                         r = match_int(&args[0], &option);
238                         if (r < 0) {
239                                 p9_debug(P9_DEBUG_ERROR,
240                                          "integer field, but no integer?\n");
241                                 ret = r;
242                                 continue;
243                         }
244                         v9ses->dfltgid = make_kgid(current_user_ns(), option);
245                         if (!gid_valid(v9ses->dfltgid)) {
246                                 p9_debug(P9_DEBUG_ERROR,
247                                          "gid field, but not a gid?\n");
248                                 ret = -EINVAL;
249                         }
250                         break;
251                 case Opt_afid:
252                         r = match_int(&args[0], &option);
253                         if (r < 0) {
254                                 p9_debug(P9_DEBUG_ERROR,
255                                          "integer field, but no integer?\n");
256                                 ret = r;
257                         } else {
258                                 v9ses->afid = option;
259                         }
260                         break;
261                 case Opt_uname:
262                         kfree(v9ses->uname);
263                         v9ses->uname = match_strdup(&args[0]);
264                         if (!v9ses->uname) {
265                                 ret = -ENOMEM;
266                                 goto free_and_return;
267                         }
268                         break;
269                 case Opt_remotename:
270                         kfree(v9ses->aname);
271                         v9ses->aname = match_strdup(&args[0]);
272                         if (!v9ses->aname) {
273                                 ret = -ENOMEM;
274                                 goto free_and_return;
275                         }
276                         break;
277                 case Opt_nodevmap:
278                         v9ses->nodev = 1;
279                         break;
280                 case Opt_cache_loose:
281                         v9ses->cache = CACHE_LOOSE;
282                         break;
283                 case Opt_fscache:
284                         v9ses->cache = CACHE_FSCACHE;
285                         break;
286                 case Opt_mmap:
287                         v9ses->cache = CACHE_MMAP;
288                         break;
289                 case Opt_cachetag:
290 #ifdef CONFIG_9P_FSCACHE
291                         kfree(v9ses->cachetag);
292                         v9ses->cachetag = match_strdup(&args[0]);
293                         if (!v9ses->cachetag) {
294                                 ret = -ENOMEM;
295                                 goto free_and_return;
296                         }
297 #endif
298                         break;
299                 case Opt_cache:
300                         s = match_strdup(&args[0]);
301                         if (!s) {
302                                 ret = -ENOMEM;
303                                 p9_debug(P9_DEBUG_ERROR,
304                                          "problem allocating copy of cache arg\n");
305                                 goto free_and_return;
306                         }
307                         r = get_cache_mode(s);
308                         if (r < 0)
309                                 ret = r;
310                         else
311                                 v9ses->cache = r;
312
313                         kfree(s);
314                         break;
315
316                 case Opt_access:
317                         s = match_strdup(&args[0]);
318                         if (!s) {
319                                 ret = -ENOMEM;
320                                 p9_debug(P9_DEBUG_ERROR,
321                                          "problem allocating copy of access arg\n");
322                                 goto free_and_return;
323                         }
324
325                         v9ses->flags &= ~V9FS_ACCESS_MASK;
326                         if (strcmp(s, "user") == 0)
327                                 v9ses->flags |= V9FS_ACCESS_USER;
328                         else if (strcmp(s, "any") == 0)
329                                 v9ses->flags |= V9FS_ACCESS_ANY;
330                         else if (strcmp(s, "client") == 0) {
331                                 v9ses->flags |= V9FS_ACCESS_CLIENT;
332                         } else {
333                                 uid_t uid;
334                                 v9ses->flags |= V9FS_ACCESS_SINGLE;
335                                 uid = simple_strtoul(s, &e, 10);
336                                 if (*e != '\0') {
337                                         ret = -EINVAL;
338                                         pr_info("Unknown access argument %s\n",
339                                                 s);
340                                         kfree(s);
341                                         continue;
342                                 }
343                                 v9ses->uid = make_kuid(current_user_ns(), uid);
344                                 if (!uid_valid(v9ses->uid)) {
345                                         ret = -EINVAL;
346                                         pr_info("Uknown uid %s\n", s);
347                                 }
348                         }
349
350                         kfree(s);
351                         break;
352
353                 case Opt_posixacl:
354 #ifdef CONFIG_9P_FS_POSIX_ACL
355                         v9ses->flags |= V9FS_POSIX_ACL;
356 #else
357                         p9_debug(P9_DEBUG_ERROR,
358                                  "Not defined CONFIG_9P_FS_POSIX_ACL. Ignoring posixacl option\n");
359 #endif
360                         break;
361
362                 default:
363                         continue;
364                 }
365         }
366
367 free_and_return:
368         kfree(tmp_options);
369 fail_option_alloc:
370         return ret;
371 }
372
373 /**
374  * v9fs_session_init - initialize session
375  * @v9ses: session information structure
376  * @dev_name: device being mounted
377  * @data: options
378  *
379  */
380
381 struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
382                   const char *dev_name, char *data)
383 {
384         struct p9_fid *fid;
385         int rc = -ENOMEM;
386
387         v9ses->uname = kstrdup(V9FS_DEFUSER, GFP_KERNEL);
388         if (!v9ses->uname)
389                 goto err_names;
390
391         v9ses->aname = kstrdup(V9FS_DEFANAME, GFP_KERNEL);
392         if (!v9ses->aname)
393                 goto err_names;
394         init_rwsem(&v9ses->rename_sem);
395
396         v9ses->uid = INVALID_UID;
397         v9ses->dfltuid = V9FS_DEFUID;
398         v9ses->dfltgid = V9FS_DEFGID;
399
400         v9ses->clnt = p9_client_create(dev_name, data);
401         if (IS_ERR(v9ses->clnt)) {
402                 rc = PTR_ERR(v9ses->clnt);
403                 p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n");
404                 goto err_names;
405         }
406
407         v9ses->flags = V9FS_ACCESS_USER;
408
409         if (p9_is_proto_dotl(v9ses->clnt)) {
410                 v9ses->flags = V9FS_ACCESS_CLIENT;
411                 v9ses->flags |= V9FS_PROTO_2000L;
412         } else if (p9_is_proto_dotu(v9ses->clnt)) {
413                 v9ses->flags |= V9FS_PROTO_2000U;
414         }
415
416         rc = v9fs_parse_options(v9ses, data);
417         if (rc < 0)
418                 goto err_clnt;
419
420         v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
421
422         if (!v9fs_proto_dotl(v9ses) &&
423             ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
424                 /*
425                  * We support ACCESS_CLIENT only for dotl.
426                  * Fall back to ACCESS_USER
427                  */
428                 v9ses->flags &= ~V9FS_ACCESS_MASK;
429                 v9ses->flags |= V9FS_ACCESS_USER;
430         }
431         /*FIXME !! */
432         /* for legacy mode, fall back to V9FS_ACCESS_ANY */
433         if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) &&
434                 ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
435
436                 v9ses->flags &= ~V9FS_ACCESS_MASK;
437                 v9ses->flags |= V9FS_ACCESS_ANY;
438                 v9ses->uid = INVALID_UID;
439         }
440         if (!v9fs_proto_dotl(v9ses) ||
441                 !((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
442                 /*
443                  * We support ACL checks on clinet only if the protocol is
444                  * 9P2000.L and access is V9FS_ACCESS_CLIENT.
445                  */
446                 v9ses->flags &= ~V9FS_ACL_MASK;
447         }
448
449         fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID,
450                                                         v9ses->aname);
451         if (IS_ERR(fid)) {
452                 rc = PTR_ERR(fid);
453                 p9_debug(P9_DEBUG_ERROR, "cannot attach\n");
454                 goto err_clnt;
455         }
456
457         if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
458                 fid->uid = v9ses->uid;
459         else
460                 fid->uid = INVALID_UID;
461
462 #ifdef CONFIG_9P_FSCACHE
463         /* register the session for caching */
464         v9fs_cache_session_get_cookie(v9ses);
465 #endif
466         spin_lock(&v9fs_sessionlist_lock);
467         list_add(&v9ses->slist, &v9fs_sessionlist);
468         spin_unlock(&v9fs_sessionlist_lock);
469
470         return fid;
471
472 err_clnt:
473 #ifdef CONFIG_9P_FSCACHE
474         kfree(v9ses->cachetag);
475 #endif
476         p9_client_destroy(v9ses->clnt);
477 err_names:
478         kfree(v9ses->uname);
479         kfree(v9ses->aname);
480         return ERR_PTR(rc);
481 }
482
483 /**
484  * v9fs_session_close - shutdown a session
485  * @v9ses: session information structure
486  *
487  */
488
489 void v9fs_session_close(struct v9fs_session_info *v9ses)
490 {
491         if (v9ses->clnt) {
492                 p9_client_destroy(v9ses->clnt);
493                 v9ses->clnt = NULL;
494         }
495
496 #ifdef CONFIG_9P_FSCACHE
497         if (v9ses->fscache) {
498                 v9fs_cache_session_put_cookie(v9ses);
499                 kfree(v9ses->cachetag);
500         }
501 #endif
502         kfree(v9ses->uname);
503         kfree(v9ses->aname);
504
505         spin_lock(&v9fs_sessionlist_lock);
506         list_del(&v9ses->slist);
507         spin_unlock(&v9fs_sessionlist_lock);
508 }
509
510 /**
511  * v9fs_session_cancel - terminate a session
512  * @v9ses: session to terminate
513  *
514  * mark transport as disconnected and cancel all pending requests.
515  */
516
517 void v9fs_session_cancel(struct v9fs_session_info *v9ses) {
518         p9_debug(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
519         p9_client_disconnect(v9ses->clnt);
520 }
521
522 /**
523  * v9fs_session_begin_cancel - Begin terminate of a session
524  * @v9ses: session to terminate
525  *
526  * After this call we don't allow any request other than clunk.
527  */
528
529 void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses)
530 {
531         p9_debug(P9_DEBUG_ERROR, "begin cancel session %p\n", v9ses);
532         p9_client_begin_disconnect(v9ses->clnt);
533 }
534
535 extern int v9fs_error_init(void);
536
537 static struct kobject *v9fs_kobj;
538
539 #ifdef CONFIG_9P_FSCACHE
540 /**
541  * caches_show - list caches associated with a session
542  *
543  * Returns the size of buffer written.
544  */
545
546 static ssize_t caches_show(struct kobject *kobj,
547                            struct kobj_attribute *attr,
548                            char *buf)
549 {
550         ssize_t n = 0, count = 0, limit = PAGE_SIZE;
551         struct v9fs_session_info *v9ses;
552
553         spin_lock(&v9fs_sessionlist_lock);
554         list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
555                 if (v9ses->cachetag) {
556                         n = snprintf(buf, limit, "%s\n", v9ses->cachetag);
557                         if (n < 0) {
558                                 count = n;
559                                 break;
560                         }
561
562                         count += n;
563                         limit -= n;
564                 }
565         }
566
567         spin_unlock(&v9fs_sessionlist_lock);
568         return count;
569 }
570
571 static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
572 #endif /* CONFIG_9P_FSCACHE */
573
574 static struct attribute *v9fs_attrs[] = {
575 #ifdef CONFIG_9P_FSCACHE
576         &v9fs_attr_cache.attr,
577 #endif
578         NULL,
579 };
580
581 static struct attribute_group v9fs_attr_group = {
582         .attrs = v9fs_attrs,
583 };
584
585 /**
586  * v9fs_sysfs_init - Initialize the v9fs sysfs interface
587  *
588  */
589
590 static int __init v9fs_sysfs_init(void)
591 {
592         v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
593         if (!v9fs_kobj)
594                 return -ENOMEM;
595
596         if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) {
597                 kobject_put(v9fs_kobj);
598                 return -ENOMEM;
599         }
600
601         return 0;
602 }
603
604 /**
605  * v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
606  *
607  */
608
609 static void v9fs_sysfs_cleanup(void)
610 {
611         sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
612         kobject_put(v9fs_kobj);
613 }
614
615 static void v9fs_inode_init_once(void *foo)
616 {
617         struct v9fs_inode *v9inode = (struct v9fs_inode *)foo;
618 #ifdef CONFIG_9P_FSCACHE
619         v9inode->fscache = NULL;
620 #endif
621         memset(&v9inode->qid, 0, sizeof(v9inode->qid));
622         inode_init_once(&v9inode->vfs_inode);
623 }
624
625 /**
626  * v9fs_init_inode_cache - initialize a cache for 9P
627  * Returns 0 on success.
628  */
629 static int v9fs_init_inode_cache(void)
630 {
631         v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache",
632                                           sizeof(struct v9fs_inode),
633                                           0, (SLAB_RECLAIM_ACCOUNT|
634                                               SLAB_MEM_SPREAD|SLAB_ACCOUNT),
635                                           v9fs_inode_init_once);
636         if (!v9fs_inode_cache)
637                 return -ENOMEM;
638
639         return 0;
640 }
641
642 /**
643  * v9fs_destroy_inode_cache - destroy the cache of 9P inode
644  *
645  */
646 static void v9fs_destroy_inode_cache(void)
647 {
648         /*
649          * Make sure all delayed rcu free inodes are flushed before we
650          * destroy cache.
651          */
652         rcu_barrier();
653         kmem_cache_destroy(v9fs_inode_cache);
654 }
655
656 static int v9fs_cache_register(void)
657 {
658         int ret;
659         ret = v9fs_init_inode_cache();
660         if (ret < 0)
661                 return ret;
662 #ifdef CONFIG_9P_FSCACHE
663         ret = fscache_register_netfs(&v9fs_cache_netfs);
664         if (ret < 0)
665                 v9fs_destroy_inode_cache();
666 #endif
667         return ret;
668 }
669
670 static void v9fs_cache_unregister(void)
671 {
672         v9fs_destroy_inode_cache();
673 #ifdef CONFIG_9P_FSCACHE
674         fscache_unregister_netfs(&v9fs_cache_netfs);
675 #endif
676 }
677
678 /**
679  * init_v9fs - Initialize module
680  *
681  */
682
683 static int __init init_v9fs(void)
684 {
685         int err;
686         pr_info("Installing v9fs 9p2000 file system support\n");
687         /* TODO: Setup list of registered trasnport modules */
688
689         err = v9fs_cache_register();
690         if (err < 0) {
691                 pr_err("Failed to register v9fs for caching\n");
692                 return err;
693         }
694
695         err = v9fs_sysfs_init();
696         if (err < 0) {
697                 pr_err("Failed to register with sysfs\n");
698                 goto out_cache;
699         }
700         err = register_filesystem(&v9fs_fs_type);
701         if (err < 0) {
702                 pr_err("Failed to register filesystem\n");
703                 goto out_sysfs_cleanup;
704         }
705
706         return 0;
707
708 out_sysfs_cleanup:
709         v9fs_sysfs_cleanup();
710
711 out_cache:
712         v9fs_cache_unregister();
713
714         return err;
715 }
716
717 /**
718  * exit_v9fs - shutdown module
719  *
720  */
721
722 static void __exit exit_v9fs(void)
723 {
724         v9fs_sysfs_cleanup();
725         v9fs_cache_unregister();
726         unregister_filesystem(&v9fs_fs_type);
727 }
728
729 module_init(init_v9fs)
730 module_exit(exit_v9fs)
731
732 MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
733 MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
734 MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
735 MODULE_LICENSE("GPL");