All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next v3 0/5] bridge: mrp: Extend br_mrp_switchdev_*
@ 2021-02-09 20:21 ` Horatiu Vultur
  0 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-09 20:21 UTC (permalink / raw)
  To: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, vladimir.oltean, claudiu.manoil, alexandre.belloni,
	UNGLinuxDriver, netdev, linux-kernel, bridge
  Cc: Horatiu Vultur

This patch series extends MRP switchdev to allow the SW to have a better
understanding if the HW can implement the MRP functionality or it needs
to help the HW to run it. There are 3 cases:
- when HW can't implement at all the functionality.
- when HW can implement a part of the functionality but needs the SW
  implement the rest. For example if it can't detect when it stops
  receiving MRP Test frames but it can copy the MRP frames to CPU to
  allow the SW to determine this.  Another example is generating the MRP
  Test frames. If HW can't do that then the SW is used as backup.
- when HW can implement completely the functionality.

So, initially the SW tries to offload the entire functionality in HW, if
that fails it tries offload parts of the functionality in HW and use the
SW as helper and if also this fails then MRP can't run on this HW.

Also implement the switchdev calls for Ocelot driver. This is an example
where the HW can't run completely the functionality but it can help the SW
to run it, by trapping all MRP frames to CPU.

v3:
 - implement the switchdev calls needed by Ocelot driver.
v2:
 - fix typos in comments and in commit messages
 - remove some of the comments
 - move repeated code in helper function
 - fix issue when deleting a node when sw_backup was true

Horatiu Vultur (5):
  switchdev: mrp: Extend ring_role_mrp and in_role_mrp
  bridge: mrp: Add 'enum br_mrp_hw_support'
  bridge: mrp: Extend br_mrp_switchdev to detect better the errors
  bridge: mrp: Update br_mrp to use new return values of
    br_mrp_switchdev
  net: mscc: ocelot: Add support for MRP

 drivers/net/ethernet/mscc/ocelot_net.c     | 154 +++++++++++++++++++
 drivers/net/ethernet/mscc/ocelot_vsc7514.c |   6 +
 include/net/switchdev.h                    |   2 +
 include/soc/mscc/ocelot.h                  |   6 +
 net/bridge/br_mrp.c                        |  43 ++++--
 net/bridge/br_mrp_switchdev.c              | 171 +++++++++++++--------
 net/bridge/br_private_mrp.h                |  38 +++--
 7 files changed, 327 insertions(+), 93 deletions(-)

-- 
2.27.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Bridge] [PATCH net-next v3 0/5] bridge: mrp: Extend br_mrp_switchdev_*
@ 2021-02-09 20:21 ` Horatiu Vultur
  0 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-09 20:21 UTC (permalink / raw)
  To: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, vladimir.oltean, claudiu.manoil, alexandre.belloni,
	UNGLinuxDriver, netdev, linux-kernel, bridge
  Cc: Horatiu Vultur

This patch series extends MRP switchdev to allow the SW to have a better
understanding if the HW can implement the MRP functionality or it needs
to help the HW to run it. There are 3 cases:
- when HW can't implement at all the functionality.
- when HW can implement a part of the functionality but needs the SW
  implement the rest. For example if it can't detect when it stops
  receiving MRP Test frames but it can copy the MRP frames to CPU to
  allow the SW to determine this.  Another example is generating the MRP
  Test frames. If HW can't do that then the SW is used as backup.
- when HW can implement completely the functionality.

So, initially the SW tries to offload the entire functionality in HW, if
that fails it tries offload parts of the functionality in HW and use the
SW as helper and if also this fails then MRP can't run on this HW.

Also implement the switchdev calls for Ocelot driver. This is an example
where the HW can't run completely the functionality but it can help the SW
to run it, by trapping all MRP frames to CPU.

v3:
 - implement the switchdev calls needed by Ocelot driver.
v2:
 - fix typos in comments and in commit messages
 - remove some of the comments
 - move repeated code in helper function
 - fix issue when deleting a node when sw_backup was true

Horatiu Vultur (5):
  switchdev: mrp: Extend ring_role_mrp and in_role_mrp
  bridge: mrp: Add 'enum br_mrp_hw_support'
  bridge: mrp: Extend br_mrp_switchdev to detect better the errors
  bridge: mrp: Update br_mrp to use new return values of
    br_mrp_switchdev
  net: mscc: ocelot: Add support for MRP

 drivers/net/ethernet/mscc/ocelot_net.c     | 154 +++++++++++++++++++
 drivers/net/ethernet/mscc/ocelot_vsc7514.c |   6 +
 include/net/switchdev.h                    |   2 +
 include/soc/mscc/ocelot.h                  |   6 +
 net/bridge/br_mrp.c                        |  43 ++++--
 net/bridge/br_mrp_switchdev.c              | 171 +++++++++++++--------
 net/bridge/br_private_mrp.h                |  38 +++--
 7 files changed, 327 insertions(+), 93 deletions(-)

-- 
2.27.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH net-next v3 1/5] switchdev: mrp: Extend ring_role_mrp and in_role_mrp
  2021-02-09 20:21 ` [Bridge] " Horatiu Vultur
@ 2021-02-09 20:21   ` Horatiu Vultur
  -1 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-09 20:21 UTC (permalink / raw)
  To: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, vladimir.oltean, claudiu.manoil, alexandre.belloni,
	UNGLinuxDriver, netdev, linux-kernel, bridge
  Cc: Horatiu Vultur

Add the member sw_backup to the structures switchdev_obj_ring_role_mrp
and switchdev_obj_in_role_mrp. In this way the SW can call the driver in
2 ways, once when sw_backup is set to false, meaning that the driver
should implement this completely in HW. And if that is not supported the
SW will call again but with sw_backup set to true, meaning that the
HW should help or allow the SW to run the protocol.

For example when role is MRM, if the HW can't detect when it stops
receiving MRP Test frames but it can trap these frames to CPU, then it
needs to return -EOPNOTSUPP when sw_backup is false and return 0 when
sw_backup is true.

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
 include/net/switchdev.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 6dcfc4c51a6e..067f259279e1 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -130,6 +130,7 @@ struct switchdev_obj_ring_role_mrp {
 	struct switchdev_obj obj;
 	u8 ring_role;
 	u32 ring_id;
+	u8 sw_backup;
 };
 
 #define SWITCHDEV_OBJ_RING_ROLE_MRP(OBJ) \
@@ -164,6 +165,7 @@ struct switchdev_obj_in_role_mrp {
 	u32 ring_id;
 	u16 in_id;
 	u8 in_role;
+	u8 sw_backup;
 };
 
 #define SWITCHDEV_OBJ_IN_ROLE_MRP(OBJ) \
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Bridge] [PATCH net-next v3 1/5] switchdev: mrp: Extend ring_role_mrp and in_role_mrp
@ 2021-02-09 20:21   ` Horatiu Vultur
  0 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-09 20:21 UTC (permalink / raw)
  To: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, vladimir.oltean, claudiu.manoil, alexandre.belloni,
	UNGLinuxDriver, netdev, linux-kernel, bridge
  Cc: Horatiu Vultur

Add the member sw_backup to the structures switchdev_obj_ring_role_mrp
and switchdev_obj_in_role_mrp. In this way the SW can call the driver in
2 ways, once when sw_backup is set to false, meaning that the driver
should implement this completely in HW. And if that is not supported the
SW will call again but with sw_backup set to true, meaning that the
HW should help or allow the SW to run the protocol.

For example when role is MRM, if the HW can't detect when it stops
receiving MRP Test frames but it can trap these frames to CPU, then it
needs to return -EOPNOTSUPP when sw_backup is false and return 0 when
sw_backup is true.

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
 include/net/switchdev.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 6dcfc4c51a6e..067f259279e1 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -130,6 +130,7 @@ struct switchdev_obj_ring_role_mrp {
 	struct switchdev_obj obj;
 	u8 ring_role;
 	u32 ring_id;
+	u8 sw_backup;
 };
 
 #define SWITCHDEV_OBJ_RING_ROLE_MRP(OBJ) \
@@ -164,6 +165,7 @@ struct switchdev_obj_in_role_mrp {
 	u32 ring_id;
 	u16 in_id;
 	u8 in_role;
+	u8 sw_backup;
 };
 
 #define SWITCHDEV_OBJ_IN_ROLE_MRP(OBJ) \
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH net-next v3 2/5] bridge: mrp: Add 'enum br_mrp_hw_support'
  2021-02-09 20:21 ` [Bridge] " Horatiu Vultur
@ 2021-02-09 20:21   ` Horatiu Vultur
  -1 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-09 20:21 UTC (permalink / raw)
  To: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, vladimir.oltean, claudiu.manoil, alexandre.belloni,
	UNGLinuxDriver, netdev, linux-kernel, bridge
  Cc: Horatiu Vultur

Add the enum br_mrp_hw_support that is used by the br_mrp_switchdev
functions to allow the SW to detect the cases where HW can't implement
the functionality or when SW is used as a backup.

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
 net/bridge/br_private_mrp.h | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/net/bridge/br_private_mrp.h b/net/bridge/br_private_mrp.h
index 32a48e5418da..a94017f86cda 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);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Bridge] [PATCH net-next v3 2/5] bridge: mrp: Add 'enum br_mrp_hw_support'
@ 2021-02-09 20:21   ` Horatiu Vultur
  0 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-09 20:21 UTC (permalink / raw)
  To: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, vladimir.oltean, claudiu.manoil, alexandre.belloni,
	UNGLinuxDriver, netdev, linux-kernel, bridge
  Cc: Horatiu Vultur

Add the enum br_mrp_hw_support that is used by the br_mrp_switchdev
functions to allow the SW to detect the cases where HW can't implement
the functionality or when SW is used as a backup.

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
 net/bridge/br_private_mrp.h | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/net/bridge/br_private_mrp.h b/net/bridge/br_private_mrp.h
index 32a48e5418da..a94017f86cda 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);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH net-next v3 3/5] bridge: mrp: Extend br_mrp_switchdev to detect better the errors
  2021-02-09 20:21 ` [Bridge] " Horatiu Vultur
@ 2021-02-09 20:21   ` Horatiu Vultur
  -1 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-09 20:21 UTC (permalink / raw)
  To: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, vladimir.oltean, claudiu.manoil, alexandre.belloni,
	UNGLinuxDriver, netdev, linux-kernel, bridge
  Cc: Horatiu Vultur

This patch extends the br_mrp_switchdev functions to be able to have a
better understanding what cause the issue and if the SW needs to be used
as a backup.

There are the following cases:
- when the code is compiled without CONFIG_NET_SWITCHDEV. In this case
  return success so the SW can continue with the protocol. Depending
  on the function, it returns 0 or BR_MRP_SW.
- when code is compiled with CONFIG_NET_SWITCHDEV and the driver doesn't
  implement any MRP callbacks. In this case the HW can't run MRP so it
  just returns -EOPNOTSUPP. So the SW will stop further to configure the
  node.
- when code is compiled with CONFIG_NET_SWITCHDEV and the driver fully
  supports any MRP functionality. In this case the SW doesn't need to do
  anything. The functions will return 0 or BR_MRP_HW.
- when code is compiled with CONFIG_NET_SWITCHDEV and the HW can't run
  completely the protocol but it can help the SW to run it. For
  example, the HW can't support completely MRM role(can't detect when it
  stops receiving MRP Test frames) but it can redirect these frames to
  CPU. In this case it is possible to have a SW fallback. The SW will
  try initially to call the driver with sw_backup set to false, meaning
  that the HW should implement completely the role. If the driver returns
  -EOPNOTSUPP, the SW will try again with sw_backup set to false,
  meaning that the SW will detect when it stops receiving the frames but
  it needs HW support to redirect the frames to CPU. In case the driver
  returns 0 then the SW will continue to configure the node accordingly.

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
 net/bridge/br_mrp_switchdev.c | 171 +++++++++++++++++++++-------------
 net/bridge/br_private_mrp.h   |  24 +++--
 2 files changed, 118 insertions(+), 77 deletions(-)

