Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: sched: convert qdisc linked list to hashtable

Convert the per-device linked list into a hashtable. The primary
motivation for this change is that currently, we're not tracking all the
qdiscs in hierarchy (e.g. excluding default qdiscs), as the lookup
performed over the linked list by qdisc_match_from_root() is rather
expensive.

The ultimate goal is to get rid of hidden qdiscs completely, which will
bring much more determinism in user experience.

Reviewed-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Jiri Kosina and committed by
David S. Miller
59cc1f61 e87a8f24

+30 -18
+4
include/linux/netdevice.h
··· 52 52 #include <uapi/linux/netdevice.h> 53 53 #include <uapi/linux/if_bonding.h> 54 54 #include <uapi/linux/pkt_cls.h> 55 + #include <linux/hashtable.h> 55 56 56 57 struct netpoll_info; 57 58 struct device; ··· 1801 1800 unsigned int num_tx_queues; 1802 1801 unsigned int real_num_tx_queues; 1803 1802 struct Qdisc *qdisc; 1803 + #ifdef CONFIG_NET_SCHED 1804 + DECLARE_HASHTABLE (qdisc_hash, 4); 1805 + #endif 1804 1806 unsigned long tx_queue_len; 1805 1807 spinlock_t tx_global_lock; 1806 1808 int watchdog_timeo;
+2 -2
include/net/pkt_sched.h
··· 90 90 void qdisc_get_default(char *id, size_t len); 91 91 int qdisc_set_default(const char *id); 92 92 93 - void qdisc_list_add(struct Qdisc *q); 94 - void qdisc_list_del(struct Qdisc *q); 93 + void qdisc_hash_add(struct Qdisc *q); 94 + void qdisc_hash_del(struct Qdisc *q); 95 95 struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle); 96 96 struct Qdisc *qdisc_lookup_class(struct net_device *dev, u32 handle); 97 97 struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
+1 -1
include/net/sch_generic.h
··· 61 61 u32 limit; 62 62 const struct Qdisc_ops *ops; 63 63 struct qdisc_size_table __rcu *stab; 64 - struct list_head list; 64 + struct hlist_node hash; 65 65 u32 handle; 66 66 u32 parent; 67 67 void *u32_node;
+3
net/core/dev.c
··· 7629 7629 INIT_LIST_HEAD(&dev->all_adj_list.lower); 7630 7630 INIT_LIST_HEAD(&dev->ptype_all); 7631 7631 INIT_LIST_HEAD(&dev->ptype_specific); 7632 + #ifdef CONFIG_NET_SCHED 7633 + hash_init(dev->qdisc_hash); 7634 + #endif 7632 7635 dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM; 7633 7636 setup(dev); 7634 7637
+13 -10
net/sched/sch_api.c
··· 29 29 #include <linux/hrtimer.h> 30 30 #include <linux/lockdep.h> 31 31 #include <linux/slab.h> 32 + #include <linux/hashtable.h> 32 33 33 34 #include <net/net_namespace.h> 34 35 #include <net/sock.h> ··· 264 263 root->handle == handle) 265 264 return root; 266 265 267 - list_for_each_entry_rcu(q, &root->list, list) { 266 + hash_for_each_possible_rcu(qdisc_dev(root)->qdisc_hash, q, hash, handle) { 268 267 if (q->handle == handle) 269 268 return q; 270 269 } 271 270 return NULL; 272 271 } 273 272 274 - void qdisc_list_add(struct Qdisc *q) 273 + void qdisc_hash_add(struct Qdisc *q) 275 274 { 276 275 if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) { 277 276 struct Qdisc *root = qdisc_dev(q)->qdisc; 278 277 279 278 WARN_ON_ONCE(root == &noop_qdisc); 280 279 ASSERT_RTNL(); 281 - list_add_tail_rcu(&q->list, &root->list); 280 + hash_add_rcu(qdisc_dev(q)->qdisc_hash, &q->hash, q->handle); 282 281 } 283 282 } 284 - EXPORT_SYMBOL(qdisc_list_add); 283 + EXPORT_SYMBOL(qdisc_hash_add); 285 284 286 - void qdisc_list_del(struct Qdisc *q) 285 + void qdisc_hash_del(struct Qdisc *q) 287 286 { 288 287 if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) { 289 288 ASSERT_RTNL(); 290 - list_del_rcu(&q->list); 289 + hash_del_rcu(&q->hash); 291 290 } 292 291 } 293 - EXPORT_SYMBOL(qdisc_list_del); 292 + EXPORT_SYMBOL(qdisc_hash_del); 294 293 295 294 struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) 296 295 { ··· 999 998 goto err_out4; 1000 999 } 1001 1000 1002 - qdisc_list_add(sch); 1001 + qdisc_hash_add(sch); 1003 1002 1004 1003 return sch; 1005 1004 } ··· 1436 1435 { 1437 1436 int ret = 0, q_idx = *q_idx_p; 1438 1437 struct Qdisc *q; 1438 + int b; 1439 1439 1440 1440 if (!root) 1441 1441 return 0; ··· 1451 1449 goto done; 1452 1450 q_idx++; 1453 1451 } 1454 - list_for_each_entry(q, &root->list, list) { 1452 + hash_for_each(qdisc_dev(root)->qdisc_hash, b, q, hash) { 1455 1453 if (q_idx < s_q_idx) { 1456 1454 q_idx++; 1457 1455 continue; ··· 1767 1765 int *t_p, int s_t) 1768 1766 { 1769 1767 struct Qdisc *q; 1768 + int b; 1770 1769 1771 1770 if (!root) 1772 1771 return 0; ··· 1775 1772 if (tc_dump_tclass_qdisc(root, skb, tcm, cb, t_p, s_t) < 0) 1776 1773 return -1; 1777 1774 1778 - list_for_each_entry(q, &root->list, list) { 1775 + hash_for_each(qdisc_dev(root)->qdisc_hash, b, q, hash) { 1779 1776 if (tc_dump_tclass_qdisc(q, skb, tcm, cb, t_p, s_t) < 0) 1780 1777 return -1; 1781 1778 }
+5 -3
net/sched/sch_generic.c
··· 423 423 .dequeue = noop_dequeue, 424 424 .flags = TCQ_F_BUILTIN, 425 425 .ops = &noop_qdisc_ops, 426 - .list = LIST_HEAD_INIT(noop_qdisc.list), 427 426 .q.lock = __SPIN_LOCK_UNLOCKED(noop_qdisc.q.lock), 428 427 .dev_queue = &noop_netdev_queue, 429 428 .running = SEQCNT_ZERO(noop_qdisc.running), ··· 612 613 sch = (struct Qdisc *) QDISC_ALIGN((unsigned long) p); 613 614 sch->padded = (char *) sch - (char *) p; 614 615 } 615 - INIT_LIST_HEAD(&sch->list); 616 616 skb_queue_head_init(&sch->q); 617 617 618 618 spin_lock_init(&sch->busylock); ··· 698 700 return; 699 701 700 702 #ifdef CONFIG_NET_SCHED 701 - qdisc_list_del(qdisc); 703 + qdisc_hash_del(qdisc); 702 704 703 705 qdisc_put_stab(rtnl_dereference(qdisc->stab)); 704 706 #endif ··· 786 788 qdisc->ops->attach(qdisc); 787 789 } 788 790 } 791 + #ifdef CONFIG_NET_SCHED 792 + if (dev->qdisc) 793 + qdisc_hash_add(dev->qdisc); 794 + #endif 789 795 } 790 796 791 797 static void transition_one_qdisc(struct net_device *dev,
+1 -1
net/sched/sch_mq.c
··· 88 88 qdisc_destroy(old); 89 89 #ifdef CONFIG_NET_SCHED 90 90 if (ntx < dev->real_num_tx_queues) 91 - qdisc_list_add(qdisc); 91 + qdisc_hash_add(qdisc); 92 92 #endif 93 93 94 94 }
+1 -1
net/sched/sch_mqprio.c
··· 182 182 if (old) 183 183 qdisc_destroy(old); 184 184 if (ntx < dev->real_num_tx_queues) 185 - qdisc_list_add(qdisc); 185 + qdisc_hash_add(qdisc); 186 186 } 187 187 kfree(priv->qdiscs); 188 188 priv->qdiscs = NULL;