fs/quota: handle overflows of sysctl fs.quota.* and report as unsigned long
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>
This commit is contained in:
Родитель
355b9aae86
Коммит
6fcbcec9cf
|
@ -2848,68 +2848,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 {
|
||||||
};
|
};
|
||||||
|
|
||||||
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];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче