root/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c
// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU Ethernet driver
 *
 * Copyright (C) 2020 Marvell.
 *
 */

#include <net/ipv6.h>
#include <linux/sort.h>

#include "otx2_common.h"

#define OTX2_DEFAULT_ACTION     0x1

struct otx2_flow {
        struct ethtool_rx_flow_spec flow_spec;
        struct list_head list;
        u32 location;
        u32 entry;
        bool is_vf;
        u8 rss_ctx_id;
#define DMAC_FILTER_RULE                BIT(0)
#define PFC_FLOWCTRL_RULE               BIT(1)
        u16 rule_type;
        int vf;
};

enum dmac_req {
        DMAC_ADDR_UPDATE,
        DMAC_ADDR_DEL
};

static void otx2_clear_ntuple_flow_info(struct otx2_nic *pfvf, struct otx2_flow_config *flow_cfg)
{
        devm_kfree(pfvf->dev, flow_cfg->flow_ent);
        flow_cfg->flow_ent = NULL;
        flow_cfg->max_flows = 0;
}

static int otx2_free_ntuple_mcam_entries(struct otx2_nic *pfvf)
{
        struct otx2_flow_config *flow_cfg = pfvf->flow_cfg;
        struct npc_mcam_free_entry_req *req;
        int ent, err;

        if (!flow_cfg->max_flows)
                return 0;

        mutex_lock(&pfvf->mbox.lock);
        for (ent = 0; ent < flow_cfg->max_flows; ent++) {
                req = otx2_mbox_alloc_msg_npc_mcam_free_entry(&pfvf->mbox);
                if (!req)
                        break;

                req->entry = flow_cfg->flow_ent[ent];

                /* Send message to AF to free MCAM entries */
                err = otx2_sync_mbox_msg(&pfvf->mbox);
                if (err)
                        break;
        }
        mutex_unlock(&pfvf->mbox.lock);
        otx2_clear_ntuple_flow_info(pfvf, flow_cfg);
        return 0;
}

int otx2_alloc_mcam_entries(struct otx2_nic *pfvf, u16 count)
{
        struct otx2_flow_config *flow_cfg = pfvf->flow_cfg;
        struct npc_mcam_alloc_entry_req *req;
        struct npc_mcam_alloc_entry_rsp *rsp;
        int ent, allocated = 0;

        /* Free current ones and allocate new ones with requested count */
        otx2_free_ntuple_mcam_entries(pfvf);

        if (!count)
                return 0;

        flow_cfg->flow_ent = devm_kmalloc_array(pfvf->dev, count,
                                                sizeof(u16), GFP_KERNEL);
        if (!flow_cfg->flow_ent) {
                netdev_err(pfvf->netdev,
                           "%s: Unable to allocate memory for flow entries\n",
                            __func__);
                return -ENOMEM;
        }

        mutex_lock(&pfvf->mbox.lock);

        /* In a single request a max of NPC_MAX_NONCONTIG_ENTRIES MCAM entries
         * can only be allocated.
         */
        while (allocated < count) {
                req = otx2_mbox_alloc_msg_npc_mcam_alloc_entry(&pfvf->mbox);
                if (!req)
                        goto exit;

                req->contig = false;
                req->count = (count - allocated) > NPC_MAX_NONCONTIG_ENTRIES ?
                                NPC_MAX_NONCONTIG_ENTRIES : count - allocated;

                /* Allocate higher priority entries for PFs, so that VF's entries
                 * will be on top of PF.
                 */
                if (!is_otx2_vf(pfvf->pcifunc)) {
                        req->priority = NPC_MCAM_HIGHER_PRIO;
                        req->ref_entry = flow_cfg->def_ent[0];
                }

                /* Send message to AF */
                if (otx2_sync_mbox_msg(&pfvf->mbox))
                        goto exit;

                rsp = (struct npc_mcam_alloc_entry_rsp *)otx2_mbox_get_rsp
                        (&pfvf->mbox.mbox, 0, &req->hdr);
                if (IS_ERR(rsp))
                        goto exit;

                for (ent = 0; ent < rsp->count; ent++)
                        flow_cfg->flow_ent[ent + allocated] = rsp->entry_list[ent];

                allocated += rsp->count;

                /* If this request is not fulfilled, no need to send
                 * further requests.
                 */
                if (rsp->count != req->count)
                        break;
        }

        /* Multiple MCAM entry alloc requests could result in non-sequential
         * MCAM entries in the flow_ent[] array. Sort them in an ascending order,
         * otherwise user installed ntuple filter index and MCAM entry index will
         * not be in sync.
         */
        if (allocated)
                sort(&flow_cfg->flow_ent[0], allocated,
                     sizeof(flow_cfg->flow_ent[0]), mcam_entry_cmp, NULL);

exit:
        mutex_unlock(&pfvf->mbox.lock);

        flow_cfg->max_flows = allocated;

        if (allocated) {
                pfvf->flags |= OTX2_FLAG_MCAM_ENTRIES_ALLOC;
                pfvf->flags |= OTX2_FLAG_NTUPLE_SUPPORT;
        }

        if (allocated != count)
                netdev_info(pfvf->netdev,
                            "Unable to allocate %d MCAM entries, got only %d\n",
                            count, allocated);
        return allocated;
}
EXPORT_SYMBOL(otx2_alloc_mcam_entries);

int otx2_mcam_entry_init(struct otx2_nic *pfvf)
{
        struct otx2_flow_config *flow_cfg = pfvf->flow_cfg;
        struct npc_get_field_status_req *freq;
        struct npc_get_field_status_rsp *frsp;
        struct npc_mcam_alloc_entry_req *req;
        struct npc_mcam_alloc_entry_rsp *rsp;
        int vf_vlan_max_flows;
        int ent, count;

        vf_vlan_max_flows = pfvf->total_vfs * OTX2_PER_VF_VLAN_FLOWS;
        count = flow_cfg->ucast_flt_cnt +
                        OTX2_MAX_VLAN_FLOWS + vf_vlan_max_flows;

        flow_cfg->def_ent = devm_kmalloc_array(pfvf->dev, count,
                                               sizeof(u16), GFP_KERNEL);
        if (!flow_cfg->def_ent)
                return -ENOMEM;

        mutex_lock(&pfvf->mbox.lock);

        req = otx2_mbox_alloc_msg_npc_mcam_alloc_entry(&pfvf->mbox);
        if (!req) {
                mutex_unlock(&pfvf->mbox.lock);
                return -ENOMEM;
        }

        req->contig = false;
        req->count = count;

        /* Send message to AF */
        if (otx2_sync_mbox_msg(&pfvf->mbox)) {
                mutex_unlock(&pfvf->mbox.lock);
                return -EINVAL;
        }

        rsp = (struct npc_mcam_alloc_entry_rsp *)otx2_mbox_get_rsp
               (&pfvf->mbox.mbox, 0, &req->hdr);
        if (IS_ERR(rsp)) {
                mutex_unlock(&pfvf->mbox.lock);
                return PTR_ERR(rsp);
        }

        if (rsp->count != req->count) {
                netdev_info(pfvf->netdev,
                            "Unable to allocate MCAM entries for ucast, vlan and vf_vlan\n");
                mutex_unlock(&pfvf->mbox.lock);
                devm_kfree(pfvf->dev, flow_cfg->def_ent);
                return 0;
        }

        for (ent = 0; ent < rsp->count; ent++)
                flow_cfg->def_ent[ent] = rsp->entry_list[ent];

        flow_cfg->vf_vlan_offset = 0;
        flow_cfg->unicast_offset = vf_vlan_max_flows;
        flow_cfg->rx_vlan_offset = flow_cfg->unicast_offset +
                                        flow_cfg->ucast_flt_cnt;
        pfvf->flags |= OTX2_FLAG_UCAST_FLTR_SUPPORT;

        /* Check if NPC_DMAC field is supported
         * by the mkex profile before setting VLAN support flag.
         */
        freq = otx2_mbox_alloc_msg_npc_get_field_status(&pfvf->mbox);
        if (!freq) {
                mutex_unlock(&pfvf->mbox.lock);
                return -ENOMEM;
        }

        freq->field = NPC_DMAC;
        if (otx2_sync_mbox_msg(&pfvf->mbox)) {
                mutex_unlock(&pfvf->mbox.lock);
                return -EINVAL;
        }

        frsp = (struct npc_get_field_status_rsp *)otx2_mbox_get_rsp
               (&pfvf->mbox.mbox, 0, &freq->hdr);
        if (IS_ERR(frsp)) {
                mutex_unlock(&pfvf->mbox.lock);
                return PTR_ERR(frsp);
        }

        if (frsp->enable) {
                pfvf->flags |= OTX2_FLAG_RX_VLAN_SUPPORT;
                pfvf->flags |= OTX2_FLAG_VF_VLAN_SUPPORT;
        }

        pfvf->flags |= OTX2_FLAG_MCAM_ENTRIES_ALLOC;
        mutex_unlock(&pfvf->mbox.lock);

        /* Allocate entries for Ntuple filters */
        count = otx2_alloc_mcam_entries(pfvf, flow_cfg->ntuple_cnt);
        if (count <= 0) {
                otx2_clear_ntuple_flow_info(pfvf, flow_cfg);
                return 0;
        }

        pfvf->flags |= OTX2_FLAG_TC_FLOWER_SUPPORT;

        refcount_set(&flow_cfg->mark_flows, 1);
        return 0;
}
EXPORT_SYMBOL(otx2_mcam_entry_init);

