From: Dmitriy Vyukov <dvyu...@google.com>

e1000_clean_tx_irq cleans buffers and sets tx_ring->next_to_clean,
then e1000_xmit_frame reuses the cleaned buffers. But there are no
memory barriers when buffers gets recycled, so the recycled buffers
can be corrupted.

Use smp_store_release to update tx_ring->next_to_clean and
smp_load_acquire to read tx_ring->next_to_clean to properly
hand off buffers from e1000_clean_tx_irq to e1000_xmit_frame.

The data race was found with KernelThreadSanitizer (KTSAN).

Signed-off-by: Dmitry Vyukov <dvyu...@google.com>
Tested-by: Aaron Brown <aaron.f.br...@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirs...@intel.com>
---
 drivers/net/ethernet/intel/e1000/e1000.h      | 7 +++++--
 drivers/net/ethernet/intel/e1000/e1000_main.c | 5 ++++-
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/intel/e1000/e1000.h 
b/drivers/net/ethernet/intel/e1000/e1000.h
index 6970710..98fe5a2 100644
--- a/drivers/net/ethernet/intel/e1000/e1000.h
+++ b/drivers/net/ethernet/intel/e1000/e1000.h
@@ -213,8 +213,11 @@ struct e1000_rx_ring {
 };
 
 #define E1000_DESC_UNUSED(R)                                           \
-       ((((R)->next_to_clean > (R)->next_to_use)                       \
-         ? 0 : (R)->count) + (R)->next_to_clean - (R)->next_to_use - 1)
+({                                                                     \
+       unsigned int clean = smp_load_acquire(&(R)->next_to_clean);     \
+       unsigned int use = READ_ONCE((R)->next_to_use);                 \
+       (clean > use ? 0 : (R)->count) + clean - use - 1;               \
+})
 
 #define E1000_RX_DESC_EXT(R, i)                                                
\
        (&(((union e1000_rx_desc_extended *)((R).desc))[i]))
diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c 
b/drivers/net/ethernet/intel/e1000/e1000_main.c
index fd7be86..0680235 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_main.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_main.c
@@ -3876,7 +3876,10 @@ static bool e1000_clean_tx_irq(struct e1000_adapter 
*adapter,
                eop_desc = E1000_TX_DESC(*tx_ring, eop);
        }
 
-       tx_ring->next_to_clean = i;
+       /* Synchronize with E1000_DESC_UNUSED called from e1000_xmit_frame,
+        * which will reuse the cleaned buffers.
+        */
+       smp_store_release(&tx_ring->next_to_clean, i);
 
        netdev_completed_queue(netdev, pkts_compl, bytes_compl);
 
-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to