blob: 44fe7fd7bfbf6ba31cc06b22d3ae4d9bbb6c8306 [file] [log] [blame]
David Howells24c8dbb2006-08-22 20:06:10 -04001/* client.c: NFS client sharing and management code
2 *
3 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12
David Howells24c8dbb2006-08-22 20:06:10 -040013#include <linux/module.h>
14#include <linux/init.h>
Alexey Dobriyane8edc6e2007-05-21 01:22:52 +040015#include <linux/sched.h>
David Howells24c8dbb2006-08-22 20:06:10 -040016#include <linux/time.h>
17#include <linux/kernel.h>
18#include <linux/mm.h>
19#include <linux/string.h>
20#include <linux/stat.h>
21#include <linux/errno.h>
22#include <linux/unistd.h>
23#include <linux/sunrpc/clnt.h>
24#include <linux/sunrpc/stats.h>
25#include <linux/sunrpc/metrics.h>
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -040026#include <linux/sunrpc/xprtsock.h>
\"Talpey, Thomas\2cf7ff72007-09-10 13:49:41 -040027#include <linux/sunrpc/xprtrdma.h>
David Howells24c8dbb2006-08-22 20:06:10 -040028#include <linux/nfs_fs.h>
29#include <linux/nfs_mount.h>
30#include <linux/nfs4_mount.h>
31#include <linux/lockd/bind.h>
David Howells24c8dbb2006-08-22 20:06:10 -040032#include <linux/seq_file.h>
33#include <linux/mount.h>
34#include <linux/nfs_idmap.h>
35#include <linux/vfs.h>
36#include <linux/inet.h>
Trond Myklebust3b0d3f92008-01-03 13:28:58 -050037#include <linux/in6.h>
38#include <net/ipv6.h>
David Howells24c8dbb2006-08-22 20:06:10 -040039#include <linux/nfs_xdr.h>
40
41#include <asm/system.h>
42
43#include "nfs4_fs.h"
44#include "callback.h"
45#include "delegation.h"
46#include "iostat.h"
47#include "internal.h"
48
49#define NFSDBG_FACILITY NFSDBG_CLIENT
50
51static DEFINE_SPINLOCK(nfs_client_lock);
52static LIST_HEAD(nfs_client_list);
David Howells54ceac42006-08-22 20:06:13 -040053static LIST_HEAD(nfs_volume_list);
David Howells24c8dbb2006-08-22 20:06:10 -040054static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
55
56/*
David Howells5006a762006-08-22 20:06:12 -040057 * RPC cruft for NFS
58 */
59static struct rpc_version *nfs_version[5] = {
60 [2] = &nfs_version2,
61#ifdef CONFIG_NFS_V3
62 [3] = &nfs_version3,
63#endif
64#ifdef CONFIG_NFS_V4
65 [4] = &nfs_version4,
66#endif
67};
68
69struct rpc_program nfs_program = {
70 .name = "nfs",
71 .number = NFS_PROGRAM,
72 .nrvers = ARRAY_SIZE(nfs_version),
73 .version = nfs_version,
74 .stats = &nfs_rpcstat,
75 .pipe_dir_name = "/nfs",
76};
77
78struct rpc_stat nfs_rpcstat = {
79 .program = &nfs_program
80};
81
82
83#ifdef CONFIG_NFS_V3_ACL
84static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program };
85static struct rpc_version * nfsacl_version[] = {
86 [3] = &nfsacl_version3,
87};
88
89struct rpc_program nfsacl_program = {
90 .name = "nfsacl",
91 .number = NFS_ACL_PROGRAM,
92 .nrvers = ARRAY_SIZE(nfsacl_version),
93 .version = nfsacl_version,
94 .stats = &nfsacl_rpcstat,
95};
96#endif /* CONFIG_NFS_V3_ACL */
97
Trond Myklebust3a498022007-12-14 14:56:04 -050098struct nfs_client_initdata {
99 const char *hostname;
100 const struct sockaddr_in *addr;
Chuck Lever6e4cffd2007-12-10 14:58:15 -0500101 size_t addrlen;
Trond Myklebust40c553192007-12-14 14:56:07 -0500102 const struct nfs_rpc_ops *rpc_ops;
Trond Myklebust3a498022007-12-14 14:56:04 -0500103};
104
David Howells5006a762006-08-22 20:06:12 -0400105/*
David Howells24c8dbb2006-08-22 20:06:10 -0400106 * Allocate a shared client record
107 *
108 * Since these are allocated/deallocated very rarely, we don't
109 * bother putting them in a slab cache...
110 */
Trond Myklebust3a498022007-12-14 14:56:04 -0500111static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
David Howells24c8dbb2006-08-22 20:06:10 -0400112{
113 struct nfs_client *clp;
David Howells24c8dbb2006-08-22 20:06:10 -0400114
115 if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
116 goto error_0;
117
Trond Myklebust40c553192007-12-14 14:56:07 -0500118 clp->rpc_ops = cl_init->rpc_ops;
119
120 if (cl_init->rpc_ops->version == 4) {
David Howells24c8dbb2006-08-22 20:06:10 -0400121 if (nfs_callback_up() < 0)
122 goto error_2;
123 __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
124 }
125
126 atomic_set(&clp->cl_count, 1);
127 clp->cl_cons_state = NFS_CS_INITING;
128
Chuck Lever6e4cffd2007-12-10 14:58:15 -0500129 memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen);
130 clp->cl_addrlen = cl_init->addrlen;
David Howells24c8dbb2006-08-22 20:06:10 -0400131
Trond Myklebust3a498022007-12-14 14:56:04 -0500132 if (cl_init->hostname) {
133 clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL);
David Howells24c8dbb2006-08-22 20:06:10 -0400134 if (!clp->cl_hostname)
135 goto error_3;
136 }
137
138 INIT_LIST_HEAD(&clp->cl_superblocks);
139 clp->cl_rpcclient = ERR_PTR(-EINVAL);
140
141#ifdef CONFIG_NFS_V4
142 init_rwsem(&clp->cl_sem);
143 INIT_LIST_HEAD(&clp->cl_delegations);
David Howells24c8dbb2006-08-22 20:06:10 -0400144 spin_lock_init(&clp->cl_lock);
David Howells65f27f32006-11-22 14:55:48 +0000145 INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
David Howells24c8dbb2006-08-22 20:06:10 -0400146 rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
147 clp->cl_boot_time = CURRENT_TIME;
148 clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
149#endif
150
151 return clp;
152
153error_3:
Trond Myklebust9c5bf382006-08-22 20:06:14 -0400154 if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
155 nfs_callback_down();
David Howells24c8dbb2006-08-22 20:06:10 -0400156error_2:
David Howells24c8dbb2006-08-22 20:06:10 -0400157 kfree(clp);
158error_0:
159 return NULL;
160}
161
Trond Myklebust5dd31772006-08-24 01:03:05 -0400162static void nfs4_shutdown_client(struct nfs_client *clp)
163{
164#ifdef CONFIG_NFS_V4
165 if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
166 nfs4_kill_renewd(clp);
Trond Myklebust9f958ab2007-07-02 13:58:33 -0400167 BUG_ON(!RB_EMPTY_ROOT(&clp->cl_state_owners));
Trond Myklebust5dd31772006-08-24 01:03:05 -0400168 if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
169 nfs_idmap_delete(clp);
170#endif
171}
172
David Howells24c8dbb2006-08-22 20:06:10 -0400173/*
174 * Destroy a shared client record
175 */
176static void nfs_free_client(struct nfs_client *clp)
177{
Trond Myklebust40c553192007-12-14 14:56:07 -0500178 dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version);
David Howells24c8dbb2006-08-22 20:06:10 -0400179
Trond Myklebust5dd31772006-08-24 01:03:05 -0400180 nfs4_shutdown_client(clp);
David Howells24c8dbb2006-08-22 20:06:10 -0400181
182 /* -EIO all pending I/O */
183 if (!IS_ERR(clp->cl_rpcclient))
184 rpc_shutdown_client(clp->cl_rpcclient);
185
186 if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
187 nfs_callback_down();
188
David Howells24c8dbb2006-08-22 20:06:10 -0400189 kfree(clp->cl_hostname);
190 kfree(clp);
191
192 dprintk("<-- nfs_free_client()\n");
193}
194
195/*
196 * Release a reference to a shared client record
197 */
198void nfs_put_client(struct nfs_client *clp)
199{
David Howells27ba8512006-07-30 14:40:56 -0400200 if (!clp)
201 return;
202
David Howells24c8dbb2006-08-22 20:06:10 -0400203 dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count));
204
205 if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) {
206 list_del(&clp->cl_share_link);
207 spin_unlock(&nfs_client_lock);
208
209 BUG_ON(!list_empty(&clp->cl_superblocks));
210
211 nfs_free_client(clp);
212 }
213}
214
Trond Myklebust3b0d3f92008-01-03 13:28:58 -0500215static int nfs_sockaddr_match_ipaddr4(const struct sockaddr_in *sa1,
216 const struct sockaddr_in *sa2)
217{
218 return sa1->sin_addr.s_addr == sa2->sin_addr.s_addr;
219}
220
221static int nfs_sockaddr_match_ipaddr6(const struct sockaddr_in6 *sa1,
222 const struct sockaddr_in6 *sa2)
223{
224 return ipv6_addr_equal(&sa1->sin6_addr, &sa2->sin6_addr);
225}
226
227static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
228 const struct sockaddr *sa2)
229{
230 switch (sa1->sa_family) {
231 case AF_INET:
232 return nfs_sockaddr_match_ipaddr4((const struct sockaddr_in *)sa1,
233 (const struct sockaddr_in *)sa2);
234 case AF_INET6:
235 return nfs_sockaddr_match_ipaddr6((const struct sockaddr_in6 *)sa1,
236 (const struct sockaddr_in6 *)sa2);
237 }
238 BUG();
239}
240
David Howells24c8dbb2006-08-22 20:06:10 -0400241/*
Trond Myklebustc81468a2007-12-14 14:56:05 -0500242 * Find a client by IP address and protocol version
243 * - returns NULL if no such client
David Howells24c8dbb2006-08-22 20:06:10 -0400244 */
Trond Myklebust3b0d3f92008-01-03 13:28:58 -0500245struct nfs_client *_nfs_find_client(const struct sockaddr *addr, int nfsversion)
Trond Myklebustc81468a2007-12-14 14:56:05 -0500246{
247 struct nfs_client *clp;
248
249 spin_lock(&nfs_client_lock);
250 list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
Trond Myklebust3b0d3f92008-01-03 13:28:58 -0500251 struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
252
Trond Myklebustc81468a2007-12-14 14:56:05 -0500253 /* Don't match clients that failed to initialise properly */
254 if (clp->cl_cons_state != NFS_CS_READY)
255 continue;
256
257 /* Different NFS versions cannot share the same nfs_client */
Trond Myklebust40c553192007-12-14 14:56:07 -0500258 if (clp->rpc_ops->version != nfsversion)
Trond Myklebustc81468a2007-12-14 14:56:05 -0500259 continue;
260
Trond Myklebust3b0d3f92008-01-03 13:28:58 -0500261 if (addr->sa_family != clap->sa_family)
262 continue;
Trond Myklebustc81468a2007-12-14 14:56:05 -0500263 /* Match only the IP address, not the port number */
Trond Myklebust3b0d3f92008-01-03 13:28:58 -0500264 if (!nfs_sockaddr_match_ipaddr(addr, clap))
Trond Myklebustc81468a2007-12-14 14:56:05 -0500265 continue;
266
267 atomic_inc(&clp->cl_count);
268 spin_unlock(&nfs_client_lock);
269 return clp;
270 }
271 spin_unlock(&nfs_client_lock);
272 return NULL;
273}
274
Trond Myklebust3b0d3f92008-01-03 13:28:58 -0500275struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversion)
276{
277 return _nfs_find_client((const struct sockaddr *)addr, nfsversion);
278}
279
Trond Myklebustc81468a2007-12-14 14:56:05 -0500280/*
281 * Find an nfs_client on the list that matches the initialisation data
282 * that is supplied.
283 */
284static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data)
David Howells24c8dbb2006-08-22 20:06:10 -0400285{
286 struct nfs_client *clp;
287
288 list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
Trond Myklebust13bbc062006-10-19 23:28:40 -0700289 /* Don't match clients that failed to initialise properly */
290 if (clp->cl_cons_state < 0)
291 continue;
292
David Howells24c8dbb2006-08-22 20:06:10 -0400293 /* Different NFS versions cannot share the same nfs_client */
Trond Myklebust40c553192007-12-14 14:56:07 -0500294 if (clp->rpc_ops != data->rpc_ops)
David Howells24c8dbb2006-08-22 20:06:10 -0400295 continue;
296
Trond Myklebustc81468a2007-12-14 14:56:05 -0500297 /* Match the full socket address */
298 if (memcmp(&clp->cl_addr, data->addr, sizeof(clp->cl_addr)) != 0)
David Howells24c8dbb2006-08-22 20:06:10 -0400299 continue;
300
Trond Myklebustc81468a2007-12-14 14:56:05 -0500301 atomic_inc(&clp->cl_count);
302 return clp;
David Howells24c8dbb2006-08-22 20:06:10 -0400303 }
David Howells24c8dbb2006-08-22 20:06:10 -0400304 return NULL;
David Howells24c8dbb2006-08-22 20:06:10 -0400305}
306
307/*
308 * Look up a client by IP address and protocol version
309 * - creates a new record if one doesn't yet exist
310 */
Trond Myklebust3a498022007-12-14 14:56:04 -0500311static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init)
David Howells24c8dbb2006-08-22 20:06:10 -0400312{
313 struct nfs_client *clp, *new = NULL;
314 int error;
315
Trond Myklebust40c553192007-12-14 14:56:07 -0500316 dprintk("--> nfs_get_client(%s,"NIPQUAD_FMT":%d,%u)\n",
Trond Myklebust3a498022007-12-14 14:56:04 -0500317 cl_init->hostname ?: "", NIPQUAD(cl_init->addr->sin_addr),
Trond Myklebust40c553192007-12-14 14:56:07 -0500318 cl_init->addr->sin_port, cl_init->rpc_ops->version);
David Howells24c8dbb2006-08-22 20:06:10 -0400319
320 /* see if the client already exists */
321 do {
322 spin_lock(&nfs_client_lock);
323
Trond Myklebustc81468a2007-12-14 14:56:05 -0500324 clp = nfs_match_client(cl_init);
David Howells24c8dbb2006-08-22 20:06:10 -0400325 if (clp)
326 goto found_client;
327 if (new)
328 goto install_client;
329
330 spin_unlock(&nfs_client_lock);
331
Trond Myklebust3a498022007-12-14 14:56:04 -0500332 new = nfs_alloc_client(cl_init);
David Howells24c8dbb2006-08-22 20:06:10 -0400333 } while (new);
334
335 return ERR_PTR(-ENOMEM);
336
337 /* install a new client and return with it unready */
338install_client:
339 clp = new;
340 list_add(&clp->cl_share_link, &nfs_client_list);
341 spin_unlock(&nfs_client_lock);
342 dprintk("--> nfs_get_client() = %p [new]\n", clp);
343 return clp;
344
345 /* found an existing client
346 * - make sure it's ready before returning
347 */
348found_client:
349 spin_unlock(&nfs_client_lock);
350
351 if (new)
352 nfs_free_client(new);
353
Linus Torvalds83250492006-10-08 17:28:25 -0700354 error = wait_event_interruptible(nfs_client_active_wq,
Trond Myklebust0bae89e2006-10-08 14:33:24 -0400355 clp->cl_cons_state != NFS_CS_INITING);
356 if (error < 0) {
357 nfs_put_client(clp);
358 return ERR_PTR(-ERESTARTSYS);
David Howells24c8dbb2006-08-22 20:06:10 -0400359 }
360
361 if (clp->cl_cons_state < NFS_CS_READY) {
362 error = clp->cl_cons_state;
363 nfs_put_client(clp);
364 return ERR_PTR(error);
365 }
366
David Howells54ceac42006-08-22 20:06:13 -0400367 BUG_ON(clp->cl_cons_state != NFS_CS_READY);
368
David Howells24c8dbb2006-08-22 20:06:10 -0400369 dprintk("--> nfs_get_client() = %p [share]\n", clp);
370 return clp;
371}
372
373/*
374 * Mark a server as ready or failed
375 */
David Howells54ceac42006-08-22 20:06:13 -0400376static void nfs_mark_client_ready(struct nfs_client *clp, int state)
David Howells24c8dbb2006-08-22 20:06:10 -0400377{
378 clp->cl_cons_state = state;
379 wake_up_all(&nfs_client_active_wq);
380}
David Howells5006a762006-08-22 20:06:12 -0400381
382/*
383 * Initialise the timeout values for a connection
384 */
385static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
386 unsigned int timeo, unsigned int retrans)
387{
388 to->to_initval = timeo * HZ / 10;
389 to->to_retries = retrans;
390 if (!to->to_retries)
391 to->to_retries = 2;
392
393 switch (proto) {
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -0400394 case XPRT_TRANSPORT_TCP:
\"Talpey, Thomas\2cf7ff72007-09-10 13:49:41 -0400395 case XPRT_TRANSPORT_RDMA:
David Howells5006a762006-08-22 20:06:12 -0400396 if (!to->to_initval)
397 to->to_initval = 60 * HZ;
398 if (to->to_initval > NFS_MAX_TCP_TIMEOUT)
399 to->to_initval = NFS_MAX_TCP_TIMEOUT;
400 to->to_increment = to->to_initval;
401 to->to_maxval = to->to_initval + (to->to_increment * to->to_retries);
402 to->to_exponential = 0;
403 break;
\"Talpey, Thomas\0896a722007-09-10 13:48:23 -0400404 case XPRT_TRANSPORT_UDP:
David Howells5006a762006-08-22 20:06:12 -0400405 default:
406 if (!to->to_initval)
407 to->to_initval = 11 * HZ / 10;
408 if (to->to_initval > NFS_MAX_UDP_TIMEOUT)
409 to->to_initval = NFS_MAX_UDP_TIMEOUT;
410 to->to_maxval = NFS_MAX_UDP_TIMEOUT;
411 to->to_exponential = 1;
412 break;
413 }
414}
415
416/*
417 * Create an RPC client handle
418 */
David Howells54ceac42006-08-22 20:06:13 -0400419static int nfs_create_rpc_client(struct nfs_client *clp, int proto,
420 unsigned int timeo,
421 unsigned int retrans,
Chuck Lever43d78ef2007-02-06 18:26:11 -0500422 rpc_authflavor_t flavor,
423 int flags)
David Howells5006a762006-08-22 20:06:12 -0400424{
425 struct rpc_timeout timeparms;
David Howells5006a762006-08-22 20:06:12 -0400426 struct rpc_clnt *clnt = NULL;
Chuck Lever41877d22006-08-22 20:06:20 -0400427 struct rpc_create_args args = {
428 .protocol = proto,
429 .address = (struct sockaddr *)&clp->cl_addr,
Chuck Lever6e4cffd2007-12-10 14:58:15 -0500430 .addrsize = clp->cl_addrlen,
Chuck Lever41877d22006-08-22 20:06:20 -0400431 .timeout = &timeparms,
432 .servername = clp->cl_hostname,
433 .program = &nfs_program,
434 .version = clp->rpc_ops->version,
435 .authflavor = flavor,
Chuck Lever43d78ef2007-02-06 18:26:11 -0500436 .flags = flags,
Chuck Lever41877d22006-08-22 20:06:20 -0400437 };
David Howells5006a762006-08-22 20:06:12 -0400438
439 if (!IS_ERR(clp->cl_rpcclient))
440 return 0;
441
442 nfs_init_timeout_values(&timeparms, proto, timeo, retrans);
443 clp->retrans_timeo = timeparms.to_initval;
444 clp->retrans_count = timeparms.to_retries;
445
Chuck Lever41877d22006-08-22 20:06:20 -0400446 clnt = rpc_create(&args);
David Howells5006a762006-08-22 20:06:12 -0400447 if (IS_ERR(clnt)) {
448 dprintk("%s: cannot create RPC client. Error = %ld\n",
449 __FUNCTION__, PTR_ERR(clnt));
450 return PTR_ERR(clnt);
451 }
452
David Howells5006a762006-08-22 20:06:12 -0400453 clp->cl_rpcclient = clnt;
454 return 0;
455}
David Howells54ceac42006-08-22 20:06:13 -0400456
457/*
458 * Version 2 or 3 client destruction
459 */
460static void nfs_destroy_server(struct nfs_server *server)
461{
David Howells54ceac42006-08-22 20:06:13 -0400462 if (!(server->flags & NFS_MOUNT_NONLM))
463 lockd_down(); /* release rpc.lockd */
464}
465
466/*
467 * Version 2 or 3 lockd setup
468 */
469static int nfs_start_lockd(struct nfs_server *server)
470{
471 int error = 0;
472
Trond Myklebust40c553192007-12-14 14:56:07 -0500473 if (server->nfs_client->rpc_ops->version > 3)
David Howells54ceac42006-08-22 20:06:13 -0400474 goto out;
475 if (server->flags & NFS_MOUNT_NONLM)
476 goto out;
NeilBrown24e36662006-10-02 02:17:45 -0700477 error = lockd_up((server->flags & NFS_MOUNT_TCP) ?
478 IPPROTO_TCP : IPPROTO_UDP);
David Howells54ceac42006-08-22 20:06:13 -0400479 if (error < 0)
480 server->flags |= NFS_MOUNT_NONLM;
481 else
482 server->destroy = nfs_destroy_server;
483out:
484 return error;
485}
486
487/*
488 * Initialise an NFSv3 ACL client connection
489 */
490#ifdef CONFIG_NFS_V3_ACL
491static void nfs_init_server_aclclient(struct nfs_server *server)
492{
Trond Myklebust40c553192007-12-14 14:56:07 -0500493 if (server->nfs_client->rpc_ops->version != 3)
David Howells54ceac42006-08-22 20:06:13 -0400494 goto out_noacl;
495 if (server->flags & NFS_MOUNT_NOACL)
496 goto out_noacl;
497
498 server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3);
499 if (IS_ERR(server->client_acl))
500 goto out_noacl;
501
502 /* No errors! Assume that Sun nfsacls are supported */
503 server->caps |= NFS_CAP_ACLS;
504 return;
505
506out_noacl:
507 server->caps &= ~NFS_CAP_ACLS;
508}
509#else
510static inline void nfs_init_server_aclclient(struct nfs_server *server)
511{
512 server->flags &= ~NFS_MOUNT_NOACL;
513 server->caps &= ~NFS_CAP_ACLS;
514}
515#endif
516
517/*
518 * Create a general RPC client
519 */
520static int nfs_init_server_rpcclient(struct nfs_server *server, rpc_authflavor_t pseudoflavour)
521{
522 struct nfs_client *clp = server->nfs_client;
523
524 server->client = rpc_clone_client(clp->cl_rpcclient);
525 if (IS_ERR(server->client)) {
526 dprintk("%s: couldn't create rpc_client!\n", __FUNCTION__);
527 return PTR_ERR(server->client);
528 }
529
530 if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) {
531 struct rpc_auth *auth;
532
533 auth = rpcauth_create(pseudoflavour, server->client);
534 if (IS_ERR(auth)) {
535 dprintk("%s: couldn't create credcache!\n", __FUNCTION__);
536 return PTR_ERR(auth);
537 }
538 }
539 server->client->cl_softrtry = 0;
540 if (server->flags & NFS_MOUNT_SOFT)
541 server->client->cl_softrtry = 1;
542
543 server->client->cl_intr = 0;
544 if (server->flags & NFS4_MOUNT_INTR)
545 server->client->cl_intr = 1;
546
547 return 0;
548}
549
550/*
551 * Initialise an NFS2 or NFS3 client
552 */
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -0400553static int nfs_init_client(struct nfs_client *clp,
554 const struct nfs_parsed_mount_data *data)
David Howells54ceac42006-08-22 20:06:13 -0400555{
David Howells54ceac42006-08-22 20:06:13 -0400556 int error;
557
558 if (clp->cl_cons_state == NFS_CS_READY) {
559 /* the client is already initialised */
560 dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp);
561 return 0;
562 }
563
David Howells54ceac42006-08-22 20:06:13 -0400564 /*
565 * Create a client RPC handle for doing FSSTAT with UNIX auth only
566 * - RFC 2623, sec 2.3.2
567 */
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -0400568 error = nfs_create_rpc_client(clp, data->nfs_server.protocol,
569 data->timeo, data->retrans, RPC_AUTH_UNIX, 0);
David Howells54ceac42006-08-22 20:06:13 -0400570 if (error < 0)
571 goto error;
572 nfs_mark_client_ready(clp, NFS_CS_READY);
573 return 0;
574
575error:
576 nfs_mark_client_ready(clp, error);
577 dprintk("<-- nfs_init_client() = xerror %d\n", error);
578 return error;
579}
580
581/*
582 * Create a version 2 or 3 client
583 */
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -0400584static int nfs_init_server(struct nfs_server *server,
585 const struct nfs_parsed_mount_data *data)
David Howells54ceac42006-08-22 20:06:13 -0400586{
Trond Myklebust3a498022007-12-14 14:56:04 -0500587 struct nfs_client_initdata cl_init = {
588 .hostname = data->nfs_server.hostname,
589 .addr = &data->nfs_server.address,
Chuck Lever6e4cffd2007-12-10 14:58:15 -0500590 .addrlen = sizeof(data->nfs_server.address),
Trond Myklebust40c553192007-12-14 14:56:07 -0500591 .rpc_ops = &nfs_v2_clientops,
Trond Myklebust3a498022007-12-14 14:56:04 -0500592 };
David Howells54ceac42006-08-22 20:06:13 -0400593 struct nfs_client *clp;
Trond Myklebust3a498022007-12-14 14:56:04 -0500594 int error;
David Howells54ceac42006-08-22 20:06:13 -0400595
596 dprintk("--> nfs_init_server()\n");
597
598#ifdef CONFIG_NFS_V3
599 if (data->flags & NFS_MOUNT_VER3)
Trond Myklebust40c553192007-12-14 14:56:07 -0500600 cl_init.rpc_ops = &nfs_v3_clientops;
David Howells54ceac42006-08-22 20:06:13 -0400601#endif
602
603 /* Allocate or find a client reference we can use */
Trond Myklebust3a498022007-12-14 14:56:04 -0500604 clp = nfs_get_client(&cl_init);
David Howells54ceac42006-08-22 20:06:13 -0400605 if (IS_ERR(clp)) {
606 dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp));
607 return PTR_ERR(clp);
608 }
609
610 error = nfs_init_client(clp, data);
611 if (error < 0)
612 goto error;
613
614 server->nfs_client = clp;
615
616 /* Initialise the client representation from the mount data */
617 server->flags = data->flags & NFS_MOUNT_FLAGMASK;
618
619 if (data->rsize)
620 server->rsize = nfs_block_size(data->rsize, NULL);
621 if (data->wsize)
622 server->wsize = nfs_block_size(data->wsize, NULL);
623
624 server->acregmin = data->acregmin * HZ;
625 server->acregmax = data->acregmax * HZ;
626 server->acdirmin = data->acdirmin * HZ;
627 server->acdirmax = data->acdirmax * HZ;
628
629 /* Start lockd here, before we might error out */
630 error = nfs_start_lockd(server);
631 if (error < 0)
632 goto error;
633
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -0400634 error = nfs_init_server_rpcclient(server, data->auth_flavors[0]);
David Howells54ceac42006-08-22 20:06:13 -0400635 if (error < 0)
636 goto error;
637
638 server->namelen = data->namlen;
639 /* Create a client RPC handle for the NFSv3 ACL management interface */
640 nfs_init_server_aclclient(server);
David Howells54ceac42006-08-22 20:06:13 -0400641 dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp);
642 return 0;
643
644error:
645 server->nfs_client = NULL;
646 nfs_put_client(clp);
647 dprintk("<-- nfs_init_server() = xerror %d\n", error);
648 return error;
649}
650
651/*
652 * Load up the server record from information gained in an fsinfo record
653 */
654static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *fsinfo)
655{
656 unsigned long max_rpc_payload;
657
658 /* Work out a lot of parameters */
659 if (server->rsize == 0)
660 server->rsize = nfs_block_size(fsinfo->rtpref, NULL);
661 if (server->wsize == 0)
662 server->wsize = nfs_block_size(fsinfo->wtpref, NULL);
663
664 if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax)
665 server->rsize = nfs_block_size(fsinfo->rtmax, NULL);
666 if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax)
667 server->wsize = nfs_block_size(fsinfo->wtmax, NULL);
668
669 max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL);
670 if (server->rsize > max_rpc_payload)
671 server->rsize = max_rpc_payload;
672 if (server->rsize > NFS_MAX_FILE_IO_SIZE)
673 server->rsize = NFS_MAX_FILE_IO_SIZE;
674 server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
Peter Zijlstrae0bf68d2007-10-16 23:25:46 -0700675
David Howells54ceac42006-08-22 20:06:13 -0400676 server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD;
677
678 if (server->wsize > max_rpc_payload)
679 server->wsize = max_rpc_payload;
680 if (server->wsize > NFS_MAX_FILE_IO_SIZE)
681 server->wsize = NFS_MAX_FILE_IO_SIZE;
682 server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
683 server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL);
684
685 server->dtsize = nfs_block_size(fsinfo->dtpref, NULL);
686 if (server->dtsize > PAGE_CACHE_SIZE)
687 server->dtsize = PAGE_CACHE_SIZE;
688 if (server->dtsize > server->rsize)
689 server->dtsize = server->rsize;
690
691 if (server->flags & NFS_MOUNT_NOAC) {
692 server->acregmin = server->acregmax = 0;
693 server->acdirmin = server->acdirmax = 0;
694 }
695
696 server->maxfilesize = fsinfo->maxfilesize;
697
698 /* We're airborne Set socket buffersize */
699 rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100);
700}
701
702/*
703 * Probe filesystem information, including the FSID on v2/v3
704 */
705static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr)
706{
707 struct nfs_fsinfo fsinfo;
708 struct nfs_client *clp = server->nfs_client;
709 int error;
710
711 dprintk("--> nfs_probe_fsinfo()\n");
712
713 if (clp->rpc_ops->set_capabilities != NULL) {
714 error = clp->rpc_ops->set_capabilities(server, mntfh);
715 if (error < 0)
716 goto out_error;
717 }
718
719 fsinfo.fattr = fattr;
720 nfs_fattr_init(fattr);
721 error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo);
722 if (error < 0)
723 goto out_error;
724
725 nfs_server_set_fsinfo(server, &fsinfo);
Peter Zijlstrae0bf68d2007-10-16 23:25:46 -0700726 error = bdi_init(&server->backing_dev_info);
727 if (error)
728 goto out_error;
729
David Howells54ceac42006-08-22 20:06:13 -0400730
731 /* Get some general file system info */
732 if (server->namelen == 0) {
733 struct nfs_pathconf pathinfo;
734
735 pathinfo.fattr = fattr;
736 nfs_fattr_init(fattr);
737
738 if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0)
739 server->namelen = pathinfo.max_namelen;
740 }
741
742 dprintk("<-- nfs_probe_fsinfo() = 0\n");
743 return 0;
744
745out_error:
746 dprintk("nfs_probe_fsinfo: error = %d\n", -error);
747 return error;
748}
749
750/*
751 * Copy useful information when duplicating a server record
752 */
753static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source)
754{
755 target->flags = source->flags;
756 target->acregmin = source->acregmin;
757 target->acregmax = source->acregmax;
758 target->acdirmin = source->acdirmin;
759 target->acdirmax = source->acdirmax;
760 target->caps = source->caps;
761}
762
763/*
764 * Allocate and initialise a server record
765 */
766static struct nfs_server *nfs_alloc_server(void)
767{
768 struct nfs_server *server;
769
770 server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL);
771 if (!server)
772 return NULL;
773
774 server->client = server->client_acl = ERR_PTR(-EINVAL);
775
776 /* Zero out the NFS state stuff */
777 INIT_LIST_HEAD(&server->client_link);
778 INIT_LIST_HEAD(&server->master_link);
779
Steve Dicksonef818a22007-11-08 04:05:04 -0500780 init_waitqueue_head(&server->active_wq);
781 atomic_set(&server->active, 0);
782
David Howells54ceac42006-08-22 20:06:13 -0400783 server->io_stats = nfs_alloc_iostats();
784 if (!server->io_stats) {
785 kfree(server);
786 return NULL;
787 }
788
789 return server;
790}
791
792/*
793 * Free up a server record
794 */
795void nfs_free_server(struct nfs_server *server)
796{
797 dprintk("--> nfs_free_server()\n");
798
799 spin_lock(&nfs_client_lock);
800 list_del(&server->client_link);
801 list_del(&server->master_link);
802 spin_unlock(&nfs_client_lock);
803
804 if (server->destroy != NULL)
805 server->destroy(server);
Trond Myklebust5cef3382007-12-11 22:01:56 -0500806
807 if (!IS_ERR(server->client_acl))
808 rpc_shutdown_client(server->client_acl);
David Howells54ceac42006-08-22 20:06:13 -0400809 if (!IS_ERR(server->client))
810 rpc_shutdown_client(server->client);
811
812 nfs_put_client(server->nfs_client);
813
814 nfs_free_iostats(server->io_stats);
Peter Zijlstrae0bf68d2007-10-16 23:25:46 -0700815 bdi_destroy(&server->backing_dev_info);
David Howells54ceac42006-08-22 20:06:13 -0400816 kfree(server);
817 nfs_release_automount_timer();
818 dprintk("<-- nfs_free_server()\n");
819}
820
821/*
822 * Create a version 2 or 3 volume record
823 * - keyed on server and FSID
824 */
\"Talpey, Thomas\2283f8d2007-09-10 13:43:56 -0400825struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
David Howells54ceac42006-08-22 20:06:13 -0400826 struct nfs_fh *mntfh)
827{
828 struct nfs_server *server;
829 struct nfs_fattr fattr;
830 int error;
831
832 server = nfs_alloc_server();
833 if (!server)
834 return ERR_PTR(-ENOMEM);
835
836 /* Get a client representation */
837 error = nfs_init_server(server, data);
838 if (error < 0)
839 goto error;
840
841 BUG_ON(!server->nfs_client);
842 BUG_ON(!server->nfs_client->rpc_ops);
843 BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
844
845 /* Probe the root fh to retrieve its FSID */
846 error = nfs_probe_fsinfo(server, mntfh, &fattr);
847 if (error < 0)
848 goto error;
Trond Myklebust54af3bb2007-09-28 12:27:41 -0400849 if (server->nfs_client->rpc_ops->version == 3) {
850 if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
851 server->namelen = NFS3_MAXNAMLEN;
852 if (!(data->flags & NFS_MOUNT_NORDIRPLUS))
853 server->caps |= NFS_CAP_READDIRPLUS;
854 } else {
855 if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN)
856 server->namelen = NFS2_MAXNAMLEN;
857 }
858
David Howells54ceac42006-08-22 20:06:13 -0400859 if (!(fattr.valid & NFS_ATTR_FATTR)) {
860 error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr);
861 if (error < 0) {
862 dprintk("nfs_create_server: getattr error = %d\n", -error);
863 goto error;
864 }
865 }
866 memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid));
867
David Howells6daabf12006-08-24 15:44:16 -0400868 dprintk("Server FSID: %llx:%llx\n",
869 (unsigned long long) server->fsid.major,
870 (unsigned long long) server->fsid.minor);
David Howells54ceac42006-08-22 20:06:13 -0400871
872 BUG_ON(!server->nfs_client);
873 BUG_ON(!server->nfs_client->rpc_ops);
874 BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
875
876 spin_lock(&nfs_client_lock);
877 list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
878 list_add_tail(&server->master_link, &nfs_volume_list);
879 spin_unlock(&nfs_client_lock);
880
881 server->mount_time = jiffies;
882 return server;
883
884error:
885 nfs_free_server(server);
886 return ERR_PTR(error);
887}
888
889#ifdef CONFIG_NFS_V4
890/*
891 * Initialise an NFS4 client record
892 */
893static int nfs4_init_client(struct nfs_client *clp,
894 int proto, int timeo, int retrans,
J. Bruce Fields7d9ac062006-10-19 23:28:39 -0700895 const char *ip_addr,
David Howells54ceac42006-08-22 20:06:13 -0400896 rpc_authflavor_t authflavour)
897{
898 int error;
899
900 if (clp->cl_cons_state == NFS_CS_READY) {
901 /* the client is initialised already */
902 dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp);
903 return 0;
904 }
905
906 /* Check NFS protocol revision and initialize RPC op vector */
907 clp->rpc_ops = &nfs_v4_clientops;
908
Chuck Lever43d78ef2007-02-06 18:26:11 -0500909 error = nfs_create_rpc_client(clp, proto, timeo, retrans, authflavour,
910 RPC_CLNT_CREATE_DISCRTRY);
David Howells54ceac42006-08-22 20:06:13 -0400911 if (error < 0)
912 goto error;
J. Bruce Fields7d9ac062006-10-19 23:28:39 -0700913 memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
David Howells54ceac42006-08-22 20:06:13 -0400914
915 error = nfs_idmap_new(clp);
916 if (error < 0) {
917 dprintk("%s: failed to create idmapper. Error = %d\n",
918 __FUNCTION__, error);
David Howells54ceac42006-08-22 20:06:13 -0400919 goto error;
920 }
Trond Myklebust9c5bf382006-08-22 20:06:14 -0400921 __set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
David Howells54ceac42006-08-22 20:06:13 -0400922
923 nfs_mark_client_ready(clp, NFS_CS_READY);
924 return 0;
925
926error:
927 nfs_mark_client_ready(clp, error);
928 dprintk("<-- nfs4_init_client() = xerror %d\n", error);
929 return error;
930}
931
932/*
933 * Set up an NFS4 client
934 */
935static int nfs4_set_client(struct nfs_server *server,
936 const char *hostname, const struct sockaddr_in *addr,
J. Bruce Fields7d9ac062006-10-19 23:28:39 -0700937 const char *ip_addr,
David Howells54ceac42006-08-22 20:06:13 -0400938 rpc_authflavor_t authflavour,
939 int proto, int timeo, int retrans)
940{
Trond Myklebust3a498022007-12-14 14:56:04 -0500941 struct nfs_client_initdata cl_init = {
942 .hostname = hostname,
943 .addr = addr,
Chuck Lever6e4cffd2007-12-10 14:58:15 -0500944 .addrlen = sizeof(*addr),
Trond Myklebust40c553192007-12-14 14:56:07 -0500945 .rpc_ops = &nfs_v4_clientops,
Trond Myklebust3a498022007-12-14 14:56:04 -0500946 };
David Howells54ceac42006-08-22 20:06:13 -0400947 struct nfs_client *clp;
948 int error;
949
950 dprintk("--> nfs4_set_client()\n");
951
952 /* Allocate or find a client reference we can use */
Trond Myklebust3a498022007-12-14 14:56:04 -0500953 clp = nfs_get_client(&cl_init);
David Howells54ceac42006-08-22 20:06:13 -0400954 if (IS_ERR(clp)) {
955 error = PTR_ERR(clp);
956 goto error;
957 }
J. Bruce Fields7d9ac062006-10-19 23:28:39 -0700958 error = nfs4_init_client(clp, proto, timeo, retrans, ip_addr, authflavour);
David Howells54ceac42006-08-22 20:06:13 -0400959 if (error < 0)
960 goto error_put;
961
962 server->nfs_client = clp;
963 dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp);
964 return 0;
965
966error_put:
967 nfs_put_client(clp);
968error:
969 dprintk("<-- nfs4_set_client() = xerror %d\n", error);
970 return error;
971}
972
973/*
974 * Create a version 4 volume record
975 */
976static int nfs4_init_server(struct nfs_server *server,
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -0400977 const struct nfs_parsed_mount_data *data)
David Howells54ceac42006-08-22 20:06:13 -0400978{
979 int error;
980
981 dprintk("--> nfs4_init_server()\n");
982
983 /* Initialise the client representation from the mount data */
984 server->flags = data->flags & NFS_MOUNT_FLAGMASK;
985 server->caps |= NFS_CAP_ATOMIC_OPEN;
986
987 if (data->rsize)
988 server->rsize = nfs_block_size(data->rsize, NULL);
989 if (data->wsize)
990 server->wsize = nfs_block_size(data->wsize, NULL);
991
992 server->acregmin = data->acregmin * HZ;
993 server->acregmax = data->acregmax * HZ;
994 server->acdirmin = data->acdirmin * HZ;
995 server->acdirmax = data->acdirmax * HZ;
996
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -0400997 error = nfs_init_server_rpcclient(server, data->auth_flavors[0]);
David Howells54ceac42006-08-22 20:06:13 -0400998
999 /* Done */
1000 dprintk("<-- nfs4_init_server() = %d\n", error);
1001 return error;
1002}
1003
1004/*
1005 * Create a version 4 volume record
1006 * - keyed on server and FSID
1007 */
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001008struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
David Howells54ceac42006-08-22 20:06:13 -04001009 struct nfs_fh *mntfh)
1010{
1011 struct nfs_fattr fattr;
1012 struct nfs_server *server;
1013 int error;
1014
1015 dprintk("--> nfs4_create_server()\n");
1016
1017 server = nfs_alloc_server();
1018 if (!server)
1019 return ERR_PTR(-ENOMEM);
1020
1021 /* Get a client record */
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001022 error = nfs4_set_client(server,
1023 data->nfs_server.hostname,
1024 &data->nfs_server.address,
1025 data->client_address,
1026 data->auth_flavors[0],
1027 data->nfs_server.protocol,
1028 data->timeo, data->retrans);
David Howells54ceac42006-08-22 20:06:13 -04001029 if (error < 0)
1030 goto error;
1031
1032 /* set up the general RPC client */
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001033 error = nfs4_init_server(server, data);
David Howells54ceac42006-08-22 20:06:13 -04001034 if (error < 0)
1035 goto error;
1036
1037 BUG_ON(!server->nfs_client);
1038 BUG_ON(!server->nfs_client->rpc_ops);
1039 BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
1040
1041 /* Probe the root fh to retrieve its FSID */
\"Talpey, Thomas\91ea40b2007-09-10 13:44:33 -04001042 error = nfs4_path_walk(server, mntfh, data->nfs_server.export_path);
David Howells54ceac42006-08-22 20:06:13 -04001043 if (error < 0)
1044 goto error;
1045
David Howells6daabf12006-08-24 15:44:16 -04001046 dprintk("Server FSID: %llx:%llx\n",
1047 (unsigned long long) server->fsid.major,
1048 (unsigned long long) server->fsid.minor);
David Howells54ceac42006-08-22 20:06:13 -04001049 dprintk("Mount FH: %d\n", mntfh->size);
1050
1051 error = nfs_probe_fsinfo(server, mntfh, &fattr);
1052 if (error < 0)
1053 goto error;
1054
Trond Myklebust54af3bb2007-09-28 12:27:41 -04001055 if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
1056 server->namelen = NFS4_MAXNAMLEN;
1057
David Howells54ceac42006-08-22 20:06:13 -04001058 BUG_ON(!server->nfs_client);
1059 BUG_ON(!server->nfs_client->rpc_ops);
1060 BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
1061
1062 spin_lock(&nfs_client_lock);
1063 list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
1064 list_add_tail(&server->master_link, &nfs_volume_list);
1065 spin_unlock(&nfs_client_lock);
1066
1067 server->mount_time = jiffies;
1068 dprintk("<-- nfs4_create_server() = %p\n", server);
1069 return server;
1070
1071error:
1072 nfs_free_server(server);
1073 dprintk("<-- nfs4_create_server() = error %d\n", error);
1074 return ERR_PTR(error);
1075}
1076
1077/*
1078 * Create an NFS4 referral server record
1079 */
1080struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
Trond Myklebustf2d0d852007-02-02 14:46:09 -08001081 struct nfs_fh *mntfh)
David Howells54ceac42006-08-22 20:06:13 -04001082{
1083 struct nfs_client *parent_client;
1084 struct nfs_server *server, *parent_server;
1085 struct nfs_fattr fattr;
1086 int error;
1087
1088 dprintk("--> nfs4_create_referral_server()\n");
1089
1090 server = nfs_alloc_server();
1091 if (!server)
1092 return ERR_PTR(-ENOMEM);
1093
1094 parent_server = NFS_SB(data->sb);
1095 parent_client = parent_server->nfs_client;
1096
1097 /* Get a client representation.
1098 * Note: NFSv4 always uses TCP, */
1099 error = nfs4_set_client(server, data->hostname, data->addr,
J. Bruce Fields7d9ac062006-10-19 23:28:39 -07001100 parent_client->cl_ipaddr,
David Howells54ceac42006-08-22 20:06:13 -04001101 data->authflavor,
1102 parent_server->client->cl_xprt->prot,
1103 parent_client->retrans_timeo,
1104 parent_client->retrans_count);
andros@citi.umich.edu297de4f2006-08-29 12:19:41 -04001105 if (error < 0)
1106 goto error;
David Howells54ceac42006-08-22 20:06:13 -04001107
1108 /* Initialise the client representation from the parent server */
1109 nfs_server_copy_userdata(server, parent_server);
1110 server->caps |= NFS_CAP_ATOMIC_OPEN;
1111
1112 error = nfs_init_server_rpcclient(server, data->authflavor);
1113 if (error < 0)
1114 goto error;
1115
1116 BUG_ON(!server->nfs_client);
1117 BUG_ON(!server->nfs_client->rpc_ops);
1118 BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
1119
Trond Myklebustf2d0d852007-02-02 14:46:09 -08001120 /* Probe the root fh to retrieve its FSID and filehandle */
1121 error = nfs4_path_walk(server, mntfh, data->mnt_path);
1122 if (error < 0)
1123 goto error;
1124
David Howells54ceac42006-08-22 20:06:13 -04001125 /* probe the filesystem info for this server filesystem */
Trond Myklebustf2d0d852007-02-02 14:46:09 -08001126 error = nfs_probe_fsinfo(server, mntfh, &fattr);
David Howells54ceac42006-08-22 20:06:13 -04001127 if (error < 0)
1128 goto error;
1129
Trond Myklebust54af3bb2007-09-28 12:27:41 -04001130 if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
1131 server->namelen = NFS4_MAXNAMLEN;
1132
David Howells54ceac42006-08-22 20:06:13 -04001133 dprintk("Referral FSID: %llx:%llx\n",
David Howells6daabf12006-08-24 15:44:16 -04001134 (unsigned long long) server->fsid.major,
1135 (unsigned long long) server->fsid.minor);
David Howells54ceac42006-08-22 20:06:13 -04001136
1137 spin_lock(&nfs_client_lock);
1138 list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
1139 list_add_tail(&server->master_link, &nfs_volume_list);
1140 spin_unlock(&nfs_client_lock);
1141
1142 server->mount_time = jiffies;
1143
1144 dprintk("<-- nfs_create_referral_server() = %p\n", server);
1145 return server;
1146
1147error:
1148 nfs_free_server(server);
1149 dprintk("<-- nfs4_create_referral_server() = error %d\n", error);
1150 return ERR_PTR(error);
1151}
1152
1153#endif /* CONFIG_NFS_V4 */
1154
1155/*
1156 * Clone an NFS2, NFS3 or NFS4 server record
1157 */
1158struct nfs_server *nfs_clone_server(struct nfs_server *source,
1159 struct nfs_fh *fh,
1160 struct nfs_fattr *fattr)
1161{
1162 struct nfs_server *server;
1163 struct nfs_fattr fattr_fsinfo;
1164 int error;
1165
1166 dprintk("--> nfs_clone_server(,%llx:%llx,)\n",
David Howells6daabf12006-08-24 15:44:16 -04001167 (unsigned long long) fattr->fsid.major,
1168 (unsigned long long) fattr->fsid.minor);
David Howells54ceac42006-08-22 20:06:13 -04001169
1170 server = nfs_alloc_server();
1171 if (!server)
1172 return ERR_PTR(-ENOMEM);
1173
1174 /* Copy data from the source */
1175 server->nfs_client = source->nfs_client;
1176 atomic_inc(&server->nfs_client->cl_count);
1177 nfs_server_copy_userdata(server, source);
1178
1179 server->fsid = fattr->fsid;
1180
1181 error = nfs_init_server_rpcclient(server, source->client->cl_auth->au_flavor);
1182 if (error < 0)
1183 goto out_free_server;
1184 if (!IS_ERR(source->client_acl))
1185 nfs_init_server_aclclient(server);
1186
1187 /* probe the filesystem info for this server filesystem */
1188 error = nfs_probe_fsinfo(server, fh, &fattr_fsinfo);
1189 if (error < 0)
1190 goto out_free_server;
1191
Trond Myklebust54af3bb2007-09-28 12:27:41 -04001192 if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
1193 server->namelen = NFS4_MAXNAMLEN;
1194
David Howells54ceac42006-08-22 20:06:13 -04001195 dprintk("Cloned FSID: %llx:%llx\n",
David Howells6daabf12006-08-24 15:44:16 -04001196 (unsigned long long) server->fsid.major,
1197 (unsigned long long) server->fsid.minor);
David Howells54ceac42006-08-22 20:06:13 -04001198
1199 error = nfs_start_lockd(server);
1200 if (error < 0)
1201 goto out_free_server;
1202
1203 spin_lock(&nfs_client_lock);
1204 list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
1205 list_add_tail(&server->master_link, &nfs_volume_list);
1206 spin_unlock(&nfs_client_lock);
1207
1208 server->mount_time = jiffies;
1209
1210 dprintk("<-- nfs_clone_server() = %p\n", server);
1211 return server;
1212
1213out_free_server:
1214 nfs_free_server(server);
1215 dprintk("<-- nfs_clone_server() = error %d\n", error);
1216 return ERR_PTR(error);
1217}
David Howells6aaca562006-08-22 20:06:13 -04001218
1219#ifdef CONFIG_PROC_FS
1220static struct proc_dir_entry *proc_fs_nfs;
1221
1222static int nfs_server_list_open(struct inode *inode, struct file *file);
1223static void *nfs_server_list_start(struct seq_file *p, loff_t *pos);
1224static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos);
1225static void nfs_server_list_stop(struct seq_file *p, void *v);
1226static int nfs_server_list_show(struct seq_file *m, void *v);
1227
1228static struct seq_operations nfs_server_list_ops = {
1229 .start = nfs_server_list_start,
1230 .next = nfs_server_list_next,
1231 .stop = nfs_server_list_stop,
1232 .show = nfs_server_list_show,
1233};
1234
Arjan van de Ven00977a52007-02-12 00:55:34 -08001235static const struct file_operations nfs_server_list_fops = {
David Howells6aaca562006-08-22 20:06:13 -04001236 .open = nfs_server_list_open,
1237 .read = seq_read,
1238 .llseek = seq_lseek,
1239 .release = seq_release,
1240};
1241
1242static int nfs_volume_list_open(struct inode *inode, struct file *file);
1243static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos);
1244static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos);
1245static void nfs_volume_list_stop(struct seq_file *p, void *v);
1246static int nfs_volume_list_show(struct seq_file *m, void *v);
1247
1248static struct seq_operations nfs_volume_list_ops = {
1249 .start = nfs_volume_list_start,
1250 .next = nfs_volume_list_next,
1251 .stop = nfs_volume_list_stop,
1252 .show = nfs_volume_list_show,
1253};
1254
Arjan van de Ven00977a52007-02-12 00:55:34 -08001255static const struct file_operations nfs_volume_list_fops = {
David Howells6aaca562006-08-22 20:06:13 -04001256 .open = nfs_volume_list_open,
1257 .read = seq_read,
1258 .llseek = seq_lseek,
1259 .release = seq_release,
1260};
1261
1262/*
1263 * open "/proc/fs/nfsfs/servers" which provides a summary of servers with which
1264 * we're dealing
1265 */
1266static int nfs_server_list_open(struct inode *inode, struct file *file)
1267{
1268 struct seq_file *m;
1269 int ret;
1270
1271 ret = seq_open(file, &nfs_server_list_ops);
1272 if (ret < 0)
1273 return ret;
1274
1275 m = file->private_data;
1276 m->private = PDE(inode)->data;
1277
1278 return 0;
1279}
1280
1281/*
1282 * set up the iterator to start reading from the server list and return the first item
1283 */
1284static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
1285{
David Howells6aaca562006-08-22 20:06:13 -04001286 /* lock the list against modification */
1287 spin_lock(&nfs_client_lock);
Pavel Emelianov259902e2007-07-15 23:39:56 -07001288 return seq_list_start_head(&nfs_client_list, *_pos);
David Howells6aaca562006-08-22 20:06:13 -04001289}
1290
1291/*
1292 * move to next server
1293 */
1294static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
1295{
Pavel Emelianov259902e2007-07-15 23:39:56 -07001296 return seq_list_next(v, &nfs_client_list, pos);
David Howells6aaca562006-08-22 20:06:13 -04001297}
1298
1299/*
1300 * clean up after reading from the transports list
1301 */
1302static void nfs_server_list_stop(struct seq_file *p, void *v)
1303{
1304 spin_unlock(&nfs_client_lock);
1305}
1306
1307/*
1308 * display a header line followed by a load of call lines
1309 */
1310static int nfs_server_list_show(struct seq_file *m, void *v)
1311{
1312 struct nfs_client *clp;
1313
1314 /* display header on line 1 */
Pavel Emelianov259902e2007-07-15 23:39:56 -07001315 if (v == &nfs_client_list) {
David Howells6aaca562006-08-22 20:06:13 -04001316 seq_puts(m, "NV SERVER PORT USE HOSTNAME\n");
1317 return 0;
1318 }
1319
1320 /* display one transport per line on subsequent lines */
1321 clp = list_entry(v, struct nfs_client, cl_share_link);
1322
Chuck Lever5d8515c2007-12-10 14:57:16 -05001323 seq_printf(m, "v%u %s %s %3d %s\n",
Trond Myklebust40c553192007-12-14 14:56:07 -05001324 clp->rpc_ops->version,
Chuck Lever5d8515c2007-12-10 14:57:16 -05001325 rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR),
1326 rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT),
David Howells6aaca562006-08-22 20:06:13 -04001327 atomic_read(&clp->cl_count),
1328 clp->cl_hostname);
1329
1330 return 0;
1331}
1332
1333/*
1334 * open "/proc/fs/nfsfs/volumes" which provides a summary of extant volumes
1335 */
1336static int nfs_volume_list_open(struct inode *inode, struct file *file)
1337{
1338 struct seq_file *m;
1339 int ret;
1340
1341 ret = seq_open(file, &nfs_volume_list_ops);
1342 if (ret < 0)
1343 return ret;
1344
1345 m = file->private_data;
1346 m->private = PDE(inode)->data;
1347
1348 return 0;
1349}
1350
1351/*
1352 * set up the iterator to start reading from the volume list and return the first item
1353 */
1354static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
1355{
David Howells6aaca562006-08-22 20:06:13 -04001356 /* lock the list against modification */
1357 spin_lock(&nfs_client_lock);
Pavel Emelianov259902e2007-07-15 23:39:56 -07001358 return seq_list_start_head(&nfs_volume_list, *_pos);
David Howells6aaca562006-08-22 20:06:13 -04001359}
1360
1361/*
1362 * move to next volume
1363 */
1364static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)
1365{
Pavel Emelianov259902e2007-07-15 23:39:56 -07001366 return seq_list_next(v, &nfs_volume_list, pos);
David Howells6aaca562006-08-22 20:06:13 -04001367}
1368
1369/*
1370 * clean up after reading from the transports list
1371 */
1372static void nfs_volume_list_stop(struct seq_file *p, void *v)
1373{
1374 spin_unlock(&nfs_client_lock);
1375}
1376
1377/*
1378 * display a header line followed by a load of call lines
1379 */
1380static int nfs_volume_list_show(struct seq_file *m, void *v)
1381{
1382 struct nfs_server *server;
1383 struct nfs_client *clp;
1384 char dev[8], fsid[17];
1385
1386 /* display header on line 1 */
Pavel Emelianov259902e2007-07-15 23:39:56 -07001387 if (v == &nfs_volume_list) {
David Howells6aaca562006-08-22 20:06:13 -04001388 seq_puts(m, "NV SERVER PORT DEV FSID\n");
1389 return 0;
1390 }
1391 /* display one transport per line on subsequent lines */
1392 server = list_entry(v, struct nfs_server, master_link);
1393 clp = server->nfs_client;
1394
1395 snprintf(dev, 8, "%u:%u",
1396 MAJOR(server->s_dev), MINOR(server->s_dev));
1397
1398 snprintf(fsid, 17, "%llx:%llx",
David Howells6daabf12006-08-24 15:44:16 -04001399 (unsigned long long) server->fsid.major,
1400 (unsigned long long) server->fsid.minor);
David Howells6aaca562006-08-22 20:06:13 -04001401
Chuck Lever5d8515c2007-12-10 14:57:16 -05001402 seq_printf(m, "v%u %s %s %-7s %-17s\n",
Trond Myklebust40c553192007-12-14 14:56:07 -05001403 clp->rpc_ops->version,
Chuck Lever5d8515c2007-12-10 14:57:16 -05001404 rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR),
1405 rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT),
David Howells6aaca562006-08-22 20:06:13 -04001406 dev,
1407 fsid);
1408
1409 return 0;
1410}
1411
1412/*
1413 * initialise the /proc/fs/nfsfs/ directory
1414 */
1415int __init nfs_fs_proc_init(void)
1416{
1417 struct proc_dir_entry *p;
1418
1419 proc_fs_nfs = proc_mkdir("nfsfs", proc_root_fs);
1420 if (!proc_fs_nfs)
1421 goto error_0;
1422
1423 proc_fs_nfs->owner = THIS_MODULE;
1424
1425 /* a file of servers with which we're dealing */
1426 p = create_proc_entry("servers", S_IFREG|S_IRUGO, proc_fs_nfs);
1427 if (!p)
1428 goto error_1;
1429
1430 p->proc_fops = &nfs_server_list_fops;
1431 p->owner = THIS_MODULE;
1432
1433 /* a file of volumes that we have mounted */
1434 p = create_proc_entry("volumes", S_IFREG|S_IRUGO, proc_fs_nfs);
1435 if (!p)
1436 goto error_2;
1437
1438 p->proc_fops = &nfs_volume_list_fops;
1439 p->owner = THIS_MODULE;
1440 return 0;
1441
1442error_2:
1443 remove_proc_entry("servers", proc_fs_nfs);
1444error_1:
1445 remove_proc_entry("nfsfs", proc_root_fs);
1446error_0:
1447 return -ENOMEM;
1448}
1449
1450/*
1451 * clean up the /proc/fs/nfsfs/ directory
1452 */
1453void nfs_fs_proc_exit(void)
1454{
1455 remove_proc_entry("volumes", proc_fs_nfs);
1456 remove_proc_entry("servers", proc_fs_nfs);
1457 remove_proc_entry("nfsfs", proc_root_fs);
1458}
1459
1460#endif /* CONFIG_PROC_FS */