Current driver dynamically allocates an skb and maps it as DMA rx buffer.
A following patch introduces AF_XDP functionality, so we need a
different allocation scheme. Buffers are allocated dynamically and
mapped into hardware. During the Rx operation the driver uses
build_skb() to produce the necessary buffers for the network stack

Signed-off-by: Ilias Apalodimas <ilias.apalodi...@linaro.org>
---
 drivers/net/ethernet/socionext/netsec.c | 239 +++++++++++++++++---------------
 1 file changed, 130 insertions(+), 109 deletions(-)

diff --git a/drivers/net/ethernet/socionext/netsec.c 
b/drivers/net/ethernet/socionext/netsec.c
index 7aa5ebb..666fee2 100644
--- a/drivers/net/ethernet/socionext/netsec.c
+++ b/drivers/net/ethernet/socionext/netsec.c
@@ -296,6 +296,11 @@ struct netsec_rx_pkt_info {
        bool err_flag;
 };
 
+static void netsec_rx_fill(struct netsec_priv *priv, u16 from, u16 num);
+
+static void *netsec_alloc_rx_data(struct netsec_priv *priv,
+                                 dma_addr_t *dma_addr, u16 *len);
+
 static void netsec_write(struct netsec_priv *priv, u32 reg_addr, u32 val)
 {
        writel(val, priv->ioaddr + reg_addr);
@@ -556,34 +561,10 @@ static const struct ethtool_ops netsec_ethtool_ops = {
 
 /************* NETDEV_OPS FOLLOW *************/
 
-static struct sk_buff *netsec_alloc_skb(struct netsec_priv *priv,
-                                       struct netsec_desc *desc)
-{
-       struct sk_buff *skb;
-
-       if (device_get_dma_attr(priv->dev) == DEV_DMA_COHERENT) {
-               skb = netdev_alloc_skb_ip_align(priv->ndev, desc->len);
-       } else {
-               desc->len = L1_CACHE_ALIGN(desc->len);
-               skb = netdev_alloc_skb(priv->ndev, desc->len);
-       }
-       if (!skb)
-               return NULL;
-
-       desc->addr = skb->data;
-       desc->dma_addr = dma_map_single(priv->dev, desc->addr, desc->len,
-                                       DMA_FROM_DEVICE);
-       if (dma_mapping_error(priv->dev, desc->dma_addr)) {
-               dev_kfree_skb_any(skb);
-               return NULL;
-       }
-       return skb;
-}
 
 static void netsec_set_rx_de(struct netsec_priv *priv,
                             struct netsec_desc_ring *dring, u16 idx,
-                            const struct netsec_desc *desc,
-                            struct sk_buff *skb)
+                            const struct netsec_desc *desc)
 {
        struct netsec_de *de = dring->vaddr + DESC_SZ * idx;
        u32 attr = (1 << NETSEC_RX_PKT_OWN_FIELD) |
@@ -602,59 +583,6 @@ static void netsec_set_rx_de(struct netsec_priv *priv,
        dring->desc[idx].dma_addr = desc->dma_addr;
        dring->desc[idx].addr = desc->addr;
        dring->desc[idx].len = desc->len;
-       dring->desc[idx].skb = skb;
-}
-
-static struct sk_buff *netsec_get_rx_de(struct netsec_priv *priv,
-                                       struct netsec_desc_ring *dring,
-                                       u16 idx,
-                                       struct netsec_rx_pkt_info *rxpi,
-                                       struct netsec_desc *desc, u16 *len)
-{
-       struct netsec_de de = {};
-
-       memcpy(&de, dring->vaddr + DESC_SZ * idx, DESC_SZ);
-
-       *len = de.buf_len_info >> 16;
-
-       rxpi->err_flag = (de.attr >> NETSEC_RX_PKT_ER_FIELD) & 1;
-       rxpi->rx_cksum_result = (de.attr >> NETSEC_RX_PKT_CO_FIELD) & 3;
-       rxpi->err_code = (de.attr >> NETSEC_RX_PKT_ERR_FIELD) &
-                                                       NETSEC_RX_PKT_ERR_MASK;
-       *desc = dring->desc[idx];
-       return desc->skb;
-}
-
-static struct sk_buff *netsec_get_rx_pkt_data(struct netsec_priv *priv,
-                                             struct netsec_rx_pkt_info *rxpi,
-                                             struct netsec_desc *desc,
-                                             u16 *len)
-{
-       struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_RX];
-       struct sk_buff *tmp_skb, *skb = NULL;
-       struct netsec_desc td;
-       int tail;
-
-       *rxpi = (struct netsec_rx_pkt_info){};
-
-       td.len = priv->ndev->mtu + 22;
-
-       tmp_skb = netsec_alloc_skb(priv, &td);
-
-       tail = dring->tail;
-
-       if (!tmp_skb) {
-               netsec_set_rx_de(priv, dring, tail, &dring->desc[tail],
-                                dring->desc[tail].skb);
-       } else {
-               skb = netsec_get_rx_de(priv, dring, tail, rxpi, desc, len);
-               netsec_set_rx_de(priv, dring, tail, &td, tmp_skb);
-       }
-
-       /* move tail ahead */
-       dring->tail = (dring->tail + 1) % DESC_NUM;
-
-       return skb;
 }
 
 static int netsec_clean_tx_dring(struct netsec_priv *priv, int budget)
@@ -721,19 +649,29 @@ static int netsec_process_tx(struct netsec_priv *priv, 
int budget)
        return done;
 }
 
