All of lore.kernel.org
 help / color / mirror / Atom feed
From: Gavin Shan <gwshan@linux.vnet.ibm.com>
To: netdev@vger.kernel.org
Cc: benh@kernel.crashing.org, davem@davemloft.net,
	sergei.shtylyov@cogentembedded.com,
	Gavin Shan <gwshan@linux.vnet.ibm.com>
Subject: [PATCH RFC 1/6] net/ncsi: Resource management
Date: Mon,  9 Nov 2015 11:10:01 +1100	[thread overview]
Message-ID: <1447027806-4744-2-git-send-email-gwshan@linux.vnet.ibm.com> (raw)
In-Reply-To: <1447027806-4744-1-git-send-email-gwshan@linux.vnet.ibm.com>

NCSI spec (DSP0222) defined several objects: package, channel,
mode, filter, version and statistics etc. This defines data
structrues to represent those objects and exports interfaces
(functions) to manage them:

   * The user (e.g. netdev driver) dereference NCSI device by
     "struct ncsi_dev", which is embedded to "struct ncsi_dev_priv".
     The later one is used by NCSI stack internally.
   * Every NCSI device can have multiple packages simultaneously,
     up to 8 packages. It's represented by "struct ncsi_package"
     and identified by 3-bits ID.
   * Every NCSI package can have multiple channels, up to 32. It's
     represented by "struct ncsi_channel" and identified by 5-bits
     ID.
   * Every NCSI channel has version, statistics, various modes and
     filters. They're represented by "struct ncsi_channel_version",
     "struct ncsi_channel_stats", "struct ncsi_channel_mode" and
     "struct ncsi_channel_filter" separately.
   * The NCSI stack works in terms of command and response. One
     command, sent over interface to target package/channel, is
     expected to have a response. The same 8-bits ID are put into
     the command and response. This patch abstracts the pair of
     command and response by "struct ncsi_req".
   * "struct ncsi_cmd_arg" is used to tell the specific command
     to be sent over NCSI interface.

This also introduces CONFIG_NET_NCSI to enable NCSI stack.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 include/net/ncsi.h     |  30 ++++
 net/Kconfig            |   1 +
 net/Makefile           |   1 +
 net/ncsi/Kconfig       |  10 ++
 net/ncsi/Makefile      |   4 +
 net/ncsi/internal.h    | 306 +++++++++++++++++++++++++++++++++++++++++
 net/ncsi/ncsi-manage.c | 363 +++++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 715 insertions(+)
 create mode 100644 include/net/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 --git a/include/net/ncsi.h b/include/net/ncsi.h
