xref: /openbmc/linux/net/bridge/br_cfm.c (revision 98534fce)
186a14b79SHenrik Bjoernlund // SPDX-License-Identifier: GPL-2.0-or-later
286a14b79SHenrik Bjoernlund 
386a14b79SHenrik Bjoernlund #include <linux/cfm_bridge.h>
486a14b79SHenrik Bjoernlund #include <uapi/linux/cfm_bridge.h>
586a14b79SHenrik Bjoernlund #include "br_private_cfm.h"
686a14b79SHenrik Bjoernlund 
br_mep_find(struct net_bridge * br,u32 instance)786a14b79SHenrik Bjoernlund static struct br_cfm_mep *br_mep_find(struct net_bridge *br, u32 instance)
886a14b79SHenrik Bjoernlund {
986a14b79SHenrik Bjoernlund 	struct br_cfm_mep *mep;
1086a14b79SHenrik Bjoernlund 
1186a14b79SHenrik Bjoernlund 	hlist_for_each_entry(mep, &br->mep_list, head)
1286a14b79SHenrik Bjoernlund 		if (mep->instance == instance)
1386a14b79SHenrik Bjoernlund 			return mep;
1486a14b79SHenrik Bjoernlund 
1586a14b79SHenrik Bjoernlund 	return NULL;
1686a14b79SHenrik Bjoernlund }
1786a14b79SHenrik Bjoernlund 
br_mep_find_ifindex(struct net_bridge * br,u32 ifindex)1886a14b79SHenrik Bjoernlund static struct br_cfm_mep *br_mep_find_ifindex(struct net_bridge *br,
1986a14b79SHenrik Bjoernlund 					      u32 ifindex)
2086a14b79SHenrik Bjoernlund {
2186a14b79SHenrik Bjoernlund 	struct br_cfm_mep *mep;
2286a14b79SHenrik Bjoernlund 
2386a14b79SHenrik Bjoernlund 	hlist_for_each_entry_rcu(mep, &br->mep_list, head,
2486a14b79SHenrik Bjoernlund 				 lockdep_rtnl_is_held())
2586a14b79SHenrik Bjoernlund 		if (mep->create.ifindex == ifindex)
2686a14b79SHenrik Bjoernlund 			return mep;
2786a14b79SHenrik Bjoernlund 
2886a14b79SHenrik Bjoernlund 	return NULL;
2986a14b79SHenrik Bjoernlund }
3086a14b79SHenrik Bjoernlund 
br_peer_mep_find(struct br_cfm_mep * mep,u32 mepid)3186a14b79SHenrik Bjoernlund static struct br_cfm_peer_mep *br_peer_mep_find(struct br_cfm_mep *mep,
3286a14b79SHenrik Bjoernlund 						u32 mepid)
3386a14b79SHenrik Bjoernlund {
3486a14b79SHenrik Bjoernlund 	struct br_cfm_peer_mep *peer_mep;
3586a14b79SHenrik Bjoernlund 
3686a14b79SHenrik Bjoernlund 	hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head,
3786a14b79SHenrik Bjoernlund 				 lockdep_rtnl_is_held())
3886a14b79SHenrik Bjoernlund 		if (peer_mep->mepid == mepid)
3986a14b79SHenrik Bjoernlund 			return peer_mep;
4086a14b79SHenrik Bjoernlund 
4186a14b79SHenrik Bjoernlund 	return NULL;
4286a14b79SHenrik Bjoernlund }
4386a14b79SHenrik Bjoernlund 
br_mep_get_port(struct net_bridge * br,u32 ifindex)4486a14b79SHenrik Bjoernlund static struct net_bridge_port *br_mep_get_port(struct net_bridge *br,
4586a14b79SHenrik Bjoernlund 					       u32 ifindex)
4686a14b79SHenrik Bjoernlund {
4786a14b79SHenrik Bjoernlund 	struct net_bridge_port *port;
4886a14b79SHenrik Bjoernlund 
4986a14b79SHenrik Bjoernlund 	list_for_each_entry(port, &br->port_list, list)
5086a14b79SHenrik Bjoernlund 		if (port->dev->ifindex == ifindex)
5186a14b79SHenrik Bjoernlund 			return port;
5286a14b79SHenrik Bjoernlund 
5386a14b79SHenrik Bjoernlund 	return NULL;
5486a14b79SHenrik Bjoernlund }
5586a14b79SHenrik Bjoernlund 
56a806ad8eSHenrik Bjoernlund /* Calculate the CCM interval in us. */
interval_to_us(enum br_cfm_ccm_interval interval)57a806ad8eSHenrik Bjoernlund static u32 interval_to_us(enum br_cfm_ccm_interval interval)
58a806ad8eSHenrik Bjoernlund {
59a806ad8eSHenrik Bjoernlund 	switch (interval) {
60a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_NONE:
61a806ad8eSHenrik Bjoernlund 		return 0;
62a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_3_3_MS:
63a806ad8eSHenrik Bjoernlund 		return 3300;
64a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_10_MS:
65a806ad8eSHenrik Bjoernlund 		return 10 * 1000;
66a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_100_MS:
67a806ad8eSHenrik Bjoernlund 		return 100 * 1000;
68a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_1_SEC:
69a806ad8eSHenrik Bjoernlund 		return 1000 * 1000;
70a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_10_SEC:
71a806ad8eSHenrik Bjoernlund 		return 10 * 1000 * 1000;
72a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_1_MIN:
73a806ad8eSHenrik Bjoernlund 		return 60 * 1000 * 1000;
74a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_10_MIN:
75a806ad8eSHenrik Bjoernlund 		return 10 * 60 * 1000 * 1000;
76a806ad8eSHenrik Bjoernlund 	}
77a806ad8eSHenrik Bjoernlund 	return 0;
78a806ad8eSHenrik Bjoernlund }
79a806ad8eSHenrik Bjoernlund 
80a806ad8eSHenrik Bjoernlund /* Convert the interface interval to CCM PDU value. */
interval_to_pdu(enum br_cfm_ccm_interval interval)81a806ad8eSHenrik Bjoernlund static u32 interval_to_pdu(enum br_cfm_ccm_interval interval)
82a806ad8eSHenrik Bjoernlund {
83a806ad8eSHenrik Bjoernlund 	switch (interval) {
84a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_NONE:
85a806ad8eSHenrik Bjoernlund 		return 0;
86a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_3_3_MS:
87a806ad8eSHenrik Bjoernlund 		return 1;
88a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_10_MS:
89a806ad8eSHenrik Bjoernlund 		return 2;
90a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_100_MS:
91a806ad8eSHenrik Bjoernlund 		return 3;
92a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_1_SEC:
93a806ad8eSHenrik Bjoernlund 		return 4;
94a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_10_SEC:
95a806ad8eSHenrik Bjoernlund 		return 5;
96a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_1_MIN:
97a806ad8eSHenrik Bjoernlund 		return 6;
98a806ad8eSHenrik Bjoernlund 	case BR_CFM_CCM_INTERVAL_10_MIN:
99a806ad8eSHenrik Bjoernlund 		return 7;
100a806ad8eSHenrik Bjoernlund 	}
101a806ad8eSHenrik Bjoernlund 	return 0;
102a806ad8eSHenrik Bjoernlund }
103a806ad8eSHenrik Bjoernlund 
104dc32cbb3SHenrik Bjoernlund /* Convert the CCM PDU value to interval on interface. */
pdu_to_interval(u32 value)105dc32cbb3SHenrik Bjoernlund static u32 pdu_to_interval(u32 value)
106dc32cbb3SHenrik Bjoernlund {
107dc32cbb3SHenrik Bjoernlund 	switch (value) {
108dc32cbb3SHenrik Bjoernlund 	case 0:
109dc32cbb3SHenrik Bjoernlund 		return BR_CFM_CCM_INTERVAL_NONE;
110dc32cbb3SHenrik Bjoernlund 	case 1:
111dc32cbb3SHenrik Bjoernlund 		return BR_CFM_CCM_INTERVAL_3_3_MS;
112dc32cbb3SHenrik Bjoernlund 	case 2:
113dc32cbb3SHenrik Bjoernlund 		return BR_CFM_CCM_INTERVAL_10_MS;
114dc32cbb3SHenrik Bjoernlund 	case 3:
115dc32cbb3SHenrik Bjoernlund 		return BR_CFM_CCM_INTERVAL_100_MS;
116dc32cbb3SHenrik Bjoernlund 	case 4:
117dc32cbb3SHenrik Bjoernlund 		return BR_CFM_CCM_INTERVAL_1_SEC;
118dc32cbb3SHenrik Bjoernlund 	case 5:
119dc32cbb3SHenrik Bjoernlund 		return BR_CFM_CCM_INTERVAL_10_SEC;
120dc32cbb3SHenrik Bjoernlund 	case 6:
121dc32cbb3SHenrik Bjoernlund 		return BR_CFM_CCM_INTERVAL_1_MIN;
122dc32cbb3SHenrik Bjoernlund 	case 7:
123dc32cbb3SHenrik Bjoernlund 		return BR_CFM_CCM_INTERVAL_10_MIN;
124dc32cbb3SHenrik Bjoernlund 	}
125dc32cbb3SHenrik Bjoernlund 	return BR_CFM_CCM_INTERVAL_NONE;
126dc32cbb3SHenrik Bjoernlund }
127dc32cbb3SHenrik Bjoernlund 
ccm_rx_timer_start(struct br_cfm_peer_mep * peer_mep)128dc32cbb3SHenrik Bjoernlund static void ccm_rx_timer_start(struct br_cfm_peer_mep *peer_mep)
129dc32cbb3SHenrik Bjoernlund {
130dc32cbb3SHenrik Bjoernlund 	u32 interval_us;
131dc32cbb3SHenrik Bjoernlund 
132dc32cbb3SHenrik Bjoernlund 	interval_us = interval_to_us(peer_mep->mep->cc_config.exp_interval);
133dc32cbb3SHenrik Bjoernlund 	/* Function ccm_rx_dwork must be called with 1/4
134dc32cbb3SHenrik Bjoernlund 	 * of the configured CC 'expected_interval'
135dc32cbb3SHenrik Bjoernlund 	 * in order to detect CCM defect after 3.25 interval.
136dc32cbb3SHenrik Bjoernlund 	 */
137dc32cbb3SHenrik Bjoernlund 	queue_delayed_work(system_wq, &peer_mep->ccm_rx_dwork,
138dc32cbb3SHenrik Bjoernlund 			   usecs_to_jiffies(interval_us / 4));
139dc32cbb3SHenrik Bjoernlund }
140dc32cbb3SHenrik Bjoernlund 
br_cfm_notify(int event,const struct net_bridge_port * port)141b6d0425bSHenrik Bjoernlund static void br_cfm_notify(int event, const struct net_bridge_port *port)
142b6d0425bSHenrik Bjoernlund {
143b6d0425bSHenrik Bjoernlund 	u32 filter = RTEXT_FILTER_CFM_STATUS;
144b6d0425bSHenrik Bjoernlund 
145*98534fceSgushengxian 	br_info_notify(event, port->br, NULL, filter);
146b6d0425bSHenrik Bjoernlund }
147b6d0425bSHenrik Bjoernlund 
cc_peer_enable(struct br_cfm_peer_mep * peer_mep)148dc32cbb3SHenrik Bjoernlund static void cc_peer_enable(struct br_cfm_peer_mep *peer_mep)
149dc32cbb3SHenrik Bjoernlund {
150dc32cbb3SHenrik Bjoernlund 	memset(&peer_mep->cc_status, 0, sizeof(peer_mep->cc_status));
151dc32cbb3SHenrik Bjoernlund 	peer_mep->ccm_rx_count_miss = 0;
152dc32cbb3SHenrik Bjoernlund 
153dc32cbb3SHenrik Bjoernlund 	ccm_rx_timer_start(peer_mep);
154dc32cbb3SHenrik Bjoernlund }
155dc32cbb3SHenrik Bjoernlund 
cc_peer_disable(struct br_cfm_peer_mep * peer_mep)156dc32cbb3SHenrik Bjoernlund static void cc_peer_disable(struct br_cfm_peer_mep *peer_mep)
157dc32cbb3SHenrik Bjoernlund {
158dc32cbb3SHenrik Bjoernlund 	cancel_delayed_work_sync(&peer_mep->ccm_rx_dwork);
159dc32cbb3SHenrik Bjoernlund }
160dc32cbb3SHenrik Bjoernlund 
ccm_frame_build(struct br_cfm_mep * mep,const struct br_cfm_cc_ccm_tx_info * const tx_info)161a806ad8eSHenrik Bjoernlund static struct sk_buff *ccm_frame_build(struct br_cfm_mep *mep,
162a806ad8eSHenrik Bjoernlund 				       const struct br_cfm_cc_ccm_tx_info *const tx_info)
163a806ad8eSHenrik Bjoernlund 
164a806ad8eSHenrik Bjoernlund {
165a806ad8eSHenrik Bjoernlund 	struct br_cfm_common_hdr *common_hdr;
166a806ad8eSHenrik Bjoernlund 	struct net_bridge_port *b_port;
167a806ad8eSHenrik Bjoernlund 	struct br_cfm_maid *maid;
168a806ad8eSHenrik Bjoernlund 	u8 *itu_reserved, *e_tlv;
169a806ad8eSHenrik Bjoernlund 	struct ethhdr *eth_hdr;
170a806ad8eSHenrik Bjoernlund 	struct sk_buff *skb;
171a806ad8eSHenrik Bjoernlund 	__be32 *status_tlv;
172a806ad8eSHenrik Bjoernlund 	__be32 *snumber;
173a806ad8eSHenrik Bjoernlund 	__be16 *mepid;
174a806ad8eSHenrik Bjoernlund 
175a806ad8eSHenrik Bjoernlund 	skb = dev_alloc_skb(CFM_CCM_MAX_FRAME_LENGTH);
176a806ad8eSHenrik Bjoernlund 	if (!skb)
177a806ad8eSHenrik Bjoernlund 		return NULL;
178a806ad8eSHenrik Bjoernlund 
179a806ad8eSHenrik Bjoernlund 	rcu_read_lock();
180a806ad8eSHenrik Bjoernlund 	b_port = rcu_dereference(mep->b_port);
181a806ad8eSHenrik Bjoernlund 	if (!b_port) {
182a806ad8eSHenrik Bjoernlund 		kfree_skb(skb);
183a806ad8eSHenrik Bjoernlund 		rcu_read_unlock();
184a806ad8eSHenrik Bjoernlund 		return NULL;
185a806ad8eSHenrik Bjoernlund 	}
186a806ad8eSHenrik Bjoernlund 	skb->dev = b_port->dev;
187a806ad8eSHenrik Bjoernlund 	rcu_read_unlock();
188a806ad8eSHenrik Bjoernlund 	/* The device cannot be deleted until the work_queue functions has
189a806ad8eSHenrik Bjoernlund 	 * completed. This function is called from ccm_tx_work_expired()
190a806ad8eSHenrik Bjoernlund 	 * that is a work_queue functions.
191a806ad8eSHenrik Bjoernlund 	 */
192a806ad8eSHenrik Bjoernlund 
193a806ad8eSHenrik Bjoernlund 	skb->protocol = htons(ETH_P_CFM);
194a806ad8eSHenrik Bjoernlund 	skb->priority = CFM_FRAME_PRIO;
195a806ad8eSHenrik Bjoernlund 
196a806ad8eSHenrik Bjoernlund 	/* Ethernet header */
197a806ad8eSHenrik Bjoernlund 	eth_hdr = skb_put(skb, sizeof(*eth_hdr));
198a806ad8eSHenrik Bjoernlund 	ether_addr_copy(eth_hdr->h_dest, tx_info->dmac.addr);
199a806ad8eSHenrik Bjoernlund 	ether_addr_copy(eth_hdr->h_source, mep->config.unicast_mac.addr);
200a806ad8eSHenrik Bjoernlund 	eth_hdr->h_proto = htons(ETH_P_CFM);
201a806ad8eSHenrik Bjoernlund 
202a806ad8eSHenrik Bjoernlund 	/* Common CFM Header */
203a806ad8eSHenrik Bjoernlund 	common_hdr = skb_put(skb, sizeof(*common_hdr));
204a806ad8eSHenrik Bjoernlund 	common_hdr->mdlevel_version = mep->config.mdlevel << 5;
205a806ad8eSHenrik Bjoernlund 	common_hdr->opcode = BR_CFM_OPCODE_CCM;
206a806ad8eSHenrik Bjoernlund 	common_hdr->flags = (mep->rdi << 7) |
207a806ad8eSHenrik Bjoernlund 			    interval_to_pdu(mep->cc_config.exp_interval);
208a806ad8eSHenrik Bjoernlund 	common_hdr->tlv_offset = CFM_CCM_TLV_OFFSET;
209a806ad8eSHenrik Bjoernlund 
210a806ad8eSHenrik Bjoernlund 	/* Sequence number */
211a806ad8eSHenrik Bjoernlund 	snumber = skb_put(skb, sizeof(*snumber));
212a806ad8eSHenrik Bjoernlund 	if (tx_info->seq_no_update) {
213a806ad8eSHenrik Bjoernlund 		*snumber = cpu_to_be32(mep->ccm_tx_snumber);
214a806ad8eSHenrik Bjoernlund 		mep->ccm_tx_snumber += 1;
215a806ad8eSHenrik Bjoernlund 	} else {
216a806ad8eSHenrik Bjoernlund 		*snumber = 0;
217a806ad8eSHenrik Bjoernlund 	}
218a806ad8eSHenrik Bjoernlund 
219a806ad8eSHenrik Bjoernlund 	mepid = skb_put(skb, sizeof(*mepid));
220a806ad8eSHenrik Bjoernlund 	*mepid = cpu_to_be16((u16)mep->config.mepid);
221a806ad8eSHenrik Bjoernlund 
222a806ad8eSHenrik Bjoernlund 	maid = skb_put(skb, sizeof(*maid));
223a806ad8eSHenrik Bjoernlund 	memcpy(maid->data, mep->cc_config.exp_maid.data, sizeof(maid->data));
224a806ad8eSHenrik Bjoernlund 
225a806ad8eSHenrik Bjoernlund 	/* ITU reserved (CFM_CCM_ITU_RESERVED_SIZE octets) */
226a806ad8eSHenrik Bjoernlund 	itu_reserved = skb_put(skb, CFM_CCM_ITU_RESERVED_SIZE);
227a806ad8eSHenrik Bjoernlund 	memset(itu_reserved, 0, CFM_CCM_ITU_RESERVED_SIZE);
228a806ad8eSHenrik Bjoernlund 
229a806ad8eSHenrik Bjoernlund 	/* Generel CFM TLV format:
230a806ad8eSHenrik Bjoernlund 	 * TLV type:		one byte
231a806ad8eSHenrik Bjoernlund 	 * TLV value length:	two bytes
232a806ad8eSHenrik Bjoernlund 	 * TLV value:		'TLV value length' bytes
233a806ad8eSHenrik Bjoernlund 	 */
234a806ad8eSHenrik Bjoernlund 
235a806ad8eSHenrik Bjoernlund 	/* Port status TLV. The value length is 1. Total of 4 bytes. */
236a806ad8eSHenrik Bjoernlund 	if (tx_info->port_tlv) {
237a806ad8eSHenrik Bjoernlund 		status_tlv = skb_put(skb, sizeof(*status_tlv));
238a806ad8eSHenrik Bjoernlund 		*status_tlv = cpu_to_be32((CFM_PORT_STATUS_TLV_TYPE << 24) |
239a806ad8eSHenrik Bjoernlund 					  (1 << 8) |	/* Value length */
240a806ad8eSHenrik Bjoernlund 					  (tx_info->port_tlv_value & 0xFF));
241a806ad8eSHenrik Bjoernlund 	}
242a806ad8eSHenrik Bjoernlund 
243a806ad8eSHenrik Bjoernlund 	/* Interface status TLV. The value length is 1. Total of 4 bytes. */
244a806ad8eSHenrik Bjoernlund 	if (tx_info->if_tlv) {
245a806ad8eSHenrik Bjoernlund 		status_tlv = skb_put(skb, sizeof(*status_tlv));
246a806ad8eSHenrik Bjoernlund 		*status_tlv = cpu_to_be32((CFM_IF_STATUS_TLV_TYPE << 24) |
247a806ad8eSHenrik Bjoernlund 					  (1 << 8) |	/* Value length */
248a806ad8eSHenrik Bjoernlund 					  (tx_info->if_tlv_value & 0xFF));
249a806ad8eSHenrik Bjoernlund 	}
250a806ad8eSHenrik Bjoernlund 
251a806ad8eSHenrik Bjoernlund 	/* End TLV */
252a806ad8eSHenrik Bjoernlund 	e_tlv = skb_put(skb, sizeof(*e_tlv));
253a806ad8eSHenrik Bjoernlund 	*e_tlv = CFM_ENDE_TLV_TYPE;
254a806ad8eSHenrik Bjoernlund 
255a806ad8eSHenrik Bjoernlund 	return skb;
256a806ad8eSHenrik Bjoernlund }
257a806ad8eSHenrik Bjoernlund 
ccm_frame_tx(struct sk_buff * skb)258a806ad8eSHenrik Bjoernlund static void ccm_frame_tx(struct sk_buff *skb)
259a806ad8eSHenrik Bjoernlund {
260a806ad8eSHenrik Bjoernlund 	skb_reset_network_header(skb);
261a806ad8eSHenrik Bjoernlund 	dev_queue_xmit(skb);
262a806ad8eSHenrik Bjoernlund }
263a806ad8eSHenrik Bjoernlund 
264a806ad8eSHenrik Bjoernlund /* This function is called with the configured CC 'expected_interval'
265a806ad8eSHenrik Bjoernlund  * in order to drive CCM transmission when enabled.
266a806ad8eSHenrik Bjoernlund  */
ccm_tx_work_expired(struct work_struct * work)267a806ad8eSHenrik Bjoernlund static void ccm_tx_work_expired(struct work_struct *work)
268a806ad8eSHenrik Bjoernlund {
269a806ad8eSHenrik Bjoernlund 	struct delayed_work *del_work;
270a806ad8eSHenrik Bjoernlund 	struct br_cfm_mep *mep;
271a806ad8eSHenrik Bjoernlund 	struct sk_buff *skb;
272a806ad8eSHenrik Bjoernlund 	u32 interval_us;
273a806ad8eSHenrik Bjoernlund 
274a806ad8eSHenrik Bjoernlund 	del_work = to_delayed_work(work);
275a806ad8eSHenrik Bjoernlund 	mep = container_of(del_work, struct br_cfm_mep, ccm_tx_dwork);
276a806ad8eSHenrik Bjoernlund 
277a806ad8eSHenrik Bjoernlund 	if (time_before_eq(mep->ccm_tx_end, jiffies)) {
278a806ad8eSHenrik Bjoernlund 		/* Transmission period has ended */
279a806ad8eSHenrik Bjoernlund 		mep->cc_ccm_tx_info.period = 0;
280a806ad8eSHenrik Bjoernlund 		return;
281a806ad8eSHenrik Bjoernlund 	}
282a806ad8eSHenrik Bjoernlund 
283a806ad8eSHenrik Bjoernlund 	skb = ccm_frame_build(mep, &mep->cc_ccm_tx_info);
284a806ad8eSHenrik Bjoernlund 	if (skb)
285a806ad8eSHenrik Bjoernlund 		ccm_frame_tx(skb);
286a806ad8eSHenrik Bjoernlund 
287a806ad8eSHenrik Bjoernlund 	interval_us = interval_to_us(mep->cc_config.exp_interval);
288a806ad8eSHenrik Bjoernlund 	queue_delayed_work(system_wq, &mep->ccm_tx_dwork,
289a806ad8eSHenrik Bjoernlund 			   usecs_to_jiffies(interval_us));
290a806ad8eSHenrik Bjoernlund }
291a806ad8eSHenrik Bjoernlund 
292dc32cbb3SHenrik Bjoernlund /* This function is called with 1/4 of the configured CC 'expected_interval'
293dc32cbb3SHenrik Bjoernlund  * in order to detect CCM defect after 3.25 interval.
294dc32cbb3SHenrik Bjoernlund  */
ccm_rx_work_expired(struct work_struct * work)295dc32cbb3SHenrik Bjoernlund static void ccm_rx_work_expired(struct work_struct *work)
296dc32cbb3SHenrik Bjoernlund {
297dc32cbb3SHenrik Bjoernlund 	struct br_cfm_peer_mep *peer_mep;
298b6d0425bSHenrik Bjoernlund 	struct net_bridge_port *b_port;
299dc32cbb3SHenrik Bjoernlund 	struct delayed_work *del_work;
300dc32cbb3SHenrik Bjoernlund 
301dc32cbb3SHenrik Bjoernlund 	del_work = to_delayed_work(work);
302dc32cbb3SHenrik Bjoernlund 	peer_mep = container_of(del_work, struct br_cfm_peer_mep, ccm_rx_dwork);
303dc32cbb3SHenrik Bjoernlund 
304dc32cbb3SHenrik Bjoernlund 	/* After 13 counts (4 * 3,25) then 3.25 intervals are expired */
305dc32cbb3SHenrik Bjoernlund 	if (peer_mep->ccm_rx_count_miss < 13) {
306dc32cbb3SHenrik Bjoernlund 		/* 3.25 intervals are NOT expired without CCM reception */
307dc32cbb3SHenrik Bjoernlund 		peer_mep->ccm_rx_count_miss++;
308dc32cbb3SHenrik Bjoernlund 
309dc32cbb3SHenrik Bjoernlund 		/* Start timer again */
310dc32cbb3SHenrik Bjoernlund 		ccm_rx_timer_start(peer_mep);
311dc32cbb3SHenrik Bjoernlund 	} else {
312dc32cbb3SHenrik Bjoernlund 		/* 3.25 intervals are expired without CCM reception.
313dc32cbb3SHenrik Bjoernlund 		 * CCM defect detected
314dc32cbb3SHenrik Bjoernlund 		 */
315dc32cbb3SHenrik Bjoernlund 		peer_mep->cc_status.ccm_defect = true;
316b6d0425bSHenrik Bjoernlund 
317b6d0425bSHenrik Bjoernlund 		/* Change in CCM defect status - notify */
318b6d0425bSHenrik Bjoernlund 		rcu_read_lock();
319b6d0425bSHenrik Bjoernlund 		b_port = rcu_dereference(peer_mep->mep->b_port);
320b6d0425bSHenrik Bjoernlund 		if (b_port)
321b6d0425bSHenrik Bjoernlund 			br_cfm_notify(RTM_NEWLINK, b_port);
322b6d0425bSHenrik Bjoernlund 		rcu_read_unlock();
323dc32cbb3SHenrik Bjoernlund 	}
324dc32cbb3SHenrik Bjoernlund }
325dc32cbb3SHenrik Bjoernlund 
ccm_tlv_extract(struct sk_buff * skb,u32 index,struct br_cfm_peer_mep * peer_mep)326dc32cbb3SHenrik Bjoernlund static u32 ccm_tlv_extract(struct sk_buff *skb, u32 index,
327dc32cbb3SHenrik Bjoernlund 			   struct br_cfm_peer_mep *peer_mep)
328dc32cbb3SHenrik Bjoernlund {
329dc32cbb3SHenrik Bjoernlund 	__be32 *s_tlv;
330dc32cbb3SHenrik Bjoernlund 	__be32 _s_tlv;
331dc32cbb3SHenrik Bjoernlund 	u32 h_s_tlv;
332dc32cbb3SHenrik Bjoernlund 	u8 *e_tlv;
333dc32cbb3SHenrik Bjoernlund 	u8 _e_tlv;
334dc32cbb3SHenrik Bjoernlund 
335dc32cbb3SHenrik Bjoernlund 	e_tlv = skb_header_pointer(skb, index, sizeof(_e_tlv), &_e_tlv);
336dc32cbb3SHenrik Bjoernlund 	if (!e_tlv)
337dc32cbb3SHenrik Bjoernlund 		return 0;
338dc32cbb3SHenrik Bjoernlund 
339dc32cbb3SHenrik Bjoernlund 	/* TLV is present - get the status TLV */
340dc32cbb3SHenrik Bjoernlund 	s_tlv = skb_header_pointer(skb,
341dc32cbb3SHenrik Bjoernlund 				   index,
342dc32cbb3SHenrik Bjoernlund 				   sizeof(_s_tlv), &_s_tlv);
343dc32cbb3SHenrik Bjoernlund 	if (!s_tlv)
344dc32cbb3SHenrik Bjoernlund 		return 0;
345dc32cbb3SHenrik Bjoernlund 
346dc32cbb3SHenrik Bjoernlund 	h_s_tlv = ntohl(*s_tlv);
347dc32cbb3SHenrik Bjoernlund 	if ((h_s_tlv >> 24) == CFM_IF_STATUS_TLV_TYPE) {
348dc32cbb3SHenrik Bjoernlund 		/* Interface status TLV */
349dc32cbb3SHenrik Bjoernlund 		peer_mep->cc_status.tlv_seen = true;
350dc32cbb3SHenrik Bjoernlund 		peer_mep->cc_status.if_tlv_value = (h_s_tlv & 0xFF);
351dc32cbb3SHenrik Bjoernlund 	}
352dc32cbb3SHenrik Bjoernlund 
353dc32cbb3SHenrik Bjoernlund 	if ((h_s_tlv >> 24) == CFM_PORT_STATUS_TLV_TYPE) {
354dc32cbb3SHenrik Bjoernlund 		/* Port status TLV */
355dc32cbb3SHenrik Bjoernlund 		peer_mep->cc_status.tlv_seen = true;
356dc32cbb3SHenrik Bjoernlund 		peer_mep->cc_status.port_tlv_value = (h_s_tlv & 0xFF);
357dc32cbb3SHenrik Bjoernlund 	}
358dc32cbb3SHenrik Bjoernlund 
359dc32cbb3SHenrik Bjoernlund 	/* The Sender ID TLV is not handled */
360dc32cbb3SHenrik Bjoernlund 	/* The Organization-Specific TLV is not handled */
361dc32cbb3SHenrik Bjoernlund 
362dc32cbb3SHenrik Bjoernlund 	/* Return the length of this tlv.
363dc32cbb3SHenrik Bjoernlund 	 * This is the length of the value field plus 3 bytes for size of type
364dc32cbb3SHenrik Bjoernlund 	 * field and length field
365dc32cbb3SHenrik Bjoernlund 	 */
366dc32cbb3SHenrik Bjoernlund 	return ((h_s_tlv >> 8) & 0xFFFF) + 3;
367dc32cbb3SHenrik Bjoernlund }
368dc32cbb3SHenrik Bjoernlund 
369dc32cbb3SHenrik Bjoernlund /* note: already called with rcu_read_lock */
br_cfm_frame_rx(struct net_bridge_port * port,struct sk_buff * skb)370dc32cbb3SHenrik Bjoernlund static int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb)
371dc32cbb3SHenrik Bjoernlund {
372dc32cbb3SHenrik Bjoernlund 	u32 mdlevel, interval, size, index, max;
373dc32cbb3SHenrik Bjoernlund 	const struct br_cfm_common_hdr *hdr;
374dc32cbb3SHenrik Bjoernlund 	struct br_cfm_peer_mep *peer_mep;
375dc32cbb3SHenrik Bjoernlund 	const struct br_cfm_maid *maid;
376dc32cbb3SHenrik Bjoernlund 	struct br_cfm_common_hdr _hdr;
377dc32cbb3SHenrik Bjoernlund 	struct br_cfm_maid _maid;
378dc32cbb3SHenrik Bjoernlund 	struct br_cfm_mep *mep;
379dc32cbb3SHenrik Bjoernlund 	struct net_bridge *br;
380dc32cbb3SHenrik Bjoernlund 	__be32 *snumber;
381dc32cbb3SHenrik Bjoernlund 	__be32 _snumber;
382dc32cbb3SHenrik Bjoernlund 	__be16 *mepid;
383dc32cbb3SHenrik Bjoernlund 	__be16 _mepid;
384dc32cbb3SHenrik Bjoernlund 
385dc32cbb3SHenrik Bjoernlund 	if (port->state == BR_STATE_DISABLED)
386dc32cbb3SHenrik Bjoernlund 		return 0;
387dc32cbb3SHenrik Bjoernlund 
388dc32cbb3SHenrik Bjoernlund 	hdr = skb_header_pointer(skb, 0, sizeof(_hdr), &_hdr);
389dc32cbb3SHenrik Bjoernlund 	if (!hdr)
390dc32cbb3SHenrik Bjoernlund 		return 1;
391dc32cbb3SHenrik Bjoernlund 
392dc32cbb3SHenrik Bjoernlund 	br = port->br;
393dc32cbb3SHenrik Bjoernlund 	mep = br_mep_find_ifindex(br, port->dev->ifindex);
394dc32cbb3SHenrik Bjoernlund 	if (unlikely(!mep))
395dc32cbb3SHenrik Bjoernlund 		/* No MEP on this port - must be forwarded */
396dc32cbb3SHenrik Bjoernlund 		return 0;
397dc32cbb3SHenrik Bjoernlund 
398dc32cbb3SHenrik Bjoernlund 	mdlevel = hdr->mdlevel_version >> 5;
399dc32cbb3SHenrik Bjoernlund 	if (mdlevel > mep->config.mdlevel)
400dc32cbb3SHenrik Bjoernlund 		/* The level is above this MEP level - must be forwarded */
401dc32cbb3SHenrik Bjoernlund 		return 0;
402dc32cbb3SHenrik Bjoernlund 
403dc32cbb3SHenrik Bjoernlund 	if ((hdr->mdlevel_version & 0x1F) != 0) {
404dc32cbb3SHenrik Bjoernlund 		/* Invalid version */
405dc32cbb3SHenrik Bjoernlund 		mep->status.version_unexp_seen = true;
406dc32cbb3SHenrik Bjoernlund 		return 1;
407dc32cbb3SHenrik Bjoernlund 	}
408dc32cbb3SHenrik Bjoernlund 
409dc32cbb3SHenrik Bjoernlund 	if (mdlevel < mep->config.mdlevel) {
410dc32cbb3SHenrik Bjoernlund 		/* The level is below this MEP level */
411dc32cbb3SHenrik Bjoernlund 		mep->status.rx_level_low_seen = true;
412dc32cbb3SHenrik Bjoernlund 		return 1;
413dc32cbb3SHenrik Bjoernlund 	}
414dc32cbb3SHenrik Bjoernlund 
415dc32cbb3SHenrik Bjoernlund 	if (hdr->opcode == BR_CFM_OPCODE_CCM) {
416dc32cbb3SHenrik Bjoernlund 		/* CCM PDU received. */
417dc32cbb3SHenrik Bjoernlund 		/* MA ID is after common header + sequence number + MEP ID */
418dc32cbb3SHenrik Bjoernlund 		maid = skb_header_pointer(skb,
419dc32cbb3SHenrik Bjoernlund 					  CFM_CCM_PDU_MAID_OFFSET,
420dc32cbb3SHenrik Bjoernlund 					  sizeof(_maid), &_maid);
421dc32cbb3SHenrik Bjoernlund 		if (!maid)
422dc32cbb3SHenrik Bjoernlund 			return 1;
423dc32cbb3SHenrik Bjoernlund 		if (memcmp(maid->data, mep->cc_config.exp_maid.data,
424dc32cbb3SHenrik Bjoernlund 			   sizeof(maid->data)))
425dc32cbb3SHenrik Bjoernlund 			/* MA ID not as expected */
426dc32cbb3SHenrik Bjoernlund 			return 1;
427dc32cbb3SHenrik Bjoernlund 
428dc32cbb3SHenrik Bjoernlund 		/* MEP ID is after common header + sequence number */
429dc32cbb3SHenrik Bjoernlund 		mepid = skb_header_pointer(skb,
430dc32cbb3SHenrik Bjoernlund 					   CFM_CCM_PDU_MEPID_OFFSET,
431dc32cbb3SHenrik Bjoernlund 					   sizeof(_mepid), &_mepid);
432dc32cbb3SHenrik Bjoernlund 		if (!mepid)
433dc32cbb3SHenrik Bjoernlund 			return 1;
434dc32cbb3SHenrik Bjoernlund 		peer_mep = br_peer_mep_find(mep, (u32)ntohs(*mepid));
435dc32cbb3SHenrik Bjoernlund 		if (!peer_mep)
436dc32cbb3SHenrik Bjoernlund 			return 1;
437dc32cbb3SHenrik Bjoernlund 
438dc32cbb3SHenrik Bjoernlund 		/* Interval is in common header flags */
439dc32cbb3SHenrik Bjoernlund 		interval = hdr->flags & 0x07;
440dc32cbb3SHenrik Bjoernlund 		if (mep->cc_config.exp_interval != pdu_to_interval(interval))
441dc32cbb3SHenrik Bjoernlund 			/* Interval not as expected */
442dc32cbb3SHenrik Bjoernlund 			return 1;
443dc32cbb3SHenrik Bjoernlund 
444dc32cbb3SHenrik Bjoernlund 		/* A valid CCM frame is received */
445dc32cbb3SHenrik Bjoernlund 		if (peer_mep->cc_status.ccm_defect) {
446dc32cbb3SHenrik Bjoernlund 			peer_mep->cc_status.ccm_defect = false;
447dc32cbb3SHenrik Bjoernlund 
448b6d0425bSHenrik Bjoernlund 			/* Change in CCM defect status - notify */
449b6d0425bSHenrik Bjoernlund 			br_cfm_notify(RTM_NEWLINK, port);
450b6d0425bSHenrik Bjoernlund 
451dc32cbb3SHenrik Bjoernlund 			/* Start CCM RX timer */
452dc32cbb3SHenrik Bjoernlund 			ccm_rx_timer_start(peer_mep);
453dc32cbb3SHenrik Bjoernlund 		}
454dc32cbb3SHenrik Bjoernlund 
455dc32cbb3SHenrik Bjoernlund 		peer_mep->cc_status.seen = true;
456dc32cbb3SHenrik Bjoernlund 		peer_mep->ccm_rx_count_miss = 0;
457dc32cbb3SHenrik Bjoernlund 
458dc32cbb3SHenrik Bjoernlund 		/* RDI is in common header flags */
459dc32cbb3SHenrik Bjoernlund 		peer_mep->cc_status.rdi = (hdr->flags & 0x80) ? true : false;
460dc32cbb3SHenrik Bjoernlund 
461dc32cbb3SHenrik Bjoernlund 		/* Sequence number is after common header */
462dc32cbb3SHenrik Bjoernlund 		snumber = skb_header_pointer(skb,
463dc32cbb3SHenrik Bjoernlund 					     CFM_CCM_PDU_SEQNR_OFFSET,
464dc32cbb3SHenrik Bjoernlund 					     sizeof(_snumber), &_snumber);
465dc32cbb3SHenrik Bjoernlund 		if (!snumber)
466dc32cbb3SHenrik Bjoernlund 			return 1;
467dc32cbb3SHenrik Bjoernlund 		if (ntohl(*snumber) != (mep->ccm_rx_snumber + 1))
468dc32cbb3SHenrik Bjoernlund 			/* Unexpected sequence number */
469dc32cbb3SHenrik Bjoernlund 			peer_mep->cc_status.seq_unexp_seen = true;
470dc32cbb3SHenrik Bjoernlund 
471dc32cbb3SHenrik Bjoernlund 		mep->ccm_rx_snumber = ntohl(*snumber);
472dc32cbb3SHenrik Bjoernlund 
473dc32cbb3SHenrik Bjoernlund 		/* TLV end is after common header + sequence number + MEP ID +
474dc32cbb3SHenrik Bjoernlund 		 * MA ID + ITU reserved
475dc32cbb3SHenrik Bjoernlund 		 */
476dc32cbb3SHenrik Bjoernlund 		index = CFM_CCM_PDU_TLV_OFFSET;
477dc32cbb3SHenrik Bjoernlund 		max = 0;
478dc32cbb3SHenrik Bjoernlund 		do { /* Handle all TLVs */
479dc32cbb3SHenrik Bjoernlund 			size = ccm_tlv_extract(skb, index, peer_mep);
480dc32cbb3SHenrik Bjoernlund 			index += size;
481dc32cbb3SHenrik Bjoernlund 			max += 1;
482dc32cbb3SHenrik Bjoernlund 		} while (size != 0 && max < 4); /* Max four TLVs possible */
483dc32cbb3SHenrik Bjoernlund 
484dc32cbb3SHenrik Bjoernlund 		return 1;
485dc32cbb3SHenrik Bjoernlund 	}
486dc32cbb3SHenrik Bjoernlund 
487dc32cbb3SHenrik Bjoernlund 	mep->status.opcode_unexp_seen = true;
488dc32cbb3SHenrik Bjoernlund 
489dc32cbb3SHenrik Bjoernlund 	return 1;
490dc32cbb3SHenrik Bjoernlund }
491dc32cbb3SHenrik Bjoernlund 
492dc32cbb3SHenrik Bjoernlund static struct br_frame_type cfm_frame_type __read_mostly = {
493dc32cbb3SHenrik Bjoernlund 	.type = cpu_to_be16(ETH_P_CFM),
494dc32cbb3SHenrik Bjoernlund 	.frame_handler = br_cfm_frame_rx,
495dc32cbb3SHenrik Bjoernlund };
496dc32cbb3SHenrik Bjoernlund 
br_cfm_mep_create(struct net_bridge * br,const u32 instance,struct br_cfm_mep_create * const create,struct netlink_ext_ack * extack)49786a14b79SHenrik Bjoernlund int br_cfm_mep_create(struct net_bridge *br,
49886a14b79SHenrik Bjoernlund 		      const u32 instance,
49986a14b79SHenrik Bjoernlund 		      struct br_cfm_mep_create *const create,
50086a14b79SHenrik Bjoernlund 		      struct netlink_ext_ack *extack)
50186a14b79SHenrik Bjoernlund {
50286a14b79SHenrik Bjoernlund 	struct net_bridge_port *p;
50386a14b79SHenrik Bjoernlund 	struct br_cfm_mep *mep;
50486a14b79SHenrik Bjoernlund 
50586a14b79SHenrik Bjoernlund 	ASSERT_RTNL();
50686a14b79SHenrik Bjoernlund 
50786a14b79SHenrik Bjoernlund 	if (create->domain == BR_CFM_VLAN) {
50886a14b79SHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
50986a14b79SHenrik Bjoernlund 				   "VLAN domain not supported");
51086a14b79SHenrik Bjoernlund 		return -EINVAL;
51186a14b79SHenrik Bjoernlund 	}
51286a14b79SHenrik Bjoernlund 	if (create->domain != BR_CFM_PORT) {
51386a14b79SHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
51486a14b79SHenrik Bjoernlund 				   "Invalid domain value");
51586a14b79SHenrik Bjoernlund 		return -EINVAL;
51686a14b79SHenrik Bjoernlund 	}
51786a14b79SHenrik Bjoernlund 	if (create->direction == BR_CFM_MEP_DIRECTION_UP) {
51886a14b79SHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
51986a14b79SHenrik Bjoernlund 				   "Up-MEP not supported");
52086a14b79SHenrik Bjoernlund 		return -EINVAL;
52186a14b79SHenrik Bjoernlund 	}
52286a14b79SHenrik Bjoernlund 	if (create->direction != BR_CFM_MEP_DIRECTION_DOWN) {
52386a14b79SHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
52486a14b79SHenrik Bjoernlund 				   "Invalid direction value");
52586a14b79SHenrik Bjoernlund 		return -EINVAL;
52686a14b79SHenrik Bjoernlund 	}
52786a14b79SHenrik Bjoernlund 	p = br_mep_get_port(br, create->ifindex);
52886a14b79SHenrik Bjoernlund 	if (!p) {
52986a14b79SHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
53086a14b79SHenrik Bjoernlund 				   "Port is not related to bridge");
53186a14b79SHenrik Bjoernlund 		return -EINVAL;
53286a14b79SHenrik Bjoernlund 	}
53386a14b79SHenrik Bjoernlund 	mep = br_mep_find(br, instance);
53486a14b79SHenrik Bjoernlund 	if (mep) {
53586a14b79SHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
53686a14b79SHenrik Bjoernlund 				   "MEP instance already exists");
53786a14b79SHenrik Bjoernlund 		return -EEXIST;
53886a14b79SHenrik Bjoernlund 	}
53986a14b79SHenrik Bjoernlund 
54086a14b79SHenrik Bjoernlund 	/* In PORT domain only one instance can be created per port */
54186a14b79SHenrik Bjoernlund 	if (create->domain == BR_CFM_PORT) {
54286a14b79SHenrik Bjoernlund 		mep = br_mep_find_ifindex(br, create->ifindex);
54386a14b79SHenrik Bjoernlund 		if (mep) {
54486a14b79SHenrik Bjoernlund 			NL_SET_ERR_MSG_MOD(extack,
54586a14b79SHenrik Bjoernlund 					   "Only one Port MEP on a port allowed");
54686a14b79SHenrik Bjoernlund 			return -EINVAL;
54786a14b79SHenrik Bjoernlund 		}
54886a14b79SHenrik Bjoernlund 	}
54986a14b79SHenrik Bjoernlund 
55086a14b79SHenrik Bjoernlund 	mep = kzalloc(sizeof(*mep), GFP_KERNEL);
55186a14b79SHenrik Bjoernlund 	if (!mep)
55286a14b79SHenrik Bjoernlund 		return -ENOMEM;
55386a14b79SHenrik Bjoernlund 
55486a14b79SHenrik Bjoernlund 	mep->create = *create;
55586a14b79SHenrik Bjoernlund 	mep->instance = instance;
55686a14b79SHenrik Bjoernlund 	rcu_assign_pointer(mep->b_port, p);
55786a14b79SHenrik Bjoernlund 
55886a14b79SHenrik Bjoernlund 	INIT_HLIST_HEAD(&mep->peer_mep_list);
559a806ad8eSHenrik Bjoernlund 	INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired);
56086a14b79SHenrik Bjoernlund 
561dc32cbb3SHenrik Bjoernlund 	if (hlist_empty(&br->mep_list))
562dc32cbb3SHenrik Bjoernlund 		br_add_frame(br, &cfm_frame_type);
563dc32cbb3SHenrik Bjoernlund 
56486a14b79SHenrik Bjoernlund 	hlist_add_tail_rcu(&mep->head, &br->mep_list);
56586a14b79SHenrik Bjoernlund 
56686a14b79SHenrik Bjoernlund 	return 0;
56786a14b79SHenrik Bjoernlund }
56886a14b79SHenrik Bjoernlund 
mep_delete_implementation(struct net_bridge * br,struct br_cfm_mep * mep)56986a14b79SHenrik Bjoernlund static void mep_delete_implementation(struct net_bridge *br,
57086a14b79SHenrik Bjoernlund 				      struct br_cfm_mep *mep)
57186a14b79SHenrik Bjoernlund {
57286a14b79SHenrik Bjoernlund 	struct br_cfm_peer_mep *peer_mep;
57386a14b79SHenrik Bjoernlund 	struct hlist_node *n_store;
57486a14b79SHenrik Bjoernlund 
57586a14b79SHenrik Bjoernlund 	ASSERT_RTNL();
57686a14b79SHenrik Bjoernlund 
57786a14b79SHenrik Bjoernlund 	/* Empty and free peer MEP list */
57886a14b79SHenrik Bjoernlund 	hlist_for_each_entry_safe(peer_mep, n_store, &mep->peer_mep_list, head) {
579dc32cbb3SHenrik Bjoernlund 		cancel_delayed_work_sync(&peer_mep->ccm_rx_dwork);
58086a14b79SHenrik Bjoernlund 		hlist_del_rcu(&peer_mep->head);
58186a14b79SHenrik Bjoernlund 		kfree_rcu(peer_mep, rcu);
58286a14b79SHenrik Bjoernlund 	}
58386a14b79SHenrik Bjoernlund 
584a806ad8eSHenrik Bjoernlund 	cancel_delayed_work_sync(&mep->ccm_tx_dwork);
585a806ad8eSHenrik Bjoernlund 
58686a14b79SHenrik Bjoernlund 	RCU_INIT_POINTER(mep->b_port, NULL);
58786a14b79SHenrik Bjoernlund 	hlist_del_rcu(&mep->head);
58886a14b79SHenrik Bjoernlund 	kfree_rcu(mep, rcu);
589dc32cbb3SHenrik Bjoernlund 
590dc32cbb3SHenrik Bjoernlund 	if (hlist_empty(&br->mep_list))
591dc32cbb3SHenrik Bjoernlund 		br_del_frame(br, &cfm_frame_type);
59286a14b79SHenrik Bjoernlund }
59386a14b79SHenrik Bjoernlund 
br_cfm_mep_delete(struct net_bridge * br,const u32 instance,struct netlink_ext_ack * extack)59486a14b79SHenrik Bjoernlund int br_cfm_mep_delete(struct net_bridge *br,
59586a14b79SHenrik Bjoernlund 		      const u32 instance,
59686a14b79SHenrik Bjoernlund 		      struct netlink_ext_ack *extack)
59786a14b79SHenrik Bjoernlund {
59886a14b79SHenrik Bjoernlund 	struct br_cfm_mep *mep;
59986a14b79SHenrik Bjoernlund 
60086a14b79SHenrik Bjoernlund 	ASSERT_RTNL();
60186a14b79SHenrik Bjoernlund 
60286a14b79SHenrik Bjoernlund 	mep = br_mep_find(br, instance);
60386a14b79SHenrik Bjoernlund 	if (!mep) {
60486a14b79SHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
60586a14b79SHenrik Bjoernlund 				   "MEP instance does not exists");
60686a14b79SHenrik Bjoernlund 		return -ENOENT;
60786a14b79SHenrik Bjoernlund 	}
60886a14b79SHenrik Bjoernlund 
60986a14b79SHenrik Bjoernlund 	mep_delete_implementation(br, mep);
61086a14b79SHenrik Bjoernlund 
61186a14b79SHenrik Bjoernlund 	return 0;
61286a14b79SHenrik Bjoernlund }
61386a14b79SHenrik Bjoernlund 
br_cfm_mep_config_set(struct net_bridge * br,const u32 instance,const struct br_cfm_mep_config * const config,struct netlink_ext_ack * extack)61486a14b79SHenrik Bjoernlund int br_cfm_mep_config_set(struct net_bridge *br,
61586a14b79SHenrik Bjoernlund 			  const u32 instance,
61686a14b79SHenrik Bjoernlund 			  const struct br_cfm_mep_config *const config,
61786a14b79SHenrik Bjoernlund 			  struct netlink_ext_ack *extack)
61886a14b79SHenrik Bjoernlund {
61986a14b79SHenrik Bjoernlund 	struct br_cfm_mep *mep;
62086a14b79SHenrik Bjoernlund 
62186a14b79SHenrik Bjoernlund 	ASSERT_RTNL();
62286a14b79SHenrik Bjoernlund 
62386a14b79SHenrik Bjoernlund 	mep = br_mep_find(br, instance);
62486a14b79SHenrik Bjoernlund 	if (!mep) {
62586a14b79SHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
62686a14b79SHenrik Bjoernlund 				   "MEP instance does not exists");
62786a14b79SHenrik Bjoernlund 		return -ENOENT;
62886a14b79SHenrik Bjoernlund 	}
62986a14b79SHenrik Bjoernlund 
63086a14b79SHenrik Bjoernlund 	mep->config = *config;
63186a14b79SHenrik Bjoernlund 
63286a14b79SHenrik Bjoernlund 	return 0;
63386a14b79SHenrik Bjoernlund }
63486a14b79SHenrik Bjoernlund 
br_cfm_cc_config_set(struct net_bridge * br,const u32 instance,const struct br_cfm_cc_config * const config,struct netlink_ext_ack * extack)635a806ad8eSHenrik Bjoernlund int br_cfm_cc_config_set(struct net_bridge *br,
636a806ad8eSHenrik Bjoernlund 			 const u32 instance,
637a806ad8eSHenrik Bjoernlund 			 const struct br_cfm_cc_config *const config,
638a806ad8eSHenrik Bjoernlund 			 struct netlink_ext_ack *extack)
639a806ad8eSHenrik Bjoernlund {
640dc32cbb3SHenrik Bjoernlund 	struct br_cfm_peer_mep *peer_mep;
641a806ad8eSHenrik Bjoernlund 	struct br_cfm_mep *mep;
642a806ad8eSHenrik Bjoernlund 
643a806ad8eSHenrik Bjoernlund 	ASSERT_RTNL();
644a806ad8eSHenrik Bjoernlund 
645a806ad8eSHenrik Bjoernlund 	mep = br_mep_find(br, instance);
646a806ad8eSHenrik Bjoernlund 	if (!mep) {
647a806ad8eSHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
648a806ad8eSHenrik Bjoernlund 				   "MEP instance does not exists");
649a806ad8eSHenrik Bjoernlund 		return -ENOENT;
650a806ad8eSHenrik Bjoernlund 	}
651a806ad8eSHenrik Bjoernlund 
652a806ad8eSHenrik Bjoernlund 	/* Check for no change in configuration */
653a806ad8eSHenrik Bjoernlund 	if (memcmp(config, &mep->cc_config, sizeof(*config)) == 0)
654a806ad8eSHenrik Bjoernlund 		return 0;
655a806ad8eSHenrik Bjoernlund 
656dc32cbb3SHenrik Bjoernlund 	if (config->enable && !mep->cc_config.enable)
657dc32cbb3SHenrik Bjoernlund 		/* CC is enabled */
658dc32cbb3SHenrik Bjoernlund 		hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head)
659dc32cbb3SHenrik Bjoernlund 			cc_peer_enable(peer_mep);
660dc32cbb3SHenrik Bjoernlund 
661dc32cbb3SHenrik Bjoernlund 	if (!config->enable && mep->cc_config.enable)
662dc32cbb3SHenrik Bjoernlund 		/* CC is disabled */
663dc32cbb3SHenrik Bjoernlund 		hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head)
664dc32cbb3SHenrik Bjoernlund 			cc_peer_disable(peer_mep);
665dc32cbb3SHenrik Bjoernlund 
666a806ad8eSHenrik Bjoernlund 	mep->cc_config = *config;
667dc32cbb3SHenrik Bjoernlund 	mep->ccm_rx_snumber = 0;
668a806ad8eSHenrik Bjoernlund 	mep->ccm_tx_snumber = 1;
669a806ad8eSHenrik Bjoernlund 
670a806ad8eSHenrik Bjoernlund 	return 0;
671a806ad8eSHenrik Bjoernlund }
672a806ad8eSHenrik Bjoernlund 
br_cfm_cc_peer_mep_add(struct net_bridge * br,const u32 instance,u32 mepid,struct netlink_ext_ack * extack)67386a14b79SHenrik Bjoernlund int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
67486a14b79SHenrik Bjoernlund 			   u32 mepid,
67586a14b79SHenrik Bjoernlund 			   struct netlink_ext_ack *extack)
67686a14b79SHenrik Bjoernlund {
67786a14b79SHenrik Bjoernlund 	struct br_cfm_peer_mep *peer_mep;
67886a14b79SHenrik Bjoernlund 	struct br_cfm_mep *mep;
67986a14b79SHenrik Bjoernlund 
68086a14b79SHenrik Bjoernlund 	ASSERT_RTNL();
68186a14b79SHenrik Bjoernlund 
68286a14b79SHenrik Bjoernlund 	mep = br_mep_find(br, instance);
68386a14b79SHenrik Bjoernlund 	if (!mep) {
68486a14b79SHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
68586a14b79SHenrik Bjoernlund 				   "MEP instance does not exists");
68686a14b79SHenrik Bjoernlund 		return -ENOENT;
68786a14b79SHenrik Bjoernlund 	}
68886a14b79SHenrik Bjoernlund 
68986a14b79SHenrik Bjoernlund 	peer_mep = br_peer_mep_find(mep, mepid);
69086a14b79SHenrik Bjoernlund 	if (peer_mep) {
69186a14b79SHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
69286a14b79SHenrik Bjoernlund 				   "Peer MEP-ID already exists");
69386a14b79SHenrik Bjoernlund 		return -EEXIST;
69486a14b79SHenrik Bjoernlund 	}
69586a14b79SHenrik Bjoernlund 
69686a14b79SHenrik Bjoernlund 	peer_mep = kzalloc(sizeof(*peer_mep), GFP_KERNEL);
69786a14b79SHenrik Bjoernlund 	if (!peer_mep)
69886a14b79SHenrik Bjoernlund 		return -ENOMEM;
69986a14b79SHenrik Bjoernlund 
70086a14b79SHenrik Bjoernlund 	peer_mep->mepid = mepid;
70186a14b79SHenrik Bjoernlund 	peer_mep->mep = mep;
702dc32cbb3SHenrik Bjoernlund 	INIT_DELAYED_WORK(&peer_mep->ccm_rx_dwork, ccm_rx_work_expired);
703dc32cbb3SHenrik Bjoernlund 
704dc32cbb3SHenrik Bjoernlund 	if (mep->cc_config.enable)
705dc32cbb3SHenrik Bjoernlund 		cc_peer_enable(peer_mep);
70686a14b79SHenrik Bjoernlund 
70786a14b79SHenrik Bjoernlund 	hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list);
70886a14b79SHenrik Bjoernlund 
70986a14b79SHenrik Bjoernlund 	return 0;
71086a14b79SHenrik Bjoernlund }
71186a14b79SHenrik Bjoernlund 
br_cfm_cc_peer_mep_remove(struct net_bridge * br,const u32 instance,u32 mepid,struct netlink_ext_ack * extack)71286a14b79SHenrik Bjoernlund int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
71386a14b79SHenrik Bjoernlund 			      u32 mepid,
71486a14b79SHenrik Bjoernlund 			      struct netlink_ext_ack *extack)
71586a14b79SHenrik Bjoernlund {
71686a14b79SHenrik Bjoernlund 	struct br_cfm_peer_mep *peer_mep;
71786a14b79SHenrik Bjoernlund 	struct br_cfm_mep *mep;
71886a14b79SHenrik Bjoernlund 
71986a14b79SHenrik Bjoernlund 	ASSERT_RTNL();
72086a14b79SHenrik Bjoernlund 
72186a14b79SHenrik Bjoernlund 	mep = br_mep_find(br, instance);
72286a14b79SHenrik Bjoernlund 	if (!mep) {
72386a14b79SHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
72486a14b79SHenrik Bjoernlund 				   "MEP instance does not exists");
72586a14b79SHenrik Bjoernlund 		return -ENOENT;
72686a14b79SHenrik Bjoernlund 	}
72786a14b79SHenrik Bjoernlund 
72886a14b79SHenrik Bjoernlund 	peer_mep = br_peer_mep_find(mep, mepid);
72986a14b79SHenrik Bjoernlund 	if (!peer_mep) {
73086a14b79SHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
73186a14b79SHenrik Bjoernlund 				   "Peer MEP-ID does not exists");
73286a14b79SHenrik Bjoernlund 		return -ENOENT;
73386a14b79SHenrik Bjoernlund 	}
73486a14b79SHenrik Bjoernlund 
735dc32cbb3SHenrik Bjoernlund 	cc_peer_disable(peer_mep);
736dc32cbb3SHenrik Bjoernlund 
73786a14b79SHenrik Bjoernlund 	hlist_del_rcu(&peer_mep->head);
73886a14b79SHenrik Bjoernlund 	kfree_rcu(peer_mep, rcu);
73986a14b79SHenrik Bjoernlund 
74086a14b79SHenrik Bjoernlund 	return 0;
74186a14b79SHenrik Bjoernlund }
74286a14b79SHenrik Bjoernlund 
br_cfm_cc_rdi_set(struct net_bridge * br,const u32 instance,const bool rdi,struct netlink_ext_ack * extack)743a806ad8eSHenrik Bjoernlund int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
744a806ad8eSHenrik Bjoernlund 		      const bool rdi, struct netlink_ext_ack *extack)
745a806ad8eSHenrik Bjoernlund {
746a806ad8eSHenrik Bjoernlund 	struct br_cfm_mep *mep;
747a806ad8eSHenrik Bjoernlund 
748a806ad8eSHenrik Bjoernlund 	ASSERT_RTNL();
749a806ad8eSHenrik Bjoernlund 
750a806ad8eSHenrik Bjoernlund 	mep = br_mep_find(br, instance);
751a806ad8eSHenrik Bjoernlund 	if (!mep) {
752a806ad8eSHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
753a806ad8eSHenrik Bjoernlund 				   "MEP instance does not exists");
754a806ad8eSHenrik Bjoernlund 		return -ENOENT;
755a806ad8eSHenrik Bjoernlund 	}
756a806ad8eSHenrik Bjoernlund 
757a806ad8eSHenrik Bjoernlund 	mep->rdi = rdi;
758a806ad8eSHenrik Bjoernlund 
759a806ad8eSHenrik Bjoernlund 	return 0;
760a806ad8eSHenrik Bjoernlund }
761a806ad8eSHenrik Bjoernlund 
br_cfm_cc_ccm_tx(struct net_bridge * br,const u32 instance,const struct br_cfm_cc_ccm_tx_info * const tx_info,struct netlink_ext_ack * extack)762a806ad8eSHenrik Bjoernlund int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
763a806ad8eSHenrik Bjoernlund 		     const struct br_cfm_cc_ccm_tx_info *const tx_info,
764a806ad8eSHenrik Bjoernlund 		     struct netlink_ext_ack *extack)
765a806ad8eSHenrik Bjoernlund {
766a806ad8eSHenrik Bjoernlund 	struct br_cfm_mep *mep;
767a806ad8eSHenrik Bjoernlund 
768a806ad8eSHenrik Bjoernlund 	ASSERT_RTNL();
769a806ad8eSHenrik Bjoernlund 
770a806ad8eSHenrik Bjoernlund 	mep = br_mep_find(br, instance);
771a806ad8eSHenrik Bjoernlund 	if (!mep) {
772a806ad8eSHenrik Bjoernlund 		NL_SET_ERR_MSG_MOD(extack,
773a806ad8eSHenrik Bjoernlund 				   "MEP instance does not exists");
774a806ad8eSHenrik Bjoernlund 		return -ENOENT;
775a806ad8eSHenrik Bjoernlund 	}
776a806ad8eSHenrik Bjoernlund 
777a806ad8eSHenrik Bjoernlund 	if (memcmp(tx_info, &mep->cc_ccm_tx_info, sizeof(*tx_info)) == 0) {
778a806ad8eSHenrik Bjoernlund 		/* No change in tx_info. */
779a806ad8eSHenrik Bjoernlund 		if (mep->cc_ccm_tx_info.period == 0)
780a806ad8eSHenrik Bjoernlund 			/* Transmission is not enabled - just return */
781a806ad8eSHenrik Bjoernlund 			return 0;
782a806ad8eSHenrik Bjoernlund 
783a806ad8eSHenrik Bjoernlund 		/* Transmission is ongoing, the end time is recalculated */
784a806ad8eSHenrik Bjoernlund 		mep->ccm_tx_end = jiffies +
785a806ad8eSHenrik Bjoernlund 				  usecs_to_jiffies(tx_info->period * 1000000);
786a806ad8eSHenrik Bjoernlund 		return 0;
787a806ad8eSHenrik Bjoernlund 	}
788a806ad8eSHenrik Bjoernlund 
789a806ad8eSHenrik Bjoernlund 	if (tx_info->period == 0 && mep->cc_ccm_tx_info.period == 0)
790a806ad8eSHenrik Bjoernlund 		/* Some change in info and transmission is not ongoing */
791a806ad8eSHenrik Bjoernlund 		goto save;
792a806ad8eSHenrik Bjoernlund 
793a806ad8eSHenrik Bjoernlund 	if (tx_info->period != 0 && mep->cc_ccm_tx_info.period != 0) {
794a806ad8eSHenrik Bjoernlund 		/* Some change in info and transmission is ongoing
795a806ad8eSHenrik Bjoernlund 		 * The end time is recalculated
796a806ad8eSHenrik Bjoernlund 		 */
797a806ad8eSHenrik Bjoernlund 		mep->ccm_tx_end = jiffies +
798a806ad8eSHenrik Bjoernlund 				  usecs_to_jiffies(tx_info->period * 1000000);
799a806ad8eSHenrik Bjoernlund 
800a806ad8eSHenrik Bjoernlund 		goto save;
801a806ad8eSHenrik Bjoernlund 	}
802a806ad8eSHenrik Bjoernlund 
803a806ad8eSHenrik Bjoernlund 	if (tx_info->period == 0 && mep->cc_ccm_tx_info.period != 0) {
804a806ad8eSHenrik Bjoernlund 		cancel_delayed_work_sync(&mep->ccm_tx_dwork);
805a806ad8eSHenrik Bjoernlund 		goto save;
806a806ad8eSHenrik Bjoernlund 	}
807a806ad8eSHenrik Bjoernlund 
808a806ad8eSHenrik Bjoernlund 	/* Start delayed work to transmit CCM frames. It is done with zero delay
809a806ad8eSHenrik Bjoernlund 	 * to send first frame immediately
810a806ad8eSHenrik Bjoernlund 	 */
811a806ad8eSHenrik Bjoernlund 	mep->ccm_tx_end = jiffies + usecs_to_jiffies(tx_info->period * 1000000);
812a806ad8eSHenrik Bjoernlund 	queue_delayed_work(system_wq, &mep->ccm_tx_dwork, 0);
813a806ad8eSHenrik Bjoernlund 
814a806ad8eSHenrik Bjoernlund save:
815a806ad8eSHenrik Bjoernlund 	mep->cc_ccm_tx_info = *tx_info;
816a806ad8eSHenrik Bjoernlund 
817a806ad8eSHenrik Bjoernlund 	return 0;
818a806ad8eSHenrik Bjoernlund }
819a806ad8eSHenrik Bjoernlund 
br_cfm_mep_count(struct net_bridge * br,u32 * count)820b6d0425bSHenrik Bjoernlund int br_cfm_mep_count(struct net_bridge *br, u32 *count)
821b6d0425bSHenrik Bjoernlund {
822b6d0425bSHenrik Bjoernlund 	struct br_cfm_mep *mep;
823b6d0425bSHenrik Bjoernlund 
824b6d0425bSHenrik Bjoernlund 	*count = 0;
825b6d0425bSHenrik Bjoernlund 
826b6d0425bSHenrik Bjoernlund 	rcu_read_lock();
827b6d0425bSHenrik Bjoernlund 	hlist_for_each_entry_rcu(mep, &br->mep_list, head)
828b6d0425bSHenrik Bjoernlund 		*count += 1;
829b6d0425bSHenrik Bjoernlund 	rcu_read_unlock();
830b6d0425bSHenrik Bjoernlund 
831b6d0425bSHenrik Bjoernlund 	return 0;
832b6d0425bSHenrik Bjoernlund }
833b6d0425bSHenrik Bjoernlund 
br_cfm_peer_mep_count(struct net_bridge * br,u32 * count)834b6d0425bSHenrik Bjoernlund int br_cfm_peer_mep_count(struct net_bridge *br, u32 *count)
835b6d0425bSHenrik Bjoernlund {
836b6d0425bSHenrik Bjoernlund 	struct br_cfm_peer_mep *peer_mep;
837b6d0425bSHenrik Bjoernlund 	struct br_cfm_mep *mep;
838b6d0425bSHenrik Bjoernlund 
839b6d0425bSHenrik Bjoernlund 	*count = 0;
840b6d0425bSHenrik Bjoernlund 
841b6d0425bSHenrik Bjoernlund 	rcu_read_lock();
842b6d0425bSHenrik Bjoernlund 	hlist_for_each_entry_rcu(mep, &br->mep_list, head)
843b6d0425bSHenrik Bjoernlund 		hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head)
844b6d0425bSHenrik Bjoernlund 			*count += 1;
845b6d0425bSHenrik Bjoernlund 	rcu_read_unlock();
846b6d0425bSHenrik Bjoernlund 
847b6d0425bSHenrik Bjoernlund 	return 0;
848b6d0425bSHenrik Bjoernlund }
849b6d0425bSHenrik Bjoernlund 
br_cfm_created(struct net_bridge * br)8502be665c3SHenrik Bjoernlund bool br_cfm_created(struct net_bridge *br)
8512be665c3SHenrik Bjoernlund {
8522be665c3SHenrik Bjoernlund 	return !hlist_empty(&br->mep_list);
8532be665c3SHenrik Bjoernlund }
8542be665c3SHenrik Bjoernlund 
85586a14b79SHenrik Bjoernlund /* Deletes the CFM instances on a specific bridge port
85686a14b79SHenrik Bjoernlund  */
br_cfm_port_del(struct net_bridge * br,struct net_bridge_port * port)85786a14b79SHenrik Bjoernlund void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *port)
85886a14b79SHenrik Bjoernlund {
85986a14b79SHenrik Bjoernlund 	struct hlist_node *n_store;
86086a14b79SHenrik Bjoernlund 	struct br_cfm_mep *mep;
86186a14b79SHenrik Bjoernlund 
86286a14b79SHenrik Bjoernlund 	ASSERT_RTNL();
86386a14b79SHenrik Bjoernlund 
86486a14b79SHenrik Bjoernlund 	hlist_for_each_entry_safe(mep, n_store, &br->mep_list, head)
86586a14b79SHenrik Bjoernlund 		if (mep->create.ifindex == port->dev->ifindex)
86686a14b79SHenrik Bjoernlund 			mep_delete_implementation(br, mep);
86786a14b79SHenrik Bjoernlund }
868