/* TODO : revisit on size */
#define OTX2_DMAC_FLTR_BITMAP_SZ (4 * 2048 + 32)

int otx2vf_mcam_flow_init(struct otx2_nic *pfvf)
{
        struct otx2_flow_config *flow_cfg;

        pfvf->flow_cfg = devm_kzalloc(pfvf->dev,
                                      sizeof(struct otx2_flow_config),
                                      GFP_KERNEL);
        if (!pfvf->flow_cfg)
                return -ENOMEM;

        pfvf->flow_cfg->dmacflt_bmap = devm_kcalloc(pfvf->dev,
                                                    BITS_TO_LONGS(OTX2_DMAC_FLTR_BITMAP_SZ),
                                                    sizeof(long), GFP_KERNEL);
        if (!pfvf->flow_cfg->dmacflt_bmap)
                return -ENOMEM;

        flow_cfg = pfvf->flow_cfg;
        INIT_LIST_HEAD(&flow_cfg->flow_list);
        INIT_LIST_HEAD(&flow_cfg->flow_list_tc);
        flow_cfg->max_flows = 0;

        return 0;
}
EXPORT_SYMBOL(otx2vf_mcam_flow_init);

int otx2_mcam_flow_init(struct otx2_nic *pf)
{
        int err;

        pf->flow_cfg = devm_kzalloc(pf->dev, sizeof(struct otx2_flow_config),
                                    GFP_KERNEL);
        if (!pf->flow_cfg)
                return -ENOMEM;

        pf->flow_cfg->dmacflt_bmap = devm_kcalloc(pf->dev,
                                                  BITS_TO_LONGS(OTX2_DMAC_FLTR_BITMAP_SZ),
                                                  sizeof(long), GFP_KERNEL);
        if (!pf->flow_cfg->dmacflt_bmap)
                return -ENOMEM;

        INIT_LIST_HEAD(&pf->flow_cfg->flow_list);
        INIT_LIST_HEAD(&pf->flow_cfg->flow_list_tc);

        pf->flow_cfg->ucast_flt_cnt = OTX2_DEFAULT_UNICAST_FLOWS;
        pf->flow_cfg->ntuple_cnt = OTX2_DEFAULT_FLOWCOUNT;

        /* Allocate bare minimum number of MCAM entries needed for
         * unicast and ntuple filters.
         */
        err = otx2_mcam_entry_init(pf);
        if (err)
                return err;

        /* Check if MCAM entries are allocate or not */
        if (!(pf->flags & OTX2_FLAG_UCAST_FLTR_SUPPORT))
                return 0;

        pf->mac_table = devm_kzalloc(pf->dev, sizeof(struct otx2_mac_table)
                                        * pf->flow_cfg->ucast_flt_cnt, GFP_KERNEL);
        if (!pf->mac_table)
                return -ENOMEM;

        otx2_dmacflt_get_max_cnt(pf);

        /* DMAC filters are not allocated */
        if (!pf->flow_cfg->dmacflt_max_flows)
                return 0;

        pf->flow_cfg->bmap_to_dmacindex =
                        devm_kzalloc(pf->dev, sizeof(u32) *
                                     pf->flow_cfg->dmacflt_max_flows,
                                     GFP_KERNEL);

        if (!pf->flow_cfg->bmap_to_dmacindex)
                return -ENOMEM;

        pf->flags |= OTX2_FLAG_DMACFLTR_SUPPORT;

        return 0;
}

void otx2_mcam_flow_del(struct otx2_nic *pf)
{
        otx2_destroy_mcam_flows(pf);
}
EXPORT_SYMBOL(otx2_mcam_flow_del);

/*  On success adds mcam entry
 *  On failure enable promisous mode
 */
static int otx2_do_add_macfilter(struct otx2_nic *pf, const u8 *mac)
{
        struct otx2_flow_config *flow_cfg = pf->flow_cfg;
        struct npc_install_flow_req *req;
        int err, i;

        if (!(pf->flags & OTX2_FLAG_UCAST_FLTR_SUPPORT))
                return -ENOMEM;

        /* dont have free mcam entries or uc list is greater than alloted */
        if (netdev_uc_count(pf->netdev) > pf->flow_cfg->ucast_flt_cnt)
                return -ENOMEM;

        mutex_lock(&pf->mbox.lock);
        req = otx2_mbox_alloc_msg_npc_install_flow(&pf->mbox);
        if (!req) {
                mutex_unlock(&pf->mbox.lock);
                return -ENOMEM;
        }

        /* unicast offset starts with 32 0..31 for ntuple */
        for (i = 0; i <  pf->flow_cfg->ucast_flt_cnt; i++) {
                if (pf->mac_table[i].inuse)
                        continue;
                ether_addr_copy(pf->mac_table[i].addr, mac);
                pf->mac_table[i].inuse = true;
                pf->mac_table[i].mcam_entry =
                        flow_cfg->def_ent[i + flow_cfg->unicast_offset];
                req->entry =  pf->mac_table[i].mcam_entry;
                break;
        }

        ether_addr_copy(req->packet.dmac, mac);
        eth_broadcast_addr((u8 *)&req->mask.dmac);
        req->features = BIT_ULL(NPC_DMAC);
        req->channel = pf->hw.rx_chan_base;
        req->intf = NIX_INTF_RX;
        req->op = NIX_RX_ACTION_DEFAULT;
        req->set_cntr = 1;

        err = otx2_sync_mbox_msg(&pf->mbox);
        mutex_unlock(&pf->mbox.lock);

        return err;
}

