inode.c 11.7 KB
Newer Older
1 2 3 4 5 6
/*
 *  linux/fs/nfs/inode.c
 *
 *  Copyright (C) 1992  Rick Sladkey
 *
 *  nfs inode and superblock handling functions
Linus Torvalds's avatar
Linus Torvalds committed
7 8 9
 *
 *  Modularised by Alan Cox <Alan.Cox@linux.org>, while hacking some
 *  experimental NFS changes. Modularisation taken straight from SYS5 fs.
Linus Torvalds's avatar
Linus Torvalds committed
10 11 12 13
 *
 *  Change to nfs_read_super() to permit NFS mounts to multi-homed hosts.
 *  J.S.Peatfield@damtp.cam.ac.uk
 *
14 15
 */

Linus Torvalds's avatar
Linus Torvalds committed
16
#include <linux/config.h>
Linus Torvalds's avatar
Linus Torvalds committed
17
#include <linux/module.h>
Linus Torvalds's avatar
Linus Torvalds committed
18

19 20 21 22 23 24 25
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/locks.h>
Linus Torvalds's avatar
Linus Torvalds committed
26 27
#include <linux/unistd.h>
#include <linux/sunrpc/clnt.h>
Linus Torvalds's avatar
Linus Torvalds committed
28
#include <linux/sunrpc/stats.h>
Linus Torvalds's avatar
Linus Torvalds committed
29 30
#include <linux/nfs_fs.h>
#include <linux/lockd/bind.h>
31

Linus Torvalds's avatar
Linus Torvalds committed
32
#include <asm/system.h>
Linus Torvalds's avatar
Linus Torvalds committed
33
#include <asm/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed
34

Linus Torvalds's avatar
Linus Torvalds committed
35
#define NFSDBG_FACILITY		NFSDBG_VFS
36

Linus Torvalds's avatar
Linus Torvalds committed
37
static int nfs_notify_change(struct inode *, struct iattr *);
38
static void nfs_put_inode(struct inode *);
39
static void nfs_put_super(struct super_block *);
Linus Torvalds's avatar
Linus Torvalds committed
40
static void nfs_read_inode(struct inode *);
Linus Torvalds's avatar
Linus Torvalds committed
41
static void nfs_statfs(struct super_block *, struct statfs *, int bufsiz);
42

Linus Torvalds's avatar
Linus Torvalds committed
43
static struct super_operations nfs_sops = { 
Linus Torvalds's avatar
Linus Torvalds committed
44
	nfs_read_inode,		/* read inode */
45 46
	nfs_notify_change,	/* notify change */
	NULL,			/* write inode */
47
	nfs_put_inode,		/* put inode */
48 49
	nfs_put_super,		/* put superblock */
	NULL,			/* write superblock */
50 51
	nfs_statfs,		/* stat filesystem */
	NULL
52 53
};

Linus Torvalds's avatar
Linus Torvalds committed
54
struct rpc_stat			nfs_rpcstat = { &nfs_program };
Linus Torvalds's avatar
Linus Torvalds committed
55

Linus Torvalds's avatar
Linus Torvalds committed
56 57 58
/*
 * The "read_inode" function doesn't actually do anything:
 * the real data is filled in later in nfs_fhget. Here we
Linus Torvalds's avatar
Linus Torvalds committed
59
 * just mark the cache times invalid, and zero out i_mode
Linus Torvalds's avatar
Linus Torvalds committed
60 61
 * (the latter makes "nfs_refresh_inode" do the right thing
 * wrt pipe inodes)
Linus Torvalds's avatar
Linus Torvalds committed
62
 */
Linus Torvalds's avatar
Linus Torvalds committed
63 64
static void
nfs_read_inode(struct inode * inode)
Linus Torvalds's avatar
Linus Torvalds committed
65
{
Linus Torvalds's avatar
Linus Torvalds committed
66
	inode->i_blksize = inode->i_sb->s_blocksize;
Linus Torvalds's avatar
Linus Torvalds committed
67
	inode->i_mode = 0;
Linus Torvalds's avatar
Linus Torvalds committed
68
	inode->i_op = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
69
	NFS_CACHEINV(inode);
Linus Torvalds's avatar
Linus Torvalds committed
70 71
}

