Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 34d35b4edbbe ("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/20241017165215.3709000-1-vladimir.oltean@nxp.com
RFC: https://lore.kernel.org/20240913152915.2981126-1-vladimir.oltean@nxp.com

For historical purposes, link to a much older (and much different) attempt:
https://lore.kernel.org/20191002233750.13566-1-olteanv@gmail.com
====================

Link: https://patch.msgid.link/20241023135251.1752488-1-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+103 -31
+42 -12
drivers/net/ethernet/mscc/ocelot_flower.c
··· 228 228 return 0; 229 229 } 230 230 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 + 231 257 static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, 232 258 bool ingress, struct flow_cls_offload *f, 233 259 struct ocelot_vcap_filter *filter) ··· 382 356 filter->type = OCELOT_VCAP_FILTER_OFFLOAD; 383 357 break; 384 358 case FLOW_ACTION_REDIRECT: 359 + case FLOW_ACTION_REDIRECT_INGRESS: 385 360 if (filter->block_id != VCAP_IS2) { 386 361 NL_SET_ERR_MSG_MOD(extack, 387 362 "Redirect action can only be offloaded to VCAP IS2"); ··· 393 366 "Last action must be GOTO"); 394 367 return -EOPNOTSUPP; 395 368 } 396 - egress_port = ocelot->ops->netdev_to_port(a->dev); 397 - if (egress_port < 0) { 398 - NL_SET_ERR_MSG_MOD(extack, 399 - "Destination not an ocelot port"); 400 - return -EOPNOTSUPP; 401 - } 369 + 370 + egress_port = ocelot_flower_parse_egress_port(ocelot, f, 371 + a, false, 372 + extack); 373 + if (egress_port < 0) 374 + return egress_port; 375 + 402 376 filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT; 403 377 filter->action.port_mask = BIT(egress_port); 404 378 filter->type = OCELOT_VCAP_FILTER_OFFLOAD; 405 379 break; 406 380 case FLOW_ACTION_MIRRED: 381 + case FLOW_ACTION_MIRRED_INGRESS: 407 382 if (filter->block_id != VCAP_IS2) { 408 383 NL_SET_ERR_MSG_MOD(extack, 409 384 "Mirror action can only be offloaded to VCAP IS2"); ··· 416 387 "Last action must be GOTO"); 417 388 return -EOPNOTSUPP; 418 389 } 419 - egress_port = ocelot->ops->netdev_to_port(a->dev); 420 - if (egress_port < 0) { 421 - NL_SET_ERR_MSG_MOD(extack, 422 - "Destination not an ocelot port"); 423 - return -EOPNOTSUPP; 424 - } 390 + 391 + egress_port = ocelot_flower_parse_egress_port(ocelot, f, 392 + a, true, 393 + extack); 394 + if (egress_port < 0) 395 + return egress_port; 396 + 425 397 filter->egress_port.value = egress_port; 426 398 filter->action.mirror_ena = true; 427 399 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
+1
include/net/flow_offload.h
··· 685 685 u32 chain_index; 686 686 __be16 protocol; 687 687 u32 prio; 688 + bool skip_sw; 688 689 struct netlink_ext_ack *extack; 689 690 }; 690 691
+1
include/net/pkt_cls.h
··· 755 755 cls_common->chain_index = tp->chain->index; 756 756 cls_common->protocol = tp->protocol; 757 757 cls_common->prio = tp->prio >> 16; 758 + cls_common->skip_sw = tc_skip_sw(flags); 758 759 if (tc_skip_sw(flags) || flags & TCA_CLS_FLAGS_VERBOSE) 759 760 cls_common->extack = extack; 760 761 }
+59 -19
net/dsa/user.c
··· 1364 1364 static int 1365 1365 dsa_user_add_cls_matchall_mirred(struct net_device *dev, 1366 1366 struct tc_cls_matchall_offload *cls, 1367 - bool ingress) 1367 + bool ingress, bool ingress_target) 1368 1368 { 1369 1369 struct netlink_ext_ack *extack = cls->common.extack; 1370 1370 struct dsa_port *dp = dsa_user_to_port(dev); ··· 1376 1376 struct dsa_port *to_dp; 1377 1377 int err; 1378 1378 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"); 1380 1382 return -EOPNOTSUPP; 1383 + } 1381 1384 1382 - if (!flow_action_basic_hw_stats_check(&cls->rule->action, 1383 - cls->common.extack)) 1385 + if (!ds->ops->port_mirror_add) { 1386 + NL_SET_ERR_MSG_MOD(extack, 1387 + "Switch does not support mirroring operation"); 1388 + return -EOPNOTSUPP; 1389 + } 1390 + 1391 + if (!flow_action_basic_hw_stats_check(&cls->rule->action, extack)) 1384 1392 return -EOPNOTSUPP; 1385 1393 1386 1394 act = &cls->rule->action.entries[0]; ··· 1396 1388 if (!act->dev) 1397 1389 return -EINVAL; 1398 1390 1399 - if (!dsa_user_dev_check(act->dev)) 1400 - return -EOPNOTSUPP; 1401 - 1402 - to_dp = dsa_user_to_port(act->dev); 1391 + if (dsa_user_dev_check(act->dev)) { 1392 + if (ingress_target) { 1393 + /* We can only fulfill this using software assist */ 1394 + if (cls->common.skip_sw) { 1395 + NL_SET_ERR_MSG_MOD(extack, 1396 + "Can only mirred to ingress of DSA user port if filter also runs in software"); 1397 + return -EOPNOTSUPP; 1398 + } 1399 + to_dp = dp->cpu_dp; 1400 + } else { 1401 + to_dp = dsa_user_to_port(act->dev); 1402 + } 1403 + } else { 1404 + /* Handle mirroring to foreign target ports as a mirror towards 1405 + * the CPU. The software tc rule will take the packets from 1406 + * there. 1407 + */ 1408 + if (cls->common.skip_sw) { 1409 + NL_SET_ERR_MSG_MOD(extack, 1410 + "Can only mirred to CPU if filter also runs in software"); 1411 + return -EOPNOTSUPP; 1412 + } 1413 + to_dp = dp->cpu_dp; 1414 + } 1403 1415 1404 1416 if (dp->ds != to_dp->ds) { 1405 1417 NL_SET_ERR_MSG_MOD(extack, ··· 1474 1446 return -EOPNOTSUPP; 1475 1447 } 1476 1448 1477 - if (!flow_action_basic_hw_stats_check(&cls->rule->action, 1478 - cls->common.extack)) 1449 + if (!flow_action_basic_hw_stats_check(&cls->rule->action, extack)) 1479 1450 return -EOPNOTSUPP; 1480 1451 1481 1452 list_for_each_entry(mall_tc_entry, &p->mall_tc_list, list) { ··· 1512 1485 struct tc_cls_matchall_offload *cls, 1513 1486 bool ingress) 1514 1487 { 1515 - int err = -EOPNOTSUPP; 1488 + const struct flow_action *action = &cls->rule->action; 1489 + struct netlink_ext_ack *extack = cls->common.extack; 1516 1490 1517 - if (cls->common.protocol == htons(ETH_P_ALL) && 1518 - flow_offload_has_one_action(&cls->rule->action) && 1519 - cls->rule->action.entries[0].id == FLOW_ACTION_MIRRED) 1520 - err = dsa_user_add_cls_matchall_mirred(dev, cls, ingress); 1521 - else if (flow_offload_has_one_action(&cls->rule->action) && 1522 - cls->rule->action.entries[0].id == FLOW_ACTION_POLICE) 1523 - err = dsa_user_add_cls_matchall_police(dev, cls, ingress); 1491 + if (!flow_offload_has_one_action(action)) { 1492 + NL_SET_ERR_MSG_MOD(extack, 1493 + "Cannot offload matchall filter with more than one action"); 1494 + return -EOPNOTSUPP; 1495 + } 1524 1496 1525 - return err; 1497 + switch (action->entries[0].id) { 1498 + case FLOW_ACTION_MIRRED: 1499 + return dsa_user_add_cls_matchall_mirred(dev, cls, ingress, 1500 + false); 1501 + case FLOW_ACTION_MIRRED_INGRESS: 1502 + return dsa_user_add_cls_matchall_mirred(dev, cls, ingress, 1503 + true); 1504 + case FLOW_ACTION_POLICE: 1505 + return dsa_user_add_cls_matchall_police(dev, cls, ingress); 1506 + default: 1507 + NL_SET_ERR_MSG_MOD(extack, "Unknown action"); 1508 + break; 1509 + } 1510 + 1511 + return -EOPNOTSUPP; 1526 1512 } 1527 1513 1528 1514 static void dsa_user_del_cls_matchall(struct net_device *dev,