[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1444100989-3437-2-git-send-email-gwshan@linux.vnet.ibm.com>
Date: Tue, 6 Oct 2015 14:09:43 +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 1/7] net/ncsi: Resource management
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@...ux.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 --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;
+}
--
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