diff options
author | Zhao Chen <zhaochen6@huawei.com> | 2018-08-08 06:37:30 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-08-08 09:46:08 -0700 |
commit | 9c2956d2ad9e0e7d5827290ba9a716ed3fb83bcd (patch) | |
tree | 6a04f35460c3a9d96158cb37b3c810bb3d3ac4f4 /drivers/net/ethernet/huawei/hinic/hinic_tx.c | |
parent | 5a0c6cee1767a551dacfa6e266ac4795bba0555e (diff) |
net-next: hinic: fix a problem in free_tx_poll()
This patch fixes the problem below. The problem can be reproduced by the
following steps:
1) Connecting all HiNIC interfaces
2) On server side
# sudo ifconfig eth0 192.168.100.1 up #Using MLX CX4 card
# iperf -s
3) On client side
# sudo ifconfig eth0 192.168.100.2 up #Using our HiNIC card
# iperf -c 192.168.101.1 -P 10 -t 100000
after hours of testing, we will see errors:
hinic 0000:05:00.0: No MGMT msg handler, mod = 0
hinic 0000:05:00.0: No MGMT msg handler, mod = 0
hinic 0000:05:00.0: No MGMT msg handler, mod = 0
hinic 0000:05:00.0: No MGMT msg handler, mod = 0
The errors are caused by the following problem.
1) The hinic_get_wqe() checks the "wq->delta" to allocate new WQEs:
if (atomic_sub_return(num_wqebbs, &wq->delta) <= 0) {
atomic_add(num_wqebbs, &wq->delta);
return ERR_PTR(-EBUSY);
}
If the WQE occupies multiple pages, the shadow WQE will be used. Then the
hinic_xmit_frame() fills the WQE.
2) While in parallel with 1), the free_tx_poll() checks the "wq->delta"
to free old WQEs:
if ((atomic_read(&wq->delta) + num_wqebbs) > wq->q_depth)
return ERR_PTR(-EBUSY);
There is a probability that the shadow WQE which hinic_xmit_frame() is
using will be damaged by copy_wqe_to_shadow():
if (curr_pg != end_pg) {
void *shadow_addr = &wq->shadow_wqe[curr_pg * wq->max_wqe_size];
copy_wqe_to_shadow(wq, shadow_addr, num_wqebbs, *cons_idx);
return shadow_addr;
}
This can cause WQE data error and you will see the above error messages.
This patch fixes the problem.
Signed-off-by: Zhao Chen <zhaochen6@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/huawei/hinic/hinic_tx.c')
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_tx.c | 18 |
1 files changed, 16 insertions, 2 deletions
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c index 2353ec829c04..c5fca0356c9c 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.c @@ -283,7 +283,11 @@ static void free_all_tx_skbs(struct hinic_txq *txq) int nr_sges; u16 ci; - while ((sq_wqe = hinic_sq_read_wqe(sq, &skb, &wqe_size, &ci))) { + while ((sq_wqe = hinic_sq_read_wqebb(sq, &skb, &wqe_size, &ci))) { + sq_wqe = hinic_sq_read_wqe(sq, &skb, wqe_size, &ci); + if (!sq_wqe) + break; + nr_sges = skb_shinfo(skb)->nr_frags + 1; hinic_sq_get_sges(sq_wqe, txq->free_sges, nr_sges); @@ -319,11 +323,21 @@ static int free_tx_poll(struct napi_struct *napi, int budget) do { hw_ci = HW_CONS_IDX(sq) & wq->mask; - sq_wqe = hinic_sq_read_wqe(sq, &skb, &wqe_size, &sw_ci); + /* Reading a WQEBB to get real WQE size and consumer index. */ + sq_wqe = hinic_sq_read_wqebb(sq, &skb, &wqe_size, &sw_ci); if ((!sq_wqe) || (((hw_ci - sw_ci) & wq->mask) * wq->wqebb_size < wqe_size)) break; + /* If this WQE have multiple WQEBBs, we will read again to get + * full size WQE. + */ + if (wqe_size > wq->wqebb_size) { + sq_wqe = hinic_sq_read_wqe(sq, &skb, wqe_size, &sw_ci); + if (unlikely(!sq_wqe)) + break; + } + tx_bytes += skb->len; pkts++; |