[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <222fc61f88a39393b8830d9bfcc7b88d24fb81d6.1539804852.git.chunkeey@gmail.com>
Date: Wed, 17 Oct 2018 21:53:36 +0200
From: Christian Lamparter <chunkeey@...il.com>
To: netdev@...r.kernel.org
Cc: "David S . Miller" <davem@...emloft.net>
Subject: [PATCH 2/2] net: emac: implement TCP TSO
This patch enables TSO(v4) hw feature for emac driver.
As atleast the APM82181's TCP/IP acceleration hardware
controller (TAH) provides TCP segmentation support in
the transmit path.
Signed-off-by: Christian Lamparter <chunkeey@...il.com>
---
drivers/net/ethernet/ibm/emac/core.c | 101 ++++++++++++++++++++++++++-
drivers/net/ethernet/ibm/emac/core.h | 4 ++
drivers/net/ethernet/ibm/emac/emac.h | 7 ++
drivers/net/ethernet/ibm/emac/tah.c | 20 ++++++
drivers/net/ethernet/ibm/emac/tah.h | 2 +
5 files changed, 133 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index be560f9031f4..49ffbd6e1707 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -38,6 +38,9 @@
#include <linux/mii.h>
#include <linux/bitops.h>
#include <linux/if_vlan.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
#include <linux/workqueue.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -1410,6 +1413,52 @@ static inline u16 emac_tx_csum(struct emac_instance *dev,
return 0;
}
+const u32 tah_ss[TAH_NO_SSR] = { 9000, 4500, 1500, 1300, 576, 176 };
+
+static int emac_tx_tso(struct emac_instance *dev, struct sk_buff *skb,
+ u16 *ctrl)
+{
+ if (emac_has_feature(dev, EMAC_FTR_TAH_HAS_TSO) &&
+ skb_is_gso(skb) && !!(skb_shinfo(skb)->gso_type &
+ (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) {
+ u32 seg_size = 0, i;
+
+ /* Get the MTU */
+ seg_size = skb_shinfo(skb)->gso_size + tcp_hdrlen(skb)
+ + skb_network_header_len(skb);
+
+ /* Restriction applied for the segmentation size
+ * to use HW segmentation offload feature: the size
+ * of the segment must not be less than 168 bytes for
+ * DIX formatted segments, or 176 bytes for
+ * IEEE formatted segments.
+ *
+ * I use value 176 to check for the segment size here
+ * as it can cover both 2 conditions above.
+ */
+ if (seg_size < 176)
+ return -ENODEV;
+
+ /* Get the best suitable MTU */
+ for (i = 0; i < ARRAY_SIZE(tah_ss); i++) {
+ u32 curr_seg = tah_ss[i];
+
+ if (curr_seg > dev->ndev->mtu ||
+ curr_seg > seg_size)
+ continue;
+
+ *ctrl &= ~EMAC_TX_CTRL_TAH_CSUM;
+ *ctrl |= EMAC_TX_CTRL_TAH_SSR(i);
+ return 0;
+ }
+
+ /* none found fall back to software */
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static inline netdev_tx_t emac_xmit_finish(struct emac_instance *dev, int len)
{
struct emac_regs __iomem *p = dev->emacp;
@@ -1452,8 +1501,46 @@ static inline u16 emac_tx_vlan(struct emac_instance *dev, struct sk_buff *skb)
return 0;
}
+static netdev_tx_t
+emac_start_xmit(struct sk_buff *skb, struct net_device *ndev);
+
+static netdev_tx_t emac_sw_tso(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct emac_instance *dev = netdev_priv(ndev);
+ struct sk_buff *segs, *curr;
+
+ segs = skb_gso_segment(skb, ndev->features &
+ ~(NETIF_F_TSO | NETIF_F_TSO6));
+ if (IS_ERR_OR_NULL(segs)) {
+ goto drop;
+ } else {
+ while (segs) {
+ /* check for overflow */
+ if (dev->tx_cnt >= NUM_TX_BUFF) {
+ dev_kfree_skb_any(segs);
+ goto drop;
+ }
+
+ curr = segs;
+ segs = curr->next;
+ curr->next = NULL;
+
+ emac_start_xmit(curr, ndev);
+ }
+ dev_consume_skb_any(skb);
+ }
+
+ return NETDEV_TX_OK;
+
+drop:
+ ++dev->estats.tx_dropped;
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+}
+
/* Tx lock BH */
-static netdev_tx_t emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t
+emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
unsigned int len = skb->len;
@@ -1462,6 +1549,9 @@ static netdev_tx_t emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
u16 ctrl = EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP | MAL_TX_CTRL_READY |
MAL_TX_CTRL_LAST | emac_tx_csum(dev, skb) | emac_tx_vlan(dev, skb);
+ if (emac_tx_tso(dev, skb, &ctrl))
+ return emac_sw_tso(skb, ndev);
+
slot = dev->tx_slot++;
if (dev->tx_slot == NUM_TX_BUFF) {
dev->tx_slot = 0;
@@ -1536,6 +1626,9 @@ emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev)
ctrl = EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP | MAL_TX_CTRL_READY |
emac_tx_csum(dev, skb) | emac_tx_vlan(dev, skb);
+ if (emac_tx_tso(dev, skb, &ctrl))
+ return emac_sw_tso(skb, ndev);
+
slot = dev->tx_slot;
/* skb data */
@@ -2946,6 +3039,9 @@ static int emac_init_config(struct emac_instance *dev)
if (dev->tah_ph != 0) {
#ifdef CONFIG_IBM_EMAC_TAH
dev->features |= EMAC_FTR_HAS_TAH;
+
+ if (of_device_is_compatible(np, "ibm,emac-apm821xx"))
+ dev->features |= EMAC_FTR_TAH_HAS_TSO;
#else
printk(KERN_ERR "%pOF: TAH support not enabled !\n", np);
return -ENXIO;
@@ -3167,6 +3263,9 @@ static int emac_probe(struct platform_device *ofdev)
if (dev->tah_dev) {
ndev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG;
+ if (emac_has_feature(dev, EMAC_FTR_TAH_HAS_TSO))
+ ndev->hw_features |= NETIF_F_TSO;
+
if (emac_has_feature(dev, EMAC_FTR_HAS_VLAN_CTAG_TX)) {
ndev->vlan_features |= ndev->hw_features;
ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX;
diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h
index 8d84d439168c..e40ccee58775 100644
--- a/drivers/net/ethernet/ibm/emac/core.h
+++ b/drivers/net/ethernet/ibm/emac/core.h
@@ -336,6 +336,8 @@ struct emac_instance {
#define EMAC_FTR_APM821XX_NO_HALF_DUPLEX 0x00001000
/* EMAC can insert 802.1Q tag */
#define EMAC_FTR_HAS_VLAN_CTAG_TX 0x00002000
+/* TAH can do TCP segmentation offload */
+#define EMAC_FTR_TAH_HAS_TSO 0x00004000
/* Right now, we don't quite handle the always/possible masks on the
* most optimal way as we don't have a way to say something like
@@ -352,6 +354,8 @@ enum {
#endif
#ifdef CONFIG_IBM_EMAC_TAH
EMAC_FTR_HAS_TAH |
+ EMAC_FTR_TAH_HAS_TSO |
+
#endif
#ifdef CONFIG_IBM_EMAC_ZMII
EMAC_FTR_HAS_ZMII |
diff --git a/drivers/net/ethernet/ibm/emac/emac.h b/drivers/net/ethernet/ibm/emac/emac.h
index e2f80cca9bed..833967aceb2f 100644
--- a/drivers/net/ethernet/ibm/emac/emac.h
+++ b/drivers/net/ethernet/ibm/emac/emac.h
@@ -266,6 +266,13 @@ struct emac_regs {
#define EMAC_TX_CTRL_IVT 0x0020
#define EMAC_TX_CTRL_RVT 0x0010
#define EMAC_TX_CTRL_TAH_CSUM 0x000e
+#define EMAC_TX_CTRL_TAH_SSR(idx) (((idx) + 1) << 1)
+#define EMAC_TX_CTRL_TAH_SSR5 0x000c
+#define EMAC_TX_CTRL_TAH_SSR4 0x000a
+#define EMAC_TX_CTRL_TAH_SSR3 0x0008
+#define EMAC_TX_CTRL_TAH_SSR2 0x0006
+#define EMAC_TX_CTRL_TAH_SSR1 0x0004
+#define EMAC_TX_CTRL_TAH_SSR0 0x0002
/* EMAC specific TX descriptor status fields (read access) */
#define EMAC_TX_ST_BFCS 0x0200
diff --git a/drivers/net/ethernet/ibm/emac/tah.c b/drivers/net/ethernet/ibm/emac/tah.c
index 9912456dca48..a0236eb305ae 100644
--- a/drivers/net/ethernet/ibm/emac/tah.c
+++ b/drivers/net/ethernet/ibm/emac/tah.c
@@ -45,6 +45,24 @@ void tah_detach(struct platform_device *ofdev, int channel)
mutex_unlock(&dev->lock);
}
+static void tah_set_ssr(struct platform_device *ofdev)
+{
+ struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
+ struct tah_regs __iomem *p = dev->base;
+ int i;
+
+ mutex_lock(&dev->lock);
+
+ for (i = 0; i < ARRAY_SIZE(tah_ss); i++) {
+ /* Segment size can be up to 16K, but needs
+ * to be a multiple of 2 bytes
+ */
+ out_be32(&p->ssr0 + i, (tah_ss[i] & 0x3ffe) << 16);
+ }
+
+ mutex_unlock(&dev->lock);
+}
+
void tah_reset(struct platform_device *ofdev)
{
struct tah_instance *dev = platform_get_drvdata(ofdev);
@@ -64,6 +82,8 @@ void tah_reset(struct platform_device *ofdev)
out_be32(&p->mr,
TAH_MR_CVR | TAH_MR_ST_768 | TAH_MR_TFS_10KB | TAH_MR_DTFP |
TAH_MR_DIG);
+
+ tah_set_ssr(ofdev);
}
int tah_get_regs_len(struct platform_device *ofdev)
diff --git a/drivers/net/ethernet/ibm/emac/tah.h b/drivers/net/ethernet/ibm/emac/tah.h
index 4d5f336f07b3..2cb0629f30e2 100644
--- a/drivers/net/ethernet/ibm/emac/tah.h
+++ b/drivers/net/ethernet/ibm/emac/tah.h
@@ -36,6 +36,8 @@ struct tah_regs {
u32 tsr;
};
+#define TAH_NO_SSR 6
+extern const u32 tah_ss[TAH_NO_SSR];
/* TAH device */
struct tah_instance {
--
2.19.1
Powered by blists - more mailing lists