Horatiu Vultur
2020-Jan-24 16:18 UTC
[Bridge] [RFC net-next v3 00/10] net: bridge: mrp: Add support for Media Redundancy Protocol (MRP)
Media Redundancy Protocol is a data network protocol standardized by International Electrotechnical Commission as IEC 62439-2. It allows rings of Ethernet switches to overcome any single failure with recovery time faster than STP. It is primarily used in Industrial Ethernet applications. Based on the previous RFC[1][2], the MRP state machine and all the timers were moved to userspace. A generic netlink interface is added to allow configuring the HW, and logic added to to implement the MRP specific forwarding rules. The userspace application that is using the new netlink can be found here[3]. The current implementation both in kernel and userspace supports only 2 roles: MRM - this one is responsible to send MRP_Test and MRP_Topo frames on both ring ports. It needs to process MRP_Test to know if the ring is open or closed. This operation is desired to be offloaded to the HW because it requires to generate and process up to 4000 frames per second. Whenever it detects that the ring open it sends MRP_Topo frames to notify all MRC about changes in the topology. MRM needs also to process MRP_LinkChange frames, these frames are generated by the MRC. When the ring is open the the state of both ports is to forward frames and when the ring is closed then the secondary port is blocked. MRC - this one is responsible to forward MRP frames between the ring ports. In case one of the ring ports gets a link down or up, then MRC will generate a MRP_LinkChange frames. This node should also process MRP_Topo frames and to clear its FDB when it receives this frame. Userspace Deamon +----------+ Client + | +--------------|-----------------------------------------+ Kernel | + Netlink | + Interrupt | | +--------------|------------------------------|----------+ HW | Switchdev | + | The user interacts using the client (called 'mrp'), the client talks to the deamon (called 'mrp_server'), which talks with the kernel using netlink. The kernel will try to offload the requests to the HW via switchdev API. For this a new generic netlink interface was added to the bridge. If the kernel cannot offload MRP to HW (maybe it does not have a switchdev driver, or it is just not supported), then all the netlink calls will return -EOPNOTSUPP. In this case the user-space deamon fallback to SW only implementation. There are required changes to the SW bridge to be able to run the MRP. First the bridge needs to initialize the netlink interface. And second it needs to know if a MRP frame was received on a MRP ring port. In case it was received the SW bridge should not forward the frame it needs to redirected to upper layes. In case it was not received on a ring port then it just forwards it as usual. To be able to offload this to the HW, it was required to extend the switchdev API. If this will be accepted then in the future the netlink interface can be expended with multiple attributes which are required by different roles of the MRP. Like Media Redundancy Automanager(MRA), Media Interconnect Manager(MIM) and Media Interconnect Client(MIC). [1] https://www.spinics.net/lists/netdev/msg623647.html [2] https://www.spinics.net/lists/netdev/msg624378.html [3] https://github.com/microchip-ung/mrp/tree/patch-v3 Horatiu Vultur (10): net: bridge: mrp: Expose mrp attributes. net: bridge: mrp: Expose function br_mrp_port_open net: bridge: mrp: Add MRP interface used by netlink net: bridge: mrp: Add generic netlink interface to configure MRP net: bridge: mrp: Update MRP interface to add switchdev support net: bridge: mrp: switchdev: Extend switchdev API to offload MRP net: bridge: mrp: switchdev: Implement MRP API for switchdev net: bridge: mrp: Connect MRP api with the switchev API net: bridge: mrp: Integrate MRP into the bridge net: bridge: mrp: Update Kconfig and Makefile include/linux/mrp_bridge.h | 25 ++ include/net/switchdev.h | 51 +++ include/uapi/linux/if_ether.h | 1 + include/uapi/linux/mrp_bridge.h | 118 ++++++ net/bridge/Kconfig | 12 + net/bridge/Makefile | 2 + net/bridge/br.c | 11 + net/bridge/br_device.c | 3 + net/bridge/br_if.c | 6 + net/bridge/br_input.c | 14 + net/bridge/br_mrp.c | 193 ++++++++++ net/bridge/br_mrp_netlink.c | 655 ++++++++++++++++++++++++++++++++ net/bridge/br_mrp_switchdev.c | 147 +++++++ net/bridge/br_private.h | 14 + net/bridge/br_private_mrp.h | 58 +++ 15 files changed, 1310 insertions(+) create mode 100644 include/linux/mrp_bridge.h create mode 100644 include/uapi/linux/mrp_bridge.h create mode 100644 net/bridge/br_mrp.c create mode 100644 net/bridge/br_mrp_netlink.c create mode 100644 net/bridge/br_mrp_switchdev.c create mode 100644 net/bridge/br_private_mrp.h -- 2.17.1
Horatiu Vultur
2020-Jan-24 16:18 UTC
[Bridge] [RFC net-next v3 01/10] net: bridge: mrp: Expose mrp attributes.
Expose the attributes and the commands used by the generic netlink interface to configure the kernel from userspace. The following commands are available: BR_MRP_GENL_ADD - creates an MRP instance. This doesn't have yet any MRP ports attach to it. BR_MRP_GENL_ADD_PORT - adds a port to an MRP instance. A port can't be part of multiple MRP instances. BR_MRP_GENL_DEL - deletes an MRP instance. By deleting the instance also the MRP ports are removed. BR_MRP_GENL_DEL_PORT - removes a port from an MRP instance. When the port is removed then the state machine of the MRP instance is reset because there is no point in having only 1 port in a MRP ring. BR_MRP_GENL_SET_PORT_STATE - sets the port state. The port state can be forwarding, which means that the port will forward frames and blocked, which will block non-MRP frames. BR_MRP_GENL_SET_PORT_ROLE - sets the port role, it can be primary or secondary. BR_MRP_GENL_SET_RING_STATE - sets the ring state. The ring state can be closed or open. This information is needed in the MRP_Test frames BR_MRP_GENL_SET_RING_ROLE - sets the ring role. The supported roles are MRM and MRC. A node can have MRM role only if, it can terminate MRP frames, redirect MRP frames(except MRP_Test) to the CPU and can notify that it stopped receiving frames. A node can have MRC role only if it can copy MRP_Topology frames and forward frames between ring ports. BR_MRP_GENL_SET_START_TEST - this command is used to notify the kernel to start to generate MRP frames. A value of 0 for the attribute BR_MRP_ATTR_TEST_INTERVAL means that it should stop to generate frames. BR_MRP_GENL_FLUSH - this command is used to flush the fdb. BR_MRP_GENL_RING_OPEN - it is used by the kernel to notify userspace applications that one of the MRP ring ports stopped receiving MRP_Test frames The calls to BR_MRP_GENL_SET_PORT_ROLE and BR_MRP_GENL_SET_RING_STATE are required in case the driver can generate MRP_Test frames because these frames contains this information. And there is no other way for the HW to get it. MRP_Test frames contain also the number of times a ring went to open state. There is no command for that because the driver can count this by counting the number of times the ring state is set to open. Signed-off-by: Horatiu Vultur <horatiu.vultur at microchip.com> --- include/uapi/linux/if_ether.h | 1 + include/uapi/linux/mrp_bridge.h | 118 ++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 include/uapi/linux/mrp_bridge.h diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index f6ceb2e63d1e..d6de2b167448 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -92,6 +92,7 @@ #define ETH_P_PREAUTH 0x88C7 /* 802.11 Preauthentication */ #define ETH_P_TIPC 0x88CA /* TIPC */ #define ETH_P_LLDP 0x88CC /* Link Layer Discovery Protocol */ +#define ETH_P_MRP 0x88E3 /* Media Redundancy Protocol */ #define ETH_P_MACSEC 0x88E5 /* 802.1ae MACsec */ #define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */ #define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */ diff --git a/include/uapi/linux/mrp_bridge.h b/include/uapi/linux/mrp_bridge.h new file mode 100644 index 000000000000..c5a3d0a2b400 --- /dev/null +++ b/include/uapi/linux/mrp_bridge.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ + +#ifndef _UAPI_LINUX_MRP_BRIDGE_H +#define _UAPI_LINUX_MRP_BRIDGE_H + +/* These are the attributes used by the generic netlink interface for MRP + * BR_MRP_ATTR_BR_IFINDEX - must be the ifindex of the bridge + * BR_MRP_ATTR_PORT_IFINDEX - represents the index of one of the ports under the + * bridge, this is used in both cases to set the + * primary and secondary port. + * BR_MRP_ATTR_RING_NR - represents the ID of each MRP instance. On a bridge can + * multiple MRP instances. + * BR_MRP_ATTR_RING_ROLE - represents the role of the MRP instance. This + * attribute corresponds to the enum + * br_mrp_ring_role_type. + * BR_MRP_ATTR_RING_STATE - represents the state of the MRP instance. This + * attribute corresponds to the enum + * br_mrp_ring_state_type. + * BR_MRP_ATTR_PORT_STATE - represents the state of the MRP port. Add means if + * the port is allowed to forward or not non-MRP + * frames. It corresponds to enum + * br_mrp_port_state_type. + * BR_MRP_ATTR_PORT_ROLE - represents the role of the MRP port. It corresponds + * to enum br_mrp_port_role_type. + * BR_MRP_ATTR_TEST_INTERVAL - represents the interval of the generated MRP_Test + * frames. The value is in us. + * BR_MRP_ATTR_TEST_MAX_MISS - represents the number of MRP_Test frames that the + * port can miss before it notifies that the ring + * is open. + * BR_MRP_ATTR_RING_OPEN - represents the Loss of continuity of a port. A value + * of 1 means that the port stopped receiving MRP_Test + * while a value of 0 means that the port started to + * receive MRP_Test frames + * + * Kernel needs to know about the attributes BR_MRP_ATTR_RING_STATE and + * BR_MRP_ATTR_PORT_ROLE because in case it can offload the generation of + * MRP_Test frames to HW then the HW needs to know these attributes to be able + * to generate correctly MRP_Test frames. + */ +enum br_mrp_attr { + BR_MRP_ATTR_NONE, + BR_MRP_ATTR_BR_IFINDEX, + BR_MRP_ATTR_PORT_IFINDEX, + BR_MRP_ATTR_RING_NR, + BR_MRP_ATTR_RING_ROLE, + BR_MRP_ATTR_RING_STATE, + BR_MRP_ATTR_PORT_STATE, + BR_MRP_ATTR_PORT_ROLE, + BR_MRP_ATTR_TEST_INTERVAL, + BR_MRP_ATTR_TEST_MAX_MISS, + BR_MRP_ATTR_RING_OPEN, + + /* This must be the last entry */ + BR_MRP_ATTR_END, +}; + +#define BR_MRP_ATTR_MAX (BR_MRP_ATTR_END - 1) + +/* These are the commands used by the generic netlink interface for MRP + * BR_MRP_GENL_ADD - creates an MRP instance. This doesn't have yet any MRP + * ports attach to it. + * BR_MRP_GENL_ADD_PORT - adds a port to an MRP instance. A port can't be part + * of multiple MRP instances. + * BR_MRP_GENL_DEL - deletes an MRP instance. By deleting the instance also the + * MRP ports are removed. + * BR_MRP_GENL_DEL_PORT - removes a port from an MRP instance. + * BR_MRP_GENL_SET_PORT_STATE - sets the port state. + * BR_MRP_GENL_SET_PORT_ROLE - sets the port role. + * BR_MRP_GENL_SET_RING_STATE - sets the ring state. + * BR_MRP_GENL_SET_RING_ROLE - sets the ring role. + * BR_MRP_GENL_SET_START_TEST - this command is used to notify the Kernel to + * start to generate MRP frames. A value of 0 for + * the attribute BR_MRP_ATTR_TEST_INTERVAL means + * that it should stop to generate frames. + * BR_MRP_GENL_FLUSH - this command is used to flush the fdb. + * BR_MRP_GENL_RING_OPEN - it is used by the kernel to notify userspace + * applications that one of the MRP ring ports stopped + * receiving MRP_Test frames + */ +enum br_mrp_genl { + BR_MRP_GENL_ADD, + BR_MRP_GENL_ADD_PORT, + BR_MRP_GENL_DEL, + BR_MRP_GENL_DEL_PORT, + BR_MRP_GENL_SET_PORT_STATE, + BR_MRP_GENL_SET_PORT_ROLE, + BR_MRP_GENL_SET_RING_STATE, + BR_MRP_GENL_SET_RING_ROLE, + BR_MRP_GENL_START_TEST, + BR_MRP_GENL_FLUSH, + BR_MRP_GENL_RING_OPEN, +}; + +enum br_mrp_ring_role_type { + BR_MRP_RING_ROLE_DISABLED, + BR_MRP_RING_ROLE_MRC, + BR_MRP_RING_ROLE_MRM, +}; + +enum br_mrp_ring_state_type { + BR_MRP_RING_STATE_OPEN, + BR_MRP_RING_STATE_CLOSED, +}; + +enum br_mrp_port_state_type { + BR_MRP_PORT_STATE_DISABLED, + BR_MRP_PORT_STATE_BLOCKED, + BR_MRP_PORT_STATE_FORWARDING, + BR_MRP_PORT_STATE_NOT_CONNECTED, +}; + +enum br_mrp_port_role_type { + BR_MRP_PORT_ROLE_PRIMARY, + BR_MRP_PORT_ROLE_SECONDARY, + BR_MRP_PORT_ROLE_NONE, +}; + +#endif -- 2.17.1
Horatiu Vultur
2020-Jan-24 16:18 UTC
[Bridge] [RFC net-next v3 02/10] net: bridge: mrp: Expose function br_mrp_port_open
In case the HW is capable to detect when the MRP ring is open or closed. It is expected that the network driver will notify the bridge that the ring is open or closed. The function br_mrp_port_open is used to notify the kernel that one of the ports stopped receiving MRP_Test frames. The argument 'loc' has a value of '1' when the port stopped receiving MRP_Test and '0' when it started to receive MRP_Test. This notification will eventually reach the userspace via netlink interface and the userspace application will process this information. Signed-off-by: Horatiu Vultur <horatiu.vultur at microchip.com> --- include/linux/mrp_bridge.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 include/linux/mrp_bridge.h diff --git a/include/linux/mrp_bridge.h b/include/linux/mrp_bridge.h new file mode 100644 index 000000000000..0508bbe6ecdb --- /dev/null +++ b/include/linux/mrp_bridge.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _LINUX_MRP_BRIDGE_H +#define _LINUX_MRO_BRIDGE_H + +#include <linux/netdevice.h> + +/* The drivers are responsible to call this function when it detects that the + * MRP port stopped receiving MRP_Test frames or it started to receive MRP_Test. + * The argument dev represents the port and loc(Lost of Continuity) has a value + * of 1 when it stopped receiving MRP_Test frames and a value of 0 when it + * started to receive frames. + * + * This eventually eventually notify the userspace which is required to react on + * thise changes. + */ + +#ifdef CONFIG_BRIDGE_MRP +void br_mrp_port_open(struct net_device *dev, u8 loc); +#else +inline void br_mrp_port_open(struct net_device *dev, u8 loc) {} +#endif + +#endif + -- 2.17.1
Horatiu Vultur
2020-Jan-24 16:18 UTC
[Bridge] [RFC net-next v3 03/10] net: bridge: mrp: Add MRP interface used by netlink
Define the MRP interface that will be used by the generic netlink to offload the calls to the HW. For this it is required for the kernel to hold in a list all the MRP instances that are created and all the ports that are part of the MRP rings. Therefore add the structure 'br_mrp'. This contains the following: - a list with all MRP instances - pointer to the net bridge on which the MRP instance is attach to - pointers to the net bridge ports, which represents the ring ports - a ring nr which represents the ID of the ring. The interface has the following functions: br_mrp_add - it creates a br_mrp instances and adds it to the list of mrp instances. br_mrp_del - it removes a br_mrp instances from the list based on the ring nr of the instance. These functions are used just by the SW to know which rings and which ports are to which ring. These function will not call eventually the switchdev API. If there is a HW required to know about these calls then the switchdev API can be extended. br_mrp_add_port - adds a port to a MRP instance. This will eventually call the switchdev API to notify the HW that the port is part of a ring so it needs to process or forward MRP frames on the other port. br_mrp_del_port - deletes a port from a MRP instance. This will eventually call switchdev API to notify the HW that the port is not part of a ring anymore. So it would not need to do special processing to MRP frames Whenever a port is added to the MRP instance, the also the SW needs to know this information in case the HW can't support MRP. This information is required when the SW bridge receives MRP frames. Because in case a frame arrived on an MRP port the SW bridge should not forward the frame. br_mrp_port_state - changes the port state. The port can be in forwarding state, which means that the frames can pass through or in blocked state which means that the frames can't pass through except MRP frames. This will eventually call the switchdev API to notify the HW. This information is used also by the SW bridge to know how to forward frames in case the HW doesn't have this capability. br_mrp_port_role - a port role can be primary or secondary. This information is required to be pushed to HW in case the HW can generate MRP_Test frames. Because the MRP_Test frames contains a file with this information. Otherwise the HW will not be able to generate the frames correctly. br_mrp_ring_state - a ring can be in state open or closed. State open means that the mrp port stopped receiving MRP_Test frames, while closed means that the mrp port received MRP_Test frames. Similar with br_mrp_port_role, this information is pushed in HW because the MRP_Test frames contain this information. For all the previous commands the userspace doesn't need to check the return value because it is not affected if the HW supports these or not. br_mrp_ring_role - a ring can have the following roles MRM or MRC. For the role MRM it is expected that the HW can terminate the MRP frames, notify the SW that it stopped receiving MRP_Test frames and trapp all the other MRP frames. While for MRC mode it is expected that the HW can forward the MRP frames only between the MRP ports and copy MRP_Topology frames to CPU. In case the HW doesn't support a role it needs to return an error code different than -EOPNOTSUPP. Because the userspace doesn't know if the kernel has HW offload capabilities it is using the return value of the netlink calls to know if there was a problem setting the role to the HW, or it should run the role in userspace. For example if the node doesn't have a switchdev driver than the return code is -EOPNOTSUPP that means that the state machine can run only in SW. If the node has switchdev support then, if the node doesn't support the role it needs to return an error code different than -EOPNOTSUPP. In this case the entire userspace application will stop. If the node support the role then it returns 0. br_mrp_start_test - this starts/stops the generation of MRP_Test frames. To stop the generation of frames the interval needs to have a value of 0. In this case the userspace needs to know if the HW supports this or not. Not to have duplicate frames(generated by HW and SW). Because if the HW supports this then the SW will not generate anymore frames and will expect that the HW will notify when it stopped receiving MRP frames using the function br_mrp_port_open. br_mrp_flush - will flush the FDB. br_mrp_port_open - this function is used by drivers to notify the userspace via a netlink callback that one of the ports stopped receiving MRP_Test frames. This function is called only when the node has the role MRM. It is not supposed to be called from userspace. Signed-off-by: Horatiu Vultur <horatiu.vultur at microchip.com> --- net/bridge/br_private_mrp.h | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 net/bridge/br_private_mrp.h diff --git a/net/bridge/br_private_mrp.h b/net/bridge/br_private_mrp.h new file mode 100644 index 000000000000..bea4ece4411c --- /dev/null +++ b/net/bridge/br_private_mrp.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _BR_PRIVATE_MRP_H_ +#define _BR_PRIVATE_MRP_H_ + +#include "br_private.h" +#include <uapi/linux/mrp_bridge.h> + +struct br_mrp { + /* list of mrp instances */ + struct list_head list; + + struct net_bridge *br; + struct net_bridge_port *p_port; + struct net_bridge_port *s_port; + + u32 ring_nr; +}; + +/* br_mrp.c */ +int br_mrp_add(struct net_bridge *br, u32 ring_nr); +int br_mrp_add_port(struct net_bridge *br, u32 ring_nr, + struct net_bridge_port *p); +int br_mrp_del(struct net_bridge *br, u32 ring_nr); +int br_mrp_del_port(struct net_bridge_port *p); +int br_mrp_set_port_state(struct net_bridge_port *p, + enum br_mrp_port_state_type state); +int br_mrp_set_port_role(struct net_bridge_port *p, u32 ring_nr, + enum br_mrp_port_role_type role); +int br_mrp_set_ring_state(struct net_bridge *br, u32 ring_nr, + enum br_mrp_ring_state_type state); +int br_mrp_set_ring_role(struct net_bridge *br, u32 ring_nr, + enum br_mrp_ring_role_type role); +int br_mrp_start_test(struct net_bridge *br, u32 ring_nr, u32 interval, + u8 max_miss); +int br_mrp_flush(struct net_bridge *br, u32 ring_nr); + +/* br_mrp_netlink.c */ +void br_mrp_port_open(struct net_device *dev, u8 loc); + +#endif /* _BR_PRIVATE_MRP_H */ + -- 2.17.1
Horatiu Vultur
2020-Jan-24 16:18 UTC
[Bridge] [RFC net-next v3 04/10] net: bridge: mrp: Add generic netlink interface to configure MRP
Implement the generic netlink interface to configure MRP. The implementation will do sanity checks over the attributes and then eventually call the MRP interface which eventually will call the switchdev API. Signed-off-by: Horatiu Vultur <horatiu.vultur at microchip.com> --- net/bridge/br_mrp_netlink.c | 655 ++++++++++++++++++++++++++++++++++++ 1 file changed, 655 insertions(+) create mode 100644 net/bridge/br_mrp_netlink.c diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c new file mode 100644 index 000000000000..cb676387e89b --- /dev/null +++ b/net/bridge/br_mrp_netlink.c @@ -0,0 +1,655 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <net/genetlink.h> + +#include <uapi/linux/mrp_bridge.h> +#include "br_private.h" +#include "br_private_mrp.h" + +static struct genl_family br_mrp_genl_family; + +static struct nla_policy br_mrp_genl_policy[BR_MRP_ATTR_END] = { + [BR_MRP_ATTR_NONE] = { .type = NLA_UNSPEC }, + [BR_MRP_ATTR_BR_IFINDEX] = { .type = NLA_U32 }, + [BR_MRP_ATTR_PORT_IFINDEX] = { .type = NLA_U32 }, + [BR_MRP_ATTR_RING_NR] = { .type = NLA_U32 }, + [BR_MRP_ATTR_RING_ROLE] = { .type = NLA_U32 }, + [BR_MRP_ATTR_RING_STATE] = { .type = NLA_U32 }, + [BR_MRP_ATTR_PORT_STATE] = { .type = NLA_U32 }, + [BR_MRP_ATTR_PORT_ROLE] = { .type = NLA_U32 }, + [BR_MRP_ATTR_TEST_INTERVAL] = { .type = NLA_U32 }, + [BR_MRP_ATTR_TEST_MAX_MISS] = { .type = NLA_U8 }, + [BR_MRP_ATTR_RING_OPEN] = { .type = NLA_U8 }, +}; + +enum br_mrp_multicast_groups { + BR_MRP_MCGRP_OPEN, +}; + +static const struct genl_multicast_group br_mrp_mcgrps[] = { + [BR_MRP_MCGRP_OPEN] = { .name = "open", }, +}; + +static int br_mrp_genl_add(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev_br; + struct net_bridge *br; + int err = 0; + + if (!info->attrs[BR_MRP_ATTR_BR_IFINDEX]) { + pr_err("ATTR_BR_IFINDEX is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_RING_NR]) { + pr_err("ATTR_RING_NR is missing\n"); + return -EINVAL; + } + + rtnl_lock(); + + dev_br = __dev_get_by_index(net, + nla_get_u32(info->attrs[BR_MRP_ATTR_BR_IFINDEX])); + if (!dev_br) { + pr_err("Invalid ATTR_BR_IFINDEX\n"); + err = -EINVAL; + goto invalid_info; + } + + if (!(dev_br->priv_flags & IFF_EBRIDGE)) { + pr_err("Port is not a bridge\n"); + err = -EINVAL; + goto invalid_info; + } + br = netdev_priv(dev_br); + + err = br_mrp_add(br, nla_get_u32(info->attrs[BR_MRP_ATTR_RING_NR])); + +invalid_info: + rtnl_unlock(); + + return err; +} + +static int br_mrp_genl_add_port(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev_br, *dev; + struct net_bridge_port *port; + struct net_bridge *br; + int err = 0; + + if (!info->attrs[BR_MRP_ATTR_BR_IFINDEX]) { + pr_err("ATTR_BR_IFINDEX is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_RING_NR]) { + pr_err("ATTR_RING_NR is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_PORT_IFINDEX]) { + pr_err("ATTR_PORT_IFINDEX is missing\n"); + return -EINVAL; + } + + rtnl_lock(); + + dev_br = __dev_get_by_index(net, + nla_get_u32(info->attrs[BR_MRP_ATTR_BR_IFINDEX])); + if (!dev_br) { + pr_err("Invalid ATTR_BR_IFINDEX\n"); + err = -EINVAL; + goto invalid_info; + } + + if (!(dev_br->priv_flags & IFF_EBRIDGE)) { + pr_err("Port is not a bridge\n"); + err = -EINVAL; + goto invalid_info; + } + br = netdev_priv(dev_br); + + dev = __dev_get_by_index(net, + nla_get_u32(info->attrs[BR_MRP_ATTR_PORT_IFINDEX])); + if (!dev) { + pr_err("Invalid ATTR_PORT_IFIINDEX\n"); + err = -EINVAL; + goto invalid_info; + } + if (!(dev->priv_flags & IFF_BRIDGE_PORT)) { + pr_err("ATTR_PORT_IFINDEX is not a bridge port"); + err = -EINVAL; + goto invalid_info; + } + + port = br_port_get_rtnl(dev); + if (port->br != br) { + pr_err("Port is not under the bridge\n"); + err = -EINVAL; + goto invalid_info; + } + + err = br_mrp_add_port(br, nla_get_u32(info->attrs[BR_MRP_ATTR_RING_NR]), + port); + +invalid_info: + rtnl_unlock(); + + return err; +} + +static int br_mrp_genl_del(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev_br; + struct net_bridge *br; + int err = 0; + + if (!info->attrs[BR_MRP_ATTR_BR_IFINDEX]) { + pr_err("ATTR_BR_IFINDEX is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_RING_NR]) { + pr_err("ATTR_RING_NR is missing\n"); + return -EINVAL; + } + + rtnl_lock(); + + dev_br = __dev_get_by_index(net, + nla_get_u32(info->attrs[BR_MRP_ATTR_BR_IFINDEX])); + if (!dev_br) { + pr_err("Invalid ATTR_BR_IFINDEX\n"); + err = -EINVAL; + goto invalid_info; + } + + if (!(dev_br->priv_flags & IFF_EBRIDGE)) { + pr_err("Port is not a bridge\n"); + err = -EINVAL; + goto invalid_info; + } + br = netdev_priv(dev_br); + + err = br_mrp_del(br, nla_get_u32(info->attrs[BR_MRP_ATTR_RING_NR])); + +invalid_info: + rtnl_unlock(); + + return err; +} + +static int br_mrp_genl_del_port(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + struct net_bridge_port *port; + struct net_device *dev; + int err = 0; + + if (!info->attrs[BR_MRP_ATTR_PORT_IFINDEX]) { + pr_err("ATTR_PORT_IFINDEX is missing\n"); + return -EINVAL; + } + + rtnl_lock(); + + dev = __dev_get_by_index(net, + nla_get_u32(info->attrs[BR_MRP_ATTR_PORT_IFINDEX])); + if (!dev) { + pr_err("Invalid ATTR_PORT_IFIINDEX\n"); + err = -EINVAL; + goto invalid_info; + } + + if (!(dev->priv_flags & IFF_BRIDGE_PORT)) { + pr_err("ATTR_PORT_IFINDEX is not a bridge port"); + err = -EINVAL; + goto invalid_info; + } + + port = br_port_get_rtnl(dev); + + err = br_mrp_del_port(port); + +invalid_info: + rtnl_unlock(); + + return err; +} + +static int br_mrp_genl_set_port_state(struct sk_buff *skb, + struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + enum br_mrp_port_state_type state; + struct net_bridge_port *port; + struct net_device *dev; + int err = 0; + + if (!info->attrs[BR_MRP_ATTR_PORT_IFINDEX]) { + pr_err("ATTR_PORT_IFINDEX is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_PORT_STATE]) { + pr_err("ATTR_PORT_STATE is missing\n"); + return -EINVAL; + } + + rtnl_lock(); + + dev = __dev_get_by_index(net, + nla_get_u32(info->attrs[BR_MRP_ATTR_PORT_IFINDEX])); + if (!dev) { + pr_err("Invalid ATTR_PORT_IFIINDEX\n"); + err = -EINVAL; + goto invalid_info; + } + + if (!(dev->priv_flags & IFF_BRIDGE_PORT)) { + pr_err("ATTR_PORT_IFINDEX is not a bridge port"); + err = -EINVAL; + goto invalid_info; + } + + port = br_port_get_rtnl(dev); + state = nla_get_u32(info->attrs[BR_MRP_ATTR_PORT_STATE]); + + err = br_mrp_set_port_state(port, state); + +invalid_info: + rtnl_unlock(); + + return err; +} + +static int br_mrp_genl_set_port_role(struct sk_buff *skb, + struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + enum br_mrp_port_role_type role; + struct net_bridge_port *port; + struct net_device *dev; + u32 ring_nr; + int err = 0; + + if (!info->attrs[BR_MRP_ATTR_PORT_IFINDEX]) { + pr_err("ATTR_PORT_IFINDEX is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_PORT_ROLE]) { + pr_err("ATTR_PORT_STATE is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_RING_NR]) { + pr_err("ATTR_RING_NR is missing\n"); + return -EINVAL; + } + + rtnl_lock(); + + dev = __dev_get_by_index(net, + nla_get_u32(info->attrs[BR_MRP_ATTR_PORT_IFINDEX])); + if (!dev) { + pr_err("Invalid ATTR_PORT_IFIINDEX\n"); + err = -EINVAL; + goto invalid_info; + } + + if (!(dev->priv_flags & IFF_BRIDGE_PORT)) { + pr_err("ATTR_PORT_IFINDEX is not a bridge port"); + err = -EINVAL; + goto invalid_info; + } + + port = br_port_get_rtnl(dev); + role = nla_get_u32(info->attrs[BR_MRP_ATTR_PORT_ROLE]); + ring_nr = nla_get_u32(info->attrs[BR_MRP_ATTR_RING_NR]); + + err = br_mrp_set_port_role(port, ring_nr, role); + +invalid_info: + rtnl_unlock(); + + return err; +} + +static int br_mrp_genl_set_ring_state(struct sk_buff *skb, + struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + enum br_mrp_ring_state_type state; + struct net_device *dev_br; + struct net_bridge *br; + int err = 0; + + if (!info->attrs[BR_MRP_ATTR_BR_IFINDEX]) { + pr_err("ATTR_BR_IFINDEX is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_RING_NR]) { + pr_err("ATTR_RING_NR is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_RING_STATE]) { + pr_err("ATTR_RING_STATE is missing\n"); + return -EINVAL; + } + + rtnl_lock(); + + dev_br = __dev_get_by_index(net, + nla_get_u32(info->attrs[BR_MRP_ATTR_BR_IFINDEX])); + if (!dev_br) { + pr_err("Invalid ATTR_BR_IFINDEX\n"); + err = -EINVAL; + goto invalid_info; + } + + if (!(dev_br->priv_flags & IFF_EBRIDGE)) { + pr_err("Port is not a bridge\n"); + err = -EINVAL; + goto invalid_info; + } + br = netdev_priv(dev_br); + + state = nla_get_u32(info->attrs[BR_MRP_ATTR_RING_STATE]); + + err = br_mrp_set_ring_state(br, + nla_get_u32(info->attrs[BR_MRP_ATTR_RING_NR]), + state); + +invalid_info: + rtnl_unlock(); + + return err; +} + +static int br_mrp_genl_set_ring_role(struct sk_buff *skb, + struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + enum br_mrp_ring_role_type role; + struct net_device *dev_br; + struct net_bridge *br; + int err = 0; + + if (!info->attrs[BR_MRP_ATTR_BR_IFINDEX]) { + pr_err("ATTR_BR_IFINDEX is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_RING_NR]) { + pr_err("ATTR_RING_NR is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_RING_ROLE]) { + pr_err("ATTR_RING_ROLE is missing\n"); + return -EINVAL; + } + + rtnl_lock(); + + dev_br = __dev_get_by_index(net, + nla_get_u32(info->attrs[BR_MRP_ATTR_BR_IFINDEX])); + if (!dev_br) { + pr_err("Invalid ATTR_BR_IFINDEX\n"); + err = -EINVAL; + goto invalid_info; + } + + if (!(dev_br->priv_flags & IFF_EBRIDGE)) { + pr_err("Port is not a bridge\n"); + err = -EINVAL; + goto invalid_info; + } + br = netdev_priv(dev_br); + + role = nla_get_u32(info->attrs[BR_MRP_ATTR_RING_ROLE]); + + err = br_mrp_set_ring_role(br, + nla_get_u32(info->attrs[BR_MRP_ATTR_RING_NR]), + role); + +invalid_info: + rtnl_unlock(); + + return err; +} + +static int br_mrp_genl_start_test(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev_br; + struct net_bridge *br; + u32 interval; + int err = 0; + u8 max_miss; + + if (!info->attrs[BR_MRP_ATTR_BR_IFINDEX]) { + pr_err("ATTR_BR_IFINDEX is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_RING_NR]) { + pr_err("ATTR_RING_NR is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_TEST_INTERVAL]) { + pr_err("ATTR_TEST_INTERVAL is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_TEST_MAX_MISS]) { + pr_err("ATTR_TEST_MAX_MISS is missing\n"); + return -EINVAL; + } + + rtnl_lock(); + + dev_br = __dev_get_by_index(net, + nla_get_u32(info->attrs[BR_MRP_ATTR_BR_IFINDEX])); + if (!dev_br) { + pr_err("Invalid ATTR_BR_IFINDEX\n"); + err = -EINVAL; + goto invalid_info; + } + + if (!(dev_br->priv_flags & IFF_EBRIDGE)) { + pr_err("Port is not a bridge\n"); + err = -EINVAL; + goto invalid_info; + } + br = netdev_priv(dev_br); + + interval = nla_get_u32(info->attrs[BR_MRP_ATTR_TEST_INTERVAL]); + max_miss = nla_get_u8(info->attrs[BR_MRP_ATTR_TEST_MAX_MISS]); + + err = br_mrp_start_test(br, nla_get_u32(info->attrs[BR_MRP_ATTR_RING_NR]), + interval, max_miss); + +invalid_info: + rtnl_unlock(); + + return err; +} + +static int br_mrp_genl_flush(struct sk_buff *skb, struct genl_info *info) +{ + struct net *net = sock_net(skb->sk); + struct net_device *dev_br; + struct net_bridge *br; + int err = 0; + + if (!info->attrs[BR_MRP_ATTR_BR_IFINDEX]) { + pr_err("ATTR_BR_IFINDEX is missing\n"); + return -EINVAL; + } + + if (!info->attrs[BR_MRP_ATTR_RING_NR]) { + pr_err("ATTR_RING_NR is missing\n"); + return -EINVAL; + } + + rtnl_lock(); + + dev_br = __dev_get_by_index(net, + nla_get_u32(info->attrs[BR_MRP_ATTR_BR_IFINDEX])); + if (!dev_br) { + pr_err("Invalid ATTR_BR_IFINDEX\n"); + err = -EINVAL; + goto invalid_info; + } + + if (!(dev_br->priv_flags & IFF_EBRIDGE)) { + pr_err("Port is not a bridge\n"); + err = -EINVAL; + goto invalid_info; + } + br = netdev_priv(dev_br); + + err = br_mrp_flush(br, nla_get_u32(info->attrs[BR_MRP_ATTR_RING_NR])); + +invalid_info: + rtnl_unlock(); + + return err; +} + +void br_mrp_port_open(struct net_device *dev, u8 loc) +{ + struct sk_buff *msg; + void *hdr; + + msg = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!msg) { + pr_err("Allocate netlink msg failed\n"); + return; + } + + hdr = genlmsg_put(msg, 0, 0, &br_mrp_genl_family, 0, + BR_MRP_GENL_RING_OPEN); + if (!hdr) { + pr_err("Create msg hdr failed \n"); + goto err_msg_free; + } + + if (nla_put_u32(msg, BR_MRP_ATTR_PORT_IFINDEX, dev->ifindex) || + nla_put_u8(msg, BR_MRP_ATTR_RING_OPEN, loc)) { + pr_err("Failed nla_put\n"); + goto nla_put_failure; + } + + genlmsg_end(msg, hdr); + + genlmsg_multicast(&br_mrp_genl_family, msg, 0, 0, GFP_ATOMIC); + return; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + +err_msg_free: + nlmsg_free(msg); +} +EXPORT_SYMBOL(br_mrp_port_open); + +static struct genl_ops br_mrp_genl_ops[] = { + { + .cmd = BR_MRP_GENL_ADD, + .doit = br_mrp_genl_add, + .validate = GENL_DONT_VALIDATE_STRICT, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = BR_MRP_GENL_ADD_PORT, + .doit = br_mrp_genl_add_port, + .validate = GENL_DONT_VALIDATE_STRICT, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = BR_MRP_GENL_DEL, + .doit = br_mrp_genl_del, + .validate = GENL_DONT_VALIDATE_STRICT, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = BR_MRP_GENL_DEL_PORT, + .doit = br_mrp_genl_del_port, + .validate = GENL_DONT_VALIDATE_STRICT, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = BR_MRP_GENL_SET_PORT_STATE, + .doit = br_mrp_genl_set_port_state, + .validate = GENL_DONT_VALIDATE_STRICT, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = BR_MRP_GENL_SET_PORT_ROLE, + .doit = br_mrp_genl_set_port_role, + .validate = GENL_DONT_VALIDATE_STRICT, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = BR_MRP_GENL_SET_RING_STATE, + .doit = br_mrp_genl_set_ring_state, + .validate = GENL_DONT_VALIDATE_STRICT, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = BR_MRP_GENL_SET_RING_ROLE, + .doit = br_mrp_genl_set_ring_role, + .validate = GENL_DONT_VALIDATE_STRICT, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = BR_MRP_GENL_START_TEST, + .doit = br_mrp_genl_start_test, + .validate = GENL_DONT_VALIDATE_STRICT, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = BR_MRP_GENL_FLUSH, + .doit = br_mrp_genl_flush, + .validate = GENL_DONT_VALIDATE_STRICT, + .flags = GENL_ADMIN_PERM, + }, +}; + +static struct genl_family br_mrp_genl_family = { + .name = "br_mrp_netlink", + .hdrsize = 0, + .version = 1, + .maxattr = BR_MRP_ATTR_MAX, + .policy = br_mrp_genl_policy, + .ops = br_mrp_genl_ops, + .n_ops = ARRAY_SIZE(br_mrp_genl_ops), + .mcgrps = br_mrp_mcgrps, + .n_mcgrps = ARRAY_SIZE(br_mrp_mcgrps), +}; + +int br_mrp_netlink_init(void) +{ + int err; + + err = genl_register_family(&br_mrp_genl_family); + if (err) + pr_err("genl_register_family failed\n"); + + return err; +} + +void br_mrp_netlink_uninit(void) +{ + genl_unregister_family(&br_mrp_genl_family); +} -- 2.17.1
Horatiu Vultur
2020-Jan-24 16:18 UTC
[Bridge] [RFC net-next v3 05/10] net: bridge: mrp: Update MRP interface to add switchdev support
Extend the MRP interface to allow switchdev support. The following functions are added: br_mrp_port_switchdev_add - this corresponds to the function br_mrp_add_port, and will notify the HW that a port is added to a MRP ring. The function gets as parameter the port and the ID of the ring. br_mrp_port_switchdev_del - this corresponds to the function br_mrp_del_port and will notify the HW that a port is removed from a MRP ring. The function gets as parameter the port and the ID of the ring. br_mrp_port_switchdev_set_state - this corresponds to the function br_mrp_port_state. It would notify the HW if it should block or not non-MRP frames. br_mrp_port_switchdev_set_port - this corresponds to the function br_mrp_port_role. It would set the port role, primary or secondary. br_mrp_switchdev_set_role - this corresponds to the function br_mrp_ring_role and would set one of the role MRM or MRC. br_mrp_switchdev_set_ring_state - this corresponds to the function br_mrp_ring_state and would set the ring to be open or closed. br_mrp_switchdev_send_ring_test - this corresponds to the function br_mrp_start_test. This will notify the HW to start or stop generating MRP_Test frames. Value 0 for the interval parameter means to stop generating the frames. Signed-off-by: Horatiu Vultur <horatiu.vultur at microchip.com> --- net/bridge/br_private_mrp.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/net/bridge/br_private_mrp.h b/net/bridge/br_private_mrp.h index bea4ece4411c..de5ba7f730f6 100644 --- a/net/bridge/br_private_mrp.h +++ b/net/bridge/br_private_mrp.h @@ -35,6 +35,22 @@ int br_mrp_start_test(struct net_bridge *br, u32 ring_nr, u32 interval, u8 max_miss); int br_mrp_flush(struct net_bridge *br, u32 ring_nr); +/* br_mrp_switchdev.c */ +int br_mrp_port_switchdev_add(struct net_bridge_port *p, u32 ring_nr); +int br_mrp_port_switchdev_del(struct net_bridge_port *p, u32 ring_nr); +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_ring_role(struct br_mrp *mrp, + enum br_mrp_ring_role_type role); +int br_mrp_switchdev_set_ring_state(struct br_mrp *mrp, + enum br_mrp_ring_state_type state); + +int br_mrp_switchdev_send_ring_test(struct br_mrp *mrp, u32 interval, + u8 max_miss); + /* br_mrp_netlink.c */ void br_mrp_port_open(struct net_device *dev, u8 loc); -- 2.17.1
Horatiu Vultur
2020-Jan-24 16:18 UTC
[Bridge] [RFC net-next v3 06/10] net: bridge: mrp: switchdev: Extend switchdev API to offload MRP
Extend switchdev API to add support for MRP. The HW is notified in following cases: SWITCHDEV_OBJ_ID_PORT_MRP: This is used when a port is added/removed from the mrp ring. SWITCHDEV_OBJ_ID_RING_ROLE_MRP: This is used when the role of the node changes. The current supported roles are Media Redundancy Manager and Media Redundancy Client. SWITCHDEV_OBJ_ID_RING_TEST_MRP: This is used when to start/stop sending MRP_Test frames on the mrp ring ports. This is called only on nodes that have the role Media Redundancy Manager. SWITCHDEV_ATTR_ID_MRP_PORT_STATE: This is used when the port's state is changed. It can be in blocking/forwarding mode. SWITCHDEV_ATTR_ID_MRP_PORT_ROLE: This is used when port's role changes. The roles of the port can be primary/secondary. This is required to notify HW because the MRP_Test frame contains the field MRP_PortRole that contains this information. SWITCHDEV_ATTR_ID_MRP_RING_STATE: This is used when the ring changes it states to open or closed. This is required to notify HW because the MRP_Test frame contains the field MRP_InState which contains this information. Signed-off-by: Horatiu Vultur <horatiu.vultur at microchip.com> --- include/net/switchdev.h | 51 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/include/net/switchdev.h b/include/net/switchdev.h index aee86a189432..b1ed170fc01d 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -40,6 +40,11 @@ enum switchdev_attr_id { SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING, SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED, SWITCHDEV_ATTR_ID_BRIDGE_MROUTER, +#ifdef CONFIG_BRIDGE_MRP + SWITCHDEV_ATTR_ID_MRP_PORT_STATE, + SWITCHDEV_ATTR_ID_MRP_PORT_ROLE, + SWITCHDEV_ATTR_ID_MRP_RING_STATE, +#endif }; struct switchdev_attr { @@ -55,6 +60,11 @@ struct switchdev_attr { clock_t ageing_time; /* BRIDGE_AGEING_TIME */ bool vlan_filtering; /* BRIDGE_VLAN_FILTERING */ bool mc_disabled; /* MC_DISABLED */ +#ifdef CONFIG_BRIDGE_MRP + u8 mrp_port_state; /* MRP_PORT_STATE */ + u8 mrp_port_role; /* MRP_PORT_ROLE */ + u8 mrp_ring_state; /* MRP_RING_STATE */ +#endif } u; }; @@ -63,6 +73,11 @@ enum switchdev_obj_id { SWITCHDEV_OBJ_ID_PORT_VLAN, SWITCHDEV_OBJ_ID_PORT_MDB, SWITCHDEV_OBJ_ID_HOST_MDB, +#ifdef CONFIG_BRIDGE_MRP + SWITCHDEV_OBJ_ID_PORT_MRP, + SWITCHDEV_OBJ_ID_RING_TEST_MRP, + SWITCHDEV_OBJ_ID_RING_ROLE_MRP, +#endif }; struct switchdev_obj { @@ -94,6 +109,42 @@ struct switchdev_obj_port_mdb { #define SWITCHDEV_OBJ_PORT_MDB(OBJ) \ container_of((OBJ), struct switchdev_obj_port_mdb, obj) + +#ifdef CONFIG_BRIDGE_MRP +/* SWITCHDEV_OBJ_ID_PORT_MRP */ +struct switchdev_obj_port_mrp { + struct switchdev_obj obj; + struct net_device *port; + u32 ring_nr; +}; + +#define SWITCHDEV_OBJ_PORT_MRP(OBJ) \ + container_of((OBJ), struct switchdev_obj_port_mrp, obj) + +/* SWITCHDEV_OBJ_ID_RING_TEST_MRP */ +struct switchdev_obj_ring_test_mrp { + struct switchdev_obj obj; + /* The value is in us and a value of 0 represents to stop */ + u32 interval; + u8 max_miss; + u32 ring_nr; +}; + +#define SWITCHDEV_OBJ_RING_TEST_MRP(OBJ) \ + container_of((OBJ), struct switchdev_obj_ring_test_mrp, obj) + +/* SWICHDEV_OBJ_ID_RING_ROLE_MRP */ +struct switchdev_obj_ring_role_mrp { + struct switchdev_obj obj; + u8 ring_role; + u32 ring_nr; +}; + +#define SWITCHDEV_OBJ_RING_ROLE_MRP(OBJ) \ + container_of((OBJ), struct switchdev_obj_ring_role_mrp, obj) + +#endif + typedef int switchdev_obj_dump_cb_t(struct switchdev_obj *obj); enum switchdev_notifier_type { -- 2.17.1
Horatiu Vultur
2020-Jan-24 16:18 UTC
[Bridge] [RFC net-next v3 07/10] net: bridge: mrp: switchdev: Implement MRP API for switchdev
Implement the MRP api for switchdev. These functions will just eventually call the switchdev functions: switchdev_port_obj_add/del and switchdev_port_attr_set. Signed-off-by: Horatiu Vultur <horatiu.vultur at microchip.com> --- net/bridge/br_mrp_switchdev.c | 147 ++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 net/bridge/br_mrp_switchdev.c diff --git a/net/bridge/br_mrp_switchdev.c b/net/bridge/br_mrp_switchdev.c new file mode 100644 index 000000000000..2226d98806de --- /dev/null +++ b/net/bridge/br_mrp_switchdev.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <net/switchdev.h> + +#include "br_private_mrp.h" + +int br_mrp_port_switchdev_add(struct net_bridge_port *p, u32 ring_nr) +{ + struct net_bridge *br = p->br; + struct switchdev_obj_port_mrp mrp = { + .obj.orig_dev = br->dev, + .obj.id = SWITCHDEV_OBJ_ID_PORT_MRP, + .port = p->dev, + .ring_nr = ring_nr, + }; + int err = 0; + + err = switchdev_port_obj_add(br->dev, &mrp.obj, NULL); + + if (err && err != -EOPNOTSUPP) + return err; + + return 0; +} + +int br_mrp_switchdev_set_ring_role(struct br_mrp *mrp, + enum br_mrp_ring_role_type role) +{ + struct switchdev_obj_ring_role_mrp mrp_role = { + .obj.orig_dev = mrp->br->dev, + .obj.id = SWITCHDEV_OBJ_ID_RING_ROLE_MRP, + .ring_role = role, + .ring_nr = mrp->ring_nr, + }; + int err = 0; + + pr_info("%s role: %d\n", __func__, role); + + if (role == BR_MRP_RING_ROLE_DISABLED) + err = switchdev_port_obj_del(mrp->br->dev, &mrp_role.obj); + else + err = switchdev_port_obj_add(mrp->br->dev, &mrp_role.obj, NULL); + + if (err && err != -EOPNOTSUPP) + return err; + + return 0; +} + +int br_mrp_switchdev_send_ring_test(struct br_mrp *mrp, u32 interval, + u8 max_miss) +{ + struct switchdev_obj_ring_test_mrp test = { + .obj.orig_dev = mrp->br->dev, + .obj.id = SWITCHDEV_OBJ_ID_RING_TEST_MRP, + .interval = interval, + .max_miss = max_miss, + .ring_nr = mrp->ring_nr, + }; + int err = 0; + + if (interval == 0) + err = switchdev_port_obj_del(mrp->br->dev, &test.obj); + else + err = switchdev_port_obj_add(mrp->br->dev, &test.obj, NULL); + + return err; +} + +int br_mrp_port_switchdev_del(struct net_bridge_port *p, u32 ring_nr) +{ + struct net_bridge *br = p->br; + struct switchdev_obj_port_mrp mrp = { + .obj.orig_dev = br->dev, + .obj.id = SWITCHDEV_OBJ_ID_PORT_MRP, + .port = p->dev, + .ring_nr = ring_nr, + }; + int err = 0; + + err = switchdev_port_obj_del(br->dev, &mrp.obj); + + if (err && err != -EOPNOTSUPP) + return err; + + return 0; +} + +int br_mrp_port_switchdev_set_state(struct net_bridge_port *p, + enum br_mrp_port_state_type state) +{ + struct switchdev_attr attr = { + .orig_dev = p->dev, + .id = SWITCHDEV_ATTR_ID_MRP_PORT_STATE, + .u.mrp_port_state = state, + }; + int err = 0; + + pr_info("%s port: %s, state: %d\n", __func__, p->dev->name, state); + + 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); + + return err; +} + +int br_mrp_port_switchdev_set_role(struct net_bridge_port *p, + enum br_mrp_port_role_type role) +{ + struct switchdev_attr attr = { + .orig_dev = p->dev, + .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; + + return 0; +} + +int br_mrp_switchdev_set_ring_state(struct br_mrp *mrp, + enum br_mrp_ring_state_type state) +{ + struct switchdev_attr attr = { + .id = SWITCHDEV_ATTR_ID_MRP_RING_STATE, + .u.mrp_ring_state = state, + }; + int err = 0; + + attr.orig_dev = mrp->p_port->dev, + err = switchdev_port_attr_set(mrp->p_port->dev, &attr); + if (err && err != -EOPNOTSUPP) + return err; + + attr.orig_dev = mrp->s_port->dev; + err = switchdev_port_attr_set(mrp->s_port->dev, &attr); + if (err && err != -EOPNOTSUPP) + return err; + + return err; +} + -- 2.17.1
Horatiu Vultur
2020-Jan-24 16:18 UTC
[Bridge] [RFC net-next v3 08/10] net: bridge: mrp: Connect MRP api with the switchev API
Implement the MRP api. The functions are just a wrapper over the switchdev API with extra checks regarding the MRP instances and ports. Signed-off-by: Horatiu Vultur <horatiu.vultur at microchip.com> --- net/bridge/br_mrp.c | 193 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 net/bridge/br_mrp.c diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c new file mode 100644 index 000000000000..69e1a3e526d5 --- /dev/null +++ b/net/bridge/br_mrp.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "br_private_mrp.h" + +static struct br_mrp *br_mrp_find_id(struct net_bridge *br, u32 ring_nr) +{ + struct br_mrp *mrp; + + list_for_each_entry(mrp, &br->mrp_list, list) { + if (mrp->ring_nr == ring_nr) + return mrp; + } + + return NULL; +} + +static struct br_mrp *br_mrp_find_port(struct net_bridge *br, + struct net_bridge_port *p) +{ + struct br_mrp *mrp; + + list_for_each_entry(mrp, &br->mrp_list, list) { + if (mrp->p_port == p || mrp->s_port == p) + return mrp; + } + + return NULL; +} + +int br_mrp_add(struct net_bridge *br, u32 ring_nr) +{ + struct br_mrp *mrp; + + /* If the ring exists, it is not possible to create another one with the + * same ring_nr + */ + mrp = br_mrp_find_id(br, ring_nr); + if (mrp) + return -EINVAL; + + mrp = devm_kzalloc(&br->dev->dev, sizeof(struct br_mrp), GFP_KERNEL); + if (!mrp) + return -ENOMEM; + + mrp->br = br; + mrp->p_port = NULL; + mrp->s_port = NULL; + mrp->ring_nr = ring_nr; + + list_add_tail(&mrp->list, &br->mrp_list); + + return 0; +} + +int br_mrp_add_port(struct net_bridge *br, u32 ring_nr, + struct net_bridge_port *p) +{ + struct br_mrp *mrp = br_mrp_find_id(br, ring_nr); + + if (!mrp) + return -EINVAL; + + p->state = BR_STATE_FORWARDING; + p->mrp_aware = true; + + br_mrp_port_switchdev_add(p, mrp->ring_nr); + + return 0; +} + +int br_mrp_del(struct net_bridge *br, u32 ring_nr) +{ + struct br_mrp *mrp = br_mrp_find_id(br, ring_nr); + + if (!mrp) + return -EINVAL; + + /* Stop sending MRP_Test frames */ + br_mrp_switchdev_send_ring_test(mrp, 0, 0); + + /* Destroy the ring */ + mrp->br = NULL; + mrp->p_port = NULL; + mrp->s_port = NULL; + + list_del(&mrp->list); + devm_kfree(&br->dev->dev, mrp); + + return 0; +} + +int br_mrp_del_port(struct net_bridge_port *p) +{ + struct net_bridge *br; + struct br_mrp *mrp; + + br = p->br; + mrp = br_mrp_find_port(br, p); + if (!mrp) + return 0; + + /* Stop sending MRP_Test frames */ + br_mrp_switchdev_send_ring_test(mrp, 0, 0); + + p->state = BR_STATE_FORWARDING; + p->mrp_aware = false; + if (p == mrp->p_port) + mrp->p_port = NULL; + if (p == mrp->s_port) + mrp->s_port = NULL; + + br_mrp_port_switchdev_del(p, mrp->ring_nr); + + return 0; +} + +int br_mrp_set_port_state(struct net_bridge_port *p, + enum br_mrp_port_state_type state) +{ + struct net_bridge *br; + + br = p->br; + if (state == BR_MRP_PORT_STATE_FORWARDING) + p->state = BR_STATE_FORWARDING; + else + p->state = BR_STATE_BLOCKING; + + br_mrp_port_switchdev_set_state(p, state); + + return 0; +} + +int br_mrp_set_port_role(struct net_bridge_port *p, u32 ring_nr, + enum br_mrp_port_role_type role) +{ + struct br_mrp *mrp = br_mrp_find_id(p->br, ring_nr); + + if (!mrp) + return -EINVAL; + + if (role == BR_MRP_PORT_ROLE_PRIMARY) + mrp->p_port = p; + if (role == BR_MRP_PORT_ROLE_SECONDARY) + mrp->s_port = p; + + br_mrp_port_switchdev_set_role(p, role); + + return 0; +} + +int br_mrp_set_ring_state(struct net_bridge *br, u32 ring_nr, + enum br_mrp_ring_state_type state) +{ + struct br_mrp *mrp = br_mrp_find_id(br, ring_nr); + + if (!mrp) + return -EINVAL; + + br_mrp_switchdev_set_ring_state(mrp, state); + + return 0; +} + +int br_mrp_set_ring_role(struct net_bridge *br, u32 ring_nr, + enum br_mrp_ring_role_type role) +{ + struct br_mrp *mrp = br_mrp_find_id(br, ring_nr); + + if (!mrp) + return -EINVAL; + + br_mrp_switchdev_set_ring_role(mrp, role); + + return 0; +} + +int br_mrp_start_test(struct net_bridge *br, u32 ring_nr, u32 interval, + u8 max_miss) +{ + struct br_mrp *mrp = br_mrp_find_id(br, ring_nr); + + if (!mrp) + return -EINVAL; + + return br_mrp_switchdev_send_ring_test(mrp, interval, max_miss); +} + +int br_mrp_flush(struct net_bridge *br, u32 ring_nr) +{ + br_fdb_flush(br); + return 0; +} + -- 2.17.1
Horatiu Vultur
2020-Jan-24 16:18 UTC
[Bridge] [RFC net-next v3 09/10] net: bridge: mrp: Integrate MRP into the bridge
To integrate MRP into the bridge, the bridge needs to do the following: - initialized and destroy the generic netlink used by MRP - detect if the MRP frame was received on a port that is part of a MRP ring. In case it was not, then forward the frame as usual, otherwise redirect the frame to the upper layer. Signed-off-by: Horatiu Vultur <horatiu.vultur at microchip.com> --- net/bridge/br.c | 11 +++++++++++ net/bridge/br_device.c | 3 +++ net/bridge/br_if.c | 6 ++++++ net/bridge/br_input.c | 14 ++++++++++++++ net/bridge/br_private.h | 14 ++++++++++++++ 5 files changed, 48 insertions(+) diff --git a/net/bridge/br.c b/net/bridge/br.c index b6fe30e3768f..d5e556eed4ba 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -344,6 +344,12 @@ static int __init br_init(void) if (err) goto err_out5; +#ifdef CONFIG_BRIDGE_MRP + err = br_mrp_netlink_init(); + if (err) + goto err_out6; +#endif + brioctl_set(br_ioctl_deviceless_stub); #if IS_ENABLED(CONFIG_ATM_LANE) @@ -358,6 +364,11 @@ static int __init br_init(void) return 0; +#ifdef CONFIG_BRIDGE_MRP +err_out6: + br_netlink_fini(); +#endif + err_out5: unregister_switchdev_notifier(&br_switchdev_notifier); err_out4: diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index fb38add21b37..29966754d86a 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -464,6 +464,9 @@ void br_dev_setup(struct net_device *dev) spin_lock_init(&br->lock); INIT_LIST_HEAD(&br->port_list); INIT_HLIST_HEAD(&br->fdb_list); +#ifdef CONFIG_BRIDGE_MRP + INIT_LIST_HEAD(&br->mrp_list); +#endif spin_lock_init(&br->hash_lock); br->bridge_id.prio[0] = 0x80; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 4fe30b182ee7..9b8bb41c0574 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -331,6 +331,9 @@ static void del_nbp(struct net_bridge_port *p) spin_lock_bh(&br->lock); br_stp_disable_port(p); +#ifdef CONFIG_BRIDGE_MRP + p->mrp_aware = false; +#endif spin_unlock_bh(&br->lock); br_ifinfo_notify(RTM_DELLINK, NULL, p); @@ -427,6 +430,9 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, p->port_no = index; p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; br_init_port(p); +#ifdef CONFIG_BRIDGE_MRP + p->mrp_aware = false; +#endif br_set_state(p, BR_STATE_DISABLED); br_stp_port_timer_init(p); err = br_multicast_add_port(p); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 8944ceb47fe9..de7066b077e2 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -21,6 +21,9 @@ #include <linux/rculist.h> #include "br_private.h" #include "br_private_tunnel.h" +#ifdef CONFIG_BRIDGE_MRP +#include "br_private_mrp.h" +#endif static int br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb) @@ -338,6 +341,17 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb) return RX_HANDLER_CONSUMED; } } +#ifdef CONFIG_BRIDGE_MRP + /* If there is no MRP instance do normal forwarding */ + if (!p->mrp_aware) + goto forward; + + if (skb->protocol == htons(ETH_P_MRP)) + return RX_HANDLER_PASS; + + if (p->state == BR_STATE_BLOCKING) + goto drop; +#endif forward: switch (p->state) { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index f540f3bdf294..a5d01a394f54 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -285,6 +285,10 @@ struct net_bridge_port { u16 backup_redirected_cnt; struct bridge_stp_xstats stp_xstats; + +#ifdef CONFIG_BRIDGE_MRP + bool mrp_aware; +#endif }; #define kobj_to_brport(obj) container_of(obj, struct net_bridge_port, kobj) @@ -424,6 +428,10 @@ struct net_bridge { int offload_fwd_mark; #endif struct hlist_head fdb_list; + +#ifdef CONFIG_BRIDGE_MRP + struct list_head mrp_list; +#endif }; struct br_input_skb_cb { @@ -1165,6 +1173,12 @@ unsigned long br_timer_value(const struct timer_list *timer); extern int (*br_fdb_test_addr_hook)(struct net_device *dev, unsigned char *addr); #endif +/* br_mrp.c */ +#ifdef CONFIG_BRIDGE_MRP +int br_mrp_netlink_init(void); +void br_mrp_netlink_uninit(void); +#endif + /* br_netlink.c */ extern struct rtnl_link_ops br_link_ops; int br_netlink_init(void); -- 2.17.1
Horatiu Vultur
2020-Jan-24 16:18 UTC
[Bridge] [RFC net-next v3 10/10] net: bridge: mrp: Update Kconfig and Makefile
Add the option BRIDGE_MRP to allow to build in or not MRP support. The default value is N. Signed-off-by: Horatiu Vultur <horatiu.vultur at microchip.com> --- net/bridge/Kconfig | 12 ++++++++++++ net/bridge/Makefile | 2 ++ 2 files changed, 14 insertions(+) diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig index e4fb050e2078..51a6414145d2 100644 --- a/net/bridge/Kconfig +++ b/net/bridge/Kconfig @@ -61,3 +61,15 @@ config BRIDGE_VLAN_FILTERING Say N to exclude this support and reduce the binary size. If unsure, say Y. + +config BRIDGE_MRP + bool "MRP protocol" + depends on BRIDGE + default n + help + If you say Y here, then the Ethernet bridge will be able to run MRP + protocol to detect loops + + Say N to exclude this support and reduce the binary size. + + If unsure, say N. diff --git a/net/bridge/Makefile b/net/bridge/Makefile index ac9ef337f0fa..b2e11b819af5 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile @@ -25,3 +25,5 @@ bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o br_vlan_tunnel.o bridge-$(CONFIG_NET_SWITCHDEV) += br_switchdev.o obj-$(CONFIG_NETFILTER) += netfilter/ + +bridge-$(CONFIG_BRIDGE_MRP) += br_mrp.o br_mrp_netlink.o br_mrp_switchdev.o -- 2.17.1
Allan W. Nielsen
2020-Jan-24 20:34 UTC
[Bridge] [RFC net-next v3 00/10] net: bridge: mrp: Add support for Media Redundancy Protocol (MRP)
On 24.01.2020 17:18, Horatiu Vultur wrote:>Media Redundancy Protocol is a data network protocol standardized by >International Electrotechnical Commission as IEC 62439-2. It allows rings of >Ethernet switches to overcome any single failure with recovery time faster than >STP. It is primarily used in Industrial Ethernet applications. > >Based on the previous RFC[1][2], the MRP state machine and all the >timers were moved to userspace. A generic netlink interface is added to >allow configuring the HW, and logic added to to implement the MRP >specific forwarding rules. > >The userspace application that is using the new netlink can be found here[3]. > >The current implementation both in kernel and userspace supports only 2 roles: > > MRM - this one is responsible to send MRP_Test and MRP_Topo frames on both > ring ports. It needs to process MRP_Test to know if the ring is open or > closed. This operation is desired to be offloaded to the HW because it > requires to generate and process up to 4000 frames per second. Whenever it > detects that the ring open it sends MRP_Topo frames to notify all MRC about > changes in the topology. MRM needs also to process MRP_LinkChange frames, > these frames are generated by the MRC. When the ring is open the the state > of both ports is to forward frames and when the ring is closed then the > secondary port is blocked. > > MRC - this one is responsible to forward MRP frames between the ring ports. > In case one of the ring ports gets a link down or up, then MRC will generate > a MRP_LinkChange frames. This node should also process MRP_Topo frames and to > clear its FDB when it receives this frame. > > Userspace > Deamon +----------+ Client > + > | > +--------------|-----------------------------------------+ > Kernel | > + Netlink > > | + Interrupt > | | > +--------------|------------------------------|----------+ > HW | Switchdev | > + | > >The user interacts using the client (called 'mrp'), the client talks to the >deamon (called 'mrp_server'), which talks with the kernel using netlink. The >kernel will try to offload the requests to the HW via switchdev API. For this a >new generic netlink interface was added to the bridge. > >If the kernel cannot offload MRP to HW (maybe it does not have a switchdev >driver, or it is just not supported), then all the netlink calls will return >-EOPNOTSUPP. In this case the user-space deamon fallback to SW only >implementation.Horatiu and I have spend a bit of time discussing what you be best here. An alternative to this would be to do the SW fallback in the kernel, instead of user-land. This would mean that the user application does not need to know if the function is offloaded (or partly offloaded) to HW. We went with this approch to make the kernel part as simple as possible. The alternative would still be much simpler than the first version posted - but it would require a bit more. Both options has pros and cons, and we looking forward to the community's view on this.>There are required changes to the SW bridge to be able to run the MRP. First the >bridge needs to initialize the netlink interface. And second it needs to know if >a MRP frame was received on a MRP ring port. In case it was received the SW >bridge should not forward the frame it needs to redirected to upper layes. In >case it was not received on a ring port then it just forwards it as usual. > >To be able to offload this to the HW, it was required to extend the switchdev >API. > >If this will be accepted then in the future the netlink interface can be >expended with multiple attributes which are required by different roles of the >MRP. Like Media Redundancy Automanager(MRA), Media Interconnect Manager(MIM) and >Media Interconnect Client(MIC). > >[1] https://www.spinics.net/lists/netdev/msg623647.html >[2] https://www.spinics.net/lists/netdev/msg624378.html >[3] https://github.com/microchip-ung/mrp/tree/patch-v3 > >Horatiu Vultur (10): > net: bridge: mrp: Expose mrp attributes. > net: bridge: mrp: Expose function br_mrp_port_open > net: bridge: mrp: Add MRP interface used by netlink > net: bridge: mrp: Add generic netlink interface to configure MRP > net: bridge: mrp: Update MRP interface to add switchdev support > net: bridge: mrp: switchdev: Extend switchdev API to offload MRP > net: bridge: mrp: switchdev: Implement MRP API for switchdev > net: bridge: mrp: Connect MRP api with the switchev API > net: bridge: mrp: Integrate MRP into the bridge > net: bridge: mrp: Update Kconfig and Makefile > > include/linux/mrp_bridge.h | 25 ++ > include/net/switchdev.h | 51 +++ > include/uapi/linux/if_ether.h | 1 + > include/uapi/linux/mrp_bridge.h | 118 ++++++ > net/bridge/Kconfig | 12 + > net/bridge/Makefile | 2 + > net/bridge/br.c | 11 + > net/bridge/br_device.c | 3 + > net/bridge/br_if.c | 6 + > net/bridge/br_input.c | 14 + > net/bridge/br_mrp.c | 193 ++++++++++ > net/bridge/br_mrp_netlink.c | 655 ++++++++++++++++++++++++++++++++ > net/bridge/br_mrp_switchdev.c | 147 +++++++ > net/bridge/br_private.h | 14 + > net/bridge/br_private_mrp.h | 58 +++ > 15 files changed, 1310 insertions(+) > create mode 100644 include/linux/mrp_bridge.h > create mode 100644 include/uapi/linux/mrp_bridge.h > create mode 100644 net/bridge/br_mrp.c > create mode 100644 net/bridge/br_mrp_netlink.c > create mode 100644 net/bridge/br_mrp_switchdev.c > create mode 100644 net/bridge/br_private_mrp.h > >-- >2.17.1 >/Allan
Allan W. Nielsen
2020-Feb-18 12:18 UTC
[Bridge] [RFC net-next v3 00/10] net: bridge: mrp: Add support for Media Redundancy Protocol (MRP)
Hi All, Its been a while since posting this serie. We got some good and very specific comments, but there has not been much discussion on the overall architecture. Here is the list of items we have noted to be fixed in the next version: - The headless chicken (it keeps sending test frames if user-space daemon dies) - Avoid loops when bringing up the network - meaning we need to let MRP do its work before the br0 device is set to up, and we need to preserve that state. - Unnessecary ifdef on the include. - Extend the existing mac-table flush instead of adding BR_MRP_GENL_FLUSH - Further optimize the changes in br_handle_frame In v1 & v2 we had the entire protocol implemented in kernel-space. Everybody told us this is a bad idea, and in v3 we have moved as much as possible to user-space, and only kept the HW offload facilites in kernel-space. The protocol is then implemented in user-space. This is nice because it simplifies the code in the kernel and moves it to user-space where such complexity is easier to handle. The downside of this is that it makes the netlink interface more specific to our HW. The way v3 is implemented, the netlink API returns an error if a given operation cannot be HW offloaded. If the netlink calls return Ok, user-space will trust that HW do the offloading as requested, if the netlink calls return an error, it will implement all the functionallity in user-space. This works at-least in 2 scenarios: The HW we have with full MRP offload capabilities, and a pure SW bridge. But we should try make sure this also works in a backwards compatible way with future MRP aware HW, and with existing (and future) SwitchDev offloaded HW. At the very least we want to make this run on Ocelot, HW offload the MRC role, but do the MRM in SW (as the HW is not capable of this). If we use the kernel to abstract the MRP forwarding (not the entire protocol like we did in v1/v2, not just the HW like we did in v3) then we will have more flxibility to support other HW with a different set of offload facilities, we can most likely achieve better performance, and it would be a cleaner design. This will mean, that if user-space ask for MRP frame to be generated, the kernel should make sure it will happen. The kernel can try to offload this via the switchdev API, or it can do it in kernel-space. Again, it will mean putting back some code into kernel space, but I think it is worth it. What do you think, what is the right design. /Allan On 24.01.2020 17:18, Horatiu Vultur wrote:>Media Redundancy Protocol is a data network protocol standardized by >International Electrotechnical Commission as IEC 62439-2. It allows rings of >Ethernet switches to overcome any single failure with recovery time faster than >STP. It is primarily used in Industrial Ethernet applications. > >Based on the previous RFC[1][2], the MRP state machine and all the >timers were moved to userspace. A generic netlink interface is added to >allow configuring the HW, and logic added to to implement the MRP >specific forwarding rules. > >The userspace application that is using the new netlink can be found here[3]. > >The current implementation both in kernel and userspace supports only 2 roles: > > MRM - this one is responsible to send MRP_Test and MRP_Topo frames on both > ring ports. It needs to process MRP_Test to know if the ring is open or > closed. This operation is desired to be offloaded to the HW because it > requires to generate and process up to 4000 frames per second. Whenever it > detects that the ring open it sends MRP_Topo frames to notify all MRC about > changes in the topology. MRM needs also to process MRP_LinkChange frames, > these frames are generated by the MRC. When the ring is open the the state > of both ports is to forward frames and when the ring is closed then the > secondary port is blocked. > > MRC - this one is responsible to forward MRP frames between the ring ports. > In case one of the ring ports gets a link down or up, then MRC will generate > a MRP_LinkChange frames. This node should also process MRP_Topo frames and to > clear its FDB when it receives this frame. > > Userspace > Deamon +----------+ Client > + > | > +--------------|-----------------------------------------+ > Kernel | > + Netlink > > | + Interrupt > | | > +--------------|------------------------------|----------+ > HW | Switchdev | > + | > >The user interacts using the client (called 'mrp'), the client talks to the >deamon (called 'mrp_server'), which talks with the kernel using netlink. The >kernel will try to offload the requests to the HW via switchdev API. For this a >new generic netlink interface was added to the bridge. > >If the kernel cannot offload MRP to HW (maybe it does not have a switchdev >driver, or it is just not supported), then all the netlink calls will return >-EOPNOTSUPP. In this case the user-space deamon fallback to SW only >implementation. > >There are required changes to the SW bridge to be able to run the MRP. First the >bridge needs to initialize the netlink interface. And second it needs to know if >a MRP frame was received on a MRP ring port. In case it was received the SW >bridge should not forward the frame it needs to redirected to upper layes. In >case it was not received on a ring port then it just forwards it as usual. > >To be able to offload this to the HW, it was required to extend the switchdev >API. > >If this will be accepted then in the future the netlink interface can be >expended with multiple attributes which are required by different roles of the >MRP. Like Media Redundancy Automanager(MRA), Media Interconnect Manager(MIM) and >Media Interconnect Client(MIC). > >[1] https://www.spinics.net/lists/netdev/msg623647.html >[2] https://www.spinics.net/lists/netdev/msg624378.html >[3] https://github.com/microchip-ung/mrp/tree/patch-v3 > >Horatiu Vultur (10): > net: bridge: mrp: Expose mrp attributes. > net: bridge: mrp: Expose function br_mrp_port_open > net: bridge: mrp: Add MRP interface used by netlink > net: bridge: mrp: Add generic netlink interface to configure MRP > net: bridge: mrp: Update MRP interface to add switchdev support > net: bridge: mrp: switchdev: Extend switchdev API to offload MRP > net: bridge: mrp: switchdev: Implement MRP API for switchdev > net: bridge: mrp: Connect MRP api with the switchev API > net: bridge: mrp: Integrate MRP into the bridge > net: bridge: mrp: Update Kconfig and Makefile > > include/linux/mrp_bridge.h | 25 ++ > include/net/switchdev.h | 51 +++ > include/uapi/linux/if_ether.h | 1 + > include/uapi/linux/mrp_bridge.h | 118 ++++++ > net/bridge/Kconfig | 12 + > net/bridge/Makefile | 2 + > net/bridge/br.c | 11 + > net/bridge/br_device.c | 3 + > net/bridge/br_if.c | 6 + > net/bridge/br_input.c | 14 + > net/bridge/br_mrp.c | 193 ++++++++++ > net/bridge/br_mrp_netlink.c | 655 ++++++++++++++++++++++++++++++++ > net/bridge/br_mrp_switchdev.c | 147 +++++++ > net/bridge/br_private.h | 14 + > net/bridge/br_private_mrp.h | 58 +++ > 15 files changed, 1310 insertions(+) > create mode 100644 include/linux/mrp_bridge.h > create mode 100644 include/uapi/linux/mrp_bridge.h > create mode 100644 net/bridge/br_mrp.c > create mode 100644 net/bridge/br_mrp_netlink.c > create mode 100644 net/bridge/br_mrp_switchdev.c > create mode 100644 net/bridge/br_private_mrp.h > >-- >2.17.1 >/Allan