aboutsummaryrefslogtreecommitdiffstats
path: root/main/linux-grsec/0013-flow-structurize-flow-cache.patch
diff options
context:
space:
mode:
Diffstat (limited to 'main/linux-grsec/0013-flow-structurize-flow-cache.patch')
-rw-r--r--main/linux-grsec/0013-flow-structurize-flow-cache.patch395
1 files changed, 395 insertions, 0 deletions
diff --git a/main/linux-grsec/0013-flow-structurize-flow-cache.patch b/main/linux-grsec/0013-flow-structurize-flow-cache.patch
new file mode 100644
index 0000000000..68fa753a2c
--- /dev/null
+++ b/main/linux-grsec/0013-flow-structurize-flow-cache.patch
@@ -0,0 +1,395 @@
+From 884f6e44f0b405c06bd234b14cc228482291bb38 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
+Date: Wed, 31 Mar 2010 00:17:06 +0000
+Subject: [PATCH 13/18] flow: structurize flow cache
+
+Group all per-cpu data to one structure instead of having many
+globals. Also prepare the internals so that we can have multiple
+instances of the flow cache if needed.
+
+Only the kmem_cache is left as a global as all flow caches share
+the same element size, and benefit from using a common cache.
+
+Signed-off-by: Timo Teras <timo.teras@iki.fi>
+Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+(cherry picked from commit d7997fe1f4584da12e9c29fb682c18e9bdc13b73)
+---
+ net/core/flow.c | 223 +++++++++++++++++++++++++++++--------------------------
+ 1 files changed, 119 insertions(+), 104 deletions(-)
+
+diff --git a/net/core/flow.c b/net/core/flow.c
+index 9601587..1d27ca6 100644
+--- a/net/core/flow.c
++++ b/net/core/flow.c
+@@ -35,104 +35,105 @@ struct flow_cache_entry {
+ atomic_t *object_ref;
+ };
+
+-atomic_t flow_cache_genid = ATOMIC_INIT(0);
+-
+-static u32 flow_hash_shift;
+-#define flow_hash_size (1 << flow_hash_shift)
+-static DEFINE_PER_CPU(struct flow_cache_entry **, flow_tables) = { NULL };
+-
+-#define flow_table(cpu) (per_cpu(flow_tables, cpu))
+-
+-static struct kmem_cache *flow_cachep __read_mostly;
+-
+-static int flow_lwm, flow_hwm;
+-
+-struct flow_percpu_info {
+- int hash_rnd_recalc;
+- u32 hash_rnd;
+- int count;
++struct flow_cache_percpu {
++ struct flow_cache_entry ** hash_table;
++ int hash_count;
++ u32 hash_rnd;
++ int hash_rnd_recalc;
++ struct tasklet_struct flush_tasklet;
+ };
+-static DEFINE_PER_CPU(struct flow_percpu_info, flow_hash_info) = { 0 };
+-
+-#define flow_hash_rnd_recalc(cpu) \
+- (per_cpu(flow_hash_info, cpu).hash_rnd_recalc)
+-#define flow_hash_rnd(cpu) \
+- (per_cpu(flow_hash_info, cpu).hash_rnd)
+-#define flow_count(cpu) \
+- (per_cpu(flow_hash_info, cpu).count)
+-
+-static struct timer_list flow_hash_rnd_timer;
+-
+-#define FLOW_HASH_RND_PERIOD (10 * 60 * HZ)
+
+ struct flow_flush_info {
+- atomic_t cpuleft;
+- struct completion completion;
++ struct flow_cache * cache;
++ atomic_t cpuleft;
++ struct completion completion;
+ };
+-static DEFINE_PER_CPU(struct tasklet_struct, flow_flush_tasklets) = { NULL };
+
+-#define flow_flush_tasklet(cpu) (&per_cpu(flow_flush_tasklets, cpu))
++struct flow_cache {
++ u32 hash_shift;
++ unsigned long order;
++ struct flow_cache_percpu * percpu;
++ struct notifier_block hotcpu_notifier;
++ int low_watermark;
++ int high_watermark;
++ struct timer_list rnd_timer;
++};
++
++atomic_t flow_cache_genid = ATOMIC_INIT(0);
++static struct flow_cache flow_cache_global;
++static struct kmem_cache *flow_cachep;
++
++#define flow_cache_hash_size(cache) (1 << (cache)->hash_shift)
++#define FLOW_HASH_RND_PERIOD (10 * 60 * HZ)
+
+ static void flow_cache_new_hashrnd(unsigned long arg)
+ {
++ struct flow_cache *fc = (void *) arg;
+ int i;
+
+ for_each_possible_cpu(i)
+- flow_hash_rnd_recalc(i) = 1;
++ per_cpu_ptr(fc->percpu, i)->hash_rnd_recalc = 1;
+
+- flow_hash_rnd_timer.expires = jiffies + FLOW_HASH_RND_PERIOD;
+- add_timer(&flow_hash_rnd_timer);
++ fc->rnd_timer.expires = jiffies + FLOW_HASH_RND_PERIOD;
++ add_timer(&fc->rnd_timer);
+ }
+
+-static void flow_entry_kill(int cpu, struct flow_cache_entry *fle)
++static void flow_entry_kill(struct flow_cache *fc,
++ struct flow_cache_percpu *fcp,
++ struct flow_cache_entry *fle)
+ {
+ if (fle->object)
+ atomic_dec(fle->object_ref);
+ kmem_cache_free(flow_cachep, fle);
+- flow_count(cpu)--;
++ fcp->hash_count--;
+ }
+
+-static void __flow_cache_shrink(int cpu, int shrink_to)
++static void __flow_cache_shrink(struct flow_cache *fc,
++ struct flow_cache_percpu *fcp,
++ int shrink_to)
+ {
+ struct flow_cache_entry *fle, **flp;
+ int i;
+
+- for (i = 0; i < flow_hash_size; i++) {
++ for (i = 0; i < flow_cache_hash_size(fc); i++) {
+ int k = 0;
+
+- flp = &flow_table(cpu)[i];
++ flp = &fcp->hash_table[i];
+ while ((fle = *flp) != NULL && k < shrink_to) {
+ k++;
+ flp = &fle->next;
+ }
+ while ((fle = *flp) != NULL) {
+ *flp = fle->next;
+- flow_entry_kill(cpu, fle);
++ flow_entry_kill(fc, fcp, fle);
+ }
+ }
+ }
+
+-static void flow_cache_shrink(int cpu)
++static void flow_cache_shrink(struct flow_cache *fc,
++ struct flow_cache_percpu *fcp)
+ {
+- int shrink_to = flow_lwm / flow_hash_size;
++ int shrink_to = fc->low_watermark / flow_cache_hash_size(fc);
+
+- __flow_cache_shrink(cpu, shrink_to);
++ __flow_cache_shrink(fc, fcp, shrink_to);
+ }
+
+-static void flow_new_hash_rnd(int cpu)
++static void flow_new_hash_rnd(struct flow_cache *fc,
++ struct flow_cache_percpu *fcp)
+ {
+- get_random_bytes(&flow_hash_rnd(cpu), sizeof(u32));
+- flow_hash_rnd_recalc(cpu) = 0;
+-
+- __flow_cache_shrink(cpu, 0);
++ get_random_bytes(&fcp->hash_rnd, sizeof(u32));
++ fcp->hash_rnd_recalc = 0;
++ __flow_cache_shrink(fc, fcp, 0);
+ }
+
+-static u32 flow_hash_code(struct flowi *key, int cpu)
++static u32 flow_hash_code(struct flow_cache *fc,
++ struct flow_cache_percpu *fcp,
++ struct flowi *key)
+ {
+ u32 *k = (u32 *) key;
+
+- return (jhash2(k, (sizeof(*key) / sizeof(u32)), flow_hash_rnd(cpu)) &
+- (flow_hash_size - 1));
++ return (jhash2(k, (sizeof(*key) / sizeof(u32)), fcp->hash_rnd)
++ & (flow_cache_hash_size(fc) - 1));
+ }
+
+ #if (BITS_PER_LONG == 64)
+@@ -168,24 +169,25 @@ static int flow_key_compare(struct flowi *key1, struct flowi *key2)
+ void *flow_cache_lookup(struct net *net, struct flowi *key, u16 family, u8 dir,
+ flow_resolve_t resolver)
+ {
++ struct flow_cache *fc = &flow_cache_global;
++ struct flow_cache_percpu *fcp;
+ struct flow_cache_entry *fle, **head;
+ unsigned int hash;
+- int cpu;
+
+ local_bh_disable();
+- cpu = smp_processor_id();
++ fcp = per_cpu_ptr(fc->percpu, smp_processor_id());
+
+ fle = NULL;
+ /* Packet really early in init? Making flow_cache_init a
+ * pre-smp initcall would solve this. --RR */
+- if (!flow_table(cpu))
++ if (!fcp->hash_table)
+ goto nocache;
+
+- if (flow_hash_rnd_recalc(cpu))
+- flow_new_hash_rnd(cpu);
+- hash = flow_hash_code(key, cpu);
++ if (fcp->hash_rnd_recalc)
++ flow_new_hash_rnd(fc, fcp);
++ hash = flow_hash_code(fc, fcp, key);
+
+- head = &flow_table(cpu)[hash];
++ head = &fcp->hash_table[hash];
+ for (fle = *head; fle; fle = fle->next) {
+ if (fle->family == family &&
+ fle->dir == dir &&
+@@ -204,8 +206,8 @@ void *flow_cache_lookup(struct net *net, struct flowi *key, u16 family, u8 dir,
+ }
+
+ if (!fle) {
+- if (flow_count(cpu) > flow_hwm)
+- flow_cache_shrink(cpu);
++ if (fcp->hash_count > fc->high_watermark)
++ flow_cache_shrink(fc, fcp);
+
+ fle = kmem_cache_alloc(flow_cachep, GFP_ATOMIC);
+ if (fle) {
+@@ -215,7 +217,7 @@ void *flow_cache_lookup(struct net *net, struct flowi *key, u16 family, u8 dir,
+ fle->dir = dir;
+ memcpy(&fle->key, key, sizeof(*key));
+ fle->object = NULL;
+- flow_count(cpu)++;
++ fcp->hash_count++;
+ }
+ }
+
+@@ -249,14 +251,15 @@ nocache:
+ static void flow_cache_flush_tasklet(unsigned long data)
+ {
+ struct flow_flush_info *info = (void *)data;
++ struct flow_cache *fc = info->cache;
++ struct flow_cache_percpu *fcp;
+ int i;
+- int cpu;
+
+- cpu = smp_processor_id();
+- for (i = 0; i < flow_hash_size; i++) {
++ fcp = per_cpu_ptr(fc->percpu, smp_processor_id());
++ for (i = 0; i < flow_cache_hash_size(fc); i++) {
+ struct flow_cache_entry *fle;
+
+- fle = flow_table(cpu)[i];
++ fle = fcp->hash_table[i];
+ for (; fle; fle = fle->next) {
+ unsigned genid = atomic_read(&flow_cache_genid);
+
+@@ -272,7 +275,6 @@ static void flow_cache_flush_tasklet(unsigned long data)
+ complete(&info->completion);
+ }
+
+-static void flow_cache_flush_per_cpu(void *) __attribute__((__unused__));
+ static void flow_cache_flush_per_cpu(void *data)
+ {
+ struct flow_flush_info *info = data;
+@@ -280,8 +282,7 @@ static void flow_cache_flush_per_cpu(void *data)
+ struct tasklet_struct *tasklet;
+
+ cpu = smp_processor_id();
+-
+- tasklet = flow_flush_tasklet(cpu);
++ tasklet = &per_cpu_ptr(info->cache->percpu, cpu)->flush_tasklet;
+ tasklet->data = (unsigned long)info;
+ tasklet_schedule(tasklet);
+ }
+@@ -294,6 +295,7 @@ void flow_cache_flush(void)
+ /* Don't want cpus going down or up during this. */
+ get_online_cpus();
+ mutex_lock(&flow_flush_sem);
++ info.cache = &flow_cache_global;
+ atomic_set(&info.cpuleft, num_online_cpus());
+ init_completion(&info.completion);
+
+@@ -307,62 +309,75 @@ void flow_cache_flush(void)
+ put_online_cpus();
+ }
+
+-static void __init flow_cache_cpu_prepare(int cpu)
++static void __init flow_cache_cpu_prepare(struct flow_cache *fc,
++ struct flow_cache_percpu *fcp)
+ {
+- struct tasklet_struct *tasklet;
+- unsigned long order;
+-
+- for (order = 0;
+- (PAGE_SIZE << order) <
+- (sizeof(struct flow_cache_entry *)*flow_hash_size);
+- order++)
+- /* NOTHING */;
+-
+- flow_table(cpu) = (struct flow_cache_entry **)
+- __get_free_pages(GFP_KERNEL|__GFP_ZERO, order);
+- if (!flow_table(cpu))
+- panic("NET: failed to allocate flow cache order %lu\n", order);
+-
+- flow_hash_rnd_recalc(cpu) = 1;
+- flow_count(cpu) = 0;
+-
+- tasklet = flow_flush_tasklet(cpu);
+- tasklet_init(tasklet, flow_cache_flush_tasklet, 0);
++ fcp->hash_table = (struct flow_cache_entry **)
++ __get_free_pages(GFP_KERNEL|__GFP_ZERO, fc->order);
++ if (!fcp->hash_table)
++ panic("NET: failed to allocate flow cache order %lu\n", fc->order);
++
++ fcp->hash_rnd_recalc = 1;
++ fcp->hash_count = 0;
++ tasklet_init(&fcp->flush_tasklet, flow_cache_flush_tasklet, 0);
+ }
+
+ static int flow_cache_cpu(struct notifier_block *nfb,
+ unsigned long action,
+ void *hcpu)
+ {
++ struct flow_cache *fc = container_of(nfb, struct flow_cache, hotcpu_notifier);
++ int cpu = (unsigned long) hcpu;
++ struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, cpu);
++
+ if (action == CPU_DEAD || action == CPU_DEAD_FROZEN)
+- __flow_cache_shrink((unsigned long)hcpu, 0);
++ __flow_cache_shrink(fc, fcp, 0);
+ return NOTIFY_OK;
+ }
+
+-static int __init flow_cache_init(void)
++static int flow_cache_init(struct flow_cache *fc)
+ {
++ unsigned long order;
+ int i;
+
+- flow_cachep = kmem_cache_create("flow_cache",
+- sizeof(struct flow_cache_entry),
+- 0, SLAB_PANIC,
+- NULL);
+- flow_hash_shift = 10;
+- flow_lwm = 2 * flow_hash_size;
+- flow_hwm = 4 * flow_hash_size;
++ fc->hash_shift = 10;
++ fc->low_watermark = 2 * flow_cache_hash_size(fc);
++ fc->high_watermark = 4 * flow_cache_hash_size(fc);
++
++ for (order = 0;
++ (PAGE_SIZE << order) <
++ (sizeof(struct flow_cache_entry *)*flow_cache_hash_size(fc));
++ order++)
++ /* NOTHING */;
++ fc->order = order;
++ fc->percpu = alloc_percpu(struct flow_cache_percpu);
+
+- setup_timer(&flow_hash_rnd_timer, flow_cache_new_hashrnd, 0);
+- flow_hash_rnd_timer.expires = jiffies + FLOW_HASH_RND_PERIOD;
+- add_timer(&flow_hash_rnd_timer);
++ setup_timer(&fc->rnd_timer, flow_cache_new_hashrnd,
++ (unsigned long) fc);
++ fc->rnd_timer.expires = jiffies + FLOW_HASH_RND_PERIOD;
++ add_timer(&fc->rnd_timer);
+
+ for_each_possible_cpu(i)
+- flow_cache_cpu_prepare(i);
++ flow_cache_cpu_prepare(fc, per_cpu_ptr(fc->percpu, i));
++
++ fc->hotcpu_notifier = (struct notifier_block){
++ .notifier_call = flow_cache_cpu,
++ };
++ register_hotcpu_notifier(&fc->hotcpu_notifier);
+
+- hotcpu_notifier(flow_cache_cpu, 0);
+ return 0;
+ }
+
+-module_init(flow_cache_init);
++static int __init flow_cache_init_global(void)
++{
++ flow_cachep = kmem_cache_create("flow_cache",
++ sizeof(struct flow_cache_entry),
++ 0, SLAB_PANIC, NULL);
++
++ return flow_cache_init(&flow_cache_global);
++}
++
++module_init(flow_cache_init_global);
+
+ EXPORT_SYMBOL(flow_cache_genid);
+ EXPORT_SYMBOL(flow_cache_lookup);
+--
+1.7.0.2
+