[qca-nss-clients] nss-lag client implementation

NSS LAG client is implemented to monitor LAG group creation
and configure LAG in NSS FW and also with SSDK to configure trunk port

Change-Id: I2cc28ca96514c3c867bb0cc0061d7e2fdea2be03
Signed-off-by: Bhaskar Valaboju <bvalaboj@codeaurora.org>
diff --git a/Makefile b/Makefile
index 2724242..4a3ff55 100644
--- a/Makefile
+++ b/Makefile
@@ -46,6 +46,9 @@
 #IPsecmgr
 obj-y+= ipsecmgr/
 
+#LAG Manager
+obj-y+= lag/
+
 # MAP-T manager
 ifeq ($(findstring 3.4, $(KERNELVERSION)),)
 obj-y+=map/map-t/
diff --git a/lag/Makefile b/lag/Makefile
new file mode 100644
index 0000000..3024e1e
--- /dev/null
+++ b/lag/Makefile
@@ -0,0 +1,14 @@
+# Makefile for the lagmgr client
+
+ifeq ($(SoC), ipq807x)
+ccflags-y += -DNSS_LAG_PPE_SUPPORT
+endif
+
+ccflags-y := -I$(obj) -I$(obj)/..
+ccflags-y += -DNSS_CLIENT_BUILD_ID="$(BUILD_ID)"
+ccflags-y += -DNSS_LAG_MGR_DEBUG_LEVEL=0
+
+obj-m += qca-nss-lag-mgr.o
+qca-nss-lag-mgr-objs := nss_lag.o
+
+obj ?= .
diff --git a/lag/nss_lag.c b/lag/nss_lag.c
new file mode 100644
index 0000000..a6b33bc
--- /dev/null
+++ b/lag/nss_lag.c
@@ -0,0 +1,368 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2016-2017, 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_lag.c
+ *	HLOS to NSS LAG Interface manager
+ */
+#include <linux/etherdevice.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/if_bonding.h>
+#if defined(NSS_LAG_PPE_SUPPORT)
+#include <fal/fal_trunk.h>
+#endif
+
+#include <nss_api_if.h>
+
+/*
+ * Compile messages for dynamic enable/disable
+ */
+#if defined(CONFIG_DYNAMIC_DEBUG)
+#define nss_lag_warn(s, ...) \
+		pr_warn("%s[%d]:" s, __func__, __LINE__, ##__VA_ARGS__)
+#define nss_lag_info(s, ...) \
+		pr_notice("%s[%d]:" s, __func__, __LINE__, ##__VA_ARGS__)
+#else /* CONFIG_DYNAMIC_DEBUG */
+/*
+ * Statically compile messages at different levels
+ */
+#if (NSS_LAG_MGR_DEBUG_LEVEL < 2)
+#define nss_lag_warn(s, ...)
+#else
+#define nss_lag_warn(s, ...) \
+		pr_warn("%s[%d]:" s, __func__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#if (NSS_LAG_MGR_DEBUG_LEVEL < 3)
+#define nss_lag_info(s, ...)
+#else
+#define nss_lag_info(s, ...) \
+		pr_notice("%s[%d]:" s, __func__, __LINE__, ##__VA_ARGS__)
+#endif
+#endif /* CONFIG_DYNAMIC_DEBUG */
+
+#define NSS_LAG_MAX_BOND_DEVICES 2
+#define NSS_LAG_BOND_ID_INVALID	0xFFFFFFFF
+#define NSS_LAG_BOND_DEV_STATE_ACTIVE 1
+#define NSS_LAG_BOND_DEV_STATE_INACTIVE 2
+
+/*
+ * LAG manager private structure
+ */
+struct nss_bond_pvt {
+	int32_t bond_dev_state;	/* Active or Inactive */
+	uint32_t members;	/* Enslaved devices:
+					   bit 0 - eth0,
+					   bit1 - eth1 ... */
+} bond_pvt[NSS_LAG_MAX_BOND_DEVICES];
+
+DEFINE_SPINLOCK(nss_lag_spinlock);
+
+/*
+ * NSS Context for LAG function
+ */
+static void *nss_lag_nss_context;	/* Registration for LAG */
+
+#if defined(NSS_LAG_PPE_SUPPORT)
+/*
+ * nss_lag_update_ppe()
+ *	Update and/or Configure LAG in Hardware when there is change in bonding
+ *	slave's state
+ */
+static int nss_lag_update_ppe(int32_t bondid, int32_t slave_ifnum,
+		enum nss_lag_state_change_ev slave_event)
+{
+	struct nss_bond_pvt *pvt;
+	int32_t enable = 1;
+
+	if (bondid > NSS_LAG_MAX_BOND_DEVICES) {
+		nss_lag_warn("bondid is wrong: bondid=%d\n", bondid);
+		return -1;
+	}
+
+	if (slave_ifnum > NSS_MAX_PHYSICAL_INTERFACES) {
+		nss_lag_warn("HW LAG Supports only Physical ports: slave=%d\n",
+				slave_ifnum);
+		return -1;
+	}
+
+	spin_lock(&nss_lag_spinlock);
+
+	pvt = &bond_pvt[bondid];
+
+	if (slave_event == NSS_LAG_ENSLAVE)
+		pvt->members |= (1 << (slave_ifnum - 1));
+	else
+		pvt->members &= ~(1 << (slave_ifnum - 1));
+
+	if (pvt->members)
+		pvt->bond_dev_state = NSS_LAG_BOND_DEV_STATE_ACTIVE;
+	else {
+		pvt->bond_dev_state = NSS_LAG_BOND_DEV_STATE_INACTIVE;
+		enable = 0;
+	}
+
+	spin_unlock(&nss_lag_spinlock);
+
+	return fal_trunk_group_set(0, bondid, enable, pvt->members);
+}
+#endif
+
+/*
+ * nss_lag_state_callback()
+ *	Call back function for nss LAG State
+ */
+static void nss_lag_state_callback(void *ctx, struct nss_lag_msg *nm)
+{
+#if defined(NSS_LAG_PPE_SUPPORT)
+	int32_t lagid, bondid, slave_ifnum;
+	enum nss_lag_state_change_ev slave_state;
+#endif
+	if (nm->cm.response != NSS_CMN_RESPONSE_ACK) {
+		nss_lag_warn("LAG State update Request failed, error=%d\n",
+				nm->cm.error);
+		return;
+	}
+
+	nss_lag_info("LAG State update Request Passed\n");
+#if defined(NSS_LAG_PPE_SUPPORT)
+	bondid = nm->cm.interface - NSS_LAG0_INTERFACE_NUM;
+	slave_ifnum = nm->msg.state.interface;
+	slave_state = nm->msg.state.event;
+
+	/* Call SSDK API to update trunk Port details */
+	ret = nss_lag_update_ppe(bondid, slave_ifnum, slave_state);
+	if (ret)
+		nss_lag_warn("Couldn't update PPE: ret =  %d\n", ret);
+#endif
+}
+
+/*
+ * nss_lag_send_lag_state()
+ *	Send the currnet LAG state of a physical interface that has changed
+ *	state in the bonding driver.
+ */
+static nss_tx_status_t nss_lag_send_lag_state(struct nss_ctx_instance *nss_ctx,
+		struct net_device *master, struct net_device *slave,
+		enum nss_lag_state_change_ev slave_state)
+{
+	int32_t lagid = 0;
+	int32_t bondid = 0;
+	int32_t slave_ifnum;
+	nss_tx_status_t nss_tx_status;
+	struct nss_lag_msg nm;
+	struct nss_lag_state_change *nlsc = NULL;
+	int ret;
+
+	nss_lag_info("Send LAG update for: %p (%s)\n", slave, slave->name);
+
+	memset(&nm, 0, sizeof(nm));
+
+	/*
+	 * Figure out the aggregation id of this slave
+	 */
+	bondid = bond_get_id(master);
+	if (bondid == NSS_LAG_BOND_ID_INVALID) {
+		nss_lag_warn("Invalid LAG group id 0x%x\n", bondid);
+		return NSS_TX_FAILURE_BAD_PARAM;
+	}
+
+	lagid = bondid + NSS_LAG0_INTERFACE_NUM;
+
+	slave_ifnum = nss_cmn_get_interface_number_by_dev(slave);
+	if (slave_ifnum < 0)
+		return NSS_TX_FAILURE_BAD_PARAM;
+
+	nss_lag_info("slave_state=%d, slave_ifnum=%d, bondid=%d\n",
+			slave_state, slave_ifnum, bondid);
+
+	/*
+	 * Construct a message to the NSS to update it
+	 */
+	nss_lag_msg_init(&nm, lagid,
+			 NSS_TX_METADATA_LAG_STATE_CHANGE,
+			 sizeof(struct nss_lag_state_change),
+			 nss_lag_state_callback, NULL);
+
+	nlsc = &nm.msg.state;
+	nlsc->event = slave_state;
+	nlsc->interface = slave_ifnum;
+
+	nss_tx_status = nss_lag_tx(nss_ctx, &nm);
+	if (nss_tx_status != NSS_TX_SUCCESS) {
+		nss_lag_warn("%p: Send LAG update failed, status: %d\n", slave,
+				nss_tx_status);
+		return NSS_TX_FAILURE;
+	}
+	nss_lag_info("%p: Send LAG update success\n", slave);
+
+	return NSS_TX_SUCCESS;
+}
+
+/*
+ * nss_lag_event_cb()
+ *	Handle LAG event from the NSS driver
+ */
+static void nss_lag_event_cb(void *if_ctx, struct nss_lag_msg *msg)
+{
+	/*
+	 * TODO: Figure out if there is anything we need to do here,
+	 * the old CM did nothing..
+	 */
+	switch (msg->cm.type) {
+	default:
+		nss_lag_warn("Unknown LAG event from NSS: %d", msg->cm.type);
+		break;
+	}
+}
+
+/*
+ * nss_lag_update_slave()
+ *	Monitor bonding slaves and send updates to hardware
+ */
+static int nss_lag_update_slave(struct netdev_notifier_info *info)
+{
+	struct net_device *slave_dev = netdev_notifier_info_to_dev(info);
+	struct netdev_notifier_changeupper_info *cu_info = info;
+	uint32_t slave_ifnum;
+
+	/*
+	 * Check if the master pointer is valid
+	 */
+	if (!cu_info->master)
+		return NOTIFY_DONE;
+
+	if (!netif_is_bond_slave(slave_dev))
+		return NOTIFY_DONE;
+
+	/*
+	 * Only care about interfaces known by NSS
+	 */
+	slave_ifnum = nss_cmn_get_interface_number_by_dev(slave_dev);
+	if (slave_ifnum < 0)
+		return NOTIFY_DONE;
+
+	if (cu_info->linking) {
+		nss_lag_info("Interface %s added in LAG: %s\n" ,
+				slave_dev->name, cu_info->upper_dev->name);
+		if (nss_lag_send_lag_state(nss_lag_nss_context,
+			cu_info->upper_dev, slave_dev, NSS_LAG_ENSLAVE)) {
+			nss_lag_warn("Ensalve %s dev failed\n",
+					slave_dev->name);
+			return NOTIFY_BAD;
+		}
+
+		return NOTIFY_DONE;
+	}
+
+	nss_lag_info("Interface %s removed from LAG: %s\n",
+			slave_dev->name, cu_info->upper_dev->name);
+	if (nss_lag_send_lag_state(nss_lag_nss_context,
+		cu_info->upper_dev, slave_dev, NSS_LAG_RELEASE)) {
+		nss_lag_warn("Deslave %s dev failed\n", slave_dev->name);
+		return NOTIFY_BAD;
+	}
+
+	return NOTIFY_DONE;
+}
+
+/*
+ * nss_lag_netdevice_event()
+ *	Handle events received from network stack
+ */
+static int nss_lag_netdevice_event(struct notifier_block *unused,
+		unsigned long event, void *ptr)
+{
+	struct netdev_notifier_info *info = (struct netdev_notifier_info *) ptr;
+
+	switch (event) {
+	case NETDEV_CHANGEUPPER:
+		return nss_lag_update_slave(info);
+	}
+	return NOTIFY_DONE;
+}
+
+/* register netdev notifier callback */
+static struct notifier_block nss_lag_netdevice __read_mostly = {
+	.notifier_call = nss_lag_netdevice_event,
+};
+
+/*
+ * nss_lag_init()
+ *	Initialize NSS LAG client
+ */
+int __init nss_lag_init(void)
+{
+	int ret;
+	/*
+	 * Register Link Aggregation interfaces with NSS driver
+	 * assuming all four LAGs are on same interface.
+	 */
+	nss_lag_nss_context = nss_register_lag_if(NSS_LAG0_INTERFACE_NUM, NULL,
+						nss_lag_event_cb, NULL);
+	if (!nss_lag_nss_context) {
+		nss_lag_info("Failed to register LAG interface with NSS\n");
+		return -1;
+	}
+
+	(void)nss_register_lag_if(NSS_LAG1_INTERFACE_NUM, NULL,
+			nss_lag_event_cb, NULL);
+	(void)nss_register_lag_if(NSS_LAG2_INTERFACE_NUM, NULL,
+			nss_lag_event_cb, NULL);
+	(void)nss_register_lag_if(NSS_LAG3_INTERFACE_NUM, NULL,
+			nss_lag_event_cb, NULL);
+
+	ret = register_netdevice_notifier(&nss_lag_netdevice);
+	if (ret) {
+		nss_lag_warn("Failed to register NETDEV notifier, error=%d\n",
+				ret);
+
+		nss_unregister_lag_if(NSS_LAG0_INTERFACE_NUM);
+		nss_unregister_lag_if(NSS_LAG1_INTERFACE_NUM);
+		nss_unregister_lag_if(NSS_LAG2_INTERFACE_NUM);
+		nss_unregister_lag_if(NSS_LAG3_INTERFACE_NUM);
+	} else
+		nss_lag_info("LAG Manager Installed\n");
+
+	return ret;
+}
+
+/*
+ * nss_lag_exit()
+ *	Cleanup NSS LAG client and exit
+ */
+void __exit nss_lag_exit(void)
+{
+	unregister_netdevice_notifier(&nss_lag_netdevice);
+
+	/*
+	 * Unregister Link Aggregation interfaces with NSS driver
+	 */
+	nss_unregister_lag_if(NSS_LAG0_INTERFACE_NUM);
+	nss_unregister_lag_if(NSS_LAG1_INTERFACE_NUM);
+	nss_unregister_lag_if(NSS_LAG2_INTERFACE_NUM);
+	nss_unregister_lag_if(NSS_LAG3_INTERFACE_NUM);
+
+	nss_lag_info("LAG Manager Removed\n");
+}
+
+module_init(nss_lag_init);
+module_exit(nss_lag_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("NSS LAG Client");