Michal Kubecek 89a2ab
From: Eric Dumazet <edumazet@google.com>
Michal Kubecek 89a2ab
Date: Fri, 11 Feb 2022 12:06:23 -0800
Michal Kubecek 89a2ab
Subject: net_sched: add __rcu annotation to netdev->qdisc
Michal Kubecek 89a2ab
Patch-mainline: v5.17-rc5
Michal Kubecek 89a2ab
Git-commit: 5891cd5ec46c2c2eb6427cb54d214b149635dd0e
Michal Kubecek 89a2ab
References: CVE-2023-0590 bsc#1207795
Michal Kubecek 89a2ab
Michal Kubecek 89a2ab
syzbot found a data-race [1] which lead me to add __rcu
Michal Kubecek 89a2ab
annotations to netdev->qdisc, and proper accessors
Michal Kubecek 89a2ab
to get LOCKDEP support.
Michal Kubecek 89a2ab
Michal Kubecek 89a2ab
[1]
Michal Kubecek 89a2ab
BUG: KCSAN: data-race in dev_activate / qdisc_lookup_rcu
Michal Kubecek 89a2ab
Michal Kubecek 89a2ab
write to 0xffff888168ad6410 of 8 bytes by task 13559 on cpu 1:
Michal Kubecek 89a2ab
 attach_default_qdiscs net/sched/sch_generic.c:1167 [inline]
Michal Kubecek 89a2ab
 dev_activate+0x2ed/0x8f0 net/sched/sch_generic.c:1221
Michal Kubecek 89a2ab
 __dev_open+0x2e9/0x3a0 net/core/dev.c:1416
Michal Kubecek 89a2ab
 __dev_change_flags+0x167/0x3f0 net/core/dev.c:8139
Michal Kubecek 89a2ab
 rtnl_configure_link+0xc2/0x150 net/core/rtnetlink.c:3150
Michal Kubecek 89a2ab
 __rtnl_newlink net/core/rtnetlink.c:3489 [inline]
Michal Kubecek 89a2ab
 rtnl_newlink+0xf4d/0x13e0 net/core/rtnetlink.c:3529
Michal Kubecek 89a2ab
 rtnetlink_rcv_msg+0x745/0x7e0 net/core/rtnetlink.c:5594
Michal Kubecek 89a2ab
 netlink_rcv_skb+0x14e/0x250 net/netlink/af_netlink.c:2494
Michal Kubecek 89a2ab
 rtnetlink_rcv+0x18/0x20 net/core/rtnetlink.c:5612
Michal Kubecek 89a2ab
 netlink_unicast_kernel net/netlink/af_netlink.c:1317 [inline]
Michal Kubecek 89a2ab
 netlink_unicast+0x602/0x6d0 net/netlink/af_netlink.c:1343
Michal Kubecek 89a2ab
 netlink_sendmsg+0x728/0x850 net/netlink/af_netlink.c:1919
Michal Kubecek 89a2ab
 sock_sendmsg_nosec net/socket.c:705 [inline]
Michal Kubecek 89a2ab
 sock_sendmsg net/socket.c:725 [inline]
Michal Kubecek 89a2ab
 ____sys_sendmsg+0x39a/0x510 net/socket.c:2413
Michal Kubecek 89a2ab
 ___sys_sendmsg net/socket.c:2467 [inline]
Michal Kubecek 89a2ab
 __sys_sendmsg+0x195/0x230 net/socket.c:2496
Michal Kubecek 89a2ab
 __do_sys_sendmsg net/socket.c:2505 [inline]
Michal Kubecek 89a2ab
 __se_sys_sendmsg net/socket.c:2503 [inline]
Michal Kubecek 89a2ab
 __x64_sys_sendmsg+0x42/0x50 net/socket.c:2503
Michal Kubecek 89a2ab
 do_syscall_x64 arch/x86/entry/common.c:50 [inline]
Michal Kubecek 89a2ab
 do_syscall_64+0x44/0xd0 arch/x86/entry/common.c:80
Michal Kubecek 89a2ab
 entry_SYSCALL_64_after_hwframe+0x44/0xae
