diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfsd/nfs4state.c | 211 |
1 files changed, 89 insertions, 122 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ebddcc173ed8..f86476c23b2f 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -533,94 +533,6 @@ gen_sessionid(struct nfsd4_session *ses) | |||
533 | */ | 533 | */ |
534 | #define NFSD_MIN_HDR_SEQ_SZ (24 + 12 + 44) | 534 | #define NFSD_MIN_HDR_SEQ_SZ (24 + 12 + 44) |
535 | 535 | ||
536 | /* | ||
537 | * Give the client the number of ca_maxresponsesize_cached slots it | ||
538 | * requests, of size bounded by NFSD_SLOT_CACHE_SIZE, | ||
539 | * NFSD_MAX_MEM_PER_SESSION, and nfsd_drc_max_mem. Do not allow more | ||
540 | * than NFSD_MAX_SLOTS_PER_SESSION. | ||
541 | * | ||
542 | * If we run out of reserved DRC memory we should (up to a point) | ||
543 | * re-negotiate active sessions and reduce their slot usage to make | ||
544 | * rooom for new connections. For now we just fail the create session. | ||
545 | */ | ||
546 | static int set_forechannel_drc_size(struct nfsd4_channel_attrs *fchan) | ||
547 | { | ||
548 | int mem, size = fchan->maxresp_cached; | ||
549 | |||
550 | if (fchan->maxreqs < 1) | ||
551 | return nfserr_inval; | ||
552 | |||
553 | if (size < NFSD_MIN_HDR_SEQ_SZ) | ||
554 | size = NFSD_MIN_HDR_SEQ_SZ; | ||
555 | size -= NFSD_MIN_HDR_SEQ_SZ; | ||
556 | if (size > NFSD_SLOT_CACHE_SIZE) | ||
557 | size = NFSD_SLOT_CACHE_SIZE; | ||
558 | |||
559 | /* bound the maxreqs by NFSD_MAX_MEM_PER_SESSION */ | ||
560 | mem = fchan->maxreqs * size; | ||
561 | if (mem > NFSD_MAX_MEM_PER_SESSION) { | ||
562 | fchan->maxreqs = NFSD_MAX_MEM_PER_SESSION / size; | ||
563 | if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION) | ||
564 | fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION; | ||
565 | mem = fchan->maxreqs * size; | ||
566 | } | ||
567 | |||
568 | spin_lock(&nfsd_drc_lock); | ||
569 | /* bound the total session drc memory ussage */ | ||
570 | if (mem + nfsd_drc_mem_used > nfsd_drc_max_mem) { | ||
571 | fchan->maxreqs = (nfsd_drc_max_mem - nfsd_drc_mem_used) / size; | ||
572 | mem = fchan->maxreqs * size; | ||
573 | } | ||
574 | nfsd_drc_mem_used += mem; | ||
575 | spin_unlock(&nfsd_drc_lock); | ||
576 | |||
577 | if (fchan->maxreqs == 0) | ||
578 | return nfserr_jukebox; | ||
579 | |||
580 | fchan->maxresp_cached = size + NFSD_MIN_HDR_SEQ_SZ; | ||
581 | return 0; | ||
582 | } | ||
583 | |||
584 | /* | ||
585 | * fchan holds the client values on input, and the server values on output | ||
586 | * sv_max_mesg is the maximum payload plus one page for overhead. | ||
587 | */ | ||
588 | static int init_forechannel_attrs(struct svc_rqst *rqstp, | ||
589 | struct nfsd4_channel_attrs *session_fchan, | ||
590 | struct nfsd4_channel_attrs *fchan) | ||
591 | { | ||
592 | int status = 0; | ||
593 | __u32 maxcount = nfsd_serv->sv_max_mesg; | ||
594 | |||
595 | /* headerpadsz set to zero in encode routine */ | ||
596 | |||
597 | /* Use the client's max request and max response size if possible */ | ||
598 | if (fchan->maxreq_sz > maxcount) | ||
599 | fchan->maxreq_sz = maxcount; | ||
600 | session_fchan->maxreq_sz = fchan->maxreq_sz; | ||
601 | |||
602 | if (fchan->maxresp_sz > maxcount) | ||
603 | fchan->maxresp_sz = maxcount; | ||
604 | session_fchan->maxresp_sz = fchan->maxresp_sz; | ||
605 | |||
606 | /* Use the client's maxops if possible */ | ||
607 | if (fchan->maxops > NFSD_MAX_OPS_PER_COMPOUND) | ||
608 | fchan->maxops = NFSD_MAX_OPS_PER_COMPOUND; | ||
609 | session_fchan->maxops = fchan->maxops; | ||
610 | |||
611 | /* FIXME: Error means no more DRC pages so the server should | ||
612 | * recover pages from existing sessions. For now fail session | ||
613 | * creation. | ||
614 | */ | ||
615 | status = set_forechannel_drc_size(fchan); | ||
616 | |||
617 | session_fchan->maxresp_cached = fchan->maxresp_cached; | ||
618 | session_fchan->maxreqs = fchan->maxreqs; | ||
619 | |||
620 | dprintk("%s status %d\n", __func__, status); | ||
621 | return status; | ||
622 | } | ||
623 | |||
624 | static void | 536 | static void |
625 | free_session_slots(struct nfsd4_session *ses) | 537 | free_session_slots(struct nfsd4_session *ses) |
626 | { | 538 | { |
@@ -639,63 +551,118 @@ static inline int slot_bytes(struct nfsd4_channel_attrs *ca) | |||
639 | return ca->maxresp_cached - NFSD_MIN_HDR_SEQ_SZ; | 551 | return ca->maxresp_cached - NFSD_MIN_HDR_SEQ_SZ; |
640 | } | 552 | } |
641 | 553 | ||
642 | static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses) | 554 | static int nfsd4_sanitize_slot_size(u32 size) |
643 | { | 555 | { |
644 | struct nfsd4_session *new, tmp; | 556 | size -= NFSD_MIN_HDR_SEQ_SZ; /* We don't cache the rpc header */ |
645 | struct nfsd4_slot *sp; | 557 | size = min_t(u32, size, NFSD_SLOT_CACHE_SIZE); |
646 | int idx, slotsize, cachesize, i; | ||
647 | int status; | ||
648 | 558 | ||
649 | memset(&tmp, 0, sizeof(tmp)); | 559 | return size; |
560 | } | ||
650 | 561 | ||
651 | /* FIXME: For now, we just accept the client back channel attributes. */ | 562 | /* |
652 | tmp.se_bchannel = cses->back_channel; | 563 | * XXX: If we run out of reserved DRC memory we could (up to a point) |
653 | status = init_forechannel_attrs(rqstp, &tmp.se_fchannel, | 564 | * re-negotiate active sessions and reduce their slot usage to make |
654 | &cses->fore_channel); | 565 | * rooom for new connections. For now we just fail the create session. |
655 | if (status) | 566 | */ |
656 | goto out; | 567 | static int nfsd4_get_drc_mem(int slotsize, u32 num) |
568 | { | ||
569 | int avail; | ||
657 | 570 | ||
658 | BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot *) | 571 | num = min_t(u32, num, NFSD_MAX_SLOTS_PER_SESSION); |
659 | + sizeof(struct nfsd4_session) > PAGE_SIZE); | ||
660 | 572 | ||
661 | status = nfserr_jukebox; | 573 | spin_lock(&nfsd_drc_lock); |
662 | /* allocate struct nfsd4_session and slot table pointers in one piece */ | 574 | avail = min_t(int, NFSD_MAX_MEM_PER_SESSION, |
663 | slotsize = tmp.se_fchannel.maxreqs * sizeof(struct nfsd4_slot *); | 575 | nfsd_drc_max_mem - nfsd_drc_mem_used); |
664 | new = kzalloc(sizeof(*new) + slotsize, GFP_KERNEL); | 576 | num = min_t(int, num, avail / slotsize); |
665 | if (!new) | 577 | nfsd_drc_mem_used += num * slotsize; |
666 | goto out; | 578 | spin_unlock(&nfsd_drc_lock); |
579 | |||
580 | return num; | ||
581 | } | ||
582 | |||
583 | static void nfsd4_put_drc_mem(int slotsize, int num) | ||
584 | { | ||
585 | spin_lock(&nfsd_drc_lock); | ||
586 | nfsd_drc_mem_used -= slotsize * num; | ||
587 | spin_unlock(&nfsd_drc_lock); | ||
588 | } | ||
589 | |||
590 | static struct nfsd4_session *alloc_session(int slotsize, int numslots) | ||
591 | { | ||
592 | struct nfsd4_session *new; | ||
593 | int mem, i; | ||
667 | 594 | ||
668 | memcpy(new, &tmp, sizeof(*new)); | 595 | BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot *) |
596 | + sizeof(struct nfsd4_session) > PAGE_SIZE); | ||
597 | mem = numslots * sizeof(struct nfsd4_slot *); | ||
669 | 598 | ||
599 | new = kzalloc(sizeof(*new) + mem, GFP_KERNEL); | ||
600 | if (!new) | ||
601 | return NULL; | ||
670 | /* allocate each struct nfsd4_slot and data cache in one piece */ | 602 | /* allocate each struct nfsd4_slot and data cache in one piece */ |
671 | cachesize = slot_bytes(&new->se_fchannel); | 603 | for (i = 0; i < numslots; i++) { |
672 | for (i = 0; i < new->se_fchannel.maxreqs; i++) { | 604 | mem = sizeof(struct nfsd4_slot) + slotsize; |
673 | sp = kzalloc(sizeof(*sp) + cachesize, GFP_KERNEL); | 605 | new->se_slots[i] = kzalloc(mem, GFP_KERNEL); |
674 | if (!sp) | 606 | if (!new->se_slots[i]) |
675 | goto out_free; | 607 | goto out_free; |
676 | new->se_slots[i] = sp; | ||
677 | } | 608 | } |
609 | return new; | ||
610 | out_free: | ||
611 | while (i--) | ||
612 | kfree(new->se_slots[i]); | ||
613 | kfree(new); | ||
614 | return NULL; | ||
615 | } | ||
616 | |||
617 | static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4_channel_attrs *req, int numslots, int slotsize) | ||
618 | { | ||
619 | u32 maxrpc = nfsd_serv->sv_max_mesg; | ||
620 | |||
621 | new->maxreqs = numslots; | ||
622 | new->maxresp_cached = slotsize + NFSD_MIN_HDR_SEQ_SZ; | ||
623 | new->maxreq_sz = min_t(u32, req->maxreq_sz, maxrpc); | ||
624 | new->maxresp_sz = min_t(u32, req->maxresp_sz, maxrpc); | ||
625 | new->maxops = min_t(u32, req->maxops, NFSD_MAX_OPS_PER_COMPOUND); | ||
626 | } | ||
627 | |||
628 | static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses) | ||
629 | { | ||
630 | struct nfsd4_session *new; | ||
631 | struct nfsd4_channel_attrs *fchan = &cses->fore_channel; | ||
632 | int numslots, slotsize; | ||
633 | int idx; | ||
634 | |||
635 | /* | ||
636 | * Note decreasing slot size below client's request may | ||
637 | * make it difficult for client to function correctly, whereas | ||
638 | * decreasing the number of slots will (just?) affect | ||
639 | * performance. When short on memory we therefore prefer to | ||
640 | * decrease number of slots instead of their size. | ||
641 | */ | ||
642 | slotsize = nfsd4_sanitize_slot_size(fchan->maxresp_cached); | ||
643 | numslots = nfsd4_get_drc_mem(slotsize, fchan->maxreqs); | ||
644 | |||
645 | new = alloc_session(slotsize, numslots); | ||
646 | if (!new) { | ||
647 | nfsd4_put_drc_mem(slotsize, fchan->maxreqs); | ||
648 | return nfserr_jukebox; | ||
649 | } | ||
650 | init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize); | ||
678 | 651 | ||
679 | new->se_client = clp; | 652 | new->se_client = clp; |
680 | gen_sessionid(new); | 653 | gen_sessionid(new); |
681 | idx = hash_sessionid(&new->se_sessionid); | ||
682 | memcpy(clp->cl_sessionid.data, new->se_sessionid.data, | 654 | memcpy(clp->cl_sessionid.data, new->se_sessionid.data, |
683 | NFS4_MAX_SESSIONID_LEN); | 655 | NFS4_MAX_SESSIONID_LEN); |
684 | 656 | ||
685 | new->se_flags = cses->flags; | 657 | new->se_flags = cses->flags; |
686 | kref_init(&new->se_ref); | 658 | kref_init(&new->se_ref); |
659 | idx = hash_sessionid(&new->se_sessionid); | ||
687 | spin_lock(&client_lock); | 660 | spin_lock(&client_lock); |
688 | list_add(&new->se_hash, &sessionid_hashtbl[idx]); | 661 | list_add(&new->se_hash, &sessionid_hashtbl[idx]); |
689 | list_add(&new->se_perclnt, &clp->cl_sessions); | 662 | list_add(&new->se_perclnt, &clp->cl_sessions); |
690 | spin_unlock(&client_lock); | 663 | spin_unlock(&client_lock); |
691 | 664 | ||
692 | status = nfs_ok; | 665 | return nfs_ok; |
693 | out: | ||
694 | return status; | ||
695 | out_free: | ||
696 | free_session_slots(new); | ||
697 | kfree(new); | ||
698 | goto out; | ||
699 | } | 666 | } |
700 | 667 | ||
701 | /* caller must hold client_lock */ | 668 | /* caller must hold client_lock */ |