root/drivers/infiniband/hw/hfi1/ipoib_rx.c
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright(c) 2020 Intel Corporation.
 *
 */

#include "netdev.h"
#include "ipoib.h"

#define HFI1_IPOIB_SKB_PAD ((NET_SKB_PAD) + (NET_IP_ALIGN))

static void copy_ipoib_buf(struct sk_buff *skb, void *data, int size)
{
        skb_checksum_none_assert(skb);
        skb->protocol = *((__be16 *)data);

        skb_put_data(skb, data, size);
        skb->mac_header = HFI1_IPOIB_PSEUDO_LEN;
        skb_pull(skb, HFI1_IPOIB_ENCAP_LEN);
}

static struct sk_buff *prepare_frag_skb(struct napi_struct *napi, int size)
{
        struct sk_buff *skb;
        int skb_size = SKB_DATA_ALIGN(size + HFI1_IPOIB_SKB_PAD);
        void *frag;

        skb_size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
        skb_size = SKB_DATA_ALIGN(skb_size);
        frag = napi_alloc_frag(skb_size);

        if (unlikely(!frag))
                return napi_alloc_skb(napi, size);

        skb = build_skb(frag, skb_size);

        if (unlikely(!skb)) {
                skb_free_frag(frag);
                return NULL;
        }

        skb_reserve(skb, HFI1_IPOIB_SKB_PAD);
        return skb;
}

struct sk_buff *hfi1_ipoib_prepare_skb(struct hfi1_netdev_rxq *rxq,
                                       int size, void *data)
{
        struct napi_struct *napi = &rxq->napi;
        int skb_size = size + HFI1_IPOIB_ENCAP_LEN;
        struct sk_buff *skb;

        /*
         * For smaller(4k + skb overhead) allocations we will go using
         * napi cache. Otherwise we will try to use napi frag cache.
         */
        if (size <= SKB_WITH_OVERHEAD(PAGE_SIZE))
                skb = napi_alloc_skb(napi, skb_size);
        else
                skb = prepare_frag_skb(napi, skb_size);

        if (unlikely(!skb))
                return NULL;

        copy_ipoib_buf(skb, data, size);

        return skb;
}

int hfi1_ipoib_rxq_init(struct net_device *netdev)
{
        struct hfi1_ipoib_dev_priv *ipoib_priv = hfi1_ipoib_priv(netdev);
        struct hfi1_devdata *dd = ipoib_priv->dd;
        int ret;

        ret = hfi1_netdev_rx_init(dd);
        if (ret)
                return ret;

        hfi1_init_aip_rsm(dd);

        return ret;
}

void hfi1_ipoib_rxq_deinit(struct net_device *netdev)
{
        struct hfi1_ipoib_dev_priv *ipoib_priv = hfi1_ipoib_priv(netdev);
        struct hfi1_devdata *dd = ipoib_priv->dd;

        hfi1_deinit_aip_rsm(dd);
        hfi1_netdev_rx_destroy(dd);
}