diff options
author | Andy Adamson <andros@netapp.com> | 2009-04-03 01:28:28 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@citi.umich.edu> | 2009-04-03 20:41:18 -0400 |
commit | ec6b5d7b5064fde27aee798b81107ea3a830de85 (patch) | |
tree | de1571b6773201268796fe5bf3db9bb0f8d23812 /fs/nfsd/nfs4state.c | |
parent | 14778a133e3be332be77d981552a79260a61ee17 (diff) |
nfsd41: create_session operation
Implement the create_session operation confoming to
http://tools.ietf.org/html/draft-ietf-nfsv4-minorversion1-26
Look up the client id (generated by the server on exchange_id,
given by the client on create_session).
If neither a confirmed or unconfirmed client is found
then the client id is stale
If a confirmed cilent is found (i.e. we already received
create_session for it) then compare the sequence id
to determine if it's a replay or possibly a mis-ordered rpc.
If the seqid is in order, update the confirmed client seqid
and procedd with updating the session parameters.
If an unconfirmed client_id is found then verify the creds
and seqid. If both match move the client id to confirmed state
and proceed with processing the create_session.
Currently, we do not support persistent sessions, and RDMA.
alloc_init_session generates a new sessionid and creates
a session structure.
NFSD_PAGES_PER_SLOT is used for the max response cached calculation, and for
the counting of DRC pages using the hard limits set in struct srv_serv.
A note on NFSD_PAGES_PER_SLOT:
Other patches in this series allow for NFSD_PAGES_PER_SLOT + 1 pages to be
cached in a DRC slot when the response size is less than NFSD_PAGES_PER_SLOT *
PAGE_SIZE but xdr_buf pages are used. e.g. a READDIR operation will encode a
small amount of data in the xdr_buf head, and then the READDIR in the xdr_buf
pages. So, the hard limit calculation use of pages by a session is
underestimated by the number of cached operations using the xdr_buf pages.
Yet another patch caches no pages for the solo sequence operation, or any
compound where cache_this is False. So the hard limit calculation use of
pages by a session is overestimated by the number of these operations in the
cache.
TODO: improve resource pre-allocation and negotiate session
parameters accordingly. Respect and possibly adjust
backchannel attributes.
Signed-off-by: Marc Eshel <eshel@almaden.ibm.com>
Signed-off-by: Dean Hildebrand <dhildeb@us.ibm.com>
[nfsd41: remove headerpadsz from channel attributes]
Our client and server only support a headerpadsz of 0.
[nfsd41: use DRC limits in fore channel init]
[nfsd41: do not change CREATE_SESSION back channel attrs]
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
[use sessionid_lock spin lock]
[nfsd41: use bool inuse for slot state]
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
[nfsd41 remove sl_session from alloc_init_session]
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
[simplify nfsd4_encode_create_session error handling]
[nfsd41: fix comment style in init_forechannel_attrs]
[nfsd41: allocate struct nfsd4_session and slot table in one piece]
[nfsd41: no need to INIT_LIST_HEAD in alloc_init_session just prior to list_add]
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Diffstat (limited to 'fs/nfsd/nfs4state.c')
-rw-r--r-- | fs/nfsd/nfs4state.c | 188 |
1 files changed, 187 insertions, 1 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 |