Michal Kubecek 89a2ab
Michal Kubecek 89a2ab
read to 0xffff888168ad6410 of 8 bytes by task 13560 on cpu 0:
Michal Kubecek 89a2ab
 qdisc_lookup_rcu+0x30/0x2e0 net/sched/sch_api.c:323
Michal Kubecek 89a2ab
 __tcf_qdisc_find+0x74/0x3a0 net/sched/cls_api.c:1050
Michal Kubecek 89a2ab
 tc_del_tfilter+0x1c7/0x1350 net/sched/cls_api.c:2211
Michal Kubecek 89a2ab
 rtnetlink_rcv_msg+0x5ba/0x7e0 net/core/rtnetlink.c:5585
Michal Kubecek 89a2ab
 netlink_rcv_skb+0x14e/0x250 net/netlink/af_netlink.c:2494
Michal Kubecek 89a2ab
 rtnetlink_rcv+0x18/0x20 net/core/rtnetlink.c:5612
Michal Kubecek 89a2ab
 netlink_unicast_kernel net/netlink/af_netlink.c:1317 [inline]
Michal Kubecek 89a2ab
 netlink_unicast+0x602/0x6d0 net/netlink/af_netlink.c:1343
Michal Kubecek 89a2ab
 netlink_sendmsg+0x728/0x850 net/netlink/af_netlink.c:1919
Michal Kubecek 89a2ab
 sock_sendmsg_nosec net/socket.c:705 [inline]
Michal Kubecek 89a2ab
 sock_sendmsg net/socket.c:725 [inline]
Michal Kubecek 89a2ab
 ____sys_sendmsg+0x39a/0x510 net/socket.c:2413
Michal Kubecek 89a2ab
 ___sys_sendmsg net/socket.c:2467 [inline]
Michal Kubecek 89a2ab
 __sys_sendmsg+0x195/0x230 net/socket.c:2496
Michal Kubecek 89a2ab
 __do_sys_sendmsg net/socket.c:2505 [inline]
Michal Kubecek 89a2ab
 __se_sys_sendmsg net/socket.c:2503 [inline]
Michal Kubecek 89a2ab
 __x64_sys_sendmsg+0x42/0x50 net/socket.c:2503
Michal Kubecek 89a2ab
 do_syscall_x64 arch/x86/entry/common.c:50 [inline]
Michal Kubecek 89a2ab
 do_syscall_64+0x44/0xd0 arch/x86/entry/common.c:80
Michal Kubecek 89a2ab
 entry_SYSCALL_64_after_hwframe+0x44/0xae
Michal Kubecek 89a2ab
Michal Kubecek 89a2ab
value changed: 0xffffffff85dee080 -> 0xffff88815d96ec00
Michal Kubecek 89a2ab
Michal Kubecek 89a2ab
Reported by Kernel Concurrency Sanitizer on:
Michal Kubecek 89a2ab
CPU: 0 PID: 13560 Comm: syz-executor.2 Not tainted 5.17.0-rc3-syzkaller-00116-gf1baf68e1383-dirty #0
Michal Kubecek 89a2ab
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Michal Kubecek 89a2ab
Michal Kubecek 89a2ab
Fixes: 470502de5bdb ("net: sched: unlock rules update API")
Michal Kubecek 89a2ab
Signed-off-by: Eric Dumazet <edumazet@google.com>
Michal Kubecek 89a2ab
Cc: Vlad Buslov <vladbu@mellanox.com>
Michal Kubecek 89a2ab
Reported-by: syzbot <syzkaller@googlegroups.com>
Michal Kubecek 89a2ab
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Michal Kubecek 89a2ab
Cc: Cong Wang <xiyou.wangcong@gmail.com>
Michal Kubecek 89a2ab
Cc: Jiri Pirko <jiri@resnulli.us>
Michal Kubecek 89a2ab
Signed-off-by: David S. Miller <davem@davemloft.net>
Michal Kubecek 89a2ab
Acked-by: Michal Kubecek <mkubecek@suse.cz>
Michal Kubecek 89a2ab
Michal Kubecek 89a2ab
---
Michal Kubecek 89a2ab
 include/linux/netdevice.h |  2 +-
