diff options
| -rw-r--r-- | net/sched/sch_api.c | 6 | ||||
| -rw-r--r-- | net/sched/sch_generic.c | 24 |
2 files changed, 28 insertions, 2 deletions
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 932bcddc55c3..fff96b79613e 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -196,10 +196,14 @@ struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) { struct Qdisc *q; + read_lock_bh(&qdisc_tree_lock); list_for_each_entry(q, &dev->qdisc_list, list) { - if (q->handle == handle) + if (q->handle == handle) { + read_unlock_bh(&qdisc_tree_lock); return q; + } } + read_unlock_bh(&qdisc_tree_lock); return NULL; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index fda8f7429c68..1b9180944904 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -483,10 +483,32 @@ static void __qdisc_destroy(struct rcu_head *head) void qdisc_destroy(struct Qdisc *qdisc) { + struct list_head cql = LIST_HEAD_INIT(cql); + struct Qdisc *cq, *q, *n; + if (qdisc->flags & TCQ_F_BUILTIN || !atomic_dec_and_test(&qdisc->refcnt)) return; - list_del(&qdisc->list); + + if (!list_empty(&qdisc->list)) { + if (qdisc->ops->cl_ops == NULL) + list_del(&qdisc->list); + else + list_move(&qdisc->list, &cql); + } + + /* unlink inner qdiscs from dev->qdisc_list immediately */ + list_for_each_entry(cq, &cql, list) + list_for_each_entry_safe(q, n, &qdisc->dev->qdisc_list, list) + if (TC_H_MAJ(q->parent) == TC_H_MAJ(cq->handle)) { + if (q->ops->cl_ops == NULL) + list_del_init(&q->list); + else + list_move_tail(&q->list, &cql); + } + list_for_each_entry_safe(cq, n, &cql, list) + list_del_init(&cq->list); + call_rcu(&qdisc->q_rcu, __qdisc_destroy); } |
