|
@@ -941,6 +941,13 @@ void qdisc_free(struct Qdisc *qdisc)
|
|
|
kfree((char *) qdisc - qdisc->padded);
|
|
|
}
|
|
|
|
|
|
+void qdisc_free_cb(struct rcu_head *head)
|
|
|
+{
|
|
|
+ struct Qdisc *q = container_of(head, struct Qdisc, rcu);
|
|
|
+
|
|
|
+ qdisc_free(q);
|
|
|
+}
|
|
|
+
|
|
|
static void qdisc_destroy(struct Qdisc *qdisc)
|
|
|
{
|
|
|
const struct Qdisc_ops *ops = qdisc->ops;
|
|
@@ -970,7 +977,7 @@ static void qdisc_destroy(struct Qdisc *qdisc)
|
|
|
kfree_skb_list(skb);
|
|
|
}
|
|
|
|
|
|
- qdisc_free(qdisc);
|
|
|
+ call_rcu(&qdisc->rcu, qdisc_free_cb);
|
|
|
}
|
|
|
|
|
|
void qdisc_put(struct Qdisc *qdisc)
|
|
@@ -983,6 +990,22 @@ void qdisc_put(struct Qdisc *qdisc)
|
|
|
}
|
|
|
EXPORT_SYMBOL(qdisc_put);
|
|
|
|
|
|
+/* Version of qdisc_put() that is called with rtnl mutex unlocked.
|
|
|
+ * Intended to be used as optimization, this function only takes rtnl lock if
|
|
|
+ * qdisc reference counter reached zero.
|
|
|
+ */
|
|
|
+
|
|
|
+void qdisc_put_unlocked(struct Qdisc *qdisc)
|
|
|
+{
|
|
|
+ if (qdisc->flags & TCQ_F_BUILTIN ||
|
|
|
+ !refcount_dec_and_rtnl_lock(&qdisc->refcnt))
|
|
|
+ return;
|
|
|
+
|
|
|
+ qdisc_destroy(qdisc);
|
|
|
+ rtnl_unlock();
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(qdisc_put_unlocked);
|
|
|
+
|
|
|
/* Attach toplevel qdisc to device queue. */
|
|
|
struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue,
|
|
|
struct Qdisc *qdisc)
|