diff --git a/net/bridge/br_mrp_switchdev.c b/net/bridge/br_mrp_switchdev.c
index c971030adeea..8beb00827186 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,
@@ -177,14 +219,11 @@ int br_mrp_port_switchdev_set_state(struct net_bridge_port *p,
 		.id = SWITCHDEV_ATTR_ID_PORT_STP_STATE,
 		.u.stp_state = state,
 	};
-	int err;
 
-	err = switchdev_port_attr_set(p->dev, &attr);
-	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);
 }
 
 int br_mrp_port_switchdev_set_role(struct net_bridge_port *p,
@@ -195,11 +234,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);
-	if (err && err != -EOPNOTSUPP)
-		return err;
+	if (!IS_ENABLED(CONFIG_NET_SWITCHDEV))
+		return 0;
 
-	return 0;
+	return switchdev_port_attr_set(p->dev, &attr);
 }
diff --git a/net/bridge/br_private_mrp.h b/net/bridge/br_private_mrp.h
index a94017f86cda..587a0381a6ce 100644
--- a/net/bridge/br_private_mrp.h
+++ b/net/bridge/br_private_mrp.h
@@ -79,24 +79,28 @@ 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,
 				    enum br_mrp_port_state_type 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);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Bridge] [PATCH net-next v3 3/5] bridge: mrp: Extend br_mrp_switchdev to detect better the errors
@ 2021-02-09 20:21   ` Horatiu Vultur
  0 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-09 20:21 UTC (permalink / raw)
  To: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, vladimir.oltean, claudiu.manoil, alexandre.belloni,
	UNGLinuxDriver, netdev, linux-kernel, bridge
  Cc: Horatiu Vultur

This patch extends the br_mrp_switchdev functions to be able to have a
better understanding what cause the issue and if the SW needs to be used
as a backup.

There are the following cases:
- when the code is compiled without CONFIG_NET_SWITCHDEV. In this case
  return success so the SW can continue with the protocol. Depending
  on the function, it returns 0 or BR_MRP_SW.
- when code is compiled with CONFIG_NET_SWITCHDEV and the driver doesn't
  implement any MRP callbacks. In this case the HW can't run MRP so it
  just returns -EOPNOTSUPP. So the SW will stop further to configure the
  node.
- when code is compiled with CONFIG_NET_SWITCHDEV and the driver fully
  supports any MRP functionality. In this case the SW doesn't need to do
  anything. The functions will return 0 or BR_MRP_HW.
- when code is compiled with CONFIG_NET_SWITCHDEV and the HW can't run
  completely the protocol but it can help the SW to run it. For
  example, the HW can't support completely MRM role(can't detect when it
  stops receiving MRP Test frames) but it can redirect these frames to
  CPU. In this case it is possible to have a SW fallback. The SW will
  try initially to call the driver with sw_backup set to false, meaning
  that the HW should implement completely the role. If the driver returns
  -EOPNOTSUPP, the SW will try again with sw_backup set to false,
  meaning that the SW will detect when it stops receiving the frames but
  it needs HW support to redirect the frames to CPU. In case the driver
  returns 0 then the SW will continue to configure the node accordingly.

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
 net/bridge/br_mrp_switchdev.c | 171 +++++++++++++++++++++-------------
 net/bridge/br_private_mrp.h   |  24 +++--
 2 files changed, 118 insertions(+), 77 deletions(-)

diff --git a/net/bridge/br_mrp_switchdev.c b/net/bridge/br_mrp_switchdev.c
index c971030adeea..8beb00827186 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,
@@ -177,14 +219,11 @@ int br_mrp_port_switchdev_set_state(struct net_bridge_port *p,
 		.id = SWITCHDEV_ATTR_ID_PORT_STP_STATE,
 		.u.stp_state = state,
 	};
-	int err;
 
-	err = switchdev_port_attr_set(p->dev, &attr);
-	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);
 }
 
 int br_mrp_port_switchdev_set_role(struct net_bridge_port *p,
@@ -195,11 +234,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);
-	if (err && err != -EOPNOTSUPP)
-		return err;
+	if (!IS_ENABLED(CONFIG_NET_SWITCHDEV))
+		return 0;
 
-	return 0;
+	return switchdev_port_attr_set(p->dev, &attr);
 }
diff --git a/net/bridge/br_private_mrp.h b/net/bridge/br_private_mrp.h
index a94017f86cda..587a0381a6ce 100644
--- a/net/bridge/br_private_mrp.h
+++ b/net/bridge/br_private_mrp.h
@@ -79,24 +79,28 @@ 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,
 				    enum br_mrp_port_state_type 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);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH net-next v3 4/5] bridge: mrp: Update br_mrp to use new return values of br_mrp_switchdev
  2021-02-09 20:21 ` [Bridge] " Horatiu Vultur
@ 2021-02-09 20:21   ` Horatiu Vultur
  -1 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-09 20:21 UTC (permalink / raw)
  To: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, vladimir.oltean, claudiu.manoil, alexandre.belloni,
	UNGLinuxDriver, netdev, linux-kernel, bridge
  Cc: Horatiu Vultur

Check the return values of the br_mrp_switchdev function.
In case of:
- BR_MRP_NONE, return the error to userspace,
- BR_MRP_SW, continue with SW implementation,
- BR_MRP_HW, continue without SW implementation,

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
 net/bridge/br_mrp.c | 43 +++++++++++++++++++++++++++----------------
 1 file changed, 27 insertions(+), 16 deletions(-)

diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
index 01c67ed727a9..12487f6fe9b4 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;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Bridge] [PATCH net-next v3 4/5] bridge: mrp: Update br_mrp to use new return values of br_mrp_switchdev
@ 2021-02-09 20:21   ` Horatiu Vultur
  0 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-09 20:21 UTC (permalink / raw)
  To: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, vladimir.oltean, claudiu.manoil, alexandre.belloni,
	UNGLinuxDriver, netdev, linux-kernel, bridge
  Cc: Horatiu Vultur

Check the return values of the br_mrp_switchdev function.
In case of:
- BR_MRP_NONE, return the error to userspace,
- BR_MRP_SW, continue with SW implementation,
- BR_MRP_HW, continue without SW implementation,

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
 net/bridge/br_mrp.c | 43 +++++++++++++++++++++++++++----------------
 1 file changed, 27 insertions(+), 16 deletions(-)

diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
index 01c67ed727a9..12487f6fe9b4 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;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH net-next v3 5/5] net: mscc: ocelot: Add support for MRP
  2021-02-09 20:21 ` [Bridge] " Horatiu Vultur
@ 2021-02-09 20:21   ` Horatiu Vultur
  -1 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-09 20:21 UTC (permalink / raw)
  To: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, vladimir.oltean, claudiu.manoil, alexandre.belloni,
	UNGLinuxDriver, netdev, linux-kernel, bridge
  Cc: Horatiu Vultur

Add basic support for MRP. The HW will just trap all MRP frames on the
ring ports to CPU and allow the SW to process them. In this way it is
possible to for this node to behave both as MRM and MRC.

Current limitations are:
- it doesn't support Interconnect roles.
- it supports only a single ring.
- the HW should be able to do forwarding of MRP Test frames so the SW
  will not need to do this. So it would be able to have the role MRC
  without SW support.

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
 drivers/net/ethernet/mscc/ocelot_net.c     | 154 +++++++++++++++++++++
 drivers/net/ethernet/mscc/ocelot_vsc7514.c |   6 +
 include/soc/mscc/ocelot.h                  |   6 +
 3 files changed, 166 insertions(+)

diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
index 8f12fa45b1b5..65971403e823 100644
--- a/drivers/net/ethernet/mscc/ocelot_net.c
+++ b/drivers/net/ethernet/mscc/ocelot_net.c
@@ -9,7 +9,10 @@
  */
 
 #include <linux/if_bridge.h>
+#include <linux/mrp_bridge.h>
 #include <net/pkt_cls.h>
+#include <soc/mscc/ocelot_vcap.h>
+#include <uapi/linux/mrp_bridge.h>
 #include "ocelot.h"
 #include "ocelot_vcap.h"
 
