Commit af356afa authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller

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: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5b9a9ccf
...@@ -832,6 +832,9 @@ struct net_device ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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); ...@@ -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);
struct Qdisc *txq_root = txq->qdisc_sleeping;
q = qdisc_match_from_root(txq_root, handle);
if (q) if (q)
goto out; 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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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));
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment