client.c 33.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* client.c: NFS client sharing and management code
 *
 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */


#include <linux/module.h>
#include <linux/init.h>
A
Alexey Dobriyan 已提交
15
#include <linux/sched.h>
16 17 18 19 20 21 22
#include <linux/time.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/unistd.h>
23
#include <linux/sunrpc/addr.h>
24 25 26
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/metrics.h>
27
#include <linux/sunrpc/xprtsock.h>
\
\"Talpey, Thomas\ 已提交
28
#include <linux/sunrpc/xprtrdma.h>
29 30 31 32 33 34 35 36
#include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
#include <linux/nfs4_mount.h>
#include <linux/lockd/bind.h>
#include <linux/seq_file.h>
#include <linux/mount.h>
#include <linux/vfs.h>
#include <linux/inet.h>
37
#include <linux/in6.h>
38
#include <linux/slab.h>
A
Al Viro 已提交
39
#include <linux/idr.h>
40
#include <net/ipv6.h>
41
#include <linux/nfs_xdr.h>
A
Andy Adamson 已提交
42
#include <linux/sunrpc/bc_xprt.h>
43 44
#include <linux/nsproxy.h>
#include <linux/pid_namespace.h>
45 46 47 48 49 50 51


#include "nfs4_fs.h"
#include "callback.h"
#include "delegation.h"
#include "iostat.h"
#include "internal.h"
52
#include "fscache.h"
R
Ricardo Labiaga 已提交
53
#include "pnfs.h"
54
#include "nfs.h"
55
#include "netns.h"
56 57 58 59

#define NFSDBG_FACILITY		NFSDBG_CLIENT

static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
60 61 62
static DEFINE_SPINLOCK(nfs_version_lock);
static DEFINE_MUTEX(nfs_version_mutex);
static LIST_HEAD(nfs_versions);
63

64 65 66
/*
 * RPC cruft for NFS
 */
67
static const struct rpc_version *nfs_version[5] = {
68 69 70
	[2] = NULL,
	[3] = NULL,
	[4] = NULL,
71 72
};

73
const struct rpc_program nfs_program = {
74 75 76 77 78
	.name			= "nfs",
	.number			= NFS_PROGRAM,
	.nrvers			= ARRAY_SIZE(nfs_version),
	.version		= nfs_version,
	.stats			= &nfs_rpcstat,
J
Jim Rees 已提交
79
	.pipe_dir_name		= NFS_PIPE_DIRNAME,
80 81 82 83 84 85
};

struct rpc_stat nfs_rpcstat = {
	.program		= &nfs_program
};

86 87 88 89 90 91 92 93 94 95
static struct nfs_subversion *find_nfs_version(unsigned int version)
{
	struct nfs_subversion *nfs;
	spin_lock(&nfs_version_lock);

	list_for_each_entry(nfs, &nfs_versions, list) {
		if (nfs->rpc_ops->version == version) {
			spin_unlock(&nfs_version_lock);
			return nfs;
		}
96
	}
97 98

	spin_unlock(&nfs_version_lock);
99
	return ERR_PTR(-EPROTONOSUPPORT);
100 101 102 103 104 105 106 107
}

struct nfs_subversion *get_nfs_version(unsigned int version)
{
	struct nfs_subversion *nfs = find_nfs_version(version);

	if (IS_ERR(nfs)) {
		mutex_lock(&nfs_version_mutex);
108
		request_module("nfsv%d", version);
109 110 111 112
		nfs = find_nfs_version(version);
		mutex_unlock(&nfs_version_mutex);
	}

113 114
	if (!IS_ERR(nfs) && !try_module_get(nfs->owner))
		return ERR_PTR(-EAGAIN);
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
	return nfs;
}

void put_nfs_version(struct nfs_subversion *nfs)
{
	module_put(nfs->owner);
}

void register_nfs_version(struct nfs_subversion *nfs)
{
	spin_lock(&nfs_version_lock);

	list_add(&nfs->list, &nfs_versions);
	nfs_version[nfs->rpc_ops->version] = nfs->rpc_vers;

	spin_unlock(&nfs_version_lock);
}
EXPORT_SYMBOL_GPL(register_nfs_version);

void unregister_nfs_version(struct nfs_subversion *nfs)
{
	spin_lock(&nfs_version_lock);

	nfs_version[nfs->rpc_ops->version] = NULL;
	list_del(&nfs->list);

	spin_unlock(&nfs_version_lock);
}
EXPORT_SYMBOL_GPL(unregister_nfs_version);

145 146 147 148 149 150
/*
 * Allocate a shared client record
 *
 * Since these are allocated/deallocated very rarely, we don't
 * bother putting them in a slab cache...
 */
151
struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
152 153
{
	struct nfs_client *clp;
154
	struct rpc_cred *cred;
155
	int err = -ENOMEM;
156 157 158 159

	if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
		goto error_0;

160
	clp->cl_nfs_mod = cl_init->nfs_mod;
161 162
	if (!try_module_get(clp->cl_nfs_mod->owner))
		goto error_dealloc;
163 164

	clp->rpc_ops = clp->cl_nfs_mod->rpc_ops;
165

166 167 168
	atomic_set(&clp->cl_count, 1);
	clp->cl_cons_state = NFS_CS_INITING;

169 170
	memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen);
	clp->cl_addrlen = cl_init->addrlen;
171

172
	if (cl_init->hostname) {
173
		err = -ENOMEM;
174
		clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL);
175
		if (!clp->cl_hostname)
176
			goto error_cleanup;
177 178 179 180 181
	}

	INIT_LIST_HEAD(&clp->cl_superblocks);
	clp->cl_rpcclient = ERR_PTR(-EINVAL);

182
	clp->cl_proto = cl_init->proto;
183
	clp->cl_net = get_net(cl_init->net);
184

185
	cred = rpc_lookup_machine_cred("*");
186 187
	if (!IS_ERR(cred))
		clp->cl_machine_cred = cred;
