lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID:
 <PAXPR04MB8510CFF87187B095D15DBD6088052@PAXPR04MB8510.eurprd04.prod.outlook.com>
Date: Wed, 18 Dec 2024 03:06:06 +0000
From: Wei Fang <wei.fang@....com>
To: Alexander Lobakin <aleksander.lobakin@...el.com>
CC: Claudiu Manoil <claudiu.manoil@....com>, Vladimir Oltean
	<vladimir.oltean@....com>, Clark Wang <xiaoning.wang@....com>,
	"andrew+netdev@...n.ch" <andrew+netdev@...n.ch>, "davem@...emloft.net"
	<davem@...emloft.net>, "edumazet@...gle.com" <edumazet@...gle.com>,
	"kuba@...nel.org" <kuba@...nel.org>, "pabeni@...hat.com" <pabeni@...hat.com>,
	Frank Li <frank.li@....com>, "horms@...nel.org" <horms@...nel.org>,
	"idosch@...sch.org" <idosch@...sch.org>, "netdev@...r.kernel.org"
	<netdev@...r.kernel.org>, "linux-kernel@...r.kernel.org"
	<linux-kernel@...r.kernel.org>, "imx@...ts.linux.dev" <imx@...ts.linux.dev>
Subject: RE: [PATCH v8 net-next 3/4] net: enetc: add LSO support for i.MX95
 ENETC PF

> > +static inline int enetc_lso_count_descs(const struct sk_buff *skb) {
> > +	/* 4 BDs: 1 BD for LSO header + 1 BD for extended BD + 1 BD
> > +	 * for linear area data but not include LSO header, namely
> > +	 * skb_headlen(skb) - lso_hdr_len. And 1 BD for gap.
> 
> What if the head contains headers only and
> `skb_headlen(skb) - lso_hdr_len` is 0?
> 

enetc_lso_count_descs() is a simple helper and only used to calculate
the number of BDs needed in the worst case, so that we can check
whether there are enough BDs to accommodate the current frame.
It has no significant impact on the case you mentioned.

> > +	 */
> > +	return skb_shinfo(skb)->nr_frags + 4; }
> > +
> > +static int enetc_lso_get_hdr_len(const struct sk_buff *skb) {
> > +	int hdr_len, tlen;
> > +
> > +	tlen = skb_is_gso_tcp(skb) ? tcp_hdrlen(skb) : sizeof(struct udphdr);
> > +	hdr_len = skb_transport_offset(skb) + tlen;
> > +
> > +	return hdr_len;
> > +}
> 
> Are you sure the kernel doesn't have similar generic helpers?

