diff options
-rw-r--r-- | fs/nfsd/nfs4state.c | 142 | ||||
-rw-r--r-- | fs/nfsd/nfssvc.c | 4 | ||||
-rw-r--r-- | include/linux/nfsd/cache.h | 1 | ||||
-rw-r--r-- | include/linux/nfsd/state.h | 13 | ||||
-rw-r--r-- | include/linux/nfsd/xdr4.h | 4 |
5 files changed, 164 insertions, 0 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 9243dca3576c..a37b91dab1bf 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -852,6 +852,148 @@ out_err: | |||
852 | return; | 852 | return; |
853 | } | 853 | } |
854 | 854 | ||
855 | void | ||
856 | nfsd4_set_statp(struct svc_rqst *rqstp, __be32 *statp) | ||
857 | { | ||
858 | struct nfsd4_compoundres *resp = rqstp->rq_resp; | ||
859 | |||
860 | resp->cstate.statp = statp; | ||
861 | } | ||
862 | |||
863 | /* | ||
864 | * Dereference the result pages. | ||
865 | */ | ||
866 | static void | ||
867 | nfsd4_release_respages(struct page **respages, short resused) | ||
868 | { | ||
869 | int i; | ||
870 | |||
871 | dprintk("--> %s\n", __func__); | ||
872 | for (i = 0; i < resused; i++) { | ||
873 | if (!respages[i]) | ||
874 | continue; | ||
875 | put_page(respages[i]); | ||
876 | respages[i] = NULL; | ||
877 | } | ||
878 | } | ||
879 | |||
880 | static void | ||
881 | nfsd4_copy_pages(struct page **topages, struct page **frompages, short count) | ||
882 | { | ||
883 | int i; | ||
884 | |||
885 | for (i = 0; i < count; i++) { | ||
886 | topages[i] = frompages[i]; | ||
887 | if (!topages[i]) | ||
888 | continue; | ||
889 | get_page(topages[i]); | ||
890 | } | ||
891 | } | ||
892 | |||
893 | /* | ||
894 | * Cache the reply pages up to NFSD_PAGES_PER_SLOT + 1, clearing the previous | ||
895 | * pages. We add a page to NFSD_PAGES_PER_SLOT for the case where the total | ||
896 | * length of the XDR response is less than se_fmaxresp_cached | ||
897 | * (NFSD_PAGES_PER_SLOT * PAGE_SIZE) but the xdr_buf pages is used for a | ||
898 | * of the reply (e.g. readdir). | ||
899 | * | ||
900 | * Store the base and length of the rq_req.head[0] page | ||
901 | * of the NFSv4.1 data, just past the rpc header. | ||
902 | */ | ||
903 | void | ||
904 | nfsd4_store_cache_entry(struct nfsd4_compoundres *resp) | ||
905 | { | ||
906 | struct nfsd4_cache_entry *entry = &resp->cstate.slot->sl_cache_entry; | ||
907 | struct svc_rqst *rqstp = resp->rqstp; | ||
908 | struct nfsd4_compoundargs *args = rqstp->rq_argp; | ||
909 | struct nfsd4_op *op = &args->ops[resp->opcnt]; | ||
910 | struct kvec *resv = &rqstp->rq_res.head[0]; | ||
911 | |||
912 | dprintk("--> %s entry %p\n", __func__, entry); | ||
913 | |||
914 | /* Don't cache a failed OP_SEQUENCE. */ | ||
915 | if (resp->opcnt == 1 && op->opnum == OP_SEQUENCE && resp->cstate.status) | ||
916 | return; | ||
917 | nfsd4_release_respages(entry->ce_respages, entry->ce_resused); | ||
918 | entry->ce_resused = rqstp->rq_resused; | ||
919 | if (entry->ce_resused > NFSD_PAGES_PER_SLOT + 1) | ||
920 | entry->ce_resused = NFSD_PAGES_PER_SLOT + 1; | ||
921 | nfsd4_copy_pages(entry->ce_respages, rqstp->rq_respages, | ||
922 | entry->ce_resused); | ||
923 | entry->ce_status = resp->cstate.status; | ||
924 | entry->ce_datav.iov_base = resp->cstate.statp; | ||
925 | entry->ce_datav.iov_len = resv->iov_len - ((char *)resp->cstate.statp - | ||
926 | (char *)page_address(rqstp->rq_respages[0])); | ||
927 | entry->ce_opcnt = resp->opcnt; | ||
928 | /* Current request rpc header length*/ | ||
929 | entry->ce_rpchdrlen = (char *)resp->cstate.statp - | ||
930 | (char *)page_address(rqstp->rq_respages[0]); | ||
931 | } | ||
932 | |||
933 | /* | ||
934 | * We keep the rpc header, but take the nfs reply from the replycache. | ||
935 | */ | ||
936 | static int | ||
937 | nfsd41_copy_replay_data(struct nfsd4_compoundres *resp, | ||
938 | struct nfsd4_cache_entry *entry) | ||
939 | { | ||
940 | struct svc_rqst *rqstp = resp->rqstp; | ||
941 | struct kvec *resv = &resp->rqstp->rq_res.head[0]; | ||
942 | int len; | ||
943 | |||
944 | /* Current request rpc header length*/ | ||
945 | len = (char *)resp->cstate.statp - | ||
946 | (char *)page_address(rqstp->rq_respages[0]); | ||
947 | if (entry->ce_datav.iov_len + len > PAGE_SIZE) { | ||
948 | dprintk("%s v41 cached reply too large (%Zd).\n", __func__, | ||
949 | entry->ce_datav.iov_len); | ||
950 | return 0; | ||
951 | } | ||
952 | /* copy the cached reply nfsd data past the current rpc header */ | ||
953 | memcpy((char *)resv->iov_base + len, entry->ce_datav.iov_base, | ||
954 | entry->ce_datav.iov_len); | ||
955 | resv->iov_len = len + entry->ce_datav.iov_len; | ||
956 | return 1; | ||
957 | } | ||
958 | |||
959 | /* | ||
960 | * Keep the first page of the replay. Copy the NFSv4.1 data from the first | ||
961 | * cached page. Replace any futher replay pages from the cache. | ||
962 | */ | ||
963 | __be32 | ||
964 | nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp) | ||
965 | { | ||
966 | struct nfsd4_cache_entry *entry = &resp->cstate.slot->sl_cache_entry; | ||
967 | __be32 status; | ||
968 | |||
969 | dprintk("--> %s entry %p\n", __func__, entry); | ||
970 | |||
971 | |||
972 | if (!nfsd41_copy_replay_data(resp, entry)) { | ||
973 | /* | ||
974 | * Not enough room to use the replay rpc header, send the | ||
975 | * cached header. Release all the allocated result pages. | ||
976 | */ | ||
977 | svc_free_res_pages(resp->rqstp); | ||
978 | nfsd4_copy_pages(resp->rqstp->rq_respages, entry->ce_respages, | ||
979 | entry->ce_resused); | ||
980 | } else { | ||
981 | /* Release all but the first allocated result page */ | ||
982 | |||
983 | resp->rqstp->rq_resused--; | ||
984 | svc_free_res_pages(resp->rqstp); | ||
985 | |||
986 | nfsd4_copy_pages(&resp->rqstp->rq_respages[1], | ||
987 | &entry->ce_respages[1], | ||
988 | entry->ce_resused - 1); | ||
989 | } | ||
990 | |||
991 | resp->rqstp->rq_resused = entry->ce_resused; | ||
992 | status = entry->ce_status; | ||
993 | |||
994 | return status; | ||
995 | } | ||
996 | |||
855 | /* | 997 | /* |
856 | * Set the exchange_id flags returned by the server. | 998 | * Set the exchange_id flags returned by the server. |
857 | */ | 999 | */ |
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index ef0a3686639d..b5168d1898ec 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c | |||
@@ -515,6 +515,10 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) | |||
515 | + rqstp->rq_res.head[0].iov_len; | 515 | + rqstp->rq_res.head[0].iov_len; |
516 | rqstp->rq_res.head[0].iov_len += sizeof(__be32); | 516 | rqstp->rq_res.head[0].iov_len += sizeof(__be32); |
517 | 517 | ||
518 | /* NFSv4.1 DRC requires statp */ | ||
519 | if (rqstp->rq_vers == 4) | ||
520 | nfsd4_set_statp(rqstp, statp); | ||
521 | |||
518 | /* Now call the procedure handler, and encode NFS status. */ | 522 | /* Now call the procedure handler, and encode NFS status. */ |
519 | nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp); | 523 | nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp); |
520 | nfserr = map_new_errors(rqstp->rq_vers, nfserr); | 524 | nfserr = map_new_errors(rqstp->rq_vers, nfserr); |
diff --git a/include/linux/nfsd/cache.h b/include/linux/nfsd/cache.h index 04b355c801d8..a59a2df6d079 100644 --- a/include/linux/nfsd/cache.h +++ b/include/linux/nfsd/cache.h | |||
@@ -75,5 +75,6 @@ int nfsd_reply_cache_init(void); | |||
75 | void nfsd_reply_cache_shutdown(void); | 75 | void nfsd_reply_cache_shutdown(void); |
76 | int nfsd_cache_lookup(struct svc_rqst *, int); | 76 | int nfsd_cache_lookup(struct svc_rqst *, int); |
77 | void nfsd_cache_update(struct svc_rqst *, int, __be32 *); | 77 | void nfsd_cache_update(struct svc_rqst *, int, __be32 *); |
78 | void nfsd4_set_statp(struct svc_rqst *rqstp, __be32 *statp); | ||
78 | 79 | ||
79 | #endif /* NFSCACHE_H */ | 80 | #endif /* NFSCACHE_H */ |
diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index 90829db76861..f1edb1d98523 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h | |||
@@ -99,9 +99,22 @@ struct nfs4_callback { | |||
99 | struct rpc_clnt * cb_client; | 99 | struct rpc_clnt * cb_client; |
100 | }; | 100 | }; |
101 | 101 | ||
102 | /* Maximum number of pages per slot cache entry */ | ||
103 | #define NFSD_PAGES_PER_SLOT 1 | ||
104 | |||
105 | struct nfsd4_cache_entry { | ||
106 | __be32 ce_status; | ||
107 | struct kvec ce_datav; /* encoded NFSv4.1 data in rq_res.head[0] */ | ||
108 | struct page *ce_respages[NFSD_PAGES_PER_SLOT + 1]; | ||
109 | short ce_resused; | ||
110 | int ce_opcnt; | ||
111 | int ce_rpchdrlen; | ||
112 | }; | ||
113 | |||
102 | struct nfsd4_slot { | 114 | struct nfsd4_slot { |
103 | bool sl_inuse; | 115 | bool sl_inuse; |
104 | u32 sl_seqid; | 116 | u32 sl_seqid; |
117 | struct nfsd4_cache_entry sl_cache_entry; | ||
105 | }; | 118 | }; |
106 | 119 | ||
107 | struct nfsd4_session { | 120 | struct nfsd4_session { |
diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h index 6e28a041008d..d091684325af 100644 --- a/include/linux/nfsd/xdr4.h +++ b/include/linux/nfsd/xdr4.h | |||
@@ -51,6 +51,8 @@ struct nfsd4_compound_state { | |||
51 | /* For sessions DRC */ | 51 | /* For sessions DRC */ |
52 | struct nfsd4_session *session; | 52 | struct nfsd4_session *session; |
53 | struct nfsd4_slot *slot; | 53 | struct nfsd4_slot *slot; |
54 | __be32 *statp; | ||
55 | u32 status; | ||
54 | }; | 56 | }; |
55 | 57 | ||
56 | struct nfsd4_change_info { | 58 | struct nfsd4_change_info { |
@@ -487,6 +489,8 @@ extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp, | |||
487 | extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | 489 | extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp, |
488 | struct nfsd4_compound_state *, | 490 | struct nfsd4_compound_state *, |
489 | struct nfsd4_setclientid_confirm *setclientid_confirm); | 491 | struct nfsd4_setclientid_confirm *setclientid_confirm); |
492 | extern void nfsd4_store_cache_entry(struct nfsd4_compoundres *resp); | ||
493 | extern __be32 nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp); | ||
490 | extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, | 494 | extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, |
491 | struct nfsd4_compound_state *, | 495 | struct nfsd4_compound_state *, |
492 | struct nfsd4_exchange_id *); | 496 | struct nfsd4_exchange_id *); |