int otx2_add_macfilter(struct net_device *netdev, const u8 *mac)
{
        struct otx2_nic *pf = netdev_priv(netdev);

        if (!bitmap_empty(pf->flow_cfg->dmacflt_bmap,
                          pf->flow_cfg->dmacflt_max_flows))
                netdev_warn(netdev,
                            "Add %pM to CGX/RPM DMAC filters list as well\n",
                            mac);

        return otx2_do_add_macfilter(pf, mac);
}

static bool otx2_get_mcamentry_for_mac(struct otx2_nic *pf, const u8 *mac,
                                       int *mcam_entry)
{
        int i;

        for (i = 0; i < pf->flow_cfg->ucast_flt_cnt; i++) {
                if (!pf->mac_table[i].inuse)
                        continue;

                if (ether_addr_equal(pf->mac_table[i].addr, mac)) {
                        *mcam_entry = pf->mac_table[i].mcam_entry;
                        pf->mac_table[i].inuse = false;
                        return true;
                }
        }
        return false;
}

int otx2_del_macfilter(struct net_device *netdev, const u8 *mac)
{
        struct otx2_nic *pf = netdev_priv(netdev);
        struct npc_delete_flow_req *req;
        int err, mcam_entry;

        /* check does mcam entry exists for given mac */
        if (!otx2_get_mcamentry_for_mac(pf, mac, &mcam_entry))
                return 0;

        mutex_lock(&pf->mbox.lock);
        req = otx2_mbox_alloc_msg_npc_delete_flow(&pf->mbox);
        if (!req) {
                mutex_unlock(&pf->mbox.lock);
                return -ENOMEM;
        }
        req->entry = mcam_entry;
        /* Send message to AF */
        err = otx2_sync_mbox_msg(&pf->mbox);
        mutex_unlock(&pf->mbox.lock);

        return err;
}

static struct otx2_flow *otx2_find_flow(struct otx2_nic *pfvf, u32 location)
{
        struct otx2_flow *iter;

        list_for_each_entry(iter, &pfvf->flow_cfg->flow_list, list) {
                if (iter->location == location)
                        return iter;
        }

        return NULL;
}

static void otx2_add_flow_to_list(struct otx2_nic *pfvf, struct otx2_flow *flow)
{
        struct list_head *head = &pfvf->flow_cfg->flow_list;
        struct otx2_flow *iter;

        list_for_each_entry(iter, &pfvf->flow_cfg->flow_list, list) {
                if (iter->location > flow->location)
                        break;
                head = &iter->list;
        }

        list_add(&flow->list, head);
}

int otx2_get_maxflows(struct otx2_flow_config *flow_cfg)
{
        if (!flow_cfg)
                return 0;

        if (flow_cfg->nr_flows == flow_cfg->max_flows ||
            !bitmap_empty(flow_cfg->dmacflt_bmap,
                          flow_cfg->dmacflt_max_flows))
                return flow_cfg->max_flows + flow_cfg->dmacflt_max_flows;
        else
                return flow_cfg->max_flows;
}
EXPORT_SYMBOL(otx2_get_maxflows);

int otx2_get_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc,
                  u32 location)
{
        struct otx2_flow *iter;

        if (location >= otx2_get_maxflows(pfvf->flow_cfg))
                return -EINVAL;

        list_for_each_entry(iter, &pfvf->flow_cfg->flow_list, list) {
                if (iter->location == location) {
                        nfc->fs = iter->flow_spec;
                        nfc->rss_context = iter->rss_ctx_id;
                        return 0;
                }
        }

        return -ENOENT;
}

int otx2_get_all_flows(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc,
                       u32 *rule_locs)
{
        u32 rule_cnt = nfc->rule_cnt;
        u32 location = 0;
        int idx = 0;
        int err = 0;

        nfc->data = otx2_get_maxflows(pfvf->flow_cfg);
        while ((!err || err == -ENOENT) && idx < rule_cnt) {
                err = otx2_get_flow(pfvf, nfc, location);
                if (!err)
                        rule_locs[idx++] = location;
                location++;
        }
        nfc->rule_cnt = rule_cnt;

        return err;
}

