aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/sch_generic.h42
-rw-r--r--net/sched/sch_api.c104
2 files changed, 146 insertions, 0 deletions
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index a87fc0312edc..073f2580b83b 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -167,6 +167,48 @@ extern void qdisc_unlock_tree(struct net_device *dev);
167extern struct Qdisc noop_qdisc; 167extern struct Qdisc noop_qdisc;
168extern struct Qdisc_ops noop_qdisc_ops; 168extern struct Qdisc_ops noop_qdisc_ops;
169 169
170struct Qdisc_class_common
171{
172 u32 classid;
173 struct hlist_node hnode;
174};
175
176struct Qdisc_class_hash
177{
178 struct hlist_head *hash;
179 unsigned int hashsize;
180 unsigned int hashmask;
181 unsigned int hashelems;
182};
183
184static inline unsigned int qdisc_class_hash(u32 id, u32 mask)
185{
186 id ^= id >> 8;
187 id ^= id >> 4;
188 return id & mask;
189}
190
191static inline struct Qdisc_class_common *
192qdisc_class_find(struct Qdisc_class_hash *hash, u32 id)
193{
194 struct Qdisc_class_common *cl;
195 struct hlist_node *n;
196 unsigned int h;
197
198 h = qdisc_class_hash(id, hash->hashmask);
199 hlist_for_each_entry(cl, n, &hash->hash[h], hnode) {
200 if (cl->classid == id)
201 return cl;
202 }
203 return NULL;
204}
205
206extern int qdisc_class_hash_init(struct Qdisc_class_hash *);
207extern void qdisc_class_hash_insert(struct Qdisc_class_hash *, struct Qdisc_class_common *);
208extern void qdisc_class_hash_remove(struct Qdisc_class_hash *, struct Qdisc_class_common *);
209extern void qdisc_class_hash_grow(struct Qdisc *, struct Qdisc_class_hash *);
210extern void qdisc_class_hash_destroy(struct Qdisc_class_hash *);
211
170extern void dev_init_scheduler(struct net_device *dev); 212extern void dev_init_scheduler(struct net_device *dev);
171extern void dev_shutdown(struct net_device *dev); 213extern void dev_shutdown(struct net_device *dev);
172extern void dev_activate(struct net_device *dev); 214extern void dev_activate(struct net_device *dev);
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 10f01ad04380..e9ebc7af049e 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -316,6 +316,110 @@ void qdisc_watchdog_cancel(struct qdisc_watchdog *wd)
316} 316}
317EXPORT_SYMBOL(qdisc_watchdog_cancel); 317EXPORT_SYMBOL(qdisc_watchdog_cancel);
318 318
319struct hlist_head *qdisc_class_hash_alloc(unsigned int n)
320{
321 unsigned int size = n * sizeof(struct hlist_head), i;
322 struct hlist_head *h;
323
324 if (size <= PAGE_SIZE)
325 h = kmalloc(size, GFP_KERNEL);
326 else
327 h = (struct hlist_head *)
328 __get_free_pages(GFP_KERNEL, get_order(size));
329
330 if (h != NULL) {
331 for (i = 0; i < n; i++)
332 INIT_HLIST_HEAD(&h[i]);
333 }
334 return h;
335}
336
337static void qdisc_class_hash_free(struct hlist_head *h, unsigned int n)
338{
339 unsigned int size = n * sizeof(struct hlist_head);
340
341 if (size <= PAGE_SIZE)
342 kfree(h);
343 else
344 free_pages((unsigned long)h, get_order(size));
345}
346
347void qdisc_class_hash_grow(struct Qdisc *sch, struct Qdisc_class_hash *clhash)
348{
349 struct Qdisc_class_common *cl;
350 struct hlist_node *n, *next;
351 struct hlist_head *nhash, *ohash;
352 unsigned int nsize, nmask, osize;
353 unsigned int i, h;
354
355 /* Rehash when load factor exceeds 0.75 */
356 if (clhash->hashelems * 4 <= clhash->hashsize * 3)
357 return;
358 nsize = clhash->hashsize * 2;
359 nmask = nsize - 1;
360 nhash = qdisc_class_hash_alloc(nsize);
361 if (nhash == NULL)
362 return;
363
364 ohash = clhash->hash;
365 osize = clhash->hashsize;
366
367 sch_tree_lock(sch);
368 for (i = 0; i < osize; i++) {
369 hlist_for_each_entry_safe(cl, n, next, &ohash[i], hnode) {
370 h = qdisc_class_hash(cl->classid, nmask);
371 hlist_add_head(&cl->hnode, &nhash[h]);
372 }
373 }
374 clhash->hash = nhash;
375 clhash->hashsize = nsize;
376 clhash->hashmask = nmask;
377 sch_tree_unlock(sch);
378
379 qdisc_class_hash_free(ohash, osize);
380}
381EXPORT_SYMBOL(qdisc_class_hash_grow);
382
383int qdisc_class_hash_init(struct Qdisc_class_hash *clhash)
384{
385 unsigned int size = 4;
386
387 clhash->hash = qdisc_class_hash_alloc(size);
388 if (clhash->hash == NULL)
389 return -ENOMEM;
390 clhash->hashsize = size;
391 clhash->hashmask = size - 1;
392 clhash->hashelems = 0;
393 return 0;
394}
395EXPORT_SYMBOL(qdisc_class_hash_init);
396
397void qdisc_class_hash_destroy(struct Qdisc_class_hash *clhash)
398{
399 qdisc_class_hash_free(clhash->hash, clhash->hashsize);
400}
401EXPORT_SYMBOL(qdisc_class_hash_destroy);
402
403void qdisc_class_hash_insert(struct Qdisc_class_hash *clhash,
404 struct Qdisc_class_common *cl)
405{
406 unsigned int h;
407
408 INIT_HLIST_NODE(&cl->hnode);
409 h = qdisc_class_hash(cl->classid, clhash->hashmask);
410 hlist_add_head(&cl->hnode, &clhash->hash[h]);
411 clhash->hashelems++;
412}
413EXPORT_SYMBOL(qdisc_class_hash_insert);
414
415void qdisc_class_hash_remove(struct Qdisc_class_hash *clhash,
416 struct Qdisc_class_common *cl)
417{
418 hlist_del(&cl->hnode);
419 clhash->hashelems--;
420}
421EXPORT_SYMBOL(qdisc_class_hash_remove);
422
319/* Allocate an unique handle from space managed by kernel */ 423/* Allocate an unique handle from space managed by kernel */
320 424
321static u32 qdisc_alloc_handle(struct net_device *dev) 425static u32 qdisc_alloc_handle(struct net_device *dev)