diff options
author | Patrick McHardy <kaber@trash.net> | 2009-09-04 02:41:18 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-09-06 05:07:03 -0400 |
commit | af356afa010f3cd2c8b8fcc3bce90f7a7b7ec02a (patch) | |
tree | 302d938363bbaec3e69a58e36dbf8a304b24144c | |
parent | 5b9a9ccfad8553dbf7a9b17ba78bad70215ed0e2 (diff) |
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>
-rw-r--r-- | include/linux/netdevice.h | 3 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 6 | ||||
-rw-r--r-- | net/sched/cls_api.c | 7 | ||||
-rw-r--r-- | net/sched/sch_api.c | 41 | ||||
-rw-r--r-- | net/sched/sch_generic.c | 25 |
5 files changed, 34 insertions, 48 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 121cbad0aae5..a44118b1b56c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -832,6 +832,9 @@ struct net_device | |||
832 | /* Number of TX queues currently active in device */ | 832 | /* Number of TX queues currently active in device */ |
833 | unsigned int real_num_tx_queues; | 833 | unsigned int real_num_tx_queues; |
834 | 834 | ||
835 | /* root qdisc from userspace point of view */ | ||
836 | struct Qdisc *qdisc; | ||
837 | |||
835 | unsigned long tx_queue_len; /* Max frames per queue allowed */ | 838 | unsigned long tx_queue_len; /* Max frames per queue allowed */ |
836 | spinlock_t tx_global_lock; | 839 | spinlock_t tx_global_lock; |
837 | /* | 840 | /* |
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index bbcba2a41018..eb42873f2a3a 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -606,7 +606,6 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
606 | int type, u32 pid, u32 seq, u32 change, | 606 | int type, u32 pid, u32 seq, u32 change, |
607 | unsigned int flags) | 607 | unsigned int flags) |
608 | { | 608 | { |
609 | struct netdev_queue *txq; | ||
610 | struct ifinfomsg *ifm; | 609 | struct ifinfomsg *ifm; |
611 | struct nlmsghdr *nlh; | 610 | struct nlmsghdr *nlh; |
612 | const struct net_device_stats *stats; | 611 | const struct net_device_stats *stats; |
@@ -637,9 +636,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
637 | if (dev->master) | 636 | if (dev->master) |
638 | NLA_PUT_U32(skb, IFLA_MASTER, dev->master->ifindex); | 637 | NLA_PUT_U32(skb, IFLA_MASTER, dev->master->ifindex); |
639 | 638 | ||
640 | txq = netdev_get_tx_queue(dev, 0); | 639 | if (dev->qdisc) |
641 | if (txq->qdisc_sleeping) | 640 | NLA_PUT_STRING(skb, IFLA_QDISC, dev->qdisc->ops->id); |
642 | NLA_PUT_STRING(skb, IFLA_QDISC, txq->qdisc_sleeping->ops->id); | ||
643 | 641 | ||
644 | if (dev->ifalias) | 642 | if (dev->ifalias) |
645 | NLA_PUT_STRING(skb, IFLA_IFALIAS, dev->ifalias); | 643 | NLA_PUT_STRING(skb, IFLA_IFALIAS, dev->ifalias); |
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index bcfbdb4758c9..6a536949cdc0 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c | |||
@@ -168,8 +168,7 @@ replay: | |||
168 | 168 | ||
169 | /* Find qdisc */ | 169 | /* Find qdisc */ |
170 | if (!parent) { | 170 | if (!parent) { |
171 | struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, 0); | 171 | q = dev->qdisc; |
172 | q = dev_queue->qdisc_sleeping; | ||
173 | parent = q->handle; | 172 | parent = q->handle; |
174 | } else { | 173 | } else { |
175 | q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent)); | 174 | 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 | static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) | 407 | static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) |
409 | { | 408 | { |
410 | struct net *net = sock_net(skb->sk); | 409 | struct net *net = sock_net(skb->sk); |
411 | struct netdev_queue *dev_queue; | ||
412 | int t; | 410 | int t; |
413 | int s_t; | 411 | int s_t; |
414 | struct net_device *dev; | 412 | struct net_device *dev; |
@@ -427,9 +425,8 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) | |||
427 | if ((dev = dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL) | 425 | if ((dev = dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL) |
428 | return skb->len; | 426 | return skb->len; |
429 | 427 | ||
430 | dev_queue = netdev_get_tx_queue(dev, 0); | ||
431 | if (!tcm->tcm_parent) | 428 | if (!tcm->tcm_parent) |
432 | q = dev_queue->qdisc_sleeping; | 429 | q = dev->qdisc; |
433 | else | 430 | else |
434 | q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); | 431 | q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); |
435 | if (!q) | 432 | if (!q) |
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 166fcca86e7a..8aa9a0c5a9eb 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c | |||
@@ -207,7 +207,7 @@ static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle) | |||
207 | static void qdisc_list_add(struct Qdisc *q) | 207 | static void qdisc_list_add(struct Qdisc *q) |
208 | { | 208 | { |
209 | if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) | 209 | if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) |
210 | list_add_tail(&q->list, &qdisc_root_sleeping(q)->list); | 210 | list_add_tail(&q->list, &qdisc_dev(q)->qdisc->list); |
211 | } | 211 | } |
212 | 212 | ||
213 | void qdisc_list_del(struct Qdisc *q) | 213 | void qdisc_list_del(struct Qdisc *q) |
@@ -219,17 +219,11 @@ EXPORT_SYMBOL(qdisc_list_del); | |||
219 | 219 | ||
220 | struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) | 220 | struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) |
221 | { | 221 | { |
222 | unsigned int i; | ||
223 | struct Qdisc *q; | 222 | struct Qdisc *q; |
224 | 223 | ||
225 | for (i = 0; i < dev->num_tx_queues; i++) { | 224 | q = qdisc_match_from_root(dev->qdisc, handle); |
226 | struct netdev_queue *txq = netdev_get_tx_queue(dev, i); | 225 | if (q) |
227 | struct Qdisc *txq_root = txq->qdisc_sleeping; | 226 | goto out; |
228 | |||
229 | q = qdisc_match_from_root(txq_root, handle); | ||
230 | if (q) | ||
231 | goto out; | ||
232 | } | ||
233 | 227 | ||
234 | q = qdisc_match_from_root(dev->rx_queue.qdisc_sleeping, handle); | 228 | q = qdisc_match_from_root(dev->rx_queue.qdisc_sleeping, handle); |
235 | out: | 229 | out: |
@@ -720,9 +714,14 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, | |||
720 | if (new && i > 0) | 714 | if (new && i > 0) |
721 | atomic_inc(&new->refcnt); | 715 | atomic_inc(&new->refcnt); |
722 | 716 | ||
723 | notify_and_destroy(skb, n, classid, old, new); | 717 | qdisc_destroy(old); |
724 | } | 718 | } |
725 | 719 | ||
720 | notify_and_destroy(skb, n, classid, dev->qdisc, new); | ||
721 | if (new) | ||
722 | atomic_inc(&new->refcnt); | ||
723 | dev->qdisc = new ? : &noop_qdisc; | ||
724 | |||
726 | if (dev->flags & IFF_UP) | 725 | if (dev->flags & IFF_UP) |
727 | dev_activate(dev); | 726 | dev_activate(dev); |
728 | } else { | 727 | } else { |
@@ -974,9 +973,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg) | |||
974 | q = dev->rx_queue.qdisc_sleeping; | 973 | q = dev->rx_queue.qdisc_sleeping; |
975 | } | 974 | } |
976 | } else { | 975 | } else { |
977 | struct netdev_queue *dev_queue; | 976 | q = dev->qdisc; |
978 | dev_queue = netdev_get_tx_queue(dev, 0); | ||
979 | q = dev_queue->qdisc_sleeping; | ||
980 | } | 977 | } |
981 | if (!q) | 978 | if (!q) |
982 | return -ENOENT; | 979 | return -ENOENT; |
@@ -1044,9 +1041,7 @@ replay: | |||
1044 | q = dev->rx_queue.qdisc_sleeping; | 1041 | q = dev->rx_queue.qdisc_sleeping; |
1045 | } | 1042 | } |
1046 | } else { | 1043 | } else { |
1047 | struct netdev_queue *dev_queue; | 1044 | q = dev->qdisc; |
1048 | dev_queue = netdev_get_tx_queue(dev, 0); | ||
1049 | q = dev_queue->qdisc_sleeping; | ||
1050 | } | 1045 | } |
1051 | 1046 | ||
1052 | /* It may be default qdisc, ignore it */ | 1047 | /* 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 | s_q_idx = 0; | 1286 | s_q_idx = 0; |
1292 | q_idx = 0; | 1287 | q_idx = 0; |
1293 | 1288 | ||
1294 | dev_queue = netdev_get_tx_queue(dev, 0); | 1289 | if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx) < 0) |
1295 | if (tc_dump_qdisc_root(dev_queue->qdisc_sleeping, skb, cb, &q_idx, s_q_idx) < 0) | ||
1296 | goto done; | 1290 | goto done; |
1297 | 1291 | ||
1298 | dev_queue = &dev->rx_queue; | 1292 | dev_queue = &dev->rx_queue; |
@@ -1323,7 +1317,6 @@ done: | |||
1323 | static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) | 1317 | static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) |
1324 | { | 1318 | { |
1325 | struct net *net = sock_net(skb->sk); | 1319 | struct net *net = sock_net(skb->sk); |
1326 | struct netdev_queue *dev_queue; | ||
1327 | struct tcmsg *tcm = NLMSG_DATA(n); | 1320 | struct tcmsg *tcm = NLMSG_DATA(n); |
1328 | struct nlattr *tca[TCA_MAX + 1]; | 1321 | struct nlattr *tca[TCA_MAX + 1]; |
1329 | struct net_device *dev; | 1322 | struct net_device *dev; |
@@ -1361,7 +1354,6 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) | |||
1361 | 1354 | ||
1362 | /* Step 1. Determine qdisc handle X:0 */ | 1355 | /* Step 1. Determine qdisc handle X:0 */ |
1363 | 1356 | ||
1364 | dev_queue = netdev_get_tx_queue(dev, 0); | ||
1365 | if (pid != TC_H_ROOT) { | 1357 | if (pid != TC_H_ROOT) { |
1366 | u32 qid1 = TC_H_MAJ(pid); | 1358 | u32 qid1 = TC_H_MAJ(pid); |
1367 | 1359 | ||
@@ -1372,7 +1364,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) | |||
1372 | } else if (qid1) { | 1364 | } else if (qid1) { |
1373 | qid = qid1; | 1365 | qid = qid1; |
1374 | } else if (qid == 0) | 1366 | } else if (qid == 0) |
1375 | qid = dev_queue->qdisc_sleeping->handle; | 1367 | qid = dev->qdisc->handle; |
1376 | 1368 | ||
1377 | /* Now qid is genuine qdisc handle consistent | 1369 | /* Now qid is genuine qdisc handle consistent |
1378 | both with parent and child. | 1370 | both with parent and child. |
@@ -1383,7 +1375,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) | |||
1383 | pid = TC_H_MAKE(qid, pid); | 1375 | pid = TC_H_MAKE(qid, pid); |
1384 | } else { | 1376 | } else { |
1385 | if (qid == 0) | 1377 | if (qid == 0) |
1386 | qid = dev_queue->qdisc_sleeping->handle; | 1378 | qid = dev->qdisc->handle; |
1387 | } | 1379 | } |
1388 | 1380 | ||
1389 | /* OK. Locate qdisc */ | 1381 | /* OK. Locate qdisc */ |
@@ -1588,8 +1580,7 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) | |||
1588 | s_t = cb->args[0]; | 1580 | s_t = cb->args[0]; |
1589 | t = 0; | 1581 | t = 0; |
1590 | 1582 | ||
1591 | dev_queue = netdev_get_tx_queue(dev, 0); | 1583 | if (tc_dump_tclass_root(dev->qdisc, skb, tcm, cb, &t, s_t) < 0) |
1592 | if (tc_dump_tclass_root(dev_queue->qdisc_sleeping, skb, tcm, cb, &t, s_t) < 0) | ||
1593 | goto done; | 1584 | goto done; |
1594 | 1585 | ||
1595 | dev_queue = &dev->rx_queue; | 1586 | dev_queue = &dev->rx_queue; |
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 6128e6f24589..a91f079fb47a 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c | |||
@@ -623,19 +623,6 @@ void qdisc_destroy(struct Qdisc *qdisc) | |||
623 | } | 623 | } |
624 | EXPORT_SYMBOL(qdisc_destroy); | 624 | EXPORT_SYMBOL(qdisc_destroy); |
625 | 625 | ||
626 | static bool dev_all_qdisc_sleeping_noop(struct net_device *dev) | ||
627 | { | ||
628 | unsigned int i; | ||
629 | |||
630 | for (i = 0; i < dev->num_tx_queues; i++) { | ||
631 | struct netdev_queue *txq = netdev_get_tx_queue(dev, i); | ||
632 | |||
633 | if (txq->qdisc_sleeping != &noop_qdisc) | ||
634 | return false; | ||
635 | } | ||
636 | return true; | ||
637 | } | ||
638 | |||
639 | static void attach_one_default_qdisc(struct net_device *dev, | 626 | static void attach_one_default_qdisc(struct net_device *dev, |
640 | struct netdev_queue *dev_queue, | 627 | struct netdev_queue *dev_queue, |
641 | void *_unused) | 628 | void *_unused) |
@@ -677,6 +664,7 @@ static void transition_one_qdisc(struct net_device *dev, | |||
677 | 664 | ||
678 | void dev_activate(struct net_device *dev) | 665 | void dev_activate(struct net_device *dev) |
679 | { | 666 | { |
667 | struct netdev_queue *txq; | ||
680 | int need_watchdog; | 668 | int need_watchdog; |
681 | 669 | ||
682 | /* No queueing discipline is attached to device; | 670 | /* No queueing discipline is attached to device; |
@@ -685,9 +673,14 @@ void dev_activate(struct net_device *dev) | |||
685 | virtual interfaces | 673 | virtual interfaces |
686 | */ | 674 | */ |
687 | 675 | ||
688 | if (dev_all_qdisc_sleeping_noop(dev)) | 676 | if (dev->qdisc == &noop_qdisc) { |
689 | netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); | 677 | netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); |
690 | 678 | ||
679 | txq = netdev_get_tx_queue(dev, 0); | ||
680 | dev->qdisc = txq->qdisc_sleeping; | ||
681 | atomic_inc(&dev->qdisc->refcnt); | ||
682 | } | ||
683 | |||
691 | if (!netif_carrier_ok(dev)) | 684 | if (!netif_carrier_ok(dev)) |
692 | /* Delay activation until next carrier-on event */ | 685 | /* Delay activation until next carrier-on event */ |
693 | return; | 686 | return; |
@@ -777,6 +770,7 @@ static void dev_init_scheduler_queue(struct net_device *dev, | |||
777 | 770 | ||
778 | void dev_init_scheduler(struct net_device *dev) | 771 | void dev_init_scheduler(struct net_device *dev) |
779 | { | 772 | { |
773 | dev->qdisc = &noop_qdisc; | ||
780 | netdev_for_each_tx_queue(dev, dev_init_scheduler_queue, &noop_qdisc); | 774 | netdev_for_each_tx_queue(dev, dev_init_scheduler_queue, &noop_qdisc); |
781 | dev_init_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc); | 775 | dev_init_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc); |
782 | 776 | ||
@@ -802,5 +796,8 @@ void dev_shutdown(struct net_device *dev) | |||
802 | { | 796 | { |
803 | netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc); | 797 | netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc); |
804 | shutdown_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc); | 798 | shutdown_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc); |
799 | qdisc_destroy(dev->qdisc); | ||
800 | dev->qdisc = &noop_qdisc; | ||
801 | |||
805 | WARN_ON(timer_pending(&dev->watchdog_timer)); | 802 | WARN_ON(timer_pending(&dev->watchdog_timer)); |
806 | } | 803 | } |