Linus Torvalds's avatar
Linus Torvalds committed
72 73
static void
nfs_put_inode(struct inode * inode)
74
{
Linus Torvalds's avatar
Linus Torvalds committed
75 76
	dprintk("NFS: put_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);

Linus Torvalds's avatar
Linus Torvalds committed
77 78
	if (inode->i_pipe)
		clear_inode(inode);
79 80
}

Linus Torvalds's avatar
Linus Torvalds committed
81 82
void
nfs_put_super(struct super_block *sb)
83
{
Linus Torvalds's avatar
Linus Torvalds committed
84
	struct nfs_server *server = &sb->u.nfs_sb.s_server;
Linus Torvalds's avatar
Linus Torvalds committed
85 86
	struct rpc_clnt	*rpc;

Linus Torvalds's avatar
Linus Torvalds committed
87
	if ((rpc = server->client) != NULL)
Linus Torvalds's avatar
Linus Torvalds committed
88 89
		rpc_shutdown_client(rpc);

Linus Torvalds's avatar
Linus Torvalds committed
90 91
	if (!(server->flags & NFS_MOUNT_NONLM))
		lockd_down();	/* release rpc.lockd */
Linus Torvalds's avatar
Linus Torvalds committed
92
	rpciod_down();		/* release rpciod */
93 94 95
	lock_super(sb);
	sb->s_dev = 0;
	unlock_super(sb);
Linus Torvalds's avatar
Linus Torvalds committed
96
	MOD_DEC_USE_COUNT;
97 98 99
}

/*
Linus Torvalds's avatar
Linus Torvalds committed
100
 * Compute and set NFS server blocksize
101
 */
Linus Torvalds's avatar
Linus Torvalds committed
102 103
static unsigned int
nfs_block_size(unsigned int bsize, unsigned char *nrbitsp)
104
{
Linus Torvalds's avatar
Linus Torvalds committed
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
	if (bsize < 1024)
		bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
	else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
		bsize = NFS_MAX_FILE_IO_BUFFER_SIZE;

	/* make sure blocksize is a power of two */
	if ((bsize & (bsize - 1)) || nrbitsp) {
		unsigned int	nrbits;

		for (nrbits = 31; nrbits && !(bsize & (1 << nrbits)); nrbits--)
			;
		bsize = 1 << nrbits;
		if (nrbitsp)
			*nrbitsp = nrbits;
		if (bsize < NFS_DEF_FILE_IO_BUFFER_SIZE)
			bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
	}
Linus Torvalds's avatar
Linus Torvalds committed
122

Linus Torvalds's avatar
Linus Torvalds committed
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
	return bsize;
}

/*
 * The way this works is that the mount process passes a structure
 * in the data argument which contains the server's IP address
 * and the root file handle obtained from the server's mount
 * daemon. We stash these away in the private superblock fields.
 */
struct super_block *
nfs_read_super(struct super_block *sb, void *raw_data, int silent)
{
	struct nfs_mount_data	*data = (struct nfs_mount_data *) raw_data;
	struct sockaddr_in	srvaddr;
	struct nfs_server	*server;
	struct rpc_timeout	timeparms;
	struct rpc_xprt		*xprt;
	struct rpc_clnt		*clnt;
	unsigned int		authflavor;
	int			tcp;
	kdev_t			dev = sb->s_dev;
144

Linus Torvalds's avatar
Linus Torvalds committed
145
	MOD_INC_USE_COUNT;
146 147 148
	if (!data) {
		printk("nfs_read_super: missing data argument\n");
		sb->s_dev = 0;
Linus Torvalds's avatar
Linus Torvalds committed
149
		MOD_DEC_USE_COUNT;
150 151 152 153 154
		return NULL;
	}
	if (data->version != NFS_MOUNT_VERSION) {
		printk("nfs warning: mount version %s than kernel\n",
			data->version < NFS_MOUNT_VERSION ? "older" : "newer");
Linus Torvalds's avatar
Linus Torvalds committed
155 156 157 158
		if (data->version < 2)
			data->namlen = 0;
		if (data->version < 3)
			data->bsize  = 0;
159
	}
Linus Torvalds's avatar
Linus Torvalds committed
160

161
	lock_super(sb);
Linus Torvalds's avatar
Linus Torvalds committed
162

Linus Torvalds's avatar
Linus Torvalds committed
163 164 165 166 167 168 169 170
	server           = &sb->u.nfs_sb.s_server;
	sb->s_magic      = NFS_SUPER_MAGIC;
	sb->s_dev        = dev;
	sb->s_op         = &nfs_sops;
	sb->s_blocksize  = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
	server->rsize    = nfs_block_size(data->rsize, NULL);
	server->wsize    = nfs_block_size(data->wsize, NULL);
	server->flags    = data->flags;
171 172 173 174 175
	server->acregmin = data->acregmin*HZ;
	server->acregmax = data->acregmax*HZ;
	server->acdirmin = data->acdirmin*HZ;
	server->acdirmax = data->acdirmax*HZ;
	strcpy(server->hostname, data->hostname);
Linus Torvalds's avatar
Linus Torvalds committed
176
	sb->u.nfs_sb.s_root = data->root;
Linus Torvalds's avatar
Linus Torvalds committed
177

Linus Torvalds's avatar
Linus Torvalds committed
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
	/* We now require that the mount process passes the remote address */
	memcpy(&srvaddr, &data->addr, sizeof(srvaddr));
	if (srvaddr.sin_addr.s_addr == INADDR_ANY) {
		printk("NFS: mount program didn't pass remote address!\n");
		MOD_DEC_USE_COUNT;
		return NULL;
	}

	/* Which protocol do we use? */
	tcp   = (data->flags & NFS_MOUNT_TCP);

	/* Initialize timeout values */
	timeparms.to_initval = data->timeo * HZ / 10;
	timeparms.to_retries = data->retrans;
	timeparms.to_maxval  = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT;
	timeparms.to_exponential = 1;

	/* Choose authentication flavor */
	if (data->flags & NFS_MOUNT_SECURE) {
		authflavor = RPC_AUTH_DES;
	} else if (data->flags & NFS_MOUNT_KERBEROS) {
		authflavor = RPC_AUTH_KRB;
Linus Torvalds's avatar
Linus Torvalds committed
200
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
201
		authflavor = RPC_AUTH_UNIX;
Linus Torvalds's avatar
Linus Torvalds committed
202 203
	}

Linus Torvalds's avatar
Linus Torvalds committed
204 205 206 207 208 209
	/* Now create transport and client */
	xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP,
						&srvaddr, &timeparms);
	if (xprt == NULL) {
		printk("NFS: cannot create RPC transport.\n");
		goto failure;
Linus Torvalds's avatar
Linus Torvalds committed
210 211
	}

Linus Torvalds's avatar
Linus Torvalds committed
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
	clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
						NFS_VERSION, authflavor);
	if (clnt == NULL) {
		printk("NFS: cannot create RPC client.\n");
		xprt_destroy(xprt);
		goto failure;
	}

	clnt->cl_intr     = (data->flags & NFS_MOUNT_INTR)? 1 : 0;
	clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0;
	clnt->cl_chatty   = 1;
	server->client    = clnt;

	/* Fire up rpciod if not yet running */
	rpciod_up();

	/* Unlock super block and try to get root fh attributes */
