diff options
-rw-r--r-- | include/net/sch_generic.h | 42 | ||||
-rw-r--r-- | net/sched/sch_api.c | 104 |
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); | |||
167 | extern struct Qdisc noop_qdisc; | 167 | extern struct Qdisc noop_qdisc; |
168 | extern struct Qdisc_ops noop_qdisc_ops; | 168 | extern struct Qdisc_ops noop_qdisc_ops; |
169 | 169 | ||
170 | struct Qdisc_class_common | ||
171 | { | ||
172 | u32 classid; | ||
173 | struct hlist_node hnode; | ||
174 | }; | ||
175 | |||
176 | struct Qdisc_class_hash | ||
177 | { | ||
178 | struct hlist_head *hash; | ||
179 | unsigned int hashsize; | ||
180 | unsigned int hashmask; | ||
181 | unsigned int hashelems; | ||
182 | }; | ||
183 | |||
184 | static 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 | |||
191 | static inline struct Qdisc_class_common * | ||
192 | qdisc_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 | |||
206 | extern int qdisc_class_hash_init(struct Qdisc_class_hash *); | ||
207 | extern void qdisc_class_hash_insert(struct Qdisc_class_hash *, struct Qdisc_class_common *); | ||
208 | extern void qdisc_class_hash_remove(struct Qdisc_class_hash *, struct Qdisc_class_common *); | ||
209 | extern void qdisc_class_hash_grow(struct Qdisc *, struct Qdisc_class_hash *); | ||
210 | extern void qdisc_class_hash_destroy(struct Qdisc_class_hash *); | ||
211 | |||
170 | extern void dev_init_scheduler(struct net_device *dev); | 212 | extern void dev_init_scheduler(struct net_device *dev); |
171 | extern void dev_shutdown(struct net_device *dev); | 213 | extern void dev_shutdown(struct net_device *dev); |
172 | extern void dev_activate(struct net_device *dev); | 214 | extern 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 | } |
317 | EXPORT_SYMBOL(qdisc_watchdog_cancel); | 317 | EXPORT_SYMBOL(qdisc_watchdog_cancel); |
318 | 318 | ||
319 | struct 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 | |||
337 | static 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 | |||
347 | void 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 | } | ||
381 | EXPORT_SYMBOL(qdisc_class_hash_grow); | ||
382 | |||
383 | int 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 | } | ||
395 | EXPORT_SYMBOL(qdisc_class_hash_init); | ||
396 | |||
397 | void qdisc_class_hash_destroy(struct Qdisc_class_hash *clhash) | ||
398 | { | ||
399 | qdisc_class_hash_free(clhash->hash, clhash->hashsize); | ||
400 | } | ||
401 | EXPORT_SYMBOL(qdisc_class_hash_destroy); | ||
402 | |||
403 | void 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 | } | ||
413 | EXPORT_SYMBOL(qdisc_class_hash_insert); | ||
414 | |||
415 | void 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 | } | ||
421 | EXPORT_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 | ||
321 | static u32 qdisc_alloc_handle(struct net_device *dev) | 425 | static u32 qdisc_alloc_handle(struct net_device *dev) |