forked from Minki/linux
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>
This commit is contained in:
parent
5a0c6cee17
commit
9c2956d2ad
@ -635,17 +635,18 @@ void hinic_sq_write_wqe(struct hinic_sq *sq, u16 prod_idx,
|
||||
}
|
||||
|
||||
/**
|
||||
* hinic_sq_read_wqe - read wqe ptr in the current ci and update the ci
|
||||
* hinic_sq_read_wqebb - read wqe ptr in the current ci and update the ci, the
|
||||
* wqe only have one wqebb
|
||||
* @sq: send queue
|
||||
* @skb: return skb that was saved
|
||||
* @wqe_size: the size of the wqe
|
||||
* @wqe_size: the wqe size ptr
|
||||
* @cons_idx: consumer index of the wqe
|
||||
*
|
||||
* Return wqe in ci position
|
||||
**/
|
||||
struct hinic_sq_wqe *hinic_sq_read_wqe(struct hinic_sq *sq,
|
||||
struct sk_buff **skb,
|
||||
unsigned int *wqe_size, u16 *cons_idx)
|
||||
struct hinic_sq_wqe *hinic_sq_read_wqebb(struct hinic_sq *sq,
|
||||
struct sk_buff **skb,
|
||||
unsigned int *wqe_size, u16 *cons_idx)
|
||||
{
|
||||
struct hinic_hw_wqe *hw_wqe;
|
||||
struct hinic_sq_wqe *sq_wqe;
|
||||
@ -658,6 +659,8 @@ struct hinic_sq_wqe *hinic_sq_read_wqe(struct hinic_sq *sq,
|
||||
if (IS_ERR(hw_wqe))
|
||||
return NULL;
|
||||
|
||||
*skb = sq->saved_skb[*cons_idx];
|
||||
|
||||
sq_wqe = &hw_wqe->sq_wqe;
|
||||
ctrl = &sq_wqe->ctrl;
|
||||
ctrl_info = be32_to_cpu(ctrl->ctrl_info);
|
||||
@ -665,12 +668,29 @@ struct hinic_sq_wqe *hinic_sq_read_wqe(struct hinic_sq *sq,
|
||||
|
||||
*wqe_size = sizeof(*ctrl) + sizeof(sq_wqe->task);
|
||||
*wqe_size += SECT_SIZE_FROM_8BYTES(buf_sect_len);
|
||||
*wqe_size = ALIGN(*wqe_size, sq->wq->wqebb_size);
|
||||
|
||||
return &hw_wqe->sq_wqe;
|
||||
}
|
||||
|
||||
/**
|
||||
* hinic_sq_read_wqe - read wqe ptr in the current ci and update the ci
|
||||
* @sq: send queue
|
||||
* @skb: return skb that was saved
|
||||
* @wqe_size: the size of the wqe
|
||||
* @cons_idx: consumer index of the wqe
|
||||
*
|
||||
* Return wqe in ci position
|
||||
**/
|
||||
struct hinic_sq_wqe *hinic_sq_read_wqe(struct hinic_sq *sq,
|
||||
struct sk_buff **skb,
|
||||
unsigned int wqe_size, u16 *cons_idx)
|
||||
{
|
||||
struct hinic_hw_wqe *hw_wqe;
|
||||
|
||||
hw_wqe = hinic_read_wqe(sq->wq, wqe_size, cons_idx);
|
||||
*skb = sq->saved_skb[*cons_idx];
|
||||
|
||||
/* using the real wqe size to read wqe again */
|
||||
hw_wqe = hinic_read_wqe(sq->wq, *wqe_size, cons_idx);
|
||||
|
||||
return &hw_wqe->sq_wqe;
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,11 @@ void hinic_sq_write_wqe(struct hinic_sq *sq, u16 prod_idx,
|
||||
|
||||
struct hinic_sq_wqe *hinic_sq_read_wqe(struct hinic_sq *sq,
|
||||
struct sk_buff **skb,
|
||||
unsigned int *wqe_size, u16 *cons_idx);
|
||||
unsigned int wqe_size, u16 *cons_idx);
|
||||
|
||||
struct hinic_sq_wqe *hinic_sq_read_wqebb(struct hinic_sq *sq,
|
||||
struct sk_buff **skb,
|
||||
unsigned int *wqe_size, u16 *cons_idx);
|
||||
|
||||
void hinic_sq_put_wqe(struct hinic_sq *sq, unsigned int wqe_size);
|
||||
|
||||
|
@ -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++;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user