forked from mirrors/linux
		
	net: sched: add rcu annotations around qdisc->qdisc_sleeping
syzbot reported a race around qdisc->qdisc_sleeping [1]
It is time we add proper annotations to reads and writes to/from
qdisc->qdisc_sleeping.
[1]
BUG: KCSAN: data-race in dev_graft_qdisc / qdisc_lookup_rcu
read to 0xffff8881286fc618 of 8 bytes by task 6928 on cpu 1:
qdisc_lookup_rcu+0x192/0x2c0 net/sched/sch_api.c:331
__tcf_qdisc_find+0x74/0x3c0 net/sched/cls_api.c:1174
tc_get_tfilter+0x18f/0x990 net/sched/cls_api.c:2547
rtnetlink_rcv_msg+0x7af/0x8c0 net/core/rtnetlink.c:6386
netlink_rcv_skb+0x126/0x220 net/netlink/af_netlink.c:2546
rtnetlink_rcv+0x1c/0x20 net/core/rtnetlink.c:6413
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x56f/0x640 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x665/0x770 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg net/socket.c:747 [inline]
____sys_sendmsg+0x375/0x4c0 net/socket.c:2503
___sys_sendmsg net/socket.c:2557 [inline]
__sys_sendmsg+0x1e3/0x270 net/socket.c:2586
__do_sys_sendmsg net/socket.c:2595 [inline]
__se_sys_sendmsg net/socket.c:2593 [inline]
__x64_sys_sendmsg+0x46/0x50 net/socket.c:2593
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x63/0xcd
write to 0xffff8881286fc618 of 8 bytes by task 6912 on cpu 0:
dev_graft_qdisc+0x4f/0x80 net/sched/sch_generic.c:1115
qdisc_graft+0x7d0/0xb60 net/sched/sch_api.c:1103
tc_modify_qdisc+0x712/0xf10 net/sched/sch_api.c:1693
rtnetlink_rcv_msg+0x807/0x8c0 net/core/rtnetlink.c:6395
netlink_rcv_skb+0x126/0x220 net/netlink/af_netlink.c:2546
rtnetlink_rcv+0x1c/0x20 net/core/rtnetlink.c:6413
netlink_unicast_kernel net/netlink/af_netlink.c:1339 [inline]
netlink_unicast+0x56f/0x640 net/netlink/af_netlink.c:1365
netlink_sendmsg+0x665/0x770 net/netlink/af_netlink.c:1913
sock_sendmsg_nosec net/socket.c:724 [inline]
sock_sendmsg net/socket.c:747 [inline]
____sys_sendmsg+0x375/0x4c0 net/socket.c:2503
___sys_sendmsg net/socket.c:2557 [inline]
__sys_sendmsg+0x1e3/0x270 net/socket.c:2586
__do_sys_sendmsg net/socket.c:2595 [inline]
__se_sys_sendmsg net/socket.c:2593 [inline]
__x64_sys_sendmsg+0x46/0x50 net/socket.c:2593
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x63/0xcd
Reported by Kernel Concurrency Sanitizer on:
CPU: 0 PID: 6912 Comm: syz-executor.5 Not tainted 6.4.0-rc3-syzkaller-00190-g0d85b27b0cc6 #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/16/2023
Fixes: 3a7d0d07a3 ("net: sched: extend Qdisc with rcu")
Reported-by: syzbot <syzkaller@googlegroups.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Vlad Buslov <vladbu@nvidia.com>
Acked-by: Jamal Hadi Salim<jhs@mojatatu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									e3144ff52f
								
							
						
					
					
						commit
						d636fc5dd6
					
				
					 13 changed files with 63 additions and 44 deletions
				
			
		|  | @ -620,7 +620,7 @@ struct netdev_queue { | ||||||
| 	netdevice_tracker	dev_tracker; | 	netdevice_tracker	dev_tracker; | ||||||
| 
 | 
 | ||||||
| 	struct Qdisc __rcu	*qdisc; | 	struct Qdisc __rcu	*qdisc; | ||||||
| 	struct Qdisc		*qdisc_sleeping; | 	struct Qdisc __rcu	*qdisc_sleeping; | ||||||
| #ifdef CONFIG_SYSFS | #ifdef CONFIG_SYSFS | ||||||
| 	struct kobject		kobj; | 	struct kobject		kobj; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -545,7 +545,7 @@ static inline struct Qdisc *qdisc_root_bh(const struct Qdisc *qdisc) | ||||||
| 
 | 
 | ||||||
| static inline struct Qdisc *qdisc_root_sleeping(const struct Qdisc *qdisc) | static inline struct Qdisc *qdisc_root_sleeping(const struct Qdisc *qdisc) | ||||||
| { | { | ||||||
| 	return qdisc->dev_queue->qdisc_sleeping; | 	return rcu_dereference_rtnl(qdisc->dev_queue->qdisc_sleeping); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline spinlock_t *qdisc_root_sleeping_lock(const struct Qdisc *qdisc) | static inline spinlock_t *qdisc_root_sleeping_lock(const struct Qdisc *qdisc) | ||||||
|  | @ -754,7 +754,9 @@ static inline bool qdisc_tx_changing(const struct net_device *dev) | ||||||
| 
 | 
 | ||||||
| 	for (i = 0; i < dev->num_tx_queues; i++) { | 	for (i = 0; i < dev->num_tx_queues; i++) { | ||||||
| 		struct netdev_queue *txq = netdev_get_tx_queue(dev, i); | 		struct netdev_queue *txq = netdev_get_tx_queue(dev, i); | ||||||
| 		if (rcu_access_pointer(txq->qdisc) != txq->qdisc_sleeping) | 
 | ||||||
|  | 		if (rcu_access_pointer(txq->qdisc) != | ||||||
|  | 		    rcu_access_pointer(txq->qdisc_sleeping)) | ||||||
| 			return true; | 			return true; | ||||||
| 	} | 	} | ||||||
| 	return false; | 	return false; | ||||||
|  |  | ||||||
|  | @ -10543,7 +10543,7 @@ struct netdev_queue *dev_ingress_queue_create(struct net_device *dev) | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	netdev_init_one_queue(dev, queue, NULL); | 	netdev_init_one_queue(dev, queue, NULL); | ||||||
| 	RCU_INIT_POINTER(queue->qdisc, &noop_qdisc); | 	RCU_INIT_POINTER(queue->qdisc, &noop_qdisc); | ||||||
| 	queue->qdisc_sleeping = &noop_qdisc; | 	RCU_INIT_POINTER(queue->qdisc_sleeping, &noop_qdisc); | ||||||
| 	rcu_assign_pointer(dev->ingress_queue, queue); | 	rcu_assign_pointer(dev->ingress_queue, queue); | ||||||
| #endif | #endif | ||||||
| 	return queue; | 	return queue; | ||||||
|  |  | ||||||
|  | @ -309,7 +309,7 @@ struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) | ||||||
| 
 | 
 | ||||||
| 	if (dev_ingress_queue(dev)) | 	if (dev_ingress_queue(dev)) | ||||||
| 		q = qdisc_match_from_root( | 		q = qdisc_match_from_root( | ||||||
| 			dev_ingress_queue(dev)->qdisc_sleeping, | 			rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping), | ||||||
| 			handle); | 			handle); | ||||||
| out: | out: | ||||||
| 	return q; | 	return q; | ||||||
|  | @ -328,7 +328,8 @@ struct Qdisc *qdisc_lookup_rcu(struct net_device *dev, u32 handle) | ||||||
| 
 | 
 | ||||||
| 	nq = dev_ingress_queue_rcu(dev); | 	nq = dev_ingress_queue_rcu(dev); | ||||||
| 	if (nq) | 	if (nq) | ||||||
| 		q = qdisc_match_from_root(nq->qdisc_sleeping, handle); | 		q = qdisc_match_from_root(rcu_dereference(nq->qdisc_sleeping), | ||||||
|  | 					  handle); | ||||||
| out: | out: | ||||||
| 	return q; | 	return q; | ||||||
| } | } | ||||||
|  | @ -634,8 +635,13 @@ EXPORT_SYMBOL(qdisc_watchdog_init); | ||||||
| void qdisc_watchdog_schedule_range_ns(struct qdisc_watchdog *wd, u64 expires, | void qdisc_watchdog_schedule_range_ns(struct qdisc_watchdog *wd, u64 expires, | ||||||
| 				      u64 delta_ns) | 				      u64 delta_ns) | ||||||
| { | { | ||||||
| 	if (test_bit(__QDISC_STATE_DEACTIVATED, | 	bool deactivated; | ||||||
| 		     &qdisc_root_sleeping(wd->qdisc)->state)) | 
 | ||||||
|  | 	rcu_read_lock(); | ||||||
|  | 	deactivated = test_bit(__QDISC_STATE_DEACTIVATED, | ||||||
|  | 			       &qdisc_root_sleeping(wd->qdisc)->state); | ||||||
|  | 	rcu_read_unlock(); | ||||||
|  | 	if (deactivated) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	if (hrtimer_is_queued(&wd->timer)) { | 	if (hrtimer_is_queued(&wd->timer)) { | ||||||
|  | @ -1478,7 +1484,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, | ||||||
| 				} | 				} | ||||||
| 				q = qdisc_leaf(p, clid); | 				q = qdisc_leaf(p, clid); | ||||||
| 			} else if (dev_ingress_queue(dev)) { | 			} else if (dev_ingress_queue(dev)) { | ||||||
| 				q = dev_ingress_queue(dev)->qdisc_sleeping; | 				q = rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping); | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			q = rtnl_dereference(dev->qdisc); | 			q = rtnl_dereference(dev->qdisc); | ||||||
|  | @ -1564,7 +1570,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, | ||||||
| 				} | 				} | ||||||
| 				q = qdisc_leaf(p, clid); | 				q = qdisc_leaf(p, clid); | ||||||
| 			} else if (dev_ingress_queue_create(dev)) { | 			} else if (dev_ingress_queue_create(dev)) { | ||||||
| 				q = dev_ingress_queue(dev)->qdisc_sleeping; | 				q = rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping); | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			q = rtnl_dereference(dev->qdisc); | 			q = rtnl_dereference(dev->qdisc); | ||||||
|  | @ -1805,8 +1811,8 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) | ||||||
| 
 | 
 | ||||||
