On Sun 10-11-19 12:49:06, Konstantin Khlebnikov wrote: > 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. > > Signed-off-by: Konstantin Khlebnikov <khlebnikov@xxxxxxxxxxxxxx> Thanks! I've added the patch to my tree. Honza > --- > 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 6e826b454082..fa6ec4f96791 100644 > --- a/fs/quota/dquot.c > +++ b/fs/quota/dquot.c > @@ -2860,68 +2860,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]; > }; > > -- Jan Kara <jack@xxxxxxxx> SUSE Labs, CR