static int otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp,
                                  struct npc_install_flow_req *req,
                                  u32 flow_type)
{
        struct ethtool_usrip4_spec *ipv4_usr_mask = &fsp->m_u.usr_ip4_spec;
        struct ethtool_usrip4_spec *ipv4_usr_hdr = &fsp->h_u.usr_ip4_spec;
        struct ethtool_tcpip4_spec *ipv4_l4_mask = &fsp->m_u.tcp_ip4_spec;
        struct ethtool_tcpip4_spec *ipv4_l4_hdr = &fsp->h_u.tcp_ip4_spec;
        struct ethtool_ah_espip4_spec *ah_esp_hdr = &fsp->h_u.ah_ip4_spec;
        struct ethtool_ah_espip4_spec *ah_esp_mask = &fsp->m_u.ah_ip4_spec;
        struct flow_msg *pmask = &req->mask;
        struct flow_msg *pkt = &req->packet;

        switch (flow_type) {
        case IP_USER_FLOW:
                if (ipv4_usr_mask->ip4src) {
                        memcpy(&pkt->ip4src, &ipv4_usr_hdr->ip4src,
                               sizeof(pkt->ip4src));
                        memcpy(&pmask->ip4src, &ipv4_usr_mask->ip4src,
                               sizeof(pmask->ip4src));
                        req->features |= BIT_ULL(NPC_SIP_IPV4);
                }
                if (ipv4_usr_mask->ip4dst) {
                        memcpy(&pkt->ip4dst, &ipv4_usr_hdr->ip4dst,
                               sizeof(pkt->ip4dst));
                        memcpy(&pmask->ip4dst, &ipv4_usr_mask->ip4dst,
                               sizeof(pmask->ip4dst));
                        req->features |= BIT_ULL(NPC_DIP_IPV4);
                }
                if (ipv4_usr_mask->tos) {
                        pkt->tos = ipv4_usr_hdr->tos;
                        pmask->tos = ipv4_usr_mask->tos;
                        req->features |= BIT_ULL(NPC_TOS);
                }
                if (ipv4_usr_mask->proto) {
                        switch (ipv4_usr_hdr->proto) {
                        case IPPROTO_ICMP:
                                req->features |= BIT_ULL(NPC_IPPROTO_ICMP);
                                break;
                        case IPPROTO_TCP:
                                req->features |= BIT_ULL(NPC_IPPROTO_TCP);
                                break;
                        case IPPROTO_UDP:
                                req->features |= BIT_ULL(NPC_IPPROTO_UDP);
                                break;
                        case IPPROTO_SCTP:
                                req->features |= BIT_ULL(NPC_IPPROTO_SCTP);
                                break;
                        case IPPROTO_AH:
                                req->features |= BIT_ULL(NPC_IPPROTO_AH);
                                break;
                        case IPPROTO_ESP:
                                req->features |= BIT_ULL(NPC_IPPROTO_ESP);
                                break;
                        default:
                                return -EOPNOTSUPP;
                        }
                }
                pkt->etype = cpu_to_be16(ETH_P_IP);
                pmask->etype = cpu_to_be16(0xFFFF);
                req->features |= BIT_ULL(NPC_ETYPE);
                break;
        case TCP_V4_FLOW:
        case UDP_V4_FLOW:
        case SCTP_V4_FLOW:
                pkt->etype = cpu_to_be16(ETH_P_IP);
                pmask->etype = cpu_to_be16(0xFFFF);
                req->features |= BIT_ULL(NPC_ETYPE);
                if (ipv4_l4_mask->ip4src) {
                        memcpy(&pkt->ip4src, &ipv4_l4_hdr->ip4src,
                               sizeof(pkt->ip4src));
                        memcpy(&pmask->ip4src, &ipv4_l4_mask->ip4src,
                               sizeof(pmask->ip4src));
                        req->features |= BIT_ULL(NPC_SIP_IPV4);
                }
                if (ipv4_l4_mask->ip4dst) {
                        memcpy(&pkt->ip4dst, &ipv4_l4_hdr->ip4dst,
                               sizeof(pkt->ip4dst));
                        memcpy(&pmask->ip4dst, &ipv4_l4_mask->ip4dst,
                               sizeof(pmask->ip4dst));
                        req->features |= BIT_ULL(NPC_DIP_IPV4);
                }
                if (ipv4_l4_mask->tos) {
                        pkt->tos = ipv4_l4_hdr->tos;
                        pmask->tos = ipv4_l4_mask->tos;
                        req->features |= BIT_ULL(NPC_TOS);
                }
                if (ipv4_l4_mask->psrc) {
                        memcpy(&pkt->sport, &ipv4_l4_hdr->psrc,
                               sizeof(pkt->sport));
                        memcpy(&pmask->sport, &ipv4_l4_mask->psrc,
                               sizeof(pmask->sport));
                        if (flow_type == UDP_V4_FLOW)
                                req->features |= BIT_ULL(NPC_SPORT_UDP);
                        else if (flow_type == TCP_V4_FLOW)
                                req->features |= BIT_ULL(NPC_SPORT_TCP);
                        else
                                req->features |= BIT_ULL(NPC_SPORT_SCTP);
                }
                if (ipv4_l4_mask->pdst) {
                        memcpy(&pkt->dport, &ipv4_l4_hdr->pdst,
                               sizeof(pkt->dport));
                        memcpy(&pmask->dport, &ipv4_l4_mask->pdst,
                               sizeof(pmask->dport));
                        if (flow_type == UDP_V4_FLOW)
                                req->features |= BIT_ULL(NPC_DPORT_UDP);
                        else if (flow_type == TCP_V4_FLOW)
                                req->features |= BIT_ULL(NPC_DPORT_TCP);
                        else
                                req->features |= BIT_ULL(NPC_DPORT_SCTP);
                }
                if (flow_type == UDP_V4_FLOW)
                        req->features |= BIT_ULL(NPC_IPPROTO_UDP);
                else if (flow_type == TCP_V4_FLOW)
                        req->features |= BIT_ULL(NPC_IPPROTO_TCP);
                else
                        req->features |= BIT_ULL(NPC_IPPROTO_SCTP);
                break;
        case AH_V4_FLOW:
        case ESP_V4_FLOW:
                pkt->etype = cpu_to_be16(ETH_P_IP);
                pmask->etype = cpu_to_be16(0xFFFF);
                req->features |= BIT_ULL(NPC_ETYPE);
                if (ah_esp_mask->ip4src) {
                        memcpy(&pkt->ip4src, &ah_esp_hdr->ip4src,
                               sizeof(pkt->ip4src));
                        memcpy(&pmask->ip4src, &ah_esp_mask->ip4src,
                               sizeof(pmask->ip4src));
                        req->features |= BIT_ULL(NPC_SIP_IPV4);
                }
                if (ah_esp_mask->ip4dst) {
                        memcpy(&pkt->ip4dst, &ah_esp_hdr->ip4dst,
                               sizeof(pkt->ip4dst));
                        memcpy(&pmask->ip4dst, &ah_esp_mask->ip4dst,
                               sizeof(pmask->ip4dst));
                        req->features |= BIT_ULL(NPC_DIP_IPV4);
                }
                if (ah_esp_mask->tos) {
                        pkt->tos = ah_esp_hdr->tos;
                        pmask->tos = ah_esp_mask->tos;
                        req->features |= BIT_ULL(NPC_TOS);
                }

                /* NPC profile doesn't extract AH/ESP header fields */
                if (ah_esp_mask->spi & ah_esp_hdr->spi)
                        return -EOPNOTSUPP;

                if (flow_type == AH_V4_FLOW)
                        req->features |= BIT_ULL(NPC_IPPROTO_AH);
                else
                        req->features |= BIT_ULL(NPC_IPPROTO_ESP);
                break;
        default:
                break;
        }

        return 0;
}

