diff options
author | Eric Dumazet <edumazet@google.com> | 2016-06-06 12:37:15 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-06-07 19:37:13 -0400 |
commit | f9eb8aea2a1e12fc2f584d1627deeb957435a801 (patch) | |
tree | bc3cf06664da45bc0e6ce9b5165dba534ed11751 | |
parent | 64151ae36ed93c45654069c8aff2a7f0125075e8 (diff) |
net_sched: transform qdisc running bit into a seqcount
Instead of using a single bit (__QDISC___STATE_RUNNING)
in sch->__state, use a seqcount.
This adds lockdep support, but more importantly it will allow us
to sample qdisc/class statistics without having to grab qdisc root lock.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Cong Wang <xiyou.wangcong@gmail.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/bonding/bond_main.c | 2 | ||||
-rw-r--r-- | drivers/net/ppp/ppp_generic.c | 3 | ||||
-rw-r--r-- | drivers/net/team/team.c | 2 | ||||
-rw-r--r-- | include/linux/netdevice.h | 1 | ||||
-rw-r--r-- | include/net/sch_generic.h | 15 | ||||
-rw-r--r-- | net/bluetooth/6lowpan.c | 2 | ||||
-rw-r--r-- | net/core/dev.c | 2 | ||||
-rw-r--r-- | net/ieee802154/6lowpan/core.c | 3 | ||||
-rw-r--r-- | net/l2tp/l2tp_eth.c | 4 | ||||
-rw-r--r-- | net/sched/sch_generic.c | 14 |
10 files changed, 32 insertions, 16 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 941ec99cd3b6..681af31a60ed 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c | |||
@@ -4610,6 +4610,7 @@ static int bond_check_params(struct bond_params *params) | |||
4610 | static struct lock_class_key bonding_netdev_xmit_lock_key; | 4610 | static struct lock_class_key bonding_netdev_xmit_lock_key; |
4611 | static struct lock_class_key bonding_netdev_addr_lock_key; | 4611 | static struct lock_class_key bonding_netdev_addr_lock_key; |
4612 | static struct lock_class_key bonding_tx_busylock_key; | 4612 | static struct lock_class_key bonding_tx_busylock_key; |
4613 | static struct lock_class_key bonding_qdisc_running_key; | ||
4613 | 4614 | ||
4614 | static void bond_set_lockdep_class_one(struct net_device *dev, | 4615 | static void bond_set_lockdep_class_one(struct net_device *dev, |
4615 | struct netdev_queue *txq, | 4616 | struct netdev_queue *txq, |
@@ -4625,6 +4626,7 @@ static void bond_set_lockdep_class(struct net_device *dev) | |||
4625 | &bonding_netdev_addr_lock_key); | 4626 | &bonding_netdev_addr_lock_key); |
4626 | netdev_for_each_tx_queue(dev, bond_set_lockdep_class_one, NULL); | 4627 | netdev_for_each_tx_queue(dev, bond_set_lockdep_class_one, NULL); |
4627 | dev->qdisc_tx_busylock = &bonding_tx_busylock_key; | 4628 | dev->qdisc_tx_busylock = &bonding_tx_busylock_key; |
4629 | dev->qdisc_running_key = &bonding_qdisc_running_key; | ||
4628 | } | 4630 | } |
4629 | 4631 | ||
4630 | /* Called from registration process */ | 4632 | /* Called from registration process */ |
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 8dedafa1a95d..aeabaa42317f 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c | |||
@@ -1313,9 +1313,12 @@ ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64) | |||
1313 | } | 1313 | } |
1314 | 1314 | ||
1315 | static struct lock_class_key ppp_tx_busylock; | 1315 | static struct lock_class_key ppp_tx_busylock; |
1316 | static struct lock_class_key ppp_qdisc_running_key; | ||
1317 | |||
1316 | static int ppp_dev_init(struct net_device *dev) | 1318 | static int ppp_dev_init(struct net_device *dev) |
1317 | { | 1319 | { |
1318 | dev->qdisc_tx_busylock = &ppp_tx_busylock; | 1320 | dev->qdisc_tx_busylock = &ppp_tx_busylock; |
1321 | dev->qdisc_running_key = &ppp_qdisc_running_key; | ||
1319 | return 0; | 1322 | return 0; |
1320 | } | 1323 | } |
1321 | 1324 | ||
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 2ace126533cd..00eb38956a2c 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c | |||
@@ -1577,6 +1577,7 @@ static const struct team_option team_options[] = { | |||
1577 | static struct lock_class_key team_netdev_xmit_lock_key; | 1577 | static struct lock_class_key team_netdev_xmit_lock_key; |
1578 | static struct lock_class_key team_netdev_addr_lock_key; | 1578 | static struct lock_class_key team_netdev_addr_lock_key; |
1579 | static struct lock_class_key team_tx_busylock_key; | 1579 | static struct lock_class_key team_tx_busylock_key; |
1580 | static struct lock_class_key team_qdisc_running_key; | ||
1580 | 1581 | ||
1581 | static void team_set_lockdep_class_one(struct net_device *dev, | 1582 | static void team_set_lockdep_class_one(struct net_device *dev, |
1582 | struct netdev_queue *txq, | 1583 | struct netdev_queue *txq, |
@@ -1590,6 +1591,7 @@ static void team_set_lockdep_class(struct net_device *dev) | |||
1590 | lockdep_set_class(&dev->addr_list_lock, &team_netdev_addr_lock_key); | 1591 | lockdep_set_class(&dev->addr_list_lock, &team_netdev_addr_lock_key); |
1591 | netdev_for_each_tx_queue(dev, team_set_lockdep_class_one, NULL); | 1592 | netdev_for_each_tx_queue(dev, team_set_lockdep_class_one, NULL); |
1592 | dev->qdisc_tx_busylock = &team_tx_busylock_key; | 1593 | dev->qdisc_tx_busylock = &team_tx_busylock_key; |
1594 | dev->qdisc_running_key = &team_qdisc_running_key; | ||
1593 | } | 1595 | } |
1594 | 1596 | ||
1595 | static int team_init(struct net_device *dev) | 1597 | static int team_init(struct net_device *dev) |
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fa6df2699532..59d7e06d88d5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -1862,6 +1862,7 @@ struct net_device { | |||
1862 | #endif | 1862 | #endif |
1863 | struct phy_device *phydev; | 1863 | struct phy_device *phydev; |
1864 | struct lock_class_key *qdisc_tx_busylock; | 1864 | struct lock_class_key *qdisc_tx_busylock; |
1865 | struct lock_class_key *qdisc_running_key; | ||
1865 | bool proto_down; | 1866 | bool proto_down; |
1866 | }; | 1867 | }; |
1867 | #define to_net_dev(d) container_of(d, struct net_device, dev) | 1868 | #define to_net_dev(d) container_of(d, struct net_device, dev) |
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index a1fd76c22a59..bff8d895ef8a 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h | |||
@@ -29,13 +29,6 @@ enum qdisc_state_t { | |||
29 | __QDISC_STATE_THROTTLED, | 29 | __QDISC_STATE_THROTTLED, |
30 | }; | 30 | }; |
31 | 31 | ||
32 | /* | ||
33 | * following bits are only changed while qdisc lock is held | ||
34 | */ | ||
35 | enum qdisc___state_t { | ||
36 | __QDISC___STATE_RUNNING = 1, | ||
37 | }; | ||
38 | |||
39 | struct qdisc_size_table { | 32 | struct qdisc_size_table { |
40 | struct rcu_head rcu; | 33 | struct rcu_head rcu; |
41 | struct list_head list; | 34 | struct list_head list; |
@@ -93,7 +86,7 @@ struct Qdisc { | |||
93 | unsigned long state; | 86 | unsigned long state; |
94 | struct sk_buff_head q; | 87 | struct sk_buff_head q; |
95 | struct gnet_stats_basic_packed bstats; | 88 | struct gnet_stats_basic_packed bstats; |
96 | unsigned int __state; | 89 | seqcount_t running; |
97 | struct gnet_stats_queue qstats; | 90 | struct gnet_stats_queue qstats; |
98 | struct rcu_head rcu_head; | 91 | struct rcu_head rcu_head; |
99 | int padded; | 92 | int padded; |
@@ -104,20 +97,20 @@ struct Qdisc { | |||
104 | 97 | ||
105 | static inline bool qdisc_is_running(const struct Qdisc *qdisc) | 98 | static inline bool qdisc_is_running(const struct Qdisc *qdisc) |
106 | { | 99 | { |
107 | return (qdisc->__state & __QDISC___STATE_RUNNING) ? true : false; | 100 | return (raw_read_seqcount(&qdisc->running) & 1) ? true : false; |
108 | } | 101 | } |
109 | 102 | ||
110 | static inline bool qdisc_run_begin(struct Qdisc *qdisc) | 103 | static inline bool qdisc_run_begin(struct Qdisc *qdisc) |
111 | { | 104 | { |
112 | if (qdisc_is_running(qdisc)) | 105 | if (qdisc_is_running(qdisc)) |
113 | return false; | 106 | return false; |
114 | qdisc->__state |= __QDISC___STATE_RUNNING; | 107 | write_seqcount_begin(&qdisc->running); |
115 | return true; | 108 | return true; |
116 | } | 109 | } |
117 | 110 | ||
118 | static inline void qdisc_run_end(struct Qdisc *qdisc) | 111 | static inline void qdisc_run_end(struct Qdisc *qdisc) |
119 | { | 112 | { |
120 | qdisc->__state &= ~__QDISC___STATE_RUNNING; | 113 | write_seqcount_end(&qdisc->running); |
121 | } | 114 | } |
122 | 115 | ||
123 | static inline bool qdisc_may_bulk(const struct Qdisc *qdisc) | 116 | static inline bool qdisc_may_bulk(const struct Qdisc *qdisc) |
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 780089d75915..977a11e418d0 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c | |||
@@ -629,6 +629,7 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev) | |||
629 | 629 | ||
630 | static struct lock_class_key bt_tx_busylock; | 630 | static struct lock_class_key bt_tx_busylock; |
631 | static struct lock_class_key bt_netdev_xmit_lock_key; | 631 | static struct lock_class_key bt_netdev_xmit_lock_key; |
632 | static struct lock_class_key bt_qdisc_running_key; | ||
632 | 633 | ||
633 | static void bt_set_lockdep_class_one(struct net_device *dev, | 634 | static void bt_set_lockdep_class_one(struct net_device *dev, |
634 | struct netdev_queue *txq, | 635 | struct netdev_queue *txq, |
@@ -641,6 +642,7 @@ static int bt_dev_init(struct net_device *dev) | |||
641 | { | 642 | { |
642 | netdev_for_each_tx_queue(dev, bt_set_lockdep_class_one, NULL); | 643 | netdev_for_each_tx_queue(dev, bt_set_lockdep_class_one, NULL); |
643 | dev->qdisc_tx_busylock = &bt_tx_busylock; | 644 | dev->qdisc_tx_busylock = &bt_tx_busylock; |
645 | dev->qdisc_running_key = &bt_qdisc_running_key; | ||
644 | 646 | ||
645 | return 0; | 647 | return 0; |
646 | } | 648 | } |
diff --git a/net/core/dev.c b/net/core/dev.c index 896b686d1966..e0bcc39f4a7d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -3075,7 +3075,7 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, | |||
3075 | /* | 3075 | /* |
3076 | * Heuristic to force contended enqueues to serialize on a | 3076 | * Heuristic to force contended enqueues to serialize on a |
3077 | * separate lock before trying to get qdisc main lock. | 3077 | * separate lock before trying to get qdisc main lock. |
3078 | * This permits __QDISC___STATE_RUNNING owner to get the lock more | 3078 | * This permits qdisc->running owner to get the lock more |
3079 | * often and dequeue packets faster. | 3079 | * often and dequeue packets faster. |
3080 | */ | 3080 | */ |
3081 | contended = qdisc_is_running(q); | 3081 | contended = qdisc_is_running(q); |
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c index dd085db8580e..14aa5effd29a 100644 --- a/net/ieee802154/6lowpan/core.c +++ b/net/ieee802154/6lowpan/core.c | |||
@@ -60,6 +60,7 @@ static struct header_ops lowpan_header_ops = { | |||
60 | 60 | ||
61 | static struct lock_class_key lowpan_tx_busylock; | 61 | static struct lock_class_key lowpan_tx_busylock; |
62 | static struct lock_class_key lowpan_netdev_xmit_lock_key; | 62 | static struct lock_class_key lowpan_netdev_xmit_lock_key; |
63 | static struct lock_class_key lowpan_qdisc_running_key; | ||
63 | 64 | ||
64 | static void lowpan_set_lockdep_class_one(struct net_device *ldev, | 65 | static void lowpan_set_lockdep_class_one(struct net_device *ldev, |
65 | struct netdev_queue *txq, | 66 | struct netdev_queue *txq, |
@@ -73,6 +74,8 @@ static int lowpan_dev_init(struct net_device *ldev) | |||
73 | { | 74 | { |
74 | netdev_for_each_tx_queue(ldev, lowpan_set_lockdep_class_one, NULL); | 75 | netdev_for_each_tx_queue(ldev, lowpan_set_lockdep_class_one, NULL); |
75 | ldev->qdisc_tx_busylock = &lowpan_tx_busylock; | 76 | ldev->qdisc_tx_busylock = &lowpan_tx_busylock; |
77 | ldev->qdisc_running_key = &lowpan_qdisc_running_key; | ||
78 | |||
76 | return 0; | 79 | return 0; |
77 | } | 80 | } |
78 | 81 | ||
diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index e253c26f31ac..c00d72d182fa 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c | |||
@@ -68,6 +68,8 @@ static inline struct l2tp_eth_net *l2tp_eth_pernet(struct net *net) | |||
68 | } | 68 | } |
69 | 69 | ||
70 | static struct lock_class_key l2tp_eth_tx_busylock; | 70 | static struct lock_class_key l2tp_eth_tx_busylock; |
71 | static struct lock_class_key l2tp_qdisc_running_key; | ||
72 | |||
71 | static int l2tp_eth_dev_init(struct net_device *dev) | 73 | static int l2tp_eth_dev_init(struct net_device *dev) |
72 | { | 74 | { |
73 | struct l2tp_eth *priv = netdev_priv(dev); | 75 | struct l2tp_eth *priv = netdev_priv(dev); |
@@ -76,6 +78,8 @@ static int l2tp_eth_dev_init(struct net_device *dev) | |||
76 | eth_hw_addr_random(dev); | 78 | eth_hw_addr_random(dev); |
77 | eth_broadcast_addr(dev->broadcast); | 79 | eth_broadcast_addr(dev->broadcast); |
78 | dev->qdisc_tx_busylock = &l2tp_eth_tx_busylock; | 80 | dev->qdisc_tx_busylock = &l2tp_eth_tx_busylock; |
81 | dev->qdisc_running_key = &l2tp_qdisc_running_key; | ||
82 | |||
79 | return 0; | 83 | return 0; |
80 | } | 84 | } |
81 | 85 | ||
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 269dd71b3828..cebea73e70ac 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c | |||
@@ -110,7 +110,7 @@ static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate, | |||
110 | 110 | ||
111 | /* | 111 | /* |
112 | * Transmit possibly several skbs, and handle the return status as | 112 | * Transmit possibly several skbs, and handle the return status as |
113 | * required. Holding the __QDISC___STATE_RUNNING bit guarantees that | 113 | * required. Owning running seqcount bit guarantees that |
114 | * only one CPU can execute this function. | 114 | * only one CPU can execute this function. |
115 | * | 115 | * |
116 | * Returns to the caller: | 116 | * Returns to the caller: |
@@ -137,10 +137,10 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q, | |||
137 | 137 | ||
138 | HARD_TX_UNLOCK(dev, txq); | 138 | HARD_TX_UNLOCK(dev, txq); |
139 | } else { | 139 | } else { |
140 | spin_lock(root_lock); | 140 | spin_lock_nested(root_lock, SINGLE_DEPTH_NESTING); |
141 | return qdisc_qlen(q); | 141 | return qdisc_qlen(q); |
142 | } | 142 | } |
143 | spin_lock(root_lock); | 143 | spin_lock_nested(root_lock, SINGLE_DEPTH_NESTING); |
144 | 144 | ||
145 | if (dev_xmit_complete(ret)) { | 145 | if (dev_xmit_complete(ret)) { |
146 | /* Driver sent out skb successfully or skb was consumed */ | 146 | /* Driver sent out skb successfully or skb was consumed */ |
@@ -163,7 +163,7 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q, | |||
163 | /* | 163 | /* |
164 | * NOTE: Called under qdisc_lock(q) with locally disabled BH. | 164 | * NOTE: Called under qdisc_lock(q) with locally disabled BH. |
165 | * | 165 | * |
166 | * __QDISC___STATE_RUNNING guarantees only one CPU can process | 166 | * running seqcount guarantees only one CPU can process |
167 | * this qdisc at a time. qdisc_lock(q) serializes queue accesses for | 167 | * this qdisc at a time. qdisc_lock(q) serializes queue accesses for |
168 | * this queue. | 168 | * this queue. |
169 | * | 169 | * |
@@ -379,6 +379,7 @@ struct Qdisc noop_qdisc = { | |||
379 | .list = LIST_HEAD_INIT(noop_qdisc.list), | 379 | .list = LIST_HEAD_INIT(noop_qdisc.list), |
380 | .q.lock = __SPIN_LOCK_UNLOCKED(noop_qdisc.q.lock), | 380 | .q.lock = __SPIN_LOCK_UNLOCKED(noop_qdisc.q.lock), |
381 | .dev_queue = &noop_netdev_queue, | 381 | .dev_queue = &noop_netdev_queue, |
382 | .running = SEQCNT_ZERO(noop_qdisc.running), | ||
382 | .busylock = __SPIN_LOCK_UNLOCKED(noop_qdisc.busylock), | 383 | .busylock = __SPIN_LOCK_UNLOCKED(noop_qdisc.busylock), |
383 | }; | 384 | }; |
384 | EXPORT_SYMBOL(noop_qdisc); | 385 | EXPORT_SYMBOL(noop_qdisc); |
@@ -537,6 +538,7 @@ struct Qdisc_ops pfifo_fast_ops __read_mostly = { | |||
537 | EXPORT_SYMBOL(pfifo_fast_ops); | 538 | EXPORT_SYMBOL(pfifo_fast_ops); |
538 | 539 | ||
539 | static struct lock_class_key qdisc_tx_busylock; | 540 | static struct lock_class_key qdisc_tx_busylock; |
541 | static struct lock_class_key qdisc_running_key; | ||
540 | 542 | ||
541 | struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, | 543 | struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, |
542 | const struct Qdisc_ops *ops) | 544 | const struct Qdisc_ops *ops) |
@@ -570,6 +572,10 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, | |||
570 | lockdep_set_class(&sch->busylock, | 572 | lockdep_set_class(&sch->busylock, |
571 | dev->qdisc_tx_busylock ?: &qdisc_tx_busylock); | 573 | dev->qdisc_tx_busylock ?: &qdisc_tx_busylock); |
572 | 574 | ||
575 | seqcount_init(&sch->running); | ||
576 | lockdep_set_class(&sch->running, | ||
577 | dev->qdisc_running_key ?: &qdisc_running_key); | ||
578 | |||
573 | sch->ops = ops; | 579 | sch->ops = ops; |
574 | sch->enqueue = ops->enqueue; | 580 | sch->enqueue = ops->enqueue; |
575 | sch->dequeue = ops->dequeue; | 581 | sch->dequeue = ops->dequeue; |