lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Tue,  6 Oct 2015 14:09:45 +1100
From:	Gavin Shan <gwshan@...ux.vnet.ibm.com>
To:	netdev@...r.kernel.org
Cc:	benh@...nel.crashing.org, davem@...emloft.net,
	Gavin Shan <gwshan@...ux.vnet.ibm.com>
Subject: [PATCH RFC v1 3/7] net/ncsi: Manage NCSI device

There are multiple packages and channels connecting to NCSI enabled
interface. The available packages and channels are probed with NCSI
packets when NCSI device is started. Among the available packages
and channels, only one channel should be selected to be active to
provide service. When the active channel enters to failure state,
we need choose another channel as active one to do failover.

This implements the state machine to support above tasks. Also, the
public interfaces are exposed for NIC driver to manage NCSI device.

Signed-off-by: Gavin Shan <gwshan@...ux.vnet.ibm.com>
---
 include/net/ncsi.h     |  38 ++++
 net/ncsi/internal.h    |   1 +
 net/ncsi/ncsi-aen.c    |   9 +-
 net/ncsi/ncsi-manage.c | 539 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 584 insertions(+), 3 deletions(-)

diff --git a/include/net/ncsi.h b/include/net/ncsi.h
index 2eecfbd..6041ca3 100644
--- a/include/net/ncsi.h
+++ b/include/net/ncsi.h
@@ -3,6 +3,17 @@
 
 #include <uapi/linux/ncsi.h>
 
+/*
+ * The NCSI device states seen from external. More NCSI device states are
+ * only visible internally (in net/ncsi/internal.h). When the NCSI device
+ * is registered, it's in ncsi_dev_state_registered state. The state
+ * ncsi_dev_state_start is used to drive to choose active package and
+ * channel. After that, its state is changed to ncsi_dev_state_functional.
+ *
+ * The state ncsi_dev_state_stop helps to shut down the currently active
+ * package and channel while ncsi_dev_state_config helps to reconfigure
+ * them.
+ */
 enum {
 	ncsi_dev_state_registered	= 0x0000,
 	ncsi_dev_state_functional	= 0x0100,
@@ -18,4 +29,31 @@ struct ncsi_dev {
 	void			(*nd_handler)(struct ncsi_dev *ndev);
 };
 
+#ifdef CONFIG_NET_NCSI
+struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
+				   void (*notifier)(struct ncsi_dev *nd));
+int ncsi_start_dev(struct ncsi_dev *nd);
+int ncsi_stop_dev(struct ncsi_dev *nd);
+void ncsi_unregister_dev(struct ncsi_dev *nd);
+#else /* !CONFIG_NET_NCSI */
+static inline struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
+						 void (*notifier)(struct ncsi_dev *nd))
+{
+	return NULL;
+}
+
+static inline int ncsi_start_dev(struct ncsi_dev *nd)
+{
+	return -ENOTTY;
+}
+
+static inline int ncsi_stop_dev(struct ncsi_dev *nd)
+{
+	return -ENOTTY;
+}
+
+void inline ncsi_unregister_dev(struct ncsi_dev *nd)
+{
+}
+#endif /* CONFIG_NET_NCSI */
 #endif /* __NET_NCSI_H */
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index 9262717..02cab91 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -149,6 +149,7 @@ void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
 struct ncsi_req *ncsi_alloc_req(struct ncsi_dev_priv *ndp);
 void ncsi_free_req(struct ncsi_req *nr, bool check, bool timeout);
 struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
+int ncsi_config_dev(struct ncsi_dev *nd);
 
 /* Packet handlers */
 int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c
index f5f4d2f..89433bc 100644
--- a/net/ncsi/ncsi-aen.c
+++ b/net/ncsi/ncsi-aen.c
@@ -60,6 +60,7 @@ static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
 static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
 				struct ncsi_aen_pkt_hdr *h)
 {
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
 	struct ncsi_aen_lsc_pkt *lsc;
 	struct ncsi_channel *nc;
 	struct ncsi_channel_mode *ncm;
@@ -88,7 +89,7 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
 	 * we have to choose another channel to be active one.
 	 */
 	ndp->ndp_flags |= NCSI_DEV_PRIV_FLAG_CHANGE_ACTIVE;
-	/* FIXME: Stop active channel and choose another one */
+	ncsi_stop_dev(nd);
 
 	return 0;
 }
