diff options
-rw-r--r-- | fs/nfsd/nfs4state.c | 188 | ||||
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 148 | ||||
-rw-r--r-- | include/linux/nfsd/state.h | 7 | ||||
-rw-r--r-- | include/linux/nfsd/xdr4.h | 21 |
4 files changed, 358 insertions, 6 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index f25a7d2a9fac..463ae39df148 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -68,6 +68,7 @@ static u32 current_delegid = 1; | |||
68 | static u32 nfs4_init; | 68 | static u32 nfs4_init; |
69 | static stateid_t zerostateid; /* bits all 0 */ | 69 | static stateid_t zerostateid; /* bits all 0 */ |
70 | static stateid_t onestateid; /* bits all 1 */ | 70 | static stateid_t onestateid; /* bits all 1 */ |
71 | static u64 current_sessionid = 1; | ||
71 | 72 | ||
72 | #define ZERO_STATEID(stateid) (!memcmp((stateid), &zerostateid, sizeof(stateid_t))) | 73 | #define ZERO_STATEID(stateid) (!memcmp((stateid), &zerostateid, sizeof(stateid_t))) |
73 | #define ONE_STATEID(stateid) (!memcmp((stateid), &onestateid, sizeof(stateid_t))) | 74 | #define ONE_STATEID(stateid) (!memcmp((stateid), &onestateid, sizeof(stateid_t))) |
@@ -401,6 +402,131 @@ dump_sessionid(const char *fn, struct nfs4_sessionid *sessionid) | |||
401 | dprintk("%s: %u:%u:%u:%u\n", fn, ptr[0], ptr[1], ptr[2], ptr[3]); | 402 | dprintk("%s: %u:%u:%u:%u\n", fn, ptr[0], ptr[1], ptr[2], ptr[3]); |
402 | } | 403 | } |
403 | 404 | ||
405 | static void | ||
406 | gen_sessionid(struct nfsd4_session *ses) | ||
407 | { | ||
408 | struct nfs4_client *clp = ses->se_client; | ||
409 | struct nfsd4_sessionid *sid; | ||
410 | |||
411 | sid = (struct nfsd4_sessionid *)ses->se_sessionid.data; | ||
412 | sid->clientid = clp->cl_clientid; | ||
413 | sid->sequence = current_sessionid++; | ||
414 | sid->reserved = 0; | ||
415 | } | ||
416 | |||
417 | /* | ||
418 | * Give the client the number of slots it requests bound by | ||
419 | * NFSD_MAX_SLOTS_PER_SESSION and by sv_drc_max_pages. | ||
420 | * | ||
421 | * If we run out of pages (sv_drc_pages_used == sv_drc_max_pages) we | ||
422 | * should (up to a point) re-negotiate active sessions and reduce their | ||
423 | * slot usage to make rooom for new connections. For now we just fail the | ||
424 | * create session. | ||
425 | */ | ||
426 | static int set_forechannel_maxreqs(struct nfsd4_channel_attrs *fchan) | ||
427 | { | ||
428 | int status = 0, np = fchan->maxreqs * NFSD_PAGES_PER_SLOT; | ||
429 | |||
430 | spin_lock(&nfsd_serv->sv_lock); | ||
431 | if (np + nfsd_serv->sv_drc_pages_used > nfsd_serv->sv_drc_max_pages) | ||
432 | np = nfsd_serv->sv_drc_max_pages - nfsd_serv->sv_drc_pages_used; | ||
433 | nfsd_serv->sv_drc_pages_used += np; | ||
434 | spin_unlock(&nfsd_serv->sv_lock); | ||
435 | |||
436 | if (np <= 0) { | ||
437 | status = nfserr_resource; | ||
438 | fchan->maxreqs = 0; | ||
439 | } else | ||
440 | fchan->maxreqs = np / NFSD_PAGES_PER_SLOT; | ||
441 | |||
442 | return status; | ||
443 | } | ||
444 | |||
445 | /* | ||
446 | * fchan holds the client values on input, and the server values on output | ||
447 | */ | ||
448 | static int init_forechannel_attrs(struct svc_rqst *rqstp, | ||
449 | struct nfsd4_session *session, | ||
450 | struct nfsd4_channel_attrs *fchan) | ||
451 | { | ||
452 | int status = 0; | ||
453 | __u32 maxcount = svc_max_payload(rqstp); | ||
454 | |||
455 | /* headerpadsz set to zero in encode routine */ | ||
456 | |||
457 | /* Use the client's max request and max response size if possible */ | ||
458 | if (fchan->maxreq_sz > maxcount) | ||
459 | fchan->maxreq_sz = maxcount; | ||
460 | session->se_fmaxreq_sz = fchan->maxreq_sz; | ||
461 | |||
462 | if (fchan->maxresp_sz > maxcount) | ||
463 | fchan->maxresp_sz = maxcount; | ||
464 | session->se_fmaxresp_sz = fchan->maxresp_sz; | ||
465 | |||
466 | /* Set the max response cached size our default which is | ||
467 | * a multiple of PAGE_SIZE and small */ | ||
468 | session->se_fmaxresp_cached = NFSD_PAGES_PER_SLOT * PAGE_SIZE; | ||
469 | fchan->maxresp_cached = session->se_fmaxresp_cached; | ||
470 | |||
471 | /* Use the client's maxops if possible */ | ||
472 | if (fchan->maxops > NFSD_MAX_OPS_PER_COMPOUND) | ||
473 | fchan->maxops = NFSD_MAX_OPS_PER_COMPOUND; | ||
474 | session->se_fmaxops = fchan->maxops; | ||
475 | |||
476 | /* try to use the client requested number of slots */ | ||
477 | if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION) | ||
478 | fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION; | ||
479 | |||
480 | /* FIXME: Error means no more DRC pages so the server should | ||
481 | * recover pages from existing sessions. For now fail session | ||
482 | * creation. | ||
483 | */ | ||
484 | status = set_forechannel_maxreqs(fchan); | ||
485 | |||
486 | session->se_fnumslots = fchan->maxreqs; | ||
487 | return status; | ||
488 | } | ||
489 | |||
490 | static int | ||
491 | alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, | ||
492 | struct nfsd4_create_session *cses) | ||
493 | { | ||
494 | struct nfsd4_session *new, tmp; | ||
495 | int idx, status = nfserr_resource, slotsize; | ||
496 | |||
497 | memset(&tmp, 0, sizeof(tmp)); | ||
498 | |||
499 | /* FIXME: For now, we just accept the client back channel attributes. */ | ||
500 | status = init_forechannel_attrs(rqstp, &tmp, &cses->fore_channel); | ||
501 | if (status) | ||
502 | goto out; | ||
503 | |||
504 | /* allocate struct nfsd4_session and slot table in one piece */ | ||
505 | slotsize = tmp.se_fnumslots * sizeof(struct nfsd4_slot); | ||
506 | new = kzalloc(sizeof(*new) + slotsize, GFP_KERNEL); | ||
507 | if (!new) | ||
508 | goto out; | ||
509 | |||
510 | memcpy(new, &tmp, sizeof(*new)); | ||
511 | |||
512 | new->se_client = clp; | ||
513 | gen_sessionid(new); | ||
514 | idx = hash_sessionid(&new->se_sessionid); | ||
515 | memcpy(clp->cl_sessionid.data, new->se_sessionid.data, | ||
516 | NFS4_MAX_SESSIONID_LEN); | ||
517 | |||
518 | new->se_flags = cses->flags; | ||
519 | kref_init(&new->se_ref); | ||
520 | spin_lock(&sessionid_lock); | ||
521 | list_add(&new->se_hash, &sessionid_hashtbl[idx]); | ||
522 | list_add(&new->se_perclnt, &clp->cl_sessions); | ||
523 | spin_unlock(&sessionid_lock); | ||
524 | |||
525 | status = nfs_ok; | ||
526 | out: | ||
527 | return status; | ||
528 | } | ||
529 | |||
404 | /* caller must hold sessionid_lock */ | 530 | /* caller must hold sessionid_lock */ |
405 | static struct nfsd4_session * | 531 | static struct nfsd4_session * |
406 | find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid) | 532 | find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid) |
@@ -1182,7 +1308,67 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
1182 | struct nfsd4_compound_state *cstate, | 1308 | struct nfsd4_compound_state *cstate, |
1183 | struct nfsd4_create_session *cr_ses) | 1309 | struct nfsd4_create_session *cr_ses) |
1184 | { | 1310 | { |
1185 | return -1; /* stub */ | 1311 | u32 ip_addr = svc_addr_in(rqstp)->sin_addr.s_addr; |
1312 | struct nfs4_client *conf, *unconf; | ||
1313 | int status = 0; | ||
1314 | |||
1315 | nfs4_lock_state(); | ||
1316 | unconf = find_unconfirmed_client(&cr_ses->clientid); | ||
1317 | conf = find_confirmed_client(&cr_ses->clientid); | ||
1318 | |||
1319 | if (conf) { | ||
1320 | status = nfs_ok; | ||
1321 | if (conf->cl_seqid == cr_ses->seqid) { | ||
1322 | dprintk("Got a create_session replay! seqid= %d\n", | ||
1323 | conf->cl_seqid); | ||
1324 | goto out_replay; | ||
1325 | } else if (cr_ses->seqid != conf->cl_seqid + 1) { | ||
1326 | status = nfserr_seq_misordered; | ||
1327 | dprintk("Sequence misordered!\n"); | ||
1328 | dprintk("Expected seqid= %d but got seqid= %d\n", | ||
1329 | conf->cl_seqid, cr_ses->seqid); | ||
1330 | goto out; | ||
1331 | } | ||
1332 | conf->cl_seqid++; | ||
1333 | } else if (unconf) { | ||
1334 | if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || | ||
1335 | (ip_addr != unconf->cl_addr)) { | ||
1336 | status = nfserr_clid_inuse; | ||
1337 | goto out; | ||
1338 | } | ||
1339 | |||
1340 | if (unconf->cl_seqid != cr_ses->seqid) { | ||
1341 | status = nfserr_seq_misordered; | ||
1342 | goto out; | ||
1343 | } | ||
1344 | |||
1345 | move_to_confirmed(unconf); | ||
1346 | |||
1347 | /* | ||
1348 | * We do not support RDMA or persistent sessions | ||
1349 | */ | ||
1350 | cr_ses->flags &= ~SESSION4_PERSIST; | ||
1351 | cr_ses->flags &= ~SESSION4_RDMA; | ||
1352 | |||
1353 | conf = unconf; | ||
1354 | } else { | ||
1355 | status = nfserr_stale_clientid; | ||
1356 | goto out; | ||
1357 | } | ||
1358 | |||
1359 | status = alloc_init_session(rqstp, conf, cr_ses); | ||
1360 | if (status) | ||
1361 | goto out; | ||
1362 | |||
1363 | out_replay: | ||
1364 | memcpy(cr_ses->sessionid.data, conf->cl_sessionid.data, | ||
1365 | NFS4_MAX_SESSIONID_LEN); | ||
1366 | cr_ses->seqid = conf->cl_seqid; | ||
1367 | |||
1368 | out: | ||
1369 | nfs4_unlock_state(); | ||
1370 | dprintk("%s returns %d\n", __func__, ntohl(status)); | ||
1371 | return status; | ||
1186 | } | 1372 | } |
1187 | 1373 | ||
1188 | __be32 | 1374 | __be32 |
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 09415bcf078e..671f9b96429b 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -1099,7 +1099,108 @@ static __be32 | |||
1099 | nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, | 1099 | nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, |
1100 | struct nfsd4_create_session *sess) | 1100 | struct nfsd4_create_session *sess) |
1101 | { | 1101 | { |
1102 | return nfserr_opnotsupp; /* stub */ | 1102 | DECODE_HEAD; |
1103 | |||
1104 | u32 dummy; | ||
1105 | char *machine_name; | ||
1106 | int i; | ||
1107 | int nr_secflavs; | ||
1108 | |||
1109 | READ_BUF(16); | ||
1110 | COPYMEM(&sess->clientid, 8); | ||
1111 | READ32(sess->seqid); | ||
1112 | READ32(sess->flags); | ||
1113 | |||
1114 | /* Fore channel attrs */ | ||
1115 | READ_BUF(28); | ||
1116 | READ32(dummy); /* headerpadsz is always 0 */ | ||
1117 | READ32(sess->fore_channel.maxreq_sz); | ||
1118 | READ32(sess->fore_channel.maxresp_sz); | ||
1119 | READ32(sess->fore_channel.maxresp_cached); | ||
1120 | READ32(sess->fore_channel.maxops); | ||
1121 | READ32(sess->fore_channel.maxreqs); | ||
1122 | READ32(sess->fore_channel.nr_rdma_attrs); | ||
1123 | if (sess->fore_channel.nr_rdma_attrs == 1) { | ||
1124 | READ_BUF(4); | ||
1125 | READ32(sess->fore_channel.rdma_attrs); | ||
1126 | } else if (sess->fore_channel.nr_rdma_attrs > 1) { | ||
1127 | dprintk("Too many fore channel attr bitmaps!\n"); | ||
1128 | goto xdr_error; | ||
1129 | } | ||
1130 | |||
1131 | /* Back channel attrs */ | ||
1132 | READ_BUF(28); | ||
1133 | READ32(dummy); /* headerpadsz is always 0 */ | ||
1134 | READ32(sess->back_channel.maxreq_sz); | ||
1135 | READ32(sess->back_channel.maxresp_sz); | ||
1136 | READ32(sess->back_channel.maxresp_cached); | ||
1137 | READ32(sess->back_channel.maxops); | ||
1138 | READ32(sess->back_channel.maxreqs); | ||
1139 | READ32(sess->back_channel.nr_rdma_attrs); | ||
1140 | if (sess->back_channel.nr_rdma_attrs == 1) { | ||
1141 | READ_BUF(4); | ||
1142 | READ32(sess->back_channel.rdma_attrs); | ||
1143 | } else if (sess->back_channel.nr_rdma_attrs > 1) { | ||
1144 | dprintk("Too many back channel attr bitmaps!\n"); | ||
1145 | goto xdr_error; | ||
1146 | } | ||
1147 | |||
1148 | READ_BUF(8); | ||
1149 | READ32(sess->callback_prog); | ||
1150 | |||
1151 | /* callback_sec_params4 */ | ||
1152 | READ32(nr_secflavs); | ||
1153 | for (i = 0; i < nr_secflavs; ++i) { | ||
1154 | READ_BUF(4); | ||
1155 | READ32(dummy); | ||
1156 | switch (dummy) { | ||
1157 | case RPC_AUTH_NULL: | ||
1158 | /* Nothing to read */ | ||
1159 | break; | ||
1160 | case RPC_AUTH_UNIX: | ||
1161 | READ_BUF(8); | ||
1162 | /* stamp */ | ||
1163 | READ32(dummy); | ||
1164 | |||
1165 | /* machine name */ | ||
1166 | READ32(dummy); | ||
1167 | READ_BUF(dummy); | ||
1168 | SAVEMEM(machine_name, dummy); | ||
1169 | |||
1170 | /* uid, gid */ | ||
1171 | READ_BUF(8); | ||
1172 | READ32(sess->uid); | ||
1173 | READ32(sess->gid); | ||
1174 | |||
1175 | /* more gids */ | ||
1176 | READ_BUF(4); | ||
1177 | READ32(dummy); | ||
1178 | READ_BUF(dummy * 4); | ||
1179 | for (i = 0; i < dummy; ++i) | ||
1180 | READ32(dummy); | ||
1181 | break; | ||
1182 | case RPC_AUTH_GSS: | ||
1183 | dprintk("RPC_AUTH_GSS callback secflavor " | ||
1184 | "not supported!\n"); | ||
1185 | READ_BUF(8); | ||
1186 | /* gcbp_service */ | ||
1187 | READ32(dummy); | ||
1188 | /* gcbp_handle_from_server */ | ||
1189 | READ32(dummy); | ||
1190 | READ_BUF(dummy); | ||
1191 | p += XDR_QUADLEN(dummy); | ||
1192 | /* gcbp_handle_from_client */ | ||
1193 | READ_BUF(4); | ||
1194 | READ32(dummy); | ||
1195 | READ_BUF(dummy); | ||
1196 | p += XDR_QUADLEN(dummy); | ||
1197 | break; | ||
1198 | default: | ||
1199 | dprintk("Illegal callback secflavor\n"); | ||
1200 | return nfserr_inval; | ||
1201 | } | ||
1202 | } | ||
1203 | DECODE_TAIL; | ||
1103 | } | 1204 | } |
1104 | 1205 | ||
1105 | static __be32 | 1206 | static __be32 |
@@ -2821,8 +2922,49 @@ static __be32 | |||
2821 | nfsd4_encode_create_session(struct nfsd4_compoundres *resp, int nfserr, | 2922 | nfsd4_encode_create_session(struct nfsd4_compoundres *resp, int nfserr, |
2822 | struct nfsd4_create_session *sess) | 2923 | struct nfsd4_create_session *sess) |
2823 | { | 2924 | { |
2824 | /* stub */ | 2925 | ENCODE_HEAD; |
2825 | return nfserr; | 2926 | |
2927 | if (nfserr) | ||
2928 | return nfserr; | ||
2929 | |||
2930 | RESERVE_SPACE(24); | ||
2931 | WRITEMEM(sess->sessionid.data, NFS4_MAX_SESSIONID_LEN); | ||
2932 | WRITE32(sess->seqid); | ||
2933 | WRITE32(sess->flags); | ||
2934 | ADJUST_ARGS(); | ||
2935 | |||
2936 | RESERVE_SPACE(28); | ||
2937 | WRITE32(0); /* headerpadsz */ | ||
2938 | WRITE32(sess->fore_channel.maxreq_sz); | ||
2939 | WRITE32(sess->fore_channel.maxresp_sz); | ||
2940 | WRITE32(sess->fore_channel.maxresp_cached); | ||
2941 | WRITE32(sess->fore_channel.maxops); | ||
2942 | WRITE32(sess->fore_channel.maxreqs); | ||
2943 | WRITE32(sess->fore_channel.nr_rdma_attrs); | ||
2944 | ADJUST_ARGS(); | ||
2945 | |||
2946 | if (sess->fore_channel.nr_rdma_attrs) { | ||
2947 | RESERVE_SPACE(4); | ||
2948 | WRITE32(sess->fore_channel.rdma_attrs); | ||
2949 | ADJUST_ARGS(); | ||
2950 | } | ||
2951 | |||
2952 | RESERVE_SPACE(28); | ||
2953 | WRITE32(0); /* headerpadsz */ | ||
2954 | WRITE32(sess->back_channel.maxreq_sz); | ||
2955 | WRITE32(sess->back_channel.maxresp_sz); | ||
2956 | WRITE32(sess->back_channel.maxresp_cached); | ||
2957 | WRITE32(sess->back_channel.maxops); | ||
2958 | WRITE32(sess->back_channel.maxreqs); | ||
2959 | WRITE32(sess->back_channel.nr_rdma_attrs); | ||
2960 | ADJUST_ARGS(); | ||
2961 | |||
2962 | if (sess->back_channel.nr_rdma_attrs) { | ||
2963 | RESERVE_SPACE(4); | ||
2964 | WRITE32(sess->back_channel.rdma_attrs); | ||
2965 | ADJUST_ARGS(); | ||
2966 | } | ||
2967 | return 0; | ||
2826 | } | 2968 | } |
2827 | 2969 | ||
2828 | static __be32 | 2970 | static __be32 |
diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index f1edb1d98523..692edf4c2e7c 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h | |||
@@ -99,8 +99,12 @@ struct nfs4_callback { | |||
99 | struct rpc_clnt * cb_client; | 99 | struct rpc_clnt * cb_client; |
100 | }; | 100 | }; |
101 | 101 | ||
102 | /* Maximum number of slots per session. 128 is useful for long haul TCP */ | ||
103 | #define NFSD_MAX_SLOTS_PER_SESSION 128 | ||
102 | /* Maximum number of pages per slot cache entry */ | 104 | /* Maximum number of pages per slot cache entry */ |
103 | #define NFSD_PAGES_PER_SLOT 1 | 105 | #define NFSD_PAGES_PER_SLOT 1 |
106 | /* Maximum number of operations per session compound */ | ||
107 | #define NFSD_MAX_OPS_PER_COMPOUND 16 | ||
104 | 108 | ||
105 | struct nfsd4_cache_entry { | 109 | struct nfsd4_cache_entry { |
106 | __be32 ce_status; | 110 | __be32 ce_status; |
@@ -129,7 +133,7 @@ struct nfsd4_session { | |||
129 | u32 se_fmaxresp_cached; | 133 | u32 se_fmaxresp_cached; |
130 | u32 se_fmaxops; | 134 | u32 se_fmaxops; |
131 | u32 se_fnumslots; | 135 | u32 se_fnumslots; |
132 | struct nfsd4_slot *se_slots; /* forward channel slots */ | 136 | struct nfsd4_slot se_slots[]; /* forward channel slots */ |
133 | }; | 137 | }; |
134 | 138 | ||
135 | static inline void | 139 | static inline void |
@@ -188,6 +192,7 @@ struct nfs4_client { | |||
188 | struct list_head cl_sessions; | 192 | struct list_head cl_sessions; |
189 | u32 cl_seqid; /* seqid for create_session */ | 193 | u32 cl_seqid; /* seqid for create_session */ |
190 | u32 cl_exchange_flags; | 194 | u32 cl_exchange_flags; |
195 | struct nfs4_sessionid cl_sessionid; | ||
191 | }; | 196 | }; |
192 | 197 | ||
193 | /* struct nfs4_client_reset | 198 | /* struct nfs4_client_reset |
diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h index 69cb467cb720..9468829adb70 100644 --- a/include/linux/nfsd/xdr4.h +++ b/include/linux/nfsd/xdr4.h | |||
@@ -359,8 +359,27 @@ struct nfsd4_exchange_id { | |||
359 | int spa_how; | 359 | int spa_how; |
360 | }; | 360 | }; |
361 | 361 | ||
362 | struct nfsd4_channel_attrs { | ||
363 | u32 headerpadsz; | ||
364 | u32 maxreq_sz; | ||
365 | u32 maxresp_sz; | ||
366 | u32 maxresp_cached; | ||
367 | u32 maxops; | ||
368 | u32 maxreqs; | ||
369 | u32 nr_rdma_attrs; | ||
370 | u32 rdma_attrs; | ||
371 | }; | ||
372 | |||
362 | struct nfsd4_create_session { | 373 | struct nfsd4_create_session { |
363 | int foo; /* stub */ | 374 | clientid_t clientid; |
375 | struct nfs4_sessionid sessionid; | ||
376 | u32 seqid; | ||
377 | u32 flags; | ||
378 | struct nfsd4_channel_attrs fore_channel; | ||
379 | struct nfsd4_channel_attrs back_channel; | ||
380 | u32 callback_prog; | ||
381 | u32 uid; | ||
382 | u32 gid; | ||
364 | }; | 383 | }; |
365 | 384 | ||
366 | struct nfsd4_sequence { | 385 | struct nfsd4_sequence { |