summaryrefslogtreecommitdiff
path: root/net/bridge/br_mrp.c
diff options
context:
space:
mode:
authorHoratiu Vultur <horatiu.vultur@microchip.com>2020-05-30 18:09:48 +0000
committerDavid S. Miller <davem@davemloft.net>2020-06-01 11:56:11 -0700
commitc6676e7d62cfb5cb7c1c5320a26f3634a11afdb0 (patch)
treeac66bfc38aaea7b6f0cd6dfc0cd4a0a884e88a4d /net/bridge/br_mrp.c
parent4b3a61b030d1131dcf3633a276158a3d0a435a47 (diff)
bridge: mrp: Add support for role MRA
A node that has the MRA role, it can behave as MRM or MRC. Initially it starts as MRM and sends MRP_Test frames on both ring ports. If it detects that there are MRP_Test send by another MRM, then it checks if these frames have a lower priority than itself. In this case it would send MRP_Nack frames to notify the other node that it needs to stop sending MRP_Test frames. If it receives a MRP_Nack frame then it stops sending MRP_Test frames and starts to behave as a MRC but it would continue to monitor the MRP_Test frames send by MRM. If at a point the MRM stops to send MRP_Test frames it would get the MRM role and start to send MRP_Test frames. Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge/br_mrp.c')
-rw-r--r--net/bridge/br_mrp.c125
1 files changed, 106 insertions, 19 deletions
diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
index f8fd037219fe..24986ec7d38c 100644
--- a/net/bridge/br_mrp.c
+++ b/net/bridge/br_mrp.c
@@ -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);
@@ -452,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,
@@ -464,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));
@@ -510,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
@@ -546,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)