@@ -96,6 +97,7 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
 static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
 			       struct ncsi_aen_pkt_hdr *h)
 {
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
 	struct ncsi_channel *nc;
 	int ret;
 
@@ -113,7 +115,7 @@ static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
 	    ndp->ndp_active_channel != nc)
 		return 0;
 
-	/* FIXME: Reconfigure active channel */
+	ncsi_config_dev(nd);
 
 	return 0;
 }
@@ -121,6 +123,7 @@ static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
 static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
 				   struct ncsi_aen_pkt_hdr *h)
 {
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
 	struct ncsi_channel *nc;
 	struct ncsi_channel_mode *ncm;
 	struct ncsi_aen_hncdsc_pkt *hncdsc;
@@ -149,7 +152,7 @@ static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
 	 * is down on the active channel.
 	 */
 	ndp->ndp_flags |= NCSI_DEV_PRIV_FLAG_CHANGE_ACTIVE;
-	/* FIXME: Stop and choose another channel as active one */
+	ncsi_stop_dev(nd);
 
 	return 0;
 }
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index 765217c..5d66b6f 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -18,6 +18,7 @@
 #include <net/net_namespace.h>
 #include <net/sock.h>
 
+#include "ncsi-pkt.h"
 #include "internal.h"
 
 LIST_HEAD(ncsi_dev_list);
@@ -350,6 +351,8 @@ void ncsi_free_req(struct ncsi_req *nr, bool check, bool timeout)
 	nr->nr_used = false;
 	spin_unlock(&ndp->ndp_req_lock);
 
+	if (check && cmd && atomic_dec_return(&ndp->ndp_pending_reqs) == 0)
+		schedule_work(&ndp->ndp_work);
 	/* Release command and response */
 	consume_skb(cmd);
 	consume_skb(rsp);
@@ -366,3 +369,539 @@ struct ncsi_dev *ncsi_find_dev(struct net_device *dev)
 
 	return NULL;
 }
