All of lore.kernel.org
 help / color / mirror / Atom feed
From: Henrik Bjoernlund <henrik.bjoernlund@microchip.com>
To: <davem@davemloft.net>, <kuba@kernel.org>, <roopa@nvidia.com>,
	<nikolay@nvidia.com>, <jiri@mellanox.com>, <idosch@mellanox.com>,
	<linux-kernel@vger.kernel.org>, <netdev@vger.kernel.org>,
	<bridge@lists.linux-foundation.org>,
	<UNGLinuxDriver@microchip.com>
Cc: Henrik Bjoernlund <henrik.bjoernlund@microchip.com>,
	Horatiu Vultur <horatiu.vultur@microchip.com>
Subject: [net-next v2 11/11] bridge: cfm: Added CFM switchdev utilization.
Date: Thu, 1 Oct 2020 10:30:19 +0000	[thread overview]
Message-ID: <20201001103019.1342470-12-henrik.bjoernlund@microchip.com> (raw)
In-Reply-To: <20201001103019.1342470-1-henrik.bjoernlund@microchip.com>

The CFM kernel implementation is now trying to offload functionallity
in HW by utilizing the switchdev interface.

MEP instances are created/deleted and CCM frames are transmitted in HW.
Also handling of received CCM frames and the defect calculation is dome
in HW.

Reviewed-by: Horatiu Vultur  <horatiu.vultur@microchip.com>
Signed-off-by: Henrik Bjoernlund  <henrik.bjoernlund@microchip.com>
---
 net/bridge/Makefile           |   2 +-
 net/bridge/br_cfm.c           | 270 ++++++++++++++++++++++++++++++----
 net/bridge/br_cfm_netlink.c   |  51 +++----
 net/bridge/br_cfm_switchdev.c | 203 +++++++++++++++++++++++++
 net/bridge/br_private_cfm.h   |  63 +++++++-
 5 files changed, 530 insertions(+), 59 deletions(-)
 create mode 100644 net/bridge/br_cfm_switchdev.c

diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index 4702702a74d3..5d0a399825ef 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -28,4 +28,4 @@ obj-$(CONFIG_NETFILTER) += netfilter/
 
 bridge-$(CONFIG_BRIDGE_MRP)	+= br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o
 
-bridge-$(CONFIG_BRIDGE_CFM)	+= br_cfm.o br_cfm_netlink.o
+bridge-$(CONFIG_BRIDGE_CFM)	+= br_cfm_switchdev.o br_cfm.o br_cfm_netlink.o
diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c
index fc8268cb76c1..bfaee33acffb 100644
--- a/net/bridge/br_cfm.c
+++ b/net/bridge/br_cfm.c
@@ -184,9 +184,11 @@ static struct sk_buff *ccm_frame_build(struct br_cfm_mep *mep,
 	}
 	skb->dev = b_port->dev;
 	rcu_read_unlock();
-	/* The device cannot be deleted until the work_queue functions has
-	 * completed. This function is called from ccm_tx_work_expired()
-	 * that is a work_queue functions.
+	/* This function is called from ccm_tx_work_expired that
+	 * is a work_queue function.
+	 * It is also called from br_cfm_cc_rdi_set and br_cfm_cc_ccm_tx
+	 * that has the RTNL.
+	 * Due to this the device cannot be deleted.
 	 */
 
 	skb->protocol = htons(ETH_P_CFM);
@@ -500,6 +502,7 @@ int br_cfm_mep_create(struct net_bridge *br,
 {
 	struct net_bridge_port *p;
 	struct br_cfm_mep *mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -546,6 +549,11 @@ int br_cfm_mep_create(struct net_bridge *br,
 		}
 	}
 
+	/* Try create MEP in Switchdev */
+	swd_ret = br_cfm_switchdev_mep_create(br, instance, create, extack);
+	if (swd_ret && swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+
 	mep = kzalloc(sizeof(*mep), GFP_KERNEL);
 	if (!mep)
 		return -ENOMEM;
@@ -555,20 +563,21 @@ int br_cfm_mep_create(struct net_bridge *br,
 	rcu_assign_pointer(mep->b_port, p);
 
 	INIT_HLIST_HEAD(&mep->peer_mep_list);
-	INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired);
-
-	if (hlist_empty(&br->mep_list))
+	if ((swd_ret == -EOPNOTSUPP) && hlist_empty(&br->mep_list))
 		br_add_frame(br, &cfm_frame_type);
-
 	hlist_add_tail_rcu(&mep->head, &br->mep_list);
 
+	INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired);
+
 	return 0;
 }
 
-static void mep_delete_implementation(struct net_bridge *br,
-				      struct br_cfm_mep *mep)
+static int mep_delete_implementation(struct net_bridge *br,
+				     struct br_cfm_mep *mep,
+				     struct netlink_ext_ack *extack)
 {
 	struct br_cfm_peer_mep *peer_mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -579,14 +588,23 @@ static void mep_delete_implementation(struct net_bridge *br,
 		kfree_rcu(peer_mep, rcu);
 	}
 
-	cancel_delayed_work_sync(&mep->ccm_tx_dwork);
-
 	RCU_INIT_POINTER(mep->b_port, NULL);
 	hlist_del_rcu(&mep->head);
-	kfree_rcu(mep, rcu);
 
+	/* Try delete MEP in Switchdev */
+	swd_ret = br_cfm_switchdev_mep_delete(br, mep, extack);
+	if (swd_ret != -EOPNOTSUPP)
+		goto free;
+
+	swd_ret = 0;
 	if (hlist_empty(&br->mep_list))
 		br_del_frame(br, &cfm_frame_type);
+	cancel_delayed_work_sync(&mep->ccm_tx_dwork);
+
+free:
+	kfree_rcu(mep, rcu);
+
+	return swd_ret;
 }
 
 int br_cfm_mep_delete(struct net_bridge *br,
@@ -604,9 +622,7 @@ int br_cfm_mep_delete(struct net_bridge *br,
 		return -ENOENT;
 	}
 
-	mep_delete_implementation(br, mep);
-
-	return 0;
+	return mep_delete_implementation(br, mep, extack);
 }
 
 int br_cfm_mep_config_set(struct net_bridge *br,
@@ -615,6 +631,7 @@ int br_cfm_mep_config_set(struct net_bridge *br,
 			  struct netlink_ext_ack *extack)
 {
 	struct br_cfm_mep *mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -637,6 +654,11 @@ int br_cfm_mep_config_set(struct net_bridge *br,
 		return -ENOENT;
 	}
 
+	/* Try configure MEP in Switchdev */
+	swd_ret = br_cfm_switchdev_mep_config_set(br, mep, config, extack);
+	if (swd_ret && swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+
 	mep->config = *config;
 
 	return 0;
@@ -649,6 +671,7 @@ int br_cfm_cc_config_set(struct net_bridge *br,
 {
 	struct br_cfm_peer_mep *peer_mep;
 	struct br_cfm_mep *mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -663,6 +686,19 @@ int br_cfm_cc_config_set(struct net_bridge *br,
 	if (memcmp(config, &mep->cc_config, sizeof(*config)) == 0)
 		return 0;
 
+	/* Try configure CC in Switchdev */
+	swd_ret = br_cfm_switchdev_cc_config_set(br, mep, config, extack);
+	if (swd_ret && swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+
+	mep->cc_config = *config;
+	mep->ccm_rx_snumber = 0;
+	mep->ccm_tx_snumber = 1;
+
+	/* Return if switchdev. CCM is not transmitted or received here */
+	if (!swd_ret)
+		return 0;
+
 	if (config->enable && !mep->cc_config.enable)
 		/* CC is enabled */
 		hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head)
@@ -673,10 +709,6 @@ int br_cfm_cc_config_set(struct net_bridge *br,
 		hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head)
 			cc_peer_disable(peer_mep);
 
-	mep->cc_config = *config;
-	mep->ccm_rx_snumber = 0;
-	mep->ccm_tx_snumber = 1;
-
 	return 0;
 }
 
@@ -686,6 +718,7 @@ int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
 {
 	struct br_cfm_peer_mep *peer_mep;
 	struct br_cfm_mep *mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -709,19 +742,29 @@ int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
 		return -EEXIST;
 	}
 
+	/* Try add peer MEP in Switchdev */
+	swd_ret = br_cfm_switchdev_cc_peer_mep_add(br, mep, mepid, extack);
+	if (swd_ret && swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+
 	peer_mep = kzalloc(sizeof(*peer_mep), GFP_KERNEL);
 	if (!peer_mep)
 		return -ENOMEM;
 
 	peer_mep->mepid = mepid;
 	peer_mep->mep = mep;
+
+	hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list);
+
+	/* Return if switchdev. CCM is not transmitted or received here */
+	if (!swd_ret)
+		return 0;
+
 	INIT_DELAYED_WORK(&peer_mep->ccm_rx_dwork, ccm_rx_work_expired);
 
 	if (mep->cc_config.enable)
 		cc_peer_enable(peer_mep);
 
-	hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list);
-
 	return 0;
 }
 
@@ -731,6 +774,7 @@ int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
 {
 	struct br_cfm_peer_mep *peer_mep;
 	struct br_cfm_mep *mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -748,18 +792,42 @@ int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
 		return -ENOENT;
 	}
 
+	hlist_del_rcu(&peer_mep->head);
+
+	/* Try remove peer MEP in Switchdev */
+	swd_ret = br_cfm_switchdev_cc_peer_mep_remove(br, mep, mepid, extack);
+	if (swd_ret != -EOPNOTSUPP)
+		goto free;
+
+	swd_ret = 0;
 	cc_peer_disable(peer_mep);
 
-	hlist_del_rcu(&peer_mep->head);
+free:
 	kfree_rcu(peer_mep, rcu);
 
-	return 0;
+	return swd_ret;
+}
+
+static int swd_ccm_tx(struct net_bridge *br, struct br_cfm_mep *mep,
+		      struct netlink_ext_ack *extack)
+{
+	struct sk_buff *skb;
+
+	skb = ccm_frame_build(mep, &mep->cc_ccm_tx_info);
+	if (!skb)
+		return -ENOMEM;
+	return br_cfm_switchdev_cc_ccm_tx(br, mep, skb,
+					  mep->cc_ccm_tx_info.period,
+					  mep->cc_config.exp_interval,
+					  extack);
 }
 
 int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
 		      const bool rdi, struct netlink_ext_ack *extack)
 {
 	struct br_cfm_mep *mep;
+	bool rdi_changed;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -770,8 +838,18 @@ int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
 		return -ENOENT;
 	}
 
+	rdi_changed = (mep->rdi != rdi) ? true : false;
 	mep->rdi = rdi;
 
+	if (mep->ccm_tx_swd && rdi_changed) {
+		/* Transmitting using Switchdev is ongoing.
+		 * Restart with new RDI status
+		 */
+		swd_ret = swd_ccm_tx(br, mep, extack);
+		if (swd_ret && swd_ret != -EOPNOTSUPP)
+			return swd_ret;
+	}
+
 	return 0;
 }
 