@@ -1069,6 +1072,139 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
 	return ocelot_port_mdb_del(ocelot, port, mdb);
 }
 
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int port)
+{
+	struct ocelot_vcap_block *block_vcap_is2;
+	struct ocelot_vcap_filter *filter;
+
+	block_vcap_is2 = &ocelot->block[VCAP_IS2];
+	filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, port,
+						     false);
+	if (!filter)
+		return 0;
+
+	return ocelot_vcap_filter_del(ocelot, filter);
+}
+
+static int ocelot_add_mrp(struct net_device *dev,
+			  const struct switchdev_obj_mrp *mrp)
+{
+	struct ocelot_port_private *priv = netdev_priv(dev);
+	struct ocelot_port *ocelot_port = &priv->port;
+	struct ocelot *ocelot = ocelot_port->ocelot;
+
+	if (mrp->p_port != dev && mrp->s_port != dev)
+		return 0;
+
+	if (ocelot->mrp_ring_id != 0 &&
+	    ocelot->mrp_s_port &&
+	    ocelot->mrp_p_port)
+		return -EINVAL;
+
+	if (mrp->p_port == dev)
+		ocelot->mrp_p_port = dev;
+
+	if (mrp->s_port == dev)
+		ocelot->mrp_s_port = dev;
+
+	ocelot->mrp_ring_id = mrp->ring_id;
+
+	return 0;
+}
+
+static int ocelot_del_mrp(struct net_device *dev,
+			  const struct switchdev_obj_mrp *mrp)
+{
+	struct ocelot_port_private *priv = netdev_priv(dev);
+	struct ocelot_port *ocelot_port = &priv->port;
+	struct ocelot *ocelot = ocelot_port->ocelot;
+
+	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
+		return 0;
+
+	if (ocelot->mrp_ring_id == 0 &&
+	    !ocelot->mrp_s_port &&
+	    !ocelot->mrp_p_port)
+		return -EINVAL;
+
+	if (ocelot_mrp_del_vcap(ocelot, priv->chip_port))
+		return -EINVAL;
+
+	if (ocelot->mrp_p_port == dev)
+		ocelot->mrp_p_port = NULL;
+
+	if (ocelot->mrp_s_port == dev)
+		ocelot->mrp_s_port = NULL;
+
+	ocelot->mrp_ring_id = 0;
+
+	return 0;
+}
+
+static int ocelot_add_ring_role(struct net_device *dev,
+				const struct switchdev_obj_ring_role_mrp *mrp)
+{
+	struct ocelot_port_private *priv = netdev_priv(dev);
+	struct ocelot_port *ocelot_port = &priv->port;
+	struct ocelot *ocelot = ocelot_port->ocelot;
+	struct ocelot_vcap_filter *filter;
+	int err;
+
+	if (ocelot->mrp_ring_id != mrp->ring_id)
+		return -EINVAL;
+
+	if (!mrp->sw_backup)
+		return -EOPNOTSUPP;
+
+	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
+		return 0;
+
+	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
+	if (!filter)
+		return -ENOMEM;
+
+	filter->key_type = OCELOT_VCAP_KEY_ETYPE;
+	filter->prio = 1;
+	filter->id.cookie = priv->chip_port;
+	filter->id.tc_offload = false;
+	filter->block_id = VCAP_IS2;
+	filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
+	filter->ingress_port_mask = BIT(priv->chip_port);
+	*(__be16 *)filter->key.etype.etype.value = htons(ETH_P_MRP);
+	*(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
+	filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
+	filter->action.port_mask = 0x0;
+	filter->action.cpu_copy_ena = true;
+	filter->action.cpu_qu_num = 0;
+
+	err = ocelot_vcap_filter_add(ocelot, filter, NULL);
+	if (err)
+		kfree(filter);
+
+	return err;
+}
+
+static int ocelot_del_ring_role(struct net_device *dev,
+				const struct switchdev_obj_ring_role_mrp *mrp)
+{
+	struct ocelot_port_private *priv = netdev_priv(dev);
+	struct ocelot_port *ocelot_port = &priv->port;
+	struct ocelot *ocelot = ocelot_port->ocelot;
+
+	if (ocelot->mrp_ring_id != mrp->ring_id)
+		return -EINVAL;
+
+	if (!mrp->sw_backup)
+		return -EOPNOTSUPP;
+
+	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
+		return 0;
+
+	return ocelot_mrp_del_vcap(ocelot, priv->chip_port);
+}
+#endif
+
 static int ocelot_port_obj_add(struct net_device *dev,
 			       const struct switchdev_obj *obj,
 			       struct netlink_ext_ack *extack)
@@ -1083,6 +1219,15 @@ static int ocelot_port_obj_add(struct net_device *dev,
 	case SWITCHDEV_OBJ_ID_PORT_MDB:
 		ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
 		break;
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+	case SWITCHDEV_OBJ_ID_MRP:
+		ret = ocelot_add_mrp(dev, SWITCHDEV_OBJ_MRP(obj));
+		break;
+	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
+		ret = ocelot_add_ring_role(dev,
+					   SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
+		break;
+#endif
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -1103,6 +1248,15 @@ static int ocelot_port_obj_del(struct net_device *dev,
 	case SWITCHDEV_OBJ_ID_PORT_MDB:
 		ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
 		break;
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+	case SWITCHDEV_OBJ_ID_MRP:
+		ret = ocelot_del_mrp(dev, SWITCHDEV_OBJ_MRP(obj));
+		break;
+	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
+		ret = ocelot_del_ring_role(dev,
+					   SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
+		break;
+#endif
 	default:
 		return -EOPNOTSUPP;
 	}
diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
index 6b6eb92149ba..96a9c9f98060 100644
--- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
+++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
@@ -698,6 +698,12 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
 			skb->offload_fwd_mark = 1;
 
 		skb->protocol = eth_type_trans(skb, dev);
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+		if (skb->protocol == ntohs(ETH_P_MRP) &&
+		    (priv->dev == ocelot->mrp_p_port ||
+		     priv->dev == ocelot->mrp_s_port))
+			skb->offload_fwd_mark = 0;
+#endif
 		if (!skb_defer_rx_timestamp(skb))
 			netif_rx(skb);
 		dev->stats.rx_bytes += len;
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index d0d48e9620fb..d95c019ad84e 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -682,6 +682,12 @@ struct ocelot {
 	/* Protects the PTP clock */
 	spinlock_t			ptp_clock_lock;
 	struct ptp_pin_desc		ptp_pins[OCELOT_PTP_PINS_NUM];
+
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+	u16				mrp_ring_id;
+	struct net_device		*mrp_p_port;
+	struct net_device		*mrp_s_port;
+#endif
 };
 
 struct ocelot_policer {
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Bridge] [PATCH net-next v3 5/5] net: mscc: ocelot: Add support for MRP
@ 2021-02-09 20:21   ` Horatiu Vultur
  0 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-09 20:21 UTC (permalink / raw)
  To: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, vladimir.oltean, claudiu.manoil, alexandre.belloni,
	UNGLinuxDriver, netdev, linux-kernel, bridge
  Cc: Horatiu Vultur

Add basic support for MRP. The HW will just trap all MRP frames on the
ring ports to CPU and allow the SW to process them. In this way it is
possible to for this node to behave both as MRM and MRC.

Current limitations are:
- it doesn't support Interconnect roles.
- it supports only a single ring.
- the HW should be able to do forwarding of MRP Test frames so the SW
  will not need to do this. So it would be able to have the role MRC
  without SW support.

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
 drivers/net/ethernet/mscc/ocelot_net.c     | 154 +++++++++++++++++++++
 drivers/net/ethernet/mscc/ocelot_vsc7514.c |   6 +
 include/soc/mscc/ocelot.h                  |   6 +
 3 files changed, 166 insertions(+)

diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
index 8f12fa45b1b5..65971403e823 100644
--- a/drivers/net/ethernet/mscc/ocelot_net.c
+++ b/drivers/net/ethernet/mscc/ocelot_net.c
@@ -9,7 +9,10 @@
  */
 
 #include <linux/if_bridge.h>
+#include <linux/mrp_bridge.h>
 #include <net/pkt_cls.h>
+#include <soc/mscc/ocelot_vcap.h>
+#include <uapi/linux/mrp_bridge.h>
 #include "ocelot.h"
 #include "ocelot_vcap.h"
 
@@ -1069,6 +1072,139 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
 	return ocelot_port_mdb_del(ocelot, port, mdb);
 }
 
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int port)
+{
+	struct ocelot_vcap_block *block_vcap_is2;
+	struct ocelot_vcap_filter *filter;
+
+	block_vcap_is2 = &ocelot->block[VCAP_IS2];
+	filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, port,
+						     false);
+	if (!filter)
+		return 0;
+
+	return ocelot_vcap_filter_del(ocelot, filter);
+}
+
+static int ocelot_add_mrp(struct net_device *dev,
+			  const struct switchdev_obj_mrp *mrp)
+{
+	struct ocelot_port_private *priv = netdev_priv(dev);
+	struct ocelot_port *ocelot_port = &priv->port;
+	struct ocelot *ocelot = ocelot_port->ocelot;
+
+	if (mrp->p_port != dev && mrp->s_port != dev)
+		return 0;
+
+	if (ocelot->mrp_ring_id != 0 &&
+	    ocelot->mrp_s_port &&
+	    ocelot->mrp_p_port)
+		return -EINVAL;
+
+	if (mrp->p_port == dev)
+		ocelot->mrp_p_port = dev;
+
+	if (mrp->s_port == dev)
+		ocelot->mrp_s_port = dev;
+
+	ocelot->mrp_ring_id = mrp->ring_id;
+
+	return 0;
+}
+
+static int ocelot_del_mrp(struct net_device *dev,
+			  const struct switchdev_obj_mrp *mrp)
+{
+	struct ocelot_port_private *priv = netdev_priv(dev);
+	struct ocelot_port *ocelot_port = &priv->port;
+	struct ocelot *ocelot = ocelot_port->ocelot;
+
+	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
+		return 0;
+
+	if (ocelot->mrp_ring_id == 0 &&
+	    !ocelot->mrp_s_port &&
+	    !ocelot->mrp_p_port)
+		return -EINVAL;
+
+	if (ocelot_mrp_del_vcap(ocelot, priv->chip_port))
+		return -EINVAL;
+
+	if (ocelot->mrp_p_port == dev)
+		ocelot->mrp_p_port = NULL;
+
+	if (ocelot->mrp_s_port == dev)
+		ocelot->mrp_s_port = NULL;
+
+	ocelot->mrp_ring_id = 0;
+
+	return 0;
+}
+
+static int ocelot_add_ring_role(struct net_device *dev,
+				const struct switchdev_obj_ring_role_mrp *mrp)
+{
+	struct ocelot_port_private *priv = netdev_priv(dev);
+	struct ocelot_port *ocelot_port = &priv->port;
+	struct ocelot *ocelot = ocelot_port->ocelot;
+	struct ocelot_vcap_filter *filter;
+	int err;
+
+	if (ocelot->mrp_ring_id != mrp->ring_id)
+		return -EINVAL;
+
+	if (!mrp->sw_backup)
+		return -EOPNOTSUPP;
+
+	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
+		return 0;
+
+	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
+	if (!filter)
+		return -ENOMEM;
+
+	filter->key_type = OCELOT_VCAP_KEY_ETYPE;
+	filter->prio = 1;
+	filter->id.cookie = priv->chip_port;
+	filter->id.tc_offload = false;
+	filter->block_id = VCAP_IS2;
+	filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
+	filter->ingress_port_mask = BIT(priv->chip_port);
+	*(__be16 *)filter->key.etype.etype.value = htons(ETH_P_MRP);
+	*(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
+	filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
+	filter->action.port_mask = 0x0;
+	filter->action.cpu_copy_ena = true;
+	filter->action.cpu_qu_num = 0;
+
+	err = ocelot_vcap_filter_add(ocelot, filter, NULL);
+	if (err)
+		kfree(filter);
+
+	return err;
+}
+
+static int ocelot_del_ring_role(struct net_device *dev,
+				const struct switchdev_obj_ring_role_mrp *mrp)
+{
+	struct ocelot_port_private *priv = netdev_priv(dev);
+	struct ocelot_port *ocelot_port = &priv->port;
+	struct ocelot *ocelot = ocelot_port->ocelot;
+
+	if (ocelot->mrp_ring_id != mrp->ring_id)
+		return -EINVAL;
+
+	if (!mrp->sw_backup)
+		return -EOPNOTSUPP;
+
+	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
+		return 0;
+
+	return ocelot_mrp_del_vcap(ocelot, priv->chip_port);
+}
+#endif
+
 static int ocelot_port_obj_add(struct net_device *dev,
 			       const struct switchdev_obj *obj,
 			       struct netlink_ext_ack *extack)
@@ -1083,6 +1219,15 @@ static int ocelot_port_obj_add(struct net_device *dev,
 	case SWITCHDEV_OBJ_ID_PORT_MDB:
 		ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
 		break;
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+	case SWITCHDEV_OBJ_ID_MRP:
+		ret = ocelot_add_mrp(dev, SWITCHDEV_OBJ_MRP(obj));
+		break;
+	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
+		ret = ocelot_add_ring_role(dev,
+					   SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
+		break;
+#endif
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -1103,6 +1248,15 @@ static int ocelot_port_obj_del(struct net_device *dev,
 	case SWITCHDEV_OBJ_ID_PORT_MDB:
 		ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
 		break;
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+	case SWITCHDEV_OBJ_ID_MRP:
+		ret = ocelot_del_mrp(dev, SWITCHDEV_OBJ_MRP(obj));
+		break;
+	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
+		ret = ocelot_del_ring_role(dev,
+					   SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
+		break;
+#endif
 	default:
 		return -EOPNOTSUPP;
 	}
diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
index 6b6eb92149ba..96a9c9f98060 100644
--- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
+++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
@@ -698,6 +698,12 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
 			skb->offload_fwd_mark = 1;
 
 		skb->protocol = eth_type_trans(skb, dev);
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+		if (skb->protocol == ntohs(ETH_P_MRP) &&
+		    (priv->dev == ocelot->mrp_p_port ||
+		     priv->dev == ocelot->mrp_s_port))
+			skb->offload_fwd_mark = 0;
+#endif
 		if (!skb_defer_rx_timestamp(skb))
 			netif_rx(skb);
 		dev->stats.rx_bytes += len;
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index d0d48e9620fb..d95c019ad84e 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -682,6 +682,12 @@ struct ocelot {
 	/* Protects the PTP clock */
 	spinlock_t			ptp_clock_lock;
 	struct ptp_pin_desc		ptp_pins[OCELOT_PTP_PINS_NUM];
+
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+	u16				mrp_ring_id;
+	struct net_device		*mrp_p_port;
+	struct net_device		*mrp_s_port;
+#endif
 };
 
 struct ocelot_policer {
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v3 0/5] bridge: mrp: Extend br_mrp_switchdev_*
  2021-02-09 20:21 ` [Bridge] " Horatiu Vultur
@ 2021-02-10 10:08   ` Vladimir Oltean
  -1 siblings, 0 replies; 20+ messages in thread
From: Vladimir Oltean @ 2021-02-10 10:08 UTC (permalink / raw)
  To: Horatiu Vultur
  Cc: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, Claudiu Manoil, alexandre.belloni, UNGLinuxDriver,
	netdev, linux-kernel, bridge

Hi Horatiu,

On Tue, Feb 09, 2021 at 09:21:07PM +0100, Horatiu Vultur wrote:
> This patch series extends MRP switchdev to allow the SW to have a better
> understanding if the HW can implement the MRP functionality or it needs
> to help the HW to run it. There are 3 cases:
> - when HW can't implement at all the functionality.
> - when HW can implement a part of the functionality but needs the SW
>   implement the rest. For example if it can't detect when it stops
>   receiving MRP Test frames but it can copy the MRP frames to CPU to
>   allow the SW to determine this.  Another example is generating the MRP
>   Test frames. If HW can't do that then the SW is used as backup.
> - when HW can implement completely the functionality.
> 
> So, initially the SW tries to offload the entire functionality in HW, if
> that fails it tries offload parts of the functionality in HW and use the
> SW as helper and if also this fails then MRP can't run on this HW.
> 
> Also implement the switchdev calls for Ocelot driver. This is an example
> where the HW can't run completely the functionality but it can help the SW
> to run it, by trapping all MRP frames to CPU.
> 
> v3:
>  - implement the switchdev calls needed by Ocelot driver.
> v2:
>  - fix typos in comments and in commit messages
>  - remove some of the comments
>  - move repeated code in helper function
>  - fix issue when deleting a node when sw_backup was true
> 
> Horatiu Vultur (5):
>   switchdev: mrp: Extend ring_role_mrp and in_role_mrp
>   bridge: mrp: Add 'enum br_mrp_hw_support'
>   bridge: mrp: Extend br_mrp_switchdev to detect better the errors
>   bridge: mrp: Update br_mrp to use new return values of
>     br_mrp_switchdev
>   net: mscc: ocelot: Add support for MRP
> 
>  drivers/net/ethernet/mscc/ocelot_net.c     | 154 +++++++++++++++++++
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c |   6 +
>  include/net/switchdev.h                    |   2 +
>  include/soc/mscc/ocelot.h                  |   6 +
>  net/bridge/br_mrp.c                        |  43 ++++--
>  net/bridge/br_mrp_switchdev.c              | 171 +++++++++++++--------
>  net/bridge/br_private_mrp.h                |  38 +++--
>  7 files changed, 327 insertions(+), 93 deletions(-)
> 
> -- 
> 2.27.0
> 

Which net-next commit can these patches be applied to? On the current
master I get:

Applying: switchdev: mrp: Extend ring_role_mrp and in_role_mrp
Applying: bridge: mrp: Add 'enum br_mrp_hw_support'
Applying: bridge: mrp: Extend br_mrp_switchdev to detect better the errors
error: patch failed: net/bridge/br_mrp_switchdev.c:177
error: net/bridge/br_mrp_switchdev.c: patch does not apply
Patch failed at 0004 bridge: mrp: Extend br_mrp_switchdev to detect better the errors
hint: Use 'git am --show-current-patch' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Bridge] [PATCH net-next v3 0/5] bridge: mrp: Extend br_mrp_switchdev_*
@ 2021-02-10 10:08   ` Vladimir Oltean
  0 siblings, 0 replies; 20+ messages in thread
From: Vladimir Oltean @ 2021-02-10 10:08 UTC (permalink / raw)
  To: Horatiu Vultur
  Cc: ivecera, andrew, alexandre.belloni, jiri, rasmus.villemoes,
	netdev, bridge, linux-kernel, UNGLinuxDriver, Claudiu Manoil,
	nikolay, roopa, kuba, davem

Hi Horatiu,

On Tue, Feb 09, 2021 at 09:21:07PM +0100, Horatiu Vultur wrote:
> This patch series extends MRP switchdev to allow the SW to have a better
> understanding if the HW can implement the MRP functionality or it needs
> to help the HW to run it. There are 3 cases:
> - when HW can't implement at all the functionality.
> - when HW can implement a part of the functionality but needs the SW
>   implement the rest. For example if it can't detect when it stops
>   receiving MRP Test frames but it can copy the MRP frames to CPU to
>   allow the SW to determine this.  Another example is generating the MRP
>   Test frames. If HW can't do that then the SW is used as backup.
> - when HW can implement completely the functionality.
> 
> So, initially the SW tries to offload the entire functionality in HW, if
> that fails it tries offload parts of the functionality in HW and use the
> SW as helper and if also this fails then MRP can't run on this HW.
> 
> Also implement the switchdev calls for Ocelot driver. This is an example
> where the HW can't run completely the functionality but it can help the SW
> to run it, by trapping all MRP frames to CPU.
> 
> v3:
>  - implement the switchdev calls needed by Ocelot driver.
> v2:
>  - fix typos in comments and in commit messages
>  - remove some of the comments
>  - move repeated code in helper function
>  - fix issue when deleting a node when sw_backup was true
> 
> Horatiu Vultur (5):
>   switchdev: mrp: Extend ring_role_mrp and in_role_mrp
>   bridge: mrp: Add 'enum br_mrp_hw_support'
>   bridge: mrp: Extend br_mrp_switchdev to detect better the errors
>   bridge: mrp: Update br_mrp to use new return values of
>     br_mrp_switchdev
>   net: mscc: ocelot: Add support for MRP
> 
>  drivers/net/ethernet/mscc/ocelot_net.c     | 154 +++++++++++++++++++
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c |   6 +
>  include/net/switchdev.h                    |   2 +
>  include/soc/mscc/ocelot.h                  |   6 +
>  net/bridge/br_mrp.c                        |  43 ++++--
>  net/bridge/br_mrp_switchdev.c              | 171 +++++++++++++--------
>  net/bridge/br_private_mrp.h                |  38 +++--
>  7 files changed, 327 insertions(+), 93 deletions(-)
> 
> -- 
> 2.27.0
> 

Which net-next commit can these patches be applied to? On the current
master I get:

Applying: switchdev: mrp: Extend ring_role_mrp and in_role_mrp
Applying: bridge: mrp: Add 'enum br_mrp_hw_support'
Applying: bridge: mrp: Extend br_mrp_switchdev to detect better the errors
error: patch failed: net/bridge/br_mrp_switchdev.c:177
error: net/bridge/br_mrp_switchdev.c: patch does not apply
Patch failed at 0004 bridge: mrp: Extend br_mrp_switchdev to detect better the errors
hint: Use 'git am --show-current-patch' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v3 5/5] net: mscc: ocelot: Add support for MRP
  2021-02-09 20:21   ` [Bridge] " Horatiu Vultur
@ 2021-02-10 10:18     ` Vladimir Oltean
  -1 siblings, 0 replies; 20+ messages in thread
From: Vladimir Oltean @ 2021-02-10 10:18 UTC (permalink / raw)
  To: Horatiu Vultur
  Cc: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, Claudiu Manoil, alexandre.belloni, UNGLinuxDriver,
	netdev, linux-kernel, bridge

Would you mind adding the switchdev MRP support for the DSA driver too,
and move the code to the common ocelot library? I would like to give it
a run. I think that's only fair, since I have to keep in sync the
vsc7514 driver too for features that get added through DSA :)

On Tue, Feb 09, 2021 at 09:21:12PM +0100, Horatiu Vultur wrote:
> Add basic support for MRP. The HW will just trap all MRP frames on the
> ring ports to CPU and allow the SW to process them. In this way it is
> possible to for this node to behave both as MRM and MRC.
> 
> Current limitations are:
> - it doesn't support Interconnect roles.
> - it supports only a single ring.
> - the HW should be able to do forwarding of MRP Test frames so the SW
>   will not need to do this. So it would be able to have the role MRC
>   without SW support.
> 
> Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
>  drivers/net/ethernet/mscc/ocelot_net.c     | 154 +++++++++++++++++++++
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c |   6 +
>  include/soc/mscc/ocelot.h                  |   6 +
>  3 files changed, 166 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index 8f12fa45b1b5..65971403e823 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -9,7 +9,10 @@
>   */
>  
>  #include <linux/if_bridge.h>
> +#include <linux/mrp_bridge.h>
>  #include <net/pkt_cls.h>
> +#include <soc/mscc/ocelot_vcap.h>
> +#include <uapi/linux/mrp_bridge.h>
>  #include "ocelot.h"
>  #include "ocelot_vcap.h"
>  
> @@ -1069,6 +1072,139 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
>  	return ocelot_port_mdb_del(ocelot, port, mdb);
>  }
>  
> +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> +static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int port)
> +{
> +	struct ocelot_vcap_block *block_vcap_is2;
> +	struct ocelot_vcap_filter *filter;
> +
> +	block_vcap_is2 = &ocelot->block[VCAP_IS2];
> +	filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, port,
> +						     false);
> +	if (!filter)
> +		return 0;
> +
> +	return ocelot_vcap_filter_del(ocelot, filter);
> +}
> +
> +static int ocelot_add_mrp(struct net_device *dev,
> +			  const struct switchdev_obj_mrp *mrp)
> +{
> +	struct ocelot_port_private *priv = netdev_priv(dev);
> +	struct ocelot_port *ocelot_port = &priv->port;
> +	struct ocelot *ocelot = ocelot_port->ocelot;
> +
> +	if (mrp->p_port != dev && mrp->s_port != dev)
> +		return 0;
> +
> +	if (ocelot->mrp_ring_id != 0 &&
> +	    ocelot->mrp_s_port &&
> +	    ocelot->mrp_p_port)
> +		return -EINVAL;
> +
> +	if (mrp->p_port == dev)
> +		ocelot->mrp_p_port = dev;
> +
> +	if (mrp->s_port == dev)
> +		ocelot->mrp_s_port = dev;
> +
> +	ocelot->mrp_ring_id = mrp->ring_id;
> +
> +	return 0;
> +}
> +
> +static int ocelot_del_mrp(struct net_device *dev,
> +			  const struct switchdev_obj_mrp *mrp)
> +{
> +	struct ocelot_port_private *priv = netdev_priv(dev);
> +	struct ocelot_port *ocelot_port = &priv->port;
> +	struct ocelot *ocelot = ocelot_port->ocelot;
> +
> +	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
> +		return 0;
> +
> +	if (ocelot->mrp_ring_id == 0 &&
> +	    !ocelot->mrp_s_port &&
> +	    !ocelot->mrp_p_port)
> +		return -EINVAL;
> +
> +	if (ocelot_mrp_del_vcap(ocelot, priv->chip_port))
> +		return -EINVAL;
> +
> +	if (ocelot->mrp_p_port == dev)
> +		ocelot->mrp_p_port = NULL;
> +
> +	if (ocelot->mrp_s_port == dev)
> +		ocelot->mrp_s_port = NULL;
> +
> +	ocelot->mrp_ring_id = 0;
> +
> +	return 0;
> +}
> +
> +static int ocelot_add_ring_role(struct net_device *dev,
> +				const struct switchdev_obj_ring_role_mrp *mrp)
> +{
> +	struct ocelot_port_private *priv = netdev_priv(dev);
> +	struct ocelot_port *ocelot_port = &priv->port;
> +	struct ocelot *ocelot = ocelot_port->ocelot;
> +	struct ocelot_vcap_filter *filter;
> +	int err;
> +
> +	if (ocelot->mrp_ring_id != mrp->ring_id)
> +		return -EINVAL;
> +
> +	if (!mrp->sw_backup)
> +		return -EOPNOTSUPP;
> +
> +	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
> +		return 0;
> +
> +	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
> +	if (!filter)
> +		return -ENOMEM;
> +
> +	filter->key_type = OCELOT_VCAP_KEY_ETYPE;
> +	filter->prio = 1;
> +	filter->id.cookie = priv->chip_port;
> +	filter->id.tc_offload = false;
> +	filter->block_id = VCAP_IS2;
> +	filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
> +	filter->ingress_port_mask = BIT(priv->chip_port);
> +	*(__be16 *)filter->key.etype.etype.value = htons(ETH_P_MRP);
> +	*(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
> +	filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
> +	filter->action.port_mask = 0x0;
> +	filter->action.cpu_copy_ena = true;
> +	filter->action.cpu_qu_num = 0;
> +
> +	err = ocelot_vcap_filter_add(ocelot, filter, NULL);
> +	if (err)
> +		kfree(filter);
> +
> +	return err;
> +}
> +
> +static int ocelot_del_ring_role(struct net_device *dev,
> +				const struct switchdev_obj_ring_role_mrp *mrp)
> +{
> +	struct ocelot_port_private *priv = netdev_priv(dev);
> +	struct ocelot_port *ocelot_port = &priv->port;
> +	struct ocelot *ocelot = ocelot_port->ocelot;
> +
> +	if (ocelot->mrp_ring_id != mrp->ring_id)
> +		return -EINVAL;
> +
> +	if (!mrp->sw_backup)
> +		return -EOPNOTSUPP;
> +
> +	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
> +		return 0;
> +
> +	return ocelot_mrp_del_vcap(ocelot, priv->chip_port);
> +}
> +#endif
> +

Would it make sense for this chunk of conditionally compiled code to
stay in a separate file like ocelot_mrp.c?

>  static int ocelot_port_obj_add(struct net_device *dev,
>  			       const struct switchdev_obj *obj,
>  			       struct netlink_ext_ack *extack)
> @@ -1083,6 +1219,15 @@ static int ocelot_port_obj_add(struct net_device *dev,
>  	case SWITCHDEV_OBJ_ID_PORT_MDB:
>  		ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
>  		break;
> +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> +	case SWITCHDEV_OBJ_ID_MRP:
> +		ret = ocelot_add_mrp(dev, SWITCHDEV_OBJ_MRP(obj));
> +		break;
> +	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
> +		ret = ocelot_add_ring_role(dev,
> +					   SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
> +		break;
> +#endif

I'm not really sure why SWITCHDEV_OBJ_ID_MRP is conditionally defined.
If you look at SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING, that isn't
conditionally defined, even though it depends on CONFIG_BRIDGE_VLAN_FILTERING
at runtime.

>  	default:
>  		return -EOPNOTSUPP;
>  	}
> @@ -1103,6 +1248,15 @@ static int ocelot_port_obj_del(struct net_device *dev,
>  	case SWITCHDEV_OBJ_ID_PORT_MDB:
>  		ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
>  		break;
> +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> +	case SWITCHDEV_OBJ_ID_MRP:
> +		ret = ocelot_del_mrp(dev, SWITCHDEV_OBJ_MRP(obj));
> +		break;
> +	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
> +		ret = ocelot_del_ring_role(dev,
> +					   SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
> +		break;
> +#endif
>  	default:
>  		return -EOPNOTSUPP;
>  	}
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 6b6eb92149ba..96a9c9f98060 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -698,6 +698,12 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
>  			skb->offload_fwd_mark = 1;
>  
>  		skb->protocol = eth_type_trans(skb, dev);
> +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> +		if (skb->protocol == ntohs(ETH_P_MRP) &&
> +		    (priv->dev == ocelot->mrp_p_port ||
> +		     priv->dev == ocelot->mrp_s_port))
> +			skb->offload_fwd_mark = 0;
> +#endif

I wonder if you could just reserve a certain CPUQ for trapped traffic,
and just generically check for that, instead of MRP port roles?

>  		if (!skb_defer_rx_timestamp(skb))
>  			netif_rx(skb);
>  		dev->stats.rx_bytes += len;
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index d0d48e9620fb..d95c019ad84e 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -682,6 +682,12 @@ struct ocelot {
>  	/* Protects the PTP clock */
>  	spinlock_t			ptp_clock_lock;
>  	struct ptp_pin_desc		ptp_pins[OCELOT_PTP_PINS_NUM];
> +
> +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> +	u16				mrp_ring_id;
> +	struct net_device		*mrp_p_port;
> +	struct net_device		*mrp_s_port;
> +#endif
>  };
>  
>  struct ocelot_policer {
> -- 
> 2.27.0
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Bridge] [PATCH net-next v3 5/5] net: mscc: ocelot: Add support for MRP
@ 2021-02-10 10:18     ` Vladimir Oltean
  0 siblings, 0 replies; 20+ messages in thread
From: Vladimir Oltean @ 2021-02-10 10:18 UTC (permalink / raw)
  To: Horatiu Vultur
  Cc: ivecera, andrew, alexandre.belloni, jiri, rasmus.villemoes,
	netdev, bridge, linux-kernel, UNGLinuxDriver, Claudiu Manoil,
	nikolay, roopa, kuba, davem

Would you mind adding the switchdev MRP support for the DSA driver too,
and move the code to the common ocelot library? I would like to give it
a run. I think that's only fair, since I have to keep in sync the
vsc7514 driver too for features that get added through DSA :)

On Tue, Feb 09, 2021 at 09:21:12PM +0100, Horatiu Vultur wrote:
> Add basic support for MRP. The HW will just trap all MRP frames on the
> ring ports to CPU and allow the SW to process them. In this way it is
> possible to for this node to behave both as MRM and MRC.
> 
> Current limitations are:
> - it doesn't support Interconnect roles.
> - it supports only a single ring.
> - the HW should be able to do forwarding of MRP Test frames so the SW
>   will not need to do this. So it would be able to have the role MRC
>   without SW support.
> 
> Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> ---
>  drivers/net/ethernet/mscc/ocelot_net.c     | 154 +++++++++++++++++++++
>  drivers/net/ethernet/mscc/ocelot_vsc7514.c |   6 +
>  include/soc/mscc/ocelot.h                  |   6 +
>  3 files changed, 166 insertions(+)
> 
> diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> index 8f12fa45b1b5..65971403e823 100644
> --- a/drivers/net/ethernet/mscc/ocelot_net.c
> +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> @@ -9,7 +9,10 @@
>   */
>  
>  #include <linux/if_bridge.h>
> +#include <linux/mrp_bridge.h>
>  #include <net/pkt_cls.h>
> +#include <soc/mscc/ocelot_vcap.h>
> +#include <uapi/linux/mrp_bridge.h>
>  #include "ocelot.h"
>  #include "ocelot_vcap.h"
>  
> @@ -1069,6 +1072,139 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
>  	return ocelot_port_mdb_del(ocelot, port, mdb);
>  }
>  
> +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> +static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int port)
> +{
> +	struct ocelot_vcap_block *block_vcap_is2;
> +	struct ocelot_vcap_filter *filter;
> +
> +	block_vcap_is2 = &ocelot->block[VCAP_IS2];
> +	filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, port,
> +						     false);
> +	if (!filter)
> +		return 0;
> +
> +	return ocelot_vcap_filter_del(ocelot, filter);
> +}
> +
> +static int ocelot_add_mrp(struct net_device *dev,
> +			  const struct switchdev_obj_mrp *mrp)
> +{
> +	struct ocelot_port_private *priv = netdev_priv(dev);
> +	struct ocelot_port *ocelot_port = &priv->port;
> +	struct ocelot *ocelot = ocelot_port->ocelot;
> +
> +	if (mrp->p_port != dev && mrp->s_port != dev)
> +		return 0;
> +
> +	if (ocelot->mrp_ring_id != 0 &&
> +	    ocelot->mrp_s_port &&
> +	    ocelot->mrp_p_port)
> +		return -EINVAL;
> +
> +	if (mrp->p_port == dev)
> +		ocelot->mrp_p_port = dev;
> +
> +	if (mrp->s_port == dev)
> +		ocelot->mrp_s_port = dev;
> +
> +	ocelot->mrp_ring_id = mrp->ring_id;
> +
> +	return 0;
> +}
> +
> +static int ocelot_del_mrp(struct net_device *dev,
> +			  const struct switchdev_obj_mrp *mrp)
> +{
> +	struct ocelot_port_private *priv = netdev_priv(dev);
> +	struct ocelot_port *ocelot_port = &priv->port;
> +	struct ocelot *ocelot = ocelot_port->ocelot;
> +
> +	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
> +		return 0;
> +
> +	if (ocelot->mrp_ring_id == 0 &&
> +	    !ocelot->mrp_s_port &&
> +	    !ocelot->mrp_p_port)
> +		return -EINVAL;
> +
> +	if (ocelot_mrp_del_vcap(ocelot, priv->chip_port))
> +		return -EINVAL;
> +
> +	if (ocelot->mrp_p_port == dev)
> +		ocelot->mrp_p_port = NULL;
> +
> +	if (ocelot->mrp_s_port == dev)
> +		ocelot->mrp_s_port = NULL;
> +
> +	ocelot->mrp_ring_id = 0;
> +
> +	return 0;
> +}
> +
> +static int ocelot_add_ring_role(struct net_device *dev,
> +				const struct switchdev_obj_ring_role_mrp *mrp)
> +{
> +	struct ocelot_port_private *priv = netdev_priv(dev);
> +	struct ocelot_port *ocelot_port = &priv->port;
> +	struct ocelot *ocelot = ocelot_port->ocelot;
> +	struct ocelot_vcap_filter *filter;
> +	int err;
> +
> +	if (ocelot->mrp_ring_id != mrp->ring_id)
> +		return -EINVAL;
> +
> +	if (!mrp->sw_backup)
> +		return -EOPNOTSUPP;
> +
> +	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
> +		return 0;
> +
> +	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
> +	if (!filter)
> +		return -ENOMEM;
> +
> +	filter->key_type = OCELOT_VCAP_KEY_ETYPE;
> +	filter->prio = 1;
> +	filter->id.cookie = priv->chip_port;
> +	filter->id.tc_offload = false;
> +	filter->block_id = VCAP_IS2;
> +	filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
> +	filter->ingress_port_mask = BIT(priv->chip_port);
> +	*(__be16 *)filter->key.etype.etype.value = htons(ETH_P_MRP);
> +	*(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
> +	filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
> +	filter->action.port_mask = 0x0;
> +	filter->action.cpu_copy_ena = true;
> +	filter->action.cpu_qu_num = 0;
> +
> +	err = ocelot_vcap_filter_add(ocelot, filter, NULL);
> +	if (err)
> +		kfree(filter);
> +
> +	return err;
> +}
> +
> +static int ocelot_del_ring_role(struct net_device *dev,
> +				const struct switchdev_obj_ring_role_mrp *mrp)
> +{
> +	struct ocelot_port_private *priv = netdev_priv(dev);
> +	struct ocelot_port *ocelot_port = &priv->port;
> +	struct ocelot *ocelot = ocelot_port->ocelot;
> +
> +	if (ocelot->mrp_ring_id != mrp->ring_id)
> +		return -EINVAL;
> +
> +	if (!mrp->sw_backup)
> +		return -EOPNOTSUPP;
> +
> +	if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
> +		return 0;
> +
> +	return ocelot_mrp_del_vcap(ocelot, priv->chip_port);
> +}
> +#endif
> +

Would it make sense for this chunk of conditionally compiled code to
stay in a separate file like ocelot_mrp.c?

>  static int ocelot_port_obj_add(struct net_device *dev,
>  			       const struct switchdev_obj *obj,
>  			       struct netlink_ext_ack *extack)
> @@ -1083,6 +1219,15 @@ static int ocelot_port_obj_add(struct net_device *dev,
>  	case SWITCHDEV_OBJ_ID_PORT_MDB:
>  		ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
>  		break;
> +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> +	case SWITCHDEV_OBJ_ID_MRP:
> +		ret = ocelot_add_mrp(dev, SWITCHDEV_OBJ_MRP(obj));
> +		break;
> +	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
> +		ret = ocelot_add_ring_role(dev,
> +					   SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
> +		break;
> +#endif

I'm not really sure why SWITCHDEV_OBJ_ID_MRP is conditionally defined.
If you look at SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING, that isn't
conditionally defined, even though it depends on CONFIG_BRIDGE_VLAN_FILTERING
at runtime.

>  	default:
>  		return -EOPNOTSUPP;
>  	}
> @@ -1103,6 +1248,15 @@ static int ocelot_port_obj_del(struct net_device *dev,
>  	case SWITCHDEV_OBJ_ID_PORT_MDB:
>  		ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
>  		break;
> +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> +	case SWITCHDEV_OBJ_ID_MRP:
> +		ret = ocelot_del_mrp(dev, SWITCHDEV_OBJ_MRP(obj));
> +		break;
> +	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
> +		ret = ocelot_del_ring_role(dev,
> +					   SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
> +		break;
> +#endif
>  	default:
>  		return -EOPNOTSUPP;
>  	}
> diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> index 6b6eb92149ba..96a9c9f98060 100644
> --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> @@ -698,6 +698,12 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
>  			skb->offload_fwd_mark = 1;
>  
>  		skb->protocol = eth_type_trans(skb, dev);
> +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> +		if (skb->protocol == ntohs(ETH_P_MRP) &&
> +		    (priv->dev == ocelot->mrp_p_port ||
> +		     priv->dev == ocelot->mrp_s_port))
> +			skb->offload_fwd_mark = 0;
> +#endif

I wonder if you could just reserve a certain CPUQ for trapped traffic,
and just generically check for that, instead of MRP port roles?

>  		if (!skb_defer_rx_timestamp(skb))
>  			netif_rx(skb);
>  		dev->stats.rx_bytes += len;
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index d0d48e9620fb..d95c019ad84e 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -682,6 +682,12 @@ struct ocelot {
>  	/* Protects the PTP clock */
>  	spinlock_t			ptp_clock_lock;
>  	struct ptp_pin_desc		ptp_pins[OCELOT_PTP_PINS_NUM];
> +
> +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> +	u16				mrp_ring_id;
> +	struct net_device		*mrp_p_port;
> +	struct net_device		*mrp_s_port;
> +#endif
>  };
>  
>  struct ocelot_policer {
> -- 
> 2.27.0
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v3 0/5] bridge: mrp: Extend br_mrp_switchdev_*
  2021-02-10 10:08   ` [Bridge] " Vladimir Oltean
@ 2021-02-10 12:15     ` Horatiu Vultur
  -1 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-10 12:15 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, Claudiu Manoil, alexandre.belloni, UNGLinuxDriver,
	netdev, linux-kernel, bridge

The 02/10/2021 10:08, Vladimir Oltean wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> Hi Horatiu,
> 
> On Tue, Feb 09, 2021 at 09:21:07PM +0100, Horatiu Vultur wrote:
> > This patch series extends MRP switchdev to allow the SW to have a better
> > understanding if the HW can implement the MRP functionality or it needs
> > to help the HW to run it. There are 3 cases:
> > - when HW can't implement at all the functionality.
> > - when HW can implement a part of the functionality but needs the SW
> >   implement the rest. For example if it can't detect when it stops
> >   receiving MRP Test frames but it can copy the MRP frames to CPU to
> >   allow the SW to determine this.  Another example is generating the MRP
> >   Test frames. If HW can't do that then the SW is used as backup.
> > - when HW can implement completely the functionality.
> >
> > So, initially the SW tries to offload the entire functionality in HW, if
> > that fails it tries offload parts of the functionality in HW and use the
> > SW as helper and if also this fails then MRP can't run on this HW.
> >
> > Also implement the switchdev calls for Ocelot driver. This is an example
> > where the HW can't run completely the functionality but it can help the SW
> > to run it, by trapping all MRP frames to CPU.
> >
> > v3:
> >  - implement the switchdev calls needed by Ocelot driver.
> > v2:
> >  - fix typos in comments and in commit messages
> >  - remove some of the comments
> >  - move repeated code in helper function
> >  - fix issue when deleting a node when sw_backup was true
> >
> > Horatiu Vultur (5):
> >   switchdev: mrp: Extend ring_role_mrp and in_role_mrp
> >   bridge: mrp: Add 'enum br_mrp_hw_support'
> >   bridge: mrp: Extend br_mrp_switchdev to detect better the errors
> >   bridge: mrp: Update br_mrp to use new return values of
> >     br_mrp_switchdev
> >   net: mscc: ocelot: Add support for MRP
> >
> >  drivers/net/ethernet/mscc/ocelot_net.c     | 154 +++++++++++++++++++
> >  drivers/net/ethernet/mscc/ocelot_vsc7514.c |   6 +
> >  include/net/switchdev.h                    |   2 +
> >  include/soc/mscc/ocelot.h                  |   6 +
> >  net/bridge/br_mrp.c                        |  43 ++++--
> >  net/bridge/br_mrp_switchdev.c              | 171 +++++++++++++--------
> >  net/bridge/br_private_mrp.h                |  38 +++--
> >  7 files changed, 327 insertions(+), 93 deletions(-)
> >
> > --
> > 2.27.0
> >
> 

Hi Vladimir,

> Which net-next commit can these patches be applied to? On the current
> master I get:

Sorry for this. I had an extra patch when I created these patches. And
based on this I have added the patch series. This extra patch was this
one:
https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/commit/?id=b2bdba1cbc84

Which was already applied to net. And I wanted to have it to be able to
do more complete test of this patch series. Next time I should be more
careful with this.

> 
> Applying: switchdev: mrp: Extend ring_role_mrp and in_role_mrp
> Applying: bridge: mrp: Add 'enum br_mrp_hw_support'
> Applying: bridge: mrp: Extend br_mrp_switchdev to detect better the errors
> error: patch failed: net/bridge/br_mrp_switchdev.c:177
> error: net/bridge/br_mrp_switchdev.c: patch does not apply
> Patch failed at 0004 bridge: mrp: Extend br_mrp_switchdev to detect better the errors
> hint: Use 'git am --show-current-patch' to see the failed patch
> When you have resolved this problem, run "git am --continue".
> If you prefer to skip this patch, run "git am --skip" instead.
> To restore the original branch and stop patching, run "git am --abort".

-- 
/Horatiu

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Bridge] [PATCH net-next v3 0/5] bridge: mrp: Extend br_mrp_switchdev_*
@ 2021-02-10 12:15     ` Horatiu Vultur
  0 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-10 12:15 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: ivecera, andrew, alexandre.belloni, jiri, rasmus.villemoes,
	netdev, bridge, linux-kernel, UNGLinuxDriver, Claudiu Manoil,
	nikolay, roopa, kuba, davem

The 02/10/2021 10:08, Vladimir Oltean wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> Hi Horatiu,
> 
> On Tue, Feb 09, 2021 at 09:21:07PM +0100, Horatiu Vultur wrote:
> > This patch series extends MRP switchdev to allow the SW to have a better
> > understanding if the HW can implement the MRP functionality or it needs
> > to help the HW to run it. There are 3 cases:
> > - when HW can't implement at all the functionality.
> > - when HW can implement a part of the functionality but needs the SW
> >   implement the rest. For example if it can't detect when it stops
> >   receiving MRP Test frames but it can copy the MRP frames to CPU to
> >   allow the SW to determine this.  Another example is generating the MRP
> >   Test frames. If HW can't do that then the SW is used as backup.
> > - when HW can implement completely the functionality.
> >
> > So, initially the SW tries to offload the entire functionality in HW, if
> > that fails it tries offload parts of the functionality in HW and use the
> > SW as helper and if also this fails then MRP can't run on this HW.
> >
> > Also implement the switchdev calls for Ocelot driver. This is an example
> > where the HW can't run completely the functionality but it can help the SW
> > to run it, by trapping all MRP frames to CPU.
> >
> > v3:
> >  - implement the switchdev calls needed by Ocelot driver.
> > v2:
> >  - fix typos in comments and in commit messages
> >  - remove some of the comments
> >  - move repeated code in helper function
> >  - fix issue when deleting a node when sw_backup was true
> >
> > Horatiu Vultur (5):
> >   switchdev: mrp: Extend ring_role_mrp and in_role_mrp
> >   bridge: mrp: Add 'enum br_mrp_hw_support'
> >   bridge: mrp: Extend br_mrp_switchdev to detect better the errors
> >   bridge: mrp: Update br_mrp to use new return values of
> >     br_mrp_switchdev
> >   net: mscc: ocelot: Add support for MRP
> >
> >  drivers/net/ethernet/mscc/ocelot_net.c     | 154 +++++++++++++++++++
> >  drivers/net/ethernet/mscc/ocelot_vsc7514.c |   6 +
> >  include/net/switchdev.h                    |   2 +
> >  include/soc/mscc/ocelot.h                  |   6 +
> >  net/bridge/br_mrp.c                        |  43 ++++--
> >  net/bridge/br_mrp_switchdev.c              | 171 +++++++++++++--------
> >  net/bridge/br_private_mrp.h                |  38 +++--
> >  7 files changed, 327 insertions(+), 93 deletions(-)
> >
> > --
> > 2.27.0
> >
> 

Hi Vladimir,

> Which net-next commit can these patches be applied to? On the current
> master I get:

Sorry for this. I had an extra patch when I created these patches. And
based on this I have added the patch series. This extra patch was this
one:
https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/commit/?id=b2bdba1cbc84

Which was already applied to net. And I wanted to have it to be able to
do more complete test of this patch series. Next time I should be more
careful with this.

> 
> Applying: switchdev: mrp: Extend ring_role_mrp and in_role_mrp
> Applying: bridge: mrp: Add 'enum br_mrp_hw_support'
> Applying: bridge: mrp: Extend br_mrp_switchdev to detect better the errors
> error: patch failed: net/bridge/br_mrp_switchdev.c:177
> error: net/bridge/br_mrp_switchdev.c: patch does not apply
> Patch failed at 0004 bridge: mrp: Extend br_mrp_switchdev to detect better the errors
> hint: Use 'git am --show-current-patch' to see the failed patch
> When you have resolved this problem, run "git am --continue".
> If you prefer to skip this patch, run "git am --skip" instead.
> To restore the original branch and stop patching, run "git am --abort".

-- 
/Horatiu

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH net-next v3 5/5] net: mscc: ocelot: Add support for MRP
  2021-02-10 10:18     ` [Bridge] " Vladimir Oltean
@ 2021-02-10 12:30       ` Horatiu Vultur
  -1 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-10 12:30 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: jiri, ivecera, davem, kuba, roopa, nikolay, rasmus.villemoes,
	andrew, Claudiu Manoil, alexandre.belloni, UNGLinuxDriver,
	netdev, linux-kernel, bridge

The 02/10/2021 10:18, Vladimir Oltean wrote:

Hi Vladimir,

> 
> Would you mind adding the switchdev MRP support for the DSA driver too,
> and move the code to the common ocelot library? I would like to give it
> a run. I think that's only fair, since I have to keep in sync the
> vsc7514 driver too for features that get added through DSA :)

That is totally fair. I will do it in the next version :)