This function refers to tso_start() in tso.c. I have not found any other similar
helper functions in kernel.
> 
> > +
> > +static void enetc_lso_start(struct sk_buff *skb, struct enetc_lso_t
> > +*lso) {
> > +	lso->lso_seg_size = skb_shinfo(skb)->gso_size;
> > +	lso->ipv6 = enetc_skb_is_ipv6(skb);
> > +	lso->tcp = skb_is_gso_tcp(skb);
> > +	lso->l3_hdr_len = skb_network_header_len(skb);
> > +	lso->l3_start = skb_network_offset(skb);
> > +	lso->hdr_len = enetc_lso_get_hdr_len(skb);
> > +	lso->total_len = skb->len - lso->hdr_len; }
> > +
> > +static void enetc_lso_map_hdr(struct enetc_bdr *tx_ring, struct sk_buff
> *skb,
> > +			      int *i, struct enetc_lso_t *lso) {
> > +	union enetc_tx_bd txbd_tmp, *txbd;
> > +	struct enetc_tx_swbd *tx_swbd;
> > +	u16 frm_len, frm_len_ext;
> > +	u8 flags, e_flags = 0;
> > +	dma_addr_t addr;
> > +	char *hdr;
> > +
> > +	/* Get the first BD of the LSO BDs chain */
> > +	txbd = ENETC_TXBD(*tx_ring, *i);
> > +	tx_swbd = &tx_ring->tx_swbd[*i];
> > +	prefetchw(txbd);
> 
> Is this prefetchw() proven to give any benefit?
> 

Just to keep the logic consistent with the current code. Existing code always
uses prefetchw() before setting up txbd, and I see no reason for it to behave
differently in different places. I also don't have a quick answer to what the
benefits are. :(

> > +
> > +	/* Prepare LSO header: MAC + IP + TCP/UDP */
> > +	hdr = tx_ring->tso_headers + *i * TSO_HEADER_SIZE;
> > +	memcpy(hdr, skb->data, lso->hdr_len);
> > +	addr = tx_ring->tso_headers_dma + *i * TSO_HEADER_SIZE;
> > +
> > +	frm_len = lso->total_len & 0xffff;
> > +	frm_len_ext = (lso->total_len >> 16) & 0xf;
> 
> Why are these magics just open-coded, even without any comment?
> I have no idea what is going on here for example.
> 
> Also, `& 0xffff` is lower_16_bits(), while `lso->total_len >> 16` is upper_16_bits().

frm_len is the lower 16 bits of the frame length, frm_len_ext is the higher 4 bits
of the frame length, I will add some comments or macros.
> 
> > +
> > +	/* Set the flags of the first BD */
> > +	flags = ENETC_TXBD_FLAGS_EX | ENETC_TXBD_FLAGS_CSUM_LSO |
> > +		ENETC_TXBD_FLAGS_LSO | ENETC_TXBD_FLAGS_L4CS;
> > +
> > +	enetc_clear_tx_bd(&txbd_tmp);
> > +	txbd_tmp.addr = cpu_to_le64(addr);
> > +	txbd_tmp.hdr_len = cpu_to_le16(lso->hdr_len);
> > +
> > +	/* first BD needs frm_len and offload flags set */
> > +	txbd_tmp.frm_len = cpu_to_le16(frm_len);
> > +	txbd_tmp.flags = flags;
> > +
> > +	txbd_tmp.l3_aux0 = FIELD_PREP(ENETC_TX_BD_L3_START, lso->l3_start);
> > +	/* l3_hdr_size in 32-bits (4 bytes) */
> > +	txbd_tmp.l3_aux1 = FIELD_PREP(ENETC_TX_BD_L3_HDR_LEN,
> > +				      lso->l3_hdr_len / 4);
> > +	if (lso->ipv6)
> > +		txbd_tmp.l3_aux1 |= FIELD_PREP(ENETC_TX_BD_L3T, 1);
> > +	else
> > +		txbd_tmp.l3_aux0 |= FIELD_PREP(ENETC_TX_BD_IPCS, 1);
> 
> Both these "fields" are single bits. You don't need FIELD_PREP() for single-bit
> fields, just `|= ENETC_TX_BD_L3T` etc.

Okay, thanks.
> 
> > +
> > +	txbd_tmp.l4_aux = FIELD_PREP(ENETC_TX_BD_L4T, lso->tcp ?
> > +				     ENETC_TXBD_L4T_TCP : ENETC_TXBD_L4T_UDP);
> > +
> > +	/* For the LSO header we do not set the dma address since
> > +	 * we do not want it unmapped when we do cleanup. We still
> > +	 * set len so that we count the bytes sent.
> > +	 */
> > +	tx_swbd->len = lso->hdr_len;
> > +	tx_swbd->do_twostep_tstamp = false;
> > +	tx_swbd->check_wb = false;
> > +
> > +	/* Actually write the header in the BD */
> > +	*txbd = txbd_tmp;
> > +
> > +	/* Get the next BD, and the next BD is extended BD */
> > +	enetc_bdr_idx_inc(tx_ring, i);
> > +	txbd = ENETC_TXBD(*tx_ring, *i);
> > +	tx_swbd = &tx_ring->tx_swbd[*i];
> > +	prefetchw(txbd);
> 
> (same question as for the previous prefetchw())
> 
> > +
> > +	enetc_clear_tx_bd(&txbd_tmp);
> > +	if (skb_vlan_tag_present(skb)) {
> > +		/* Setup the VLAN fields */
> > +		txbd_tmp.ext.vid = cpu_to_le16(skb_vlan_tag_get(skb));
> > +		txbd_tmp.ext.tpid = 0; /* < C-TAG */
> 
> ???
> 
> Maybe #define it somewhere, that 0 means CVLAN etc.?

Okay, accept.

> 
> > +		e_flags = ENETC_TXBD_E_FLAGS_VLAN_INS;
> > +	}
> > +
> > +	/* Write the BD */
> > +	txbd_tmp.ext.e_flags = e_flags;
> > +	txbd_tmp.ext.lso_sg_size = cpu_to_le16(lso->lso_seg_size);
> > +	txbd_tmp.ext.frm_len_ext = cpu_to_le16(frm_len_ext);
> > +	*txbd = txbd_tmp;
> > +}
> > +
> > +static int enetc_lso_map_data(struct enetc_bdr *tx_ring, struct sk_buff *skb,
> > +			      int *i, struct enetc_lso_t *lso, int *count) {
> > +	union enetc_tx_bd txbd_tmp, *txbd = NULL;
> > +	struct enetc_tx_swbd *tx_swbd;
> > +	skb_frag_t *frag;
> > +	dma_addr_t dma;
> > +	u8 flags = 0;
> > +	int len, f;
> > +
> > +	len = skb_headlen(skb) - lso->hdr_len;
> > +	if (len > 0) {
> > +		dma = dma_map_single(tx_ring->dev, skb->data + lso->hdr_len,
> > +				     len, DMA_TO_DEVICE);
> > +		if (unlikely(dma_mapping_error(tx_ring->dev, dma)))
> 
> dma_mapping_error() already contains unlikely().

Will remove 'unlikely'. Thanks.

And I will remove all the 'unlikely' for dma_mapping_error() in the future.

> 
> > +			return -ENOMEM;
> > +
> > +		enetc_bdr_idx_inc(tx_ring, i);
> > +		txbd = ENETC_TXBD(*tx_ring, *i);
> > +		tx_swbd = &tx_ring->tx_swbd[*i];
> > +		prefetchw(txbd);
> > +		*count += 1;
> > +
> > +		enetc_clear_tx_bd(&txbd_tmp);
> > +		txbd_tmp.addr = cpu_to_le64(dma);
> > +		txbd_tmp.buf_len = cpu_to_le16(len);
> > +
> > +		tx_swbd->dma = dma;
> > +		tx_swbd->len = len;
> > +		tx_swbd->is_dma_page = 0;
> > +		tx_swbd->dir = DMA_TO_DEVICE;
> > +	}
> > +
> > +	frag = &skb_shinfo(skb)->frags[0];
> > +	for (f = 0; f < skb_shinfo(skb)->nr_frags; f++, frag++) {
> > +		if (txbd)
> > +			*txbd = txbd_tmp;
> > +
> > +		len = skb_frag_size(frag);
> > +		dma = skb_frag_dma_map(tx_ring->dev, frag, 0, len,
> > +				       DMA_TO_DEVICE);
> 
> You now can use skb_frag_dma_map() with 2-4 arguments, so this can be
> replaced to
> 
> 		dma = skb_frag_dma_map(tx_ring->dev, frag);


But my compiler complains the error:
drivers/net/ethernet/freescale/enetc/enetc.c: In function ‘enetc_lso_map_data’:
drivers/net/ethernet/freescale/enetc/enetc.c:684:23: error: too few arguments to function ‘skb_frag_dma_map’
  684 |                 dma = skb_frag_dma_map(tx_ring->dev, frag);
      |                       ^~~~~~~~~~~~~~~~

> 
> > +		if (unlikely(dma_mapping_error(tx_ring->dev, dma)))
> > +			return -ENOMEM;
> > +
> > +		/* Get the next BD */
> > +		enetc_bdr_idx_inc(tx_ring, i);
> > +		txbd = ENETC_TXBD(*tx_ring, *i);
> > +		tx_swbd = &tx_ring->tx_swbd[*i];
> > +		prefetchw(txbd);
> > +		*count += 1;
> > +
> > +		enetc_clear_tx_bd(&txbd_tmp);
> > +		txbd_tmp.addr = cpu_to_le64(dma);
> > +		txbd_tmp.buf_len = cpu_to_le16(len);
> > +
> > +		tx_swbd->dma = dma;
> > +		tx_swbd->len = len;
> > +		tx_swbd->is_dma_page = 1;
> > +		tx_swbd->dir = DMA_TO_DEVICE;
> > +	}
> > +
> > +	/* Last BD needs 'F' bit set */
> > +	flags |= ENETC_TXBD_FLAGS_F;
> > +	txbd_tmp.flags = flags;
> > +	*txbd = txbd_tmp;
> > +
> > +	tx_swbd->is_eof = 1;
> > +	tx_swbd->skb = skb;
> > +
> > +	return 0;
> > +}
> 
> [...]
> 
> > @@ -2096,6 +2329,13 @@ static int enetc_setup_default_rss_table(struct
> enetc_si *si, int num_groups)
> >  	return 0;
> >  }
> >
> > +static void enetc_set_lso_flags_mask(struct enetc_hw *hw) {
> > +	enetc_wr(hw, ENETC4_SILSOSFMR0,
> > +		 SILSOSFMR0_VAL_SET(TCP_NL_SEG_FLAGS_DMASK,
> TCP_NL_SEG_FLAGS_DMASK));
> > +	enetc_wr(hw, ENETC4_SILSOSFMR1, 0);
> > +}
> > +
> >  int enetc_configure_si(struct enetc_ndev_priv *priv)  {
> >  	struct enetc_si *si = priv->si;
> > @@ -2109,6 +2349,9 @@ int enetc_configure_si(struct enetc_ndev_priv
> *priv)
> >  	/* enable SI */
> >  	enetc_wr(hw, ENETC_SIMR, ENETC_SIMR_EN);
> >
> > +	if (si->hw_features & ENETC_SI_F_LSO)
> > +		enetc_set_lso_flags_mask(hw);
> > +
> >  	/* TODO: RSS support for i.MX95 will be supported later, and the
> >  	 * is_enetc_rev1() condition will be removed
> >  	 */
> > diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h
> > b/drivers/net/ethernet/freescale/enetc/enetc.h
> > index 1e680f0f5123..6db6b3eee45c 100644
> > --- a/drivers/net/ethernet/freescale/enetc/enetc.h
> > +++ b/drivers/net/ethernet/freescale/enetc/enetc.h
> > @@ -41,6 +41,19 @@ struct enetc_tx_swbd {
> >  	u8 qbv_en:1;
> >  };
> >
> > +struct enetc_lso_t {
> > +	bool	ipv6;
> > +	bool	tcp;
> > +	u8	l3_hdr_len;
> > +	u8	hdr_len; /* LSO header length */
> > +	u8	l3_start;
> > +	u16	lso_seg_size;
> > +	int	total_len; /* total data length, not include LSO header */
> > +};
> > +
> > +#define ENETC_1KB_SIZE			1024
> 
> SZ_1K
> 
> > +#define ENETC_LSO_MAX_DATA_LEN		(256 * ENETC_1KB_SIZE)
> 
> SZ_256K
> 
> > +
> >  #define ENETC_RX_MAXFRM_SIZE	ENETC_MAC_MAXFRM_SIZE
> >  #define ENETC_RXB_TRUESIZE	2048 /* PAGE_SIZE >> 1 */
> >  #define ENETC_RXB_PAD		NET_SKB_PAD /* add extra space if needed
> */
> > @@ -238,6 +251,7 @@ enum enetc_errata {  #define ENETC_SI_F_PSFP
> > BIT(0)  #define ENETC_SI_F_QBV  BIT(1)  #define ENETC_SI_F_QBU
> BIT(2)
> > +#define ENETC_SI_F_LSO	BIT(3)
> >
> >  struct enetc_drvdata {
> >  	u32 pmac_offset; /* Only valid for PSI which supports 802.1Qbu */ @@
> > -351,6 +365,7 @@ enum enetc_active_offloads {
> >  	ENETC_F_QCI			= BIT(10),
> >  	ENETC_F_QBU			= BIT(11),
> >  	ENETC_F_TXCSUM			= BIT(12),
> > +	ENETC_F_LSO			= BIT(13),
> >  };
> >
> >  enum enetc_flags_bit {
> > diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
> > b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
> > index 26b220677448..cdde8e93a73c 100644
> > --- a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
> > +++ b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
> > @@ -12,6 +12,28 @@
> >  #define NXP_ENETC_VENDOR_ID		0x1131
> >  #define NXP_ENETC_PF_DEV_ID		0xe101
> >
> > +/**********************Station interface
> > +registers************************/
> > +/* Station interface LSO segmentation flag mask register 0/1 */
> > +#define ENETC4_SILSOSFMR0		0x1300
> > +#define  SILSOSFMR0_TCP_MID_SEG		GENMASK(27, 16)
> > +#define  SILSOSFMR0_TCP_1ST_SEG		GENMASK(11, 0)
> > +#define  SILSOSFMR0_VAL_SET(first, mid)	((((mid) << 16) &
> SILSOSFMR0_TCP_MID_SEG) | \
> 
> Why not FIELD_PREP()?

Okay, accept.

> 
> > +					 ((first) & SILSOSFMR0_TCP_1ST_SEG))
> > +
> > +#define ENETC4_SILSOSFMR1		0x1304
> > +#define  SILSOSFMR1_TCP_LAST_SEG	GENMASK(11, 0)
> > +#define   TCP_FLAGS_FIN			BIT(0)
> > +#define   TCP_FLAGS_SYN			BIT(1)
> > +#define   TCP_FLAGS_RST			BIT(2)
> > +#define   TCP_FLAGS_PSH			BIT(3)
> > +#define   TCP_FLAGS_ACK			BIT(4)
> > +#define   TCP_FLAGS_URG			BIT(5)
> > +#define   TCP_FLAGS_ECE			BIT(6)
> > +#define   TCP_FLAGS_CWR			BIT(7)
> > +#define   TCP_FLAGS_NS			BIT(8)
> 
> Why are you open-coding these if they're present in uapi/linux/tcp.h?

Okay, I will add 'ENETC' prefix.
> 
> > +/* According to tso_build_hdr(), clear all special flags for not last
> > +packet. */
> 
> But this mask is used only to do a writel(), I don't see it anywhere clearing
> anything...

The hardware will help mask of TCP header flags, we just need to set
flag mask register.

> 
> > +#define TCP_NL_SEG_FLAGS_DMASK		(TCP_FLAGS_FIN |
> TCP_FLAGS_RST | TCP_FLAGS_PSH)
> > +
> >  /***************************ENETC port
> registers**************************/
> >  #define ENETC4_ECAPR0			0x0
> >  #define  ECAPR0_RFS			BIT(2)
> Thanks,
> Olek

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