@@ -780,6 +858,7 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
 		     struct netlink_ext_ack *extack)
 {
 	struct br_cfm_mep *mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -792,11 +871,21 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
 
 	if (memcmp(tx_info, &mep->cc_ccm_tx_info, sizeof(*tx_info)) == 0) {
 		/* No change in tx_info. */
+
 		if (mep->cc_ccm_tx_info.period == 0)
 			/* Transmission is not enabled - just return */
 			return 0;
 
-		/* Transmission is ongoing, the end time is recalculated */
+		/* Transmission is ongoing */
+
+		if (mep->ccm_tx_swd) {
+			/* Switchdev transmission started. Re-start transmission */
+			swd_ret = swd_ccm_tx(br, mep, extack);
+			if (swd_ret && swd_ret != -EOPNOTSUPP)
+				return swd_ret;
+		}
+
+		/* The period end time is recalculated */
 		mep->ccm_tx_end = jiffies +
 				  usecs_to_jiffies(tx_info->period * 1000000);
 		return 0;
@@ -807,9 +896,16 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
 		goto save;
 
 	if (tx_info->period != 0 && mep->cc_ccm_tx_info.period != 0) {
-		/* Some change in info and transmission is ongoing
-		 * The end time is recalculated
-		 */
+		/* Some change in info and transmission is ongoing */
+
+		if (mep->ccm_tx_swd) {
+			/* Switchdev transmission started. Re-start transmission */
+			swd_ret = swd_ccm_tx(br, mep, extack);
+			if (swd_ret && swd_ret != -EOPNOTSUPP)
+				return swd_ret;
+		}
+
+		/* The period end time is recalculated */
 		mep->ccm_tx_end = jiffies +
 				  usecs_to_jiffies(tx_info->period * 1000000);
 
@@ -817,12 +913,31 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
 	}
 
 	if (tx_info->period == 0 && mep->cc_ccm_tx_info.period != 0) {
+		/* Stop transmission */
+
+		/* Try stop transmission in Switchdev */
+		(void)br_cfm_switchdev_cc_ccm_tx(br, mep, NULL, 0, 0, extack);
+
 		cancel_delayed_work_sync(&mep->ccm_tx_dwork);
 		goto save;
 	}
 
-	/* Start delayed work to transmit CCM frames. It is done with zero delay
-	 * to send first frame immediately
+	/* Try start transmitting using Switchdev */
+	swd_ret = swd_ccm_tx(br, mep, extack);
+	if (swd_ret && swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+	if (!swd_ret) {
+		/* Switchdev transmission started */
+		mep->ccm_tx_swd = true;
+		goto save;
+	}
+
+	/* Switchdev CCM tx is not supported */
+	swd_ret = 0;
+	mep->ccm_tx_swd = false;
+
+	/* Start delayed work to transmit CCM frames. It is done with zero
+	 *  delay to send first frame immediately.
 	 */
 	mep->ccm_tx_end = jiffies + usecs_to_jiffies(tx_info->period * 1000000);
 	queue_delayed_work(system_wq, &mep->ccm_tx_dwork, 0);
@@ -830,6 +945,78 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
 save:
 	mep->cc_ccm_tx_info = *tx_info;
 
+	return swd_ret;
+}
+
+int br_cfm_mep_status_get(struct net_bridge *br, const u32 instance,
+			  bool clear, struct br_cfm_mep_status *const status,
+			  struct netlink_ext_ack *extack)
+{
+	struct br_cfm_mep *mep;
+	int swd_ret;
+
+	memset(status, 0, sizeof(*status));
+
+	mep = br_mep_find(br, instance);
+	if (!mep) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "MEP instance does not exists");
+		return -ENOENT;
+	}
+
+	/* Try get MEP status in Switchdev */
+	swd_ret = br_cfm_switchdev_mep_status_get(br, mep, clear, status, extack);
+	if (swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+
+	*status = mep->status;
+	if (clear) {
+		mep->status.opcode_unexp_seen = false;
+		mep->status.version_unexp_seen = false;
+		mep->status.rx_level_low_seen = false;
+	}
+
+	return 0;
+}
+
+int br_cfm_cc_peer_status_get(struct net_bridge *br, const u32 instance,
+			      u32 mepid, bool clear,
+			      struct br_cfm_cc_peer_status *const status,
+			      struct netlink_ext_ack *extack)
+{
+	struct br_cfm_peer_mep *peer_mep;
+	struct br_cfm_mep *mep;
+	int swd_ret;
+
+	memset(status, 0, sizeof(*status));
+
+	mep = br_mep_find(br, instance);
+	if (!mep) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "MEP instance does not exists");
+		return -ENOENT;
+	}
+
+	peer_mep = br_peer_mep_find(mep, mepid);
+	if (!peer_mep) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "Peer MEP-ID does not exists");
+		return -ENOENT;
+	}
+
+	/* Try get Peer MEP status in Switchdev */
+	swd_ret = br_cfm_switchdev_cc_peer_status_get(br, mep, mepid, clear,
+						      status, extack);
+	if (swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+
+	*status = peer_mep->cc_status;
+	if (clear) {
+		peer_mep->cc_status.seen = false;
+		peer_mep->cc_status.tlv_seen = false;
+		peer_mep->cc_status.seq_unexp_seen = false;
+	}
+
 	return 0;
 }
 
@@ -878,5 +1065,28 @@ void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *port)
 
 	hlist_for_each_entry(mep, &br->mep_list, head)
 		if (mep->create.ifindex == port->dev->ifindex)
-			mep_delete_implementation(br, mep);
+			(void)mep_delete_implementation(br, mep, NULL);
+}
+
+/* Notification function called from CFM driver */
+void br_cfm_notification(struct net_device *dev, const struct br_cfm_notif_info *const notif_info)
+{
+	struct net_bridge *br = netdev_priv(dev);
+	struct br_cfm_peer_mep *peer_mep;
+	struct net_bridge_port *b_port;
+	struct br_cfm_mep *mep;
+
+	mep = br_mep_find(br, notif_info->instance);
+	if (!mep)
+		return;
+
+	peer_mep = br_peer_mep_find(mep, notif_info->peer_mep);
+	if (!peer_mep)
+		return;
+
+	rcu_read_lock();
+	b_port = rcu_dereference(mep->b_port);
+	if (b_port)
+		br_cfm_notify(RTM_NEWLINK, b_port);
+	rcu_read_unlock();
 }
