Look for sudden changes in the first and second derivatives in order to eliminate outlier changes to target_highest_slotid (which are due to out-of-order RPC requests). Signed-off-by: Trond Myklebust <Trond.Myklebust@xxxxxxxxxx> --- fs/nfsd/nfs4state.c | 91 +++++++++++++++++++++++++++++++++++++++++++---------- fs/nfsd/state.h | 3 ++ 2 files changed, 77 insertions(+), 17 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ed937c8..0eb563d 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2064,6 +2064,57 @@ out: return status; } +static s32 nfs41_derivative_target_slotid(s32 s1, s32 s2) +{ + s1 -= s2; + if (s1 == 0) + return 0; + if (s1 < 0) + return (s1 - 1) >> 1; + return (s1 + 1) >> 1; +} + +static int nfs41_sign_s32(s32 s1) +{ + if (s1 > 0) + return 1; + if (s1 < 0) + return -1; + return 0; +} + +static bool nfs41_same_sign_or_zero_s32(s32 s1, s32 s2) +{ + if (!s1 || !s2) + return true; + return nfs41_sign_s32(s1) == nfs41_sign_s32(s2); +} + +/* Try to eliminate outliers by checking for sharp changes in the + * derivatives and second derivatives + */ +static bool nfsd4_is_outlier_target_slotid(struct nfsd4_slot_table *tbl, + u32 new_highest) +{ + s32 d_highest, d2_highest; + bool ret = true; + + d_highest = nfs41_derivative_target_slotid(new_highest, + tbl->slt_client_highest_slotid); + d2_highest = nfs41_derivative_target_slotid(d_highest, + tbl->slt_d_client_highest_slotid); + /* Is first derivative same sign? */ + if (nfs41_same_sign_or_zero_s32(d_highest, tbl->slt_d_client_highest_slotid)) + ret = false; + /* Is second derivative same sign? */ + if (nfs41_same_sign_or_zero_s32(d2_highest, tbl->slt_d2_client_highest_slotid)) + ret = false; + tbl->slt_client_highest_slotid = new_highest; + tbl->slt_d_client_highest_slotid = d_highest; + tbl->slt_d2_client_highest_slotid = d2_highest; + return ret; +} + static void nfsd4_sequence_adjust_slot_table(struct nfsd4_session *session, struct nfsd4_slot *slot, u32 sa_highest_slotid, struct nfsd4_sequence *res) @@ -2074,26 +2125,32 @@ static void nfsd4_sequence_adjust_slot_table(struct nfsd4_session *session, spin_lock(&tbl->slt_lock); + if (nfsd4_is_outlier_target_slotid(tbl, sa_highest_slotid)) + goto out; + if (slot->sl_generation != tbl->slt_generation) + goto out; + + /* Deal with slot table truncation... */ + nfsd4_truncate_slot_table_locked(tbl, + max(sa_highest_slotid, tbl->slt_highest_slotid)); + next_target = tbl->slt_target_highest_slotid; + next_highest = tbl->slt_highest_slotid; /* Is the client bumping against our current window limits? */ - if (sa_highest_slotid >= tbl->slt_target_highest_slotid) { - /* Yes! Grow the window size by 1/4 */ - next_target += 1 + (tbl->slt_target_highest_slotid >> 2); - next_highest = next_target; - } else { - /* - * If this slot hasn't seen our previous values, then don't - * trust that the client has seen them. Don't adjust the - * slot table yet. - */ - if (slot->sl_generation != tbl->slt_generation) - goto out; - /* No! Try to shrink the window size by 1/2 */ - next_target >>= 1; - if (sa_highest_slotid + 1 > next_target) - next_target = sa_highest_slotid + 1; - next_highest = tbl->slt_target_highest_slotid; + if (tbl->slt_d_client_highest_slotid > 0) { + if (sa_highest_slotid >= tbl->slt_target_highest_slotid) { + /* Yes! Grow the window size by 1/4 */ + next_target += 1 + (tbl->slt_target_highest_slotid >> 2); + next_highest = next_target; + } + } else /* (tbl->slt_d_client_highest_slotid <= 0) */ { + if (next_target > sa_highest_slotid + 1) { + /* Try to shrink the window size by 1/2 */ + next_target >>= 1; + next_target = max(next_target, sa_highest_slotid + 1); + } + next_highest = max(tbl->slt_target_highest_slotid, next_target); } nfsd4_fc_set_highest_slotid_locked(session, tbl, next_target, next_highest); diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index c2f897f..94a72e5 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -150,6 +150,9 @@ struct nfsd4_slot_table { unsigned long slt_generation; u32 slt_target_highest_slotid; u32 slt_highest_slotid; + u32 slt_client_highest_slotid; + s32 slt_d_client_highest_slotid; + s32 slt_d2_client_highest_slotid; }; struct nfsd4_channel_attrs { -- 1.7.11.7 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html