Michal Kubecek 89a2ab
 net/core/rtnetlink.c      |  6 ++++--
Michal Kubecek 89a2ab
 net/sched/cls_api.c       |  6 +++---
Michal Kubecek 89a2ab
 net/sched/sch_api.c       | 22 ++++++++++++----------
Michal Kubecek 89a2ab
 net/sched/sch_generic.c   | 20 +++++++++++---------
Michal Kubecek 89a2ab
 5 files changed, 31 insertions(+), 25 deletions(-)
Michal Kubecek 89a2ab
Michal Kubecek 89a2ab
--- a/include/linux/netdevice.h
Michal Kubecek 89a2ab
+++ b/include/linux/netdevice.h
Michal Kubecek 89a2ab
@@ -1985,7 +1985,7 @@ struct net_device {
Michal Kubecek 89a2ab
 	struct netdev_queue	*_tx ____cacheline_aligned_in_smp;
Michal Kubecek 89a2ab
 	unsigned int		num_tx_queues;
Michal Kubecek 89a2ab
 	unsigned int		real_num_tx_queues;
Michal Kubecek 89a2ab
-	struct Qdisc		*qdisc;
Michal Kubecek 89a2ab
+	struct Qdisc __rcu	*qdisc;
Michal Kubecek 89a2ab
 #ifdef CONFIG_NET_SCHED
Michal Kubecek 89a2ab
 	DECLARE_HASHTABLE	(qdisc_hash, 4);
Michal Kubecek 89a2ab
 #endif
Michal Kubecek 89a2ab
--- a/net/core/rtnetlink.c
Michal Kubecek 89a2ab
+++ b/net/core/rtnetlink.c
Michal Kubecek 89a2ab
@@ -1660,6 +1660,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
Michal Kubecek 89a2ab
 {
Michal Kubecek 89a2ab
 	struct ifinfomsg *ifm;
Michal Kubecek 89a2ab
 	struct nlmsghdr *nlh;
Michal Kubecek 89a2ab
+	struct Qdisc *qdisc;
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 	ASSERT_RTNL();
Michal Kubecek 89a2ab
 	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags);
Michal Kubecek 89a2ab
@@ -1677,6 +1678,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
Michal Kubecek 89a2ab
 	if (tgt_netnsid >= 0 && nla_put_s32(skb, IFLA_TARGET_NETNSID, tgt_netnsid))
Michal Kubecek 89a2ab
 		goto nla_put_failure;
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
+	qdisc = rtnl_dereference(dev->qdisc);
Michal Kubecek 89a2ab
 	if (nla_put_string(skb, IFLA_IFNAME, dev->name) ||
Michal Kubecek 89a2ab
 	    nla_put_u32(skb, IFLA_TXQLEN, dev->tx_queue_len) ||
Michal Kubecek 89a2ab
 	    nla_put_u8(skb, IFLA_OPERSTATE,
Michal Kubecek 89a2ab
@@ -1695,8 +1697,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
Michal Kubecek 89a2ab
 #endif
Michal Kubecek 89a2ab
 	    put_master_ifindex(skb, dev) ||
Michal Kubecek 89a2ab
 	    nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) ||
Michal Kubecek 89a2ab
-	    (dev->qdisc &&
Michal Kubecek 89a2ab
-	     nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) ||
Michal Kubecek 89a2ab
+	    (qdisc &&
Michal Kubecek 89a2ab
+	     nla_put_string(skb, IFLA_QDISC, qdisc->ops->id)) ||
Michal Kubecek 89a2ab
 	    nla_put_ifalias(skb, dev) ||
Michal Kubecek 89a2ab
 	    nla_put_u32(skb, IFLA_CARRIER_CHANGES,
Michal Kubecek 89a2ab
 			atomic_read(&dev->carrier_up_count) +
Michal Kubecek 89a2ab
--- a/net/sched/cls_api.c
Michal Kubecek 89a2ab
+++ b/net/sched/cls_api.c
Michal Kubecek 89a2ab
@@ -1065,7 +1065,7 @@ static int __tcf_qdisc_find(struct net *net, struct Qdisc **q,
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 	/* Find qdisc */
Michal Kubecek 89a2ab
 	if (!*parent) {
Michal Kubecek 89a2ab
-		*q = dev->qdisc;
Michal Kubecek 89a2ab
+		*q = rcu_dereference(dev->qdisc);
Michal Kubecek 89a2ab
 		*parent = (*q)->handle;
Michal Kubecek 89a2ab
 	} else {
Michal Kubecek 89a2ab
 		*q = qdisc_lookup_rcu(dev, TC_H_MAJ(*parent));
Michal Kubecek 89a2ab
@@ -2524,7 +2524,7 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 		parent = tcm->tcm_parent;
Michal Kubecek 89a2ab
 		if (!parent) {
Michal Kubecek 89a2ab
-			q = dev->qdisc;
Michal Kubecek 89a2ab
+			q = rtnl_dereference(dev->qdisc);
Michal Kubecek 89a2ab
 			parent = q->handle;
Michal Kubecek 89a2ab
 		} else {
Michal Kubecek 89a2ab
 			q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
Michal Kubecek 89a2ab
@@ -2911,7 +2911,7 @@ static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 		parent = tcm->tcm_parent;
Michal Kubecek 89a2ab
 		if (!parent) {
Michal Kubecek 89a2ab
-			q = dev->qdisc;
Michal Kubecek 89a2ab
+			q = rtnl_dereference(dev->qdisc);
Michal Kubecek 89a2ab
 			parent = q->handle;
Michal Kubecek 89a2ab
 		} else {
Michal Kubecek 89a2ab
 			q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
Michal Kubecek 89a2ab
--- a/net/sched/sch_api.c
Michal Kubecek 89a2ab
+++ b/net/sched/sch_api.c
Michal Kubecek 89a2ab
@@ -298,7 +298,7 @@ struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle)
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 	if (!handle)
Michal Kubecek 89a2ab
 		return NULL;
Michal Kubecek 89a2ab
-	q = qdisc_match_from_root(dev->qdisc, handle);
Michal Kubecek 89a2ab
+	q = qdisc_match_from_root(rtnl_dereference(dev->qdisc), handle);
Michal Kubecek 89a2ab
 	if (q)
Michal Kubecek 89a2ab
 		goto out;
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
@@ -317,7 +317,7 @@ struct Qdisc *qdisc_lookup_rcu(struct net_device *dev, u32 handle)
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 	if (!handle)
Michal Kubecek 89a2ab
 		return NULL;
Michal Kubecek 89a2ab
-	q = qdisc_match_from_root(dev->qdisc, handle);
Michal Kubecek 89a2ab
+	q = qdisc_match_from_root(rcu_dereference(dev->qdisc), handle);
Michal Kubecek 89a2ab
 	if (q)
Michal Kubecek 89a2ab
 		goto out;
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
@@ -1065,10 +1065,10 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
Michal Kubecek 89a2ab
 skip:
Michal Kubecek 89a2ab
 		if (!ingress) {
Michal Kubecek 89a2ab
 			notify_and_destroy(net, skb, n, classid,
Michal Kubecek 89a2ab
-					   dev->qdisc, new);
Michal Kubecek 89a2ab
+					   rtnl_dereference(dev->qdisc), new);
Michal Kubecek 89a2ab
 			if (new && !new->ops->attach)
Michal Kubecek 89a2ab
 				qdisc_refcount_inc(new);
Michal Kubecek 89a2ab
-			dev->qdisc = new ? : &noop_qdisc;
Michal Kubecek 89a2ab
+			rcu_assign_pointer(dev->qdisc, new ? : &noop_qdisc);
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 			if (new && new->ops->attach)
Michal Kubecek 89a2ab
 				new->ops->attach(new);
Michal Kubecek 89a2ab
@@ -1448,7 +1448,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
Michal Kubecek 89a2ab
 				q = dev_ingress_queue(dev)->qdisc_sleeping;
Michal Kubecek 89a2ab
 			}
Michal Kubecek 89a2ab
 		} else {
Michal Kubecek 89a2ab
-			q = dev->qdisc;
Michal Kubecek 89a2ab
+			q = rtnl_dereference(dev->qdisc);
Michal Kubecek 89a2ab
 		}
Michal Kubecek 89a2ab
 		if (!q) {
Michal Kubecek 89a2ab
 			NL_SET_ERR_MSG(extack, "Cannot find specified qdisc on specified device");
Michal Kubecek 89a2ab
@@ -1537,7 +1537,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
Michal Kubecek 89a2ab
 				q = dev_ingress_queue(dev)->qdisc_sleeping;
Michal Kubecek 89a2ab
 			}
Michal Kubecek 89a2ab
 		} else {
Michal Kubecek 89a2ab
-			q = dev->qdisc;
Michal Kubecek 89a2ab
+			q = rtnl_dereference(dev->qdisc);
Michal Kubecek 89a2ab
 		}
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 		/* It may be default qdisc, ignore it */
Michal Kubecek 89a2ab
@@ -1759,7 +1759,8 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
Michal Kubecek 89a2ab
 			s_q_idx = 0;
Michal Kubecek 89a2ab
 		q_idx = 0;
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
-		if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx,
Michal Kubecek 89a2ab
+		if (tc_dump_qdisc_root(rtnl_dereference(dev->qdisc),
Michal Kubecek 89a2ab
+				       skb, cb, &q_idx, s_q_idx,
Michal Kubecek 89a2ab
 				       true, tca[TCA_DUMP_INVISIBLE]) < 0)
Michal Kubecek 89a2ab
 			goto done;
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
@@ -2035,7 +2036,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n,
Michal Kubecek 89a2ab
 		} else if (qid1) {
Michal Kubecek 89a2ab
 			qid = qid1;
Michal Kubecek 89a2ab
 		} else if (qid == 0)
Michal Kubecek 89a2ab
-			qid = dev->qdisc->handle;
Michal Kubecek 89a2ab
+			qid = rtnl_dereference(dev->qdisc)->handle;
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 		/* Now qid is genuine qdisc handle consistent
Michal Kubecek 89a2ab
 		 * both with parent and child.
Michal Kubecek 89a2ab
@@ -2046,7 +2047,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n,
Michal Kubecek 89a2ab
 			portid = TC_H_MAKE(qid, portid);
Michal Kubecek 89a2ab
 	} else {
Michal Kubecek 89a2ab
 		if (qid == 0)
Michal Kubecek 89a2ab
-			qid = dev->qdisc->handle;
Michal Kubecek 89a2ab
+			qid = rtnl_dereference(dev->qdisc)->handle;
Michal Kubecek 89a2ab
 	}
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 	/* OK. Locate qdisc */
Michal Kubecek 89a2ab
@@ -2207,7 +2208,8 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb)
Michal Kubecek 89a2ab
 	s_t = cb->args[0];
Michal Kubecek 89a2ab
 	t = 0;
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
-	if (tc_dump_tclass_root(dev->qdisc, skb, tcm, cb, &t, s_t) < 0)
Michal Kubecek 89a2ab
+	if (tc_dump_tclass_root(rtnl_dereference(dev->qdisc),
Michal Kubecek 89a2ab
+				skb, tcm, cb, &t, s_t) < 0)
Michal Kubecek 89a2ab
 		goto done;
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 	dev_queue = dev_ingress_queue(dev);
Michal Kubecek 89a2ab
--- a/net/sched/sch_generic.c
Michal Kubecek 89a2ab
+++ b/net/sched/sch_generic.c
Michal Kubecek 89a2ab
@@ -1074,18 +1074,20 @@ static void attach_default_qdiscs(struct net_device *dev)
Michal Kubecek 89a2ab
 	if (!netif_is_multiqueue(dev) ||
Michal Kubecek 89a2ab
 	    dev->priv_flags & IFF_NO_QUEUE) {
Michal Kubecek 89a2ab
 		netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL);
Michal Kubecek 89a2ab
-		dev->qdisc = txq->qdisc_sleeping;
Michal Kubecek 89a2ab
-		qdisc_refcount_inc(dev->qdisc);
Michal Kubecek 89a2ab
+		qdisc = txq->qdisc_sleeping;
Michal Kubecek 89a2ab
+		rcu_assign_pointer(dev->qdisc, qdisc);
Michal Kubecek 89a2ab
+		qdisc_refcount_inc(qdisc);
Michal Kubecek 89a2ab
 	} else {
Michal Kubecek 89a2ab
 		qdisc = qdisc_create_dflt(txq, &mq_qdisc_ops, TC_H_ROOT, NULL);
Michal Kubecek 89a2ab
 		if (qdisc) {
Michal Kubecek 89a2ab
-			dev->qdisc = qdisc;
Michal Kubecek 89a2ab
+			rcu_assign_pointer(dev->qdisc, qdisc);
Michal Kubecek 89a2ab
 			qdisc->ops->attach(qdisc);
Michal Kubecek 89a2ab
 		}
Michal Kubecek 89a2ab
 	}
