summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c144
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c76
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api.c233
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api_client.h14
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c526
-rw-r--r--include/net/flow_offload.h6
-rw-r--r--net/core/flow_offload.c7
7 files changed, 990 insertions, 16 deletions
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c
index 7b364f6b4546..a48baeacc1d2 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c
@@ -21,15 +21,34 @@ struct sparx5_tc_flower_parse_usage {
unsigned int used_keys;
};
+struct sparx5_tc_rule_pkt_cnt {
+ u64 cookie;
+ u32 pkts;
+};
+
/* These protocols have dedicated keysets in IS2 and a TC dissector
* ETH_P_ARP does not have a TC dissector
*/
static u16 sparx5_tc_known_etypes[] = {
ETH_P_ALL,
+ ETH_P_ARP,
ETH_P_IP,
ETH_P_IPV6,
};
+enum sparx5_is2_arp_opcode {
+ SPX5_IS2_ARP_REQUEST,
+ SPX5_IS2_ARP_REPLY,
+ SPX5_IS2_RARP_REQUEST,
+ SPX5_IS2_RARP_REPLY,
+};
+
+enum tc_arp_opcode {
+ TC_ARP_OP_RESERVED,
+ TC_ARP_OP_REQUEST,
+ TC_ARP_OP_REPLY,
+};
+
static bool sparx5_tc_is_known_etype(u16 etype)
{
int idx;
@@ -405,6 +424,67 @@ out:
}
static int
+sparx5_tc_flower_handler_arp_usage(struct sparx5_tc_flower_parse_usage *st)
+{
+ struct flow_match_arp mt;
+ u16 value, mask;
+ u32 ipval, ipmsk;
+ int err;
+
+ flow_rule_match_arp(st->frule, &mt);
+
+ if (mt.mask->op) {
+ mask = 0x3;
+ if (st->l3_proto == ETH_P_ARP) {
+ value = mt.key->op == TC_ARP_OP_REQUEST ?
+ SPX5_IS2_ARP_REQUEST :
+ SPX5_IS2_ARP_REPLY;
+ } else { /* RARP */
+ value = mt.key->op == TC_ARP_OP_REQUEST ?
+ SPX5_IS2_RARP_REQUEST :
+ SPX5_IS2_RARP_REPLY;
+ }
+ err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ARP_OPCODE,
+ value, mask);
+ if (err)
+ goto out;
+ }
+
+ /* The IS2 ARP keyset does not support ARP hardware addresses */
+ if (!is_zero_ether_addr(mt.mask->sha) ||
+ !is_zero_ether_addr(mt.mask->tha))
+ goto out;
+
+ if (mt.mask->sip) {
+ ipval = be32_to_cpu((__force __be32)mt.key->sip);
+ ipmsk = be32_to_cpu((__force __be32)mt.mask->sip);
+
+ err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_SIP,
+ ipval, ipmsk);
+ if (err)
+ goto out;
+ }
+
+ if (mt.mask->tip) {
+ ipval = be32_to_cpu((__force __be32)mt.key->tip);
+ ipmsk = be32_to_cpu((__force __be32)mt.mask->tip);
+
+ err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_DIP,
+ ipval, ipmsk);
+ if (err)
+ goto out;
+ }
+
+ st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ARP);
+
+ return err;
+
+out:
+ NL_SET_ERR_MSG_MOD(st->fco->common.extack, "arp parse error");
+ return err;
+}
+
+static int
sparx5_tc_flower_handler_ip_usage(struct sparx5_tc_flower_parse_usage *st)
{
struct flow_match_ip mt;
@@ -438,6 +518,7 @@ static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_us
[FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage,
[FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage,
[FLOW_DISSECTOR_KEY_TCP] = sparx5_tc_flower_handler_tcp_usage,
+ [FLOW_DISSECTOR_KEY_ARP] = sparx5_tc_flower_handler_arp_usage,
[FLOW_DISSECTOR_KEY_IP] = sparx5_tc_flower_handler_ip_usage,
};
@@ -529,6 +610,20 @@ static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
return 0;
}
+/* Add a rule counter action - only IS2 is considered for now */
+static int sparx5_tc_add_rule_counter(struct vcap_admin *admin,
+ struct vcap_rule *vrule)
+{
+ int err;
+
+ err = vcap_rule_add_action_u32(vrule, VCAP_AF_CNT_ID, vrule->id);
+ if (err)
+ return err;
+
+ vcap_rule_set_counter_id(vrule, vrule->id);
+ return err;
+}
+
static int sparx5_tc_flower_replace(struct net_device *ndev,
struct flow_cls_offload *fco,
struct vcap_admin *admin)
@@ -554,6 +649,11 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
vrule->cookie = fco->cookie;
sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto);
+
+ err = sparx5_tc_add_rule_counter(admin, vrule);
+ if (err)
+ goto out;
+
frule = flow_cls_offload_flow_rule(fco);
flow_action_for_each(idx, act, &frule->action) {
switch (act->id) {
@@ -632,6 +732,48 @@ static int sparx5_tc_flower_destroy(struct net_device *ndev,
return err;
}
+/* Collect packet counts from all rules with the same cookie */
+static int sparx5_tc_rule_counter_cb(void *arg, struct vcap_rule *rule)
+{
+ struct sparx5_tc_rule_pkt_cnt *rinfo = arg;
+ struct vcap_counter counter;
+ int err = 0;
+
+ if (rule->cookie == rinfo->cookie) {
+ err = vcap_rule_get_counter(rule, &counter);
+ if (err)
+ return err;
+ rinfo->pkts += counter.value;
+ /* Reset the rule counter */
+ counter.value = 0;
+ vcap_rule_set_counter(rule, &counter);
+ }
+ return err;
+}
+
+static int sparx5_tc_flower_stats(struct net_device *ndev,
+ struct flow_cls_offload *fco,
+ struct vcap_admin *admin)
+{
+ struct sparx5_port *port = netdev_priv(ndev);
+ struct sparx5_tc_rule_pkt_cnt rinfo = {};
+ struct vcap_control *vctrl;
+ ulong lastused = 0;
+ u64 drops = 0;
+ u32 pkts = 0;
+ int err;
+
+ rinfo.cookie = fco->cookie;
+ vctrl = port->sparx5->vcap_ctrl;
+ err = vcap_rule_iter(vctrl, sparx5_tc_rule_counter_cb, &rinfo);
+ if (err)
+ return err;
+ pkts = rinfo.pkts;
+ flow_stats_update(&fco->stats, 0x0, pkts, drops, lastused,
+ FLOW_ACTION_HW_STATS_IMMEDIATE);
+ return err;
+}
+
int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,
bool ingress)
{
@@ -653,6 +795,8 @@ int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,
return sparx5_tc_flower_replace(ndev, fco, admin);
case FLOW_CLS_DESTROY:
return sparx5_tc_flower_destroy(ndev, fco, admin);
+ case FLOW_CLS_STATS:
+ return sparx5_tc_flower_stats(ndev, fco, admin);
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
index 10bc56cd0045..e8f3d030eba2 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
@@ -428,15 +428,58 @@ static void sparx5_vcap_cache_write(struct net_device *ndev,
default:
break;
}
+ if (sel & VCAP_SEL_COUNTER) {
+ start = start & 0xfff; /* counter limit */
+ if (admin->vinst == 0)
+ spx5_wr(admin->cache.counter, sparx5,
+ ANA_ACL_CNT_A(start));
+ else
+ spx5_wr(admin->cache.counter, sparx5,
+ ANA_ACL_CNT_B(start));
+ spx5_wr(admin->cache.sticky, sparx5,
+ VCAP_SUPER_VCAP_CNT_DAT(0));
+ }
}
/* API callback used for reading from the VCAP into the VCAP cache */
static void sparx5_vcap_cache_read(struct net_device *ndev,
struct vcap_admin *admin,
- enum vcap_selection sel, u32 start,
+ enum vcap_selection sel,
+ u32 start,
u32 count)
{
- /* this will be added later */
+ struct sparx5_port *port = netdev_priv(ndev);
+ struct sparx5 *sparx5 = port->sparx5;
+ u32 *keystr, *mskstr, *actstr;
+ int idx;
+
+ keystr = &admin->cache.keystream[start];
+ mskstr = &admin->cache.maskstream[start];
+ actstr = &admin->cache.actionstream[start];
+ if (sel & VCAP_SEL_ENTRY) {
+ for (idx = 0; idx < count; ++idx) {
+ keystr[idx] = spx5_rd(sparx5,
+ VCAP_SUPER_VCAP_ENTRY_DAT(idx));
+ mskstr[idx] = ~spx5_rd(sparx5,
+ VCAP_SUPER_VCAP_MASK_DAT(idx));
+ }
+ }
+ if (sel & VCAP_SEL_ACTION) {
+ for (idx = 0; idx < count; ++idx)
+ actstr[idx] = spx5_rd(sparx5,
+ VCAP_SUPER_VCAP_ACTION_DAT(idx));
+ }
+ if (sel & VCAP_SEL_COUNTER) {
+ start = start & 0xfff; /* counter limit */
+ if (admin->vinst == 0)
+ admin->cache.counter =
+ spx5_rd(sparx5, ANA_ACL_CNT_A(start));
+ else
+ admin->cache.counter =
+ spx5_rd(sparx5, ANA_ACL_CNT_B(start));
+ admin->cache.sticky =
+ spx5_rd(sparx5, VCAP_SUPER_VCAP_CNT_DAT(0));
+ }
}
/* API callback used for initializing a VCAP address range */
@@ -477,7 +520,32 @@ static void sparx5_vcap_update(struct net_device *ndev,
static void sparx5_vcap_move(struct net_device *ndev, struct vcap_admin *admin,
u32 addr, int offset, int count)
{
- /* this will be added later */
+ struct sparx5_port *port = netdev_priv(ndev);
+ struct sparx5 *sparx5 = port->sparx5;
+ enum vcap_command cmd;
+ u16 mv_num_pos;
+ u16 mv_size;
+
+ mv_size = count - 1;
+ if (offset > 0) {
+ mv_num_pos = offset - 1;
+ cmd = VCAP_CMD_MOVE_DOWN;
+ } else {
+ mv_num_pos = -offset - 1;
+ cmd = VCAP_CMD_MOVE_UP;
+ }
+ spx5_wr(VCAP_SUPER_CFG_MV_NUM_POS_SET(mv_num_pos) |
+ VCAP_SUPER_CFG_MV_SIZE_SET(mv_size),
+ sparx5, VCAP_SUPER_CFG);
+ spx5_wr(VCAP_SUPER_CTRL_UPDATE_CMD_SET(cmd) |
+ VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET(0) |
+ VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET(0) |
+ VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET(0) |
+ VCAP_SUPER_CTRL_UPDATE_ADDR_SET(addr) |
+ VCAP_SUPER_CTRL_CLEAR_CACHE_SET(false) |
+ VCAP_SUPER_CTRL_UPDATE_SHOT_SET(true),
+ sparx5, VCAP_SUPER_CTRL);
+ sparx5_vcap_wait_super_update(sparx5);
}
/* Provide port information via a callback interface */
@@ -540,7 +608,7 @@ static void sparx5_vcap_port_key_selection(struct sparx5 *sparx5,
VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER,
VCAP_IS2_PS_IPV6_MC_IP_7TUPLE,
VCAP_IS2_PS_IPV6_UC_IP_7TUPLE,
- VCAP_IS2_PS_ARP_MAC_ETYPE);
+ VCAP_IS2_PS_ARP_ARP);
for (lookup = 0; lookup < admin->lookups; ++lookup) {
for (portno = 0; portno < SPX5_PORTS; ++portno) {
spx5_wr(keysel, sparx5,
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c
index b6ab6bae28c0..d12c8ec40fe2 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c
@@ -25,6 +25,8 @@ struct vcap_rule_internal {
int actionset_sw_regs; /* registers in a subword in an actionset */
int size; /* the size of the rule: max(entry, action) */
u32 addr; /* address in the VCAP at insertion */
+ u32 counter_id; /* counter id (if a dedicated counter is available) */
+ struct vcap_counter counter; /* last read counter value */
};
/* Moving a rule in the VCAP address space */
@@ -651,6 +653,20 @@ static int vcap_write_rule(struct vcap_rule_internal *ri)
return 0;
}
+static int vcap_write_counter(struct vcap_rule_internal *ri,
+ struct vcap_counter *ctr)
+{
+ struct vcap_admin *admin = ri->admin;
+
+ admin->cache.counter = ctr->value;
+ admin->cache.sticky = ctr->sticky;
+ ri->vctrl->ops->cache_write(ri->ndev, admin, VCAP_SEL_COUNTER,
+ ri->counter_id, 0);
+ ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_WRITE,
+ VCAP_SEL_COUNTER, ri->addr);
+ return 0;
+}
+
/* Convert a chain id to a VCAP lookup index */
int vcap_chain_id_to_lookup(struct vcap_admin *admin, int cur_cid)
{
@@ -941,6 +957,16 @@ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto)
}
EXPORT_SYMBOL_GPL(vcap_val_rule);
+/* Entries are sorted with increasing values of sort_key.
+ * I.e. Lowest numerical sort_key is first in list.
+ * In order to locate largest keys first in list we negate the key size with
+ * (max_size - size).
+ */
+static u32 vcap_sort_key(u32 max_size, u32 size, u8 user, u16 prio)
+{
+ return ((max_size - size) << 24) | (user << 16) | prio;
+}
+
/* calculate the address of the next rule after this (lower address and prio) */
static u32 vcap_next_rule_addr(u32 addr, struct vcap_rule_internal *ri)
{
@@ -970,17 +996,67 @@ static u32 vcap_set_rule_id(struct vcap_rule_internal *ri)
static int vcap_insert_rule(struct vcap_rule_internal *ri,
struct vcap_rule_move *move)
{
+ int sw_count = ri->vctrl->vcaps[ri->admin->vtype].sw_count;
+ struct vcap_rule_internal *duprule, *iter, *elem = NULL;
struct vcap_admin *admin = ri->admin;
- struct vcap_rule_internal *duprule;
+ u32 addr;
+
+ ri->sort_key = vcap_sort_key(sw_count, ri->size, ri->data.user,
+ ri->data.priority);
+
+ /* Insert the new rule in the list of rule based on the sort key
+ * If the rule needs to be inserted between existing rules then move
+ * these rules to make room for the new rule and update their start
+ * address.
+ */
+ list_for_each_entry(iter, &admin->rules, list) {
+ if (ri->sort_key < iter->sort_key) {
+ elem = iter;
+ break;
+ }
+ }
+
+ if (!elem) {
+ ri->addr = vcap_next_rule_addr(admin->last_used_addr, ri);
+ admin->last_used_addr = ri->addr;
+
+ /* Add a shallow copy of the rule to the VCAP list */
+ duprule = vcap_dup_rule(ri);
+ if (IS_ERR(duprule))
+ return PTR_ERR(duprule);
+
+ list_add_tail(&duprule->list, &admin->rules);
+ return 0;
+ }
+
+ /* Reuse the space of the current rule */
+ addr = elem->addr + elem->size;
+ ri->addr = vcap_next_rule_addr(addr, ri);
+ addr = ri->addr;
- /* Only support appending rules for now */
- ri->addr = vcap_next_rule_addr(admin->last_used_addr, ri);
- admin->last_used_addr = ri->addr;
/* Add a shallow copy of the rule to the VCAP list */
duprule = vcap_dup_rule(ri);
if (IS_ERR(duprule))
return PTR_ERR(duprule);
- list_add_tail(&duprule->list, &admin->rules);
+
+ /* Add before the current entry */
+ list_add_tail(&duprule->list, &elem->list);
+
+ /* Update the current rule */
+ elem->addr = vcap_next_rule_addr(addr, elem);
+ addr = elem->addr;
+
+ /* Update the address in the remaining rules in the list */
+ list_for_each_entry_continue(elem, &admin->rules, list) {
+ elem->addr = vcap_next_rule_addr(addr, elem);
+ addr = elem->addr;
+ }
+
+ /* Update the move info */
+ move->addr = admin->last_used_addr;
+ move->count = ri->addr - addr;
+ move->offset = admin->last_used_addr - addr;
+ admin->last_used_addr = addr;
return 0;
}
@@ -1032,8 +1108,11 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl,
{
struct vcap_rule_internal *ri;
struct vcap_admin *admin;
- int maxsize;
+ int err, maxsize;
+ err = vcap_api_check(vctrl);
+ if (err)
+ return ERR_PTR(err);
if (!ndev)
return ERR_PTR(-ENODEV);
/* Get the VCAP instance */
@@ -1098,12 +1177,57 @@ void vcap_free_rule(struct vcap_rule *rule)
}
EXPORT_SYMBOL_GPL(vcap_free_rule);
+/* Return the alignment offset for a new rule address */
+static int vcap_valid_rule_move(struct vcap_rule_internal *el, int offset)
+{
+ return (el->addr + offset) % el->size;
+}
+
+/* Update the rule address with an offset */
+static void vcap_adjust_rule_addr(struct vcap_rule_internal *el, int offset)
+{
+ el->addr += offset;
+}
+
+/* Rules needs to be moved to fill the gap of the deleted rule */
+static int vcap_fill_rule_gap(struct vcap_rule_internal *ri)
+{
+ struct vcap_admin *admin = ri->admin;
+ struct vcap_rule_internal *elem;
+ struct vcap_rule_move move;
+ int gap = 0, offset = 0;
+
+ /* If the first rule is deleted: Move other rules to the top */
+ if (list_is_first(&ri->list, &admin->rules))
+ offset = admin->last_valid_addr + 1 - ri->addr - ri->size;
+
+ /* Locate gaps between odd size rules and adjust the move */
+ elem = ri;
+ list_for_each_entry_continue(elem, &admin->rules, list)
+ gap += vcap_valid_rule_move(elem, ri->size);
+
+ /* Update the address in the remaining rules in the list */
+ elem = ri;
+ list_for_each_entry_continue(elem, &admin->rules, list)
+ vcap_adjust_rule_addr(elem, ri->size + gap + offset);
+
+ /* Update the move info */
+ move.addr = admin->last_used_addr;
+ move.count = ri->addr - admin->last_used_addr - gap;
+ move.offset = -(ri->size + gap + offset);
+
+ /* Do the actual move operation */
+ vcap_move_rules(ri, &move);
+
+ return gap + offset;
+}
+
/* Delete rule in a VCAP instance */
int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id)
{
struct vcap_rule_internal *ri, *elem;
struct vcap_admin *admin;
- int err;
+ int gap = 0, err;
/* This will later also handle rule moving */
if (!ndev)
@@ -1116,18 +1240,23 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id)
if (!ri)
return -EINVAL;
admin = ri->admin;
+
+ if (ri->addr > admin->last_used_addr)
+ gap = vcap_fill_rule_gap(ri);
+
+ /* Delete the rule from the list of rules and the cache */
list_del(&ri->list);
+ vctrl->ops->init(ndev, admin, admin->last_used_addr, ri->size + gap);
+ kfree(ri);
- /* delete the rule in the cache */
- vctrl->ops->init(ndev, admin, ri->addr, ri->size);
+ /* Update the last used address */
if (list_empty(&admin->rules)) {
admin->last_used_addr = admin->last_valid_addr;
} else {
- /* update the address range end marker from the last rule in the list */
- elem = list_last_entry(&admin->rules, struct vcap_rule_internal, list);
+ elem = list_last_entry(&admin->rules, struct vcap_rule_internal,
+ list);
admin->last_used_addr = elem->addr;
}
- kfree(ri);
return 0;
}
EXPORT_SYMBOL_GPL(vcap_del_rule);
@@ -1434,6 +1563,20 @@ int vcap_rule_add_action_u32(struct vcap_rule *rule,
}
EXPORT_SYMBOL_GPL(vcap_rule_add_action_u32);
+static int vcap_read_counter(struct vcap_rule_internal *ri,
+ struct vcap_counter *ctr)
+{
+ struct vcap_admin *admin = ri->admin;
+
+ ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_READ, VCAP_SEL_COUNTER,
+ ri->addr);
+ ri->vctrl->ops->cache_read(ri->ndev, admin, VCAP_SEL_COUNTER,
+ ri->counter_id, 0);
+ ctr->value = admin->cache.counter;
+ ctr->sticky = admin->cache.sticky;
+ return 0;
+}
+
/* Copy to host byte order */
void vcap_netbytes_copy(u8 *dst, u8 *src, int count)
{
@@ -1577,6 +1720,72 @@ int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev,
}
EXPORT_SYMBOL_GPL(vcap_enable_lookups);
+/* Set a rule counter id (for certain vcaps only) */
+void vcap_rule_set_counter_id(struct vcap_rule *rule, u32 counter_id)
+{
+ struct vcap_rule_internal *ri = to_intrule(rule);
+
+ ri->counter_id = counter_id;
+}
+EXPORT_SYMBOL_GPL(vcap_rule_set_counter_id);
+
+/* Provide all rules via a callback interface */
+int vcap_rule_iter(struct vcap_control *vctrl,
+ int (*callback)(void *, struct vcap_rule *), void *arg)
+{
+ struct vcap_rule_internal *ri;
+ struct vcap_admin *admin;
+ int ret;
+
+ ret = vcap_api_check(vctrl);
+ if (ret)
+ return ret;
+
+ /* Iterate all rules in each VCAP instance */
+ list_for_each_entry(admin, &vctrl->list, list) {
+ list_for_each_entry(ri, &admin->rules, list) {
+ ret = callback(arg, &ri->data);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vcap_rule_iter);
+
+int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr)
+{
+ struct vcap_rule_internal *ri = to_intrule(rule);
+ int err;
+
+ err = vcap_api_check(ri->vctrl);
+ if (err)
+ return err;
+ if (!ctr) {
+ pr_err("%s:%d: counter is missing\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ return vcap_write_counter(ri, ctr);
+}
+EXPORT_SYMBOL_GPL(vcap_rule_set_counter);
+
+int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr)
+{
+ struct vcap_rule_internal *ri = to_intrule(rule);
+ int err;
+
+ err = vcap_api_check(ri->vctrl);
+ if (err)
+ return err;
+ if (!ctr) {
+ pr_err("%s:%d: counter is missing\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ return vcap_read_counter(ri, ctr);
+}
+EXPORT_SYMBOL_GPL(vcap_rule_get_counter);
+
#ifdef CONFIG_VCAP_KUNIT_TEST
#include "vcap_api_kunit.c"
#endif
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h
index 0ea5ec96adc8..654ef8fa6d62 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h
@@ -143,6 +143,11 @@ enum vcap_bit {
VCAP_BIT_1
};
+struct vcap_counter {
+ u32 value;
+ bool sticky;
+};
+
/* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */
int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev,
int chain_id, unsigned long cookie, bool enable);
@@ -170,6 +175,8 @@ int vcap_set_rule_set_keyset(struct vcap_rule *rule,
/* Update the actionset for the rule */
int vcap_set_rule_set_actionset(struct vcap_rule *rule,
enum vcap_actionfield_set actionset);
+/* Set a rule counter id (for certain VCAPs only) */
+void vcap_rule_set_counter_id(struct vcap_rule *rule, u32 counter_id);
/* VCAP rule field operations */
int vcap_rule_add_key_bit(struct vcap_rule *rule, enum vcap_key_field key,
@@ -187,6 +194,10 @@ int vcap_rule_add_action_bit(struct vcap_rule *rule,
int vcap_rule_add_action_u32(struct vcap_rule *rule,
enum vcap_action_field action, u32 value);
+/* VCAP rule counter operations */
+int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr);
+int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr);
+
/* VCAP lookup operations */
/* Convert a chain id to a VCAP lookup index */
int vcap_chain_id_to_lookup(struct vcap_admin *admin, int cur_cid);
@@ -199,6 +210,9 @@ const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule,
int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie);
/* Is the next chain id in the following lookup, possible in another VCAP */
bool vcap_is_next_lookup(struct vcap_control *vctrl, int cur_cid, int next_cid);
+/* Provide all rules via a callback interface */
+int vcap_rule_iter(struct vcap_control *vctrl,
+ int (*callback)(void *, struct vcap_rule *), void *arg);
/* Copy to host byte order */
void vcap_netbytes_copy(u8 *dst, u8 *src, int count);
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c
index b0ec51b37683..6858e44ce4a5 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c
@@ -23,6 +23,9 @@ static u32 test_init_count;
static u32 test_hw_counter_id;
static struct vcap_cache_data test_hw_cache;
static struct net_device test_netdev = {};
+static int test_move_addr;
+static int test_move_offset;
+static int test_move_count;
/* Callback used by the VCAP API */
static enum vcap_keyfield_set test_val_keyset(struct net_device *ndev,
@@ -195,6 +198,9 @@ static void test_cache_update(struct net_device *ndev, struct vcap_admin *admin,
static void test_cache_move(struct net_device *ndev, struct vcap_admin *admin,
u32 addr, int offset, int count)
{
+ test_move_addr = addr;
+ test_move_offset = offset;
+ test_move_count = count;
}
/* Provide port information via a callback interface */
@@ -242,6 +248,88 @@ static void vcap_test_api_init(struct vcap_admin *admin)
test_updateaddridx = 0;
}
+/* Helper function to create a rule of a specific size */
+static struct vcap_rule *
+test_vcap_xn_rule_creator(struct kunit *test, int cid, enum vcap_user user,
+ u16 priority,
+ int id, int size, int expected_addr)
+{
+ struct vcap_rule *rule = 0;
+ struct vcap_rule_internal *ri = 0;
+ enum vcap_keyfield_set keyset = VCAP_KFS_NO_VALUE;
+ enum vcap_actionfield_set actionset = VCAP_AFS_NO_VALUE;
+ int ret;
+
+ /* init before testing */
+ memset(test_updateaddr, 0, sizeof(test_updateaddr));
+ test_updateaddridx = 0;
+ test_move_addr = 0;
+ test_move_offset = 0;
+ test_move_count = 0;
+
+ switch (size) {
+ case 2:
+ keyset = VCAP_KFS_ETAG;
+ actionset = VCAP_AFS_CLASS_REDUCED;
+ break;
+ case 3:
+ keyset = VCAP_KFS_PURE_5TUPLE_IP4;
+ actionset = VCAP_AFS_CLASSIFICATION;
+ break;
+ case 6:
+ keyset = VCAP_KFS_NORMAL_5TUPLE_IP4;
+ actionset = VCAP_AFS_CLASSIFICATION;
+ break;
+ case 12:
+ keyset = VCAP_KFS_NORMAL_7TUPLE;
+ actionset = VCAP_AFS_FULL;
+ break;
+ default:
+ break;
+ }
+
+ /* Check that a valid size was used */
+ KUNIT_ASSERT_NE(test, VCAP_KFS_NO_VALUE, keyset);
+
+ /* Allocate the rule */
+ rule = vcap_alloc_rule(&test_vctrl, &test_netdev, cid, user, priority,
+ id);
+ KUNIT_EXPECT_PTR_NE(test, NULL, rule);
+
+ ri = (struct vcap_rule_internal *)rule;
+
+ /* Override rule keyset */
+ ret = vcap_set_rule_set_keyset(rule, keyset);
+
+ /* Add rule actions : there must be at least one action */
+ ret = vcap_rule_add_action_u32(rule, VCAP_AF_COSID_VAL, 0);
+
+ /* Override rule actionset */
+ ret = vcap_set_rule_set_actionset(rule, actionset);
+
+ ret = vcap_val_rule(rule, ETH_P_ALL);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ KUNIT_EXPECT_EQ(test, keyset, rule->keyset);
+ KUNIT_EXPECT_EQ(test, actionset, rule->actionset);
+ KUNIT_EXPECT_EQ(test, size, ri->size);
+
+ /* Add rule with write callback */
+ ret = vcap_add_rule(rule);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ KUNIT_EXPECT_EQ(test, expected_addr, ri->addr);
+ return rule;
+}
+
+/* Prepare testing rule deletion */
+static void test_init_rule_deletion(void)
+{
+ test_move_addr = 0;
+ test_move_offset = 0;
+ test_move_count = 0;
+ test_init_start = 0;
+ test_init_count = 0;
+}
+
/* Define the test cases. */
static void vcap_api_set_bit_1_test(struct kunit *test)
@@ -1333,6 +1421,414 @@ static void vcap_api_encode_rule_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, true, ret);
}
+static void vcap_api_set_rule_counter_test(struct kunit *test)
+{
+ struct vcap_admin is2_admin = {
+ .cache = {
+ .counter = 100,
+ .sticky = true,
+ },
+ };
+ struct vcap_rule_internal ri = {
+ .data = {
+ .id = 1001,
+ },
+ .addr = 600,
+ .admin = &is2_admin,
+ .counter_id = 1002,
+ .vctrl = &test_vctrl,
+ };
+ struct vcap_rule_internal ri2 = {
+ .data = {
+ .id = 2001,
+ },
+ .addr = 700,
+ .admin = &is2_admin,
+ .counter_id = 2002,
+ .vctrl = &test_vctrl,
+ };
+ struct vcap_counter ctr = { .value = 0, .sticky = false};
+ struct vcap_counter ctr2 = { .value = 101, .sticky = true};
+ int ret;
+
+ vcap_test_api_init(&is2_admin);
+ list_add_tail(&ri.list, &is2_admin.rules);
+ list_add_tail(&ri2.list, &is2_admin.rules);
+
+ pr_info("%s:%d\n", __func__, __LINE__);
+ ret = vcap_rule_set_counter(&ri.data, &ctr);
+ pr_info("%s:%d\n", __func__, __LINE__);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+
+ KUNIT_EXPECT_EQ(test, 1002, test_hw_counter_id);
+ KUNIT_EXPECT_EQ(test, 0, test_hw_cache.counter);
+ KUNIT_EXPECT_EQ(test, false, test_hw_cache.sticky);
+ KUNIT_EXPECT_EQ(test, 600, test_updateaddr[0]);
+
+ ret = vcap_rule_set_counter(&ri2.data, &ctr2);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+
+ KUNIT_EXPECT_EQ(test, 2002, test_hw_counter_id);
+ KUNIT_EXPECT_EQ(test, 101, test_hw_cache.counter);
+ KUNIT_EXPECT_EQ(test, true, test_hw_cache.sticky);
+ KUNIT_EXPECT_EQ(test, 700, test_updateaddr[1]);
+}
+
+static void vcap_api_get_rule_counter_test(struct kunit *test)
+{
+ struct vcap_admin is2_admin = {
+ .cache = {
+ .counter = 100,
+ .sticky = true,
+ },
+ };
+ struct vcap_rule_internal ri = {
+ .data = {
+ .id = 1010,
+ },
+ .addr = 400,
+ .admin = &is2_admin,
+ .counter_id = 1011,
+ .vctrl = &test_vctrl,
+ };
+ struct vcap_rule_internal ri2 = {
+ .data = {
+ .id = 2011,
+ },
+ .addr = 300,
+ .admin = &is2_admin,
+ .counter_id = 2012,
+ .vctrl = &test_vctrl,
+ };
+ struct vcap_counter ctr = {};
+ struct vcap_counter ctr2 = {};
+ int ret;
+
+ vcap_test_api_init(&is2_admin);
+ test_hw_cache.counter = 55;
+ test_hw_cache.sticky = true;
+
+ list_add_tail(&ri.list, &is2_admin.rules);
+ list_add_tail(&ri2.list, &is2_admin.rules);
+
+ ret = vcap_rule_get_counter(&ri.data, &ctr);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+
+ KUNIT_EXPECT_EQ(test, 1011, test_hw_counter_id);
+ KUNIT_EXPECT_EQ(test, 55, ctr.value);
+ KUNIT_EXPECT_EQ(test, true, ctr.sticky);
+ KUNIT_EXPECT_EQ(test, 400, test_updateaddr[0]);
+
+ test_hw_cache.counter = 22;
+ test_hw_cache.sticky = false;
+
+ ret = vcap_rule_get_counter(&ri2.data, &ctr2);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+
+ KUNIT_EXPECT_EQ(test, 2012, test_hw_counter_id);
+ KUNIT_EXPECT_EQ(test, 22, ctr2.value);
+ KUNIT_EXPECT_EQ(test, false, ctr2.sticky);
+ KUNIT_EXPECT_EQ(test, 300, test_updateaddr[1]);
+}
+
+static void vcap_api_rule_insert_in_order_test(struct kunit *test)
+{
+ /* Data used by VCAP Library callback */
+ static u32 keydata[32] = {};
+ static u32 mskdata[32] = {};
+ static u32 actdata[32] = {};
+
+ struct vcap_admin admin = {
+ .vtype = VCAP_TYPE_IS0,
+ .first_cid = 10000,
+ .last_cid = 19999,
+ .lookups = 4,
+ .last_valid_addr = 3071,
+ .first_valid_addr = 0,
+ .last_used_addr = 800,
+ .cache = {
+ .keystream = keydata,
+ .maskstream = mskdata,
+ .actionstream = actdata,
+ },
+ };
+
+ vcap_test_api_init(&admin);
+
+ /* Create rules with different sizes and check that they are placed
+ * at the correct address in the VCAP according to size
+ */
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 10, 500, 12, 780);
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 774);
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 771);
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 200, 2, 768);
+}
+
+static void vcap_api_rule_insert_reverse_order_test(struct kunit *test)
+{
+ /* Data used by VCAP Library callback */
+ static u32 keydata[32] = {};
+ static u32 mskdata[32] = {};
+ static u32 actdata[32] = {};
+
+ struct vcap_admin admin = {
+ .vtype = VCAP_TYPE_IS0,
+ .first_cid = 10000,
+ .last_cid = 19999,
+ .lookups = 4,
+ .last_valid_addr = 3071,
+ .first_valid_addr = 0,
+ .last_used_addr = 800,
+ .cache = {
+ .keystream = keydata,
+ .maskstream = mskdata,
+ .actionstream = actdata,
+ },
+ };
+ struct vcap_rule_internal *elem;
+ u32 exp_addr[] = {780, 774, 771, 768, 767};
+ int idx;
+
+ vcap_test_api_init(&admin);
+
+ /* Create rules with different sizes and check that they are placed
+ * at the correct address in the VCAP according to size
+ */
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 200, 2, 798);
+ KUNIT_EXPECT_EQ(test, 0, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 0, test_move_count);
+ KUNIT_EXPECT_EQ(test, 0, test_move_addr);
+
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 795);
+ KUNIT_EXPECT_EQ(test, 6, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 3, test_move_count);
+ KUNIT_EXPECT_EQ(test, 798, test_move_addr);
+
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 400, 6, 792);
+ KUNIT_EXPECT_EQ(test, 6, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 6, test_move_count);
+ KUNIT_EXPECT_EQ(test, 792, test_move_addr);
+
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 50, 500, 12, 780);
+ KUNIT_EXPECT_EQ(test, 18, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 12, test_move_count);
+ KUNIT_EXPECT_EQ(test, 786, test_move_addr);
+
+ idx = 0;
+ list_for_each_entry(elem, &admin.rules, list) {
+ KUNIT_EXPECT_EQ(test, exp_addr[idx], elem->addr);
+ ++idx;
+ }
+ KUNIT_EXPECT_EQ(test, 768, admin.last_used_addr);
+}
+
+static void vcap_api_rule_remove_at_end_test(struct kunit *test)
+{
+ /* Data used by VCAP Library callback */
+ static u32 keydata[32] = {};
+ static u32 mskdata[32] = {};
+ static u32 actdata[32] = {};
+
+ struct vcap_admin admin = {
+ .vtype = VCAP_TYPE_IS0,
+ .first_cid = 10000,
+ .last_cid = 19999,
+ .lookups = 4,
+ .last_valid_addr = 3071,
+ .first_valid_addr = 0,
+ .last_used_addr = 800,
+ .cache = {
+ .keystream = keydata,
+ .maskstream = mskdata,
+ .actionstream = actdata,
+ },
+ };
+ int ret;
+
+ vcap_test_api_init(&admin);
+ test_init_rule_deletion();
+
+ /* Create rules with different sizes and check that they are placed
+ * at the correct address in the VCAP according to size
+ */
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 10, 500, 12, 780);
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 774);
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 771);
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 200, 2, 768);
+
+ /* Remove rules again from the end */
+ ret = vcap_del_rule(&test_vctrl, &test_netdev, 200);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ KUNIT_EXPECT_EQ(test, 0, test_move_addr);
+ KUNIT_EXPECT_EQ(test, 0, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 0, test_move_count);
+ KUNIT_EXPECT_EQ(test, 768, test_init_start);
+ KUNIT_EXPECT_EQ(test, 2, test_init_count);
+ KUNIT_EXPECT_EQ(test, 771, admin.last_used_addr);
+
+ ret = vcap_del_rule(&test_vctrl, &test_netdev, 300);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, 0, test_move_addr);
+ KUNIT_EXPECT_EQ(test, 0, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 0, test_move_count);
+ KUNIT_EXPECT_EQ(test, 771, test_init_start);
+ KUNIT_EXPECT_EQ(test, 3, test_init_count);
+ KUNIT_EXPECT_EQ(test, 774, admin.last_used_addr);
+
+ ret = vcap_del_rule(&test_vctrl, &test_netdev, 400);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, 0, test_move_addr);
+ KUNIT_EXPECT_EQ(test, 0, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 0, test_move_count);
+ KUNIT_EXPECT_EQ(test, 774, test_init_start);
+ KUNIT_EXPECT_EQ(test, 6, test_init_count);
+ KUNIT_EXPECT_EQ(test, 780, admin.last_used_addr);
+
+ ret = vcap_del_rule(&test_vctrl, &test_netdev, 500);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, 0, test_move_addr);
+ KUNIT_EXPECT_EQ(test, 0, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 0, test_move_count);
+ KUNIT_EXPECT_EQ(test, 780, test_init_start);
+ KUNIT_EXPECT_EQ(test, 12, test_init_count);
+ KUNIT_EXPECT_EQ(test, 3071, admin.last_used_addr);
+}
+
+static void vcap_api_rule_remove_in_middle_test(struct kunit *test)
+{
+ /* Data used by VCAP Library callback */
+ static u32 keydata[32] = {};
+ static u32 mskdata[32] = {};
+ static u32 actdata[32] = {};
+
+ struct vcap_admin admin = {
+ .vtype = VCAP_TYPE_IS0,
+ .first_cid = 10000,
+ .last_cid = 19999,
+ .lookups = 4,
+ .first_valid_addr = 0,
+ .last_used_addr = 800,
+ .last_valid_addr = 800 - 1,
+ .cache = {
+ .keystream = keydata,
+ .maskstream = mskdata,
+ .actionstream = actdata,
+ },
+ };
+ int ret;
+
+ vcap_test_api_init(&admin);
+
+ /* Create rules with different sizes and check that they are placed
+ * at the correct address in the VCAP according to size
+ */
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 10, 500, 12, 780);
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 774);
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 771);
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 200, 2, 768);
+
+ /* Remove rules in the middle */
+ test_init_rule_deletion();
+ ret = vcap_del_rule(&test_vctrl, &test_netdev, 400);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ KUNIT_EXPECT_EQ(test, 768, test_move_addr);
+ KUNIT_EXPECT_EQ(test, -6, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 6, test_move_count);
+ KUNIT_EXPECT_EQ(test, 768, test_init_start);
+ KUNIT_EXPECT_EQ(test, 6, test_init_count);
+ KUNIT_EXPECT_EQ(test, 774, admin.last_used_addr);
+
+ test_init_rule_deletion();
+ ret = vcap_del_rule(&test_vctrl, &test_netdev, 300);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ KUNIT_EXPECT_EQ(test, 774, test_move_addr);
+ KUNIT_EXPECT_EQ(test, -4, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 2, test_move_count);
+ KUNIT_EXPECT_EQ(test, 774, test_init_start);
+ KUNIT_EXPECT_EQ(test, 4, test_init_count);
+ KUNIT_EXPECT_EQ(test, 778, admin.last_used_addr);
+
+ test_init_rule_deletion();
+ ret = vcap_del_rule(&test_vctrl, &test_netdev, 500);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ KUNIT_EXPECT_EQ(test, 778, test_move_addr);
+ KUNIT_EXPECT_EQ(test, -20, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 2, test_move_count);
+ KUNIT_EXPECT_EQ(test, 778, test_init_start);
+ KUNIT_EXPECT_EQ(test, 20, test_init_count);
+ KUNIT_EXPECT_EQ(test, 798, admin.last_used_addr);
+
+ test_init_rule_deletion();
+ ret = vcap_del_rule(&test_vctrl, &test_netdev, 200);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ KUNIT_EXPECT_EQ(test, 0, test_move_addr);
+ KUNIT_EXPECT_EQ(test, 0, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 0, test_move_count);
+ KUNIT_EXPECT_EQ(test, 798, test_init_start);
+ KUNIT_EXPECT_EQ(test, 2, test_init_count);
+ KUNIT_EXPECT_EQ(test, 799, admin.last_used_addr);
+}
+
+static void vcap_api_rule_remove_in_front_test(struct kunit *test)
+{
+ /* Data used by VCAP Library callback */
+ static u32 keydata[32] = {};
+ static u32 mskdata[32] = {};
+ static u32 actdata[32] = {};
+
+ struct vcap_admin admin = {
+ .vtype = VCAP_TYPE_IS0,
+ .first_cid = 10000,
+ .last_cid = 19999,
+ .lookups = 4,
+ .first_valid_addr = 0,
+ .last_used_addr = 800,
+ .last_valid_addr = 800 - 1,
+ .cache = {
+ .keystream = keydata,
+ .maskstream = mskdata,
+ .actionstream = actdata,
+ },
+ };
+ int ret;
+
+ vcap_test_api_init(&admin);
+
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 10, 500, 12, 780);
+ KUNIT_EXPECT_EQ(test, 780, admin.last_used_addr);
+
+ test_init_rule_deletion();
+ ret = vcap_del_rule(&test_vctrl, &test_netdev, 500);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ KUNIT_EXPECT_EQ(test, 0, test_move_addr);
+ KUNIT_EXPECT_EQ(test, 0, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 0, test_move_count);
+ KUNIT_EXPECT_EQ(test, 780, test_init_start);
+ KUNIT_EXPECT_EQ(test, 12, test_init_count);
+ KUNIT_EXPECT_EQ(test, 799, admin.last_used_addr);
+
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 792);
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 789);
+ test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 200, 2, 786);
+
+ test_init_rule_deletion();
+ ret = vcap_del_rule(&test_vctrl, &test_netdev, 400);
+ KUNIT_EXPECT_EQ(test, 0, ret);
+ KUNIT_EXPECT_EQ(test, 786, test_move_addr);
+ KUNIT_EXPECT_EQ(test, -8, test_move_offset);
+ KUNIT_EXPECT_EQ(test, 6, test_move_count);
+ KUNIT_EXPECT_EQ(test, 786, test_init_start);
+ KUNIT_EXPECT_EQ(test, 8, test_init_count);
+ KUNIT_EXPECT_EQ(test, 794, admin.last_used_addr);
+}
+
+static struct kunit_case vcap_api_rule_remove_test_cases[] = {
+ KUNIT_CASE(vcap_api_rule_remove_at_end_test),
+ KUNIT_CASE(vcap_api_rule_remove_in_middle_test),
+ KUNIT_CASE(vcap_api_rule_remove_in_front_test),
+ {}
+};
+
static void vcap_api_next_lookup_basic_test(struct kunit *test)
{
struct vcap_admin admin1 = {
@@ -1458,6 +1954,33 @@ static void vcap_api_next_lookup_advanced_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, true, ret);
}
+static struct kunit_suite vcap_api_rule_remove_test_suite = {
+ .name = "VCAP_API_Rule_Remove_Testsuite",
+ .test_cases = vcap_api_rule_remove_test_cases,
+};
+
+static struct kunit_case vcap_api_rule_insert_test_cases[] = {
+ KUNIT_CASE(vcap_api_rule_insert_in_order_test),
+ KUNIT_CASE(vcap_api_rule_insert_reverse_order_test),
+ {}
+};
+
+static struct kunit_suite vcap_api_rule_insert_test_suite = {
+ .name = "VCAP_API_Rule_Insert_Testsuite",
+ .test_cases = vcap_api_rule_insert_test_cases,
+};
+
+static struct kunit_case vcap_api_rule_counter_test_cases[] = {
+ KUNIT_CASE(vcap_api_set_rule_counter_test),
+ KUNIT_CASE(vcap_api_get_rule_counter_test),
+ {}
+};
+
+static struct kunit_suite vcap_api_rule_counter_test_suite = {
+ .name = "VCAP_API_Rule_Counter_Testsuite",
+ .test_cases = vcap_api_rule_counter_test_cases,
+};
+
static struct kunit_case vcap_api_support_test_cases[] = {
KUNIT_CASE(vcap_api_next_lookup_basic_test),
KUNIT_CASE(vcap_api_next_lookup_advanced_test),
@@ -1519,6 +2042,9 @@ static struct kunit_suite vcap_api_encoding_test_suite = {
.test_cases = vcap_api_encoding_test_cases,
};
+kunit_test_suite(vcap_api_rule_remove_test_suite);
+kunit_test_suite(vcap_api_rule_insert_test_suite);
+kunit_test_suite(vcap_api_rule_counter_test_suite);
kunit_test_suite(vcap_api_support_test_suite);
kunit_test_suite(vcap_api_full_rule_test_suite);
kunit_test_suite(vcap_api_rule_value_test_suite);
diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h
index 7a60bc6d72c9..0400a0ac8a29 100644
--- a/include/net/flow_offload.h
+++ b/include/net/flow_offload.h
@@ -32,6 +32,10 @@ struct flow_match_vlan {
struct flow_dissector_key_vlan *key, *mask;
};
+struct flow_match_arp {
+ struct flow_dissector_key_arp *key, *mask;
+};
+
struct flow_match_ipv4_addrs {
struct flow_dissector_key_ipv4_addrs *key, *mask;
};
@@ -98,6 +102,8 @@ void flow_rule_match_vlan(const struct flow_rule *rule,
struct flow_match_vlan *out);
void flow_rule_match_cvlan(const struct flow_rule *rule,
struct flow_match_vlan *out);
+void flow_rule_match_arp(const struct flow_rule *rule,
+ struct flow_match_arp *out);
void flow_rule_match_ipv4_addrs(const struct flow_rule *rule,
struct flow_match_ipv4_addrs *out);
void flow_rule_match_ipv6_addrs(const struct flow_rule *rule,
diff --git a/net/core/flow_offload.c b/net/core/flow_offload.c
index abe423fd5736..acfc1f88ea79 100644
--- a/net/core/flow_offload.c
+++ b/net/core/flow_offload.c
@@ -97,6 +97,13 @@ void flow_rule_match_cvlan(const struct flow_rule *rule,
}
EXPORT_SYMBOL(flow_rule_match_cvlan);
+void flow_rule_match_arp(const struct flow_rule *rule,
+ struct flow_match_arp *out)
+{
+ FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ARP, out);
+}
+EXPORT_SYMBOL(flow_rule_match_arp);
+
void flow_rule_match_ipv4_addrs(const struct flow_rule *rule,
struct flow_match_ipv4_addrs *out)
{