diff options
author | Alexander Duyck <alexander.h.duyck@intel.com> | 2012-02-07 02:29:06 +0000 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2012-03-12 20:23:57 -0700 |
commit | b37c0fbe3f6dfba1f8ad2aed47fb40578a254635 (patch) | |
tree | 8059fe5b80795afc4931492fe4fa1991c4805561 /include/linux/netdevice.h | |
parent | 5c4903549c05bbb373479e0ce2992573c120654a (diff) |
net: Add memory barriers to prevent possible race in byte queue limits
This change adds a memory barrier to the byte queue limit code to address a
possible race as has been seen in the past with the
netif_stop_queue/netif_wake_queue logic.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Tested-by: Stephen Ko <stephen.s.ko@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'include/linux/netdevice.h')
-rw-r--r-- | include/linux/netdevice.h | 49 |
1 files changed, 33 insertions, 16 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4bf314fe214..4535a4ea976 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1899,12 +1899,22 @@ static inline void netdev_tx_sent_queue(struct netdev_queue *dev_queue, { #ifdef CONFIG_BQL dql_queued(&dev_queue->dql, bytes); - if (unlikely(dql_avail(&dev_queue->dql) < 0)) { - set_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state); - if (unlikely(dql_avail(&dev_queue->dql) >= 0)) - clear_bit(__QUEUE_STATE_STACK_XOFF, - &dev_queue->state); - } + + if (likely(dql_avail(&dev_queue->dql) >= 0)) + return; + + set_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state); + + /* + * The XOFF flag must be set before checking the dql_avail below, + * because in netdev_tx_completed_queue we update the dql_completed + * before checking the XOFF flag. + */ + smp_mb(); + + /* check again in case another CPU has just made room avail */ + if (unlikely(dql_avail(&dev_queue->dql) >= 0)) + clear_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state); #endif } @@ -1917,16 +1927,23 @@ static inline void netdev_tx_completed_queue(struct netdev_queue *dev_queue, unsigned pkts, unsigned bytes) { #ifdef CONFIG_BQL - if (likely(bytes)) { - dql_completed(&dev_queue->dql, bytes); - if (unlikely(test_bit(__QUEUE_STATE_STACK_XOFF, - &dev_queue->state) && - dql_avail(&dev_queue->dql) >= 0)) { - if (test_and_clear_bit(__QUEUE_STATE_STACK_XOFF, - &dev_queue->state)) - netif_schedule_queue(dev_queue); - } - } + if (unlikely(!bytes)) + return; + + dql_completed(&dev_queue->dql, bytes); + + /* + * Without the memory barrier there is a small possiblity that + * netdev_tx_sent_queue will miss the update and cause the queue to + * be stopped forever + */ + smp_mb(); + + if (dql_avail(&dev_queue->dql) < 0) + return; + + if (test_and_clear_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state)) + netif_schedule_queue(dev_queue); #endif } |