diff mbox

[net-next,v4,1/1] net: fec: Enable imx6 enet checksum acceleration.

Message ID 1366384215-21441-1-git-send-email-jim_baxter@mentor.com
State Superseded, archived
Headers show

Commit Message

Jim Baxter April 19, 2013, 3:10 p.m. UTC
Enables hardware generation of IP header and
protocol specific checksums for transmitted
packets.

Enabled hardware discarding of received packets with
invalid IP header or protocol specific checksums.

The feature is enabled by default but can be
enabled/disabled by ethtool.

Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Jim Baxter <jim_baxter@mentor.com>
---

- Added IPV6 support.
- Simplified checksum function to remove unnecessary IP header checksum
generation and only update the protocol checksum in
skb->csum_start + skb->csum_offset.
- fec_enet_clear_csum() now returns error value which is checked by
fec_enet_start_xmit() which returns NETDEV_TX_BUSY.
- Simplified fec_set_features() by removing unrequired variables.


 drivers/net/ethernet/freescale/fec.h      |   10 ++-
 drivers/net/ethernet/freescale/fec_main.c |  106 ++++++++++++++++++++++++++++-
 2 files changed, 112 insertions(+), 4 deletions(-)

Comments

Eric Dumazet April 19, 2013, 3:29 p.m. UTC | #1
On Fri, 2013-04-19 at 16:10 +0100, Jim Baxter wrote:
>  
> +static int
> +fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
> +{
> +	/* Only run for packets requiring a checksum. */
> +	if (skb->ip_summed != CHECKSUM_PARTIAL)
> +		return 0;
> +
> +	if (unlikely(skb_cow_head(skb, 0)))
> +		return -1;
> +
> +	*(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0;
> +
> +	return 0;
> +}


 
> +	/* HW acceleration for ICMP TCP UDP checksum */
> +	if (fec_enet_clear_csum(skb, ndev))
> +		return NETDEV_TX_BUSY;

No : You must drop the packet and return NETDEV_TX_OK



--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings April 19, 2013, 3:34 p.m. UTC | #2
On Fri, 2013-04-19 at 16:10 +0100, Jim Baxter wrote:
> Enables hardware generation of IP header and
> protocol specific checksums for transmitted
> packets.
> 
> Enabled hardware discarding of received packets with
> invalid IP header or protocol specific checksums.
> 
> The feature is enabled by default but can be
> enabled/disabled by ethtool.
> 
> Signed-off-by: Fugang Duan <B38611@freescale.com>
> Signed-off-by: Jim Baxter <jim_baxter@mentor.com>
> ---
> 
> - Added IPV6 support.
[...]
> @@ -1439,6 +1496,14 @@ static int fec_enet_alloc_buffers(struct net_device *ndev)
>  		if (fep->bufdesc_ex) {
>  			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
>  			ebdp->cbd_esc = BD_ENET_TX_INT;
> +
> +			/* Enable protocol checksum flags
> +			 * We do not bother with the IP Checksum bits as they
> +			 * are done by the kernel
> +			 */
> +			if (ndev->features &
> +					(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))
> +				ebdp->cbd_esc |= BD_ENET_TX_PINS;
>  		}
>  
>  		bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
[...]

This condition needs to be based on skb->ip_summed.

Ben.
Jim Baxter April 19, 2013, 3:55 p.m. UTC | #3
On 19/04/13 16:29, Eric Dumazet wrote:
> On Fri, 2013-04-19 at 16:10 +0100, Jim Baxter wrote:
>>  
>> +static int
>> +fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
>> +{
>> +	/* Only run for packets requiring a checksum. */
>> +	if (skb->ip_summed != CHECKSUM_PARTIAL)
>> +		return 0;
>> +
>> +	if (unlikely(skb_cow_head(skb, 0)))
>> +		return -1;
>> +
>> +	*(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0;
>> +
>> +	return 0;
>> +}
> 
> 
>  
>> +	/* HW acceleration for ICMP TCP UDP checksum */
>> +	if (fec_enet_clear_csum(skb, ndev))
>> +		return NETDEV_TX_BUSY;
> 
> No : You must drop the packet and return NETDEV_TX_OK
> 
> 
> 

I have no issue with changing it, but I am curious, by returning OK will
the kernel not regard it a sent packet and it will be lost?

Thank you,
Jim
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings April 19, 2013, 3:56 p.m. UTC | #4
On Fri, 2013-04-19 at 16:55 +0100, Jim Baxter wrote:
> On 19/04/13 16:29, Eric Dumazet wrote:
> > On Fri, 2013-04-19 at 16:10 +0100, Jim Baxter wrote:
> >>  
> >> +static int
> >> +fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
> >> +{
> >> +	/* Only run for packets requiring a checksum. */
> >> +	if (skb->ip_summed != CHECKSUM_PARTIAL)
> >> +		return 0;
> >> +
> >> +	if (unlikely(skb_cow_head(skb, 0)))
> >> +		return -1;
> >> +
> >> +	*(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0;
> >> +
> >> +	return 0;
> >> +}
> > 
> > 
> >  
> >> +	/* HW acceleration for ICMP TCP UDP checksum */
> >> +	if (fec_enet_clear_csum(skb, ndev))
> >> +		return NETDEV_TX_BUSY;
> > 
> > No : You must drop the packet and return NETDEV_TX_OK
> > 
> > 
> > 
> 
> I have no issue with changing it, but I am curious, by returning OK will
> the kernel not regard it a sent packet and it will be lost?

Correct.  This is normal behaviour when short of memory; look at any
other driver that allocates memory on the TX path.

Ben.
Eric Dumazet April 19, 2013, 3:59 p.m. UTC | #5
On Fri, 2013-04-19 at 16:55 +0100, Jim Baxter wrote:

> 
> I have no issue with changing it, but I am curious, by returning OK will
> the kernel not regard it a sent packet and it will be lost?

Answer is : The sender will loop forever trying to send this packet.
Just drop it and avoid a possible deadlock.

NETDEV_TX_BUSY is deprecated and its use is not for solving this kind of
problem.


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jim Baxter April 19, 2013, 4:16 p.m. UTC | #6
On 19/04/13 16:34, Ben Hutchings wrote:
> On Fri, 2013-04-19 at 16:10 +0100, Jim Baxter wrote:
>> Enables hardware generation of IP header and
>> protocol specific checksums for transmitted
>> packets.
>>
>> Enabled hardware discarding of received packets with
>> invalid IP header or protocol specific checksums.
>>
>> The feature is enabled by default but can be
>> enabled/disabled by ethtool.
>>
>> Signed-off-by: Fugang Duan <B38611@freescale.com>
>> Signed-off-by: Jim Baxter <jim_baxter@mentor.com>
>> ---
>>
>> - Added IPV6 support.
> [...]
>> @@ -1439,6 +1496,14 @@ static int fec_enet_alloc_buffers(struct net_device *ndev)
>>  		if (fep->bufdesc_ex) {
>>  			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
>>  			ebdp->cbd_esc = BD_ENET_TX_INT;
>> +
>> +			/* Enable protocol checksum flags
>> +			 * We do not bother with the IP Checksum bits as they
>> +			 * are done by the kernel
>> +			 */
>> +			if (ndev->features &
>> +					(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))
>> +				ebdp->cbd_esc |= BD_ENET_TX_PINS;
>>  		}
>>  
>>  		bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> [...]
> 
> This condition needs to be based on skb->ip_summed.
> 
> Ben.
> 


These buffers are setup when the network driver is opened
(fec_enet_open), with the same settings as the transmit buffers.

Thinking about it, they should probably not be set here and only set
during the fec_enet_start_xmit() function.

Jim
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings April 19, 2013, 4:20 p.m. UTC | #7
On Fri, 2013-04-19 at 17:16 +0100, Jim Baxter wrote:
> On 19/04/13 16:34, Ben Hutchings wrote:
> > On Fri, 2013-04-19 at 16:10 +0100, Jim Baxter wrote:
> >> Enables hardware generation of IP header and
> >> protocol specific checksums for transmitted
> >> packets.
> >>
> >> Enabled hardware discarding of received packets with
> >> invalid IP header or protocol specific checksums.
> >>
> >> The feature is enabled by default but can be
> >> enabled/disabled by ethtool.
> >>
> >> Signed-off-by: Fugang Duan <B38611@freescale.com>
> >> Signed-off-by: Jim Baxter <jim_baxter@mentor.com>
> >> ---
> >>
> >> - Added IPV6 support.
> > [...]
> >> @@ -1439,6 +1496,14 @@ static int fec_enet_alloc_buffers(struct net_device *ndev)
> >>  		if (fep->bufdesc_ex) {
> >>  			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
> >>  			ebdp->cbd_esc = BD_ENET_TX_INT;
> >> +
> >> +			/* Enable protocol checksum flags
> >> +			 * We do not bother with the IP Checksum bits as they
> >> +			 * are done by the kernel
> >> +			 */
> >> +			if (ndev->features &
> >> +					(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))
> >> +				ebdp->cbd_esc |= BD_ENET_TX_PINS;
> >>  		}
> >>  
> >>  		bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
> > [...]
> > 
> > This condition needs to be based on skb->ip_summed.
> > 
> > Ben.
> > 
> 
> 
> These buffers are setup when the network driver is opened
> (fec_enet_open), with the same settings as the transmit buffers.
> 
> Thinking about it, they should probably not be set here and only set
> during the fec_enet_start_xmit() function.

Right.  Even when TX checksum offload is enabled, the kernel might not
want you to offload TX checksums for every packet.  For example, if your
interface is part of a bridge, packets should be forwarded even if they
have a bad layer-4 checksum on RX, and the checksum must not be
'corrected' on TX.

Ben.
diff mbox

Patch

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index eb43729..d44f65b 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -52,6 +52,7 @@ 
 #define FEC_R_FIFO_RSEM		0x194 /* Receive FIFO section empty threshold */
 #define FEC_R_FIFO_RAEM		0x198 /* Receive FIFO almost empty threshold */
 #define FEC_R_FIFO_RAFL		0x19c /* Receive FIFO almost full threshold */
+#define FEC_RACC		0x1C4 /* Receive Accelerator function */
 #define FEC_MIIGSK_CFGR		0x300 /* MIIGSK Configuration reg */
 #define FEC_MIIGSK_ENR		0x308 /* MIIGSK Enable reg */
 
@@ -164,9 +165,11 @@  struct bufdesc_ex {
 #define BD_ENET_TX_CSL          ((ushort)0x0001)
 #define BD_ENET_TX_STATS        ((ushort)0x03ff)        /* All status bits */
 
-/*enhanced buffer desciptor control/status used by Ethernet transmit*/
+/*enhanced buffer descriptor control/status used by Ethernet transmit*/
 #define BD_ENET_TX_INT          0x40000000
 #define BD_ENET_TX_TS           0x20000000
+#define BD_ENET_TX_PINS         0x10000000
+#define BD_ENET_TX_IINS         0x08000000
 
 
 /* This device has up to three irqs on some platforms */
@@ -190,6 +193,10 @@  struct bufdesc_ex {
 
 #define BD_ENET_RX_INT          0x00800000
 #define BD_ENET_RX_PTP          ((ushort)0x0400)
+#define BD_ENET_RX_ICE		0x00000020
+#define BD_ENET_RX_PCR		0x00000010
+#define FLAG_RX_CSUM_ENABLED	(BD_ENET_RX_ICE | BD_ENET_RX_PCR)
+#define FLAG_RX_CSUM_ERROR	(BD_ENET_RX_ICE | BD_ENET_RX_PCR)
 
 /* The FEC buffer descriptors track the ring buffers.  The rx_bd_base and
  * tx_bd_base always point to the base of the buffer descriptors.  The
@@ -247,6 +254,7 @@  struct fec_enet_private {
 	int	pause_flag;
 
 	struct	napi_struct napi;
+	int	csum_flags;
 
 	struct ptp_clock *ptp_clock;
 	struct ptp_clock_info ptp_caps;
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index d7657a4..d6694fc 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -34,6 +34,12 @@ 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <net/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/icmp.h>
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
 #include <linux/bitops.h>
@@ -181,6 +187,11 @@  MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 #define PKT_MINBUF_SIZE		64
 #define PKT_MAXBLR_SIZE		1520
 
+/* FEC receive acceleration */
+#define FEC_RACC_IPDIS		(1 << 1)
+#define FEC_RACC_PRODIS		(1 << 2)
+#define FEC_RACC_OPTIONS	(FEC_RACC_IPDIS | FEC_RACC_PRODIS)
+
 /*
  * The 5270/5271/5280/5282/532x RX control register also contains maximum frame
  * size bits. Other FEC hardware does not, so we need to take that into
@@ -241,6 +252,21 @@  static void *swap_buffer(void *bufaddr, int len)
 	return bufaddr;
 }
 
+static int
+fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
+{
+	/* Only run for packets requiring a checksum. */
+	if (skb->ip_summed != CHECKSUM_PARTIAL)
+		return 0;
+
+	if (unlikely(skb_cow_head(skb, 0)))
+		return -1;
+
+	*(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0;
+
+	return 0;
+}
+
 static netdev_tx_t
 fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
@@ -253,7 +279,7 @@  fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 	unsigned int index;
 
 	if (!fep->link) {
-		/* Link is down or autonegotiation is in progress. */
+		/* Link is down or auto-negotiation is in progress. */
 		return NETDEV_TX_BUSY;
 	}
 
@@ -270,6 +296,10 @@  fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 		return NETDEV_TX_BUSY;
 	}
 
+	/* HW acceleration for ICMP TCP UDP checksum */
+	if (fec_enet_clear_csum(skb, ndev))
+		return NETDEV_TX_BUSY;
+
 	/* Clear all of the status flags */
 	status &= ~BD_ENET_TX_STATS;
 
@@ -326,8 +356,14 @@  fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 			ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT);
 			skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
 		} else {
-
 			ebdp->cbd_esc = BD_ENET_TX_INT;
+
+			/* Enable protocol checksum flags
+			 * We do not bother with the IP Checksum bits as they
+			 * are done by the kernel
+			 */
+			if (skb->ip_summed == CHECKSUM_PARTIAL)
+				ebdp->cbd_esc |= BD_ENET_TX_PINS;
 		}
 	}
 	/* If this was the last BD in the ring, start at the beginning again. */
@@ -407,6 +443,7 @@  fec_restart(struct net_device *ndev, int duplex)
 	const struct platform_device_id *id_entry =
 				platform_get_device_id(fep->pdev);
 	int i;
+	u32 val;
 	u32 temp_mac[2];
 	u32 rcntl = OPT_FRAME_SIZE | 0x04;
 	u32 ecntl = 0x2; /* ETHEREN */
@@ -473,6 +510,14 @@  fec_restart(struct net_device *ndev, int duplex)
 	/* Set MII speed */
 	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
 
+	/* set RX checksum */
+	val = readl(fep->hwp + FEC_RACC);
+	if (fep->csum_flags & FLAG_RX_CSUM_ENABLED)
+		val |= FEC_RACC_OPTIONS;
+	else
+		val &= ~FEC_RACC_OPTIONS;
+	writel(val, fep->hwp + FEC_RACC);
+
 	/*
 	 * The phy interface and speed need to get configured
 	 * differently on enet-mac.
@@ -530,7 +575,7 @@  fec_restart(struct net_device *ndev, int duplex)
 	     fep->phy_dev && fep->phy_dev->pause)) {
 		rcntl |= FEC_ENET_FCE;
 
-		/* set FIFO thresh hold parameter to reduce overrun */
+		/* set FIFO threshold parameter to reduce overrun */
 		writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM);
 		writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL);
 		writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM);