+
+static int ncsi_select_active_channel(struct ncsi_dev_priv *ndp)
+{
+	struct ncsi_package *np;
+	struct ncsi_channel *nc;
+
+	/* For now, we simply choose the first valid channel as active one.
+	 * There might be more factors, like the channel's capacity, can
+	 * be considered to pick the active channel in future.
+	 */
+	NCSI_FOR_EACH_PACKAGE(ndp, np) {
+		NCSI_FOR_EACH_CHANNEL(np, nc) {
+			ndp->ndp_active_package = np;
+			ndp->ndp_active_channel = nc;
+			return 0;
+		}
+	}
+
+	return -ENXIO;
+}
+
+static void ncsi_dev_config(struct ncsi_dev_priv *ndp)
+{
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
+	struct net_device *dev = nd->nd_dev;
+	struct ncsi_package *np = ndp->ndp_active_package;
+	struct ncsi_channel *nc = ndp->ndp_active_channel;
+	struct ncsi_cmd_arg nca;
+	unsigned char index;
+	int ret;
+
+	nca.nca_ndp = ndp;
+	nca.nca_nlh = NULL;
+
+	/* When we're reconfiguring the active channel, the active package
+	 * should be selected and the old setting on the active channel
+	 * should be cleared.
+	 */
+	switch (nd->nd_state) {
+	case ncsi_dev_state_config:
+	case ncsi_dev_state_config_sp:
+		atomic_set(&ndp->ndp_pending_reqs, 1);
+
+		/* Select the specific package */
+		nca.nca_type = NCSI_PKT_CMD_SP;
+		nca.nca_bytes[0] = 1;
+		nca.nca_package = np->np_id;
+		nca.nca_channel = 0x1f;
+		ret = ncsi_xmit_cmd(&nca);
+		if (ret)
+			goto error;
+
+		nd->nd_state = ncsi_dev_state_config_cis;
+		break;
+	case ncsi_dev_state_config_cis:
+		atomic_set(&ndp->ndp_pending_reqs, 1);
+
+		/* Clear initial state */
+		nca.nca_type = NCSI_PKT_CMD_CIS;
+		nca.nca_package = np->np_id;
+		nca.nca_channel = nc->nc_id;
+		ret = ncsi_xmit_cmd(&nca);
+		if (ret)
+			goto error;
+
+		nd->nd_state = ncsi_dev_state_config_sma;
+		break;
+	case ncsi_dev_state_config_sma:
+	case ncsi_dev_state_config_ebf:
+	case ncsi_dev_state_config_ecnt:
+	case ncsi_dev_state_config_ec:
+	case ncsi_dev_state_config_gls:
+		atomic_set(&ndp->ndp_pending_reqs, 1);
+
+		nca.nca_package = np->np_id;
+		nca.nca_channel = nc->nc_id;
+
+		/* Use first entry in unicast filter table. Note that
+		 * the MAC filter table starts from entry 1 instead of
+		 * 0.
+		 */
+		if (nd->nd_state == ncsi_dev_state_config_sma) {
+			nca.nca_type = NCSI_PKT_CMD_SMA;
+			for (index = 0; index < 6; index++)
+				nca.nca_bytes[index] = dev->dev_addr[index];
+			nca.nca_bytes[6] = 0x1;
+			nca.nca_bytes[7] = 0x1;
+			nd->nd_state = ncsi_dev_state_config_ebf;
+		} else if (nd->nd_state == ncsi_dev_state_config_ebf) {
+			nca.nca_type = NCSI_PKT_CMD_EBF;
+			nca.nca_dwords[0] = nc->nc_caps[NCSI_CAP_BC].ncc_cap;
+			nd->nd_state = ncsi_dev_state_config_ecnt;
+		} else if (nd->nd_state == ncsi_dev_state_config_ecnt) {
+			nca.nca_type = NCSI_PKT_CMD_ECNT;
+			nd->nd_state = ncsi_dev_state_config_ec;
+		} else if (nd->nd_state == ncsi_dev_state_config_ec) {
+			nca.nca_type = NCSI_PKT_CMD_EC;
+			nd->nd_state = ncsi_dev_state_config_gls;
+		} else if (nd->nd_state == ncsi_dev_state_config_gls) {
+			nca.nca_type = NCSI_PKT_CMD_GLS;
+			nd->nd_state = ncsi_dev_state_config_done;
+		}
+
+		ret = ncsi_xmit_cmd(&nca);
+		if (ret)
+			goto error;
+
+		break;
+	case ncsi_dev_state_config_done:
+		nd->nd_state = ncsi_dev_state_functional;
+		nd->nd_link_up = 0;
+		if (nc->nc_modes[NCSI_MODE_LINK].ncm_data[2] & 0x1)
+			nd->nd_link_up = 1;
+
+		if (!(ndp->ndp_flags & NCSI_DEV_PRIV_FLAG_CHANGE_ACTIVE))
+			nd->nd_handler(nd);
+		ndp->ndp_flags &= ~NCSI_DEV_PRIV_FLAG_CHANGE_ACTIVE;
+
+		break;
+	default:
+		pr_debug("%s: Unrecognized NCSI dev state 0x%x\n",
+			 __func__, nd->nd_state);
+		return;
+	}
+
+	return;
+
+error:
+	nd->nd_state = ncsi_dev_state_functional;
+	nd->nd_link_up = 0;
+	ndp->ndp_flags &= ~NCSI_DEV_PRIV_FLAG_CHANGE_ACTIVE;
+	nd->nd_handler(nd);
+}
+
+static void ncsi_dev_start(struct ncsi_dev_priv *ndp)
+{
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
+	struct ncsi_package *np;
+	struct ncsi_channel *nc;
+	struct ncsi_cmd_arg nca;
+	unsigned char index;
+	int ret;
+
+	nca.nca_ndp = ndp;
+	nca.nca_nlh = NULL;
+	switch (nd->nd_state) {
+	case ncsi_dev_state_start:
+		nd->nd_state = ncsi_dev_state_start_deselect;
+		/* Fall through */
+	case ncsi_dev_state_start_deselect:
+		atomic_set(&ndp->ndp_pending_reqs, 8);
+
+		/* Deselect all possible packages */
+		nca.nca_type = NCSI_PKT_CMD_DP;
+		nca.nca_channel = 0x1f;
+		for (index = 0; index < 8; index++) {
+			nca.nca_package = index;
+			ret = ncsi_xmit_cmd(&nca);
+			if (ret)
+				goto error;
+		}
+
+		nd->nd_state = ncsi_dev_state_start_package;
+		break;
+	case ncsi_dev_state_start_package:
+		atomic_set(&ndp->ndp_pending_reqs, 16);
+
+		/* Select all possible packages */
+		nca.nca_type = NCSI_PKT_CMD_SP;
+		nca.nca_bytes[0] = 1;
+		nca.nca_channel = 0x1f;
+		for (index = 0; index < 8; index++) {
+			nca.nca_package = index;
+			ret = ncsi_xmit_cmd(&nca);
+			if (ret)
+				goto error;
+		}
+
+		/* Disable all possible packages */
+		nca.nca_type = NCSI_PKT_CMD_DP;
+		for (index = 0; index < 8; index++) {
+			nca.nca_package = index;
+			ret = ncsi_xmit_cmd(&nca);
+			if (ret)
+				goto error;
+		}
+
+		nd->nd_state = ncsi_dev_state_start_channel;
+		break;
+	case ncsi_dev_state_start_channel:
+		/* The available packages should have been detected. To
+		 * iterate every package to probe its channels.
+		 */
+		if (!ndp->ndp_active_package) {
+			ndp->ndp_active_package = list_first_or_null_rcu(
+				&ndp->ndp_packages, struct ncsi_package,
+				np_node);
+			if (!ndp->ndp_active_package)
+				goto error;
+		} else {
+			if (list_is_last(&ndp->ndp_active_package->np_node,
+					 &ndp->ndp_packages)) {
+				nd->nd_state = ncsi_dev_state_start_active;
+				goto choose_active_channel;
+			}
+
+			ndp->ndp_active_package = list_entry_rcu(
+				ndp->ndp_active_package->np_node.next,
+				struct ncsi_package, np_node);
+		}
+		/* Fall through */
+	case ncsi_dev_state_start_sp:
+		atomic_set(&ndp->ndp_pending_reqs, 1);
+
+		/* Select the specific package */
+		nca.nca_type = NCSI_PKT_CMD_SP;
+		nca.nca_bytes[0] = 1;
+		nca.nca_package = ndp->ndp_active_package->np_id;
+		nca.nca_channel = 0x1f;
+		ret = ncsi_xmit_cmd(&nca);
+		if (ret)
+			goto error;
+
+		nd->nd_state = ncsi_dev_state_start_cis;
+		break;
+	case ncsi_dev_state_start_cis:
+		atomic_set(&ndp->ndp_pending_reqs, 0x20);
+
+		/* Clear initial state */
+		nca.nca_type = NCSI_PKT_CMD_CIS;
+		nca.nca_package = ndp->ndp_active_package->np_id;
+		for (index = 0; index < 0x20; index++) {
+			nca.nca_channel = index;
+			ret = ncsi_xmit_cmd(&nca);
+			if (ret)
+				goto error;
+		}
+
+		nd->nd_state = ncsi_dev_state_start_gvi;
+		break;
+	case ncsi_dev_state_start_gvi:
+	case ncsi_dev_state_start_gc:
+		/* The available channels of the active package should have
+		 * been populated.
+		 */
+		np = ndp->ndp_active_package;
+		atomic_set(&ndp->ndp_pending_reqs,
+			   atomic_read(&np->np_channel_num));
+
+		/* Get version information or get capacity */
+		if (nd->nd_state == ncsi_dev_state_start_gvi)
+			nca.nca_type = NCSI_PKT_CMD_GVI;
+		else
+			nca.nca_type = NCSI_PKT_CMD_GC;
+
+		nca.nca_package = np->np_id;
+		NCSI_FOR_EACH_CHANNEL(np, nc) {
+			nca.nca_channel = nc->nc_id;
+			ret = ncsi_xmit_cmd(&nca);
+			if (ret)
+				goto error;
+		}
+
+		if (nd->nd_state == ncsi_dev_state_start_gvi)
+			nd->nd_state = ncsi_dev_state_start_gc;
+		else
+			nd->nd_state = ncsi_dev_state_start_dp;
+		break;
+	case ncsi_dev_state_start_dp:
+		atomic_set(&ndp->ndp_pending_reqs, 1);
+
+		/* Deselect the active package */
+		nca.nca_type = NCSI_PKT_CMD_DP;
+		nca.nca_package = ndp->ndp_active_package->np_id;
+		nca.nca_channel = 0x1f;
+		ret = ncsi_xmit_cmd(&nca);
+		if (ret)
+			goto error;
+
+		nd->nd_state = ncsi_dev_state_start_channel;
+		break;
+	case ncsi_dev_state_start_active:
+choose_active_channel:
+		/* All packages and channels should have been populated. Also,
+		 * the information for all channels should have been retrieved.
+		 */
+		ndp->ndp_active_package = NULL;
+		ncsi_select_active_channel(ndp);
+		if (!ndp->ndp_active_package ||
+		    !ndp->ndp_active_channel)
+			goto error;
+
+		/* To configure the active channel */
+		nd->nd_state = ncsi_dev_state_config_sma;
+		ncsi_dev_config(ndp);
+	default:
+		pr_debug("%s: Unrecognized NCSI dev state 0x%x\n",
+			 __func__, nd->nd_state);
+	}
+
+	return;
+
+error:
+	ndp->ndp_flags &= ~NCSI_DEV_PRIV_FLAG_CHANGE_ACTIVE;
+	nd->nd_state = ncsi_dev_state_functional;
+	nd->nd_link_up = 0;
+	nd->nd_handler(nd);
+}
+
+static void ncsi_dev_stop(struct ncsi_dev_priv *ndp)
+{
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
+	struct ncsi_package *np, *tmp;
+	struct ncsi_channel *nc;
+	struct ncsi_cmd_arg nca;
+	int ret;
+
+	nca.nca_ndp = ndp;
+	nca.nca_nlh = NULL;
+	switch (nd->nd_state) {
+	case ncsi_dev_state_stop:
+		/* If there're no active channel, we're done */
+		if (!ndp->ndp_active_channel) {
+			nd->nd_state = ncsi_dev_state_stop_done;
+			goto done;
+		}
+
+		nd->nd_state = ncsi_dev_state_stop_select;
+		/* Fall through */
+	case ncsi_dev_state_stop_select:
+	case ncsi_dev_state_stop_dcnt:
+	case ncsi_dev_state_stop_dc:
+	case ncsi_dev_state_stop_deselect:
+		atomic_set(&ndp->ndp_pending_reqs, 1);
+
+		np = ndp->ndp_active_package;
+		nc = ndp->ndp_active_channel;
+		nca.nca_package = np->np_id;
+		if (nd->nd_state == ncsi_dev_state_stop_select) {
+			nca.nca_type = NCSI_PKT_CMD_SP;
+			nca.nca_channel = 0x1f;
+			nca.nca_bytes[0] = 1;
+			nd->nd_state = ncsi_dev_state_stop_dcnt;
+		} else if (nd->nd_state == ncsi_dev_state_stop_dcnt) {
+			nca.nca_type = NCSI_PKT_CMD_DCNT;
+			nca.nca_channel = nc->nc_id;
+			nd->nd_state = ncsi_dev_state_stop_dc;
+		} else if (nd->nd_state == ncsi_dev_state_stop_dc) {
+			nca.nca_type = NCSI_PKT_CMD_DC;
+			nca.nca_channel = nc->nc_id;
+			nca.nca_bytes[0] = 1;
+			nd->nd_state = ncsi_dev_state_stop_deselect;
+		} else if (nd->nd_state == ncsi_dev_state_stop_deselect) {
+			nca.nca_type = NCSI_PKT_CMD_DP;
+			nca.nca_channel = 0x1f;
+			nd->nd_state = ncsi_dev_state_stop_done;
+		}
+
+		ret = ncsi_xmit_cmd(&nca);
+		if (ret) {
+			nd->nd_state = ncsi_dev_state_stop_done;
+			goto done;
+		}
+
+		break;
+	case ncsi_dev_state_stop_done:
+done:
+		spin_lock(&ndp->ndp_package_lock);
+		list_for_each_entry_safe(np, tmp, &ndp->ndp_packages, np_node)
+			ncsi_release_package(np);
+		spin_unlock(&ndp->ndp_package_lock);
+
+		if (!(ndp->ndp_flags & NCSI_DEV_PRIV_FLAG_CHANGE_ACTIVE)) {
+			nd->nd_state = ncsi_dev_state_functional;
+			nd->nd_link_up = 0;
+			nd->nd_handler(nd);
+		} else {
+			nd->nd_state = ncsi_dev_state_start;
+			ncsi_dev_start(ndp);
+		}
+
+		break;
+	default:
+		pr_warn("%s: Unsupported NCSI dev state 0x%x\n",
+			__func__, nd->nd_state);
+	}
+}
+
+static void ncsi_dev_work(struct work_struct *work)
+{
+	struct ncsi_dev_priv *ndp = container_of(work, struct ncsi_dev_priv,
+						 ndp_work);
+	struct ncsi_dev *nd = &ndp->ndp_ndev;
+
+	switch (nd->nd_state & ncsi_dev_state_major) {
+	case ncsi_dev_state_start:
+		ncsi_dev_start(ndp);
+		break;
+	case ncsi_dev_state_stop:
+		ncsi_dev_stop(ndp);
+		break;
+	case ncsi_dev_state_config:
+		ncsi_dev_config(ndp);
+		break;
+	default:
+		pr_warn("%s: Unsupported NCSI dev state 0x%x\n",
+			__func__, nd->nd_state);
+	}
+}
+
+static void ncsi_req_timeout(unsigned long data)
+{
+	struct ncsi_req *nr = (struct ncsi_req *)data;
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+
+	/* If the request already had associated response,
+	 * let the response handler to release it.
+	 */
+	spin_lock(&ndp->ndp_req_lock);
+	nr->nr_timer_enabled = false;
+	if (nr->nr_rsp || !nr->nr_cmd) {
+		spin_unlock(&ndp->ndp_req_lock);
+		return;
+	}
+	spin_unlock(&ndp->ndp_req_lock);
+
+	/* Release the request */
+	ncsi_free_req(nr, true, true);
+}
+
+struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
+				   void (*handler)(struct ncsi_dev *ndev))
+{
+	struct ncsi_dev_priv *ndp;
+	struct ncsi_dev *nd;
+	int idx;
+
+	/* Check if the device has been registered or not */
+	nd = ncsi_find_dev(dev);
+	if (nd)
+		return nd;
+
+	/* Create NCSI device */
+	ndp = kzalloc(sizeof(*ndp), GFP_ATOMIC);
+	if (!ndp) {
+		pr_warn("%s: Out of memory !\n", __func__);
+		return NULL;
+	}
+
+	nd = &ndp->ndp_ndev;
+	nd->nd_state = ncsi_dev_state_registered;
+	nd->nd_dev = dev;
+	nd->nd_handler = handler;
+
+	/* Initialize private NCSI device */
+	spin_lock_init(&ndp->ndp_package_lock);
+	INIT_LIST_HEAD(&ndp->ndp_packages);
+	INIT_WORK(&ndp->ndp_work, ncsi_dev_work);
+	spin_lock_init(&ndp->ndp_req_lock);
+	atomic_set(&ndp->ndp_last_req_idx, 0);
+	for (idx = 0; idx < 256; idx++) {
+		ndp->ndp_reqs[idx].nr_id = idx;
+		ndp->ndp_reqs[idx].nr_ndp = ndp;
+		setup_timer(&ndp->ndp_reqs[idx].nr_timer, ncsi_req_timeout,
+			    (unsigned long)&ndp->ndp_reqs[idx]);
+	}
+
+	spin_lock(&ncsi_dev_lock);
+	list_add_tail_rcu(&ndp->ndp_node, &ncsi_dev_list);
+	spin_unlock(&ncsi_dev_lock);
+
+	/* Register NCSI packet receiption handler */
+	ndp->ndp_ptype.type = cpu_to_be16(ETH_P_NCSI);
+	ndp->ndp_ptype.func = ncsi_rcv_rsp;
+	ndp->ndp_ptype.dev = dev;
+	dev_add_pack(&ndp->ndp_ptype);
+
+	return nd;
+}
+EXPORT_SYMBOL_GPL(ncsi_register_dev);
+
+int ncsi_start_dev(struct ncsi_dev *nd)
+{
+	struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
+
+	if (nd->nd_state != ncsi_dev_state_registered &&
+	    nd->nd_state != ncsi_dev_state_functional)
+		return -ENOTTY;
+
+	nd->nd_state = ncsi_dev_state_start;
+	schedule_work(&ndp->ndp_work);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ncsi_start_dev);
+
+int ncsi_config_dev(struct ncsi_dev *nd)
+{
+	struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
+
+	if (nd->nd_state != ncsi_dev_state_functional)
+		return -ENOTTY;
+
+	nd->nd_state = ncsi_dev_state_config;
+	schedule_work(&ndp->ndp_work);
+
+	return 0;
+}
+
+int ncsi_stop_dev(struct ncsi_dev *nd)
+{
+	struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
+
+	if (nd->nd_state != ncsi_dev_state_functional)
+		return -ENOTTY;
+
+	nd->nd_state = ncsi_dev_state_stop;
+	schedule_work(&ndp->ndp_work);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ncsi_stop_dev);
+
+void ncsi_unregister_dev(struct ncsi_dev *nd)
+{
+	struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
+	struct ncsi_package *np, *tmp;
+
+	dev_remove_pack(&ndp->ndp_ptype);
+
+	spin_lock(&ndp->ndp_package_lock);
+	list_for_each_entry_safe(np, tmp, &ndp->ndp_packages, np_node)
+		ncsi_release_package(np);
+	spin_unlock(&ndp->ndp_package_lock);
+}
+EXPORT_SYMBOL_GPL(ncsi_unregister_dev);
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