Commit 4d1bbdf5 authored by David S. Miller's avatar David S. Miller

Merge branch 'sparx5-sorted-VCAP-rules'

Steen Hegelund says:

====================
net: Add support for sorted VCAP rules in Sparx5

This provides support for adding Sparx5 VCAP rules in sorted order, VCAP
rule counters and TC filter matching on ARP frames.

It builds on top of the initial IS2 VCAP support found in these series:

https://lore.kernel.org/all/20221020130904.1215072-1-steen.hegelund@microchip.com/
https://lore.kernel.org/all/20221109114116.3612477-1-steen.hegelund@microchip.com/

Functionality
=============

When a new VCAP rule is added the driver will now ensure that the rule is
inserted in sorted order, and when a rule is removed, the remaining rules
will be moved to keep the sorted order and remove any gaps in the VCAP
address space.

A VCAP rule is ordered using these 3 values:

 - Rule size: the count of VCAP addresses used by the rule.  The largest
   rule have highest priority

 - Rule User: The rules are ordered by the user enumeration

 - Priority: The priority provided in the flower filter.  The lowest value
   has the highest priority.

A VCAP instance may contain the counter as part of the VCAP cache area, and
this counter may be one or more bits in width.  This type of counter
automatically increments its value when the rule is hit.

Other VCAP instances have a dedicated counter area outside of the VCAP and
in this case the rule must contain the counter id to be able to locate the
counter value and cause the counter to be incremented.  In this case there
must also be a VCAP rule action that sets the counter id.

The Sparx5 IS2 VCAP uses a dedicated counter area with 32bit counters.

This series adds support for getting VCAP rule counters and provide these
via the TC statistic interface.

This only support packet counters, not byte counters.

Finally the series adds support for the ARP frame dissector and configures
the Sparx5 IS2 VCAP to generate the ARP keyset when ARP traffic is
received.

Delivery:
=========

This is current plan for delivering the full VCAP feature set of Sparx5:

- DebugFS support for inspecting rules
- TC protocol all support
- Sparx5 IS0 VCAP support
- TC policer and drop action support (depends on the Sparx5 QoS support
  upstreamed separately)
