[qca-nss-clients] Added support for client map manager
Change-Id: I7bd708a836b2632b9d8d6576a666d0d5608c5adb
Signed-off-by: Suman Ghosh <sumaghos@codeaurora.org>
Signed-off-by: Suruchi Suman <surusuma@codeaurora.org>
diff --git a/clmapmgr/Makefile b/clmapmgr/Makefile
new file mode 100644
index 0000000..1131778
--- /dev/null
+++ b/clmapmgr/Makefile
@@ -0,0 +1,5 @@
+ccflags-y := -I$(obj)/../exports -I$(obj)/.. \
+ -DNSS_CLIENT_BUILD_ID="$(BUILD_ID)" -DNSS_CLMAPMGR_DEBUG_LEVEL=2 -Wall -Werror
+
+obj-m += qca-nss-clmapmgr.o
+qca-nss-clmapmgr-objs := nss_clmapmgr.o
diff --git a/clmapmgr/nss_clmapmgr.c b/clmapmgr/nss_clmapmgr.c
new file mode 100644
index 0000000..a8687b2
--- /dev/null
+++ b/clmapmgr/nss_clmapmgr.c
@@ -0,0 +1,924 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/*
+ * nss_clmapmgr.c
+ * This file implements client for CLient map manager.
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+
+#include <nss_api_if.h>
+#include <nss_dynamic_interface.h>
+
+#include "nss_clmapmgr_private.h"
+#include "nss_clmapmgr.h"
+#include "nss_eogremgr.h"
+
+#define NSS_CLMAPMGR_CMD_MAX_RETRY_COUNT 3
+#define NSS_CLMAP_MAX_HEADROOM NSS_EOGREMGR_MAX_HEADROOM
+
+/*
+ * nss_clmapmgr_dev_xmit()
+ * Netdev ops function to send packet to NSS.
+ */
+static netdev_tx_t nss_clmapmgr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ nss_tx_status_t status;
+ int if_number;
+ struct nss_ctx_instance *clmap_ctx;
+ struct nss_clmapmgr_priv_t *priv = netdev_priv(dev);
+
+ if_number = priv->nss_if_number_us;
+ if (unlikely(if_number <= 0)) {
+ nss_clmapmgr_info("%p: clmapmgr dev is not registered with nss\n", dev);
+ goto fail;
+ }
+
+ clmap_ctx = nss_clmap_get_ctx();
+ if (unlikely(!clmap_ctx)) {
+ nss_clmapmgr_info("%p: NSS clmapmgr context not found.\n", dev);
+ goto fail;
+ }
+
+ status = nss_clmap_tx_buf(clmap_ctx, skb, (uint32_t)if_number);
+ if (unlikely(status != NSS_TX_SUCCESS)) {
+ nss_clmapmgr_info("%p: NSS clmapmgr could not send packet to NSS %d\n", dev, if_number);
+ goto fail;
+ }
+
+ return NETDEV_TX_OK;
+
+fail:
+ dev->stats.tx_dropped++;
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+}
+
+/*
+ * nss_clmapmgr_dev_stats64()
+ * Netdev ops function to retrieve stats.
+ */
+struct rtnl_link_stats64 *nss_clmapmgr_dev_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct nss_clmapmgr_priv_t *priv;
+
+ if (!stats) {
+ nss_clmapmgr_warning("%p: invalid rtnl structure\n", dev);
+ return stats;
+ }
+
+ dev_hold(dev);
+
+ /*
+ * Netdev seems to be incrementing rx_dropped because we don't give IP header.
+ * So reset it as it's of no use for us.
+ */
+ atomic_long_set(&dev->rx_dropped, 0);
+ priv = netdev_priv(dev);
+ memset(stats, 0, sizeof(struct rtnl_link_stats64));
+ memcpy(stats, &priv->stats, sizeof(struct rtnl_link_stats64));
+ dev_put(dev);
+
+ return stats;
+}
+
+/*
+ * nss_clmapmgr_dev_init()
+ * Netdev ops function to intialize netdevice.
+ */
+static int nss_clmapmgr_dev_init(struct net_device *dev)
+{
+ dev->mtu = ETH_DATA_LEN;
+ dev->needed_headroom = NSS_CLMAP_MAX_HEADROOM;
+ return 0;
+}
+
+/*
+ * nss_clmapmgr_dev_open()
+ * Netdev ops function to open netdevice.
+ */
+static int nss_clmapmgr_dev_open(struct net_device *dev)
+{
+ netif_start_queue(dev);
+ return 0;
+}
+
+/*
+ * nss_clmapmgr_dev_close()
+ * Netdevice ops function to close netdevice.
+ */
+static int nss_clmapmgr_dev_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ return 0;
+}
+
+/*
+ * clmap netdevice ops
+ */
+static const struct net_device_ops nss_clmapmgr_ops = {
+ .ndo_init = nss_clmapmgr_dev_init,
+ .ndo_open = nss_clmapmgr_dev_open,
+ .ndo_stop = nss_clmapmgr_dev_close,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_start_xmit = nss_clmapmgr_dev_xmit,
+ .ndo_get_stats64 = nss_clmapmgr_dev_stats64,
+};
+
+/*
+ * nss_clmapmgr_setup()
+ */
+static void nss_clmapmgr_setup(struct net_device *dev)
+{
+ char name[IFNAMSIZ] = {0};
+
+ strlcpy(name, "nssclmap%d", IFNAMSIZ);
+ memcpy(dev->name, name, IFNAMSIZ);
+ dev->netdev_ops = &nss_clmapmgr_ops;
+ eth_hw_addr_random(dev);
+}
+
+/*
+ * nss_clmapmgr_ds_exception()
+ * Client map manager ds exception handler to receive packet from NSS.
+ */
+static void nss_clmapmgr_ds_exception(struct net_device *dev, struct sk_buff *skb,
+ __attribute__((unused)) struct napi_struct *napi)
+{
+ /*
+ * Note: preheader needs to be processed by the user
+ * before processing the ethernet packet
+ */
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_receive_skb(skb);
+}
+
+/*
+ * nss_clmapmgr_us_exception()
+ * Client map manager us exception handler to receive packet from NSS.
+ */
+static void nss_clmapmgr_us_exception(struct net_device *dev, struct sk_buff *skb,
+ __attribute__((unused)) struct napi_struct *napi)
+{
+ /*
+ * This is an error packet and needs to be dropped.
+ */
+ nss_clmapmgr_warning("%p: upstream packet got exceptioned, dropping the packet..", dev);
+ dev_kfree_skb_any(skb);
+}
+
+/*
+ * nss_clmapmgr_event_receive()
+ * Event Callback to receive events from NSS
+ */
+static void nss_clmapmgr_event_receive(void *if_ctx, struct nss_cmn_msg *cmsg)
+{
+ struct net_device *dev = (struct net_device *)if_ctx;
+ struct nss_clmapmgr_priv_t *priv;
+ struct nss_clmap_msg *clmsg = (struct nss_clmap_msg *)cmsg;
+ struct nss_clmap_stats_msg *stats = &clmsg->msg.stats;
+ struct rtnl_link_stats64 *netdev_stats;
+ enum nss_dynamic_interface_type interface_type;
+ uint64_t dropped = 0;
+
+ dev_hold(dev);
+ priv = netdev_priv(dev);
+ netdev_stats = &priv->stats;
+
+ switch (clmsg->cm.type) {
+ case NSS_CLMAP_MSG_TYPE_SYNC_STATS:
+ interface_type = nss_dynamic_interface_get_type(nss_clmap_get_ctx(), clmsg->cm.interface);
+ if (interface_type == NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US) {
+ netdev_stats->tx_packets += stats->node_stats.tx_packets;
+ netdev_stats->tx_bytes += stats->node_stats.tx_bytes;
+ } else if (interface_type == NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS) {
+ netdev_stats->rx_packets += stats->node_stats.rx_packets;
+ netdev_stats->rx_bytes += stats->node_stats.rx_bytes;
+ }
+ dropped += stats->dropped_macdb_lookup_failed;
+ dropped += stats->dropped_invalid_packet_size;
+ dropped += stats->dropped_low_hroom;
+ dropped += stats->dropped_next_node_queue_full;
+ dropped += stats->dropped_pbuf_alloc_failed;
+ dropped += stats->dropped_linear_failed;
+ dropped += stats->shared_packet_count;
+ dropped += stats->ethernet_frame_error;
+ netdev_stats->tx_dropped += dropped;
+ if (interface_type == NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS) {
+ netdev_stats->rx_dropped += nss_cmn_rx_dropped_sum(&stats->node_stats);
+ }
+ break;
+
+ default:
+ nss_clmapmgr_info("%p: Unknown Event from NSS\n", dev);
+ break;
+ }
+
+ dev_put(dev);
+}
+
+/*
+ * nss_clmapmgr_us_get_if_num
+ * return us NSS interface number
+ */
+int nss_clmapmgr_us_get_if_num(struct net_device *dev)
+{
+ struct nss_clmapmgr_priv_t *priv;
+
+ if (!dev) {
+ nss_clmapmgr_info("Netdev is NULL");
+ return -1;
+ }
+
+ priv = (struct nss_clmapmgr_priv_t *)netdev_priv(dev);
+ return priv->nss_if_number_us;
+}
+EXPORT_SYMBOL(nss_clmapmgr_us_get_if_num);
+
+/*
+ * nss_clmapmgr_ds_get_if_num
+ * return ds NSS interface number
+ */
+int nss_clmapmgr_ds_get_if_num(struct net_device *dev)
+{
+ struct nss_clmapmgr_priv_t *priv;
+
+ if (!dev) {
+ nss_clmapmgr_info("Netdev is NULL");
+ return -1;
+ }
+
+ priv = (struct nss_clmapmgr_priv_t *)netdev_priv(dev);
+ return priv->nss_if_number_ds;
+}
+EXPORT_SYMBOL(nss_clmapmgr_ds_get_if_num);
+
+/*
+ * nss_clmapmgr_mac_add()
+ * API to send notification to NSS to add the MAC entry.
+ */
+nss_clmapmgr_status_t nss_clmapmgr_mac_add(struct net_device *dev, struct nss_clmapmgr_msg *clmapmsg)
+{
+ struct nss_clmap_msg req;
+ struct nss_clmap_mac_msg *mmsg;
+ int us_if, next_ifnum;
+ struct nss_ctx_instance *nss_ctx = NULL;
+ nss_tx_status_t status;
+
+ if (!dev) {
+ nss_clmapmgr_info("Netdev is NULL !!\n");
+ return NSS_CLMAPMGR_ERR_BAD_PARAM;
+ }
+
+ if (!clmapmsg) {
+ nss_clmapmgr_info("%p: nss_clmapmgr_msg is NULL !!\n", dev);
+ return NSS_CLMAPMGR_ERR_BAD_PARAM;
+ }
+
+ /*
+ * Get Interface number, based on tunnel type
+ */
+ switch (clmapmsg->tunnel_type) {
+ case NSS_CLMAPMGR_TUNNEL_EOGRE:
+ /*
+ * For EoGRE tunnel, the next node for the packet from clmap node in NSS
+ * would be GRE inner node. Get the GRE inner interface.
+ */
+ next_ifnum = nss_eogremgr_get_if_num_inner(clmapmsg->tunnel_id);
+ if (next_ifnum < 0) {
+ nss_clmapmgr_info("%p: No NSS interface registered for the tunnel id: %d\n", dev, clmapmsg->tunnel_id);
+ return NSS_CLMAPMGR_ERR_TUNNEL_NOT_FOUND;
+ }
+ break;
+ default:
+ nss_clmapmgr_info("%p: Invalid tunnel type: %d\n", dev, clmapmsg->tunnel_type);
+ return NSS_CLMAPMGR_ERR_BAD_PARAM;
+ }
+
+ nss_ctx = nss_clmap_get_ctx();
+
+ /*
+ * Check if upstream clmap interface is registered with NSS.
+ */
+ us_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
+ if (us_if < 0) {
+ nss_clmapmgr_info("%p: Net device is not registered with nss\n", dev);
+ dev_put(dev);
+ return NSS_CLMAPMGR_ERR_NETDEV_UNKNOWN;
+ }
+
+ memset(&req, 0, sizeof(struct nss_clmap_msg));
+ mmsg = &req.msg.mac_add;
+
+ /*
+ * Set mac_add message.
+ */
+ memcpy(mmsg->mac_addr, clmapmsg->mac_addr, ETH_ALEN);
+ mmsg->flags = clmapmsg->flags;
+ mmsg->vlan_id = clmapmsg->vlan_id;
+ mmsg->needed_headroom = clmapmsg->needed_headroom;
+ mmsg->nexthop_ifnum = next_ifnum;
+ nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_MAC_ADD, sizeof(struct nss_clmap_mac_msg), NULL, NULL);
+ status = nss_clmap_tx_msg_sync(nss_ctx, &req);
+ if (status != NSS_TX_SUCCESS) {
+ nss_clmapmgr_warning("%p: nss clmap mac add command error:%d if_num: %d\n", dev, status, us_if);
+ dev_put(dev);
+ return NSS_CLMAPMGR_ERR_MAC_ADD_FAILED;
+ }
+
+ return NSS_CLMAPMGR_SUCCESS;
+}
+EXPORT_SYMBOL(nss_clmapmgr_mac_add);
+
+/*
+ * nss_clmapmgr_mac_remove()
+ * API to send notification to NSS to delete the MAC entry.
+ */
+nss_clmapmgr_status_t nss_clmapmgr_mac_remove(struct net_device *dev, uint8_t *mac_addr)
+{
+ struct nss_clmap_msg req;
+ struct nss_clmap_mac_msg *mmsg;
+ int us_if;
+ struct nss_ctx_instance *nss_ctx = NULL;
+ nss_tx_status_t status;
+
+ if (!dev) {
+ nss_clmapmgr_info("Netdev is NULL !!\n");
+ return NSS_CLMAPMGR_ERR_BAD_PARAM;
+ }
+
+ if (!mac_addr) {
+ nss_clmapmgr_info("%p: mac address is NULL !!\n", dev);
+ return NSS_CLMAPMGR_ERR_BAD_PARAM;
+ }
+
+ nss_ctx = nss_clmap_get_ctx();
+
+ /*
+ * Check if upstream clmap interface is registered with NSS
+ */
+ us_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
+ if (us_if < 0) {
+ nss_clmapmgr_info("%p: Net device is not registered with nss\n", dev);
+ dev_put(dev);
+ return NSS_CLMAPMGR_ERR_NETDEV_UNKNOWN;
+ }
+
+ memset(&req, 0, sizeof(struct nss_clmap_msg));
+ mmsg = &req.msg.mac_add;
+
+ /*
+ * Set mac_del message. Only MAC address is required, the other
+ * fields as set to 0.
+ *
+ */
+ memcpy(mmsg->mac_addr, mac_addr, ETH_ALEN);
+ nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_MAC_DEL, sizeof(struct nss_clmap_mac_msg), NULL, NULL);
+ status = nss_clmap_tx_msg_sync(nss_ctx, &req);
+ if (status != NSS_TX_SUCCESS) {
+ nss_clmapmgr_warning("%p: NSS clmap mac del command error:%d if_num: %d\n", dev, status, us_if);
+ dev_put(dev);
+ return NSS_CLMAPMGR_ERR_MAC_DEL_FAILED;
+ }
+
+ return NSS_CLMAPMGR_SUCCESS;
+}
+EXPORT_SYMBOL(nss_clmapmgr_mac_remove);
+
+/*
+ * nss_clmapmgr_mac_flush()
+ * API to send notification to NSS to flush MAC entry.
+ */
+nss_clmapmgr_status_t nss_clmapmgr_mac_flush(struct net_device *dev, uint32_t tunnel_id, nss_clmapmgr_tunnel_type_t tunnel_type)
+{
+ struct nss_clmap_msg req;
+ struct nss_clmap_flush_mac_msg *mmsg = &req.msg.mac_flush;
+ int us_if, next_ifnum;
+ struct nss_ctx_instance *nss_ctx = NULL;
+ nss_tx_status_t status;
+
+ if (!dev) {
+ nss_clmapmgr_info("Netdev is NULL !!\n");
+ return NSS_CLMAPMGR_ERR_BAD_PARAM;
+ }
+
+ switch (tunnel_type) {
+ case NSS_CLMAPMGR_TUNNEL_EOGRE:
+ /*
+ * Get GRE inner interface number
+ */
+ next_ifnum = nss_eogremgr_get_if_num_inner(tunnel_id);
+ if (next_ifnum < 0) {
+ nss_clmapmgr_info("%p: No NSS interface registered for the tunnel id: %d\n", dev, tunnel_id);
+ return NSS_CLMAPMGR_ERR_TUNNEL_NOT_FOUND;
+ }
+ break;
+ default:
+ nss_clmapmgr_info("%p: Invalid tunnel type: %d\n", dev, tunnel_type);
+ return NSS_CLMAPMGR_ERR_BAD_PARAM;
+ }
+
+ nss_ctx = nss_clmap_get_ctx();
+
+ /*
+ * Check if upstream clmap interface is registered with NSS
+ */
+ us_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
+ if (us_if < 0) {
+ nss_clmapmgr_info("%p: Net device is not registered with nss\n", dev);
+ dev_put(dev);
+ return NSS_CLMAPMGR_ERR_NETDEV_UNKNOWN;
+ }
+
+ /*
+ * Set mac_flush message
+ */
+ mmsg->nexthop_ifnum = next_ifnum;
+ nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_MAC_FLUSH, sizeof(struct nss_clmap_mac_msg), NULL, NULL);
+ status = nss_clmap_tx_msg_sync(nss_ctx, &req);
+ if (status != NSS_TX_SUCCESS) {
+ nss_clmapmgr_warning("%p: NSS clmap mac flush command error:%d if_num: %d\n", dev, status, us_if);
+ dev_put(dev);
+ return NSS_CLMAPMGR_ERR_MAC_FLUSH_FAILED;
+ }
+
+ return NSS_CLMAPMGR_SUCCESS;
+}
+EXPORT_SYMBOL(nss_clmapmgr_mac_flush);
+
+/*
+ * nss_clmapmgr_netdev_enable()
+ * Call back to enable NSS for clmap device.
+ */
+int nss_clmapmgr_netdev_enable(struct net_device *dev)
+{
+ struct nss_clmap_msg req;
+ int us_if, ds_if;
+ struct nss_ctx_instance *nss_ctx = NULL;
+ nss_tx_status_t status;
+
+ if (!dev) {
+ nss_clmapmgr_info("Netdev is NULL !!\n");
+ return NOTIFY_DONE;
+ }
+
+ nss_ctx = nss_clmap_get_ctx();
+
+ /*
+ * Check if upstream clmap interface is registered with NSS
+ */
+ us_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
+ if (us_if < 0) {
+ nss_clmapmgr_info("%p: Net device is not registered with nss\n", dev);
+ goto release_ref;
+ }
+
+ /*
+ * Check if downstream clmap interface is registered with NSS
+ */
+ ds_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS);
+ if (ds_if < 0) {
+ nss_clmapmgr_info("%p: Net device is not registered with nss\n", dev);
+ goto release_ref;
+ }
+
+ /*
+ * Send enable session command for upstream interface
+ */
+ nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_INTERFACE_ENABLE, 0, NULL, NULL);
+ status = nss_clmap_tx_msg_sync(nss_ctx, &req);
+ if (status != NSS_TX_SUCCESS) {
+ nss_clmapmgr_warning("%p: NSS clmap enable command error:%d if_num: %d\n", dev, status, us_if);
+ goto release_ref;
+ }
+
+ /*
+ * Send enable session command for downstream interface
+ */
+ nss_clmap_msg_init(&req, ds_if, NSS_CLMAP_MSG_TYPE_INTERFACE_ENABLE, 0, NULL, NULL);
+ status = nss_clmap_tx_msg_sync(nss_ctx, &req);
+ if (status != NSS_TX_SUCCESS) {
+ nss_clmapmgr_warning("%p: NSS clmap enable command error:%d if_num: %d\n", dev, status, ds_if);
+ goto disable_us;
+ }
+
+ /*
+ * Open the netdev to accept packets
+ */
+ nss_clmapmgr_dev_open(dev);
+
+ return NOTIFY_OK;
+
+disable_us:
+ nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_INTERFACE_DISABLE, 0, NULL, NULL);
+ status = nss_clmap_tx_msg_sync(nss_ctx, &req);
+ if (status != NSS_TX_SUCCESS) {
+ nss_clmapmgr_warning("%p: NSS clmap enable command error:%d if_num: %d\n", dev, status, us_if);
+ }
+
+release_ref:
+ return NOTIFY_DONE;
+}
+EXPORT_SYMBOL(nss_clmapmgr_netdev_enable);
+
+/*
+ * nss_clmapmgr_netdev_disable()
+ * Call back to disable clmap interface in NSS.
+ */
+int nss_clmapmgr_netdev_disable(struct net_device *dev)
+{
+ struct nss_clmap_msg req;
+ int us_if, ds_if;
+ struct nss_ctx_instance *nss_ctx = NULL;
+ nss_tx_status_t status;
+
+ if (!dev) {
+ nss_clmapmgr_info("Netdev is NULL !!\n");
+ return NOTIFY_DONE;
+ }
+
+ nss_ctx = nss_clmap_get_ctx();
+
+ /*
+ * Check if upstream clmap interface is registered with NSS
+ */
+ us_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
+ if (us_if < 0) {
+ nss_clmapmgr_info("%p: Net device is not registered with NSS\n", dev);
+ goto release_ref;
+ }
+
+ /*
+ * Check if downstream clmap interface is registered with NSS
+ */
+ ds_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS);
+ if (ds_if < 0) {
+ nss_clmapmgr_info("%p: Net device is not registered with NSS\n", dev);
+ goto release_ref;
+ }
+
+ /*
+ * Send disable session command for upstream interface
+ */
+ nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_INTERFACE_DISABLE, 0, NULL, NULL);
+ status = nss_clmap_tx_msg_sync(nss_ctx, &req);
+ if (status != NSS_TX_SUCCESS) {
+ nss_clmapmgr_warning("%p: NSS clmap disable command error:%d if_num: %d\n", dev, status, us_if);
+ goto release_ref;
+ }
+
+ /*
+ * Send disable session command for downstream interface
+ */
+ nss_clmap_msg_init(&req, ds_if, NSS_CLMAP_MSG_TYPE_INTERFACE_DISABLE, 0, NULL, NULL);
+ status = nss_clmap_tx_msg_sync(nss_ctx, &req);
+ if (status != NSS_TX_SUCCESS) {
+ nss_clmapmgr_warning("%p: NSS clmap disable command error:%d if_num: %d\n", dev, status, ds_if);
+ goto enable_us;
+ }
+
+ /*
+ * Close the netdev
+ */
+ nss_clmapmgr_dev_close(dev);
+
+ return NOTIFY_OK;
+
+enable_us:
+ nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_INTERFACE_ENABLE, 0, NULL, NULL);
+ status = nss_clmap_tx_msg_sync(nss_ctx, &req);
+ if (status != NSS_TX_SUCCESS) {
+ nss_clmapmgr_warning("%p: NSS clmap disable command error:%d if_num: %d\n", dev, status, us_if);
+ }
+
+release_ref:
+ return NOTIFY_DONE;
+}
+EXPORT_SYMBOL(nss_clmapmgr_netdev_disable);
+
+/*
+ * nss_clmapmgr_dev_event()
+ * Netdevice notifier call back function.
+ */
+static int nss_clmapmgr_dev_event(struct notifier_block *nb,
+ unsigned long event, void *dev)
+{
+ struct net_device *netdev;
+ netdev = netdev_notifier_info_to_dev(dev);
+
+ switch (event) {
+ case NETDEV_UP:
+ return nss_clmapmgr_netdev_enable(netdev);
+
+ case NETDEV_DOWN:
+ return nss_clmapmgr_netdev_disable(netdev);
+
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * nss_clmapmgr_destroy_us_interface()
+ * Destroy upstream clmap interface.
+ */
+static nss_clmapmgr_status_t nss_clmapmgr_destroy_us_interface(struct net_device *dev, int interface_num)
+{
+ nss_tx_status_t status;
+ int retry = 0;
+
+ if (!nss_clmap_unregister(interface_num)) {
+ nss_clmapmgr_warning("%p: clmap NSS upstream interface unregister failed\n.", dev);
+ return NSS_CLMAPMGR_ERR_NSSIF_UNREGISTER_FAILED;
+ }
+
+dealloc_us:
+ status = nss_dynamic_interface_dealloc_node(interface_num, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
+ if (status != NSS_TX_SUCCESS) {
+ nss_clmapmgr_info("%p: clmap dealloc node failure for interface_num = %d\n", dev, interface_num);
+ if (++retry <= NSS_CLMAPMGR_CMD_MAX_RETRY_COUNT) {
+ goto dealloc_us;
+ }
+
+ nss_clmapmgr_error("%p: fatal Error, failed to dealloc upstream clmap NSS interface.\n", dev);
+ return NSS_CLMAPMGR_ERR_NSSIF_DEALLOC_FAILED;
+ }
+
+ return NSS_CLMAPMGR_SUCCESS;
+}
+
+/*
+ * nss_clmapmgr_destroy_ds_interface()
+ * Destroy downstream clmap interface.
+ */
+static nss_clmapmgr_status_t nss_clmapmgr_destroy_ds_interface(struct net_device *dev, int interface_num)
+{
+ nss_tx_status_t status;
+ int retry = 0;
+
+ if (!nss_clmap_unregister(interface_num)) {
+ nss_clmapmgr_warning("%p: clmap NSS downstream interface unregister failed\n.", dev);
+ return NSS_CLMAPMGR_ERR_NSSIF_UNREGISTER_FAILED;
+ }
+
+dealloc_ds:
+ status = nss_dynamic_interface_dealloc_node(interface_num, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS);
+ if (status != NSS_TX_SUCCESS) {
+ nss_clmapmgr_info("%p: clmap dealloc node failure for ds_if = %d\n", dev, interface_num);
+ if (++retry <= NSS_CLMAPMGR_CMD_MAX_RETRY_COUNT) {
+ goto dealloc_ds;
+ }
+
+ nss_clmapmgr_error("%p: fatal Error, failed to dealloc downstream clmap NSS interface.\n", dev);
+ return NSS_CLMAPMGR_ERR_NSSIF_DEALLOC_FAILED;
+ }
+
+ return NSS_CLMAPMGR_SUCCESS;
+}
+
+/*
+ * nss_clmapmgr_netdev_destroy()
+ * API for destroying a netdevice.
+ * Note: User needs to flush all MAC entries in the clmap before destroying the clmap netdevice
+ */
+nss_clmapmgr_status_t nss_clmapmgr_netdev_destroy(struct net_device *dev)
+{
+ int us_if, ds_if;
+ nss_clmapmgr_status_t ret;
+
+ netif_tx_disable(dev);
+
+ /*
+ * Check if upstream clmap interface is registered with NSS
+ */
+ us_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
+ if (us_if < 0) {
+ nss_clmapmgr_info("%p: Net device is not registered with NSS\n", dev);
+ return NSS_CLMAPMGR_ERR_NETDEV_UNKNOWN;
+ }
+
+ /*
+ * Check if downstream clmap interface is registered with NSS
+ */
+ ds_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS);
+ if (ds_if < 0) {
+ nss_clmapmgr_info("%p: Net device is not registered with NSS\n", dev);
+ return NSS_CLMAPMGR_ERR_NETDEV_UNKNOWN;
+ }
+
+ ret = nss_clmapmgr_destroy_us_interface(dev, us_if);
+ if (ret != NSS_CLMAPMGR_SUCCESS) {
+ nss_clmapmgr_warning("%p: failed to destroy clmap upstream interface: %d\n", dev, us_if);
+ return ret;
+ }
+
+ ret = nss_clmapmgr_destroy_ds_interface(dev, ds_if);
+ if (ret != NSS_CLMAPMGR_SUCCESS) {
+ nss_clmapmgr_warning("%p: failed to destroy clmap downstream interface: %d\n", dev, ds_if);
+ return ret;
+ }
+
+ nss_clmapmgr_info("%p: deleted clmap instance, us_if = %d ds_if = %d\n",
+ dev, us_if, ds_if);
+
+ unregister_netdev(dev);
+ free_netdev(dev);
+ return NSS_CLMAPMGR_SUCCESS;
+}
+EXPORT_SYMBOL(nss_clmapmgr_netdev_destroy);
+
+/*
+ * nss_clmapmgr_netdev_create()
+ * User API to create clmap interface
+ */
+struct net_device *nss_clmapmgr_netdev_create(void)
+{
+ struct nss_ctx_instance *nss_ctx;
+ struct net_device *dev = NULL;
+ struct nss_clmapmgr_priv_t *priv;
+ nss_tx_status_t status;
+ uint32_t features = 0;
+ int32_t us_if, ds_if;
+ int ret = -1, retry = 0;
+
+ dev = alloc_etherdev(sizeof(struct nss_clmapmgr_priv_t));
+ if (!dev) {
+ nss_clmapmgr_warning("Allocation of netdev failed\n");
+ return NULL;
+ }
+
+ nss_clmapmgr_setup(dev);
+
+ /*
+ * Register net_device
+ */
+ ret = register_netdev(dev);
+ if (ret) {
+ nss_clmapmgr_warning("%p: Netdevice registration failed\n", dev);
+ free_netdev(dev);
+ return NULL;
+ }
+
+ /*
+ * Create NSS clmap downstream dynamic interface
+ */
+ ds_if = nss_dynamic_interface_alloc_node(NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS);
+ if (ds_if < 0) {
+ nss_clmapmgr_warning("%p: NSS dynamic interface alloc failed for clmap downstream\n", dev);
+ goto deregister_netdev;
+ }
+
+ /*
+ * Create NSS clmap upstream dynamic interface
+ */
+ us_if = nss_dynamic_interface_alloc_node(NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
+ if (us_if < 0) {
+ nss_clmapmgr_warning("%p: NSS dynamic interface alloc failed for clmap upstream\n", dev);
+ goto dealloc_ds_node;
+ }
+
+ priv = (struct nss_clmapmgr_priv_t *)netdev_priv(dev);
+ priv->nss_if_number_us = us_if;
+ priv->nss_if_number_ds = ds_if;
+
+ /*
+ * Register downstream clmap interface with NSS
+ */
+ nss_ctx = nss_clmap_register(ds_if,
+ NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS,
+ nss_clmapmgr_ds_exception,
+ nss_clmapmgr_event_receive,
+ dev,
+ features);
+ if (!nss_ctx) {
+ nss_clmapmgr_info("%p: nss_clmap_register failed for downstream interface\n", dev);
+ goto dealloc_us_node;
+ }
+
+ /*
+ * Register upstream clmap interface with NSS
+ */
+ nss_ctx = nss_clmap_register(us_if,
+ NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US,
+ nss_clmapmgr_us_exception,
+ nss_clmapmgr_event_receive,
+ dev,
+ features);
+ if (!nss_ctx) {
+ nss_clmapmgr_info("%p: nss_clmap_register failed for upstream interface\n", dev);
+ goto unregister_ds;
+ }
+
+ nss_clmapmgr_info("%p: nss_clmap_register() successful. nss_ctx = %p\n", dev, nss_ctx);
+
+ /*
+ * Success
+ */
+ return dev;
+
+unregister_ds:
+ nss_clmap_unregister(ds_if);
+
+dealloc_us_node:
+ status = nss_dynamic_interface_dealloc_node(us_if, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
+ if (status != NSS_TX_SUCCESS) {
+ if (++retry <= NSS_CLMAPMGR_CMD_MAX_RETRY_COUNT) {
+ goto dealloc_us_node;
+ }
+ nss_clmapmgr_error("%p: fatal Error, Unable to dealloc the node[%d] in the NSS FW!\n", dev, us_if);
+ }
+
+ retry = 0;
+dealloc_ds_node:
+ status = nss_dynamic_interface_dealloc_node(ds_if, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS);
+ if (status != NSS_TX_SUCCESS) {
+ if (++retry <= NSS_CLMAPMGR_CMD_MAX_RETRY_COUNT) {
+ goto dealloc_ds_node;
+ }
+ nss_clmapmgr_error("%p: fatal Error, Unable to dealloc the node[%d] in the NSS FW!\n", dev, ds_if);
+ }
+
+deregister_netdev:
+ unregister_netdev(dev);
+ free_netdev(dev);
+
+ return NULL;
+}
+EXPORT_SYMBOL(nss_clmapmgr_netdev_create);
+
+/*
+ * Linux Net device Notifier
+ */
+static struct notifier_block nss_clmapmgr_notifier = {
+ .notifier_call = nss_clmapmgr_dev_event,
+};
+
+/*
+ * nss_clmapmgr_dev_init_module()
+ * Client map module init function
+ */
+static int __init nss_clmapmgr_dev_init_module(void)
+{
+#ifdef CONFIG_OF
+ /*
+ * If the node is not compatible, don't do anything.
+ */
+ if (!of_find_node_by_name(NULL, "nss-common")) {
+ return 0;
+ }
+#endif
+ register_netdevice_notifier(&nss_clmapmgr_notifier);
+
+ return 0;
+}
+
+/*
+ * nss_clmapmgr_exit_module
+ * Client map module exit function
+ */
+static void __exit nss_clmapmgr_exit_module(void)
+{
+#ifdef CONFIG_OF
+ /*
+ * If the node is not compatible, don't do anything.
+ */
+ if (!of_find_node_by_name(NULL, "nss-common")) {
+ return;
+ }
+#endif
+ /*
+ * Unregister clmap interfaces created
+ */
+ unregister_netdevice_notifier(&nss_clmapmgr_notifier);
+}
+
+module_init(nss_clmapmgr_dev_init_module);
+module_exit(nss_clmapmgr_exit_module);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("NSS client map manager");
diff --git a/clmapmgr/nss_clmapmgr_private.h b/clmapmgr/nss_clmapmgr_private.h
new file mode 100644
index 0000000..921eb34
--- /dev/null
+++ b/clmapmgr/nss_clmapmgr_private.h
@@ -0,0 +1,91 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/*
+ * nss_clmapmgr_private.h
+ *
+ * Private header file for NSS clmapmgr
+ */
+
+#ifndef _NSS_CLMAPMGR_PRIVATE_H_
+#define _NSS_CLMAPMGR_PRIVATE_H_
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+
+#include <nss_api_if.h>
+#include <nss_dynamic_interface.h>
+
+#include "nss_clmapmgr.h"
+
+/*
+ * clmap debug macros
+ */
+#if (NSS_CLMAPMGR_DEBUG_LEVEL < 1)
+#define nss_clmapmgr_assert(fmt, args...)
+#else
+#define nss_clmapmgr_assert(c) BUG_ON(!(c));
+#endif
+
+#if defined(CONFIG_DYNAMIC_DEBUG)
+
+/*
+ * Compile messages for dynamic enable/disable
+ */
+#define nss_clmapmgr_warning(s, ...) pr_debug("%s[%d]:" s, __func__, __LINE__, ##__VA_ARGS__)
+#define nss_clmapmgr_info(s, ...) pr_debug("%s[%d]:" s, __func__, __LINE__, ##__VA_ARGS__)
+#define nss_clmapmgr_trace(s, ...) pr_debug("%s[%d]:" s, __func__, __LINE__, ##__VA_ARGS__)
+
+#else
+
+/*
+ * Statically compile messages at different levels
+ */
+#if (NSS_CLMAPMGR_DEBUG_LEVEL < 2)
+#define nss_clmapmgr_warning(s, ...)
+#else
+#define nss_clmapmgr_warning(s, ...) pr_warn("%s[%d]:" s, __func__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#if (NSS_CLMAPMGR_DEBUG_LEVEL < 3)
+#define nss_clmapmgr_info(s, ...)
+#else
+#define nss_clmapmgr_info(s, ...) pr_notice("%s[%d]:" s, __func__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#if (NSS_CLMAPMGR_DEBUG_LEVEL < 4)
+#define nss_clmapmgr_trace(s, ...)
+#else
+#define nss_clmapmgr_trace(s, ...) pr_info("%s[%d]:" s, __func__, __LINE__, ##__VA_ARGS__)
+#endif
+#endif
+#define nss_clmapmgr_error(s, ...) pr_err("%s[%d]:" s, __func__, __LINE__, ##__VA_ARGS__)
+
+/*
+ * nss_clmapmgr_priv_t
+ * Private structure for NSS clmapmgr.
+ */
+struct nss_clmapmgr_priv_t {
+ int nss_if_number_us; /* Clmapmgr upstream NSS interface number */
+ int nss_if_number_ds; /* Clmapmgr downstream NSS interface number */
+ struct rtnl_link_stats64 stats; /* Netdev stats */
+};
+
+#endif