@@ -818,6 +863,18 @@  fec_enet_rx(struct net_device *ndev, int budget)
 				spin_unlock_irqrestore(&fep->tmreg_lock, flags);
 			}
 
+			if (fep->bufdesc_ex &&
+				(fep->csum_flags & FLAG_RX_CSUM_ENABLED)) {
+				struct bufdesc_ex *ebdp =
+					(struct bufdesc_ex *)bdp;
+				if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) {
+					/* don't check it */
+					skb->ip_summed = CHECKSUM_UNNECESSARY;
+				} else {
+					skb_checksum_none_assert(skb);
+				}
+			}
+
 			if (!skb_defer_rx_timestamp(skb))
 				napi_gro_receive(&fep->napi, skb);
 		}
@@ -1439,6 +1496,14 @@  static int fec_enet_alloc_buffers(struct net_device *ndev)
 		if (fep->bufdesc_ex) {
 			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
 			ebdp->cbd_esc = BD_ENET_TX_INT;
+
+			/* Enable protocol checksum flags
+			 * We do not bother with the IP Checksum bits as they
+			 * are done by the kernel
+			 */
+			if (ndev->features &
+					(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))
+				ebdp->cbd_esc |= BD_ENET_TX_PINS;
 		}
 
 		bdp = fec_enet_get_nextdesc(bdp, fep->bufdesc_ex);
