diff options
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 |