| 		dev_queue = dev_ingress_queue(dev); | 		dev_queue = dev_ingress_queue(dev); | ||||||
| 		if (dev_queue && | 		if (dev_queue && | ||||||
| 		    tc_dump_qdisc_root(dev_queue->qdisc_sleeping, skb, cb, | 		    tc_dump_qdisc_root(rtnl_dereference(dev_queue->qdisc_sleeping), | ||||||
| 				       &q_idx, s_q_idx, false, | 				       skb, cb, &q_idx, s_q_idx, false, | ||||||
| 				       tca[TCA_DUMP_INVISIBLE]) < 0) | 				       tca[TCA_DUMP_INVISIBLE]) < 0) | ||||||
| 			goto done; | 			goto done; | ||||||
| 
 | 
 | ||||||
|  | @ -2249,8 +2255,8 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) | ||||||
| 
 | 
 | ||||||
| 	dev_queue = dev_ingress_queue(dev); | 	dev_queue = dev_ingress_queue(dev); | ||||||
| 	if (dev_queue && | 	if (dev_queue && | ||||||
| 	    tc_dump_tclass_root(dev_queue->qdisc_sleeping, skb, tcm, cb, | 	    tc_dump_tclass_root(rtnl_dereference(dev_queue->qdisc_sleeping), | ||||||
| 				&t, s_t, false) < 0) | 				skb, tcm, cb, &t, s_t, false) < 0) | ||||||
| 		goto done; | 		goto done; | ||||||
| 
 | 
 | ||||||
