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.vul...@microchip.com>
Signed-off-by: Henrik Bjoernlund  <henrik.bjoernl...@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

Reply via email to