diff options
author | David S. Miller <davem@davemloft.net> | 2011-01-24 16:17:06 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-01-24 16:17:06 -0500 |
commit | e92427b289d252cfbd4cb5282d92f4ce1a5bb1fb (patch) | |
tree | 6d30e5e7b7f8e9aaa51d43b7128ac56860fa03bb /fs/nfs/client.c | |
parent | c506653d35249bb4738bb139c24362e1ae724bc1 (diff) | |
parent | ec30f343d61391ab23705e50a525da1d55395780 (diff) |
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'fs/nfs/client.c')
-rw-r--r-- | fs/nfs/client.c | 302 |
1 files changed, 210 insertions, 92 deletions
diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 0870d0d4efc0..192f2f860265 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c | |||
@@ -56,6 +56,30 @@ static DEFINE_SPINLOCK(nfs_client_lock); | |||
56 | static LIST_HEAD(nfs_client_list); | 56 | static LIST_HEAD(nfs_client_list); |
57 | static LIST_HEAD(nfs_volume_list); | 57 | static LIST_HEAD(nfs_volume_list); |
58 | static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); | 58 | static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); |
59 | #ifdef CONFIG_NFS_V4 | ||
60 | static DEFINE_IDR(cb_ident_idr); /* Protected by nfs_client_lock */ | ||
61 | |||
62 | /* | ||
63 | * Get a unique NFSv4.0 callback identifier which will be used | ||
64 | * by the V4.0 callback service to lookup the nfs_client struct | ||
65 | */ | ||
66 | static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion) | ||
67 | { | ||
68 | int ret = 0; | ||
69 | |||
70 | if (clp->rpc_ops->version != 4 || minorversion != 0) | ||
71 | return ret; | ||
72 | retry: | ||
73 | if (!idr_pre_get(&cb_ident_idr, GFP_KERNEL)) | ||
74 | return -ENOMEM; | ||
75 | spin_lock(&nfs_client_lock); | ||
76 | ret = idr_get_new(&cb_ident_idr, clp, &clp->cl_cb_ident); | ||
77 | spin_unlock(&nfs_client_lock); | ||
78 | if (ret == -EAGAIN) | ||
79 | goto retry; | ||
80 | return ret; | ||
81 | } | ||
82 | #endif /* CONFIG_NFS_V4 */ | ||
59 | 83 | ||
60 | /* | 84 | /* |
61 | * RPC cruft for NFS | 85 | * RPC cruft for NFS |
@@ -144,7 +168,10 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ | |||
144 | clp->cl_proto = cl_init->proto; | 168 | clp->cl_proto = cl_init->proto; |
145 | 169 | ||
146 | #ifdef CONFIG_NFS_V4 | 170 | #ifdef CONFIG_NFS_V4 |
147 | INIT_LIST_HEAD(&clp->cl_delegations); | 171 | err = nfs_get_cb_ident_idr(clp, cl_init->minorversion); |
172 | if (err) | ||
173 | goto error_cleanup; | ||
174 | |||
148 | spin_lock_init(&clp->cl_lock); | 175 | spin_lock_init(&clp->cl_lock); |
149 | INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); | 176 | INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); |
150 | rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); | 177 | rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); |
@@ -170,21 +197,17 @@ error_0: | |||
170 | } | 197 | } |
171 | 198 | ||
172 | #ifdef CONFIG_NFS_V4 | 199 | #ifdef CONFIG_NFS_V4 |
173 | /* | ||
174 | * Clears/puts all minor version specific parts from an nfs_client struct | ||
175 | * reverting it to minorversion 0. | ||
176 | */ | ||
177 | static void nfs4_clear_client_minor_version(struct nfs_client *clp) | ||
178 | { | ||
179 | #ifdef CONFIG_NFS_V4_1 | 200 | #ifdef CONFIG_NFS_V4_1 |
180 | if (nfs4_has_session(clp)) { | 201 | static void nfs4_shutdown_session(struct nfs_client *clp) |
202 | { | ||
203 | if (nfs4_has_session(clp)) | ||
181 | nfs4_destroy_session(clp->cl_session); | 204 | nfs4_destroy_session(clp->cl_session); |
182 | clp->cl_session = NULL; | ||
183 | } | ||
184 | |||
185 | clp->cl_mvops = nfs_v4_minor_ops[0]; | ||
186 | #endif /* CONFIG_NFS_V4_1 */ | ||
187 | } | 205 | } |
206 | #else /* CONFIG_NFS_V4_1 */ | ||
207 | static void nfs4_shutdown_session(struct nfs_client *clp) | ||
208 | { | ||
209 | } | ||
210 | #endif /* CONFIG_NFS_V4_1 */ | ||
188 | 211 | ||
189 | /* | 212 | /* |
190 | * Destroy the NFS4 callback service | 213 | * Destroy the NFS4 callback service |
@@ -199,17 +222,49 @@ static void nfs4_shutdown_client(struct nfs_client *clp) | |||
199 | { | 222 | { |
200 | if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) | 223 | if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) |
201 | nfs4_kill_renewd(clp); | 224 | nfs4_kill_renewd(clp); |
202 | nfs4_clear_client_minor_version(clp); | 225 | nfs4_shutdown_session(clp); |
203 | nfs4_destroy_callback(clp); | 226 | nfs4_destroy_callback(clp); |
204 | if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) | 227 | if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) |
205 | nfs_idmap_delete(clp); | 228 | nfs_idmap_delete(clp); |
206 | 229 | ||
207 | rpc_destroy_wait_queue(&clp->cl_rpcwaitq); | 230 | rpc_destroy_wait_queue(&clp->cl_rpcwaitq); |
208 | } | 231 | } |
232 | |||
233 | /* idr_remove_all is not needed as all id's are removed by nfs_put_client */ | ||
234 | void nfs_cleanup_cb_ident_idr(void) | ||
235 | { | ||
236 | idr_destroy(&cb_ident_idr); | ||
237 | } | ||
238 | |||
239 | /* nfs_client_lock held */ | ||
240 | static void nfs_cb_idr_remove_locked(struct nfs_client *clp) | ||
241 | { | ||
242 | if (clp->cl_cb_ident) | ||
243 | idr_remove(&cb_ident_idr, clp->cl_cb_ident); | ||
244 | } | ||
245 | |||
246 | static void pnfs_init_server(struct nfs_server *server) | ||
247 | { | ||
248 | rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC"); | ||
249 | } | ||
250 | |||
209 | #else | 251 | #else |
210 | static void nfs4_shutdown_client(struct nfs_client *clp) | 252 | static void nfs4_shutdown_client(struct nfs_client *clp) |
211 | { | 253 | { |
212 | } | 254 | } |
255 | |||
256 | void nfs_cleanup_cb_ident_idr(void) | ||
257 | { | ||
258 | } | ||
259 | |||
260 | static void nfs_cb_idr_remove_locked(struct nfs_client *clp) | ||
261 | { | ||
262 | } | ||
263 | |||
264 | static void pnfs_init_server(struct nfs_server *server) | ||
265 | { | ||
266 | } | ||
267 | |||
213 | #endif /* CONFIG_NFS_V4 */ | 268 | #endif /* CONFIG_NFS_V4 */ |
214 | 269 | ||
215 | /* | 270 | /* |
@@ -248,6 +303,7 @@ void nfs_put_client(struct nfs_client *clp) | |||
248 | 303 | ||
249 | if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) { | 304 | if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) { |
250 | list_del(&clp->cl_share_link); | 305 | list_del(&clp->cl_share_link); |
306 | nfs_cb_idr_remove_locked(clp); | ||
251 | spin_unlock(&nfs_client_lock); | 307 | spin_unlock(&nfs_client_lock); |
252 | 308 | ||
253 | BUG_ON(!list_empty(&clp->cl_superblocks)); | 309 | BUG_ON(!list_empty(&clp->cl_superblocks)); |
@@ -363,70 +419,28 @@ static int nfs_sockaddr_cmp(const struct sockaddr *sa1, | |||
363 | return 0; | 419 | return 0; |
364 | } | 420 | } |
365 | 421 | ||
366 | /* | 422 | /* Common match routine for v4.0 and v4.1 callback services */ |
367 | * Find a client by IP address and protocol version | 423 | bool |
368 | * - returns NULL if no such client | 424 | nfs4_cb_match_client(const struct sockaddr *addr, struct nfs_client *clp, |
369 | */ | 425 | u32 minorversion) |
370 | struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion) | ||
371 | { | ||
372 | struct nfs_client *clp; | ||
373 | |||
374 | spin_lock(&nfs_client_lock); | ||
375 | list_for_each_entry(clp, &nfs_client_list, cl_share_link) { | ||
376 | struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; | ||
377 | |||
378 | /* Don't match clients that failed to initialise properly */ | ||
379 | if (!(clp->cl_cons_state == NFS_CS_READY || | ||
380 | clp->cl_cons_state == NFS_CS_SESSION_INITING)) | ||
381 | continue; | ||
382 | |||
383 | /* Different NFS versions cannot share the same nfs_client */ | ||
384 | if (clp->rpc_ops->version != nfsversion) | ||
385 | continue; | ||
386 | |||
387 | /* Match only the IP address, not the port number */ | ||
388 | if (!nfs_sockaddr_match_ipaddr(addr, clap)) | ||
389 | continue; | ||
390 | |||
391 | atomic_inc(&clp->cl_count); | ||
392 | spin_unlock(&nfs_client_lock); | ||
393 | return clp; | ||
394 | } | ||
395 | spin_unlock(&nfs_client_lock); | ||
396 | return NULL; | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * Find a client by IP address and protocol version | ||
401 | * - returns NULL if no such client | ||
402 | */ | ||
403 | struct nfs_client *nfs_find_client_next(struct nfs_client *clp) | ||
404 | { | 426 | { |
405 | struct sockaddr *sap = (struct sockaddr *)&clp->cl_addr; | 427 | struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; |
406 | u32 nfsvers = clp->rpc_ops->version; | ||
407 | 428 | ||
408 | spin_lock(&nfs_client_lock); | 429 | /* Don't match clients that failed to initialise */ |
409 | list_for_each_entry_continue(clp, &nfs_client_list, cl_share_link) { | 430 | if (!(clp->cl_cons_state == NFS_CS_READY || |
410 | struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; | 431 | clp->cl_cons_state == NFS_CS_SESSION_INITING)) |
432 | return false; | ||
411 | 433 | ||
412 | /* Don't match clients that failed to initialise properly */ | 434 | /* Match the version and minorversion */ |
413 | if (clp->cl_cons_state != NFS_CS_READY) | 435 | if (clp->rpc_ops->version != 4 || |
414 | continue; | 436 | clp->cl_minorversion != minorversion) |
437 | return false; | ||
415 | 438 | ||
416 | /* Different NFS versions cannot share the same nfs_client */ | 439 | /* Match only the IP address, not the port number */ |
417 | if (clp->rpc_ops->version != nfsvers) | 440 | if (!nfs_sockaddr_match_ipaddr(addr, clap)) |
418 | continue; | 441 | return false; |
419 | 442 | ||
420 | /* Match only the IP address, not the port number */ | 443 | return true; |
421 | if (!nfs_sockaddr_match_ipaddr(sap, clap)) | ||
422 | continue; | ||
423 | |||
424 | atomic_inc(&clp->cl_count); | ||
425 | spin_unlock(&nfs_client_lock); | ||
426 | return clp; | ||
427 | } | ||
428 | spin_unlock(&nfs_client_lock); | ||
429 | return NULL; | ||
430 | } | 444 | } |
431 | 445 | ||
432 | /* | 446 | /* |
@@ -988,6 +1002,27 @@ static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_serve | |||
988 | target->options = source->options; | 1002 | target->options = source->options; |
989 | } | 1003 | } |
990 | 1004 | ||
1005 | static void nfs_server_insert_lists(struct nfs_server *server) | ||
1006 | { | ||
1007 | struct nfs_client *clp = server->nfs_client; | ||
1008 | |||
1009 | spin_lock(&nfs_client_lock); | ||
1010 | list_add_tail_rcu(&server->client_link, &clp->cl_superblocks); | ||
1011 | list_add_tail(&server->master_link, &nfs_volume_list); | ||
1012 | spin_unlock(&nfs_client_lock); | ||
1013 | |||
1014 | } | ||
1015 | |||
1016 | static void nfs_server_remove_lists(struct nfs_server *server) | ||
1017 | { | ||
1018 | spin_lock(&nfs_client_lock); | ||
1019 | list_del_rcu(&server->client_link); | ||
1020 | list_del(&server->master_link); | ||
1021 | spin_unlock(&nfs_client_lock); | ||
1022 | |||
1023 | synchronize_rcu(); | ||
1024 | } | ||
1025 | |||
991 | /* | 1026 | /* |
992 | * Allocate and initialise a server record | 1027 | * Allocate and initialise a server record |
993 | */ | 1028 | */ |
@@ -1004,6 +1039,7 @@ static struct nfs_server *nfs_alloc_server(void) | |||
1004 | /* Zero out the NFS state stuff */ | 1039 | /* Zero out the NFS state stuff */ |
1005 | INIT_LIST_HEAD(&server->client_link); | 1040 | INIT_LIST_HEAD(&server->client_link); |
1006 | INIT_LIST_HEAD(&server->master_link); | 1041 | INIT_LIST_HEAD(&server->master_link); |
1042 | INIT_LIST_HEAD(&server->delegations); | ||
1007 | 1043 | ||
1008 | atomic_set(&server->active, 0); | 1044 | atomic_set(&server->active, 0); |
1009 | 1045 | ||
@@ -1019,6 +1055,8 @@ static struct nfs_server *nfs_alloc_server(void) | |||
1019 | return NULL; | 1055 | return NULL; |
1020 | } | 1056 | } |
1021 | 1057 | ||
1058 | pnfs_init_server(server); | ||
1059 | |||
1022 | return server; | 1060 | return server; |
1023 | } | 1061 | } |
1024 | 1062 | ||
@@ -1029,11 +1067,8 @@ void nfs_free_server(struct nfs_server *server) | |||
1029 | { | 1067 | { |
1030 | dprintk("--> nfs_free_server()\n"); | 1068 | dprintk("--> nfs_free_server()\n"); |
1031 | 1069 | ||
1070 | nfs_server_remove_lists(server); | ||
1032 | unset_pnfs_layoutdriver(server); | 1071 | unset_pnfs_layoutdriver(server); |
1033 | spin_lock(&nfs_client_lock); | ||
1034 | list_del(&server->client_link); | ||
1035 | list_del(&server->master_link); | ||
1036 | spin_unlock(&nfs_client_lock); | ||
1037 | 1072 | ||
1038 | if (server->destroy != NULL) | 1073 | if (server->destroy != NULL) |
1039 | server->destroy(server); | 1074 | server->destroy(server); |
@@ -1108,11 +1143,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, | |||
1108 | (unsigned long long) server->fsid.major, | 1143 | (unsigned long long) server->fsid.major, |
1109 | (unsigned long long) server->fsid.minor); | 1144 | (unsigned long long) server->fsid.minor); |
1110 | 1145 | ||
1111 | spin_lock(&nfs_client_lock); | 1146 | nfs_server_insert_lists(server); |
1112 | list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); | ||
1113 | list_add_tail(&server->master_link, &nfs_volume_list); | ||
1114 | spin_unlock(&nfs_client_lock); | ||
1115 | |||
1116 | server->mount_time = jiffies; | 1147 | server->mount_time = jiffies; |
1117 | nfs_free_fattr(fattr); | 1148 | nfs_free_fattr(fattr); |
1118 | return server; | 1149 | return server; |
@@ -1125,6 +1156,101 @@ error: | |||
1125 | 1156 | ||
1126 | #ifdef CONFIG_NFS_V4 | 1157 | #ifdef CONFIG_NFS_V4 |
1127 | /* | 1158 | /* |
1159 | * NFSv4.0 callback thread helper | ||
1160 | * | ||
1161 | * Find a client by IP address, protocol version, and minorversion | ||
1162 | * | ||
1163 | * Called from the pg_authenticate method. The callback identifier | ||
1164 | * is not used as it has not been decoded. | ||
1165 | * | ||
1166 | * Returns NULL if no such client | ||
1167 | */ | ||
1168 | struct nfs_client * | ||
1169 | nfs4_find_client_no_ident(const struct sockaddr *addr) | ||
1170 | { | ||
1171 | struct nfs_client *clp; | ||
1172 | |||
1173 | spin_lock(&nfs_client_lock); | ||
1174 | list_for_each_entry(clp, &nfs_client_list, cl_share_link) { | ||
1175 | if (nfs4_cb_match_client(addr, clp, 0) == false) | ||
1176 | continue; | ||
1177 | atomic_inc(&clp->cl_count); | ||
1178 | spin_unlock(&nfs_client_lock); | ||
1179 | return clp; | ||
1180 | } | ||
1181 | spin_unlock(&nfs_client_lock); | ||
1182 | return NULL; | ||
1183 | } | ||
1184 | |||
1185 | /* | ||
1186 | * NFSv4.0 callback thread helper | ||
1187 | * | ||
1188 | * Find a client by callback identifier | ||
1189 | */ | ||
1190 | struct nfs_client * | ||
1191 | nfs4_find_client_ident(int cb_ident) | ||
1192 | { | ||
1193 | struct nfs_client *clp; | ||
1194 | |||
1195 | spin_lock(&nfs_client_lock); | ||
1196 | clp = idr_find(&cb_ident_idr, cb_ident); | ||
1197 | if (clp) | ||
1198 | atomic_inc(&clp->cl_count); | ||
1199 | spin_unlock(&nfs_client_lock); | ||
1200 | return clp; | ||
1201 | } | ||
1202 | |||
1203 | #if defined(CONFIG_NFS_V4_1) | ||
1204 | /* | ||
1205 | * NFSv4.1 callback thread helper | ||
1206 | * For CB_COMPOUND calls, find a client by IP address, protocol version, | ||
1207 | * minorversion, and sessionID | ||
1208 | * | ||
1209 | * CREATE_SESSION triggers a CB_NULL ping from servers. The callback service | ||
1210 | * sessionid can only be set after the CREATE_SESSION return, so a CB_NULL | ||
1211 | * can arrive before the callback sessionid is set. For CB_NULL calls, | ||
1212 | * find a client by IP address protocol version, and minorversion. | ||
1213 | * | ||
1214 | * Returns NULL if no such client | ||
1215 | */ | ||
1216 | struct nfs_client * | ||
1217 | nfs4_find_client_sessionid(const struct sockaddr *addr, | ||
1218 | struct nfs4_sessionid *sid, int is_cb_compound) | ||
1219 | { | ||
1220 | struct nfs_client *clp; | ||
1221 | |||
1222 | spin_lock(&nfs_client_lock); | ||
1223 | list_for_each_entry(clp, &nfs_client_list, cl_share_link) { | ||
1224 | if (nfs4_cb_match_client(addr, clp, 1) == false) | ||
1225 | continue; | ||
1226 | |||
1227 | if (!nfs4_has_session(clp)) | ||
1228 | continue; | ||
1229 | |||
1230 | /* Match sessionid unless cb_null call*/ | ||
1231 | if (is_cb_compound && (memcmp(clp->cl_session->sess_id.data, | ||
1232 | sid->data, NFS4_MAX_SESSIONID_LEN) != 0)) | ||
1233 | continue; | ||
1234 | |||
1235 | atomic_inc(&clp->cl_count); | ||
1236 | spin_unlock(&nfs_client_lock); | ||
1237 | return clp; | ||
1238 | } | ||
1239 | spin_unlock(&nfs_client_lock); | ||
1240 | return NULL; | ||
1241 | } | ||
1242 | |||
1243 | #else /* CONFIG_NFS_V4_1 */ | ||
1244 | |||
1245 | struct nfs_client * | ||
1246 | nfs4_find_client_sessionid(const struct sockaddr *addr, | ||
1247 | struct nfs4_sessionid *sid, int is_cb_compound) | ||
1248 | { | ||
1249 | return NULL; | ||
1250 | } | ||
1251 | #endif /* CONFIG_NFS_V4_1 */ | ||
1252 | |||
1253 | /* | ||
1128 | * Initialize the NFS4 callback service | 1254 | * Initialize the NFS4 callback service |
1129 | */ | 1255 | */ |
1130 | static int nfs4_init_callback(struct nfs_client *clp) | 1256 | static int nfs4_init_callback(struct nfs_client *clp) |
@@ -1342,11 +1468,7 @@ static int nfs4_server_common_setup(struct nfs_server *server, | |||
1342 | if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) | 1468 | if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) |
1343 | server->namelen = NFS4_MAXNAMLEN; | 1469 | server->namelen = NFS4_MAXNAMLEN; |
1344 | 1470 | ||
1345 | spin_lock(&nfs_client_lock); | 1471 | nfs_server_insert_lists(server); |
1346 | list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); | ||
1347 | list_add_tail(&server->master_link, &nfs_volume_list); | ||
1348 | spin_unlock(&nfs_client_lock); | ||
1349 | |||
1350 | server->mount_time = jiffies; | 1472 | server->mount_time = jiffies; |
1351 | out: | 1473 | out: |
1352 | nfs_free_fattr(fattr); | 1474 | nfs_free_fattr(fattr); |
@@ -1551,11 +1673,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source, | |||
1551 | if (error < 0) | 1673 | if (error < 0) |
1552 | goto out_free_server; | 1674 | goto out_free_server; |
1553 | 1675 | ||
1554 | spin_lock(&nfs_client_lock); | 1676 | nfs_server_insert_lists(server); |
1555 | list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); | ||
1556 | list_add_tail(&server->master_link, &nfs_volume_list); | ||
1557 | spin_unlock(&nfs_client_lock); | ||
1558 | |||
1559 | server->mount_time = jiffies; | 1677 | server->mount_time = jiffies; |
1560 | 1678 | ||
1561 | nfs_free_fattr(fattr_fsinfo); | 1679 | nfs_free_fattr(fattr_fsinfo); |