diff --git a/net/bridge/br_cfm_netlink.c b/net/bridge/br_cfm_netlink.c
index 5f81262c9caa..3d8561e59ace 100644
--- a/net/bridge/br_cfm_netlink.c
+++ b/net/bridge/br_cfm_netlink.c
@@ -622,42 +622,40 @@ int br_cfm_status_fill_info(struct sk_buff *skb,
 			    struct net_bridge *br,
 			    bool getlink)
 {
-	struct nlattr *tb;
-	struct br_cfm_mep *mep;
+	struct br_cfm_cc_peer_status cc_peer_status;
+	struct br_cfm_mep_status mep_status;
 	struct br_cfm_peer_mep *peer_mep;
+	struct br_cfm_mep *mep;
+	struct nlattr *tb;
 
 	hlist_for_each_entry_rcu(mep, &br->mep_list, head) {
 		tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_MEP_STATUS_INFO);
 		if (!tb)
 			goto nla_info_failure;
 
+		if (br_cfm_mep_status_get(br, mep->instance, getlink,
+					  &mep_status, NULL))
+			goto nla_info_failure;
+
 		if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE,
 				mep->instance))
 			goto nla_put_failure;
 
 		if (nla_put_u32(skb,
 				IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN,
-				mep->status.opcode_unexp_seen))
+				mep_status.opcode_unexp_seen))
 			goto nla_put_failure;
 
 		if (nla_put_u32(skb,
 				IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN,
-				mep->status.version_unexp_seen))
+				mep_status.version_unexp_seen))
 			goto nla_put_failure;
 
 		if (nla_put_u32(skb,
 				IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN,
-				mep->status.rx_level_low_seen))
+				mep_status.rx_level_low_seen))
 			goto nla_put_failure;
 
-		/* Only clear if this is a GETLINK */
-		if (getlink) {
-			/* Clear all 'seen' indications */
-			mep->status.opcode_unexp_seen = false;
-			mep->status.version_unexp_seen = false;
-			mep->status.rx_level_low_seen = false;
-		}
-
 		nla_nest_end(skb, tb);
 
 		hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head) {
@@ -666,6 +664,12 @@ int br_cfm_status_fill_info(struct sk_buff *skb,
 			if (!tb)
 				goto nla_info_failure;
 
+			if (br_cfm_cc_peer_status_get(br, mep->instance,
+						      peer_mep->mepid,
+						      getlink,
+						      &cc_peer_status, NULL))
+				goto nla_info_failure;
+
 			if (nla_put_u32(skb,
 					IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE,
 					mep->instance))
@@ -678,45 +682,38 @@ int br_cfm_status_fill_info(struct sk_buff *skb,
 
 			if (nla_put_u32(skb,
 					IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT,
-					peer_mep->cc_status.ccm_defect))
+					cc_peer_status.ccm_defect))
 				goto nla_put_failure;
 
 			if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI,
-					peer_mep->cc_status.rdi))
+					cc_peer_status.rdi))
 				goto nla_put_failure;
 
 			if (nla_put_u8(skb,
 				       IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE,
-				       peer_mep->cc_status.port_tlv_value))
+				       cc_peer_status.port_tlv_value))
 				goto nla_put_failure;
 
 			if (nla_put_u8(skb,
 				       IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE,
-				       peer_mep->cc_status.if_tlv_value))
+				       cc_peer_status.if_tlv_value))
 				goto nla_put_failure;
 
 			if (nla_put_u32(skb,
 					IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN,
-					peer_mep->cc_status.seen))
+					cc_peer_status.seen))
 				goto nla_put_failure;
 
 			if (nla_put_u32(skb,
 					IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN,
-					peer_mep->cc_status.tlv_seen))
+					cc_peer_status.tlv_seen))
 				goto nla_put_failure;
 
 			if (nla_put_u32(skb,
 					IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN,
-					peer_mep->cc_status.seq_unexp_seen))
+					cc_peer_status.seq_unexp_seen))
 				goto nla_put_failure;
 
-			if (getlink) { /* Only clear if this is a GETLINK */
-				/* Clear all 'seen' indications */
-				peer_mep->cc_status.seen = false;
-				peer_mep->cc_status.tlv_seen = false;
-				peer_mep->cc_status.seq_unexp_seen = false;
-			}
-
 			nla_nest_end(skb, tb);
 		}
 	}
diff --git a/net/bridge/br_cfm_switchdev.c b/net/bridge/br_cfm_switchdev.c
new file mode 100644
index 000000000000..d7441d57d113
--- /dev/null
+++ b/net/bridge/br_cfm_switchdev.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <net/switchdev.h>
+
+#include "br_private_cfm.h"
+
+int br_cfm_switchdev_mep_create(struct net_bridge *br,
+				const u32 instance,
+				struct br_cfm_mep_create *const create,
+				struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_mep cfm_obj;
+	struct net_bridge_port *port;
+	bool found = false;
+
+	list_for_each_entry(port, &br->port_list, list)
+		if (port->dev->ifindex == create->ifindex) {
+			found = true;
+			break;
+		}
+	if (!found)
+		return -EINVAL;
+
+	cfm_obj.obj.orig_dev = br->dev;
+	cfm_obj.obj.id = SWITCHDEV_OBJ_ID_MEP_CFM;
+	cfm_obj.obj.flags = 0;
+	cfm_obj.domain = create->domain;
+	cfm_obj.direction = create->direction;
+	cfm_obj.port = port->dev;
+
+	return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_mep_delete(struct net_bridge *br,
+				struct br_cfm_mep *mep,
+				struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_mep cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_MEP_CFM,
+		.obj.flags = 0,
+		.domain = 0,
+		.direction = 0,
+		.port = NULL,
+	};
+
+	return switchdev_port_obj_del(br->dev, &cfm_obj.obj);
+}
+
+int br_cfm_switchdev_mep_config_set(struct net_bridge *br,
+				    struct br_cfm_mep *mep,
+				    const struct br_cfm_mep_config *const config,
+				    struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_mep_config_set cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.unicast_mac = config->unicast_mac,
+		.mdlevel = config->mdlevel,
+		.mepid = config->mepid,
+	};
+
+	return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_cc_config_set(struct net_bridge *br,
+				   struct br_cfm_mep *mep,
+				   const struct br_cfm_cc_config *const config,
+				   struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_cc_config_set cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_CC_CONFIG_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.interval = config->exp_interval,
+		.maid = config->exp_maid,
+		.enable = config->enable,
+	};
+
+	return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_cc_peer_mep_add(struct net_bridge *br,
+				     struct br_cfm_mep *mep,
+				     u32 peer_mep_id,
+				     struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_cc_peer_mep cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.peer_mep_id = peer_mep_id,
+	};
+
+	return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_cc_peer_mep_remove(struct net_bridge *br,
+					struct br_cfm_mep *mep,
+					u32 peer_mep_id,
+					struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_cc_peer_mep cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.peer_mep_id = peer_mep_id,
+	};
+
+	return switchdev_port_obj_del(br->dev, &cfm_obj.obj);
+}
+
+int br_cfm_switchdev_cc_ccm_tx(struct net_bridge *br,
+			       struct br_cfm_mep *mep,
+			       struct sk_buff *skb,
+			       u32 period,
+			       enum br_cfm_ccm_interval interval,
+			       struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_cc_ccm_tx cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.skb = skb,
+		.interval = interval,
+		.period = period,
+	};
+
+	return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_mep_status_get(struct net_bridge *br,
+				    struct br_cfm_mep *mep,
+				    bool   clear,
+				    struct br_cfm_mep_status *const status,
+				    struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_mep_status_get cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_MEP_STATUS_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.clear = clear,
+		.opcode_unexp_seen = false,
+		.version_unexp_seen = false,
+		.rx_level_low_seen = false,
+	};
+	int err;
+
+	err = switchdev_port_obj_get(br->dev, &cfm_obj.obj, extack);
+	if (err)
+		return err;
+
+	status->opcode_unexp_seen = cfm_obj.opcode_unexp_seen;
+	status->version_unexp_seen = cfm_obj.version_unexp_seen;
+	status->rx_level_low_seen = cfm_obj.rx_level_low_seen;
+
+	return 0;
+}
+
+int br_cfm_switchdev_cc_peer_status_get(struct net_bridge *br,
+					struct br_cfm_mep *mep,
+					u32 peer_mep_id,
+					bool clear,
+					struct br_cfm_cc_peer_status *const status,
+					struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_cc_peer_status_get cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.clear = clear,
+		.port_tlv_value = 0,
+		.if_tlv_value = 0,
+		.ccm_defect = false,
+		.rdi = false,
+		.seen = false,
+		.tlv_seen = false,
+		.seq_unexp_seen = false,
+	};
+	int err;
+
+	err = switchdev_port_obj_get(br->dev, &cfm_obj.obj, extack);
+	if (err)
+		return err;
+
+	status->port_tlv_value = cfm_obj.port_tlv_value;
+	status->if_tlv_value = cfm_obj.if_tlv_value;
+	status->ccm_defect = cfm_obj.ccm_defect;
+	status->rdi = cfm_obj.rdi;
+	status->seen = cfm_obj.seen;
+	status->tlv_seen = cfm_obj.tlv_seen;
+	status->seq_unexp_seen = cfm_obj.seq_unexp_seen;
+
+	return 0;
+}
diff --git a/net/bridge/br_private_cfm.h b/net/bridge/br_private_cfm.h
index 6a2294c0ea79..a91d0b59c27f 100644
--- a/net/bridge/br_private_cfm.h
+++ b/net/bridge/br_private_cfm.h
@@ -6,6 +6,7 @@
 #include "br_private.h"
 #include <uapi/linux/cfm_bridge.h>
 
