aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--net/core/dev.c4
-rw-r--r--net/sched/sch_api.c57
-rw-r--r--net/sched/sch_generic.c8
3 files changed, 23 insertions, 46 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index 8d13a9b9f1df..63d6bcddbf46 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2100,7 +2100,7 @@ static int ing_filter(struct sk_buff *skb)
2100 rxq = &dev->rx_queue; 2100 rxq = &dev->rx_queue;
2101 2101
2102 q = rxq->qdisc; 2102 q = rxq->qdisc;
2103 if (q) { 2103 if (q != &noop_qdisc) {
2104 spin_lock(qdisc_lock(q)); 2104 spin_lock(qdisc_lock(q));
2105 result = qdisc_enqueue_root(skb, q); 2105 result = qdisc_enqueue_root(skb, q);
2106 spin_unlock(qdisc_lock(q)); 2106 spin_unlock(qdisc_lock(q));
@@ -2113,7 +2113,7 @@ static inline struct sk_buff *handle_ing(struct sk_buff *skb,
2113 struct packet_type **pt_prev, 2113 struct packet_type **pt_prev,
2114 int *ret, struct net_device *orig_dev) 2114 int *ret, struct net_device *orig_dev)
2115{ 2115{
2116 if (!skb->dev->rx_queue.qdisc) 2116 if (skb->dev->rx_queue.qdisc == &noop_qdisc)
2117 goto out; 2117 goto out;
2118 2118
2119 if (*pt_prev) { 2119 if (*pt_prev) {
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index b0601642e227..4840aff47256 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -572,44 +572,21 @@ static u32 qdisc_alloc_handle(struct net_device *dev)
572static struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue, 572static struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue,
573 struct Qdisc *qdisc) 573 struct Qdisc *qdisc)
574{ 574{
575 struct Qdisc *oqdisc = dev_queue->qdisc_sleeping;
575 spinlock_t *root_lock; 576 spinlock_t *root_lock;
576 struct Qdisc *oqdisc;
577 int ingress;
578
579 ingress = 0;
580 if (qdisc && qdisc->flags&TCQ_F_INGRESS)
581 ingress = 1;
582
583 if (ingress) {
584 oqdisc = dev_queue->qdisc;
585 } else {
586 oqdisc = dev_queue->qdisc_sleeping;
587 }
588 577
589 root_lock = qdisc_root_lock(oqdisc); 578 root_lock = qdisc_root_lock(oqdisc);
590 spin_lock_bh(root_lock); 579 spin_lock_bh(root_lock);
591 580
592 if (ingress) { 581 /* Prune old scheduler */
593 /* Prune old scheduler */ 582 if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1)
594 if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1) { 583 qdisc_reset(oqdisc);
595 /* delete */
596 qdisc_reset(oqdisc);
597 dev_queue->qdisc = NULL;
598 } else { /* new */
599 dev_queue->qdisc = qdisc;
600 }
601 584
602 } else { 585 /* ... and graft new one */
603 /* Prune old scheduler */ 586 if (qdisc == NULL)
604 if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1) 587 qdisc = &noop_qdisc;
605 qdisc_reset(oqdisc); 588 dev_queue->qdisc_sleeping = qdisc;
606 589 dev_queue->qdisc = &noop_qdisc;
607 /* ... and graft new one */
608 if (qdisc == NULL)
609 qdisc = &noop_qdisc;
610 dev_queue->qdisc_sleeping = qdisc;
611 dev_queue->qdisc = &noop_qdisc;
612 }
613 590
614 spin_unlock_bh(root_lock); 591 spin_unlock_bh(root_lock);
615 592
@@ -678,7 +655,8 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
678 655
679 ingress = 0; 656 ingress = 0;
680 num_q = dev->num_tx_queues; 657 num_q = dev->num_tx_queues;
681 if (q && q->flags & TCQ_F_INGRESS) { 658 if ((q && q->flags & TCQ_F_INGRESS) ||
659 (new && new->flags & TCQ_F_INGRESS)) {
682 num_q = 1; 660 num_q = 1;
683 ingress = 1; 661 ingress = 1;
684 } 662 }
@@ -692,13 +670,10 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
692 if (!ingress) 670 if (!ingress)
693 dev_queue = netdev_get_tx_queue(dev, i); 671 dev_queue = netdev_get_tx_queue(dev, i);
694 672
695 if (ingress) { 673 old = dev_graft_qdisc(dev_queue, new);
696 old = dev_graft_qdisc(dev_queue, q); 674 if (new && i > 0)
697 } else { 675 atomic_inc(&new->refcnt);
698 old = dev_graft_qdisc(dev_queue, new); 676
699 if (new && i > 0)
700 atomic_inc(&new->refcnt);
701 }
702 notify_and_destroy(skb, n, classid, old, new); 677 notify_and_destroy(skb, n, classid, old, new);
703 } 678 }
704 679
@@ -817,7 +792,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
817 goto err_out3; 792 goto err_out3;
818 } 793 }
819 } 794 }
820 if (parent) 795 if (parent && !(sch->flags & TCQ_F_INGRESS))
821 list_add_tail(&sch->list, &dev_queue->qdisc->list); 796 list_add_tail(&sch->list, &dev_queue->qdisc->list);
822 797
823 return sch; 798 return sch;
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}