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.c191
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
162error_3: 161error_cleanup:
163 if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
164 nfs_callback_down();
165error_2:
166 kfree(clp); 162 kfree(clp);
167error_0: 163error_0:
168 return NULL; 164 return ERR_PTR(err);
169} 165}
170 166
171static void nfs4_shutdown_client(struct nfs_client *clp) 167static 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 */
183static 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 */
195static 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 */
187static void nfs_free_client(struct nfs_client *clp) 212static 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 */
464install_client: 491install_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 */
502static void nfs_mark_client_ready(struct nfs_client *clp, int state) 529void 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 */
542int 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 */
511static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, 554static 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 */
1098static 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 */
1126static 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 */
1055static int nfs4_init_client(struct nfs_client *clp, 1153static 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
1089error: 1192error:
@@ -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 */
1249static 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 */
1265static 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 */
1143static int nfs4_init_server(struct nfs_server *server, 1278static 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