+/* br_cfm.c */
 struct br_cfm_mep_create {
 	enum br_cfm_domain domain; /* Domain for this MEP */
 	enum br_cfm_mep_direction direction; /* Up or Down MEP direction */
@@ -55,13 +56,13 @@ int br_cfm_cc_config_set(struct net_bridge *br,
 int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
 			   u32 peer_mep_id,
 			   struct netlink_ext_ack *extack);
+
 int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
 			      u32 peer_mep_id,
 			      struct netlink_ext_ack *extack);
 
 /* Transmitted CCM Remote Defect Indication status set.
  * This RDI is inserted in transmitted CCM PDUs if CCM transmission is enabled.
- * See br_cfm_cc_ccm_tx() with interval != BR_CFM_CCM_INTERVAL_NONE
  */
 int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
 		      const bool rdi, struct netlink_ext_ack *extack);
@@ -96,6 +97,10 @@ struct br_cfm_mep_status {
 	bool rx_level_low_seen; /* Rx of OAM PDU with level low */
 };
 
+int br_cfm_mep_status_get(struct net_bridge *br, const u32 instance,
+			  bool clear, struct br_cfm_mep_status *const status,
+			  struct netlink_ext_ack *extack);
+
 struct br_cfm_cc_peer_status {
 	/* This CCM related status is based on the latest received CCM PDU. */
 	u8 port_tlv_value; /* Port Status TLV value */
@@ -114,6 +119,11 @@ struct br_cfm_cc_peer_status {
 	bool seq_unexp_seen;
 };
 
+int br_cfm_cc_peer_status_get(struct net_bridge *br, const u32 instance,
+			      u32 mepid, bool clear,
+			      struct br_cfm_cc_peer_status *const status,
+			      struct netlink_ext_ack *extack);
+
 struct br_cfm_mep {
 	/* list header of MEP instances */
 	struct hlist_node		head;
@@ -131,6 +141,7 @@ struct br_cfm_mep {
 	u32				ccm_rx_snumber;
 	struct br_cfm_mep_status	status;
 	bool				rdi;
+	bool				ccm_tx_swd;
 	struct rcu_head			rcu;
 };
 
@@ -144,4 +155,54 @@ struct br_cfm_peer_mep {
 	struct rcu_head			rcu;
 };
 
+/* br_cfm_switchdev.c */
+int br_cfm_switchdev_mep_create(struct net_bridge *br,
+				const u32 instance,
+				struct br_cfm_mep_create *const create,
+				struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_mep_delete(struct net_bridge *br,
+				struct br_cfm_mep *mep,
+				struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_mep_config_set(struct net_bridge *br,
+				    struct br_cfm_mep *mep,
+				    const struct br_cfm_mep_config *const config,
+				    struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_config_set(struct net_bridge *br,
+				   struct br_cfm_mep *mep,
+				   const struct br_cfm_cc_config *const config,
+				   struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_peer_mep_add(struct net_bridge *br,
+				     struct br_cfm_mep *mep,
+				     u32 peer_mep_id,
+				     struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_peer_mep_remove(struct net_bridge *br,
+					struct br_cfm_mep *mep,
+					u32 peer_mep_id,
+					struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_ccm_tx(struct net_bridge *br,
+			       struct br_cfm_mep *mep,
+			       struct sk_buff *skb,
+			       u32 period,
+			       enum br_cfm_ccm_interval interval,
+			       struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_mep_status_get(struct net_bridge *br,
+				    struct br_cfm_mep *mep,
+				    bool   clear,
+				    struct br_cfm_mep_status *const status,
+				    struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_peer_status_get(struct net_bridge *br,
+					struct br_cfm_mep *mep,
+					u32 peer_mep_id,
+					bool clear,
+					struct br_cfm_cc_peer_status *const status,
+					struct netlink_ext_ack *extack);
+
 #endif /* _BR_PRIVATE_CFM_H_ */
-- 
2.28.0


WARNING: multiple messages have this Message-ID (diff)
From: Henrik Bjoernlund <henrik.bjoernlund@microchip.com>
To: davem@davemloft.net, kuba@kernel.org, roopa@nvidia.com,
	nikolay@nvidia.com, jiri@mellanox.com, idosch@mellanox.com,
	linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
	bridge@lists.linux-foundation.org, UNGLinuxDriver@microchip.com
Cc: Henrik Bjoernlund <henrik.bjoernlund@microchip.com>,
	Horatiu Vultur <horatiu.vultur@microchip.com>
Subject: [Bridge] [net-next v2 11/11] bridge: cfm: Added CFM switchdev utilization.
Date: Thu, 1 Oct 2020 10:30:19 +0000	[thread overview]
Message-ID: <20201001103019.1342470-12-henrik.bjoernlund@microchip.com> (raw)
In-Reply-To: <20201001103019.1342470-1-henrik.bjoernlund@microchip.com>

The CFM kernel implementation is now trying to offload functionallity
in HW by utilizing the switchdev interface.

MEP instances are created/deleted and CCM frames are transmitted in HW.
Also handling of received CCM frames and the defect calculation is dome
in HW.

Reviewed-by: Horatiu Vultur  <horatiu.vultur@microchip.com>
Signed-off-by: Henrik Bjoernlund  <henrik.bjoernlund@microchip.com>
---
 net/bridge/Makefile           |   2 +-
 net/bridge/br_cfm.c           | 270 ++++++++++++++++++++++++++++++----
 net/bridge/br_cfm_netlink.c   |  51 +++----
 net/bridge/br_cfm_switchdev.c | 203 +++++++++++++++++++++++++
 net/bridge/br_private_cfm.h   |  63 +++++++-
 5 files changed, 530 insertions(+), 59 deletions(-)
 create mode 100644 net/bridge/br_cfm_switchdev.c

diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index 4702702a74d3..5d0a399825ef 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -28,4 +28,4 @@ obj-$(CONFIG_NETFILTER) += netfilter/
 
 bridge-$(CONFIG_BRIDGE_MRP)	+= br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o
 
-bridge-$(CONFIG_BRIDGE_CFM)	+= br_cfm.o br_cfm_netlink.o
+bridge-$(CONFIG_BRIDGE_CFM)	+= br_cfm_switchdev.o br_cfm.o br_cfm_netlink.o
diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c
index fc8268cb76c1..bfaee33acffb 100644
--- a/net/bridge/br_cfm.c
+++ b/net/bridge/br_cfm.c
@@ -184,9 +184,11 @@ static struct sk_buff *ccm_frame_build(struct br_cfm_mep *mep,
 	}
 	skb->dev = b_port->dev;
 	rcu_read_unlock();
-	/* The device cannot be deleted until the work_queue functions has
-	 * completed. This function is called from ccm_tx_work_expired()
-	 * that is a work_queue functions.
+	/* This function is called from ccm_tx_work_expired that
+	 * is a work_queue function.
+	 * It is also called from br_cfm_cc_rdi_set and br_cfm_cc_ccm_tx
+	 * that has the RTNL.
+	 * Due to this the device cannot be deleted.
 	 */
 
 	skb->protocol = htons(ETH_P_CFM);
@@ -500,6 +502,7 @@ int br_cfm_mep_create(struct net_bridge *br,
 {
 	struct net_bridge_port *p;
 	struct br_cfm_mep *mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -546,6 +549,11 @@ int br_cfm_mep_create(struct net_bridge *br,
 		}
 	}
 
+	/* Try create MEP in Switchdev */
+	swd_ret = br_cfm_switchdev_mep_create(br, instance, create, extack);
+	if (swd_ret && swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+
 	mep = kzalloc(sizeof(*mep), GFP_KERNEL);
 	if (!mep)
 		return -ENOMEM;
@@ -555,20 +563,21 @@ int br_cfm_mep_create(struct net_bridge *br,
 	rcu_assign_pointer(mep->b_port, p);
 
 	INIT_HLIST_HEAD(&mep->peer_mep_list);
-	INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired);
-
-	if (hlist_empty(&br->mep_list))
+	if ((swd_ret == -EOPNOTSUPP) && hlist_empty(&br->mep_list))
 		br_add_frame(br, &cfm_frame_type);
-
 	hlist_add_tail_rcu(&mep->head, &br->mep_list);
 
+	INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired);
+
 	return 0;
 }
 
-static void mep_delete_implementation(struct net_bridge *br,
-				      struct br_cfm_mep *mep)
+static int mep_delete_implementation(struct net_bridge *br,
+				     struct br_cfm_mep *mep,
+				     struct netlink_ext_ack *extack)
 {
 	struct br_cfm_peer_mep *peer_mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -579,14 +588,23 @@ static void mep_delete_implementation(struct net_bridge *br,
 		kfree_rcu(peer_mep, rcu);
 	}
 
-	cancel_delayed_work_sync(&mep->ccm_tx_dwork);
-
 	RCU_INIT_POINTER(mep->b_port, NULL);
 	hlist_del_rcu(&mep->head);
-	kfree_rcu(mep, rcu);
 
+	/* Try delete MEP in Switchdev */
+	swd_ret = br_cfm_switchdev_mep_delete(br, mep, extack);
+	if (swd_ret != -EOPNOTSUPP)
+		goto free;
+
+	swd_ret = 0;
 	if (hlist_empty(&br->mep_list))
 		br_del_frame(br, &cfm_frame_type);
+	cancel_delayed_work_sync(&mep->ccm_tx_dwork);
+
+free:
+	kfree_rcu(mep, rcu);
+
+	return swd_ret;
 }
 
 int br_cfm_mep_delete(struct net_bridge *br,
@@ -604,9 +622,7 @@ int br_cfm_mep_delete(struct net_bridge *br,
 		return -ENOENT;
 	}
 
-	mep_delete_implementation(br, mep);
-
-	return 0;
+	return mep_delete_implementation(br, mep, extack);
 }
 
 int br_cfm_mep_config_set(struct net_bridge *br,
@@ -615,6 +631,7 @@ int br_cfm_mep_config_set(struct net_bridge *br,
 			  struct netlink_ext_ack *extack)
 {
 	struct br_cfm_mep *mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -637,6 +654,11 @@ int br_cfm_mep_config_set(struct net_bridge *br,
 		return -ENOENT;
 	}
 
+	/* Try configure MEP in Switchdev */
+	swd_ret = br_cfm_switchdev_mep_config_set(br, mep, config, extack);
+	if (swd_ret && swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+
 	mep->config = *config;
 
 	return 0;
@@ -649,6 +671,7 @@ int br_cfm_cc_config_set(struct net_bridge *br,
 {
 	struct br_cfm_peer_mep *peer_mep;
 	struct br_cfm_mep *mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -663,6 +686,19 @@ int br_cfm_cc_config_set(struct net_bridge *br,
 	if (memcmp(config, &mep->cc_config, sizeof(*config)) == 0)
 		return 0;
 
+	/* Try configure CC in Switchdev */
+	swd_ret = br_cfm_switchdev_cc_config_set(br, mep, config, extack);
+	if (swd_ret && swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+
+	mep->cc_config = *config;
+	mep->ccm_rx_snumber = 0;
+	mep->ccm_tx_snumber = 1;
+
+	/* Return if switchdev. CCM is not transmitted or received here */
+	if (!swd_ret)
+		return 0;
+
 	if (config->enable && !mep->cc_config.enable)
 		/* CC is enabled */
 		hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head)
@@ -673,10 +709,6 @@ int br_cfm_cc_config_set(struct net_bridge *br,
 		hlist_for_each_entry(peer_mep, &mep->peer_mep_list, head)
 			cc_peer_disable(peer_mep);
 
-	mep->cc_config = *config;
-	mep->ccm_rx_snumber = 0;
-	mep->ccm_tx_snumber = 1;
-
 	return 0;
 }
 
@@ -686,6 +718,7 @@ int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
 {
 	struct br_cfm_peer_mep *peer_mep;
 	struct br_cfm_mep *mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -709,19 +742,29 @@ int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
 		return -EEXIST;
 	}
 
+	/* Try add peer MEP in Switchdev */
+	swd_ret = br_cfm_switchdev_cc_peer_mep_add(br, mep, mepid, extack);
+	if (swd_ret && swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+
 	peer_mep = kzalloc(sizeof(*peer_mep), GFP_KERNEL);
 	if (!peer_mep)
 		return -ENOMEM;
 
 	peer_mep->mepid = mepid;
 	peer_mep->mep = mep;
+
+	hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list);
+
+	/* Return if switchdev. CCM is not transmitted or received here */
+	if (!swd_ret)
+		return 0;
+
 	INIT_DELAYED_WORK(&peer_mep->ccm_rx_dwork, ccm_rx_work_expired);
 
 	if (mep->cc_config.enable)
 		cc_peer_enable(peer_mep);
 
-	hlist_add_tail_rcu(&peer_mep->head, &mep->peer_mep_list);
-
 	return 0;
 }
 
@@ -731,6 +774,7 @@ int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
 {
 	struct br_cfm_peer_mep *peer_mep;
 	struct br_cfm_mep *mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -748,18 +792,42 @@ int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
 		return -ENOENT;
 	}
 
+	hlist_del_rcu(&peer_mep->head);
+
+	/* Try remove peer MEP in Switchdev */
+	swd_ret = br_cfm_switchdev_cc_peer_mep_remove(br, mep, mepid, extack);
+	if (swd_ret != -EOPNOTSUPP)
+		goto free;
+
+	swd_ret = 0;
 	cc_peer_disable(peer_mep);
 
-	hlist_del_rcu(&peer_mep->head);
+free:
 	kfree_rcu(peer_mep, rcu);
 
-	return 0;
+	return swd_ret;
+}
+
+static int swd_ccm_tx(struct net_bridge *br, struct br_cfm_mep *mep,
+		      struct netlink_ext_ack *extack)
+{
+	struct sk_buff *skb;
+
+	skb = ccm_frame_build(mep, &mep->cc_ccm_tx_info);
+	if (!skb)
+		return -ENOMEM;
+	return br_cfm_switchdev_cc_ccm_tx(br, mep, skb,
+					  mep->cc_ccm_tx_info.period,
+					  mep->cc_config.exp_interval,
+					  extack);
 }
 
 int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
 		      const bool rdi, struct netlink_ext_ack *extack)
 {
 	struct br_cfm_mep *mep;
+	bool rdi_changed;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -770,8 +838,18 @@ int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
 		return -ENOENT;
 	}
 
+	rdi_changed = (mep->rdi != rdi) ? true : false;
 	mep->rdi = rdi;
 
+	if (mep->ccm_tx_swd && rdi_changed) {
+		/* Transmitting using Switchdev is ongoing.
+		 * Restart with new RDI status
+		 */
+		swd_ret = swd_ccm_tx(br, mep, extack);
+		if (swd_ret && swd_ret != -EOPNOTSUPP)
+			return swd_ret;
+	}
+
 	return 0;
 }
 
@@ -780,6 +858,7 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
 		     struct netlink_ext_ack *extack)
 {
 	struct br_cfm_mep *mep;
+	int swd_ret;
 
 	ASSERT_RTNL();
 
@@ -792,11 +871,21 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
 
 	if (memcmp(tx_info, &mep->cc_ccm_tx_info, sizeof(*tx_info)) == 0) {
 		/* No change in tx_info. */
+
 		if (mep->cc_ccm_tx_info.period == 0)
 			/* Transmission is not enabled - just return */
 			return 0;
 
-		/* Transmission is ongoing, the end time is recalculated */
+		/* Transmission is ongoing */
+
+		if (mep->ccm_tx_swd) {
+			/* Switchdev transmission started. Re-start transmission */
+			swd_ret = swd_ccm_tx(br, mep, extack);
+			if (swd_ret && swd_ret != -EOPNOTSUPP)
+				return swd_ret;
+		}
+
+		/* The period end time is recalculated */
 		mep->ccm_tx_end = jiffies +
 				  usecs_to_jiffies(tx_info->period * 1000000);
 		return 0;
@@ -807,9 +896,16 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
 		goto save;
 
 	if (tx_info->period != 0 && mep->cc_ccm_tx_info.period != 0) {
-		/* Some change in info and transmission is ongoing
-		 * The end time is recalculated
-		 */
+		/* Some change in info and transmission is ongoing */
+
+		if (mep->ccm_tx_swd) {
+			/* Switchdev transmission started. Re-start transmission */
+			swd_ret = swd_ccm_tx(br, mep, extack);
+			if (swd_ret && swd_ret != -EOPNOTSUPP)
+				return swd_ret;
+		}
+
+		/* The period end time is recalculated */
 		mep->ccm_tx_end = jiffies +
 				  usecs_to_jiffies(tx_info->period * 1000000);
 
@@ -817,12 +913,31 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
 	}
 
 	if (tx_info->period == 0 && mep->cc_ccm_tx_info.period != 0) {
+		/* Stop transmission */
+
+		/* Try stop transmission in Switchdev */
+		(void)br_cfm_switchdev_cc_ccm_tx(br, mep, NULL, 0, 0, extack);
+
 		cancel_delayed_work_sync(&mep->ccm_tx_dwork);
 		goto save;
 	}
 
-	/* Start delayed work to transmit CCM frames. It is done with zero delay
-	 * to send first frame immediately
+	/* Try start transmitting using Switchdev */
+	swd_ret = swd_ccm_tx(br, mep, extack);
+	if (swd_ret && swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+	if (!swd_ret) {
+		/* Switchdev transmission started */
+		mep->ccm_tx_swd = true;
+		goto save;
+	}
+
+	/* Switchdev CCM tx is not supported */
+	swd_ret = 0;
+	mep->ccm_tx_swd = false;
+
+	/* Start delayed work to transmit CCM frames. It is done with zero
+	 *  delay to send first frame immediately.
 	 */
 	mep->ccm_tx_end = jiffies + usecs_to_jiffies(tx_info->period * 1000000);
 	queue_delayed_work(system_wq, &mep->ccm_tx_dwork, 0);
@@ -830,6 +945,78 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
 save:
 	mep->cc_ccm_tx_info = *tx_info;
 
+	return swd_ret;
+}
+
+int br_cfm_mep_status_get(struct net_bridge *br, const u32 instance,
+			  bool clear, struct br_cfm_mep_status *const status,
+			  struct netlink_ext_ack *extack)
+{
+	struct br_cfm_mep *mep;
+	int swd_ret;
+
+	memset(status, 0, sizeof(*status));
+
+	mep = br_mep_find(br, instance);
+	if (!mep) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "MEP instance does not exists");
+		return -ENOENT;
+	}
+
+	/* Try get MEP status in Switchdev */
+	swd_ret = br_cfm_switchdev_mep_status_get(br, mep, clear, status, extack);
+	if (swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+
+	*status = mep->status;
+	if (clear) {
+		mep->status.opcode_unexp_seen = false;
+		mep->status.version_unexp_seen = false;
+		mep->status.rx_level_low_seen = false;
+	}
+
+	return 0;
+}
+
+int br_cfm_cc_peer_status_get(struct net_bridge *br, const u32 instance,
+			      u32 mepid, bool clear,
+			      struct br_cfm_cc_peer_status *const status,
+			      struct netlink_ext_ack *extack)
+{
+	struct br_cfm_peer_mep *peer_mep;
+	struct br_cfm_mep *mep;
+	int swd_ret;
+
+	memset(status, 0, sizeof(*status));
+
+	mep = br_mep_find(br, instance);
+	if (!mep) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "MEP instance does not exists");
+		return -ENOENT;
+	}
+
+	peer_mep = br_peer_mep_find(mep, mepid);
+	if (!peer_mep) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "Peer MEP-ID does not exists");
+		return -ENOENT;
+	}
+
+	/* Try get Peer MEP status in Switchdev */
+	swd_ret = br_cfm_switchdev_cc_peer_status_get(br, mep, mepid, clear,
+						      status, extack);
+	if (swd_ret != -EOPNOTSUPP)
+		return swd_ret;
+
+	*status = peer_mep->cc_status;
+	if (clear) {
+		peer_mep->cc_status.seen = false;
+		peer_mep->cc_status.tlv_seen = false;
+		peer_mep->cc_status.seq_unexp_seen = false;
+	}
+
 	return 0;
 }
 
@@ -878,5 +1065,28 @@ void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *port)
 
 	hlist_for_each_entry(mep, &br->mep_list, head)
 		if (mep->create.ifindex == port->dev->ifindex)
-			mep_delete_implementation(br, mep);
+			(void)mep_delete_implementation(br, mep, NULL);
+}
+
+/* Notification function called from CFM driver */
+void br_cfm_notification(struct net_device *dev, const struct br_cfm_notif_info *const notif_info)
+{
+	struct net_bridge *br = netdev_priv(dev);
+	struct br_cfm_peer_mep *peer_mep;
+	struct net_bridge_port *b_port;
+	struct br_cfm_mep *mep;
+
+	mep = br_mep_find(br, notif_info->instance);
+	if (!mep)
+		return;
+
+	peer_mep = br_peer_mep_find(mep, notif_info->peer_mep);
+	if (!peer_mep)
+		return;
+
+	rcu_read_lock();
+	b_port = rcu_dereference(mep->b_port);
+	if (b_port)
+		br_cfm_notify(RTM_NEWLINK, b_port);
+	rcu_read_unlock();
 }
diff --git a/net/bridge/br_cfm_netlink.c b/net/bridge/br_cfm_netlink.c
index 5f81262c9caa..3d8561e59ace 100644
--- a/net/bridge/br_cfm_netlink.c
+++ b/net/bridge/br_cfm_netlink.c
@@ -622,42 +622,40 @@ int br_cfm_status_fill_info(struct sk_buff *skb,
 			    struct net_bridge *br,
 			    bool getlink)
 {
-	struct nlattr *tb;
-	struct br_cfm_mep *mep;
+	struct br_cfm_cc_peer_status cc_peer_status;
+	struct br_cfm_mep_status mep_status;
 	struct br_cfm_peer_mep *peer_mep;
+	struct br_cfm_mep *mep;
+	struct nlattr *tb;
 
 	hlist_for_each_entry_rcu(mep, &br->mep_list, head) {
 		tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_MEP_STATUS_INFO);
 		if (!tb)
 			goto nla_info_failure;
 
+		if (br_cfm_mep_status_get(br, mep->instance, getlink,
+					  &mep_status, NULL))
+			goto nla_info_failure;
+
 		if (nla_put_u32(skb, IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE,
 				mep->instance))
 			goto nla_put_failure;
 
 		if (nla_put_u32(skb,
 				IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN,
-				mep->status.opcode_unexp_seen))
+				mep_status.opcode_unexp_seen))
 			goto nla_put_failure;
 
 		if (nla_put_u32(skb,
 				IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN,
-				mep->status.version_unexp_seen))
+				mep_status.version_unexp_seen))
 			goto nla_put_failure;
 
 		if (nla_put_u32(skb,
 				IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN,
-				mep->status.rx_level_low_seen))
+				mep_status.rx_level_low_seen))
 			goto nla_put_failure;
 
-		/* Only clear if this is a GETLINK */
-		if (getlink) {
-			/* Clear all 'seen' indications */
-			mep->status.opcode_unexp_seen = false;
-			mep->status.version_unexp_seen = false;
-			mep->status.rx_level_low_seen = false;
-		}
-
 		nla_nest_end(skb, tb);
 
 		hlist_for_each_entry_rcu(peer_mep, &mep->peer_mep_list, head) {
@@ -666,6 +664,12 @@ int br_cfm_status_fill_info(struct sk_buff *skb,
 			if (!tb)
 				goto nla_info_failure;
 
+			if (br_cfm_cc_peer_status_get(br, mep->instance,
+						      peer_mep->mepid,
+						      getlink,
+						      &cc_peer_status, NULL))
+				goto nla_info_failure;
+
 			if (nla_put_u32(skb,
 					IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE,
 					mep->instance))
@@ -678,45 +682,38 @@ int br_cfm_status_fill_info(struct sk_buff *skb,
 
 			if (nla_put_u32(skb,
 					IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT,
-					peer_mep->cc_status.ccm_defect))
+					cc_peer_status.ccm_defect))
 				goto nla_put_failure;
 
 			if (nla_put_u32(skb, IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI,
-					peer_mep->cc_status.rdi))
+					cc_peer_status.rdi))
 				goto nla_put_failure;
 
 			if (nla_put_u8(skb,
 				       IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE,
-				       peer_mep->cc_status.port_tlv_value))
+				       cc_peer_status.port_tlv_value))
 				goto nla_put_failure;
 
 			if (nla_put_u8(skb,
 				       IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE,
-				       peer_mep->cc_status.if_tlv_value))
+				       cc_peer_status.if_tlv_value))
 				goto nla_put_failure;
 
 			if (nla_put_u32(skb,
 					IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN,
-					peer_mep->cc_status.seen))
+					cc_peer_status.seen))
 				goto nla_put_failure;
 
 			if (nla_put_u32(skb,
 					IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN,
-					peer_mep->cc_status.tlv_seen))
+					cc_peer_status.tlv_seen))
 				goto nla_put_failure;
 
 			if (nla_put_u32(skb,
 					IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN,
-					peer_mep->cc_status.seq_unexp_seen))
+					cc_peer_status.seq_unexp_seen))
 				goto nla_put_failure;
 