- Sparx5 ES0 VCAP support
- TC flower template support
- TC matchall filter support for mirroring and policing ports
- TC flower filter mirror action support
- Sparx5 ES2 VCAP support
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 2fd450cd dccc30cc
...@@ -21,15 +21,34 @@ struct sparx5_tc_flower_parse_usage { ...@@ -21,15 +21,34 @@ struct sparx5_tc_flower_parse_usage {
unsigned int used_keys; 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 /* These protocols have dedicated keysets in IS2 and a TC dissector
* ETH_P_ARP does not have a TC dissector * ETH_P_ARP does not have a TC dissector
*/ */
static u16 sparx5_tc_known_etypes[] = { static u16 sparx5_tc_known_etypes[] = {
ETH_P_ALL, ETH_P_ALL,
ETH_P_ARP,
ETH_P_IP, ETH_P_IP,
ETH_P_IPV6, 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) static bool sparx5_tc_is_known_etype(u16 etype)
{ {
int idx; int idx;
...@@ -404,6 +423,67 @@ sparx5_tc_flower_handler_tcp_usage(struct sparx5_tc_flower_parse_usage *st) ...@@ -404,6 +423,67 @@ sparx5_tc_flower_handler_tcp_usage(struct sparx5_tc_flower_parse_usage *st)
return err; return err;
} }
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 static int
sparx5_tc_flower_handler_ip_usage(struct sparx5_tc_flower_parse_usage *st) sparx5_tc_flower_handler_ip_usage(struct sparx5_tc_flower_parse_usage *st)
{ {
...@@ -438,6 +518,7 @@ static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_us ...@@ -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_BASIC] = sparx5_tc_flower_handler_basic_usage,
[FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage, [FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage,
[FLOW_DISSECTOR_KEY_TCP] = sparx5_tc_flower_handler_tcp_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, [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, ...@@ -529,6 +610,20 @@ static int sparx5_tc_flower_action_check(struct vcap_control *vctrl,
return 0; 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, static int sparx5_tc_flower_replace(struct net_device *ndev,
struct flow_cls_offload *fco, struct flow_cls_offload *fco,
struct vcap_admin *admin) struct vcap_admin *admin)
...@@ -554,6 +649,11 @@ static int sparx5_tc_flower_replace(struct net_device *ndev, ...@@ -554,6 +649,11 @@ static int sparx5_tc_flower_replace(struct net_device *ndev,
vrule->cookie = fco->cookie; vrule->cookie = fco->cookie;
sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto); 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); frule = flow_cls_offload_flow_rule(fco);
flow_action_for_each(idx, act, &frule->action) { flow_action_for_each(idx, act, &frule->action) {
switch (act->id) { switch (act->id) {
...@@ -632,6 +732,48 @@ static int sparx5_tc_flower_destroy(struct net_device *ndev, ...@@ -632,6 +732,48 @@ static int sparx5_tc_flower_destroy(struct net_device *ndev,
return err; 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, int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco,
bool ingress) bool ingress)
{ {
...@@ -653,6 +795,8 @@ int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, ...@@ -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); return sparx5_tc_flower_replace(ndev, fco, admin);
case FLOW_CLS_DESTROY: case FLOW_CLS_DESTROY:
return sparx5_tc_flower_destroy(ndev, fco, admin); return sparx5_tc_flower_destroy(ndev, fco, admin);
case FLOW_CLS_STATS:
return sparx5_tc_flower_stats(ndev, fco, admin);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -428,15 +428,58 @@ static void sparx5_vcap_cache_write(struct net_device *ndev, ...@@ -428,15 +428,58 @@ static void sparx5_vcap_cache_write(struct net_device *ndev,
default: default:
break; 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 */ /* API callback used for reading from the VCAP into the VCAP cache */
static void sparx5_vcap_cache_read(struct net_device *ndev, static void sparx5_vcap_cache_read(struct net_device *ndev,
struct vcap_admin *admin, struct vcap_admin *admin,
enum vcap_selection sel, u32 start, enum vcap_selection sel,
u32 start,
u32 count) 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 */ /* API callback used for initializing a VCAP address range */
...@@ -477,7 +520,32 @@ static void sparx5_vcap_update(struct net_device *ndev, ...@@ -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, static void sparx5_vcap_move(struct net_device *ndev, struct vcap_admin *admin,
u32 addr, int offset, int count) 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 */ /* Provide port information via a callback interface */
...@@ -540,7 +608,7 @@ static void sparx5_vcap_port_key_selection(struct sparx5 *sparx5, ...@@ -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_IPV4_UC_IP4_TCP_UDP_OTHER,
VCAP_IS2_PS_IPV6_MC_IP_7TUPLE, VCAP_IS2_PS_IPV6_MC_IP_7TUPLE,
VCAP_IS2_PS_IPV6_UC_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 (lookup = 0; lookup < admin->lookups; ++lookup) {
for (portno = 0; portno < SPX5_PORTS; ++portno) { for (portno = 0; portno < SPX5_PORTS; ++portno) {
spx5_wr(keysel, sparx5, spx5_wr(keysel, sparx5,
......
...@@ -25,6 +25,8 @@ struct vcap_rule_internal { ...@@ -25,6 +25,8 @@ struct vcap_rule_internal {
int actionset_sw_regs; /* registers in a subword in an actionset */ int actionset_sw_regs; /* registers in a subword in an actionset */
int size; /* the size of the rule: max(entry, action) */ int size; /* the size of the rule: max(entry, action) */
u32 addr; /* address in the VCAP at insertion */ 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 */ /* Moving a rule in the VCAP address space */
...@@ -651,6 +653,20 @@ static int vcap_write_rule(struct vcap_rule_internal *ri) ...@@ -651,6 +653,20 @@ static int vcap_write_rule(struct vcap_rule_internal *ri)
return 0; 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 */ /* Convert a chain id to a VCAP lookup index */
int vcap_chain_id_to_lookup(struct vcap_admin *admin, int cur_cid) 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) ...@@ -941,6 +957,16 @@ int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto)
} }
EXPORT_SYMBOL_GPL(vcap_val_rule); 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) */ /* 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) static u32 vcap_next_rule_addr(u32 addr, struct vcap_rule_internal *ri)
{ {
...@@ -970,18 +996,68 @@ static u32 vcap_set_rule_id(struct vcap_rule_internal *ri) ...@@ -970,18 +996,68 @@ static u32 vcap_set_rule_id(struct vcap_rule_internal *ri)
static int vcap_insert_rule(struct vcap_rule_internal *ri, static int vcap_insert_rule(struct vcap_rule_internal *ri,
struct vcap_rule_move *move) 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_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);
/* Only support appending rules for now */ /* 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); ri->addr = vcap_next_rule_addr(admin->last_used_addr, ri);
admin->last_used_addr = ri->addr; admin->last_used_addr = ri->addr;
/* Add a shallow copy of the rule to the VCAP list */ /* Add a shallow copy of the rule to the VCAP list */
duprule = vcap_dup_rule(ri); duprule = vcap_dup_rule(ri);
if (IS_ERR(duprule)) if (IS_ERR(duprule))
return PTR_ERR(duprule); return PTR_ERR(duprule);
list_add_tail(&duprule->list, &admin->rules); list_add_tail(&duprule->list, &admin->rules);
return 0; 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;
/* Add a shallow copy of the rule to the VCAP list */
duprule = vcap_dup_rule(ri);
if (IS_ERR(duprule))
return PTR_ERR(duprule);
/* 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;
} }
static void vcap_move_rules(struct vcap_rule_internal *ri, static void vcap_move_rules(struct vcap_rule_internal *ri,
...@@ -1032,8 +1108,11 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, ...@@ -1032,8 +1108,11 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl,
{ {
struct vcap_rule_internal *ri; struct vcap_rule_internal *ri;
struct vcap_admin *admin; struct vcap_admin *admin;
int maxsize; int err, maxsize;
err = vcap_api_check(vctrl);
if (err)
return ERR_PTR(err);
if (!ndev) if (!ndev)
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
/* Get the VCAP instance */ /* Get the VCAP instance */
...@@ -1098,12 +1177,57 @@ void vcap_free_rule(struct vcap_rule *rule) ...@@ -1098,12 +1177,57 @@ void vcap_free_rule(struct vcap_rule *rule)
} }
EXPORT_SYMBOL_GPL(vcap_free_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 */ /* Delete rule in a VCAP instance */
int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id)
{ {
struct vcap_rule_internal *ri, *elem; struct vcap_rule_internal *ri, *elem;
struct vcap_admin *admin; struct vcap_admin *admin;
int err; int gap = 0, err;
/* This will later also handle rule moving */ /* This will later also handle rule moving */
if (!ndev) if (!ndev)
...@@ -1116,18 +1240,23 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) ...@@ -1116,18 +1240,23 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id)
if (!ri) if (!ri)
return -EINVAL; return -EINVAL;
admin = ri->admin; 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); list_del(&ri->list);
vctrl->ops->init(ndev, admin, admin->last_used_addr, ri->size + gap);
kfree(ri);
/* delete the rule in the cache */ /* Update the last used address */
vctrl->ops->init(ndev, admin, ri->addr, ri->size);
if (list_empty(&admin->rules)) { if (list_empty(&admin->rules)) {
admin->last_used_addr = admin->last_valid_addr; admin->last_used_addr = admin->last_valid_addr;
} else { } else {
/* update the address range end marker from the last rule in the list */ elem = list_last_entry(&admin->rules, struct vcap_rule_internal,
elem = list_last_entry(&admin->rules, struct vcap_rule_internal, list); list);
admin->last_used_addr = elem->addr; admin->last_used_addr = elem->addr;
} }
kfree(ri);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(vcap_del_rule); EXPORT_SYMBOL_GPL(vcap_del_rule);
...@@ -1434,6 +1563,20 @@ int vcap_rule_add_action_u32(struct vcap_rule *rule, ...@@ -1434,6 +1563,20 @@ int vcap_rule_add_action_u32(struct vcap_rule *rule,
} }
EXPORT_SYMBOL_GPL(vcap_rule_add_action_u32); 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 */ /* Copy to host byte order */
void vcap_netbytes_copy(u8 *dst, u8 *src, int count) 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, ...@@ -1577,6 +1720,72 @@ int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev,
} }
EXPORT_SYMBOL_GPL(vcap_enable_lookups); 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 #ifdef CONFIG_VCAP_KUNIT_TEST
#include "vcap_api_kunit.c" #include "vcap_api_kunit.c"
#endif #endif
...@@ -143,6 +143,11 @@ enum vcap_bit { ...@@ -143,6 +143,11 @@ enum vcap_bit {
VCAP_BIT_1 VCAP_BIT_1
}; };
struct vcap_counter {
u32 value;
bool sticky;
};
/* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */ /* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */
int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev, int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev,
int chain_id, unsigned long cookie, bool enable); int chain_id, unsigned long cookie, bool enable);
...@@ -170,6 +175,8 @@ int vcap_set_rule_set_keyset(struct vcap_rule *rule, ...@@ -170,6 +175,8 @@ int vcap_set_rule_set_keyset(struct vcap_rule *rule,
/* Update the actionset for the rule */ /* Update the actionset for the rule */
int vcap_set_rule_set_actionset(struct vcap_rule *rule, int vcap_set_rule_set_actionset(struct vcap_rule *rule,
enum vcap_actionfield_set actionset); 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 */ /* VCAP rule field operations */
int vcap_rule_add_key_bit(struct vcap_rule *rule, enum vcap_key_field key, 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, ...@@ -187,6 +194,10 @@ int vcap_rule_add_action_bit(struct vcap_rule *rule,
int vcap_rule_add_action_u32(struct vcap_rule *rule, int vcap_rule_add_action_u32(struct vcap_rule *rule,
enum vcap_action_field action, u32 value); 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 */ /* VCAP lookup operations */
/* Convert a chain id to a VCAP lookup index */ /* Convert a chain id to a VCAP lookup index */
int vcap_chain_id_to_lookup(struct vcap_admin *admin, int cur_cid); 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, ...@@ -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); 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 */ /* 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); 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 */ /* Copy to host byte order */
void vcap_netbytes_copy(u8 *dst, u8 *src, int count); void vcap_netbytes_copy(u8 *dst, u8 *src, int count);
......
...@@ -32,6 +32,10 @@ struct flow_match_vlan { ...@@ -32,6 +32,10 @@ struct flow_match_vlan {
struct flow_dissector_key_vlan *key, *mask; 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_match_ipv4_addrs {
struct flow_dissector_key_ipv4_addrs *key, *mask; struct flow_dissector_key_ipv4_addrs *key, *mask;
}; };
...@@ -98,6 +102,8 @@ void flow_rule_match_vlan(const struct flow_rule *rule, ...@@ -98,6 +102,8 @@ void flow_rule_match_vlan(const struct flow_rule *rule,
struct flow_match_vlan *out); struct flow_match_vlan *out);
void flow_rule_match_cvlan(const struct flow_rule *rule, void flow_rule_match_cvlan(const struct flow_rule *rule,
struct flow_match_vlan *out); 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, void flow_rule_match_ipv4_addrs(const struct flow_rule *rule,
struct flow_match_ipv4_addrs *out); struct flow_match_ipv4_addrs *out);
void flow_rule_match_ipv6_addrs(const struct flow_rule *rule, void flow_rule_match_ipv6_addrs(const struct flow_rule *rule,
......
...@@ -97,6 +97,13 @@ void flow_rule_match_cvlan(const struct flow_rule *rule, ...@@ -97,6 +97,13 @@ void flow_rule_match_cvlan(const struct flow_rule *rule,
} }
EXPORT_SYMBOL(flow_rule_match_cvlan); 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, void flow_rule_match_ipv4_addrs(const struct flow_rule *rule,
struct flow_match_ipv4_addrs *out) struct flow_match_ipv4_addrs *out)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment