Commit 6fcbcec9 authored by Konstantin Khlebnikov's avatar Konstantin Khlebnikov Committed by Jan Kara
Browse files

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: default avatarKonstantin Khlebnikov <khlebnikov@yandex-team.ru>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent 355b9aae
Loading
Loading
Loading
Loading
+17 −12
Original line number Diff line number Diff line
@@ -2848,68 +2848,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,
	},
+1 −1
Original line number Diff line number Diff line
@@ -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];
};