diff options
author | Andi Kleen <ak@linux.intel.com> | 2009-12-23 21:00:20 +0100 |
---|---|---|
committer | Andi Kleen <ak@linux.intel.com> | 2009-12-23 21:00:20 +0100 |
commit | 4440095c8268c1a5e11577097d2be429cec036ca (patch) | |
tree | 7e1ca48bcd8fd0b947a7cc0edf31c0af9bbc4ec3 /kernel/sysctl_binary.c | |
parent | Merge branch 'drm-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/air... (diff) | |
download | linux-4440095c8268c1a5e11577097d2be429cec036ca.tar.xz linux-4440095c8268c1a5e11577097d2be429cec036ca.zip |
SYSCTL: Print binary sysctl warnings (nearly) only once
When printing legacy sysctls print the warning message
for each of them only once. This way there is a guarantee
the syslog won't be flooded for any sane program.
The original attempt at this made the tables non const and stored
the flag inline.
Linus suggested using a separate hash table for this, this is based on a
code snippet from him.
The hash implies this is not exact and can sometimes not print a
new sysctl due to a hash collision, but in practice this should not
be a problem
I used a FNV32 hash over the binary string with a 32byte bitmap. This
gives relatively little collisions when all the predefined binary sysctls
are hashed:
size 256
bucket
length number
0: [25]
1: [67]
2: [88]
3: [47]
4: [22]
5: [6]
6: [1]
The worst case is a single collision of 6 hash values.
Signed-off-by: Andi Kleen <ak@linux.intel.com>
Diffstat (limited to 'kernel/sysctl_binary.c')
-rw-r--r-- | kernel/sysctl_binary.c | 31 |
1 files changed, 30 insertions, 1 deletions
diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c index 112533d5fc08..8f5d16e0707a 100644 --- a/kernel/sysctl_binary.c +++ b/kernel/sysctl_binary.c @@ -1417,6 +1417,35 @@ static void deprecated_sysctl_warning(const int *name, int nlen) return; } +#define WARN_ONCE_HASH_BITS 8 +#define WARN_ONCE_HASH_SIZE (1<<WARN_ONCE_HASH_BITS) + +static DECLARE_BITMAP(warn_once_bitmap, WARN_ONCE_HASH_SIZE); + +#define FNV32_OFFSET 2166136261U +#define FNV32_PRIME 0x01000193 + +/* + * Print each legacy sysctl (approximately) only once. + * To avoid making the tables non-const use a external + * hash-table instead. + * Worst case hash collision: 6, but very rarely. + * NOTE! We don't use the SMP-safe bit tests. We simply + * don't care enough. + */ +static void warn_on_bintable(const int *name, int nlen) +{ + int i; + u32 hash = FNV32_OFFSET; + + for (i = 0; i < nlen; i++) + hash = (hash ^ name[i]) * FNV32_PRIME; + hash %= WARN_ONCE_HASH_SIZE; + if (__test_and_set_bit(hash, warn_once_bitmap)) + return; + deprecated_sysctl_warning(name, nlen); +} + static ssize_t do_sysctl(int __user *args_name, int nlen, void __user *oldval, size_t oldlen, void __user *newval, size_t newlen) { @@ -1431,7 +1460,7 @@ static ssize_t do_sysctl(int __user *args_name, int nlen, if (get_user(name[i], args_name + i)) return -EFAULT; - deprecated_sysctl_warning(name, nlen); + warn_on_bintable(name, nlen); return binary_sysctl(name, nlen, oldval, oldlen, newval, newlen); } |