]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
net: mscc: ocelot: allow tc-flower mirred action towards foreign interfaces
authorVladimir Oltean <vladimir.oltean@nxp.com>
Wed, 23 Oct 2024 13:52:51 +0000 (16:52 +0300)
committerJakub Kicinski <kuba@kernel.org>
Thu, 31 Oct 2024 00:33:55 +0000 (17:33 -0700)
Debugging certain flows in the offloaded switch data path can be done by
installing two tc-mirred filters for mirroring: one in the hardware data
path, which copies the frames to the CPU, and one which takes the frame
from there and mirrors it to a virtual interface like a dummy device,
where it can be seen with tcpdump.

The effect of having 2 filters run on the same packet can be obtained by
default using tc, by not specifying either the 'skip_sw' or 'skip_hw'
keywords.

Instead of refusing to offload mirroring/redirecting packets towards
interfaces that aren't switch ports, just treat every other destination
for what it is: something that is handled in software, behind the CPU
port.

Usage:

$ ip link add dummy0 type dummy; ip link set dummy0 up
$ tc qdisc add dev swp0 clsact
$ tc filter add dev swp0 ingress protocol ip flower action mirred ingress mirror dev dummy0

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Link: https://patch.msgid.link/20241023135251.1752488-7-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/mscc/ocelot_flower.c

index a057ec3dab974b7db941cb0098faf4e6b108bc81..986b1f150e3b3d9b3b5cc69a7e09b4aa443f9679 100644 (file)
@@ -228,6 +228,32 @@ ocelot_flower_parse_egress_vlan_modify(struct ocelot_vcap_filter *filter,
        return 0;
 }
 
+static int
+ocelot_flower_parse_egress_port(struct ocelot *ocelot, struct flow_cls_offload *f,
+                               const struct flow_action_entry *a, bool mirror,
+                               struct netlink_ext_ack *extack)
+{
+       const char *act_string = mirror ? "mirror" : "redirect";
+       int egress_port = ocelot->ops->netdev_to_port(a->dev);
+       enum flow_action_id offloadable_act_id;
+
+       offloadable_act_id = mirror ? FLOW_ACTION_MIRRED : FLOW_ACTION_REDIRECT;
+
+       /* Mirroring towards foreign interfaces is handled in software */
+       if (egress_port < 0 || a->id != offloadable_act_id) {
+               if (f->common.skip_sw) {
+                       NL_SET_ERR_MSG_FMT(extack,
+                                          "Can only %s to %s if filter also runs in software",
+                                          act_string, egress_port < 0 ?
+                                          "CPU" : "ingress of ocelot port");
+                       return -EOPNOTSUPP;
+               }
+               egress_port = ocelot->num_phys_ports;
+       }
+
+       return egress_port;
+}
+
 static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
                                      bool ingress, struct flow_cls_offload *f,
                                      struct ocelot_vcap_filter *filter)
@@ -356,6 +382,7 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
                        break;
                case FLOW_ACTION_REDIRECT:
+               case FLOW_ACTION_REDIRECT_INGRESS:
                        if (filter->block_id != VCAP_IS2) {
                                NL_SET_ERR_MSG_MOD(extack,
                                                   "Redirect action can only be offloaded to VCAP IS2");
@@ -366,17 +393,19 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
                                                   "Last action must be GOTO");
                                return -EOPNOTSUPP;
                        }
-                       egress_port = ocelot->ops->netdev_to_port(a->dev);
-                       if (egress_port < 0) {
-                               NL_SET_ERR_MSG_MOD(extack,
-                                                  "Destination not an ocelot port");
-                               return -EOPNOTSUPP;
-                       }
+
+                       egress_port = ocelot_flower_parse_egress_port(ocelot, f,
+                                                                     a, false,
+                                                                     extack);
+                       if (egress_port < 0)
+                               return egress_port;
+
                        filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
                        filter->action.port_mask = BIT(egress_port);
                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
                        break;
                case FLOW_ACTION_MIRRED:
+               case FLOW_ACTION_MIRRED_INGRESS:
                        if (filter->block_id != VCAP_IS2) {
                                NL_SET_ERR_MSG_MOD(extack,
                                                   "Mirror action can only be offloaded to VCAP IS2");
@@ -387,12 +416,13 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
                                                   "Last action must be GOTO");
                                return -EOPNOTSUPP;
                        }
-                       egress_port = ocelot->ops->netdev_to_port(a->dev);
-                       if (egress_port < 0) {
-                               NL_SET_ERR_MSG_MOD(extack,
-                                                  "Destination not an ocelot port");
-                               return -EOPNOTSUPP;
-                       }
+
+                       egress_port = ocelot_flower_parse_egress_port(ocelot, f,
+                                                                     a, true,
+                                                                     extack);
+                       if (egress_port < 0)
+                               return egress_port;
+
                        filter->egress_port.value = egress_port;
                        filter->action.mirror_ena = true;
                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;