188 189
	nfs_fscache_get_client_cookie(clp);

190 191
	return clp;

192
error_cleanup:
193
	put_nfs_version(clp->cl_nfs_mod);
194
error_dealloc:
195 196
	kfree(clp);
error_0:
197
	return ERR_PTR(err);
198
}
B
Bryan Schumaker 已提交
199
EXPORT_SYMBOL_GPL(nfs_alloc_client);
200

B
Bryan Schumaker 已提交
201
#if IS_ENABLED(CONFIG_NFS_V4)
202
void nfs_cleanup_cb_ident_idr(struct net *net)
A
Andy Adamson 已提交
203
{
204 205 206
	struct nfs_net *nn = net_generic(net, nfs_net_id);

	idr_destroy(&nn->cb_ident_idr);
A
Andy Adamson 已提交
207 208 209 210 211
}

/* nfs_client_lock held */
static void nfs_cb_idr_remove_locked(struct nfs_client *clp)
{
212
	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
213

A
Andy Adamson 已提交
214
	if (clp->cl_cb_ident)
215
		idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident);
A
Andy Adamson 已提交
216 217
}

F
Fred Isaman 已提交
218 219 220
static void pnfs_init_server(struct nfs_server *server)
{
	rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC");
221
	rpc_init_wait_queue(&server->uoc_rpcwaitq, "NFS UOC");
F
Fred Isaman 已提交
222 223
}

224
#else
225
void nfs_cleanup_cb_ident_idr(struct net *net)
A
Andy Adamson 已提交
226 227 228 229 230 231
{
}

static void nfs_cb_idr_remove_locked(struct nfs_client *clp)
{
}
F
Fred Isaman 已提交
232 233 234 235 236

static void pnfs_init_server(struct nfs_server *server)
{
}

237
#endif /* CONFIG_NFS_V4 */
238

239 240 241
/*
 * Destroy a shared client record
 */
242
void nfs_free_client(struct nfs_client *clp)
243
{
244 245
	nfs_fscache_release_client_cookie(clp);

246 247 248 249
	/* -EIO all pending I/O */
	if (!IS_ERR(clp->cl_rpcclient))
		rpc_shutdown_client(clp->cl_rpcclient);

250 251 252
	if (clp->cl_machine_cred != NULL)
		put_rpccred(clp->cl_machine_cred);

253
	put_net(clp->cl_net);
254
	put_nfs_version(clp->cl_nfs_mod);
255
	kfree(clp->cl_hostname);
256
	kfree(clp->cl_acceptor);
257 258
	kfree(clp);
}
B
Bryan Schumaker 已提交
259
EXPORT_SYMBOL_GPL(nfs_free_client);
260 261 262 263 264 265

/*
 * Release a reference to a shared client record
 */
void nfs_put_client(struct nfs_client *clp)
{
266 267
	struct nfs_net *nn;

D
David Howells 已提交
268 269 270
	if (!clp)
		return;

271
	nn = net_generic(clp->cl_net, nfs_net_id);
272

273
	if (atomic_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) {
274
		list_del(&clp->cl_share_link);
A
Andy Adamson 已提交
275
		nfs_cb_idr_remove_locked(clp);
276
		spin_unlock(&nn->nfs_client_lock);
277

278
		WARN_ON_ONCE(!list_empty(&clp->cl_superblocks));
279

280
		clp->rpc_ops->free_client(clp);
281 282
	}
}
283
EXPORT_SYMBOL_GPL(nfs_put_client);
284 285

/*
286 287
 * Find an nfs_client on the list that matches the initialisation data
 * that is supplied.
288
 */
289
static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data)
290 291
{
	struct nfs_client *clp;
I
Ian Dall 已提交
292
	const struct sockaddr *sap = data->addr;
293
	struct nfs_net *nn = net_generic(data->net, nfs_net_id);
294

295
	list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
I
Ian Dall 已提交
296
	        const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
297 298 299 300 301
		/* Don't match clients that failed to initialise properly */
		if (clp->cl_cons_state < 0)
			continue;

		/* Different NFS versions cannot share the same nfs_client */
302
		if (clp->rpc_ops != data->nfs_mod->rpc_ops)
303 304
			continue;

305 306
		if (clp->cl_proto != data->proto)
			continue;
307 308 309
		/* Match nfsv4 minorversion */
		if (clp->cl_minorversion != data->minorversion)
			continue;
310
		/* Match the full socket address */
311
		if (!rpc_cmp_addr_port(sap, clap))
312
			/* Match all xprt_switch full socket addresses */
313 314
			if (IS_ERR(clp->cl_rpcclient) ||
                            !rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient,
315 316
							   sap))
				continue;
317 318 319

		atomic_inc(&clp->cl_count);
		return clp;
320
	}
321
	return NULL;
322 323
}

324 325 326 327 328 329
/*
 * Return true if @clp is done initializing, false if still working on it.
 *
 * Use nfs_client_init_status to check if it was successful.
 */
bool nfs_client_init_is_complete(const struct nfs_client *clp)
330
{
331
	return clp->cl_cons_state <= NFS_CS_READY;
332
}
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
EXPORT_SYMBOL_GPL(nfs_client_init_is_complete);

/*
 * Return 0 if @clp was successfully initialized, -errno otherwise.
 *
 * This must be called *after* nfs_client_init_is_complete() returns true,
 * otherwise it will pop WARN_ON_ONCE and return -EINVAL
 */
int nfs_client_init_status(const struct nfs_client *clp)
{
	/* called without checking nfs_client_init_is_complete */
	if (clp->cl_cons_state > NFS_CS_READY) {
		WARN_ON_ONCE(1);
		return -EINVAL;
	}
	return clp->cl_cons_state;
}
EXPORT_SYMBOL_GPL(nfs_client_init_status);
351 352 353 354 355 356

