aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/client.c')
-rw-r--r--fs/nfs/client.c182
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
162error_3: 159error_cleanup:
163 if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
164 nfs_callback_down();
165error_2:
166 kfree(clp); 160 kfree(clp);
167error_0: 161error_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 */
181static 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 */
193static 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 */
187static void nfs_free_client(struct nfs_client *clp) 210static 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 */
502static void nfs_mark_client_ready(struct nfs_client *clp, int state) 526void 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 */
539int 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 */
511static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, 551static 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 */
1095static 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 */
1123static 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 */
1055static int nfs4_init_client(struct nfs_client *clp, 1150static 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
1089error: 1189error:
@@ -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 */
1246static 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 */
1262static 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 */
1143static int nfs4_init_server(struct nfs_server *server, 1275static 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