229
	unlock_super(sb);
Linus Torvalds's avatar
Linus Torvalds committed
230

Linus Torvalds's avatar
Linus Torvalds committed
231 232
	sb->s_root = d_alloc_root(nfs_fhget(sb, &data->root, NULL), NULL);
	if (sb->s_root != NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
233
		/* We're airborne */
Linus Torvalds's avatar
Linus Torvalds committed
234 235
		if (!(server->flags & NFS_MOUNT_NONLM))
			lockd_up();
Linus Torvalds's avatar
Linus Torvalds committed
236
		return sb;
237
	}
Linus Torvalds's avatar
Linus Torvalds committed
238 239 240 241 242 243 244 245 246 247 248 249

	/* Yargs. It didn't work out. */
	printk("nfs_read_super: get root inode failed\n");
	rpc_shutdown_client(server->client);
	rpciod_down();

failure:
	MOD_DEC_USE_COUNT;
	if (sb->s_lock)
		unlock_super(sb);
	sb->s_dev = 0;
	return NULL;
250 251
}

Linus Torvalds's avatar
Linus Torvalds committed
252 253
static void
nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
254 255 256
{
	int error;
	struct nfs_fsinfo res;
Linus Torvalds's avatar
Linus Torvalds committed
257
	struct statfs tmp;
258 259 260 261

	error = nfs_proc_statfs(&sb->u.nfs_sb.s_server, &sb->u.nfs_sb.s_root,
		&res);
	if (error) {
262
		printk("nfs_statfs: statfs error = %d\n", -error);
263 264
		res.bsize = res.blocks = res.bfree = res.bavail = 0;
	}
Linus Torvalds's avatar
Linus Torvalds committed
265 266 267 268 269 270 271 272
	tmp.f_type = NFS_SUPER_MAGIC;
	tmp.f_bsize = res.bsize;
	tmp.f_blocks = res.blocks;
	tmp.f_bfree = res.bfree;
	tmp.f_bavail = res.bavail;
	tmp.f_files = 0;
	tmp.f_ffree = 0;
	tmp.f_namelen = NAME_MAX;
Linus Torvalds's avatar
Linus Torvalds committed
273
	copy_to_user(buf, &tmp, bufsiz);
274 275 276 277 278 279 280 281 282 283
}