int nfs_wait_client_init_complete(const struct nfs_client *clp)
{
	return wait_event_killable(nfs_client_active_wq,
			nfs_client_init_is_complete(clp));
}
B
Bryan Schumaker 已提交
357
EXPORT_SYMBOL_GPL(nfs_wait_client_init_complete);
358

359 360 361 362 363 364 365 366 367
/*
 * Found an existing client.  Make sure it's ready before returning.
 */
static struct nfs_client *
nfs_found_client(const struct nfs_client_initdata *cl_init,
		 struct nfs_client *clp)
{
	int error;

368
	error = nfs_wait_client_init_complete(clp);
369 370 371 372 373 374 375 376 377 378 379
	if (error < 0) {
		nfs_put_client(clp);
		return ERR_PTR(-ERESTARTSYS);
	}

	if (clp->cl_cons_state < NFS_CS_READY) {
		error = clp->cl_cons_state;
		nfs_put_client(clp);
		return ERR_PTR(error);
	}

380
	smp_rmb();
381 382 383
	return clp;
}

384 385 386 387
/*
 * Look up a client by IP address and protocol version
 * - creates a new record if one doesn't yet exist
 */
388
struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init)
389 390
{
	struct nfs_client *clp, *new = NULL;
391
	struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id);
392
	const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops;
393

P
Peng Tao 已提交
394 395 396 397 398
	if (cl_init->hostname == NULL) {
		WARN_ON(1);
		return NULL;
	}

399 400
	/* see if the client already exists */
	do {
401
		spin_lock(&nn->nfs_client_lock);
402

403
		clp = nfs_match_client(cl_init);
404 405 406
		if (clp) {
			spin_unlock(&nn->nfs_client_lock);
			if (new)
407
				new->rpc_ops->free_client(new);
408 409
			return nfs_found_client(cl_init, clp);
		}
410
		if (new) {
411 412
			list_add_tail(&new->cl_share_link,
					&nn->nfs_client_list);
413
			spin_unlock(&nn->nfs_client_lock);
C
Chuck Lever 已提交
414
			new->cl_flags = cl_init->init_flags;
415
			return rpc_ops->init_client(new, cl_init);
416
		}
417

418
		spin_unlock(&nn->nfs_client_lock);
419

420
		new = rpc_ops->alloc_client(cl_init);
421
	} while (!IS_ERR(new));
422

423
	return new;
424
}
B
Bryan Schumaker 已提交
425
EXPORT_SYMBOL_GPL(nfs_get_client);
426 427 428 429

/*
 * Mark a server as ready or failed
 */
430
void nfs_mark_client_ready(struct nfs_client *clp, int state)
431
{
432
	smp_wmb();
433 434 435
	clp->cl_cons_state = state;
	wake_up_all(&nfs_client_active_wq);
}
B
Bryan Schumaker 已提交
436
EXPORT_SYMBOL_GPL(nfs_mark_client_ready);
437 438 439 440

/*
 * Initialise the timeout values for a connection
 */
441
void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
442
				    int timeo, int retrans)
443 444 445 446 447
{
	to->to_initval = timeo * HZ / 10;
	to->to_retries = retrans;

	switch (proto) {
448
	case XPRT_TRANSPORT_TCP:
\
\"Talpey, Thomas\ 已提交
449
	case XPRT_TRANSPORT_RDMA:
450
		if (retrans == NFS_UNSPEC_RETRANS)
451
			to->to_retries = NFS_DEF_TCP_RETRANS;
452
		if (timeo == NFS_UNSPEC_TIMEO || to->to_retries == 0)
453
			to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10;
454 455 456 457
		if (to->to_initval > NFS_MAX_TCP_TIMEOUT)
			to->to_initval = NFS_MAX_TCP_TIMEOUT;
		to->to_increment = to->to_initval;
		to->to_maxval = to->to_initval + (to->to_increment * to->to_retries);
458 459 460 461
		if (to->to_maxval > NFS_MAX_TCP_TIMEOUT)
			to->to_maxval = NFS_MAX_TCP_TIMEOUT;
		if (to->to_maxval < to->to_initval)
			to->to_maxval = to->to_initval;
462 463
		to->to_exponential = 0;
		break;
464
	case XPRT_TRANSPORT_UDP:
465
		if (retrans == NFS_UNSPEC_RETRANS)
466
			to->to_retries = NFS_DEF_UDP_RETRANS;
467
		if (timeo == NFS_UNSPEC_TIMEO || to->to_initval == 0)
468
			to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10;
469 470 471 472 473
		if (to->to_initval > NFS_MAX_UDP_TIMEOUT)
			to->to_initval = NFS_MAX_UDP_TIMEOUT;
		to->to_maxval = NFS_MAX_UDP_TIMEOUT;
		to->to_exponential = 1;
		break;
474 475
	default:
		BUG();
476 477
	}
}
B
Bryan Schumaker 已提交
478
EXPORT_SYMBOL_GPL(nfs_init_timeout_values);
479 480 481 482

/*
 * Create an RPC client handle
 */
483
int nfs_create_rpc_client(struct nfs_client *clp,
484
			  const struct nfs_client_initdata *cl_init,
485
			  rpc_authflavor_t flavor)
486 487
{
	struct rpc_clnt		*clnt = NULL;
488
	struct rpc_create_args args = {
489
		.net		= clp->cl_net,
490
		.protocol	= clp->cl_proto,
491
		.address	= (struct sockaddr *)&clp->cl_addr,
492
		.addrsize	= clp->cl_addrlen,
493
		.timeout	= cl_init->timeparms,
494
		.servername	= clp->cl_hostname,
495
		.nodename	= cl_init->nodename,
496 497 498 499
		.program	= &nfs_program,
		.version	= clp->rpc_ops->version,
		.authflavor	= flavor,
	};
500

C
Chuck Lever 已提交
501
	if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags))
502
		args.flags |= RPC_CLNT_CREATE_DISCRTRY;
503 504
	if (test_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags))
		args.flags |= RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT;
C
Chuck Lever 已提交
505
	if (test_bit(NFS_CS_NORESVPORT, &clp->cl_flags))
