From mboxrd@z Thu Jan 1 00:00:00 1970 From: Matan Azrad Subject: Re: [PATCH v3 5/7] net/mlx4: separate Tx segment cases Date: Mon, 30 Oct 2017 18:23:31 +0000 Message-ID: References: <1508768520-4810-1-git-send-email-ophirmu@mellanox.com> <1509358049-18854-1-git-send-email-matan@mellanox.com> <1509358049-18854-6-git-send-email-matan@mellanox.com> <20171030142344.GB26782@6wind.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Cc: "dev@dpdk.org" , Ophir Munk To: Adrien Mazarguil Return-path: Received: from EUR01-DB5-obe.outbound.protection.outlook.com (mail-db5eur01on0059.outbound.protection.outlook.com [104.47.2.59]) by dpdk.org (Postfix) with ESMTP id DE9771B29C for ; Mon, 30 Oct 2017 19:23:34 +0100 (CET) In-Reply-To: <20171030142344.GB26782@6wind.com> Content-Language: en-US List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Hi Adrien > -----Original Message----- > From: Adrien Mazarguil [mailto:adrien.mazarguil@6wind.com] > Sent: Monday, October 30, 2017 4:24 PM > To: Matan Azrad > Cc: dev@dpdk.org; Ophir Munk > Subject: Re: [PATCH v3 5/7] net/mlx4: separate Tx segment cases >=20 > On Mon, Oct 30, 2017 at 10:07:27AM +0000, Matan Azrad wrote: > > Since single segment packets shouldn't use additional memory to save > > segments byte count we can prevent additional memory unnecessary > usage > > in this case; Prevent loop management. > > >=20 > Sorry for asking but I really don't understand the above, can you reformu= late > the problem addressed by this patch? >=20 What's about next? Optimize single segment case by processing it in different code which preve= nts checks and calculations relevant only to multi segment case.=20 > > Call a dedicated function for handling multi segments case. >=20 > This sentence is clearer, I'll base my review on what this patch does, no= t the > reasons behind it. >=20 > > Signed-off-by: Matan Azrad > > Signed-off-by: Ophir Munk > > --- > > drivers/net/mlx4/mlx4_rxtx.c | 247 > > +++++++++++++++++++++++++++---------------- > > 1 file changed, 158 insertions(+), 89 deletions(-) > > > > diff --git a/drivers/net/mlx4/mlx4_rxtx.c > > b/drivers/net/mlx4/mlx4_rxtx.c index 8ce70d6..8ea8851 100644 > > --- a/drivers/net/mlx4/mlx4_rxtx.c > > +++ b/drivers/net/mlx4/mlx4_rxtx.c > > @@ -62,6 +62,9 @@ > > #include "mlx4_rxtx.h" > > #include "mlx4_utils.h" > > > > +#define WQE_ONE_DATA_SEG_SIZE \ > > + (sizeof(struct mlx4_wqe_ctrl_seg) + sizeof(struct > > +mlx4_wqe_data_seg)) > > + > > /** > > * Pointer-value pair structure used in tx_post_send for saving the fi= rst > > * DWORD (32 byte) of a TXBB. > > @@ -140,22 +143,19 @@ struct pv { > > * @return > > * 0 on success, -1 on failure. > > */ > > -static int > > -mlx4_txq_complete(struct txq *txq) > > +static inline int >=20 > While likely harmless, I think the addition of this inline keyword is not= related > to this patch. >=20 Yes, you right, will be fixed in next version. > > +mlx4_txq_complete(struct txq *txq, const unsigned int elts_n, > > + struct mlx4_sq *sq) >=20 > Looks like an indentation issue, you should align it to the contents of t= he > opening "(" to match the coding style of this file. >=20 OK. > > { > > unsigned int elts_comp =3D txq->elts_comp; > > unsigned int elts_tail =3D txq->elts_tail; > > - const unsigned int elts_n =3D txq->elts_n; > > struct mlx4_cq *cq =3D &txq->mcq; > > - struct mlx4_sq *sq =3D &txq->msq; > > struct mlx4_cqe *cqe; > > uint32_t cons_index =3D cq->cons_index; > > uint16_t new_index; > > uint16_t nr_txbbs =3D 0; > > int pkts =3D 0; > > > > - if (unlikely(elts_comp =3D=3D 0)) > > - return 0; > > /* > > * Traverse over all CQ entries reported and handle each WQ entry > > * reported by them. > > @@ -238,6 +238,122 @@ struct pv { > > return buf->pool; > > } > > > > +static int handle_multi_segs(struct rte_mbuf *buf, > > + struct txq *txq, > > + struct mlx4_wqe_ctrl_seg **pctrl) >=20 > How about naming this function in a way that follows the mlx4_something() > convention? >=20 > Here's a suggestion based on how this function remains tied to > mlx4_tx_burst(): >=20 > mlx4_tx_burst_seg() >=20 Good, thanks! > > +{ > > + int wqe_real_size; > > + int nr_txbbs; > > + struct pv *pv =3D (struct pv *)txq->bounce_buf; > > + struct mlx4_sq *sq =3D &txq->msq; > > + uint32_t head_idx =3D sq->head & sq->txbb_cnt_mask; > > + struct mlx4_wqe_ctrl_seg *ctrl; > > + struct mlx4_wqe_data_seg *dseg; > > + uint32_t lkey; > > + uintptr_t addr; > > + uint32_t byte_count; > > + int pv_counter =3D 0; > > + > > + /* Calculate the needed work queue entry size for this packet. */ > > + wqe_real_size =3D sizeof(struct mlx4_wqe_ctrl_seg) + > > + buf->nb_segs * sizeof(struct mlx4_wqe_data_seg); > > + nr_txbbs =3D MLX4_SIZE_TO_TXBBS(wqe_real_size); > > + /* > > + * Check that there is room for this WQE in the send queue and that > > + * the WQE size is legal. > > + */ > > + if (((sq->head - sq->tail) + nr_txbbs + > > + sq->headroom_txbbs) >=3D sq->txbb_cnt || > > + nr_txbbs > MLX4_MAX_WQE_TXBBS) { > > + return -1; > > + } > > + >=20 > Extra empty line. >=20 > > + /* Get the control and data entries of the WQE. */ > > + ctrl =3D (struct mlx4_wqe_ctrl_seg *)mlx4_get_send_wqe(sq, > head_idx); > > + dseg =3D (struct mlx4_wqe_data_seg *)((uintptr_t)ctrl + > > + sizeof(struct mlx4_wqe_ctrl_seg)); > > + *pctrl =3D ctrl; > > + /* Fill the data segments with buffer information. */ > > + struct rte_mbuf *sbuf; >=20 > I'm usually fine with mixing declarations and code when there's a good > reason, however in this case there's no point. sbuf could have been defin= ed > with the rest at the beginning of the function. > OK. =20 > > + >=20 > Extra empty line here as well. >=20 > > + for (sbuf =3D buf; sbuf !=3D NULL; sbuf =3D sbuf->next, dseg++) { > > + addr =3D rte_pktmbuf_mtod(sbuf, uintptr_t); > > + rte_prefetch0((volatile void *)addr); > > + /* Handle WQE wraparound. */ > > + if (dseg >=3D (struct mlx4_wqe_data_seg *)sq->eob) > > + dseg =3D (struct mlx4_wqe_data_seg *)sq->buf; > > + dseg->addr =3D rte_cpu_to_be_64(addr); > > + /* Memory region key (big endian) for this memory pool. */ > > + lkey =3D mlx4_txq_mp2mr(txq, mlx4_txq_mb2mp(sbuf)); > > + dseg->lkey =3D rte_cpu_to_be_32(lkey); #ifndef NDEBUG > > + /* Calculate the needed work queue entry size for this > packet */ > > + if (unlikely(dseg->lkey =3D=3D rte_cpu_to_be_32((uint32_t)-1))) { > > + /* MR does not exist. */ > > + DEBUG("%p: unable to get MP <-> MR association", > > + (void *)txq); > > + /* > > + * Restamp entry in case of failure. > > + * Make sure that size is written correctly > > + * Note that we give ownership to the SW, not the > HW. > > + */ > > + wqe_real_size =3D sizeof(struct mlx4_wqe_ctrl_seg) + > > + buf->nb_segs * sizeof(struct > mlx4_wqe_data_seg); > > + ctrl->fence_size =3D (wqe_real_size >> 4) & 0x3f; > > + mlx4_txq_stamp_freed_wqe(sq, head_idx, > > + (sq->head & sq->txbb_cnt) ? 0 : 1); > > + return -1; > > + } > > +#endif /* NDEBUG */ > > + if (likely(sbuf->data_len)) { > > + byte_count =3D rte_cpu_to_be_32(sbuf->data_len); > > + } else { > > + /* > > + * Zero length segment is treated as inline segment > > + * with zero data. > > + */ > > + byte_count =3D RTE_BE32(0x80000000); > > + } > > + /* > > + * If the data segment is not at the beginning of a > > + * Tx basic block (TXBB) then write the byte count, > > + * else postpone the writing to just before updating the > > + * control segment. > > + */ > > + if ((uintptr_t)dseg & (uintptr_t)(MLX4_TXBB_SIZE - 1)) { > > + /* > > + * Need a barrier here before writing the byte_count > > + * fields to make sure that all the data is visible > > + * before the byte_count field is set. > > + * Otherwise, if the segment begins a new cacheline, > > + * the HCA prefetcher could grab the 64-byte chunk > and > > + * get a valid (!=3D 0xffffffff) byte count but stale > > + * data, and end up sending the wrong data. > > + */ > > + rte_io_wmb(); > > + dseg->byte_count =3D byte_count; > > + } else { > > + /* > > + * This data segment starts at the beginning of a new > > + * TXBB, so we need to postpone its byte_count > writing > > + * for later. > > + */ > > + pv[pv_counter].dseg =3D dseg; > > + pv[pv_counter++].val =3D byte_count; > > + } > > + } > > + /* Write the first DWORD of each TXBB save earlier. */ > > + if (pv_counter) { > > + /* Need a barrier here before writing the byte_count. */ > > + rte_io_wmb(); > > + for (--pv_counter; pv_counter >=3D 0; pv_counter--) > > + pv[pv_counter].dseg->byte_count =3D > pv[pv_counter].val; > > + } > > + /* Fill the control parameters for this packet. */ > > + ctrl->fence_size =3D (wqe_real_size >> 4) & 0x3f; > > + >=20 > Extra empty line. >=20 > > + return nr_txbbs; > > +} > > /** > > * DPDK callback for Tx. > > * > > @@ -261,10 +377,11 @@ struct pv { > > unsigned int i; > > unsigned int max; > > struct mlx4_sq *sq =3D &txq->msq; > > - struct pv *pv =3D (struct pv *)txq->bounce_buf; > > + int nr_txbbs; > > > > assert(txq->elts_comp_cd !=3D 0); > > - mlx4_txq_complete(txq); > > + if (likely(txq->elts_comp !=3D 0)) > > + mlx4_txq_complete(txq, elts_n, sq); > > max =3D (elts_n - (elts_head - txq->elts_tail)); > > if (max > elts_n) > > max -=3D elts_n; > > @@ -283,7 +400,6 @@ struct pv { > > uint32_t owner_opcode =3D MLX4_OPCODE_SEND; > > struct mlx4_wqe_ctrl_seg *ctrl; > > struct mlx4_wqe_data_seg *dseg; > > - struct rte_mbuf *sbuf; > > union { > > uint32_t flags; > > uint16_t flags16[2]; > > @@ -291,10 +407,6 @@ struct pv { > > uint32_t head_idx =3D sq->head & sq->txbb_cnt_mask; > > uint32_t lkey; > > uintptr_t addr; > > - uint32_t byte_count; > > - int wqe_real_size; > > - int nr_txbbs; > > - int pv_counter =3D 0; > > > > /* Clean up old buffer. */ > > if (likely(elt->buf !=3D NULL)) { > > @@ -313,40 +425,29 @@ struct pv { > > } while (tmp !=3D NULL); > > } > > RTE_MBUF_PREFETCH_TO_FREE(elt_next->buf); > > - > > - /* > > - * Calculate the needed work queue entry size > > - * for this packet. > > - */ > > - wqe_real_size =3D sizeof(struct mlx4_wqe_ctrl_seg) + > > - buf->nb_segs * sizeof(struct > mlx4_wqe_data_seg); > > - nr_txbbs =3D MLX4_SIZE_TO_TXBBS(wqe_real_size); > > - /* > > - * Check that there is room for this WQE in the send > > - * queue and that the WQE size is legal. > > - */ > > - if (((sq->head - sq->tail) + nr_txbbs + > > - sq->headroom_txbbs) >=3D sq->txbb_cnt || > > - nr_txbbs > MLX4_MAX_WQE_TXBBS) { > > - elt->buf =3D NULL; > > - break; > > - } > > - /* Get the control and data entries of the WQE. */ > > - ctrl =3D (struct mlx4_wqe_ctrl_seg *) > > - mlx4_get_send_wqe(sq, head_idx); > > - dseg =3D (struct mlx4_wqe_data_seg *)((uintptr_t)ctrl + > > - sizeof(struct mlx4_wqe_ctrl_seg)); > > - /* Fill the data segments with buffer information. */ > > - for (sbuf =3D buf; sbuf !=3D NULL; sbuf =3D sbuf->next, dseg++) { > > - addr =3D rte_pktmbuf_mtod(sbuf, uintptr_t); > > + if (buf->nb_segs =3D=3D 1) { > > + /* > > + * Check that there is room for this WQE in the send > > + * queue and that the WQE size is legal > > + */ > > + if (((sq->head - sq->tail) + 1 + sq->headroom_txbbs) > >=3D > > + sq->txbb_cnt || 1 > MLX4_MAX_WQE_TXBBS) { > > + elt->buf =3D NULL; > > + break; > > + } > > + /* Get the control and data entries of the WQE. */ > > + ctrl =3D (struct mlx4_wqe_ctrl_seg *) > > + mlx4_get_send_wqe(sq, head_idx); > > + dseg =3D (struct mlx4_wqe_data_seg *)((uintptr_t)ctrl > + > > + sizeof(struct mlx4_wqe_ctrl_seg)); > > + addr =3D rte_pktmbuf_mtod(buf, uintptr_t); > > rte_prefetch0((volatile void *)addr); > > /* Handle WQE wraparound. */ > > - if (unlikely(dseg >=3D > > - (struct mlx4_wqe_data_seg *)sq->eob)) > > + if (dseg >=3D (struct mlx4_wqe_data_seg *)sq->eob) >=20 > Ideally this change should have been on its own in a fix commit. >=20 > > dseg =3D (struct mlx4_wqe_data_seg *)sq- > >buf; > > dseg->addr =3D rte_cpu_to_be_64(addr); > > /* Memory region key (big endian). */ > > - lkey =3D mlx4_txq_mp2mr(txq, > mlx4_txq_mb2mp(sbuf)); > > + lkey =3D mlx4_txq_mp2mr(txq, > mlx4_txq_mb2mp(buf)); > > dseg->lkey =3D rte_cpu_to_be_32(lkey); #ifndef > NDEBUG > > if (unlikely(dseg->lkey =3D=3D > > @@ -360,61 +461,28 @@ struct pv { > > * Note that we give ownership to the SW, > > * not the HW. > > */ > > - ctrl->fence_size =3D (wqe_real_size >> 4) & > 0x3f; > > + ctrl->fence_size =3D > > + (WQE_ONE_DATA_SEG_SIZE >> 4) & > 0x3f; > > mlx4_txq_stamp_freed_wqe(sq, head_idx, > > (sq->head & sq->txbb_cnt) ? 0 : 1); > > elt->buf =3D NULL; > > break; > > } > > #endif /* NDEBUG */ > > - if (likely(sbuf->data_len)) { > > - byte_count =3D rte_cpu_to_be_32(sbuf- > >data_len); > > - } else { > > - /* > > - * Zero length segment is treated as inline > > - * segment with zero data. > > - */ > > - byte_count =3D RTE_BE32(0x80000000); > > - } > > - /* > > - * If the data segment is not at the beginning > > - * of a Tx basic block (TXBB) then write the > > - * byte count, else postpone the writing to > > - * just before updating the control segment. > > - */ > > - if ((uintptr_t)dseg & (uintptr_t)(MLX4_TXBB_SIZE - > 1)) { > > - /* > > - * Need a barrier here before writing the > > - * byte_count fields to make sure that all the > > - * data is visible before the byte_count field > > - * is set. otherwise, if the segment begins a > > - * new cacheline, the HCA prefetcher could > grab > > - * the 64-byte chunk and get a valid > > - * (!=3D 0xffffffff) byte count but stale data, > > - * and end up sending the wrong data. > > - */ > > - rte_io_wmb(); > > - dseg->byte_count =3D byte_count; > > - } else { > > - /* > > - * This data segment starts at the beginning > of > > - * a new TXBB, so we need to postpone its > > - * byte_count writing for later. > > - */ > > - pv[pv_counter].dseg =3D dseg; > > - pv[pv_counter++].val =3D byte_count; > > - } > > - } > > - /* Write the first DWORD of each TXBB save earlier. */ > > - if (pv_counter) { > > - /* Need a barrier before writing the byte_count. */ > > + /* Need a barrier here before byte count store. */ > > rte_io_wmb(); > > - for (--pv_counter; pv_counter >=3D 0; pv_counter--) > > - pv[pv_counter].dseg->byte_count =3D > > - pv[pv_counter].val; > > + dseg->byte_count =3D rte_cpu_to_be_32(buf- > >data_len); > > + >=20 > Extra empty line. >=20 > > + /* Fill the control parameters for this packet. */ > > + ctrl->fence_size =3D (WQE_ONE_DATA_SEG_SIZE >> 4) > & 0x3f; > > + nr_txbbs =3D 1; > > + } else { > > + nr_txbbs =3D handle_multi_segs(buf, txq, &ctrl); > > + if (nr_txbbs < 0) { > > + elt->buf =3D NULL; > > + break; > > + } > > } > > - /* Fill the control parameters for this packet. */ > > - ctrl->fence_size =3D (wqe_real_size >> 4) & 0x3f; > > /* > > * For raw Ethernet, the SOLICIT flag is used to indicate > > * that no ICRC should be calculated. > > @@ -469,6 +537,7 @@ struct pv { > > ctrl->owner_opcode =3D rte_cpu_to_be_32(owner_opcode | > > ((sq->head & sq->txbb_cnt) ? > > MLX4_BIT_WQE_OWN : > 0)); > > + >=20 > Extra empty line. >=20 > > sq->head +=3D nr_txbbs; > > elt->buf =3D buf; > > bytes_sent +=3D buf->pkt_len; > > -- > > 1.8.3.1 > > >=20 > -- > Adrien Mazarguil > 6WIND Thanks!