/*
 * This is our own version of iget that looks up inodes by file handle
 * instead of inode number.  We use this technique instead of using
 * the vfs read_inode function because there is no way to pass the
 * file handle or current attributes into the read_inode function.
 * We just have to be careful not to subvert iget's special handling
 * of mount points.
 */
Linus Torvalds's avatar
Linus Torvalds committed
284 285 286
struct inode *
nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
				  struct nfs_fattr *fattr)
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
{
	struct nfs_fattr newfattr;
	int error;
	struct inode *inode;

	if (!sb) {
		printk("nfs_fhget: super block is NULL\n");
		return NULL;
	}
	if (!fattr) {
		error = nfs_proc_getattr(&sb->u.nfs_sb.s_server, fhandle,
			&newfattr);
		if (error) {
			printk("nfs_fhget: getattr error = %d\n", -error);
			return NULL;
		}
		fattr = &newfattr;
	}
	if (!(inode = iget(sb, fattr->fileid))) {
		printk("nfs_fhget: iget failed\n");
		return NULL;
	}
	if (inode->i_dev == sb->s_dev) {
		if (inode->i_ino != fattr->fileid) {
			printk("nfs_fhget: unexpected inode from iget\n");
			return inode;
		}
		*NFS_FH(inode) = *fhandle;
		nfs_refresh_inode(inode, fattr);
	}
Linus Torvalds's avatar
Linus Torvalds committed
317
	dprintk("NFS: fhget(%x/%ld ct=%d)\n",
Linus Torvalds's avatar
Linus Torvalds committed
318 319
		inode->i_dev, inode->i_ino,
		atomic_read(&inode->i_count));
Linus Torvalds's avatar
Linus Torvalds committed
320

321 322 323
	return inode;
}