@@ -1618,6 +1683,33 @@  static void fec_poll_controller(struct net_device *dev)
 }
 #endif
 
+static int fec_set_features(struct net_device *netdev,
+	netdev_features_t features)
+{
+	struct fec_enet_private *fep = netdev_priv(netdev);
+	netdev_features_t changed = features ^ netdev->features;
+
+	netdev->features = features;
+
+	/* Receive checksum has been changed */
+	if (changed & NETIF_F_RXCSUM) {
+		if (features & NETIF_F_RXCSUM)
+			fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
+		else
+			fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED;
+
+		if (netif_running(netdev)) {
+			fec_stop(netdev);
+			fec_restart(netdev, fep->phy_dev->duplex);
+			netif_wake_queue(netdev);
+		} else {
+			fec_restart(netdev, fep->phy_dev->duplex);
+		}
+	}
+
+	return 0;
+}
+
 static const struct net_device_ops fec_netdev_ops = {
 	.ndo_open		= fec_enet_open,
 	.ndo_stop		= fec_enet_close,
@@ -1631,6 +1723,7 @@  static const struct net_device_ops fec_netdev_ops = {
 #ifdef CONFIG_NET_POLL_CONTROLLER
 	.ndo_poll_controller	= fec_poll_controller,
 #endif
+	.ndo_set_features	= fec_set_features,
 };
 
  /*
@@ -1672,6 +1765,13 @@  static int fec_enet_init(struct net_device *ndev)
 	writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
 	netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT);
 
+	/* enable hw accelerator */
+	ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
+			| NETIF_F_RXCSUM);
+	ndev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
+			| NETIF_F_RXCSUM);
+	fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
+
 	fec_restart(ndev, 0);
 
 	return 0;