diff options
author | Natanael Copa <ncopa@alpinelinux.org> | 2010-03-23 09:40:14 +0000 |
---|---|---|
committer | Natanael Copa <ncopa@alpinelinux.org> | 2010-03-23 09:40:14 +0000 |
commit | aec9c8c2ded84cc042069aab670bf5cf17c516b3 (patch) | |
tree | 49aacc7adbc008b2c471a4537a21571c3c7e0c40 | |
parent | 1109f54eb91e05dd7684ee1b30bc8328a52a5005 (diff) | |
download | aports-aec9c8c2ded84cc042069aab670bf5cf17c516b3.tar.bz2 aports-aec9c8c2ded84cc042069aab670bf5cf17c516b3.tar.xz |
main/linux-grsec: cleanup unused patches
-rw-r--r-- | main/linux-grsec/net-2.6.git-87c1e12b5eeb7b30b4b41291bef8e0b41fc3dde9.patch | 109 | ||||
-rw-r--r-- | main/linux-grsec/xfrm-flow-cache-grsec.patch | 1154 |
2 files changed, 0 insertions, 1263 deletions
diff --git a/main/linux-grsec/net-2.6.git-87c1e12b5eeb7b30b4b41291bef8e0b41fc3dde9.patch b/main/linux-grsec/net-2.6.git-87c1e12b5eeb7b30b4b41291bef8e0b41fc3dde9.patch deleted file mode 100644 index 7cc9bf789..000000000 --- a/main/linux-grsec/net-2.6.git-87c1e12b5eeb7b30b4b41291bef8e0b41fc3dde9.patch +++ /dev/null @@ -1,109 +0,0 @@ -From 87c1e12b5eeb7b30b4b41291bef8e0b41fc3dde9 Mon Sep 17 00:00:00 2001 -From: Herbert Xu <herbert@gondor.apana.org.au> -Date: Tue, 2 Mar 2010 02:51:56 +0000 -Subject: [PATCH] ipsec: Fix bogus bundle flowi - -When I merged the bundle creation code, I introduced a bogus -flowi value in the bundle. Instead of getting from the caller, -it was instead set to the flow in the route object, which is -totally different. - -The end result is that the bundles we created never match, and -we instead end up with an ever growing bundle list. - -Thanks to Jamal for find this problem. - -Reported-by: Jamal Hadi Salim <hadi@cyberus.ca> -Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> -Acked-by: Steffen Klassert <steffen.klassert@secunet.com> -Acked-by: Jamal Hadi Salim <hadi@cyberus.ca> -Signed-off-by: David S. Miller <davem@davemloft.net> ---- - include/net/xfrm.h | 3 ++- - net/ipv4/xfrm4_policy.c | 5 +++-- - net/ipv6/xfrm6_policy.c | 3 ++- - net/xfrm/xfrm_policy.c | 7 ++++--- - 4 files changed, 11 insertions(+), 7 deletions(-) - -diff --git a/include/net/xfrm.h b/include/net/xfrm.h -index a7df327..d74e080 100644 ---- a/include/net/xfrm.h -+++ b/include/net/xfrm.h -@@ -275,7 +275,8 @@ struct xfrm_policy_afinfo { - struct dst_entry *dst, - int nfheader_len); - int (*fill_dst)(struct xfrm_dst *xdst, -- struct net_device *dev); -+ struct net_device *dev, -+ struct flowi *fl); - }; - - extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo); -diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c -index 67107d6..e4a1483 100644 ---- a/net/ipv4/xfrm4_policy.c -+++ b/net/ipv4/xfrm4_policy.c -@@ -91,11 +91,12 @@ static int xfrm4_init_path(struct xfrm_dst *path, struct dst_entry *dst, - return 0; - } - --static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev) -+static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, -+ struct flowi *fl) - { - struct rtable *rt = (struct rtable *)xdst->route; - -- xdst->u.rt.fl = rt->fl; -+ xdst->u.rt.fl = *fl; - - xdst->u.dst.dev = dev; - dev_hold(dev); -diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c -index dbdc696..ae18165 100644 ---- a/net/ipv6/xfrm6_policy.c -+++ b/net/ipv6/xfrm6_policy.c -@@ -116,7 +116,8 @@ static int xfrm6_init_path(struct xfrm_dst *path, struct dst_entry *dst, - return 0; - } - --static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev) -+static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, -+ struct flowi *fl) - { - struct rt6_info *rt = (struct rt6_info*)xdst->route; - -diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c -index 34a5ef8..843e066 100644 ---- a/net/xfrm/xfrm_policy.c -+++ b/net/xfrm/xfrm_policy.c -@@ -1372,7 +1372,8 @@ static inline int xfrm_init_path(struct xfrm_dst *path, struct dst_entry *dst, - return err; - } - --static inline int xfrm_fill_dst(struct xfrm_dst *xdst, struct net_device *dev) -+static inline int xfrm_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, -+ struct flowi *fl) - { - struct xfrm_policy_afinfo *afinfo = - xfrm_policy_get_afinfo(xdst->u.dst.ops->family); -@@ -1381,7 +1382,7 @@ static inline int xfrm_fill_dst(struct xfrm_dst *xdst, struct net_device *dev) - if (!afinfo) - return -EINVAL; - -- err = afinfo->fill_dst(xdst, dev); -+ err = afinfo->fill_dst(xdst, dev, fl); - - xfrm_policy_put_afinfo(afinfo); - -@@ -1486,7 +1487,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, - for (dst_prev = dst0; dst_prev != dst; dst_prev = dst_prev->child) { - struct xfrm_dst *xdst = (struct xfrm_dst *)dst_prev; - -- err = xfrm_fill_dst(xdst, dev); -+ err = xfrm_fill_dst(xdst, dev, fl); - if (err) - goto free_dst; - --- -1.7.0.2 - diff --git a/main/linux-grsec/xfrm-flow-cache-grsec.patch b/main/linux-grsec/xfrm-flow-cache-grsec.patch deleted file mode 100644 index 881623d8a..000000000 --- a/main/linux-grsec/xfrm-flow-cache-grsec.patch +++ /dev/null @@ -1,1154 +0,0 @@ -From 3519d7c86a6e87584d25f3292b53d3ce865a659e Mon Sep 17 00:00:00 2001 -From: Natanael Copa <ncopa@alpinelinux.org> -Date: Mon, 15 Mar 2010 15:31:37 +0000 -Subject: [PATCH] xfrm: flow cache2 - ---- - include/net/flow.h | 39 ++++- - include/net/netns/xfrm.h | 4 + - include/net/xfrm.h | 1 + - net/core/flow.c | 342 ++++++++++++++++++-------------------- - net/ipv6/inet6_connection_sock.c | 6 +- - net/xfrm/xfrm_policy.c | 271 +++++++++++++++++++++--------- - 6 files changed, 394 insertions(+), 269 deletions(-) - -diff --git a/include/net/flow.h b/include/net/flow.h -index 809970b..814a9d2 100644 ---- a/include/net/flow.h -+++ b/include/net/flow.h -@@ -8,6 +8,9 @@ - #define _NET_FLOW_H - - #include <linux/in6.h> -+#include <linux/notifier.h> -+#include <linux/timer.h> -+#include <linux/slab.h> - #include <asm/atomic.h> - - struct flowi { -@@ -86,13 +89,37 @@ struct flowi { - - struct net; - struct sock; --typedef int (*flow_resolve_t)(struct net *net, struct flowi *key, u16 family, -- u8 dir, void **objp, atomic_t **obj_refp); - --extern void *flow_cache_lookup(struct net *net, struct flowi *key, u16 family, -- u8 dir, flow_resolve_t resolver); --extern void flow_cache_flush(void); --extern atomic_t flow_cache_genid; -+struct flow_cache_percpu; -+struct flow_cache_entry; -+ -+struct flow_cache { -+ u32 hash_shift; -+ u32 order; -+ struct flow_cache_percpu * percpu; -+ struct notifier_block hotcpu_notifier; -+ int low_watermark; -+ int high_watermark; -+ struct timer_list rnd_timer; -+ struct kmem_cache * flow_cachep; -+}; -+ -+struct flow_cache_entry { -+ struct flow_cache_entry *next; -+ struct flowi key; -+ u16 family; -+ u8 dir; -+}; -+ -+extern struct flow_cache_entry *flow_cache_lookup( -+ struct flow_cache *cache, struct flowi *key, -+ u16 family, u8 dir); -+extern void flow_cache_entry_put(struct flow_cache_entry *fce); -+ -+void flow_cache_flush(struct flow_cache *fc, -+ void (*flush)(struct flow_cache *fc, struct flow_cache_entry *fce)); -+extern int flow_cache_init(struct flow_cache *cache, size_t entry_size); -+extern void flow_cache_fini(struct flow_cache *cache); - - static inline int flow_cache_uli_match(struct flowi *fl1, struct flowi *fl2) - { -diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h -index 1ba9127..4bb72c4 100644 ---- a/include/net/netns/xfrm.h -+++ b/include/net/netns/xfrm.h -@@ -41,6 +41,10 @@ struct netns_xfrm { - struct xfrm_policy_hash policy_bydst[XFRM_POLICY_MAX * 2]; - unsigned int policy_count[XFRM_POLICY_MAX * 2]; - struct work_struct policy_hash_work; -+ atomic_t policy_genid; -+ struct hlist_head policy_gc_list; -+ struct work_struct policy_gc_work; -+ struct flow_cache flow_cache; - - struct sock *nlsk; - -diff --git a/include/net/xfrm.h b/include/net/xfrm.h -index 223e90a..5cd4e29 100644 ---- a/include/net/xfrm.h -+++ b/include/net/xfrm.h -@@ -487,6 +487,7 @@ struct xfrm_policy - struct xfrm_lifetime_cfg lft; - struct xfrm_lifetime_cur curlft; - struct dst_entry *bundles; -+ atomic_t bundles_genid; - struct xfrm_policy_walk_entry walk; - u8 type; - u8 action; -diff --git a/net/core/flow.c b/net/core/flow.c -index 5b27992..e3782c2 100644 ---- a/net/core/flow.c -+++ b/net/core/flow.c -@@ -25,114 +25,85 @@ - #include <asm/atomic.h> - #include <linux/security.h> - --struct flow_cache_entry { -- struct flow_cache_entry *next; -- u16 family; -- u8 dir; -- u32 genid; -- struct flowi key; -- void *object; -- 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); -- --#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); -- --#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; -+ void (*flush)(struct flow_cache *fc, struct flow_cache_entry *fce); -+ struct flow_cache * cache; -+ atomic_t cpuleft; -+ struct completion completion; - }; --static DEFINE_PER_CPU(struct tasklet_struct, flow_flush_tasklets); - --#define flow_flush_tasklet(cpu) (&per_cpu(flow_flush_tasklets, cpu)) -+#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 = (struct flow_cache *) 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) --{ -- if (fle->object) -- atomic_dec(fle->object_ref); -- kmem_cache_free(flow_cachep, fle); -- flow_count(cpu)--; --} -- --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); -+ -+ kmem_cache_free(fc->flow_cachep, fle); -+ fcp->hash_count--; - } - } - } - --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) -@@ -165,128 +136,100 @@ static int flow_key_compare(struct flowi *key1, struct flowi *key2) - return 0; - } - --void *flow_cache_lookup(struct net *net, struct flowi *key, u16 family, u8 dir, -- flow_resolve_t resolver) -+struct flow_cache_entry *flow_cache_lookup(struct flow_cache *fc, -+ struct flowi *key, -+ u16 family, u8 dir) - { - struct flow_cache_entry *fle, **head; -+ struct flow_cache_percpu *fcp; - 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 && - flow_key_compare(key, &fle->key) == 0) { -- if (fle->genid == atomic_read(&flow_cache_genid)) { -- void *ret = fle->object; -- -- if (ret) -- atomic_inc(fle->object_ref); -- local_bh_enable(); -- -- return ret; -- } -- break; -- } -- } -- -- if (!fle) { -- if (flow_count(cpu) > flow_hwm) -- flow_cache_shrink(cpu); -- -- fle = kmem_cache_alloc(flow_cachep, GFP_ATOMIC); -- if (fle) { -- fle->next = *head; -- *head = fle; -- fle->family = family; -- fle->dir = dir; -- memcpy(&fle->key, key, sizeof(*key)); -- fle->object = NULL; -- flow_count(cpu)++; -+ return fle; - } - } - --nocache: -- { -- int err; -- void *obj; -- atomic_t *obj_ref; -- -- err = resolver(net, key, family, dir, &obj, &obj_ref); -+ if (fcp->hash_count > fc->high_watermark) -+ flow_cache_shrink(fc, fcp); - -- if (fle && !err) { -- fle->genid = atomic_read(&flow_cache_genid); -+ fle = kmem_cache_zalloc(fc->flow_cachep, GFP_ATOMIC); -+ if (!fle) -+ goto nocache; - -- if (fle->object) -- atomic_dec(fle->object_ref); -+ fle->next = *head; -+ *head = fle; -+ fle->family = family; -+ fle->dir = dir; -+ memcpy(&fle->key, key, sizeof(*key)); -+ fcp->hash_count++; -+ return fle; - -- fle->object = obj; -- fle->object_ref = obj_ref; -- if (obj) -- atomic_inc(fle->object_ref); -- } -- local_bh_enable(); -+nocache: -+ local_bh_enable(); -+ return NULL; -+} - -- if (err) -- obj = ERR_PTR(err); -- return obj; -- } -+void flow_cache_entry_put(struct flow_cache_entry *fce) -+{ -+ local_bh_enable(); - } - - static void flow_cache_flush_tasklet(unsigned long data) - { -- struct flow_flush_info *info = (void *)data; -+ struct flow_flush_info *info = (void *) data; -+ struct flow_cache *fc = (void *) info->cache; -+ struct flow_cache_percpu *fcp; - int i; -- int cpu; - -- cpu = smp_processor_id(); -- for (i = 0; i < flow_hash_size; i++) { -- struct flow_cache_entry *fle; -+ if (info->flush == NULL) -+ goto done; - -- fle = flow_table(cpu)[i]; -- for (; fle; fle = fle->next) { -- unsigned genid = atomic_read(&flow_cache_genid); -- -- if (!fle->object || fle->genid == genid) -- continue; -+ 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->object = NULL; -- atomic_dec(fle->object_ref); -- } -+ fle = fcp->hash_table[i]; -+ for (; fle; fle = fle->next) -+ info->flush(fc, fle); - } - -+done: - if (atomic_dec_and_test(&info->cpuleft)) - 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; -- int cpu; - struct tasklet_struct *tasklet; -+ int cpu; - - cpu = smp_processor_id(); -- -- tasklet = flow_flush_tasklet(cpu); -- tasklet->data = (unsigned long)info; -+ tasklet = &per_cpu_ptr(info->cache->percpu, cpu)->flush_tasklet; -+ tasklet->data = (unsigned long) data; - tasklet_schedule(tasklet); - } - --void flow_cache_flush(void) -+void flow_cache_flush(struct flow_cache *fc, -+ void (*flush)(struct flow_cache *fc, struct flow_cache_entry *fce)) - { - struct flow_flush_info info; - static DEFINE_MUTEX(flow_flush_sem); -@@ -294,6 +237,8 @@ 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 = fc; -+ info.flush = flush; - atomic_set(&info.cpuleft, num_online_cpus()); - init_completion(&info.completion); - -@@ -307,62 +252,99 @@ 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) -+{ -+ fcp->hash_table = (struct flow_cache_entry **) -+ __get_free_pages(GFP_KERNEL|__GFP_ZERO, fc->order); -+ fcp->hash_rnd_recalc = 1; -+ fcp->hash_count = 0; -+ -+ tasklet_init(&fcp->flush_tasklet, flow_cache_flush_tasklet, 0); -+} -+ -+static int __cpuinit 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); -+ -+ switch (action) { -+ case CPU_UP_PREPARE: -+ case CPU_UP_PREPARE_FROZEN: -+ flow_cache_cpu_prepare(fc, fcp); -+ if (!fcp->hash_table) -+ return NOTIFY_BAD; -+ break; -+ case CPU_UP_CANCELED: -+ case CPU_UP_CANCELED_FROZEN: -+ case CPU_DEAD: -+ case CPU_DEAD_FROZEN: -+ if (fcp->hash_table) { -+ __flow_cache_shrink(fc, fcp, 0); -+ free_pages((unsigned long) fcp->hash_table, fc->order); -+ fcp->hash_table = NULL; -+ } -+ break; -+ } -+ return NOTIFY_OK; -+} -+ -+int flow_cache_init(struct flow_cache *fc, size_t entry_size) - { -- struct tasklet_struct *tasklet; - unsigned long order; -+ int i, r; -+ -+ BUG_ON(entry_size < sizeof(struct flow_cache_entry)); -+ fc->flow_cachep = kmem_cache_create("flow_cache", -+ entry_size, -+ 0, SLAB_PANIC, -+ NULL); -+ fc->hash_shift = 10; -+ fc->low_watermark = 2 * flow_cache_hash_size(fc); -+ fc->high_watermark = 4 * flow_cache_hash_size(fc); -+ fc->percpu = alloc_percpu(struct flow_cache_percpu); - - for (order = 0; - (PAGE_SIZE << order) < -- (sizeof(struct flow_cache_entry *)*flow_hash_size); -+ (sizeof(struct flow_cache_entry *) * flow_cache_hash_size(fc)); - order++) - /* NOTHING */; -+ fc->order = order; - -- 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); -+ 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); - -- flow_hash_rnd_recalc(cpu) = 1; -- flow_count(cpu) = 0; -+ for_each_online_cpu(i) { -+ r = flow_cache_cpu(&fc->hotcpu_notifier, -+ CPU_UP_PREPARE, (void*) i); -+ if (r != NOTIFY_OK) -+ panic("NET: failed to allocate flow cache order %lu\n", order); -+ } - -- tasklet = flow_flush_tasklet(cpu); -- tasklet_init(tasklet, flow_cache_flush_tasklet, 0); --} -+ fc->hotcpu_notifier = (struct notifier_block){ -+ .notifier_call = flow_cache_cpu, -+ }; -+ register_hotcpu_notifier(&fc->hotcpu_notifier); - --static int flow_cache_cpu(struct notifier_block *nfb, -- unsigned long action, -- void *hcpu) --{ -- if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) -- __flow_cache_shrink((unsigned long)hcpu, 0); -- return NOTIFY_OK; -+ return 0; - } - --static int __init flow_cache_init(void) -+void flow_cache_fini(struct flow_cache *fc) - { - 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; -- -- 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); -+ del_timer(&fc->rnd_timer); -+ unregister_hotcpu_notifier(&fc->hotcpu_notifier); - - for_each_possible_cpu(i) -- flow_cache_cpu_prepare(i); -+ flow_cache_cpu(&fc->hotcpu_notifier, CPU_DEAD, (void*) i); - -- hotcpu_notifier(flow_cache_cpu, 0); -- return 0; -+ free_percpu(fc->percpu); -+ kmem_cache_destroy(fc->flow_cachep); - } - --module_init(flow_cache_init); -- --EXPORT_SYMBOL(flow_cache_genid); - EXPORT_SYMBOL(flow_cache_lookup); -diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c -index cc4797d..399853e 100644 ---- a/net/ipv6/inet6_connection_sock.c -+++ b/net/ipv6/inet6_connection_sock.c -@@ -151,8 +151,9 @@ void __inet6_csk_dst_store(struct sock *sk, struct dst_entry *dst, - - #ifdef CONFIG_XFRM - { -+ struct net *net = sock_net(sk); - struct rt6_info *rt = (struct rt6_info *)dst; -- rt->rt6i_flow_cache_genid = atomic_read(&flow_cache_genid); -+ rt->rt6i_flow_cache_genid = atomic_read(&net->xfrm.policy_genid); - } - #endif - } -@@ -166,8 +167,9 @@ struct dst_entry *__inet6_csk_dst_check(struct sock *sk, u32 cookie) - - #ifdef CONFIG_XFRM - if (dst) { -+ struct net *net = sock_net(sk); - struct rt6_info *rt = (struct rt6_info *)dst; -- if (rt->rt6i_flow_cache_genid != atomic_read(&flow_cache_genid)) { -+ if (rt->rt6i_flow_cache_genid != atomic_read(&net->xfrm.policy_genid)) { - sk->sk_dst_cache = NULL; - dst_release(dst); - dst = NULL; -diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c -index cb81ca3..82b01c3 100644 ---- a/net/xfrm/xfrm_policy.c -+++ b/net/xfrm/xfrm_policy.c -@@ -44,7 +44,6 @@ static struct xfrm_policy_afinfo *xfrm_policy_afinfo[NPROTO]; - - static struct kmem_cache *xfrm_dst_cache __read_mostly; - --static HLIST_HEAD(xfrm_policy_gc_list); - static DEFINE_SPINLOCK(xfrm_policy_gc_lock); - - static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family); -@@ -53,6 +52,7 @@ static void xfrm_init_pmtu(struct dst_entry *dst); - - static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, - int dir); -+static int stale_bundle(struct dst_entry *dst); - - static inline int - __xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl) -@@ -216,6 +216,35 @@ expired: - xfrm_pol_put(xp); - } - -+struct xfrm_flow_cache_entry { -+ struct flow_cache_entry fce; -+ struct xfrm_policy *policy; -+ struct xfrm_dst *dst; -+ u32 policy_genid, bundles_genid; -+}; -+#define XFRM_CACHE_NO_POLICY ((struct xfrm_policy *) -1) -+ -+void xfrm_flow_cache_entry_validate(struct flow_cache *fc, -+ struct flow_cache_entry *fce) -+{ -+ struct net *net = container_of(fc, struct net, xfrm.flow_cache); -+ struct xfrm_flow_cache_entry *xfc = -+ container_of(fce, struct xfrm_flow_cache_entry, fce); -+ -+ if (xfc->policy_genid != atomic_read(&net->xfrm.policy_genid)) -+ goto invalid; -+ if (xfc->policy == NULL || xfc->policy == XFRM_CACHE_NO_POLICY) -+ return; -+ if (xfc->policy->walk.dead) -+ goto invalid; -+ if (xfc->bundles_genid != atomic_read(&xfc->policy->bundles_genid)) -+ goto invalid_dst; -+ return; -+invalid: -+ xfc->policy = NULL; -+invalid_dst: -+ xfc->dst = NULL; -+} - - /* Allocate xfrm_policy. Not used here, it is supposed to be used by pfkeyv2 - * SPD calls. -@@ -269,27 +298,26 @@ static void xfrm_policy_gc_kill(struct xfrm_policy *policy) - if (del_timer(&policy->timer)) - atomic_dec(&policy->refcnt); - -- if (atomic_read(&policy->refcnt) > 1) -- flow_cache_flush(); -- - xfrm_pol_put(policy); - } - - static void xfrm_policy_gc_task(struct work_struct *work) - { -+ struct net *net = container_of(work, struct net, xfrm.policy_gc_work); - struct xfrm_policy *policy; - struct hlist_node *entry, *tmp; - struct hlist_head gc_list; - - spin_lock_bh(&xfrm_policy_gc_lock); -- gc_list.first = xfrm_policy_gc_list.first; -- INIT_HLIST_HEAD(&xfrm_policy_gc_list); -+ gc_list.first = net->xfrm.policy_gc_list.first; -+ INIT_HLIST_HEAD(&net->xfrm.policy_gc_list); - spin_unlock_bh(&xfrm_policy_gc_lock); - -+ flow_cache_flush(&net->xfrm.flow_cache, xfrm_flow_cache_entry_validate); -+ - hlist_for_each_entry_safe(policy, entry, tmp, &gc_list, bydst) - xfrm_policy_gc_kill(policy); - } --static DECLARE_WORK(xfrm_policy_gc_work, xfrm_policy_gc_task); - - /* Rule must be locked. Release descentant resources, announce - * entry dead. The rule must be unlinked from lists to the moment. -@@ -297,6 +325,7 @@ static DECLARE_WORK(xfrm_policy_gc_work, xfrm_policy_gc_task); - - static void xfrm_policy_kill(struct xfrm_policy *policy) - { -+ struct net *net = xp_net(policy); - int dead; - - write_lock_bh(&policy->lock); -@@ -310,10 +339,10 @@ static void xfrm_policy_kill(struct xfrm_policy *policy) - } - - spin_lock_bh(&xfrm_policy_gc_lock); -- hlist_add_head(&policy->bydst, &xfrm_policy_gc_list); -+ hlist_add_head(&policy->bydst, &net->xfrm.policy_gc_list); - spin_unlock_bh(&xfrm_policy_gc_lock); - -- schedule_work(&xfrm_policy_gc_work); -+ schedule_work(&net->xfrm.policy_gc_work); - } - - static unsigned int xfrm_policy_hashmax __read_mostly = 1 * 1024 * 1024; -@@ -586,7 +615,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) - hlist_add_head(&policy->bydst, chain); - xfrm_pol_hold(policy); - net->xfrm.policy_count[dir]++; -- atomic_inc(&flow_cache_genid); -+ atomic_inc(&net->xfrm.policy_genid); - if (delpol) - __xfrm_policy_unlink(delpol, dir); - policy->index = delpol ? delpol->index : xfrm_gen_index(net, dir); -@@ -619,11 +648,13 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) - gc_list = dst; - - policy->bundles = NULL; -+ atomic_inc(&policy->bundles_genid); - } - write_unlock(&policy->lock); - } - read_unlock_bh(&xfrm_policy_lock); - -+ flow_cache_flush(&net->xfrm.flow_cache, NULL); - while (gc_list) { - struct dst_entry *dst = gc_list; - -@@ -669,7 +700,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u8 type, int dir, - write_unlock_bh(&xfrm_policy_lock); - - if (ret && delete) { -- atomic_inc(&flow_cache_genid); -+ atomic_inc(&net->xfrm.policy_genid); - xfrm_policy_kill(ret); - } - return ret; -@@ -710,7 +741,7 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, u8 type, int dir, u32 id, - write_unlock_bh(&xfrm_policy_lock); - - if (ret && delete) { -- atomic_inc(&flow_cache_genid); -+ atomic_inc(&net->xfrm.policy_genid); - xfrm_policy_kill(ret); - } - return ret; -@@ -824,7 +855,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) - } - - } -- atomic_inc(&flow_cache_genid); -+ atomic_inc(&net->xfrm.policy_genid); - out: - write_unlock_bh(&xfrm_policy_lock); - return err; -@@ -977,32 +1008,18 @@ fail: - return ret; - } - --static int xfrm_policy_lookup(struct net *net, struct flowi *fl, u16 family, -- u8 dir, void **objp, atomic_t **obj_refp) -+static struct xfrm_policy *xfrm_policy_lookup( -+ struct net *net, struct flowi *fl, -+ u16 family, u8 dir) - { -+#ifdef CONFIG_XFRM_SUB_POLICY - struct xfrm_policy *pol; -- int err = 0; - --#ifdef CONFIG_XFRM_SUB_POLICY - pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_SUB, fl, family, dir); -- if (IS_ERR(pol)) { -- err = PTR_ERR(pol); -- pol = NULL; -- } -- if (pol || err) -- goto end; -+ if (pol != NULL) -+ return pol; - #endif -- pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir); -- if (IS_ERR(pol)) { -- err = PTR_ERR(pol); -- pol = NULL; -- } --#ifdef CONFIG_XFRM_SUB_POLICY --end: --#endif -- if ((*objp = (void *) pol) != NULL) -- *obj_refp = &pol->refcnt; -- return err; -+ return xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir); - } - - static inline int policy_to_flow_dir(int dir) -@@ -1083,12 +1100,14 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, - - int xfrm_policy_delete(struct xfrm_policy *pol, int dir) - { -+ struct net *net = xp_net(pol); -+ - write_lock_bh(&xfrm_policy_lock); - pol = __xfrm_policy_unlink(pol, dir); - write_unlock_bh(&xfrm_policy_lock); - if (pol) { - if (dir < XFRM_POLICY_MAX) -- atomic_inc(&flow_cache_genid); -+ atomic_inc(&net->xfrm.policy_genid); - xfrm_policy_kill(pol); - return 0; - } -@@ -1512,13 +1531,34 @@ xfrm_dst_update_origin(struct dst_entry *dst, struct flowi *fl) - #endif - } - --static int stale_bundle(struct dst_entry *dst); -- - /* Main function: finds/creates a bundle for given flow. - * - * At the moment we eat a raw IP route. Mostly to speed up lookups - * on interfaces with disabled IPsec. - */ -+ -+static void xfrm_flow_cache_update(struct net *net, struct flowi *key, -+ u16 family, u8 dir, -+ struct xfrm_policy *pol, -+ struct xfrm_dst *dst) -+{ -+ struct flow_cache_entry *fce; -+ struct xfrm_flow_cache_entry *xf; -+ -+ fce = flow_cache_lookup(&net->xfrm.flow_cache, -+ key, family, dir); -+ if (fce == NULL) -+ return; -+ -+ xf = container_of(fce, struct xfrm_flow_cache_entry, fce); -+ xf->policy_genid = atomic_read(&net->xfrm.policy_genid); -+ xf->policy = pol; -+ if (dst != NULL) -+ xf->bundles_genid = atomic_read(&pol->bundles_genid); -+ xf->dst = dst; -+ flow_cache_entry_put(fce); -+} -+ - int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, struct flowi *fl, - struct sock *sk, int flags) - { -@@ -1537,8 +1577,10 @@ int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, struct flowi *fl, - u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT); - - restart: -- genid = atomic_read(&flow_cache_genid); -+ family = dst_orig->ops->family; -+ genid = atomic_read(&net->xfrm.policy_genid); - policy = NULL; -+ dst = NULL; - for (pi = 0; pi < ARRAY_SIZE(pols); pi++) - pols[pi] = NULL; - npols = 0; -@@ -1555,24 +1597,51 @@ restart: - } - - if (!policy) { -+ struct flow_cache_entry *fce; -+ struct xfrm_flow_cache_entry *xf; -+ - /* To accelerate a bit... */ - if ((dst_orig->flags & DST_NOXFRM) || - !net->xfrm.policy_count[XFRM_POLICY_OUT]) - goto nopol; - -- policy = flow_cache_lookup(net, fl, dst_orig->ops->family, -- dir, xfrm_policy_lookup); -- err = PTR_ERR(policy); -- if (IS_ERR(policy)) { -- XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR); -- goto dropdst; -+ fce = flow_cache_lookup(&net->xfrm.flow_cache, -+ fl, family, dir); -+ if (fce == NULL) -+ goto no_cache; -+ -+ xf = container_of(fce, struct xfrm_flow_cache_entry, fce); -+ xfrm_flow_cache_entry_validate(&net->xfrm.flow_cache, fce); -+ if (xf->policy != NULL) { -+ policy = xf->policy; -+ if (policy != XFRM_CACHE_NO_POLICY) -+ xfrm_pol_hold(policy); -+ if (xf->dst != NULL) -+ dst = dst_clone((struct dst_entry *) xf->dst); -+ } -+ flow_cache_entry_put(fce); -+ if (policy == XFRM_CACHE_NO_POLICY) -+ goto nopol; -+ if (dst && !xfrm_bundle_ok(policy, (struct xfrm_dst *) dst, fl, family, 0)) { -+ dst_release(dst); -+ dst = NULL; - } - } -+no_cache: -+ if (!policy) { -+ policy = xfrm_policy_lookup(net, fl, family, dir); -+ if (!policy) { -+ xfrm_flow_cache_update( -+ net, fl, family, dir, -+ XFRM_CACHE_NO_POLICY, NULL); -+ goto nopol; -+ } -+ } -+ if (IS_ERR(policy)) { -+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR); -+ goto dropdst; -+ } - -- if (!policy) -- goto nopol; -- -- family = dst_orig->ops->family; - pols[0] = policy; - npols ++; - xfrm_nr += pols[0]->xfrm_nr; -@@ -1583,6 +1652,9 @@ restart: - - policy->curlft.use_time = get_seconds(); - -+ if (dst) -+ goto dst_found; -+ - switch (policy->action) { - default: - case XFRM_POLICY_BLOCK: -@@ -1593,18 +1665,11 @@ restart: - - case XFRM_POLICY_ALLOW: - #ifndef CONFIG_XFRM_SUB_POLICY -- if (policy->xfrm_nr == 0) { -- /* Flow passes not transformed. */ -- xfrm_pol_put(policy); -- return 0; -- } -+ if (policy->xfrm_nr == 0) -+ goto no_transform; - #endif - -- /* Try to find matching bundle. -- * -- * LATER: help from flow cache. It is optional, this -- * is required only for output policy. -- */ -+ /* Try to find matching bundle the hard way. */ - dst = xfrm_find_bundle(fl, policy, family); - if (IS_ERR(dst)) { - XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLECHECKERROR); -@@ -1644,12 +1709,8 @@ restart: - * they are searched. See above not-transformed bypass - * is surrounded by non-sub policy configuration, too. - */ -- if (xfrm_nr == 0) { -- /* Flow passes not transformed. */ -- xfrm_pols_put(pols, npols); -- return 0; -- } -- -+ if (xfrm_nr == 0) -+ goto no_transform; - #endif - nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family); - -@@ -1680,7 +1741,7 @@ restart: - goto error; - } - if (nx == -EAGAIN || -- genid != atomic_read(&flow_cache_genid)) { -+ genid != atomic_read(&net->xfrm.policy_genid)) { - xfrm_pols_put(pols, npols); - goto restart; - } -@@ -1691,11 +1752,8 @@ restart: - goto error; - } - } -- if (nx == 0) { -- /* Flow passes not transformed. */ -- xfrm_pols_put(pols, npols); -- return 0; -- } -+ if (nx == 0) -+ goto no_transform; - - dst = xfrm_bundle_create(policy, xfrm, nx, fl, dst_orig); - err = PTR_ERR(dst); -@@ -1744,6 +1802,9 @@ restart: - dst_hold(dst); - write_unlock_bh(&policy->lock); - } -+ xfrm_flow_cache_update(net, fl, family, dir, -+ policy, (struct xfrm_dst *) dst); -+dst_found: - *dst_p = dst; - dst_release(dst_orig); - xfrm_pols_put(pols, npols); -@@ -1761,7 +1822,12 @@ nopol: - if (flags & XFRM_LOOKUP_ICMP) - goto dropdst; - return 0; -+no_transform: -+ /* Flow passes not transformed. */ -+ xfrm_pols_put(pols, npols); -+ return 0; - } -+ - EXPORT_SYMBOL(__xfrm_lookup); - - int xfrm_lookup(struct net *net, struct dst_entry **dst_p, struct flowi *fl, -@@ -1919,10 +1985,35 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, - } - } - -- if (!pol) -- pol = flow_cache_lookup(net, &fl, family, fl_dir, -- xfrm_policy_lookup); -- -+ if (!pol) { -+ struct flow_cache_entry *fce; -+ struct xfrm_flow_cache_entry *xf; -+ -+ fce = flow_cache_lookup(&net->xfrm.flow_cache, -+ &fl, family, dir); -+ if (fce != NULL) { -+ xf = container_of(fce, struct xfrm_flow_cache_entry, fce); -+ xfrm_flow_cache_entry_validate(&net->xfrm.flow_cache, fce); -+ if (xf->policy != NULL) { -+ pol = xf->policy; -+ if (pol != XFRM_CACHE_NO_POLICY) -+ xfrm_pol_hold(pol); -+ else -+ pol = NULL; -+ } else { -+ pol = xfrm_policy_lookup(net, &fl, family, dir); -+ if (!IS_ERR(pol)) { -+ if (pol) -+ xf->policy = pol; -+ else -+ xf->policy = XFRM_CACHE_NO_POLICY; -+ } -+ xf->dst = NULL; -+ xf->policy_genid = atomic_read(&net->xfrm.policy_genid); -+ } -+ flow_cache_entry_put(fce); -+ } -+ } - if (IS_ERR(pol)) { - XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR); - return 0; -@@ -2121,6 +2212,7 @@ static void prune_one_bundle(struct xfrm_policy *pol, int (*func)(struct dst_ent - dstp = &dst->next; - } - } -+ atomic_inc(&pol->bundles_genid); - write_unlock(&pol->lock); - } - -@@ -2148,6 +2240,7 @@ static void xfrm_prune_bundles(struct net *net, int (*func)(struct dst_entry *)) - } - read_unlock_bh(&xfrm_policy_lock); - -+ flow_cache_flush(&net->xfrm.flow_cache, NULL); - while (gc_list) { - struct dst_entry *dst = gc_list; - gc_list = dst->next; -@@ -2428,6 +2521,9 @@ static int __net_init xfrm_policy_init(struct net *net) - - INIT_LIST_HEAD(&net->xfrm.policy_all); - INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize); -+ INIT_HLIST_HEAD(&net->xfrm.policy_gc_list); -+ INIT_WORK(&net->xfrm.policy_gc_work, xfrm_policy_gc_task); -+ flow_cache_init(&net->xfrm.flow_cache, sizeof(struct xfrm_flow_cache_entry)); - if (net_eq(net, &init_net)) - register_netdevice_notifier(&xfrm_dev_notifier); - return 0; -@@ -2461,7 +2557,7 @@ static void xfrm_policy_fini(struct net *net) - audit_info.sessionid = -1; - audit_info.secid = 0; - xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info); -- flush_work(&xfrm_policy_gc_work); -+ flush_work(&net->xfrm.policy_gc_work); - - WARN_ON(!list_empty(&net->xfrm.policy_all)); - -@@ -2479,6 +2575,8 @@ static void xfrm_policy_fini(struct net *net) - sz = (net->xfrm.policy_idx_hmask + 1) * sizeof(struct hlist_head); - WARN_ON(!hlist_empty(net->xfrm.policy_byidx)); - xfrm_hash_free(net->xfrm.policy_byidx, sz); -+ -+ flow_cache_fini(&net->xfrm.flow_cache); - } - - static int __net_init xfrm_net_init(struct net *net) -@@ -2685,8 +2783,9 @@ static int migrate_tmpl_match(struct xfrm_migrate *m, struct xfrm_tmpl *t) - static int xfrm_policy_migrate(struct xfrm_policy *pol, - struct xfrm_migrate *m, int num_migrate) - { -+ struct net *net = xp_net(pol); - struct xfrm_migrate *mp; -- struct dst_entry *dst; -+ struct dst_entry *gc_list = NULL, *tail; - int i, j, n = 0; - - write_lock_bh(&pol->lock); -@@ -2711,15 +2810,25 @@ static int xfrm_policy_migrate(struct xfrm_policy *pol, - sizeof(pol->xfrm_vec[i].saddr)); - pol->xfrm_vec[i].encap_family = mp->new_family; - /* flush bundles */ -- while ((dst = pol->bundles) != NULL) { -- pol->bundles = dst->next; -- dst_free(dst); -- } -+ tail = pol->bundles; -+ while (tail->next) -+ tail = tail->next; -+ tail->next = gc_list; -+ gc_list = pol->bundles; -+ pol->bundles = NULL; -+ atomic_inc(&pol->bundles_genid); - } - } -- - write_unlock_bh(&pol->lock); - -+ flow_cache_flush(&net->xfrm.flow_cache, NULL); -+ while (gc_list) { -+ struct dst_entry *dst = gc_list; -+ -+ gc_list = dst->next; -+ dst_free(dst); -+ } -+ - if (!n) - return -ENODATA; - --- -1.7.0.2 - |