diff mbox

[RFC,v1,1/7] net/ncsi: Resource management

Message ID 1444100989-3437-2-git-send-email-gwshan@linux.vnet.ibm.com
State RFC, archived
Delegated to: David Miller
Headers show

Commit Message

Gavin Shan Oct. 6, 2015, 3:09 a.m. UTC
According to NCSI spec (DSP0222), the NCSI enabled interface can
connected to multiple packages simultaneously, up to 8 packages.
Each package includes multiple channels, up to 32 channels. At
one moment, one channel is enabled to provide service to the NCSI
enabled interface. Besides, each channel comprises capabilities,
modes, filters, version and statistics etc. All of them are
resources to NCSI protocol stack.

At the meanwhile, the NCSI device seen from NIC driver is
represented by "struct ncsi_dev", which is expected to populated
and started by NIC driver before the NIC can work. All possible
NCSI packages and NCSI channels connected to the NCSI interface
are tracked from the NCSI device. Also, the NCSI device recognizes
active channel that is currently providing service to NCSI enabled
interface. Also, the NCSI requests (pairs of command and response)
are embedded in NCSI device.

This introduces the data structs to represents the NCSI resources
mentioned as above. Also, functions used by NCSI stack internally
are implemented. Besides, this introduces kernel config option
CONFIG_NET_NCSI to enable NCSI stack.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 include/net/ncsi.h        |  21 +++
 include/uapi/linux/ncsi.h | 200 +++++++++++++++++++++++++
 net/Kconfig               |   1 +
 net/Makefile              |   1 +
 net/ncsi/Kconfig          |  10 ++
 net/ncsi/Makefile         |   4 +
 net/ncsi/internal.h       | 153 +++++++++++++++++++
 net/ncsi/ncsi-manage.c    | 368 ++++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 758 insertions(+)
 create mode 100644 include/net/ncsi.h
 create mode 100644 include/uapi/linux/ncsi.h
 create mode 100644 net/ncsi/Kconfig
 create mode 100644 net/ncsi/Makefile
 create mode 100644 net/ncsi/internal.h
 create mode 100644 net/ncsi/ncsi-manage.c
diff mbox

Patch

