Skip to content

Commit b4d0679

Browse files
committed
Merge branch 'mirroring-to-dsa-cpu-port'
Vladimir Oltean says: ==================== Mirroring to DSA CPU port Users of the NXP LS1028A SoC (drivers/net/dsa/ocelot L2 switch inside) have requested to mirror packets from the ingress of a switch port to software. Both port-based and flow-based mirroring is required. The simplest way I could come up with was to set up tc mirred actions towards a dummy net_device, and make the offloading of that be accepted by the driver. Currently, the pattern in drivers is to reject mirred towards ports they don't know about, but I'm now permitting that, precisely by mirroring "to the CPU". For testers, this series depends on commit 34d35b4 ("net/sched: act_api: deny mismatched skip_sw/skip_hw flags for actions created by classifiers") from net/main, which is absent from net-next as of the day of posting (Oct 23). Without the bug fix it is possible to create invalid configurations which are not rejected by the kernel. v2: https://lore.kernel.org/[email protected] RFC: https://lore.kernel.org/[email protected] For historical purposes, link to a much older (and much different) attempt: https://lore.kernel.org/[email protected] ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
2 parents 427b064 + 49a0907 commit b4d0679

File tree

4 files changed

+103
-31
lines changed

4 files changed

+103
-31
lines changed

drivers/net/ethernet/mscc/ocelot_flower.c

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,32 @@ ocelot_flower_parse_egress_vlan_modify(struct ocelot_vcap_filter *filter,
228228
return 0;
229229
}
230230

231+
static int
232+
ocelot_flower_parse_egress_port(struct ocelot *ocelot, struct flow_cls_offload *f,
233+
const struct flow_action_entry *a, bool mirror,
234+
struct netlink_ext_ack *extack)
235+
{
236+
const char *act_string = mirror ? "mirror" : "redirect";
237+
int egress_port = ocelot->ops->netdev_to_port(a->dev);
238+
enum flow_action_id offloadable_act_id;
239+
240+
offloadable_act_id = mirror ? FLOW_ACTION_MIRRED : FLOW_ACTION_REDIRECT;
241+
242+
/* Mirroring towards foreign interfaces is handled in software */
243+
if (egress_port < 0 || a->id != offloadable_act_id) {
244+
if (f->common.skip_sw) {
245+
NL_SET_ERR_MSG_FMT(extack,
246+
"Can only %s to %s if filter also runs in software",
247+
act_string, egress_port < 0 ?
248+
"CPU" : "ingress of ocelot port");
249+
return -EOPNOTSUPP;
250+
}
251+
egress_port = ocelot->num_phys_ports;
252+
}
253+
254+
return egress_port;
255+
}
256+
231257
static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
232258
bool ingress, struct flow_cls_offload *f,
233259
struct ocelot_vcap_filter *filter)
@@ -356,6 +382,7 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
356382
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
357383
break;
358384
case FLOW_ACTION_REDIRECT:
385+
case FLOW_ACTION_REDIRECT_INGRESS:
359386
if (filter->block_id != VCAP_IS2) {
360387
NL_SET_ERR_MSG_MOD(extack,
361388
"Redirect action can only be offloaded to VCAP IS2");
@@ -366,17 +393,19 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
366393
"Last action must be GOTO");
367394
return -EOPNOTSUPP;
368395
}
369-
egress_port = ocelot->ops->netdev_to_port(a->dev);
370-
if (egress_port < 0) {
371-
NL_SET_ERR_MSG_MOD(extack,
372-
"Destination not an ocelot port");
373-
return -EOPNOTSUPP;
374-
}
396+
397+
egress_port = ocelot_flower_parse_egress_port(ocelot, f,
398+
a, false,
399+
extack);
400+
if (egress_port < 0)
401+
return egress_port;
402+
375403
filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
376404
filter->action.port_mask = BIT(egress_port);
377405
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
378406
break;
379407
case FLOW_ACTION_MIRRED:
408+
case FLOW_ACTION_MIRRED_INGRESS:
380409
if (filter->block_id != VCAP_IS2) {
381410
NL_SET_ERR_MSG_MOD(extack,
382411
"Mirror action can only be offloaded to VCAP IS2");
@@ -387,12 +416,13 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
387416
"Last action must be GOTO");
388417
return -EOPNOTSUPP;
389418
}
390-
egress_port = ocelot->ops->netdev_to_port(a->dev);
391-
if (egress_port < 0) {
392-
NL_SET_ERR_MSG_MOD(extack,
393-
"Destination not an ocelot port");
394-
return -EOPNOTSUPP;
395-
}
419+
420+
egress_port = ocelot_flower_parse_egress_port(ocelot, f,
421+
a, true,
422+
extack);
423+
if (egress_port < 0)
424+
return egress_port;
425+
396426
filter->egress_port.value = egress_port;
397427
filter->action.mirror_ena = true;
398428
filter->type = OCELOT_VCAP_FILTER_OFFLOAD;

include/net/flow_offload.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,7 @@ struct flow_cls_common_offload {
685685
u32 chain_index;
686686
__be16 protocol;
687687
u32 prio;
688+
bool skip_sw;
688689
struct netlink_ext_ack *extack;
689690
};
690691

include/net/pkt_cls.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,7 @@ tc_cls_common_offload_init(struct flow_cls_common_offload *cls_common,
755755
cls_common->chain_index = tp->chain->index;
756756
cls_common->protocol = tp->protocol;
757757
cls_common->prio = tp->prio >> 16;
758+
cls_common->skip_sw = tc_skip_sw(flags);
758759
if (tc_skip_sw(flags) || flags & TCA_CLS_FLAGS_VERBOSE)
759760
cls_common->extack = extack;
760761
}

