diff options
author | Andy Adamson <andros@netapp.com> | 2011-01-05 21:04:32 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-01-06 14:46:24 -0500 |
commit | c36fca52f5e4594ffd0ff175b328966b0d393184 (patch) | |
tree | 6d771744cc49f0edc0d2b6b2f9fe919163002346 /fs/nfs/client.c | |
parent | 2c2618c6f29c41a0a966f14f05c8bf45fcabb750 (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.c | 171 |
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 | 414 | bool |
415 | * - returns NULL if no such client | 415 | nfs4_cb_match_client(const struct sockaddr *addr, struct nfs_client *clp, |
416 | */ | 416 | u32 minorversion) |
417 | struct 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 | */ | ||
450 | struct 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 | */ | ||
1142 | struct nfs_client * | ||
1143 | nfs4_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 | */ | ||
1164 | struct nfs_client * | ||
1165 | nfs4_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 | */ | ||
1190 | struct nfs_client * | ||
1191 | nfs4_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 | |||
1219 | struct nfs_client * | ||
1220 | nfs4_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 | */ |
1177 | static int nfs4_init_callback(struct nfs_client *clp) | 1230 | static int nfs4_init_callback(struct nfs_client *clp) |