diff options
author | Vladislav Zolotarov <vladz@broadcom.com> | 2010-02-27 19:12:02 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-28 21:37:12 -0500 |
commit | c16cc0b464b8876cfd57ce1c1dbcb6f9a6a0bce3 (patch) | |
tree | 8f70b0b1a48a5e9e2ad44d7d446936a75a936215 | |
parent | 76dadd76c265a0cdb5a76aa4eef03fcc9639b388 (diff) |
bnx2x: Tx barriers and locks
[Resending with the proper subject. Sorry for the mess. ]
This patch is based on the RFC of Stanislaw Gruszka.
More specifically it fixes two possible races:
- One, described by Stanislaw, may lead to permanent disabling of the Tx
queue.
This is fixed by adding the smp_wmb() to propagate the BD consumer
change towards the memory.
- Second may lead to bnx2x_start_xmit() returning NETDEV_TX_BUSY.
This is fixed by taking a tx_lock() before rechecking the number of
available Tx BDs.
thanks,
vlad
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
Signed-off-by: Vladislav Zolotarov <vladz@broadcom.com>
Signed-off-by: Eilon Greenstein <eilong@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/bnx2x_main.c | 31 |
1 files changed, 22 insertions, 9 deletions
diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 5adf2a05246f..ed785a30e98b 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c | |||
@@ -57,8 +57,8 @@ | |||
57 | #include "bnx2x_init_ops.h" | 57 | #include "bnx2x_init_ops.h" |
58 | #include "bnx2x_dump.h" | 58 | #include "bnx2x_dump.h" |
59 | 59 | ||
60 | #define DRV_MODULE_VERSION "1.52.1-6" | 60 | #define DRV_MODULE_VERSION "1.52.1-7" |
61 | #define DRV_MODULE_RELDATE "2010/02/16" | 61 | #define DRV_MODULE_RELDATE "2010/02/28" |
62 | #define BNX2X_BC_VER 0x040200 | 62 | #define BNX2X_BC_VER 0x040200 |
63 | 63 | ||
64 | #include <linux/firmware.h> | 64 | #include <linux/firmware.h> |
@@ -957,21 +957,34 @@ static int bnx2x_tx_int(struct bnx2x_fastpath *fp) | |||
957 | fp->tx_pkt_cons = sw_cons; | 957 | fp->tx_pkt_cons = sw_cons; |
958 | fp->tx_bd_cons = bd_cons; | 958 | fp->tx_bd_cons = bd_cons; |
959 | 959 | ||
960 | /* Need to make the tx_bd_cons update visible to start_xmit() | ||
961 | * before checking for netif_tx_queue_stopped(). Without the | ||
962 | * memory barrier, there is a small possibility that | ||
963 | * start_xmit() will miss it and cause the queue to be stopped | ||
964 | * forever. | ||
965 | */ | ||
966 | smp_wmb(); | ||
967 | |||
960 | /* TBD need a thresh? */ | 968 | /* TBD need a thresh? */ |
961 | if (unlikely(netif_tx_queue_stopped(txq))) { | 969 | if (unlikely(netif_tx_queue_stopped(txq))) { |
962 | 970 | /* Taking tx_lock() is needed to prevent reenabling the queue | |
963 | /* Need to make the tx_bd_cons update visible to start_xmit() | 971 | * while it's empty. This could have happen if rx_action() gets |
964 | * before checking for netif_tx_queue_stopped(). Without the | 972 | * suspended in bnx2x_tx_int() after the condition before |
965 | * memory barrier, there is a small possibility that | 973 | * netif_tx_wake_queue(), while tx_action (bnx2x_start_xmit()): |
966 | * start_xmit() will miss it and cause the queue to be stopped | 974 | * |
967 | * forever. | 975 | * stops the queue->sees fresh tx_bd_cons->releases the queue-> |
976 | * sends some packets consuming the whole queue again-> | ||
977 | * stops the queue | ||
968 | */ | 978 | */ |
969 | smp_mb(); | 979 | |
980 | __netif_tx_lock(txq, smp_processor_id()); | ||
970 | 981 | ||
971 | if ((netif_tx_queue_stopped(txq)) && | 982 | if ((netif_tx_queue_stopped(txq)) && |
972 | (bp->state == BNX2X_STATE_OPEN) && | 983 | (bp->state == BNX2X_STATE_OPEN) && |
973 | (bnx2x_tx_avail(fp) >= MAX_SKB_FRAGS + 3)) | 984 | (bnx2x_tx_avail(fp) >= MAX_SKB_FRAGS + 3)) |
974 | netif_tx_wake_queue(txq); | 985 | netif_tx_wake_queue(txq); |
986 | |||
987 | __netif_tx_unlock(txq); | ||
975 | } | 988 | } |
976 | return 0; | 989 | return 0; |
977 | } | 990 | } |