-			if (getlink) { /* Only clear if this is a GETLINK */
-				/* Clear all 'seen' indications */
-				peer_mep->cc_status.seen = false;
-				peer_mep->cc_status.tlv_seen = false;
-				peer_mep->cc_status.seq_unexp_seen = false;
-			}
-
 			nla_nest_end(skb, tb);
 		}
 	}
diff --git a/net/bridge/br_cfm_switchdev.c b/net/bridge/br_cfm_switchdev.c
new file mode 100644
index 000000000000..d7441d57d113
--- /dev/null
+++ b/net/bridge/br_cfm_switchdev.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <net/switchdev.h>
+
+#include "br_private_cfm.h"
+
+int br_cfm_switchdev_mep_create(struct net_bridge *br,
+				const u32 instance,
+				struct br_cfm_mep_create *const create,
+				struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_mep cfm_obj;
+	struct net_bridge_port *port;
+	bool found = false;
+
+	list_for_each_entry(port, &br->port_list, list)
+		if (port->dev->ifindex == create->ifindex) {
+			found = true;
+			break;
+		}
+	if (!found)
+		return -EINVAL;
+
+	cfm_obj.obj.orig_dev = br->dev;
+	cfm_obj.obj.id = SWITCHDEV_OBJ_ID_MEP_CFM;
+	cfm_obj.obj.flags = 0;
+	cfm_obj.domain = create->domain;
+	cfm_obj.direction = create->direction;
+	cfm_obj.port = port->dev;
+
+	return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_mep_delete(struct net_bridge *br,
+				struct br_cfm_mep *mep,
+				struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_mep cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_MEP_CFM,
+		.obj.flags = 0,
+		.domain = 0,
+		.direction = 0,
+		.port = NULL,
+	};
+
+	return switchdev_port_obj_del(br->dev, &cfm_obj.obj);
+}
+
+int br_cfm_switchdev_mep_config_set(struct net_bridge *br,
+				    struct br_cfm_mep *mep,
+				    const struct br_cfm_mep_config *const config,
+				    struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_mep_config_set cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_MEP_CONFIG_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.unicast_mac = config->unicast_mac,
+		.mdlevel = config->mdlevel,
+		.mepid = config->mepid,
+	};
+
+	return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_cc_config_set(struct net_bridge *br,
+				   struct br_cfm_mep *mep,
+				   const struct br_cfm_cc_config *const config,
+				   struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_cc_config_set cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_CC_CONFIG_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.interval = config->exp_interval,
+		.maid = config->exp_maid,
+		.enable = config->enable,
+	};
+
+	return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_cc_peer_mep_add(struct net_bridge *br,
+				     struct br_cfm_mep *mep,
+				     u32 peer_mep_id,
+				     struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_cc_peer_mep cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.peer_mep_id = peer_mep_id,
+	};
+
+	return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_cc_peer_mep_remove(struct net_bridge *br,
+					struct br_cfm_mep *mep,
+					u32 peer_mep_id,
+					struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_cc_peer_mep cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_CC_PEER_MEP_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.peer_mep_id = peer_mep_id,
+	};
+
+	return switchdev_port_obj_del(br->dev, &cfm_obj.obj);
+}
+
+int br_cfm_switchdev_cc_ccm_tx(struct net_bridge *br,
+			       struct br_cfm_mep *mep,
+			       struct sk_buff *skb,
+			       u32 period,
+			       enum br_cfm_ccm_interval interval,
+			       struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_cc_ccm_tx cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_CC_CCM_TX_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.skb = skb,
+		.interval = interval,
+		.period = period,
+	};
+
+	return switchdev_port_obj_add(br->dev, &cfm_obj.obj, extack);
+}
+
+int br_cfm_switchdev_mep_status_get(struct net_bridge *br,
+				    struct br_cfm_mep *mep,
+				    bool   clear,
+				    struct br_cfm_mep_status *const status,
+				    struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_mep_status_get cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_MEP_STATUS_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.clear = clear,
+		.opcode_unexp_seen = false,
+		.version_unexp_seen = false,
+		.rx_level_low_seen = false,
+	};
+	int err;
+
+	err = switchdev_port_obj_get(br->dev, &cfm_obj.obj, extack);
+	if (err)
+		return err;
+
+	status->opcode_unexp_seen = cfm_obj.opcode_unexp_seen;
+	status->version_unexp_seen = cfm_obj.version_unexp_seen;
+	status->rx_level_low_seen = cfm_obj.rx_level_low_seen;
+
+	return 0;
+}
+
+int br_cfm_switchdev_cc_peer_status_get(struct net_bridge *br,
+					struct br_cfm_mep *mep,
+					u32 peer_mep_id,
+					bool clear,
+					struct br_cfm_cc_peer_status *const status,
+					struct netlink_ext_ack *extack)
+{
+	struct switchdev_obj_cfm_cc_peer_status_get cfm_obj = {
+		.obj.orig_dev = br->dev,
+		.obj.id = SWITCHDEV_OBJ_ID_PEER_MEP_STATUS_CFM,
+		.obj.flags = 0,
+		.instance = mep->instance,
+		.clear = clear,
+		.port_tlv_value = 0,
+		.if_tlv_value = 0,
+		.ccm_defect = false,
+		.rdi = false,
+		.seen = false,
+		.tlv_seen = false,
+		.seq_unexp_seen = false,
+	};
+	int err;
+
+	err = switchdev_port_obj_get(br->dev, &cfm_obj.obj, extack);
+	if (err)
+		return err;
+
+	status->port_tlv_value = cfm_obj.port_tlv_value;
+	status->if_tlv_value = cfm_obj.if_tlv_value;
+	status->ccm_defect = cfm_obj.ccm_defect;
+	status->rdi = cfm_obj.rdi;
+	status->seen = cfm_obj.seen;
+	status->tlv_seen = cfm_obj.tlv_seen;
+	status->seq_unexp_seen = cfm_obj.seq_unexp_seen;
+
+	return 0;
+}
diff --git a/net/bridge/br_private_cfm.h b/net/bridge/br_private_cfm.h
index 6a2294c0ea79..a91d0b59c27f 100644
--- a/net/bridge/br_private_cfm.h
+++ b/net/bridge/br_private_cfm.h
@@ -6,6 +6,7 @@
 #include "br_private.h"
 #include <uapi/linux/cfm_bridge.h>
 
