forked from mirrors/linux
		
	net_sched: reintroduce dev->qdisc for use by sch_api
Currently the multiqueue integration with the qdisc API suffers from a few problems: - with multiple queues, all root qdiscs use the same handle. This means they can't be exposed to userspace in a backwards compatible fashion. - all API operations always refer to queue number 0. Newly created qdiscs are automatically shared between all queues, its not possible to address individual queues or restore multiqueue behaviour once a shared qdisc has been attached. - Dumps only contain the root qdisc of queue 0, in case of non-shared qdiscs this means the statistics are incomplete. This patch reintroduces dev->qdisc, which points to the (single) root qdisc from userspace's point of view. Currently it either points to the first (non-shared) default qdisc, or a qdisc shared between all queues. The following patches will introduce a classful dummy qdisc, which will be used as root qdisc and contain the per-queue qdiscs as children. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									5b9a9ccfad
								
							
						
					
					
						commit
						af356afa01
					
				
					 5 changed files with 34 additions and 48 deletions
				
			
		| 
						 | 
					@ -832,6 +832,9 @@ struct net_device
 | 
				
			||||||
	/* Number of TX queues currently active in device  */
 | 
						/* Number of TX queues currently active in device  */
 | 
				
			||||||
	unsigned int		real_num_tx_queues;
 | 
						unsigned int		real_num_tx_queues;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* root qdisc from userspace point of view */
 | 
				
			||||||
 | 
						struct Qdisc		*qdisc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unsigned long		tx_queue_len;	/* Max frames per queue allowed */
 | 
						unsigned long		tx_queue_len;	/* Max frames per queue allowed */
 | 
				
			||||||
	spinlock_t		tx_global_lock;
 | 
						spinlock_t		tx_global_lock;
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -606,7 +606,6 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
 | 
				
			||||||
			    int type, u32 pid, u32 seq, u32 change,
 | 
								    int type, u32 pid, u32 seq, u32 change,
 | 
				
			||||||
			    unsigned int flags)
 | 
								    unsigned int flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct netdev_queue *txq;
 | 
					 | 
				
			||||||
	struct ifinfomsg *ifm;
 | 
						struct ifinfomsg *ifm;
 | 
				
			||||||
	struct nlmsghdr *nlh;
 | 
						struct nlmsghdr *nlh;
 | 
				
			||||||
	const struct net_device_stats *stats;
 | 
						const struct net_device_stats *stats;
 | 
				
			||||||
| 
						 | 
					@ -637,9 +636,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
 | 
				
			||||||
	if (dev->master)
 | 
						if (dev->master)
 | 
				
			||||||
		NLA_PUT_U32(skb, IFLA_MASTER, dev->master->ifindex);
 | 
							NLA_PUT_U32(skb, IFLA_MASTER, dev->master->ifindex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	txq = netdev_get_tx_queue(dev, 0);
 | 
						if (dev->qdisc)
 | 
				
			||||||
	if (txq->qdisc_sleeping)
 | 
							NLA_PUT_STRING(skb, IFLA_QDISC, dev->qdisc->ops->id);
 | 
				
			||||||
		NLA_PUT_STRING(skb, IFLA_QDISC, txq->qdisc_sleeping->ops->id);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dev->ifalias)
 | 
						if (dev->ifalias)
 | 
				
			||||||
		NLA_PUT_STRING(skb, IFLA_IFALIAS, dev->ifalias);
 | 
							NLA_PUT_STRING(skb, IFLA_IFALIAS, dev->ifalias);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -168,8 +168,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Find qdisc */
 | 
						/* Find qdisc */
 | 
				
			||||||
	if (!parent) {
 | 
						if (!parent) {
 | 
				
			||||||
		struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, 0);
 | 
							q = dev->qdisc;
 | 
				
			||||||
		q = dev_queue->qdisc_sleeping;
 | 
					 | 
				
			||||||
		parent = q->handle;
 | 
							parent = q->handle;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent));
 | 
							q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent));
 | 
				
			||||||
