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