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][3][4][5], and patches[6][7], the MRP state
machine and all the timers were moved to userspace, except for the timers used
to generate MRP Test frames. In this way the userspace doesn't know and should
not know if the HW or the kernel will generate the MRP Test frames. The
following changes were added to the bridge to support the MRP:
- the existing netlink interface was extended with MRP support,
- allow to detect when a MRP frame was received on a MRP ring port
- allow MRP instance to forward/terminate MRP frames
- generate MRP Test frames in case the HW doesn't have support for this
To be able to offload MRP support to HW, the switchdev API was extend.
With these changes the userspace doesn't do the following because already the
kernel/HW will do:
- doesn't need to forward/terminate MRP frames
- doesn't need to generate MRP Test frames
- doesn't need to detect when the ring is open/closed.
The userspace application that is using the new netlink can be found here[8].
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 is 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 then 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.
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://www.spinics.net/lists/netdev/msg627500.html
[4] https://www.spinics.net/lists/netdev/msg641005.html
[5] https://www.spinics.net/lists/netdev/msg643991.html
[6] https://www.spinics.net/lists/netdev/msg645378.html
[7] https://www.spinics.net/lists/kernel/msg3484685.html
[8] https://github.com/microchip-ung/mrp/tree/patch-v8
-v3:
- fix unused variables
-v2:
- drop patch 4
- add port flag BR_MRP_LOST_CONT;
- another fix for bisectability
-v1:
- fix bisectability issues
- in case of errors use extack
-RFC v5:
- use nla_parse_nested
- rework the usage of the rcu in br_mrp
- reorder patches
- few other small issues raised by Nikolay
-RFC v4:
- extend existing netlink interface to add mrp support
- use rcu locks
-RFC v3:
- move MRP state machine in userspace
- create generic netlink interface for configuring the HW using switchdev API
-RFC v2:
- extend switchdev API to offload to HW
Horatiu Vultur (11):
bridge: uapi: mrp: Add mrp attributes.
bridge: mrp: Update Kconfig
bridge: mrp: Extend bridge interface
net: bridge: Add port attribute IFLA_BRPORT_MRP_RING_OPEN
bridge: mrp: Add MRP interface.
switchdev: mrp: Extend switchdev API to offload MRP
bridge: switchdev: mrp: Implement MRP API for switchdev
bridge: mrp: Connect MRP API with the switchdev API
bridge: mrp: Implement netlink interface to configure MRP
bridge: mrp: Integrate MRP into the bridge
net: bridge: Add checks for enabling the STP.
include/linux/if_bridge.h | 2 +
include/net/switchdev.h | 62 ++++
include/uapi/linux/if_bridge.h | 42 +++
include/uapi/linux/if_ether.h | 1 +
include/uapi/linux/if_link.h | 1 +
include/uapi/linux/mrp_bridge.h | 84 +++++
net/bridge/Kconfig | 12 +
net/bridge/Makefile | 2 +
net/bridge/br_device.c | 3 +
net/bridge/br_if.c | 2 +
net/bridge/br_input.c | 3 +
net/bridge/br_ioctl.c | 3 +-
net/bridge/br_mrp.c | 556 +++++++++++++++++++++++++++++
net/bridge/br_mrp_netlink.c | 120 +++++++
net/bridge/br_mrp_switchdev.c | 140 ++++++++
net/bridge/br_netlink.c | 12 +-
net/bridge/br_private.h | 38 +-
net/bridge/br_private_mrp.h | 63 ++++
net/bridge/br_stp.c | 6 +
net/bridge/br_stp_if.c | 11 +-
net/bridge/br_sysfs_br.c | 4 +-
tools/include/uapi/linux/if_link.h | 1 +
22 files changed, 1160 insertions(+), 8 deletions(-)
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
To integrate MRP into the bridge, first the bridge needs to be aware of ports
that are part of an MRP ring and which rings are on the bridge.
Therefore extend bridge interface with the following:
- add new flag(BR_MPP_AWARE) to the net bridge ports, this bit will be
set when the port is added to an MRP instance. In this way it knows if
the frame was received on MRP ring port
- add new flag(BR_MRP_LOST_CONT) to the net bridge ports, this bit will be set
when the port lost the continuity of MRP Test frames.
- add a list of MRP instances
Signed-off-by: Horatiu Vultur <[email protected]>
---
include/linux/if_bridge.h | 2 ++
net/bridge/br_private.h | 4 ++++
2 files changed, 6 insertions(+)
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index 9e57c4411734..b3a8d3054af0 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -47,6 +47,8 @@ struct br_ip_list {
#define BR_BCAST_FLOOD BIT(14)
#define BR_NEIGH_SUPPRESS BIT(15)
#define BR_ISOLATED BIT(16)
+#define BR_MRP_AWARE BIT(17)
+#define BR_MRP_LOST_CONT BIT(18)
#define BR_DEFAULT_AGEING_TIME (300 * HZ)
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 1f97703a52ff..835a70f8d3ea 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -428,6 +428,10 @@ struct net_bridge {
int offload_fwd_mark;
#endif
struct hlist_head fdb_list;
+
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+ struct list_head __rcu mrp_list;
+#endif
};
struct br_input_skb_cb {
--
2.17.1
Add new nested netlink attribute to configure the MRP. These attributes are used
by the userspace to add/delete/configure MRP instances and by the kernel to
notify the userspace when the MRP ring gets open/closed. MRP nested attribute
has the following attributes:
IFLA_BRIDGE_MRP_INSTANCE - the parameter type is br_mrp_instance which contains
the instance id, and the ifindex of the two ports. The ports can't be part of
multiple instances. This is used to create/delete MRP instances.
IFLA_BRIDGE_MRP_PORT_STATE - the parameter type is u32. Which can be forwarding,
blocking or disabled.
IFLA_BRIDGE_MRP_PORT_ROLE - the parameter type is br_mrp_port_role which
contains the instance id and the role. The role can be primary or secondary.
IFLA_BRIDGE_MRP_RING_STATE - the parameter type is br_mrp_ring_state which
contains the instance id and the state. The state can be open or closed.
IFLA_BRIDGE_MRP_RING_ROLE - the parameter type is br_mrp_ring_role which
contains the instance id and the ring role. The role can be MRM or MRC.
IFLA_BRIDGE_MRP_START_TEST - the parameter type is br_mrp_start_test which
contains the instance id, the interval at which to send the MRP_Test frames,
how many test frames can be missed before declaring the ring open and the
period which represent for how long to send the test frames.
Also add the file include/uapi/linux/mrp_bridge.h which defines all the types
used by MRP that are also needed by the userpace.
Signed-off-by: Horatiu Vultur <[email protected]>
---
include/uapi/linux/if_bridge.h | 42 +++++++++++++++++
include/uapi/linux/if_ether.h | 1 +
include/uapi/linux/mrp_bridge.h | 84 +++++++++++++++++++++++++++++++++
3 files changed, 127 insertions(+)
create mode 100644 include/uapi/linux/mrp_bridge.h
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index bfe621ea51b3..bd8c95488f16 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -120,6 +120,7 @@ enum {
IFLA_BRIDGE_MODE,
IFLA_BRIDGE_VLAN_INFO,
IFLA_BRIDGE_VLAN_TUNNEL_INFO,
+ IFLA_BRIDGE_MRP,
__IFLA_BRIDGE_MAX,
};
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
@@ -157,6 +158,47 @@ struct bridge_vlan_xstats {
__u32 pad2;
};
+enum {
+ IFLA_BRIDGE_MRP_UNSPEC,
+ IFLA_BRIDGE_MRP_INSTANCE,
+ IFLA_BRIDGE_MRP_PORT_STATE,
+ IFLA_BRIDGE_MRP_PORT_ROLE,
+ IFLA_BRIDGE_MRP_RING_STATE,
+ IFLA_BRIDGE_MRP_RING_ROLE,
+ IFLA_BRIDGE_MRP_START_TEST,
+ __IFLA_BRIDGE_MRP_MAX,
+};
+
+struct br_mrp_instance {
+ __u32 ring_id;
+ __u32 p_ifindex;
+ __u32 s_ifindex;
+};
+
+struct br_mrp_port_role {
+ __u32 ring_id;
+ __u32 role;
+};
+
+struct br_mrp_ring_state {
+ __u32 ring_id;
+ __u32 ring_state;
+};
+
+struct br_mrp_ring_role {
+ __u32 ring_id;
+ __u32 ring_role;
+};
+
+struct br_mrp_start_test {
+ __u32 ring_id;
+ __u32 interval;
+ __u32 max_miss;
+ __u32 period;
+};
+
+#define IFLA_BRIDGE_MRP_MAX (__IFLA_BRIDGE_MRP_MAX - 1)
+
struct bridge_stp_xstats {
__u64 transition_blk;
__u64 transition_fwd;
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..2600cdf5a284
--- /dev/null
+++ b/include/uapi/linux/mrp_bridge.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+
+#ifndef _UAPI_LINUX_MRP_BRIDGE_H_
+#define _UAPI_LINUX_MRP_BRIDGE_H_
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+
+#define MRP_MAX_FRAME_LENGTH 200
+#define MRP_DEFAULT_PRIO 0x8000
+#define MRP_DOMAIN_UUID_LENGTH 16
+#define MRP_VERSION 1
+#define MRP_FRAME_PRIO 7
+
+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,
+};
+
+enum br_mrp_tlv_header_type {
+ BR_MRP_TLV_HEADER_END = 0x0,
+ BR_MRP_TLV_HEADER_COMMON = 0x1,
+ BR_MRP_TLV_HEADER_RING_TEST = 0x2,
+ BR_MRP_TLV_HEADER_RING_TOPO = 0x3,
+ BR_MRP_TLV_HEADER_RING_LINK_DOWN = 0x4,
+ BR_MRP_TLV_HEADER_RING_LINK_UP = 0x5,
+};
+
+struct br_mrp_tlv_hdr {
+ __u8 type;
+ __u8 length;
+};
+
+struct br_mrp_end_hdr {
+ struct br_mrp_tlv_hdr hdr;
+};
+
+struct br_mrp_common_hdr {
+ __u16 seq_id;
+ __u8 domain[MRP_DOMAIN_UUID_LENGTH];
+};
+
+struct br_mrp_ring_test_hdr {
+ __u16 prio;
+ __u8 sa[ETH_ALEN];
+ __u16 port_role;
+ __u16 state;
+ __u16 transitions;
+ __u32 timestamp;
+};
+
+struct br_mrp_ring_topo_hdr {
+ __u16 prio;
+ __u8 sa[ETH_ALEN];
+ __u16 interval;
+};
+
+struct br_mrp_ring_link_hdr {
+ __u8 sa[ETH_ALEN];
+ __u16 port_role;
+ __u16 interval;
+ __u16 blocked;
+};
+
+#endif
--
2.17.1
Implement the MRP API.
In case the HW can't generate MRP Test frames then the SW will try to generate
the frames. In case that also the SW will fail in generating the frames then a
error is return to the userspace. The userspace is responsible to generate all
the other MRP frames regardless if the test frames are generated by HW or SW.
The forwarding/termination of MRP frames is happening in the kernel and is done
by the MRP instance. The userspace application doesn't do the forwarding.
Signed-off-by: Horatiu Vultur <[email protected]>
---
net/bridge/Makefile | 2 +-
net/bridge/br_mrp.c | 556 ++++++++++++++++++++++++++++++++++++
net/bridge/br_mrp_netlink.c | 29 ++
3 files changed, 586 insertions(+), 1 deletion(-)
create mode 100644 net/bridge/br_mrp.c
create mode 100644 net/bridge/br_mrp_netlink.c
diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index 3cacf9dd78d5..ccb394236fbd 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -26,4 +26,4 @@ bridge-$(CONFIG_NET_SWITCHDEV) += br_switchdev.o
obj-$(CONFIG_NETFILTER) += netfilter/
-bridge-$(CONFIG_BRIDGE_MRP) += br_mrp_switchdev.o
+bridge-$(CONFIG_BRIDGE_MRP) += br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o
diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
new file mode 100644
index 000000000000..8b1e85c11e3b
--- /dev/null
+++ b/net/bridge/br_mrp.c
@@ -0,0 +1,556 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/mrp_bridge.h>
+#include "br_private_mrp.h"
+
+static const u8 mrp_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x1 };
+
+static struct net_bridge_port *br_mrp_get_port(struct net_bridge *br,
+ u32 ifindex)
+{
+ struct net_bridge_port *res = NULL;
+ struct net_bridge_port *port;
+
+ list_for_each_entry(port, &br->port_list, list) {
+ if (port->dev->ifindex == ifindex) {
+ res = port;
+ break;
+ }
+ }
+
+ return res;
+}
+
+static struct br_mrp *br_mrp_find_id(struct net_bridge *br, u32 ring_id)
+{
+ struct br_mrp *res = NULL;
+ struct br_mrp *mrp;
+
+ list_for_each_entry_rcu(mrp, &br->mrp_list, list,
+ lockdep_rtnl_is_held()) {
+ if (mrp->ring_id == ring_id) {
+ res = mrp;
+ break;
+ }
+ }
+
+ return res;
+}
+
+static struct br_mrp *br_mrp_find_port(struct net_bridge *br,
+ struct net_bridge_port *p)
+{
+ struct br_mrp *res = NULL;
+ struct br_mrp *mrp;
+
+ list_for_each_entry_rcu(mrp, &br->mrp_list, list,
+ lockdep_rtnl_is_held()) {
+ if (rcu_access_pointer(mrp->p_port) == p ||
+ rcu_access_pointer(mrp->s_port) == p) {
+ res = mrp;
+ break;
+ }
+ }
+
+ return res;
+}
+
+static int br_mrp_next_seq(struct br_mrp *mrp)
+{
+ mrp->seq_id++;
+ return mrp->seq_id;
+}
+
+static struct sk_buff *br_mrp_skb_alloc(struct net_bridge_port *p,
+ const u8 *src, const u8 *dst)
+{
+ struct ethhdr *eth_hdr;
+ struct sk_buff *skb;
+ u16 *version;
+
+ skb = dev_alloc_skb(MRP_MAX_FRAME_LENGTH);
+ if (!skb)
+ return NULL;
+
+ skb->dev = p->dev;
+ skb->protocol = htons(ETH_P_MRP);
+ skb->priority = MRP_FRAME_PRIO;
+ skb_reserve(skb, sizeof(*eth_hdr));
+
+ eth_hdr = skb_push(skb, sizeof(*eth_hdr));
+ ether_addr_copy(eth_hdr->h_dest, dst);
+ ether_addr_copy(eth_hdr->h_source, src);
+ eth_hdr->h_proto = htons(ETH_P_MRP);
+
+ version = skb_put(skb, sizeof(*version));
+ *version = cpu_to_be16(MRP_VERSION);
+
+ return skb;
+}
+
+static void br_mrp_skb_tlv(struct sk_buff *skb,
+ enum br_mrp_tlv_header_type type,
+ u8 length)
+{
+ struct br_mrp_tlv_hdr *hdr;
+
+ hdr = skb_put(skb, sizeof(*hdr));
+ hdr->type = type;
+ hdr->length = length;
+}
+
+static void br_mrp_skb_common(struct sk_buff *skb, struct br_mrp *mrp)
+{
+ struct br_mrp_common_hdr *hdr;
+
+ br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_COMMON, sizeof(*hdr));
+
+ hdr = skb_put(skb, sizeof(*hdr));
+ hdr->seq_id = cpu_to_be16(br_mrp_next_seq(mrp));
+ memset(hdr->domain, 0xff, MRP_DOMAIN_UUID_LENGTH);
+}
+
+static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
+ struct net_bridge_port *p,
+ enum br_mrp_port_role_type port_role)
+{
+ struct br_mrp_ring_test_hdr *hdr = NULL;
+ struct sk_buff *skb = NULL;
+
+ if (!p)
+ return NULL;
+
+ skb = br_mrp_skb_alloc(p, p->dev->dev_addr, mrp_test_dmac);
+ if (!skb)
+ return NULL;
+
+ br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_RING_TEST, sizeof(*hdr));
+ hdr = skb_put(skb, sizeof(*hdr));
+
+ hdr->prio = cpu_to_be16(MRP_DEFAULT_PRIO);
+ ether_addr_copy(hdr->sa, p->br->dev->dev_addr);
+ hdr->port_role = cpu_to_be16(port_role);
+ hdr->state = cpu_to_be16(mrp->ring_state);
+ hdr->transitions = cpu_to_be16(mrp->ring_transitions);
+ hdr->timestamp = cpu_to_be32(jiffies_to_msecs(jiffies));
+
+ br_mrp_skb_common(skb, mrp);
+ br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_END, 0x0);
+
+ return skb;
+}
+
+static void br_mrp_test_work_expired(struct work_struct *work)
+{
+ struct delayed_work *del_work = to_delayed_work(work);
+ struct br_mrp *mrp = container_of(del_work, struct br_mrp, test_work);
+ struct net_bridge_port *p;
+ bool notify_open = false;
+ struct sk_buff *skb;
+
+ if (time_before_eq(mrp->test_end, jiffies))
+ return;
+
+ if (mrp->test_count_miss < mrp->test_max_miss) {
+ mrp->test_count_miss++;
+ } else {
+ /* Notify that the ring is open only if the ring state is
+ * closed, otherwise it would continue to notify at every
+ * interval.
+ */
+ if (mrp->ring_state == BR_MRP_RING_STATE_CLOSED)
+ notify_open = true;
+ }
+
+ rcu_read_lock();
+
+ p = rcu_dereference(mrp->p_port);
+ if (p) {
+ skb = br_mrp_alloc_test_skb(mrp, p, BR_MRP_PORT_ROLE_PRIMARY);
+ if (!skb)
+ goto out;
+
+ skb_reset_network_header(skb);
+ dev_queue_xmit(skb);
+
+ if (notify_open && !mrp->ring_role_offloaded)
+ br_mrp_port_open(p->dev, true);
+ }
+
+ p = rcu_dereference(mrp->s_port);
+ if (p) {
+ skb = br_mrp_alloc_test_skb(mrp, p, BR_MRP_PORT_ROLE_SECONDARY);
+ if (!skb)
+ goto out;
+
+ skb_reset_network_header(skb);
+ dev_queue_xmit(skb);
+
+ if (notify_open && !mrp->ring_role_offloaded)
+ br_mrp_port_open(p->dev, true);
+ }
+
+out:
+ rcu_read_unlock();
+
+ queue_delayed_work(system_wq, &mrp->test_work,
+ usecs_to_jiffies(mrp->test_interval));
+}
+
+/* Deletes the MRP instance.
+ * note: called under rtnl_lock
+ */
+static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp)
+{
+ struct net_bridge_port *p;
+
+ /* Stop sending MRP_Test frames */
+ cancel_delayed_work_sync(&mrp->test_work);
+ br_mrp_switchdev_send_ring_test(br, mrp, 0, 0, 0);
+
+ br_mrp_switchdev_del(br, mrp);
+
+ /* Reset the ports */
+ p = rtnl_dereference(mrp->p_port);
+ if (p) {
+ spin_lock_bh(&br->lock);
+ p->state = BR_STATE_FORWARDING;
+ p->flags &= ~BR_MRP_AWARE;
+ spin_unlock_bh(&br->lock);
+ br_mrp_port_switchdev_set_state(p, BR_STATE_FORWARDING);
+ rcu_assign_pointer(mrp->p_port, NULL);
+ }
+
+ p = rtnl_dereference(mrp->s_port);
+ if (p) {
+ spin_lock_bh(&br->lock);
+ p->state = BR_STATE_FORWARDING;
+ p->flags &= ~BR_MRP_AWARE;
+ spin_unlock_bh(&br->lock);
+ br_mrp_port_switchdev_set_state(p, BR_STATE_FORWARDING);
+ rcu_assign_pointer(mrp->s_port, NULL);
+ }
+
+ list_del_rcu(&mrp->list);
+ kfree_rcu(mrp, rcu);
+}
+
+/* Adds a new MRP instance.
+ * note: called under rtnl_lock
+ */
+int br_mrp_add(struct net_bridge *br, struct br_mrp_instance *instance)
+{
+ struct net_bridge_port *p;
+ struct br_mrp *mrp;
+ int err;
+
+ /* If the ring exists, it is not possible to create another one with the
+ * same ring_id
+ */
+ mrp = br_mrp_find_id(br, instance->ring_id);
+ if (mrp)
+ return -EINVAL;
+
+ if (!br_mrp_get_port(br, instance->p_ifindex) ||
+ !br_mrp_get_port(br, instance->s_ifindex))
+ return -EINVAL;
+
+ mrp = kzalloc(sizeof(*mrp), GFP_KERNEL);
+ if (!mrp)
+ return -ENOMEM;
+
+ mrp->ring_id = instance->ring_id;
+
+ p = br_mrp_get_port(br, instance->p_ifindex);
+ spin_lock_bh(&br->lock);
+ p->state = BR_STATE_FORWARDING;
+ p->flags |= BR_MRP_AWARE;
+ spin_unlock_bh(&br->lock);
+ rcu_assign_pointer(mrp->p_port, p);
+
+ p = br_mrp_get_port(br, instance->s_ifindex);
+ spin_lock_bh(&br->lock);
+ p->state = BR_STATE_FORWARDING;
+ p->flags |= BR_MRP_AWARE;
+ spin_unlock_bh(&br->lock);
+ rcu_assign_pointer(mrp->s_port, p);
+
+ INIT_DELAYED_WORK(&mrp->test_work, br_mrp_test_work_expired);
+ list_add_tail_rcu(&mrp->list, &br->mrp_list);
+
+ err = br_mrp_switchdev_add(br, mrp);
+ if (err)
+ goto delete_mrp;
+
+ return 0;
+
+delete_mrp:
+ br_mrp_del_impl(br, mrp);
+
+ return err;
+}
+
+/* Deletes the MRP instance from which the port is part of
+ * note: called under rtnl_lock
+ */
+void br_mrp_port_del(struct net_bridge *br, struct net_bridge_port *p)
+{
+ struct br_mrp *mrp = br_mrp_find_port(br, p);
+
+ /* If the port is not part of a MRP instance just bail out */
+ if (!mrp)
+ return;
+
+ br_mrp_del_impl(br, mrp);
+}
+
+/* Deletes existing MRP instance based on ring_id
+ * note: called under rtnl_lock
+ */
+int br_mrp_del(struct net_bridge *br, struct br_mrp_instance *instance)
+{
+ struct br_mrp *mrp = br_mrp_find_id(br, instance->ring_id);
+
+ if (!mrp)
+ return -EINVAL;
+
+ br_mrp_del_impl(br, mrp);
+
+ return 0;
+}
+
+/* Set port state, port state can be forwarding, blocked or disabled
+ * note: already called with rcu_read_lock
+ */
+int br_mrp_set_port_state(struct net_bridge_port *p,
+ enum br_mrp_port_state_type state)
+{
+ if (!p || !(p->flags & BR_MRP_AWARE))
+ return -EINVAL;
+
+ spin_lock_bh(&p->br->lock);
+
+ if (state == BR_MRP_PORT_STATE_FORWARDING)
+ p->state = BR_STATE_FORWARDING;
+ else
+ p->state = BR_STATE_BLOCKING;
+
+ spin_unlock_bh(&p->br->lock);
+
+ br_mrp_port_switchdev_set_state(p, state);
+
+ return 0;
+}
+
+/* Set port role, port role can be primary or secondary
+ * note: already called with rcu_read_lock
+ */
+int br_mrp_set_port_role(struct net_bridge_port *p,
+ struct br_mrp_port_role *role)
+{
+ struct br_mrp *mrp;
+
+ if (!p || !(p->flags & BR_MRP_AWARE))
+ return -EINVAL;
+
+ mrp = br_mrp_find_id(p->br, role->ring_id);
+
+ if (!mrp)
+ return -EINVAL;
+
+ if (role->role == BR_MRP_PORT_ROLE_PRIMARY)
+ rcu_assign_pointer(mrp->p_port, p);
+ else
+ rcu_assign_pointer(mrp->s_port, p);
+
+ br_mrp_port_switchdev_set_role(p, role->role);
+
+ return 0;
+}
+
+/* Set ring state, ring state can be only Open or Closed
+ * note: already called with rcu_read_lock
+ */
+int br_mrp_set_ring_state(struct net_bridge *br,
+ struct br_mrp_ring_state *state)
+{
+ struct br_mrp *mrp = br_mrp_find_id(br, state->ring_id);
+
+ if (!mrp)
+ return -EINVAL;
+
+ if (mrp->ring_state == BR_MRP_RING_STATE_CLOSED &&
+ state->ring_state != BR_MRP_RING_STATE_CLOSED)
+ mrp->ring_transitions++;
+
+ mrp->ring_state = state->ring_state;
+
+ br_mrp_switchdev_set_ring_state(br, mrp, state->ring_state);
+
+ return 0;
+}
+
+/* Set ring role, ring role can be only MRM(Media Redundancy Manager) or
+ * MRC(Media Redundancy Client).
+ * note: already called with rcu_read_lock
+ */
+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;
+
+ if (!mrp)
+ return -EINVAL;
+
+ 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;
+
+ /* 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
+ * anymore. For example if the role ir MRM then the HW will notify the
+ * 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;
+
+ return 0;
+}
+
+/* Start to generate MRP test frames, the frames are generated by HW and if it
+ * fails, they are generated by the SW.
+ * note: already called with rcu_read_lock
+ */
+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);
+
+ if (!mrp)
+ return -EINVAL;
+
+ /* Try to push is to the HW and if it fails then continue to generate in
+ * SW and if that also fails then return error
+ */
+ if (!br_mrp_switchdev_send_ring_test(br, mrp, test->interval,
+ test->max_miss, test->period))
+ return 0;
+
+ mrp->test_interval = test->interval;
+ mrp->test_end = jiffies + usecs_to_jiffies(test->period);
+ mrp->test_max_miss = test->max_miss;
+ mrp->test_count_miss = 0;
+ queue_delayed_work(system_wq, &mrp->test_work,
+ usecs_to_jiffies(test->interval));
+
+ return 0;
+}
+
+/* Process only MRP Test frame. All the other MRP frames are processed by
+ * userspace application
+ * note: already called with rcu_read_lock
+ */
+static void br_mrp_mrm_process(struct br_mrp *mrp, struct net_bridge_port *port,
+ struct sk_buff *skb)
+{
+ struct br_mrp_tlv_hdr *hdr;
+
+ hdr = (struct br_mrp_tlv_hdr *)(skb->data + sizeof(uint16_t));
+
+ if (!hdr)
+ return;
+
+ if (hdr->type != BR_MRP_TLV_HEADER_RING_TEST)
+ return;
+
+ mrp->test_count_miss = 0;
+
+ /* Notify the userspace that the ring is closed only when the ring is
+ * not closed
+ */
+ if (mrp->ring_state != BR_MRP_RING_STATE_CLOSED)
+ br_mrp_port_open(port->dev, false);
+}
+
+/* This will just forward the frame to the other mrp ring port(MRC role) or will
+ * not do anything.
+ * note: already called with rcu_read_lock
+ */
+static int br_mrp_rcv(struct net_bridge_port *p,
+ struct sk_buff *skb, struct net_device *dev)
+{
+ struct net_device *s_dev, *p_dev, *d_dev;
+ struct net_bridge_port *p_port, *s_port;
+ struct net_bridge *br;
+ struct sk_buff *nskb;
+ struct br_mrp *mrp;
+
+ /* If port is disabled don't accept any frames */
+ if (p->state == BR_STATE_DISABLED)
+ return 0;
+
+ br = p->br;
+ mrp = br_mrp_find_port(br, p);
+ if (unlikely(!mrp))
+ return 0;
+
+ p_port = rcu_dereference(mrp->p_port);
+ if (!p_port)
+ return 0;
+
+ s_port = rcu_dereference(mrp->s_port);
+ if (!s_port)
+ return 0;
+
+ /* If the role is MRM then don't forward the frames */
+ if (mrp->ring_role == BR_MRP_RING_ROLE_MRM) {
+ br_mrp_mrm_process(mrp, p, skb);
+ return 1;
+ }
+
+ /* Clone the frame and forward it on the other MRP port */
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (!nskb)
+ return 0;
+
+ p_dev = p_port->dev;
+ s_dev = s_port->dev;
+
+ if (p_dev == dev)
+ d_dev = s_dev;
+ else
+ d_dev = p_dev;
+
+ nskb->dev = d_dev;
+ skb_push(nskb, ETH_HLEN);
+ dev_queue_xmit(nskb);
+
+ return 1;
+}
+
+/* Check if the frame was received on a port that is part of MRP ring
+ * and if the frame has MRP eth. In that case process the frame otherwise do
+ * normal forwarding.
+ * note: already called with rcu_read_lock
+ */
+int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
+{
+ /* If there is no MRP instance do normal forwarding */
+ if (likely(!(p->flags & BR_MRP_AWARE)))
+ goto out;
+
+ if (unlikely(skb->protocol == htons(ETH_P_MRP)))
+ return br_mrp_rcv(p, skb, p->dev);
+
+out:
+ return 0;
+}
+
+bool br_mrp_enabled(struct net_bridge *br)
+{
+ return !list_empty(&br->mrp_list);
+}
diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c
new file mode 100644
index 000000000000..b982db14bbf4
--- /dev/null
+++ b/net/bridge/br_mrp_netlink.c
@@ -0,0 +1,29 @@
+// 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"
+
+int br_mrp_port_open(struct net_device *dev, u8 loc)
+{
+ struct net_bridge_port *p;
+ int err = 0;
+
+ p = br_port_get_rcu(dev);
+ if (!p) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (loc)
+ p->flags |= BR_MRP_LOST_CONT;
+ else
+ p->flags &= ~BR_MRP_LOST_CONT;
+
+ br_ifinfo_notify(RTM_NEWLINK, NULL, p);
+
+out:
+ return err;
+}
--
2.17.1
Implement netlink interface to configure MRP. The implementation
will do sanity checks over the attributes and then eventually call the MRP
interface.
Signed-off-by: Horatiu Vultur <[email protected]>
---
net/bridge/br_mrp_netlink.c | 91 +++++++++++++++++++++++++++++++++++++
1 file changed, 91 insertions(+)
diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c
index b982db14bbf4..7b4b8b0dcea0 100644
--- a/net/bridge/br_mrp_netlink.c
+++ b/net/bridge/br_mrp_netlink.c
@@ -6,6 +6,97 @@
#include "br_private.h"
#include "br_private_mrp.h"
+static const struct nla_policy br_mrp_policy[IFLA_BRIDGE_MRP_MAX + 1] = {
+ [IFLA_BRIDGE_MRP_UNSPEC] = { .type = NLA_REJECT },
+ [IFLA_BRIDGE_MRP_INSTANCE] = { .type = NLA_EXACT_LEN,
+ .len = sizeof(struct br_mrp_instance)},
+ [IFLA_BRIDGE_MRP_PORT_STATE] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_MRP_PORT_ROLE] = { .type = NLA_EXACT_LEN,
+ .len = sizeof(struct br_mrp_port_role)},
+ [IFLA_BRIDGE_MRP_RING_STATE] = { .type = NLA_EXACT_LEN,
+ .len = sizeof(struct br_mrp_ring_state)},
+ [IFLA_BRIDGE_MRP_RING_ROLE] = { .type = NLA_EXACT_LEN,
+ .len = sizeof(struct br_mrp_ring_role)},
+ [IFLA_BRIDGE_MRP_START_TEST] = { .type = NLA_EXACT_LEN,
+ .len = sizeof(struct br_mrp_start_test)},
+};
+
+int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p,
+ struct nlattr *attr, int cmd, struct netlink_ext_ack *extack)
+{
+ struct nlattr *tb[IFLA_BRIDGE_MRP_MAX + 1];
+ int err;
+
+ if (br->stp_enabled != BR_NO_STP) {
+ NL_SET_ERR_MSG_MOD(extack, "MRP can't be enabled if STP is already enabled\n");
+ return -EINVAL;
+ }
+
+ err = nla_parse_nested(tb, IFLA_BRIDGE_MRP_MAX, attr,
+ br_mrp_policy, extack);
+ if (err)
+ return err;
+
+ if (tb[IFLA_BRIDGE_MRP_INSTANCE]) {
+ struct br_mrp_instance *instance =
+ nla_data(tb[IFLA_BRIDGE_MRP_INSTANCE]);
+
+ if (cmd == RTM_SETLINK)
+ err = br_mrp_add(br, instance);
+ else
+ err = br_mrp_del(br, instance);
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_BRIDGE_MRP_PORT_STATE]) {
+ enum br_mrp_port_state_type state =
+ nla_get_u32(tb[IFLA_BRIDGE_MRP_PORT_STATE]);
+
+ err = br_mrp_set_port_state(p, state);
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_BRIDGE_MRP_PORT_ROLE]) {
+ struct br_mrp_port_role *role =
+ nla_data(tb[IFLA_BRIDGE_MRP_PORT_ROLE]);
+
+ err = br_mrp_set_port_role(p, role);
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_BRIDGE_MRP_RING_STATE]) {
+ struct br_mrp_ring_state *state =
+ nla_data(tb[IFLA_BRIDGE_MRP_RING_STATE]);
+
+ err = br_mrp_set_ring_state(br, state);
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_BRIDGE_MRP_RING_ROLE]) {
+ struct br_mrp_ring_role *role =
+ nla_data(tb[IFLA_BRIDGE_MRP_RING_ROLE]);
+
+ err = br_mrp_set_ring_role(br, role);
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_BRIDGE_MRP_START_TEST]) {
+ struct br_mrp_start_test *test =
+ nla_data(tb[IFLA_BRIDGE_MRP_START_TEST]);
+
+ err = br_mrp_start_test(br, test);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
int br_mrp_port_open(struct net_device *dev, u8 loc)
{
struct net_bridge_port *p;
--
2.17.1
It is not possible to have the MRP and STP running at the same time on the
bridge, therefore add check when enabling the STP to check if MRP is already
enabled. In that case return error.
Signed-off-by: Horatiu Vultur <[email protected]>
---
net/bridge/br_ioctl.c | 3 +--
net/bridge/br_netlink.c | 4 +++-
net/bridge/br_private.h | 3 ++-
net/bridge/br_stp.c | 6 ++++++
net/bridge/br_stp_if.c | 11 ++++++++++-
net/bridge/br_sysfs_br.c | 4 +---
6 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
index ae22d784b88a..5e71fc8b826f 100644
--- a/net/bridge/br_ioctl.c
+++ b/net/bridge/br_ioctl.c
@@ -242,8 +242,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
- br_stp_set_enabled(br, args[1]);
- ret = 0;
+ ret = br_stp_set_enabled(br, args[1], NULL);
break;
case BRCTL_SET_BRIDGE_PRIORITY:
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 1a5e681a626a..a774e19c41bb 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -1109,7 +1109,9 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
if (data[IFLA_BR_STP_STATE]) {
u32 stp_enabled = nla_get_u32(data[IFLA_BR_STP_STATE]);
- br_stp_set_enabled(br, stp_enabled);
+ err = br_stp_set_enabled(br, stp_enabled, extack);
+ if (err)
+ return err;
}
if (data[IFLA_BR_PRIORITY]) {
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 5835828320b6..c35647cb138a 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1283,7 +1283,8 @@ int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time);
/* br_stp_if.c */
void br_stp_enable_bridge(struct net_bridge *br);
void br_stp_disable_bridge(struct net_bridge *br);
-void br_stp_set_enabled(struct net_bridge *br, unsigned long val);
+int br_stp_set_enabled(struct net_bridge *br, unsigned long val,
+ struct netlink_ext_ack *extack);
void br_stp_enable_port(struct net_bridge_port *p);
void br_stp_disable_port(struct net_bridge_port *p);
bool br_stp_recalculate_bridge_id(struct net_bridge *br);
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index 1f14b8455345..3e88be7aa269 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -36,6 +36,12 @@ void br_set_state(struct net_bridge_port *p, unsigned int state)
};
int err;
+ /* Don't change the state of the ports if they are driven by a different
+ * protocol.
+ */
+ if (p->flags & BR_MRP_AWARE)
+ return;
+
p->state = state;
err = switchdev_port_attr_set(p->dev, &attr);
if (err && err != -EOPNOTSUPP)
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index d174d3a566aa..a42850b7eb9a 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -196,10 +196,17 @@ static void br_stp_stop(struct net_bridge *br)
br->stp_enabled = BR_NO_STP;
}
-void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
+int br_stp_set_enabled(struct net_bridge *br, unsigned long val,
+ struct netlink_ext_ack *extack)
{
ASSERT_RTNL();
+ if (br_mrp_enabled(br)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "STP can't be enabled if MRP is already enabled\n");
+ return -EINVAL;
+ }
+
if (val) {
if (br->stp_enabled == BR_NO_STP)
br_stp_start(br);
@@ -207,6 +214,8 @@ void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
if (br->stp_enabled != BR_NO_STP)
br_stp_stop(br);
}
+
+ return 0;
}
/* called under bridge lock */
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 9ab0f00b1081..7db06e3f642a 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -126,9 +126,7 @@ static ssize_t stp_state_show(struct device *d,
static int set_stp_state(struct net_bridge *br, unsigned long val)
{
- br_stp_set_enabled(br, val);
-
- return 0;
+ return br_stp_set_enabled(br, val, NULL);
}
static ssize_t stp_state_store(struct device *d,
--
2.17.1
On 22/04/2020 19:18, Horatiu Vultur wrote:
> Add new nested netlink attribute to configure the MRP. These attributes are used
> by the userspace to add/delete/configure MRP instances and by the kernel to
> notify the userspace when the MRP ring gets open/closed. MRP nested attribute
> has the following attributes:
>
> IFLA_BRIDGE_MRP_INSTANCE - the parameter type is br_mrp_instance which contains
> the instance id, and the ifindex of the two ports. The ports can't be part of
> multiple instances. This is used to create/delete MRP instances.
>
> IFLA_BRIDGE_MRP_PORT_STATE - the parameter type is u32. Which can be forwarding,
> blocking or disabled.
>
> IFLA_BRIDGE_MRP_PORT_ROLE - the parameter type is br_mrp_port_role which
> contains the instance id and the role. The role can be primary or secondary.
>
> IFLA_BRIDGE_MRP_RING_STATE - the parameter type is br_mrp_ring_state which
> contains the instance id and the state. The state can be open or closed.
>
> IFLA_BRIDGE_MRP_RING_ROLE - the parameter type is br_mrp_ring_role which
> contains the instance id and the ring role. The role can be MRM or MRC.
>
> IFLA_BRIDGE_MRP_START_TEST - the parameter type is br_mrp_start_test which
> contains the instance id, the interval at which to send the MRP_Test frames,
> how many test frames can be missed before declaring the ring open and the
> period which represent for how long to send the test frames.
>
> Also add the file include/uapi/linux/mrp_bridge.h which defines all the types
> used by MRP that are also needed by the userpace.
>
> Signed-off-by: Horatiu Vultur <[email protected]>
> ---
> include/uapi/linux/if_bridge.h | 42 +++++++++++++++++
> include/uapi/linux/if_ether.h | 1 +
> include/uapi/linux/mrp_bridge.h | 84 +++++++++++++++++++++++++++++++++
> 3 files changed, 127 insertions(+)
> create mode 100644 include/uapi/linux/mrp_bridge.h
>
Reviewed-by: Nikolay Aleksandrov <[email protected]>
On 22/04/2020 19:18, Horatiu Vultur wrote:
> To integrate MRP into the bridge, first the bridge needs to be aware of ports
> that are part of an MRP ring and which rings are on the bridge.
> Therefore extend bridge interface with the following:
> - add new flag(BR_MPP_AWARE) to the net bridge ports, this bit will be
> set when the port is added to an MRP instance. In this way it knows if
> the frame was received on MRP ring port
> - add new flag(BR_MRP_LOST_CONT) to the net bridge ports, this bit will be set
> when the port lost the continuity of MRP Test frames.
> - add a list of MRP instances
>
> Signed-off-by: Horatiu Vultur <[email protected]>
> ---
> include/linux/if_bridge.h | 2 ++
> net/bridge/br_private.h | 4 ++++
> 2 files changed, 6 insertions(+)
>
> diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
> index 9e57c4411734..b3a8d3054af0 100644
> --- a/include/linux/if_bridge.h
> +++ b/include/linux/if_bridge.h
> @@ -47,6 +47,8 @@ struct br_ip_list {
> #define BR_BCAST_FLOOD BIT(14)
> #define BR_NEIGH_SUPPRESS BIT(15)
> #define BR_ISOLATED BIT(16)
> +#define BR_MRP_AWARE BIT(17)
> +#define BR_MRP_LOST_CONT BIT(18)
>
> #define BR_DEFAULT_AGEING_TIME (300 * HZ)
>
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 1f97703a52ff..835a70f8d3ea 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -428,6 +428,10 @@ struct net_bridge {
> int offload_fwd_mark;
> #endif
> struct hlist_head fdb_list;
> +
> +#if IS_ENABLED(CONFIG_BRIDGE_MRP)
> + struct list_head __rcu mrp_list;
> +#endif
> };
>
> struct br_input_skb_cb {
>
Reviewed-by: Nikolay Aleksandrov <[email protected]>
On 22/04/2020 19:18, Horatiu Vultur wrote:
> It is not possible to have the MRP and STP running at the same time on the
> bridge, therefore add check when enabling the STP to check if MRP is already
> enabled. In that case return error.
>
> Signed-off-by: Horatiu Vultur <[email protected]>
> ---
> net/bridge/br_ioctl.c | 3 +--
> net/bridge/br_netlink.c | 4 +++-
> net/bridge/br_private.h | 3 ++-
> net/bridge/br_stp.c | 6 ++++++
> net/bridge/br_stp_if.c | 11 ++++++++++-
> net/bridge/br_sysfs_br.c | 4 +---
> 6 files changed, 23 insertions(+), 8 deletions(-)
>
> diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
> index ae22d784b88a..5e71fc8b826f 100644
> --- a/net/bridge/br_ioctl.c
> +++ b/net/bridge/br_ioctl.c
> @@ -242,8 +242,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
> if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
> return -EPERM;
>
> - br_stp_set_enabled(br, args[1]);
> - ret = 0;
> + ret = br_stp_set_enabled(br, args[1], NULL);
> break;
>
> case BRCTL_SET_BRIDGE_PRIORITY:
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index 1a5e681a626a..a774e19c41bb 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
> @@ -1109,7 +1109,9 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
> if (data[IFLA_BR_STP_STATE]) {
> u32 stp_enabled = nla_get_u32(data[IFLA_BR_STP_STATE]);
>
> - br_stp_set_enabled(br, stp_enabled);
> + err = br_stp_set_enabled(br, stp_enabled, extack);
> + if (err)
> + return err;
> }
>
> if (data[IFLA_BR_PRIORITY]) {
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 5835828320b6..c35647cb138a 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -1283,7 +1283,8 @@ int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time);
> /* br_stp_if.c */
> void br_stp_enable_bridge(struct net_bridge *br);
> void br_stp_disable_bridge(struct net_bridge *br);
> -void br_stp_set_enabled(struct net_bridge *br, unsigned long val);
> +int br_stp_set_enabled(struct net_bridge *br, unsigned long val,
> + struct netlink_ext_ack *extack);
> void br_stp_enable_port(struct net_bridge_port *p);
> void br_stp_disable_port(struct net_bridge_port *p);
> bool br_stp_recalculate_bridge_id(struct net_bridge *br);
> diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
> index 1f14b8455345..3e88be7aa269 100644
> --- a/net/bridge/br_stp.c
> +++ b/net/bridge/br_stp.c
> @@ -36,6 +36,12 @@ void br_set_state(struct net_bridge_port *p, unsigned int state)
> };
> int err;
>
> + /* Don't change the state of the ports if they are driven by a different
> + * protocol.
> + */
> + if (p->flags & BR_MRP_AWARE)
> + return;
> +
> p->state = state;
> err = switchdev_port_attr_set(p->dev, &attr);
> if (err && err != -EOPNOTSUPP)
> diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
> index d174d3a566aa..a42850b7eb9a 100644
> --- a/net/bridge/br_stp_if.c
> +++ b/net/bridge/br_stp_if.c
> @@ -196,10 +196,17 @@ static void br_stp_stop(struct net_bridge *br)
> br->stp_enabled = BR_NO_STP;
> }
>
> -void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
> +int br_stp_set_enabled(struct net_bridge *br, unsigned long val,
> + struct netlink_ext_ack *extack)
> {
> ASSERT_RTNL();
>
> + if (br_mrp_enabled(br)) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "STP can't be enabled if MRP is already enabled\n");
The operation could be disable (noop in case it's already disabled) and this will still
return an error.
> + return -EINVAL;
> + }
> +
> if (val) {
> if (br->stp_enabled == BR_NO_STP)
> br_stp_start(br);
> @@ -207,6 +214,8 @@ void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
> if (br->stp_enabled != BR_NO_STP)
> br_stp_stop(br);
> }
> +
> + return 0;
> }
>
> /* called under bridge lock */
> diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
> index 9ab0f00b1081..7db06e3f642a 100644
> --- a/net/bridge/br_sysfs_br.c
> +++ b/net/bridge/br_sysfs_br.c
> @@ -126,9 +126,7 @@ static ssize_t stp_state_show(struct device *d,
>
> static int set_stp_state(struct net_bridge *br, unsigned long val)
> {
> - br_stp_set_enabled(br, val);
> -
> - return 0;
> + return br_stp_set_enabled(br, val, NULL);
> }
>
> static ssize_t stp_state_store(struct device *d,
>
On 22/04/2020 19:18, Horatiu Vultur wrote:
> Implement the MRP API.
>
> In case the HW can't generate MRP Test frames then the SW will try to generate
> the frames. In case that also the SW will fail in generating the frames then a
> error is return to the userspace. The userspace is responsible to generate all
> the other MRP frames regardless if the test frames are generated by HW or SW.
>
> The forwarding/termination of MRP frames is happening in the kernel and is done
> by the MRP instance. The userspace application doesn't do the forwarding.
>
> Signed-off-by: Horatiu Vultur <[email protected]>
> ---
> net/bridge/Makefile | 2 +-
> net/bridge/br_mrp.c | 556 ++++++++++++++++++++++++++++++++++++
> net/bridge/br_mrp_netlink.c | 29 ++
> 3 files changed, 586 insertions(+), 1 deletion(-)
> create mode 100644 net/bridge/br_mrp.c
> create mode 100644 net/bridge/br_mrp_netlink.c
>
> diff --git a/net/bridge/Makefile b/net/bridge/Makefile
> index 3cacf9dd78d5..ccb394236fbd 100644
> --- a/net/bridge/Makefile
> +++ b/net/bridge/Makefile
> @@ -26,4 +26,4 @@ bridge-$(CONFIG_NET_SWITCHDEV) += br_switchdev.o
>
> obj-$(CONFIG_NETFILTER) += netfilter/
>
> -bridge-$(CONFIG_BRIDGE_MRP) += br_mrp_switchdev.o
> +bridge-$(CONFIG_BRIDGE_MRP) += br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o
> diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
> new file mode 100644
> index 000000000000..8b1e85c11e3b
> --- /dev/null
> +++ b/net/bridge/br_mrp.c
> @@ -0,0 +1,556 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +
> +#include <linux/mrp_bridge.h>
> +#include "br_private_mrp.h"
> +
> +static const u8 mrp_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x1 };
> +
> +static struct net_bridge_port *br_mrp_get_port(struct net_bridge *br,
> + u32 ifindex)
> +{
> + struct net_bridge_port *res = NULL;
> + struct net_bridge_port *port;
> +
> + list_for_each_entry(port, &br->port_list, list) {
> + if (port->dev->ifindex == ifindex) {
> + res = port;
> + break;
> + }
> + }
> +
> + return res;
> +}
> +
> +static struct br_mrp *br_mrp_find_id(struct net_bridge *br, u32 ring_id)
> +{
> + struct br_mrp *res = NULL;
> + struct br_mrp *mrp;
> +
> + list_for_each_entry_rcu(mrp, &br->mrp_list, list,
> + lockdep_rtnl_is_held()) {
> + if (mrp->ring_id == ring_id) {
> + res = mrp;
> + break;
> + }
> + }
> +
> + return res;
> +}
> +
> +static struct br_mrp *br_mrp_find_port(struct net_bridge *br,
> + struct net_bridge_port *p)
> +{
> + struct br_mrp *res = NULL;
> + struct br_mrp *mrp;
> +
> + list_for_each_entry_rcu(mrp, &br->mrp_list, list,
> + lockdep_rtnl_is_held()) {
> + if (rcu_access_pointer(mrp->p_port) == p ||
> + rcu_access_pointer(mrp->s_port) == p) {
> + res = mrp;
> + break;
> + }
> + }
> +
> + return res;
> +}
> +
> +static int br_mrp_next_seq(struct br_mrp *mrp)
> +{
> + mrp->seq_id++;
> + return mrp->seq_id;
> +}
> +
> +static struct sk_buff *br_mrp_skb_alloc(struct net_bridge_port *p,
> + const u8 *src, const u8 *dst)
> +{
> + struct ethhdr *eth_hdr;
> + struct sk_buff *skb;
> + u16 *version;
> +
> + skb = dev_alloc_skb(MRP_MAX_FRAME_LENGTH);
> + if (!skb)
> + return NULL;
> +
> + skb->dev = p->dev;
> + skb->protocol = htons(ETH_P_MRP);
> + skb->priority = MRP_FRAME_PRIO;
> + skb_reserve(skb, sizeof(*eth_hdr));
> +
> + eth_hdr = skb_push(skb, sizeof(*eth_hdr));
> + ether_addr_copy(eth_hdr->h_dest, dst);
> + ether_addr_copy(eth_hdr->h_source, src);
> + eth_hdr->h_proto = htons(ETH_P_MRP);
> +
> + version = skb_put(skb, sizeof(*version));
> + *version = cpu_to_be16(MRP_VERSION);
> +
> + return skb;
> +}
> +
> +static void br_mrp_skb_tlv(struct sk_buff *skb,
> + enum br_mrp_tlv_header_type type,
> + u8 length)
> +{
> + struct br_mrp_tlv_hdr *hdr;
> +
> + hdr = skb_put(skb, sizeof(*hdr));
> + hdr->type = type;
> + hdr->length = length;
> +}
> +
> +static void br_mrp_skb_common(struct sk_buff *skb, struct br_mrp *mrp)
> +{
> + struct br_mrp_common_hdr *hdr;
> +
> + br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_COMMON, sizeof(*hdr));
> +
> + hdr = skb_put(skb, sizeof(*hdr));
> + hdr->seq_id = cpu_to_be16(br_mrp_next_seq(mrp));
> + memset(hdr->domain, 0xff, MRP_DOMAIN_UUID_LENGTH);
> +}
> +
> +static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
> + struct net_bridge_port *p,
> + enum br_mrp_port_role_type port_role)
> +{
> + struct br_mrp_ring_test_hdr *hdr = NULL;
> + struct sk_buff *skb = NULL;
> +
> + if (!p)
> + return NULL;
> +
> + skb = br_mrp_skb_alloc(p, p->dev->dev_addr, mrp_test_dmac);
> + if (!skb)
> + return NULL;
> +
> + br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_RING_TEST, sizeof(*hdr));
> + hdr = skb_put(skb, sizeof(*hdr));
> +
> + hdr->prio = cpu_to_be16(MRP_DEFAULT_PRIO);
> + ether_addr_copy(hdr->sa, p->br->dev->dev_addr);
> + hdr->port_role = cpu_to_be16(port_role);
> + hdr->state = cpu_to_be16(mrp->ring_state);
> + hdr->transitions = cpu_to_be16(mrp->ring_transitions);
> + hdr->timestamp = cpu_to_be32(jiffies_to_msecs(jiffies));
> +
> + br_mrp_skb_common(skb, mrp);
> + br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_END, 0x0);
> +
> + return skb;
> +}
> +
> +static void br_mrp_test_work_expired(struct work_struct *work)
> +{
> + struct delayed_work *del_work = to_delayed_work(work);
> + struct br_mrp *mrp = container_of(del_work, struct br_mrp, test_work);
> + struct net_bridge_port *p;
> + bool notify_open = false;
> + struct sk_buff *skb;
> +
> + if (time_before_eq(mrp->test_end, jiffies))
> + return;
> +
> + if (mrp->test_count_miss < mrp->test_max_miss) {
> + mrp->test_count_miss++;
> + } else {
> + /* Notify that the ring is open only if the ring state is
> + * closed, otherwise it would continue to notify at every
> + * interval.
> + */
> + if (mrp->ring_state == BR_MRP_RING_STATE_CLOSED)
> + notify_open = true;
> + }
> +
> + rcu_read_lock();
> +
> + p = rcu_dereference(mrp->p_port);
> + if (p) {
> + skb = br_mrp_alloc_test_skb(mrp, p, BR_MRP_PORT_ROLE_PRIMARY);
> + if (!skb)
> + goto out;
> +
> + skb_reset_network_header(skb);
> + dev_queue_xmit(skb);
> +
> + if (notify_open && !mrp->ring_role_offloaded)
> + br_mrp_port_open(p->dev, true);
> + }
> +
> + p = rcu_dereference(mrp->s_port);
> + if (p) {
> + skb = br_mrp_alloc_test_skb(mrp, p, BR_MRP_PORT_ROLE_SECONDARY);
> + if (!skb)
> + goto out;
> +
> + skb_reset_network_header(skb);
> + dev_queue_xmit(skb);
> +
> + if (notify_open && !mrp->ring_role_offloaded)
> + br_mrp_port_open(p->dev, true);
> + }
> +
> +out:
> + rcu_read_unlock();
> +
> + queue_delayed_work(system_wq, &mrp->test_work,
> + usecs_to_jiffies(mrp->test_interval));
> +}
> +
> +/* Deletes the MRP instance.
> + * note: called under rtnl_lock
> + */
> +static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp)
> +{
> + struct net_bridge_port *p;
> +
> + /* Stop sending MRP_Test frames */
> + cancel_delayed_work_sync(&mrp->test_work);
> + br_mrp_switchdev_send_ring_test(br, mrp, 0, 0, 0);
> +
> + br_mrp_switchdev_del(br, mrp);
> +
> + /* Reset the ports */
> + p = rtnl_dereference(mrp->p_port);
> + if (p) {
> + spin_lock_bh(&br->lock);
> + p->state = BR_STATE_FORWARDING;
> + p->flags &= ~BR_MRP_AWARE;
> + spin_unlock_bh(&br->lock);
> + br_mrp_port_switchdev_set_state(p, BR_STATE_FORWARDING);
> + rcu_assign_pointer(mrp->p_port, NULL);
> + }
> +
> + p = rtnl_dereference(mrp->s_port);
> + if (p) {
> + spin_lock_bh(&br->lock);
> + p->state = BR_STATE_FORWARDING;
> + p->flags &= ~BR_MRP_AWARE;
> + spin_unlock_bh(&br->lock);
> + br_mrp_port_switchdev_set_state(p, BR_STATE_FORWARDING);
> + rcu_assign_pointer(mrp->s_port, NULL);
> + }
> +
> + list_del_rcu(&mrp->list);
> + kfree_rcu(mrp, rcu);
> +}
> +
> +/* Adds a new MRP instance.
> + * note: called under rtnl_lock
> + */
> +int br_mrp_add(struct net_bridge *br, struct br_mrp_instance *instance)
> +{
> + struct net_bridge_port *p;
> + struct br_mrp *mrp;
> + int err;
> +
> + /* If the ring exists, it is not possible to create another one with the
> + * same ring_id
> + */
> + mrp = br_mrp_find_id(br, instance->ring_id);
> + if (mrp)
> + return -EINVAL;
> +
> + if (!br_mrp_get_port(br, instance->p_ifindex) ||
> + !br_mrp_get_port(br, instance->s_ifindex))
> + return -EINVAL;
> +
> + mrp = kzalloc(sizeof(*mrp), GFP_KERNEL);
> + if (!mrp)
> + return -ENOMEM;
> +
> + mrp->ring_id = instance->ring_id;
> +
> + p = br_mrp_get_port(br, instance->p_ifindex);
> + spin_lock_bh(&br->lock);
> + p->state = BR_STATE_FORWARDING;
> + p->flags |= BR_MRP_AWARE;
> + spin_unlock_bh(&br->lock);
> + rcu_assign_pointer(mrp->p_port, p);
> +
> + p = br_mrp_get_port(br, instance->s_ifindex);
> + spin_lock_bh(&br->lock);
> + p->state = BR_STATE_FORWARDING;
> + p->flags |= BR_MRP_AWARE;
> + spin_unlock_bh(&br->lock);
> + rcu_assign_pointer(mrp->s_port, p);
> +
> + INIT_DELAYED_WORK(&mrp->test_work, br_mrp_test_work_expired);
> + list_add_tail_rcu(&mrp->list, &br->mrp_list);
> +
> + err = br_mrp_switchdev_add(br, mrp);
> + if (err)
> + goto delete_mrp;
> +
> + return 0;
> +
> +delete_mrp:
> + br_mrp_del_impl(br, mrp);
> +
> + return err;
> +}
> +
> +/* Deletes the MRP instance from which the port is part of
> + * note: called under rtnl_lock
> + */
> +void br_mrp_port_del(struct net_bridge *br, struct net_bridge_port *p)
> +{
> + struct br_mrp *mrp = br_mrp_find_port(br, p);
> +
> + /* If the port is not part of a MRP instance just bail out */
> + if (!mrp)
> + return;
> +
> + br_mrp_del_impl(br, mrp);
> +}
> +
> +/* Deletes existing MRP instance based on ring_id
> + * note: called under rtnl_lock
> + */
> +int br_mrp_del(struct net_bridge *br, struct br_mrp_instance *instance)
> +{
> + struct br_mrp *mrp = br_mrp_find_id(br, instance->ring_id);
> +
> + if (!mrp)
> + return -EINVAL;
> +
> + br_mrp_del_impl(br, mrp);
> +
> + return 0;
> +}
> +
> +/* Set port state, port state can be forwarding, blocked or disabled
> + * note: already called with rcu_read_lock
> + */
But is that true? I think it's called under rtnl.
> +int br_mrp_set_port_state(struct net_bridge_port *p,
> + enum br_mrp_port_state_type state)
> +{
> + if (!p || !(p->flags & BR_MRP_AWARE))
> + return -EINVAL;
> +
> + spin_lock_bh(&p->br->lock);
> +
> + if (state == BR_MRP_PORT_STATE_FORWARDING)
> + p->state = BR_STATE_FORWARDING;
> + else
> + p->state = BR_STATE_BLOCKING;
> +
> + spin_unlock_bh(&p->br->lock);
> +
> + br_mrp_port_switchdev_set_state(p, state);
> +
> + return 0;
> +}
> +
> +/* Set port role, port role can be primary or secondary
> + * note: already called with rcu_read_lock
> + */
same here
> +int br_mrp_set_port_role(struct net_bridge_port *p,
> + struct br_mrp_port_role *role)
> +{
> + struct br_mrp *mrp;
> +
> + if (!p || !(p->flags & BR_MRP_AWARE))
> + return -EINVAL;
> +
> + mrp = br_mrp_find_id(p->br, role->ring_id);
> +
> + if (!mrp)
> + return -EINVAL;
> +
> + if (role->role == BR_MRP_PORT_ROLE_PRIMARY)
> + rcu_assign_pointer(mrp->p_port, p);
> + else
> + rcu_assign_pointer(mrp->s_port, p);
> +
> + br_mrp_port_switchdev_set_role(p, role->role);
> +
> + return 0;
> +}
> +
> +/* Set ring state, ring state can be only Open or Closed
> + * note: already called with rcu_read_lock
> + */
same here
> +int br_mrp_set_ring_state(struct net_bridge *br,
> + struct br_mrp_ring_state *state)
> +{
> + struct br_mrp *mrp = br_mrp_find_id(br, state->ring_id);
> +
> + if (!mrp)
> + return -EINVAL;
> +
> + if (mrp->ring_state == BR_MRP_RING_STATE_CLOSED &&
> + state->ring_state != BR_MRP_RING_STATE_CLOSED)
> + mrp->ring_transitions++;
> +
> + mrp->ring_state = state->ring_state;
> +
> + br_mrp_switchdev_set_ring_state(br, mrp, state->ring_state);
> +
> + return 0;
> +}
> +
> +/* Set ring role, ring role can be only MRM(Media Redundancy Manager) or
> + * MRC(Media Redundancy Client).
> + * note: already called with rcu_read_lock
> + */
and here
> +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;
> +
> + if (!mrp)
> + return -EINVAL;
> +
> + 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;
> +
> + /* 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
> + * anymore. For example if the role ir MRM then the HW will notify the
> + * 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;
> +
> + return 0;
> +}
> +
> +/* Start to generate MRP test frames, the frames are generated by HW and if it
> + * fails, they are generated by the SW.
> + * note: already called with rcu_read_lock
> + */
here too
> +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);
> +
> + if (!mrp)
> + return -EINVAL;
> +
> + /* Try to push is to the HW and if it fails then continue to generate in
s/is/it/ ?
> + * SW and if that also fails then return error
> + */
> + if (!br_mrp_switchdev_send_ring_test(br, mrp, test->interval,
> + test->max_miss, test->period))
> + return 0;
> +
> + mrp->test_interval = test->interval;
> + mrp->test_end = jiffies + usecs_to_jiffies(test->period);
> + mrp->test_max_miss = test->max_miss;
> + mrp->test_count_miss = 0;
> + queue_delayed_work(system_wq, &mrp->test_work,
> + usecs_to_jiffies(test->interval));
> +
> + return 0;
> +}
> +
> +/* Process only MRP Test frame. All the other MRP frames are processed by
> + * userspace application
> + * note: already called with rcu_read_lock
> + */
> +static void br_mrp_mrm_process(struct br_mrp *mrp, struct net_bridge_port *port,
> + struct sk_buff *skb)
> +{
> + struct br_mrp_tlv_hdr *hdr;
> +
> + hdr = (struct br_mrp_tlv_hdr *)(skb->data + sizeof(uint16_t));
> +
Missed this in my previous reviews, "+ sizeof(uint16_t)" is that because of MRP_VERSION ?
I'd use skb_header_pointer() and also please drop the unnecessary newline here.
> + if (!hdr)
> + return;
> +
> + if (hdr->type != BR_MRP_TLV_HEADER_RING_TEST)
> + return;
> +
> + mrp->test_count_miss = 0;
> +
> + /* Notify the userspace that the ring is closed only when the ring is
> + * not closed
> + */
> + if (mrp->ring_state != BR_MRP_RING_STATE_CLOSED)
> + br_mrp_port_open(port->dev, false);
> +}
> +
> +/* This will just forward the frame to the other mrp ring port(MRC role) or will
> + * not do anything.
> + * note: already called with rcu_read_lock
> + */
> +static int br_mrp_rcv(struct net_bridge_port *p,
> + struct sk_buff *skb, struct net_device *dev)
> +{
> + struct net_device *s_dev, *p_dev, *d_dev;
> + struct net_bridge_port *p_port, *s_port;
> + struct net_bridge *br;
> + struct sk_buff *nskb;
> + struct br_mrp *mrp;
> +
> + /* If port is disabled don't accept any frames */
> + if (p->state == BR_STATE_DISABLED)
> + return 0;
> +
> + br = p->br;
> + mrp = br_mrp_find_port(br, p);
> + if (unlikely(!mrp))
> + return 0;
> +
> + p_port = rcu_dereference(mrp->p_port);
> + if (!p_port)
> + return 0;
> +
> + s_port = rcu_dereference(mrp->s_port);
> + if (!s_port)
> + return 0;
> +
> + /* If the role is MRM then don't forward the frames */
> + if (mrp->ring_role == BR_MRP_RING_ROLE_MRM) {
> + br_mrp_mrm_process(mrp, p, skb);
> + return 1;
> + }
> +
> + /* Clone the frame and forward it on the other MRP port */
> + nskb = skb_clone(skb, GFP_ATOMIC);
> + if (!nskb)
> + return 0;
> +
> + p_dev = p_port->dev;
> + s_dev = s_port->dev;
> +
> + if (p_dev == dev)
> + d_dev = s_dev;
> + else
> + d_dev = p_dev;
> +
> + nskb->dev = d_dev;
> + skb_push(nskb, ETH_HLEN);
> + dev_queue_xmit(nskb);
> +
> + return 1;
> +}
> +
> +/* Check if the frame was received on a port that is part of MRP ring
> + * and if the frame has MRP eth. In that case process the frame otherwise do
> + * normal forwarding.
> + * note: already called with rcu_read_lock
> + */
> +int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
> +{
> + /* If there is no MRP instance do normal forwarding */
> + if (likely(!(p->flags & BR_MRP_AWARE)))
> + goto out;
> +
> + if (unlikely(skb->protocol == htons(ETH_P_MRP)))
> + return br_mrp_rcv(p, skb, p->dev);
> +
> +out:
> + return 0;
> +}
> +
> +bool br_mrp_enabled(struct net_bridge *br)
> +{
> + return !list_empty(&br->mrp_list);
> +}
> diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c
> new file mode 100644
> index 000000000000..b982db14bbf4
> --- /dev/null
> +++ b/net/bridge/br_mrp_netlink.c
> @@ -0,0 +1,29 @@
> +// 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"
> +
> +int br_mrp_port_open(struct net_device *dev, u8 loc)
> +{
> + struct net_bridge_port *p;
> + int err = 0;
> +
> + p = br_port_get_rcu(dev);
> + if (!p) {
> + err = -EINVAL;
> + goto out;
> + }
> +
> + if (loc)
> + p->flags |= BR_MRP_LOST_CONT;
> + else
> + p->flags &= ~BR_MRP_LOST_CONT;
> +
> + br_ifinfo_notify(RTM_NEWLINK, NULL, p);
> +
> +out:
> + return err;
> +}
>
The 04/24/2020 16:47, Nikolay Aleksandrov wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
> On 22/04/2020 19:18, Horatiu Vultur wrote:
> > Implement the MRP API.
> >
> > In case the HW can't generate MRP Test frames then the SW will try to generate
> > the frames. In case that also the SW will fail in generating the frames then a
> > error is return to the userspace. The userspace is responsible to generate all
> > the other MRP frames regardless if the test frames are generated by HW or SW.
> >
> > The forwarding/termination of MRP frames is happening in the kernel and is done
> > by the MRP instance. The userspace application doesn't do the forwarding.
> >
> > Signed-off-by: Horatiu Vultur <[email protected]>
> > ---
> > net/bridge/Makefile | 2 +-
> > net/bridge/br_mrp.c | 556 ++++++++++++++++++++++++++++++++++++
> > net/bridge/br_mrp_netlink.c | 29 ++
> > 3 files changed, 586 insertions(+), 1 deletion(-)
> > create mode 100644 net/bridge/br_mrp.c
> > create mode 100644 net/bridge/br_mrp_netlink.c
> >
> > diff --git a/net/bridge/Makefile b/net/bridge/Makefile
> > index 3cacf9dd78d5..ccb394236fbd 100644
> > --- a/net/bridge/Makefile
> > +++ b/net/bridge/Makefile
> > @@ -26,4 +26,4 @@ bridge-$(CONFIG_NET_SWITCHDEV) += br_switchdev.o
> >
> > obj-$(CONFIG_NETFILTER) += netfilter/
> >
> > -bridge-$(CONFIG_BRIDGE_MRP) += br_mrp_switchdev.o
> > +bridge-$(CONFIG_BRIDGE_MRP) += br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o
> > diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
> > new file mode 100644
> > index 000000000000..8b1e85c11e3b
> > --- /dev/null
> > +++ b/net/bridge/br_mrp.c
> > @@ -0,0 +1,556 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +
> > +#include <linux/mrp_bridge.h>
> > +#include "br_private_mrp.h"
> > +
> > +static const u8 mrp_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x1 };
> > +
> > +static struct net_bridge_port *br_mrp_get_port(struct net_bridge *br,
> > + u32 ifindex)
> > +{
> > + struct net_bridge_port *res = NULL;
> > + struct net_bridge_port *port;
> > +
> > + list_for_each_entry(port, &br->port_list, list) {
> > + if (port->dev->ifindex == ifindex) {
> > + res = port;
> > + break;
> > + }
> > + }
> > +
> > + return res;
> > +}
> > +
> > +static struct br_mrp *br_mrp_find_id(struct net_bridge *br, u32 ring_id)
> > +{
> > + struct br_mrp *res = NULL;
> > + struct br_mrp *mrp;
> > +
> > + list_for_each_entry_rcu(mrp, &br->mrp_list, list,
> > + lockdep_rtnl_is_held()) {
> > + if (mrp->ring_id == ring_id) {
> > + res = mrp;
> > + break;
> > + }
> > + }
> > +
> > + return res;
> > +}
> > +
> > +static struct br_mrp *br_mrp_find_port(struct net_bridge *br,
> > + struct net_bridge_port *p)
> > +{
> > + struct br_mrp *res = NULL;
> > + struct br_mrp *mrp;
> > +
> > + list_for_each_entry_rcu(mrp, &br->mrp_list, list,
> > + lockdep_rtnl_is_held()) {
> > + if (rcu_access_pointer(mrp->p_port) == p ||
> > + rcu_access_pointer(mrp->s_port) == p) {
> > + res = mrp;
> > + break;
> > + }
> > + }
> > +
> > + return res;
> > +}
> > +
> > +static int br_mrp_next_seq(struct br_mrp *mrp)
> > +{
> > + mrp->seq_id++;
> > + return mrp->seq_id;
> > +}
> > +
> > +static struct sk_buff *br_mrp_skb_alloc(struct net_bridge_port *p,
> > + const u8 *src, const u8 *dst)
> > +{
> > + struct ethhdr *eth_hdr;
> > + struct sk_buff *skb;
> > + u16 *version;
> > +
> > + skb = dev_alloc_skb(MRP_MAX_FRAME_LENGTH);
> > + if (!skb)
> > + return NULL;
> > +
> > + skb->dev = p->dev;
> > + skb->protocol = htons(ETH_P_MRP);
> > + skb->priority = MRP_FRAME_PRIO;
> > + skb_reserve(skb, sizeof(*eth_hdr));
> > +
> > + eth_hdr = skb_push(skb, sizeof(*eth_hdr));
> > + ether_addr_copy(eth_hdr->h_dest, dst);
> > + ether_addr_copy(eth_hdr->h_source, src);
> > + eth_hdr->h_proto = htons(ETH_P_MRP);
> > +
> > + version = skb_put(skb, sizeof(*version));
> > + *version = cpu_to_be16(MRP_VERSION);
> > +
> > + return skb;
> > +}
> > +
> > +static void br_mrp_skb_tlv(struct sk_buff *skb,
> > + enum br_mrp_tlv_header_type type,
> > + u8 length)
> > +{
> > + struct br_mrp_tlv_hdr *hdr;
> > +
> > + hdr = skb_put(skb, sizeof(*hdr));
> > + hdr->type = type;
> > + hdr->length = length;
> > +}
> > +
> > +static void br_mrp_skb_common(struct sk_buff *skb, struct br_mrp *mrp)
> > +{
> > + struct br_mrp_common_hdr *hdr;
> > +
> > + br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_COMMON, sizeof(*hdr));
> > +
> > + hdr = skb_put(skb, sizeof(*hdr));
> > + hdr->seq_id = cpu_to_be16(br_mrp_next_seq(mrp));
> > + memset(hdr->domain, 0xff, MRP_DOMAIN_UUID_LENGTH);
> > +}
> > +
> > +static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
> > + struct net_bridge_port *p,
> > + enum br_mrp_port_role_type port_role)
> > +{
> > + struct br_mrp_ring_test_hdr *hdr = NULL;
> > + struct sk_buff *skb = NULL;
> > +
> > + if (!p)
> > + return NULL;
> > +
> > + skb = br_mrp_skb_alloc(p, p->dev->dev_addr, mrp_test_dmac);
> > + if (!skb)
> > + return NULL;
> > +
> > + br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_RING_TEST, sizeof(*hdr));
> > + hdr = skb_put(skb, sizeof(*hdr));
> > +
> > + hdr->prio = cpu_to_be16(MRP_DEFAULT_PRIO);
> > + ether_addr_copy(hdr->sa, p->br->dev->dev_addr);
> > + hdr->port_role = cpu_to_be16(port_role);
> > + hdr->state = cpu_to_be16(mrp->ring_state);
> > + hdr->transitions = cpu_to_be16(mrp->ring_transitions);
> > + hdr->timestamp = cpu_to_be32(jiffies_to_msecs(jiffies));
> > +
> > + br_mrp_skb_common(skb, mrp);
> > + br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_END, 0x0);
> > +
> > + return skb;
> > +}
> > +
> > +static void br_mrp_test_work_expired(struct work_struct *work)
> > +{
> > + struct delayed_work *del_work = to_delayed_work(work);
> > + struct br_mrp *mrp = container_of(del_work, struct br_mrp, test_work);
> > + struct net_bridge_port *p;
> > + bool notify_open = false;
> > + struct sk_buff *skb;
> > +
> > + if (time_before_eq(mrp->test_end, jiffies))
> > + return;
> > +
> > + if (mrp->test_count_miss < mrp->test_max_miss) {
> > + mrp->test_count_miss++;
> > + } else {
> > + /* Notify that the ring is open only if the ring state is
> > + * closed, otherwise it would continue to notify at every
> > + * interval.
> > + */
> > + if (mrp->ring_state == BR_MRP_RING_STATE_CLOSED)
> > + notify_open = true;
> > + }
> > +
> > + rcu_read_lock();
> > +
> > + p = rcu_dereference(mrp->p_port);
> > + if (p) {
> > + skb = br_mrp_alloc_test_skb(mrp, p, BR_MRP_PORT_ROLE_PRIMARY);
> > + if (!skb)
> > + goto out;
> > +
> > + skb_reset_network_header(skb);
> > + dev_queue_xmit(skb);
> > +
> > + if (notify_open && !mrp->ring_role_offloaded)
> > + br_mrp_port_open(p->dev, true);
> > + }
> > +
> > + p = rcu_dereference(mrp->s_port);
> > + if (p) {
> > + skb = br_mrp_alloc_test_skb(mrp, p, BR_MRP_PORT_ROLE_SECONDARY);
> > + if (!skb)
> > + goto out;
> > +
> > + skb_reset_network_header(skb);
> > + dev_queue_xmit(skb);
> > +
> > + if (notify_open && !mrp->ring_role_offloaded)
> > + br_mrp_port_open(p->dev, true);
> > + }
> > +
> > +out:
> > + rcu_read_unlock();
> > +
> > + queue_delayed_work(system_wq, &mrp->test_work,
> > + usecs_to_jiffies(mrp->test_interval));
> > +}
> > +
> > +/* Deletes the MRP instance.
> > + * note: called under rtnl_lock
> > + */
> > +static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp)
> > +{
> > + struct net_bridge_port *p;
> > +
> > + /* Stop sending MRP_Test frames */
> > + cancel_delayed_work_sync(&mrp->test_work);
> > + br_mrp_switchdev_send_ring_test(br, mrp, 0, 0, 0);
> > +
> > + br_mrp_switchdev_del(br, mrp);
> > +
> > + /* Reset the ports */
> > + p = rtnl_dereference(mrp->p_port);
> > + if (p) {
> > + spin_lock_bh(&br->lock);
> > + p->state = BR_STATE_FORWARDING;
> > + p->flags &= ~BR_MRP_AWARE;
> > + spin_unlock_bh(&br->lock);
> > + br_mrp_port_switchdev_set_state(p, BR_STATE_FORWARDING);
> > + rcu_assign_pointer(mrp->p_port, NULL);
> > + }
> > +
> > + p = rtnl_dereference(mrp->s_port);
> > + if (p) {
> > + spin_lock_bh(&br->lock);
> > + p->state = BR_STATE_FORWARDING;
> > + p->flags &= ~BR_MRP_AWARE;
> > + spin_unlock_bh(&br->lock);
> > + br_mrp_port_switchdev_set_state(p, BR_STATE_FORWARDING);
> > + rcu_assign_pointer(mrp->s_port, NULL);
> > + }
> > +
> > + list_del_rcu(&mrp->list);
> > + kfree_rcu(mrp, rcu);
> > +}
> > +
> > +/* Adds a new MRP instance.
> > + * note: called under rtnl_lock
> > + */
> > +int br_mrp_add(struct net_bridge *br, struct br_mrp_instance *instance)
> > +{
> > + struct net_bridge_port *p;
> > + struct br_mrp *mrp;
> > + int err;
> > +
> > + /* If the ring exists, it is not possible to create another one with the
> > + * same ring_id
> > + */
> > + mrp = br_mrp_find_id(br, instance->ring_id);
> > + if (mrp)
> > + return -EINVAL;
> > +
> > + if (!br_mrp_get_port(br, instance->p_ifindex) ||
> > + !br_mrp_get_port(br, instance->s_ifindex))
> > + return -EINVAL;
> > +
> > + mrp = kzalloc(sizeof(*mrp), GFP_KERNEL);
> > + if (!mrp)
> > + return -ENOMEM;
> > +
> > + mrp->ring_id = instance->ring_id;
> > +
> > + p = br_mrp_get_port(br, instance->p_ifindex);
> > + spin_lock_bh(&br->lock);
> > + p->state = BR_STATE_FORWARDING;
> > + p->flags |= BR_MRP_AWARE;
> > + spin_unlock_bh(&br->lock);
> > + rcu_assign_pointer(mrp->p_port, p);
> > +
> > + p = br_mrp_get_port(br, instance->s_ifindex);
> > + spin_lock_bh(&br->lock);
> > + p->state = BR_STATE_FORWARDING;
> > + p->flags |= BR_MRP_AWARE;
> > + spin_unlock_bh(&br->lock);
> > + rcu_assign_pointer(mrp->s_port, p);
> > +
> > + INIT_DELAYED_WORK(&mrp->test_work, br_mrp_test_work_expired);
> > + list_add_tail_rcu(&mrp->list, &br->mrp_list);
> > +
> > + err = br_mrp_switchdev_add(br, mrp);
> > + if (err)
> > + goto delete_mrp;
> > +
> > + return 0;
> > +
> > +delete_mrp:
> > + br_mrp_del_impl(br, mrp);
> > +
> > + return err;
> > +}
> > +
> > +/* Deletes the MRP instance from which the port is part of
> > + * note: called under rtnl_lock
> > + */
> > +void br_mrp_port_del(struct net_bridge *br, struct net_bridge_port *p)
> > +{
> > + struct br_mrp *mrp = br_mrp_find_port(br, p);
> > +
> > + /* If the port is not part of a MRP instance just bail out */
> > + if (!mrp)
> > + return;
> > +
> > + br_mrp_del_impl(br, mrp);
> > +}
> > +
> > +/* Deletes existing MRP instance based on ring_id
> > + * note: called under rtnl_lock
> > + */
> > +int br_mrp_del(struct net_bridge *br, struct br_mrp_instance *instance)
> > +{
> > + struct br_mrp *mrp = br_mrp_find_id(br, instance->ring_id);
> > +
> > + if (!mrp)
> > + return -EINVAL;
> > +
> > + br_mrp_del_impl(br, mrp);
> > +
> > + return 0;
> > +}
> > +
> > +/* Set port state, port state can be forwarding, blocked or disabled
> > + * note: already called with rcu_read_lock
> > + */
>
> But is that true? I think it's called under rtnl.
Actually is not true, it is under rtnl_lock. I will update it in the
next patch.
>
> > +int br_mrp_set_port_state(struct net_bridge_port *p,
> > + enum br_mrp_port_state_type state)
> > +{
> > + if (!p || !(p->flags & BR_MRP_AWARE))
> > + return -EINVAL;
> > +
> > + spin_lock_bh(&p->br->lock);
> > +
> > + if (state == BR_MRP_PORT_STATE_FORWARDING)
> > + p->state = BR_STATE_FORWARDING;
> > + else
> > + p->state = BR_STATE_BLOCKING;
> > +
> > + spin_unlock_bh(&p->br->lock);
> > +
> > + br_mrp_port_switchdev_set_state(p, state);
> > +
> > + return 0;
> > +}
> > +
> > +/* Set port role, port role can be primary or secondary
> > + * note: already called with rcu_read_lock
> > + */
>
> same here
>
> > +int br_mrp_set_port_role(struct net_bridge_port *p,
> > + struct br_mrp_port_role *role)
> > +{
> > + struct br_mrp *mrp;
> > +
> > + if (!p || !(p->flags & BR_MRP_AWARE))
> > + return -EINVAL;
> > +
> > + mrp = br_mrp_find_id(p->br, role->ring_id);
> > +
> > + if (!mrp)
> > + return -EINVAL;
> > +
> > + if (role->role == BR_MRP_PORT_ROLE_PRIMARY)
> > + rcu_assign_pointer(mrp->p_port, p);
> > + else
> > + rcu_assign_pointer(mrp->s_port, p);
> > +
> > + br_mrp_port_switchdev_set_role(p, role->role);
> > +
> > + return 0;
> > +}
> > +
> > +/* Set ring state, ring state can be only Open or Closed
> > + * note: already called with rcu_read_lock
> > + */
>
> same here
>
> > +int br_mrp_set_ring_state(struct net_bridge *br,
> > + struct br_mrp_ring_state *state)
> > +{
> > + struct br_mrp *mrp = br_mrp_find_id(br, state->ring_id);
> > +
> > + if (!mrp)
> > + return -EINVAL;
> > +
> > + if (mrp->ring_state == BR_MRP_RING_STATE_CLOSED &&
> > + state->ring_state != BR_MRP_RING_STATE_CLOSED)
> > + mrp->ring_transitions++;
> > +
> > + mrp->ring_state = state->ring_state;
> > +
> > + br_mrp_switchdev_set_ring_state(br, mrp, state->ring_state);
> > +
> > + return 0;
> > +}
> > +
> > +/* Set ring role, ring role can be only MRM(Media Redundancy Manager) or
> > + * MRC(Media Redundancy Client).
> > + * note: already called with rcu_read_lock
> > + */
>
> and here
>
> > +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;
> > +
> > + if (!mrp)
> > + return -EINVAL;
> > +
> > + 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;
> > +
> > + /* 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
> > + * anymore. For example if the role ir MRM then the HW will notify the
> > + * 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;
> > +
> > + return 0;
> > +}
> > +
> > +/* Start to generate MRP test frames, the frames are generated by HW and if it
> > + * fails, they are generated by the SW.
> > + * note: already called with rcu_read_lock
> > + */
>
> here too
>
> > +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);
> > +
> > + if (!mrp)
> > + return -EINVAL;
> > +
> > + /* Try to push is to the HW and if it fails then continue to generate in
>
> s/is/it/ ?
>
> > + * SW and if that also fails then return error
> > + */
> > + if (!br_mrp_switchdev_send_ring_test(br, mrp, test->interval,
> > + test->max_miss, test->period))
> > + return 0;
> > +
> > + mrp->test_interval = test->interval;
> > + mrp->test_end = jiffies + usecs_to_jiffies(test->period);
> > + mrp->test_max_miss = test->max_miss;
> > + mrp->test_count_miss = 0;
> > + queue_delayed_work(system_wq, &mrp->test_work,
> > + usecs_to_jiffies(test->interval));
> > +
> > + return 0;
> > +}
> > +
> > +/* Process only MRP Test frame. All the other MRP frames are processed by
> > + * userspace application
> > + * note: already called with rcu_read_lock
> > + */
> > +static void br_mrp_mrm_process(struct br_mrp *mrp, struct net_bridge_port *port,
> > + struct sk_buff *skb)
> > +{
> > + struct br_mrp_tlv_hdr *hdr;
> > +
> > + hdr = (struct br_mrp_tlv_hdr *)(skb->data + sizeof(uint16_t));
> > +
>
> Missed this in my previous reviews, "+ sizeof(uint16_t)" is that because of MRP_VERSION ?
> I'd use skb_header_pointer() and also please drop the unnecessary newline here.
Yes the "+ sizeof(uint16_t) is because of MRP_VERSION. In the next a
patch I will add a comment why it needs skip the first 2 bytes of the
data. Also I will use skb_header_pointer.
>
> > + if (!hdr)
> > + return;
> > +
> > + if (hdr->type != BR_MRP_TLV_HEADER_RING_TEST)
> > + return;
> > +
> > + mrp->test_count_miss = 0;
> > +
> > + /* Notify the userspace that the ring is closed only when the ring is
> > + * not closed
> > + */
> > + if (mrp->ring_state != BR_MRP_RING_STATE_CLOSED)
> > + br_mrp_port_open(port->dev, false);
> > +}
> > +
> > +/* This will just forward the frame to the other mrp ring port(MRC role) or will
> > + * not do anything.
> > + * note: already called with rcu_read_lock
> > + */
> > +static int br_mrp_rcv(struct net_bridge_port *p,
> > + struct sk_buff *skb, struct net_device *dev)
> > +{
> > + struct net_device *s_dev, *p_dev, *d_dev;
> > + struct net_bridge_port *p_port, *s_port;
> > + struct net_bridge *br;
> > + struct sk_buff *nskb;
> > + struct br_mrp *mrp;
> > +
> > + /* If port is disabled don't accept any frames */
> > + if (p->state == BR_STATE_DISABLED)
> > + return 0;
> > +
> > + br = p->br;
> > + mrp = br_mrp_find_port(br, p);
> > + if (unlikely(!mrp))
> > + return 0;
> > +
> > + p_port = rcu_dereference(mrp->p_port);
> > + if (!p_port)
> > + return 0;
> > +
> > + s_port = rcu_dereference(mrp->s_port);
> > + if (!s_port)
> > + return 0;
> > +
> > + /* If the role is MRM then don't forward the frames */
> > + if (mrp->ring_role == BR_MRP_RING_ROLE_MRM) {
> > + br_mrp_mrm_process(mrp, p, skb);
> > + return 1;
> > + }
> > +
> > + /* Clone the frame and forward it on the other MRP port */
> > + nskb = skb_clone(skb, GFP_ATOMIC);
> > + if (!nskb)
> > + return 0;
> > +
> > + p_dev = p_port->dev;
> > + s_dev = s_port->dev;
> > +
> > + if (p_dev == dev)
> > + d_dev = s_dev;
> > + else
> > + d_dev = p_dev;
> > +
> > + nskb->dev = d_dev;
> > + skb_push(nskb, ETH_HLEN);
> > + dev_queue_xmit(nskb);
> > +
> > + return 1;
> > +}
> > +
> > +/* Check if the frame was received on a port that is part of MRP ring
> > + * and if the frame has MRP eth. In that case process the frame otherwise do
> > + * normal forwarding.
> > + * note: already called with rcu_read_lock
> > + */
> > +int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
> > +{
> > + /* If there is no MRP instance do normal forwarding */
> > + if (likely(!(p->flags & BR_MRP_AWARE)))
> > + goto out;
> > +
> > + if (unlikely(skb->protocol == htons(ETH_P_MRP)))
> > + return br_mrp_rcv(p, skb, p->dev);
> > +
> > +out:
> > + return 0;
> > +}
> > +
> > +bool br_mrp_enabled(struct net_bridge *br)
> > +{
> > + return !list_empty(&br->mrp_list);
> > +}
> > diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c
> > new file mode 100644
> > index 000000000000..b982db14bbf4
> > --- /dev/null
> > +++ b/net/bridge/br_mrp_netlink.c
> > @@ -0,0 +1,29 @@
> > +// 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"
> > +
> > +int br_mrp_port_open(struct net_device *dev, u8 loc)
> > +{
> > + struct net_bridge_port *p;
> > + int err = 0;
> > +
> > + p = br_port_get_rcu(dev);
> > + if (!p) {
> > + err = -EINVAL;
> > + goto out;
> > + }
> > +
> > + if (loc)
> > + p->flags |= BR_MRP_LOST_CONT;
> > + else
> > + p->flags &= ~BR_MRP_LOST_CONT;
> > +
> > + br_ifinfo_notify(RTM_NEWLINK, NULL, p);
> > +
> > +out:
> > + return err;
> > +}
> >
>
--
/Horatiu