[PATCH] SYSCTL: Print binary sysctl warnings (nearly) only once

From: Andi Kleen
Date: Sun Dec 20 2009 - 20:23:33 EST


On Sat, Dec 19, 2009 at 09:40:56AM -0800, Linus Torvalds wrote:
>
>
> On Sat, 19 Dec 2009, Andi Kleen wrote:
> >
> > Turn all the bin_tables from const to non-const. This is needed for the next
> > patch. Unfortunately there were a lot of them, so the patch is rather
> > large, but it is completely mechanic.
>
> Ouch. I hate this.
>
> I'd much rather have a constant sysctl bin_table.
>
> If the _only_ thing you want to do is to do a "warn-on-once" logic, I
> would seriously suggest you use a small hash-table instead of turning
> bin_table non-const.

I played around with this. The standard name hash couldn't be used
because this is a string of ints, not chars. I ended up using
FNV32 hash which seemed to do bests when hashing all the possible
binary sysctls.

Tested patch appended.

-Andi

---

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]
ak@xxxxxxxxxxxxxxx -- Speaking for myself only.
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@xxxxxxxxxxxxxxx>

---
kernel/sysctl_binary.c | 31 ++++++++++++++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)

Index: linux-2.6.33-rc1-ak/kernel/sysctl_binary.c
===================================================================
--- linux-2.6.33-rc1-ak.orig/kernel/sysctl_binary.c
+++ linux-2.6.33-rc1-ak/kernel/sysctl_binary.c
@@ -1417,6 +1417,35 @@ static void deprecated_sysctl_warning(co
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 *arg
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);
}
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/