Michal Kubecek 89a2ab
+	qdisc = rtnl_dereference(dev->qdisc);
Michal Kubecek 89a2ab
 #ifdef CONFIG_NET_SCHED
Michal Kubecek 89a2ab
-	if (dev->qdisc != &noop_qdisc)
Michal Kubecek 89a2ab
-		qdisc_hash_add(dev->qdisc, false);
Michal Kubecek 89a2ab
+	if (qdisc != &noop_qdisc)
Michal Kubecek 89a2ab
+		qdisc_hash_add(qdisc, false);
Michal Kubecek 89a2ab
 #endif
Michal Kubecek 89a2ab
 }
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
@@ -1115,7 +1117,7 @@ void dev_activate(struct net_device *dev)
Michal Kubecek 89a2ab
 	 * and noqueue_qdisc for virtual interfaces
Michal Kubecek 89a2ab
 	 */
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
-	if (dev->qdisc == &noop_qdisc)
Michal Kubecek 89a2ab
+	if (rtnl_dereference(dev->qdisc) == &noop_qdisc)
Michal Kubecek 89a2ab
 		attach_default_qdiscs(dev);
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 	if (!netif_carrier_ok(dev))
Michal Kubecek 89a2ab
@@ -1298,7 +1300,7 @@ static void dev_init_scheduler_queue(struct net_device *dev,
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 void dev_init_scheduler(struct net_device *dev)
Michal Kubecek 89a2ab
 {
Michal Kubecek 89a2ab
-	dev->qdisc = &noop_qdisc;
Michal Kubecek 89a2ab
+	rcu_assign_pointer(dev->qdisc, &noop_qdisc);
Michal Kubecek 89a2ab
 	netdev_for_each_tx_queue(dev, dev_init_scheduler_queue, &noop_qdisc);
Michal Kubecek 89a2ab
 	if (dev_ingress_queue(dev))
Michal Kubecek 89a2ab
 		dev_init_scheduler_queue(dev, dev_ingress_queue(dev), &noop_qdisc);
Michal Kubecek 89a2ab
@@ -1326,8 +1328,8 @@ void dev_shutdown(struct net_device *dev)
Michal Kubecek 89a2ab
 	netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc);
Michal Kubecek 89a2ab
 	if (dev_ingress_queue(dev))
Michal Kubecek 89a2ab
 		shutdown_scheduler_queue(dev, dev_ingress_queue(dev), &noop_qdisc);
Michal Kubecek 89a2ab
-	qdisc_put(dev->qdisc);
Michal Kubecek 89a2ab
-	dev->qdisc = &noop_qdisc;
Michal Kubecek 89a2ab
+	qdisc_put(rtnl_dereference(dev->qdisc));
Michal Kubecek 89a2ab
+	rcu_assign_pointer(dev->qdisc, &noop_qdisc);
Michal Kubecek 89a2ab
 
Michal Kubecek 89a2ab
 	WARN_ON(timer_pending(&dev->watchdog_timer));
Michal Kubecek 89a2ab
 }