| 
						 | 
					@ -408,7 +407,6 @@ static int tcf_node_dump(struct tcf_proto *tp, unsigned long n,
 | 
				
			||||||
static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
 | 
					static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct net *net = sock_net(skb->sk);
 | 
						struct net *net = sock_net(skb->sk);
 | 
				
			||||||
	struct netdev_queue *dev_queue;
 | 
					 | 
				
			||||||
	int t;
 | 
						int t;
 | 
				
			||||||
	int s_t;
 | 
						int s_t;
 | 
				
			||||||
	struct net_device *dev;
 | 
						struct net_device *dev;
 | 
				
			||||||
| 
						 | 
					@ -427,9 +425,8 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
 | 
				
			||||||
	if ((dev = dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL)
 | 
						if ((dev = dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL)
 | 
				
			||||||
		return skb->len;
 | 
							return skb->len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev_queue = netdev_get_tx_queue(dev, 0);
 | 
					 | 
				
			||||||
	if (!tcm->tcm_parent)
 | 
						if (!tcm->tcm_parent)
 | 
				
			||||||
		q = dev_queue->qdisc_sleeping;
 | 
							q = dev->qdisc;
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
 | 
							q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
 | 
				
			||||||
	if (!q)
 | 
						if (!q)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -207,7 +207,7 @@ static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle)
 | 
				
			||||||
static void qdisc_list_add(struct Qdisc *q)
 | 
					static void qdisc_list_add(struct Qdisc *q)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS))
 | 
						if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS))
 | 
				
			||||||
		list_add_tail(&q->list, &qdisc_root_sleeping(q)->list);
 | 
							list_add_tail(&q->list, &qdisc_dev(q)->qdisc->list);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void qdisc_list_del(struct Qdisc *q)
 | 
					void qdisc_list_del(struct Qdisc *q)
 | 
				
			||||||
| 
						 | 
					@ -219,17 +219,11 @@ EXPORT_SYMBOL(qdisc_list_del);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle)
 | 
					struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned int i;
 | 
					 | 
				
			||||||
	struct Qdisc *q;
 | 
						struct Qdisc *q;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < dev->num_tx_queues; i++) {
 | 
						q = qdisc_match_from_root(dev->qdisc, handle);
 | 
				
			||||||
		struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
 | 
						if (q)
 | 
				
			||||||
		struct Qdisc *txq_root = txq->qdisc_sleeping;
 | 
							goto out;
 | 
				
			||||||
 | 
					 | 
				
			||||||
		q = qdisc_match_from_root(txq_root, handle);
 | 
					 | 
				
			||||||
		if (q)
 | 
					 | 
				
			||||||
			goto out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	q = qdisc_match_from_root(dev->rx_queue.qdisc_sleeping, handle);
 | 
						q = qdisc_match_from_root(dev->rx_queue.qdisc_sleeping, handle);
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
| 
						 | 
					@ -720,9 +714,14 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
 | 
				
			||||||
			if (new && i > 0)
 | 
								if (new && i > 0)
 | 
				
			||||||
				atomic_inc(&new->refcnt);
 | 
									atomic_inc(&new->refcnt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			notify_and_destroy(skb, n, classid, old, new);
 | 
								qdisc_destroy(old);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							notify_and_destroy(skb, n, classid, dev->qdisc, new);
 | 
				
			||||||
 | 
							if (new)
 | 
				
			||||||
 | 
								atomic_inc(&new->refcnt);
 | 
				
			||||||
 | 
							dev->qdisc = new ? : &noop_qdisc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (dev->flags & IFF_UP)
 | 
							if (dev->flags & IFF_UP)
 | 
				
			||||||
			dev_activate(dev);
 | 
								dev_activate(dev);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
| 
						 | 
					@ -974,9 +973,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
 | 
				
			||||||
				q = dev->rx_queue.qdisc_sleeping;
 | 
									q = dev->rx_queue.qdisc_sleeping;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			struct netdev_queue *dev_queue;
 | 
								q = dev->qdisc;
 | 
				
			||||||
			dev_queue = netdev_get_tx_queue(dev, 0);
 | 
					 | 
				
			||||||
			q = dev_queue->qdisc_sleeping;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (!q)
 | 
							if (!q)
 | 
				
			||||||
			return -ENOENT;
 | 
								return -ENOENT;
 | 
				
			||||||
| 
						 | 
					@ -1044,9 +1041,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
 | 
				
			||||||
				q = dev->rx_queue.qdisc_sleeping;
 | 
									q = dev->rx_queue.qdisc_sleeping;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			struct netdev_queue *dev_queue;
 | 
								q = dev->qdisc;
 | 
				
			||||||
			dev_queue = netdev_get_tx_queue(dev, 0);
 | 
					 | 
				
			||||||
			q = dev_queue->qdisc_sleeping;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* It may be default qdisc, ignore it */
 | 
							/* It may be default qdisc, ignore it */
 | 
				
			||||||
| 
						 | 
					@ -1291,8 +1286,7 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
 | 
				
			||||||
			s_q_idx = 0;
 | 
								s_q_idx = 0;
 | 
				
			||||||
		q_idx = 0;
 | 
							q_idx = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dev_queue = netdev_get_tx_queue(dev, 0);
 | 
							if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx) < 0)
 | 
				
			||||||
		if (tc_dump_qdisc_root(dev_queue->qdisc_sleeping, skb, cb, &q_idx, s_q_idx) < 0)
 | 
					 | 
				
			||||||
			goto done;
 | 
								goto done;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dev_queue = &dev->rx_queue;
 | 
							dev_queue = &dev->rx_queue;
 | 
				
			||||||