506
		args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
507 508
	if (test_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags))
		args.flags |= RPC_CLNT_CREATE_INFINITE_SLOTS;
509

510 511 512
	if (!IS_ERR(clp->cl_rpcclient))
		return 0;

513
	clnt = rpc_create(&args);
514 515
	if (IS_ERR(clnt)) {
		dprintk("%s: cannot create RPC client. Error = %ld\n",
516
				__func__, PTR_ERR(clnt));
517 518 519 520 521 522
		return PTR_ERR(clnt);
	}

	clp->cl_rpcclient = clnt;
	return 0;
}
B
Bryan Schumaker 已提交
523
EXPORT_SYMBOL_GPL(nfs_create_rpc_client);
524 525 526 527 528 529

/*
 * Version 2 or 3 client destruction
 */
static void nfs_destroy_server(struct nfs_server *server)
{
530
	if (server->nlm_host)
531
		nlmclnt_done(server->nlm_host);
532 533 534 535 536 537 538
}

/*
 * Version 2 or 3 lockd setup
 */
static int nfs_start_lockd(struct nfs_server *server)
{
539 540
	struct nlm_host *host;
	struct nfs_client *clp = server->nfs_client;
541 542 543 544 545
	struct nlmclnt_initdata nlm_init = {
		.hostname	= clp->cl_hostname,
		.address	= (struct sockaddr *)&clp->cl_addr,
		.addrlen	= clp->cl_addrlen,
		.nfs_version	= clp->rpc_ops->version,
546 547
		.noresvport	= server->flags & NFS_MOUNT_NORESVPORT ?
					1 : 0,
548
		.net		= clp->cl_net,
549
		.nlmclnt_ops 	= clp->cl_nfs_mod->rpc_ops->nlmclnt_ops,
550
	};
551

552
	if (nlm_init.nfs_version > 3)
553
		return 0;
554 555
	if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) &&
			(server->flags & NFS_MOUNT_LOCAL_FCNTL))
556 557
		return 0;

558 559 560 561 562 563 564 565
	switch (clp->cl_proto) {
		default:
			nlm_init.protocol = IPPROTO_TCP;
			break;
		case XPRT_TRANSPORT_UDP:
			nlm_init.protocol = IPPROTO_UDP;
	}

566
	host = nlmclnt_init(&nlm_init);
567 568 569 570 571 572
	if (IS_ERR(host))
		return PTR_ERR(host);

	server->nlm_host = host;
	server->destroy = nfs_destroy_server;
	return 0;
573 574 575 576 577
}

/*
 * Create a general RPC client
 */
578
int nfs_init_server_rpcclient(struct nfs_server *server,
579 580
		const struct rpc_timeout *timeo,
		rpc_authflavor_t pseudoflavour)
581 582 583
{
	struct nfs_client *clp = server->nfs_client;

584 585
	server->client = rpc_clone_client_set_auth(clp->cl_rpcclient,
							pseudoflavour);
586
	if (IS_ERR(server->client)) {
587
		dprintk("%s: couldn't create rpc_client!\n", __func__);
588 589 590
		return PTR_ERR(server->client);
	}

591 592 593 594
	memcpy(&server->client->cl_timeout_default,
			timeo,
			sizeof(server->client->cl_timeout_default));
	server->client->cl_timeout = &server->client->cl_timeout_default;
595 596 597 598 599 600
	server->client->cl_softrtry = 0;
	if (server->flags & NFS_MOUNT_SOFT)
		server->client->cl_softrtry = 1;

	return 0;
}
B
Bryan Schumaker 已提交
601
EXPORT_SYMBOL_GPL(nfs_init_server_rpcclient);
602

603 604 605 606
/**
 * nfs_init_client - Initialise an NFS2 or NFS3 client
 *
 * @clp: nfs_client to initialise
607
 * @cl_init: Initialisation parameters
608 609
 *
 * Returns pointer to an NFS client, or an ERR_PTR value.
610
 */
611
struct nfs_client *nfs_init_client(struct nfs_client *clp,
612
				   const struct nfs_client_initdata *cl_init)
613 614 615
{
	int error;

A
Anna Schumaker 已提交
616 617
	/* the client is already initialised */
	if (clp->cl_cons_state == NFS_CS_READY)
618
		return clp;
619 620 621 622 623

	/*
	 * Create a client RPC handle for doing FSSTAT with UNIX auth only
	 * - RFC 2623, sec 2.3.2
	 */
624
	error = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_UNIX);
A
Anna Schumaker 已提交
625 626 627 628 629
	nfs_mark_client_ready(clp, error == 0 ? NFS_CS_READY : error);
	if (error < 0) {
		nfs_put_client(clp);
		clp = ERR_PTR(error);
	}
630
	return clp;
631
}
B
Bryan Schumaker 已提交
632
EXPORT_SYMBOL_GPL(nfs_init_client);
633 634 635 636

/*
 * Create a version 2 or 3 client
 */
637
static int nfs_init_server(struct nfs_server *server,
638 639
			   const struct nfs_parsed_mount_data *data,
			   struct nfs_subversion *nfs_mod)
640
{
641
	struct rpc_timeout timeparms;
642 643
	struct nfs_client_initdata cl_init = {
		.hostname = data->nfs_server.hostname,
644
		.addr = (const struct sockaddr *)&data->nfs_server.address,
645
		.addrlen = data->nfs_server.addrlen,
646
		.nfs_mod = nfs_mod,
647
		.proto = data->nfs_server.protocol,
648
		.net = data->net,
649
		.timeparms = &timeparms,
650
	};
651
	struct nfs_client *clp;
652
	int error;
653

654 655
	nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
			data->timeo, data->retrans);
C
Chuck Lever 已提交
656 657
	if (data->flags & NFS_MOUNT_NORESVPORT)
		set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
658

659
	/* Allocate or find a client reference we can use */
660
	clp = nfs_get_client(&cl_init);