static int otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp,
                                  struct npc_install_flow_req *req,
                                  u32 flow_type)
{
        struct ethtool_usrip6_spec *ipv6_usr_mask = &fsp->m_u.usr_ip6_spec;
        struct ethtool_usrip6_spec *ipv6_usr_hdr = &fsp->h_u.usr_ip6_spec;
        struct ethtool_tcpip6_spec *ipv6_l4_mask = &fsp->m_u.tcp_ip6_spec;
        struct ethtool_tcpip6_spec *ipv6_l4_hdr = &fsp->h_u.tcp_ip6_spec;
        struct ethtool_ah_espip6_spec *ah_esp_hdr = &fsp->h_u.ah_ip6_spec;
        struct ethtool_ah_espip6_spec *ah_esp_mask = &fsp->m_u.ah_ip6_spec;
        struct flow_msg *pmask = &req->mask;
        struct flow_msg *pkt = &req->packet;

        switch (flow_type) {
        case IPV6_USER_FLOW:
                if (!ipv6_addr_any((struct in6_addr *)ipv6_usr_mask->ip6src)) {
                        memcpy(&pkt->ip6src, &ipv6_usr_hdr->ip6src,
                               sizeof(pkt->ip6src));
                        memcpy(&pmask->ip6src, &ipv6_usr_mask->ip6src,
                               sizeof(pmask->ip6src));
                        req->features |= BIT_ULL(NPC_SIP_IPV6);
                }
                if (!ipv6_addr_any((struct in6_addr *)ipv6_usr_mask->ip6dst)) {
                        memcpy(&pkt->ip6dst, &ipv6_usr_hdr->ip6dst,
                               sizeof(pkt->ip6dst));
                        memcpy(&pmask->ip6dst, &ipv6_usr_mask->ip6dst,
                               sizeof(pmask->ip6dst));
                        req->features |= BIT_ULL(NPC_DIP_IPV6);
                }
                if (ipv6_usr_hdr->l4_proto == IPPROTO_FRAGMENT) {
                        pkt->next_header = ipv6_usr_hdr->l4_proto;
                        pmask->next_header = ipv6_usr_mask->l4_proto;
                        req->features |= BIT_ULL(NPC_IPFRAG_IPV6);
                }
                pkt->etype = cpu_to_be16(ETH_P_IPV6);
                pmask->etype = cpu_to_be16(0xFFFF);
                req->features |= BIT_ULL(NPC_ETYPE);
                break;
        case TCP_V6_FLOW:
        case UDP_V6_FLOW:
        case SCTP_V6_FLOW:
                pkt->etype = cpu_to_be16(ETH_P_IPV6);
                pmask->etype = cpu_to_be16(0xFFFF);
                req->features |= BIT_ULL(NPC_ETYPE);
                if (!ipv6_addr_any((struct in6_addr *)ipv6_l4_mask->ip6src)) {
                        memcpy(&pkt->ip6src, &ipv6_l4_hdr->ip6src,
                               sizeof(pkt->ip6src));
                        memcpy(&pmask->ip6src, &ipv6_l4_mask->ip6src,
                               sizeof(pmask->ip6src));
                        req->features |= BIT_ULL(NPC_SIP_IPV6);
                }
                if (!ipv6_addr_any((struct in6_addr *)ipv6_l4_mask->ip6dst)) {
                        memcpy(&pkt->ip6dst, &ipv6_l4_hdr->ip6dst,
                               sizeof(pkt->ip6dst));
                        memcpy(&pmask->ip6dst, &ipv6_l4_mask->ip6dst,
                               sizeof(pmask->ip6dst));
                        req->features |= BIT_ULL(NPC_DIP_IPV6);
                }
                if (ipv6_l4_mask->psrc) {
                        memcpy(&pkt->sport, &ipv6_l4_hdr->psrc,
                               sizeof(pkt->sport));
                        memcpy(&pmask->sport, &ipv6_l4_mask->psrc,
                               sizeof(pmask->sport));
                        if (flow_type == UDP_V6_FLOW)
                                req->features |= BIT_ULL(NPC_SPORT_UDP);
                        else if (flow_type == TCP_V6_FLOW)
                                req->features |= BIT_ULL(NPC_SPORT_TCP);
                        else
                                req->features |= BIT_ULL(NPC_SPORT_SCTP);
                }
                if (ipv6_l4_mask->pdst) {
                        memcpy(&pkt->dport, &ipv6_l4_hdr->pdst,
                               sizeof(pkt->dport));
                        memcpy(&pmask->dport, &ipv6_l4_mask->pdst,
                               sizeof(pmask->dport));
                        if (flow_type == UDP_V6_FLOW)
                                req->features |= BIT_ULL(NPC_DPORT_UDP);
                        else if (flow_type == TCP_V6_FLOW)
                                req->features |= BIT_ULL(NPC_DPORT_TCP);
                        else
                                req->features |= BIT_ULL(NPC_DPORT_SCTP);
                }
                if (flow_type == UDP_V6_FLOW)
                        req->features |= BIT_ULL(NPC_IPPROTO_UDP);
                else if (flow_type == TCP_V6_FLOW)
                        req->features |= BIT_ULL(NPC_IPPROTO_TCP);
                else
                        req->features |= BIT_ULL(NPC_IPPROTO_SCTP);
                break;
        case AH_V6_FLOW:
        case ESP_V6_FLOW:
                pkt->etype = cpu_to_be16(ETH_P_IPV6);
                pmask->etype = cpu_to_be16(0xFFFF);
                req->features |= BIT_ULL(NPC_ETYPE);
                if (!ipv6_addr_any((struct in6_addr *)ah_esp_hdr->ip6src)) {
                        memcpy(&pkt->ip6src, &ah_esp_hdr->ip6src,
                               sizeof(pkt->ip6src));
                        memcpy(&pmask->ip6src, &ah_esp_mask->ip6src,
                               sizeof(pmask->ip6src));
                        req->features |= BIT_ULL(NPC_SIP_IPV6);
                }
                if (!ipv6_addr_any((struct in6_addr *)ah_esp_hdr->ip6dst)) {
                        memcpy(&pkt->ip6dst, &ah_esp_hdr->ip6dst,
                               sizeof(pkt->ip6dst));
                        memcpy(&pmask->ip6dst, &ah_esp_mask->ip6dst,
                               sizeof(pmask->ip6dst));
                        req->features |= BIT_ULL(NPC_DIP_IPV6);
                }

                /* NPC profile doesn't extract AH/ESP header fields */
                if ((ah_esp_mask->spi & ah_esp_hdr->spi) ||
                    (ah_esp_mask->tclass & ah_esp_hdr->tclass))
                        return -EOPNOTSUPP;

                if (flow_type == AH_V6_FLOW)
                        req->features |= BIT_ULL(NPC_IPPROTO_AH);
                else
                        req->features |= BIT_ULL(NPC_IPPROTO_ESP);
                break;
        default:
                break;
        }

        return 0;
}

static int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp,
                              struct npc_install_flow_req *req)
{
        struct ethhdr *eth_mask = &fsp->m_u.ether_spec;
        struct ethhdr *eth_hdr = &fsp->h_u.ether_spec;
        struct flow_msg *pmask = &req->mask;
        struct flow_msg *pkt = &req->packet;
        u32 flow_type;
        int ret;

        flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS);
        switch (flow_type) {
        /* bits not set in mask are don't care */
        case ETHER_FLOW:
                if (!is_zero_ether_addr(eth_mask->h_source)) {
                        ether_addr_copy(pkt->smac, eth_hdr->h_source);
                        ether_addr_copy(pmask->smac, eth_mask->h_source);
                        req->features |= BIT_ULL(NPC_SMAC);
                }
                if (!is_zero_ether_addr(eth_mask->h_dest)) {
                        ether_addr_copy(pkt->dmac, eth_hdr->h_dest);
                        ether_addr_copy(pmask->dmac, eth_mask->h_dest);
                        req->features |= BIT_ULL(NPC_DMAC);
                }
                if (eth_hdr->h_proto) {
                        memcpy(&pkt->etype, &eth_hdr->h_proto,
                               sizeof(pkt->etype));
                        memcpy(&pmask->etype, &eth_mask->h_proto,
                               sizeof(pmask->etype));
                        req->features |= BIT_ULL(NPC_ETYPE);
                }
                break;
        case IP_USER_FLOW:
        case TCP_V4_FLOW:
        case UDP_V4_FLOW:
        case SCTP_V4_FLOW:
        case AH_V4_FLOW:
        case ESP_V4_FLOW:
                ret = otx2_prepare_ipv4_flow(fsp, req, flow_type);
                if (ret)
                        return ret;
                break;
        case IPV6_USER_FLOW:
        case TCP_V6_FLOW:
        case UDP_V6_FLOW:
        case SCTP_V6_FLOW:
        case AH_V6_FLOW:
        case ESP_V6_FLOW:
                ret = otx2_prepare_ipv6_flow(fsp, req, flow_type);
                if (ret)
                        return ret;
                break;
        default:
                return -EOPNOTSUPP;
        }
        if (fsp->flow_type & FLOW_EXT) {
                u16 vlan_etype;

                if (fsp->m_ext.vlan_etype) {
                        /* Partial masks not supported */
                        if (be16_to_cpu(fsp->m_ext.vlan_etype) != 0xFFFF)
                                return -EINVAL;

                        vlan_etype = be16_to_cpu(fsp->h_ext.vlan_etype);

                        /* Drop rule with vlan_etype == 802.1Q
                         * and vlan_id == 0 is not supported
                         */
                        if (vlan_etype == ETH_P_8021Q && !fsp->m_ext.vlan_tci &&
                            fsp->ring_cookie == RX_CLS_FLOW_DISC)
                                return -EINVAL;

                        /* Only ETH_P_8021Q and ETH_P_802AD types supported */
                        if (vlan_etype != ETH_P_8021Q &&
                            vlan_etype != ETH_P_8021AD)
                                return -EINVAL;

                        memcpy(&pkt->vlan_etype, &fsp->h_ext.vlan_etype,
                               sizeof(pkt->vlan_etype));
                        memcpy(&pmask->vlan_etype, &fsp->m_ext.vlan_etype,
                               sizeof(pmask->vlan_etype));

                        if (vlan_etype == ETH_P_8021Q)
                                req->features |= BIT_ULL(NPC_VLAN_ETYPE_CTAG);
                        else
                                req->features |= BIT_ULL(NPC_VLAN_ETYPE_STAG);
                }

                if (fsp->m_ext.vlan_tci) {
                        memcpy(&pkt->vlan_tci, &fsp->h_ext.vlan_tci,
                               sizeof(pkt->vlan_tci));
                        memcpy(&pmask->vlan_tci, &fsp->m_ext.vlan_tci,
                               sizeof(pmask->vlan_tci));
                        req->features |= BIT_ULL(NPC_OUTER_VID);
                }

                if (fsp->m_ext.data[1]) {
                        if (flow_type == IP_USER_FLOW) {
                                if (be32_to_cpu(fsp->h_ext.data[1]) != IPV4_FLAG_MORE)
                                        return -EINVAL;

                                pkt->ip_flag = be32_to_cpu(fsp->h_ext.data[1]);
                                pmask->ip_flag = be32_to_cpu(fsp->m_ext.data[1]);
                                req->features |= BIT_ULL(NPC_IPFRAG_IPV4);
                        } else if (fsp->h_ext.data[1] ==
                                        cpu_to_be32(OTX2_DEFAULT_ACTION)) {
                                /* Not Drop/Direct to queue but use action
                                 * in default entry
                                 */
                                req->op = NIX_RX_ACTION_DEFAULT;
                        }
                }
        }

        if (fsp->flow_type & FLOW_MAC_EXT &&
            !is_zero_ether_addr(fsp->m_ext.h_dest)) {
                ether_addr_copy(pkt->dmac, fsp->h_ext.h_dest);
                ether_addr_copy(pmask->dmac, fsp->m_ext.h_dest);
                req->features |= BIT_ULL(NPC_DMAC);
        }

        if (!req->features)
                return -EOPNOTSUPP;

        return 0;
}

