aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/client.c
diff options
context:
space:
mode:
authorAndy Adamson <andros@netapp.com>2011-01-05 21:04:32 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2011-01-06 14:46:24 -0500
commitc36fca52f5e4594ffd0ff175b328966b0d393184 (patch)
tree6d771744cc49f0edc0d2b6b2f9fe919163002346 /fs/nfs/client.c
parent2c2618c6f29c41a0a966f14f05c8bf45fcabb750 (diff)
NFS refactor nfs_find_client and reference client across callback processing
Fixes a bug where the nfs_client could be freed during callback processing. Refactor nfs_find_client to use minorversion specific means to locate the correct nfs_client structure. In the NFS layer, V4.0 clients are found using the callback_ident field in the CB_COMPOUND header. V4.1 clients are found using the sessionID in the CB_SEQUENCE operation which is also compared against the sessionID associated with the back channel thread after a successful CREATE_SESSION. Each of these methods finds the one an only nfs_client associated with the incoming callback request - so nfs_find_client_next is not needed. In the RPC layer, the pg_authenticate call needs to find the nfs_client. For the v4.0 callback service, the callback identifier has not been decoded so a search by address, version, and minorversion is used. The sessionid for the sessions based callback service has (usually) not been set for the pg_authenticate on a CB_NULL call which can be sent prior to the return of a CREATE_SESSION call, so the sessionid associated with the back channel thread is not used to find the client in pg_authenticate for CB_NULL calls. Pass the referenced nfs_client to each CB_COMPOUND operation being proceesed via the new cb_process_state structure. The reference is held across cb_compound processing. Use the new cb_process_state struct to move the NFS4ERR_RETRY_UNCACHED_REP processing from process_op into nfs4_callback_sequence where it belongs. Signed-off-by: Andy Adamson <andros@netapp.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/client.c')
-rw-r--r--fs/nfs/client.c171
1 files changed, 112 insertions, 59 deletions
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index bc3a8620e8c3..11eb9934c747 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -410,70 +410,28 @@ static int nfs_sockaddr_cmp(const struct sockaddr *sa1,
410 return 0; 410 return 0;
411} 411}
412 412
413/* 413/* Common match routine for v4.0 and v4.1 callback services */
414 * Find a client by IP address and protocol version 414bool
415 * - returns NULL if no such client 415nfs4_cb_match_client(const struct sockaddr *addr, struct nfs_client *clp,
416 */ 416 u32 minorversion)
417struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion)
418{ 417{
419 struct nfs_client *clp; 418 struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
420 419
421 spin_lock(&nfs_client_lock); 420 /* Don't match clients that failed to initialise */
422 list_for_each_entry(clp, &nfs_client_list, cl_share_link) { 421 if (!(clp->cl_cons_state == NFS_CS_READY ||
423 struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 422 clp->cl_cons_state == NFS_CS_SESSION_INITING))
423 return false;
424 424
425 /* Don't match clients that failed to initialise properly */ 425 /* Match the version and minorversion */
426 if (!(clp->cl_cons_state == NFS_CS_READY || 426 if (clp->rpc_ops->version != 4 ||
427 clp->cl_cons_state == NFS_CS_SESSION_INITING)) 427 clp->cl_minorversion != minorversion)
428 continue; 428 return false;
429 429
430 /* Different NFS versions cannot share the same nfs_client */ 430 /* Match only the IP address, not the port number */
431 if (clp->rpc_ops->version != nfsversion) 431 if (!nfs_sockaddr_match_ipaddr(addr, clap))
432 continue; 432 return false;
433
434 /* Match only the IP address, not the port number */
435 if (!nfs_sockaddr_match_ipaddr(addr, clap))
436 continue;
437 433
438 atomic_inc(&clp->cl_count); 434 return true;
439 spin_unlock(&nfs_client_lock);
440 return clp;
441 }
442 spin_unlock(&nfs_client_lock);
443 return NULL;
444}
445
446/*
447 * Find a client by IP address and protocol version
448 * - returns NULL if no such client
449 */
450struct nfs_client *nfs_find_client_next(struct nfs_client *clp)
451{
452 struct sockaddr *sap = (struct sockaddr *)&clp->cl_addr;
453 u32 nfsvers = clp->rpc_ops->version;
454
455 spin_lock(&nfs_client_lock);
456 list_for_each_entry_continue(clp, &nfs_client_list, cl_share_link) {
457 struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
458
459 /* Don't match clients that failed to initialise properly */
460 if (clp->cl_cons_state != NFS_CS_READY)
461 continue;
462
463 /* Different NFS versions cannot share the same nfs_client */
464 if (clp->rpc_ops->version != nfsvers)
465 continue;
466
467 /* Match only the IP address, not the port number */
468 if (!nfs_sockaddr_match_ipaddr(sap, clap))
469 continue;
470
471 atomic_inc(&clp->cl_count);
472 spin_unlock(&nfs_client_lock);
473 return clp;
474 }
475 spin_unlock(&nfs_client_lock);
476 return NULL;
477} 435}
478 436
479/* 437/*
@@ -1172,6 +1130,101 @@ error:
1172 1130
1173#ifdef CONFIG_NFS_V4 1131#ifdef CONFIG_NFS_V4
1174/* 1132/*
1133 * NFSv4.0 callback thread helper
1134 *
1135 * Find a client by IP address, protocol version, and minorversion
1136 *
1137 * Called from the pg_authenticate method. The callback identifier
1138 * is not used as it has not been decoded.
1139 *
1140 * Returns NULL if no such client
1141 */
1142struct nfs_client *
1143nfs4_find_client_no_ident(const struct sockaddr *addr)
1144{
1145 struct nfs_client *clp;
1146
1147 spin_lock(&nfs_client_lock);
1148 list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
1149 if (nfs4_cb_match_client(addr, clp, 0) == false)
1150 continue;
1151 atomic_inc(&clp->cl_count);
1152 spin_unlock(&nfs_client_lock);
1153 return clp;
1154 }
1155 spin_unlock(&nfs_client_lock);
1156 return NULL;
1157}
1158
1159/*
1160 * NFSv4.0 callback thread helper
1161 *
1162 * Find a client by callback identifier
1163 */
1164struct nfs_client *
1165nfs4_find_client_ident(int cb_ident)
1166{
1167 struct nfs_client *clp;
1168
1169 spin_lock(&nfs_client_lock);
1170 clp = idr_find(&cb_ident_idr, cb_ident);
1171 if (clp)
1172 atomic_inc(&clp->cl_count);
1173 spin_unlock(&nfs_client_lock);
1174 return clp;
1175}
1176
1177#if defined(CONFIG_NFS_V4_1)
1178/*
1179 * NFSv4.1 callback thread helper
1180 * For CB_COMPOUND calls, find a client by IP address, protocol version,
1181 * minorversion, and sessionID
1182 *
1183 * CREATE_SESSION triggers a CB_NULL ping from servers. The callback service
1184 * sessionid can only be set after the CREATE_SESSION return, so a CB_NULL
1185 * can arrive before the callback sessionid is set. For CB_NULL calls,
1186 * find a client by IP address protocol version, and minorversion.
1187 *
1188 * Returns NULL if no such client
1189 */
1190struct nfs_client *
1191nfs4_find_client_sessionid(const struct sockaddr *addr,
1192 struct nfs4_sessionid *sid, int is_cb_compound)
1193{
1194 struct nfs_client *clp;
1195
1196 spin_lock(&nfs_client_lock);
1197 list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
1198 if (nfs4_cb_match_client(addr, clp, 1) == false)
1199 continue;
1200
1201 if (!nfs4_has_session(clp))
1202 continue;
1203
1204 /* Match sessionid unless cb_null call*/
1205 if (is_cb_compound && (memcmp(clp->cl_session->sess_id.data,
1206 sid->data, NFS4_MAX_SESSIONID_LEN) != 0))
1207 continue;
1208
1209 atomic_inc(&clp->cl_count);
1210 spin_unlock(&nfs_client_lock);
1211 return clp;
1212 }
1213 spin_unlock(&nfs_client_lock);
1214 return NULL;
1215}
1216
1217#else /* CONFIG_NFS_V4_1 */
1218
1219struct nfs_client *
1220nfs4_find_client_sessionid(const struct sockaddr *addr,
1221 struct nfs4_sessionid *sid, int is_cb_compound)
1222{
1223 return NULL;
1224}
1225#endif /* CONFIG_NFS_V4_1 */
1226
1227/*
1175 * Initialize the NFS4 callback service 1228 * Initialize the NFS4 callback service
1176 */ 1229 */
1177static int nfs4_init_callback(struct nfs_client *clp) 1230static int nfs4_init_callback(struct nfs_client *clp)