cb5e92463bdb087bb8d63fb53f1d254569e7ae0f
1 /* client.c: NFS client sharing and management code
3 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
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.
13 #include <linux/config.h>
14 #include <linux/module.h>
15 #include <linux/init.h>
17 #include <linux/time.h>
18 #include <linux/kernel.h>
20 #include <linux/string.h>
21 #include <linux/stat.h>
22 #include <linux/errno.h>
23 #include <linux/unistd.h>
24 #include <linux/sunrpc/clnt.h>
25 #include <linux/sunrpc/stats.h>
26 #include <linux/sunrpc/metrics.h>
27 #include <linux/nfs_fs.h>
28 #include <linux/nfs_mount.h>
29 #include <linux/nfs4_mount.h>
30 #include <linux/lockd/bind.h>
31 #include <linux/smp_lock.h>
32 #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>
37 #include <linux/nfs_xdr.h>
39 #include <asm/system.h>
43 #include "delegation.h"
47 #define NFSDBG_FACILITY NFSDBG_CLIENT
49 static DEFINE_SPINLOCK(nfs_client_lock
);
50 static LIST_HEAD(nfs_client_list
);
51 static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq
);
54 * Allocate a shared client record
56 * Since these are allocated/deallocated very rarely, we don't
57 * bother putting them in a slab cache...
59 static struct nfs_client
*nfs_alloc_client(const char *hostname
,
60 const struct sockaddr_in
*addr
,
63 struct nfs_client
*clp
;
66 if ((clp
= kzalloc(sizeof(*clp
), GFP_KERNEL
)) == NULL
)
71 dprintk("%s: couldn't start rpciod! Error = %d\n",
73 __set_bit(NFS_CS_RPCIOD
, &clp
->cl_res_state
);
77 if (nfsversion
== 4) {
78 if (nfs_callback_up() < 0)
80 __set_bit(NFS_CS_CALLBACK
, &clp
->cl_res_state
);
83 atomic_set(&clp
->cl_count
, 1);
84 clp
->cl_cons_state
= NFS_CS_INITING
;
86 clp
->cl_nfsversion
= nfsversion
;
87 memcpy(&clp
->cl_addr
, addr
, sizeof(clp
->cl_addr
));
90 clp
->cl_hostname
= kstrdup(hostname
, GFP_KERNEL
);
91 if (!clp
->cl_hostname
)
95 INIT_LIST_HEAD(&clp
->cl_superblocks
);
96 clp
->cl_rpcclient
= ERR_PTR(-EINVAL
);
99 init_rwsem(&clp
->cl_sem
);
100 INIT_LIST_HEAD(&clp
->cl_delegations
);
101 INIT_LIST_HEAD(&clp
->cl_state_owners
);
102 INIT_LIST_HEAD(&clp
->cl_unused
);
103 spin_lock_init(&clp
->cl_lock
);
104 INIT_WORK(&clp
->cl_renewd
, nfs4_renew_state
, clp
);
105 rpc_init_wait_queue(&clp
->cl_rpcwaitq
, "NFS client");
106 clp
->cl_boot_time
= CURRENT_TIME
;
107 clp
->cl_state
= 1 << NFS4CLNT_LEASE_EXPIRED
;
114 __clear_bit(NFS_CS_CALLBACK
, &clp
->cl_res_state
);
117 __clear_bit(NFS_CS_RPCIOD
, &clp
->cl_res_state
);
125 * Destroy a shared client record
127 static void nfs_free_client(struct nfs_client
*clp
)
129 dprintk("--> nfs_free_client(%d)\n", clp
->cl_nfsversion
);
132 if (__test_and_clear_bit(NFS_CS_IDMAP
, &clp
->cl_res_state
)) {
133 while (!list_empty(&clp
->cl_unused
)) {
134 struct nfs4_state_owner
*sp
;
136 sp
= list_entry(clp
->cl_unused
.next
,
137 struct nfs4_state_owner
,
139 list_del(&sp
->so_list
);
142 BUG_ON(!list_empty(&clp
->cl_state_owners
));
143 nfs_idmap_delete(clp
);
147 /* -EIO all pending I/O */
148 if (!IS_ERR(clp
->cl_rpcclient
))
149 rpc_shutdown_client(clp
->cl_rpcclient
);
151 if (__test_and_clear_bit(NFS_CS_CALLBACK
, &clp
->cl_res_state
))
154 if (__test_and_clear_bit(NFS_CS_RPCIOD
, &clp
->cl_res_state
))
157 kfree(clp
->cl_hostname
);
160 dprintk("<-- nfs_free_client()\n");
164 * Release a reference to a shared client record
166 void nfs_put_client(struct nfs_client
*clp
)
168 dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp
->cl_count
));
170 if (atomic_dec_and_lock(&clp
->cl_count
, &nfs_client_lock
)) {
171 list_del(&clp
->cl_share_link
);
172 spin_unlock(&nfs_client_lock
);
174 BUG_ON(!list_empty(&clp
->cl_superblocks
));
176 nfs_free_client(clp
);
181 * Find a client by address
182 * - caller must hold nfs_client_lock
184 static struct nfs_client
*__nfs_find_client(const struct sockaddr_in
*addr
, int nfsversion
)
186 struct nfs_client
*clp
;
188 list_for_each_entry(clp
, &nfs_client_list
, cl_share_link
) {
189 /* Different NFS versions cannot share the same nfs_client */
190 if (clp
->cl_nfsversion
!= nfsversion
)
193 if (memcmp(&clp
->cl_addr
.sin_addr
, &addr
->sin_addr
,
194 sizeof(clp
->cl_addr
.sin_addr
)) != 0)
197 if (clp
->cl_addr
.sin_port
== addr
->sin_port
)
204 atomic_inc(&clp
->cl_count
);
209 * Find a client by IP address and protocol version
210 * - returns NULL if no such client
212 struct nfs_client
*nfs_find_client(const struct sockaddr_in
*addr
, int nfsversion
)
214 struct nfs_client
*clp
;
216 spin_lock(&nfs_client_lock
);
217 clp
= __nfs_find_client(addr
, nfsversion
);
218 spin_unlock(&nfs_client_lock
);
220 BUG_ON(clp
->cl_cons_state
== 0);
226 * Look up a client by IP address and protocol version
227 * - creates a new record if one doesn't yet exist
229 struct nfs_client
*nfs_get_client(const char *hostname
,
230 const struct sockaddr_in
*addr
,
233 struct nfs_client
*clp
, *new = NULL
;
236 dprintk("--> nfs_get_client(%s,"NIPQUAD_FMT
":%d,%d)\n",
237 hostname
?: "", NIPQUAD(addr
->sin_addr
),
238 addr
->sin_port
, nfsversion
);
240 /* see if the client already exists */
242 spin_lock(&nfs_client_lock
);
244 clp
= __nfs_find_client(addr
, nfsversion
);
250 spin_unlock(&nfs_client_lock
);
252 new = nfs_alloc_client(hostname
, addr
, nfsversion
);
255 return ERR_PTR(-ENOMEM
);
257 /* install a new client and return with it unready */
260 list_add(&clp
->cl_share_link
, &nfs_client_list
);
261 spin_unlock(&nfs_client_lock
);
262 dprintk("--> nfs_get_client() = %p [new]\n", clp
);
265 /* found an existing client
266 * - make sure it's ready before returning
269 spin_unlock(&nfs_client_lock
);
272 nfs_free_client(new);
274 if (clp
->cl_cons_state
== NFS_CS_INITING
) {
275 DECLARE_WAITQUEUE(myself
, current
);
277 add_wait_queue(&nfs_client_active_wq
, &myself
);
280 set_current_state(TASK_INTERRUPTIBLE
);
281 if (signal_pending(current
) ||
282 clp
->cl_cons_state
> NFS_CS_READY
)
287 remove_wait_queue(&nfs_client_active_wq
, &myself
);
289 if (signal_pending(current
)) {
291 return ERR_PTR(-ERESTARTSYS
);
295 if (clp
->cl_cons_state
< NFS_CS_READY
) {
296 error
= clp
->cl_cons_state
;
298 return ERR_PTR(error
);
301 dprintk("--> nfs_get_client() = %p [share]\n", clp
);
306 * Mark a server as ready or failed
308 void nfs_mark_client_ready(struct nfs_client
*clp
, int state
)
310 clp
->cl_cons_state
= state
;
311 wake_up_all(&nfs_client_active_wq
);
This page took 0.036921 seconds and 5 git commands to generate.