> 
> On Tue, Feb 09, 2021 at 09:21:12PM +0100, Horatiu Vultur wrote:
> > Add basic support for MRP. The HW will just trap all MRP frames on the
> > ring ports to CPU and allow the SW to process them. In this way it is
> > possible to for this node to behave both as MRM and MRC.
> >
> > Current limitations are:
> > - it doesn't support Interconnect roles.
> > - it supports only a single ring.
> > - the HW should be able to do forwarding of MRP Test frames so the SW
> >   will not need to do this. So it would be able to have the role MRC
> >   without SW support.
> >
> > Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> > ---
> >  drivers/net/ethernet/mscc/ocelot_net.c     | 154 +++++++++++++++++++++
> >  drivers/net/ethernet/mscc/ocelot_vsc7514.c |   6 +
> >  include/soc/mscc/ocelot.h                  |   6 +
> >  3 files changed, 166 insertions(+)
> >
> > diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> > index 8f12fa45b1b5..65971403e823 100644
> > --- a/drivers/net/ethernet/mscc/ocelot_net.c
> > +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> > @@ -9,7 +9,10 @@
> >   */
> >
> >  #include <linux/if_bridge.h>
> > +#include <linux/mrp_bridge.h>
> >  #include <net/pkt_cls.h>
> > +#include <soc/mscc/ocelot_vcap.h>
> > +#include <uapi/linux/mrp_bridge.h>
> >  #include "ocelot.h"
> >  #include "ocelot_vcap.h"
> >
> > @@ -1069,6 +1072,139 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
> >       return ocelot_port_mdb_del(ocelot, port, mdb);
> >  }
> >
> > +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> > +static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int port)
> > +{
> > +     struct ocelot_vcap_block *block_vcap_is2;
> > +     struct ocelot_vcap_filter *filter;
> > +
> > +     block_vcap_is2 = &ocelot->block[VCAP_IS2];
> > +     filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, port,
> > +                                                  false);
> > +     if (!filter)
> > +             return 0;
> > +
> > +     return ocelot_vcap_filter_del(ocelot, filter);
> > +}
> > +
> > +static int ocelot_add_mrp(struct net_device *dev,
> > +                       const struct switchdev_obj_mrp *mrp)
> > +{
> > +     struct ocelot_port_private *priv = netdev_priv(dev);
> > +     struct ocelot_port *ocelot_port = &priv->port;
> > +     struct ocelot *ocelot = ocelot_port->ocelot;
> > +
> > +     if (mrp->p_port != dev && mrp->s_port != dev)
> > +             return 0;
> > +
> > +     if (ocelot->mrp_ring_id != 0 &&
> > +         ocelot->mrp_s_port &&
> > +         ocelot->mrp_p_port)
> > +             return -EINVAL;
> > +
> > +     if (mrp->p_port == dev)
> > +             ocelot->mrp_p_port = dev;
> > +
> > +     if (mrp->s_port == dev)
> > +             ocelot->mrp_s_port = dev;
> > +
> > +     ocelot->mrp_ring_id = mrp->ring_id;
> > +
> > +     return 0;
> > +}
> > +
> > +static int ocelot_del_mrp(struct net_device *dev,
> > +                       const struct switchdev_obj_mrp *mrp)
> > +{
> > +     struct ocelot_port_private *priv = netdev_priv(dev);
> > +     struct ocelot_port *ocelot_port = &priv->port;
> > +     struct ocelot *ocelot = ocelot_port->ocelot;
> > +
> > +     if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
> > +             return 0;
> > +
> > +     if (ocelot->mrp_ring_id == 0 &&
> > +         !ocelot->mrp_s_port &&
> > +         !ocelot->mrp_p_port)
> > +             return -EINVAL;
> > +
> > +     if (ocelot_mrp_del_vcap(ocelot, priv->chip_port))
> > +             return -EINVAL;
> > +
> > +     if (ocelot->mrp_p_port == dev)
> > +             ocelot->mrp_p_port = NULL;
> > +
> > +     if (ocelot->mrp_s_port == dev)
> > +             ocelot->mrp_s_port = NULL;
> > +
> > +     ocelot->mrp_ring_id = 0;
> > +
> > +     return 0;
> > +}
> > +
> > +static int ocelot_add_ring_role(struct net_device *dev,
> > +                             const struct switchdev_obj_ring_role_mrp *mrp)
> > +{
> > +     struct ocelot_port_private *priv = netdev_priv(dev);
> > +     struct ocelot_port *ocelot_port = &priv->port;
> > +     struct ocelot *ocelot = ocelot_port->ocelot;
> > +     struct ocelot_vcap_filter *filter;
> > +     int err;
> > +
> > +     if (ocelot->mrp_ring_id != mrp->ring_id)
> > +             return -EINVAL;
> > +
> > +     if (!mrp->sw_backup)
> > +             return -EOPNOTSUPP;
> > +
> > +     if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
> > +             return 0;
> > +
> > +     filter = kzalloc(sizeof(*filter), GFP_KERNEL);
> > +     if (!filter)
> > +             return -ENOMEM;
> > +
> > +     filter->key_type = OCELOT_VCAP_KEY_ETYPE;
> > +     filter->prio = 1;
> > +     filter->id.cookie = priv->chip_port;
> > +     filter->id.tc_offload = false;
> > +     filter->block_id = VCAP_IS2;
> > +     filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
> > +     filter->ingress_port_mask = BIT(priv->chip_port);
> > +     *(__be16 *)filter->key.etype.etype.value = htons(ETH_P_MRP);
> > +     *(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
> > +     filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
> > +     filter->action.port_mask = 0x0;
> > +     filter->action.cpu_copy_ena = true;
> > +     filter->action.cpu_qu_num = 0;
> > +
> > +     err = ocelot_vcap_filter_add(ocelot, filter, NULL);
> > +     if (err)
> > +             kfree(filter);
> > +
> > +     return err;
> > +}
> > +
> > +static int ocelot_del_ring_role(struct net_device *dev,
> > +                             const struct switchdev_obj_ring_role_mrp *mrp)
> > +{
> > +     struct ocelot_port_private *priv = netdev_priv(dev);
> > +     struct ocelot_port *ocelot_port = &priv->port;
> > +     struct ocelot *ocelot = ocelot_port->ocelot;
> > +
> > +     if (ocelot->mrp_ring_id != mrp->ring_id)
> > +             return -EINVAL;
> > +
> > +     if (!mrp->sw_backup)
> > +             return -EOPNOTSUPP;
> > +
> > +     if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
> > +             return 0;
> > +
> > +     return ocelot_mrp_del_vcap(ocelot, priv->chip_port);
> > +}
> > +#endif
> > +
> 
> Would it make sense for this chunk of conditionally compiled code to
> stay in a separate file like ocelot_mrp.c?

Also initially I was thinking to add this code in a different file but I
was not sure. But I will do that in the next version.

> 
> >  static int ocelot_port_obj_add(struct net_device *dev,
> >                              const struct switchdev_obj *obj,
> >                              struct netlink_ext_ack *extack)
> > @@ -1083,6 +1219,15 @@ static int ocelot_port_obj_add(struct net_device *dev,
> >       case SWITCHDEV_OBJ_ID_PORT_MDB:
> >               ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
> >               break;
> > +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> > +     case SWITCHDEV_OBJ_ID_MRP:
> > +             ret = ocelot_add_mrp(dev, SWITCHDEV_OBJ_MRP(obj));
> > +             break;
> > +     case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
> > +             ret = ocelot_add_ring_role(dev,
> > +                                        SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
> > +             break;
> > +#endif
> 
> I'm not really sure why SWITCHDEV_OBJ_ID_MRP is conditionally defined.
> If you look at SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING, that isn't
> conditionally defined, even though it depends on CONFIG_BRIDGE_VLAN_FILTERING
> at runtime.

Then should I just create another patch for removing this? To be honest
I also prefer that SWITCHDEV_OBJ_ID_MRP will not be conditionally
defined.

> 
> >       default:
> >               return -EOPNOTSUPP;
> >       }
> > @@ -1103,6 +1248,15 @@ static int ocelot_port_obj_del(struct net_device *dev,
> >       case SWITCHDEV_OBJ_ID_PORT_MDB:
> >               ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
> >               break;
> > +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> > +     case SWITCHDEV_OBJ_ID_MRP:
> > +             ret = ocelot_del_mrp(dev, SWITCHDEV_OBJ_MRP(obj));
> > +             break;
> > +     case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
> > +             ret = ocelot_del_ring_role(dev,
> > +                                        SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
> > +             break;
> > +#endif
> >       default:
> >               return -EOPNOTSUPP;
> >       }
> > diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > index 6b6eb92149ba..96a9c9f98060 100644
> > --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > @@ -698,6 +698,12 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
> >                       skb->offload_fwd_mark = 1;
> >
> >               skb->protocol = eth_type_trans(skb, dev);
> > +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> > +             if (skb->protocol == ntohs(ETH_P_MRP) &&
> > +                 (priv->dev == ocelot->mrp_p_port ||
> > +                  priv->dev == ocelot->mrp_s_port))
> > +                     skb->offload_fwd_mark = 0;
> > +#endif
> 
> I wonder if you could just reserve a certain CPUQ for trapped traffic,
> and just generically check for that, instead of MRP port roles?

That is a good idea, I would try to have a look.

> 
> >               if (!skb_defer_rx_timestamp(skb))
> >                       netif_rx(skb);
> >               dev->stats.rx_bytes += len;
> > diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> > index d0d48e9620fb..d95c019ad84e 100644
> > --- a/include/soc/mscc/ocelot.h
> > +++ b/include/soc/mscc/ocelot.h
> > @@ -682,6 +682,12 @@ struct ocelot {
> >       /* Protects the PTP clock */
> >       spinlock_t                      ptp_clock_lock;
> >       struct ptp_pin_desc             ptp_pins[OCELOT_PTP_PINS_NUM];
> > +
> > +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> > +     u16                             mrp_ring_id;
> > +     struct net_device               *mrp_p_port;
> > +     struct net_device               *mrp_s_port;
> > +#endif
> >  };
> >
> >  struct ocelot_policer {
> > --
> > 2.27.0
> >

-- 
/Horatiu

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [Bridge] [PATCH net-next v3 5/5] net: mscc: ocelot: Add support for MRP
@ 2021-02-10 12:30       ` Horatiu Vultur
  0 siblings, 0 replies; 20+ messages in thread
From: Horatiu Vultur @ 2021-02-10 12:30 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: ivecera, andrew, alexandre.belloni, jiri, rasmus.villemoes,
	netdev, bridge, linux-kernel, UNGLinuxDriver, Claudiu Manoil,
	nikolay, roopa, kuba, davem

The 02/10/2021 10:18, Vladimir Oltean wrote:

Hi Vladimir,

> 
> Would you mind adding the switchdev MRP support for the DSA driver too,
> and move the code to the common ocelot library? I would like to give it
> a run. I think that's only fair, since I have to keep in sync the
> vsc7514 driver too for features that get added through DSA :)

That is totally fair. I will do it in the next version :)

> 
> On Tue, Feb 09, 2021 at 09:21:12PM +0100, Horatiu Vultur wrote:
> > Add basic support for MRP. The HW will just trap all MRP frames on the
> > ring ports to CPU and allow the SW to process them. In this way it is
> > possible to for this node to behave both as MRM and MRC.
> >
> > Current limitations are:
> > - it doesn't support Interconnect roles.
> > - it supports only a single ring.
> > - the HW should be able to do forwarding of MRP Test frames so the SW
> >   will not need to do this. So it would be able to have the role MRC
> >   without SW support.
> >
> > Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
> > ---
> >  drivers/net/ethernet/mscc/ocelot_net.c     | 154 +++++++++++++++++++++
> >  drivers/net/ethernet/mscc/ocelot_vsc7514.c |   6 +
> >  include/soc/mscc/ocelot.h                  |   6 +
> >  3 files changed, 166 insertions(+)
> >
> > diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
> > index 8f12fa45b1b5..65971403e823 100644
> > --- a/drivers/net/ethernet/mscc/ocelot_net.c
> > +++ b/drivers/net/ethernet/mscc/ocelot_net.c
> > @@ -9,7 +9,10 @@
> >   */
> >
> >  #include <linux/if_bridge.h>
> > +#include <linux/mrp_bridge.h>
> >  #include <net/pkt_cls.h>
> > +#include <soc/mscc/ocelot_vcap.h>
> > +#include <uapi/linux/mrp_bridge.h>
> >  #include "ocelot.h"
> >  #include "ocelot_vcap.h"
> >
> > @@ -1069,6 +1072,139 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
> >       return ocelot_port_mdb_del(ocelot, port, mdb);
> >  }
> >
> > +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> > +static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int port)
> > +{
> > +     struct ocelot_vcap_block *block_vcap_is2;
> > +     struct ocelot_vcap_filter *filter;
> > +
> > +     block_vcap_is2 = &ocelot->block[VCAP_IS2];
> > +     filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, port,
> > +                                                  false);
> > +     if (!filter)
> > +             return 0;
> > +
> > +     return ocelot_vcap_filter_del(ocelot, filter);
> > +}
> > +
> > +static int ocelot_add_mrp(struct net_device *dev,
> > +                       const struct switchdev_obj_mrp *mrp)
> > +{
> > +     struct ocelot_port_private *priv = netdev_priv(dev);
> > +     struct ocelot_port *ocelot_port = &priv->port;
> > +     struct ocelot *ocelot = ocelot_port->ocelot;
> > +
> > +     if (mrp->p_port != dev && mrp->s_port != dev)
> > +             return 0;
> > +
> > +     if (ocelot->mrp_ring_id != 0 &&
> > +         ocelot->mrp_s_port &&
> > +         ocelot->mrp_p_port)
> > +             return -EINVAL;
> > +
> > +     if (mrp->p_port == dev)
> > +             ocelot->mrp_p_port = dev;
> > +
> > +     if (mrp->s_port == dev)
> > +             ocelot->mrp_s_port = dev;
> > +
> > +     ocelot->mrp_ring_id = mrp->ring_id;
> > +
> > +     return 0;
> > +}
> > +
> > +static int ocelot_del_mrp(struct net_device *dev,
> > +                       const struct switchdev_obj_mrp *mrp)
> > +{
> > +     struct ocelot_port_private *priv = netdev_priv(dev);
> > +     struct ocelot_port *ocelot_port = &priv->port;
> > +     struct ocelot *ocelot = ocelot_port->ocelot;
> > +
> > +     if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
> > +             return 0;
> > +
> > +     if (ocelot->mrp_ring_id == 0 &&
> > +         !ocelot->mrp_s_port &&
> > +         !ocelot->mrp_p_port)
> > +             return -EINVAL;
> > +
> > +     if (ocelot_mrp_del_vcap(ocelot, priv->chip_port))
> > +             return -EINVAL;
> > +
> > +     if (ocelot->mrp_p_port == dev)
> > +             ocelot->mrp_p_port = NULL;
> > +
> > +     if (ocelot->mrp_s_port == dev)
> > +             ocelot->mrp_s_port = NULL;
> > +
> > +     ocelot->mrp_ring_id = 0;
> > +
> > +     return 0;
> > +}
> > +
> > +static int ocelot_add_ring_role(struct net_device *dev,
> > +                             const struct switchdev_obj_ring_role_mrp *mrp)
> > +{
> > +     struct ocelot_port_private *priv = netdev_priv(dev);
> > +     struct ocelot_port *ocelot_port = &priv->port;
> > +     struct ocelot *ocelot = ocelot_port->ocelot;
> > +     struct ocelot_vcap_filter *filter;
> > +     int err;
> > +
> > +     if (ocelot->mrp_ring_id != mrp->ring_id)
> > +             return -EINVAL;
> > +
> > +     if (!mrp->sw_backup)
> > +             return -EOPNOTSUPP;
> > +
> > +     if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
> > +             return 0;
> > +
> > +     filter = kzalloc(sizeof(*filter), GFP_KERNEL);
> > +     if (!filter)
> > +             return -ENOMEM;
> > +
> > +     filter->key_type = OCELOT_VCAP_KEY_ETYPE;
> > +     filter->prio = 1;
> > +     filter->id.cookie = priv->chip_port;
> > +     filter->id.tc_offload = false;
> > +     filter->block_id = VCAP_IS2;
> > +     filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
> > +     filter->ingress_port_mask = BIT(priv->chip_port);
> > +     *(__be16 *)filter->key.etype.etype.value = htons(ETH_P_MRP);
> > +     *(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
> > +     filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
> > +     filter->action.port_mask = 0x0;
> > +     filter->action.cpu_copy_ena = true;
> > +     filter->action.cpu_qu_num = 0;
> > +
> > +     err = ocelot_vcap_filter_add(ocelot, filter, NULL);
> > +     if (err)
> > +             kfree(filter);
> > +
> > +     return err;
> > +}
> > +
> > +static int ocelot_del_ring_role(struct net_device *dev,
> > +                             const struct switchdev_obj_ring_role_mrp *mrp)
> > +{
> > +     struct ocelot_port_private *priv = netdev_priv(dev);
> > +     struct ocelot_port *ocelot_port = &priv->port;
> > +     struct ocelot *ocelot = ocelot_port->ocelot;
> > +
> > +     if (ocelot->mrp_ring_id != mrp->ring_id)
> > +             return -EINVAL;
> > +
> > +     if (!mrp->sw_backup)
> > +             return -EOPNOTSUPP;
> > +
> > +     if (ocelot->mrp_p_port != dev && ocelot->mrp_s_port != dev)
> > +             return 0;
> > +
> > +     return ocelot_mrp_del_vcap(ocelot, priv->chip_port);
> > +}
> > +#endif
> > +
> 
> Would it make sense for this chunk of conditionally compiled code to
> stay in a separate file like ocelot_mrp.c?

Also initially I was thinking to add this code in a different file but I
was not sure. But I will do that in the next version.

> 
> >  static int ocelot_port_obj_add(struct net_device *dev,
> >                              const struct switchdev_obj *obj,
> >                              struct netlink_ext_ack *extack)
> > @@ -1083,6 +1219,15 @@ static int ocelot_port_obj_add(struct net_device *dev,
> >       case SWITCHDEV_OBJ_ID_PORT_MDB:
> >               ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
> >               break;
> > +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> > +     case SWITCHDEV_OBJ_ID_MRP:
> > +             ret = ocelot_add_mrp(dev, SWITCHDEV_OBJ_MRP(obj));
> > +             break;
> > +     case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
> > +             ret = ocelot_add_ring_role(dev,
> > +                                        SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
> > +             break;
> > +#endif
> 
> I'm not really sure why SWITCHDEV_OBJ_ID_MRP is conditionally defined.
> If you look at SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING, that isn't
> conditionally defined, even though it depends on CONFIG_BRIDGE_VLAN_FILTERING
> at runtime.

Then should I just create another patch for removing this? To be honest
I also prefer that SWITCHDEV_OBJ_ID_MRP will not be conditionally
defined.

> 
> >       default:
> >               return -EOPNOTSUPP;
> >       }
> > @@ -1103,6 +1248,15 @@ static int ocelot_port_obj_del(struct net_device *dev,
> >       case SWITCHDEV_OBJ_ID_PORT_MDB:
> >               ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
> >               break;
> > +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> > +     case SWITCHDEV_OBJ_ID_MRP:
> > +             ret = ocelot_del_mrp(dev, SWITCHDEV_OBJ_MRP(obj));
> > +             break;
> > +     case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
> > +             ret = ocelot_del_ring_role(dev,
> > +                                        SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
> > +             break;
> > +#endif
> >       default:
> >               return -EOPNOTSUPP;
> >       }
> > diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > index 6b6eb92149ba..96a9c9f98060 100644
> > --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
> > @@ -698,6 +698,12 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
> >                       skb->offload_fwd_mark = 1;
> >
> >               skb->protocol = eth_type_trans(skb, dev);
> > +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> > +             if (skb->protocol == ntohs(ETH_P_MRP) &&
> > +                 (priv->dev == ocelot->mrp_p_port ||
> > +                  priv->dev == ocelot->mrp_s_port))
> > +                     skb->offload_fwd_mark = 0;
> > +#endif
> 
> I wonder if you could just reserve a certain CPUQ for trapped traffic,
> and just generically check for that, instead of MRP port roles?

That is a good idea, I would try to have a look.

> 
> >               if (!skb_defer_rx_timestamp(skb))
> >                       netif_rx(skb);
> >               dev->stats.rx_bytes += len;
> > diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> > index d0d48e9620fb..d95c019ad84e 100644
> > --- a/include/soc/mscc/ocelot.h
> > +++ b/include/soc/mscc/ocelot.h
> > @@ -682,6 +682,12 @@ struct ocelot {
> >       /* Protects the PTP clock */
> >       spinlock_t                      ptp_clock_lock;
> >       struct ptp_pin_desc             ptp_pins[OCELOT_PTP_PINS_NUM];
> > +
> > +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> > +     u16                             mrp_ring_id;
> > +     struct net_device               *mrp_p_port;
> > +     struct net_device               *mrp_s_port;
> > +#endif
> >  };
> >
> >  struct ocelot_policer {
> > --
> > 2.27.0
> >

-- 
/Horatiu

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2021-02-10 12:35 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-09 20:21 [PATCH net-next v3 0/5] bridge: mrp: Extend br_mrp_switchdev_* Horatiu Vultur
2021-02-09 20:21 ` [Bridge] " Horatiu Vultur
2021-02-09 20:21 ` [PATCH net-next v3 1/5] switchdev: mrp: Extend ring_role_mrp and in_role_mrp Horatiu Vultur
2021-02-09 20:21   ` [Bridge] " Horatiu Vultur
2021-02-09 20:21 ` [PATCH net-next v3 2/5] bridge: mrp: Add 'enum br_mrp_hw_support' Horatiu Vultur
2021-02-09 20:21   ` [Bridge] " Horatiu Vultur
2021-02-09 20:21 ` [PATCH net-next v3 3/5] bridge: mrp: Extend br_mrp_switchdev to detect better the errors Horatiu Vultur
2021-02-09 20:21   ` [Bridge] " Horatiu Vultur
2021-02-09 20:21 ` [PATCH net-next v3 4/5] bridge: mrp: Update br_mrp to use new return values of br_mrp_switchdev Horatiu Vultur
2021-02-09 20:21   ` [Bridge] " Horatiu Vultur
2021-02-09 20:21 ` [PATCH net-next v3 5/5] net: mscc: ocelot: Add support for MRP Horatiu Vultur
2021-02-09 20:21   ` [Bridge] " Horatiu Vultur
2021-02-10 10:18   ` Vladimir Oltean
2021-02-10 10:18     ` [Bridge] " Vladimir Oltean
2021-02-10 12:30     ` Horatiu Vultur
2021-02-10 12:30       ` [Bridge] " Horatiu Vultur
2021-02-10 10:08 ` [PATCH net-next v3 0/5] bridge: mrp: Extend br_mrp_switchdev_* Vladimir Oltean
2021-02-10 10:08   ` [Bridge] " Vladimir Oltean
2021-02-10 12:15   ` Horatiu Vultur
2021-02-10 12:15     ` [Bridge] " Horatiu Vultur

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.