static int otx2_is_flow_rule_dmacfilter(struct otx2_nic *pfvf,
                                        struct ethtool_rx_flow_spec *fsp)
{
        struct ethhdr *eth_mask = &fsp->m_u.ether_spec;
        struct ethhdr *eth_hdr = &fsp->h_u.ether_spec;
        u64 ring_cookie = fsp->ring_cookie;
        u32 flow_type;

        if (!(pfvf->flags & OTX2_FLAG_DMACFLTR_SUPPORT))
                return false;

        flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS);

        /* CGX/RPM block dmac filtering configured for white listing
         * check for action other than DROP
         */
        if (flow_type == ETHER_FLOW && ring_cookie != RX_CLS_FLOW_DISC &&
            !ethtool_get_flow_spec_ring_vf(ring_cookie)) {
                if (is_zero_ether_addr(eth_mask->h_dest) &&
                    is_valid_ether_addr(eth_hdr->h_dest))
                        return true;
        }

        return false;
}

static int otx2_add_flow_msg(struct otx2_nic *pfvf, struct otx2_flow *flow)
{
        u64 ring_cookie = flow->flow_spec.ring_cookie;
#ifdef CONFIG_DCB
        int vlan_prio, qidx, pfc_rule = 0;
#endif
        struct npc_install_flow_req *req;
        int err, vf = 0;

        mutex_lock(&pfvf->mbox.lock);
        req = otx2_mbox_alloc_msg_npc_install_flow(&pfvf->mbox);
        if (!req) {
                mutex_unlock(&pfvf->mbox.lock);
                return -ENOMEM;
        }

        err = otx2_prepare_flow_request(&flow->flow_spec, req);
        if (err) {
                /* free the allocated msg above */
                otx2_mbox_reset(&pfvf->mbox.mbox, 0);
                mutex_unlock(&pfvf->mbox.lock);
                return err;
        }

        req->entry = flow->entry;
        req->intf = NIX_INTF_RX;
        req->set_cntr = 1;
        req->channel = pfvf->hw.rx_chan_base;
        if (ring_cookie == RX_CLS_FLOW_DISC) {
                req->op = NIX_RX_ACTIONOP_DROP;
        } else {
                /* change to unicast only if action of default entry is not
                 * requested by user
                 */
                if (flow->flow_spec.flow_type & FLOW_RSS) {
                        req->op = NIX_RX_ACTIONOP_RSS;
                        req->index = flow->rss_ctx_id;
                        req->flow_key_alg = pfvf->hw.flowkey_alg_idx;
                } else {
                        req->op = NIX_RX_ACTIONOP_UCAST;
                        req->index = ethtool_get_flow_spec_ring(ring_cookie);
                }
                vf = ethtool_get_flow_spec_ring_vf(ring_cookie);
                if (vf > pci_num_vf(pfvf->pdev)) {
                        mutex_unlock(&pfvf->mbox.lock);
                        return -EINVAL;
                }

#ifdef CONFIG_DCB
                /* Identify PFC rule if PFC enabled and ntuple rule is vlan */
                if (!vf && (req->features & BIT_ULL(NPC_OUTER_VID)) &&
                    pfvf->pfc_en && req->op != NIX_RX_ACTIONOP_RSS) {
                        vlan_prio = ntohs(req->packet.vlan_tci) &
                                    ntohs(req->mask.vlan_tci);

                        /* Get the priority */
                        vlan_prio >>= 13;
                        flow->rule_type |= PFC_FLOWCTRL_RULE;
                        /* Check if PFC enabled for this priority */
                        if (pfvf->pfc_en & BIT(vlan_prio)) {
                                pfc_rule = true;
                                qidx = req->index;
                        }
                }
#endif
        }

        /* ethtool ring_cookie has (VF + 1) for VF */
        if (vf) {
                req->vf = vf;
                flow->is_vf = true;
                flow->vf = vf;
        }

        /* Send message to AF */
        err = otx2_sync_mbox_msg(&pfvf->mbox);

#ifdef CONFIG_DCB
        if (!err && pfc_rule)
                otx2_update_bpid_in_rqctx(pfvf, vlan_prio, qidx, true);
#endif

        mutex_unlock(&pfvf->mbox.lock);
        return err;
}

static int otx2_add_flow_with_pfmac(struct otx2_nic *pfvf,
                                    struct otx2_flow *flow)
{
        struct otx2_flow *pf_mac;
        struct ethhdr *eth_hdr;

        pf_mac = kzalloc_obj(*pf_mac);
        if (!pf_mac)
                return -ENOMEM;

        pf_mac->entry = 0;
        pf_mac->rule_type |= DMAC_FILTER_RULE;
        pf_mac->location = pfvf->flow_cfg->max_flows;
        memcpy(&pf_mac->flow_spec, &flow->flow_spec,
               sizeof(struct ethtool_rx_flow_spec));
        pf_mac->flow_spec.location = pf_mac->location;

        /* Copy PF mac address */
        eth_hdr = &pf_mac->flow_spec.h_u.ether_spec;
        ether_addr_copy(eth_hdr->h_dest, pfvf->netdev->dev_addr);

        /* Install DMAC filter with PF mac address */
        otx2_dmacflt_add(pfvf, eth_hdr->h_dest, 0);

        otx2_add_flow_to_list(pfvf, pf_mac);
        pfvf->flow_cfg->nr_flows++;
        set_bit(0, pfvf->flow_cfg->dmacflt_bmap);

        return 0;
}