Linus Torvalds's avatar
Linus Torvalds committed
324 325
int
nfs_notify_change(struct inode *inode, struct iattr *attr)
326 327 328 329 330
{
	struct nfs_sattr sattr;
	struct nfs_fattr fattr;
	int error;

Linus Torvalds's avatar
Linus Torvalds committed
331 332
	sattr.mode = (u32) -1;
	if (attr->ia_valid & ATTR_MODE) 
Linus Torvalds's avatar
Linus Torvalds committed
333 334
		sattr.mode = attr->ia_mode;

Linus Torvalds's avatar
Linus Torvalds committed
335
	sattr.uid = (u32) -1;
Linus Torvalds's avatar
Linus Torvalds committed
336 337 338
	if (attr->ia_valid & ATTR_UID)
		sattr.uid = attr->ia_uid;

Linus Torvalds's avatar
Linus Torvalds committed
339
	sattr.gid = (u32) -1;
Linus Torvalds's avatar
Linus Torvalds committed
340 341 342
	if (attr->ia_valid & ATTR_GID)
		sattr.gid = attr->ia_gid;

Linus Torvalds's avatar
Linus Torvalds committed
343

Linus Torvalds's avatar
Linus Torvalds committed
344 345 346
	sattr.size = (u32) -1;
	if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode))
		sattr.size = attr->ia_size;
Linus Torvalds's avatar
Linus Torvalds committed
347

Linus Torvalds's avatar
Linus Torvalds committed
348
	sattr.mtime.seconds = sattr.mtime.useconds = (u32) -1;
Linus Torvalds's avatar
Linus Torvalds committed
349
	if (attr->ia_valid & ATTR_MTIME) {
Linus Torvalds's avatar
Linus Torvalds committed
350
		sattr.mtime.seconds = attr->ia_mtime;
351
		sattr.mtime.useconds = 0;
Linus Torvalds's avatar
Linus Torvalds committed
352
	}
Linus Torvalds's avatar
Linus Torvalds committed
353

Linus Torvalds's avatar
Linus Torvalds committed
354
	sattr.atime.seconds = sattr.atime.useconds = (u32) -1;
Linus Torvalds's avatar
Linus Torvalds committed
355
	if (attr->ia_valid & ATTR_ATIME) {
Linus Torvalds's avatar
Linus Torvalds committed
356 357
		sattr.atime.seconds = attr->ia_atime;
		sattr.atime.useconds = 0;
Linus Torvalds's avatar
Linus Torvalds committed
358
	}
Linus Torvalds's avatar
Linus Torvalds committed
359

360 361
	error = nfs_proc_setattr(NFS_SERVER(inode), NFS_FH(inode),
		&sattr, &fattr);
Linus Torvalds's avatar
Linus Torvalds committed
362 363
	if (!error) {
		nfs_truncate_dirty_pages(inode, sattr.size);
364
		nfs_refresh_inode(inode, &fattr);
Linus Torvalds's avatar
Linus Torvalds committed
365
	}
366 367
	return error;
}
Linus Torvalds's avatar
Linus Torvalds committed
368

Linus Torvalds's avatar
Linus Torvalds committed
369 370 371 372 373 374 375 376
/*
 * Externally visible revalidation function
 */
int
nfs_revalidate(struct inode *inode)
{
	return nfs_revalidate_inode(NFS_SERVER(inode), inode);
}
Linus Torvalds's avatar
Linus Torvalds committed
377

Linus Torvalds's avatar
Linus Torvalds committed
378
/*
Linus Torvalds's avatar
Linus Torvalds committed
379 380 381 382 383 384 385 386 387
 * This function is called whenever some part of NFS notices that
 * the cached attributes have to be refreshed.
 *
 * This is a bit tricky because we have to make sure all dirty pages
 * have been sent off to the server before calling invalidate_inode_pages.
 * To make sure no other process adds more write requests while we try
 * our best to flush them, we make them sleep during the attribute refresh.
 *
 * A very similar scenario holds for the dir cache.
Linus Torvalds's avatar
Linus Torvalds committed
388
 */
