Commit 413f1063 authored by Konstantin Khlebnikov's avatar Konstantin Khlebnikov Committed by Kleber Sacilotto de Souza

fs/quota: handle overflows of sysctl fs.quota.* and report as unsigned long

BugLink: https://bugs.launchpad.net/bugs/1858462

[ Upstream commit 6fcbcec9 ]

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@buzzSigned-off-by: default avatarKonstantin Khlebnikov <khlebnikov@yandex-team.ru>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
Signed-off-by: default avatarConnor Kuehl <connor.kuehl@canonical.com>
Signed-off-by: default avatarKleber Sacilotto de Souza <kleber.souza@canonical.com>
parent 4a2a71a5
...@@ -2791,68 +2791,73 @@ EXPORT_SYMBOL(dquot_quotactl_sysfile_ops); ...@@ -2791,68 +2791,73 @@ EXPORT_SYMBOL(dquot_quotactl_sysfile_ops);
static int do_proc_dqstats(struct ctl_table *table, int write, static int do_proc_dqstats(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos) 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 */ /* Update global table */
dqstats.stat[type] = dqstats.stat[type] = value;
percpu_counter_sum_positive(&dqstats.counter[type]); return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
return proc_dointvec(table, write, buffer, lenp, ppos);
} }
static struct ctl_table fs_dqstats_table[] = { static struct ctl_table fs_dqstats_table[] = {
{ {
.procname = "lookups", .procname = "lookups",
.data = &dqstats.stat[DQST_LOOKUPS], .data = &dqstats.stat[DQST_LOOKUPS],
.maxlen = sizeof(int), .maxlen = sizeof(unsigned long),
.mode = 0444, .mode = 0444,
.proc_handler = do_proc_dqstats, .proc_handler = do_proc_dqstats,
}, },
{ {
.procname = "drops", .procname = "drops",
.data = &dqstats.stat[DQST_DROPS], .data = &dqstats.stat[DQST_DROPS],
.maxlen = sizeof(int), .maxlen = sizeof(unsigned long),
.mode = 0444, .mode = 0444,
.proc_handler = do_proc_dqstats, .proc_handler = do_proc_dqstats,
}, },
{ {
.procname = "reads", .procname = "reads",
.data = &dqstats.stat[DQST_READS], .data = &dqstats.stat[DQST_READS],
.maxlen = sizeof(int), .maxlen = sizeof(unsigned long),
.mode = 0444, .mode = 0444,
.proc_handler = do_proc_dqstats, .proc_handler = do_proc_dqstats,
}, },
{ {
.procname = "writes", .procname = "writes",
.data = &dqstats.stat[DQST_WRITES], .data = &dqstats.stat[DQST_WRITES],
.maxlen = sizeof(int), .maxlen = sizeof(unsigned long),
.mode = 0444, .mode = 0444,
.proc_handler = do_proc_dqstats, .proc_handler = do_proc_dqstats,
}, },
{ {
.procname = "cache_hits", .procname = "cache_hits",
.data = &dqstats.stat[DQST_CACHE_HITS], .data = &dqstats.stat[DQST_CACHE_HITS],
.maxlen = sizeof(int), .maxlen = sizeof(unsigned long),
.mode = 0444, .mode = 0444,
.proc_handler = do_proc_dqstats, .proc_handler = do_proc_dqstats,
}, },
{ {
.procname = "allocated_dquots", .procname = "allocated_dquots",
.data = &dqstats.stat[DQST_ALLOC_DQUOTS], .data = &dqstats.stat[DQST_ALLOC_DQUOTS],
.maxlen = sizeof(int), .maxlen = sizeof(unsigned long),
.mode = 0444, .mode = 0444,
.proc_handler = do_proc_dqstats, .proc_handler = do_proc_dqstats,
}, },
{ {
.procname = "free_dquots", .procname = "free_dquots",
.data = &dqstats.stat[DQST_FREE_DQUOTS], .data = &dqstats.stat[DQST_FREE_DQUOTS],
.maxlen = sizeof(int), .maxlen = sizeof(unsigned long),
.mode = 0444, .mode = 0444,
.proc_handler = do_proc_dqstats, .proc_handler = do_proc_dqstats,
}, },
{ {
.procname = "syncs", .procname = "syncs",
.data = &dqstats.stat[DQST_SYNCS], .data = &dqstats.stat[DQST_SYNCS],
.maxlen = sizeof(int), .maxlen = sizeof(unsigned long),
.mode = 0444, .mode = 0444,
.proc_handler = do_proc_dqstats, .proc_handler = do_proc_dqstats,
}, },
......
...@@ -263,7 +263,7 @@ enum { ...@@ -263,7 +263,7 @@ enum {
}; };
struct dqstats { struct dqstats {
int stat[_DQST_DQSTAT_LAST]; unsigned long stat[_DQST_DQSTAT_LAST];
struct percpu_counter counter[_DQST_DQSTAT_LAST]; struct percpu_counter counter[_DQST_DQSTAT_LAST];
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment