Skip to content

Commit c6676e7

Browse files
HoratiuVulturdavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 4b3a61b commit c6676e7

File tree

7 files changed

+159
-21
lines changed

7 files changed

+159
-21
lines changed

include/net/switchdev.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ struct switchdev_obj_ring_test_mrp {
130130
u8 max_miss;
131131
u32 ring_id;
132132
u32 period;
133+
bool monitor;
133134
};
134135

135136
#define SWITCHDEV_OBJ_RING_TEST_MRP(OBJ) \

include/uapi/linux/if_bridge.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ enum {
222222
IFLA_BRIDGE_MRP_START_TEST_INTERVAL,
223223
IFLA_BRIDGE_MRP_START_TEST_MAX_MISS,
224224
IFLA_BRIDGE_MRP_START_TEST_PERIOD,
225+
IFLA_BRIDGE_MRP_START_TEST_MONITOR,
225226
__IFLA_BRIDGE_MRP_START_TEST_MAX,
226227
};
227228

@@ -249,6 +250,7 @@ struct br_mrp_start_test {
249250
__u32 interval;
250251
__u32 max_miss;
251252
__u32 period;
253+
__u32 monitor;
252254
};
253255

254256
struct bridge_stp_xstats {

include/uapi/linux/mrp_bridge.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
#define MRP_DOMAIN_UUID_LENGTH 16
1212
#define MRP_VERSION 1
1313
#define MRP_FRAME_PRIO 7
14+
#define MRP_OUI_LENGTH 3
15+
#define MRP_MANUFACTURE_DATA_LENGTH 2
1416

1517
enum br_mrp_ring_role_type {
1618
BR_MRP_RING_ROLE_DISABLED,
1719
BR_MRP_RING_ROLE_MRC,
1820
BR_MRP_RING_ROLE_MRM,
21+
BR_MRP_RING_ROLE_MRA,
1922
};
2023

2124
enum br_mrp_ring_state_type {
@@ -43,13 +46,25 @@ enum br_mrp_tlv_header_type {
4346
BR_MRP_TLV_HEADER_RING_TOPO = 0x3,
4447
BR_MRP_TLV_HEADER_RING_LINK_DOWN = 0x4,
4548
BR_MRP_TLV_HEADER_RING_LINK_UP = 0x5,
49+
BR_MRP_TLV_HEADER_OPTION = 0x7f,
50+
};
51+
52+
enum br_mrp_sub_tlv_header_type {
53+
BR_MRP_SUB_TLV_HEADER_TEST_MGR_NACK = 0x1,
54+
BR_MRP_SUB_TLV_HEADER_TEST_PROPAGATE = 0x2,
55+
BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR = 0x3,
4656
};
4757

4858
struct br_mrp_tlv_hdr {
4959
__u8 type;
5060
__u8 length;
5161
};
5262

63+
struct br_mrp_sub_tlv_hdr {
64+
__u8 type;
65+
__u8 length;
66+
};
67+
5368
struct br_mrp_end_hdr {
5469
struct br_mrp_tlv_hdr hdr;
5570
};
@@ -81,4 +96,27 @@ struct br_mrp_ring_link_hdr {
8196
__be16 blocked;
8297
};
8398

99+
struct br_mrp_sub_opt_hdr {
100+
__u8 type;
101+
__u8 manufacture_data[MRP_MANUFACTURE_DATA_LENGTH];
102+
};
103+
104+
struct br_mrp_test_mgr_nack_hdr {
105+
__be16 prio;
106+
__u8 sa[ETH_ALEN];
107+
__be16 other_prio;
108+
__u8 other_sa[ETH_ALEN];
109+
};
110+
111+
struct br_mrp_test_prop_hdr {
112+
__be16 prio;
113+
__u8 sa[ETH_ALEN];
114+
__be16 other_prio;
115+
__u8 other_sa[ETH_ALEN];
116+
};
117+
118+
struct br_mrp_oui_hdr {
119+
__u8 oui[MRP_OUI_LENGTH];
120+
};
121+
84122
#endif

net/bridge/br_mrp.c

Lines changed: 106 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,16 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
160160
return skb;
161161
}
162162

163+
/* This function is continuously called in the following cases:
164+
* - when node role is MRM, in this case test_monitor is always set to false
165+
* because it needs to notify the userspace that the ring is open and needs to
166+
* send MRP_Test frames
167+
* - when node role is MRA, there are 2 subcases:
168+
* - when MRA behaves as MRM, in this case is similar with MRM role
169+
* - when MRA behaves as MRC, in this case test_monitor is set to true,
170+
* because it needs to detect when it stops seeing MRP_Test frames
171+
* from MRM node but it doesn't need to send MRP_Test frames.
172+
*/
163173
static void br_mrp_test_work_expired(struct work_struct *work)
164174
{
165175
struct delayed_work *del_work = to_delayed_work(work);
@@ -177,34 +187,46 @@ static void br_mrp_test_work_expired(struct work_struct *work)
177187
/* Notify that the ring is open only if the ring state is
178188
* closed, otherwise it would continue to notify at every
179189
* interval.
190+
* Also notify that the ring is open when the node has the
191+
* role MRA and behaves as MRC. The reason is that the
192+
* userspace needs to know when the MRM stopped sending
193+
* MRP_Test frames so that the current node to try to take
194+
* the role of a MRM.
180195
*/
181-
if (mrp->ring_state == BR_MRP_RING_STATE_CLOSED)
196+
if (mrp->ring_state == BR_MRP_RING_STATE_CLOSED ||
197+
mrp->test_monitor)
182198
notify_open = true;
183199
}
184200

185201
rcu_read_lock();
186202

187203
p = rcu_dereference(mrp->p_port);
188204
if (p) {
189-
skb = br_mrp_alloc_test_skb(mrp, p, BR_MRP_PORT_ROLE_PRIMARY);
190-
if (!skb)
191-
goto out;
192-
193-
skb_reset_network_header(skb);
194-
dev_queue_xmit(skb);
205+
if (!mrp->test_monitor) {
206+
skb = br_mrp_alloc_test_skb(mrp, p,
207+
BR_MRP_PORT_ROLE_PRIMARY);
208+
if (!skb)
209+
goto out;
210+
211+
skb_reset_network_header(skb);
212+
dev_queue_xmit(skb);
213+
}
195214

196215
if (notify_open && !mrp->ring_role_offloaded)
197216
br_mrp_port_open(p->dev, true);
198217
}
199218

200219
p = rcu_dereference(mrp->s_port);
201220
if (p) {
202-
skb = br_mrp_alloc_test_skb(mrp, p, BR_MRP_PORT_ROLE_SECONDARY);
203-
if (!skb)
204-
goto out;
205-
206-
skb_reset_network_header(skb);
207-
dev_queue_xmit(skb);
221+
if (!mrp->test_monitor) {
222+
skb = br_mrp_alloc_test_skb(mrp, p,
223+
BR_MRP_PORT_ROLE_SECONDARY);
224+
if (!skb)
225+
goto out;
226+
227+
skb_reset_network_header(skb);
228+
dev_queue_xmit(skb);
229+
}
208230

209231
if (notify_open && !mrp->ring_role_offloaded)
210232
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)
227249

228250
/* Stop sending MRP_Test frames */
229251
cancel_delayed_work_sync(&mrp->test_work);
230-
br_mrp_switchdev_send_ring_test(br, mrp, 0, 0, 0);
252+
br_mrp_switchdev_send_ring_test(br, mrp, 0, 0, 0, 0);
231253

232254
br_mrp_switchdev_del(br, mrp);
233255

@@ -452,8 +474,8 @@ int br_mrp_set_ring_role(struct net_bridge *br,
452474
return 0;
453475
}
454476

455-
/* Start to generate MRP test frames, the frames are generated by HW and if it
456-
* fails, they are generated by the SW.
477+
/* Start to generate or monitor MRP test frames, the frames are generated by
478+
* HW and if it fails, they are generated by the SW.
457479
* note: already called with rtnl_lock
458480
*/
459481
int br_mrp_start_test(struct net_bridge *br,
@@ -464,16 +486,18 @@ int br_mrp_start_test(struct net_bridge *br,
464486
if (!mrp)
465487
return -EINVAL;
466488

467-
/* Try to push it to the HW and if it fails then continue to generate in
468-
* SW and if that also fails then return error
489+
/* Try to push it to the HW and if it fails then continue with SW
490+
* implementation and if that also fails then return error.
469491
*/
470492
if (!br_mrp_switchdev_send_ring_test(br, mrp, test->interval,
471-
test->max_miss, test->period))
493+
test->max_miss, test->period,
494+
test->monitor))
472495
return 0;
473496

474497
mrp->test_interval = test->interval;
475498
mrp->test_end = jiffies + usecs_to_jiffies(test->period);
476499
mrp->test_max_miss = test->max_miss;
500+
mrp->test_monitor = test->monitor;
477501
mrp->test_count_miss = 0;
478502
queue_delayed_work(system_wq, &mrp->test_work,
479503
usecs_to_jiffies(test->interval));
@@ -510,6 +534,57 @@ static void br_mrp_mrm_process(struct br_mrp *mrp, struct net_bridge_port *port,
510534
br_mrp_port_open(port->dev, false);
511535
}
512536

537+
/* Determin if the test hdr has a better priority than the node */
538+
static bool br_mrp_test_better_than_own(struct br_mrp *mrp,
539+
struct net_bridge *br,
540+
const struct br_mrp_ring_test_hdr *hdr)
541+
{
542+
u16 prio = be16_to_cpu(hdr->prio);
543+
544+
if (prio < mrp->prio ||
545+
(prio == mrp->prio &&
546+
ether_addr_to_u64(hdr->sa) < ether_addr_to_u64(br->dev->dev_addr)))
547+
return true;
548+
549+
return false;
550+
}
551+
552+
/* Process only MRP Test frame. All the other MRP frames are processed by
553+
* userspace application
554+
* note: already called with rcu_read_lock
555+
*/
556+
static void br_mrp_mra_process(struct br_mrp *mrp, struct net_bridge *br,
557+
struct net_bridge_port *port,
558+
struct sk_buff *skb)
559+
{
560+
const struct br_mrp_ring_test_hdr *test_hdr;
561+
struct br_mrp_ring_test_hdr _test_hdr;
562+
const struct br_mrp_tlv_hdr *hdr;
563+
struct br_mrp_tlv_hdr _hdr;
564+
565+
/* Each MRP header starts with a version field which is 16 bits.
566+
* Therefore skip the version and get directly the TLV header.
567+
*/
568+
hdr = skb_header_pointer(skb, sizeof(uint16_t), sizeof(_hdr), &_hdr);
569+
if (!hdr)
570+
return;
571+
572+
if (hdr->type != BR_MRP_TLV_HEADER_RING_TEST)
573+
return;
574+
575+
test_hdr = skb_header_pointer(skb, sizeof(uint16_t) + sizeof(_hdr),
576+
sizeof(_test_hdr), &_test_hdr);
577+
if (!test_hdr)
578+
return;
579+
580+
/* Only frames that have a better priority than the node will
581+
* clear the miss counter because otherwise the node will need to behave
582+
* as MRM.
583+
*/
584+
if (br_mrp_test_better_than_own(mrp, br, test_hdr))
585+
mrp->test_count_miss = 0;
586+
}
587+
513588
/* This will just forward the frame to the other mrp ring port(MRC role) or will
514589
* not do anything.
515590
* note: already called with rcu_read_lock
@@ -546,6 +621,18 @@ static int br_mrp_rcv(struct net_bridge_port *p,
546621
return 1;
547622
}
548623

624+
/* If the role is MRA then don't forward the frames if it behaves as
625+
* MRM node
626+
*/
627+
if (mrp->ring_role == BR_MRP_RING_ROLE_MRA) {
628+
if (!mrp->test_monitor) {
629+
br_mrp_mrm_process(mrp, p, skb);
630+
return 1;
631+
}
632+
633+
br_mrp_mra_process(mrp, br, p, skb);
634+
}
635+
549636
/* Clone the frame and forward it on the other MRP port */
550637
nskb = skb_clone(skb, GFP_ATOMIC);
551638
if (!nskb)

net/bridge/br_mrp_netlink.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ br_mrp_start_test_policy[IFLA_BRIDGE_MRP_START_TEST_MAX + 1] = {
196196
[IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = { .type = NLA_U32 },
197197
[IFLA_BRIDGE_MRP_START_TEST_MAX_MISS] = { .type = NLA_U32 },
198198
[IFLA_BRIDGE_MRP_START_TEST_PERIOD] = { .type = NLA_U32 },
199+
[IFLA_BRIDGE_MRP_START_TEST_MONITOR] = { .type = NLA_U32 },
199200
};
200201

201202
static int br_mrp_start_test_parse(struct net_bridge *br, struct nlattr *attr,
@@ -225,6 +226,11 @@ static int br_mrp_start_test_parse(struct net_bridge *br, struct nlattr *attr,
225226
test.interval = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_TEST_INTERVAL]);
226227
test.max_miss = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_TEST_MAX_MISS]);
227228
test.period = nla_get_u32(tb[IFLA_BRIDGE_MRP_START_TEST_PERIOD]);
229+
test.monitor = false;
230+
231+
if (tb[IFLA_BRIDGE_MRP_START_TEST_MONITOR])
232+
test.monitor =
233+
nla_get_u32(tb[IFLA_BRIDGE_MRP_START_TEST_MONITOR]);
228234