661
	if (IS_ERR(clp))
662 663 664 665 666
		return PTR_ERR(clp);

	server->nfs_client = clp;

	/* Initialise the client representation from the mount data */
667
	server->flags = data->flags;
668
	server->options = data->options;
669 670
	server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
		NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP|
671
		NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME;
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687

	if (data->rsize)
		server->rsize = nfs_block_size(data->rsize, NULL);
	if (data->wsize)
		server->wsize = nfs_block_size(data->wsize, NULL);

	server->acregmin = data->acregmin * HZ;
	server->acregmax = data->acregmax * HZ;
	server->acdirmin = data->acdirmin * HZ;
	server->acdirmax = data->acdirmax * HZ;

	/* Start lockd here, before we might error out */
	error = nfs_start_lockd(server);
	if (error < 0)
		goto error;

688
	server->port = data->nfs_server.port;
689
	server->auth_info = data->auth_info;
690

691 692
	error = nfs_init_server_rpcclient(server, &timeparms,
					  data->selected_flavor);
693 694 695
	if (error < 0)
		goto error;

696 697 698 699 700 701 702 703 704 705
	/* Preserve the values of mount_server-related mount options */
	if (data->mount_server.addrlen) {
		memcpy(&server->mountd_address, &data->mount_server.address,
			data->mount_server.addrlen);
		server->mountd_addrlen = data->mount_server.addrlen;
	}
	server->mountd_version = data->mount_server.version;
	server->mountd_port = data->mount_server.port;
	server->mountd_protocol = data->mount_server.protocol;

706 707 708 709 710 711 712 713 714 715 716 717
	server->namelen  = data->namlen;
	return 0;

error:
	server->nfs_client = NULL;
	nfs_put_client(clp);
	return error;
}

/*
 * Load up the server record from information gained in an fsinfo record
 */
718 719
static void nfs_server_set_fsinfo(struct nfs_server *server,
				  struct nfs_fsinfo *fsinfo)
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
{
	unsigned long max_rpc_payload;

	/* Work out a lot of parameters */
	if (server->rsize == 0)
		server->rsize = nfs_block_size(fsinfo->rtpref, NULL);
	if (server->wsize == 0)
		server->wsize = nfs_block_size(fsinfo->wtpref, NULL);

	if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax)
		server->rsize = nfs_block_size(fsinfo->rtmax, NULL);
	if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax)
		server->wsize = nfs_block_size(fsinfo->wtmax, NULL);

	max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL);
	if (server->rsize > max_rpc_payload)
		server->rsize = max_rpc_payload;
	if (server->rsize > NFS_MAX_FILE_IO_SIZE)
		server->rsize = NFS_MAX_FILE_IO_SIZE;
739
	server->rpages = (server->rsize + PAGE_SIZE - 1) >> PAGE_SHIFT;
P
Peter Zijlstra 已提交
740

741 742 743 744
	if (server->wsize > max_rpc_payload)
		server->wsize = max_rpc_payload;
	if (server->wsize > NFS_MAX_FILE_IO_SIZE)
		server->wsize = NFS_MAX_FILE_IO_SIZE;
745
	server->wpages = (server->wsize + PAGE_SIZE - 1) >> PAGE_SHIFT;
R
Ricardo Labiaga 已提交
746

747 748 749
	server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL);

	server->dtsize = nfs_block_size(fsinfo->dtpref, NULL);
750 751
	if (server->dtsize > PAGE_SIZE * NFS_MAX_READDIR_PAGES)
		server->dtsize = PAGE_SIZE * NFS_MAX_READDIR_PAGES;
752 753 754 755 756 757 758 759 760 761
	if (server->dtsize > server->rsize)
		server->dtsize = server->rsize;

	if (server->flags & NFS_MOUNT_NOAC) {
		server->acregmin = server->acregmax = 0;
		server->acdirmin = server->acdirmax = 0;
	}

	server->maxfilesize = fsinfo->maxfilesize;

R
Ricardo Labiaga 已提交
762 763
	server->time_delta = fsinfo->time_delta;

764
	server->clone_blksize = fsinfo->clone_blksize;
765 766 767 768 769 770 771
	/* We're airborne Set socket buffersize */
	rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100);
}

/*
 * Probe filesystem information, including the FSID on v2/v3
 */
772
int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr)
773 774 775 776 777 778 779 780
{
	struct nfs_fsinfo fsinfo;
	struct nfs_client *clp = server->nfs_client;
	int error;

	if (clp->rpc_ops->set_capabilities != NULL) {
		error = clp->rpc_ops->set_capabilities(server, mntfh);
		if (error < 0)
781
			return error;
782 783 784
	}

	fsinfo.fattr = fattr;
785
	fsinfo.nlayouttypes = 0;
786
	memset(fsinfo.layouttype, 0, sizeof(fsinfo.layouttype));
787 788
	error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo);
	if (error < 0)
789
		return error;
790

791
	nfs_server_set_fsinfo(server, &fsinfo);
792 793 794 795 796 797 798 799 800 801 802 803 804 805

	/* Get some general file system info */
	if (server->namelen == 0) {
		struct nfs_pathconf pathinfo;

		pathinfo.fattr = fattr;
		nfs_fattr_init(fattr);

		if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0)
			server->namelen = pathinfo.max_namelen;
	}

	return 0;
}
B
Bryan Schumaker 已提交
806
EXPORT_SYMBOL_GPL(nfs_probe_fsinfo);
807 808 809 810

/*
 * Copy useful information when duplicating a server record
 */
811
void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source)
812 813
{
	target->flags = source->flags;
814 815
	target->rsize = source->rsize;
	target->wsize = source->wsize;
816 817 818 819 820
	target->acregmin = source->acregmin;
	target->acregmax = source->acregmax;
	target->acdirmin = source->acdirmin;
	target->acdirmax = source->acdirmax;
	target->caps = source->caps;
821
	target->options = source->options;
822
	target->auth_info = source->auth_info;
823
}
B
Bryan Schumaker 已提交
824
EXPORT_SYMBOL_GPL(nfs_server_copy_userdata);
825

