summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2020-06-01 11:56:11 -0700
committerDavid S. Miller <davem@davemloft.net>2020-06-01 11:56:11 -0700
commit2a67ab99aad078504ded787211351a12717c6e96 (patch)
treeac66bfc38aaea7b6f0cd6dfc0cd4a0a884e88a4d /net
parent3e1c6846b9e108740ef8a37be80314053f5dd52a (diff)
parentc6676e7d62cfb5cb7c1c5320a26f3634a11afdb0 (diff)
Merge branch 'bridge-mrp-Add-support-for-MRA-role'
Horatiu Vultur says: ==================== bridge: mrp: Add support for MRA role This patch series extends the MRP with the MRA role. A node that has the MRA role can behave as a MRM or as a MRC. In case there are multiple nodes in the topology that has the MRA role then only one node can behave as MRM and all the others need to be have as MRC. The node that has the higher priority(lower value) will behave as MRM. A node that has the MRA role and behaves as MRC, it just needs to forward the MRP_Test frames between the ring ports but also it needs to detect in case it stops receiving MRP_Test frames. In that case it would try to behave as MRM. v2: - add new patch that fixes sparse warnings - fix parsing of prio attribute ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/bridge/br_mrp.c128
-rw-r--r--net/bridge/br_mrp_netlink.c11
-rw-r--r--net/bridge/br_mrp_switchdev.c5
-rw-r--r--net/bridge/br_private_mrp.h5
4 files changed, 127 insertions, 22 deletions
diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
index 8ea59504ef47..24986ec7d38c 100644
--- a/net/bridge/br_mrp.c
+++ b/net/bridge/br_mrp.c
@@ -147,7 +147,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
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);
+ hdr->prio = cpu_to_be16(mrp->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);
@@ -160,6 +160,16 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
return skb;
}
+/* This function is continuously called in the following cases:
+ * - when node role is MRM, in this case test_monitor is always set to false
+ * because it needs to notify the userspace that the ring is open and needs to
+ * send MRP_Test frames
+ * - when node role is MRA, there are 2 subcases:
+ * - when MRA behaves as MRM, in this case is similar with MRM role
+ * - when MRA behaves as MRC, in this case test_monitor is set to true,
+ * because it needs to detect when it stops seeing MRP_Test frames
+ * from MRM node but it doesn't need to send MRP_Test frames.
+ */
static void br_mrp_test_work_expired(struct work_struct *work)
{
struct delayed_work *del_work = to_delayed_work(work);
@@ -177,8 +187,14 @@ static void br_mrp_test_work_expired(struct work_struct *work)
/* Notify that the ring is open only if the ring state is
* closed, otherwise it would continue to notify at every
* interval.
+ * Also notify that the ring is open when the node has the
+ * role MRA and behaves as MRC. The reason is that the
+ * userspace needs to know when the MRM stopped sending
+ * MRP_Test frames so that the current node to try to take
+ * the role of a MRM.
*/
- if (mrp->ring_state == BR_MRP_RING_STATE_CLOSED)
+ if (mrp->ring_state == BR_MRP_RING_STATE_CLOSED ||
+ mrp->test_monitor)
notify_open = true;
}
@@ -186,12 +202,15 @@ static void br_mrp_test_work_expired(struct work_struct *work)
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 (!mrp->test_monitor) {
+ 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);
@@ -199,12 +218,15 @@ static void br_mrp_test_work_expired(struct work_struct *work)
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 (!mrp->test_monitor) {
+ 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);
@@ -227,7 +249,7 @@ static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp)
/* 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_send_ring_test(br, mrp, 0, 0, 0, 0);
br_mrp_switchdev_del(br, mrp);
@@ -290,6 +312,7 @@ int br_mrp_add(struct net_bridge *br, struct br_mrp_instance *instance)
return -ENOMEM;
mrp->ring_id = instance->ring_id;
+ mrp->prio = instance->prio;
p = br_mrp_get_port(br, instance->p_ifindex);
spin_lock_bh(&br->lock);
@@ -451,8 +474,8 @@ int br_mrp_set_ring_role(struct net_bridge *br,
return 0;
}
-/* Start to generate MRP test frames, the frames are generated by HW and if it
- * fails, they are generated by the SW.
+/* Start to generate or monitor MRP test frames, the frames are generated by
+ * HW and if it fails, they are generated by the SW.
* note: already called with rtnl_lock
*/
int br_mrp_start_test(struct net_bridge *br,
@@ -463,16 +486,18 @@ int br_mrp_start_test(struct net_bridge *br,
if (!mrp)
return -EINVAL;
- /* Try to push it to the HW and if it fails then continue to generate in
- * SW and if that also fails then return error
+ /* Try to push it to the HW and if it fails then continue with SW
+ * implementation and if that also fails then return error.
*/
if (!br_mrp_switchdev_send_ring_test(br, mrp, test->interval,
- test->max_miss, test->period))
+ test->max_miss, test->period,
+ test->monitor))
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_monitor = test->monitor;
mrp->test_count_miss = 0;
queue_delayed_work(system_wq, &mrp->test_work,
usecs_to_jiffies(test->interval));
@@ -509,6 +534,57 @@ static void br_mrp_mrm_process(struct br_mrp *mrp, struct net_bridge_port *port,
br_mrp_port_open(port->dev, false);
}
+/* Determin if the test hdr has a better priority than the node */
+static bool br_mrp_test_better_than_own(struct br_mrp *mrp,
+ struct net_bridge *br,
+ const struct br_mrp_ring_test_hdr *hdr)
+{
+ u16 prio = be16_to_cpu(hdr->prio);
+
+ if (prio < mrp->prio ||
+ (prio == mrp->prio &&
+ ether_addr_to_u64(hdr->sa) < ether_addr_to_u64(br->dev->dev_addr)))
+ return true;
+
+ return false;
+}
+
+/* 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_mra_process(struct br_mrp *mrp, struct net_bridge *br,
+ struct net_bridge_port *port,
+ struct sk_buff *skb)
+{
+ const struct br_mrp_ring_test_hdr *test_hdr;
+ struct br_mrp_ring_test_hdr _test_hdr;
+ const struct br_mrp_tlv_hdr *hdr;
+ struct br_mrp_tlv_hdr _hdr;
+
+ /* Each MRP header starts with a version field which is 16 bits.
+ * Therefore skip the version and get directly the TLV header.
+ */
+ hdr = skb_header_pointer(skb, sizeof(uint16_t), sizeof(_hdr), &_hdr);
+ if (!hdr)
+ return;
+
+ if (hdr->type != BR_MRP_TLV_HEADER_RING_TEST)
+ return;
+
+ test_hdr = skb_header_pointer(skb, sizeof(uint16_t) + sizeof(_hdr),
+ sizeof(_test_hdr), &_test_hdr);
+ if (!test_hdr)
+ return;
+
+ /* Only frames that have a better priority than the node will
+ * clear the miss counter because otherwise the node will need to behave
+ * as MRM.
+ */
+ if (br_mrp_test_better_than_own(mrp, br, test_hdr))
+ mrp->test_count_miss = 0;
+}
+
/* 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
@@ -545,6 +621,18 @@ static int br_mrp_rcv(struct net_bridge_port *p,
return 1;
}
+ /* If the role is MRA then don't forward the frames if it behaves as
+ * MRM node
+ */
+ if (mrp->ring_role == BR_MRP_RING_ROLE_MRA) {
+ if (!mrp->test_monitor) {
+ br_mrp_mrm_process(mrp, p, skb);
+ return 1;
+ }
+
+ br_mrp_mra_process(mrp, br, p, skb);
+ }
+
/* Clone the frame and forward it on the other MRP port */
nskb = skb_clone(skb, GFP_ATOMIC);
if (!nskb)
diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c
index d9de780d2ce0..34b3a8776991 100644
--- a/net/bridge/br_mrp_netlink.c
+++ b/net/bridge/br_mrp_netlink.c
@@ -22,6 +22,7 @@ br_mrp_instance_policy[IFLA_BRIDGE_MRP_INSTANCE_MAX + 1] = {
[IFLA_BRIDGE_MRP_INSTANCE_RING_ID] = { .type = NLA_U32 },
[IFLA_BRIDGE_MRP_INSTANCE_P_IFINDEX] = { .type = NLA_U32 },
[IFLA_BRIDGE_MRP_INSTANCE_S_IFINDEX] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_MRP_INSTANCE_PRIO] = { .type = NLA_U16 },
};
static int br_mrp_instance_parse(struct net_bridge *br, struct nlattr *attr,
@@ -49,6 +50,10 @@ static int br_mrp_instance_parse(struct net_bridge *br, struct nlattr *attr,
inst.ring_id = nla_get_u32(tb[IFLA_BRIDGE_MRP_INSTANCE_RING_ID]);
inst.p_ifindex = nla_get_u32(tb[IFLA_BRIDGE_MRP_INSTANCE_P_IFINDEX]);
inst.s_ifindex = nla_get_u32(tb[IFLA_BRIDGE_MRP_INSTANCE_S_IFINDEX]);
+ inst.prio = MRP_DEFAULT_PRIO;
+
+ if (tb[IFLA_BRIDGE_MRP_INSTANCE_PRIO])
+ inst.prio = nla_get_u16(tb[IFLA_BRIDGE_MRP_INSTANCE_PRIO]);
if (cmd == RTM_SETLINK)
return br_mrp_add(br, &inst);
@@ -191,6 +196,7 @@ br_mrp_start_test_policy[IFLA_BRIDGE_MRP_START_TEST_MAX + 1] = {
[IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = { .type = NLA_U32 },
[IFLA_BRIDGE_MRP_START_TEST_MAX_MISS] = { .type = NLA_U32 },
[IFLA_BRIDGE_MRP_START_TEST_PERIOD] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_MRP_START_TEST_MONITOR] = { .type = NLA_U32 },
};
static int br_mrp_start_test_parse(struct net_bridge *br, struct nlattr *attr,
@@ -220,6 +226,11 @@ static int br_mrp_start_test_parse(struct net_bridge *br, struct nlattr *attr,
test.interval = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_TEST_INTERVAL]);
test.max_miss = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_TEST_MAX_MISS]);
test.period = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_TEST_PERIOD]);
+ test.monitor = false;
+
+ if (tb[IFLA_BRIDGE_MRP_START_TEST_MONITOR])
+ test.monitor =
+ nla_get_u32(tb[IFLA_BRIDGE_MRP_START_TEST_MONITOR]);
return br_mrp_start_test(br, &test);
}
diff --git a/net/bridge/br_mrp_switchdev.c b/net/bridge/br_mrp_switchdev.c
index 51cb1d5a24b4..0da68a0da4b5 100644
--- a/net/bridge/br_mrp_switchdev.c
+++ b/net/bridge/br_mrp_switchdev.c
@@ -12,6 +12,7 @@ int br_mrp_switchdev_add(struct net_bridge *br, struct br_mrp *mrp)
.p_port = rtnl_dereference(mrp->p_port)->dev,
.s_port = rtnl_dereference(mrp->s_port)->dev,
.ring_id = mrp->ring_id,
+ .prio = mrp->prio,
};
int err;
@@ -64,7 +65,8 @@ int br_mrp_switchdev_set_ring_role(struct net_bridge *br,
int br_mrp_switchdev_send_ring_test(struct net_bridge *br,
struct br_mrp *mrp, u32 interval,
- u8 max_miss, u32 period)
+ u8 max_miss, u32 period,
+ bool monitor)
{
struct switchdev_obj_ring_test_mrp test = {
.obj.orig_dev = br->dev,
@@ -73,6 +75,7 @@ int br_mrp_switchdev_send_ring_test(struct net_bridge *br,
.max_miss = max_miss,
.ring_id = mrp->ring_id,
.period = period,
+ .monitor = monitor,
};
int err;
diff --git a/net/bridge/br_private_mrp.h b/net/bridge/br_private_mrp.h
index a0f53cc3ab85..33b255e38ffe 100644
--- a/net/bridge/br_private_mrp.h
+++ b/net/bridge/br_private_mrp.h
@@ -14,6 +14,7 @@ struct br_mrp {
struct net_bridge_port __rcu *s_port;
u32 ring_id;
+ u16 prio;
enum br_mrp_ring_role_type ring_role;
u8 ring_role_offloaded;
@@ -25,6 +26,7 @@ struct br_mrp {
unsigned long test_end;
u32 test_count_miss;
u32 test_max_miss;
+ bool test_monitor;
u32 seq_id;
@@ -51,7 +53,8 @@ int br_mrp_switchdev_set_ring_role(struct net_bridge *br, struct br_mrp *mrp,
int br_mrp_switchdev_set_ring_state(struct net_bridge *br, struct br_mrp *mrp,
enum br_mrp_ring_state_type state);
int br_mrp_switchdev_send_ring_test(struct net_bridge *br, struct br_mrp *mrp,
- u32 interval, u8 max_miss, u32 period);
+ u32 interval, u8 max_miss, u32 period,
+ bool monitor);
int br_mrp_port_switchdev_set_state(struct net_bridge_port *p,
enum br_mrp_port_state_type state);
int br_mrp_port_switchdev_set_role(struct net_bridge_port *p,