int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc)
{
        struct otx2_flow_config *flow_cfg = pfvf->flow_cfg;
        struct ethtool_rx_flow_spec *fsp = &nfc->fs;
        struct otx2_flow *flow;
        struct ethhdr *eth_hdr;
        bool new = false;
        int err = 0;
        u64 vf_num;
        u32 ring;

        if (!flow_cfg->max_flows) {
                netdev_err(pfvf->netdev,
                           "Ntuple rule count is 0, allocate and retry\n");
                return -EINVAL;
        }

        ring = ethtool_get_flow_spec_ring(fsp->ring_cookie);
        if (!(pfvf->flags & OTX2_FLAG_NTUPLE_SUPPORT))
                return -ENOMEM;

        /* Number of queues on a VF can be greater or less than
         * the PF's queue. Hence no need to check for the
         * queue count. Hence no need to check queue count if PF
         * is installing for its VF. Below is the expected vf_num value
         * based on the ethtool commands.
         *
         * e.g.
         * 1. ethtool -U <netdev> ... action -1  ==> vf_num:255
         * 2. ethtool -U <netdev> ... action <queue_num>  ==> vf_num:0
         * 3. ethtool -U <netdev> ... vf <vf_idx> queue <queue_num>  ==>
         *    vf_num:vf_idx+1
         */
        vf_num = ethtool_get_flow_spec_ring_vf(fsp->ring_cookie);
        if (!is_otx2_vf(pfvf->pcifunc) && !vf_num &&
            ring >= pfvf->hw.rx_queues && fsp->ring_cookie != RX_CLS_FLOW_DISC)
                return -EINVAL;

        if (fsp->location >= otx2_get_maxflows(flow_cfg))
                return -EINVAL;

        flow = otx2_find_flow(pfvf, fsp->location);
        if (!flow) {
                flow = kzalloc_obj(*flow);
                if (!flow)
                        return -ENOMEM;
                flow->location = fsp->location;
                flow->entry = flow_cfg->flow_ent[flow->location];
                new = true;
        }
        /* struct copy */
        flow->flow_spec = *fsp;

        if (fsp->flow_type & FLOW_RSS)
                flow->rss_ctx_id = nfc->rss_context;

        if (otx2_is_flow_rule_dmacfilter(pfvf, &flow->flow_spec)) {
                eth_hdr = &flow->flow_spec.h_u.ether_spec;

                /* Sync dmac filter table with updated fields */
                if (flow->rule_type & DMAC_FILTER_RULE)
                        return otx2_dmacflt_update(pfvf, eth_hdr->h_dest,
                                                   flow->entry);

                if (bitmap_full(flow_cfg->dmacflt_bmap,
                                flow_cfg->dmacflt_max_flows)) {
                        netdev_warn(pfvf->netdev,
                                    "Can't insert the rule %d as max allowed dmac filters are %d\n",
                                    flow->location +
                                    flow_cfg->dmacflt_max_flows,
                                    flow_cfg->dmacflt_max_flows);
                        err = -EINVAL;
                        if (new)
                                kfree(flow);
                        return err;
                }

                /* Install PF mac address to DMAC filter list */
                if (!test_bit(0, flow_cfg->dmacflt_bmap))
                        otx2_add_flow_with_pfmac(pfvf, flow);

                flow->rule_type |= DMAC_FILTER_RULE;
                flow->entry = find_first_zero_bit(flow_cfg->dmacflt_bmap,
                                                  flow_cfg->dmacflt_max_flows);
                fsp->location = flow_cfg->max_flows + flow->entry;
                flow->flow_spec.location = fsp->location;
                flow->location = fsp->location;

                set_bit(flow->entry, flow_cfg->dmacflt_bmap);
                otx2_dmacflt_add(pfvf, eth_hdr->h_dest, flow->entry);

        } else {
                if (flow->location >= pfvf->flow_cfg->max_flows) {
                        netdev_warn(pfvf->netdev,
                                    "Can't insert non dmac ntuple rule at %d, allowed range %d-0\n",
                                    flow->location,
                                    flow_cfg->max_flows - 1);
                        err = -EINVAL;
                } else {
                        err = otx2_add_flow_msg(pfvf, flow);
                }
        }

        if (err) {
                if (err == MBOX_MSG_INVALID)
                        err = -EINVAL;
                if (new)
                        kfree(flow);
                return err;
        }

        /* add the new flow installed to list */
        if (new) {
                otx2_add_flow_to_list(pfvf, flow);
                flow_cfg->nr_flows++;
        }

        if (flow->is_vf)
                netdev_info(pfvf->netdev,
                            "Make sure that VF's queue number is within its queue limit\n");
        return 0;
}

static int otx2_remove_flow_msg(struct otx2_nic *pfvf, u16 entry, bool all)
{
        struct npc_delete_flow_req *req;
        int err;

        mutex_lock(&pfvf->mbox.lock);
        req = otx2_mbox_alloc_msg_npc_delete_flow(&pfvf->mbox);
        if (!req) {
                mutex_unlock(&pfvf->mbox.lock);
                return -ENOMEM;
        }

        req->entry = entry;
        if (all)
                req->all = 1;

        /* Send message to AF */
        err = otx2_sync_mbox_msg(&pfvf->mbox);
        mutex_unlock(&pfvf->mbox.lock);
        return err;
}

static void otx2_update_rem_pfmac(struct otx2_nic *pfvf, int req)
{
        struct otx2_flow *iter;
        struct ethhdr *eth_hdr;
        bool found = false;

        list_for_each_entry(iter, &pfvf->flow_cfg->flow_list, list) {
                if ((iter->rule_type & DMAC_FILTER_RULE) && iter->entry == 0) {
                        eth_hdr = &iter->flow_spec.h_u.ether_spec;
                        if (req == DMAC_ADDR_DEL) {
                                otx2_dmacflt_remove(pfvf, eth_hdr->h_dest,
                                                    0);
                                clear_bit(0, pfvf->flow_cfg->dmacflt_bmap);
                                found = true;
                        } else {
                                ether_addr_copy(eth_hdr->h_dest,
                                                pfvf->netdev->dev_addr);

                                otx2_dmacflt_update(pfvf, eth_hdr->h_dest, 0);
                        }
                        break;
                }
        }

        if (found) {
                list_del(&iter->list);
                kfree(iter);
                pfvf->flow_cfg->nr_flows--;
        }
}

int otx2_remove_flow(struct otx2_nic *pfvf, u32 location)
{
        struct otx2_flow_config *flow_cfg = pfvf->flow_cfg;
        struct otx2_flow *flow;
        int err;

        if (location >= otx2_get_maxflows(flow_cfg))
                return -EINVAL;

        flow = otx2_find_flow(pfvf, location);
        if (!flow)
                return -ENOENT;

        if (flow->rule_type & DMAC_FILTER_RULE) {
                struct ethhdr *eth_hdr = &flow->flow_spec.h_u.ether_spec;

                /* user not allowed to remove dmac filter with interface mac */
                if (ether_addr_equal(pfvf->netdev->dev_addr, eth_hdr->h_dest))
                        return -EPERM;

                err = otx2_dmacflt_remove(pfvf, eth_hdr->h_dest,
                                          flow->entry);
                clear_bit(flow->entry, flow_cfg->dmacflt_bmap);
                /* If all dmac filters are removed delete macfilter with
                 * interface mac address and configure CGX/RPM block in
                 * promiscuous mode
                 */
                if (bitmap_weight(flow_cfg->dmacflt_bmap,
                                  flow_cfg->dmacflt_max_flows) == 1)
                        otx2_update_rem_pfmac(pfvf, DMAC_ADDR_DEL);
        } else {
#ifdef CONFIG_DCB
                if (flow->rule_type & PFC_FLOWCTRL_RULE)
                        otx2_update_bpid_in_rqctx(pfvf, 0,
                                                  flow->flow_spec.ring_cookie,
                                                  false);
#endif

                err = otx2_remove_flow_msg(pfvf, flow->entry, false);
        }

        if (err)
                return err;

        list_del(&flow->list);
        kfree(flow);
        flow_cfg->nr_flows--;

        return 0;
}