826
void nfs_server_insert_lists(struct nfs_server *server)
827 828
{
	struct nfs_client *clp = server->nfs_client;
829
	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
830

831
	spin_lock(&nn->nfs_client_lock);
832
	list_add_tail_rcu(&server->client_link, &clp->cl_superblocks);
833
	list_add_tail(&server->master_link, &nn->nfs_volume_list);
834
	clear_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state);
835
	spin_unlock(&nn->nfs_client_lock);
836 837

}
B
Bryan Schumaker 已提交
838
EXPORT_SYMBOL_GPL(nfs_server_insert_lists);
839

C
Chuck Lever 已提交
840
void nfs_server_remove_lists(struct nfs_server *server)
841
{
842
	struct nfs_client *clp = server->nfs_client;
843
	struct nfs_net *nn;
844

845 846
	if (clp == NULL)
		return;
847
	nn = net_generic(clp->cl_net, nfs_net_id);
848
	spin_lock(&nn->nfs_client_lock);
849
	list_del_rcu(&server->client_link);
850
	if (list_empty(&clp->cl_superblocks))
851
		set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state);
852
	list_del(&server->master_link);
853
	spin_unlock(&nn->nfs_client_lock);
854 855 856

	synchronize_rcu();
}
C
Chuck Lever 已提交
857
EXPORT_SYMBOL_GPL(nfs_server_remove_lists);
858

859 860 861
/*
 * Allocate and initialise a server record
 */
862
struct nfs_server *nfs_alloc_server(void)
863 864 865 866 867 868 869 870 871 872 873 874
{
	struct nfs_server *server;

	server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL);
	if (!server)
		return NULL;

	server->client = server->client_acl = ERR_PTR(-EINVAL);

	/* Zero out the NFS state stuff */
	INIT_LIST_HEAD(&server->client_link);
	INIT_LIST_HEAD(&server->master_link);
875
	INIT_LIST_HEAD(&server->delegations);
876
	INIT_LIST_HEAD(&server->layouts);
877
	INIT_LIST_HEAD(&server->state_owners_lru);
878

879 880
	atomic_set(&server->active, 0);

881 882 883 884 885 886
	server->io_stats = nfs_alloc_iostats();
	if (!server->io_stats) {
		kfree(server);
		return NULL;
	}

887
	ida_init(&server->openowner_id);
888
	ida_init(&server->lockowner_id);
F
Fred Isaman 已提交
889 890
	pnfs_init_server(server);

891 892
	return server;
}
B
Bryan Schumaker 已提交
893
EXPORT_SYMBOL_GPL(nfs_alloc_server);
894 895 896 897 898 899

/*
 * Free up a server record
 */
void nfs_free_server(struct nfs_server *server)
{
900
	nfs_server_remove_lists(server);
901 902 903

	if (server->destroy != NULL)
		server->destroy(server);
904 905 906

	if (!IS_ERR(server->client_acl))
		rpc_shutdown_client(server->client_acl);
907 908 909 910 911
	if (!IS_ERR(server->client))
		rpc_shutdown_client(server->client);

	nfs_put_client(server->nfs_client);

912
	ida_destroy(&server->lockowner_id);
913
	ida_destroy(&server->openowner_id);
914 915 916 917
	nfs_free_iostats(server->io_stats);
	kfree(server);
	nfs_release_automount_timer();
}
B
Bryan Schumaker 已提交
918
EXPORT_SYMBOL_GPL(nfs_free_server);
919 920 921 922 923

/*
 * Create a version 2 or 3 volume record
 * - keyed on server and FSID
 */
924
struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info,
925
				     struct nfs_subversion *nfs_mod)
926 927
{
	struct nfs_server *server;
928
	struct nfs_fattr *fattr;
929 930 931 932 933 934
	int error;

	server = nfs_alloc_server();
	if (!server)
		return ERR_PTR(-ENOMEM);

935 936 937 938 939
	error = -ENOMEM;
	fattr = nfs_alloc_fattr();
	if (fattr == NULL)
		goto error;

940
	/* Get a client representation */
941
	error = nfs_init_server(server, mount_info->parsed, nfs_mod);
942 943 944 945
	if (error < 0)
		goto error;

	/* Probe the root fh to retrieve its FSID */
946
	error = nfs_probe_fsinfo(server, mount_info->mntfh, fattr);
947 948
	if (error < 0)
		goto error;
949 950 951
	if (server->nfs_client->rpc_ops->version == 3) {
		if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
			server->namelen = NFS3_MAXNAMLEN;
952
		if (!(mount_info->parsed->flags & NFS_MOUNT_NORDIRPLUS))
953 954 955 956 957 958
			server->caps |= NFS_CAP_READDIRPLUS;
	} else {
		if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN)
			server->namelen = NFS2_MAXNAMLEN;
	}

959
	if (!(fattr->valid & NFS_ATTR_FATTR)) {
960
		error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr, NULL);
961 962 963 964 965
		if (error < 0) {
			dprintk("nfs_create_server: getattr error = %d\n", -error);
			goto error;
		}
	}
966
	memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
967

968 969 970
	dprintk("Server FSID: %llx:%llx\n",
		(unsigned long long) server->fsid.major,
		(unsigned long long) server->fsid.minor);
971

972
	nfs_server_insert_lists(server);
973
	server->mount_time = jiffies;
974
	nfs_free_fattr(fattr);
975 976 977
	return server;

error:
978
	nfs_free_fattr(fattr);
979 980 981
	nfs_free_server(server);
	return ERR_PTR(error);
}
B
Bryan Schumaker 已提交
982
EXPORT_SYMBOL_GPL(nfs_create_server);
983 984 985 986 987 988

/*
 * Clone an NFS2, NFS3 or NFS4 server record
 */
