aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2010-04-06 20:30:07 -0400
committerDavid S. Miller <davem@davemloft.net>2010-04-07 06:43:20 -0400
commit8e4795605d1e1b39113818ad7c147b8a867a1f6a (patch)
treef02854e92aa0c6e67db85beb9fda4148170a5acb
parent285ead175c5dd5075cab5b6c94f35a3e6c0a3ae6 (diff)
flow: delayed deletion of flow cache entries
Speed up lookups by freeing flow cache entries later. After virtualizing flow cache entry operations, the flow cache may now end up calling policy or bundle destructor which can be slowish. As gc_list is more effective with double linked list, the flow cache is converted to use common hlist and list macroes where appropriate. Signed-off-by: Timo Teras <timo.teras@iki.fi> Signed-off-by: David S. Miller <davem@davemloft.net>
-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;