Linus Torvalds's avatar
Linus Torvalds committed
389 390
int
_nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
Linus Torvalds's avatar
Linus Torvalds committed
391
{
Linus Torvalds's avatar
Linus Torvalds committed
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
	struct nfs_fattr fattr;
	int		 status;

	if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
		return 0;

	dfprintk(PAGECACHE, "NFS: revalidating %x/%ld inode\n",
			inode->i_dev, inode->i_ino);
	NFS_READTIME(inode) = jiffies;
	if ((status = nfs_proc_getattr(server, NFS_FH(inode), &fattr)) < 0)
		goto done;

	nfs_refresh_inode(inode, &fattr);
	if (fattr.mtime.seconds != NFS_OLDMTIME(inode)) {
		if (!S_ISDIR(inode->i_mode)) {
			/* This sends off all dirty pages off to the server.
			 * Note that this function must not sleep. */
			nfs_invalidate_pages(inode);
			invalidate_inode_pages(inode);
		} else {
			nfs_invalidate_dircache(inode);
		}
Linus Torvalds's avatar
Linus Torvalds committed
414

Linus Torvalds's avatar
Linus Torvalds committed
415 416 417 418 419 420 421 422 423 424 425 426 427 428
		NFS_OLDMTIME(inode)  = fattr.mtime.seconds;
		NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
	} else {
		/* Update attrtimeo value */
		if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
			NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
	}
	status = 0;

done:
	dfprintk(PAGECACHE,
		"NFS: inode %x/%ld revalidation complete (status %d).\n",
				inode->i_dev, inode->i_ino, status);
	return status;
Linus Torvalds's avatar
Linus Torvalds committed
429 430
}

Linus Torvalds's avatar
Linus Torvalds committed
431 432 433 434
/*
 * File system information
 */
static struct file_system_type nfs_fs_type = {
Linus Torvalds's avatar
Linus Torvalds committed
435
	"nfs",
Linus Torvalds's avatar
Linus Torvalds committed
436
	0 /* FS_NO_DCACHE - this doesn't work right now*/,
Linus Torvalds's avatar
Linus Torvalds committed
437 438
	nfs_read_super,
	NULL
Linus Torvalds's avatar
Linus Torvalds committed
439 440 441 442 443 444 445
};

/*
 * Initialize NFS
 */
int
init_nfs_fs(void)
Linus Torvalds's avatar
Linus Torvalds committed
446
{
Linus Torvalds's avatar
Linus Torvalds committed
447
#ifdef CONFIG_PROC_FS
Linus Torvalds's avatar
Linus Torvalds committed
448
	rpc_proc_register(&nfs_rpcstat);
Linus Torvalds's avatar
Linus Torvalds committed
449
#endif
Linus Torvalds's avatar
Linus Torvalds committed
450 451 452
        return register_filesystem(&nfs_fs_type);
}

Linus Torvalds's avatar
Linus Torvalds committed
453 454 455
/*
 * Every kernel module contains stuff like this.
 */
Linus Torvalds's avatar
Linus Torvalds committed
456
#ifdef MODULE
Linus Torvalds's avatar
Linus Torvalds committed
457

Linus Torvalds's avatar
Linus Torvalds committed
458
EXPORT_NO_SYMBOLS;
Linus Torvalds's avatar
Linus Torvalds committed
459 460
/* Not quite true; I just maintain it */
MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
Linus Torvalds's avatar
Linus Torvalds committed
461

Linus Torvalds's avatar
Linus Torvalds committed
462 463
int
init_module(void)
Linus Torvalds's avatar
Linus Torvalds committed
464
{
Linus Torvalds's avatar
Linus Torvalds committed
465
	return init_nfs_fs();
Linus Torvalds's avatar
Linus Torvalds committed
466 467
}

Linus Torvalds's avatar
Linus Torvalds committed
468 469
void
cleanup_module(void)
Linus Torvalds's avatar
Linus Torvalds committed
470
{
Linus Torvalds's avatar
Linus Torvalds committed
471
#ifdef CONFIG_PROC_FS
Linus Torvalds's avatar
Linus Torvalds committed
472
	rpc_proc_unregister("nfs");
Linus Torvalds's avatar
Linus Torvalds committed
473
#endif
Linus Torvalds's avatar
Linus Torvalds committed
474
	unregister_filesystem(&nfs_fs_type);
Linus Torvalds's avatar
Linus Torvalds committed
475
	nfs_free_dircache();
Linus Torvalds's avatar
Linus Torvalds committed
476 477
}
#endif