net/dsa/user.c

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,7 +1364,7 @@ dsa_user_mall_tc_entry_find(struct net_device *dev, unsigned long cookie)
13641364
static int
13651365
dsa_user_add_cls_matchall_mirred(struct net_device *dev,
13661366
struct tc_cls_matchall_offload *cls,
1367-
bool ingress)
1367+
bool ingress, bool ingress_target)
13681368
{
13691369
struct netlink_ext_ack *extack = cls->common.extack;
13701370
struct dsa_port *dp = dsa_user_to_port(dev);
@@ -1376,22 +1376,50 @@ dsa_user_add_cls_matchall_mirred(struct net_device *dev,
13761376
struct dsa_port *to_dp;
13771377
int err;
13781378

1379-
if (!ds->ops->port_mirror_add)
1379+
if (cls->common.protocol != htons(ETH_P_ALL)) {
1380+
NL_SET_ERR_MSG_MOD(extack,
1381+
"Can only offload \"protocol all\" matchall filter");
1382+
return -EOPNOTSUPP;
1383+
}
1384+
1385+
if (!ds->ops->port_mirror_add) {
1386+
NL_SET_ERR_MSG_MOD(extack,
1387+
"Switch does not support mirroring operation");
13801388
return -EOPNOTSUPP;
1389+
}
13811390

1382-
if (!flow_action_basic_hw_stats_check(&cls->rule->action,
1383-
cls->common.extack))
1391+
if (!flow_action_basic_hw_stats_check(&cls->rule->action, extack))
13841392
return -EOPNOTSUPP;
13851393

13861394
act = &cls->rule->action.entries[0];
13871395

13881396
if (!act->dev)
13891397
return -EINVAL;
13901398

1391-
if (!dsa_user_dev_check(act->dev))
1392-
return -EOPNOTSUPP;
1393-
1394-
to_dp = dsa_user_to_port(act->dev);
1399+
if (dsa_user_dev_check(act->dev)) {
1400+
if (ingress_target) {
1401+
/* We can only fulfill this using software assist */
1402+
if (cls->common.skip_sw) {
1403+
NL_SET_ERR_MSG_MOD(extack,
1404+
"Can only mirred to ingress of DSA user port if filter also runs in software");
1405+
return -EOPNOTSUPP;
1406+
}
1407+
to_dp = dp->cpu_dp;
1408+
} else {
1409+
to_dp = dsa_user_to_port(act->dev);
1410+
}
1411+
} else {
1412+
/* Handle mirroring to foreign target ports as a mirror towards
1413+
* the CPU. The software tc rule will take the packets from
1414+
* there.
1415+
*/
1416+
if (cls->common.skip_sw) {
1417+
NL_SET_ERR_MSG_MOD(extack,
1418+
"Can only mirred to CPU if filter also runs in software");
1419+
return -EOPNOTSUPP;
1420+
}
1421+
to_dp = dp->cpu_dp;
1422+
}
13951423

13961424
if (dp->ds != to_dp->ds) {
13971425
NL_SET_ERR_MSG_MOD(extack,
@@ -1446,8 +1474,7 @@ dsa_user_add_cls_matchall_police(struct net_device *dev,
14461474
return -EOPNOTSUPP;
14471475
}
14481476

1449-
if (!flow_action_basic_hw_stats_check(&cls->rule->action,
1450-
cls->common.extack))
1477+
if (!flow_action_basic_hw_stats_check(&cls->rule->action, extack))
14511478
return -EOPNOTSUPP;
14521479

14531480
list_for_each_entry(mall_tc_entry, &p->mall_tc_list, list) {
@@ -1485,17 +1512,30 @@ static int dsa_user_add_cls_matchall(struct net_device *dev,
14851512
struct tc_cls_matchall_offload *cls,
14861513
bool ingress)
14871514
{
1488-
int err = -EOPNOTSUPP;
1515+
const struct flow_action *action = &cls->rule->action;
1516+
struct netlink_ext_ack *extack = cls->common.extack;
1517+
1518+
if (!flow_offload_has_one_action(action)) {
1519+
NL_SET_ERR_MSG_MOD(extack,
1520+
"Cannot offload matchall filter with more than one action");
1521+
return -EOPNOTSUPP;
1522+
}
14891523

1490-
if (cls->common.protocol == htons(ETH_P_ALL) &&
1491-
flow_offload_has_one_action(&cls->rule->action) &&
1492-
cls->rule->action.entries[0].id == FLOW_ACTION_MIRRED)
1493-
err = dsa_user_add_cls_matchall_mirred(dev, cls, ingress);
1494-
else if (flow_offload_has_one_action(&cls->rule->action) &&
1495-
cls->rule->action.entries[0].id == FLOW_ACTION_POLICE)
1496-
err = dsa_user_add_cls_matchall_police(dev, cls, ingress);
1524+
switch (action->entries[0].id) {
1525+
case FLOW_ACTION_MIRRED:
1526+
return dsa_user_add_cls_matchall_mirred(dev, cls, ingress,
1527+
false);
1528+
case FLOW_ACTION_MIRRED_INGRESS:
1529+
return dsa_user_add_cls_matchall_mirred(dev, cls, ingress,
1530+
true);
1531+
case FLOW_ACTION_POLICE:
1532+
return dsa_user_add_cls_matchall_police(dev, cls, ingress);
1533+
default:
1534+
NL_SET_ERR_MSG_MOD(extack, "Unknown action");
1535+
break;
1536+
}
14971537

1498-
return err;
1538+
return -EOPNOTSUPP;
14991539
}
15001540

15011541
static void dsa_user_del_cls_matchall(struct net_device *dev,

0 commit comments

Comments
 (0)