diff options
author | Padmanabh Ratnakar <padmanabh.ratnakar@emulex.com> | 2011-05-10 01:13:57 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-05-11 19:10:03 -0400 |
commit | 4d586b823acc46c55c889ae1798de236c9d403da (patch) | |
tree | 32efa3d399b50b30c0850cc58c00369ee8c30a0f | |
parent | ecd0bf0f7b280bac3ac7419ed3aac84cd92878e9 (diff) |
be2net: Fix to prevent flooding of TX queue
Start/stop TX queue is controlled by TX queue "used" counter.
It is incremented while WRBs are posted to TX queue and
decremented when TX completions are received. This counter was
getting decremented before HW is informed about processing of TX
completions. As used counter is decremented, transmit function
posts new WRBs and creates completion queue full scenario in HW.
Signed-off-by: Padmanabh Ratnakar <padmanabh.ratnakar@emulex.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/benet/be_main.c | 20 |
1 files changed, 12 insertions, 8 deletions
diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c index babe53af7e86..243172bedfa6 100644 --- a/drivers/net/benet/be_main.c +++ b/drivers/net/benet/be_main.c | |||
@@ -1275,7 +1275,7 @@ static struct be_eth_tx_compl *be_tx_compl_get(struct be_queue_info *tx_cq) | |||
1275 | return txcp; | 1275 | return txcp; |
1276 | } | 1276 | } |
1277 | 1277 | ||
1278 | static void be_tx_compl_process(struct be_adapter *adapter, u16 last_index) | 1278 | static u16 be_tx_compl_process(struct be_adapter *adapter, u16 last_index) |
1279 | { | 1279 | { |
1280 | struct be_queue_info *txq = &adapter->tx_obj.q; | 1280 | struct be_queue_info *txq = &adapter->tx_obj.q; |
1281 | struct be_eth_wrb *wrb; | 1281 | struct be_eth_wrb *wrb; |
@@ -1302,9 +1302,8 @@ static void be_tx_compl_process(struct be_adapter *adapter, u16 last_index) | |||
1302 | queue_tail_inc(txq); | 1302 | queue_tail_inc(txq); |
1303 | } while (cur_index != last_index); | 1303 | } while (cur_index != last_index); |
1304 | 1304 | ||
1305 | atomic_sub(num_wrbs, &txq->used); | ||
1306 | |||
1307 | kfree_skb(sent_skb); | 1305 | kfree_skb(sent_skb); |
1306 | return num_wrbs; | ||
1308 | } | 1307 | } |
1309 | 1308 | ||
1310 | static inline struct be_eq_entry *event_get(struct be_eq_obj *eq_obj) | 1309 | static inline struct be_eq_entry *event_get(struct be_eq_obj *eq_obj) |
@@ -1387,7 +1386,7 @@ static void be_tx_compl_clean(struct be_adapter *adapter) | |||
1387 | struct be_queue_info *tx_cq = &adapter->tx_obj.cq; | 1386 | struct be_queue_info *tx_cq = &adapter->tx_obj.cq; |
1388 | struct be_queue_info *txq = &adapter->tx_obj.q; | 1387 | struct be_queue_info *txq = &adapter->tx_obj.q; |
1389 | struct be_eth_tx_compl *txcp; | 1388 | struct be_eth_tx_compl *txcp; |
1390 | u16 end_idx, cmpl = 0, timeo = 0; | 1389 | u16 end_idx, cmpl = 0, timeo = 0, num_wrbs = 0; |
1391 | struct sk_buff **sent_skbs = adapter->tx_obj.sent_skb_list; | 1390 | struct sk_buff **sent_skbs = adapter->tx_obj.sent_skb_list; |
1392 | struct sk_buff *sent_skb; | 1391 | struct sk_buff *sent_skb; |
1393 | bool dummy_wrb; | 1392 | bool dummy_wrb; |
@@ -1397,12 +1396,14 @@ static void be_tx_compl_clean(struct be_adapter *adapter) | |||
1397 | while ((txcp = be_tx_compl_get(tx_cq))) { | 1396 | while ((txcp = be_tx_compl_get(tx_cq))) { |
1398 | end_idx = AMAP_GET_BITS(struct amap_eth_tx_compl, | 1397 | end_idx = AMAP_GET_BITS(struct amap_eth_tx_compl, |
1399 | wrb_index, txcp); | 1398 | wrb_index, txcp); |
1400 | be_tx_compl_process(adapter, end_idx); | 1399 | num_wrbs += be_tx_compl_process(adapter, end_idx); |
1401 | cmpl++; | 1400 | cmpl++; |
1402 | } | 1401 | } |
1403 | if (cmpl) { | 1402 | if (cmpl) { |
1404 | be_cq_notify(adapter, tx_cq->id, false, cmpl); | 1403 | be_cq_notify(adapter, tx_cq->id, false, cmpl); |
1404 | atomic_sub(num_wrbs, &txq->used); | ||
1405 | cmpl = 0; | 1405 | cmpl = 0; |
1406 | num_wrbs = 0; | ||
1406 | } | 1407 | } |
1407 | 1408 | ||
1408 | if (atomic_read(&txq->used) == 0 || ++timeo > 200) | 1409 | if (atomic_read(&txq->used) == 0 || ++timeo > 200) |
@@ -1422,7 +1423,8 @@ static void be_tx_compl_clean(struct be_adapter *adapter) | |||
1422 | index_adv(&end_idx, | 1423 | index_adv(&end_idx, |
1423 | wrb_cnt_for_skb(adapter, sent_skb, &dummy_wrb) - 1, | 1424 | wrb_cnt_for_skb(adapter, sent_skb, &dummy_wrb) - 1, |
1424 | txq->len); | 1425 | txq->len); |
1425 | be_tx_compl_process(adapter, end_idx); | 1426 | num_wrbs = be_tx_compl_process(adapter, end_idx); |
1427 | atomic_sub(num_wrbs, &txq->used); | ||
1426 | } | 1428 | } |
1427 | } | 1429 | } |
1428 | 1430 | ||
@@ -1796,12 +1798,12 @@ static int be_poll_tx_mcc(struct napi_struct *napi, int budget) | |||
1796 | struct be_queue_info *tx_cq = &adapter->tx_obj.cq; | 1798 | struct be_queue_info *tx_cq = &adapter->tx_obj.cq; |
1797 | struct be_eth_tx_compl *txcp; | 1799 | struct be_eth_tx_compl *txcp; |
1798 | int tx_compl = 0, mcc_compl, status = 0; | 1800 | int tx_compl = 0, mcc_compl, status = 0; |
1799 | u16 end_idx; | 1801 | u16 end_idx, num_wrbs = 0; |
1800 | 1802 | ||
1801 | while ((txcp = be_tx_compl_get(tx_cq))) { | 1803 | while ((txcp = be_tx_compl_get(tx_cq))) { |
1802 | end_idx = AMAP_GET_BITS(struct amap_eth_tx_compl, | 1804 | end_idx = AMAP_GET_BITS(struct amap_eth_tx_compl, |
1803 | wrb_index, txcp); | 1805 | wrb_index, txcp); |
1804 | be_tx_compl_process(adapter, end_idx); | 1806 | num_wrbs += be_tx_compl_process(adapter, end_idx); |
1805 | tx_compl++; | 1807 | tx_compl++; |
1806 | } | 1808 | } |
1807 | 1809 | ||
@@ -1817,6 +1819,8 @@ static int be_poll_tx_mcc(struct napi_struct *napi, int budget) | |||
1817 | if (tx_compl) { | 1819 | if (tx_compl) { |
1818 | be_cq_notify(adapter, adapter->tx_obj.cq.id, true, tx_compl); | 1820 | be_cq_notify(adapter, adapter->tx_obj.cq.id, true, tx_compl); |
1819 | 1821 | ||
1822 | atomic_sub(num_wrbs, &txq->used); | ||
1823 | |||
1820 | /* As Tx wrbs have been freed up, wake up netdev queue if | 1824 | /* As Tx wrbs have been freed up, wake up netdev queue if |
1821 | * it was stopped due to lack of tx wrbs. | 1825 | * it was stopped due to lack of tx wrbs. |
1822 | */ | 1826 | */ |