| done: | done: | ||||||
|  |  | ||||||
|  | @ -379,6 +379,7 @@ static void fq_pie_timer(struct timer_list *t) | ||||||
| 	spinlock_t *root_lock; /* to lock qdisc for probability calculations */ | 	spinlock_t *root_lock; /* to lock qdisc for probability calculations */ | ||||||
| 	u32 idx; | 	u32 idx; | ||||||
| 
 | 
 | ||||||
|  | 	rcu_read_lock(); | ||||||
| 	root_lock = qdisc_lock(qdisc_root_sleeping(sch)); | 	root_lock = qdisc_lock(qdisc_root_sleeping(sch)); | ||||||
| 	spin_lock(root_lock); | 	spin_lock(root_lock); | ||||||
| 
 | 
 | ||||||
|  | @ -391,6 +392,7 @@ static void fq_pie_timer(struct timer_list *t) | ||||||
| 		mod_timer(&q->adapt_timer, jiffies + q->p_params.tupdate); | 		mod_timer(&q->adapt_timer, jiffies + q->p_params.tupdate); | ||||||
| 
 | 
 | ||||||
| 	spin_unlock(root_lock); | 	spin_unlock(root_lock); | ||||||
|  | 	rcu_read_unlock(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int fq_pie_init(struct Qdisc *sch, struct nlattr *opt, | static int fq_pie_init(struct Qdisc *sch, struct nlattr *opt, | ||||||
|  |  | ||||||
|  | @ -648,7 +648,7 @@ struct Qdisc_ops noop_qdisc_ops __read_mostly = { | ||||||
| 
 | 
 | ||||||
| static struct netdev_queue noop_netdev_queue = { | static struct netdev_queue noop_netdev_queue = { | ||||||
| 	RCU_POINTER_INITIALIZER(qdisc, &noop_qdisc), | 	RCU_POINTER_INITIALIZER(qdisc, &noop_qdisc), | ||||||
| 	.qdisc_sleeping	=	&noop_qdisc, | 	RCU_POINTER_INITIALIZER(qdisc_sleeping, &noop_qdisc), | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct Qdisc noop_qdisc = { | struct Qdisc noop_qdisc = { | ||||||
|  | @ -1103,7 +1103,7 @@ EXPORT_SYMBOL(qdisc_put_unlocked); | ||||||
| struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue, | struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue, | ||||||
| 			      struct Qdisc *qdisc) | 			      struct Qdisc *qdisc) | ||||||
| { | { | ||||||
| 	struct Qdisc *oqdisc = dev_queue->qdisc_sleeping; | 	struct Qdisc *oqdisc = rtnl_dereference(dev_queue->qdisc_sleeping); | ||||||
| 	spinlock_t *root_lock; | 	spinlock_t *root_lock; | ||||||
| 
 | 
 | ||||||
| 	root_lock = qdisc_lock(oqdisc); | 	root_lock = qdisc_lock(oqdisc); | ||||||
|  | @ -1112,7 +1112,7 @@ struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue, | ||||||
| 	/* ... and graft new one */ | 	/* ... and graft new one */ | ||||||
| 	if (qdisc == NULL) | 	if (qdisc == NULL) | ||||||
| 		qdisc = &noop_qdisc; | 		qdisc = &noop_qdisc; | ||||||
| 	dev_queue->qdisc_sleeping = qdisc; | 	rcu_assign_pointer(dev_queue->qdisc_sleeping, qdisc); | ||||||
| 	rcu_assign_pointer(dev_queue->qdisc, &noop_qdisc); | 	rcu_assign_pointer(dev_queue->qdisc, &noop_qdisc); | ||||||
| 
 | 
 | ||||||
| 	spin_unlock_bh(root_lock); | 	spin_unlock_bh(root_lock); | ||||||
|  | @ -1125,12 +1125,12 @@ static void shutdown_scheduler_queue(struct net_device *dev, | ||||||
| 				     struct netdev_queue *dev_queue, | 				     struct netdev_queue *dev_queue, | ||||||
| 				     void *_qdisc_default) | 				     void *_qdisc_default) | ||||||
| { | { | ||||||
| 	struct Qdisc *qdisc = dev_queue->qdisc_sleeping; | 	struct Qdisc *qdisc = rtnl_dereference(dev_queue->qdisc_sleeping); | ||||||
| 	struct Qdisc *qdisc_default = _qdisc_default; | 	struct Qdisc *qdisc_default = _qdisc_default; | ||||||
| 
 | 
 | ||||||
| 	if (qdisc) { | 	if (qdisc) { | ||||||
| 		rcu_assign_pointer(dev_queue->qdisc, qdisc_default); | 		rcu_assign_pointer(dev_queue->qdisc, qdisc_default); | ||||||
| 		dev_queue->qdisc_sleeping = qdisc_default; | 		rcu_assign_pointer(dev_queue->qdisc_sleeping, qdisc_default); | ||||||
| 
 | 
 | ||||||
| 		qdisc_put(qdisc); | 		qdisc_put(qdisc); | ||||||
| 	} | 	} | ||||||
|  | @ -1154,7 +1154,7 @@ static void attach_one_default_qdisc(struct net_device *dev, | ||||||
| 
 | 
 | ||||||
| 	if (!netif_is_multiqueue(dev)) | 	if (!netif_is_multiqueue(dev)) | ||||||
| 		qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; | 		qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; | ||||||
| 	dev_queue->qdisc_sleeping = qdisc; | 	rcu_assign_pointer(dev_queue->qdisc_sleeping, qdisc); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void attach_default_qdiscs(struct net_device *dev) | static void attach_default_qdiscs(struct net_device *dev) | ||||||
|  | @ -1167,7 +1167,7 @@ static void attach_default_qdiscs(struct net_device *dev) | ||||||
| 	if (!netif_is_multiqueue(dev) || | 	if (!netif_is_multiqueue(dev) || | ||||||
| 	    dev->priv_flags & IFF_NO_QUEUE) { | 	    dev->priv_flags & IFF_NO_QUEUE) { | ||||||
| 		netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); | 		netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); | ||||||
| 		qdisc = txq->qdisc_sleeping; | 		qdisc = rtnl_dereference(txq->qdisc_sleeping); | ||||||
| 		rcu_assign_pointer(dev->qdisc, qdisc); | 		rcu_assign_pointer(dev->qdisc, qdisc); | ||||||
| 		qdisc_refcount_inc(qdisc); | 		qdisc_refcount_inc(qdisc); | ||||||
| 	} else { | 	} else { | ||||||
|  | @ -1186,7 +1186,7 @@ static void attach_default_qdiscs(struct net_device *dev) | ||||||
| 		netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc); | 		netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc); | ||||||
| 		dev->priv_flags |= IFF_NO_QUEUE; | 		dev->priv_flags |= IFF_NO_QUEUE; | ||||||
| 		netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); | 		netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); | ||||||
| 		qdisc = txq->qdisc_sleeping; | 		qdisc = rtnl_dereference(txq->qdisc_sleeping); | ||||||
| 		rcu_assign_pointer(dev->qdisc, qdisc); | 		rcu_assign_pointer(dev->qdisc, qdisc); | ||||||
| 		qdisc_refcount_inc(qdisc); | 		qdisc_refcount_inc(qdisc); | ||||||
| 		dev->priv_flags ^= IFF_NO_QUEUE; | 		dev->priv_flags ^= IFF_NO_QUEUE; | ||||||
|  | @ -1202,7 +1202,7 @@ static void transition_one_qdisc(struct net_device *dev, | ||||||
| 				 struct netdev_queue *dev_queue, | 				 struct netdev_queue *dev_queue, | ||||||
| 				 void *_need_watchdog) | 				 void *_need_watchdog) | ||||||
| { | { | ||||||
| 	struct Qdisc *new_qdisc = dev_queue->qdisc_sleeping; | 	struct Qdisc *new_qdisc = rtnl_dereference(dev_queue->qdisc_sleeping); | ||||||
| 	int *need_watchdog_p = _need_watchdog; | 	int *need_watchdog_p = _need_watchdog; | ||||||
| 
 | 
 | ||||||
| 	if (!(new_qdisc->flags & TCQ_F_BUILTIN)) | 	if (!(new_qdisc->flags & TCQ_F_BUILTIN)) | ||||||
|  | @ -1272,7 +1272,7 @@ static void dev_reset_queue(struct net_device *dev, | ||||||
| 	struct Qdisc *qdisc; | 	struct Qdisc *qdisc; | ||||||
| 	bool nolock; | 	bool nolock; | ||||||
| 
 | 
 | ||||||
| 	qdisc = dev_queue->qdisc_sleeping; | 	qdisc = rtnl_dereference(dev_queue->qdisc_sleeping); | ||||||
| 	if (!qdisc) | 	if (!qdisc) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
|  | @ -1303,7 +1303,7 @@ static bool some_qdisc_is_busy(struct net_device *dev) | ||||||
| 		int val; | 		int val; | ||||||
| 
 | 
 | ||||||
| 		dev_queue = netdev_get_tx_queue(dev, i); | 		dev_queue = netdev_get_tx_queue(dev, i); | ||||||
| 		q = dev_queue->qdisc_sleeping; | 		q = rtnl_dereference(dev_queue->qdisc_sleeping); | ||||||
| 
 | 
 | ||||||
| 		root_lock = qdisc_lock(q); | 		root_lock = qdisc_lock(q); | ||||||
| 		spin_lock_bh(root_lock); | 		spin_lock_bh(root_lock); | ||||||
|  | @ -1379,7 +1379,7 @@ EXPORT_SYMBOL(dev_deactivate); | ||||||
| static int qdisc_change_tx_queue_len(struct net_device *dev, | static int qdisc_change_tx_queue_len(struct net_device *dev, | ||||||
| 				     struct netdev_queue *dev_queue) | 				     struct netdev_queue *dev_queue) | ||||||
| { | { | ||||||
| 	struct Qdisc *qdisc = dev_queue->qdisc_sleeping; | 	struct Qdisc *qdisc = rtnl_dereference(dev_queue->qdisc_sleeping); | ||||||
| 	const struct Qdisc_ops *ops = qdisc->ops; | 	const struct Qdisc_ops *ops = qdisc->ops; | ||||||
| 
 | 
 | ||||||
| 	if (ops->change_tx_queue_len) | 	if (ops->change_tx_queue_len) | ||||||
|  | @ -1404,7 +1404,7 @@ void mq_change_real_num_tx(struct Qdisc *sch, unsigned int new_real_tx) | ||||||
| 	unsigned int i; | 	unsigned int i; | ||||||
| 
 | 
 | ||||||
| 	for (i = new_real_tx; i < dev->real_num_tx_queues; i++) { | 	for (i = new_real_tx; i < dev->real_num_tx_queues; i++) { | ||||||
| 		qdisc = netdev_get_tx_queue(dev, i)->qdisc_sleeping; | 		qdisc = rtnl_dereference(netdev_get_tx_queue(dev, i)->qdisc_sleeping); | ||||||
| 		/* Only update the default qdiscs we created,
 | 		/* Only update the default qdiscs we created,
 | ||||||
| 		 * qdiscs with handles are always hashed. | 		 * qdiscs with handles are always hashed. | ||||||
| 		 */ | 		 */ | ||||||
|  | @ -1412,7 +1412,7 @@ void mq_change_real_num_tx(struct Qdisc *sch, unsigned int new_real_tx) | ||||||
| 			qdisc_hash_del(qdisc); | 			qdisc_hash_del(qdisc); | ||||||
| 	} | 	} | ||||||
| 	for (i = dev->real_num_tx_queues; i < new_real_tx; i++) { | 	for (i = dev->real_num_tx_queues; i < new_real_tx; i++) { | ||||||
| 		qdisc = netdev_get_tx_queue(dev, i)->qdisc_sleeping; | 		qdisc = rtnl_dereference(netdev_get_tx_queue(dev, i)->qdisc_sleeping); | ||||||
| 		if (qdisc != &noop_qdisc && !qdisc->handle) | 		if (qdisc != &noop_qdisc && !qdisc->handle) | ||||||
| 			qdisc_hash_add(qdisc, false); | 			qdisc_hash_add(qdisc, false); | ||||||
| 	} | 	} | ||||||
|  | @ -1449,7 +1449,7 @@ static void dev_init_scheduler_queue(struct net_device *dev, | ||||||
| 	struct Qdisc *qdisc = _qdisc; | 	struct Qdisc *qdisc = _qdisc; | ||||||
| 
 | 
 | ||||||
| 	rcu_assign_pointer(dev_queue->qdisc, qdisc); | 	rcu_assign_pointer(dev_queue->qdisc, qdisc); | ||||||
| 	dev_queue->qdisc_sleeping = qdisc; | 	rcu_assign_pointer(dev_queue->qdisc_sleeping, qdisc); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void dev_init_scheduler(struct net_device *dev) | void dev_init_scheduler(struct net_device *dev) | ||||||
|  |  | ||||||
|  | @ -141,7 +141,7 @@ static int mq_dump(struct Qdisc *sch, struct sk_buff *skb) | ||||||
| 	 * qdisc totals are added at end. | 	 * qdisc totals are added at end. | ||||||
| 	 */ | 	 */ | ||||||
| 	for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { | 	for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { | ||||||
| 		qdisc = netdev_get_tx_queue(dev, ntx)->qdisc_sleeping; | 		qdisc = rtnl_dereference(netdev_get_tx_queue(dev, ntx)->qdisc_sleeping); | ||||||
| 		spin_lock_bh(qdisc_lock(qdisc)); | 		spin_lock_bh(qdisc_lock(qdisc)); | ||||||
| 
 | 
 | ||||||
| 		gnet_stats_add_basic(&sch->bstats, qdisc->cpu_bstats, | 		gnet_stats_add_basic(&sch->bstats, qdisc->cpu_bstats, | ||||||
|  | @ -202,7 +202,7 @@ static struct Qdisc *mq_leaf(struct Qdisc *sch, unsigned long cl) | ||||||
| { | { | ||||||
| 	struct netdev_queue *dev_queue = mq_queue_get(sch, cl); | 	struct netdev_queue *dev_queue = mq_queue_get(sch, cl); | ||||||
| 
 | 
 | ||||||
| 	return dev_queue->qdisc_sleeping; | 	return rtnl_dereference(dev_queue->qdisc_sleeping); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static unsigned long mq_find(struct Qdisc *sch, u32 classid) | static unsigned long mq_find(struct Qdisc *sch, u32 classid) | ||||||
|  | @ -221,7 +221,7 @@ static int mq_dump_class(struct Qdisc *sch, unsigned long cl, | ||||||
| 
 | 
 | ||||||
| 	tcm->tcm_parent = TC_H_ROOT; | 	tcm->tcm_parent = TC_H_ROOT; | ||||||
| 	tcm->tcm_handle |= TC_H_MIN(cl); | 	tcm->tcm_handle |= TC_H_MIN(cl); | ||||||
| 	tcm->tcm_info = dev_queue->qdisc_sleeping->handle; | 	tcm->tcm_info = rtnl_dereference(dev_queue->qdisc_sleeping)->handle; | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -230,7 +230,7 @@ static int mq_dump_class_stats(struct Qdisc *sch, unsigned long cl, | ||||||
| { | { | ||||||
| 	struct netdev_queue *dev_queue = mq_queue_get(sch, cl); | 	struct netdev_queue *dev_queue = mq_queue_get(sch, cl); | ||||||
| 
 | 
 | ||||||
| 	sch = dev_queue->qdisc_sleeping; | 	sch = rtnl_dereference(dev_queue->qdisc_sleeping); | ||||||
| 	if (gnet_stats_copy_basic(d, sch->cpu_bstats, &sch->bstats, true) < 0 || | 	if (gnet_stats_copy_basic(d, sch->cpu_bstats, &sch->bstats, true) < 0 || | ||||||
| 	    qdisc_qstats_copy(d, sch) < 0) | 	    qdisc_qstats_copy(d, sch) < 0) | ||||||
| 		return -1; | 		return -1; | ||||||
|  |  | ||||||
|  | @ -557,7 +557,7 @@ static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb) | ||||||
| 	 * qdisc totals are added at end. | 	 * qdisc totals are added at end. | ||||||
| 	 */ | 	 */ | ||||||
| 	for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { | 	for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { | ||||||
| 		qdisc = netdev_get_tx_queue(dev, ntx)->qdisc_sleeping; | 		qdisc = rtnl_dereference(netdev_get_tx_queue(dev, ntx)->qdisc_sleeping); | ||||||
| 		spin_lock_bh(qdisc_lock(qdisc)); | 		spin_lock_bh(qdisc_lock(qdisc)); | ||||||
| 
 | 
 | ||||||
| 		gnet_stats_add_basic(&sch->bstats, qdisc->cpu_bstats, | 		gnet_stats_add_basic(&sch->bstats, qdisc->cpu_bstats, | ||||||
|  | @ -604,7 +604,7 @@ static struct Qdisc *mqprio_leaf(struct Qdisc *sch, unsigned long cl) | ||||||
| 	if (!dev_queue) | 	if (!dev_queue) | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 
 | 
 | ||||||
| 	return dev_queue->qdisc_sleeping; | 	return rtnl_dereference(dev_queue->qdisc_sleeping); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static unsigned long mqprio_find(struct Qdisc *sch, u32 classid) | static unsigned long mqprio_find(struct Qdisc *sch, u32 classid) | ||||||
|  | @ -637,7 +637,7 @@ static int mqprio_dump_class(struct Qdisc *sch, unsigned long cl, | ||||||
| 		tcm->tcm_parent = (tc < 0) ? 0 : | 		tcm->tcm_parent = (tc < 0) ? 0 : | ||||||
| 			TC_H_MAKE(TC_H_MAJ(sch->handle), | 			TC_H_MAKE(TC_H_MAJ(sch->handle), | ||||||
| 				  TC_H_MIN(tc + TC_H_MIN_PRIORITY)); | 				  TC_H_MIN(tc + TC_H_MIN_PRIORITY)); | ||||||
| 		tcm->tcm_info = dev_queue->qdisc_sleeping->handle; | 		tcm->tcm_info = rtnl_dereference(dev_queue->qdisc_sleeping)->handle; | ||||||
| 	} else { | 	} else { | ||||||
| 		tcm->tcm_parent = TC_H_ROOT; | 		tcm->tcm_parent = TC_H_ROOT; | ||||||
| 		tcm->tcm_info = 0; | 		tcm->tcm_info = 0; | ||||||
|  | @ -693,7 +693,7 @@ static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl, | ||||||
| 	} else { | 	} else { | ||||||
| 		struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl); | 		struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl); | ||||||
| 
 | 
 | ||||||
| 		sch = dev_queue->qdisc_sleeping; | 		sch = rtnl_dereference(dev_queue->qdisc_sleeping); | ||||||
| 		if (gnet_stats_copy_basic(d, sch->cpu_bstats, | 		if (gnet_stats_copy_basic(d, sch->cpu_bstats, | ||||||
| 					  &sch->bstats, true) < 0 || | 					  &sch->bstats, true) < 0 || | ||||||
| 		    qdisc_qstats_copy(d, sch) < 0) | 		    qdisc_qstats_copy(d, sch) < 0) | ||||||
|  |  | ||||||
|  | @ -421,8 +421,10 @@ static void pie_timer(struct timer_list *t) | ||||||
| { | { | ||||||
| 	struct pie_sched_data *q = from_timer(q, t, adapt_timer); | 	struct pie_sched_data *q = from_timer(q, t, adapt_timer); | ||||||
| 	struct Qdisc *sch = q->sch; | 	struct Qdisc *sch = q->sch; | ||||||
| 	spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch)); | 	spinlock_t *root_lock; | ||||||
| 
 | 
 | ||||||
|  | 	rcu_read_lock(); | ||||||
|  | 	root_lock = qdisc_lock(qdisc_root_sleeping(sch)); | ||||||
| 	spin_lock(root_lock); | 	spin_lock(root_lock); | ||||||
| 	pie_calculate_probability(&q->params, &q->vars, sch->qstats.backlog); | 	pie_calculate_probability(&q->params, &q->vars, sch->qstats.backlog); | ||||||
| 
 | 
 | ||||||
|  | @ -430,6 +432,7 @@ static void pie_timer(struct timer_list *t) | ||||||
| 	if (q->params.tupdate) | 	if (q->params.tupdate) | ||||||
| 		mod_timer(&q->adapt_timer, jiffies + q->params.tupdate); | 		mod_timer(&q->adapt_timer, jiffies + q->params.tupdate); | ||||||
| 	spin_unlock(root_lock); | 	spin_unlock(root_lock); | ||||||
|  | 	rcu_read_unlock(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int pie_init(struct Qdisc *sch, struct nlattr *opt, | static int pie_init(struct Qdisc *sch, struct nlattr *opt, | ||||||
|  |  | ||||||
|  | @ -321,12 +321,15 @@ static inline void red_adaptative_timer(struct timer_list *t) | ||||||
| { | { | ||||||
| 	struct red_sched_data *q = from_timer(q, t, adapt_timer); | 	struct red_sched_data *q = from_timer(q, t, adapt_timer); | ||||||
| 	struct Qdisc *sch = q->sch; | 	struct Qdisc *sch = q->sch; | ||||||
| 	spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch)); | 	spinlock_t *root_lock; | ||||||
| 
 | 
 | ||||||
|  | 	rcu_read_lock(); | ||||||
|  | 	root_lock = qdisc_lock(qdisc_root_sleeping(sch)); | ||||||
| 	spin_lock(root_lock); | 	spin_lock(root_lock); | ||||||
| 	red_adaptative_algo(&q->parms, &q->vars); | 	red_adaptative_algo(&q->parms, &q->vars); | ||||||
| 	mod_timer(&q->adapt_timer, jiffies + HZ/2); | 	mod_timer(&q->adapt_timer, jiffies + HZ/2); | ||||||
| 	spin_unlock(root_lock); | 	spin_unlock(root_lock); | ||||||
|  | 	rcu_read_unlock(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int red_init(struct Qdisc *sch, struct nlattr *opt, | static int red_init(struct Qdisc *sch, struct nlattr *opt, | ||||||
|  |  | ||||||
|  | @ -606,10 +606,12 @@ static void sfq_perturbation(struct timer_list *t) | ||||||
| { | { | ||||||
| 	struct sfq_sched_data *q = from_timer(q, t, perturb_timer); | 	struct sfq_sched_data *q = from_timer(q, t, perturb_timer); | ||||||
| 	struct Qdisc *sch = q->sch; | 	struct Qdisc *sch = q->sch; | ||||||
| 	spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch)); | 	spinlock_t *root_lock; | ||||||
| 	siphash_key_t nkey; | 	siphash_key_t nkey; | ||||||
| 
 | 
 | ||||||
| 	get_random_bytes(&nkey, sizeof(nkey)); | 	get_random_bytes(&nkey, sizeof(nkey)); | ||||||
|  | 	rcu_read_lock(); | ||||||
|  | 	root_lock = qdisc_lock(qdisc_root_sleeping(sch)); | ||||||
| 	spin_lock(root_lock); | 	spin_lock(root_lock); | ||||||
| 	q->perturbation = nkey; | 	q->perturbation = nkey; | ||||||
| 	if (!q->filter_list && q->tail) | 	if (!q->filter_list && q->tail) | ||||||
|  | @ -618,6 +620,7 @@ static void sfq_perturbation(struct timer_list *t) | ||||||
| 
 | 
 | ||||||
| 	if (q->perturb_period) | 	if (q->perturb_period) | ||||||
| 		mod_timer(&q->perturb_timer, jiffies + q->perturb_period); | 		mod_timer(&q->perturb_timer, jiffies + q->perturb_period); | ||||||
|  | 	rcu_read_unlock(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int sfq_change(struct Qdisc *sch, struct nlattr *opt) | static int sfq_change(struct Qdisc *sch, struct nlattr *opt) | ||||||
|  |  | ||||||
|  | @ -2358,7 +2358,7 @@ static struct Qdisc *taprio_leaf(struct Qdisc *sch, unsigned long cl) | ||||||
| 	if (!dev_queue) | 	if (!dev_queue) | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 
 | 
 | ||||||
| 	return dev_queue->qdisc_sleeping; | 	return rtnl_dereference(dev_queue->qdisc_sleeping); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static unsigned long taprio_find(struct Qdisc *sch, u32 classid) | static unsigned long taprio_find(struct Qdisc *sch, u32 classid) | ||||||
|  | @ -2377,7 +2377,7 @@ static int taprio_dump_class(struct Qdisc *sch, unsigned long cl, | ||||||
| 
 | 
 | ||||||
| 	tcm->tcm_parent = TC_H_ROOT; | 	tcm->tcm_parent = TC_H_ROOT; | ||||||
| 	tcm->tcm_handle |= TC_H_MIN(cl); | 	tcm->tcm_handle |= TC_H_MIN(cl); | ||||||
| 	tcm->tcm_info = dev_queue->qdisc_sleeping->handle; | 	tcm->tcm_info = rtnl_dereference(dev_queue->qdisc_sleeping)->handle; | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  | @ -2389,7 +2389,7 @@ static int taprio_dump_class_stats(struct Qdisc *sch, unsigned long cl, | ||||||
| { | { | ||||||
| 	struct netdev_queue *dev_queue = taprio_queue_get(sch, cl); | 	struct netdev_queue *dev_queue = taprio_queue_get(sch, cl); | ||||||
| 
 | 
 | ||||||
| 	sch = dev_queue->qdisc_sleeping; | 	sch = rtnl_dereference(dev_queue->qdisc_sleeping); | ||||||
| 	if (gnet_stats_copy_basic(d, NULL, &sch->bstats, true) < 0 || | 	if (gnet_stats_copy_basic(d, NULL, &sch->bstats, true) < 0 || | ||||||
| 	    qdisc_qstats_copy(d, sch) < 0) | 	    qdisc_qstats_copy(d, sch) < 0) | ||||||
| 		return -1; | 		return -1; | ||||||
|  |  | ||||||
|  | @ -297,7 +297,7 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) | ||||||
| 		struct net_device *slave = qdisc_dev(q); | 		struct net_device *slave = qdisc_dev(q); | ||||||
| 		struct netdev_queue *slave_txq = netdev_get_tx_queue(slave, 0); | 		struct netdev_queue *slave_txq = netdev_get_tx_queue(slave, 0); | ||||||
| 
 | 
 | ||||||
| 		if (slave_txq->qdisc_sleeping != q) | 		if (rcu_access_pointer(slave_txq->qdisc_sleeping) != q) | ||||||
| 			continue; | 			continue; | ||||||
| 		if (netif_xmit_stopped(netdev_get_tx_queue(slave, subq)) || | 		if (netif_xmit_stopped(netdev_get_tx_queue(slave, subq)) || | ||||||
| 		    !netif_running(slave)) { | 		    !netif_running(slave)) { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Eric Dumazet
						Eric Dumazet