[qca-nss-clients] Mirror client module.

Change-Id: I61e3b6452db7096abe66a8dc6e2b079397331372
Signed-off-by: Manish Verma <maniverm@codeaurora.org>
diff --git a/Makefile b/Makefile
index e52480d..8b16c6c 100644
--- a/Makefile
+++ b/Makefile
@@ -35,6 +35,7 @@
 obj-$(clmapmgr)+= clmapmgr/
 obj-$(match)+= match/
 obj-$(tlsmgr)+= tls/
+obj-$(mirror)+= mirror/
 
 #NSS NETLINK
 obj-$(netlink)+= netlink/
diff --git a/exports/nss_mirror_public.h b/exports/nss_mirror_public.h
new file mode 100644
index 0000000..de3e227
--- /dev/null
+++ b/exports/nss_mirror_public.h
@@ -0,0 +1,131 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2020, 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_mirror_public.h
+ *	NSS Mirror interface definitions.
+ */
+#ifndef __NSS_MIRROR_H
+#define __NSS_MIRROR_H
+
+#include <nss_api_if.h>
+
+/**
+ * nss_mirror_disable
+ *	API to send disable message to mirror interface in NSS firmware.
+ *
+ * @datatypes
+ * struct net_device \n
+ *
+ * @param[in] mirror_dev  Mirror netdevice pointer.
+ *
+ * @return
+ * Return 0 on success, -1 on failure.
+ */
+extern int nss_mirror_disable(struct net_device *mirror_dev);
+
+/**
+ * nss_mirror_enable
+ *	API to send enable message to mirror interface in NSS firmware.
+ *
+ * @datatypes
+ * struct net_device \n
+ *
+ * @param[in] mirror_dev  Mirror netdevice pointer.
+ *
+ * @return
+ * Return 0 on success, -1 on failure.
+ */
+extern int nss_mirror_enable(struct net_device *mirror_dev);
+
+/**
+ * nss_mirror_set_nexthop
+ *	API to send set nexthop message to mirror interface in NSS firmware.
+ *
+ * @datatypes
+ * struct net_device \n
+ *
+ * @param[in] mirror_dev       Mirror netdevice pointer.
+ * @param[in] mirror_next_hop  Nexthop interface number.
+ *
+ * @return
+ * Return 0 on success, -1 on failure.
+ */
+extern int nss_mirror_set_nexthop(struct net_device *mirror_dev, int32_t mirror_next_hop);
+
+/**
+ * nss_mirror_reset_nexthop
+ *	API to send reset nexthop message to mirror interface in NSS firmware.
+ *
+ * @datatypes
+ * struct net_device \n
+ *
+ * @param[in] mirror_dev  Mirror netdevice pointer.
+ *
+ * @return
+ * Return 0 on success, -1 on failure.
+ */
+extern int nss_mirror_reset_nexthop(struct net_device *mirror_dev);
+
+/**
+ * nss_mirror_configure
+ *	API to send configure message to mirror interface in NSS firmware.
+ *
+ * @datatypes
+ * struct net_device \n
+ * nss_mirror_pkt_clone_point \n
+ *
+ * @param[in] mirror_dev        Mirror netdevice pointer.
+ * @param[in] pkt_clone_point   Point in the packet to copy from.
+ * @param[in] pkt_clone_size    Number of bytes to copy.
+ * @param[in] pkt_clone_offset  Copy offset.
+ *
+ * @return
+ * Return 0 on success, -1 on failure.
+ */
+extern int nss_mirror_configure(struct net_device *mirror_dev, enum nss_mirror_pkt_clone_point pkt_clone_point,
+		 uint16_t pkt_clone_size, uint16_t pkt_clone_offset);
+
+/**
+ * nss_mirror_destroy
+ *	API to de-register and delete mirror interface.
+ *
+ * @datatypes
+ * struct net_device \n
+ *
+ * @param[in] mirror_dev  Mirror netdevice pointer.
+ *
+ * @return
+ * Return 0 on success, -1 on failure.
+ */
+extern int nss_mirror_destroy(struct net_device *mirror_dev);
+
+/**
+ * nss_mirror_create
+ *	API to create and register mirror interface.
+ *
+ * @datatypes
+ * struct net_device \n
+ *
+ * @return
+ * Return pointer of the newly created device.
+ */
+
+extern struct net_device *nss_mirror_create(void);
+
+#endif /* __NSS_MIRROR_H */
diff --git a/mirror/Makefile b/mirror/Makefile
new file mode 100644
index 0000000..213f0b9
--- /dev/null
+++ b/mirror/Makefile
@@ -0,0 +1,11 @@
+# Makefile for NSS MIRROR
+
+ccflags-y += $(NSS_CCFLAGS) -I$(obj)/../../exports
+ccflags-y += -DNSS_MIRROR_DEBUG_LEVEL=2
+ccflags-y += -Werror
+
+obj-m += qca-nss-mirror.o
+qca-nss-mirror-objs := \
+		  nss_mirror.o \
+		  nss_mirror_ctl.o \
+		  nss_mirror_init.o
diff --git a/mirror/nss_mirror.c b/mirror/nss_mirror.c
new file mode 100644
index 0000000..b976a33
--- /dev/null
+++ b/mirror/nss_mirror.c
@@ -0,0 +1,885 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2020, 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.
+ ***************************************************************************
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include <nss_api_if.h>
+#include <nss_cmn.h>
+
+#include "nss_mirror.h"
+
+#define NSS_MIRROR_NETDEV_NAME "mirror."
+
+/*
+ * Mirror netdevice count for naming convension.
+ */
+static u8 nss_mirror_netdev_count = 0;
+
+/*
+ * Mirror device statistics lock.
+ */
+static DEFINE_SPINLOCK(nss_mirror_stats_lock);
+
+/*
+ * Mirror interface array lock.
+ */
+static DEFINE_SPINLOCK(nss_mirror_arr_lock);
+
+/*
+ * Array of mirror interfaces.
+ */
+static struct net_device *nss_mirror_arr[NSS_MAX_MIRROR_DYNAMIC_INTERFACES];
+
+/*
+ * nss_mirror_arr_del()
+ *	API to delete mirror netdev from mirror array.
+ */
+static void nss_mirror_arr_del(struct net_device *mirror_dev)
+{
+	struct nss_mirror_instance_priv *mirror_priv = netdev_priv(mirror_dev);
+
+	spin_lock(&nss_mirror_arr_lock);
+	nss_mirror_arr[mirror_priv->mirror_arr_index] = NULL;
+	spin_unlock(&nss_mirror_arr_lock);
+}
+
+/*
+ * nss_mirror_arr_add()
+ *	API to add mirror netdev in mirror array.
+ */
+static int nss_mirror_arr_add(struct net_device *dev)
+{
+	uint8_t i;
+
+	spin_lock(&nss_mirror_arr_lock);
+	for (i = 0; i < NSS_MAX_MIRROR_DYNAMIC_INTERFACES; i++) {
+		struct nss_mirror_instance_priv *mirror_priv;
+
+		if (nss_mirror_arr[i] != NULL) {
+			continue;
+		}
+
+		/*
+		 * Add interface in mirror array.
+		 */
+		nss_mirror_arr[i] = dev;
+		mirror_priv = netdev_priv(dev);
+		mirror_priv->mirror_arr_index = i;
+		spin_unlock(&nss_mirror_arr_lock);
+		return 0;
+	}
+	spin_unlock(&nss_mirror_arr_lock);
+	return -1;
+}
+
+/*
+ * nss_mirror_display_info()
+ *	API to display configure information for the given mirror device.
+ */
+void nss_mirror_display_info(struct net_device *mirror_dev)
+{
+	struct nss_mirror_instance_priv *mirror_priv = netdev_priv(mirror_dev);
+
+	pr_info("Mirror interface:%s info:\n", mirror_dev->name);
+
+	pr_info("mirror interface number: %d\n"
+			"mirror configure: mirror size: %d, mirror point: %d, mirror offset: %d\n"
+			"rule config mode: %d\n",
+			 mirror_priv->mirror_instance_if_num, mirror_priv->mirror_size,
+			 mirror_priv->mirror_point, mirror_priv->mirror_offset,
+			 mirror_priv->rule_config_mode);
+}
+
+/*
+ * nss_mirror_display_all_info()
+ *	API to display configure information for all mirror devices.
+ */
+void nss_mirror_display_all_info(void)
+{
+	int i;
+	struct net_device *mirror_dev;
+
+	spin_lock(&nss_mirror_arr_lock);
+	for (i = 0; i < NSS_MAX_MIRROR_DYNAMIC_INTERFACES; i++) {
+
+		if (nss_mirror_arr[i] == NULL) {
+			continue;
+		}
+
+		mirror_dev = nss_mirror_arr[i];
+		nss_mirror_display_info(mirror_dev);
+	}
+	spin_unlock(&nss_mirror_arr_lock);
+}
+
+/*
+ * nss_mirror_netdev_up()
+ *	API to make the net device up.
+ */
+static int nss_mirror_netdev_up(struct net_device *dev)
+{
+	netif_start_queue(dev);
+	return 0;
+}
+
+/*
+ * nss_mirror_netdev_down()
+ *	API to make the net device down.
+ */
+static int nss_mirror_netdev_down(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+	return 0;
+}
+
+/*
+ * nss_mirror_get_stats()
+ *	API to get statistics for mirror interface.
+ */
+static struct rtnl_link_stats64 *nss_mirror_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+	struct nss_mirror_instance_priv *mirror_priv;
+
+	if (!stats) {
+		nss_mirror_warn("%p: Invalid stats parameter.\n", dev);
+		return stats;
+	}
+
+	memset(stats, 0, sizeof(struct rtnl_link_stats64));
+
+	spin_lock_bh(&nss_mirror_stats_lock);
+	if (!dev) {
+		spin_unlock_bh(&nss_mirror_stats_lock);
+		nss_mirror_warn("Invalid netdev.\n");
+		return stats;
+	}
+	dev_hold(dev);
+	mirror_priv = netdev_priv(dev);
+	memcpy(stats, &mirror_priv->stats, sizeof(struct rtnl_link_stats64));
+	dev_put(dev);
+	spin_unlock_bh(&nss_mirror_stats_lock);
+
+	return stats;
+}
+
+/*
+ * nss_mirror_netdev_ops
+ *	Mirror net device operations.
+ */
+static const struct net_device_ops nss_mirror_netdev_ops = {
+	.ndo_open		= nss_mirror_netdev_up,
+	.ndo_stop		= nss_mirror_netdev_down,
+	.ndo_get_stats64	= nss_mirror_get_stats,
+};
+
+/*
+ * nss_mirror_netdev_setup()
+ *	Setup the mirror net device.
+ */
+static void nss_mirror_netdev_setup(struct net_device *dev)
+{
+	dev->addr_len = 0;
+	dev->flags = IFF_NOARP;
+	dev->features = NETIF_F_FRAGLIST;
+	dev->netdev_ops = &nss_mirror_netdev_ops;
+}
+
+/*
+ * nss_mirror_stats_update()
+ *	API to update mirror interface statistics.
+ */
+static void nss_mirror_stats_update(struct net_device *mirror_dev, struct nss_mirror_stats_sync_msg *stats)
+{
+	struct rtnl_link_stats64 *netdev_stats;
+	struct nss_mirror_instance_priv *mirror_priv;
+	uint64_t dropped = 0;
+
+	spin_lock_bh(&nss_mirror_stats_lock);
+	if (!mirror_dev) {
+		spin_unlock_bh(&nss_mirror_stats_lock);
+		nss_mirror_warn("Invalid mirror interface\n");
+		return;
+	}
+
+	dev_hold(mirror_dev);
+	mirror_priv = netdev_priv(mirror_dev);
+	netdev_stats = &mirror_priv->stats;
+
+	dropped += nss_cmn_rx_dropped_sum(&stats->node_stats);
+
+	/*
+	 * TODO: Currently mirror stat tx_send fail is used by NSS firmware
+	 * even after sending the packet to the host.
+	 * So, at present, this stat cannot be used as drop stats by this API.
+	 * In future, if we need to add this stat too in mirror interface
+	 * drop stats, then NSS firmware should not use this stat after sending
+	 * the packets to the host and should use some new stat for post mirrored
+	 * errors (errors which may occur after sending the packet to the host).
+	 */
+	dropped += stats->mirror_stats.mem_alloc_fail;
+	dropped += stats->mirror_stats.copy_fail;
+
+	/*
+	 * There will not be any tx stats for mirror interface, as mirror interface
+	 * is intented to receive mirrored packets only.
+	 */
+	netdev_stats->rx_packets += stats->mirror_stats.mirror_pkts;
+	netdev_stats->rx_bytes += stats->mirror_stats.mirror_bytes;
+	netdev_stats->rx_dropped += dropped;
+	dev_put(mirror_dev);
+	spin_unlock_bh(&nss_mirror_stats_lock);
+}
+
+/*
+ * nss_mirror_event_cb()
+ *	Event callback.
+ */
+static void nss_mirror_event_cb(void *if_ctx, struct nss_cmn_msg *ncm)
+{
+	struct net_device *netdev = if_ctx;
+	struct nss_mirror_msg *nim = (struct nss_mirror_msg *)ncm;
+
+	switch (ncm->type) {
+	case NSS_MIRROR_MSG_SYNC_STATS:
+		nss_mirror_stats_update(netdev, &nim->msg.stats);
+		break;
+
+       default:
+		nss_mirror_warn("%p: Unknown Event from NSS\n", netdev);
+		break;
+       }
+}
+
+/*
+ * nss_mirror_data_cb()
+ *	Data callback.
+ */
+static void nss_mirror_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi)
+{
+	if (skb && skb->data) {
+		nss_mirror_info("Printing 64 bytes of data\n");
+		print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1,
+				skb->data, 64, 0);
+	}
+
+	dev_hold(netdev);
+
+	/*
+	 * Free the packet if mirror device is not up.
+	 */
+	if (!(netdev->flags & IFF_UP)) {
+		kfree_skb(skb);
+		dev_put(netdev);
+		return;
+	}
+
+	skb->dev = netdev;
+	skb->pkt_type = PACKET_HOST;
+	skb->skb_iif = netdev->ifindex;
+	skb_reset_mac_header(skb);
+	netif_receive_skb(skb);
+
+	dev_put(netdev);
+}
+
+/*
+ * nss_mirror_disable()
+ *	API to send disable message to mirror interface in NSS firmware.
+ */
+int nss_mirror_disable(struct net_device *mirror_dev)
+{
+	struct nss_mirror_msg nmm = {0};
+	struct nss_ctx_instance *nss_ctx;
+	const struct net_device_ops *ops;
+	nss_tx_status_t status;
+	int32_t if_num, ret;
+
+	if (!mirror_dev) {
+		nss_mirror_warn("Invalid input mirror interface\n");
+		return -1;
+	}
+
+	/*
+	 * Fetch the mirror netdevice.
+	 */
+	if ((if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+		nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+		return -1;
+	}
+
+	nss_ctx = nss_mirror_get_context();
+	if (!nss_ctx) {
+		nss_mirror_warn("Invalid NSS context\n");
+		return -1;
+	}
+
+	nss_cmn_msg_init(&nmm.cm, if_num, NSS_MIRROR_MSG_DISABLE, 0, NULL, NULL);
+
+	status = nss_mirror_tx_msg_sync(nss_ctx, &nmm);
+	if (status != NSS_TX_SUCCESS) {
+		nss_mirror_warn("Sending disable config to %d mirror interface failed\n", if_num);
+		return -1;
+	}
+
+	/*
+	 * Bring down the mirror netdev.
+	 */
+	ops = mirror_dev->netdev_ops;
+	if (ops->ndo_stop) {
+		ret = ops->ndo_stop(mirror_dev);
+		if (ret) {
+			nss_mirror_warn("%s device stop function failed: %d\n", mirror_dev->name, ret);
+			return -1;
+		}
+		mirror_dev->flags &= ~IFF_UP;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(nss_mirror_disable);
+
+/*
+ * nss_mirror_enable()
+ *	API to send enable message to mirror interface in NSS firmware.
+ */
+int nss_mirror_enable(struct net_device *mirror_dev)
+{
+	struct nss_mirror_msg nmm = {0};
+	struct nss_ctx_instance *nss_ctx;
+	const struct net_device_ops *ops;
+	nss_tx_status_t status;
+	int32_t if_num, ret;
+
+	if (!mirror_dev) {
+		nss_mirror_warn("Invalid input mirror interface\n");
+		return -1;
+	}
+
+	/*
+	 * Verify the mirror netdevice.
+	 */
+	if ((if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+		nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+		return -1;
+	}
+
+	nss_ctx = nss_mirror_get_context();
+	if (!nss_ctx) {
+		nss_mirror_warn("Invalid NSS context\n");
+		return -1;
+	}
+
+	nss_cmn_msg_init(&nmm.cm, if_num, NSS_MIRROR_MSG_ENABLE, 0, NULL, NULL);
+
+	status = nss_mirror_tx_msg_sync(nss_ctx, &nmm);
+	if (status != NSS_TX_SUCCESS) {
+		nss_mirror_warn("Sending enable config to %d mirror interface failed\n", if_num);
+		return -1;
+	}
+
+	/*
+	 * Bring up the mirror netdev.
+	 */
+	ops = mirror_dev->netdev_ops;
+	if (ops->ndo_open) {
+		ret = ops->ndo_open(mirror_dev);
+		if (ret) {
+			nss_mirror_warn("%s device open function failed: %d\n", mirror_dev->name, ret);
+			return -1;
+		}
+		mirror_dev->flags |= IFF_UP;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(nss_mirror_enable);
+
+/*
+ * nss_mirror_set_nexthop()
+ *	API to send set nexthop message to mirror interface in NSS firmware.
+ */
+int nss_mirror_set_nexthop(struct net_device *mirror_dev, int32_t mirror_next_hop)
+{
+	struct nss_mirror_msg nmm = {0};
+	struct nss_ctx_instance *nss_ctx;
+	struct nss_mirror_instance_priv *mirror_priv;
+	int32_t mirror_if_num;
+	nss_tx_status_t status;
+
+	if (!mirror_dev) {
+		nss_mirror_warn("Invalid input mirror interface\n");
+		return -1;
+	}
+
+	/*
+	 * Validate the nexthop argument.
+	 */
+	if (mirror_next_hop >= NSS_MAX_NET_INTERFACES) {
+		nss_mirror_warn("Invalid mirror next hop interface:%d\n", mirror_next_hop);
+		return -1;
+	}
+
+	/*
+	 * Fetch the mirror netdevice.
+	 */
+	if ((mirror_if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+		nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+		return -1;
+	}
+
+	/*
+	 * Return if the mirror interface is in open state.
+	 */
+	if ((mirror_dev->flags & IFF_UP)) {
+		nss_mirror_warn("%s mirror interface is in open state\n", mirror_dev->name);
+		return -1;
+	}
+
+	mirror_priv = netdev_priv(mirror_dev);
+	if (mirror_priv->rule_config_mode == NSS_MIRROR_MODE_INGRESS_PMC) {
+		nss_mirror_warn("Nexthop already configured for %s mirror device\n", mirror_dev->name);
+		return -1;
+	}
+
+	nss_ctx = nss_mirror_get_context();
+	if (!nss_ctx) {
+		nss_mirror_warn("Invalid NSS context\n");
+		return -1;
+	}
+
+	nss_cmn_msg_init(&nmm.cm, mirror_if_num, NSS_MIRROR_MSG_SET_NEXTHOP, 0, NULL, NULL);
+	nmm.msg.nexthop.if_num = mirror_next_hop;
+
+	status = nss_mirror_tx_msg_sync(nss_ctx, &nmm);
+	if (status != NSS_TX_SUCCESS) {
+		nss_mirror_warn("Sending set nexthop config to %d mirror interface failed"
+				" Nexthop: %d\n", mirror_if_num, mirror_next_hop);
+		return -1;
+	}
+
+	/*
+	 * Update the configure mode in mirror netdev.
+	 */
+	mirror_priv->rule_config_mode = NSS_MIRROR_MODE_INGRESS_PMC;
+
+	return 0;
+}
+EXPORT_SYMBOL(nss_mirror_set_nexthop);
+
+/*
+ * nss_mirror_reset_nexthop()
+ *	API to send reset nexthop message to mirror interface in NSS firmware.
+ */
+int nss_mirror_reset_nexthop(struct net_device *mirror_dev)
+{
+	struct nss_mirror_msg nmm = {0};
+	struct nss_ctx_instance *nss_ctx;
+	struct nss_mirror_instance_priv *mirror_priv;
+	nss_tx_status_t status;
+	int32_t if_num;
+
+	if (!mirror_dev) {
+		nss_mirror_warn("Invalid input mirror interface\n");
+		return -1;
+	}
+
+	/*
+	 * Verify the mirror netdevice.
+	 */
+	if ((if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+		nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+		return -1;
+	}
+
+	mirror_priv = netdev_priv(mirror_dev);
+	if (mirror_priv->rule_config_mode != NSS_MIRROR_MODE_INGRESS_PMC) {
+		nss_mirror_warn("Invalid request for %s mirror device\n", mirror_dev->name);
+		return -1;
+	}
+
+	nss_ctx = nss_mirror_get_context();
+	if (!nss_ctx) {
+		nss_mirror_warn("Invalid NSS context\n");
+		return -1;
+	}
+
+	nss_cmn_msg_init(&nmm.cm, if_num, NSS_MIRROR_MSG_RESET_NEXTHOP, 0, NULL, NULL);
+
+	status = nss_mirror_tx_msg_sync(nss_ctx, &nmm);
+	if (status != NSS_TX_SUCCESS) {
+		nss_mirror_warn("Sending reset nexthop config to %d mirror interface failed\n", if_num);
+		return -1;
+	}
+
+	/*
+	 * Reset the mirror configuration.
+	 */
+	mirror_priv->rule_config_mode = NSS_MIRROR_MODE_NONE;
+
+	return 0;
+}
+EXPORT_SYMBOL(nss_mirror_reset_nexthop);
+
+/*
+ * nss_mirror_configure()
+ *	API to send configure message to mirror interface in NSS firmware.
+ */
+int nss_mirror_configure(struct net_device *mirror_dev, enum nss_mirror_pkt_clone_point pkt_clone_point,
+		 uint16_t pkt_clone_size, uint16_t pkt_clone_offset)
+{
+	struct nss_mirror_msg nmm = {0};
+	struct nss_ctx_instance *nss_ctx;
+	struct nss_mirror_instance_priv *mirror_priv;
+	nss_tx_status_t status;
+	int32_t if_num;
+
+	if (!mirror_dev) {
+		nss_mirror_warn("Invalid input mirror interface\n");
+		return -1;
+	}
+
+	/*
+	 * Validate clone point parameter.
+	 */
+	if (!pkt_clone_point || (pkt_clone_point >= NSS_MIRROR_PKT_CLONE_POINT_MAX)) {
+		nss_mirror_warn("Invalid clone point value: %d for device %s\n", pkt_clone_point, mirror_dev->name);
+		return -1;
+	}
+
+	/*
+	 * Fetch the mirror netdevice.
+	 */
+	if ((if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+		nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+		return -1;
+	}
+
+	nss_ctx = nss_mirror_get_context();
+	if (!nss_ctx) {
+		nss_mirror_warn("Invalid NSS context\n");
+		return -1;
+	}
+
+	nss_cmn_msg_init(&nmm.cm, if_num, NSS_MIRROR_MSG_CONFIGURE,
+			 sizeof(struct nss_mirror_configure_msg), NULL, NULL);
+	nmm.msg.config.pkt_clone_size = pkt_clone_size;
+	nmm.msg.config.pkt_clone_point = pkt_clone_point;
+	nmm.msg.config.pkt_clone_offset = pkt_clone_offset;
+
+	nss_mirror_trace("pkt_clone_size : %d pkt_clone_point %d pkt_clone_offset %d\n", nmm.msg.config.pkt_clone_size, nmm.msg.config.pkt_clone_point, nmm.msg.config.pkt_clone_offset);
+
+	status = nss_mirror_tx_msg_sync(nss_ctx, &nmm);
+	if (status != NSS_TX_SUCCESS) {
+		nss_mirror_warn("Sending create config to %d mirror interface failed\n", if_num);
+		return -1;
+	}
+
+	/*
+	 * Save the mirror configurations in mirror netdev.
+	 */
+	mirror_priv = netdev_priv(mirror_dev);
+	mirror_priv->mirror_point = pkt_clone_point;
+	mirror_priv->mirror_offset = pkt_clone_offset;
+	mirror_priv->mirror_size = pkt_clone_size;
+
+	return 0;
+}
+EXPORT_SYMBOL(nss_mirror_configure);
+
+/*
+ * nss_mirror_reset_if_nexthop()
+ *	API to send reset nexthop command to NSS firmware.
+ * TODO: Remove this API and its usage and use NSS driver API, once
+ * similar API is added into NSS driver.
+ */
+nss_tx_status_t nss_mirror_reset_if_nexthop(uint32_t if_num)
+{
+	struct nss_if_msg nim;
+	struct nss_ctx_instance *nss_ctx;
+	nss_tx_status_t status;
+
+	/*
+	 * TODO: Use context of the input interface instead of mirror context.
+	 */
+	nss_ctx = nss_mirror_get_context();
+	if (!nss_ctx) {
+		nss_mirror_warn("Invalid NSS context\n");
+		return -1;
+	}
+
+	nss_cmn_msg_init(&nim.cm, if_num, NSS_IF_RESET_NEXTHOP, 0, NULL, NULL);
+
+	status = nss_if_tx_msg(nss_ctx, &nim);
+	if (status != NSS_TX_SUCCESS) {
+		nss_mirror_warn("%p: Failed to send reset nexthop message to %d interface\n", nss_ctx, if_num);
+		return status;
+	}
+
+	nss_mirror_info("%p: Reset nexthop message is sent successfully\n", nss_ctx);
+	return status;
+}
+
+/*
+ * nss_mirror_destroy_if()
+ *	API to un-register and destroy the mirror instance in NSS firmware.
+ */
+static int nss_mirror_destroy_if(struct net_device *mirror_dev)
+{
+	int32_t if_num;
+
+	/*
+	 * Fetch the mirror netdevice.
+	 */
+	if ((if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+		nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+		return -1;
+	}
+
+	nss_mirror_unregister_if(if_num);
+	if (nss_dynamic_interface_dealloc_node(if_num, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)
+			!= NSS_TX_SUCCESS) {
+		nss_mirror_warn("Failed to dealloc mirror interface instance\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * nss_mirror_netdev_destroy()
+ *	API to un-register and free mirror net device.
+ */
+static void nss_mirror_netdev_destroy(struct net_device *mirror_dev)
+{
+	if (!mirror_dev) {
+		nss_mirror_warn("Invalid mirror device pointer\n");
+		return;
+	}
+
+	unregister_netdev(mirror_dev);
+
+	spin_lock_bh(&nss_mirror_stats_lock);
+	free_netdev(mirror_dev);
+	spin_unlock_bh(&nss_mirror_stats_lock);
+}
+
+/*
+ * nss_mirror_destroy()
+ *	API to de-register and delete mirror interface.
+ */
+int nss_mirror_destroy(struct net_device *mirror_dev)
+{
+	if (!mirror_dev) {
+		nss_mirror_warn("Invalid input mirror interface\n");
+		return -1;
+	}
+
+	/*
+	 * Destroy mirror instance in NSS firmware.
+	 */
+	if (nss_mirror_destroy_if(mirror_dev)) {
+		nss_mirror_warn("Error in destroying %s mirror interface from NSS"
+				" firmware\n", mirror_dev->name);
+		return -1;
+	}
+
+	/*
+	 * Remove mirror netdev from mirror array.
+	 */
+	nss_mirror_arr_del(mirror_dev);
+
+	/*
+	 * Destroy the mirror device.
+	 */
+	nss_mirror_netdev_destroy(mirror_dev);
+	return 0;
+}
+EXPORT_SYMBOL(nss_mirror_destroy);
+
+/*
+ * nss_mirror_create_if()
+ *	Create and register mirror instance in NSS.
+ */
+static int32_t nss_mirror_create_if(struct net_device *dev, nss_mirror_data_callback_t data_callback)
+{
+	struct nss_ctx_instance *nss_ctx;
+	struct nss_mirror_instance_priv *mirror_priv;
+	int32_t if_num, ret;
+	uint32_t features = 0;
+
+	if_num = nss_dynamic_interface_alloc_node(NSS_DYNAMIC_INTERFACE_TYPE_MIRROR);
+	if (if_num < 0) {
+		nss_mirror_warn("Mirror interface creation failed - alloc node failed\n");
+		return -1;
+	}
+
+	nss_ctx = nss_mirror_register_if(if_num,
+			data_callback,
+			nss_mirror_event_cb,
+			dev,
+			features);
+	if (!nss_ctx) {
+		nss_mirror_warn("%d interface registration failed\n", if_num);
+		ret = nss_dynamic_interface_dealloc_node(if_num, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR);
+		if (ret != NSS_TX_SUCCESS) {
+			nss_mirror_warn("%d interface dealloc failed\n", if_num);
+		}
+		return -1;
+	}
+
+	nss_mirror_trace("Alloc status on %d interface\n", if_num);
+
+	/*
+	 * Save NSS mirror interface number in mirror netdev.
+	 */
+	mirror_priv = netdev_priv(dev);
+	mirror_priv->mirror_instance_if_num = if_num;
+
+	return if_num;
+}
+
+/*
+ * nss_mirror_create()
+ *	API to create and register mirror interface.
+ */
+struct net_device *nss_mirror_create(void)
+{
+	struct net_device *dev;
+	char *devname;
+	int32_t mirror_if_num;
+	int ret;
+
+	devname = vzalloc(strlen(NSS_MIRROR_NETDEV_NAME)+3+1);
+	if (!devname) {
+		nss_mirror_warn("memory allocation failed\n");
+		return NULL;
+	}
+
+	snprintf(devname, strlen(NSS_MIRROR_NETDEV_NAME)+3, "%s%d", NSS_MIRROR_NETDEV_NAME, nss_mirror_netdev_count);
+
+	dev = alloc_netdev(sizeof(struct nss_mirror_instance_priv), devname, NET_NAME_UNKNOWN, nss_mirror_netdev_setup);
+	if (!dev) {
+		nss_mirror_warn("netdev allocation failed\n");
+		vfree(devname);
+		return NULL;
+	}
+
+	mirror_if_num = nss_mirror_create_if(dev, nss_mirror_data_cb);
+	if (mirror_if_num < 0) {
+		nss_mirror_warn("mirror interface instance creation failed\n");
+		free_netdev(dev);
+		vfree(devname);
+		return NULL;
+	}
+
+	ret = register_netdev(dev);
+	if (ret) {
+		nss_mirror_warn("netdev registration failed\n");
+		if (nss_mirror_destroy_if(dev)) {
+			nss_mirror_warn("Error in destroying %s mirror device from"
+					" NSS firmware\n", dev->name);
+		}
+		free_netdev(dev);
+		vfree(devname);
+		return NULL;
+	}
+
+	/*
+	 * Add the mirror netdev to mirror array.
+	 */
+	if (nss_mirror_arr_add(dev)) {
+		nss_mirror_warn("Error in adding %s mirror device in mirror array\n", dev->name);
+		if (nss_mirror_destroy_if(dev)) {
+			nss_mirror_warn("Error in destroying %s mirror device from"
+					" NSS firmware\n", dev->name);
+		}
+		nss_mirror_netdev_destroy(dev);
+		vfree(devname);
+		return NULL;
+	}
+
+	nss_mirror_netdev_count++;
+	return dev;
+}
+EXPORT_SYMBOL(nss_mirror_create);
+
+/*
+ * nss_mirror_deconfigure_mirror()
+ *	API to deconfigure mirror device.
+ */
+int nss_mirror_deconfigure_mirror(struct net_device *mirror_dev)
+{
+	struct nss_mirror_instance_priv *mirror_priv = netdev_priv(mirror_dev);
+
+	if (mirror_priv->rule_config_mode == NSS_MIRROR_MODE_NONE) {
+		return 0;
+	}
+
+	if (mirror_priv->rule_config_mode == NSS_MIRROR_MODE_INGRESS_PMC) {
+		if (nss_mirror_reset_nexthop(mirror_dev)) {
+			nss_mirror_warn("Error in sending reset nexthop config to mirror interface: %d\n",
+					mirror_priv->mirror_instance_if_num);
+			return -1;
+		}
+	}
+
+	/*
+	 * Reset the mirror mode.
+	 */
+	mirror_priv->rule_config_mode = NSS_MIRROR_MODE_NONE;
+	return 0;
+}
+
+/*
+ * nss_mirror_destroy_all()
+ *	API to destroy all the configured mirror devices.
+ */
+int nss_mirror_destroy_all(void)
+{
+	int i;
+
+	for (i = 0; i < NSS_MAX_MIRROR_DYNAMIC_INTERFACES; i++) {
+		struct net_device *mirror_dev;
+
+		spin_lock(&nss_mirror_arr_lock);
+		if (nss_mirror_arr[i] == NULL) {
+			spin_unlock(&nss_mirror_arr_lock);
+			continue;
+		}
+
+		mirror_dev = nss_mirror_arr[i];
+		spin_unlock(&nss_mirror_arr_lock);
+
+		/*
+		 * Deconfigure mirror interface.
+		 */
+		if (nss_mirror_deconfigure_mirror(mirror_dev) < 0) {
+			nss_mirror_warn("Error in deconfiguring mirror interface: %s\n", mirror_dev->name);
+			return -1;
+		}
+
+		if (nss_mirror_destroy(mirror_dev)) {
+			nss_mirror_warn("Error in sending delete config to mirror interface: %s\n", mirror_dev->name);
+			return -1;
+		}
+	}
+	return 0;
+}
diff --git a/mirror/nss_mirror.h b/mirror/nss_mirror.h
new file mode 100644
index 0000000..8f9dc0b
--- /dev/null
+++ b/mirror/nss_mirror.h
@@ -0,0 +1,110 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2020, 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.
+ ***************************************************************************
+ */
+
+#include <linux/etherdevice.h>
+
+#include <nss_api_if.h>
+#include <nss_cmn.h>
+
+#if (NSS_MIRROR_DEBUG_LEVEL < 1)
+#define nss_mirror_assert(fmt, args...)
+#else
+#define nss_mirror_assert(c) if (!(c)) { BUG_ON(!(c)); }
+#endif /* NSS_MIRROR_DEBUG_LEVEL */
+
+/*
+ * Compile messages for dynamic enable/disable
+ */
+#if defined(CONFIG_DYNAMIC_DEBUG)
+#define nss_mirror_warn(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#define nss_mirror_info(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#define nss_mirror_trace(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#else /* CONFIG_DYNAMIC_DEBUG */
+/*
+ * Statically compile messages at different levels
+ */
+#if (NSS_MIRROR_DEBUG_LEVEL < 2)
+#define nss_mirror_warn(s, ...)
+#else
+#define nss_mirror_warn(s, ...) pr_warn("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#if (NSS_MIRROR_DEBUG_LEVEL < 3)
+#define nss_mirror_info(s, ...)
+#else
+#define nss_mirror_info(s, ...)   pr_notice("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#if (NSS_MIRROR_DEBUG_LEVEL < 4)
+#define nss_mirror_trace(s, ...)
+#else
+#define nss_mirror_trace(s, ...)  pr_info("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#endif
+#endif /* CONFIG_DYNAMIC_DEBUG */
+
+/*
+ * Mirror configure modes.
+ */
+#define NSS_MIRROR_MODE_NONE 0x0
+#define NSS_MIRROR_MODE_INGRESS_PMC 0x1
+
+/*
+ * nss_mirror_instance_priv
+ *	Private structure of mirror net device.
+ */
+struct nss_mirror_instance_priv {
+	struct rtnl_link_stats64 stats;			/* Netdev stats. */
+	uint32_t mirror_instance_if_num;		/* NSS interface number for this mirror device. */
+	enum nss_mirror_pkt_clone_point mirror_point;	/* Point in the packet to copy from. */
+	uint16_t mirror_size;				/* Number of bytes to copy. */
+	uint16_t mirror_offset;				/* Copy offset. */
+	uint8_t rule_config_mode;			/* Configure mode. */
+	uint8_t mirror_arr_index;			/* Netdev index in mirror array. */
+};
+
+/*
+ * nss_mirror_display_info()
+ *	API to display configure information for the given mirror device.
+ */
+extern void nss_mirror_display_info(struct net_device *mirror_dev);
+
+/*
+ * nss_mirror_display_all_info()
+ *	API to display configure information for all mirror devices.
+ */
+extern void nss_mirror_display_all_info(void);
+
+/*
+ * nss_mirror_reset_if_nexthop()
+ *	API to send reset nexthop command to NSS firmware.
+ * TODO: Remove this API and its usage and use NSS driver API, once
+ * similar API is added into NSS driver.
+ */
+extern nss_tx_status_t nss_mirror_reset_if_nexthop(uint32_t if_num);
+
+/*
+ * nss_mirror_deconfigure_mirror()
+ *	API to deconfigure mirror device.
+ */
+extern int nss_mirror_deconfigure_mirror(struct net_device *mirror_dev);
+
+/*
+ * nss_mirror_destroy_all()
+ *	API to destroy all the configured mirror devices.
+ */
+extern int nss_mirror_destroy_all(void);
diff --git a/mirror/nss_mirror_ctl.c b/mirror/nss_mirror_ctl.c
new file mode 100644
index 0000000..9adb250
--- /dev/null
+++ b/mirror/nss_mirror_ctl.c
@@ -0,0 +1,959 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2020, 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.
+ ***************************************************************************
+ */
+
+#include <linux/sysctl.h>
+#include <linux/string.h>
+
+#include "nss_mirror_public.h"
+#include "nss_mirror.h"
+
+
+#define NSS_MIRROR_CONFIG_PARAM_NUM 3
+#define NSS_MIRROR_SET_NEXTHOP_PARAM_NUM 2
+#define NSS_MIRROR_ENABLE_INGRESS_PMC_NUM 2
+
+/*
+ * nss_mirror_ctl_cmd_type
+ *	Mirror command types.
+ */
+enum nss_mirror_ctl_cmd_type {
+	NSS_MIRROR_CMD_UNKNOWN,			/* Unknown command. */
+	NSS_MIRROR_CMD_CREATE,			/* Create command. */
+	NSS_MIRROR_CMD_CONFIGURE,		/* Configure command. */
+	NSS_MIRROR_CMD_SET_NEXTHOP,		/* Set nexthop command. */
+	NSS_MIRROR_CMD_RESET_NEXTHOP,		/* Reset nexthop command. */
+	NSS_MIRROR_CMD_INGRESS_PMC_ENABLE,	/* Ingress promiscuous enable command. */
+	NSS_MIRROR_CMD_INGRESS_PMC_DISABLE,	/* Ingress promiscuous disable command. */
+	NSS_MIRROR_CMD_ENABLE,			/* Enable command. */
+	NSS_MIRROR_CMD_DISABLE,			/* Disable command. */
+	NSS_MIRROR_CMD_DISPLAY,			/* Display command. */
+	NSS_MIRROR_CMD_DESTROY,			/* Destroy command. */
+	NSS_MIRROR_CMD_HELP,			/* Help command. */
+};
+
+/*
+ * Sysctl table header for mirror.
+ */
+static struct ctl_table_header *nss_mirror_ctl_header;
+
+/*
+ * Mirror config data.
+ */
+static unsigned char nss_mirror_config_data[128] __read_mostly;
+
+/*
+ * nss_mirror_ctl_read_nextarg()
+ *	API to read the next argument in the command.
+ */
+static char *nss_mirror_ctl_read_nextarg(char **buf_ptr)
+{
+	if (!buf_ptr || !(*buf_ptr)) {
+		nss_mirror_warn("Read Buf is NULL\n");
+		return NULL;
+	}
+
+	return strsep(buf_ptr, " ");
+}
+
+/*
+ * nss_mirror_ctl_read_value()
+ *	API to read a value of the param in the command.
+ */
+static char *nss_mirror_ctl_read_value(char **buf_ptr, char **value_ptr, char *delim)
+{
+	*value_ptr = nss_mirror_ctl_read_nextarg(buf_ptr);
+
+	if (!(*value_ptr)) {
+		return NULL;
+	}
+
+	return strsep(value_ptr, delim);
+}
+
+/*
+ * nss_mirror_ctl_convert_char_to_u32()
+ *	API to convert character to u32.
+ */
+static int nss_mirror_ctl_convert_char_to_u32(char *buf, uint32_t *arg)
+{
+	int ret;
+
+	/*
+	 * Write the tokens to unsigned integer.
+	 */
+	ret = sscanf(buf, "%u", arg);
+	if (ret != 1) {
+		nss_mirror_warn("Failed to write the %s token to u32\n", buf);
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_convert_char_to_u16()
+ *	API to convert character to u16.
+ */
+static int nss_mirror_ctl_convert_char_to_u16(char *buf, uint16_t *arg)
+{
+	int ret;
+
+	/*
+	 * Write the tokens to unsigned short integer.
+	 */
+	ret = sscanf(buf, "%hu", arg);
+	if (ret != 1) {
+		nss_mirror_warn("Failed to write the %s token to u16\n", buf);
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_get_netdev_by_name()
+ *	API to get netdev from name.
+ *
+ * Note: Caller is expected to release the hold on the dev.
+ */
+static int nss_mirror_ctl_get_netdev_by_name(char *name, struct net_device **dev)
+{
+	char dev_name[IFNAMSIZ] = {0};
+
+	strlcpy(dev_name, name, IFNAMSIZ);
+	if (dev_name[strlen(dev_name) - 1] == '\n') {
+		dev_name[strlen(dev_name) - 1] = '\0';
+	}
+	nss_mirror_info("Device name: %s\n", dev_name);
+	*dev = dev_get_by_name(&init_net, dev_name);
+	if (!*dev) {
+		nss_mirror_warn("Cannot find %s netdevice\n", dev_name);
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_display_help()
+ *	API to display help.
+ */
+static void nss_mirror_ctl_display_help(void)
+{
+	pr_info("Usage: \n"
+		"echo create > <REDIRECT PATH>\n"
+		"echo configure mirrordev=<mirror dev name> [mirror_size=<MIRROR SIZE>]"
+		" [mirror_point=<MIRROR POINT>] [mirror_offset=<MIRROR OFFSET>] > <REDIRECT PATH>\n"
+		"echo enable mirrordev=<mirror dev name> > <REDIRECT PATH>\n"
+		"echo disable mirrordev=<mirror dev name> > <REDIRECT PATH>\n"
+		"echo set_nexthop mirrordev=<mirror dev name> mirror_next_hop=n2h/eth_rx\n > <REDIRECT PATH>"
+		"echo reset_nexthop mirrordev=<mirror dev name> > <REDIRECT PATH>\n"
+		"echo ingress_pmc_enable devname=<eth/ath dev name> mirrordev=<mirror dev name> > <REDIRECT PATH>\n"
+		"echo ingress_pmc_disable devname=<eth/ath dev name> > <REDIRECT PATH>\n"
+		"echo display [mirrordev=<dev name> / all] > <REDIRECT PATH>\n"
+		"echo destroy mirrordev=<mirror dev name> > <REDIRECT PATH>\n"
+		"Where,\n"
+		"\t<REDIRECT PATH> = /proc/sys/dev/nss/mirror/config\n"
+	      );
+}
+
+/*
+ * nss_mirror_ctl_parse_reset_nexthop_cmd()
+ *	API to parse reset nexthop command.
+ */
+static int nss_mirror_ctl_parse_reset_nexthop_cmd(char *buffer)
+{
+	struct net_device *mirror_dev;
+	char *param, *value;
+
+	param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+	if (!param || !value) {
+		return -1;
+	}
+
+	if (strcmp(param, "mirrordev")) {
+		nss_mirror_warn("Invalid param: %s in promiscuous enable cmd\n", value);
+		return -1;
+	}
+
+	if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+		nss_mirror_warn("Error in getting mirror net device.\n");
+		return -1;
+	}
+
+	if (nss_mirror_reset_nexthop(mirror_dev)) {
+		nss_mirror_warn("Error in sending reset nexthop config to mirror interface: %s\n", mirror_dev->name);
+		dev_put(mirror_dev);
+		return -1;
+	}
+
+	dev_put(mirror_dev);
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_set_nexthop_cmd()
+ *	API to parse set nexthop command.
+ */
+static int nss_mirror_ctl_parse_set_nexthop_cmd(char *buffer)
+{
+	struct net_device *mirror_dev = NULL;
+	char *param, *value;
+	uint32_t nexthop_if_num;
+	uint8_t param_num = NSS_MIRROR_SET_NEXTHOP_PARAM_NUM;
+
+	do {
+		param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+		if (!param || !value) {
+			return -1;
+		}
+
+		if (!strcmp(param, "mirrordev")) {
+			if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+				nss_mirror_warn("Error in getting mirror net device.\n");
+				return -1;
+			}
+		} else if (!strcmp(param, "mirror_next_hop")) {
+			if (value[strlen(value) - 1] == '\n') {
+				value[strlen(value) - 1] = '\0';
+			}
+			if (!strcmp(value, "eth_rx")) {
+				nexthop_if_num = NSS_ETH_RX_INTERFACE;
+			} else if (!strcmp(value, "n2h")) {
+				nexthop_if_num = NSS_N2H_INTERFACE;
+			} else {
+				if (mirror_dev) {
+					dev_put(mirror_dev);
+				}
+				nss_mirror_warn("Invalid mirror next hop value %s\n", value);
+				return -1;
+			}
+		} else {
+			if (mirror_dev) {
+				dev_put(mirror_dev);
+			}
+			nss_mirror_warn("Invalid param: %s in promiscuous enable cmd\n", value);
+			return -1;
+		}
+	} while (--param_num);
+
+	if (nss_mirror_set_nexthop(mirror_dev, nexthop_if_num)) {
+		nss_mirror_warn("Error in sending set nexthop config to mirror interface: %s\n", mirror_dev->name);
+		dev_put(mirror_dev);
+		return -1;
+	}
+
+	dev_put(mirror_dev);
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_disable_cmd()
+ *	API to parse disable command.
+ */
+static int nss_mirror_ctl_parse_disable_cmd(char *buffer)
+{
+	struct net_device *mirror_dev;
+	char *param, *value;
+
+	param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+	if (!param || !value) {
+		return -1;
+	}
+
+	if (strcmp(param, "mirrordev")) {
+		nss_mirror_warn("Invalid parameter %s in disable cmd\n", param);
+		return -1;
+	}
+
+	if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+		nss_mirror_warn("Error in getting mirror net device.\n");
+		return -1;
+	}
+
+	if (nss_mirror_disable(mirror_dev)) {
+		nss_mirror_warn("Error in sending disable config to mirror interface: %s\n", mirror_dev->name);
+		dev_put(mirror_dev);
+		return -1;
+	}
+
+	dev_put(mirror_dev);
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_enable_cmd()
+ *	API to parse enable command.
+ */
+static int nss_mirror_ctl_parse_enable_cmd(char *buffer)
+{
+	struct net_device *mirror_dev;
+	char *param, *value;
+
+	param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+	if (!param || !value) {
+		return -1;
+	}
+
+	if (strcmp(param, "mirrordev")) {
+		nss_mirror_warn("Invalid parameter %s in enable cmd\n", param);
+		return -1;
+	}
+
+	if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+		nss_mirror_warn("Error in getting mirror net device.\n");
+		return -1;
+	}
+
+	if (nss_mirror_enable(mirror_dev)) {
+		nss_mirror_warn("Error in sending enable config to mirror interface: %s\n", mirror_dev->name);
+		dev_put(mirror_dev);
+		return -1;
+	}
+
+	dev_put(mirror_dev);
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_display_cmd()
+ *	API to parse display command.
+ */
+static int nss_mirror_ctl_parse_display_cmd(char *buffer)
+{
+	char *param, *value;
+	char dev_name[IFNAMSIZ] = {0};
+	struct net_device *mirror_dev;
+
+	param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+	if (!param || !value) {
+		return -1;
+	}
+
+	if (strcmp(param, "mirrordev")) {
+		nss_mirror_warn("Invalid param %s in mirror display cmd\n", param);
+		return -1;
+	}
+
+	strlcpy(dev_name, value, IFNAMSIZ);
+	if (dev_name[strlen(dev_name) - 1] == '\n') {
+		dev_name[strlen(dev_name) - 1] = '\0';
+	}
+	nss_mirror_info("dev name: %s\n", dev_name);
+
+	if (!strcmp(dev_name, "all")) {
+		nss_mirror_display_all_info();
+		return 0;
+	}
+
+	mirror_dev = dev_get_by_name(&init_net, dev_name);
+	if (!mirror_dev) {
+		nss_mirror_warn("Cannot find the %s netdevice\n", dev_name);
+		return -1;
+	}
+
+	/*
+	 * Verify the mirror netdevice.
+	 */
+	if (nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR) < 0) {
+		dev_put(mirror_dev);
+		nss_mirror_warn("No valid NSS FW interface for %s device\n", dev_name);
+		return -1;
+	}
+
+	nss_mirror_display_info(mirror_dev);
+
+	dev_put(mirror_dev);
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_enable_ingress_pmc_cmd()
+ *	API to parse ingress promiscuous enable command.
+ */
+static int nss_mirror_ctl_parse_enable_ingress_pmc_cmd(char *buffer)
+{
+	char *param, *value;
+	struct nss_ctx_instance *nss_ctx;
+	struct net_device *dev, *mirror_dev = NULL;
+	nss_tx_status_t status;
+	int32_t if_num, type = 0, mirror_if_num;
+	uint8_t param_num = NSS_MIRROR_ENABLE_INGRESS_PMC_NUM;
+
+	do {
+		param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+		if (!param || !value) {
+			return -1;
+		}
+
+		if (!strcmp(param, "devname")) {
+			if (nss_mirror_ctl_get_netdev_by_name(value, &dev) < 0) {
+				nss_mirror_warn("Error in getting devname net device.\n");
+				if (mirror_dev) {
+					dev_put(mirror_dev);
+				}
+				return -1;
+			}
+
+			if ((if_num = nss_cmn_get_interface_number_by_dev(dev)) < 0) {
+				nss_mirror_warn("No valid NSS FW interface for %s device\n", dev->name);
+				if (mirror_dev) {
+					dev_put(mirror_dev);
+				}
+				dev_put(dev);
+				return -1;
+			}
+			dev_put(dev);
+		} else if (!strcmp(param, "mirrordev")) {
+			if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+				nss_mirror_warn("Error in getting mirror net device.\n");
+				return -1;
+			}
+
+			/*
+			 * Verify the mirror netdevice.
+			 */
+			if ((mirror_if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+				nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+				dev_put(mirror_dev);
+				return -1;
+			}
+		} else {
+			if (mirror_dev) {
+				dev_put(mirror_dev);
+			}
+			nss_mirror_warn("Invalid param: %s in promiscuous enable cmd\n", value);
+			return -1;
+		}
+	} while (--param_num);
+
+	/*
+	 * Return if the mirror interface is in open state.
+	 */
+	if (mirror_dev->flags & IFF_UP) {
+		nss_mirror_warn("%s mirror interface is in open state\n", mirror_dev->name);
+		dev_put(mirror_dev);
+		return -1;
+	}
+
+	dev_put(mirror_dev);
+
+	nss_ctx = nss_mirror_get_context();
+	if (!nss_ctx) {
+		nss_mirror_warn("Invalid NSS context\n");
+		return -1;
+	}
+
+	type = nss_dynamic_interface_get_type(nss_ctx, if_num);
+
+	if (type == NSS_DYNAMIC_INTERFACE_TYPE_VAP) {
+		status = nss_wifi_vdev_set_next_hop(nss_wifi_get_context(), if_num, mirror_if_num);
+	} else if (if_num < NSS_MAX_PHYSICAL_INTERFACES) {
+		status = nss_phys_if_set_nexthop(nss_ctx, if_num, mirror_if_num);
+	} else {
+		nss_mirror_warn("Invalid nexthop interface:%d type:%d\n", if_num, type);
+		return -1;
+	}
+
+	if (status != NSS_TX_SUCCESS) {
+		nss_mirror_warn("Setting nexthop failed. if_num: %u, nexthop: %u\n", if_num, mirror_if_num);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_disable_ingress_pmc_cmd()
+ *	API to parse ingress promiscuous disable command.
+ */
+static int nss_mirror_ctl_parse_disable_ingress_pmc_cmd(char *buffer)
+{
+	char *param, *value;
+	struct nss_ctx_instance *nss_ctx;
+	struct net_device *dev;
+	nss_tx_status_t status;
+	int32_t if_num, type = 0;
+
+	param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+	if (!param || !value) {
+		return -1;
+	}
+
+	if (strcmp(param, "devname")) {
+		nss_mirror_warn("Invalid param: %s in promiscuous enable cmd\n", value);
+		return -1;
+	}
+
+	if (nss_mirror_ctl_get_netdev_by_name(value, &dev) < 0) {
+		nss_mirror_warn("Error in getting devname net device.\n");
+		return -1;
+	}
+
+	if ((if_num = nss_cmn_get_interface_number_by_dev(dev)) < 0) {
+		nss_mirror_warn("No valid NSS FW interface for %s device\n", dev->name);
+		dev_put(dev);
+		return -1;
+	}
+
+	dev_put(dev);
+
+	nss_ctx = nss_mirror_get_context();
+	if (!nss_ctx) {
+		nss_mirror_warn("Invalid NSS context\n");
+		return -1;
+	}
+
+	type = nss_dynamic_interface_get_type(nss_ctx, if_num);
+
+	if (type == NSS_DYNAMIC_INTERFACE_TYPE_VAP) {
+		status = nss_mirror_reset_if_nexthop(if_num);
+	} else if (if_num < NSS_MAX_PHYSICAL_INTERFACES) {
+		status = nss_phys_if_reset_nexthop(nss_ctx, if_num);
+	} else {
+		nss_mirror_warn("Reset nexthop failed for %d interface, type:%d\n", if_num, type);
+		return -1;
+	}
+
+	if (status != NSS_TX_SUCCESS) {
+		nss_mirror_warn("Re-setting nexthop failed for if_num: %u\n", if_num);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_destroy_cmd()
+ *	API to parse destroy command.
+ */
+static int nss_mirror_ctl_parse_destroy_cmd(char *buffer)
+{
+	struct net_device *mirror_dev;
+	char *param, *value;
+
+	param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+	if (!param || !value) {
+		return -1;
+	}
+
+	if (strcmp(param, "mirrordev")) {
+		nss_mirror_warn("Invalid param %s in mirror delete command\n", param);
+		return -1;
+	}
+
+	if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+		nss_mirror_warn("Error in getting mirror net device.\n");
+		return -1;
+	}
+
+	/*
+	 * Deconfigure mirror interface.
+	 */
+	if (nss_mirror_deconfigure_mirror(mirror_dev) < 0) {
+		nss_mirror_warn("Error in deconfiguring mirror interface: %s\n", mirror_dev->name);
+		dev_put(mirror_dev);
+		return -1;
+	}
+
+	dev_put(mirror_dev);
+
+	if (nss_mirror_destroy(mirror_dev)) {
+		nss_mirror_warn("Error in sending delete config to mirror interface: %s\n", mirror_dev->name);
+		dev_put(mirror_dev);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_config_param()
+ *	API to parse configure parameters.
+ */
+static int nss_mirror_ctl_parse_config_param(char *buffer, uint16_t *size_ptr,
+		enum nss_mirror_pkt_clone_point *pt_ptr, uint16_t *offset_ptr)
+{
+	uint8_t param_num = NSS_MIRROR_CONFIG_PARAM_NUM;
+	char *param, *value;
+
+	do {
+		param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+
+		/*
+		 * No configure parameter is given. Use the default configure values.
+		 */
+		if (!param || !value) {
+			return 0;
+		}
+
+		if (!strcmp(param, "mirror_size")) {
+			if (nss_mirror_ctl_convert_char_to_u16(value, size_ptr)) {
+				nss_mirror_warn("Not able to parse %s to u16\n", value);
+				return -1;
+			}
+		} else if (!strcmp(param, "mirror_point")) {
+			if (nss_mirror_ctl_convert_char_to_u32(value, pt_ptr)) {
+				nss_mirror_warn("Not able to parse %s to u32\n", value);
+				return -1;
+			}
+		} else if (!strcmp(param, "mirror_offset")) {
+			if (nss_mirror_ctl_convert_char_to_u16(value, offset_ptr)) {
+				nss_mirror_warn("Not able to parse %s to u16\n", value);
+				return -1;
+			}
+		} else {
+			nss_mirror_warn("Invalid param:%s in create cmd\n", param);
+			return -1;
+		}
+	} while (--param_num);
+
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_configure_cmd()
+ *	API to parse configure command.
+ */
+static int nss_mirror_ctl_parse_configure_cmd(char *buffer)
+{
+	struct net_device *mirror_dev;
+	char *param, *value;
+	enum nss_mirror_pkt_clone_point mirror_point = NSS_MIRROR_PKT_CLONE_POINT_DEFAULT;
+	uint16_t mirror_size = 0;
+	uint16_t mirror_offset = 0;
+
+	param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+	if (!param || !value) {
+		return -1;
+	}
+
+	if (strcmp(param, "mirrordev")) {
+		nss_mirror_warn("Invalid param %s in mirror configure command\n", param);
+		return -1;
+	}
+
+	if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+		nss_mirror_warn("Error in getting mirror net device.\n");
+		return -1;
+	}
+
+	if (nss_mirror_ctl_parse_config_param(buffer, &mirror_size,
+				&mirror_point, &mirror_offset)) {
+		dev_put(mirror_dev);
+		nss_mirror_warn("Error in parsing mirror configure command\n");
+		return -1;
+	}
+
+	nss_mirror_info("Mirror size: %d, mirror point: %d, mirror offset: %d\n",
+			mirror_size, mirror_point, mirror_offset);
+
+	if (nss_mirror_configure(mirror_dev, mirror_point,
+			mirror_size, mirror_offset)) {
+		nss_mirror_warn("Error in sending config message to %s mirror interface\n", mirror_dev->name);
+		dev_put(mirror_dev);
+		return -1;
+	}
+
+	dev_put(mirror_dev);
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_cmd()
+ *	API to parse mirror commands.
+ */
+static int32_t nss_mirror_ctl_parse_cmd(char *cmd)
+{
+	if (cmd == NULL) {
+		return NSS_MIRROR_CMD_UNKNOWN;
+	}
+
+	if (!strcmp(cmd, "create")) {
+		return NSS_MIRROR_CMD_CREATE;
+	}
+
+	if (!strcmp(cmd, "configure")) {
+		return NSS_MIRROR_CMD_CONFIGURE;
+	}
+
+	if (!strcmp(cmd, "enable")) {
+		return NSS_MIRROR_CMD_ENABLE;
+	}
+
+	if (!strcmp(cmd, "disable")) {
+		return NSS_MIRROR_CMD_DISABLE;
+	}
+
+	if (!strcmp(cmd, "display")) {
+		return NSS_MIRROR_CMD_DISPLAY;
+	}
+
+	if (!strcmp(cmd, "destroy")) {
+		return NSS_MIRROR_CMD_DESTROY;
+	}
+
+	if (!strcmp(cmd, "set_nexthop")) {
+		return NSS_MIRROR_CMD_SET_NEXTHOP;
+	}
+
+	if (!strcmp(cmd, "reset_nexthop")) {
+		return NSS_MIRROR_CMD_RESET_NEXTHOP;
+	}
+
+	if (!strcmp(cmd, "ingress_pmc_enable")) {
+		return NSS_MIRROR_CMD_INGRESS_PMC_ENABLE;
+	}
+
+	if (!strcmp(cmd, "ingress_pmc_disable")) {
+		return NSS_MIRROR_CMD_INGRESS_PMC_DISABLE;
+	}
+
+	if (!strcmp(cmd, "help")) {
+		return NSS_MIRROR_CMD_HELP;
+	}
+
+	nss_mirror_warn("Invalid string:%s in command\n", cmd);
+	return NSS_MIRROR_CMD_UNKNOWN;
+}
+
+/*
+ * nss_mirror_ctl_config_handler()
+ *	Mirror sysctl config handler.
+ */
+static int nss_mirror_ctl_config_handler(struct ctl_table *ctl, int write,
+		 void __user *buf, size_t *lenp, loff_t *ppos)
+{
+	char *buffer, *pfree;
+	char * nextarg;
+	int command, ret;
+	size_t count = *lenp;
+
+	/*
+	 * Find the string, return an error if not found
+	 */
+	ret = proc_dostring(ctl, write, buf, lenp, ppos);
+	if (ret || !write) {
+		return ret;
+	}
+
+	buffer = vzalloc(count + 1);
+	if (!buffer) {
+		nss_mirror_warn("%p: Dynamic allocation failed for input buffer\n", ctl);
+		return -ENOMEM;
+	}
+
+	pfree = buffer;
+
+	if (copy_from_user(buffer, buf, count)) {
+		vfree(pfree);
+		return -EFAULT;
+	}
+
+	nextarg = nss_mirror_ctl_read_nextarg(&buffer);
+	if (!nextarg) {
+		goto err;
+	}
+
+	if (nextarg[strlen(nextarg) - 1] == '\n') {
+		nextarg[strlen(nextarg) - 1] = '\0';
+	}
+
+	command = nss_mirror_ctl_parse_cmd(nextarg);
+	switch (command) {
+
+	case NSS_MIRROR_CMD_CREATE:
+	{
+		struct net_device *mirror_dev;
+		if (!(mirror_dev = nss_mirror_create())) {
+			nss_mirror_warn("%s Error in create cmd\n", __func__);
+			goto err;
+		}
+		break;
+	}
+
+	case NSS_MIRROR_CMD_CONFIGURE:
+	{
+		if (nss_mirror_ctl_parse_configure_cmd(buffer)) {
+			nss_mirror_warn("Error in parsing configure cmd\n");
+			goto err;
+		}
+		break;
+	}
+
+	case NSS_MIRROR_CMD_SET_NEXTHOP:
+	{
+		if (nss_mirror_ctl_parse_set_nexthop_cmd(buffer)) {
+			nss_mirror_warn("Error in parsing promiscuous rx enable cmd\n");
+			goto err;
+		}
+		break;
+	}
+
+	case NSS_MIRROR_CMD_RESET_NEXTHOP:
+	{
+		if (nss_mirror_ctl_parse_reset_nexthop_cmd(buffer)) {
+			nss_mirror_warn("Error in parsing promiscuous rx disable cmd\n");
+			goto err;
+		}
+		break;
+	}
+
+	case NSS_MIRROR_CMD_INGRESS_PMC_ENABLE:
+	{
+		if (nss_mirror_ctl_parse_enable_ingress_pmc_cmd(buffer)) {
+			nss_mirror_warn("Error in parsing set nexthop cmd\n");
+			goto err;
+		}
+		break;
+	}
+
+	case NSS_MIRROR_CMD_INGRESS_PMC_DISABLE:
+	{
+		if (nss_mirror_ctl_parse_disable_ingress_pmc_cmd(buffer)) {
+			nss_mirror_warn("Error in parsing reset nexthop cmd\n");
+			goto err;
+		}
+		break;
+	}
+
+	case NSS_MIRROR_CMD_DISABLE:
+	{
+		if (nss_mirror_ctl_parse_disable_cmd(buffer)) {
+			nss_mirror_warn("Error in parsing disable cmd\n");
+			goto err;
+		}
+		break;
+	}
+
+	case NSS_MIRROR_CMD_ENABLE:
+	{
+		if (nss_mirror_ctl_parse_enable_cmd(buffer)) {
+			nss_mirror_warn("Error in parsing enable cmd\n");
+			goto err;
+		}
+		break;
+	}
+
+	case NSS_MIRROR_CMD_DISPLAY:
+	{
+		if (nss_mirror_ctl_parse_display_cmd(buffer)) {
+			nss_mirror_warn("Error in parsing display cmd\n");
+			goto err;
+		}
+		break;
+	}
+
+	case NSS_MIRROR_CMD_DESTROY:
+	{
+		if (nss_mirror_ctl_parse_destroy_cmd(buffer)) {
+			nss_mirror_warn("Error in parsing destroy cmd\n");
+			goto err;
+		}
+		break;
+	}
+
+	case NSS_MIRROR_CMD_HELP:
+	{
+		nss_mirror_ctl_display_help();
+		break;
+	}
+
+	default:
+		nss_mirror_warn("Invalid input in command.\n");
+		goto err;
+	}
+
+	vfree(pfree);
+	return ret;
+err:
+	vfree(pfree);
+	return -EINVAL;
+}
+
+/*
+ * nss mirror sysctl table
+ */
+static struct ctl_table nss_mirror_table[] = {
+	{
+		.procname	= "config",
+		.data		= &nss_mirror_config_data,
+		.maxlen		= sizeof(nss_mirror_config_data),
+		.mode		= 0644,
+		.proc_handler	= &nss_mirror_ctl_config_handler,
+	},
+	{ }
+};
+
+/*
+ * nss mirror dir
+ */
+static struct ctl_table nss_mirror_root_dir[] = {
+	{
+		.procname		= "mirror",
+		.mode			= 0555,
+		.child			= nss_mirror_table,
+	},
+	{ }
+};
+
+/*
+ * nss mirror sysctl nss root dir
+ */
+static struct ctl_table nss_mirror_nss_root_dir[] = {
+	{
+		.procname		= "nss",
+		.mode			= 0555,
+		.child			= nss_mirror_root_dir,
+	},
+	{ }
+};
+
+/*
+ * nss mirror sysctl root dir
+ */
+static struct ctl_table nss_mirror_root[] = {
+	{
+		.procname		= "dev",
+		.mode			= 0555,
+		.child			= nss_mirror_nss_root_dir,
+	},
+	{ }
+};
+
+/*
+ * nss_mirror_ctl_register()
+ *	Register command line interface for mirror.
+ */
+int nss_mirror_ctl_register(void)
+{
+	nss_mirror_ctl_header = register_sysctl_table(nss_mirror_root);
+	if (!nss_mirror_ctl_header) {
+		nss_mirror_warn("Creating sysctl directory table header for mirror failed\n");
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * nss_mirror_ctl_unregister()
+ *	Un-register command line interface for mirror.
+ */
+void nss_mirror_ctl_unregister(void)
+{
+	if (nss_mirror_ctl_header) {
+		unregister_sysctl_table(nss_mirror_ctl_header);
+	}
+}
diff --git a/mirror/nss_mirror_ctl.h b/mirror/nss_mirror_ctl.h
new file mode 100644
index 0000000..0b0c2ec
--- /dev/null
+++ b/mirror/nss_mirror_ctl.h
@@ -0,0 +1,29 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2020, 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_mirror_ctl_register()
+ *	Register command line interface for mirror.
+ */
+extern int nss_mirror_ctl_register(void);
+
+/*
+ * nss_mirror_ctl_unregister()
+ *	Un-register command line interface for mirror.
+ */
+extern void nss_mirror_ctl_unregister(void);
diff --git a/mirror/nss_mirror_init.c b/mirror/nss_mirror_init.c
new file mode 100644
index 0000000..b918fb7
--- /dev/null
+++ b/mirror/nss_mirror_init.c
@@ -0,0 +1,61 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2020, 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.
+ ***************************************************************************
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/module.h>
+
+#include "nss_mirror_ctl.h"
+#include "nss_mirror.h"
+
+/*
+ * nss_mirror_module_init()
+ *	Initialize mirror module.
+ */
+static int __init nss_mirror_module_init(void)
+{
+	int ret = 0;
+
+	ret = nss_mirror_ctl_register();
+	if (ret) {
+		nss_mirror_warn("Mirror ctl registration failed\n");
+		return ret;
+	}
+
+	nss_mirror_info("Mirror module initialized\n");
+	return 0;
+}
+
+/*
+ * nss_mirror_module_exit()
+ *	De-initialize mirror module.
+ */
+static void __exit nss_mirror_module_exit(void)
+{
+	if (nss_mirror_destroy_all() < 0) {
+		nss_mirror_warn("Error in destroying all the configured mirror devices\n");
+	}
+	nss_mirror_ctl_unregister();
+	nss_mirror_info("Mirror module unloaded.\n");
+}
+
+module_init(nss_mirror_module_init);
+module_exit(nss_mirror_module_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("NSS MIRROR Client");