| 
						 | 
					@ -1323,7 +1317,6 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
 | 
				
			||||||
static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
 | 
					static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct net *net = sock_net(skb->sk);
 | 
						struct net *net = sock_net(skb->sk);
 | 
				
			||||||
	struct netdev_queue *dev_queue;
 | 
					 | 
				
			||||||
	struct tcmsg *tcm = NLMSG_DATA(n);
 | 
						struct tcmsg *tcm = NLMSG_DATA(n);
 | 
				
			||||||
	struct nlattr *tca[TCA_MAX + 1];
 | 
						struct nlattr *tca[TCA_MAX + 1];
 | 
				
			||||||
	struct net_device *dev;
 | 
						struct net_device *dev;
 | 
				
			||||||
| 
						 | 
					@ -1361,7 +1354,6 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Step 1. Determine qdisc handle X:0 */
 | 
						/* Step 1. Determine qdisc handle X:0 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev_queue = netdev_get_tx_queue(dev, 0);
 | 
					 | 
				
			||||||
	if (pid != TC_H_ROOT) {
 | 
						if (pid != TC_H_ROOT) {
 | 
				
			||||||
		u32 qid1 = TC_H_MAJ(pid);
 | 
							u32 qid1 = TC_H_MAJ(pid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1372,7 +1364,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
 | 
				
			||||||
		} else if (qid1) {
 | 
							} else if (qid1) {
 | 
				
			||||||
			qid = qid1;
 | 
								qid = qid1;
 | 
				
			||||||
		} else if (qid == 0)
 | 
							} else if (qid == 0)
 | 
				
			||||||
			qid = dev_queue->qdisc_sleeping->handle;
 | 
								qid = dev->qdisc->handle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Now qid is genuine qdisc handle consistent
 | 
							/* Now qid is genuine qdisc handle consistent
 | 
				
			||||||
		   both with parent and child.
 | 
							   both with parent and child.
 | 
				
			||||||
| 
						 | 
					@ -1383,7 +1375,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
 | 
				
			||||||
			pid = TC_H_MAKE(qid, pid);
 | 
								pid = TC_H_MAKE(qid, pid);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if (qid == 0)
 | 
							if (qid == 0)
 | 
				
			||||||
			qid = dev_queue->qdisc_sleeping->handle;
 | 
								qid = dev->qdisc->handle;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* OK. Locate qdisc */
 | 
						/* OK. Locate qdisc */
 | 
				
			||||||
