mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-21 09:12:05 +00:00
Currently lan966x, doesn't allow to run PTP over interfaces that are part of the bridge. The reason is when the lan966x was receiving a PTP frame (regardless if L2/IPv4/IPv6) the HW it would flood this frame. Now that it is possible to add VCAP rules to the HW, such to trap these frames to the CPU, it is possible to run PTP also over interfaces that are part of the bridge. Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
255 lines
6.1 KiB
C
255 lines
6.1 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
|
|
#include "lan966x_main.h"
|
|
#include "vcap_api.h"
|
|
#include "vcap_api_client.h"
|
|
|
|
struct lan966x_tc_flower_parse_usage {
|
|
struct flow_cls_offload *f;
|
|
struct flow_rule *frule;
|
|
struct vcap_rule *vrule;
|
|
unsigned int used_keys;
|
|
u16 l3_proto;
|
|
};
|
|
|
|
static int lan966x_tc_flower_handler_ethaddr_usage(struct lan966x_tc_flower_parse_usage *st)
|
|
{
|
|
enum vcap_key_field smac_key = VCAP_KF_L2_SMAC;
|
|
enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC;
|
|
struct flow_match_eth_addrs match;
|
|
struct vcap_u48_key smac, dmac;
|
|
int err = 0;
|
|
|
|
flow_rule_match_eth_addrs(st->frule, &match);
|
|
|
|
if (!is_zero_ether_addr(match.mask->src)) {
|
|
vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN);
|
|
vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN);
|
|
err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac);
|
|
if (err)
|
|
goto out;
|
|
}
|
|
|
|
if (!is_zero_ether_addr(match.mask->dst)) {
|
|
vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN);
|
|
vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN);
|
|
err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac);
|
|
if (err)
|
|
goto out;
|
|
}
|
|
|
|
st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS);
|
|
|
|
return err;
|
|
|
|
out:
|
|
NL_SET_ERR_MSG_MOD(st->f->common.extack, "eth_addr parse error");
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
(*lan966x_tc_flower_handlers_usage[])(struct lan966x_tc_flower_parse_usage *st) = {
|
|
[FLOW_DISSECTOR_KEY_ETH_ADDRS] = lan966x_tc_flower_handler_ethaddr_usage,
|
|
};
|
|
|
|
static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f,
|
|
struct vcap_admin *admin,
|
|
struct vcap_rule *vrule,
|
|
u16 *l3_proto)
|
|
{
|
|
struct lan966x_tc_flower_parse_usage state = {
|
|
.f = f,
|
|
.vrule = vrule,
|
|
.l3_proto = ETH_P_ALL,
|
|
};
|
|
int err = 0;
|
|
|
|
state.frule = flow_cls_offload_flow_rule(f);
|
|
for (int i = 0; i < ARRAY_SIZE(lan966x_tc_flower_handlers_usage); ++i) {
|
|
if (!flow_rule_match_key(state.frule, i) ||
|
|
!lan966x_tc_flower_handlers_usage[i])
|
|
continue;
|
|
|
|
err = lan966x_tc_flower_handlers_usage[i](&state);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
if (l3_proto)
|
|
*l3_proto = state.l3_proto;
|
|
|
|
return err;
|
|
}
|
|
|
|
static int lan966x_tc_flower_action_check(struct vcap_control *vctrl,
|
|
struct flow_cls_offload *fco,
|
|
struct vcap_admin *admin)
|
|
{
|
|
struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
|
|
struct flow_action_entry *actent, *last_actent = NULL;
|
|
struct flow_action *act = &rule->action;
|
|
u64 action_mask = 0;
|
|
int idx;
|
|
|
|
if (!flow_action_has_entries(act)) {
|
|
NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
|
|
return -EOPNOTSUPP;
|
|
|
|
flow_action_for_each(idx, actent, act) {
|
|
if (action_mask & BIT(actent->id)) {
|
|
NL_SET_ERR_MSG_MOD(fco->common.extack,
|
|
"More actions of the same type");
|
|
return -EINVAL;
|
|
}
|
|
action_mask |= BIT(actent->id);
|
|
last_actent = actent; /* Save last action for later check */
|
|
}
|
|
|
|
/* Check that last action is a goto */
|
|
if (last_actent->id != FLOW_ACTION_GOTO) {
|
|
NL_SET_ERR_MSG_MOD(fco->common.extack,
|
|
"Last action must be 'goto'");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Check if the goto chain is in the next lookup */
|
|
if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
|
|
last_actent->chain_index)) {
|
|
NL_SET_ERR_MSG_MOD(fco->common.extack,
|
|
"Invalid goto chain");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Catch unsupported combinations of actions */
|
|
if (action_mask & BIT(FLOW_ACTION_TRAP) &&
|
|
action_mask & BIT(FLOW_ACTION_ACCEPT)) {
|
|
NL_SET_ERR_MSG_MOD(fco->common.extack,
|
|
"Cannot combine pass and trap action");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int lan966x_tc_flower_add(struct lan966x_port *port,
|
|
struct flow_cls_offload *f,
|
|
struct vcap_admin *admin)
|
|
{
|
|
struct flow_action_entry *act;
|
|
u16 l3_proto = ETH_P_ALL;
|
|
struct flow_rule *frule;
|
|
struct vcap_rule *vrule;
|
|
int err, idx;
|
|
|
|
err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl, f,
|
|
admin);
|
|
if (err)
|
|
return err;
|
|
|
|
vrule = vcap_alloc_rule(port->lan966x->vcap_ctrl, port->dev,
|
|
f->common.chain_index, VCAP_USER_TC,
|
|
f->common.prio, 0);
|
|
if (IS_ERR(vrule))
|
|
return PTR_ERR(vrule);
|
|
|
|
vrule->cookie = f->cookie;
|
|
err = lan966x_tc_flower_use_dissectors(f, admin, vrule, &l3_proto);
|
|
if (err)
|
|
goto out;
|
|
|
|
frule = flow_cls_offload_flow_rule(f);
|
|
|
|
flow_action_for_each(idx, act, &frule->action) {
|
|
switch (act->id) {
|
|
case FLOW_ACTION_TRAP:
|
|
err = vcap_rule_add_action_bit(vrule,
|
|
VCAP_AF_CPU_COPY_ENA,
|
|
VCAP_BIT_1);
|
|
err |= vcap_rule_add_action_u32(vrule,
|
|
VCAP_AF_CPU_QUEUE_NUM,
|
|
0);
|
|
err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,
|
|
LAN966X_PMM_REPLACE);
|
|
err |= vcap_set_rule_set_actionset(vrule,
|
|
VCAP_AFS_BASE_TYPE);
|
|
if (err)
|
|
goto out;
|
|
|
|
break;
|
|
case FLOW_ACTION_GOTO:
|
|
break;
|
|
default:
|
|
NL_SET_ERR_MSG_MOD(f->common.extack,
|
|
"Unsupported TC action");
|
|
err = -EOPNOTSUPP;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
err = vcap_val_rule(vrule, l3_proto);
|
|
if (err) {
|
|
vcap_set_tc_exterr(f, vrule);
|
|
goto out;
|
|
}
|
|
|
|
err = vcap_add_rule(vrule);
|
|
if (err)
|
|
NL_SET_ERR_MSG_MOD(f->common.extack,
|
|
"Could not add the filter");
|
|
out:
|
|
vcap_free_rule(vrule);
|
|
return err;
|
|
}
|
|
|
|
static int lan966x_tc_flower_del(struct lan966x_port *port,
|
|
struct flow_cls_offload *f,
|
|
struct vcap_admin *admin)
|
|
{
|
|
struct vcap_control *vctrl;
|
|
int err = -ENOENT, rule_id;
|
|
|
|
vctrl = port->lan966x->vcap_ctrl;
|
|
while (true) {
|
|
rule_id = vcap_lookup_rule_by_cookie(vctrl, f->cookie);
|
|
if (rule_id <= 0)
|
|
break;
|
|
|
|
err = vcap_del_rule(vctrl, port->dev, rule_id);
|
|
if (err) {
|
|
NL_SET_ERR_MSG_MOD(f->common.extack,
|
|
"Cannot delete rule");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int lan966x_tc_flower(struct lan966x_port *port,
|
|
struct flow_cls_offload *f)
|
|
{
|
|
struct vcap_admin *admin;
|
|
|
|
admin = vcap_find_admin(port->lan966x->vcap_ctrl,
|
|
f->common.chain_index);
|
|
if (!admin) {
|
|
NL_SET_ERR_MSG_MOD(f->common.extack, "Invalid chain");
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (f->command) {
|
|
case FLOW_CLS_REPLACE:
|
|
return lan966x_tc_flower_add(port, f, admin);
|
|
case FLOW_CLS_DESTROY:
|
|
return lan966x_tc_flower_del(port, f, admin);
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
return 0;
|
|
}
|