+static void nsetsec_adv_desc(u16 *idx)
+{
+       *idx = *idx + 1;
+       if (unlikely(*idx >= DESC_NUM))
+               *idx = 0;
+}
+
 static int netsec_process_rx(struct netsec_priv *priv, int budget)
 {
        struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_RX];
        struct net_device *ndev = priv->ndev;
-       struct netsec_rx_pkt_info rx_info;
-       int done = 0;
-       struct netsec_desc desc;
        struct sk_buff *skb;
-       u16 len;
+       int done = 0;
 
        while (done < budget) {
                u16 idx = dring->tail;
                struct netsec_de *de = dring->vaddr + (DESC_SZ * idx);
+               struct netsec_desc *desc = &dring->desc[idx];
+               struct netsec_rx_pkt_info rpi;
+               dma_addr_t dma_handle;
+               void *buf_addr;
+               u16 pkt_len;
+               u16 desc_len;
 
                if (de->attr & (1U << NETSEC_RX_PKT_OWN_FIELD))
                        break;
@@ -744,28 +682,62 @@ static int netsec_process_rx(struct netsec_priv *priv, 
int budget)
                 */
                dma_rmb();
                done++;
-               skb = netsec_get_rx_pkt_data(priv, &rx_info, &desc, &len);
-               if (unlikely(!skb) || rx_info.err_flag) {
+
+               pkt_len = de->buf_len_info >> 16;
+               rpi.err_code = (de->attr >> NETSEC_RX_PKT_ERR_FIELD) &
+                       NETSEC_RX_PKT_ERR_MASK;
+               rpi.err_flag = (de->attr >> NETSEC_RX_PKT_ER_FIELD) & 1;
+               if (rpi.err_flag) {
                        netif_err(priv, drv, priv->ndev,
-                                 "%s: rx fail err(%d)\n",
-                                 __func__, rx_info.err_code);
+                                 "%s: rx fail err(%d)\n", __func__,
+                                 rpi.err_code);
                        ndev->stats.rx_dropped++;
+                       nsetsec_adv_desc(&dring->tail);
+                       /* reuse buffer page frag */
+                       netsec_rx_fill(priv, idx, 1);
                        continue;
                }
+               rpi.rx_cksum_result = (de->attr >> NETSEC_RX_PKT_CO_FIELD) & 3;
 
-               dma_unmap_single(priv->dev, desc.dma_addr, desc.len,
-                                DMA_FROM_DEVICE);
-               skb_put(skb, len);
+               dma_sync_single_for_cpu(priv->dev, desc->dma_addr, pkt_len,
+                                       DMA_FROM_DEVICE);
+
+               prefetch(desc->addr);
+               buf_addr = netsec_alloc_rx_data(priv, &dma_handle, &desc_len);
+               if (unlikely(!buf_addr))
+                       break;
+
+               skb = build_skb(desc->addr, desc->len);
+               if (unlikely(!skb)) {
+                       dma_unmap_single(priv->dev, dma_handle, desc_len,
+                                        DMA_TO_DEVICE);
+                       skb_free_frag(buf_addr);
+                       netif_err(priv, drv, priv->ndev,
+                                 "rx failed to alloc skb\n");
+                       break;
+               }
+               dma_unmap_single_attrs(priv->dev, desc->dma_addr, desc->len,
+                                      DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
+
+               /* Update the descriptor with fresh buffers */
+               desc->len = desc_len;
+               desc->dma_addr = dma_handle;
+               desc->addr = buf_addr;
+
+               skb_put(skb, pkt_len);
                skb->protocol = eth_type_trans(skb, priv->ndev);
 
                if (priv->rx_cksum_offload_flag &&
-                   rx_info.rx_cksum_result == NETSEC_RX_CKSUM_OK)
+                   rpi.rx_cksum_result == NETSEC_RX_CKSUM_OK)
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
 
                if (napi_gro_receive(&priv->napi, skb) != GRO_DROP) {
                        ndev->stats.rx_packets++;
-                       ndev->stats.rx_bytes += len;
+                       ndev->stats.rx_bytes += pkt_len;
                }
+
+               netsec_rx_fill(priv, idx, 1);
+               nsetsec_adv_desc(&dring->tail);
        }
 
        return done;
@@ -928,7 +900,10 @@ static void netsec_uninit_pkt_dring(struct netsec_priv 
*priv, int id)
                dma_unmap_single(priv->dev, desc->dma_addr, desc->len,
                                 id == NETSEC_RING_RX ? DMA_FROM_DEVICE :
                                                              DMA_TO_DEVICE);
-               dev_kfree_skb(desc->skb);
+               if (id == NETSEC_RING_RX)
+                       skb_free_frag(desc->addr);
+               else if (id == NETSEC_RING_TX)
+                       dev_kfree_skb(desc->skb);
        }
 
        memset(dring->desc, 0, sizeof(struct netsec_desc) * DESC_NUM);
@@ -953,50 +928,96 @@ static void netsec_free_dring(struct netsec_priv *priv, 
int id)
        dring->desc = NULL;
 }
 
+static void *netsec_alloc_rx_data(struct netsec_priv *priv,
+                                 dma_addr_t *dma_handle, u16 *desc_len)
+{
+       size_t len = priv->ndev->mtu + ETH_HLEN + VLAN_HLEN * 2 + NET_SKB_PAD +
+               NET_IP_ALIGN;
+       dma_addr_t mapping;
+       void *buf;
+
+       len = SKB_DATA_ALIGN(len);
+       len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+       buf = napi_alloc_frag(len);
+       if (!buf)
+               return NULL;
+
+       mapping = dma_map_single(priv->dev, buf, len, DMA_FROM_DEVICE);
+       if (unlikely(dma_mapping_error(priv->dev, mapping)))
+               goto err_out;
+
+       *dma_handle = mapping;
+       *desc_len = len;
+
+       return buf;
+
+err_out:
+       skb_free_frag(buf);
+       return NULL;
+}
+
+static void netsec_rx_fill(struct netsec_priv *priv, u16 from, u16 num)
+{
+       struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_RX];
+       u16 idx = from;
+
+       while (num) {
+               netsec_set_rx_de(priv, dring, idx, &dring->desc[idx]);
+               idx++;
+               if (idx >= DESC_NUM)
+                       idx = 0;
+               num--;
+       }
+}
+
 static int netsec_alloc_dring(struct netsec_priv *priv, enum ring_id id)
 {
        struct netsec_desc_ring *dring = &priv->desc_ring[id];
-       int ret = 0;
 
        dring->vaddr = dma_zalloc_coherent(priv->dev, DESC_SZ * DESC_NUM,
                                           &dring->desc_dma, GFP_KERNEL);
-       if (!dring->vaddr) {
-               ret = -ENOMEM;
+       if (!dring->vaddr)
                goto err;
-       }
 
        dring->desc = kcalloc(DESC_NUM, sizeof(*dring->desc), GFP_KERNEL);
-       if (!dring->desc) {
-               ret = -ENOMEM;
+       if (!dring->desc)
                goto err;
-       }
 
        return 0;
 err:
        netsec_free_dring(priv, id);
 
-       return ret;
+       return -ENOMEM;
 }
 
 static int netsec_setup_rx_dring(struct netsec_priv *priv)
 {
        struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_RX];
-       struct netsec_desc desc;
-       struct sk_buff *skb;
-       int n;
+       int i;
 
-       desc.len = priv->ndev->mtu + 22;
+       for (i = 0; i < DESC_NUM; i++) {
+               struct netsec_desc *desc = &dring->desc[i];
+               dma_addr_t dma_handle;
+               void *buf;
+               u16 len;
 
-       for (n = 0; n < DESC_NUM; n++) {
-               skb = netsec_alloc_skb(priv, &desc);
-               if (!skb) {
+               buf = netsec_alloc_rx_data(priv, &dma_handle, &len);
+               if (!buf) {
                        netsec_uninit_pkt_dring(priv, NETSEC_RING_RX);
-                       return -ENOMEM;
+                       goto err_out;
                }
-               netsec_set_rx_de(priv, dring, n, &desc, skb);
+               desc->dma_addr = dma_handle;
+               desc->addr = buf;
+               desc->len = len;
        }
 
+       netsec_rx_fill(priv, 0, DESC_NUM);
+
        return 0;
+
+err_out:
+       return -ENOMEM;
 }
 
 static int netsec_netdev_load_ucode_region(struct netsec_priv *priv, u32 reg,
-- 
2.7.4

Reply via email to