diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/br_mrp.c | 43 | ||||
-rw-r--r-- | net/bridge/br_mrp_switchdev.c | 171 | ||||
-rw-r--r-- | net/bridge/br_private_mrp.h | 38 | ||||
-rw-r--r-- | net/dsa/dsa_priv.h | 26 | ||||
-rw-r--r-- | net/dsa/port.c | 48 | ||||
-rw-r--r-- | net/dsa/slave.c | 22 | ||||
-rw-r--r-- | net/dsa/switch.c | 105 | ||||
-rw-r--r-- | net/dsa/tag_ocelot.c | 8 |
8 files changed, 368 insertions, 93 deletions
diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c index 01c67ed727a9c..12487f6fe9b48 100644 --- a/net/bridge/br_mrp.c +++ b/net/bridge/br_mrp.c @@ -639,7 +639,7 @@ int br_mrp_set_ring_role(struct net_bridge *br, struct br_mrp_ring_role *role) { struct br_mrp *mrp = br_mrp_find_id(br, role->ring_id); - int err; + enum br_mrp_hw_support support; if (!mrp) return -EINVAL; @@ -647,9 +647,9 @@ int br_mrp_set_ring_role(struct net_bridge *br, mrp->ring_role = role->ring_role; /* If there is an error just bailed out */ - err = br_mrp_switchdev_set_ring_role(br, mrp, role->ring_role); - if (err && err != -EOPNOTSUPP) - return err; + support = br_mrp_switchdev_set_ring_role(br, mrp, role->ring_role); + if (support == BR_MRP_NONE) + return -EOPNOTSUPP; /* Now detect if the HW actually applied the role or not. If the HW * applied the role it means that the SW will not to do those operations @@ -657,7 +657,7 @@ int br_mrp_set_ring_role(struct net_bridge *br, * SW when ring is open, but if the is not pushed to the HW the SW will * need to detect when the ring is open */ - mrp->ring_role_offloaded = err == -EOPNOTSUPP ? 0 : 1; + mrp->ring_role_offloaded = support == BR_MRP_SW ? 0 : 1; return 0; } @@ -670,6 +670,7 @@ int br_mrp_start_test(struct net_bridge *br, struct br_mrp_start_test *test) { struct br_mrp *mrp = br_mrp_find_id(br, test->ring_id); + enum br_mrp_hw_support support; if (!mrp) return -EINVAL; @@ -677,9 +678,13 @@ int br_mrp_start_test(struct net_bridge *br, /* Try to push it to the HW and if it fails then continue with SW * implementation and if that also fails then return error. */ - if (!br_mrp_switchdev_send_ring_test(br, mrp, test->interval, - test->max_miss, test->period, - test->monitor)) + support = br_mrp_switchdev_send_ring_test(br, mrp, test->interval, + test->max_miss, test->period, + test->monitor); + if (support == BR_MRP_NONE) + return -EOPNOTSUPP; + + if (support == BR_MRP_HW) return 0; mrp->test_interval = test->interval; @@ -721,8 +726,8 @@ int br_mrp_set_in_state(struct net_bridge *br, struct br_mrp_in_state *state) int br_mrp_set_in_role(struct net_bridge *br, struct br_mrp_in_role *role) { struct br_mrp *mrp = br_mrp_find_id(br, role->ring_id); + enum br_mrp_hw_support support; struct net_bridge_port *p; - int err; if (!mrp) return -EINVAL; @@ -780,10 +785,10 @@ int br_mrp_set_in_role(struct net_bridge *br, struct br_mrp_in_role *role) mrp->in_id = role->in_id; /* If there is an error just bailed out */ - err = br_mrp_switchdev_set_in_role(br, mrp, role->in_id, - role->ring_id, role->in_role); - if (err && err != -EOPNOTSUPP) - return err; + support = br_mrp_switchdev_set_in_role(br, mrp, role->in_id, + role->ring_id, role->in_role); + if (support == BR_MRP_NONE) + return -EOPNOTSUPP; /* Now detect if the HW actually applied the role or not. If the HW * applied the role it means that the SW will not to do those operations @@ -791,7 +796,7 @@ int br_mrp_set_in_role(struct net_bridge *br, struct br_mrp_in_role *role) * SW when interconnect ring is open, but if the is not pushed to the HW * the SW will need to detect when the interconnect ring is open. */ - mrp->in_role_offloaded = err == -EOPNOTSUPP ? 0 : 1; + mrp->in_role_offloaded = support == BR_MRP_SW ? 0 : 1; return 0; } @@ -804,6 +809,7 @@ int br_mrp_start_in_test(struct net_bridge *br, struct br_mrp_start_in_test *in_test) { struct br_mrp *mrp = br_mrp_find_in_id(br, in_test->in_id); + enum br_mrp_hw_support support; if (!mrp) return -EINVAL; @@ -814,8 +820,13 @@ int br_mrp_start_in_test(struct net_bridge *br, /* Try to push it to the HW and if it fails then continue with SW * implementation and if that also fails then return error. */ - if (!br_mrp_switchdev_send_in_test(br, mrp, in_test->interval, - in_test->max_miss, in_test->period)) + support = br_mrp_switchdev_send_in_test(br, mrp, in_test->interval, + in_test->max_miss, + in_test->period); + if (support == BR_MRP_NONE) + return -EOPNOTSUPP; + + if (support == BR_MRP_HW) return 0; mrp->in_test_interval = in_test->interval; diff --git a/net/bridge/br_mrp_switchdev.c b/net/bridge/br_mrp_switchdev.c index 3c9a4abcf4eea..cb54b324fa8cc 100644 --- a/net/bridge/br_mrp_switchdev.c +++ b/net/bridge/br_mrp_switchdev.c @@ -4,6 +4,30 @@ #include "br_private_mrp.h" +static enum br_mrp_hw_support +br_mrp_switchdev_port_obj(struct net_bridge *br, + const struct switchdev_obj *obj, bool add) +{ + int err; + + if (add) + err = switchdev_port_obj_add(br->dev, obj, NULL); + else + err = switchdev_port_obj_del(br->dev, obj); + + /* In case of success just return and notify the SW that doesn't need + * to do anything + */ + if (!err) + return BR_MRP_HW; + + if (err != -EOPNOTSUPP) + return BR_MRP_NONE; + + /* Continue with SW backup */ + return BR_MRP_SW; +} + int br_mrp_switchdev_add(struct net_bridge *br, struct br_mrp *mrp) { struct switchdev_obj_mrp mrp_obj = { @@ -14,14 +38,11 @@ int br_mrp_switchdev_add(struct net_bridge *br, struct br_mrp *mrp) .ring_id = mrp->ring_id, .prio = mrp->prio, }; - int err; - err = switchdev_port_obj_add(br->dev, &mrp_obj.obj, NULL); + if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) + return 0; - if (err && err != -EOPNOTSUPP) - return err; - - return 0; + return switchdev_port_obj_add(br->dev, &mrp_obj.obj, NULL); } int br_mrp_switchdev_del(struct net_bridge *br, struct br_mrp *mrp) @@ -33,40 +54,54 @@ int br_mrp_switchdev_del(struct net_bridge *br, struct br_mrp *mrp) .s_port = NULL, .ring_id = mrp->ring_id, }; - int err; - - err = switchdev_port_obj_del(br->dev, &mrp_obj.obj); - if (err && err != -EOPNOTSUPP) - return err; + if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) + return 0; - return 0; + return switchdev_port_obj_del(br->dev, &mrp_obj.obj); } -int br_mrp_switchdev_set_ring_role(struct net_bridge *br, - struct br_mrp *mrp, - enum br_mrp_ring_role_type role) +enum br_mrp_hw_support +br_mrp_switchdev_set_ring_role(struct net_bridge *br, struct br_mrp *mrp, + enum br_mrp_ring_role_type role) { struct switchdev_obj_ring_role_mrp mrp_role = { .obj.orig_dev = br->dev, .obj.id = SWITCHDEV_OBJ_ID_RING_ROLE_MRP, .ring_role = role, .ring_id = mrp->ring_id, + .sw_backup = false, }; + enum br_mrp_hw_support support; int err; - if (role == BR_MRP_RING_ROLE_DISABLED) - err = switchdev_port_obj_del(br->dev, &mrp_role.obj); - else + if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) + return BR_MRP_SW; + + support = br_mrp_switchdev_port_obj(br, &mrp_role.obj, + role != BR_MRP_RING_ROLE_DISABLED); + if (support != BR_MRP_SW) + return support; + + /* If the driver can't configure to run completely the protocol in HW, + * then try again to configure the HW so the SW can run the protocol. + */ + mrp_role.sw_backup = true; + if (role != BR_MRP_RING_ROLE_DISABLED) err = switchdev_port_obj_add(br->dev, &mrp_role.obj, NULL); + else + err = switchdev_port_obj_del(br->dev, &mrp_role.obj); - return err; + if (!err) + return BR_MRP_SW; + + return BR_MRP_NONE; } -int br_mrp_switchdev_send_ring_test(struct net_bridge *br, - struct br_mrp *mrp, u32 interval, - u8 max_miss, u32 period, - bool monitor) +enum br_mrp_hw_support +br_mrp_switchdev_send_ring_test(struct net_bridge *br, struct br_mrp *mrp, + u32 interval, u8 max_miss, u32 period, + bool monitor) { struct switchdev_obj_ring_test_mrp test = { .obj.orig_dev = br->dev, @@ -77,14 +112,11 @@ int br_mrp_switchdev_send_ring_test(struct net_bridge *br, .period = period, .monitor = monitor, }; - int err; - if (interval == 0) - err = switchdev_port_obj_del(br->dev, &test.obj); - else - err = switchdev_port_obj_add(br->dev, &test.obj, NULL); + if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) + return BR_MRP_SW; - return err; + return br_mrp_switchdev_port_obj(br, &test.obj, interval != 0); } int br_mrp_switchdev_set_ring_state(struct net_bridge *br, @@ -97,19 +129,17 @@ int br_mrp_switchdev_set_ring_state(struct net_bridge *br, .ring_state = state, .ring_id = mrp->ring_id, }; - int err; - - err = switchdev_port_obj_add(br->dev, &mrp_state.obj, NULL); - if (err && err != -EOPNOTSUPP) - return err; + if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) + return 0; - return 0; + return switchdev_port_obj_add(br->dev, &mrp_state.obj, NULL); } -int br_mrp_switchdev_set_in_role(struct net_bridge *br, struct br_mrp *mrp, - u16 in_id, u32 ring_id, - enum br_mrp_in_role_type role) +enum br_mrp_hw_support +br_mrp_switchdev_set_in_role(struct net_bridge *br, struct br_mrp *mrp, + u16 in_id, u32 ring_id, + enum br_mrp_in_role_type role) { struct switchdev_obj_in_role_mrp mrp_role = { .obj.orig_dev = br->dev, @@ -118,15 +148,32 @@ int br_mrp_switchdev_set_in_role(struct net_bridge *br, struct br_mrp *mrp, .in_id = mrp->in_id, .ring_id = mrp->ring_id, .i_port = rtnl_dereference(mrp->i_port)->dev, + .sw_backup = false, }; + enum br_mrp_hw_support support; int err; - if (role == BR_MRP_IN_ROLE_DISABLED) - err = switchdev_port_obj_del(br->dev, &mrp_role.obj); - else + if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) + return BR_MRP_SW; + + support = br_mrp_switchdev_port_obj(br, &mrp_role.obj, + role != BR_MRP_IN_ROLE_DISABLED); + if (support != BR_MRP_NONE) + return support; + + /* If the driver can't configure to run completely the protocol in HW, + * then try again to configure the HW so the SW can run the protocol. + */ + mrp_role.sw_backup = true; + if (role != BR_MRP_IN_ROLE_DISABLED) err = switchdev_port_obj_add(br->dev, &mrp_role.obj, NULL); + else + err = switchdev_port_obj_del(br->dev, &mrp_role.obj); + + if (!err) + return BR_MRP_SW; - return err; + return BR_MRP_NONE; } int br_mrp_switchdev_set_in_state(struct net_bridge *br, struct br_mrp *mrp, @@ -138,18 +185,16 @@ int br_mrp_switchdev_set_in_state(struct net_bridge *br, struct br_mrp *mrp, .in_state = state, .in_id = mrp->in_id, }; - int err; - - err = switchdev_port_obj_add(br->dev, &mrp_state.obj, NULL); - if (err && err != -EOPNOTSUPP) - return err; + if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) + return 0; - return 0; + return switchdev_port_obj_add(br->dev, &mrp_state.obj, NULL); } -int br_mrp_switchdev_send_in_test(struct net_bridge *br, struct br_mrp *mrp, - u32 interval, u8 max_miss, u32 period) +enum br_mrp_hw_support +br_mrp_switchdev_send_in_test(struct net_bridge *br, struct br_mrp *mrp, + u32 interval, u8 max_miss, u32 period) { struct switchdev_obj_in_test_mrp test = { .obj.orig_dev = br->dev, @@ -159,14 +204,11 @@ int br_mrp_switchdev_send_in_test(struct net_bridge *br, struct br_mrp *mrp, .in_id = mrp->in_id, .period = period, }; - int err; - if (interval == 0) - err = switchdev_port_obj_del(br->dev, &test.obj); - else - err = switchdev_port_obj_add(br->dev, &test.obj, NULL); + if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) + return BR_MRP_SW; - return err; + return br_mrp_switchdev_port_obj(br, &test.obj, interval != 0); } int br_mrp_port_switchdev_set_state(struct net_bridge_port *p, u32 state) @@ -176,14 +218,11 @@ int br_mrp_port_switchdev_set_state(struct net_bridge_port *p, u32 state) .id = SWITCHDEV_ATTR_ID_PORT_STP_STATE, .u.stp_state = state, }; - int err; - err = switchdev_port_attr_set(p->dev, &attr, NULL); - if (err && err != -EOPNOTSUPP) - br_warn(p->br, "error setting offload MRP state on port %u(%s)\n", - (unsigned int)p->port_no, p->dev->name); + if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) + return 0; - return err; + return switchdev_port_attr_set(p->dev, &attr, NULL); } int br_mrp_port_switchdev_set_role(struct net_bridge_port *p, @@ -194,11 +233,9 @@ int br_mrp_port_switchdev_set_role(struct net_bridge_port *p, .id = SWITCHDEV_ATTR_ID_MRP_PORT_ROLE, .u.mrp_port_role = role, }; - int err; - err = switchdev_port_attr_set(p->dev, &attr, NULL); - if (err && err != -EOPNOTSUPP) - return err; + if (!IS_ENABLED(CONFIG_NET_SWITCHDEV)) + return 0; - return 0; + return switchdev_port_attr_set(p->dev, &attr, NULL); } diff --git a/net/bridge/br_private_mrp.h b/net/bridge/br_private_mrp.h index 2514954c14316..9559aa2750fbc 100644 --- a/net/bridge/br_private_mrp.h +++ b/net/bridge/br_private_mrp.h @@ -46,6 +46,20 @@ struct br_mrp { struct rcu_head rcu; }; +/* This type is returned by br_mrp_switchdev functions that allow to have a SW + * backup in case the HW can't implement completely the protocol. + * BR_MRP_NONE - means the HW can't run at all the protocol, so the SW stops + * configuring the node anymore. + * BR_MRP_SW - the HW can help the SW to run the protocol, by redirecting MRP + * frames to CPU. + * BR_MRP_HW - the HW can implement completely the protocol. + */ +enum br_mrp_hw_support { + BR_MRP_NONE, + BR_MRP_SW, + BR_MRP_HW, +}; + /* br_mrp.c */ int br_mrp_add(struct net_bridge *br, struct br_mrp_instance *instance); int br_mrp_del(struct net_bridge *br, struct br_mrp_instance *instance); @@ -65,23 +79,27 @@ int br_mrp_start_in_test(struct net_bridge *br, /* br_mrp_switchdev.c */ int br_mrp_switchdev_add(struct net_bridge *br, struct br_mrp *mrp); int br_mrp_switchdev_del(struct net_bridge *br, struct br_mrp *mrp); -int br_mrp_switchdev_set_ring_role(struct net_bridge *br, struct br_mrp *mrp, - enum br_mrp_ring_role_type role); +enum br_mrp_hw_support +br_mrp_switchdev_set_ring_role(struct net_bridge *br, struct br_mrp *mrp, + enum br_mrp_ring_role_type role); int br_mrp_switchdev_set_ring_state(struct net_bridge *br, struct br_mrp *mrp, enum br_mrp_ring_state_type state); -int br_mrp_switchdev_send_ring_test(struct net_bridge *br, struct br_mrp *mrp, - u32 interval, u8 max_miss, u32 period, - bool monitor); +enum br_mrp_hw_support +br_mrp_switchdev_send_ring_test(struct net_bridge *br, struct br_mrp *mrp, + u32 interval, u8 max_miss, u32 period, + bool monitor); int br_mrp_port_switchdev_set_state(struct net_bridge_port *p, u32 state); int br_mrp_port_switchdev_set_role(struct net_bridge_port *p, enum br_mrp_port_role_type role); -int br_mrp_switchdev_set_in_role(struct net_bridge *br, struct br_mrp *mrp, - u16 in_id, u32 ring_id, - enum br_mrp_in_role_type role); +enum br_mrp_hw_support +br_mrp_switchdev_set_in_role(struct net_bridge *br, struct br_mrp *mrp, + u16 in_id, u32 ring_id, + enum br_mrp_in_role_type role); int br_mrp_switchdev_set_in_state(struct net_bridge *br, struct br_mrp *mrp, enum br_mrp_in_state_type state); -int br_mrp_switchdev_send_in_test(struct net_bridge *br, struct br_mrp *mrp, - u32 interval, u8 max_miss, u32 period); +enum br_mrp_hw_support +br_mrp_switchdev_send_in_test(struct net_bridge *br, struct br_mrp *mrp, + u32 interval, u8 max_miss, u32 period); /* br_mrp_netlink.c */ int br_mrp_ring_port_open(struct net_device *dev, u8 loc); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index e9d1e76c42ba2..2eeaa42f2e083 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -31,6 +31,10 @@ enum { DSA_NOTIFIER_VLAN_DEL, DSA_NOTIFIER_MTU, DSA_NOTIFIER_TAG_PROTO, + DSA_NOTIFIER_MRP_ADD, + DSA_NOTIFIER_MRP_DEL, + DSA_NOTIFIER_MRP_ADD_RING_ROLE, + DSA_NOTIFIER_MRP_DEL_RING_ROLE, }; /* DSA_NOTIFIER_AGEING_TIME */ @@ -91,6 +95,20 @@ struct dsa_notifier_tag_proto_info { const struct dsa_device_ops *tag_ops; }; +/* DSA_NOTIFIER_MRP_* */ +struct dsa_notifier_mrp_info { + const struct switchdev_obj_mrp *mrp; + int sw_index; + int port; +}; + +/* DSA_NOTIFIER_MRP_* */ +struct dsa_notifier_mrp_ring_role_info { + const struct switchdev_obj_ring_role_mrp *mrp; + int sw_index; + int port; +}; + struct dsa_switchdev_event_work { struct dsa_switch *ds; int port; @@ -198,6 +216,14 @@ int dsa_port_vlan_add(struct dsa_port *dp, struct netlink_ext_ack *extack); int dsa_port_vlan_del(struct dsa_port *dp, const struct switchdev_obj_port_vlan *vlan); +int dsa_port_mrp_add(const struct dsa_port *dp, + const struct switchdev_obj_mrp *mrp); +int dsa_port_mrp_del(const struct dsa_port *dp, + const struct switchdev_obj_mrp *mrp); +int dsa_port_mrp_add_ring_role(const struct dsa_port *dp, + const struct switchdev_obj_ring_role_mrp *mrp); +int dsa_port_mrp_del_ring_role(const struct dsa_port *dp, + const struct switchdev_obj_ring_role_mrp *mrp); int dsa_port_link_register_of(struct dsa_port *dp); void dsa_port_link_unregister_of(struct dsa_port *dp); int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr); diff --git a/net/dsa/port.c b/net/dsa/port.c index 14a1d0d776573..c9c6d7ab3f476 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -564,6 +564,54 @@ int dsa_port_vlan_del(struct dsa_port *dp, return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); } +int dsa_port_mrp_add(const struct dsa_port *dp, + const struct switchdev_obj_mrp *mrp) +{ + struct dsa_notifier_mrp_info info = { + .sw_index = dp->ds->index, + .port = dp->index, + .mrp = mrp, + }; + + return dsa_port_notify(dp, DSA_NOTIFIER_MRP_ADD, &info); +} + +int dsa_port_mrp_del(const struct dsa_port *dp, + const struct switchdev_obj_mrp *mrp) +{ + struct dsa_notifier_mrp_info info = { + .sw_index = dp->ds->index, + .port = dp->index, + .mrp = mrp, + }; + + return dsa_port_notify(dp, DSA_NOTIFIER_MRP_DEL, &info); +} + +int dsa_port_mrp_add_ring_role(const struct dsa_port *dp, + const struct switchdev_obj_ring_role_mrp *mrp) +{ + struct dsa_notifier_mrp_ring_role_info info = { + .sw_index = dp->ds->index, + .port = dp->index, + .mrp = mrp, + }; + + return dsa_port_notify(dp, DSA_NOTIFIER_MRP_ADD_RING_ROLE, &info); +} + +int dsa_port_mrp_del_ring_role(const struct dsa_port *dp, + const struct switchdev_obj_ring_role_mrp *mrp) +{ + struct dsa_notifier_mrp_ring_role_info info = { + .sw_index = dp->ds->index, + .port = dp->index, + .mrp = mrp, + }; + + return dsa_port_notify(dp, DSA_NOTIFIER_MRP_DEL_RING_ROLE, &info); +} + void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, const struct dsa_device_ops *tag_ops) { diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 5ecb43a1b6e07..491e3761b5f4a 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -404,6 +404,17 @@ static int dsa_slave_port_obj_add(struct net_device *dev, case SWITCHDEV_OBJ_ID_PORT_VLAN: err = dsa_slave_vlan_add(dev, obj, extack); break; + case SWITCHDEV_OBJ_ID_MRP: + if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) + return -EOPNOTSUPP; + err = dsa_port_mrp_add(dp, SWITCHDEV_OBJ_MRP(obj)); + break; + case SWITCHDEV_OBJ_ID_RING_ROLE_MRP: + if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) + return -EOPNOTSUPP; + err = dsa_port_mrp_add_ring_role(dp, + SWITCHDEV_OBJ_RING_ROLE_MRP(obj)); + break; default: err = -EOPNOTSUPP; break; @@ -461,6 +472,17 @@ static int dsa_slave_port_obj_del(struct net_device *dev, case SWITCHDEV_OBJ_ID_PORT_VLAN: err = dsa_slave_vlan_del(dev, obj); break; + case SWITCHDEV_OBJ_ID_MRP: + if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) + return -EOPNOTSUPP; + err = dsa_port_mrp_del(dp, SWITCHDEV_OBJ_MRP(obj)); + break; + case SWITCHDEV_OBJ_ID_RING_ROLE_MRP: + if (!dsa_port_offloads_netdev(dp, obj->orig_dev)) + return -EOPNOTSUPP; + err = dsa_port_mrp_del_ring_role(dp, + SWITCHDEV_OBJ_RING_ROLE_MRP(obj)); + break; default: err = -EOPNOTSUPP; break; diff --git a/net/dsa/switch.c b/net/dsa/switch.c index db2a9b2219889..4b5da89dc27a2 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -372,6 +372,99 @@ static int dsa_switch_change_tag_proto(struct dsa_switch *ds, return 0; } +static bool dsa_switch_mrp_match(struct dsa_switch *ds, int port, + struct dsa_notifier_mrp_info *info) +{ + if (ds->index == info->sw_index && port == info->port) + return true; + + if (dsa_is_dsa_port(ds, port)) + return true; + + return false; +} + +static int dsa_switch_mrp_add(struct dsa_switch *ds, + struct dsa_notifier_mrp_info *info) +{ + int err = 0; + int port; + + if (!ds->ops->port_mrp_add) + return -EOPNOTSUPP; + + for (port = 0; port < ds->num_ports; port++) { + if (dsa_switch_mrp_match(ds, port, info)) { + err = ds->ops->port_mrp_add(ds, port, info->mrp); + if (err) + break; + } + } + + return err; +} + +static int dsa_switch_mrp_del(struct dsa_switch *ds, + struct dsa_notifier_mrp_info *info) +{ + if (!ds->ops->port_mrp_del) + return -EOPNOTSUPP; + + if (ds->index == info->sw_index) + return ds->ops->port_mrp_del(ds, info->port, info->mrp); + + return 0; +} + +static bool +dsa_switch_mrp_ring_role_match(struct dsa_switch *ds, int port, + struct dsa_notifier_mrp_ring_role_info *info) +{ + if (ds->index == info->sw_index && port == info->port) + return true; + + if (dsa_is_dsa_port(ds, port)) + return true; + + return false; +} + +static int +dsa_switch_mrp_add_ring_role(struct dsa_switch *ds, + struct dsa_notifier_mrp_ring_role_info *info) +{ + int err = 0; + int port; + + if (!ds->ops->port_mrp_add) + return -EOPNOTSUPP; + + for (port = 0; port < ds->num_ports; port++) { + if (dsa_switch_mrp_ring_role_match(ds, port, info)) { + err = ds->ops->port_mrp_add_ring_role(ds, port, + info->mrp); + if (err) + break; + } + } + + return err; +} + +static int +dsa_switch_mrp_del_ring_role(struct dsa_switch *ds, + struct dsa_notifier_mrp_ring_role_info *info) +{ + if (!ds->ops->port_mrp_del) + return -EOPNOTSUPP; + + if (ds->index == info->sw_index) + return ds->ops->port_mrp_del_ring_role(ds, info->port, + info->mrp); + + return 0; +} + static int dsa_switch_event(struct notifier_block *nb, unsigned long event, void *info) { @@ -427,6 +520,18 @@ static int dsa_switch_event(struct notifier_block *nb, case DSA_NOTIFIER_TAG_PROTO: err = dsa_switch_change_tag_proto(ds, info); break; + case DSA_NOTIFIER_MRP_ADD: + err = dsa_switch_mrp_add(ds, info); + break; + case DSA_NOTIFIER_MRP_DEL: + err = dsa_switch_mrp_del(ds, info); + break; + case DSA_NOTIFIER_MRP_ADD_RING_ROLE: + err = dsa_switch_mrp_add_ring_role(ds, info); + break; + case DSA_NOTIFIER_MRP_DEL_RING_ROLE: + err = dsa_switch_mrp_del_ring_role(ds, info); + break; default: err = -EOPNOTSUPP; break; diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c index f9df9cac81c58..743809b5806b8 100644 --- a/net/dsa/tag_ocelot.c +++ b/net/dsa/tag_ocelot.c @@ -83,6 +83,7 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, struct dsa_port *dp; u8 *extraction; u16 vlan_tpid; + u64 cpuq; /* Revert skb->data by the amount consumed by the DSA master, * so it points to the beginning of the frame. @@ -112,6 +113,7 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, ocelot_xfh_get_qos_class(extraction, &qos_class); ocelot_xfh_get_tag_type(extraction, &tag_type); ocelot_xfh_get_vlan_tci(extraction, &vlan_tci); + ocelot_xfh_get_cpuq(extraction, &cpuq); skb->dev = dsa_master_find_slave(netdev, 0, src_port); if (!skb->dev) @@ -126,6 +128,12 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, skb->offload_fwd_mark = 1; skb->priority = qos_class; +#if IS_ENABLED(CONFIG_BRIDGE_MRP) + if (eth_hdr(skb)->h_proto == cpu_to_be16(ETH_P_MRP) && + cpuq & BIT(OCELOT_MRP_CPUQ)) + skb->offload_fwd_mark = 0; +#endif + /* Ocelot switches copy frames unmodified to the CPU. However, it is * possible for the user to request a VLAN modification through * VCAP_IS1_ACT_VID_REPLACE_ENA. In this case, what will happen is that |