aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/core/flow.c100
1 files changed, 69 insertions, 31 deletions
diff --git a/net/core/flow.c b/net/core/flow.c
index 521df52a77d2..161900674009 100644
--- a/net/core/flow.c
+++ b/net/core/flow.c
@@ -26,7 +26,10 @@
26#include <linux/security.h> 26#include <linux/security.h>
27 27
28struct flow_cache_entry { 28struct flow_cache_entry {
29 struct flow_cache_entry *next; 29 union {
30 struct hlist_node hlist;
31 struct list_head gc_list;
32 } u;
30 u16 family; 33 u16 family;
31 u8 dir; 34 u8 dir;
32 u32 genid; 35 u32 genid;
@@ -35,7 +38,7 @@ struct flow_cache_entry {
35}; 38};
36 39
37struct flow_cache_percpu { 40struct flow_cache_percpu {
38 struct flow_cache_entry **hash_table; 41 struct hlist_head *hash_table;
39 int hash_count; 42 int hash_count;
40 u32 hash_rnd; 43 u32 hash_rnd;
41 int hash_rnd_recalc; 44 int hash_rnd_recalc;
@@ -62,6 +65,9 @@ atomic_t flow_cache_genid = ATOMIC_INIT(0);
62static struct flow_cache flow_cache_global; 65static struct flow_cache flow_cache_global;
63static struct kmem_cache *flow_cachep; 66static struct kmem_cache *flow_cachep;
64 67
68static DEFINE_SPINLOCK(flow_cache_gc_lock);
69static LIST_HEAD(flow_cache_gc_list);
70
65#define flow_cache_hash_size(cache) (1 << (cache)->hash_shift) 71#define flow_cache_hash_size(cache) (1 << (cache)->hash_shift)
66#define FLOW_HASH_RND_PERIOD (10 * 60 * HZ) 72#define FLOW_HASH_RND_PERIOD (10 * 60 * HZ)
67 73
@@ -86,38 +92,66 @@ static int flow_entry_valid(struct flow_cache_entry *fle)
86 return 1; 92 return 1;
87} 93}
88 94
89static void flow_entry_kill(struct flow_cache *fc, 95static void flow_entry_kill(struct flow_cache_entry *fle)
90 struct flow_cache_percpu *fcp,
91 struct flow_cache_entry *fle)
92{ 96{
93 if (fle->object) 97 if (fle->object)
94 fle->object->ops->delete(fle->object); 98 fle->object->ops->delete(fle->object);
95 kmem_cache_free(flow_cachep, fle); 99 kmem_cache_free(flow_cachep, fle);
96 fcp->hash_count--; 100}
101
102static void flow_cache_gc_task(struct work_struct *work)
103{
104 struct list_head gc_list;
105 struct flow_cache_entry *fce, *n;
106
107 INIT_LIST_HEAD(&gc_list);
108 spin_lock_bh(&flow_cache_gc_lock);
109 list_splice_tail_init(&flow_cache_gc_list, &gc_list);
110 spin_unlock_bh(&flow_cache_gc_lock);
111
112 list_for_each_entry_safe(fce, n, &gc_list, u.gc_list)
113 flow_entry_kill(fce);
114}
115static DECLARE_WORK(flow_cache_gc_work, flow_cache_gc_task);
116
117static void flow_cache_queue_garbage(struct flow_cache_percpu *fcp,
118 int deleted, struct list_head *gc_list)
119{
120 if (deleted) {
121 fcp->hash_count -= deleted;
122 spin_lock_bh(&flow_cache_gc_lock);
123 list_splice_tail(gc_list, &flow_cache_gc_list);
124 spin_unlock_bh(&flow_cache_gc_lock);
125 schedule_work(&flow_cache_gc_work);
126 }
97} 127}
98 128
99static void __flow_cache_shrink(struct flow_cache *fc, 129static void __flow_cache_shrink(struct flow_cache *fc,
100 struct flow_cache_percpu *fcp, 130 struct flow_cache_percpu *fcp,
101 int shrink_to) 131 int shrink_to)
102{ 132{
103 struct flow_cache_entry *fle, **flp; 133 struct flow_cache_entry *fle;
104 int i; 134 struct hlist_node *entry, *tmp;
135 LIST_HEAD(gc_list);
136 int i, deleted = 0;
105 137
106 for (i = 0; i < flow_cache_hash_size(fc); i++) { 138 for (i = 0; i < flow_cache_hash_size(fc); i++) {
107 int saved = 0; 139 int saved = 0;
108 140
109 flp = &fcp->hash_table[i]; 141 hlist_for_each_entry_safe(fle, entry, tmp,
110 while ((fle = *flp) != NULL) { 142 &fcp->hash_table[i], u.hlist) {
111 if (saved < shrink_to && 143 if (saved < shrink_to &&
112 flow_entry_valid(fle)) { 144 flow_entry_valid(fle)) {
113 saved++; 145 saved++;
114 flp = &fle->next;
115 } else { 146 } else {
116 *flp = fle->next; 147 deleted++;
117 flow_entry_kill(fc, fcp, fle); 148 hlist_del(&fle->u.hlist);
149 list_add_tail(&fle->u.gc_list, &gc_list);
118 } 150 }
119 } 151 }
120 } 152 }
153
154 flow_cache_queue_garbage(fcp, deleted, &gc_list);
121} 155}
122 156
123static void flow_cache_shrink(struct flow_cache *fc, 157static void flow_cache_shrink(struct flow_cache *fc,
@@ -182,7 +216,8 @@ flow_cache_lookup(struct net *net, struct flowi *key, u16 family, u8 dir,
182{ 216{
183 struct flow_cache *fc = &flow_cache_global; 217 struct flow_cache *fc = &flow_cache_global;
184 struct flow_cache_percpu *fcp; 218 struct flow_cache_percpu *fcp;
185 struct flow_cache_entry *fle, **head; 219 struct flow_cache_entry *fle, *tfle;
220 struct hlist_node *entry;
186 struct flow_cache_object *flo; 221 struct flow_cache_object *flo;
187 unsigned int hash; 222 unsigned int hash;
188 223
@@ -200,12 +235,13 @@ flow_cache_lookup(struct net *net, struct flowi *key, u16 family, u8 dir,
200 flow_new_hash_rnd(fc, fcp); 235 flow_new_hash_rnd(fc, fcp);
201 236
202 hash = flow_hash_code(fc, fcp, key); 237 hash = flow_hash_code(fc, fcp, key);
203 head = &fcp->hash_table[hash]; 238 hlist_for_each_entry(tfle, entry, &fcp->hash_table[hash], u.hlist) {
204 for (fle = *head; fle; fle = fle->next) { 239 if (tfle->family == family &&
205 if (fle->family == family && 240 tfle->dir == dir &&
206 fle->dir == dir && 241 flow_key_compare(key, &tfle->key) == 0) {
207 flow_key_compare(key, &fle->key) == 0) 242 fle = tfle;
208 break; 243 break;
244 }
209 } 245 }
210 246
211 if (unlikely(!fle)) { 247 if (unlikely(!fle)) {
@@ -214,12 +250,11 @@ flow_cache_lookup(struct net *net, struct flowi *key, u16 family, u8 dir,
214 250
215 fle = kmem_cache_alloc(flow_cachep, GFP_ATOMIC); 251 fle = kmem_cache_alloc(flow_cachep, GFP_ATOMIC);
216 if (fle) { 252 if (fle) {
217 fle->next = *head;
218 *head = fle;
219 fle->family = family; 253 fle->family = family;
220 fle->dir = dir; 254 fle->dir = dir;
221 memcpy(&fle->key, key, sizeof(*key)); 255 memcpy(&fle->key, key, sizeof(*key));
222 fle->object = NULL; 256 fle->object = NULL;
257 hlist_add_head(&fle->u.hlist, &fcp->hash_table[hash]);
223 fcp->hash_count++; 258 fcp->hash_count++;
224 } 259 }
225 } else if (likely(fle->genid == atomic_read(&flow_cache_genid))) { 260 } else if (likely(fle->genid == atomic_read(&flow_cache_genid))) {
@@ -262,23 +297,26 @@ static void flow_cache_flush_tasklet(unsigned long data)
262 struct flow_flush_info *info = (void *)data; 297 struct flow_flush_info *info = (void *)data;
263 struct flow_cache *fc = info->cache; 298 struct flow_cache *fc = info->cache;
264 struct flow_cache_percpu *fcp; 299 struct flow_cache_percpu *fcp;
265 int i; 300 struct flow_cache_entry *fle;
301 struct hlist_node *entry, *tmp;
302 LIST_HEAD(gc_list);
303 int i, deleted = 0;
266 304
267 fcp = per_cpu_ptr(fc->percpu, smp_processor_id()); 305 fcp = per_cpu_ptr(fc->percpu, smp_processor_id());
268 for (i = 0; i < flow_cache_hash_size(fc); i++) { 306 for (i = 0; i < flow_cache_hash_size(fc); i++) {
269 struct flow_cache_entry *fle; 307 hlist_for_each_entry_safe(fle, entry, tmp,
270 308 &fcp->hash_table[i], u.hlist) {
271 fle = fcp->hash_table[i];
272 for (; fle; fle = fle->next) {
273 if (flow_entry_valid(fle)) 309 if (flow_entry_valid(fle))
274 continue; 310 continue;
275 311
276 if (fle->object) 312 deleted++;
277 fle->object->ops->delete(fle->object); 313 hlist_del(&fle->u.hlist);
278 fle->object = NULL; 314 list_add_tail(&fle->u.gc_list, &gc_list);
279 } 315 }
280 } 316 }
281 317
318 flow_cache_queue_garbage(fcp, deleted, &gc_list);
319
282 if (atomic_dec_and_test(&info->cpuleft)) 320 if (atomic_dec_and_test(&info->cpuleft))
283 complete(&info->completion); 321 complete(&info->completion);
284} 322}
@@ -320,7 +358,7 @@ void flow_cache_flush(void)
320static void __init flow_cache_cpu_prepare(struct flow_cache *fc, 358static void __init flow_cache_cpu_prepare(struct flow_cache *fc,
321 struct flow_cache_percpu *fcp) 359 struct flow_cache_percpu *fcp)
322{ 360{
323 fcp->hash_table = (struct flow_cache_entry **) 361 fcp->hash_table = (struct hlist_head *)
324 __get_free_pages(GFP_KERNEL|__GFP_ZERO, fc->order); 362 __get_free_pages(GFP_KERNEL|__GFP_ZERO, fc->order);
325 if (!fcp->hash_table) 363 if (!fcp->hash_table)
326 panic("NET: failed to allocate flow cache order %lu\n", fc->order); 364 panic("NET: failed to allocate flow cache order %lu\n", fc->order);
@@ -354,7 +392,7 @@ static int flow_cache_init(struct flow_cache *fc)
354 392
355 for (order = 0; 393 for (order = 0;
356 (PAGE_SIZE << order) < 394 (PAGE_SIZE << order) <
357 (sizeof(struct flow_cache_entry *)*flow_cache_hash_size(fc)); 395 (sizeof(struct hlist_head)*flow_cache_hash_size(fc));
358 order++) 396 order++)
359 /* NOTHING */; 397 /* NOTHING */;
360 fc->order = order; 398 fc->order = order;