aboutsummaryrefslogtreecommitdiffstats
path: root/net/sched/sch_generic.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2008-07-30 05:37:46 -0400
committerDavid S. Miller <davem@davemloft.net>2008-07-30 05:44:25 -0400
commit8d50b53d66a8a6ae41bafbdcabe401467803f33a (patch)
tree7a23efd740161b5b32d3fd473cf024e1a8931ce1 /net/sched/sch_generic.c
parent38c080ffa9c1b840390832b42ce8621464ab9f97 (diff)
pkt_sched: Fix OOPS on ingress qdisc add.
Bug report from Steven Jan Springl: Issuing the following command causes a kernel oops: tc qdisc add dev eth0 handle ffff: ingress The problem mostly stems from all of the special case handling of ingress qdiscs. So, to fix this, do the grafting operation the same way we do for TX qdiscs. Which means that dev_activate() and dev_deactivate() now do the "qdisc_sleeping <--> qdisc" transitions on dev->rx_queue too. Future simplifications are possible now, mainly because it is impossible for dev_queue->{qdisc,qdisc_sleeping} to be NULL. There are NULL checks all over to handle the ingress qdisc special case that used to exist before this commit. Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sched/sch_generic.c')
-rw-r--r--net/sched/sch_generic.c8
1 files changed, 5 insertions, 3 deletions
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index fd2a6cadb115..345838a2e369 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -596,7 +596,7 @@ static void transition_one_qdisc(struct net_device *dev,
596 int *need_watchdog_p = _need_watchdog; 596 int *need_watchdog_p = _need_watchdog;
597 597
598 rcu_assign_pointer(dev_queue->qdisc, new_qdisc); 598 rcu_assign_pointer(dev_queue->qdisc, new_qdisc);
599 if (new_qdisc != &noqueue_qdisc) 599 if (need_watchdog_p && new_qdisc != &noqueue_qdisc)
600 *need_watchdog_p = 1; 600 *need_watchdog_p = 1;
601} 601}
602 602
@@ -619,6 +619,7 @@ void dev_activate(struct net_device *dev)
619 619
620 need_watchdog = 0; 620 need_watchdog = 0;
621 netdev_for_each_tx_queue(dev, transition_one_qdisc, &need_watchdog); 621 netdev_for_each_tx_queue(dev, transition_one_qdisc, &need_watchdog);
622 transition_one_qdisc(dev, &dev->rx_queue, NULL);
622 623
623 if (need_watchdog) { 624 if (need_watchdog) {
624 dev->trans_start = jiffies; 625 dev->trans_start = jiffies;
@@ -677,6 +678,7 @@ void dev_deactivate(struct net_device *dev)
677 bool running; 678 bool running;
678 679
679 netdev_for_each_tx_queue(dev, dev_deactivate_queue, &noop_qdisc); 680 netdev_for_each_tx_queue(dev, dev_deactivate_queue, &noop_qdisc);
681 dev_deactivate_queue(dev, &dev->rx_queue, &noop_qdisc);
680 682
681 dev_watchdog_down(dev); 683 dev_watchdog_down(dev);
682 684
@@ -718,7 +720,7 @@ static void dev_init_scheduler_queue(struct net_device *dev,
718void dev_init_scheduler(struct net_device *dev) 720void dev_init_scheduler(struct net_device *dev)
719{ 721{
720 netdev_for_each_tx_queue(dev, dev_init_scheduler_queue, &noop_qdisc); 722 netdev_for_each_tx_queue(dev, dev_init_scheduler_queue, &noop_qdisc);
721 dev_init_scheduler_queue(dev, &dev->rx_queue, NULL); 723 dev_init_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc);
722 724
723 setup_timer(&dev->watchdog_timer, dev_watchdog, (unsigned long)dev); 725 setup_timer(&dev->watchdog_timer, dev_watchdog, (unsigned long)dev);
724} 726}
@@ -745,6 +747,6 @@ static void shutdown_scheduler_queue(struct net_device *dev,
745void dev_shutdown(struct net_device *dev) 747void dev_shutdown(struct net_device *dev)
746{ 748{
747 netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc); 749 netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc);
748 shutdown_scheduler_queue(dev, &dev->rx_queue, NULL); 750 shutdown_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc);
749 WARN_ON(timer_pending(&dev->watchdog_timer)); 751 WARN_ON(timer_pending(&dev->watchdog_timer));
750} 752}