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