Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 59cc1f61 authored by Jiri Kosina's avatar Jiri Kosina Committed by David S. Miller
Browse files

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: default avatarCong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e87a8f24
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@
#include <uapi/linux/netdevice.h>
#include <uapi/linux/if_bonding.h>
#include <uapi/linux/pkt_cls.h>
#include <linux/hashtable.h>

struct netpoll_info;
struct device;
@@ -1800,6 +1801,9 @@ struct net_device {
	unsigned int		num_tx_queues;
	unsigned int		real_num_tx_queues;
	struct Qdisc		*qdisc;
#ifdef CONFIG_NET_SCHED
	DECLARE_HASHTABLE	(qdisc_hash, 4);
#endif
	unsigned long		tx_queue_len;
	spinlock_t		tx_global_lock;
	int			watchdog_timeo;
+2 −2
Original line number Diff line number Diff line
@@ -90,8 +90,8 @@ int unregister_qdisc(struct Qdisc_ops *qops);
void qdisc_get_default(char *id, size_t len);
int qdisc_set_default(const char *id);

void qdisc_list_add(struct Qdisc *q);
void qdisc_list_del(struct Qdisc *q);
void qdisc_hash_add(struct Qdisc *q);
void qdisc_hash_del(struct Qdisc *q);
struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle);
struct Qdisc *qdisc_lookup_class(struct net_device *dev, u32 handle);
struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
+1 −1
Original line number Diff line number Diff line
@@ -61,7 +61,7 @@ struct Qdisc {
	u32			limit;
	const struct Qdisc_ops	*ops;
	struct qdisc_size_table	__rcu *stab;
	struct list_head	list;
	struct hlist_node       hash;
	u32			handle;
	u32			parent;
	void			*u32_node;
+3 −0
Original line number Diff line number Diff line
@@ -7629,6 +7629,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
	INIT_LIST_HEAD(&dev->all_adj_list.lower);
	INIT_LIST_HEAD(&dev->ptype_all);
	INIT_LIST_HEAD(&dev->ptype_specific);
#ifdef CONFIG_NET_SCHED
	hash_init(dev->qdisc_hash);
#endif
	dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
	setup(dev);

+13 −10
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <linux/hrtimer.h>
#include <linux/lockdep.h>
#include <linux/slab.h>
#include <linux/hashtable.h>

#include <net/net_namespace.h>
#include <net/sock.h>
@@ -263,33 +264,33 @@ static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle)
	    root->handle == handle)
		return root;

	list_for_each_entry_rcu(q, &root->list, list) {
	hash_for_each_possible_rcu(qdisc_dev(root)->qdisc_hash, q, hash, handle) {
		if (q->handle == handle)
			return q;
	}
	return NULL;
}

void qdisc_list_add(struct Qdisc *q)
void qdisc_hash_add(struct Qdisc *q)
{
	if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) {
		struct Qdisc *root = qdisc_dev(q)->qdisc;

		WARN_ON_ONCE(root == &noop_qdisc);
		ASSERT_RTNL();
		list_add_tail_rcu(&q->list, &root->list);
		hash_add_rcu(qdisc_dev(q)->qdisc_hash, &q->hash, q->handle);
	}
}
EXPORT_SYMBOL(qdisc_list_add);
EXPORT_SYMBOL(qdisc_hash_add);

void qdisc_list_del(struct Qdisc *q)
void qdisc_hash_del(struct Qdisc *q)
{
	if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) {
		ASSERT_RTNL();
		list_del_rcu(&q->list);
		hash_del_rcu(&q->hash);
	}
}
EXPORT_SYMBOL(qdisc_list_del);
EXPORT_SYMBOL(qdisc_hash_del);

struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle)
{
@@ -998,7 +999,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
				goto err_out4;
		}

		qdisc_list_add(sch);
		qdisc_hash_add(sch);

		return sch;
	}
@@ -1435,6 +1436,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
{
	int ret = 0, q_idx = *q_idx_p;
	struct Qdisc *q;
	int b;

	if (!root)
		return 0;
@@ -1449,7 +1451,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
			goto done;
		q_idx++;
	}
	list_for_each_entry(q, &root->list, list) {
	hash_for_each(qdisc_dev(root)->qdisc_hash, b, q, hash) {
		if (q_idx < s_q_idx) {
			q_idx++;
			continue;
@@ -1765,6 +1767,7 @@ static int tc_dump_tclass_root(struct Qdisc *root, struct sk_buff *skb,
			       int *t_p, int s_t)
{
	struct Qdisc *q;
	int b;

	if (!root)
		return 0;
@@ -1772,7 +1775,7 @@ static int tc_dump_tclass_root(struct Qdisc *root, struct sk_buff *skb,
	if (tc_dump_tclass_qdisc(root, skb, tcm, cb, t_p, s_t) < 0)
		return -1;

	list_for_each_entry(q, &root->list, list) {
	hash_for_each(qdisc_dev(root)->qdisc_hash, b, q, hash) {
		if (tc_dump_tclass_qdisc(q, skb, tcm, cb, t_p, s_t) < 0)
			return -1;
	}
Loading