struct nfs_server *nfs_clone_server(struct nfs_server *source,
				    struct nfs_fh *fh,
989 990
				    struct nfs_fattr *fattr,
				    rpc_authflavor_t flavor)
991 992
{
	struct nfs_server *server;
993
	struct nfs_fattr *fattr_fsinfo;
994 995 996 997 998 999
	int error;

	server = nfs_alloc_server();
	if (!server)
		return ERR_PTR(-ENOMEM);

1000 1001 1002 1003 1004
	error = -ENOMEM;
	fattr_fsinfo = nfs_alloc_fattr();
	if (fattr_fsinfo == NULL)
		goto out_free_server;

1005 1006
	/* Copy data from the source */
	server->nfs_client = source->nfs_client;
1007
	server->destroy = source->destroy;
1008 1009 1010 1011 1012
	atomic_inc(&server->nfs_client->cl_count);
	nfs_server_copy_userdata(server, source);

	server->fsid = fattr->fsid;

1013 1014
	error = nfs_init_server_rpcclient(server,
			source->client->cl_timeout,
1015
			flavor);
1016 1017 1018 1019
	if (error < 0)
		goto out_free_server;

	/* probe the filesystem info for this server filesystem */
1020
	error = nfs_probe_fsinfo(server, fh, fattr_fsinfo);
1021 1022 1023
	if (error < 0)
		goto out_free_server;

1024 1025 1026
	if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
		server->namelen = NFS4_MAXNAMLEN;

1027 1028 1029 1030
	error = nfs_start_lockd(server);
	if (error < 0)
		goto out_free_server;

1031
	nfs_server_insert_lists(server);
1032 1033
	server->mount_time = jiffies;

1034
	nfs_free_fattr(fattr_fsinfo);
1035 1036 1037
	return server;

out_free_server:
1038
	nfs_free_fattr(fattr_fsinfo);
1039 1040 1041
	nfs_free_server(server);
	return ERR_PTR(error);
}
B
Bryan Schumaker 已提交
1042
EXPORT_SYMBOL_GPL(nfs_clone_server);
1043

1044 1045 1046 1047 1048
void nfs_clients_init(struct net *net)
{
	struct nfs_net *nn = net_generic(net, nfs_net_id);

	INIT_LIST_HEAD(&nn->nfs_client_list);
1049
	INIT_LIST_HEAD(&nn->nfs_volume_list);
B
Bryan Schumaker 已提交
1050
#if IS_ENABLED(CONFIG_NFS_V4)
1051 1052
	idr_init(&nn->cb_ident_idr);
#endif
1053
	spin_lock_init(&nn->nfs_client_lock);
1054
	nn->boot_time = ktime_get_real();
1055 1056
}

1057 1058 1059 1060 1061 1062 1063
#ifdef CONFIG_PROC_FS
static int nfs_server_list_open(struct inode *inode, struct file *file);
static void *nfs_server_list_start(struct seq_file *p, loff_t *pos);
static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos);
static void nfs_server_list_stop(struct seq_file *p, void *v);
static int nfs_server_list_show(struct seq_file *m, void *v);

J
James Morris 已提交
1064
static const struct seq_operations nfs_server_list_ops = {
1065 1066 1067 1068 1069 1070
	.start	= nfs_server_list_start,
	.next	= nfs_server_list_next,
	.stop	= nfs_server_list_stop,
	.show	= nfs_server_list_show,
};

1071
static const struct file_operations nfs_server_list_fops = {
1072 1073 1074
	.open		= nfs_server_list_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
1075
	.release	= seq_release_net,
1076 1077 1078 1079 1080 1081 1082 1083
};

static int nfs_volume_list_open(struct inode *inode, struct file *file);
static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos);
static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos);
static void nfs_volume_list_stop(struct seq_file *p, void *v);
static int nfs_volume_list_show(struct seq_file *m, void *v);

J
James Morris 已提交
1084
static const struct seq_operations nfs_volume_list_ops = {
1085 1086 1087 1088 1089 1090
	.start	= nfs_volume_list_start,
	.next	= nfs_volume_list_next,
	.stop	= nfs_volume_list_stop,
	.show	= nfs_volume_list_show,
};

1091
static const struct file_operations nfs_volume_list_fops = {
1092 1093 1094
	.open		= nfs_volume_list_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
1095
	.release	= seq_release_net,
1096 1097 1098 1099 1100 1101 1102 1103
};

/*
 * open "/proc/fs/nfsfs/servers" which provides a summary of servers with which
 * we're dealing
 */
static int nfs_server_list_open(struct inode *inode, struct file *file)
{
1104 1105
	return seq_open_net(inode, file, &nfs_server_list_ops,
			   sizeof(struct seq_net_private));
1106 1107 1108 1109 1110 1111
}

/*
 * set up the iterator to start reading from the server list and return the first item
 */
static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
1112
				__acquires(&nn->nfs_client_lock)
1113
{
1114
	struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
1115

1116
	/* lock the list against modification */
1117
	spin_lock(&nn->nfs_client_lock);
1118
	return seq_list_start_head(&nn->nfs_client_list, *_pos);
1119 1120 1121 1122 1123 1124 1125
}

/*
 * move to next server
 */
static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
{
1126
	struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
1127 1128

	return seq_list_next(v, &nn->nfs_client_list, pos);
1129 1130 1131 1132 1133 1134
}

/*
 * clean up after reading from the transports list
 */
static void nfs_server_list_stop(struct seq_file *p, void *v)
1135
				__releases(&nn->nfs_client_lock)
1136
{
1137
	struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
1138 1139

	spin_unlock(&nn->nfs_client_lock);
1140 1141 1142 1143 1144 1145 1146 1147
}

/*
 * display a header line followed by a load of call lines
 */