| 
						 | 
					@ -1588,8 +1580,7 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb)
 | 
				
			||||||
	s_t = cb->args[0];
 | 
						s_t = cb->args[0];
 | 
				
			||||||
	t = 0;
 | 
						t = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev_queue = netdev_get_tx_queue(dev, 0);
 | 
						if (tc_dump_tclass_root(dev->qdisc, skb, tcm, cb, &t, s_t) < 0)
 | 
				
			||||||
	if (tc_dump_tclass_root(dev_queue->qdisc_sleeping, skb, tcm, cb, &t, s_t) < 0)
 | 
					 | 
				
			||||||
		goto done;
 | 
							goto done;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev_queue = &dev->rx_queue;
 | 
						dev_queue = &dev->rx_queue;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -623,19 +623,6 @@ void qdisc_destroy(struct Qdisc *qdisc)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(qdisc_destroy);
 | 
					EXPORT_SYMBOL(qdisc_destroy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool dev_all_qdisc_sleeping_noop(struct net_device *dev)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	unsigned int i;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (i = 0; i < dev->num_tx_queues; i++) {
 | 
					 | 
				
			||||||
		struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (txq->qdisc_sleeping != &noop_qdisc)
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void attach_one_default_qdisc(struct net_device *dev,
 | 
					static void attach_one_default_qdisc(struct net_device *dev,
 | 
				
			||||||
				     struct netdev_queue *dev_queue,
 | 
									     struct netdev_queue *dev_queue,
 | 
				
			||||||
				     void *_unused)
 | 
									     void *_unused)
 | 
				
			||||||
| 
						 | 
					@ -677,6 +664,7 @@ static void transition_one_qdisc(struct net_device *dev,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dev_activate(struct net_device *dev)
 | 
					void dev_activate(struct net_device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct netdev_queue *txq;
 | 
				
			||||||
	int need_watchdog;
 | 
						int need_watchdog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* No queueing discipline is attached to device;
 | 
						/* No queueing discipline is attached to device;
 | 
				
			||||||
| 
						 | 
					@ -685,9 +673,14 @@ void dev_activate(struct net_device *dev)
 | 
				
			||||||
	   virtual interfaces
 | 
						   virtual interfaces
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dev_all_qdisc_sleeping_noop(dev))
 | 
						if (dev->qdisc == &noop_qdisc) {
 | 
				
			||||||
		netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL);
 | 
							netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							txq = netdev_get_tx_queue(dev, 0);
 | 
				
			||||||
 | 
							dev->qdisc = txq->qdisc_sleeping;
 | 
				
			||||||
 | 
							atomic_inc(&dev->qdisc->refcnt);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!netif_carrier_ok(dev))
 | 
						if (!netif_carrier_ok(dev))
 | 
				
			||||||
		/* Delay activation until next carrier-on event */
 | 
							/* Delay activation until next carrier-on event */
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -777,6 +770,7 @@ static void dev_init_scheduler_queue(struct net_device *dev,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dev_init_scheduler(struct net_device *dev)
 | 
					void dev_init_scheduler(struct net_device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						dev->qdisc = &noop_qdisc;
 | 
				
			||||||
	netdev_for_each_tx_queue(dev, dev_init_scheduler_queue, &noop_qdisc);
 | 
						netdev_for_each_tx_queue(dev, dev_init_scheduler_queue, &noop_qdisc);
 | 
				
			||||||
	dev_init_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc);
 | 
						dev_init_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -802,5 +796,8 @@ void dev_shutdown(struct net_device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc);
 | 
						netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc);
 | 
				
			||||||
	shutdown_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc);
 | 
						shutdown_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc);
 | 
				
			||||||
 | 
						qdisc_destroy(dev->qdisc);
 | 
				
			||||||
 | 
						dev->qdisc = &noop_qdisc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	WARN_ON(timer_pending(&dev->watchdog_timer));
 | 
						WARN_ON(timer_pending(&dev->watchdog_timer));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue