diff options
author | John Fastabend <john.r.fastabend@intel.com> | 2010-07-01 09:21:57 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-07-03 00:59:07 -0400 |
commit | f0796d5c73e59786d09a1e617689d1d415f2db44 (patch) | |
tree | ecb17c65c5b6a162824a1e11fee24364852837f8 | |
parent | 4ef6acff83222f4496ceef7d1f0ee9e50a5bb403 (diff) |
net: decreasing real_num_tx_queues needs to flush qdisc
Reducing real_num_queues needs to flush the qdisc otherwise
skbs with queue_mappings greater then real_num_tx_queues can
be sent to the underlying driver.
The flow for this is,
dev_queue_xmit()
dev_pick_tx()
skb_tx_hash() => hash using real_num_tx_queues
skb_set_queue_mapping()
...
qdisc_enqueue_root() => enqueue skb on txq from hash
...
dev->real_num_tx_queues -= n
...
sch_direct_xmit()
dev_hard_start_xmit()
ndo_start_xmit(skb,dev) => skb queue set with old hash
skbs are enqueued on the qdisc with skb->queue_mapping set
0 < queue_mappings < real_num_tx_queues. When the driver
decreases real_num_tx_queues skb's may be dequeued from the
qdisc with a queue_mapping greater then real_num_tx_queues.
This fixes a case in ixgbe where this was occurring with DCB
and FCoE. Because the driver is using queue_mapping to map
skbs to tx descriptor rings we can potentially map skbs to
rings that no longer exist.
Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
Tested-by: Ross Brattain <ross.b.brattain@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ixgbe/ixgbe_main.c | 2 | ||||
-rw-r--r-- | include/linux/netdevice.h | 3 | ||||
-rw-r--r-- | include/net/sch_generic.h | 12 | ||||
-rw-r--r-- | net/core/dev.c | 18 |
4 files changed, 30 insertions, 5 deletions
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index a0b33165b989..7b5d9764f317 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c | |||
@@ -4001,7 +4001,7 @@ static void ixgbe_set_num_queues(struct ixgbe_adapter *adapter) | |||
4001 | 4001 | ||
4002 | done: | 4002 | done: |
4003 | /* Notify the stack of the (possibly) reduced Tx Queue count. */ | 4003 | /* Notify the stack of the (possibly) reduced Tx Queue count. */ |
4004 | adapter->netdev->real_num_tx_queues = adapter->num_tx_queues; | 4004 | netif_set_real_num_tx_queues(adapter->netdev, adapter->num_tx_queues); |
4005 | } | 4005 | } |
4006 | 4006 | ||
4007 | static void ixgbe_acquire_msix_vectors(struct ixgbe_adapter *adapter, | 4007 | static void ixgbe_acquire_msix_vectors(struct ixgbe_adapter *adapter, |
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 40291f375024..5e6188d9f017 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -1656,6 +1656,9 @@ static inline int netif_is_multiqueue(const struct net_device *dev) | |||
1656 | return (dev->num_tx_queues > 1); | 1656 | return (dev->num_tx_queues > 1); |
1657 | } | 1657 | } |
1658 | 1658 | ||
1659 | extern void netif_set_real_num_tx_queues(struct net_device *dev, | ||
1660 | unsigned int txq); | ||
1661 | |||
1659 | /* Use this variant when it is known for sure that it | 1662 | /* Use this variant when it is known for sure that it |
1660 | * is executing from hardware interrupt context or with hardware interrupts | 1663 | * is executing from hardware interrupt context or with hardware interrupts |
1661 | * disabled. | 1664 | * disabled. |
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index ba749be1e354..433604bb3fe8 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h | |||
@@ -313,13 +313,12 @@ extern void qdisc_calculate_pkt_len(struct sk_buff *skb, | |||
313 | extern void tcf_destroy(struct tcf_proto *tp); | 313 | extern void tcf_destroy(struct tcf_proto *tp); |
314 | extern void tcf_destroy_chain(struct tcf_proto **fl); | 314 | extern void tcf_destroy_chain(struct tcf_proto **fl); |
315 | 315 | ||
316 | /* Reset all TX qdiscs of a device. */ | 316 | /* Reset all TX qdiscs greater then index of a device. */ |
317 | static inline void qdisc_reset_all_tx(struct net_device *dev) | 317 | static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i) |
318 | { | 318 | { |
319 | unsigned int i; | ||
320 | struct Qdisc *qdisc; | 319 | struct Qdisc *qdisc; |
321 | 320 | ||
322 | for (i = 0; i < dev->num_tx_queues; i++) { | 321 | for (; i < dev->num_tx_queues; i++) { |
323 | qdisc = netdev_get_tx_queue(dev, i)->qdisc; | 322 | qdisc = netdev_get_tx_queue(dev, i)->qdisc; |
324 | if (qdisc) { | 323 | if (qdisc) { |
325 | spin_lock_bh(qdisc_lock(qdisc)); | 324 | spin_lock_bh(qdisc_lock(qdisc)); |
@@ -329,6 +328,11 @@ static inline void qdisc_reset_all_tx(struct net_device *dev) | |||
329 | } | 328 | } |
330 | } | 329 | } |
331 | 330 | ||
331 | static inline void qdisc_reset_all_tx(struct net_device *dev) | ||
332 | { | ||
333 | qdisc_reset_all_tx_gt(dev, 0); | ||
334 | } | ||
335 | |||
332 | /* Are all TX queues of the device empty? */ | 336 | /* Are all TX queues of the device empty? */ |
333 | static inline bool qdisc_all_tx_empty(const struct net_device *dev) | 337 | static inline bool qdisc_all_tx_empty(const struct net_device *dev) |
334 | { | 338 | { |
diff --git a/net/core/dev.c b/net/core/dev.c index 2b3bf53bc687..723a34710ad4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -1553,6 +1553,24 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) | |||
1553 | rcu_read_unlock(); | 1553 | rcu_read_unlock(); |
1554 | } | 1554 | } |
1555 | 1555 | ||
1556 | /* | ||
1557 | * Routine to help set real_num_tx_queues. To avoid skbs mapped to queues | ||
1558 | * greater then real_num_tx_queues stale skbs on the qdisc must be flushed. | ||
1559 | */ | ||
1560 | void netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq) | ||
1561 | { | ||
1562 | unsigned int real_num = dev->real_num_tx_queues; | ||
1563 | |||
1564 | if (unlikely(txq > dev->num_tx_queues)) | ||
1565 | ; | ||
1566 | else if (txq > real_num) | ||
1567 | dev->real_num_tx_queues = txq; | ||
1568 | else if (txq < real_num) { | ||
1569 | dev->real_num_tx_queues = txq; | ||
1570 | qdisc_reset_all_tx_gt(dev, txq); | ||
1571 | } | ||
1572 | } | ||
1573 | EXPORT_SYMBOL(netif_set_real_num_tx_queues); | ||
1556 | 1574 | ||
1557 | static inline void __netif_reschedule(struct Qdisc *q) | 1575 | static inline void __netif_reschedule(struct Qdisc *q) |
1558 | { | 1576 | { |