diff --git a/include/net/ncsi.h b/include/net/ncsi.h
new file mode 100644
index 0000000..2eecfbd
--- /dev/null
+++ b/include/net/ncsi.h
@@ -0,0 +1,21 @@ 
+#ifndef __NET_NCSI_H
+#define __NET_NCSI_H
+
+#include <uapi/linux/ncsi.h>
+
+enum {
+	ncsi_dev_state_registered	= 0x0000,
+	ncsi_dev_state_functional	= 0x0100,
+	ncsi_dev_state_start		= 0x0200,
+	ncsi_dev_state_config		= 0x0300,
+	ncsi_dev_state_stop		= 0x0400
+};
+
+struct ncsi_dev {
+	int			nd_state;
+	int			nd_link_up;
+	struct net_device	*nd_dev;
+	void			(*nd_handler)(struct ncsi_dev *ndev);
+};
+
+#endif /* __NET_NCSI_H */
diff --git a/include/uapi/linux/ncsi.h b/include/uapi/linux/ncsi.h
new file mode 100644
index 0000000..9a3d180
--- /dev/null
+++ b/include/uapi/linux/ncsi.h
@@ -0,0 +1,200 @@ 
+#ifndef _UAPI_LINUX_NCSI_H
+#define _UAPI_LINUX_NCSI_H
+
+/* NCSI netlink message type */
+enum {
+	NCSI_MSG_BASE		= 16,
+	NCSI_MSG_GET_LAYOUT	= 16,
+	NCSI_MSG_GET_VERSION,
+	NCSI_MSG_GET_CAP,
+	NCSI_MSG_GET_MODE,
+	NCSI_MSG_GET_FILTER,
+	NCSI_MSG_GET_STATS,
+	NCSI_MSG_SET_MODE,
+	NCSI_MSG_SET_FILTER,
+	NCSI_MSG_MAX
+};
+
+
+/* NCSI channel capabilities */
+enum {
+	NCSI_CAP_BASE		= 0,
+	NCSI_CAP_GENERIC	= 0,
+	NCSI_CAP_BC,
+	NCSI_CAP_MC,
+	NCSI_CAP_BUFFER,
+	NCSI_CAP_AEN,
+	NCSI_CAP_VLAN,
+	NCSI_CAP_MAX
+};
+
+enum {
+	NCSI_CAP_GENERIC_HWA	= 0x01,	/* HW arbitration             */
+	NCSI_CAP_GENERIC_HDS	= 0x02,	/* HNC driver status change   */
+	NCSI_CAP_GENERIC_FC	= 0x04,	/* HNC to MC flow control     */
+	NCSI_CAP_GENERIC_FC1	= 0x08,	/* MC to HNC flow control     */
+	NCSI_CAP_GENERIC_MC	= 0x10,	/* Global multicast filtering */
+	NCSI_CAP_GENERIC_MASK	= 0x1f,
+	NCSI_CAP_BC_ARP		= 0x01,	/* ARP packet filtering       */
+	NCSI_CAP_BC_DHCPC	= 0x02,	/* DHCP client filtering      */
+	NCSI_CAP_BC_DHCPS	= 0x04,	/* DHCP server filtering      */
+	NCSI_CAP_BC_NETBIOS	= 0x08,	/* NetBIOS packet filtering   */
+	NCSI_CAP_BC_MASK	= 0x0f,
+	NCSI_CAP_MC_NEIGHBOR	= 0x01,	/* IPv6 neighbor filtering    */
+	NCSI_CAP_MC_ROUTER	= 0x02,	/* IPv6 router filering       */
+	NCSI_CAP_MC_DHCPv6	= 0x04,	/* DHCPv6 filtering           */
+	NCSI_CAP_MC_MASK	= 0x07,
+	NCSI_CAP_AEN_LSC	= 0x01,	/* Link status change AEN     */
+	NCSI_CAP_AEN_CR		= 0x02,	/* Configuration required AEN */
+	NCSI_CAP_AEN_HDS	= 0x04,	/* HNC driver status AEN      */
+	NCSI_CAP_AEN_MASK	= 0x07,
+	NCSI_CAP_VLAN_ONLY	= 0x01,	/* VLAN is supported          */
+	NCSI_CAP_VLAN_NO	= 0x02,	/* Filter VLAN and non-VLAN   */
+	NCSI_CAP_VLAN_ANY	= 0x04,	/* Filter Any-and-non-VLAN    */
+	NCSI_CAP_VLAN_MASK	= 0x07
+};
+
+/* NCSI channel mode */
+enum {
+	NCSI_MODE_BASE		= 0,
+	NCSI_MODE_ENABLE	= 0,
+	NCSI_MODE_TX_ENABLE,
+	NCSI_MODE_LINK,
+	NCSI_MODE_VLAN,
+	NCSI_MODE_BC,
+	NCSI_MODE_MC,
+	NCSI_MODE_AEN,
+	NCSI_MODE_FC,
+	NCSI_MODE_MAX
+};
+
+/* NCSI channel filters */
+enum {
+	NCSI_FILTER_BASE	= 0,
+	NCSI_FILTER_VLAN	= 0,
+	NCSI_FILTER_UC,
+	NCSI_FILTER_MC,
+	NCSI_FILTER_MIXED,
+	NCSI_FILTER_MAX
+};
+
+/*
+ * It's put right after netlink message header. Also, it's
+ * used to convey NCSI topology layout.
+ */
+struct ncsi_msg {
+	__u32	nm_flag;
+#define NCSI_FLAG_REQUEST		0x1
+#define NCSI_FLAG_RESPONSE		0x2
+#define NCSI_FLAG_ACTIVE_CHANNEL	0x4
+
+	__u32	nm_ifindex;		/* ID of network device               */
+	__u32	nm_package_id;		/* ID of NCSI package                 */
+	__u32	nm_channel_id;		/* ID of NCSI channel                 */
+	__u32	nm_index;		/* ID of mode, capability or filter   */
+	__u32	nm_errcode;		/* Error code                         */
+};
+
+enum {
+	NCSI_SUCCESS,
+	NCSI_ERR_PARAM,
+	NCSI_ERR_NO_MEM,
+	NCSI_ERR_NO_DEV,
+	NCSI_ERR_NOT_ACTIVE,
+	NCSI_ERR_INTERNAL,
+};
+
+/* NCSI channel version */
+struct ncsi_channel_version {
+	__u32	ncv_version;		/* Supported BCD encoded NCSI version */
+	__u32	ncv_alpha2;		/* Supported BCD encoded NCSI version */
+	__u8	ncv_fw_name[12];	/* Firware name string                */
+	__u32	ncv_fw_version;		/* Firmware version                   */
+	__u16	ncv_pci_ids[4];		/* PCI identification                 */
+	__u32	ncv_mf_id;		/* Manufacture ID                     */
+};
+
+/* NCSI channel capability */
+struct ncsi_channel_cap {
+	__u32	ncc_index;		/* Index of channel capabilities     */
+	__u32	ncc_cap;		/* NCSI channel capability           */
+};
+
+/* NCSI channel mode */
+struct ncsi_channel_mode {
+	__u32	ncm_index;		/* Index of channel modes            */
+	__u32	ncm_enable;		/* Enabled or disabled               */
+	__u32	ncm_size;		/* Valid entries in ncm_data[]       */
+	__u32	ncm_data[8];		/* Data entries                      */
+};
+
+/* NCSI channel filter */
+struct ncsi_channel_filter {
+	__u32	ncf_index;		/* Index of channel filters          */
+	__u32	ncf_total;		/* Total entries in the filter table */
+	__u64	ncf_bitmap;		/* Bitmap of valid entries           */
+	__u8	ncf_data[];		/* Data for the valid entries        */
+};
+
+/* NCSI channel statistics */
+struct ncsi_channel_stats {
+	__u32	ncs_hnc_cnt_hi;			/* Counter cleared            */
+	__u32	ncs_hnc_cnt_lo;			/* Counter cleared            */
+	__u32	ncs_hnc_rx_bytes;		/* Rx bytes                   */
+	__u32	ncs_hnc_tx_bytes;		/* Tx bytes                   */
+	__u32	ncs_hnc_rx_uc_pkts;		/* Rx UC packets              */
+	__u32	ncs_hnc_rx_mc_pkts;		/* Rx MC packets              */
+	__u32	ncs_hnc_rx_bc_pkts;		/* Rx BC packets              */
+	__u32	ncs_hnc_tx_uc_pkts;		/* Tx UC packets              */
+	__u32	ncs_hnc_tx_mc_pkts;		/* Tx MC packets              */
+	__u32	ncs_hnc_tx_bc_pkts;		/* Tx BC packets              */
+	__u32	ncs_hnc_fcs_err;		/* FCS errors                 */
+	__u32	ncs_hnc_align_err;		/* Alignment errors           */
+	__u32	ncs_hnc_false_carrier;		/* False carrier detection    */
+	__u32	ncs_hnc_runt_pkts;		/* Rx runt packets            */
+	__u32	ncs_hnc_jabber_pkts;		/* Rx jabber packets          */
+	__u32	ncs_hnc_rx_pause_xon;		/* Rx pause XON frames        */
+	__u32	ncs_hnc_rx_pause_xoff;		/* Rx XOFF frames             */
+	__u32	ncs_hnc_tx_pause_xon;		/* Tx XON frames              */
+	__u32	ncs_hnc_tx_pause_xoff;		/* Tx XOFF frames             */
+	__u32	ncs_hnc_tx_s_collision;		/* Single collision frames    */
+	__u32	ncs_hnc_tx_m_collision;		/* Multiple collision frames  */
+	__u32	ncs_hnc_l_collision;		/* Late collision frames      */
+	__u32	ncs_hnc_e_collision;		/* Excessive collision frames */
+	__u32	ncs_hnc_rx_ctl_frames;		/* Rx control frames          */
+	__u32	ncs_hnc_rx_64_frames;		/* Rx 64-bytes frames         */
+	__u32	ncs_hnc_rx_127_frames;		/* Rx 65-127 bytes frames     */
+	__u32	ncs_hnc_rx_255_frames;		/* Rx 128-255 bytes frames    */
+	__u32	ncs_hnc_rx_511_frames;		/* Rx 256-511 bytes frames    */
+	__u32	ncs_hnc_rx_1023_frames;		/* Rx 512-1023 bytes frames   */
+	__u32	ncs_hnc_rx_1522_frames;		/* Rx 1024-1522 bytes frames  */
+	__u32	ncs_hnc_rx_9022_frames;		/* Rx 1523-9022 bytes frames  */
+	__u32	ncs_hnc_tx_64_frames;		/* Tx 64-bytes frames         */
+	__u32	ncs_hnc_tx_127_frames;		/* Tx 65-127 bytes frames     */
+	__u32	ncs_hnc_tx_255_frames;		/* Tx 128-255 bytes frames    */
+	__u32	ncs_hnc_tx_511_frames;		/* Tx 256-511 bytes frames    */
+	__u32	ncs_hnc_tx_1023_frames;		/* Tx 512-1023 bytes frames   */
+	__u32	ncs_hnc_tx_1522_frames;		/* Tx 1024-1522 bytes frames  */
+	__u32	ncs_hnc_tx_9022_frames;		/* Tx 1523-9022 bytes frames  */
+	__u32	ncs_hnc_rx_valid_bytes;		/* Rx valid bytes             */
+	__u32	ncs_hnc_rx_runt_pkts;		/* Rx error runt packets      */
+	__u32	ncs_hnc_rx_jabber_pkts;		/* Rx error jabber packets    */
+	__u32	ncs_ncsi_rx_cmds;		/* Rx NCSI commands           */
+	__u32	ncs_ncsi_dropped_cmds;		/* Dropped commands           */
+	__u32	ncs_ncsi_cmd_type_errs;		/* Command type errors        */
+	__u32	ncs_ncsi_cmd_csum_errs;		/* Command checksum errors    */
+	__u32	ncs_ncsi_rx_pkts;		/* Rx NCSI packets            */
+	__u32	ncs_ncsi_tx_pkts;		/* Tx NCSI packets            */
+	__u32	ncs_ncsi_tx_aen_pkts;		/* Tx AEN packets             */
+	__u32	ncs_pt_tx_pkts;			/* Tx packets                 */
+	__u32	ncs_pt_tx_dropped;		/* Tx dropped packets         */
+	__u32	ncs_pt_tx_channel_err;		/* Tx channel errors          */
+	__u32	ncs_pt_tx_us_err;		/* Tx undersize errors        */
+	__u32	ncs_pt_rx_pkts;			/* Rx packets                 */
+	__u32	ncs_pt_rx_dropped;		/* Rx dropped packets         */
+	__u32	ncs_pt_rx_channel_err;		/* Rx channel errors          */
+	__u32	ncs_pt_rx_us_err;		/* Rx undersize errors        */
+	__u32	ncs_pt_rx_os_err;		/* Rx oversize errors         */
+};
+
+#endif /* _UAPI_LINUX_NCSI_H */
diff --git a/net/Kconfig b/net/Kconfig
index 57a7c5a..a92fc73 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -232,6 +232,7 @@  source "net/netlink/Kconfig"
 source "net/mpls/Kconfig"
 source "net/hsr/Kconfig"
 source "net/switchdev/Kconfig"