+/* br_cfm.c */
 struct br_cfm_mep_create {
 	enum br_cfm_domain domain; /* Domain for this MEP */
 	enum br_cfm_mep_direction direction; /* Up or Down MEP direction */
@@ -55,13 +56,13 @@ int br_cfm_cc_config_set(struct net_bridge *br,
 int br_cfm_cc_peer_mep_add(struct net_bridge *br, const u32 instance,
 			   u32 peer_mep_id,
 			   struct netlink_ext_ack *extack);
+
 int br_cfm_cc_peer_mep_remove(struct net_bridge *br, const u32 instance,
 			      u32 peer_mep_id,
 			      struct netlink_ext_ack *extack);
 
 /* Transmitted CCM Remote Defect Indication status set.
  * This RDI is inserted in transmitted CCM PDUs if CCM transmission is enabled.
- * See br_cfm_cc_ccm_tx() with interval != BR_CFM_CCM_INTERVAL_NONE
  */
 int br_cfm_cc_rdi_set(struct net_bridge *br, const u32 instance,
 		      const bool rdi, struct netlink_ext_ack *extack);
@@ -96,6 +97,10 @@ struct br_cfm_mep_status {
 	bool rx_level_low_seen; /* Rx of OAM PDU with level low */
 };
 
+int br_cfm_mep_status_get(struct net_bridge *br, const u32 instance,
+			  bool clear, struct br_cfm_mep_status *const status,
+			  struct netlink_ext_ack *extack);
+
 struct br_cfm_cc_peer_status {
 	/* This CCM related status is based on the latest received CCM PDU. */
 	u8 port_tlv_value; /* Port Status TLV value */
@@ -114,6 +119,11 @@ struct br_cfm_cc_peer_status {
 	bool seq_unexp_seen;
 };
 
+int br_cfm_cc_peer_status_get(struct net_bridge *br, const u32 instance,
+			      u32 mepid, bool clear,
+			      struct br_cfm_cc_peer_status *const status,
+			      struct netlink_ext_ack *extack);
+
 struct br_cfm_mep {
 	/* list header of MEP instances */
 	struct hlist_node		head;
@@ -131,6 +141,7 @@ struct br_cfm_mep {
 	u32				ccm_rx_snumber;
 	struct br_cfm_mep_status	status;
 	bool				rdi;
+	bool				ccm_tx_swd;
 	struct rcu_head			rcu;
 };
 
@@ -144,4 +155,54 @@ struct br_cfm_peer_mep {
 	struct rcu_head			rcu;
 };
 
+/* br_cfm_switchdev.c */
+int br_cfm_switchdev_mep_create(struct net_bridge *br,
+				const u32 instance,
+				struct br_cfm_mep_create *const create,
+				struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_mep_delete(struct net_bridge *br,
+				struct br_cfm_mep *mep,
+				struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_mep_config_set(struct net_bridge *br,
+				    struct br_cfm_mep *mep,
+				    const struct br_cfm_mep_config *const config,
+				    struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_config_set(struct net_bridge *br,
+				   struct br_cfm_mep *mep,
+				   const struct br_cfm_cc_config *const config,
+				   struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_peer_mep_add(struct net_bridge *br,
+				     struct br_cfm_mep *mep,
+				     u32 peer_mep_id,
+				     struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_peer_mep_remove(struct net_bridge *br,
+					struct br_cfm_mep *mep,
+					u32 peer_mep_id,
+					struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_ccm_tx(struct net_bridge *br,
+			       struct br_cfm_mep *mep,
+			       struct sk_buff *skb,
+			       u32 period,
+			       enum br_cfm_ccm_interval interval,
+			       struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_mep_status_get(struct net_bridge *br,
+				    struct br_cfm_mep *mep,
+				    bool   clear,
+				    struct br_cfm_mep_status *const status,
+				    struct netlink_ext_ack *extack);
+
+int br_cfm_switchdev_cc_peer_status_get(struct net_bridge *br,
+					struct br_cfm_mep *mep,
+					u32 peer_mep_id,
+					bool clear,
+					struct br_cfm_cc_peer_status *const status,
+					struct netlink_ext_ack *extack);
+
 #endif /* _BR_PRIVATE_CFM_H_ */
-- 
2.28.0


  parent reply	other threads:[~2020-10-01 10:33 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-10-01 10:30 [net-next v2 00/11] net: bridge: cfm: Add support for Connectivity Fault Management(CFM) Henrik Bjoernlund
2020-10-01 10:30 ` [Bridge] " Henrik Bjoernlund
2020-10-01 10:30 ` [net-next v2 01/11] net: bridge: extend the process of special frames Henrik Bjoernlund
2020-10-01 10:30   ` [Bridge] " Henrik Bjoernlund
2020-10-01 16:31   ` kernel test robot
2020-10-01 16:31     ` kernel test robot
2020-10-01 18:02   ` kernel test robot
2020-10-01 18:02     ` kernel test robot
2020-10-01 18:36   ` kernel test robot
2020-10-01 18:36     ` kernel test robot
2020-10-06 14:13   ` Nikolay Aleksandrov
2020-10-06 14:13     ` [Bridge] " Nikolay Aleksandrov
2020-10-01 10:30 ` [net-next v2 02/11] bridge: cfm: Add BRIDGE_CFM to Kconfig Henrik Bjoernlund
2020-10-01 10:30   ` [Bridge] " Henrik Bjoernlund
2020-10-01 10:30 ` [net-next v2 03/11] bridge: uapi: cfm: Added EtherType used by the CFM protocol Henrik Bjoernlund
2020-10-01 10:30   ` [Bridge] " Henrik Bjoernlund
2020-10-01 10:30 ` [net-next v2 04/11] bridge: cfm: Kernel space implementation of CFM Henrik Bjoernlund
2020-10-01 10:30   ` [Bridge] " Henrik Bjoernlund
2020-10-06 14:29   ` Nikolay Aleksandrov
2020-10-06 14:29     ` [Bridge] " Nikolay Aleksandrov
2020-10-01 10:30 ` [net-next v2 05/11] " Henrik Bjoernlund
2020-10-01 10:30   ` [Bridge] " Henrik Bjoernlund
2020-10-01 10:30 ` [net-next v2 06/11] " Henrik Bjoernlund
2020-10-01 10:30   ` [Bridge] " Henrik Bjoernlund
2020-10-01 10:30 ` [net-next v2 07/11] bridge: cfm: Netlink Interface Henrik Bjoernlund
2020-10-01 10:30   ` [Bridge] " Henrik Bjoernlund
2020-10-06 14:44   ` Nikolay Aleksandrov
2020-10-06 14:44     ` [Bridge] " Nikolay Aleksandrov
2020-10-01 10:30 ` [net-next v2 08/11] bridge: cfm: Netlink Notifications Henrik Bjoernlund
2020-10-01 10:30   ` [Bridge] " Henrik Bjoernlund
2020-10-06 14:49   ` Nikolay Aleksandrov
2020-10-06 14:49     ` [Bridge] " Nikolay Aleksandrov
2020-10-01 10:30 ` [net-next v2 09/11] bridge: cfm: Bridge port remove Henrik Bjoernlund
2020-10-01 10:30   ` [Bridge] " Henrik Bjoernlund
2020-10-06 14:53   ` Nikolay Aleksandrov
2020-10-06 14:53     ` [Bridge] " Nikolay Aleksandrov
2020-10-01 10:30 ` [net-next v2 10/11] bridge: switchdev: cfm: switchdev interface implementation Henrik Bjoernlund
2020-10-01 10:30   ` [Bridge] " Henrik Bjoernlund
2020-10-01 12:49   ` Jiri Pirko
2020-10-05 13:07     ` Allan W. Nielsen
2020-10-05 13:07       ` [Bridge] " Allan W. Nielsen
2020-10-06  9:02       ` Jiri Pirko
2020-10-06 10:50       ` Nikolay Aleksandrov
2020-10-06 10:50         ` [Bridge] " Nikolay Aleksandrov
2020-10-06 10:53         ` allan.nielsen
2020-10-06 10:53           ` [Bridge] " allan.nielsen
2020-10-01 15:20   ` kernel test robot
2020-10-01 15:20     ` kernel test robot
2020-10-01 15:38   ` kernel test robot
2020-10-01 15:38     ` kernel test robot
2020-10-01 10:30 ` Henrik Bjoernlund [this message]
2020-10-01 10:30   ` [Bridge] [net-next v2 11/11] bridge: cfm: Added CFM switchdev utilization Henrik Bjoernlund

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201001103019.1342470-12-henrik.bjoernlund@microchip.com \
    --to=henrik.bjoernlund@microchip.com \
    --cc=UNGLinuxDriver@microchip.com \
    --cc=bridge@lists.linux-foundation.org \
    --cc=davem@davemloft.net \
    --cc=horatiu.vultur@microchip.com \
    --cc=idosch@mellanox.com \
    --cc=jiri@mellanox.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=nikolay@nvidia.com \
    --cc=roopa@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.