blob: e8b35c321068f9d024b9f240b0e95b4cc51dbfc6 [file] [log] [blame]
/*******************************************************************************
Copyright (C) Marvell Interfdbional Ltd. and its affiliates
This software file (the "File") is owned and distributed by Marvell
Interfdbional Ltd. and/or its affiliates ("Marvell") under the following
alterfdbive licensing terms. Once you have made an election to distribute the
File under one of the following license alterfdbives, please (i) delete this
introductory statement regarding license alterfdbives, (ii) delete the two
license alterfdbives that you have not elected to use and (iii) preserve the
Marvell copyright notice above.
********************************************************************************
Marvell Commercial License Option
If you received this File from Marvell and you have entered into a commercial
license agreement (a "Commercial License") with Marvell, the File is licensed
to you under the terms of the applicable Commercial License.
********************************************************************************
Marvell GPL License Option
If you received this File from Marvell, you may opt to use, redistribute and/or
modify this File in accordance with the terms and conditions of the General
Public License Version 2, June 1991 (the "GPL License"), a copy of which is
available along with the File in the license.txt file or by writing to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or
on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
DISCLAIMED. The GPL License provides additional details about this warranty
disclaimer.
********************************************************************************
Marvell BSD License Option
If you received this File from Marvell, you may opt to use, redistribute and/or
modify this File under the following licensing terms.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Marvell nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
/*******************************************************************************
* mvNfpCt.c - Marvell Fast Network Processing
*
* DESCRIPTION:
*
* Supported Features:
* - OS independent.
*
*******************************************************************************/
/* includes */
#include "mvOs.h"
#include "mvDebug.h"
#include "mvList.h"
#include "gbe/mvNeta.h"
#include "mvNfpDefs.h"
#include "mvNfp.h"
MV_LIST_ELEMENT *ct_inv_list = NULL;
NFP_RULE_CT **ct_hash = NULL;
int ct_iterator_row = 0;
#ifdef NFP_LIMIT
MV_LIST_ELEMENT *tbfs;
#endif /* NFP_LIMIT */
MV_STATUS _INIT mvNfpCtInit(MV_VOID)
{
MV_U32 bytes = sizeof(NFP_RULE_CT *) * NFP_CT_HASH_SIZE;
ct_hash = (NFP_RULE_CT **)mvOsMalloc(bytes);
if (ct_hash == NULL) {
mvOsPrintf("NFP (ct): not enough memory for CT database\n");
return MV_NO_RESOURCE;
}
mvOsMemset(ct_hash, 0, bytes);
ct_inv_list = mvListCreate();
if (ct_inv_list == NULL) {
mvOsPrintf("NFP (ct): not enough memory for CT database\n");
mvOsFree(ct_hash);
return MV_NO_RESOURCE;
}
mvOsPrintf("NFP (ct) init %d entries, %d bytes\n", NFP_CT_HASH_SIZE, bytes);
#ifdef NFP_LIMIT
tbfs = mvListCreate();
if (tbfs == NULL) {
mvOsPrintf("NFP (ct): not enough memory for TBF database\n");
mvListDestroy(ct_inv_list);
mvOsFree(ct_hash);
return MV_NO_RESOURCE;
}
#endif /* NFP_LIMIT */
return MV_OK;
}
static INLINE MV_U32 mvNfpCtHash(NFP_RULE_CT *ct)
{
MV_U32 hash = mv_jhash_2addr(ct->family, (const MV_U8 *)&ct->srcL3, (const MV_U8 *)&ct->dstL3,
(ct->ports | ct->proto), nfp_jhash_iv);
hash &= NFP_CT_HASH_MASK;
return hash;
}
static INLINE NFP_RULE_CT *mvNfpCtLookup(NFP_RULE_CT *ct2)
{
MV_U32 hash;
NFP_RULE_CT *ct;
if (!ct_hash)
return NULL;
hash = mvNfpCtHash(ct2);
ct = ct_hash[hash];
while (ct) {
if ((ct->family == ct2->family) &&
l3_addr_eq(ct->family, ct->srcL3, ct2->srcL3) &&
l3_addr_eq(ct->family, ct->dstL3, ct2->dstL3) &&
(ct->ports == ct2->ports) && (ct->proto == ct2->proto))
return ct;
ct = ct->next;
}
return NULL;
}
/* Move CT rule from ct_inv_list to ct_hash */
static MV_STATUS mvNfpCtRuleValid(NFP_RULE_CT *ct, MV_LIST_ELEMENT *curr)
{
MV_U32 hash;
if (((ct->flags & NFP_F_CT_DROP) || !(ct->flags & NFP_F_CT_FIB_INV)) && !(ct->flags & NFP_F_CT_NOT_EXIST)) {
/* CT rule became ready */
/* Delete CT rule from incomplete list */
mvListDel(curr);
/* Add ct to hash table */
hash = mvNfpCtHash(ct);
ct->next = ct_hash[hash];
ct_hash[hash] = ct;
ct->visited = 0;
return MV_OK;
}
return MV_BAD_PARAM;
}
MV_VOID mvNfpCtRuleFibUpdate(NFP_RULE_FIB *fib)
{
NFP_RULE_CT *ct;
MV_LIST_ELEMENT *curr, *tmp;
int i;
/* Update FIB rule pointers in CT hash */
for (i = 0; i < NFP_CT_HASH_SIZE; i++) {
ct = ct_hash[i];
while (ct) {
if ((fib->family == ct->family) &&
(l3_addr_eq(fib->family, fib->srcL3, ct->srcL3)) &&
#ifdef NFP_NAT
((!(ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) ||
((ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, (MV_U8 *)&(ct->new_dip)))))) {
#else
(l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) {
#endif /* NFP_NAT */
/* Invalidate CT route */
if (ct->fib && !(ct->flags & NFP_F_CT_FIB_INV))
(ct->fib)->ct_ref--;
if ((ct->fib) && (ct->flags & NFP_F_CT_HWF) && !(ct->flags & NFP_F_CT_FIB_INV))
(ct->fib)->ct_hwf_ref--;
ct->flags &= ~NFP_F_CT_FIB_INV;
ct->fib = fib;
(ct->fib)->ct_ref++;
if (ct->flags & NFP_F_CT_HWF)
(ct->fib)->ct_hwf_ref++;
}
ct = ct->next;
}
}
/* Update FIB rule pointers in CT invalid list */
if (!ct_inv_list)
return;
curr = ct_inv_list->next;
while (curr) {
ct = (NFP_RULE_CT *)curr->data;
tmp = curr->next;
if ((fib->family == ct->family) &&
(l3_addr_eq(fib->family, fib->srcL3, ct->srcL3)) &&
#ifdef NFP_NAT
((!(ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) ||
((ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, (MV_U8 *)&(ct->new_dip)))))) {
#else
(l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) {
#endif /* NFP_NAT */
/* Found incomplete CT entry with a matching FIB rule */
if (ct->fib && !(ct->flags & NFP_F_CT_FIB_INV))
(ct->fib)->ct_ref--;
if ((ct->fib) && (ct->flags & NFP_F_CT_HWF) && !(ct->flags & NFP_F_CT_FIB_INV))
(ct->fib)->ct_hwf_ref--;
ct->fib = fib;
(ct->fib)->ct_ref++;
if (ct->flags & NFP_F_CT_HWF)
(ct->fib)->ct_hwf_ref++;
ct->flags &= ~NFP_F_CT_FIB_INV;
mvNfpCtRuleValid(ct, curr);
}
curr = tmp;
}
}
MV_VOID mvNfpCtRuleFibInvalidate(NFP_RULE_FIB *fib)
{
int i;
NFP_RULE_CT *ct, *ct_prev;
MV_LIST_ELEMENT *curr;
for (i = 0; i < NFP_CT_HASH_SIZE; i++) {
ct = ct_hash[i];
ct_prev = NULL;
while (ct) {
if ((fib->family == ct->family) &&
(l3_addr_eq(fib->family, fib->srcL3, ct->srcL3)) &&
#ifdef NFP_NAT
((!(ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) ||
((ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, (MV_U8 *)&(ct->new_dip)))))) {
#else
(l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) {
#endif /* NFP_NAT */
/* Invalidate CT route */
if (ct->fib && !(ct->flags & NFP_F_CT_FIB_INV))
(ct->fib)->ct_ref--;
if ((ct->fib) && (ct->flags & NFP_F_CT_HWF) && !(ct->flags & NFP_F_CT_FIB_INV))
(ct->fib)->ct_hwf_ref--;
ct->flags |= NFP_F_CT_FIB_INV;
ct->fib = NULL;
if (!(ct->flags & NFP_F_CT_DROP)) {
/* Remove CT rule from hash table only if it is not a DROP rule */
if (ct_prev)
ct_prev->next = ct->next;
else
ct_hash[i] = ct->next;
/* Add CT rule to incomplete list */
mvListAddHead(ct_inv_list, (MV_ULONG)ct);
}
}
ct_prev = ct;
ct = ct->next;
}
}
/* Invalidate FIB rule pointers in CT invalid list (possible in classification context rules) */
if (!ct_inv_list)
return;
curr = ct_inv_list->next;
while (curr) {
ct = (NFP_RULE_CT *)curr->data;
if ((fib->family == ct->family) &&
(l3_addr_eq(fib->family, fib->srcL3, ct->srcL3)) &&
#ifdef NFP_NAT
((!(ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) ||
((ct->flags & NFP_F_CT_DNAT) && (l3_addr_eq(fib->family, fib->dstL3, (MV_U8 *)&(ct->new_dip)))))) {
#else
(l3_addr_eq(fib->family, fib->dstL3, ct->dstL3))) {
#endif /* NFP_NAT */
if (ct->fib && !(ct->flags & NFP_F_CT_FIB_INV))
(ct->fib)->ct_ref--;
if ((ct->fib) && (ct->flags & NFP_F_CT_HWF) && !(ct->flags & NFP_F_CT_FIB_INV))
(ct->fib)->ct_hwf_ref--;
ct->flags |= NFP_F_CT_FIB_INV;
ct->fib = NULL;
}
curr = curr->next;
}
}
static MV_VOID mvNfpCtRulePrint(NFP_RULE_CT *ct)
{
if (ct->family == MV_INET)
mvOsPrintf("IPv4: "MV_IPQUAD_FMT":%d->"MV_IPQUAD_FMT":%d",
MV_IPQUAD(ct->srcL3), MV_16BIT_BE(ct->ports & 0xFFFF),
MV_IPQUAD(ct->dstL3), MV_16BIT_BE(ct->ports >> 16));
else /* MV_INET6 */
mvOsPrintf(" IPv6: "MV_IP6_FMT":%d->"MV_IP6_FMT":%d",
MV_IP6_ARG(ct->srcL3), MV_16BIT_BE(ct->ports & 0xFFFF),
MV_IP6_ARG(ct->dstL3), MV_16BIT_BE(ct->ports >> 16));
if (ct->proto == MV_IP_PROTO_TCP)
mvOsPrintf(" TCP - ");
else if (ct->proto == MV_IP_PROTO_UDP)
mvOsPrintf(" UDP - ");
else
mvOsPrintf(" %-2d - ", ct->proto);
if (ct->flags & NFP_F_CT_DROP)
mvOsPrintf("Drop, ");
else if (ct->flags & NFP_F_CT_NOT_EXIST)
mvOsPrintf("Not Exist, ");
else
mvOsPrintf("Forward, ");
mvOsPrintf("flags=0x%04x, age=%d, hit=%d\n", ct->flags, ct->age, ct->hit_cntr);
#ifdef NFP_NAT
if (ct->flags & NFP_F_CT_DNAT)
mvOsPrintf("DNAT: "MV_IPQUAD_FMT":%d, ", MV_IPQUAD(((MV_U8 *)&ct->new_dip)), MV_16BIT_BE(ct->new_dport));
if (ct->flags & NFP_F_CT_SNAT)
mvOsPrintf("SNAT: "MV_IPQUAD_FMT":%d, ", MV_IPQUAD(((MV_U8 *)&ct->new_sip)), MV_16BIT_BE(ct->new_sport));
#endif /* NFP_NAT */
#ifdef NFP_CLASSIFY
if (ct->flags & NFP_F_CT_SET_MH)
mvOsPrintf("MH=0x%X ", ct->mh);
if (ct->flags & NFP_F_CT_SET_TXP)
mvOsPrintf("txp=0x%X ", ct->txp);
if (ct->flags & NFP_F_CT_SET_TXQ) {
int i;
mvOsPrintf(" TXQ Map: DSCP ----> TXQ\n");
for (i = 0; i <= NFP_DSCP_MAP_GLOBAL; i++) {
if (ct->txq_map[i].valid) {
if (i == NFP_DSCP_MAP_GLOBAL)
mvOsPrintf(" Global %2d\n", ct->txq_map[i].txq);
else
mvOsPrintf(" %6d %2d\n", i, ct->txq_map[i].txq);
}
}
}
if (ct->flags & NFP_F_CT_SET_DSCP) {
int i;
mvOsPrintf(" DSCP Map: Old ----> New\n");
for (i = 0; i <= NFP_DSCP_MAP_GLOBAL; i++) {
if (ct->dscp_map[i].valid) {
if (i == NFP_DSCP_MAP_GLOBAL)
mvOsPrintf(" Global %2d\n", ct->dscp_map[i].new_dscp);
else
mvOsPrintf(" %6d %2d\n", i, ct->dscp_map[i].new_dscp);
}
}
}
if (ct->flags & NFP_F_CT_SET_VLAN_PRIO) {
int i;
mvOsPrintf(" VPri Map: Old New\n");
for (i = 0; i <= NFP_VPRI_MAP_GLOBAL; i++) {
if (ct->vpri_map[i].valid) {
if (i == NFP_VPRI_MAP_GLOBAL)
mvOsPrintf(" Global %2d\n", ct->vpri_map[i].new_prio);
else
mvOsPrintf(" %6d %2d\n", i, ct->vpri_map[i].new_prio);
}
}
}
#endif /* NFP_CLASSIFY */
}
static NFP_RULE_CT *mvNfpCtRuleInvalidLookup(NFP_RULE_CT *ct2)
{
MV_LIST_ELEMENT *curr;
NFP_RULE_CT *ct;
if (!ct_inv_list)
return NULL;
curr = ct_inv_list->next;
while (curr) {
ct = (NFP_RULE_CT *)curr->data;
if ((ct->family == ct2->family) &&
l3_addr_eq(ct->family, ct->srcL3, ct2->srcL3) &&
l3_addr_eq(ct->family, ct->dstL3, ct2->dstL3) &&
(ct->ports == ct2->ports) &&
(ct->proto == ct2->proto))
return ct;
curr = curr->next;
}
return NULL;
}
MV_STATUS mvNfpCtRuleAge(NFP_RULE_CT *ct2)
{
NFP_RULE_CT *ct;
ct = mvNfpCtRuleInvalidLookup(ct2);
if (ct) {
ct2->age = (ct->flags & NFP_F_CT_HWF) ? 1 : ct->age;
ct->age = (ct->flags & NFP_F_CT_HWF) ? ct->age : 0;
return MV_OK;
}
ct = mvNfpCtLookup(ct2);
if (ct) {
ct2->age = (ct->flags & NFP_F_CT_HWF) ? 1 : ct->age;
ct->age = (ct->flags & NFP_F_CT_HWF) ? ct->age : 0;
return MV_OK;
}
ct2->age = 0;
return MV_NOT_FOUND;
}
/* create rule from classification context *
* - search and update if relevant FIB rule exist *
* - set NFP_F_CT_NOT_EXIST flag *
* - add rule to invalid rule list (NFP_F_CT_NOT_EXIST is set) *
* - if mvNfpCtFilterModeSet is called later, then clear NFP_F_CT_NOT_EXIST flag *
* Return: pointer to created rule */
NFP_RULE_CT *mvNfpCtClassifyRuleCreate(NFP_RULE_CT *ct2)
{
/* rule not exist - create rule and mark it as "not exist" */
NFP_RULE_CT *ct = (NFP_RULE_CT *)mvOsMalloc(sizeof(NFP_RULE_CT));
if (!ct) {
mvOsPrintf("%s: OOM\n", __func__);
return NULL;
}
mvOsMemcpy(ct, ct2, sizeof(NFP_RULE_CT));
ct->flags |= NFP_F_CT_NOT_EXIST;
ct->flags |= NFP_F_CT_FIB_INV;
/* this rule is invalid until mvNfpCtFilterModeSet will be called */
mvListAddHead(ct_inv_list, (MV_ULONG)ct);
return ct;
}
#ifdef NFP_NAT
MV_STATUS mvNfpCtNatRuleAdd(NFP_RULE_CT *nat2)
{
NFP_RULE_CT *nat;
MV_U32 hash;
/* Update rule if it exists as a valid rule or as an invalid rule */
nat = mvNfpCtLookup(nat2);
if (!nat)
nat = mvNfpCtRuleInvalidLookup(nat2);
if (nat) {
if (nat->flags & NFP_F_CT_NOT_EXIST)
nat->flags &= ~NFP_F_CT_NOT_EXIST;
if (nat2->flags & NFP_F_CT_SNAT) {
nat->new_sip = nat2->new_sip;
nat->new_sport = nat2->new_sport;
nat->flags |= NFP_F_CT_SNAT;
}
if (nat2->flags & NFP_F_CT_DNAT) {
nat->new_dip = nat2->new_dip;
nat->new_dport = nat2->new_dport;
nat->flags |= NFP_F_CT_DNAT;
}
/* copy other information to nat2 - classification, tbf, etc.. */
mvOsMemcpy(nat2, nat, sizeof(NFP_RULE_CT));
/* delete previous rule, and add it again later (will search for fib again) */
/* delete is important because it updates fib->ct_ref, rate limit, etc.. */
mvNfpCtRuleDel(nat);
}
nat = (NFP_RULE_CT *)mvOsMalloc(sizeof(NFP_RULE_CT));
if (!nat) {
mvOsPrintf("%s: OOM\n", __func__);
return MV_FAIL;
}
mvOsMemcpy(nat, nat2, sizeof(NFP_RULE_CT));
if (nat->flags & NFP_F_CT_DNAT)
nat->fib = mvNfpFibLookup(nat2->family, nat2->srcL3, (const MV_U8 *)&(nat2->new_dip));
else
nat->fib = mvNfpFibLookup(nat2->family, nat2->srcL3, nat2->dstL3);
if (nat->fib) {
(nat->fib)->ct_ref++; /* update FIB reference count */
hash = mvNfpCtHash(nat2);
nat->next = ct_hash[hash];
ct_hash[hash] = nat;
nat->visited = 0;
} else {
mvListAddHead(ct_inv_list, (MV_ULONG)nat);
}
NFP_DBG("NFP (nat) add %p\n", nat);
return MV_OK;
}
#endif /* NFP_NAT */
MV_STATUS mvNfpCtFilterModeSet(NFP_RULE_CT *ct2)
{
NFP_RULE_CT *ct, *new_ct;
MV_LIST_ELEMENT *element;
MV_U32 hash;
/* The rule can be in one of several initial states, each requires different handling */
/* The rule can exists as valid, exist as invalid or not exist at this point */
ct = mvNfpCtLookup(ct2);
if (ct) {
/* Rule exists as valid */
/* Either it has valid FIB information, or it is a DROP rule */
if (ct2->flags & NFP_F_CT_DROP) {
/* Updated rule says DROP, so we don't care if FIB information exists or not */
ct->flags |= NFP_F_CT_DROP;
} else {
/* Updated rule says FORWARD */
ct->flags &= ~NFP_F_CT_DROP;
if (ct->flags & NFP_F_CT_FIB_INV) {
/* need to move this rule to the invalid list */
new_ct = (NFP_RULE_CT *)mvOsMalloc(sizeof(NFP_RULE_CT));
if (!new_ct) {
mvOsPrintf("%s: OOM\n", __func__);
return MV_FAIL;
}
mvOsMemcpy(new_ct, ct, sizeof(NFP_RULE_CT));
#ifdef NFP_LIMIT
if (new_ct->tbfInfo)
new_ct->tbfInfo->refCnt++;
#endif /* NFP_LIMIT */
mvNfpCtRuleDel(ct);
mvListAddHead(ct_inv_list, (MV_ULONG)new_ct);
}
}
return MV_OK;
}
ct = mvNfpCtRuleInvalidLookup(ct2);
/* Rule exists as invalid */
if (ct) {
/* this rule was created by classification API *
* mark it as "real" rule *
* search for relevant fib rule */
if (ct->flags & NFP_F_CT_NOT_EXIST) {
ct->flags &= ~NFP_F_CT_NOT_EXIST;
ct->flags |= NFP_F_CT_FIB_INV;
ct->fib = mvNfpFibLookup(ct2->family, ct2->srcL3, ct2->dstL3);
if (ct->fib) {
(ct->fib)->ct_ref++;
ct->flags &= ~NFP_F_CT_FIB_INV;
}
}
if (ct2->flags & NFP_F_CT_DROP || !(ct->flags & NFP_F_CT_FIB_INV)) {
/* need to move this rule to the valid rules database */
if (ct2->flags & NFP_F_CT_DROP)
ct->flags |= NFP_F_CT_DROP;
element = mvListFind(ct_inv_list, (MV_ULONG)ct);
if (!element)
return MV_FAIL;
return mvNfpCtRuleValid(ct, element);
} else {
/* no need to do anything special - clearing DROP flag just for clarity, it is already cleared */
ct->flags &= ~NFP_F_CT_DROP;
return MV_OK;
}
}
/* Rule doesn't exist, need to create a new one */
ct = (NFP_RULE_CT *)mvOsMalloc(sizeof(NFP_RULE_CT));
if (!ct) {
mvOsPrintf("%s: OOM\n", __func__);
return MV_FAIL;
}
mvOsMemcpy(ct, ct2, sizeof(NFP_RULE_CT));
ct->flags |= NFP_F_CT_FIB_INV;
ct->fib = mvNfpFibLookup(ct2->family, ct2->srcL3, ct2->dstL3);
if (ct->fib) {
(ct->fib)->ct_ref++;
ct->flags &= ~NFP_F_CT_FIB_INV;
}
if ((ct->flags & NFP_F_CT_DROP) || !(ct->flags & NFP_F_CT_FIB_INV)) {
hash = mvNfpCtHash(ct2);
ct->next = ct_hash[hash];
ct_hash[hash] = ct;
ct->visited = 0;
} else {
mvListAddHead(ct_inv_list, (MV_ULONG)ct);
}
NFP_DBG("NFP (ct filter mode) set %p\n", ct);
return MV_OK;
}
#ifdef NFP_LIMIT
void mvNfpCtTbfsDump(void)
{
MV_LIST_ELEMENT *curr;
NFP_TBF_INFO *tbf_data;
mvOsPrintf("Tbfs list:\n");
if (!tbfs)
return;
curr = tbfs->next;
while (curr) {
tbf_data = (NFP_TBF_INFO *)curr->data;
mvOsPrintf("index: %d, limit: %d, burst: %d, refCnt: %d\n", tbf_data->index,
tbf_data->creditPerTick * mvOsGetTicksFreq(), tbf_data->creditMax, tbf_data->refCnt);
curr = curr->next;
}
mvOsPrintf("\n");
}
/* limit units = KBytes/sec, burst_limit units = Kbytes */
int mvNfpTbfCreate(int limit, int burst_limit)
{
NFP_TBF_INFO *tbf;
if (limit < 0 || burst_limit < 0 || !tbfs)
return -1;
tbf = mvOsMalloc(sizeof(NFP_TBF_INFO));
if (!tbf) {
mvOsPrintf("%s: OOM\n", __func__);
return -1;
}
tbf->refCnt = 0;
tbf->credit = tbf->creditMax = (burst_limit * 1000); /* Bytes */
/* convert from KBytes/sec to Bytes/tick */
tbf->creditPerTick = (limit * 1000) / mvOsGetTicksFreq(); /* Bytes per tick */
if (tbf->creditPerTick == 0)
tbf->maxElapsedTicks = 0;
else
tbf->maxElapsedTicks = tbf->creditMax / tbf->creditPerTick; /* ticks */
tbf->lastUpdate = mvOsGetTicks(); /* ticks */
/* calculate index */
if (tbfs->next)
tbf->index = ((NFP_TBF_INFO *)(tbfs->next->data))->index + 1;
else
tbf->index = 0;
if (!mvListAddHead(tbfs, (MV_U32)tbf)) {
mvOsPrintf("%s: OOM\n", __func__);
mvOsFree(tbf);
return -1;
}
return tbf->index;
}
NFP_TBF_INFO *mvNfpTbfGet(int tbf)
{
MV_LIST_ELEMENT *curr;
NFP_TBF_INFO *tbf_data;
if (tbf < 0 || !tbfs)
return NULL;
curr = tbfs->next;
while (curr) {
tbf_data = (NFP_TBF_INFO *)curr->data;
if (tbf_data->index == tbf)
return tbf_data;
curr = curr->next;
}
return NULL;
}
MV_STATUS mvNfpTbfDel(int tbf)
{
MV_LIST_ELEMENT *element;
NFP_TBF_INFO *tbf_data = mvNfpTbfGet(tbf);
if (!tbf_data) {
mvOsPrintf("%s: Invalid Token Bucket Filter index (%d)\n", __func__, tbf);
return MV_BAD_PARAM;
}
/* delete tbf only if there are no rules attached */
if (!tbf_data->refCnt) {
element = mvListFind(tbfs, (MV_ULONG)tbf_data);
mvListDel(element);
mvOsFree(tbf_data);
}
return MV_OK;
}
MV_STATUS mvNfpCtRateLimitSet(NFP_RULE_CT *ct2, int tbf_index)
{
NFP_RULE_CT *ct;
NFP_TBF_INFO *tbf = mvNfpTbfGet(tbf_index);
if (!ct2)
return MV_BAD_PARAM;
/* Rule must exist already, but it can be valid or invalid */
ct = mvNfpCtLookup(ct2);
if (!ct)
ct = mvNfpCtRuleInvalidLookup(ct2);
if (ct) {
if (tbf) {
if (ct->tbfInfo)
(ct->tbfInfo)->refCnt--;
tbf->refCnt++;
ct->tbfInfo = tbf;
ct->flags |= NFP_F_CT_LIMIT;
} else {
mvOsPrintf("%s: Invalid Token Bucket Filter index (%d)\n", __func__, tbf_index);
return MV_BAD_PARAM;
}
} else {
mvOsPrintf("%s Error: Could not find existing 5 tuple rule\n", __func__);
return MV_NOT_FOUND;
}
NFP_DBG("NFP (ct rate limit) set %p\n", ct);
return MV_OK;
}
MV_STATUS mvNfpCtRateLimitDel(NFP_RULE_CT *ct2)
{
NFP_RULE_CT *ct;
if (!ct2)
return MV_BAD_PARAM;
/* Rule must exist already, but it can be valid or invalid */
ct = mvNfpCtLookup(ct2);
if (!ct)
ct = mvNfpCtRuleInvalidLookup(ct2);
if (ct) {
if (ct->tbfInfo)
(ct->tbfInfo)->refCnt--;
ct->tbfInfo = NULL;
ct->flags &= ~NFP_F_CT_LIMIT;
return MV_OK;
}
return MV_NOT_FOUND;
}
MV_STATUS mvNfpTbfProcess(NFP_TBF_INFO *tbf, MV_U32 packetSize)
{
MV_U32 ticks = mvOsGetTicks();
MV_U32 elapsed;
if (!tbf)
return MV_CONTINUE;
/* Update credit */
elapsed = ticks - tbf->lastUpdate;
tbf->lastUpdate = ticks;
/* safe check if elapsed time is higher than "time that gives maximum credit" */
if (elapsed > tbf->maxElapsedTicks) {
tbf->credit = tbf->creditMax;
} else {
tbf->credit += elapsed * tbf->creditPerTick;
if (tbf->credit > tbf->creditMax)
tbf->credit = tbf->creditMax;
}
/* Check result */
if (packetSize > tbf->credit)
return MV_DROPPED;
tbf->credit -= packetSize;
return MV_CONTINUE;
}
#endif /* NFP_LIMIT */
#ifdef NFP_CLASSIFY
/* Add DSCP mapping for an existing 5 tuple rule */
MV_STATUS mvNfpCtDscpRuleAdd(NFP_RULE_CT *ct2, int dscp, int new_dscp)
{
NFP_RULE_CT *ct;
/* sanity: chack new_dscp parameter */
if ((new_dscp < NFP_DSCP_MIN) || (new_dscp > NFP_DSCP_MAX)) {
mvOsPrintf("%s Error: new_dscp value (%d) is out of range\n", __func__, new_dscp);
return MV_BAD_PARAM;
}
/* Rule must exist already, but it can be valid or invalid */
ct = mvNfpCtLookup(ct2);
if (!ct)
ct = mvNfpCtRuleInvalidLookup(ct2);
/* rule is not exist - create rule from classification context */
if (!ct) {
ct = mvNfpCtClassifyRuleCreate(ct2);
if (!ct)
return MV_FAIL;
}
/* Update rule DSCP map table */
if (ct2->flags & NFP_F_CT_SET_DSCP) {
if (dscp == MV_ETH_NFP_GLOBAL_MAP) {
ct->dscp_map[NFP_DSCP_MAP_GLOBAL].new_dscp = new_dscp;
ct->dscp_map[NFP_DSCP_MAP_GLOBAL].valid = MV_TRUE;
} else if ((dscp >= NFP_DSCP_MIN) && (dscp <= NFP_DSCP_MAX)) {
ct->dscp_map[dscp].new_dscp = new_dscp;
ct->dscp_map[dscp].valid = MV_TRUE;
} else {
mvOsPrintf("%s Error: dscp value (%d) is out of range\n", __func__, dscp);
return MV_BAD_PARAM;
}
ct->flags |= NFP_F_CT_SET_DSCP;
} else {
mvOsPrintf("%s Error: NFP_F_CT_SET_DSCP flag is not set\n", __func__);
return MV_BAD_PARAM;
}
NFP_DBG("NFP (ct dscp) set %p\n", ct);
return MV_OK;
}
static INLINE MV_STATUS mvNfpIsDscpSet(NFP_RULE_CT *ct)
{
int i;
for (i = 0; i <= NFP_DSCP_MAP_GLOBAL; i++)
if (ct->dscp_map[i].valid)
return MV_TRUE;
return MV_FALSE;
}
MV_STATUS mvNfpCtDscpRuleDel(NFP_RULE_CT *ct2, int dscp)
{
NFP_RULE_CT *ct;
/* Rule must exist already, but it can be valid or invalid */
ct = mvNfpCtLookup(ct2);
if (!ct)
ct = mvNfpCtRuleInvalidLookup(ct2);
if (ct) {
if (dscp == MV_ETH_NFP_GLOBAL_MAP) {
ct->dscp_map[NFP_DSCP_MAP_GLOBAL].new_dscp = 0;
ct->dscp_map[NFP_DSCP_MAP_GLOBAL].valid = MV_FALSE;
} else if ((dscp >= NFP_DSCP_MIN) && (dscp <= NFP_DSCP_MAX)) {
ct->dscp_map[dscp].new_dscp = 0;
ct->dscp_map[dscp].valid = MV_FALSE;
} else {
mvOsPrintf("%s Error: dscp value (%d) is out of range\n", __func__, dscp);
return MV_BAD_PARAM;
}
if (!mvNfpIsDscpSet(ct))
ct->flags &= ~NFP_F_CT_SET_DSCP;
return MV_OK;
}
return MV_NOT_FOUND;
}
MV_STATUS mvNfpCtVlanPrioRuleAdd(NFP_RULE_CT *ct2, int prio, int new_prio)
{
NFP_RULE_CT *ct;
/* sanity: chack new_prio parameter */
if ((new_prio < NFP_VPRI_MIN) || (new_prio > NFP_VPRI_MAX)) {
mvOsPrintf("%s Error: new_prio value (%d) is out of range\n", __func__, new_prio);
return MV_BAD_PARAM;
}
/* Rule must exist already, but it can be valid or invalid */
ct = mvNfpCtLookup(ct2);
if (!ct)
ct = mvNfpCtRuleInvalidLookup(ct2);
/* rule is not exist - create rule from classification context */
if (!ct) {
ct = mvNfpCtClassifyRuleCreate(ct2);
if (!ct)
return MV_FAIL;
}
/* Update rule VLAN Priority map table */
if (ct2->flags & NFP_F_CT_SET_VLAN_PRIO) {
if (prio == MV_ETH_NFP_GLOBAL_MAP) {
ct->vpri_map[NFP_VPRI_MAP_GLOBAL].new_prio = new_prio;
ct->vpri_map[NFP_VPRI_MAP_GLOBAL].valid = MV_TRUE;
} else if ((prio >= NFP_VPRI_MIN) && (prio <= NFP_VPRI_MAX)) {
ct->vpri_map[prio].new_prio = new_prio;
ct->vpri_map[prio].valid = MV_TRUE;
} else {
mvOsPrintf("%s Error: prio value (%d) is out of range\n", __func__, prio);
return MV_BAD_PARAM;
}
ct->flags |= NFP_F_CT_SET_VLAN_PRIO;
} else {
mvOsPrintf("%s: NFP_F_CT_SET_VLAN_PRIO flag is not set\n", __func__);
return MV_BAD_PARAM;
}
NFP_DBG("NFP (ct vpri) set %p\n", ct);
return MV_OK;
}
static INLINE MV_STATUS mvNfpIsVpriSet(NFP_RULE_CT *ct)
{
int i;
for (i = 0; i <= NFP_VPRI_MAP_GLOBAL; i++)
if (ct->vpri_map[i].valid)
return MV_TRUE;
return MV_FALSE;
}
MV_STATUS mvNfpCtVlanPrioRuleDel(NFP_RULE_CT *ct2, int prio)
{
NFP_RULE_CT *ct;
/* sanity: chack prio parameter */
if ((prio < MV_ETH_NFP_GLOBAL_MAP) || (prio > NFP_VPRI_MAP_GLOBAL)) {
mvOsPrintf("%s Error: prio value (%d) is out of range\n", __func__, prio);
return MV_BAD_PARAM;
}
/* Rule must exist already, but it can be valid or invalid */
ct = mvNfpCtLookup(ct2);
if (!ct)
ct = mvNfpCtRuleInvalidLookup(ct2);
if (ct) {
if (prio == MV_ETH_NFP_GLOBAL_MAP) {
ct->vpri_map[NFP_VPRI_MAP_GLOBAL].new_prio = 0;
ct->vpri_map[NFP_VPRI_MAP_GLOBAL].valid = MV_FALSE;
} else {
ct->vpri_map[prio].new_prio = 0;
ct->vpri_map[prio].valid = MV_FALSE;
}
if (!mvNfpIsVpriSet(ct))
ct->flags &= ~NFP_F_CT_SET_VLAN_PRIO;
return MV_OK;
}
return MV_NOT_FOUND;
}
MV_STATUS mvNfpCtTxqRuleAdd(NFP_RULE_CT *ct2, int dscp, int txq)
{
NFP_RULE_CT *ct;
/* sanity: chack txq parameter */
if ((txq < 0) || (txq >= CONFIG_MV_ETH_TXQ)) {
mvOsPrintf("%s Error: txq (%d) is out of range\n", __func__, txq);
return MV_BAD_PARAM;
}
/* Rule must exist already, but it can be valid or invalid */
ct = mvNfpCtLookup(ct2);
if (!ct)
ct = mvNfpCtRuleInvalidLookup(ct2);
/* rule is not exist - create rule from classification context */
if (!ct) {
ct = mvNfpCtClassifyRuleCreate(ct2);
if (!ct)
return MV_FAIL;
}
/* Update rule DSCP to TXQ map table */
if (ct2->flags & NFP_F_CT_SET_TXQ) {
if (dscp == MV_ETH_NFP_GLOBAL_MAP) {
ct->txq_map[NFP_DSCP_MAP_GLOBAL].txq = txq;
ct->txq_map[NFP_DSCP_MAP_GLOBAL].valid = MV_TRUE;
} else if ((dscp >= NFP_DSCP_MIN) && (dscp <= NFP_DSCP_MAX)) {
ct->txq_map[dscp].txq = txq;
ct->txq_map[dscp].valid = MV_TRUE;
} else {
mvOsPrintf("%s Error: dscp value (%d) is out of range\n", __func__, dscp);
return MV_BAD_PARAM;
}
ct->flags |= NFP_F_CT_SET_TXQ;
} else {
mvOsPrintf("%s Error: NFP_F_CT_SET_TXQ flag is not set\n", __func__);
return MV_BAD_PARAM;
}
NFP_DBG("NFP (ct txq) set %p\n", ct);
return MV_OK;
}
static INLINE MV_STATUS mvNfpIsTxqSet(NFP_RULE_CT *ct)
{
int i;
for (i = 0; i <= NFP_DSCP_MAP_GLOBAL; i++)
if (ct->txq_map[i].valid)
return MV_TRUE;
return MV_FALSE;
}
MV_STATUS mvNfpCtTxqRuleDel(NFP_RULE_CT *ct2, int dscp)
{
NFP_RULE_CT *ct;
/* Rule must exist already, but it can be valid or invalid */
ct = mvNfpCtLookup(ct2);
if (!ct)
ct = mvNfpCtRuleInvalidLookup(ct2);
if (ct) {
if (dscp == MV_ETH_NFP_GLOBAL_MAP) {
ct->txq_map[NFP_DSCP_MAP_GLOBAL].txq = 0;
ct->txq_map[NFP_DSCP_MAP_GLOBAL].valid = MV_FALSE;
} else if ((dscp >= NFP_DSCP_MIN) && (dscp <= NFP_DSCP_MAX)) {
ct->txq_map[dscp].txq = 0;
ct->txq_map[dscp].valid = MV_FALSE;
} else {
mvOsPrintf("%s Error: dscp value (%d) is out of range\n", __func__, dscp);
return MV_BAD_PARAM;
}
if (!mvNfpIsTxqSet(ct))
ct->flags &= ~NFP_F_CT_SET_TXQ;
return MV_OK;
}
return MV_NOT_FOUND;
}
MV_STATUS mvNfpCtTxpRuleAdd(NFP_RULE_CT *ct2)
{
NFP_RULE_CT *ct;
/* Rule must exist already, but it can be valid or invalid */
ct = mvNfpCtLookup(ct2);
if (!ct)
ct = mvNfpCtRuleInvalidLookup(ct2);
/* rule is not exist - create rule from classification context */
if (!ct) {
ct = mvNfpCtClassifyRuleCreate(ct2);
if (!ct)
return MV_FAIL;
}
/* Update rule Txq table */
if (ct2->flags & NFP_F_CT_SET_TXP) {
ct->txp = ct2->txp;
ct->flags |= NFP_F_CT_SET_TXP;
} else {
mvOsPrintf("%s: NFP_F_CT_SET_TXP flag is not set\n", __func__);
return MV_BAD_PARAM;
}
NFP_DBG("NFP (ct txp) set %p\n", ct);
return MV_OK;
}
MV_STATUS mvNfpCtTxpRuleDel(NFP_RULE_CT *ct2)
{
NFP_RULE_CT *ct;
/* Rule must exist already, but it can be valid or invalid */
ct = mvNfpCtLookup(ct2);
if (!ct)
ct = mvNfpCtRuleInvalidLookup(ct2);
if (ct) {
ct->txp = 0;
ct->flags &= ~NFP_F_CT_SET_TXP;
return MV_OK;
}
return MV_NOT_FOUND;
}
MV_STATUS mvNfpCtMhRuleAdd(NFP_RULE_CT *ct2)
{
NFP_RULE_CT *ct;
/* sanity: chack mh parameter */
if (ct2->mh < 0) {
mvOsPrintf("%s Error: mh (%d) is out of range\n", __func__, ct2->mh);
return MV_BAD_PARAM;
}
/* Rule must exist already, but it can be valid or invalid */
ct = mvNfpCtLookup(ct2);
if (!ct)
ct = mvNfpCtRuleInvalidLookup(ct2);
/* rule is not exist - create rule from classification context */
if (!ct) {
ct = mvNfpCtClassifyRuleCreate(ct2);
if (!ct)
return MV_FAIL;
}
/* Update rule MH table */
if (ct2->flags & NFP_F_CT_SET_MH) {
ct->mh = ct2->mh;
ct->flags |= NFP_F_CT_SET_MH;
} else {
mvOsPrintf("%s: NFP_F_CT_SET_MH flag is not set\n", __func__);
return MV_BAD_PARAM;
}
NFP_DBG("NFP (ct mh) set %p\n", ct);
return MV_OK;
}
MV_STATUS mvNfpCtMhRuleDel(NFP_RULE_CT *ct2)
{
NFP_RULE_CT *ct;
/* Rule must exist already, but it can be valid or invalid */
ct = mvNfpCtLookup(ct2);
if (!ct)
ct = mvNfpCtRuleInvalidLookup(ct2);
if (ct) {
ct->mh = 0;
ct->flags &= ~NFP_F_CT_SET_MH;
return MV_OK;
}
return MV_NOT_FOUND;
}
#endif /* NFP_CLASSIFY */
MV_STATUS mvNfpCtRuleDel(NFP_RULE_CT *ct2)
{
MV_U32 hash;
NFP_RULE_CT *ct, *prev;
MV_LIST_ELEMENT *element;
/* If this rule currently exists in the Invalid Rules DB, delete it */
ct = mvNfpCtRuleInvalidLookup(ct2);
if (ct) {
if (ct->fib && !(ct->flags & NFP_F_CT_FIB_INV))
(ct->fib)->ct_ref--;
if ((ct->fib) && (ct->flags & NFP_F_CT_HWF) && !(ct->flags & NFP_F_CT_FIB_INV))
(ct->fib)->ct_hwf_ref--;
element = mvListFind(ct_inv_list, (MV_ULONG)ct);
#ifdef NFP_LIMIT
if (ct->tbfInfo)
mvNfpCtRateLimitDel(ct);
#endif /* NFP_LIMIT */
mvListDel(element);
mvOsFree(ct);
return MV_OK;
}
hash = mvNfpCtHash(ct2);
ct = ct_hash[hash];
prev = NULL;
while (ct) {
if ((ct->family == ct2->family) &&
l3_addr_eq(ct->family, ct->srcL3, ct2->srcL3) &&
l3_addr_eq(ct->family, ct->dstL3, ct2->dstL3) &&
(ct->ports == ct2->ports) &&
(ct->proto == ct2->proto)) {
if (ct->fib && !(ct->flags & NFP_F_CT_FIB_INV))
(ct->fib)->ct_ref--;
if ((ct->fib) && (ct->flags & NFP_F_CT_HWF) && !(ct->flags & NFP_F_CT_FIB_INV))
(ct->fib)->ct_hwf_ref--;
if (prev)
prev->next = ct->next;
else
ct_hash[hash] = ct->next;
NFP_DBG("NFP (ct) del %p\n", ct);
#ifdef NFP_LIMIT
if (ct->tbfInfo)
mvNfpCtRateLimitDel(ct);
#endif /* NFP_LIMIT */
mvOsFree(ct);
return MV_OK;
}
prev = ct;
ct = ct->next;
}
return MV_NOT_FOUND;
}
void mvNfpCtClean(int family)
{
int i;
NFP_RULE_CT *ct, *next;
#ifdef NFP_LIMIT
NFP_TBF_INFO *tbf_data;
#endif /* NFP_LIMIT */
MV_LIST_ELEMENT *curr, *tmp;
/* Clean CT incomplete rules list */
if (ct_inv_list) {
curr = ct_inv_list->next;
while (curr) {
tmp = curr->next;
ct = (NFP_RULE_CT *)curr->data;
if (ct->family == family) {
#ifdef NFP_LIMIT
if (ct->tbfInfo)
mvNfpCtRateLimitDel(ct);
#endif /* NFP_LIMIT */
mvOsFree(ct);
mvListDel(curr);
}
curr = tmp;
}
}
/* Clean CT hash table */
for (i = 0; i < NFP_CT_HASH_SIZE; i++) {
ct = ct_hash[i];
while (ct) {
next = ct->next;
if (ct->family == family) {
#ifdef NFP_LIMIT
if (ct->tbfInfo)
mvNfpCtRateLimitDel(ct);
#endif /* NFP_LIMIT */
mvOsFree(ct);
}
ct = next;
}
ct_hash[i] = NULL;
}
#ifdef NFP_LIMIT
/* clean tbfs */
if (tbfs) {
curr = tbfs->next;
while (curr) {
tbf_data = (NFP_TBF_INFO *)mvListDel(curr);
mvOsFree(tbf_data);
curr = tbfs->next;
}
}
#endif /* NFP_LIMIT */
}
void mvNfpCtDestroy(void)
{
if (ct_hash)
mvOsFree(ct_hash);
#ifdef NFP_LIMIT
if (tbfs)
mvOsFree(tbfs);
#endif /* NFP_LIMIT */
mvListDestroy(ct_inv_list);
}
MV_VOID mvNfpCtCleanVisited(MV_U32 row, MV_U32 iterator_id)
{
NFP_RULE_CT *curr = ct_hash[row];
while (curr) {
curr->visited = curr->visited & ~(1 << iterator_id);
curr = curr->next;
}
}
MV_STATUS mvNfpCtFirstRuleGet(NFP_RULE_CT **rule, MV_U32 iterator_id)
{
mvNfpCtCleanVisited(ct_iterator_row, iterator_id);
ct_iterator_row = 0;
return mvNfpCtNextRuleGet(rule, iterator_id);
}
MV_STATUS mvNfpCtNextRuleGet(NFP_RULE_CT **rule, MV_U32 iterator_id)
{
NFP_RULE_CT *curr;
while (ct_iterator_row < NFP_CT_HASH_SIZE) {
curr = ct_hash[ct_iterator_row];
/* skip visited and HWF processed rules */
while (curr && ((curr->visited & (1 << iterator_id))
|| (curr->flags & NFP_F_CT_HWF)))
curr = curr->next;
if (!curr) { /* reached end of line */
mvNfpCtCleanVisited(ct_iterator_row, iterator_id);
ct_iterator_row++;
continue;
}
curr->visited = 1; /* update - this rule is now visited by iterator */
*rule = curr;
return MV_OK;
}
/* reached end of DB - no rule is found */
ct_iterator_row = 0; /* next call start from the begining of the DB */
return MV_NOT_FOUND;
}
MV_STATUS mvNfpCtRuleMaxHitCntrGet(NFP_RULE_CT **rule)
{
int i;
MV_U32 max = -1;
NFP_RULE_CT *curr, *max_rule = NULL;
for (i = 0; i < NFP_CT_HASH_SIZE; i++) {
curr = ct_hash[i];
while (curr) {
if (curr->flags & NFP_F_CT_HWF) {
curr = curr->next;
continue;
}
if (curr->hit_cntr > max || !max_rule) {
max = curr->hit_cntr;
max_rule = curr;
}
curr = curr->next;
}
}
if (!max_rule)
return MV_NOT_FOUND;
*rule = max_rule;
return MV_OK;
}
/* 0 - set zero to UDP csum on TX, 1 - recalculate UDP csum on TX */
MV_STATUS mvNfpCtRuleUdpCsumSet(NFP_RULE_CT *ct2, int mode)
{
NFP_RULE_CT *ct;
if (!ct2 || (mode < 0) || (mode > 1) || (ct2->proto != MV_IP_PROTO_UDP))
return MV_BAD_PARAM;
ct = mvNfpCtLookup(ct2);
if (!ct)
ct = mvNfpCtRuleInvalidLookup(ct2);
if (!ct)
return MV_NOT_FOUND;
if (mode)
ct->flags |= NFP_F_CT_UDP_CSUM;
else
ct->flags &= ~NFP_F_CT_UDP_CSUM;
return MV_OK;
}
/* mode = 1:HWF, 0:NFP */
MV_STATUS mvNfpCtRuleHwfSet(MV_NFP_CT_KEY *key, int mode)
{
NFP_RULE_CT *rule;
MV_U32 nflags = 0, ports;
if (!key || (mode < 0) || (mode > 1))
return MV_BAD_PARAM;
ports = (MV_16BIT_BE(key->dport) << 16) | MV_16BIT_BE(key->sport);
rule = mvNfpCtLookupByTuple(key->family, key->src_l3, key->dst_l3, ports, key->proto);
if (!rule)
return MV_NOT_FOUND;
/* sanity check */
if (!rule->fib || (rule->flags & NFP_F_CT_FIB_INV))
return MV_NOT_FOUND;
if (mode)
nflags = (rule->flags | NFP_F_CT_HWF);
else
nflags = (rule->flags & ~NFP_F_CT_HWF);
if (rule->flags != nflags) {
/* Mode is changed - update hwf ref count */
if (mode) /* go to HWF mode */
rule->fib->ct_hwf_ref++;
else {
/* exit from HWF mode */
rule->fib->ct_hwf_ref--;
rule->hit_cntr = 0;
}
}
rule->flags = nflags;
return MV_OK;
}
MV_STATUS mvNfpCtRuleHitCntrGet(MV_NFP_CT_KEY *key, MV_U32 *hit_cntr)
{
NFP_RULE_CT *rule;
MV_U32 ports;
if (!key || !hit_cntr)
return MV_BAD_PARAM;
ports = (MV_16BIT_BE(key->dport) << 16) | MV_16BIT_BE(key->sport);
rule = mvNfpCtLookupByTuple(key->family, key->src_l3, key->dst_l3, ports, key->proto);
if (!rule)
return MV_NOT_FOUND;
*hit_cntr = rule->hit_cntr;
return MV_OK;
}
MV_STATUS mvNfpCtRuleHitCntrSet(MV_NFP_CT_KEY *key, MV_U32 val)
{
NFP_RULE_CT *rule;
MV_U32 ports;
if (!key || val < 0)
return MV_BAD_PARAM;
ports = (MV_16BIT_BE(key->dport) << 16) | MV_16BIT_BE(key->sport);
rule = mvNfpCtLookupByTuple(key->family, key->src_l3, key->dst_l3, ports, key->proto);
if (!rule)
return MV_NOT_FOUND;
rule->hit_cntr = val;
return MV_OK;
}
MV_STATUS mvNfpCtRuleInfoGet(MV_NFP_CT_KEY *key, MV_NFP_CT_INFO *ct_info)
{
NFP_RULE_CT *rule;
NFP_IF_MAP *ifMap;
MV_U32 ports;
if (!key || !ct_info)
return MV_BAD_PARAM;
ports = (MV_16BIT_BE(key->dport) << 16) | MV_16BIT_BE(key->sport);
rule = mvNfpCtLookupByTuple(key->family, key->src_l3, key->dst_l3, ports, key->proto);
if (!rule)
return MV_NOT_FOUND;
ct_info->flags = rule->flags;
ct_info->new_sip = rule->new_sip;
ct_info->new_dip = rule->new_dip;
ct_info->new_sport = rule->new_sport;
ct_info->new_dport = rule->new_dport;
if (rule->fib) {
memcpy(ct_info->sa, (rule->fib)->sa, MV_MAC_ADDR_SIZE);
memcpy(ct_info->da, (rule->fib)->da, MV_MAC_ADDR_SIZE);
ifMap = mvNfpIfMapGet((rule->fib)->oif);
ct_info->out_port = ifMap->port;
}
#ifdef NFP_CLASSIFY
ct_info->mh = rule->mh;
ct_info->txp = rule->txp;
/* return only global txq/dscp/vprio mapping */
ct_info->txq = (rule->txq_map[NFP_DSCP_MAP_GLOBAL]).txq;
if (!(rule->txq_map[NFP_DSCP_MAP_GLOBAL]).valid)
ct_info->flags = ct_info->flags & ~NFP_F_CT_SET_TXQ;
ct_info->dscp = (rule->dscp_map[NFP_DSCP_MAP_GLOBAL]).new_dscp;
if (!(rule->dscp_map[NFP_DSCP_MAP_GLOBAL]).valid)
ct_info->flags = ct_info->flags & ~NFP_F_CT_SET_DSCP;
ct_info->vprio = (rule->vpri_map[NFP_VPRI_MAP_GLOBAL]).new_prio;
if (!(rule->vpri_map[NFP_VPRI_MAP_GLOBAL]).valid)
ct_info->flags = ct_info->flags & ~NFP_F_CT_SET_VLAN_PRIO;
#endif /* NFP_CLASSIFY */
return MV_OK;
}
void mvNfpCtDump(void)
{
MV_U32 i;
NFP_RULE_CT *ct;
MV_LIST_ELEMENT *curr;
mvOsPrintf("\n(ct hash)\n");
for (i = 0; i < NFP_CT_HASH_SIZE; i++) {
ct = ct_hash[i];
while (ct) {
mvOsPrintf(" [%2d] ", i);
mvNfpCtRulePrint(ct);
ct = ct->next;
}
}
if (!ct_inv_list) {
mvOsPrintf("(ct_inv_list) does not exist\n");
return;
}
mvOsPrintf("(ct_inv_list)\n");
curr = ct_inv_list->next;
while (curr) {
ct = (NFP_RULE_CT *)curr->data;
mvNfpCtRulePrint(ct);
curr = curr->next;
}
}