void otx2_rss_ctx_flow_del(struct otx2_nic *pfvf, int ctx_id)
{
        struct otx2_flow *flow, *tmp;
        int err;

        list_for_each_entry_safe(flow, tmp, &pfvf->flow_cfg->flow_list, list) {
                if (flow->rss_ctx_id != ctx_id)
                        continue;
                err = otx2_remove_flow(pfvf, flow->location);
                if (err)
                        netdev_warn(pfvf->netdev,
                                    "Can't delete the rule %d associated with this rss group err:%d",
                                    flow->location, err);
        }
}

int otx2_destroy_ntuple_flows(struct otx2_nic *pfvf)
{
        struct otx2_flow_config *flow_cfg = pfvf->flow_cfg;
        struct npc_delete_flow_req *req;
        struct otx2_flow *iter, *tmp;
        int err;

        if (!(pfvf->flags & OTX2_FLAG_NTUPLE_SUPPORT))
                return 0;

        if (!flow_cfg->max_flows)
                return 0;

        mutex_lock(&pfvf->mbox.lock);
        req = otx2_mbox_alloc_msg_npc_delete_flow(&pfvf->mbox);
        if (!req) {
                mutex_unlock(&pfvf->mbox.lock);
                return -ENOMEM;
        }

        req->start = flow_cfg->flow_ent[0];
        req->end   = flow_cfg->flow_ent[flow_cfg->max_flows - 1];
        err = otx2_sync_mbox_msg(&pfvf->mbox);
        mutex_unlock(&pfvf->mbox.lock);

        list_for_each_entry_safe(iter, tmp, &flow_cfg->flow_list, list) {
                list_del(&iter->list);
                kfree(iter);
                flow_cfg->nr_flows--;
        }
        return err;
}

int otx2_destroy_mcam_flows(struct otx2_nic *pfvf)
{
        struct otx2_flow_config *flow_cfg = pfvf->flow_cfg;
        struct npc_mcam_free_entry_req *req;
        struct otx2_flow *iter, *tmp;
        int err;

        if (!(pfvf->flags & OTX2_FLAG_MCAM_ENTRIES_ALLOC))
                return 0;

        /* remove all flows */
        err = otx2_remove_flow_msg(pfvf, 0, true);
        if (err)
                return err;

        list_for_each_entry_safe(iter, tmp, &flow_cfg->flow_list, list) {
                list_del(&iter->list);
                kfree(iter);
                flow_cfg->nr_flows--;
        }

        mutex_lock(&pfvf->mbox.lock);
        req = otx2_mbox_alloc_msg_npc_mcam_free_entry(&pfvf->mbox);
        if (!req) {
                mutex_unlock(&pfvf->mbox.lock);
                return -ENOMEM;
        }

        req->all = 1;
        /* Send message to AF to free MCAM entries */
        err = otx2_sync_mbox_msg(&pfvf->mbox);
        if (err) {
                mutex_unlock(&pfvf->mbox.lock);
                return err;
        }

        pfvf->flags &= ~OTX2_FLAG_MCAM_ENTRIES_ALLOC;
        flow_cfg->max_flows = 0;
        mutex_unlock(&pfvf->mbox.lock);

        return 0;
}

int otx2_install_rxvlan_offload_flow(struct otx2_nic *pfvf)
{
        struct otx2_flow_config *flow_cfg = pfvf->flow_cfg;
        struct npc_install_flow_req *req;
        int err;

        mutex_lock(&pfvf->mbox.lock);
        req = otx2_mbox_alloc_msg_npc_install_flow(&pfvf->mbox);
        if (!req) {
                mutex_unlock(&pfvf->mbox.lock);
                return -ENOMEM;
        }

        req->entry = flow_cfg->def_ent[flow_cfg->rx_vlan_offset];
        req->intf = NIX_INTF_RX;
        ether_addr_copy(req->packet.dmac, pfvf->netdev->dev_addr);
        eth_broadcast_addr((u8 *)&req->mask.dmac);
        req->channel = pfvf->hw.rx_chan_base;
        req->op = NIX_RX_ACTION_DEFAULT;
        req->features = BIT_ULL(NPC_OUTER_VID) | BIT_ULL(NPC_DMAC);
        req->vtag0_valid = true;
        req->vtag0_type = NIX_AF_LFX_RX_VTAG_TYPE0;

        /* Send message to AF */
        err = otx2_sync_mbox_msg(&pfvf->mbox);
        mutex_unlock(&pfvf->mbox.lock);
        return err;
}

static int otx2_delete_rxvlan_offload_flow(struct otx2_nic *pfvf)
{
        struct otx2_flow_config *flow_cfg = pfvf->flow_cfg;
        struct npc_delete_flow_req *req;
        int err;

        mutex_lock(&pfvf->mbox.lock);
        req = otx2_mbox_alloc_msg_npc_delete_flow(&pfvf->mbox);
        if (!req) {
                mutex_unlock(&pfvf->mbox.lock);
                return -ENOMEM;
        }

        req->entry = flow_cfg->def_ent[flow_cfg->rx_vlan_offset];
        /* Send message to AF */
        err = otx2_sync_mbox_msg(&pfvf->mbox);
        mutex_unlock(&pfvf->mbox.lock);
        return err;
}

int otx2_enable_rxvlan(struct otx2_nic *pf, bool enable)
{
        struct nix_vtag_config *req;
        struct mbox_msghdr *rsp_hdr;
        int err;

        /* Dont have enough mcam entries */
        if (!(pf->flags & OTX2_FLAG_RX_VLAN_SUPPORT))
                return -ENOMEM;

        if (enable) {
                err = otx2_install_rxvlan_offload_flow(pf);
                if (err)
                        return err;
        } else {
                err = otx2_delete_rxvlan_offload_flow(pf);
                if (err)
                        return err;
        }

        mutex_lock(&pf->mbox.lock);
        req = otx2_mbox_alloc_msg_nix_vtag_cfg(&pf->mbox);
        if (!req) {
                mutex_unlock(&pf->mbox.lock);
                return -ENOMEM;
        }

        /* config strip, capture and size */
        req->vtag_size = VTAGSIZE_T4;
        req->cfg_type = 1; /* rx vlan cfg */
        req->rx.vtag_type = NIX_AF_LFX_RX_VTAG_TYPE0;
        req->rx.strip_vtag = enable;
        req->rx.capture_vtag = enable;

        err = otx2_sync_mbox_msg(&pf->mbox);
        if (err) {
                mutex_unlock(&pf->mbox.lock);
                return err;
        }

        rsp_hdr = otx2_mbox_get_rsp(&pf->mbox.mbox, 0, &req->hdr);
        if (IS_ERR(rsp_hdr)) {
                mutex_unlock(&pf->mbox.lock);
                return PTR_ERR(rsp_hdr);
        }

        mutex_unlock(&pf->mbox.lock);
        return rsp_hdr->rc;
}

void otx2_dmacflt_reinstall_flows(struct otx2_nic *pf)
{
        struct otx2_flow *iter;
        struct ethhdr *eth_hdr;

        list_for_each_entry(iter, &pf->flow_cfg->flow_list, list) {
                if (iter->rule_type & DMAC_FILTER_RULE) {
                        eth_hdr = &iter->flow_spec.h_u.ether_spec;
                        otx2_dmacflt_add(pf, eth_hdr->h_dest,
                                         iter->entry);
                }
        }
}

void otx2_dmacflt_update_pfmac_flow(struct otx2_nic *pfvf)
{
        otx2_update_rem_pfmac(pfvf, DMAC_ADDR_UPDATE);
}