+source "net/ncsi/Kconfig"
 
 config RPS
 	bool
diff --git a/net/Makefile b/net/Makefile
index 3995613..9c1d4b1 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -74,3 +74,4 @@  obj-$(CONFIG_HSR)		+= hsr/
 ifneq ($(CONFIG_NET_SWITCHDEV),)
 obj-y				+= switchdev/
 endif
+obj-$(CONFIG_NET_NCSI)		+= ncsi/
diff --git a/net/ncsi/Kconfig b/net/ncsi/Kconfig
new file mode 100644
index 0000000..723f0eb
--- /dev/null
+++ b/net/ncsi/Kconfig
@@ -0,0 +1,10 @@ 
+#
+# Configuration for NCSI support
+#
+
+config NET_NCSI
+	bool "NCSI interface support (EXPERIMENTAL)"
+	depends on INET
+	---help---
+	  This module provides NCSI (Network Controller Sideband Interface)
+	  support.
diff --git a/net/ncsi/Makefile b/net/ncsi/Makefile
new file mode 100644
index 0000000..07b5625
--- /dev/null
+++ b/net/ncsi/Makefile
@@ -0,0 +1,4 @@ 
+#
+# Makefile for NCSI API
+#
+obj-$(CONFIG_NET_NCSI) += ncsi-manage.o
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
new file mode 100644
index 0000000..c9511bb
--- /dev/null
+++ b/net/ncsi/internal.h
@@ -0,0 +1,153 @@ 
+#ifndef __NCSI_INTERNAL_H__
+#define __NCSI_INTERNAL_H__
+
+struct ncsi_dev_priv;
+struct ncsi_package;
+
+#define NCSI_PACKAGE_INDEX(c)	(((c) >> 5) & 0x7)
+#define NCSI_CHANNEL_INDEX(c)	((c) & 0x1ffff)
+#define NCSI_TO_CHANNEL(p, c)	((((p) & 0x7) << 5) | ((c) & 0x1ffff))
+
+/* Channel state */
+enum {
+	ncsi_channel_state_deselected_initial,
+	ncsi_channel_state_selected_initial,
+	ncsi_channel_state_deselected_ready,
+	ncsi_channel_state_selected_ready,
+};
+
+struct ncsi_channel {
+	unsigned char			nc_id;
+	int				nc_state;
+	struct ncsi_package		*nc_package;
+	struct ncsi_channel_version	nc_version;
+	struct ncsi_channel_cap		nc_caps[NCSI_CAP_MAX];
+	struct ncsi_channel_mode	nc_modes[NCSI_MODE_MAX];
+	struct ncsi_channel_filter	*nc_filters[NCSI_FILTER_MAX];
+	struct ncsi_channel_stats	nc_stats;
+	struct list_head		nc_node;
+};
+
+struct ncsi_package {
+	unsigned char		np_id;
+	struct ncsi_dev_priv	*np_ndp;
+	atomic_t		np_channel_num;
+	spinlock_t		np_channel_lock;
+	struct list_head	np_channels;
+	struct list_head	np_node;
+};
+
+struct ncsi_skb_parms {
+	unsigned int		nsp_valid;
+	unsigned int		nsp_portid;
+	struct nlmsghdr		nsp_nlh;
+};
+
+#define NCSI_CB(skb)	(*(struct ncsi_skb_parms*)&((skb)->cb))
+
+struct ncsi_req {
+	unsigned char		nr_id;
+	bool			nr_used;
+	struct ncsi_dev_priv	*nr_ndp;
+	struct sk_buff		*nr_cmd;
+	struct sk_buff		*nr_rsp;
+	struct timer_list	nr_timer;
+	bool			nr_timer_enabled;
+};
+
+enum {
+	ncsi_dev_state_major		= 0xff00,
+	ncsi_dev_state_minor		= 0x00ff,
+	ncsi_dev_state_start_deselect	= 0x0201,
+	ncsi_dev_state_start_package,
+	ncsi_dev_state_start_channel,
+	ncsi_dev_state_start_sp,
+	ncsi_dev_state_start_cis,
+	ncsi_dev_state_start_gvi,
+	ncsi_dev_state_start_gc,
+	ncsi_dev_state_start_dp,
+	ncsi_dev_state_start_active,
+	ncsi_dev_state_config_sp	= 0x0301,
+	ncsi_dev_state_config_cis,
+	ncsi_dev_state_config_sma,
+	ncsi_dev_state_config_ebf,
+	ncsi_dev_state_config_ecnt,
+	ncsi_dev_state_config_ec,
+	ncsi_dev_state_config_gls,
+	ncsi_dev_state_config_done,
+	ncsi_dev_state_stop_select	= 0x0401,
+	ncsi_dev_state_stop_dcnt,
+	ncsi_dev_state_stop_dc,
+	ncsi_dev_state_stop_deselect,
+	ncsi_dev_state_stop_done
+};
+
+struct ncsi_dev_priv {
+	struct ncsi_dev		ndp_ndev;
+	int			ndp_flags;
+#define NCSI_DEV_PRIV_FLAG_CHANGE_ACTIVE	0x1
+	struct ncsi_package	*ndp_active_package;
+	struct ncsi_channel	*ndp_active_channel;
+	atomic_t		ndp_package_num;
+	spinlock_t		ndp_package_lock;
+	struct list_head	ndp_packages;
+	atomic_t		ndp_pending_reqs;
+	atomic_t		ndp_last_req_idx;
+	spinlock_t		ndp_req_lock;
+	struct ncsi_req		ndp_reqs[256];
+	struct work_struct	ndp_work;
+	struct packet_type	ndp_ptype;
+	struct list_head	ndp_node;
+};
+
+struct ncsi_cmd_arg {
+	struct ncsi_dev_priv	*nca_ndp;
+	unsigned char		nca_type;
+	unsigned char		nca_id;
+	unsigned char		nca_package;
+	unsigned char		nca_channel;
+	unsigned short		nca_payload;
+	struct nlmsghdr		*nca_nlh;
+	unsigned int		nca_portid;
+	union {
+		unsigned char	nca_bytes[16];
+		unsigned short	nca_words[8];
+		unsigned int	nca_dwords[4];
+	};
+};
+
+extern struct net *ncsi_net;
+extern struct list_head ncsi_dev_list;
+extern spinlock_t ncsi_dev_lock;
+
+#define TO_NCSI_DEV_PRIV(nd) \
+	container_of(nd, struct ncsi_dev_priv, ndp_ndev)
+#define NCSI_FOR_EACH_DEV(ndp) \
+	list_for_each_entry_rcu(ndp, &ncsi_dev_list, ndp_node)
+#define NCSI_FOR_EACH_PACKAGE(ndp, np) \
+	list_for_each_entry_rcu(np, &ndp->ndp_packages, np_node)
+#define NCSI_FOR_EACH_CHANNEL(np, nc) \
+	list_for_each_entry_rcu(nc, &np->np_channels, nc_node)
+
+/* Resources */
+int ncsi_find_channel_filter(struct ncsi_channel *nc, int table, void *data);
+int ncsi_add_channel_filter(struct ncsi_channel *nc, int table, void *data);
+int ncsi_del_channel_filter(struct ncsi_channel *nc, int table, int index);
+struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np,
+				      unsigned char id);
+struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
+				       unsigned char id);
+struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
+				      unsigned char id);
+struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
+				       unsigned char id);
+void ncsi_release_package(struct ncsi_package *np);
+void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
+				   unsigned char id,
+				   struct ncsi_package **np,
+				   struct ncsi_channel **nc);
+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);
+
+#endif /* __NCSI_INTERNAL_H__ */
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
new file mode 100644
index 0000000..765217c
--- /dev/null
+++ b/net/ncsi/ncsi-manage.c
@@ -0,0 +1,368 @@ 
+/*
+ * Copyright Gavin Shan, IBM Corporation 2015.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+
+#include <net/ncsi.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "internal.h"
+
+LIST_HEAD(ncsi_dev_list);
+DEFINE_SPINLOCK(ncsi_dev_lock);
+
+int ncsi_find_channel_filter(struct ncsi_channel *nc, int table, void *data)
+{
+	struct ncsi_channel_filter *ncf;
+	int idx, entry_size;
+	void *bitmap;
+
+	switch (table) {
+	case NCSI_FILTER_VLAN:
+		entry_size = 2;
+		break;
+	case NCSI_FILTER_UC:
+	case NCSI_FILTER_MC:
+	case NCSI_FILTER_MIXED:
+		entry_size = 6;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Check if the filter table has been initialized */
+	ncf = nc->nc_filters[table];
+	if (!ncf)
+		return -ENODEV;
+
+	/* Check the valid entries one by one */
+	bitmap = (void *)&ncf->ncf_bitmap;
+	idx = -1;
+	while ((idx = find_next_bit(bitmap, ncf->ncf_total, idx+1))
+		< ncf->ncf_total) {
+		if (!memcmp(ncf->ncf_data + entry_size * idx, data, entry_size))
+			return idx;
+	}
+
+	return -ENOENT;
+}
+
+int ncsi_add_channel_filter(struct ncsi_channel *nc, int table, void *data)
+{
+	struct ncsi_channel_filter *ncf;
+	int idx, entry_size;
+	void *bitmap;
+
+	/* Needn't add it if it's already existing */
+	idx = ncsi_find_channel_filter(nc, table, data);
+	if (idx >= 0)
+		return idx;
+
+	switch (table) {
+	case NCSI_FILTER_VLAN:
+		entry_size = 2;
+		break;
+	case NCSI_FILTER_UC:
+	case NCSI_FILTER_MC:
+	case NCSI_FILTER_MIXED:
+		entry_size = 6;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Check if the filter table has been initialized */
+	ncf = nc->nc_filters[table];
+	if (!ncf)
+		return -ENODEV;
+
+	/* Propagate the filter */
+	bitmap = (void *)&ncf->ncf_bitmap;
+	do {
+		idx = find_next_zero_bit(bitmap, ncf->ncf_total, 0);
+		if (idx >= ncf->ncf_total)
+			return -ENOSPC;
+	} while (test_and_set_bit(idx, bitmap));
+
+	memcpy(ncf->ncf_data + entry_size * idx, data, entry_size);
+	return idx;
+}
+
+int ncsi_del_channel_filter(struct ncsi_channel *nc, int table, int index)
+{
+	struct ncsi_channel_filter *ncf;
+	int entry_size;
+	void *bitmap;
+
+	switch (table) {
+	case NCSI_FILTER_VLAN:
+		entry_size = 2;
+		break;
+	case NCSI_FILTER_UC:
+	case NCSI_FILTER_MC:
+	case NCSI_FILTER_MIXED:
+		entry_size = 6;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Check if the filter table has been initialized */
+	ncf = nc->nc_filters[table];
+	if (!ncf || index >= ncf->ncf_total)
+		return -ENODEV;
+
+	/* Check if the entry is valid */
+	bitmap = (void *)&ncf->ncf_bitmap;
+	if (test_and_clear_bit(index, bitmap))
+		memset(ncf->ncf_data + entry_size * index, 0, entry_size);
+
+	return 0;
+}
+
+struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
+{
+	struct ncsi_channel *nc, *tmp;
+	int index;
+
+	nc = kzalloc(sizeof(*nc), GFP_ATOMIC);
+	if (!nc) {
+		pr_warn("%s: Out of memory !\n", __func__);
+		return NULL;
+	}
+
+	nc->nc_package = np;
+	nc->nc_id = id;
+	for (index = 0; index < NCSI_CAP_MAX; index++)
+		nc->nc_caps[index].ncc_index = index;
+	for (index = 0; index < NCSI_MODE_MAX; index++)
+		nc->nc_modes[index].ncm_index = index;
+
+	spin_lock(&np->np_channel_lock);
+	tmp = ncsi_find_channel(np, id);
+	if (tmp) {
+		spin_unlock(&np->np_channel_lock);
+		kfree(nc);
+		return tmp;
+	}
+	list_add_tail_rcu(&nc->nc_node, &np->np_channels);
+	spin_unlock(&np->np_channel_lock);
+
+	atomic_inc(&np->np_channel_num);
+	return nc;
+}
+
+struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
+				       unsigned char id)
+{
+	struct ncsi_channel *nc;
+
+	NCSI_FOR_EACH_CHANNEL(np, nc) {
+		if (nc->nc_id == id)
+			return nc;
+	}
+
+	return NULL;
+}
+
+static void ncsi_release_channel(struct ncsi_channel *nc)
+{
+	struct ncsi_dev_priv *ndp = nc->nc_package->np_ndp;
+	struct ncsi_package *np = nc->nc_package;
+	struct ncsi_channel_filter *ncf;
+	int i;
+
+	/* Release filters */
+	for (i = 0; i < NCSI_FILTER_MAX; i++) {
+		ncf = nc->nc_filters[i];
+		if (!ncf)
+			continue;
+
+		nc->nc_filters[i] = NULL;
+		kfree(ncf);
+	}
+
+	/* Update active channel if necessary */
+	if (ndp->ndp_active_channel == nc) {
+		ndp->ndp_active_package = NULL;
+		ndp->ndp_active_channel = NULL;
+	}
+
+	/* Remove and free channel */
+	list_del_rcu(&nc->nc_node);
+	kfree(nc);
+	BUG_ON(atomic_dec_return(&np->np_channel_num) < 0);
+}
+
+struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
+				      unsigned char id)
+{
+	struct ncsi_package *np, *tmp;
+
+	np = kzalloc(sizeof(*np), GFP_ATOMIC);
+	if (!np) {
+		pr_warn("%s: Out of memory !\n", __func__);
+		return NULL;
+	}
+
+	np->np_id = id;
+	np->np_ndp = ndp;
+	spin_lock_init(&np->np_channel_lock);
+	INIT_LIST_HEAD(&np->np_channels);
+
+	spin_lock(&ndp->ndp_package_lock);
+	tmp = ncsi_find_package(ndp, id);
+	if (tmp) {
+		spin_unlock(&ndp->ndp_package_lock);
+		kfree(np);
+		return tmp;
+	}
+	list_add_tail_rcu(&np->np_node, &ndp->ndp_packages);
+	spin_unlock(&ndp->ndp_package_lock);
+
+	atomic_inc(&ndp->ndp_package_num);
+	return np;
+}
+
+struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
+				       unsigned char id)
+{
+	struct ncsi_package *np;
+
+	NCSI_FOR_EACH_PACKAGE(ndp, np) {
+		if (np->np_id == id)
+			return np;
+	}
+
+	return NULL;
+}
+
+void ncsi_release_package(struct ncsi_package *np)
+{
+	struct ncsi_dev_priv *ndp = np->np_ndp;
+	struct ncsi_channel *nc, *tmp;
+
+	/* Release all child channels */
+	spin_lock(&np->np_channel_lock);
+	list_for_each_entry_safe(nc, tmp, &np->np_channels, nc_node)
+		ncsi_release_channel(nc);
+	spin_unlock(&np->np_channel_lock);
+
+	/* Clear active package if necessary */
+	if (ndp->ndp_active_package == np) {
+		ndp->ndp_active_package = NULL;
+		ndp->ndp_active_channel = NULL;
+	}
+
+	/* Remove and free package */
+	list_del_rcu(&np->np_node);
+	kfree(np);
+
+	/* Decrease number of packages */
+	BUG_ON(atomic_dec_return(&ndp->ndp_package_num) < 0);
+}
+
+void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
+				   unsigned char id,
+				   struct ncsi_package **np,
+				   struct ncsi_channel **nc)
+{
+	struct ncsi_package *p;
+	struct ncsi_channel *c;
+
+	p = ncsi_find_package(ndp, NCSI_PACKAGE_INDEX(id));
+	c = p ? ncsi_find_channel(p, NCSI_CHANNEL_INDEX(id)) : NULL;
+
+	if (np)
+		*np = p;
+	if (nc)
+		*nc = c;
+}
+
+/*
+ * For two consective NCSI commands, the packet IDs shouldn't be
+ * same. Otherwise, the bogus response might be replied. So the
+ * available IDs are allocated in round-robin fasion.
+ */
+struct ncsi_req *ncsi_alloc_req(struct ncsi_dev_priv *ndp)
+{
+	struct ncsi_req *nr = NULL;
+	int idx, limit = 256;
+
+	spin_lock(&ndp->ndp_req_lock);
+
+	/* Check if there is one available request until the ceiling */
+	for (idx = atomic_read(&ndp->ndp_last_req_idx);
+	     !nr && idx < limit; idx++) {
+		if (ndp->ndp_reqs[idx].nr_used)
+			continue;
+
+		ndp->ndp_reqs[idx].nr_used = true;
+		nr = &ndp->ndp_reqs[idx];
+		atomic_inc(&ndp->ndp_last_req_idx);
+		if (atomic_read(&ndp->ndp_last_req_idx) >= limit)
+			atomic_set(&ndp->ndp_last_req_idx, 0);
+	}
+
+	/* Fail back to check from the starting cursor */
+	for (idx = 0; !nr && idx < atomic_read(&ndp->ndp_last_req_idx); idx++) {
+		if (ndp->ndp_reqs[idx].nr_used)
+			continue;
+
+		ndp->ndp_reqs[idx].nr_used = true;
+		nr = &ndp->ndp_reqs[idx];
+		atomic_inc(&ndp->ndp_last_req_idx);
+		if (atomic_read(&ndp->ndp_last_req_idx) >= limit)
+			atomic_set(&ndp->ndp_last_req_idx, 0);
+	}
+
+	spin_unlock(&ndp->ndp_req_lock);
+	return nr;
+}
+
+void ncsi_free_req(struct ncsi_req *nr, bool check, bool timeout)
+{
+	struct ncsi_dev_priv *ndp = nr->nr_ndp;
+	struct sk_buff *cmd, *rsp;
+
+	if (nr->nr_timer_enabled) {
+		nr->nr_timer_enabled = false;
+		del_timer_sync(&nr->nr_timer);
+	}
+
+	spin_lock(&ndp->ndp_req_lock);
+	cmd = nr->nr_cmd;
+	rsp = nr->nr_rsp;
+	nr->nr_cmd = NULL;
+	nr->nr_rsp = NULL;
+	nr->nr_used = false;
+	spin_unlock(&ndp->ndp_req_lock);
+
+	/* Release command and response */
+	consume_skb(cmd);
+	consume_skb(rsp);
+}
+
+struct ncsi_dev *ncsi_find_dev(struct net_device *dev)
+{
+	struct ncsi_dev_priv *ndp;
+
+	NCSI_FOR_EACH_DEV(ndp) {
+		if (ndp->ndp_ndev.nd_dev == dev)
+			return &ndp->ndp_ndev;
+	}
+
+	return NULL;
+}