Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Konstantin Khlebnikov and committed by
Jan Kara
6fcbcec9 355b9aae

+18 -13
+17 -12
fs/quota/dquot.c
··· 2848 2848 static int do_proc_dqstats(struct ctl_table *table, int write, 2849 2849 void __user *buffer, size_t *lenp, loff_t *ppos) 2850 2850 { 2851 - unsigned int type = (int *)table->data - dqstats.stat; 2851 + unsigned int type = (unsigned long *)table->data - dqstats.stat; 2852 + s64 value = percpu_counter_sum(&dqstats.counter[type]); 2853 + 2854 + /* Filter negative values for non-monotonic counters */ 2855 + if (value < 0 && (type == DQST_ALLOC_DQUOTS || 2856 + type == DQST_FREE_DQUOTS)) 2857 + value = 0; 2852 2858 2853 2859 /* Update global table */ 2854 - dqstats.stat[type] = 2855 - percpu_counter_sum_positive(&dqstats.counter[type]); 2856 - return proc_dointvec(table, write, buffer, lenp, ppos); 2860 + dqstats.stat[type] = value; 2861 + return proc_doulongvec_minmax(table, write, buffer, lenp, ppos); 2857 2862 } 2858 2863 2859 2864 static struct ctl_table fs_dqstats_table[] = { 2860 2865 { 2861 2866 .procname = "lookups", 2862 2867 .data = &dqstats.stat[DQST_LOOKUPS], 2863 - .maxlen = sizeof(int), 2868 + .maxlen = sizeof(unsigned long), 2864 2869 .mode = 0444, 2865 2870 .proc_handler = do_proc_dqstats, 2866 2871 }, 2867 2872 { 2868 2873 .procname = "drops", 2869 2874 .data = &dqstats.stat[DQST_DROPS], 2870 - .maxlen = sizeof(int), 2875 + .maxlen = sizeof(unsigned long), 2871 2876 .mode = 0444, 2872 2877 .proc_handler = do_proc_dqstats, 2873 2878 }, 2874 2879 { 2875 2880 .procname = "reads", 2876 2881 .data = &dqstats.stat[DQST_READS], 2877 - .maxlen = sizeof(int), 2882 + .maxlen = sizeof(unsigned long), 2878 2883 .mode = 0444, 2879 2884 .proc_handler = do_proc_dqstats, 2880 2885 }, 2881 2886 { 2882 2887 .procname = "writes", 2883 2888 .data = &dqstats.stat[DQST_WRITES], 2884 - .maxlen = sizeof(int), 2889 + .maxlen = sizeof(unsigned long), 2885 2890 .mode = 0444, 2886 2891 .proc_handler = do_proc_dqstats, 2887 2892 }, 2888 2893 { 2889 2894 .procname = "cache_hits", 2890 2895 .data = &dqstats.stat[DQST_CACHE_HITS], 2891 - .maxlen = sizeof(int), 2896 + .maxlen = sizeof(unsigned long), 2892 2897 .mode = 0444, 2893 2898 .proc_handler = do_proc_dqstats, 2894 2899 }, 2895 2900 { 2896 2901 .procname = "allocated_dquots", 2897 2902 .data = &dqstats.stat[DQST_ALLOC_DQUOTS], 2898 - .maxlen = sizeof(int), 2903 + .maxlen = sizeof(unsigned long), 2899 2904 .mode = 0444, 2900 2905 .proc_handler = do_proc_dqstats, 2901 2906 }, 2902 2907 { 2903 2908 .procname = "free_dquots", 2904 2909 .data = &dqstats.stat[DQST_FREE_DQUOTS], 2905 - .maxlen = sizeof(int), 2910 + .maxlen = sizeof(unsigned long), 2906 2911 .mode = 0444, 2907 2912 .proc_handler = do_proc_dqstats, 2908 2913 }, 2909 2914 { 2910 2915 .procname = "syncs", 2911 2916 .data = &dqstats.stat[DQST_SYNCS], 2912 - .maxlen = sizeof(int), 2917 + .maxlen = sizeof(unsigned long), 2913 2918 .mode = 0444, 2914 2919 .proc_handler = do_proc_dqstats, 2915 2920 },
+1 -1
include/linux/quota.h
··· 263 263 }; 264 264 265 265 struct dqstats { 266 - int stat[_DQST_DQSTAT_LAST]; 266 + unsigned long stat[_DQST_DQSTAT_LAST]; 267 267 struct percpu_counter counter[_DQST_DQSTAT_LAST]; 268 268 }; 269 269