diff options
-rw-r--r-- | fs/nfsd/nfs4proc.c | 34 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 47 | ||||
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 5 | ||||
-rw-r--r-- | include/linux/nfsd/state.h | 1 | ||||
-rw-r--r-- | include/linux/nfsd/xdr4.h | 15 |
5 files changed, 91 insertions, 11 deletions
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 9e2ee75e0f7c..ae21a4efe36c 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c | |||
@@ -828,6 +828,34 @@ static struct nfsd4_operation nfsd4_ops[]; | |||
828 | static const char *nfsd4_op_name(unsigned opnum); | 828 | static const char *nfsd4_op_name(unsigned opnum); |
829 | 829 | ||
830 | /* | 830 | /* |
831 | * This is a replay of a compound for which no cache entry pages | ||
832 | * were used. Encode the sequence operation, and if cachethis is FALSE | ||
833 | * encode the uncache rep error on the next operation. | ||
834 | */ | ||
835 | static __be32 | ||
836 | nfsd4_enc_uncached_replay(struct nfsd4_compoundargs *args, | ||
837 | struct nfsd4_compoundres *resp) | ||
838 | { | ||
839 | struct nfsd4_op *op; | ||
840 | |||
841 | dprintk("--> %s resp->opcnt %d ce_cachethis %u \n", __func__, | ||
842 | resp->opcnt, resp->cstate.slot->sl_cache_entry.ce_cachethis); | ||
843 | |||
844 | /* Encode the replayed sequence operation */ | ||
845 | BUG_ON(resp->opcnt != 1); | ||
846 | op = &args->ops[resp->opcnt - 1]; | ||
847 | nfsd4_encode_operation(resp, op); | ||
848 | |||
849 | /*return nfserr_retry_uncached_rep in next operation. */ | ||
850 | if (resp->cstate.slot->sl_cache_entry.ce_cachethis == 0) { | ||
851 | op = &args->ops[resp->opcnt++]; | ||
852 | op->status = nfserr_retry_uncached_rep; | ||
853 | nfsd4_encode_operation(resp, op); | ||
854 | } | ||
855 | return op->status; | ||
856 | } | ||
857 | |||
858 | /* | ||
831 | * Enforce NFSv4.1 COMPOUND ordering rules. | 859 | * Enforce NFSv4.1 COMPOUND ordering rules. |
832 | * | 860 | * |
833 | * TODO: | 861 | * TODO: |
@@ -895,7 +923,6 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, | |||
895 | dprintk("nfsv4 compound op #%d/%d: %d (%s)\n", | 923 | dprintk("nfsv4 compound op #%d/%d: %d (%s)\n", |
896 | resp->opcnt, args->opcnt, op->opnum, | 924 | resp->opcnt, args->opcnt, op->opnum, |
897 | nfsd4_op_name(op->opnum)); | 925 | nfsd4_op_name(op->opnum)); |
898 | |||
899 | /* | 926 | /* |
900 | * The XDR decode routines may have pre-set op->status; | 927 | * The XDR decode routines may have pre-set op->status; |
901 | * for example, if there is a miscellaneous XDR error | 928 | * for example, if there is a miscellaneous XDR error |
@@ -939,7 +966,10 @@ encode_op: | |||
939 | /* Only from SEQUENCE or CREATE_SESSION */ | 966 | /* Only from SEQUENCE or CREATE_SESSION */ |
940 | if (resp->cstate.status == nfserr_replay_cache) { | 967 | if (resp->cstate.status == nfserr_replay_cache) { |
941 | dprintk("%s NFS4.1 replay from cache\n", __func__); | 968 | dprintk("%s NFS4.1 replay from cache\n", __func__); |
942 | status = op->status; | 969 | if (nfsd4_not_cached(resp)) |
970 | status = nfsd4_enc_uncached_replay(args, resp); | ||
971 | else | ||
972 | status = op->status; | ||
943 | goto out; | 973 | goto out; |
944 | } | 974 | } |
945 | if (op->status == nfserr_replay_me) { | 975 | if (op->status == nfserr_replay_me) { |
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 58f9797eb09e..04a395fb5dce 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -1049,17 +1049,31 @@ nfsd4_store_cache_entry(struct nfsd4_compoundres *resp) | |||
1049 | /* Don't cache a failed OP_SEQUENCE. */ | 1049 | /* Don't cache a failed OP_SEQUENCE. */ |
1050 | if (resp->opcnt == 1 && op->opnum == OP_SEQUENCE && resp->cstate.status) | 1050 | if (resp->opcnt == 1 && op->opnum == OP_SEQUENCE && resp->cstate.status) |
1051 | return; | 1051 | return; |
1052 | |||
1052 | nfsd4_release_respages(entry->ce_respages, entry->ce_resused); | 1053 | nfsd4_release_respages(entry->ce_respages, entry->ce_resused); |
1054 | entry->ce_opcnt = resp->opcnt; | ||
1055 | entry->ce_status = resp->cstate.status; | ||
1056 | |||
1057 | /* | ||
1058 | * Don't need a page to cache just the sequence operation - the slot | ||
1059 | * does this for us! | ||
1060 | */ | ||
1061 | |||
1062 | if (nfsd4_not_cached(resp)) { | ||
1063 | entry->ce_resused = 0; | ||
1064 | entry->ce_rpchdrlen = 0; | ||
1065 | dprintk("%s Just cache SEQUENCE. ce_cachethis %d\n", __func__, | ||
1066 | resp->cstate.slot->sl_cache_entry.ce_cachethis); | ||
1067 | return; | ||
1068 | } | ||
1053 | entry->ce_resused = rqstp->rq_resused; | 1069 | entry->ce_resused = rqstp->rq_resused; |
1054 | if (entry->ce_resused > NFSD_PAGES_PER_SLOT + 1) | 1070 | if (entry->ce_resused > NFSD_PAGES_PER_SLOT + 1) |
1055 | entry->ce_resused = NFSD_PAGES_PER_SLOT + 1; | 1071 | entry->ce_resused = NFSD_PAGES_PER_SLOT + 1; |
1056 | nfsd4_copy_pages(entry->ce_respages, rqstp->rq_respages, | 1072 | nfsd4_copy_pages(entry->ce_respages, rqstp->rq_respages, |
1057 | entry->ce_resused); | 1073 | entry->ce_resused); |
1058 | entry->ce_status = resp->cstate.status; | ||
1059 | entry->ce_datav.iov_base = resp->cstate.statp; | 1074 | entry->ce_datav.iov_base = resp->cstate.statp; |
1060 | entry->ce_datav.iov_len = resv->iov_len - ((char *)resp->cstate.statp - | 1075 | entry->ce_datav.iov_len = resv->iov_len - ((char *)resp->cstate.statp - |
1061 | (char *)page_address(rqstp->rq_respages[0])); | 1076 | (char *)page_address(rqstp->rq_respages[0])); |
1062 | entry->ce_opcnt = resp->opcnt; | ||
1063 | /* Current request rpc header length*/ | 1077 | /* Current request rpc header length*/ |
1064 | entry->ce_rpchdrlen = (char *)resp->cstate.statp - | 1078 | entry->ce_rpchdrlen = (char *)resp->cstate.statp - |
1065 | (char *)page_address(rqstp->rq_respages[0]); | 1079 | (char *)page_address(rqstp->rq_respages[0]); |
@@ -1096,13 +1110,28 @@ nfsd41_copy_replay_data(struct nfsd4_compoundres *resp, | |||
1096 | * cached page. Replace any futher replay pages from the cache. | 1110 | * cached page. Replace any futher replay pages from the cache. |
1097 | */ | 1111 | */ |
1098 | __be32 | 1112 | __be32 |
1099 | nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp) | 1113 | nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp, |
1114 | struct nfsd4_sequence *seq) | ||
1100 | { | 1115 | { |
1101 | struct nfsd4_cache_entry *entry = &resp->cstate.slot->sl_cache_entry; | 1116 | struct nfsd4_cache_entry *entry = &resp->cstate.slot->sl_cache_entry; |
1102 | __be32 status; | 1117 | __be32 status; |
1103 | 1118 | ||
1104 | dprintk("--> %s entry %p\n", __func__, entry); | 1119 | dprintk("--> %s entry %p\n", __func__, entry); |
1105 | 1120 | ||
1121 | /* | ||
1122 | * If this is just the sequence operation, we did not keep | ||
1123 | * a page in the cache entry because we can just use the | ||
1124 | * slot info stored in struct nfsd4_sequence that was checked | ||
1125 | * against the slot in nfsd4_sequence(). | ||
1126 | * | ||
1127 | * This occurs when seq->cachethis is FALSE, or when the client | ||
1128 | * session inactivity timer fires and a solo sequence operation | ||
1129 | * is sent (lease renewal). | ||
1130 | */ | ||
1131 | if (seq && nfsd4_not_cached(resp)) { | ||
1132 | seq->maxslots = resp->cstate.session->se_fnumslots; | ||
1133 | return nfs_ok; | ||
1134 | } | ||
1106 | 1135 | ||
1107 | if (!nfsd41_copy_replay_data(resp, entry)) { | 1136 | if (!nfsd41_copy_replay_data(resp, entry)) { |
1108 | /* | 1137 | /* |
@@ -1330,7 +1359,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
1330 | cstate->slot = slot; | 1359 | cstate->slot = slot; |
1331 | cstate->status = status; | 1360 | cstate->status = status; |
1332 | /* Return the cached reply status */ | 1361 | /* Return the cached reply status */ |
1333 | status = nfsd4_replay_cache_entry(resp); | 1362 | status = nfsd4_replay_cache_entry(resp, NULL); |
1334 | goto out; | 1363 | goto out; |
1335 | } else if (cr_ses->seqid != conf->cl_slot.sl_seqid + 1) { | 1364 | } else if (cr_ses->seqid != conf->cl_slot.sl_seqid + 1) { |
1336 | status = nfserr_seq_misordered; | 1365 | status = nfserr_seq_misordered; |
@@ -1380,6 +1409,8 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
1380 | 1409 | ||
1381 | slot->sl_inuse = true; | 1410 | slot->sl_inuse = true; |
1382 | cstate->slot = slot; | 1411 | cstate->slot = slot; |
1412 | /* Ensure a page is used for the cache */ | ||
1413 | slot->sl_cache_entry.ce_cachethis = 1; | ||
1383 | out: | 1414 | out: |
1384 | nfs4_unlock_state(); | 1415 | nfs4_unlock_state(); |
1385 | dprintk("%s returns %d\n", __func__, ntohl(status)); | 1416 | dprintk("%s returns %d\n", __func__, ntohl(status)); |
@@ -1425,8 +1456,8 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
1425 | cstate->slot = slot; | 1456 | cstate->slot = slot; |
1426 | cstate->session = session; | 1457 | cstate->session = session; |
1427 | /* Return the cached reply status and set cstate->status | 1458 | /* Return the cached reply status and set cstate->status |
1428 | * for nfsd4_svc_encode_compoundres processing*/ | 1459 | * for nfsd4_svc_encode_compoundres processing */ |
1429 | status = nfsd4_replay_cache_entry(resp); | 1460 | status = nfsd4_replay_cache_entry(resp, seq); |
1430 | cstate->status = nfserr_replay_cache; | 1461 | cstate->status = nfserr_replay_cache; |
1431 | goto replay_cache; | 1462 | goto replay_cache; |
1432 | } | 1463 | } |
@@ -1436,6 +1467,10 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
1436 | /* Success! bump slot seqid */ | 1467 | /* Success! bump slot seqid */ |
1437 | slot->sl_inuse = true; | 1468 | slot->sl_inuse = true; |
1438 | slot->sl_seqid = seq->seqid; | 1469 | slot->sl_seqid = seq->seqid; |
1470 | slot->sl_cache_entry.ce_cachethis = seq->cachethis; | ||
1471 | /* Always set the cache entry cachethis for solo sequence */ | ||
1472 | if (nfsd4_is_solo_sequence(resp)) | ||
1473 | slot->sl_cache_entry.ce_cachethis = 1; | ||
1439 | 1474 | ||
1440 | cstate->slot = slot; | 1475 | cstate->slot = slot; |
1441 | cstate->session = session; | 1476 | cstate->session = session; |
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 671f9b96429b..64bc2150a6fa 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -2975,7 +2975,7 @@ nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, int nfserr, | |||
2975 | return nfserr; | 2975 | return nfserr; |
2976 | } | 2976 | } |
2977 | 2977 | ||
2978 | static __be32 | 2978 | __be32 |
2979 | nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr, | 2979 | nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr, |
2980 | struct nfsd4_sequence *seq) | 2980 | struct nfsd4_sequence *seq) |
2981 | { | 2981 | { |
@@ -3192,7 +3192,8 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo | |||
3192 | iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base; | 3192 | iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base; |
3193 | BUG_ON(iov->iov_len > PAGE_SIZE); | 3193 | BUG_ON(iov->iov_len > PAGE_SIZE); |
3194 | if (resp->cstate.slot != NULL) { | 3194 | if (resp->cstate.slot != NULL) { |
3195 | if (resp->cstate.status == nfserr_replay_cache) { | 3195 | if (resp->cstate.status == nfserr_replay_cache && |
3196 | !nfsd4_not_cached(resp)) { | ||
3196 | iov->iov_len = resp->cstate.iovlen; | 3197 | iov->iov_len = resp->cstate.iovlen; |
3197 | } else { | 3198 | } else { |
3198 | nfsd4_store_cache_entry(resp); | 3199 | nfsd4_store_cache_entry(resp); |
diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index f063df7ad134..18dcffa57f77 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h | |||
@@ -110,6 +110,7 @@ struct nfsd4_cache_entry { | |||
110 | __be32 ce_status; | 110 | __be32 ce_status; |
111 | struct kvec ce_datav; /* encoded NFSv4.1 data in rq_res.head[0] */ | 111 | struct kvec ce_datav; /* encoded NFSv4.1 data in rq_res.head[0] */ |
112 | struct page *ce_respages[NFSD_PAGES_PER_SLOT + 1]; | 112 | struct page *ce_respages[NFSD_PAGES_PER_SLOT + 1]; |
113 | int ce_cachethis; | ||
113 | short ce_resused; | 114 | short ce_resused; |
114 | int ce_opcnt; | 115 | int ce_opcnt; |
115 | int ce_rpchdrlen; | 116 | int ce_rpchdrlen; |
diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h index 9468829adb70..486188810a60 100644 --- a/include/linux/nfsd/xdr4.h +++ b/include/linux/nfsd/xdr4.h | |||
@@ -480,6 +480,18 @@ struct nfsd4_compoundres { | |||
480 | struct nfsd4_compound_state cstate; | 480 | struct nfsd4_compound_state cstate; |
481 | }; | 481 | }; |
482 | 482 | ||
483 | static inline bool nfsd4_is_solo_sequence(struct nfsd4_compoundres *resp) | ||
484 | { | ||
485 | struct nfsd4_compoundargs *args = resp->rqstp->rq_argp; | ||
486 | return args->opcnt == 1; | ||
487 | } | ||
488 | |||
489 | static inline bool nfsd4_not_cached(struct nfsd4_compoundres *resp) | ||
490 | { | ||
491 | return !resp->cstate.slot->sl_cache_entry.ce_cachethis || | ||
492 | nfsd4_is_solo_sequence(resp); | ||
493 | } | ||
494 | |||
483 | #define NFS4_SVC_XDRSIZE sizeof(struct nfsd4_compoundargs) | 495 | #define NFS4_SVC_XDRSIZE sizeof(struct nfsd4_compoundargs) |
484 | 496 | ||
485 | static inline void | 497 | static inline void |
@@ -510,7 +522,8 @@ extern __be32 nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
510 | struct nfsd4_compound_state *, | 522 | struct nfsd4_compound_state *, |
511 | struct nfsd4_setclientid_confirm *setclientid_confirm); | 523 | struct nfsd4_setclientid_confirm *setclientid_confirm); |
512 | extern void nfsd4_store_cache_entry(struct nfsd4_compoundres *resp); | 524 | extern void nfsd4_store_cache_entry(struct nfsd4_compoundres *resp); |
513 | extern __be32 nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp); | 525 | extern __be32 nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp, |
526 | struct nfsd4_sequence *seq); | ||
514 | extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, | 527 | extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, |
515 | struct nfsd4_compound_state *, | 528 | struct nfsd4_compound_state *, |
516 | struct nfsd4_exchange_id *); | 529 | struct nfsd4_exchange_id *); |