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