lkml.org 
[lkml]   [2020]   [Jan]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.19 033/114] fs/quota: handle overflows of sysctl fs.quota.* and report as unsigned long
    Date
    From: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>

    [ Upstream commit 6fcbcec9cfc7b3c6a2c1f1a23ebacedff7073e0a ]

    Quota statistics counted as 64-bit per-cpu counter. Reading sums per-cpu
    fractions as signed 64-bit int, filters negative values and then reports
    lower half as signed 32-bit int.

    Result may looks like:

    fs.quota.allocated_dquots = 22327
    fs.quota.cache_hits = -489852115
    fs.quota.drops = -487288718
    fs.quota.free_dquots = 22083
    fs.quota.lookups = -486883485
    fs.quota.reads = 22327
    fs.quota.syncs = 335064
    fs.quota.writes = 3088689

    Values bigger than 2^31-1 reported as negative.

    All counters except "allocated_dquots" and "free_dquots" are monotonic,
    thus they should be reported as is without filtering negative values.

    Kernel doesn't have generic helper for 64-bit sysctl yet,
    let's use at least unsigned long.

    Link: https://lore.kernel.org/r/157337934693.2078.9842146413181153727.stgit@buzz
    Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
    Signed-off-by: Jan Kara <jack@suse.cz>
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    fs/quota/dquot.c | 29 +++++++++++++++++------------
    include/linux/quota.h | 2 +-
    2 files changed, 18 insertions(+), 13 deletions(-)

    diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
    index 59b00d8db22c..154f175066b3 100644
    --- a/fs/quota/dquot.c
    +++ b/fs/quota/dquot.c
    @@ -2853,68 +2853,73 @@ EXPORT_SYMBOL(dquot_quotactl_sysfile_ops);
    static int do_proc_dqstats(struct ctl_table *table, int write,
    void __user *buffer, size_t *lenp, loff_t *ppos)
    {
    - unsigned int type = (int *)table->data - dqstats.stat;
    + unsigned int type = (unsigned long *)table->data - dqstats.stat;
    + s64 value = percpu_counter_sum(&dqstats.counter[type]);
    +
    + /* Filter negative values for non-monotonic counters */
    + if (value < 0 && (type == DQST_ALLOC_DQUOTS ||
    + type == DQST_FREE_DQUOTS))
    + value = 0;

    /* Update global table */
    - dqstats.stat[type] =
    - percpu_counter_sum_positive(&dqstats.counter[type]);
    - return proc_dointvec(table, write, buffer, lenp, ppos);
    + dqstats.stat[type] = value;
    + return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
    }

    static struct ctl_table fs_dqstats_table[] = {
    {
    .procname = "lookups",
    .data = &dqstats.stat[DQST_LOOKUPS],
    - .maxlen = sizeof(int),
    + .maxlen = sizeof(unsigned long),
    .mode = 0444,
    .proc_handler = do_proc_dqstats,
    },
    {
    .procname = "drops",
    .data = &dqstats.stat[DQST_DROPS],
    - .maxlen = sizeof(int),
    + .maxlen = sizeof(unsigned long),
    .mode = 0444,
    .proc_handler = do_proc_dqstats,
    },
    {
    .procname = "reads",
    .data = &dqstats.stat[DQST_READS],
    - .maxlen = sizeof(int),
    + .maxlen = sizeof(unsigned long),
    .mode = 0444,
    .proc_handler = do_proc_dqstats,
    },
    {
    .procname = "writes",
    .data = &dqstats.stat[DQST_WRITES],
    - .maxlen = sizeof(int),
    + .maxlen = sizeof(unsigned long),
    .mode = 0444,
    .proc_handler = do_proc_dqstats,
    },
    {
    .procname = "cache_hits",
    .data = &dqstats.stat[DQST_CACHE_HITS],
    - .maxlen = sizeof(int),
    + .maxlen = sizeof(unsigned long),
    .mode = 0444,
    .proc_handler = do_proc_dqstats,
    },
    {
    .procname = "allocated_dquots",
    .data = &dqstats.stat[DQST_ALLOC_DQUOTS],
    - .maxlen = sizeof(int),
    + .maxlen = sizeof(unsigned long),
    .mode = 0444,
    .proc_handler = do_proc_dqstats,
    },
    {
    .procname = "free_dquots",
    .data = &dqstats.stat[DQST_FREE_DQUOTS],
    - .maxlen = sizeof(int),
    + .maxlen = sizeof(unsigned long),
    .mode = 0444,
    .proc_handler = do_proc_dqstats,
    },
    {
    .procname = "syncs",
    .data = &dqstats.stat[DQST_SYNCS],
    - .maxlen = sizeof(int),
    + .maxlen = sizeof(unsigned long),
    .mode = 0444,
    .proc_handler = do_proc_dqstats,
    },
    diff --git a/include/linux/quota.h b/include/linux/quota.h
    index f32dd270b8e3..27aab84fcbaa 100644
    --- a/include/linux/quota.h
    +++ b/include/linux/quota.h
    @@ -263,7 +263,7 @@ enum {
    };

    struct dqstats {
    - int stat[_DQST_DQSTAT_LAST];
    + unsigned long stat[_DQST_DQSTAT_LAST];
    struct percpu_counter counter[_DQST_DQSTAT_LAST];
    };

    --
    2.20.1


    \
     
     \ /
      Last update: 2020-01-02 23:55    [W:3.406 / U:0.004 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site