static int nfs_server_list_show(struct seq_file *m, void *v)
{
	struct nfs_client *clp;
1148
	struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
1149 1150

	/* display header on line 1 */
1151
	if (v == &nn->nfs_client_list) {
1152 1153 1154 1155 1156 1157 1158
		seq_puts(m, "NV SERVER   PORT USE HOSTNAME\n");
		return 0;
	}

	/* display one transport per line on subsequent lines */
	clp = list_entry(v, struct nfs_client, cl_share_link);

1159 1160 1161 1162
	/* Check if the client is initialized */
	if (clp->cl_cons_state != NFS_CS_READY)
		return 0;

1163
	rcu_read_lock();
1164
	seq_printf(m, "v%u %s %s %3d %s\n",
1165
		   clp->rpc_ops->version,
1166 1167
		   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR),
		   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT),
1168 1169
		   atomic_read(&clp->cl_count),
		   clp->cl_hostname);
1170
	rcu_read_unlock();
1171 1172 1173 1174 1175 1176 1177 1178 1179

	return 0;
}

/*
 * open "/proc/fs/nfsfs/volumes" which provides a summary of extant volumes
 */
static int nfs_volume_list_open(struct inode *inode, struct file *file)
{
1180
	return seq_open_net(inode, file, &nfs_volume_list_ops,
1181
			   sizeof(struct seq_net_private));
1182 1183 1184 1185 1186 1187
}

/*
 * set up the iterator to start reading from the volume list and return the first item
 */
static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
1188
				__acquires(&nn->nfs_client_lock)
1189
{
1190
	struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
1191

1192
	/* lock the list against modification */
1193
	spin_lock(&nn->nfs_client_lock);
1194
	return seq_list_start_head(&nn->nfs_volume_list, *_pos);
1195 1196 1197 1198 1199 1200 1201
}

/*
 * move to next volume
 */
static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)
{
1202
	struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
1203 1204

	return seq_list_next(v, &nn->nfs_volume_list, pos);
1205 1206 1207 1208 1209 1210
}

/*
 * clean up after reading from the transports list
 */
static void nfs_volume_list_stop(struct seq_file *p, void *v)
1211
				__releases(&nn->nfs_client_lock)
1212
{
1213
	struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
1214 1215

	spin_unlock(&nn->nfs_client_lock);
1216 1217 1218 1219 1220 1221 1222 1223 1224
}

/*
 * display a header line followed by a load of call lines
 */
static int nfs_volume_list_show(struct seq_file *m, void *v)
{
	struct nfs_server *server;
	struct nfs_client *clp;
1225 1226
	char dev[13];	// 8 for 2^24, 1 for ':', 3 for 2^8, 1 for '\0'
	char fsid[34];	// 2 * 16 for %llx, 1 for ':', 1 for '\0'
1227
	struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
1228 1229

	/* display header on line 1 */
1230
	if (v == &nn->nfs_volume_list) {
1231 1232
		seq_puts(m, "NV SERVER   PORT DEV          FSID"
			    "                              FSC\n");
1233 1234 1235 1236 1237 1238
		return 0;
	}
	/* display one transport per line on subsequent lines */
	server = list_entry(v, struct nfs_server, master_link);
	clp = server->nfs_client;

1239
	snprintf(dev, sizeof(dev), "%u:%u",
1240 1241
		 MAJOR(server->s_dev), MINOR(server->s_dev));

1242
	snprintf(fsid, sizeof(fsid), "%llx:%llx",
1243 1244
		 (unsigned long long) server->fsid.major,
		 (unsigned long long) server->fsid.minor);
1245

1246
	rcu_read_lock();
1247
	seq_printf(m, "v%u %s %s %-12s %-33s %s\n",
1248
		   clp->rpc_ops->version,
1249 1250
		   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR),
		   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT),
1251
		   dev,
D
David Howells 已提交
1252 1253
		   fsid,
		   nfs_server_fscache_state(server));
1254
	rcu_read_unlock();
1255 1256 1257 1258

	return 0;
}

1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277
int nfs_fs_proc_net_init(struct net *net)
{
	struct nfs_net *nn = net_generic(net, nfs_net_id);
	struct proc_dir_entry *p;

	nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net);
	if (!nn->proc_nfsfs)
		goto error_0;

	/* a file of servers with which we're dealing */
	p = proc_create("servers", S_IFREG|S_IRUGO,
			nn->proc_nfsfs, &nfs_server_list_fops);
	if (!p)
		goto error_1;

	/* a file of volumes that we have mounted */
	p = proc_create("volumes", S_IFREG|S_IRUGO,
			nn->proc_nfsfs, &nfs_volume_list_fops);
	if (!p)
1278
		goto error_1;
1279 1280 1281
	return 0;

error_1:
1282
	remove_proc_subtree("nfsfs", net->proc_net);
1283 1284 1285 1286 1287 1288
error_0:
	return -ENOMEM;
}

void nfs_fs_proc_net_exit(struct net *net)
{
1289
	remove_proc_subtree("nfsfs", net->proc_net);
1290 1291
}

1292 1293 1294 1295 1296
/*
 * initialise the /proc/fs/nfsfs/ directory
 */
int __init nfs_fs_proc_init(void)
{
1297
	if (!proc_mkdir("fs/nfsfs", NULL))
1298 1299 1300
		goto error_0;

	/* a file of servers with which we're dealing */
1301
	if (!proc_symlink("fs/nfsfs/servers", NULL, "../../net/nfsfs/servers"))
1302 1303 1304
		goto error_1;

	/* a file of volumes that we have mounted */
1305 1306
	if (!proc_symlink("fs/nfsfs/volumes", NULL, "../../net/nfsfs/volumes"))
		goto error_1;
1307

1308
	return 0;
1309
error_1:
1310
	remove_proc_subtree("fs/nfsfs", NULL);
1311 1312 1313 1314 1315 1316 1317 1318 1319
error_0:
	return -ENOMEM;
}

/*
 * clean up the /proc/fs/nfsfs/ directory
 */
void nfs_fs_proc_exit(void)
{
1320
	remove_proc_subtree("fs/nfsfs", NULL);
1321 1322 1323
}

#endif /* CONFIG_PROC_FS */