From patchwork Tue Oct 23 10:04:14 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Lamparter X-Patchwork-Id: 988108 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="bMnSVyFz"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 42fTWY1wFjz9sNH for ; Tue, 23 Oct 2018 21:04:25 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728989AbeJWS1G (ORCPT ); Tue, 23 Oct 2018 14:27:06 -0400 Received: from mail-ed1-f68.google.com ([209.85.208.68]:40579 "EHLO mail-ed1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728844AbeJWS1E (ORCPT ); Tue, 23 Oct 2018 14:27:04 -0400 Received: by mail-ed1-f68.google.com with SMTP id r1-v6so1017367edd.7 for ; Tue, 23 Oct 2018 03:04:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=dW2ERZ6ZcAP93nPhTf40er+2J4qAzx9/0NJZGWSlch4=; b=bMnSVyFzjoRsGqeiRAO/CR9+xBd6h63oAPJpPMjXa2hPZMVhH+x6mStOW0fAMn/bz6 rw3ck5ruFW0IieSESdBmu4buv7+p7o/neCjpFiQEMKNuQ+TI+v+CUH2o6N7eYaiD/q9v Ft1hRcI51XAbKbvP0lClMoUlQRs/Jb99roi7VeTXbzP5tURAGRBGYyi8kphqPgEZnTNt BzJM+VsMfM0Bn0kV6GGYCLYCgBOaF0qTe6crwdH+tDQgTRgbaOZLx9vsPyxRIpspngxw XEBecLpWd+jnUkezT+KqdRzNP/qKVRWkktZeOVMaf5d+cywtKNvgSsVZ4t1gCdo3ctlz kSwg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=dW2ERZ6ZcAP93nPhTf40er+2J4qAzx9/0NJZGWSlch4=; b=DTdms37Sy9RAZ82H70fCK6uc6eDdMWnZcR+YkWwd0oxTyEK0clw/LtKeQttR3SO+Nf jIUcImbNoorapSitcNywqi+AC6ZEaIZZNnzc8IOJV7fGfRqjiKD0MqSIP2FA9s3lvV9X QAnNjpL87agzxVeMv4Bqin2pzVendi9P2Hqk2hji2QYTOy7T+fDDTml1hrh1eHCF0a5Q AON/qVko8WC621aJr3C5IV85y4/U20YPmTPmLAXhAB0z/B8ej/N2Eb+xKOc3lOhpJd6I zut0d5c45NiaiPE3vKhvOKCdXPROzztd9IQWH2d38kp5k9JCBATZNrRcQyKgsDycoi8A FguQ== X-Gm-Message-State: ABuFfohXTR//h8JIBAhROJbbQXguUEahibtaPHRMtq8Fn9L5cC2DtrAr Q9fcZtoqXozYGMS7fTF+tbS0Y+2rlU4= X-Google-Smtp-Source: ACcGV63v3lNiDfOYfeaKGEc+Xts/uCWgSwMkI9Ne5XeCGwuqnvzL1xnpCWyet1S6zn9noYn+ModmVQ== X-Received: by 2002:a50:c09a:: with SMTP id k26-v6mr16029699edf.111.1540289059034; Tue, 23 Oct 2018 03:04:19 -0700 (PDT) Received: from debian64.daheim (p5B0D7C05.dip0.t-ipconnect.de. [91.13.124.5]) by smtp.gmail.com with ESMTPSA id h44-v6sm416234edb.38.2018.10.23.03.04.17 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Tue, 23 Oct 2018 03:04:17 -0700 (PDT) Received: from root by debian64.daheim with local (Exim 4.91) (envelope-from ) id 1gEtXs-0004Ct-Ev; Tue, 23 Oct 2018 12:04:16 +0200 From: Christian Lamparter To: netdev@vger.kernel.org Cc: "David S . Miller" Subject: [PATCH v3 2/4] net: emac: implement TCP segmentation offload (TSO) Date: Tue, 23 Oct 2018 12:04:14 +0200 Message-Id: X-Mailer: git-send-email 2.19.1 In-Reply-To: References: MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- drivers/net/ethernet/ibm/emac/core.c | 112 ++++++++++++++++++++++++++- drivers/net/ethernet/ibm/emac/core.h | 7 ++ drivers/net/ethernet/ibm/emac/emac.h | 7 ++ drivers/net/ethernet/ibm/emac/tah.c | 22 +++++- drivers/net/ethernet/ibm/emac/tah.h | 2 + 5 files changed, 148 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index be560f9031f4..80aafd7552aa 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -1118,6 +1121,32 @@ static int emac_resize_rx_ring(struct emac_instance *dev, int new_mtu) return ret; } +/* 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. However based on actual + * tests any MTU less than 416 causes excessive retries + * due to TX FIFO underruns. + */ +const u32 tah_ss[TAH_NO_SSR] = { 1500, 1344, 1152, 960, 768, 416 }; + +/* look-up matching segment size for the given mtu */ +static void emac_find_tso_ss_for_mtu(struct emac_instance *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tah_ss); i++) { + if (tah_ss[i] <= dev->ndev->mtu) + break; + } + /* if no matching segment size is found, set the tso_ss_mtu_start + * variable anyway. This will cause the emac_tx_tso to skip straight + * to the software fallback. + */ + dev->tso_ss_mtu_start = i; +} + /* Process ctx, rtnl_lock semaphore */ static int emac_change_mtu(struct net_device *ndev, int new_mtu) { @@ -1134,6 +1163,7 @@ static int emac_change_mtu(struct net_device *ndev, int new_mtu) if (!ret) { ndev->mtu = new_mtu; + emac_find_tso_ss_for_mtu(dev); dev->rx_skb_size = emac_rx_skb_size(new_mtu); dev->rx_sync_size = emac_rx_sync_size(new_mtu); } @@ -1410,6 +1440,33 @@ static inline u16 emac_tx_csum(struct emac_instance *dev, return 0; } +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); + + for (i = dev->tso_ss_mtu_start; i < ARRAY_SIZE(tah_ss); i++) { + if (tah_ss[i] > seg_size) + continue; + + *ctrl |= EMAC_TX_CTRL_TAH_SSR(i); + return 0; + } + + /* none found fall back to software */ + return -EINVAL; + } + + *ctrl |= emac_tx_csum(dev, skb); + return 0; +} + static inline netdev_tx_t emac_xmit_finish(struct emac_instance *dev, int len) { struct emac_regs __iomem *p = dev->emacp; @@ -1452,6 +1509,46 @@ static inline u16 emac_tx_vlan(struct emac_instance *dev, struct sk_buff *skb) return 0; } +static netdev_tx_t +emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev); + +static int +emac_sw_tso(struct sk_buff *skb, struct net_device *ndev) +{ + struct emac_instance *dev = netdev_priv(ndev); + struct sk_buff *segs, *curr; + unsigned int i, frag_slots; + + /* make sure to not overflow the tx ring */ + frag_slots = dev->tx_cnt; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i]; + + frag_slots += mal_tx_chunks(skb_frag_size(frag)); + + if (frag_slots >= NUM_TX_BUFF) + return -ENOSPC; + }; + + segs = skb_gso_segment(skb, ndev->features & + ~(NETIF_F_TSO | NETIF_F_TSO6)); + if (IS_ERR_OR_NULL(segs)) { + ++dev->estats.tx_dropped; + dev_kfree_skb_any(skb); + } else { + while (segs) { + curr = segs; + segs = curr->next; + curr->next = NULL; + + emac_start_xmit_sg(curr, ndev); + } + dev_consume_skb_any(skb); + } + + return 0; +} + /* Tx lock BH */ static netdev_tx_t emac_start_xmit(struct sk_buff *skb, struct net_device *ndev) { @@ -1535,7 +1632,12 @@ emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev) goto stop_queue; ctrl = EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP | MAL_TX_CTRL_READY | - emac_tx_csum(dev, skb) | emac_tx_vlan(dev, skb); + emac_tx_vlan(dev, skb); + if (emac_tx_tso(dev, skb, &ctrl)) { + if (emac_sw_tso(skb, ndev)) + goto stop_queue; + } + slot = dev->tx_slot; /* skb data */ @@ -2946,6 +3048,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; @@ -3113,6 +3218,8 @@ static int emac_probe(struct platform_device *ofdev) } dev->rx_skb_size = emac_rx_skb_size(ndev->mtu); dev->rx_sync_size = emac_rx_sync_size(ndev->mtu); + ndev->gso_max_segs = NUM_TX_BUFF / 2; + emac_find_tso_ss_for_mtu(dev); /* Get pointers to BD rings */ dev->tx_desc = @@ -3167,6 +3274,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..0bcfe952a3cf 100644 --- a/drivers/net/ethernet/ibm/emac/core.h +++ b/drivers/net/ethernet/ibm/emac/core.h @@ -245,6 +245,9 @@ struct emac_instance { u32 xaht_slots_shift; u32 xaht_width_shift; + /* TAH TSO start index */ + int tso_ss_mtu_start; + /* Descriptor management */ struct mal_descriptor *tx_desc; @@ -336,6 +339,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 +357,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..619c08ee22f7 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] & 0x3ffc) << 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) @@ -118,7 +138,7 @@ static int tah_probe(struct platform_device *ofdev) platform_set_drvdata(ofdev, dev); - /* Initialize TAH and enable IPv4 checksum verification, no TSO yet */ + /* Initialize TAH and enable IPv4 checksum verification */ tah_reset(ofdev); printk(KERN_INFO "TAH %pOF initialized\n", ofdev->dev.of_node); 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 {