diff options
Diffstat (limited to 'fs/nfs/client.c')
-rw-r--r-- | fs/nfs/client.c | 191 |
1 files changed, 166 insertions, 25 deletions
diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 75c9cd2aa119..c2d061675d80 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <linux/in6.h> | 37 | #include <linux/in6.h> |
38 | #include <net/ipv6.h> | 38 | #include <net/ipv6.h> |
39 | #include <linux/nfs_xdr.h> | 39 | #include <linux/nfs_xdr.h> |
40 | #include <linux/sunrpc/bc_xprt.h> | ||
40 | 41 | ||
41 | #include <asm/system.h> | 42 | #include <asm/system.h> |
42 | 43 | ||
@@ -102,6 +103,7 @@ struct nfs_client_initdata { | |||
102 | size_t addrlen; | 103 | size_t addrlen; |
103 | const struct nfs_rpc_ops *rpc_ops; | 104 | const struct nfs_rpc_ops *rpc_ops; |
104 | int proto; | 105 | int proto; |
106 | u32 minorversion; | ||
105 | }; | 107 | }; |
106 | 108 | ||
107 | /* | 109 | /* |
@@ -114,18 +116,13 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ | |||
114 | { | 116 | { |
115 | struct nfs_client *clp; | 117 | struct nfs_client *clp; |
116 | struct rpc_cred *cred; | 118 | struct rpc_cred *cred; |
119 | int err = -ENOMEM; | ||
117 | 120 | ||
118 | if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) | 121 | if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) |
119 | goto error_0; | 122 | goto error_0; |
120 | 123 | ||
121 | clp->rpc_ops = cl_init->rpc_ops; | 124 | clp->rpc_ops = cl_init->rpc_ops; |
122 | 125 | ||
123 | if (cl_init->rpc_ops->version == 4) { | ||
124 | if (nfs_callback_up() < 0) | ||
125 | goto error_2; | ||
126 | __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); | ||
127 | } | ||
128 | |||
129 | atomic_set(&clp->cl_count, 1); | 126 | atomic_set(&clp->cl_count, 1); |
130 | clp->cl_cons_state = NFS_CS_INITING; | 127 | clp->cl_cons_state = NFS_CS_INITING; |
131 | 128 | ||
@@ -133,9 +130,10 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ | |||
133 | clp->cl_addrlen = cl_init->addrlen; | 130 | clp->cl_addrlen = cl_init->addrlen; |
134 | 131 | ||
135 | if (cl_init->hostname) { | 132 | if (cl_init->hostname) { |
133 | err = -ENOMEM; | ||
136 | clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); | 134 | clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); |
137 | if (!clp->cl_hostname) | 135 | if (!clp->cl_hostname) |
138 | goto error_3; | 136 | goto error_cleanup; |
139 | } | 137 | } |
140 | 138 | ||
141 | INIT_LIST_HEAD(&clp->cl_superblocks); | 139 | INIT_LIST_HEAD(&clp->cl_superblocks); |
@@ -150,6 +148,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ | |||
150 | rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); | 148 | rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); |
151 | clp->cl_boot_time = CURRENT_TIME; | 149 | clp->cl_boot_time = CURRENT_TIME; |
152 | clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; | 150 | clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; |
151 | clp->cl_minorversion = cl_init->minorversion; | ||
153 | #endif | 152 | #endif |
154 | cred = rpc_lookup_machine_cred(); | 153 | cred = rpc_lookup_machine_cred(); |
155 | if (!IS_ERR(cred)) | 154 | if (!IS_ERR(cred)) |
@@ -159,13 +158,10 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ | |||
159 | 158 | ||
160 | return clp; | 159 | return clp; |
161 | 160 | ||
162 | error_3: | 161 | error_cleanup: |
163 | if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) | ||
164 | nfs_callback_down(); | ||
165 | error_2: | ||
166 | kfree(clp); | 162 | kfree(clp); |
167 | error_0: | 163 | error_0: |
168 | return NULL; | 164 | return ERR_PTR(err); |
169 | } | 165 | } |
170 | 166 | ||
171 | static void nfs4_shutdown_client(struct nfs_client *clp) | 167 | static void nfs4_shutdown_client(struct nfs_client *clp) |
@@ -182,12 +178,42 @@ static void nfs4_shutdown_client(struct nfs_client *clp) | |||
182 | } | 178 | } |
183 | 179 | ||
184 | /* | 180 | /* |
181 | * Destroy the NFS4 callback service | ||
182 | */ | ||
183 | static void nfs4_destroy_callback(struct nfs_client *clp) | ||
184 | { | ||
185 | #ifdef CONFIG_NFS_V4 | ||
186 | if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) | ||
187 | nfs_callback_down(clp->cl_minorversion); | ||
188 | #endif /* CONFIG_NFS_V4 */ | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * Clears/puts all minor version specific parts from an nfs_client struct | ||
193 | * reverting it to minorversion 0. | ||
194 | */ | ||
195 | static void nfs4_clear_client_minor_version(struct nfs_client *clp) | ||
196 | { | ||
197 | #ifdef CONFIG_NFS_V4_1 | ||
198 | if (nfs4_has_session(clp)) { | ||
199 | nfs4_destroy_session(clp->cl_session); | ||
200 | clp->cl_session = NULL; | ||
201 | } | ||
202 | |||
203 | clp->cl_call_sync = _nfs4_call_sync; | ||
204 | #endif /* CONFIG_NFS_V4_1 */ | ||
205 | |||
206 | nfs4_destroy_callback(clp); | ||
207 | } | ||
208 | |||
209 | /* | ||
185 | * Destroy a shared client record | 210 | * Destroy a shared client record |
186 | */ | 211 | */ |
187 | static void nfs_free_client(struct nfs_client *clp) | 212 | static void nfs_free_client(struct nfs_client *clp) |
188 | { | 213 | { |
189 | dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); | 214 | dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); |
190 | 215 | ||
216 | nfs4_clear_client_minor_version(clp); | ||
191 | nfs4_shutdown_client(clp); | 217 | nfs4_shutdown_client(clp); |
192 | 218 | ||
193 | nfs_fscache_release_client_cookie(clp); | 219 | nfs_fscache_release_client_cookie(clp); |
@@ -196,9 +222,6 @@ static void nfs_free_client(struct nfs_client *clp) | |||
196 | if (!IS_ERR(clp->cl_rpcclient)) | 222 | if (!IS_ERR(clp->cl_rpcclient)) |
197 | rpc_shutdown_client(clp->cl_rpcclient); | 223 | rpc_shutdown_client(clp->cl_rpcclient); |
198 | 224 | ||
199 | if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) | ||
200 | nfs_callback_down(); | ||
201 | |||
202 | if (clp->cl_machine_cred != NULL) | 225 | if (clp->cl_machine_cred != NULL) |
203 | put_rpccred(clp->cl_machine_cred); | 226 | put_rpccred(clp->cl_machine_cred); |
204 | 227 | ||
@@ -347,7 +370,8 @@ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion) | |||
347 | struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; | 370 | struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; |
348 | 371 | ||
349 | /* Don't match clients that failed to initialise properly */ | 372 | /* Don't match clients that failed to initialise properly */ |
350 | if (clp->cl_cons_state != NFS_CS_READY) | 373 | if (!(clp->cl_cons_state == NFS_CS_READY || |
374 | clp->cl_cons_state == NFS_CS_SESSION_INITING)) | ||
351 | continue; | 375 | continue; |
352 | 376 | ||
353 | /* Different NFS versions cannot share the same nfs_client */ | 377 | /* Different NFS versions cannot share the same nfs_client */ |
@@ -420,7 +444,9 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat | |||
420 | 444 | ||
421 | if (clp->cl_proto != data->proto) | 445 | if (clp->cl_proto != data->proto) |
422 | continue; | 446 | continue; |
423 | 447 | /* Match nfsv4 minorversion */ | |
448 | if (clp->cl_minorversion != data->minorversion) | ||
449 | continue; | ||
424 | /* Match the full socket address */ | 450 | /* Match the full socket address */ |
425 | if (!nfs_sockaddr_cmp(sap, clap)) | 451 | if (!nfs_sockaddr_cmp(sap, clap)) |
426 | continue; | 452 | continue; |
@@ -456,9 +482,10 @@ static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_in | |||
456 | spin_unlock(&nfs_client_lock); | 482 | spin_unlock(&nfs_client_lock); |
457 | 483 | ||
458 | new = nfs_alloc_client(cl_init); | 484 | new = nfs_alloc_client(cl_init); |
459 | } while (new); | 485 | } while (!IS_ERR(new)); |
460 | 486 | ||
461 | return ERR_PTR(-ENOMEM); | 487 | dprintk("--> nfs_get_client() = %ld [failed]\n", PTR_ERR(new)); |
488 | return new; | ||
462 | 489 | ||
463 | /* install a new client and return with it unready */ | 490 | /* install a new client and return with it unready */ |
464 | install_client: | 491 | install_client: |
@@ -478,7 +505,7 @@ found_client: | |||
478 | nfs_free_client(new); | 505 | nfs_free_client(new); |
479 | 506 | ||
480 | error = wait_event_killable(nfs_client_active_wq, | 507 | error = wait_event_killable(nfs_client_active_wq, |
481 | clp->cl_cons_state != NFS_CS_INITING); | 508 | clp->cl_cons_state < NFS_CS_INITING); |
482 | if (error < 0) { | 509 | if (error < 0) { |
483 | nfs_put_client(clp); | 510 | nfs_put_client(clp); |
484 | return ERR_PTR(-ERESTARTSYS); | 511 | return ERR_PTR(-ERESTARTSYS); |
@@ -499,13 +526,29 @@ found_client: | |||
499 | /* | 526 | /* |
500 | * Mark a server as ready or failed | 527 | * Mark a server as ready or failed |
501 | */ | 528 | */ |
502 | static void nfs_mark_client_ready(struct nfs_client *clp, int state) | 529 | void nfs_mark_client_ready(struct nfs_client *clp, int state) |
503 | { | 530 | { |
504 | clp->cl_cons_state = state; | 531 | clp->cl_cons_state = state; |
505 | wake_up_all(&nfs_client_active_wq); | 532 | wake_up_all(&nfs_client_active_wq); |
506 | } | 533 | } |
507 | 534 | ||
508 | /* | 535 | /* |
536 | * With sessions, the client is not marked ready until after a | ||
537 | * successful EXCHANGE_ID and CREATE_SESSION. | ||
538 | * | ||
539 | * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate | ||
540 | * other versions of NFS can be tried. | ||
541 | */ | ||
542 | int nfs4_check_client_ready(struct nfs_client *clp) | ||
543 | { | ||
544 | if (!nfs4_has_session(clp)) | ||
545 | return 0; | ||
546 | if (clp->cl_cons_state < NFS_CS_READY) | ||
547 | return -EPROTONOSUPPORT; | ||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | /* | ||
509 | * Initialise the timeout values for a connection | 552 | * Initialise the timeout values for a connection |
510 | */ | 553 | */ |
511 | static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, | 554 | static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, |
@@ -1050,6 +1093,61 @@ error: | |||
1050 | 1093 | ||
1051 | #ifdef CONFIG_NFS_V4 | 1094 | #ifdef CONFIG_NFS_V4 |
1052 | /* | 1095 | /* |
1096 | * Initialize the NFS4 callback service | ||
1097 | */ | ||
1098 | static int nfs4_init_callback(struct nfs_client *clp) | ||
1099 | { | ||
1100 | int error; | ||
1101 | |||
1102 | if (clp->rpc_ops->version == 4) { | ||
1103 | if (nfs4_has_session(clp)) { | ||
1104 | error = xprt_setup_backchannel( | ||
1105 | clp->cl_rpcclient->cl_xprt, | ||
1106 | NFS41_BC_MIN_CALLBACKS); | ||
1107 | if (error < 0) | ||
1108 | return error; | ||
1109 | } | ||
1110 | |||
1111 | error = nfs_callback_up(clp->cl_minorversion, | ||
1112 | clp->cl_rpcclient->cl_xprt); | ||
1113 | if (error < 0) { | ||
1114 | dprintk("%s: failed to start callback. Error = %d\n", | ||
1115 | __func__, error); | ||
1116 | return error; | ||
1117 | } | ||
1118 | __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); | ||
1119 | } | ||
1120 | return 0; | ||
1121 | } | ||
1122 | |||
1123 | /* | ||
1124 | * Initialize the minor version specific parts of an NFS4 client record | ||
1125 | */ | ||
1126 | static int nfs4_init_client_minor_version(struct nfs_client *clp) | ||
1127 | { | ||
1128 | clp->cl_call_sync = _nfs4_call_sync; | ||
1129 | |||
1130 | #if defined(CONFIG_NFS_V4_1) | ||
1131 | if (clp->cl_minorversion) { | ||
1132 | struct nfs4_session *session = NULL; | ||
1133 | /* | ||
1134 | * Create the session and mark it expired. | ||
1135 | * When a SEQUENCE operation encounters the expired session | ||
1136 | * it will do session recovery to initialize it. | ||
1137 | */ | ||
1138 | session = nfs4_alloc_session(clp); | ||
1139 | if (!session) | ||
1140 | return -ENOMEM; | ||
1141 | |||
1142 | clp->cl_session = session; | ||
1143 | clp->cl_call_sync = _nfs4_call_sync_session; | ||
1144 | } | ||
1145 | #endif /* CONFIG_NFS_V4_1 */ | ||
1146 | |||
1147 | return nfs4_init_callback(clp); | ||
1148 | } | ||
1149 | |||
1150 | /* | ||
1053 | * Initialise an NFS4 client record | 1151 | * Initialise an NFS4 client record |
1054 | */ | 1152 | */ |
1055 | static int nfs4_init_client(struct nfs_client *clp, | 1153 | static int nfs4_init_client(struct nfs_client *clp, |
@@ -1083,7 +1181,12 @@ static int nfs4_init_client(struct nfs_client *clp, | |||
1083 | } | 1181 | } |
1084 | __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); | 1182 | __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); |
1085 | 1183 | ||
1086 | nfs_mark_client_ready(clp, NFS_CS_READY); | 1184 | error = nfs4_init_client_minor_version(clp); |
1185 | if (error < 0) | ||
1186 | goto error; | ||
1187 | |||
1188 | if (!nfs4_has_session(clp)) | ||
1189 | nfs_mark_client_ready(clp, NFS_CS_READY); | ||
1087 | return 0; | 1190 | return 0; |
1088 | 1191 | ||
1089 | error: | 1192 | error: |
@@ -1101,7 +1204,8 @@ static int nfs4_set_client(struct nfs_server *server, | |||
1101 | const size_t addrlen, | 1204 | const size_t addrlen, |
1102 | const char *ip_addr, | 1205 | const char *ip_addr, |
1103 | rpc_authflavor_t authflavour, | 1206 | rpc_authflavor_t authflavour, |
1104 | int proto, const struct rpc_timeout *timeparms) | 1207 | int proto, const struct rpc_timeout *timeparms, |
1208 | u32 minorversion) | ||
1105 | { | 1209 | { |
1106 | struct nfs_client_initdata cl_init = { | 1210 | struct nfs_client_initdata cl_init = { |
1107 | .hostname = hostname, | 1211 | .hostname = hostname, |
@@ -1109,6 +1213,7 @@ static int nfs4_set_client(struct nfs_server *server, | |||
1109 | .addrlen = addrlen, | 1213 | .addrlen = addrlen, |
1110 | .rpc_ops = &nfs_v4_clientops, | 1214 | .rpc_ops = &nfs_v4_clientops, |
1111 | .proto = proto, | 1215 | .proto = proto, |
1216 | .minorversion = minorversion, | ||
1112 | }; | 1217 | }; |
1113 | struct nfs_client *clp; | 1218 | struct nfs_client *clp; |
1114 | int error; | 1219 | int error; |
@@ -1138,6 +1243,36 @@ error: | |||
1138 | } | 1243 | } |
1139 | 1244 | ||
1140 | /* | 1245 | /* |
1246 | * Initialize a session. | ||
1247 | * Note: save the mount rsize and wsize for create_server negotiation. | ||
1248 | */ | ||
1249 | static void nfs4_init_session(struct nfs_client *clp, | ||
1250 | unsigned int wsize, unsigned int rsize) | ||
1251 | { | ||
1252 | #if defined(CONFIG_NFS_V4_1) | ||
1253 | if (nfs4_has_session(clp)) { | ||
1254 | clp->cl_session->fc_attrs.max_rqst_sz = wsize; | ||
1255 | clp->cl_session->fc_attrs.max_resp_sz = rsize; | ||
1256 | } | ||
1257 | #endif /* CONFIG_NFS_V4_1 */ | ||
1258 | } | ||
1259 | |||
1260 | /* | ||
1261 | * Session has been established, and the client marked ready. | ||
1262 | * Set the mount rsize and wsize with negotiated fore channel | ||
1263 | * attributes which will be bound checked in nfs_server_set_fsinfo. | ||
1264 | */ | ||
1265 | static void nfs4_session_set_rwsize(struct nfs_server *server) | ||
1266 | { | ||
1267 | #ifdef CONFIG_NFS_V4_1 | ||
1268 | if (!nfs4_has_session(server->nfs_client)) | ||
1269 | return; | ||
1270 | server->rsize = server->nfs_client->cl_session->fc_attrs.max_resp_sz; | ||
1271 | server->wsize = server->nfs_client->cl_session->fc_attrs.max_rqst_sz; | ||
1272 | #endif /* CONFIG_NFS_V4_1 */ | ||
1273 | } | ||
1274 | |||
1275 | /* | ||
1141 | * Create a version 4 volume record | 1276 | * Create a version 4 volume record |
1142 | */ | 1277 | */ |
1143 | static int nfs4_init_server(struct nfs_server *server, | 1278 | static int nfs4_init_server(struct nfs_server *server, |
@@ -1164,7 +1299,8 @@ static int nfs4_init_server(struct nfs_server *server, | |||
1164 | data->client_address, | 1299 | data->client_address, |
1165 | data->auth_flavors[0], | 1300 | data->auth_flavors[0], |
1166 | data->nfs_server.protocol, | 1301 | data->nfs_server.protocol, |
1167 | &timeparms); | 1302 | &timeparms, |
1303 | data->minorversion); | ||
1168 | if (error < 0) | 1304 | if (error < 0) |
1169 | goto error; | 1305 | goto error; |
1170 | 1306 | ||
@@ -1214,6 +1350,8 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, | |||
1214 | BUG_ON(!server->nfs_client->rpc_ops); | 1350 | BUG_ON(!server->nfs_client->rpc_ops); |
1215 | BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); | 1351 | BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); |
1216 | 1352 | ||
1353 | nfs4_init_session(server->nfs_client, server->wsize, server->rsize); | ||
1354 | |||
1217 | /* Probe the root fh to retrieve its FSID */ | 1355 | /* Probe the root fh to retrieve its FSID */ |
1218 | error = nfs4_path_walk(server, mntfh, data->nfs_server.export_path); | 1356 | error = nfs4_path_walk(server, mntfh, data->nfs_server.export_path); |
1219 | if (error < 0) | 1357 | if (error < 0) |
@@ -1224,6 +1362,8 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, | |||
1224 | (unsigned long long) server->fsid.minor); | 1362 | (unsigned long long) server->fsid.minor); |
1225 | dprintk("Mount FH: %d\n", mntfh->size); | 1363 | dprintk("Mount FH: %d\n", mntfh->size); |
1226 | 1364 | ||
1365 | nfs4_session_set_rwsize(server); | ||
1366 | |||
1227 | error = nfs_probe_fsinfo(server, mntfh, &fattr); | 1367 | error = nfs_probe_fsinfo(server, mntfh, &fattr); |
1228 | if (error < 0) | 1368 | if (error < 0) |
1229 | goto error; | 1369 | goto error; |
@@ -1282,7 +1422,8 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, | |||
1282 | parent_client->cl_ipaddr, | 1422 | parent_client->cl_ipaddr, |
1283 | data->authflavor, | 1423 | data->authflavor, |
1284 | parent_server->client->cl_xprt->prot, | 1424 | parent_server->client->cl_xprt->prot, |
1285 | parent_server->client->cl_timeout); | 1425 | parent_server->client->cl_timeout, |
1426 | parent_client->cl_minorversion); | ||
1286 | if (error < 0) | 1427 | if (error < 0) |
1287 | goto error; | 1428 | goto error; |
1288 | 1429 | ||