229235
return br_mrp_start_test(br, &test);
230236
}

net/bridge/br_mrp_switchdev.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ int br_mrp_switchdev_set_ring_role(struct net_bridge *br,
6565

6666
int br_mrp_switchdev_send_ring_test(struct net_bridge *br,
6767
struct br_mrp *mrp, u32 interval,
68-
u8 max_miss, u32 period)
68+
u8 max_miss, u32 period,
69+
bool monitor)
6970
{
7071
struct switchdev_obj_ring_test_mrp test = {
7172
.obj.orig_dev = br->dev,
@@ -74,6 +75,7 @@ int br_mrp_switchdev_send_ring_test(struct net_bridge *br,
7475
.max_miss = max_miss,
7576
.ring_id = mrp->ring_id,
7677
.period = period,
78+
.monitor = monitor,
7779
};
7880
int err;
7981

net/bridge/br_private_mrp.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ struct br_mrp {
2626
unsigned long test_end;
2727
u32 test_count_miss;
2828
u32 test_max_miss;
29+
bool test_monitor;
2930

3031
u32 seq_id;
3132

@@ -52,7 +53,8 @@ int br_mrp_switchdev_set_ring_role(struct net_bridge *br, struct br_mrp *mrp,
5253
int br_mrp_switchdev_set_ring_state(struct net_bridge *br, struct br_mrp *mrp,
5354
enum br_mrp_ring_state_type state);
5455
int br_mrp_switchdev_send_ring_test(struct net_bridge *br, struct br_mrp *mrp,
55-
u32 interval, u8 max_miss, u32 period);
56+
u32 interval, u8 max_miss, u32 period,
57+
bool monitor);
5658
int br_mrp_port_switchdev_set_state(struct net_bridge_port *p,
5759
enum br_mrp_port_state_type state);
5860
int br_mrp_port_switchdev_set_role(struct net_bridge_port *p,

0 commit comments

Comments
 (0)