bridge: cfm: Kernel space implementation of CFM. CCM frame RX added.
[linux-block.git] / net / bridge / br_cfm.c
CommitLineData
86a14b79
HB
1// SPDX-License-Identifier: GPL-2.0-or-later
2
3#include <linux/cfm_bridge.h>
4#include <uapi/linux/cfm_bridge.h>
5#include "br_private_cfm.h"
6
7static struct br_cfm_mep *br_mep_find(struct net_bridge *br, u32 instance)
8{
9 struct br_cfm_mep *mep;
10
11 hlist_for_each_entry(mep, &br->mep_list, head)
12 if (mep->instance == instance)
13 return mep;
14
15 return NULL;
16}
17
18static struct br_cfm_mep *br_mep_find_ifindex(struct net_bridge *br,
19 u32 ifindex)
20{
21 struct br_cfm_mep *mep;
22
23 hlist_for_each_entry_rcu(mep, &br->mep_list, head,
24 lockdep_rtnl_is_held())
25 if (mep->create.ifindex == ifindex)
26 return mep;
27
28 return NULL;
29}
30
31static struct br_cfm_peer_mep *br_peer_mep_find(struct br_cfm_mep *mep,
32 u32 mepid)
33{
34 struct br_cfm_peer_mep *peer_mep;
35
36 hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head,
37 lockdep_rtnl_is_held())
38 if (peer_mep->mepid == mepid)
39 return peer_mep;
40
41 return NULL;
42}
43
44static struct net_bridge_port *br_mep_get_port(struct net_bridge *br,
45 u32 ifindex)
46{
47 struct net_bridge_port *port;
48
49 list_for_each_entry(port, &br->port_list, list)
50 if (port->dev->ifindex == ifindex)
51 return port;
52
53 return NULL;
54}
55
a806ad8e
HB
56/* Calculate the CCM interval in us. */
57static u32 interval_to_us(enum br_cfm_ccm_interval interval)
58{
59 switch (interval) {
60 case BR_CFM_CCM_INTERVAL_NONE:
61 return 0;
62 case BR_CFM_CCM_INTERVAL_3_3_MS:
63 return 3300;
64 case BR_CFM_CCM_INTERVAL_10_MS:
65 return 10 * 1000;
66 case BR_CFM_CCM_INTERVAL_100_MS:
67 return 100 * 1000;
68 case BR_CFM_CCM_INTERVAL_1_SEC:
69 return 1000 * 1000;
70 case BR_CFM_CCM_INTERVAL_10_SEC:
71 return 10 * 1000 * 1000;
72 case BR_CFM_CCM_INTERVAL_1_MIN:
73 return 60 * 1000 * 1000;
74 case BR_CFM_CCM_INTERVAL_10_MIN:
75 return 10 * 60 * 1000 * 1000;
76 }
77 return 0;
78}
79
80/* Convert the interface interval to CCM PDU value. */
81static u32 interval_to_pdu(enum br_cfm_ccm_interval interval)
82{
83 switch (interval) {
84 case BR_CFM_CCM_INTERVAL_NONE:
85 return 0;
86 case BR_CFM_CCM_INTERVAL_3_3_MS:
87 return 1;
88 case BR_CFM_CCM_INTERVAL_10_MS:
89 return 2;
90 case BR_CFM_CCM_INTERVAL_100_MS:
91 return 3;
92 case BR_CFM_CCM_INTERVAL_1_SEC:
93 return 4;
94 case BR_CFM_CCM_INTERVAL_10_SEC:
95 return 5;
96 case BR_CFM_CCM_INTERVAL_1_MIN:
97 return 6;
98 case BR_CFM_CCM_INTERVAL_10_MIN:
99 return 7;
100 }
101 return 0;
102}
103
dc32cbb3
HB
104/* Convert the CCM PDU value to interval on interface. */
105static u32 pdu_to_interval(u32 value)
106{
107 switch (value) {
108 case 0:
109 return BR_CFM_CCM_INTERVAL_NONE;
110 case 1:
111 return BR_CFM_CCM_INTERVAL_3_3_MS;
112 case 2:
113 return BR_CFM_CCM_INTERVAL_10_MS;
114 case 3:
115 return BR_CFM_CCM_INTERVAL_100_MS;
116 case 4:
117 return BR_CFM_CCM_INTERVAL_1_SEC;
118 case 5:
119 return BR_CFM_CCM_INTERVAL_10_SEC;
120 case 6:
121 return BR_CFM_CCM_INTERVAL_1_MIN;
122 case 7:
123 return BR_CFM_CCM_INTERVAL_10_MIN;
124 }
125 return BR_CFM_CCM_INTERVAL_NONE;
126}
127
128static void ccm_rx_timer_start(struct br_cfm_peer_mep *peer_mep)
129{
130 u32 interval_us;
131
132 interval_us = interval_to_us(peer_mep->mep->cc_config.exp_interval);
133 /* Function ccm_rx_dwork must be called with 1/4
134 * of the configured CC 'expected_interval'
135 * in order to detect CCM defect after 3.25 interval.
136 */
137 queue_delayed_work(system_wq, &peer_mep->ccm_rx_dwork,
138 usecs_to_jiffies(interval_us / 4));
139}
140
141static void cc_peer_enable(struct br_cfm_peer_mep *peer_mep)
142{
143 memset(&peer_mep->cc_status, 0, sizeof(peer_mep->cc_status));
144 peer_mep->ccm_rx_count_miss = 0;
145
146 ccm_rx_timer_start(peer_mep);
147}
148
149static void cc_peer_disable(struct br_cfm_peer_mep *peer_mep)
150{
151 cancel_delayed_work_sync(&peer_mep->ccm_rx_dwork);
152}
153
a806ad8e
HB
154static struct sk_buff *ccm_frame_build(struct br_cfm_mep *mep,
155 const struct br_cfm_cc_ccm_tx_info *const tx_info)
156
157{
158 struct br_cfm_common_hdr *common_hdr;
159 struct net_bridge_port *b_port;
160 struct br_cfm_maid *maid;
161 u8 *itu_reserved, *e_tlv;
162 struct ethhdr *eth_hdr;
163 struct sk_buff *skb;
164 __be32 *status_tlv;
165 __be32 *snumber;
166 __be16 *mepid;
167
168 skb = dev_alloc_skb(CFM_CCM_MAX_FRAME_LENGTH);
169 if (!skb)
170 return NULL;
171
172 rcu_read_lock();
173 b_port = rcu_dereference(mep->b_port);
174 if (!b_port) {
175 kfree_skb(skb);
176 rcu_read_unlock();
177 return NULL;
178 }
179 skb->dev = b_port->dev;
180 rcu_read_unlock();
181 /* The device cannot be deleted until the work_queue functions has
182 * completed. This function is called from ccm_tx_work_expired()
183 * that is a work_queue functions.
184 */
185
186 skb->protocol = htons(ETH_P_CFM);
187 skb->priority = CFM_FRAME_PRIO;
188
189 /* Ethernet header */
190 eth_hdr = skb_put(skb, sizeof(*eth_hdr));
191 ether_addr_copy(eth_hdr->h_dest, tx_info->dmac.addr);
192 ether_addr_copy(eth_hdr->h_source, mep->config.unicast_mac.addr);
193 eth_hdr->h_proto = htons(ETH_P_CFM);
194
195 /* Common CFM Header */
196 common_hdr = skb_put(skb, sizeof(*common_hdr));
197 common_hdr->mdlevel_version = mep->config.mdlevel << 5;
198 common_hdr->opcode = BR_CFM_OPCODE_CCM;
199 common_hdr->flags = (mep->rdi << 7) |
200 interval_to_pdu(mep->cc_config.exp_interval);
201 common_hdr->tlv_offset = CFM_CCM_TLV_OFFSET;
202
203 /* Sequence number */
204 snumber = skb_put(skb, sizeof(*snumber));
205 if (tx_info->seq_no_update) {
206 *snumber = cpu_to_be32(mep->ccm_tx_snumber);
207 mep->ccm_tx_snumber += 1;
208 } else {
209 *snumber = 0;
210 }
211
212 mepid = skb_put(skb, sizeof(*mepid));
213 *mepid = cpu_to_be16((u16)mep->config.mepid);
214
215 maid = skb_put(skb, sizeof(*maid));
216 memcpy(maid->data, mep->cc_config.exp_maid.data, sizeof(maid->data));
217
218 /* ITU reserved (CFM_CCM_ITU_RESERVED_SIZE octets) */
219 itu_reserved = skb_put(skb, CFM_CCM_ITU_RESERVED_SIZE);
220 memset(itu_reserved, 0, CFM_CCM_ITU_RESERVED_SIZE);
221
222 /* Generel CFM TLV format:
223 * TLV type: one byte
224 * TLV value length: two bytes
225 * TLV value: 'TLV value length' bytes
226 */
227
228 /* Port status TLV. The value length is 1. Total of 4 bytes. */
229 if (tx_info->port_tlv) {
230 status_tlv = skb_put(skb, sizeof(*status_tlv));
231 *status_tlv = cpu_to_be32((CFM_PORT_STATUS_TLV_TYPE << 24) |
232 (1 << 8) | /* Value length */
233 (tx_info->port_tlv_value & 0xFF));
234 }
235
236 /* Interface status TLV. The value length is 1. Total of 4 bytes. */
237 if (tx_info->if_tlv) {
238 status_tlv = skb_put(skb, sizeof(*status_tlv));
239 *status_tlv = cpu_to_be32((CFM_IF_STATUS_TLV_TYPE << 24) |
240 (1 << 8) | /* Value length */
241 (tx_info->if_tlv_value & 0xFF));
242 }
243
244 /* End TLV */
245 e_tlv = skb_put(skb, sizeof(*e_tlv));
246 *e_tlv = CFM_ENDE_TLV_TYPE;
247
248 return skb;
249}
250
251static void ccm_frame_tx(struct sk_buff *skb)
252{
253 skb_reset_network_header(skb);
254 dev_queue_xmit(skb);
255}
256
257/* This function is called with the configured CC 'expected_interval'
258 * in order to drive CCM transmission when enabled.
259 */
260static void ccm_tx_work_expired(struct work_struct *work)
261{
262 struct delayed_work *del_work;
263 struct br_cfm_mep *mep;
264 struct sk_buff *skb;
265 u32 interval_us;
266
267 del_work = to_delayed_work(work);
268 mep = container_of(del_work, struct br_cfm_mep, ccm_tx_dwork);
269
270 if (time_before_eq(mep->ccm_tx_end, jiffies)) {
271 /* Transmission period has ended */
272 mep->cc_ccm_tx_info.period = 0;
273 return;
274 }
275
276 skb = ccm_frame_build(mep, &mep->cc_ccm_tx_info);
277 if (skb)
278 ccm_frame_tx(skb);
279
280 interval_us = interval_to_us(mep->cc_config.exp_interval);
281 queue_delayed_work(system_wq, &mep->ccm_tx_dwork,
282 usecs_to_jiffies(interval_us));
283}
284
dc32cbb3
HB
285/* This function is called with 1/4 of the configured CC 'expected_interval'
286 * in order to detect CCM defect after 3.25 interval.
287 */
288static void ccm_rx_work_expired(struct work_struct *work)
289{
290 struct br_cfm_peer_mep *peer_mep;
291 struct delayed_work *del_work;
292
293 del_work = to_delayed_work(work);
294 peer_mep = container_of(del_work, struct br_cfm_peer_mep, ccm_rx_dwork);
295
296 /* After 13 counts (4 * 3,25) then 3.25 intervals are expired */
297 if (peer_mep->ccm_rx_count_miss < 13) {
298 /* 3.25 intervals are NOT expired without CCM reception */
299 peer_mep->ccm_rx_count_miss++;
300
301 /* Start timer again */
302 ccm_rx_timer_start(peer_mep);
303 } else {
304 /* 3.25 intervals are expired without CCM reception.
305 * CCM defect detected
306 */
307 peer_mep->cc_status.ccm_defect = true;
308 }
309}
310
311static u32 ccm_tlv_extract(struct sk_buff *skb, u32 index,
312 struct br_cfm_peer_mep *peer_mep)
313{
314 __be32 *s_tlv;
315 __be32 _s_tlv;
316 u32 h_s_tlv;
317 u8 *e_tlv;
318 u8 _e_tlv;
319
320 e_tlv = skb_header_pointer(skb, index, sizeof(_e_tlv), &_e_tlv);
321 if (!e_tlv)
322 return 0;
323
324 /* TLV is present - get the status TLV */
325 s_tlv = skb_header_pointer(skb,
326 index,
327 sizeof(_s_tlv), &_s_tlv);
328 if (!s_tlv)
329 return 0;
330
331 h_s_tlv = ntohl(*s_tlv);
332 if ((h_s_tlv >> 24) == CFM_IF_STATUS_TLV_TYPE) {
333 /* Interface status TLV */
334 peer_mep->cc_status.tlv_seen = true;
335 peer_mep->cc_status.if_tlv_value = (h_s_tlv & 0xFF);
336 }
337
338 if ((h_s_tlv >> 24) == CFM_PORT_STATUS_TLV_TYPE) {
339 /* Port status TLV */
340 peer_mep->cc_status.tlv_seen = true;
341 peer_mep->cc_status.port_tlv_value = (h_s_tlv & 0xFF);
342 }
343
344 /* The Sender ID TLV is not handled */
345 /* The Organization-Specific TLV is not handled */
346
347 /* Return the length of this tlv.
348 * This is the length of the value field plus 3 bytes for size of type
349 * field and length field
350 */
351 return ((h_s_tlv >> 8) & 0xFFFF) + 3;
352}
353
354/* note: already called with rcu_read_lock */
355static int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb)
356{
357 u32 mdlevel, interval, size, index, max;
358 const struct br_cfm_common_hdr *hdr;
359 struct br_cfm_peer_mep *peer_mep;
360 const struct br_cfm_maid *maid;
361 struct br_cfm_common_hdr _hdr;
362 struct br_cfm_maid _maid;
363 struct br_cfm_mep *mep;
364 struct net_bridge *br;
365 __be32 *snumber;
366 __be32 _snumber;
367 __be16 *mepid;
368 __be16 _mepid;
369
370 if (port->state == BR_STATE_DISABLED)
371 return 0;
372
373 hdr = skb_header_pointer(skb, 0, sizeof(_hdr), &_hdr);
374 if (!hdr)
375 return 1;
376
377 br = port->br;
378 mep = br_mep_find_ifindex(br, port->dev->ifindex);
379 if (unlikely(!mep))
380 /* No MEP on this port - must be forwarded */
381 return 0;
382
383 mdlevel = hdr->mdlevel_version >> 5;
384 if (mdlevel > mep->config.mdlevel)
385 /* The level is above this MEP level - must be forwarded */
386 return 0;
387
388 if ((hdr->mdlevel_version & 0x1F) != 0) {
389 /* Invalid version */
390 mep->status.version_unexp_seen = true;
391 return 1;
392 }
393
394 if (mdlevel < mep->config.mdlevel) {
395 /* The level is below this MEP level */
396 mep->status.rx_level_low_seen = true;
397 return 1;
398 }
399
400 if (hdr->opcode == BR_CFM_OPCODE_CCM) {
401 /* CCM PDU received. */
402 /* MA ID is after common header + sequence number + MEP ID */
403 maid = skb_header_pointer(skb,
404 CFM_CCM_PDU_MAID_OFFSET,
405 sizeof(_maid), &_maid);
406 if (!maid)
407 return 1;
408 if (memcmp(maid->data, mep->cc_config.exp_maid.data,
409 sizeof(maid->data)))
410 /* MA ID not as expected */
411 return 1;
412
413 /* MEP ID is after common header + sequence number */
414 mepid = skb_header_pointer(skb,
415 CFM_CCM_PDU_MEPID_OFFSET,
416 sizeof(_mepid), &_mepid);
417 if (!mepid)
418 return 1;
419 peer_mep = br_peer_mep_find(mep, (u32)ntohs(*mepid));
420 if (!peer_mep)
421 return 1;
422
423 /* Interval is in common header flags */
424 interval = hdr->flags & 0x07;
425 if (mep->cc_config.exp_interval != pdu_to_interval(interval))
426 /* Interval not as expected */
427 return 1;
428
429 /* A valid CCM frame is received */
430 if (peer_mep->cc_status.ccm_defect) {
431 peer_mep->cc_status.ccm_defect = false;
432
433 /* Start CCM RX timer */
434 ccm_rx_timer_start(peer_mep);
435 }
436
437 peer_mep->cc_status.seen = true;
438 peer_mep->ccm_rx_count_miss = 0;
439
440 /* RDI is in common header flags */
441 peer_mep->cc_status.rdi = (hdr->flags & 0x80) ? true : false;
442
443 /* Sequence number is after common header */
444 snumber = skb_header_pointer(skb,
445 CFM_CCM_PDU_SEQNR_OFFSET,
446 sizeof(_snumber), &_snumber);
447 if (!snumber)
448 return 1;
449 if (ntohl(*snumber) != (mep->ccm_rx_snumber + 1))
450 /* Unexpected sequence number */
451 peer_mep->cc_status.seq_unexp_seen = true;
452
453 mep->ccm_rx_snumber = ntohl(*snumber);
454
455 /* TLV end is after common header + sequence number + MEP ID +
456 * MA ID + ITU reserved
457 */
458 index = CFM_CCM_PDU_TLV_OFFSET;
459 max = 0;
460 do { /* Handle all TLVs */
461 size = ccm_tlv_extract(skb, index, peer_mep);
462 index += size;
463 max += 1;
464 } while (size != 0 && max < 4); /* Max four TLVs possible */
465
466 return 1;
467 }
468
469 mep->status.opcode_unexp_seen = true;
470
471 return 1;
472}
473
474static struct br_frame_type cfm_frame_type __read_mostly = {
475 .type = cpu_to_be16(ETH_P_CFM),
476 .frame_handler = br_cfm_frame_rx,
477};
478
86a14b79
HB
479int br_cfm_mep_create(struct net_bridge *br,
480 const u32 instance,
481 struct br_cfm_mep_create *const create,
482 struct netlink_ext_ack *extack)
483{
484 struct net_bridge_port *p;
485 struct br_cfm_mep *mep;
486
487 ASSERT_RTNL();
488
489 if (create->domain == BR_CFM_VLAN) {
490 NL_SET_ERR_MSG_MOD(extack,
491 "VLAN domain not supported");
492 return -EINVAL;
493 }
494 if (create->domain != BR_CFM_PORT) {
495 NL_SET_ERR_MSG_MOD(extack,
496 "Invalid domain value");
497 return -EINVAL;
498 }
499 if (create->direction == BR_CFM_MEP_DIRECTION_UP) {
500 NL_SET_ERR_MSG_MOD(extack,
501 "Up-MEP not supported");
502 return -EINVAL;
503 }
504 if (create->direction != BR_CFM_MEP_DIRECTION_DOWN) {
505 NL_SET_ERR_MSG_MOD(extack,
506 "Invalid direction value");
507 return -EINVAL;
508 }
509 p = br_mep_get_port(br, create->ifindex);
510 if (!p) {
511 NL_SET_ERR_MSG_MOD(extack,
512 "Port is not related to bridge");
513 return -EINVAL;
514 }
515 mep = br_mep_find(br, instance);
516 if (mep) {
517 NL_SET_ERR_MSG_MOD(extack,
518 "MEP instance already exists");
519 return -EEXIST;
520 }
521
522 /* In PORT domain only one instance can be created per port */
523 if (create->domain == BR_CFM_PORT) {
524 mep = br_mep_find_ifindex(br, create->ifindex);
525 if (mep) {
526 NL_SET_ERR_MSG_MOD(extack,
527 "Only one Port MEP on a port allowed");
528 return -EINVAL;
529 }
530 }
531
532 mep = kzalloc(sizeof(*mep), GFP_KERNEL);
533 if (!mep)
534 return -ENOMEM;
535
536 mep->create = *create;
537 mep->instance = instance;
538 rcu_assign_pointer(mep->b_port, p);
539
540 INIT_HLIST_HEAD(&mep->peer_mep_list);
a806ad8e 541 INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired);
86a14b79 542
dc32cbb3
HB
543 if (hlist_empty(&br->mep_list))
544 br_add_frame(br, &cfm_frame_type);
545
86a14b79
HB
546 hlist_add_tail_rcu(&mep->head, &br->mep_list);
547
548 return 0;
549}
550
551static void mep_delete_implementation(struct net_bridge *br,
552 struct br_cfm_mep *mep)
553{
554 struct br_cfm_peer_mep *peer_mep;
555 struct hlist_node *n_store;
556
557 ASSERT_RTNL();
558
559 /* Empty and free peer MEP list */
560 hlist_for_each_entry_safe(peer_mep, n_store, &mep->peer_mep_list, head) {
dc32cbb3 561 cancel_delayed_work_sync(&peer_mep->ccm_rx_dwork);
86a14b79
HB
562 hlist_del_rcu(&peer_mep->head);
563 kfree_rcu(peer_mep, rcu);
564 }
565
a806ad8e
HB
566 cancel_delayed_work_sync(&mep->ccm_tx_dwork);
567
86a14b79
HB
568 RCU_INIT_POINTER(mep->b_port, NULL);
569 hlist_del_rcu(&mep->head);
570 kfree_rcu(mep, rcu);
dc32cbb3
HB
571
572 if (hlist_empty(&br->mep_list))
573 br_del_frame(br, &cfm_frame_type);
86a14b79
HB
574}
575
576int br_cfm_mep_delete(struct net_bridge *br,
577 const u32 instance,
578 struct netlink_ext_ack *extack)
579{
580 struct br_cfm_mep *mep;
581
582 ASSERT_RTNL();
583
584 mep = br_mep_find(br, instance);
585 if (!mep) {
586 NL_SET_ERR_MSG_MOD(extack,
587 "MEP instance does not exists");
588 return -ENOENT;
589 }
590
591 mep_delete_implementation(br, mep);
592
593 return 0;
594}
595
596int br_cfm_mep_config_set(struct net_bridge *br,
597 const u32 instance,
598 const struct br_cfm_mep_config *const config,
599 struct netlink_ext_ack *extack)
600{
601 struct br_cfm_mep *mep;
602
603 ASSERT_RTNL();
604
605 mep = br_mep_find(br, instance);
606 if (!mep) {
607 NL_SET_ERR_MSG_MOD(extack,
608 "MEP instance does not exists");
609 return -ENOENT;
610 }
611
612 mep->config = *config;
613
614 return 0;
615}
616
a806ad8e
HB
617int br_cfm_cc_config_set(struct net_bridge *br,
618 const u32 instance,
619 const struct br_cfm_cc_config *const config,
620 struct netlink_ext_ack *extack)
621{
dc32cbb3 622 struct br_cfm_peer_mep *peer_mep;
a806ad8e
HB
623 struct br_cfm_mep *mep;
624
625 ASSERT_RTNL();
626
627 mep = br_mep_find(br, instance);
628 if (!mep) {
629 NL_SET_ERR_MSG_MOD(extack,
630 "MEP instance does not exists");
631 return -ENOENT;
632 }
633
634 /* Check for no change in configuration */
635 if (memcmp(config, &mep->cc_config, sizeof(*config)) == 0)
636 return 0;
637
dc32cbb3
HB
638 if (config->enable && !mep->cc_config.enable)
639 /* CC is enabled */
640 hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head)
641 cc_peer_enable(peer_mep);
642
643 if (!config->enable && mep->cc_config.enable)
644 /* CC is disabled */
645 hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head)
646 cc_peer_disable(peer_mep);
647
a806ad8e 648 mep->cc_config = *config;
dc32cbb3 649 mep->ccm_rx_snumber = 0;
a806ad8e
HB
650 mep->ccm_tx_snumber = 1;
651
652 return 0;
653}
654
86a14b79
HB
655int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
656 u32 mepid,
657 struct netlink_ext_ack *extack)
658{
659 struct br_cfm_peer_mep *peer_mep;
660 struct br_cfm_mep *mep;
661
662 ASSERT_RTNL();
663
664 mep = br_mep_find(br, instance);
665 if (!mep) {
666 NL_SET_ERR_MSG_MOD(extack,
667 "MEP instance does not exists");
668 return -ENOENT;
669 }
670
671 peer_mep = br_peer_mep_find(mep, mepid);
672 if (peer_mep) {
673 NL_SET_ERR_MSG_MOD(extack,
674 "Peer MEP-ID already exists");
675 return -EEXIST;
676 }
677
678 peer_mep = kzalloc(sizeof(*peer_mep), GFP_KERNEL);
679 if (!peer_mep)
680 return -ENOMEM;
681
682 peer_mep->mepid = mepid;
683 peer_mep->mep = mep;
dc32cbb3
HB
684 INIT_DELAYED_WORK(&peer_mep->ccm_rx_dwork, ccm_rx_work_expired);
685
686 if (mep->cc_config.enable)
687 cc_peer_enable(peer_mep);
86a14b79
HB
688
689 hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list);
690
691 return 0;
692}
693
694int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
695 u32 mepid,
696 struct netlink_ext_ack *extack)
697{
698 struct br_cfm_peer_mep *peer_mep;
699 struct br_cfm_mep *mep;
700
701 ASSERT_RTNL();
702
703 mep = br_mep_find(br, instance);
704 if (!mep) {
705 NL_SET_ERR_MSG_MOD(extack,
706 "MEP instance does not exists");
707 return -ENOENT;
708 }
709
710 peer_mep = br_peer_mep_find(mep, mepid);
711 if (!peer_mep) {
712 NL_SET_ERR_MSG_MOD(extack,
713 "Peer MEP-ID does not exists");
714 return -ENOENT;
715 }
716
dc32cbb3
HB
717 cc_peer_disable(peer_mep);
718
86a14b79
HB
719 hlist_del_rcu(&peer_mep->head);
720 kfree_rcu(peer_mep, rcu);
721
722 return 0;
723}
724
a806ad8e
HB
725int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
726 const bool rdi, struct netlink_ext_ack *extack)
727{
728 struct br_cfm_mep *mep;
729
730 ASSERT_RTNL();
731
732 mep = br_mep_find(br, instance);
733 if (!mep) {
734 NL_SET_ERR_MSG_MOD(extack,
735 "MEP instance does not exists");
736 return -ENOENT;
737 }
738
739 mep->rdi = rdi;
740
741 return 0;
742}
743
744int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
745 const struct br_cfm_cc_ccm_tx_info *const tx_info,
746 struct netlink_ext_ack *extack)
747{
748 struct br_cfm_mep *mep;
749
750 ASSERT_RTNL();
751
752 mep = br_mep_find(br, instance);
753 if (!mep) {
754 NL_SET_ERR_MSG_MOD(extack,
755 "MEP instance does not exists");
756 return -ENOENT;
757 }
758
759 if (memcmp(tx_info, &mep->cc_ccm_tx_info, sizeof(*tx_info)) == 0) {
760 /* No change in tx_info. */
761 if (mep->cc_ccm_tx_info.period == 0)
762 /* Transmission is not enabled - just return */
763 return 0;
764
765 /* Transmission is ongoing, the end time is recalculated */
766 mep->ccm_tx_end = jiffies +
767 usecs_to_jiffies(tx_info->period * 1000000);
768 return 0;
769 }
770
771 if (tx_info->period == 0 && mep->cc_ccm_tx_info.period == 0)
772 /* Some change in info and transmission is not ongoing */
773 goto save;
774
775 if (tx_info->period != 0 && mep->cc_ccm_tx_info.period != 0) {
776 /* Some change in info and transmission is ongoing
777 * The end time is recalculated
778 */
779 mep->ccm_tx_end = jiffies +
780 usecs_to_jiffies(tx_info->period * 1000000);
781
782 goto save;
783 }
784
785 if (tx_info->period == 0 && mep->cc_ccm_tx_info.period != 0) {
786 cancel_delayed_work_sync(&mep->ccm_tx_dwork);
787 goto save;
788 }
789
790 /* Start delayed work to transmit CCM frames. It is done with zero delay
791 * to send first frame immediately
792 */
793 mep->ccm_tx_end = jiffies + usecs_to_jiffies(tx_info->period * 1000000);
794 queue_delayed_work(system_wq, &mep->ccm_tx_dwork, 0);
795
796save:
797 mep->cc_ccm_tx_info = *tx_info;
798
799 return 0;
800}
801
86a14b79
HB
802/* Deletes the CFM instances on a specific bridge port
803 */
804void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *port)
805{
806 struct hlist_node *n_store;
807 struct br_cfm_mep *mep;
808
809 ASSERT_RTNL();
810
811 hlist_for_each_entry_safe(mep, n_store, &br->mep_list, head)
812 if (mep->create.ifindex == port->dev->ifindex)
813 mep_delete_implementation(br, mep);
814}