new file mode 100644
index 0000000..2735f19
--- /dev/null
+++ b/include/net/ncsi.h
@@ -0,0 +1,30 @@
+#ifndef __NET_NCSI_H
+#define __NET_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,
+	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/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..958fd69
--- /dev/null
+++ b/net/ncsi/internal.h
@@ -0,0 +1,306 @@
+#ifndef __NCSI_INTERNAL_H__
+#define __NCSI_INTERNAL_H__
+
+/* 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,	/* Channel enabled or disabled */
+	NCSI_MODE_TX_ENABLE,		/* Tx enabled or disabled      */ 
+	NCSI_MODE_LINK,			/* Link mode                   */
+	NCSI_MODE_VLAN,			/* VLAN filtering mode         */
+	NCSI_MODE_BC,			/* Broadcom filtering mode     */
+	NCSI_MODE_MC,			/* Multicast filtering mode    */
+	NCSI_MODE_AEN,			/* AEN enabled or disabled     */
+	NCSI_MODE_FC,			/* Flow control mode           */
+	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
+};
+
+/* NCSI channel version */
+struct ncsi_channel_version {
+	unsigned int	ncv_version;		/* BCD encoded NCSI version */
+	unsigned int	ncv_alpha2;		/* BCD encoded NCSI version */
+	unsigned char	ncv_fw_name[12];	/* Firware name string      */
+	unsigned int	ncv_fw_version;		/* Firmware version         */
+	unsigned short	ncv_pci_ids[4];		/* PCI identification       */
+	unsigned int	ncv_mf_id;		/* Manufacture ID           */
+};
+
+/* NCSI channel capability */
+struct ncsi_channel_cap {
+	unsigned int	ncc_cap;	/* NCSI channel capability */
+};
+
+/* NCSI channel mode */
+struct ncsi_channel_mode {
+	unsigned int	ncm_enable;	/* Enabled or disabled         */
+	unsigned int	ncm_size;	/* Valid entries in ncm_data[] */
+	unsigned int	ncm_data[8];	/* Specific data for the mode  */
+};
+
+/*
+ * NCSI channel filter. According to NCSI spec, the total number
+ * of filters can't exceed 32. So it's enough to use 32 bits to
+ * track their usage
+ */
+struct ncsi_channel_filter {
+	unsigned int	ncf_total;	/* Total entries in the filter table */
+	unsigned int	ncf_entry_size;	/* Number of bytes per entry         */
+	unsigned int	ncf_bitmap;	/* Bitmap of valid entries           */
+	unsigned char	ncf_data[];	/* Data for the valid entries        */
+};
+
+/* NCSI channel statistics */
+struct ncsi_channel_stats {
+	unsigned int	ncs_hnc_cnt_hi;		/* Counter cleared            */
+	unsigned int	ncs_hnc_cnt_lo;		/* Counter cleared            */
+	unsigned int	ncs_hnc_rx_bytes;	/* Rx bytes                   */
+	unsigned int	ncs_hnc_tx_bytes;	/* Tx bytes                   */
+	unsigned int	ncs_hnc_rx_uc_pkts;	/* Rx UC packets              */
+	unsigned int	ncs_hnc_rx_mc_pkts;	/* Rx MC packets              */
+	unsigned int	ncs_hnc_rx_bc_pkts;	/* Rx BC packets              */
+	unsigned int	ncs_hnc_tx_uc_pkts;	/* Tx UC packets              */
+	unsigned int	ncs_hnc_tx_mc_pkts;	/* Tx MC packets              */
+	unsigned int	ncs_hnc_tx_bc_pkts;	/* Tx BC packets              */
+	unsigned int	ncs_hnc_fcs_err;	/* FCS errors                 */
+	unsigned int	ncs_hnc_align_err;	/* Alignment errors           */
+	unsigned int	ncs_hnc_false_carrier;	/* False carrier detection    */
+	unsigned int	ncs_hnc_runt_pkts;	/* Rx runt packets            */
+	unsigned int	ncs_hnc_jabber_pkts;	/* Rx jabber packets          */
+	unsigned int	ncs_hnc_rx_pause_xon;	/* Rx pause XON frames        */
+	unsigned int	ncs_hnc_rx_pause_xoff;	/* Rx XOFF frames             */
+	unsigned int	ncs_hnc_tx_pause_xon;	/* Tx XON frames              */
+	unsigned int	ncs_hnc_tx_pause_xoff;	/* Tx XOFF frames             */
+	unsigned int	ncs_hnc_tx_s_collision;	/* Single collision frames    */
+	unsigned int	ncs_hnc_tx_m_collision;	/* Multiple collision frames  */
+	unsigned int	ncs_hnc_l_collision;	/* Late collision frames      */
+	unsigned int	ncs_hnc_e_collision;	/* Excessive collision frames */
+	unsigned int	ncs_hnc_rx_ctl_frames;	/* Rx control frames          */
+	unsigned int	ncs_hnc_rx_64_frames;	/* Rx 64-bytes frames         */
+	unsigned int	ncs_hnc_rx_127_frames;	/* Rx 65-127 bytes frames     */
+	unsigned int	ncs_hnc_rx_255_frames;	/* Rx 128-255 bytes frames    */
+	unsigned int	ncs_hnc_rx_511_frames;	/* Rx 256-511 bytes frames    */
+	unsigned int	ncs_hnc_rx_1023_frames;	/* Rx 512-1023 bytes frames   */
+	unsigned int	ncs_hnc_rx_1522_frames;	/* Rx 1024-1522 bytes frames  */
+	unsigned int	ncs_hnc_rx_9022_frames;	/* Rx 1523-9022 bytes frames  */
+	unsigned int	ncs_hnc_tx_64_frames;	/* Tx 64-bytes frames         */
+	unsigned int	ncs_hnc_tx_127_frames;	/* Tx 65-127 bytes frames     */
+	unsigned int	ncs_hnc_tx_255_frames;	/* Tx 128-255 bytes frames    */
+	unsigned int	ncs_hnc_tx_511_frames;	/* Tx 256-511 bytes frames    */
+	unsigned int	ncs_hnc_tx_1023_frames;	/* Tx 512-1023 bytes frames   */
+	unsigned int	ncs_hnc_tx_1522_frames;	/* Tx 1024-1522 bytes frames  */
+	unsigned int	ncs_hnc_tx_9022_frames;	/* Tx 1523-9022 bytes frames  */
+	unsigned int	ncs_hnc_rx_valid_bytes;	/* Rx valid bytes             */
+	unsigned int	ncs_hnc_rx_runt_pkts;	/* Rx error runt packets      */
+	unsigned int	ncs_hnc_rx_jabber_pkts;	/* Rx error jabber packets    */
+	unsigned int	ncs_ncsi_rx_cmds;	/* Rx NCSI commands           */
+	unsigned int	ncs_ncsi_dropped_cmds;	/* Dropped commands           */
+	unsigned int	ncs_ncsi_cmd_type_errs;	/* Command type errors        */
+	unsigned int	ncs_ncsi_cmd_csum_errs;	/* Command checksum errors    */
+	unsigned int	ncs_ncsi_rx_pkts;	/* Rx NCSI packets            */
+	unsigned int	ncs_ncsi_tx_pkts;	/* Tx NCSI packets            */
+	unsigned int	ncs_ncsi_tx_aen_pkts;	/* Tx AEN packets             */
+	unsigned int	ncs_pt_tx_pkts;		/* Tx packets                 */
+	unsigned int	ncs_pt_tx_dropped;	/* Tx dropped packets         */
+	unsigned int	ncs_pt_tx_channel_err;	/* Tx channel errors          */
+	unsigned int	ncs_pt_tx_us_err;	/* Tx undersize errors        */
+	unsigned int	ncs_pt_rx_pkts;		/* Rx packets                 */
+	unsigned int	ncs_pt_rx_dropped;	/* Rx dropped packets         */
+	unsigned int	ncs_pt_rx_channel_err;	/* Rx channel errors          */
+	unsigned int	ncs_pt_rx_us_err;	/* Rx undersize errors        */
+	unsigned int	ncs_pt_rx_os_err;	/* Rx oversize errors         */
+};
+
+/* NCSI 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_package;
+struct ncsi_dev_priv;
+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_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;
+};
+
+/*
+ * The NCSI device state is made up of major and minor states.
+ * The major state is driven by the public APIs. The minor states
+ * are for internal usage to complete the function in that API.
+ */
+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;
+#define NCSI_DEV_FLAG_CHANGE_ACTIVE	0x1
+	int			ndp_flags;
+	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;
+#define NCSI_DEV_MAX_REQ	256
+	struct ncsi_req		ndp_reqs[NCSI_DEV_MAX_REQ];
+	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;
+	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 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))
+
+#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)
+
+/* Resource management */
+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..eb23222
--- /dev/null
+++ b/net/ncsi/ncsi-manage.c
@@ -0,0 +1,363 @@
+/*
+ * 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;
+
+	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;
+
+	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;
+}
-- 
2.1.0

  reply	other threads:[~2015-11-09  0:11 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-11-09  0:10 [PATCH RFC 0/6] NCSI Support Gavin Shan
2015-11-09  0:10 ` Gavin Shan [this message]
2015-11-09  0:10 ` [PATCH RFC 2/6] net/ncsi: Packet handler Gavin Shan
2015-11-09  0:41   ` Benjamin Herrenschmidt
2015-11-09  0:10 ` [PATCH RFC 3/6] net/ncsi: Manage NCSI device Gavin Shan
2015-11-09  0:10 ` [PATCH RFC 4/6] net/faraday: Replace use_nc_si with use_ncsi Gavin Shan
2015-11-09  0:30   ` Benjamin Herrenschmidt
2015-11-09  0:45     ` Gavin Shan
2015-11-09  0:10 ` [PATCH RFC 5/6] net/faraday: Enable NCSI interface Gavin Shan
2015-11-09  0:32   ` Benjamin Herrenschmidt
2015-11-09  7:30     ` Gavin Shan
2015-11-09 20:28       ` Benjamin Herrenschmidt
2015-11-10  6:12         ` Gavin Shan
2015-11-10 10:34           ` Benjamin Herrenschmidt
2015-11-09  0:10 ` [PATCH RFC 6/6] net/faraday: Enable offload checksum according to device-tree Gavin Shan
2015-11-09  0:36   ` Benjamin Herrenschmidt
2015-11-09  0:45     ` Gavin Shan
2016-02-24  2:59 ` [PATCH RFC 0/6] NCSI Support Gavin Shan
2016-02-24 14:49   ` David Miller

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1447027806-4744-2-git-send-email-gwshan@linux.vnet.ibm.com \
    --to=gwshan@linux.vnet.ibm.com \
    --cc=benh@kernel.crashing.org \
    --cc=davem@davemloft.net \
    --cc=netdev@vger.kernel.org \
    --cc=sergei.shtylyov@cogentembedded.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.