* [PATCH net-next v2 00/10] NCSI Support
@ 2016-07-15 10:44 Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 01/10] net/ncsi: Resource management Gavin Shan
` (9 more replies)
0 siblings, 10 replies; 19+ messages in thread
From: Gavin Shan @ 2016-07-15 10:44 UTC (permalink / raw)
To: netdev
Cc: davem, benh, joel, gerlitz.or, weixue, yuvali,
alexei.starovoitov, f.fainelli, Gavin Shan
This series rebases on David's linux-net git repo ("master" branch). It's
to support NCSI stack on drivers/net/ethernet/faraday/ftgmac100.c. The
implementation is based on NCSI spec (version: 1.1.0):
https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
As the following figure shows and defined in NCSI spec:
* The NC-SI (aka NCSI) is defined as the interface between a (Base)
Management Controller (BMC) and one or multiple Network Interface
Controlers (NIC) on host side. The interface is responsible for providing
external network connectivity for BMC.
* Each BMC can connect to multiple packages, up to 8. Each package can have
multiple channels, up to 32. Every package and channel are identified by
3-bits and 5-bits in NCSI packet.
* NCSI packet, encapsulated in ethernet frame, has 0x88F8 in the protocol
field. The destination MAC address should be 0xFF's while the source MAC
address can be arbitrary one.
* NCSI packets are classified to command, response, AEN (Asynchronous Event Notification).
Commands are sent from BMC to host (NIC) for configuration and
information retrival. Responses, corresponding to commands, are sent from
host to BMC for confirmation and requested information. One command should
have one and only one response. AEN is sent from host to BMC for notification
(e.g. link down on active channel) so that BMC can take appropriate action.
+------------------+ +----------------------------------------------+
| | | Host |
| BMC | | |
| | | +-------------------+ +-------------------+ |
| +---------+ | | | Package-A | | Package-B | |
| | | | | +---------+---------+ +-------------------+ |
| |ftgmac100| | | | Channel | Channel | | Channel | Channel | |
+----+----+----+---+ +-+---------+---------+--+---------+---------+-+
| | |
| | |
+-----------------------------+----------------------+
The series of patches is highlighted as:
The design for the patchset is highlighted as below:
* The network driver uses 3 interfaces exported from NCSI stack:
ncsi_register_dev() - Register (create) a associated NCSI device.
ncsi_start_dev() - Bring up the NCSI device.
ncsi_unregister_dev() - Destroy the registered NCSI device.
* There are several data structures introduced for different objects:
struct ncsi_dev - NCSI device seen by network device driver.
struct ncsi_dev_priv - NCSI device seen by NCSI stack.
struct ncsi_package - NCSI package which can have multiple channels.
struct ncsi_channel - NCSI channel.
* The NCSI stack is driven by workqueue and state machine internally.
* The all available NCSI packages and channels are enumerated (probed) on
the first call to ncsi_start_dev(). The NCSI topology won't change until
the NCSI device is destroyed.
* All available channels will be brought up When the hardware arbitration
is enabled. Otherwise, only one channel is selected as active one. The
NCSI internal is driven by state machine with help of a workqueue. In
the meanwhile, there are 3 states for each channel which can be put into
a queue requesting for configuration or suspending. Channels in the queue
with inactive state set will be configured (bringup) while channels in
the queue with active state will be suspended (teardown). The request
configuration or suspending is being applied on the channel if it's in
invisible state.
* Failover, another inactive channel is selected as active, can happen when
the hardware arbitration is disabled. The failover can be caused by timeout
on link monitor and AEN.
* NCSI stack should be configurable through netlink or another mechanism, it's
not implemented in this patchset. It's something TBD.
* The first NIC driver that is aware of NCSI: drivers/net/ethernet/faraday/ftgmac100.c
Changelog
=========
v1 -> v2:
* Support NCSI spec v1.1.0 (3 more commands and 4 hardware arbitration
modes added).
* Enable AEN packets according to the supported list.
* Introduce NCSI channel states and processing queue in order to support
the hardware arbitration.
* The hardware arbitration is supported (tested with emulated environment).
* Introduce link monitor with GLS (Get Link Status) command/response as part
of the error handling defined in NCSI spec.
* Support IPv6 address discovery when CONFIG_IPV6 is enabled.
Gavin Shan (10):
net/ncsi: Resource management
net/ncsi: NCSI command packet handler
net/ncsi: NCSI response packet handler
net/ncsi: Package and channel management
net/ncsi: NCSI AEN packet handler
net/faraday: Helper functions to create or destroy MDIO interface
net/faraday: Read MAC address from chip
net/faraday: Support NCSI mode
net/faraday: Match driver according to compatible property
net/faraday: Mask PHY interrupt with NCSI mode
drivers/net/ethernet/faraday/ftgmac100.c | 280 +++++--
include/net/ncsi.h | 52 ++
net/Kconfig | 1 +
net/Makefile | 1 +
net/ncsi/Kconfig | 12 +
net/ncsi/Makefile | 4 +
net/ncsi/internal.h | 328 ++++++++
net/ncsi/ncsi-aen.c | 193 +++++
net/ncsi/ncsi-cmd.c | 367 +++++++++
net/ncsi/ncsi-manage.c | 1199 ++++++++++++++++++++++++++++++
net/ncsi/ncsi-pkt.h | 415 +++++++++++
net/ncsi/ncsi-rsp.c | 1035 ++++++++++++++++++++++++++
12 files changed, 3824 insertions(+), 63 deletions(-)
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-aen.c
create mode 100644 net/ncsi/ncsi-cmd.c
create mode 100644 net/ncsi/ncsi-manage.c
create mode 100644 net/ncsi/ncsi-pkt.h
create mode 100644 net/ncsi/ncsi-rsp.c
--
2.1.0
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH net-next v2 01/10] net/ncsi: Resource management
2016-07-15 10:44 [PATCH net-next v2 00/10] NCSI Support Gavin Shan
@ 2016-07-15 10:44 ` Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 02/10] net/ncsi: NCSI command packet handler Gavin Shan
` (8 subsequent siblings)
9 siblings, 0 replies; 19+ messages in thread
From: Gavin Shan @ 2016-07-15 10:44 UTC (permalink / raw)
To: netdev
Cc: davem, benh, joel, gerlitz.or, weixue, yuvali,
alexei.starovoitov, f.fainelli, Gavin Shan
NCSI spec (DSP0222) defines several objects: package, channel, mode,
filter, version and statistics etc. This introduces the data structs
to represent those objects and implement functions to manage them.
Also, this introduces CONFIG_NET_NCSI for the newly implemented NCSI
stack.
* 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 are represented by "struct ncsi_channel_version",
"struct ncsi_channel_stats", "struct ncsi_channel_mode" and
"struct ncsi_channel_filter" separately.
* Apart from AEN (Asynchronous Event Notification), the NCSI stack
works in terms of command and response. This introduces "struct
ncsi_req" to represent a complete NCSI transaction made of NCSI
request and response.
link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
---
include/net/ncsi.h | 46 ++++++
net/Kconfig | 1 +
net/Makefile | 1 +
net/ncsi/Kconfig | 12 ++
net/ncsi/Makefile | 4 +
net/ncsi/internal.h | 256 +++++++++++++++++++++++++++++
net/ncsi/ncsi-manage.c | 436 +++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 756 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..70d14ee
--- /dev/null
+++ b/include/net/ncsi.h
@@ -0,0 +1,46 @@
+#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_probe = 0x0200,
+ ncsi_dev_state_config = 0x0300,
+ ncsi_dev_state_suspend = 0x0400,
+};
+
+struct ncsi_dev {
+ int state;
+ int link_up;
+ struct net_device *dev;
+ void (*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));
+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 void ncsi_unregister_dev(struct ncsi_dev *nd)
+{
+}
+#endif /* CONFIG_NET_NCSI */
+
+#endif /* __NET_NCSI_H */
diff --git a/net/Kconfig b/net/Kconfig
index ff40562..c2cdbce 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -237,6 +237,7 @@ source "net/hsr/Kconfig"
source "net/switchdev/Kconfig"
source "net/l3mdev/Kconfig"
source "net/qrtr/Kconfig"
+source "net/ncsi/Kconfig"
config RPS
bool
diff --git a/net/Makefile b/net/Makefile
index bdd1455..9bd20bb 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -79,3 +79,4 @@ ifneq ($(CONFIG_NET_L3_MASTER_DEV),)
obj-y += l3mdev/
endif
obj-$(CONFIG_QRTR) += qrtr/
+obj-$(CONFIG_NET_NCSI) += ncsi/
diff --git a/net/ncsi/Kconfig b/net/ncsi/Kconfig
new file mode 100644
index 0000000..08a8a60
--- /dev/null
+++ b/net/ncsi/Kconfig
@@ -0,0 +1,12 @@
+#
+# Configuration for NCSI support
+#
+
+config NET_NCSI
+ bool "NCSI interface support"
+ depends on INET
+ ---help---
+ This module provides NCSI (Network Controller Sideband Interface)
+ support. Enable this only if your system connects to a network
+ device via NCSI and the ethernet driver you're using supports
+ the protocol explicitly.
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..89028e1
--- /dev/null
+++ b/net/ncsi/internal.h
@@ -0,0 +1,256 @@
+/*
+ * Copyright Gavin Shan, IBM Corporation 2016.
+ *
+ * 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.
+ */
+
+#ifndef __NCSI_INTERNAL_H__
+#define __NCSI_INTERNAL_H__
+
+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 MC filtering */
+ NCSI_CAP_GENERIC_HWA_UNKNOWN = 0x00, /* Unknown HW arbitration */
+ NCSI_CAP_GENERIC_HWA_SUPPORT = 0x20, /* Supported HW arbitration */
+ NCSI_CAP_GENERIC_HWA_NOT_SUPPORT = 0x40, /* No HW arbitration */
+ NCSI_CAP_GENERIC_HWA_RESERVED = 0x60, /* Reserved HW arbitration */
+ NCSI_CAP_GENERIC_HWA_MASK = 0x60, /* Mask for HW arbitration */
+ NCSI_CAP_GENERIC_MASK = 0x7f,
+ 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_IPV6_NEIGHBOR = 0x01, /* IPv6 neighbor filtering */
+ NCSI_CAP_MC_IPV6_ROUTER = 0x02, /* IPv6 router filering */
+ NCSI_CAP_MC_DHCPV6_RELAY = 0x04, /* DHCPv6 relay / server MC */
+ NCSI_CAP_MC_DHCPV6_WELL_KNOWN = 0x08, /* DHCPv6 well-known MC */
+ NCSI_CAP_MC_IPV6_MLD = 0x10, /* IPv6 MLD filtering */
+ NCSI_CAP_MC_IPV6_NEIGHBOR_S = 0x20, /* IPv6 neighbour filtering */
+ NCSI_CAP_MC_MASK = 0x3f,
+ NCSI_CAP_AEN_LSC = 0x01, /* Link status change */
+ NCSI_CAP_AEN_CR = 0x02, /* Configuration required */
+ NCSI_CAP_AEN_HDS = 0x04, /* HNC driver status */
+ NCSI_CAP_AEN_MASK = 0x07,
+ NCSI_CAP_VLAN_ONLY = 0x01, /* Filter VLAN packet only */
+ 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
+};
+
+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
+};
+
+enum {
+ NCSI_FILTER_BASE = 0,
+ NCSI_FILTER_VLAN = 0,
+ NCSI_FILTER_UC,
+ NCSI_FILTER_MC,
+ NCSI_FILTER_MIXED,
+ NCSI_FILTER_MAX
+};
+
+struct ncsi_channel_version {
+ u32 version; /* Supported BCD encoded NCSI version */
+ u32 alpha2; /* Supported BCD encoded NCSI version */
+ u8 fw_name[12]; /* Firware name string */
+ u32 fw_version; /* Firmware version */
+ u16 pci_ids[4]; /* PCI identification */
+ u32 mf_id; /* Manufacture ID */
+};
+
+struct ncsi_channel_cap {
+ u32 index; /* Index of channel capabilities */
+ u32 cap; /* NCSI channel capability */
+};
+
+struct ncsi_channel_mode {
+ u32 index; /* Index of channel modes */
+ u32 enable; /* Enabled or disabled */
+ u32 size; /* Valid entries in ncm_data[] */
+ u32 data[8]; /* Data entries */
+};
+
+struct ncsi_channel_filter {
+ u32 index; /* Index of channel filters */
+ u32 total; /* Total entries in the filter table */
+ u64 bitmap; /* Bitmap of valid entries */
+ u32 data[]; /* Data for the valid entries */
+};
+
+struct ncsi_channel_stats {
+ u32 hnc_cnt_hi; /* Counter cleared */
+ u32 hnc_cnt_lo; /* Counter cleared */
+ u32 hnc_rx_bytes; /* Rx bytes */
+ u32 hnc_tx_bytes; /* Tx bytes */
+ u32 hnc_rx_uc_pkts; /* Rx UC packets */
+ u32 hnc_rx_mc_pkts; /* Rx MC packets */
+ u32 hnc_rx_bc_pkts; /* Rx BC packets */
+ u32 hnc_tx_uc_pkts; /* Tx UC packets */
+ u32 hnc_tx_mc_pkts; /* Tx MC packets */
+ u32 hnc_tx_bc_pkts; /* Tx BC packets */
+ u32 hnc_fcs_err; /* FCS errors */
+ u32 hnc_align_err; /* Alignment errors */
+ u32 hnc_false_carrier; /* False carrier detection */
+ u32 hnc_runt_pkts; /* Rx runt packets */
+ u32 hnc_jabber_pkts; /* Rx jabber packets */
+ u32 hnc_rx_pause_xon; /* Rx pause XON frames */
+ u32 hnc_rx_pause_xoff; /* Rx XOFF frames */
+ u32 hnc_tx_pause_xon; /* Tx XON frames */
+ u32 hnc_tx_pause_xoff; /* Tx XOFF frames */
+ u32 hnc_tx_s_collision; /* Single collision frames */
+ u32 hnc_tx_m_collision; /* Multiple collision frames */
+ u32 hnc_l_collision; /* Late collision frames */
+ u32 hnc_e_collision; /* Excessive collision frames */
+ u32 hnc_rx_ctl_frames; /* Rx control frames */
+ u32 hnc_rx_64_frames; /* Rx 64-bytes frames */
+ u32 hnc_rx_127_frames; /* Rx 65-127 bytes frames */
+ u32 hnc_rx_255_frames; /* Rx 128-255 bytes frames */
+ u32 hnc_rx_511_frames; /* Rx 256-511 bytes frames */
+ u32 hnc_rx_1023_frames; /* Rx 512-1023 bytes frames */
+ u32 hnc_rx_1522_frames; /* Rx 1024-1522 bytes frames */
+ u32 hnc_rx_9022_frames; /* Rx 1523-9022 bytes frames */
+ u32 hnc_tx_64_frames; /* Tx 64-bytes frames */
+ u32 hnc_tx_127_frames; /* Tx 65-127 bytes frames */
+ u32 hnc_tx_255_frames; /* Tx 128-255 bytes frames */
+ u32 hnc_tx_511_frames; /* Tx 256-511 bytes frames */
+ u32 hnc_tx_1023_frames; /* Tx 512-1023 bytes frames */
+ u32 hnc_tx_1522_frames; /* Tx 1024-1522 bytes frames */
+ u32 hnc_tx_9022_frames; /* Tx 1523-9022 bytes frames */
+ u32 hnc_rx_valid_bytes; /* Rx valid bytes */
+ u32 hnc_rx_runt_pkts; /* Rx error runt packets */
+ u32 hnc_rx_jabber_pkts; /* Rx error jabber packets */
+ u32 ncsi_rx_cmds; /* Rx NCSI commands */
+ u32 ncsi_dropped_cmds; /* Dropped commands */
+ u32 ncsi_cmd_type_errs; /* Command type errors */
+ u32 ncsi_cmd_csum_errs; /* Command checksum errors */
+ u32 ncsi_rx_pkts; /* Rx NCSI packets */
+ u32 ncsi_tx_pkts; /* Tx NCSI packets */
+ u32 ncsi_tx_aen_pkts; /* Tx AEN packets */
+ u32 pt_tx_pkts; /* Tx packets */
+ u32 pt_tx_dropped; /* Tx dropped packets */
+ u32 pt_tx_channel_err; /* Tx channel errors */
+ u32 pt_tx_us_err; /* Tx undersize errors */
+ u32 pt_rx_pkts; /* Rx packets */
+ u32 pt_rx_dropped; /* Rx dropped packets */
+ u32 pt_rx_channel_err; /* Rx channel errors */
+ u32 pt_rx_us_err; /* Rx undersize errors */
+ u32 pt_rx_os_err; /* Rx oversize errors */
+};
+
+struct ncsi_dev_priv;
+struct ncsi_package;
+
+#define NCSI_PACKAGE_SHIFT 5
+#define NCSI_PACKAGE_INDEX(c) (((c) >> NCSI_PACKAGE_SHIFT) & 0x7)
+#define NCSI_CHANNEL_INDEX(c) ((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1))
+#define NCSI_TO_CHANNEL(p, c) (((p) << NCSI_PACKAGE_SHIFT) | (c))
+
+struct ncsi_channel {
+ unsigned char id;
+ int state;
+#define NCSI_CHANNEL_INACTIVE 1
+#define NCSI_CHANNEL_ACTIVE 2
+ spinlock_t lock; /* Protect filters etc */
+ struct ncsi_package *package;
+ struct ncsi_channel_version version;
+ struct ncsi_channel_cap caps[NCSI_CAP_MAX];
+ struct ncsi_channel_mode modes[NCSI_MODE_MAX];
+ struct ncsi_channel_filter *filters[NCSI_FILTER_MAX];
+ struct ncsi_channel_stats stats;
+ struct list_head node;
+};
+
+struct ncsi_package {
+ unsigned char id; /* NCSI 3-bits package ID */
+ unsigned char uuid[16]; /* UUID */
+ struct ncsi_dev_priv *ndp; /* NCSI device */
+ spinlock_t lock; /* Protect the package */
+ unsigned int channel_num; /* Number of channels */
+ struct list_head channels; /* List of chanels */
+ struct list_head node; /* Form list of packages */
+};
+
+struct ncsi_request {
+ unsigned char id; /* Request ID - 0 to 255 */
+ bool used; /* Request that has been assigned */
+ bool driven; /* Drive state machine */
+ struct ncsi_dev_priv *ndp; /* Associated NCSI device */
+ struct sk_buff *cmd; /* Associated NCSI command packet */
+ struct sk_buff *rsp; /* Associated NCSI response packet */
+ struct timer_list timer; /* Timer on waiting for response */
+ bool enabled; /* Time has been enabled or not */
+};
+
+struct ncsi_dev_priv {
+ struct ncsi_dev ndev; /* Associated NCSI device */
+ unsigned int flags; /* NCSI device flags */
+ spinlock_t lock; /* Protect the NCSI device */
+ unsigned int package_num; /* Number of packages */
+ struct list_head packages; /* List of packages */
+ struct ncsi_request requests[256]; /* Request table */
+ unsigned int request_id; /* Last used request ID */
+ struct list_head node; /* Form NCSI device list */
+};
+
+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, ndev)
+#define NCSI_FOR_EACH_DEV(ndp) \
+ list_for_each_entry_rcu(ndp, &ncsi_dev_list, node)
+#define NCSI_FOR_EACH_PACKAGE(ndp, np) \
+ list_for_each_entry_rcu(np, &ndp->packages, node)
+#define NCSI_FOR_EACH_CHANNEL(np, nc) \
+ list_for_each_entry_rcu(nc, &np->channels, node)
+
+/* Resources */
+int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data);
+int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data);
+int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index);
+struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
+ unsigned char id);
+struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np,
+ unsigned char id);
+struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
+ unsigned char id);
+struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
+ unsigned char id);
+void ncsi_remove_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_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, bool driven);
+void ncsi_free_request(struct ncsi_request *nr);
+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..0e28ed8
--- /dev/null
+++ b/net/ncsi/ncsi-manage.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright Gavin Shan, IBM Corporation 2016.
+ *
+ * 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);
+
+static inline int ncsi_filter_size(int table)
+{
+ int sizes[] = { 2, 6, 6, 6 };
+
+ BUILD_BUG_ON(ARRAY_SIZE(sizes) != NCSI_FILTER_MAX);
+ if (table < NCSI_FILTER_BASE || table >= NCSI_FILTER_MAX)
+ return -EINVAL;
+
+ return sizes[table];
+}
+
+int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data)
+{
+ struct ncsi_channel_filter *ncf;
+ void *bitmap;
+ int index, size;
+ unsigned long flags;
+
+ ncf = nc->filters[table];
+ if (!ncf)
+ return -ENXIO;
+
+ size = ncsi_filter_size(table);
+ if (size < 0)
+ return size;
+
+ spin_lock_irqsave(&nc->lock, flags);
+ bitmap = (void *)&ncf->bitmap;
+ index = -1;
+ while ((index = find_next_bit(bitmap, ncf->total, index + 1))
+ < ncf->total) {
+ if (!memcmp(ncf->data + size * index, data, size)) {
+ spin_unlock_irqrestore(&nc->lock, flags);
+ return index;
+ }
+ }
+ spin_unlock_irqrestore(&nc->lock, flags);
+
+ return -ENOENT;
+}
+
+int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data)
+{
+ struct ncsi_channel_filter *ncf;
+ int index, size;
+ void *bitmap;
+ unsigned long flags;
+
+ size = ncsi_filter_size(table);
+ if (size < 0)
+ return size;
+
+ index = ncsi_find_filter(nc, table, data);
+ if (index >= 0)
+ return index;
+
+ ncf = nc->filters[table];
+ if (!ncf)
+ return -ENODEV;
+
+ spin_lock_irqsave(&nc->lock, flags);
+ bitmap = (void *)&ncf->bitmap;
+ do {
+ index = find_next_zero_bit(bitmap, ncf->total, 0);
+ if (index >= ncf->total) {
+ spin_unlock_irqrestore(&nc->lock, flags);
+ return -ENOSPC;
+ }
+ } while (test_and_set_bit(index, bitmap));
+
+ memcpy(ncf->data + size * index, data, size);
+ spin_unlock_irqrestore(&nc->lock, flags);
+
+ return index;
+}
+
+int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index)
+{
+ struct ncsi_channel_filter *ncf;
+ int size;
+ void *bitmap;
+ unsigned long flags;
+
+ size = ncsi_filter_size(table);
+ if (size < 0)
+ return size;
+
+ ncf = nc->filters[table];
+ if (!ncf || index >= ncf->total)
+ return -ENODEV;
+
+ spin_lock_irqsave(&nc->lock, flags);
+ bitmap = (void *)&ncf->bitmap;
+ if (test_and_clear_bit(index, bitmap))
+ memset(ncf->data + size * index, 0, size);
+ spin_unlock_irqrestore(&nc->lock, flags);
+
+ return 0;
+}
+
+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->id == id)
+ return nc;
+ }
+
+ return NULL;
+}
+
+struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
+{
+ struct ncsi_channel *nc, *tmp;
+ int index;
+ unsigned long flags;
+
+ nc = kzalloc(sizeof(*nc), GFP_ATOMIC);
+ if (!nc)
+ return NULL;
+
+ nc->id = id;
+ nc->package = np;
+ nc->state = NCSI_CHANNEL_INACTIVE;
+ spin_lock_init(&nc->lock);
+ for (index = 0; index < NCSI_CAP_MAX; index++)
+ nc->caps[index].index = index;
+ for (index = 0; index < NCSI_MODE_MAX; index++)
+ nc->modes[index].index = index;
+
+ spin_lock_irqsave(&np->lock, flags);
+ tmp = ncsi_find_channel(np, id);
+ if (tmp) {
+ spin_unlock_irqrestore(&np->lock, flags);
+ kfree(nc);
+ return tmp;
+ }
+
+ list_add_tail_rcu(&nc->node, &np->channels);
+ np->channel_num++;
+ spin_unlock_irqrestore(&np->lock, flags);
+
+ return nc;
+}
+
+static void ncsi_remove_channel(struct ncsi_channel *nc)
+{
+ struct ncsi_package *np = nc->package;
+ struct ncsi_channel_filter *ncf;
+ unsigned long flags;
+ int i;
+
+ /* Release filters */
+ spin_lock_irqsave(&nc->lock, flags);
+ for (i = 0; i < NCSI_FILTER_MAX; i++) {
+ ncf = nc->filters[i];
+ if (!ncf)
+ continue;
+
+ nc->filters[i] = NULL;
+ kfree(ncf);
+ }
+
+ nc->state = NCSI_CHANNEL_INACTIVE;
+ spin_unlock_irqrestore(&nc->lock, flags);
+
+ /* Remove and free channel */
+ spin_lock_irqsave(&np->lock, flags);
+ list_del_rcu(&nc->node);
+ np->channel_num--;
+ spin_unlock_irqrestore(&np->lock, flags);
+
+ kfree(nc);
+}
+
+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->id == id)
+ return np;
+ }
+
+ return NULL;
+}
+
+struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
+ unsigned char id)
+{
+ struct ncsi_package *np, *tmp;
+ unsigned long flags;
+
+ np = kzalloc(sizeof(*np), GFP_ATOMIC);
+ if (!np)
+ return NULL;
+
+ np->id = id;
+ np->ndp = ndp;
+ spin_lock_init(&np->lock);
+ INIT_LIST_HEAD(&np->channels);
+
+ spin_lock_irqsave(&ndp->lock, flags);
+ tmp = ncsi_find_package(ndp, id);
+ if (tmp) {
+ spin_unlock_irqrestore(&ndp->lock, flags);
+ kfree(np);
+ return tmp;
+ }
+
+ list_add_tail_rcu(&np->node, &ndp->packages);
+ ndp->package_num++;
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ return np;
+}
+
+void ncsi_remove_package(struct ncsi_package *np)
+{
+ struct ncsi_dev_priv *ndp = np->ndp;
+ struct ncsi_channel *nc, *tmp;
+ unsigned long flags;
+
+ /* Release all child channels */
+ list_for_each_entry_safe(nc, tmp, &np->channels, node)
+ ncsi_remove_channel(nc);
+
+ /* Remove and free package */
+ spin_lock_irqsave(&ndp->lock, flags);
+ list_del_rcu(&np->node);
+ ndp->package_num--;
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ kfree(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_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 consecutive 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 fashion.
+ */
+struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, bool driven)
+{
+ struct ncsi_request *nr = NULL;
+ int i, limit = ARRAY_SIZE(ndp->requests);
+ unsigned long flags;
+
+ /* Check if there is one available request until the ceiling */
+ spin_lock_irqsave(&ndp->lock, flags);
+ for (i = ndp->request_id; !nr && i < limit; i++) {
+ if (ndp->requests[i].used)
+ continue;
+
+ nr = &ndp->requests[i];
+ nr->used = true;
+ nr->driven = driven;
+ if (++ndp->request_id >= limit)
+ ndp->request_id = 0;
+ }
+
+ /* Fail back to check from the starting cursor */
+ for (i = 0; !nr && i < ndp->request_id; i++) {
+ if (ndp->requests[i].used)
+ continue;
+
+ nr = &ndp->requests[i];
+ nr->used = true;
+ nr->driven = driven;
+ if (++ndp->request_id >= limit)
+ ndp->request_id = 0;
+ }
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ return nr;
+}
+
+void ncsi_free_request(struct ncsi_request *nr)
+{
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct sk_buff *cmd, *rsp;
+ unsigned long flags;
+
+ if (nr->enabled) {
+ nr->enabled = false;
+ del_timer_sync(&nr->timer);
+ }
+
+ spin_lock_irqsave(&ndp->lock, flags);
+ cmd = nr->cmd;
+ rsp = nr->rsp;
+ nr->cmd = NULL;
+ nr->rsp = NULL;
+ nr->used = false;
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ /* 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->ndev.dev == dev)
+ return &ndp->ndev;
+ }
+
+ return NULL;
+}
+
+static void ncsi_request_timeout(unsigned long data)
+{
+ struct ncsi_request *nr = (struct ncsi_request *)data;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ unsigned long flags;
+
+ /* If the request already had associated response,
+ * let the response handler to release it.
+ */
+ spin_lock_irqsave(&ndp->lock, flags);
+ nr->enabled = false;
+ if (nr->rsp || !nr->cmd) {
+ spin_unlock_irqrestore(&ndp->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ /* Release the request */
+ ncsi_free_request(nr);
+}
+
+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;
+ unsigned long flags;
+ int i;
+
+ /* 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)
+ return NULL;
+
+ nd = &ndp->ndev;
+ nd->state = ncsi_dev_state_registered;
+ nd->dev = dev;
+ nd->handler = handler;
+
+ /* Initialize private NCSI device */
+ spin_lock_init(&ndp->lock);
+ INIT_LIST_HEAD(&ndp->packages);
+ ndp->request_id = 0;
+ for (i = 0; i < ARRAY_SIZE(ndp->requests); i++) {
+ ndp->requests[i].id = i;
+ ndp->requests[i].ndp = ndp;
+ setup_timer(&ndp->requests[i].timer,
+ ncsi_request_timeout,
+ (unsigned long)&ndp->requests[i]);
+ }
+
+ spin_lock_irqsave(&ncsi_dev_lock, flags);
+ list_add_tail_rcu(&ndp->node, &ncsi_dev_list);
+ spin_unlock_irqrestore(&ncsi_dev_lock, flags);
+
+ return nd;
+}
+EXPORT_SYMBOL_GPL(ncsi_register_dev);
+
+void ncsi_unregister_dev(struct ncsi_dev *nd)
+{
+ struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
+ struct ncsi_package *np, *tmp;
+ unsigned long flags;
+
+ list_for_each_entry_safe(np, tmp, &ndp->packages, node)
+ ncsi_remove_package(np);
+
+ spin_lock_irqsave(&ncsi_dev_lock, flags);
+ list_del_rcu(&ndp->node);
+ spin_unlock_irqrestore(&ncsi_dev_lock, flags);
+
+ kfree(ndp);
+}
+EXPORT_SYMBOL_GPL(ncsi_unregister_dev);
--
2.1.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 02/10] net/ncsi: NCSI command packet handler
2016-07-15 10:44 [PATCH net-next v2 00/10] NCSI Support Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 01/10] net/ncsi: Resource management Gavin Shan
@ 2016-07-15 10:44 ` Gavin Shan
2016-07-15 14:08 ` kbuild test robot
2016-07-15 10:44 ` [PATCH net-next v2 03/10] net/ncsi: NCSI response " Gavin Shan
` (7 subsequent siblings)
9 siblings, 1 reply; 19+ messages in thread
From: Gavin Shan @ 2016-07-15 10:44 UTC (permalink / raw)
To: netdev
Cc: davem, benh, joel, gerlitz.or, weixue, yuvali,
alexei.starovoitov, f.fainelli, Gavin Shan
The NCSI command packets are sent from MC (Management Controller)
to remote end. They are used for multiple purposes: probe existing
NCSI package/channel, retrieve NCSI channel's capability, configure
NCSI channel etc.
This defines struct to represent NCSI command packets and introduces
function ncsi_xmit_cmd(), which will be used to transmit NCSI command
packet according to the request. The request is represented by struct
ncsi_cmd_arg.
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
---
net/ncsi/Makefile | 2 +-
net/ncsi/internal.h | 19 +++
net/ncsi/ncsi-cmd.c | 367 ++++++++++++++++++++++++++++++++++++++++++++++++++++
net/ncsi/ncsi-pkt.h | 171 ++++++++++++++++++++++++
4 files changed, 558 insertions(+), 1 deletion(-)
create mode 100644 net/ncsi/ncsi-cmd.c
create mode 100644 net/ncsi/ncsi-pkt.h
diff --git a/net/ncsi/Makefile b/net/ncsi/Makefile
index 07b5625..abc4046 100644
--- a/net/ncsi/Makefile
+++ b/net/ncsi/Makefile
@@ -1,4 +1,4 @@
#
# Makefile for NCSI API
#
-obj-$(CONFIG_NET_NCSI) += ncsi-manage.o
+obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-manage.o
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index 89028e1..3d81697 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -220,6 +220,21 @@ struct ncsi_dev_priv {
struct list_head node; /* Form NCSI device list */
};
+struct ncsi_cmd_arg {
+ struct ncsi_dev_priv *ndp; /* Associated NCSI device */
+ unsigned char type; /* Command in the NCSI packet */
+ unsigned char id; /* Request ID (sequence number) */
+ unsigned char package; /* Destination package ID */
+ unsigned char channel; /* Detination channel ID or 0x1f */
+ unsigned short payload; /* Command packet payload length */
+ bool driven; /* Drive the state machine? */
+ union {
+ unsigned char bytes[16]; /* Command packet specific data */
+ unsigned short words[8];
+ unsigned int dwords[4];
+ };
+};
+
extern struct list_head ncsi_dev_list;
extern spinlock_t ncsi_dev_lock;
@@ -253,4 +268,8 @@ struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, bool driven);
void ncsi_free_request(struct ncsi_request *nr);
struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
+/* Packet handlers */
+u32 ncsi_calculate_checksum(unsigned char *data, int len);
+int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
+
#endif /* __NCSI_INTERNAL_H__ */
diff --git a/net/ncsi/ncsi-cmd.c b/net/ncsi/ncsi-cmd.c
new file mode 100644
index 0000000..21057a8
--- /dev/null
+++ b/net/ncsi/ncsi-cmd.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright Gavin Shan, IBM Corporation 2016.
+ *
+ * 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/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+
+#include <net/ncsi.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "internal.h"
+#include "ncsi-pkt.h"
+
+u32 ncsi_calculate_checksum(unsigned char *data, int len)
+{
+ u32 checksum = 0;
+ int i;
+
+ for (i = 0; i < len; i += 2)
+ checksum += (((u32)data[i] << 8) | data[i + 1]);
+
+ checksum = (~checksum + 1);
+ return checksum;
+}
+
+/* This function should be called after the data area has been
+ * populated completely.
+ */
+static void ncsi_cmd_build_header(struct ncsi_pkt_hdr *h,
+ struct ncsi_cmd_arg *nca)
+{
+ u32 checksum;
+ __be32 *pchecksum;
+
+ h->mc_id = 0;
+ h->revision = NCSI_PKT_REVISION;
+ h->reserved = 0;
+ h->id = nca->id;
+ h->type = nca->type;
+ h->channel = NCSI_TO_CHANNEL(nca->package,
+ nca->channel);
+ h->length = htons(nca->payload);
+ h->reserved1[0] = 0;
+ h->reserved1[1] = 0;
+
+ /* Fill with calculated checksum */
+ checksum = ncsi_calculate_checksum((unsigned char *)h,
+ sizeof(*h) + nca->payload);
+ pchecksum = (__be32 *)((void *)h + sizeof(struct ncsi_pkt_hdr) +
+ nca->payload);
+ *pchecksum = htonl(checksum);
+}
+
+static int ncsi_cmd_handler_default(struct sk_buff *skb,
+ struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_cmd_pkt *cmd;
+
+ cmd = (struct ncsi_cmd_pkt *)skb_put(skb, sizeof(*cmd));
+ memset(cmd, 0, sizeof(*cmd));
+ ncsi_cmd_build_header(&cmd->cmd.common, nca);
+
+ return 0;
+}
+
+static int ncsi_cmd_handler_sp(struct sk_buff *skb,
+ struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_cmd_sp_pkt *cmd;
+
+ cmd = (struct ncsi_cmd_sp_pkt *)skb_put(skb, sizeof(*cmd));
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->hw_arbitration = nca->bytes[0];
+ ncsi_cmd_build_header(&cmd->cmd.common, nca);
+
+ return 0;
+}
+
+static int ncsi_cmd_handler_dc(struct sk_buff *skb,
+ struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_cmd_dc_pkt *cmd;
+
+ cmd = (struct ncsi_cmd_dc_pkt *)skb_put(skb, sizeof(*cmd));
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->ald = nca->bytes[0];
+ ncsi_cmd_build_header(&cmd->cmd.common, nca);
+
+ return 0;
+}
+
+static int ncsi_cmd_handler_rc(struct sk_buff *skb,
+ struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_cmd_rc_pkt *cmd;
+
+ cmd = (struct ncsi_cmd_rc_pkt *)skb_put(skb, sizeof(*cmd));
+ memset(cmd, 0, sizeof(*cmd));
+ ncsi_cmd_build_header(&cmd->cmd.common, nca);
+
+ return 0;
+}
+
+static int ncsi_cmd_handler_ae(struct sk_buff *skb,
+ struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_cmd_ae_pkt *cmd;
+
+ cmd = (struct ncsi_cmd_ae_pkt *)skb_put(skb, sizeof(*cmd));
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->mc_id = nca->bytes[0];
+ cmd->mode = htonl(nca->dwords[1]);
+ ncsi_cmd_build_header(&cmd->cmd.common, nca);
+
+ return 0;
+}
+
+static int ncsi_cmd_handler_sl(struct sk_buff *skb,
+ struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_cmd_sl_pkt *cmd;
+
+ cmd = (struct ncsi_cmd_sl_pkt *)skb_put(skb, sizeof(*cmd));
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->mode = htonl(nca->dwords[0]);
+ cmd->oem_mode = htonl(nca->dwords[1]);
+ ncsi_cmd_build_header(&cmd->cmd.common, nca);
+
+ return 0;
+}
+
+static int ncsi_cmd_handler_svf(struct sk_buff *skb,
+ struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_cmd_svf_pkt *cmd;
+
+ cmd = (struct ncsi_cmd_svf_pkt *)skb_put(skb, sizeof(*cmd));
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->vlan = htons(nca->words[0]);
+ cmd->index = nca->bytes[2];
+ cmd->enable = nca->bytes[3];
+ ncsi_cmd_build_header(&cmd->cmd.common, nca);
+
+ return 0;
+}
+
+static int ncsi_cmd_handler_ev(struct sk_buff *skb,
+ struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_cmd_ev_pkt *cmd;
+
+ cmd = (struct ncsi_cmd_ev_pkt *)skb_put(skb, sizeof(*cmd));
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->mode = nca->bytes[0];
+ ncsi_cmd_build_header(&cmd->cmd.common, nca);
+
+ return 0;
+}
+
+static int ncsi_cmd_handler_sma(struct sk_buff *skb,
+ struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_cmd_sma_pkt *cmd;
+ int i;
+
+ cmd = (struct ncsi_cmd_sma_pkt *)skb_put(skb, sizeof(*cmd));
+ memset(cmd, 0, sizeof(*cmd));
+ for (i = 0; i < 6; i++)
+ cmd->mac[i] = nca->bytes[i];
+ cmd->index = nca->bytes[6];
+ cmd->at_e = nca->bytes[7];
+ ncsi_cmd_build_header(&cmd->cmd.common, nca);
+
+ return 0;
+}
+
+static int ncsi_cmd_handler_ebf(struct sk_buff *skb,
+ struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_cmd_ebf_pkt *cmd;
+
+ cmd = (struct ncsi_cmd_ebf_pkt *)skb_put(skb, sizeof(*cmd));
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->mode = htonl(nca->dwords[0]);
+ ncsi_cmd_build_header(&cmd->cmd.common, nca);
+
+ return 0;
+}
+
+static int ncsi_cmd_handler_egmf(struct sk_buff *skb,
+ struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_cmd_egmf_pkt *cmd;
+
+ cmd = (struct ncsi_cmd_egmf_pkt *)skb_put(skb, sizeof(*cmd));
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->mode = htonl(nca->dwords[0]);
+ ncsi_cmd_build_header(&cmd->cmd.common, nca);
+
+ return 0;
+}
+
+static int ncsi_cmd_handler_snfc(struct sk_buff *skb,
+ struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_cmd_snfc_pkt *cmd;
+
+ cmd = (struct ncsi_cmd_snfc_pkt *)skb_put(skb, sizeof(*cmd));
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->mode = nca->bytes[0];
+ ncsi_cmd_build_header(&cmd->cmd.common, nca);
+
+ return 0;
+}
+
+static struct ncsi_cmd_handler {
+ unsigned char type;
+ int payload;
+ int (*handler)(struct sk_buff *skb,
+ struct ncsi_cmd_arg *nca);
+} ncsi_cmd_handlers[] = {
+ { NCSI_PKT_CMD_CIS, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_SP, 4, ncsi_cmd_handler_sp },
+ { NCSI_PKT_CMD_DP, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_EC, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_DC, 4, ncsi_cmd_handler_dc },
+ { NCSI_PKT_CMD_RC, 4, ncsi_cmd_handler_rc },
+ { NCSI_PKT_CMD_ECNT, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_DCNT, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_AE, 8, ncsi_cmd_handler_ae },
+ { NCSI_PKT_CMD_SL, 8, ncsi_cmd_handler_sl },
+ { NCSI_PKT_CMD_GLS, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_SVF, 4, ncsi_cmd_handler_svf },
+ { NCSI_PKT_CMD_EV, 4, ncsi_cmd_handler_ev },
+ { NCSI_PKT_CMD_DV, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_SMA, 8, ncsi_cmd_handler_sma },
+ { NCSI_PKT_CMD_EBF, 4, ncsi_cmd_handler_ebf },
+ { NCSI_PKT_CMD_DBF, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_EGMF, 4, ncsi_cmd_handler_egmf },
+ { NCSI_PKT_CMD_DGMF, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_SNFC, 4, ncsi_cmd_handler_snfc },
+ { NCSI_PKT_CMD_GVI, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_GC, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_GP, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_GCPS, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_GNS, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_GNPTS, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_GPS, 0, ncsi_cmd_handler_default },
+ { NCSI_PKT_CMD_OEM, 0, NULL },
+ { NCSI_PKT_CMD_PLDM, 0, NULL },
+ { NCSI_PKT_CMD_GPUUID, 0, ncsi_cmd_handler_default }
+};
+
+static struct ncsi_request *ncsi_alloc_command(struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_dev_priv *ndp = nca->ndp;
+ struct ncsi_dev *nd = &ndp->ndev;
+ struct net_device *dev = nd->dev;
+ int hlen = LL_RESERVED_SPACE(dev);
+ int tlen = dev->needed_tailroom;
+ int len = hlen + tlen;
+ struct sk_buff *skb;
+ struct ncsi_request *nr;
+
+ nr = ncsi_alloc_request(ndp, nca->driven);
+ if (!nr)
+ return NULL;
+
+ /* NCSI command packet has 16-bytes header, payload, 4 bytes checksum.
+ * The packet needs padding if its payload is less than 26 bytes to
+ * meet 64 bytes minimal ethernet frame length.
+ */
+ len += sizeof(struct ncsi_cmd_pkt_hdr) + 4;
+ if (nca->payload < 26)
+ len += 26;
+ else
+ len += nca->payload;
+
+ /* Allocate skb */
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+ ncsi_free_request(nr);
+ return NULL;
+ }
+
+ nr->cmd = skb;
+ skb_reserve(skb, hlen);
+ skb_reset_network_header(skb);
+
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_NCSI);
+
+ return nr;
+}
+
+int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
+{
+ struct ncsi_request *nr;
+ struct ethhdr *eh;
+ struct ncsi_cmd_handler *nch = NULL;
+ int i, ret;
+
+ /* Search for the handler */
+ for (i = 0; i < ARRAY_SIZE(ncsi_cmd_handlers); i++) {
+ if (ncsi_cmd_handlers[i].type == nca->type) {
+ if (ncsi_cmd_handlers[i].handler)
+ nch = &ncsi_cmd_handlers[i];
+ else
+ nch = NULL;
+
+ break;
+ }
+ }
+
+ if (!nch) {
+ netdev_err(nca->ndp->ndev.dev,
+ "Cannot send packet with type 0x%02x\n", nca->type);
+ return -ENOENT;
+ }
+
+ /* Get packet payload length and allocate the request */
+ nca->payload = nch->payload;
+ nr = ncsi_alloc_command(nca);
+ if (!nr)
+ return -ENOMEM;
+
+ /* Prepare the packet */
+ nca->id = nr->id;
+ ret = nch->handler(nr->cmd, nca);
+ if (ret) {
+ ncsi_free_request(nr);
+ return ret;
+ }
+
+ /* Fill the ethernet header */
+ eh = (struct ethhdr *)skb_push(nr->cmd, sizeof(*eh));
+ eh->h_proto = htons(ETH_P_NCSI);
+ eth_broadcast_addr(eh->h_dest);
+ eth_broadcast_addr(eh->h_source);
+
+ /* Start the timer for the request that might not have
+ * corresponding response. Given NCSI is an internal
+ * connection a 1 second delay should be sufficient.
+ */
+ nr->enabled = true;
+ mod_timer(&nr->timer, jiffies + 1 * HZ);
+
+ /* Send NCSI packet */
+ skb_get(nr->cmd);
+ ret = dev_queue_xmit(nr->cmd);
+ if (ret < 0) {
+ ncsi_free_request(nr);
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/net/ncsi/ncsi-pkt.h b/net/ncsi/ncsi-pkt.h
new file mode 100644
index 0000000..5481458
--- /dev/null
+++ b/net/ncsi/ncsi-pkt.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright Gavin Shan, IBM Corporation 2016.
+ *
+ * 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.
+ */
+
+#ifndef __NCSI_PKT_H__
+#define __NCSI_PKT_H__
+
+struct ncsi_pkt_hdr {
+ unsigned char mc_id; /* Management controller ID */
+ unsigned char revision; /* NCSI version - 0x01 */
+ unsigned char reserved; /* Reserved */
+ unsigned char id; /* Packet sequence number */
+ unsigned char type; /* Packet type */
+ unsigned char channel; /* Network controller ID */
+ __be16 length; /* Payload length */
+ __be32 reserved1[2]; /* Reserved */
+};
+
+struct ncsi_cmd_pkt_hdr {
+ struct ncsi_pkt_hdr common; /* Common NCSI packet header */
+};
+
+/* NCSI common command packet */
+struct ncsi_cmd_pkt {
+ struct ncsi_cmd_pkt_hdr cmd; /* Command header */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[26];
+};
+
+/* Select Package */
+struct ncsi_cmd_sp_pkt {
+ struct ncsi_cmd_pkt_hdr cmd; /* Command header */
+ unsigned char reserved[3]; /* Reserved */
+ unsigned char hw_arbitration; /* HW arbitration */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[22];
+};
+
+/* Disable Channel */
+struct ncsi_cmd_dc_pkt {
+ struct ncsi_cmd_pkt_hdr cmd; /* Command header */
+ unsigned char reserved[3]; /* Reserved */
+ unsigned char ald; /* Allow link down */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[22];
+};
+
+/* Reset Channel */
+struct ncsi_cmd_rc_pkt {
+ struct ncsi_cmd_pkt_hdr cmd; /* Command header */
+ __be32 reserved; /* Reserved */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[22];
+};
+
+/* AEN Enable */
+struct ncsi_cmd_ae_pkt {
+ struct ncsi_cmd_pkt_hdr cmd; /* Command header */
+ unsigned char reserved[3]; /* Reserved */
+ unsigned char mc_id; /* MC ID */
+ __be32 mode; /* AEN working mode */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[18];
+};
+
+/* Set Link */
+struct ncsi_cmd_sl_pkt {
+ struct ncsi_cmd_pkt_hdr cmd; /* Command header */
+ __be32 mode; /* Link working mode */
+ __be32 oem_mode; /* OEM link mode */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[18];
+};
+
+/* Set VLAN Filter */
+struct ncsi_cmd_svf_pkt {
+ struct ncsi_cmd_pkt_hdr cmd; /* Command header */
+ __be16 reserved; /* Reserved */
+ __be16 vlan; /* VLAN ID */
+ __be16 reserved1; /* Reserved */
+ unsigned char index; /* VLAN table index */
+ unsigned char enable; /* Enable or disable */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[14];
+};
+
+/* Enable VLAN */
+struct ncsi_cmd_ev_pkt {
+ struct ncsi_cmd_pkt_hdr cmd; /* Command header */
+ unsigned char reserved[3]; /* Reserved */
+ unsigned char mode; /* VLAN filter mode */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[22];
+};
+
+/* Set MAC Address */
+struct ncsi_cmd_sma_pkt {
+ struct ncsi_cmd_pkt_hdr cmd; /* Command header */
+ unsigned char mac[6]; /* MAC address */
+ unsigned char index; /* MAC table index */
+ unsigned char at_e; /* Addr type and operation */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[18];
+};
+
+/* Enable Broadcast Filter */
+struct ncsi_cmd_ebf_pkt {
+ struct ncsi_cmd_pkt_hdr cmd; /* Command header */
+ __be32 mode; /* Filter mode */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[22];
+};
+
+/* Enable Global Multicast Filter */
+struct ncsi_cmd_egmf_pkt {
+ struct ncsi_cmd_pkt_hdr cmd; /* Command header */
+ __be32 mode; /* Global MC mode */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[22];
+};
+
+/* Set NCSI Flow Control */
+struct ncsi_cmd_snfc_pkt {
+ struct ncsi_cmd_pkt_hdr cmd; /* Command header */
+ unsigned char reserved[3]; /* Reserved */
+ unsigned char mode; /* Flow control mode */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[22];
+};
+
+/* NCSI packet revision */
+#define NCSI_PKT_REVISION 0x01
+
+/* NCSI packet commands */
+#define NCSI_PKT_CMD_CIS 0x00 /* Clear Initial State */
+#define NCSI_PKT_CMD_SP 0x01 /* Select Package */
+#define NCSI_PKT_CMD_DP 0x02 /* Deselect Package */
+#define NCSI_PKT_CMD_EC 0x03 /* Enable Channel */
+#define NCSI_PKT_CMD_DC 0x04 /* Disable Channel */
+#define NCSI_PKT_CMD_RC 0x05 /* Reset Channel */
+#define NCSI_PKT_CMD_ECNT 0x06 /* Enable Channel Network Tx */
+#define NCSI_PKT_CMD_DCNT 0x07 /* Disable Channel Network Tx */
+#define NCSI_PKT_CMD_AE 0x08 /* AEN Enable */
+#define NCSI_PKT_CMD_SL 0x09 /* Set Link */
+#define NCSI_PKT_CMD_GLS 0x0a /* Get Link */
+#define NCSI_PKT_CMD_SVF 0x0b /* Set VLAN Filter */
+#define NCSI_PKT_CMD_EV 0x0c /* Enable VLAN */
+#define NCSI_PKT_CMD_DV 0x0d /* Disable VLAN */
+#define NCSI_PKT_CMD_SMA 0x0e /* Set MAC address */
+#define NCSI_PKT_CMD_EBF 0x10 /* Enable Broadcast Filter */
+#define NCSI_PKT_CMD_DBF 0x11 /* Disable Broadcast Filter */
+#define NCSI_PKT_CMD_EGMF 0x12 /* Enable Global Multicast Filter */
+#define NCSI_PKT_CMD_DGMF 0x13 /* Disable Global Multicast Filter */
+#define NCSI_PKT_CMD_SNFC 0x14 /* Set NCSI Flow Control */
+#define NCSI_PKT_CMD_GVI 0x15 /* Get Version ID */
+#define NCSI_PKT_CMD_GC 0x16 /* Get Capabilities */
+#define NCSI_PKT_CMD_GP 0x17 /* Get Parameters */
+#define NCSI_PKT_CMD_GCPS 0x18 /* Get Controller Packet Statistics */
+#define NCSI_PKT_CMD_GNS 0x19 /* Get NCSI Statistics */
+#define NCSI_PKT_CMD_GNPTS 0x1a /* Get NCSI Pass-throu Statistics */
+#define NCSI_PKT_CMD_GPS 0x1b /* Get package status */
+#define NCSI_PKT_CMD_OEM 0x50 /* OEM */
+#define NCSI_PKT_CMD_PLDM 0x51 /* PLDM request over NCSI over RBT */
+#define NCSI_PKT_CMD_GPUUID 0x52 /* Get package UUID */
+
+#endif /* __NCSI_PKT_H__ */
--
2.1.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 03/10] net/ncsi: NCSI response packet handler
2016-07-15 10:44 [PATCH net-next v2 00/10] NCSI Support Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 01/10] net/ncsi: Resource management Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 02/10] net/ncsi: NCSI command packet handler Gavin Shan
@ 2016-07-15 10:44 ` Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 04/10] net/ncsi: Package and channel management Gavin Shan
` (6 subsequent siblings)
9 siblings, 0 replies; 19+ messages in thread
From: Gavin Shan @ 2016-07-15 10:44 UTC (permalink / raw)
To: netdev
Cc: davem, benh, joel, gerlitz.or, weixue, yuvali,
alexei.starovoitov, f.fainelli, Gavin Shan
The NCSI response packets are sent to MC (Management Controller)
from the remote end. They are responses of NCSI command packets
for multiple purposes: completion status of NCSI command packets,
return NCSI channel's capability or configuration etc.
This defines struct to represent NCSI response packets and introduces
function ncsi_rcv_rsp() which will be used to receive NCSI response
packets and parse them.
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
---
net/ncsi/Makefile | 2 +-
net/ncsi/internal.h | 2 +
net/ncsi/ncsi-pkt.h | 208 +++++++++++
net/ncsi/ncsi-rsp.c | 1016 +++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1227 insertions(+), 1 deletion(-)
create mode 100644 net/ncsi/ncsi-rsp.c
diff --git a/net/ncsi/Makefile b/net/ncsi/Makefile
index abc4046..4751819 100644
--- a/net/ncsi/Makefile
+++ b/net/ncsi/Makefile
@@ -1,4 +1,4 @@
#
# Makefile for NCSI API
#
-obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-manage.o
+obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-manage.o
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index 3d81697..bd000c9 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -271,5 +271,7 @@ struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
/* Packet handlers */
u32 ncsi_calculate_checksum(unsigned char *data, int len);
int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
+int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev);
#endif /* __NCSI_INTERNAL_H__ */
diff --git a/net/ncsi/ncsi-pkt.h b/net/ncsi/ncsi-pkt.h
index 5481458..4bdefd9 100644
--- a/net/ncsi/ncsi-pkt.h
+++ b/net/ncsi/ncsi-pkt.h
@@ -25,6 +25,12 @@ struct ncsi_cmd_pkt_hdr {
struct ncsi_pkt_hdr common; /* Common NCSI packet header */
};
+struct ncsi_rsp_pkt_hdr {
+ struct ncsi_pkt_hdr common; /* Common NCSI packet header */
+ __be16 code; /* Response code */
+ __be16 reason; /* Response reason */
+};
+
/* NCSI common command packet */
struct ncsi_cmd_pkt {
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
@@ -32,6 +38,12 @@ struct ncsi_cmd_pkt {
unsigned char pad[26];
};
+struct ncsi_rsp_pkt {
+ struct ncsi_rsp_pkt_hdr rsp; /* Response header */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[22];
+};
+
/* Select Package */
struct ncsi_cmd_sp_pkt {
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
@@ -133,6 +145,157 @@ struct ncsi_cmd_snfc_pkt {
unsigned char pad[22];
};
+/* Get Link Status */
+struct ncsi_rsp_gls_pkt {
+ struct ncsi_rsp_pkt_hdr rsp; /* Response header */
+ __be32 status; /* Link status */
+ __be32 other; /* Other indications */
+ __be32 oem_status; /* OEM link status */
+ __be32 checksum;
+ unsigned char pad[10];
+};
+
+/* Get Version ID */
+struct ncsi_rsp_gvi_pkt {
+ struct ncsi_rsp_pkt_hdr rsp; /* Response header */
+ __be32 ncsi_version; /* NCSI version */
+ unsigned char reserved[3]; /* Reserved */
+ unsigned char alpha2; /* NCSI version */
+ unsigned char fw_name[12]; /* f/w name string */
+ __be32 fw_version; /* f/w version */
+ __be16 pci_ids[4]; /* PCI IDs */
+ __be32 mf_id; /* Manufacture ID */
+ __be32 checksum;
+};
+
+/* Get Capabilities */
+struct ncsi_rsp_gc_pkt {
+ struct ncsi_rsp_pkt_hdr rsp; /* Response header */
+ __be32 cap; /* Capabilities */
+ __be32 bc_cap; /* Broadcast cap */
+ __be32 mc_cap; /* Multicast cap */
+ __be32 buf_cap; /* Buffering cap */
+ __be32 aen_cap; /* AEN cap */
+ unsigned char vlan_cnt; /* VLAN filter count */
+ unsigned char mixed_cnt; /* Mix filter count */
+ unsigned char mc_cnt; /* MC filter count */
+ unsigned char uc_cnt; /* UC filter count */
+ unsigned char reserved[2]; /* Reserved */
+ unsigned char vlan_mode; /* VLAN mode */
+ unsigned char channel_cnt; /* Channel count */
+ __be32 checksum; /* Checksum */
+};
+
+/* Get Parameters */
+struct ncsi_rsp_gp_pkt {
+ struct ncsi_rsp_pkt_hdr rsp; /* Response header */
+ unsigned char mac_cnt; /* Number of MAC addr */
+ unsigned char reserved[2]; /* Reserved */
+ unsigned char mac_enable; /* MAC addr enable flags */
+ unsigned char vlan_cnt; /* VLAN tag count */
+ unsigned char reserved1; /* Reserved */
+ __be16 vlan_enable; /* VLAN tag enable flags */
+ __be32 link_mode; /* Link setting */
+ __be32 bc_mode; /* BC filter mode */
+ __be32 valid_modes; /* Valid mode parameters */
+ unsigned char vlan_mode; /* VLAN mode */
+ unsigned char fc_mode; /* Flow control mode */
+ unsigned char reserved2[2]; /* Reserved */
+ __be32 aen_mode; /* AEN mode */
+ unsigned char mac[6]; /* Supported MAC addr */
+ __be16 vlan; /* Supported VLAN tags */
+ __be32 checksum; /* Checksum */
+};
+
+/* Get Controller Packet Statistics */
+struct ncsi_rsp_gcps_pkt {
+ struct ncsi_rsp_pkt_hdr rsp; /* Response header */
+ __be32 cnt_hi; /* Counter cleared */
+ __be32 cnt_lo; /* Counter cleared */
+ __be32 rx_bytes; /* Rx bytes */
+ __be32 tx_bytes; /* Tx bytes */
+ __be32 rx_uc_pkts; /* Rx UC packets */
+ __be32 rx_mc_pkts; /* Rx MC packets */
+ __be32 rx_bc_pkts; /* Rx BC packets */
+ __be32 tx_uc_pkts; /* Tx UC packets */
+ __be32 tx_mc_pkts; /* Tx MC packets */
+ __be32 tx_bc_pkts; /* Tx BC packets */
+ __be32 fcs_err; /* FCS errors */
+ __be32 align_err; /* Alignment errors */
+ __be32 false_carrier; /* False carrier detection */
+ __be32 runt_pkts; /* Rx runt packets */
+ __be32 jabber_pkts; /* Rx jabber packets */
+ __be32 rx_pause_xon; /* Rx pause XON frames */
+ __be32 rx_pause_xoff; /* Rx XOFF frames */
+ __be32 tx_pause_xon; /* Tx XON frames */
+ __be32 tx_pause_xoff; /* Tx XOFF frames */
+ __be32 tx_s_collision; /* Single collision frames */
+ __be32 tx_m_collision; /* Multiple collision frames */
+ __be32 l_collision; /* Late collision frames */
+ __be32 e_collision; /* Excessive collision frames */
+ __be32 rx_ctl_frames; /* Rx control frames */
+ __be32 rx_64_frames; /* Rx 64-bytes frames */
+ __be32 rx_127_frames; /* Rx 65-127 bytes frames */
+ __be32 rx_255_frames; /* Rx 128-255 bytes frames */
+ __be32 rx_511_frames; /* Rx 256-511 bytes frames */
+ __be32 rx_1023_frames; /* Rx 512-1023 bytes frames */
+ __be32 rx_1522_frames; /* Rx 1024-1522 bytes frames */
+ __be32 rx_9022_frames; /* Rx 1523-9022 bytes frames */
+ __be32 tx_64_frames; /* Tx 64-bytes frames */
+ __be32 tx_127_frames; /* Tx 65-127 bytes frames */
+ __be32 tx_255_frames; /* Tx 128-255 bytes frames */
+ __be32 tx_511_frames; /* Tx 256-511 bytes frames */
+ __be32 tx_1023_frames; /* Tx 512-1023 bytes frames */
+ __be32 tx_1522_frames; /* Tx 1024-1522 bytes frames */
+ __be32 tx_9022_frames; /* Tx 1523-9022 bytes frames */
+ __be32 rx_valid_bytes; /* Rx valid bytes */
+ __be32 rx_runt_pkts; /* Rx error runt packets */
+ __be32 rx_jabber_pkts; /* Rx error jabber packets */
+ __be32 checksum; /* Checksum */
+};
+
+/* Get NCSI Statistics */
+struct ncsi_rsp_gns_pkt {
+ struct ncsi_rsp_pkt_hdr rsp; /* Response header */
+ __be32 rx_cmds; /* Rx NCSI commands */
+ __be32 dropped_cmds; /* Dropped commands */
+ __be32 cmd_type_errs; /* Command type errors */
+ __be32 cmd_csum_errs; /* Command checksum errors */
+ __be32 rx_pkts; /* Rx NCSI packets */
+ __be32 tx_pkts; /* Tx NCSI packets */
+ __be32 tx_aen_pkts; /* Tx AEN packets */
+ __be32 checksum; /* Checksum */
+};
+
+/* Get NCSI Pass-through Statistics */
+struct ncsi_rsp_gnpts_pkt {
+ struct ncsi_rsp_pkt_hdr rsp; /* Response header */
+ __be32 tx_pkts; /* Tx packets */
+ __be32 tx_dropped; /* Tx dropped packets */
+ __be32 tx_channel_err; /* Tx channel errors */
+ __be32 tx_us_err; /* Tx undersize errors */
+ __be32 rx_pkts; /* Rx packets */
+ __be32 rx_dropped; /* Rx dropped packets */
+ __be32 rx_channel_err; /* Rx channel errors */
+ __be32 rx_us_err; /* Rx undersize errors */
+ __be32 rx_os_err; /* Rx oversize errors */
+ __be32 checksum; /* Checksum */
+};
+
+/* Get package status */
+struct ncsi_rsp_gps_pkt {
+ struct ncsi_rsp_pkt_hdr rsp; /* Response header */
+ __be32 status; /* Hardware arbitration status */
+ __be32 checksum;
+};
+
+/* Get package UUID */
+struct ncsi_rsp_gpuuid_pkt {
+ struct ncsi_rsp_pkt_hdr rsp; /* Response header */
+ unsigned char uuid[16]; /* UUID */
+ __be32 checksum;
+};
+
/* NCSI packet revision */
#define NCSI_PKT_REVISION 0x01
@@ -168,4 +331,49 @@ struct ncsi_cmd_snfc_pkt {
#define NCSI_PKT_CMD_PLDM 0x51 /* PLDM request over NCSI over RBT */
#define NCSI_PKT_CMD_GPUUID 0x52 /* Get package UUID */
+/* NCSI packet responses */
+#define NCSI_PKT_RSP_CIS (NCSI_PKT_CMD_CIS + 0x80)
+#define NCSI_PKT_RSP_SP (NCSI_PKT_CMD_SP + 0x80)
+#define NCSI_PKT_RSP_DP (NCSI_PKT_CMD_DP + 0x80)
+#define NCSI_PKT_RSP_EC (NCSI_PKT_CMD_EC + 0x80)
+#define NCSI_PKT_RSP_DC (NCSI_PKT_CMD_DC + 0x80)
+#define NCSI_PKT_RSP_RC (NCSI_PKT_CMD_RC + 0x80)
+#define NCSI_PKT_RSP_ECNT (NCSI_PKT_CMD_ECNT + 0x80)
+#define NCSI_PKT_RSP_DCNT (NCSI_PKT_CMD_DCNT + 0x80)
+#define NCSI_PKT_RSP_AE (NCSI_PKT_CMD_AE + 0x80)
+#define NCSI_PKT_RSP_SL (NCSI_PKT_CMD_SL + 0x80)
+#define NCSI_PKT_RSP_GLS (NCSI_PKT_CMD_GLS + 0x80)
+#define NCSI_PKT_RSP_SVF (NCSI_PKT_CMD_SVF + 0x80)
+#define NCSI_PKT_RSP_EV (NCSI_PKT_CMD_EV + 0x80)
+#define NCSI_PKT_RSP_DV (NCSI_PKT_CMD_DV + 0x80)
+#define NCSI_PKT_RSP_SMA (NCSI_PKT_CMD_SMA + 0x80)
+#define NCSI_PKT_RSP_EBF (NCSI_PKT_CMD_EBF + 0x80)
+#define NCSI_PKT_RSP_DBF (NCSI_PKT_CMD_DBF + 0x80)
+#define NCSI_PKT_RSP_EGMF (NCSI_PKT_CMD_EGMF + 0x80)
+#define NCSI_PKT_RSP_DGMF (NCSI_PKT_CMD_DGMF + 0x80)
+#define NCSI_PKT_RSP_SNFC (NCSI_PKT_CMD_SNFC + 0x80)
+#define NCSI_PKT_RSP_GVI (NCSI_PKT_CMD_GVI + 0x80)
+#define NCSI_PKT_RSP_GC (NCSI_PKT_CMD_GC + 0x80)
+#define NCSI_PKT_RSP_GP (NCSI_PKT_CMD_GP + 0x80)
+#define NCSI_PKT_RSP_GCPS (NCSI_PKT_CMD_GCPS + 0x80)
+#define NCSI_PKT_RSP_GNS (NCSI_PKT_CMD_GNS + 0x80)
+#define NCSI_PKT_RSP_GNPTS (NCSI_PKT_CMD_GNPTS + 0x80)
+#define NCSI_PKT_RSP_GPS (NCSI_PKT_CMD_GPS + 0x80)
+#define NCSI_PKT_RSP_OEM (NCSI_PKT_CMD_OEM + 0x80)
+#define NCSI_PKT_RSP_PLDM (NCSI_PKT_CMD_PLDM + 0x80)
+#define NCSI_PKT_RSP_GPUUID (NCSI_PKT_CMD_GPUUID + 0x80)
+
+/* NCSI response code/reason */
+#define NCSI_PKT_RSP_C_COMPLETED 0x0000 /* Command Completed */
+#define NCSI_PKT_RSP_C_FAILED 0x0001 /* Command Failed */
+#define NCSI_PKT_RSP_C_UNAVAILABLE 0x0002 /* Command Unavailable */
+#define NCSI_PKT_RSP_C_UNSUPPORTED 0x0003 /* Command Unsupported */
+#define NCSI_PKT_RSP_R_NO_ERROR 0x0000 /* No Error */
+#define NCSI_PKT_RSP_R_INTERFACE 0x0001 /* Interface not ready */
+#define NCSI_PKT_RSP_R_PARAM 0x0002 /* Invalid Parameter */
+#define NCSI_PKT_RSP_R_CHANNEL 0x0003 /* Channel not Ready */
+#define NCSI_PKT_RSP_R_PACKAGE 0x0004 /* Package not Ready */
+#define NCSI_PKT_RSP_R_LENGTH 0x0005 /* Invalid payload length */
+#define NCSI_PKT_RSP_R_UNKNOWN 0x7fff /* Command type unsupported */
+
#endif /* __NCSI_PKT_H__ */
diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c
new file mode 100644
index 0000000..6ec25cb
--- /dev/null
+++ b/net/ncsi/ncsi-rsp.c
@@ -0,0 +1,1016 @@
+/*
+ * Copyright Gavin Shan, IBM Corporation 2016.
+ *
+ * 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 <net/ncsi.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "internal.h"
+#include "ncsi-pkt.h"
+
+static int ncsi_validate_rsp_pkt(struct ncsi_request *nr,
+ unsigned short payload)
+{
+ struct ncsi_rsp_pkt_hdr *h;
+ u32 checksum;
+ __be32 *pchecksum;
+
+ /* Check NCSI packet header. We don't need validate
+ * the packet type, which should have been checked
+ * before calling this function.
+ */
+ h = (struct ncsi_rsp_pkt_hdr *)skb_network_header(nr->rsp);
+ if (h->common.revision != NCSI_PKT_REVISION)
+ return -EINVAL;
+ if (ntohs(h->common.length) != payload)
+ return -EINVAL;
+
+ /* Check on code and reason */
+ if (ntohs(h->code) != NCSI_PKT_RSP_C_COMPLETED ||
+ ntohs(h->reason) != NCSI_PKT_RSP_R_NO_ERROR)
+ return -EINVAL;
+
+ /* Validate checksum, which might be zeroes if the
+ * sender doesn't support checksum according to NCSI
+ * specification.
+ */
+ pchecksum = (__be32 *)((void *)(h + 1) + payload - 4);
+ if (ntohl(*pchecksum) == 0)
+ return 0;
+
+ checksum = ncsi_calculate_checksum((unsigned char *)h,
+ sizeof(*h) + payload - 4);
+ if (*pchecksum != htonl(checksum))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_cis(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_package *np;
+ struct ncsi_channel *nc;
+ unsigned char id;
+
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, &np, &nc);
+ if (!nc) {
+ id = NCSI_CHANNEL_INDEX(rsp->rsp.common.channel);
+ nc = ncsi_add_channel(np, id);
+ }
+
+ return nc ? 0 : -ENODEV;
+}
+
+static int ncsi_rsp_handler_sp(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_package *np;
+ unsigned char id;
+
+ /* Add the package if it's not existing. Otherwise,
+ * to change the state of its child channels.
+ */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ &np, NULL);
+ if (!np) {
+ id = NCSI_PACKAGE_INDEX(rsp->rsp.common.channel);
+ np = ncsi_add_package(ndp, id);
+ if (!np)
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_dp(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_package *np;
+ struct ncsi_channel *nc;
+ unsigned long flags;
+
+ /* Find the package */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ &np, NULL);
+ if (!np)
+ return -ENODEV;
+
+ /* Change state of all channels attached to the package */
+ NCSI_FOR_EACH_CHANNEL(np, nc) {
+ spin_lock_irqsave(&nc->lock, flags);
+ nc->state = NCSI_CHANNEL_INACTIVE;
+ spin_unlock_irqrestore(&nc->lock, flags);
+ }
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_ec(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ ncm = &nc->modes[NCSI_MODE_ENABLE];
+ if (ncm->enable)
+ return -EBUSY;
+
+ ncm->enable = 1;
+ return 0;
+}
+
+static int ncsi_rsp_handler_dc(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+ int ret;
+
+ ret = ncsi_validate_rsp_pkt(nr, 4);
+ if (ret)
+ return ret;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ ncm = &nc->modes[NCSI_MODE_ENABLE];
+ if (!ncm->enable)
+ return -EBUSY;
+
+ ncm->enable = 0;
+ return 0;
+}
+
+static int ncsi_rsp_handler_rc(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ unsigned long flags;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Update state for the specified channel */
+ spin_lock_irqsave(&nc->lock, flags);
+ nc->state = NCSI_CHANNEL_INACTIVE;
+ spin_unlock_irqrestore(&nc->lock, flags);
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_ecnt(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
+ if (ncm->enable)
+ return -EBUSY;
+
+ ncm->enable = 1;
+ return 0;
+}
+
+static int ncsi_rsp_handler_dcnt(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
+ if (!ncm->enable)
+ return -EBUSY;
+
+ ncm->enable = 1;
+ return 0;
+}
+
+static int ncsi_rsp_handler_ae(struct ncsi_request *nr)
+{
+ struct ncsi_cmd_ae_pkt *cmd;
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Check if the AEN has been enabled */
+ ncm = &nc->modes[NCSI_MODE_AEN];
+ if (ncm->enable)
+ return -EBUSY;
+
+ /* Update to AEN configuration */
+ cmd = (struct ncsi_cmd_ae_pkt *)skb_network_header(nr->cmd);
+ ncm->enable = 1;
+ ncm->data[0] = cmd->mc_id;
+ ncm->data[1] = ntohl(cmd->mode);
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_sl(struct ncsi_request *nr)
+{
+ struct ncsi_cmd_sl_pkt *cmd;
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ cmd = (struct ncsi_cmd_sl_pkt *)skb_network_header(nr->cmd);
+ ncm = &nc->modes[NCSI_MODE_LINK];
+ ncm->data[0] = ntohl(cmd->mode);
+ ncm->data[1] = ntohl(cmd->oem_mode);
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_gls(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_gls_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_gls_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ ncm = &nc->modes[NCSI_MODE_LINK];
+ ncm->data[2] = ntohl(rsp->status);
+ ncm->data[3] = ntohl(rsp->other);
+ ncm->data[4] = ntohl(rsp->oem_status);
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_svf(struct ncsi_request *nr)
+{
+ struct ncsi_cmd_svf_pkt *cmd;
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_filter *ncf;
+ unsigned short vlan;
+ int ret;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ cmd = (struct ncsi_cmd_svf_pkt *)skb_network_header(nr->cmd);
+ ncf = nc->filters[NCSI_FILTER_VLAN];
+ if (!ncf)
+ return -ENOENT;
+ if (cmd->index >= ncf->total)
+ return -ERANGE;
+
+ /* Add or remove the VLAN filter */
+ if (!(cmd->enable & 0x1)) {
+ ret = ncsi_remove_filter(nc, NCSI_FILTER_VLAN, cmd->index);
+ } else {
+ vlan = ntohs(cmd->vlan);
+ ret = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan);
+ }
+
+ return ret;
+}
+
+static int ncsi_rsp_handler_ev(struct ncsi_request *nr)
+{
+ struct ncsi_cmd_ev_pkt *cmd;
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Check if VLAN mode has been enabled */
+ ncm = &nc->modes[NCSI_MODE_VLAN];
+ if (ncm->enable)
+ return -EBUSY;
+
+ /* Update to VLAN mode */
+ cmd = (struct ncsi_cmd_ev_pkt *)skb_network_header(nr->cmd);
+ ncm->enable = 1;
+ ncm->data[0] = ntohl(cmd->mode);
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_dv(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Check if VLAN mode has been enabled */
+ ncm = &nc->modes[NCSI_MODE_VLAN];
+ if (!ncm->enable)
+ return -EBUSY;
+
+ /* Update to VLAN mode */
+ ncm->enable = 0;
+ return 0;
+}
+
+static int ncsi_rsp_handler_sma(struct ncsi_request *nr)
+{
+ struct ncsi_cmd_sma_pkt *cmd;
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_filter *ncf;
+ void *bitmap;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* According to NCSI spec 1.01, the mixed filter table
+ * isn't supported yet.
+ */
+ cmd = (struct ncsi_cmd_sma_pkt *)skb_network_header(nr->cmd);
+ switch (cmd->at_e >> 5) {
+ case 0x0: /* UC address */
+ ncf = nc->filters[NCSI_FILTER_UC];
+ break;
+ case 0x1: /* MC address */
+ ncf = nc->filters[NCSI_FILTER_MC];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Sanity check on the filter */
+ if (!ncf)
+ return -ENOENT;
+ else if (cmd->index >= ncf->total)
+ return -ERANGE;
+
+ bitmap = &ncf->bitmap;
+ if (cmd->at_e & 0x1) {
+ if (test_and_set_bit(cmd->index, bitmap))
+ return -EBUSY;
+ memcpy(ncf->data + 6 * cmd->index, cmd->mac, 6);
+ } else {
+ if (!test_and_clear_bit(cmd->index, bitmap))
+ return -EBUSY;
+
+ memset(ncf->data + 6 * cmd->index, 0, 6);
+ }
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_ebf(struct ncsi_request *nr)
+{
+ struct ncsi_cmd_ebf_pkt *cmd;
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+
+ /* Find the package and channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Check if broadcast filter has been enabled */
+ ncm = &nc->modes[NCSI_MODE_BC];
+ if (ncm->enable)
+ return -EBUSY;
+
+ /* Update to broadcast filter mode */
+ cmd = (struct ncsi_cmd_ebf_pkt *)skb_network_header(nr->cmd);
+ ncm->enable = 1;
+ ncm->data[0] = ntohl(cmd->mode);
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_dbf(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Check if broadcast filter isn't enabled */
+ ncm = &nc->modes[NCSI_MODE_BC];
+ if (!ncm->enable)
+ return -EBUSY;
+
+ /* Update to broadcast filter mode */
+ ncm->enable = 0;
+ ncm->data[0] = 0;
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_egmf(struct ncsi_request *nr)
+{
+ struct ncsi_cmd_egmf_pkt *cmd;
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+
+ /* Find the channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Check if multicast filter has been enabled */
+ ncm = &nc->modes[NCSI_MODE_MC];
+ if (ncm->enable)
+ return -EBUSY;
+
+ /* Update to multicast filter mode */
+ cmd = (struct ncsi_cmd_egmf_pkt *)skb_network_header(nr->cmd);
+ ncm->enable = 1;
+ ncm->data[0] = ntohl(cmd->mode);
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_dgmf(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Check if multicast filter has been enabled */
+ ncm = &nc->modes[NCSI_MODE_MC];
+ if (!ncm->enable)
+ return -EBUSY;
+
+ /* Update to multicast filter mode */
+ ncm->enable = 0;
+ ncm->data[0] = 0;
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_snfc(struct ncsi_request *nr)
+{
+ struct ncsi_cmd_snfc_pkt *cmd;
+ struct ncsi_rsp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+
+ /* Find the channel */
+ rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Check if flow control has been enabled */
+ ncm = &nc->modes[NCSI_MODE_FC];
+ if (ncm->enable)
+ return -EBUSY;
+
+ /* Update to flow control mode */
+ cmd = (struct ncsi_cmd_snfc_pkt *)skb_network_header(nr->cmd);
+ ncm->enable = 1;
+ ncm->data[0] = cmd->mode;
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_gvi(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_gvi_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_version *ncv;
+ int i;
+
+ /* Find the channel */
+ rsp = (struct ncsi_rsp_gvi_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Update to channel's version info */
+ ncv = &nc->version;
+ ncv->version = ntohl(rsp->ncsi_version);
+ ncv->alpha2 = rsp->alpha2;
+ memcpy(ncv->fw_name, rsp->fw_name, 12);
+ ncv->fw_version = ntohl(rsp->fw_version);
+ for (i = 0; i < ARRAY_SIZE(ncv->pci_ids); i++)
+ ncv->pci_ids[i] = ntohs(rsp->pci_ids[i]);
+ ncv->mf_id = ntohl(rsp->mf_id);
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_gc(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_gc_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_filter *ncf;
+ size_t size, entry_size;
+ int cnt, i;
+
+ /* Find the channel */
+ rsp = (struct ncsi_rsp_gc_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Update channel's capabilities */
+ nc->caps[NCSI_CAP_GENERIC].cap = ntohl(rsp->cap) &
+ NCSI_CAP_GENERIC_MASK;
+ nc->caps[NCSI_CAP_BC].cap = ntohl(rsp->bc_cap) &
+ NCSI_CAP_BC_MASK;
+ nc->caps[NCSI_CAP_MC].cap = ntohl(rsp->mc_cap) &
+ NCSI_CAP_MC_MASK;
+ nc->caps[NCSI_CAP_BUFFER].cap = ntohl(rsp->buf_cap);
+ nc->caps[NCSI_CAP_AEN].cap = ntohl(rsp->aen_cap) &
+ NCSI_CAP_AEN_MASK;
+ nc->caps[NCSI_CAP_VLAN].cap = rsp->vlan_mode &
+ NCSI_CAP_VLAN_MASK;
+
+ /* Build filters */
+ for (i = 0; i < NCSI_FILTER_MAX; i++) {
+ switch (i) {
+ case NCSI_FILTER_VLAN:
+ cnt = rsp->vlan_cnt;
+ entry_size = 2;
+ break;
+ case NCSI_FILTER_MIXED:
+ cnt = rsp->mixed_cnt;
+ entry_size = 6;
+ break;
+ case NCSI_FILTER_MC:
+ cnt = rsp->mc_cnt;
+ entry_size = 6;
+ break;
+ case NCSI_FILTER_UC:
+ cnt = rsp->uc_cnt;
+ entry_size = 6;
+ break;
+ default:
+ continue;
+ }
+
+ if (!cnt || nc->filters[i])
+ continue;
+
+ size = sizeof(*ncf) + cnt * entry_size;
+ ncf = kzalloc(size, GFP_ATOMIC);
+ if (!ncf) {
+ pr_warn("%s: Cannot alloc filter table (%d)\n",
+ __func__, i);
+ return -ENOMEM;
+ }
+
+ ncf->index = i;
+ ncf->total = cnt;
+ ncf->bitmap = 0x0ul;
+ nc->filters[i] = ncf;
+ }
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_gp(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_gp_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ unsigned short enable, vlan;
+ unsigned char *pdata;
+ int table, i;
+
+ /* Find the channel */
+ rsp = (struct ncsi_rsp_gp_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Modes with explicit enabled indications */
+ if (ntohl(rsp->valid_modes) & 0x1) { /* BC filter mode */
+ nc->modes[NCSI_MODE_BC].enable = 1;
+ nc->modes[NCSI_MODE_BC].data[0] = ntohl(rsp->bc_mode);
+ }
+ if (ntohl(rsp->valid_modes) & 0x2) /* Channel enabled */
+ nc->modes[NCSI_MODE_ENABLE].enable = 1;
+ if (ntohl(rsp->valid_modes) & 0x4) /* Channel Tx enabled */
+ nc->modes[NCSI_MODE_TX_ENABLE].enable = 1;
+ if (ntohl(rsp->valid_modes) & 0x8) /* MC filter mode */
+ nc->modes[NCSI_MODE_MC].enable = 1;
+
+ /* Modes without explicit enabled indications */
+ nc->modes[NCSI_MODE_LINK].enable = 1;
+ nc->modes[NCSI_MODE_LINK].data[0] = ntohl(rsp->link_mode);
+ nc->modes[NCSI_MODE_VLAN].enable = 1;
+ nc->modes[NCSI_MODE_VLAN].data[0] = rsp->vlan_mode;
+ nc->modes[NCSI_MODE_FC].enable = 1;
+ nc->modes[NCSI_MODE_FC].data[0] = rsp->fc_mode;
+ nc->modes[NCSI_MODE_AEN].enable = 1;
+ nc->modes[NCSI_MODE_AEN].data[0] = ntohl(rsp->aen_mode);
+
+ /* MAC addresses filter table */
+ pdata = (unsigned char *)rsp + 48;
+ enable = rsp->mac_enable;
+ for (i = 0; i < rsp->mac_cnt; i++, pdata += 6) {
+ if (i >= (nc->filters[NCSI_FILTER_UC]->total +
+ nc->filters[NCSI_FILTER_MC]->total))
+ table = NCSI_FILTER_MIXED;
+ else if (i >= nc->filters[NCSI_FILTER_UC]->total)
+ table = NCSI_FILTER_MC;
+ else
+ table = NCSI_FILTER_UC;
+
+ if (!(enable & (0x1 << i)))
+ continue;
+
+ if (ncsi_find_filter(nc, table, pdata) >= 0)
+ continue;
+
+ ncsi_add_filter(nc, table, pdata);
+ }
+
+ /* VLAN filter table */
+ enable = ntohs(rsp->vlan_enable);
+ for (i = 0; i < rsp->vlan_cnt; i++, pdata += 2) {
+ if (!(enable & (0x1 << i)))
+ continue;
+
+ vlan = ntohs(*(__be16 *)pdata);
+ if (ncsi_find_filter(nc, NCSI_FILTER_VLAN, &vlan) >= 0)
+ continue;
+
+ ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan);
+ }
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_gcps(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_gcps_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_stats *ncs;
+
+ /* Find the channel */
+ rsp = (struct ncsi_rsp_gcps_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Update HNC's statistics */
+ ncs = &nc->stats;
+ ncs->hnc_cnt_hi = ntohl(rsp->cnt_hi);
+ ncs->hnc_cnt_lo = ntohl(rsp->cnt_lo);
+ ncs->hnc_rx_bytes = ntohl(rsp->rx_bytes);
+ ncs->hnc_tx_bytes = ntohl(rsp->tx_bytes);
+ ncs->hnc_rx_uc_pkts = ntohl(rsp->rx_uc_pkts);
+ ncs->hnc_rx_mc_pkts = ntohl(rsp->rx_mc_pkts);
+ ncs->hnc_rx_bc_pkts = ntohl(rsp->rx_bc_pkts);
+ ncs->hnc_tx_uc_pkts = ntohl(rsp->tx_uc_pkts);
+ ncs->hnc_tx_mc_pkts = ntohl(rsp->tx_mc_pkts);
+ ncs->hnc_tx_bc_pkts = ntohl(rsp->tx_bc_pkts);
+ ncs->hnc_fcs_err = ntohl(rsp->fcs_err);
+ ncs->hnc_align_err = ntohl(rsp->align_err);
+ ncs->hnc_false_carrier = ntohl(rsp->false_carrier);
+ ncs->hnc_runt_pkts = ntohl(rsp->runt_pkts);
+ ncs->hnc_jabber_pkts = ntohl(rsp->jabber_pkts);
+ ncs->hnc_rx_pause_xon = ntohl(rsp->rx_pause_xon);
+ ncs->hnc_rx_pause_xoff = ntohl(rsp->rx_pause_xoff);
+ ncs->hnc_tx_pause_xon = ntohl(rsp->tx_pause_xon);
+ ncs->hnc_tx_pause_xoff = ntohl(rsp->tx_pause_xoff);
+ ncs->hnc_tx_s_collision = ntohl(rsp->tx_s_collision);
+ ncs->hnc_tx_m_collision = ntohl(rsp->tx_m_collision);
+ ncs->hnc_l_collision = ntohl(rsp->l_collision);
+ ncs->hnc_e_collision = ntohl(rsp->e_collision);
+ ncs->hnc_rx_ctl_frames = ntohl(rsp->rx_ctl_frames);
+ ncs->hnc_rx_64_frames = ntohl(rsp->rx_64_frames);
+ ncs->hnc_rx_127_frames = ntohl(rsp->rx_127_frames);
+ ncs->hnc_rx_255_frames = ntohl(rsp->rx_255_frames);
+ ncs->hnc_rx_511_frames = ntohl(rsp->rx_511_frames);
+ ncs->hnc_rx_1023_frames = ntohl(rsp->rx_1023_frames);
+ ncs->hnc_rx_1522_frames = ntohl(rsp->rx_1522_frames);
+ ncs->hnc_rx_9022_frames = ntohl(rsp->rx_9022_frames);
+ ncs->hnc_tx_64_frames = ntohl(rsp->tx_64_frames);
+ ncs->hnc_tx_127_frames = ntohl(rsp->tx_127_frames);
+ ncs->hnc_tx_255_frames = ntohl(rsp->tx_255_frames);
+ ncs->hnc_tx_511_frames = ntohl(rsp->tx_511_frames);
+ ncs->hnc_tx_1023_frames = ntohl(rsp->tx_1023_frames);
+ ncs->hnc_tx_1522_frames = ntohl(rsp->tx_1522_frames);
+ ncs->hnc_tx_9022_frames = ntohl(rsp->tx_9022_frames);
+ ncs->hnc_rx_valid_bytes = ntohl(rsp->rx_valid_bytes);
+ ncs->hnc_rx_runt_pkts = ntohl(rsp->rx_runt_pkts);
+ ncs->hnc_rx_jabber_pkts = ntohl(rsp->rx_jabber_pkts);
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_gns(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_gns_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_stats *ncs;
+
+ /* Find the channel */
+ rsp = (struct ncsi_rsp_gns_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Update HNC's statistics */
+ ncs = &nc->stats;
+ ncs->ncsi_rx_cmds = ntohl(rsp->rx_cmds);
+ ncs->ncsi_dropped_cmds = ntohl(rsp->dropped_cmds);
+ ncs->ncsi_cmd_type_errs = ntohl(rsp->cmd_type_errs);
+ ncs->ncsi_cmd_csum_errs = ntohl(rsp->cmd_csum_errs);
+ ncs->ncsi_rx_pkts = ntohl(rsp->rx_pkts);
+ ncs->ncsi_tx_pkts = ntohl(rsp->tx_pkts);
+ ncs->ncsi_tx_aen_pkts = ntohl(rsp->tx_aen_pkts);
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_gnpts(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_gnpts_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_stats *ncs;
+
+ /* Find the channel */
+ rsp = (struct ncsi_rsp_gnpts_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Update HNC's statistics */
+ ncs = &nc->stats;
+ ncs->pt_tx_pkts = ntohl(rsp->tx_pkts);
+ ncs->pt_tx_dropped = ntohl(rsp->tx_dropped);
+ ncs->pt_tx_channel_err = ntohl(rsp->tx_channel_err);
+ ncs->pt_tx_us_err = ntohl(rsp->tx_us_err);
+ ncs->pt_rx_pkts = ntohl(rsp->rx_pkts);
+ ncs->pt_rx_dropped = ntohl(rsp->rx_dropped);
+ ncs->pt_rx_channel_err = ntohl(rsp->rx_channel_err);
+ ncs->pt_rx_us_err = ntohl(rsp->rx_us_err);
+ ncs->pt_rx_os_err = ntohl(rsp->rx_os_err);
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_gps(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_gps_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_package *np;
+
+ /* Find the package */
+ rsp = (struct ncsi_rsp_gps_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ &np, NULL);
+ if (!np)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int ncsi_rsp_handler_gpuuid(struct ncsi_request *nr)
+{
+ struct ncsi_rsp_gpuuid_pkt *rsp;
+ struct ncsi_dev_priv *ndp = nr->ndp;
+ struct ncsi_package *np;
+
+ /* Find the package */
+ rsp = (struct ncsi_rsp_gpuuid_pkt *)skb_network_header(nr->rsp);
+ ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
+ &np, NULL);
+ if (!np)
+ return -ENODEV;
+
+ memcpy(np->uuid, rsp->uuid, sizeof(rsp->uuid));
+
+ return 0;
+}
+
+static struct ncsi_rsp_handler {
+ unsigned char type;
+ int payload;
+ int (*handler)(struct ncsi_request *nr);
+} ncsi_rsp_handlers[] = {
+ { NCSI_PKT_RSP_CIS, 4, ncsi_rsp_handler_cis },
+ { NCSI_PKT_RSP_SP, 4, ncsi_rsp_handler_sp },
+ { NCSI_PKT_RSP_DP, 4, ncsi_rsp_handler_dp },
+ { NCSI_PKT_RSP_EC, 4, ncsi_rsp_handler_ec },
+ { NCSI_PKT_RSP_DC, 4, ncsi_rsp_handler_dc },
+ { NCSI_PKT_RSP_RC, 4, ncsi_rsp_handler_rc },
+ { NCSI_PKT_RSP_ECNT, 4, ncsi_rsp_handler_ecnt },
+ { NCSI_PKT_RSP_DCNT, 4, ncsi_rsp_handler_dcnt },
+ { NCSI_PKT_RSP_AE, 4, ncsi_rsp_handler_ae },
+ { NCSI_PKT_RSP_SL, 4, ncsi_rsp_handler_sl },
+ { NCSI_PKT_RSP_GLS, 16, ncsi_rsp_handler_gls },
+ { NCSI_PKT_RSP_SVF, 4, ncsi_rsp_handler_svf },
+ { NCSI_PKT_RSP_EV, 4, ncsi_rsp_handler_ev },
+ { NCSI_PKT_RSP_DV, 4, ncsi_rsp_handler_dv },
+ { NCSI_PKT_RSP_SMA, 4, ncsi_rsp_handler_sma },
+ { NCSI_PKT_RSP_EBF, 4, ncsi_rsp_handler_ebf },
+ { NCSI_PKT_RSP_DBF, 4, ncsi_rsp_handler_dbf },
+ { NCSI_PKT_RSP_EGMF, 4, ncsi_rsp_handler_egmf },
+ { NCSI_PKT_RSP_DGMF, 4, ncsi_rsp_handler_dgmf },
+ { NCSI_PKT_RSP_SNFC, 4, ncsi_rsp_handler_snfc },
+ { NCSI_PKT_RSP_GVI, 36, ncsi_rsp_handler_gvi },
+ { NCSI_PKT_RSP_GC, 32, ncsi_rsp_handler_gc },
+ { NCSI_PKT_RSP_GP, -1, ncsi_rsp_handler_gp },
+ { NCSI_PKT_RSP_GCPS, 172, ncsi_rsp_handler_gcps },
+ { NCSI_PKT_RSP_GNS, 172, ncsi_rsp_handler_gns },
+ { NCSI_PKT_RSP_GNPTS, 172, ncsi_rsp_handler_gnpts },
+ { NCSI_PKT_RSP_GPS, 8, ncsi_rsp_handler_gps },
+ { NCSI_PKT_RSP_OEM, 0, NULL },
+ { NCSI_PKT_RSP_PLDM, 0, NULL },
+ { NCSI_PKT_RSP_GPUUID, 20, ncsi_rsp_handler_gpuuid }
+};
+
+int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct ncsi_rsp_handler *nrh = NULL;
+ struct ncsi_dev *nd;
+ struct ncsi_dev_priv *ndp;
+ struct ncsi_request *nr;
+ struct ncsi_pkt_hdr *hdr;
+ unsigned long flags;
+ int payload, i, ret;
+
+ /* Find the NCSI device */
+ nd = ncsi_find_dev(dev);
+ ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
+ if (!ndp)
+ return -ENODEV;
+
+ /* Find the handler */
+ hdr = (struct ncsi_pkt_hdr *)skb_network_header(skb);
+ for (i = 0; i < ARRAY_SIZE(ncsi_rsp_handlers); i++) {
+ if (ncsi_rsp_handlers[i].type == hdr->type) {
+ if (ncsi_rsp_handlers[i].handler)
+ nrh = &ncsi_rsp_handlers[i];
+ else
+ nrh = NULL;
+
+ break;
+ }
+ }
+
+ if (!nrh) {
+ netdev_err(nd->dev, "Received unrecognized packet (0x%x)\n",
+ hdr->type);
+ return -ENOENT;
+ }
+
+ /* Associate with the request */
+ spin_lock_irqsave(&ndp->lock, flags);
+ nr = &ndp->requests[hdr->id];
+ if (!nr->used) {
+ spin_unlock_irqrestore(&ndp->lock, flags);
+ return -ENODEV;
+ }
+
+ nr->rsp = skb;
+ if (!nr->enabled) {
+ spin_unlock_irqrestore(&ndp->lock, flags);
+ ret = -ENOENT;
+ goto out;
+ }
+
+ /* Validate the packet */
+ spin_unlock_irqrestore(&ndp->lock, flags);
+ payload = nrh->payload;
+ if (payload < 0)
+ payload = ntohs(hdr->length);
+ ret = ncsi_validate_rsp_pkt(nr, payload);
+ if (ret)
+ goto out;
+
+ /* Process the packet */
+ ret = nrh->handler(nr);
+out:
+ ncsi_free_request(nr);
+ return ret;
+}
--
2.1.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 04/10] net/ncsi: Package and channel management
2016-07-15 10:44 [PATCH net-next v2 00/10] NCSI Support Gavin Shan
` (2 preceding siblings ...)
2016-07-15 10:44 ` [PATCH net-next v2 03/10] net/ncsi: NCSI response " Gavin Shan
@ 2016-07-15 10:44 ` Gavin Shan
2016-07-15 14:30 ` kbuild test robot
2016-07-15 10:44 ` [PATCH net-next v2 05/10] net/ncsi: NCSI AEN packet handler Gavin Shan
` (5 subsequent siblings)
9 siblings, 1 reply; 19+ messages in thread
From: Gavin Shan @ 2016-07-15 10:44 UTC (permalink / raw)
To: netdev
Cc: davem, benh, joel, gerlitz.or, weixue, yuvali,
alexei.starovoitov, f.fainelli, Gavin Shan
This manages NCSI packages and channels:
* The available packages and channels are enumerated in the first
time of calling ncsi_start_dev(). The channels' capabilities are
probed in the meanwhile. The NCSI network topology won't change
until the NCSI device is destroyed.
* There in a queue in every NCSI device. The element in the queue,
channel, is waiting for configuration (bringup) or suspending
(teardown). The channel's state (inactive/active) indicates the
futher action (configuration or suspending) will be applied on the
channel. Another channel's state (invisible) means the requested
action is being applied.
* The hardware arbitration will be enabled if all available packages
and channels support it. All available channels try to provide
service when hardware arbitration is enabled. Otherwise, one channel
is selected as the active one at once.
* When channel is in active state, meaning it's providing service, a
timer started to retrieve the channe's link status. If the channel's
link status fails to be updated in the determined period, the channel
is going to be reconfigured. It's the error handling implementation
as defined in NCSI spec.
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
---
include/net/ncsi.h | 6 +
net/ncsi/internal.h | 50 ++++
net/ncsi/ncsi-manage.c | 763 +++++++++++++++++++++++++++++++++++++++++++++++++
net/ncsi/ncsi-rsp.c | 15 +
4 files changed, 834 insertions(+)
diff --git a/include/net/ncsi.h b/include/net/ncsi.h
index 70d14ee..1dbf42f 100644
--- a/include/net/ncsi.h
+++ b/include/net/ncsi.h
@@ -30,6 +30,7 @@ struct ncsi_dev {
#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);
void ncsi_unregister_dev(struct ncsi_dev *nd);
#else /* !CONFIG_NET_NCSI */
static inline struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
@@ -38,6 +39,11 @@ static inline struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
return NULL;
}
+static inline int ncsi_start_dev(struct ncsi_dev *nd)
+{
+ return -ENOTTY;
+}
+
static inline void ncsi_unregister_dev(struct ncsi_dev *nd)
{
}
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index bd000c9..38fc95a 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -178,6 +178,7 @@ struct ncsi_channel {
int state;
#define NCSI_CHANNEL_INACTIVE 1
#define NCSI_CHANNEL_ACTIVE 2
+#define NCSI_CHANNEL_INVISIBLE 3
spinlock_t lock; /* Protect filters etc */
struct ncsi_package *package;
struct ncsi_channel_version version;
@@ -185,7 +186,11 @@ struct ncsi_channel {
struct ncsi_channel_mode modes[NCSI_MODE_MAX];
struct ncsi_channel_filter *filters[NCSI_FILTER_MAX];
struct ncsi_channel_stats stats;
+ struct timer_list timer; /* Link monitor timer */
+ bool enabled; /* Timer is enabled */
+ unsigned int timeout; /* Times of timeout */
struct list_head node;
+ struct list_head link;
};
struct ncsi_package {
@@ -209,14 +214,56 @@ struct ncsi_request {
bool enabled; /* Time has been enabled or not */
};
+enum {
+ ncsi_dev_state_major = 0xff00,
+ ncsi_dev_state_minor = 0x00ff,
+ ncsi_dev_state_probe_deselect = 0x0201,
+ ncsi_dev_state_probe_package,
+ ncsi_dev_state_probe_channel,
+ ncsi_dev_state_probe_cis,
+ ncsi_dev_state_probe_gvi,
+ ncsi_dev_state_probe_gc,
+ ncsi_dev_state_probe_gls,
+ ncsi_dev_state_probe_dp,
+ ncsi_dev_state_config_sp = 0x0301,
+ ncsi_dev_state_config_cis,
+ ncsi_dev_state_config_sma,
+ ncsi_dev_state_config_ebf,
+#if IS_ENABLED(CONFIG_IPV6)
+ ncsi_dev_state_config_egmf,
+#endif
+ ncsi_dev_state_config_ecnt,
+ ncsi_dev_state_config_ec,
+ ncsi_dev_state_config_ae,
+ ncsi_dev_state_config_gls,
+ ncsi_dev_state_config_done,
+ ncsi_dev_state_suspend_select = 0x0401,
+ ncsi_dev_state_suspend_dcnt,
+ ncsi_dev_state_suspend_dc,
+ ncsi_dev_state_suspend_deselect,
+ ncsi_dev_state_suspend_done
+};
+
struct ncsi_dev_priv {
struct ncsi_dev ndev; /* Associated NCSI device */
unsigned int flags; /* NCSI device flags */
+#define NCSI_DEV_PROBED 1 /* Finalized NCSI topology */
+#define NCSI_DEV_HWA 2 /* Enabled HW arbitration */
+#define NCSI_DEV_RESHUFFLE 4
spinlock_t lock; /* Protect the NCSI device */
+#if IS_ENABLED(CONFIG_IPV6)
+ unsigned int inet6_addr_num; /* Number of IPv6 addresses */
+#endif
unsigned int package_num; /* Number of packages */
struct list_head packages; /* List of packages */
struct ncsi_request requests[256]; /* Request table */
unsigned int request_id; /* Last used request ID */
+ unsigned int pending_req_num; /* Number of pending requests */
+ struct ncsi_package *active_package; /* Currently handled package */
+ struct ncsi_channel *active_channel; /* Currently handled channel */
+ struct list_head channel_queue; /* Config queue of channels */
+ struct work_struct work; /* For channel management */
+ struct packet_type ptype; /* NCSI packet Rx handler */
struct list_head node; /* Form NCSI device list */
};
@@ -251,6 +298,8 @@ extern spinlock_t ncsi_dev_lock;
int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data);
int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data);
int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index);
+void ncsi_start_channel_monitor(struct ncsi_channel *nc);
+void ncsi_stop_channel_monitor(struct ncsi_channel *nc);
struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
unsigned char id);
struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np,
@@ -267,6 +316,7 @@ void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, bool driven);
void ncsi_free_request(struct ncsi_request *nr);
struct ncsi_dev *ncsi_find_dev(struct net_device *dev);
+int ncsi_process_next_channel(struct ncsi_dev_priv *ndp);
/* Packet handlers */
u32 ncsi_calculate_checksum(unsigned char *data, int len);
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index 0e28ed8..d627a39 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -17,8 +17,12 @@
#include <net/ncsi.h>
#include <net/net_namespace.h>
#include <net/sock.h>
+#include <net/addrconf.h>
+#include <net/ipv6.h>
+#include <net/if_inet6.h>
#include "internal.h"
+#include "ncsi-pkt.h"
LIST_HEAD(ncsi_dev_list);
DEFINE_SPINLOCK(ncsi_dev_lock);
@@ -123,6 +127,120 @@ int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index)
return 0;
}
+static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
+{
+ struct ncsi_dev *nd = &ndp->ndev;
+ struct ncsi_package *np;
+ struct ncsi_channel *nc;
+
+ nd->state = ncsi_dev_state_functional;
+ if (force_down) {
+ nd->link_up = 0;
+ goto report;
+ }
+
+ nd->link_up = 0;
+ NCSI_FOR_EACH_PACKAGE(ndp, np) {
+ NCSI_FOR_EACH_CHANNEL(np, nc) {
+ if (!list_empty(&nc->link) ||
+ nc->state != NCSI_CHANNEL_ACTIVE)
+ continue;
+
+ if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
+ nd->link_up = 1;
+ goto report;
+ }
+ }
+ }
+
+report:
+ nd->handler(nd);
+}
+
+static void ncsi_channel_monitor(unsigned long data)
+{
+ struct ncsi_channel *nc = (struct ncsi_channel *)data;
+ struct ncsi_package *np = nc->package;
+ struct ncsi_dev_priv *ndp = np->ndp;
+ struct ncsi_cmd_arg nca;
+ bool enabled;
+ unsigned int timeout;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&nc->lock, flags);
+ timeout = nc->timeout;
+ enabled = nc->enabled;
+ spin_unlock_irqrestore(&nc->lock, flags);
+
+ if (!enabled || !list_empty(&nc->link))
+ return;
+ if (nc->state != NCSI_CHANNEL_INACTIVE &&
+ nc->state != NCSI_CHANNEL_ACTIVE)
+ return;
+
+ if (!(timeout % 2)) {
+ nca.ndp = ndp;
+ nca.package = np->id;
+ nca.channel = nc->id;
+ nca.type = NCSI_PKT_CMD_GLS;
+ nca.driven = false;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret) {
+ netdev_err(ndp->ndev.dev, "Error %d sending GLS\n",
+ ret);
+ return;
+ }
+ }
+
+ if (timeout + 1 >= 3) {
+ if (!(ndp->flags & NCSI_DEV_HWA) &&
+ nc->state == NCSI_CHANNEL_ACTIVE)
+ ncsi_report_link(ndp, true);
+
+ spin_lock_irqsave(&ndp->lock, flags);
+ xchg(&nc->state, NCSI_CHANNEL_INACTIVE);
+ list_add_tail_rcu(&nc->link, &ndp->channel_queue);
+ spin_unlock_irqrestore(&ndp->lock, flags);
+ ncsi_process_next_channel(ndp);
+ return;
+ }
+
+ spin_lock_irqsave(&nc->lock, flags);
+ nc->timeout = timeout + 1;
+ nc->enabled = true;
+ spin_unlock_irqrestore(&nc->lock, flags);
+ mod_timer(&nc->timer, jiffies + HZ * (1 << (nc->timeout / 2)));
+}
+
+void ncsi_start_channel_monitor(struct ncsi_channel *nc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&nc->lock, flags);
+ WARN_ON_ONCE(nc->enabled);
+ nc->timeout = 0;
+ nc->enabled = true;
+ spin_unlock_irqrestore(&nc->lock, flags);
+
+ mod_timer(&nc->timer, jiffies + HZ * (1 << (nc->timeout / 2)));
+}
+
+void ncsi_stop_channel_monitor(struct ncsi_channel *nc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&nc->lock, flags);
+ if (!nc->enabled) {
+ spin_unlock_irqrestore(&nc->lock, flags);
+ return;
+ }
+ nc->enabled = false;
+ spin_unlock_irqrestore(&nc->lock, flags);
+
+ del_timer_sync(&nc->timer);
+}
+
struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
unsigned char id)
{
@@ -149,7 +267,10 @@ struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
nc->id = id;
nc->package = np;
nc->state = NCSI_CHANNEL_INACTIVE;
+ nc->enabled = false;
+ setup_timer(&nc->timer, ncsi_channel_monitor, (unsigned long)nc);
spin_lock_init(&nc->lock);
+ INIT_LIST_HEAD(&nc->link);
for (index = 0; index < NCSI_CAP_MAX; index++)
nc->caps[index].index = index;
for (index = 0; index < NCSI_MODE_MAX; index++)
@@ -190,6 +311,7 @@ static void ncsi_remove_channel(struct ncsi_channel *nc)
nc->state = NCSI_CHANNEL_INACTIVE;
spin_unlock_irqrestore(&nc->lock, flags);
+ ncsi_stop_channel_monitor(nc);
/* Remove and free channel */
spin_lock_irqsave(&np->lock, flags);
@@ -323,6 +445,7 @@ void ncsi_free_request(struct ncsi_request *nr)
struct ncsi_dev_priv *ndp = nr->ndp;
struct sk_buff *cmd, *rsp;
unsigned long flags;
+ bool driven;
if (nr->enabled) {
nr->enabled = false;
@@ -335,8 +458,12 @@ void ncsi_free_request(struct ncsi_request *nr)
nr->cmd = NULL;
nr->rsp = NULL;
nr->used = false;
+ driven = nr->driven;
spin_unlock_irqrestore(&ndp->lock, flags);
+ if (driven && cmd && --ndp->pending_req_num == 0)
+ schedule_work(&ndp->work);
+
/* Release command and response */
consume_skb(cmd);
consume_skb(rsp);
@@ -375,6 +502,587 @@ static void ncsi_request_timeout(unsigned long data)
ncsi_free_request(nr);
}
+static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
+{
+ struct ncsi_dev *nd = &ndp->ndev;
+ struct ncsi_package *np = ndp->active_package;
+ struct ncsi_channel *nc = ndp->active_channel;
+ struct ncsi_cmd_arg nca;
+ int ret;
+
+ nca.ndp = ndp;
+ nca.driven = true;
+ switch (nd->state) {
+ case ncsi_dev_state_suspend:
+ nd->state = ncsi_dev_state_suspend_select;
+ /* Fall through */
+ case ncsi_dev_state_suspend_select:
+ case ncsi_dev_state_suspend_dcnt:
+ case ncsi_dev_state_suspend_dc:
+ case ncsi_dev_state_suspend_deselect:
+ ndp->pending_req_num = 1;
+
+ np = ndp->active_package;
+ nc = ndp->active_channel;
+ nca.package = np->id;
+ if (nd->state == ncsi_dev_state_suspend_select) {
+ nca.type = NCSI_PKT_CMD_SP;
+ nca.channel = 0x1f;
+ if (ndp->flags & NCSI_DEV_HWA)
+ nca.bytes[0] = 0;
+ else
+ nca.bytes[0] = 1;
+ nd->state = ncsi_dev_state_suspend_dcnt;
+ } else if (nd->state == ncsi_dev_state_suspend_dcnt) {
+ nca.type = NCSI_PKT_CMD_DCNT;
+ nca.channel = nc->id;
+ nd->state = ncsi_dev_state_suspend_dc;
+ } else if (nd->state == ncsi_dev_state_suspend_dc) {
+ nca.type = NCSI_PKT_CMD_DC;
+ nca.channel = nc->id;
+ nca.bytes[0] = 1;
+ nd->state = ncsi_dev_state_suspend_deselect;
+ } else if (nd->state == ncsi_dev_state_suspend_deselect) {
+ nca.type = NCSI_PKT_CMD_DP;
+ nca.channel = 0x1f;
+ nd->state = ncsi_dev_state_suspend_done;
+ }
+
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret) {
+ nd->state = ncsi_dev_state_functional;
+ return;
+ }
+
+ break;
+ case ncsi_dev_state_suspend_done:
+ xchg(&nc->state, NCSI_CHANNEL_INACTIVE);
+ ncsi_process_next_channel(ndp);
+
+ break;
+ default:
+ netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n",
+ nd->state);
+ }
+}
+
+static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
+{
+ struct ncsi_dev *nd = &ndp->ndev;
+ struct net_device *dev = nd->dev;
+ struct ncsi_package *np = ndp->active_package;
+ struct ncsi_channel *nc = ndp->active_channel;
+ struct ncsi_cmd_arg nca;
+ unsigned char index;
+ int ret;
+
+ nca.ndp = ndp;
+ nca.driven = true;
+ switch (nd->state) {
+ case ncsi_dev_state_config:
+ case ncsi_dev_state_config_sp:
+ ndp->pending_req_num = 1;
+
+ /* Select the specific package */
+ nca.type = NCSI_PKT_CMD_SP;
+ if (ndp->flags & NCSI_DEV_HWA)
+ nca.bytes[0] = 0;
+ else
+ nca.bytes[0] = 1;
+ nca.package = np->id;
+ nca.channel = 0x1f;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
+
+ nd->state = ncsi_dev_state_config_cis;
+ break;
+ case ncsi_dev_state_config_cis:
+ ndp->pending_req_num = 1;
+
+ /* Clear initial state */
+ nca.type = NCSI_PKT_CMD_CIS;
+ nca.package = np->id;
+ nca.channel = nc->id;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
+
+ nd->state = ncsi_dev_state_config_sma;
+ break;
+ case ncsi_dev_state_config_sma:
+ case ncsi_dev_state_config_ebf:
+#if IS_ENABLED(CONFIG_IPV6)
+ case ncsi_dev_state_config_egmf:
+#endif
+ case ncsi_dev_state_config_ecnt:
+ case ncsi_dev_state_config_ec:
+ case ncsi_dev_state_config_ae:
+ case ncsi_dev_state_config_gls:
+ ndp->pending_req_num = 1;
+
+ nca.package = np->id;
+ nca.channel = nc->id;
+
+ /* Use first entry in unicast filter table. Note that
+ * the MAC filter table starts from entry 1 instead of
+ * 0.
+ */
+ if (nd->state == ncsi_dev_state_config_sma) {
+ nca.type = NCSI_PKT_CMD_SMA;
+ for (index = 0; index < 6; index++)
+ nca.bytes[index] = dev->dev_addr[index];
+ nca.bytes[6] = 0x1;
+ nca.bytes[7] = 0x1;
+ nd->state = ncsi_dev_state_config_ebf;
+ } else if (nd->state == ncsi_dev_state_config_ebf) {
+ nca.type = NCSI_PKT_CMD_EBF;
+ nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap;
+ nd->state = ncsi_dev_state_config_ecnt;
+#if IS_ENABLED(CONFIG_IPV6)
+ if (ndp->inet6_addr_num > 0 &&
+ (nc->caps[NCSI_CAP_GENERIC].cap &
+ NCSI_CAP_GENERIC_MC))
+ nd->state = ncsi_dev_state_config_egmf;
+ else
+ nd->state = ncsi_dev_state_config_ecnt;
+ } else if (nd->state == ncsi_dev_state_config_egmf) {
+ nca.type = NCSI_PKT_CMD_EGMF;
+ nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap;
+ nd->state = ncsi_dev_state_config_ecnt;
+#endif /* CONFIG_IPV6 */
+ } else if (nd->state == ncsi_dev_state_config_ecnt) {
+ nca.type = NCSI_PKT_CMD_ECNT;
+ nd->state = ncsi_dev_state_config_ec;
+ } else if (nd->state == ncsi_dev_state_config_ec) {
+ /* Enable AEN if it's supported */
+ nca.type = NCSI_PKT_CMD_EC;
+ nd->state = ncsi_dev_state_config_ae;
+ if (!(nc->caps[NCSI_CAP_AEN].cap & NCSI_CAP_AEN_MASK))
+ nd->state = ncsi_dev_state_config_gls;
+ } else if (nd->state == ncsi_dev_state_config_ae) {
+ nca.type = NCSI_PKT_CMD_AE;
+ nca.bytes[0] = 0;
+ nca.dwords[1] = nc->caps[NCSI_CAP_AEN].cap;
+ nd->state = ncsi_dev_state_config_gls;
+ } else if (nd->state == ncsi_dev_state_config_gls) {
+ nca.type = NCSI_PKT_CMD_GLS;
+ nd->state = ncsi_dev_state_config_done;
+ }
+
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
+ break;
+ case ncsi_dev_state_config_done:
+ if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1)
+ xchg(&nc->state, NCSI_CHANNEL_ACTIVE);
+ else
+ xchg(&nc->state, NCSI_CHANNEL_INACTIVE);
+
+ ncsi_start_channel_monitor(nc);
+ ncsi_process_next_channel(ndp);
+ break;
+ default:
+ netdev_warn(dev, "Wrong NCSI state 0x%x in config\n",
+ nd->state);
+ }
+
+ return;
+
+error:
+ ncsi_report_link(ndp, true);
+}
+
+static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
+{
+ struct ncsi_package *np;
+ struct ncsi_channel *nc, *found;
+ struct ncsi_channel_mode *ncm;
+ unsigned long flags;
+
+ /* The search is done once an inactive channel with up
+ * link is found.
+ */
+ found = NULL;
+ NCSI_FOR_EACH_PACKAGE(ndp, np) {
+ NCSI_FOR_EACH_CHANNEL(np, nc) {
+ if (!list_empty(&nc->link) ||
+ nc->state != NCSI_CHANNEL_INACTIVE)
+ continue;
+
+ if (!found)
+ found = nc;
+
+ ncm = &nc->modes[NCSI_MODE_LINK];
+ if (ncm->data[2] & 0x1) {
+ found = nc;
+ goto out;
+ }
+ }
+ }
+
+ if (!found) {
+ ncsi_report_link(ndp, true);
+ return -ENODEV;
+ }
+
+out:
+ spin_lock_irqsave(&ndp->lock, flags);
+ list_add_tail_rcu(&found->link, &ndp->channel_queue);
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ return ncsi_process_next_channel(ndp);
+}
+
+static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp)
+{
+ struct ncsi_package *np;
+ struct ncsi_channel *nc;
+ unsigned int cap;
+
+ /* The hardware arbitration is disabled if any one channel
+ * doesn't support explicitly.
+ */
+ NCSI_FOR_EACH_PACKAGE(ndp, np) {
+ NCSI_FOR_EACH_CHANNEL(np, nc) {
+ cap = nc->caps[NCSI_CAP_GENERIC].cap;
+ if (!(cap & NCSI_CAP_GENERIC_HWA) ||
+ (cap & NCSI_CAP_GENERIC_HWA_MASK) !=
+ NCSI_CAP_GENERIC_HWA_SUPPORT) {
+ ndp->flags &= ~NCSI_DEV_HWA;
+ return false;
+ }
+ }
+ }
+
+ ndp->flags |= NCSI_DEV_HWA;
+ return true;
+}
+
+static int ncsi_enable_hwa(struct ncsi_dev_priv *ndp)
+{
+ struct ncsi_package *np;
+ struct ncsi_channel *nc;
+ unsigned long flags;
+
+ /* Move all available channels to processing queue */
+ spin_lock_irqsave(&ndp->lock, flags);
+ NCSI_FOR_EACH_PACKAGE(ndp, np) {
+ NCSI_FOR_EACH_CHANNEL(np, nc) {
+ WARN_ON_ONCE(nc->state != NCSI_CHANNEL_INACTIVE ||
+ !list_empty(&nc->link));
+ ncsi_stop_channel_monitor(nc);
+ list_add_tail_rcu(&nc->link, &ndp->channel_queue);
+ }
+ }
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ /* We can have no channels in extremely case */
+ if (list_empty(&ndp->channel_queue)) {
+ ncsi_report_link(ndp, false);
+ return -ENOENT;
+ }
+
+ return ncsi_process_next_channel(ndp);
+}
+
+static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
+{
+ struct ncsi_dev *nd = &ndp->ndev;
+ struct ncsi_package *np;
+ struct ncsi_channel *nc;
+ struct ncsi_cmd_arg nca;
+ unsigned char index;
+ int ret;
+
+ nca.ndp = ndp;
+ nca.driven = true;
+ switch (nd->state) {
+ case ncsi_dev_state_probe:
+ nd->state = ncsi_dev_state_probe_deselect;
+ /* Fall through */
+ case ncsi_dev_state_probe_deselect:
+ ndp->pending_req_num = 8;
+
+ /* Deselect all possible packages */
+ nca.type = NCSI_PKT_CMD_DP;
+ nca.channel = 0x1f;
+ for (index = 0; index < 8; index++) {
+ nca.package = index;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
+ }
+
+ nd->state = ncsi_dev_state_probe_package;
+ break;
+ case ncsi_dev_state_probe_package:
+ ndp->pending_req_num = 16;
+
+ /* Select all possible packages */
+ nca.type = NCSI_PKT_CMD_SP;
+ nca.bytes[0] = 1;
+ nca.channel = 0x1f;
+ for (index = 0; index < 8; index++) {
+ nca.package = index;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
+ }
+
+ /* Disable all possible packages */
+ nca.type = NCSI_PKT_CMD_DP;
+ for (index = 0; index < 8; index++) {
+ nca.package = index;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
+ }
+
+ nd->state = ncsi_dev_state_probe_channel;
+ break;
+ case ncsi_dev_state_probe_channel:
+ if (!ndp->active_package)
+ ndp->active_package = list_first_or_null_rcu(
+ &ndp->packages, struct ncsi_package, node);
+ else if (list_is_last(&ndp->active_package->node,
+ &ndp->packages))
+ ndp->active_package = NULL;
+ else
+ ndp->active_package = list_next_entry(
+ ndp->active_package, node);
+
+ /* All available packages and channels are enumerated. The
+ * enumeration happens for once when the NCSI interface is
+ * started. So we need continue to start the interface after
+ * the enumeration.
+ *
+ * We have to choose an active channel before configuring it.
+ * Note that we possibly don't have active channel in extreme
+ * situation.
+ */
+ if (!ndp->active_package) {
+ ndp->flags |= NCSI_DEV_PROBED;
+ if (ncsi_check_hwa(ndp))
+ ncsi_enable_hwa(ndp);
+ else
+ ncsi_choose_active_channel(ndp);
+ return;
+ }
+
+ /* Select the active package */
+ ndp->pending_req_num = 1;
+ nca.type = NCSI_PKT_CMD_SP;
+ nca.bytes[0] = 1;
+ nca.package = ndp->active_package->id;
+ nca.channel = 0x1f;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
+
+ nd->state = ncsi_dev_state_probe_cis;
+ break;
+ case ncsi_dev_state_probe_cis:
+ ndp->pending_req_num = 32;
+
+ /* Clear initial state */
+ nca.type = NCSI_PKT_CMD_CIS;
+ nca.package = ndp->active_package->id;
+ for (index = 0; index < 0x20; index++) {
+ nca.channel = index;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
+ }
+
+ nd->state = ncsi_dev_state_probe_gvi;
+ break;
+ case ncsi_dev_state_probe_gvi:
+ case ncsi_dev_state_probe_gc:
+ case ncsi_dev_state_probe_gls:
+ np = ndp->active_package;
+ ndp->pending_req_num = np->channel_num;
+
+ /* Retrieve version, capability or link status */
+ if (nd->state == ncsi_dev_state_probe_gvi)
+ nca.type = NCSI_PKT_CMD_GVI;
+ else if (nd->state == ncsi_dev_state_probe_gc)
+ nca.type = NCSI_PKT_CMD_GC;
+ else
+ nca.type = NCSI_PKT_CMD_GLS;
+
+ nca.package = np->id;
+ NCSI_FOR_EACH_CHANNEL(np, nc) {
+ nca.channel = nc->id;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
+ }
+
+ if (nd->state == ncsi_dev_state_probe_gvi)
+ nd->state = ncsi_dev_state_probe_gc;
+ else if (nd->state == ncsi_dev_state_probe_gc)
+ nd->state = ncsi_dev_state_probe_gls;
+ else
+ nd->state = ncsi_dev_state_probe_dp;
+ break;
+ case ncsi_dev_state_probe_dp:
+ ndp->pending_req_num = 1;
+
+ /* Deselect the active package */
+ nca.type = NCSI_PKT_CMD_DP;
+ nca.package = ndp->active_package->id;
+ nca.channel = 0x1f;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret)
+ goto error;
+
+ /* Scan channels in next package */
+ nd->state = ncsi_dev_state_probe_channel;
+ break;
+ default:
+ netdev_warn(nd->dev, "Wrong NCSI state 0x%0x in enumeration\n",
+ nd->state);
+ }
+
+ return;
+error:
+ ncsi_report_link(ndp, true);
+}
+
+static void ncsi_dev_work(struct work_struct *work)
+{
+ struct ncsi_dev_priv *ndp = container_of(work,
+ struct ncsi_dev_priv, work);
+ struct ncsi_dev *nd = &ndp->ndev;
+
+ switch (nd->state & ncsi_dev_state_major) {
+ case ncsi_dev_state_probe:
+ ncsi_probe_channel(ndp);
+ break;
+ case ncsi_dev_state_suspend:
+ ncsi_suspend_channel(ndp);
+ break;
+ case ncsi_dev_state_config:
+ ncsi_configure_channel(ndp);
+ break;
+ default:
+ netdev_warn(nd->dev, "Wrong NCSI state 0x%x in workqueue\n",
+ nd->state);
+ }
+}
+
+int ncsi_process_next_channel(struct ncsi_dev_priv *ndp)
+{
+ struct ncsi_channel *nc;
+ int old_state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ndp->lock, flags);
+ nc = list_first_or_null_rcu(&ndp->channel_queue,
+ struct ncsi_channel, link);
+ if (nc) {
+ old_state = xchg(&nc->state, NCSI_CHANNEL_INVISIBLE);
+ list_del_init(&nc->link);
+ }
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ ndp->active_channel = nc;
+ ndp->active_package = nc ? nc->package : NULL;
+ if (!nc) {
+ if (ndp->flags & NCSI_DEV_RESHUFFLE) {
+ ndp->flags &= ~NCSI_DEV_RESHUFFLE;
+ return ncsi_choose_active_channel(ndp);
+ }
+
+ ncsi_report_link(ndp, false);
+ return -ENODEV;
+ }
+
+ switch (old_state) {
+ case NCSI_CHANNEL_INACTIVE:
+ ndp->ndev.state = ncsi_dev_state_config;
+ ncsi_configure_channel(ndp);
+ break;
+ case NCSI_CHANNEL_ACTIVE:
+ ndp->ndev.state = ncsi_dev_state_suspend;
+ ncsi_suspend_channel(ndp);
+ break;
+ default:
+ netdev_err(ndp->ndev.dev, "Invalid state 0x%x on %d:%d\n",
+ nc->state, nc->package->id, nc->id);
+ ncsi_report_link(ndp, false);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static int ncsi_inet6addr_event(struct notifier_block *this,
+ unsigned long event, void *data)
+{
+ struct inet6_ifaddr *ifa = data;
+ struct net_device *dev = ifa->idev->dev;
+ struct ncsi_dev *nd = ncsi_find_dev(dev);
+ struct ncsi_dev_priv *ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
+ struct ncsi_package *np;
+ struct ncsi_channel *nc;
+ struct ncsi_cmd_arg nca;
+ bool action;
+ int ret;
+
+ if (!ndp || (ipv6_addr_type(&ifa->addr) &
+ (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK)))
+ return NOTIFY_OK;
+
+ switch (event) {
+ case NETDEV_UP:
+ action = (++ndp->inet6_addr_num) == 1;
+ nca.type = NCSI_PKT_CMD_EGMF;
+ break;
+ case NETDEV_DOWN:
+ action = (--ndp->inet6_addr_num == 0);
+ nca.type = NCSI_PKT_CMD_DGMF;
+ break;
+ default:
+ return NOTIFY_OK;
+ }
+
+ /* We might not have active channel or packages. The IPv6
+ * required multicast will be enabled when active channel
+ * or packages are chosen.
+ */
+ np = ndp->active_package;
+ nc = ndp->active_channel;
+ if (!action || !np || !nc)
+ return NOTIFY_OK;
+
+ /* We needn't enable or disable it if the function isn't supported */
+ if (!(nc->caps[NCSI_CAP_GENERIC].cap & NCSI_CAP_GENERIC_MC))
+ return NOTIFY_OK;
+
+ nca.ndp = ndp;
+ nca.driven = false;
+ nca.package = np->id;
+ nca.channel = nc->id;
+ nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap;
+ ret = ncsi_xmit_cmd(&nca);
+ if (ret) {
+ netdev_warn(dev, "Fail to %s global multicast filter (%d)\n",
+ (event == NETDEV_UP) ? "enable" : "disable", ret);
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block ncsi_inet6addr_notifier = {
+ .notifier_call = ncsi_inet6addr_event,
+};
+#endif /* CONFIG_IPV6 */
+
struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
void (*handler)(struct ncsi_dev *ndev))
{
@@ -397,6 +1105,9 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
nd->state = ncsi_dev_state_registered;
nd->dev = dev;
nd->handler = handler;
+ ndp->pending_req_num = 0;
+ INIT_LIST_HEAD(&ndp->channel_queue);
+ INIT_WORK(&ndp->work, ncsi_dev_work);
/* Initialize private NCSI device */
spin_lock_init(&ndp->lock);
@@ -411,24 +1122,76 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
}
spin_lock_irqsave(&ncsi_dev_lock, flags);
+#if IS_ENABLED(CONFIG_IPV6)
+ ndp->inet6_addr_num = 0;
+ if (list_empty(&ncsi_dev_list))
+ register_inet6addr_notifier(&ncsi_inet6addr_notifier);
+#endif
list_add_tail_rcu(&ndp->node, &ncsi_dev_list);
spin_unlock_irqrestore(&ncsi_dev_lock, flags);
+ /* Register NCSI packet Rx handler */
+ ndp->ptype.type = cpu_to_be16(ETH_P_NCSI);
+ ndp->ptype.func = ncsi_rcv_rsp;
+ ndp->ptype.dev = dev;
+ dev_add_pack(&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);
+ struct ncsi_package *np;
+ struct ncsi_channel *nc;
+ int old_state, ret;
+
+ if (nd->state != ncsi_dev_state_registered &&
+ nd->state != ncsi_dev_state_functional)
+ return -ENOTTY;
+
+ if (!(ndp->flags & NCSI_DEV_PROBED)) {
+ nd->state = ncsi_dev_state_probe;
+ schedule_work(&ndp->work);
+ return 0;
+ }
+
+ /* Reset channel's state and start over */
+ NCSI_FOR_EACH_PACKAGE(ndp, np) {
+ NCSI_FOR_EACH_CHANNEL(np, nc) {
+ old_state = xchg(&nc->state, NCSI_CHANNEL_INACTIVE);
+ WARN_ON_ONCE(!list_empty(&nc->link) ||
+ old_state == NCSI_CHANNEL_INVISIBLE);
+ }
+ }
+
+ if (ndp->flags & NCSI_DEV_HWA)
+ ret = ncsi_enable_hwa(ndp);
+ else
+ ret = ncsi_choose_active_channel(ndp);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ncsi_start_dev);
+
void ncsi_unregister_dev(struct ncsi_dev *nd)
{
struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
struct ncsi_package *np, *tmp;
unsigned long flags;
+ dev_remove_pack(&ndp->ptype);
+
list_for_each_entry_safe(np, tmp, &ndp->packages, node)
ncsi_remove_package(np);
spin_lock_irqsave(&ncsi_dev_lock, flags);
list_del_rcu(&ndp->node);
+#if IS_ENABLED(CONFIG_IPV6)
+ if (list_empty(&ncsi_dev_list))
+ unregister_inet6addr_notifier(&ncsi_inet6addr_notifier);
+#endif
spin_unlock_irqrestore(&ncsi_dev_lock, flags);
kfree(ndp);
diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c
index 6ec25cb..a21af88 100644
--- a/net/ncsi/ncsi-rsp.c
+++ b/net/ncsi/ncsi-rsp.c
@@ -69,6 +69,9 @@ static int ncsi_rsp_handler_cis(struct ncsi_request *nr)
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, &np, &nc);
if (!nc) {
+ if (ndp->flags & NCSI_DEV_PROBED)
+ return -ENXIO;
+
id = NCSI_CHANNEL_INDEX(rsp->rsp.common.channel);
nc = ncsi_add_channel(np, id);
}
@@ -90,6 +93,9 @@ static int ncsi_rsp_handler_sp(struct ncsi_request *nr)
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
&np, NULL);
if (!np) {
+ if (ndp->flags & NCSI_DEV_PROBED)
+ return -ENXIO;
+
id = NCSI_PACKAGE_INDEX(rsp->rsp.common.channel);
np = ncsi_add_package(ndp, id);
if (!np)
@@ -297,6 +303,7 @@ static int ncsi_rsp_handler_gls(struct ncsi_request *nr)
struct ncsi_dev_priv *ndp = nr->ndp;
struct ncsi_channel *nc;
struct ncsi_channel_mode *ncm;
+ unsigned long flags;
/* Find the package and channel */
rsp = (struct ncsi_rsp_gls_pkt *)skb_network_header(nr->rsp);
@@ -310,6 +317,14 @@ static int ncsi_rsp_handler_gls(struct ncsi_request *nr)
ncm->data[3] = ntohl(rsp->other);
ncm->data[4] = ntohl(rsp->oem_status);
+ if (nr->driven)
+ return 0;
+
+ /* Reset the channel monitor if it has been enabled */
+ spin_lock_irqsave(&nc->lock, flags);
+ nc->timeout = 0;
+ spin_unlock_irqrestore(&nc->lock, flags);
+
return 0;
}
--
2.1.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 05/10] net/ncsi: NCSI AEN packet handler
2016-07-15 10:44 [PATCH net-next v2 00/10] NCSI Support Gavin Shan
` (3 preceding siblings ...)
2016-07-15 10:44 ` [PATCH net-next v2 04/10] net/ncsi: Package and channel management Gavin Shan
@ 2016-07-15 10:44 ` Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 06/10] net/faraday: Helper functions to create or destroy MDIO interface Gavin Shan
` (4 subsequent siblings)
9 siblings, 0 replies; 19+ messages in thread
From: Gavin Shan @ 2016-07-15 10:44 UTC (permalink / raw)
To: netdev
Cc: davem, benh, joel, gerlitz.or, weixue, yuvali,
alexei.starovoitov, f.fainelli, Gavin Shan
This introduces NCSI AEN packet handlers that result in (A) the
currently active channel is reconfigured; (B) Currently active
channel is deconfigured and disabled, another channel is chosen
as active one and configured. Case (B) won't happen if hardware
arbitration has been enabled, the channel that was in active
state is suspended simply.
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
---
net/ncsi/Makefile | 2 +-
net/ncsi/internal.h | 1 +
net/ncsi/ncsi-aen.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++++++++
net/ncsi/ncsi-pkt.h | 36 ++++++++++
net/ncsi/ncsi-rsp.c | 6 +-
5 files changed, 236 insertions(+), 2 deletions(-)
create mode 100644 net/ncsi/ncsi-aen.c
diff --git a/net/ncsi/Makefile b/net/ncsi/Makefile
index 4751819..dd12b56 100644
--- a/net/ncsi/Makefile
+++ b/net/ncsi/Makefile
@@ -1,4 +1,4 @@
#
# Makefile for NCSI API
#
-obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-manage.o
+obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-aen.o ncsi-manage.o
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index 38fc95a..33738c0 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -323,5 +323,6 @@ u32 ncsi_calculate_checksum(unsigned char *data, int len);
int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca);
int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev);
+int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb);
#endif /* __NCSI_INTERNAL_H__ */
diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c
new file mode 100644
index 0000000..d463468
--- /dev/null
+++ b/net/ncsi/ncsi-aen.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright Gavin Shan, IBM Corporation 2016.
+ *
+ * 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 <net/ncsi.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "internal.h"
+#include "ncsi-pkt.h"
+
+static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h,
+ const unsigned short payload)
+{
+ u32 checksum;
+ __be32 *pchecksum;
+
+ if (h->common.revision != NCSI_PKT_REVISION)
+ return -EINVAL;
+ if (ntohs(h->common.length) != payload)
+ return -EINVAL;
+
+ /* Validate checksum, which might be zeroes if the
+ * sender doesn't support checksum according to NCSI
+ * specification.
+ */
+ pchecksum = (__be32 *)((void *)(h + 1) + payload - 4);
+ if (ntohl(*pchecksum) == 0)
+ return 0;
+
+ checksum = ncsi_calculate_checksum((unsigned char *)h,
+ sizeof(*h) + payload - 4);
+ if (*pchecksum != htonl(checksum))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp,
+ struct ncsi_aen_pkt_hdr *h)
+{
+ struct ncsi_aen_lsc_pkt *lsc;
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+ unsigned long old_data;
+ unsigned long flags;
+
+ /* Find the NCSI channel */
+ ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* Update the link status */
+ ncm = &nc->modes[NCSI_MODE_LINK];
+ lsc = (struct ncsi_aen_lsc_pkt *)h;
+ old_data = ncm->data[2];
+ ncm->data[2] = ntohl(lsc->status);
+ ncm->data[4] = ntohl(lsc->oem_status);
+ if (!((old_data ^ ncm->data[2]) & 0x1) ||
+ !list_empty(&nc->link))
+ return 0;
+ if (!(nc->state == NCSI_CHANNEL_INACTIVE && (ncm->data[2] & 0x1)) &&
+ !(nc->state == NCSI_CHANNEL_ACTIVE && !(ncm->data[2] & 0x1)))
+ return 0;
+
+ if (!(ndp->flags & NCSI_DEV_HWA) &&
+ nc->state == NCSI_CHANNEL_ACTIVE)
+ ndp->flags |= NCSI_DEV_RESHUFFLE;
+
+ ncsi_stop_channel_monitor(nc);
+ spin_lock_irqsave(&ndp->lock, flags);
+ list_add_tail_rcu(&nc->link, &ndp->channel_queue);
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ return ncsi_process_next_channel(ndp);
+}
+
+static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp,
+ struct ncsi_aen_pkt_hdr *h)
+{
+ struct ncsi_channel *nc;
+ unsigned long flags;
+
+ /* Find the NCSI channel */
+ ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ if (!list_empty(&nc->link) ||
+ nc->state != NCSI_CHANNEL_ACTIVE)
+ return 0;
+
+ ncsi_stop_channel_monitor(nc);
+ spin_lock_irqsave(&ndp->lock, flags);
+ xchg(&nc->state, NCSI_CHANNEL_INACTIVE);
+ list_add_tail_rcu(&nc->link, &ndp->channel_queue);
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ return ncsi_process_next_channel(ndp);
+}
+
+static int ncsi_aen_handler_hncdsc(struct ncsi_dev_priv *ndp,
+ struct ncsi_aen_pkt_hdr *h)
+{
+ struct ncsi_channel *nc;
+ struct ncsi_channel_mode *ncm;
+ struct ncsi_aen_hncdsc_pkt *hncdsc;
+ unsigned long flags;
+
+ /* Find the NCSI channel */
+ ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc);
+ if (!nc)
+ return -ENODEV;
+
+ /* If the channel is active one, we need reconfigure it */
+ ncm = &nc->modes[NCSI_MODE_LINK];
+ hncdsc = (struct ncsi_aen_hncdsc_pkt *)h;
+ ncm->data[3] = ntohl(hncdsc->status);
+ if (!list_empty(&nc->link) ||
+ nc->state != NCSI_CHANNEL_ACTIVE ||
+ (ncm->data[3] & 0x1))
+ return 0;
+
+ if (ndp->flags & NCSI_DEV_HWA)
+ ndp->flags |= NCSI_DEV_RESHUFFLE;
+
+ /* If this channel is the active one and the link doesn't
+ * work, we have to choose another channel to be active one.
+ * The logic here is exactly similar to what we do when link
+ * is down on the active channel.
+ */
+ ncsi_stop_channel_monitor(nc);
+ spin_lock_irqsave(&ndp->lock, flags);
+ list_add_tail_rcu(&nc->link, &ndp->channel_queue);
+ spin_unlock_irqrestore(&ndp->lock, flags);
+
+ ncsi_process_next_channel(ndp);
+
+ return 0;
+}
+
+static struct ncsi_aen_handler {
+ unsigned char type;
+ int payload;
+ int (*handler)(struct ncsi_dev_priv *ndp,
+ struct ncsi_aen_pkt_hdr *h);
+} ncsi_aen_handlers[] = {
+ { NCSI_PKT_AEN_LSC, 12, ncsi_aen_handler_lsc },
+ { NCSI_PKT_AEN_CR, 4, ncsi_aen_handler_cr },
+ { NCSI_PKT_AEN_HNCDSC, 4, ncsi_aen_handler_hncdsc }
+};
+
+int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb)
+{
+ struct ncsi_aen_pkt_hdr *h;
+ struct ncsi_aen_handler *nah = NULL;
+ int i, ret;
+
+ /* Find the handler */
+ h = (struct ncsi_aen_pkt_hdr *)skb_network_header(skb);
+ for (i = 0; i < ARRAY_SIZE(ncsi_aen_handlers); i++) {
+ if (ncsi_aen_handlers[i].type == h->type) {
+ nah = &ncsi_aen_handlers[i];
+ break;
+ }
+ }
+
+ if (!nah) {
+ netdev_warn(ndp->ndev.dev, "Invalid AEN (0x%x) received\n",
+ h->type);
+ return -ENOENT;
+ }
+
+ ret = ncsi_validate_aen_pkt(h, nah->payload);
+ if (ret)
+ goto out;
+
+ ret = nah->handler(ndp, h);
+out:
+ consume_skb(skb);
+ return ret;
+}
diff --git a/net/ncsi/ncsi-pkt.h b/net/ncsi/ncsi-pkt.h
index 4bdefd9..3ea49ed 100644
--- a/net/ncsi/ncsi-pkt.h
+++ b/net/ncsi/ncsi-pkt.h
@@ -31,6 +31,12 @@ struct ncsi_rsp_pkt_hdr {
__be16 reason; /* Response reason */
};
+struct ncsi_aen_pkt_hdr {
+ struct ncsi_pkt_hdr common; /* Common NCSI packet header */
+ unsigned char reserved2[3]; /* Reserved */
+ unsigned char type; /* AEN packet type */
+};
+
/* NCSI common command packet */
struct ncsi_cmd_pkt {
struct ncsi_cmd_pkt_hdr cmd; /* Command header */
@@ -296,6 +302,30 @@ struct ncsi_rsp_gpuuid_pkt {
__be32 checksum;
};
+/* AEN: Link State Change */
+struct ncsi_aen_lsc_pkt {
+ struct ncsi_aen_pkt_hdr aen; /* AEN header */
+ __be32 status; /* Link status */
+ __be32 oem_status; /* OEM link status */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[14];
+};
+
+/* AEN: Configuration Required */
+struct ncsi_aen_cr_pkt {
+ struct ncsi_aen_pkt_hdr aen; /* AEN header */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[22];
+};
+
+/* AEN: Host Network Controller Driver Status Change */
+struct ncsi_aen_hncdsc_pkt {
+ struct ncsi_aen_pkt_hdr aen; /* AEN header */
+ __be32 status; /* Status */
+ __be32 checksum; /* Checksum */
+ unsigned char pad[18];
+};
+
/* NCSI packet revision */
#define NCSI_PKT_REVISION 0x01
@@ -376,4 +406,10 @@ struct ncsi_rsp_gpuuid_pkt {
#define NCSI_PKT_RSP_R_LENGTH 0x0005 /* Invalid payload length */
#define NCSI_PKT_RSP_R_UNKNOWN 0x7fff /* Command type unsupported */
+/* NCSI AEN packet type */
+#define NCSI_PKT_AEN 0xFF /* AEN Packet */
+#define NCSI_PKT_AEN_LSC 0x00 /* Link status change */
+#define NCSI_PKT_AEN_CR 0x01 /* Configuration required */
+#define NCSI_PKT_AEN_HNCDSC 0x02 /* HNC driver status change */
+
#endif /* __NCSI_PKT_H__ */
diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c
index a21af88..af84389 100644
--- a/net/ncsi/ncsi-rsp.c
+++ b/net/ncsi/ncsi-rsp.c
@@ -980,8 +980,12 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
if (!ndp)
return -ENODEV;
- /* Find the handler */
+ /* Check if it is AEN packet */
hdr = (struct ncsi_pkt_hdr *)skb_network_header(skb);
+ if (hdr->type == NCSI_PKT_AEN)
+ return ncsi_aen_handler(ndp, skb);
+
+ /* Find the handler */
for (i = 0; i < ARRAY_SIZE(ncsi_rsp_handlers); i++) {
if (ncsi_rsp_handlers[i].type == hdr->type) {
if (ncsi_rsp_handlers[i].handler)
--
2.1.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 06/10] net/faraday: Helper functions to create or destroy MDIO interface
2016-07-15 10:44 [PATCH net-next v2 00/10] NCSI Support Gavin Shan
` (4 preceding siblings ...)
2016-07-15 10:44 ` [PATCH net-next v2 05/10] net/ncsi: NCSI AEN packet handler Gavin Shan
@ 2016-07-15 10:44 ` Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 07/10] net/faraday: Read MAC address from chip Gavin Shan
` (3 subsequent siblings)
9 siblings, 0 replies; 19+ messages in thread
From: Gavin Shan @ 2016-07-15 10:44 UTC (permalink / raw)
To: netdev
Cc: davem, benh, joel, gerlitz.or, weixue, yuvali,
alexei.starovoitov, f.fainelli, Gavin Shan
This introduces two helper functions to create or destroy MDIO
interface. No logical changes introduced except the proper MDIO
names are given when having more than one MDIO bus.
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
---
drivers/net/ethernet/faraday/ftgmac100.c | 95 ++++++++++++++++++++------------
1 file changed, 60 insertions(+), 35 deletions(-)
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index e7cf313..9b09493 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -1146,6 +1146,60 @@ static const struct net_device_ops ftgmac100_netdev_ops = {
.ndo_do_ioctl = ftgmac100_do_ioctl,
};
+static int ftgmac100_setup_mdio(struct net_device *netdev)
+{
+ struct ftgmac100 *priv = netdev_priv(netdev);
+ struct platform_device *pdev = to_platform_device(priv->dev);
+ int i, err = 0;
+
+ /* initialize mdio bus */
+ priv->mii_bus = mdiobus_alloc();
+ if (!priv->mii_bus)
+ return -EIO;
+
+ priv->mii_bus->name = "ftgmac100_mdio";
+ snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%d",
+ pdev->name, pdev->id);
+ priv->mii_bus->priv = priv->netdev;
+ priv->mii_bus->read = ftgmac100_mdiobus_read;
+ priv->mii_bus->write = ftgmac100_mdiobus_write;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ priv->mii_bus->irq[i] = PHY_POLL;
+
+ err = mdiobus_register(priv->mii_bus);
+ if (err) {
+ dev_err(priv->dev, "Cannot register MDIO bus!\n");
+ goto err_register_mdiobus;
+ }
+
+ err = ftgmac100_mii_probe(priv);
+ if (err) {
+ dev_err(priv->dev, "MII Probe failed!\n");
+ goto err_mii_probe;
+ }
+
+ return 0;
+
+err_mii_probe:
+ mdiobus_unregister(priv->mii_bus);
+err_register_mdiobus:
+ mdiobus_free(priv->mii_bus);
+ return err;
+}
+
+static void ftgmac100_destroy_mdio(struct net_device *netdev)
+{
+ struct ftgmac100 *priv = netdev_priv(netdev);
+
+ if (!netdev->phydev)
+ return;
+
+ phy_disconnect(netdev->phydev);
+ mdiobus_unregister(priv->mii_bus);
+ mdiobus_free(priv->mii_bus);
+}
+
/******************************************************************************
* struct platform_driver functions
*****************************************************************************/
@@ -1211,31 +1265,9 @@ static int ftgmac100_probe(struct platform_device *pdev)
priv->irq = irq;
- /* initialize mdio bus */
- priv->mii_bus = mdiobus_alloc();
- if (!priv->mii_bus) {
- err = -EIO;
- goto err_alloc_mdiobus;
- }
-
- priv->mii_bus->name = "ftgmac100_mdio";
- snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "ftgmac100_mii");
-
- priv->mii_bus->priv = netdev;
- priv->mii_bus->read = ftgmac100_mdiobus_read;
- priv->mii_bus->write = ftgmac100_mdiobus_write;
-
- err = mdiobus_register(priv->mii_bus);
- if (err) {
- dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
- goto err_register_mdiobus;
- }
-
- err = ftgmac100_mii_probe(priv);
- if (err) {
- dev_err(&pdev->dev, "MII Probe failed!\n");
- goto err_mii_probe;
- }
+ err = ftgmac100_setup_mdio(netdev);
+ if (err)
+ goto err_setup_mdio;
/* register network device */
err = register_netdev(netdev);
@@ -1255,12 +1287,8 @@ static int ftgmac100_probe(struct platform_device *pdev)
return 0;
err_register_netdev:
- phy_disconnect(netdev->phydev);
-err_mii_probe:
- mdiobus_unregister(priv->mii_bus);
-err_register_mdiobus:
- mdiobus_free(priv->mii_bus);
-err_alloc_mdiobus:
+ ftgmac100_destroy_mdio(netdev);
+err_setup_mdio:
iounmap(priv->base);
err_ioremap:
release_resource(priv->res);
@@ -1280,10 +1308,7 @@ static int __exit ftgmac100_remove(struct platform_device *pdev)
priv = netdev_priv(netdev);
unregister_netdev(netdev);
-
- phy_disconnect(netdev->phydev);
- mdiobus_unregister(priv->mii_bus);
- mdiobus_free(priv->mii_bus);
+ ftgmac100_destroy_mdio(netdev);
iounmap(priv->base);
release_resource(priv->res);
--
2.1.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 07/10] net/faraday: Read MAC address from chip
2016-07-15 10:44 [PATCH net-next v2 00/10] NCSI Support Gavin Shan
` (5 preceding siblings ...)
2016-07-15 10:44 ` [PATCH net-next v2 06/10] net/faraday: Helper functions to create or destroy MDIO interface Gavin Shan
@ 2016-07-15 10:44 ` Gavin Shan
2016-07-19 10:50 ` David Laight
2016-07-15 10:44 ` [PATCH net-next v2 08/10] net/faraday: Support NCSI mode Gavin Shan
` (2 subsequent siblings)
9 siblings, 1 reply; 19+ messages in thread
From: Gavin Shan @ 2016-07-15 10:44 UTC (permalink / raw)
To: netdev
Cc: davem, benh, joel, gerlitz.or, weixue, yuvali,
alexei.starovoitov, f.fainelli, Gavin Shan
The device is assigned with random MAC address. It isn't reasonable.
An valid MAC address might have been provided by (uboot) firmware by
device-tree or in chip. It's reasonable to use it to maintain consistency.
This uses the MAC address from device-tree or that in the chip if it's
valid. Otherwise, a random MAC address is given as before.
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
---
drivers/net/ethernet/faraday/ftgmac100.c | 69 ++++++++++++++++++++++++++++----
1 file changed, 62 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 9b09493..2c3f656 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -141,6 +141,64 @@ static void ftgmac100_set_mac(struct ftgmac100 *priv, const unsigned char *mac)
iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR);
}
+static void ftgmac100_setup_mac(struct ftgmac100 *priv)
+{
+ u8 mac[ETH_ALEN];
+ unsigned int m;
+ unsigned int l;
+ void *addr;
+
+ addr = device_get_mac_address(priv->dev, mac, ETH_ALEN);
+ if (addr) {
+ ether_addr_copy(priv->netdev->dev_addr, mac);
+ dev_info(priv->dev, "Read MAC address %pM from device tree\n",
+ mac);
+ return;
+ }
+
+ m = ioread32(priv->base + FTGMAC100_OFFSET_MAC_MADR);
+ l = ioread32(priv->base + FTGMAC100_OFFSET_MAC_LADR);
+
+ mac[0] = (m >> 8) & 0xff;
+ mac[1] = m & 0xff;
+ mac[2] = (l >> 24) & 0xff;
+ mac[3] = (l >> 16) & 0xff;
+ mac[4] = (l >> 8) & 0xff;
+ mac[5] = l & 0xff;
+
+ if (!is_valid_ether_addr(mac)) {
+ mac[5] = (m >> 8) & 0xff;
+ mac[4] = m & 0xff;
+ mac[3] = (l >> 24) & 0xff;
+ mac[2] = (l >> 16) & 0xff;
+ mac[1] = (l >> 8) & 0xff;
+ mac[0] = l & 0xff;
+ }
+
+ if (is_valid_ether_addr(mac)) {
+ ether_addr_copy(priv->netdev->dev_addr, mac);
+ dev_info(priv->dev, "Read MAC address %pM from chip\n", mac);
+ } else {
+ eth_hw_addr_random(priv->netdev);
+ dev_info(priv->dev, "Generated random MAC address %pM\n",
+ priv->netdev->dev_addr);
+ }
+}
+
+static int ftgmac100_set_mac_addr(struct net_device *dev, void *p)
+{
+ int ret;
+
+ ret = eth_prepare_mac_addr_change(dev, p);
+ if (ret < 0)
+ return ret;
+
+ eth_commit_mac_addr_change(dev, p);
+ ftgmac100_set_mac(netdev_priv(dev), dev->dev_addr);
+
+ return 0;
+}
+
static void ftgmac100_init_hw(struct ftgmac100 *priv)
{
/* setup ring buffer base registers */
@@ -1141,7 +1199,7 @@ static const struct net_device_ops ftgmac100_netdev_ops = {
.ndo_open = ftgmac100_open,
.ndo_stop = ftgmac100_stop,
.ndo_start_xmit = ftgmac100_hard_start_xmit,
- .ndo_set_mac_address = eth_mac_addr,
+ .ndo_set_mac_address = ftgmac100_set_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = ftgmac100_do_ioctl,
};
@@ -1265,6 +1323,9 @@ static int ftgmac100_probe(struct platform_device *pdev)
priv->irq = irq;
+ /* MAC address from chip or random one */
+ ftgmac100_setup_mac(priv);
+
err = ftgmac100_setup_mdio(netdev);
if (err)
goto err_setup_mdio;
@@ -1278,12 +1339,6 @@ static int ftgmac100_probe(struct platform_device *pdev)
netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base);
- if (!is_valid_ether_addr(netdev->dev_addr)) {
- eth_hw_addr_random(netdev);
- netdev_info(netdev, "generated random MAC address %pM\n",
- netdev->dev_addr);
- }
-
return 0;
err_register_netdev:
--
2.1.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 08/10] net/faraday: Support NCSI mode
2016-07-15 10:44 [PATCH net-next v2 00/10] NCSI Support Gavin Shan
` (6 preceding siblings ...)
2016-07-15 10:44 ` [PATCH net-next v2 07/10] net/faraday: Read MAC address from chip Gavin Shan
@ 2016-07-15 10:44 ` Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 09/10] net/faraday: Match driver according to compatible property Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 10/10] net/faraday: Mask PHY interrupt with NCSI mode Gavin Shan
9 siblings, 0 replies; 19+ messages in thread
From: Gavin Shan @ 2016-07-15 10:44 UTC (permalink / raw)
To: netdev
Cc: davem, benh, joel, gerlitz.or, weixue, yuvali,
alexei.starovoitov, f.fainelli, Gavin Shan
This makes ftgmac100 driver support NCSI mode. The NCSI is enabled
on the interface if property "use-nc-si" or "use-ncsi" is found from
the device node in device tree.
* No PHY device is used when NCSI mode is enabled.
* The NCSI device (struct ncsi_dev) is created when probing the
device while it's enabled/started when the interface is brought
up.
* Hardware IP checksum dosn't work when NCSI mode is enabled. It
is disabled on enabled NCSI.
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
---
drivers/net/ethernet/faraday/ftgmac100.c | 85 ++++++++++++++++++++++++++++----
1 file changed, 75 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 2c3f656..1cd4975 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -31,6 +31,7 @@
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <net/ip.h>
+#include <net/ncsi.h>
#include "ftgmac100.h"
@@ -68,10 +69,13 @@ struct ftgmac100 {
struct net_device *netdev;
struct device *dev;
+ struct ncsi_dev *ndev;
struct napi_struct napi;
struct mii_bus *mii_bus;
int old_speed;
+ bool use_ncsi;
+ bool enabled;
};
static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
@@ -1010,7 +1014,10 @@ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
struct net_device *netdev = dev_id;
struct ftgmac100 *priv = netdev_priv(netdev);
- if (likely(netif_running(netdev))) {
+ /* When running in NCSI mode, the interface should be ready for
+ * receiving or transmitting NCSI packets before it's opened.
+ */
+ if (likely(priv->use_ncsi || netif_running(netdev))) {
/* Disable interrupts for polling */
iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
napi_schedule(&priv->napi);
@@ -1123,17 +1130,33 @@ static int ftgmac100_open(struct net_device *netdev)
goto err_hw;
ftgmac100_init_hw(priv);
- ftgmac100_start_hw(priv, 10);
-
- phy_start(netdev->phydev);
+ ftgmac100_start_hw(priv, priv->use_ncsi ? 100 : 10);
+ if (netdev->phydev)
+ phy_start(netdev->phydev);
+ else if (priv->use_ncsi)
+ netif_carrier_on(netdev);
napi_enable(&priv->napi);
netif_start_queue(netdev);
/* enable all interrupts */
iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
+
+ /* Start the NCSI device */
+ if (priv->use_ncsi) {
+ err = ncsi_start_dev(priv->ndev);
+ if (err)
+ goto err_ncsi;
+ }
+
+ priv->enabled = true;
+
return 0;
+err_ncsi:
+ napi_disable(&priv->napi);
+ netif_stop_queue(netdev);
+ iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
err_hw:
free_irq(priv->irq, netdev);
err_irq:
@@ -1146,12 +1169,17 @@ static int ftgmac100_stop(struct net_device *netdev)
{
struct ftgmac100 *priv = netdev_priv(netdev);
+ if (!priv->enabled)
+ return 0;
+
/* disable all interrupts */
+ priv->enabled = false;
iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
netif_stop_queue(netdev);
napi_disable(&priv->napi);
- phy_stop(netdev->phydev);
+ if (netdev->phydev)
+ phy_stop(netdev->phydev);
ftgmac100_stop_hw(priv);
free_irq(priv->irq, netdev);
@@ -1192,6 +1220,9 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
/* optional */
static int ftgmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
{
+ if (!netdev->phydev)
+ return -ENXIO;
+
return phy_mii_ioctl(netdev->phydev, ifr, cmd);
}
@@ -1258,6 +1289,15 @@ static void ftgmac100_destroy_mdio(struct net_device *netdev)
mdiobus_free(priv->mii_bus);
}
+static void ftgmac100_ncsi_handler(struct ncsi_dev *nd)
+{
+ if (unlikely(nd->state != ncsi_dev_state_functional))
+ return;
+
+ netdev_info(nd->dev, "NCSI interface %s\n",
+ nd->link_up ? "up" : "down");
+}
+
/******************************************************************************
* struct platform_driver functions
*****************************************************************************/
@@ -1267,7 +1307,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
int irq;
struct net_device *netdev;
struct ftgmac100 *priv;
- int err;
+ int err = 0;
if (!pdev)
return -ENODEV;
@@ -1291,7 +1331,6 @@ static int ftgmac100_probe(struct platform_device *pdev)
netdev->ethtool_ops = &ftgmac100_ethtool_ops;
netdev->netdev_ops = &ftgmac100_netdev_ops;
- netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO;
platform_set_drvdata(pdev, netdev);
@@ -1326,9 +1365,34 @@ static int ftgmac100_probe(struct platform_device *pdev)
/* MAC address from chip or random one */
ftgmac100_setup_mac(priv);
- err = ftgmac100_setup_mdio(netdev);
- if (err)
- goto err_setup_mdio;
+ if (pdev->dev.of_node &&
+ of_get_property(pdev->dev.of_node, "use-ncsi", NULL)) {
+ if (!IS_ENABLED(CONFIG_NET_NCSI)) {
+ dev_err(&pdev->dev, "NCSI stack not enabled\n");
+ goto err_ncsi_dev;
+ }
+
+ dev_info(&pdev->dev, "Using NCSI interface\n");
+ priv->use_ncsi = true;
+ priv->ndev = ncsi_register_dev(netdev, ftgmac100_ncsi_handler);
+ if (!priv->ndev)
+ goto err_ncsi_dev;
+ } else {
+ priv->use_ncsi = false;
+ err = ftgmac100_setup_mdio(netdev);
+ if (err)
+ goto err_setup_mdio;
+ }
+
+ /* We have to disable on-chip IP checksum functionality
+ * when NCSI is enabled on the interface. It doesn't work
+ * in that case.
+ */
+ netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO;
+ if (priv->use_ncsi &&
+ of_get_property(pdev->dev.of_node, "no-hw-checksum", NULL))
+ netdev->features &= ~NETIF_F_IP_CSUM;
+
/* register network device */
err = register_netdev(netdev);
@@ -1341,6 +1405,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
return 0;
+err_ncsi_dev:
err_register_netdev:
ftgmac100_destroy_mdio(netdev);
err_setup_mdio:
--
2.1.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 09/10] net/faraday: Match driver according to compatible property
2016-07-15 10:44 [PATCH net-next v2 00/10] NCSI Support Gavin Shan
` (7 preceding siblings ...)
2016-07-15 10:44 ` [PATCH net-next v2 08/10] net/faraday: Support NCSI mode Gavin Shan
@ 2016-07-15 10:44 ` Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 10/10] net/faraday: Mask PHY interrupt with NCSI mode Gavin Shan
9 siblings, 0 replies; 19+ messages in thread
From: Gavin Shan @ 2016-07-15 10:44 UTC (permalink / raw)
To: netdev
Cc: davem, benh, joel, gerlitz.or, weixue, yuvali,
alexei.starovoitov, f.fainelli, Gavin Shan
This matches the driver with devices compatible with "faraday,ftgmac100"
declared in the device tree. Originally, device's name from device
tree for it.
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
---
drivers/net/ethernet/faraday/ftgmac100.c | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 1cd4975..d8afa2d 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -1438,14 +1438,20 @@ static int __exit ftgmac100_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id ftgmac100_of_match[] = {
+ { .compatible = "faraday,ftgmac100" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ftgmac100_of_match);
+
static struct platform_driver ftgmac100_driver = {
- .probe = ftgmac100_probe,
- .remove = __exit_p(ftgmac100_remove),
- .driver = {
- .name = DRV_NAME,
+ .probe = ftgmac100_probe,
+ .remove = __exit_p(ftgmac100_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = ftgmac100_of_match,
},
};
-
module_platform_driver(ftgmac100_driver);
MODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>");
--
2.1.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next v2 10/10] net/faraday: Mask PHY interrupt with NCSI mode
2016-07-15 10:44 [PATCH net-next v2 00/10] NCSI Support Gavin Shan
` (8 preceding siblings ...)
2016-07-15 10:44 ` [PATCH net-next v2 09/10] net/faraday: Match driver according to compatible property Gavin Shan
@ 2016-07-15 10:44 ` Gavin Shan
9 siblings, 0 replies; 19+ messages in thread
From: Gavin Shan @ 2016-07-15 10:44 UTC (permalink / raw)
To: netdev
Cc: davem, benh, joel, gerlitz.or, weixue, yuvali,
alexei.starovoitov, f.fainelli, Gavin Shan
Bogus PHY interrupts are observed. This masks the PHY interrupt
when the interface works in NCSI mode as there is no attached
PHY under the circumstance.
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
---
drivers/net/ethernet/faraday/ftgmac100.c | 27 +++++++++++++++------------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index d8afa2d..2d4c7ea 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -74,6 +74,7 @@ struct ftgmac100 {
struct mii_bus *mii_bus;
int old_speed;
+ int int_mask_all;
bool use_ncsi;
bool enabled;
};
@@ -84,14 +85,6 @@ static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
/******************************************************************************
* internal functions (hardware register access)
*****************************************************************************/
-#define INT_MASK_ALL_ENABLED (FTGMAC100_INT_RPKT_LOST | \
- FTGMAC100_INT_XPKT_ETH | \
- FTGMAC100_INT_XPKT_LOST | \
- FTGMAC100_INT_AHB_ERR | \
- FTGMAC100_INT_PHYSTS_CHG | \
- FTGMAC100_INT_RPKT_BUF | \
- FTGMAC100_INT_NO_RXBUF)
-
static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr)
{
iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR);
@@ -1070,8 +1063,9 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget)
ftgmac100_tx_complete(priv);
}
- if (status & (FTGMAC100_INT_NO_RXBUF | FTGMAC100_INT_RPKT_LOST |
- FTGMAC100_INT_AHB_ERR | FTGMAC100_INT_PHYSTS_CHG)) {
+ if (status & priv->int_mask_all & (FTGMAC100_INT_NO_RXBUF |
+ FTGMAC100_INT_RPKT_LOST | FTGMAC100_INT_AHB_ERR |
+ FTGMAC100_INT_PHYSTS_CHG)) {
if (net_ratelimit())
netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status,
status & FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "",
@@ -1094,7 +1088,8 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget)
napi_complete(napi);
/* enable all interrupts */
- iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
+ iowrite32(priv->int_mask_all,
+ priv->base + FTGMAC100_OFFSET_IER);
}
return rx;
@@ -1140,7 +1135,7 @@ static int ftgmac100_open(struct net_device *netdev)
netif_start_queue(netdev);
/* enable all interrupts */
- iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
+ iowrite32(priv->int_mask_all, priv->base + FTGMAC100_OFFSET_IER);
/* Start the NCSI device */
if (priv->use_ncsi) {
@@ -1365,6 +1360,13 @@ static int ftgmac100_probe(struct platform_device *pdev)
/* MAC address from chip or random one */
ftgmac100_setup_mac(priv);
+ priv->int_mask_all = (FTGMAC100_INT_RPKT_LOST |
+ FTGMAC100_INT_XPKT_ETH |
+ FTGMAC100_INT_XPKT_LOST |
+ FTGMAC100_INT_AHB_ERR |
+ FTGMAC100_INT_PHYSTS_CHG |
+ FTGMAC100_INT_RPKT_BUF |
+ FTGMAC100_INT_NO_RXBUF);
if (pdev->dev.of_node &&
of_get_property(pdev->dev.of_node, "use-ncsi", NULL)) {
if (!IS_ENABLED(CONFIG_NET_NCSI)) {
@@ -1374,6 +1376,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "Using NCSI interface\n");
priv->use_ncsi = true;
+ priv->int_mask_all &= ~FTGMAC100_INT_PHYSTS_CHG;
priv->ndev = ncsi_register_dev(netdev, ftgmac100_ncsi_handler);
if (!priv->ndev)
goto err_ncsi_dev;
--
2.1.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 02/10] net/ncsi: NCSI command packet handler
2016-07-15 10:44 ` [PATCH net-next v2 02/10] net/ncsi: NCSI command packet handler Gavin Shan
@ 2016-07-15 14:08 ` kbuild test robot
2016-07-18 0:15 ` Gavin Shan
0 siblings, 1 reply; 19+ messages in thread
From: kbuild test robot @ 2016-07-15 14:08 UTC (permalink / raw)
To: Gavin Shan
Cc: kbuild-all, netdev, davem, benh, joel, gerlitz.or, weixue,
yuvali, alexei.starovoitov, f.fainelli, Gavin Shan
[-- Attachment #1: Type: text/plain, Size: 3971 bytes --]
Hi,
[auto build test ERROR on net-next/master]
url: https://github.com/0day-ci/linux/commits/Gavin-Shan/NCSI-Support/20160715-190549
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All error/warnings (new ones prefixed by >>):
In file included from include/linux/swab.h:4:0,
from include/uapi/linux/byteorder/little_endian.h:12,
from include/linux/byteorder/little_endian.h:4,
from arch/x86/include/uapi/asm/byteorder.h:4,
from include/asm-generic/bitops/le.h:5,
from arch/x86/include/asm/bitops.h:504,
from include/linux/bitops.h:36,
from include/linux/kernel.h:10,
from include/linux/list.h:8,
from include/linux/module.h:9,
from net/ncsi/ncsi-cmd.c:10:
net/ncsi/ncsi-cmd.c: In function 'ncsi_alloc_command':
>> net/ncsi/ncsi-cmd.c:301:24: error: 'ETH_P_NCSI' undeclared (first use in this function)
skb->protocol = htons(ETH_P_NCSI);
^
include/uapi/linux/swab.h:100:54: note: in definition of macro '__swab16'
#define __swab16(x) (__u16)__builtin_bswap16((__u16)(x))
^
>> include/linux/byteorder/generic.h:134:21: note: in expansion of macro '__cpu_to_be16'
#define ___htons(x) __cpu_to_be16(x)
^~~~~~~~~~~~~
>> include/linux/byteorder/generic.h:140:18: note: in expansion of macro '___htons'
#define htons(x) ___htons(x)
^~~~~~~~
>> net/ncsi/ncsi-cmd.c:301:18: note: in expansion of macro 'htons'
skb->protocol = htons(ETH_P_NCSI);
^~~~~
net/ncsi/ncsi-cmd.c:301:24: note: each undeclared identifier is reported only once for each function it appears in
skb->protocol = htons(ETH_P_NCSI);
^
include/uapi/linux/swab.h:100:54: note: in definition of macro '__swab16'
#define __swab16(x) (__u16)__builtin_bswap16((__u16)(x))
^
>> include/linux/byteorder/generic.h:134:21: note: in expansion of macro '__cpu_to_be16'
#define ___htons(x) __cpu_to_be16(x)
^~~~~~~~~~~~~
>> include/linux/byteorder/generic.h:140:18: note: in expansion of macro '___htons'
#define htons(x) ___htons(x)
^~~~~~~~
>> net/ncsi/ncsi-cmd.c:301:18: note: in expansion of macro 'htons'
skb->protocol = htons(ETH_P_NCSI);
^~~~~
net/ncsi/ncsi-cmd.c: In function 'ncsi_xmit_cmd':
net/ncsi/ncsi-cmd.c:347:22: error: 'ETH_P_NCSI' undeclared (first use in this function)
eh->h_proto = htons(ETH_P_NCSI);
^
include/uapi/linux/swab.h:100:54: note: in definition of macro '__swab16'
#define __swab16(x) (__u16)__builtin_bswap16((__u16)(x))
^
>> include/linux/byteorder/generic.h:134:21: note: in expansion of macro '__cpu_to_be16'
#define ___htons(x) __cpu_to_be16(x)
^~~~~~~~~~~~~
>> include/linux/byteorder/generic.h:140:18: note: in expansion of macro '___htons'
#define htons(x) ___htons(x)
^~~~~~~~
net/ncsi/ncsi-cmd.c:347:16: note: in expansion of macro 'htons'
eh->h_proto = htons(ETH_P_NCSI);
^~~~~
vim +/ETH_P_NCSI +301 net/ncsi/ncsi-cmd.c
295
296 nr->cmd = skb;
297 skb_reserve(skb, hlen);
298 skb_reset_network_header(skb);
299
300 skb->dev = dev;
> 301 skb->protocol = htons(ETH_P_NCSI);
302
303 return nr;
304 }
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 55093 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 04/10] net/ncsi: Package and channel management
2016-07-15 10:44 ` [PATCH net-next v2 04/10] net/ncsi: Package and channel management Gavin Shan
@ 2016-07-15 14:30 ` kbuild test robot
2016-07-18 0:16 ` Gavin Shan
0 siblings, 1 reply; 19+ messages in thread
From: kbuild test robot @ 2016-07-15 14:30 UTC (permalink / raw)
To: Gavin Shan
Cc: kbuild-all, netdev, davem, benh, joel, gerlitz.or, weixue,
yuvali, alexei.starovoitov, f.fainelli, Gavin Shan
[-- Attachment #1: Type: text/plain, Size: 3104 bytes --]
Hi,
[auto build test ERROR on net-next/master]
url: https://github.com/0day-ci/linux/commits/Gavin-Shan/NCSI-Support/20160715-190549
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All error/warnings (new ones prefixed by >>):
In file included from include/linux/swab.h:4:0,
from include/uapi/linux/byteorder/little_endian.h:12,
from include/linux/byteorder/little_endian.h:4,
from arch/x86/include/uapi/asm/byteorder.h:4,
from include/asm-generic/bitops/le.h:5,
from arch/x86/include/asm/bitops.h:504,
from include/linux/bitops.h:36,
from include/linux/kernel.h:10,
from include/linux/list.h:8,
from include/linux/module.h:9,
from net/ncsi/ncsi-manage.c:10:
net/ncsi/ncsi-manage.c: In function 'ncsi_register_dev':
>> net/ncsi/ncsi-manage.c:1134:32: error: 'ETH_P_NCSI' undeclared (first use in this function)
ndp->ptype.type = cpu_to_be16(ETH_P_NCSI);
^
include/uapi/linux/swab.h:100:54: note: in definition of macro '__swab16'
#define __swab16(x) (__u16)__builtin_bswap16((__u16)(x))
^
include/linux/byteorder/generic.h:95:21: note: in expansion of macro '__cpu_to_be16'
#define cpu_to_be16 __cpu_to_be16
^~~~~~~~~~~~~
>> net/ncsi/ncsi-manage.c:1134:20: note: in expansion of macro 'cpu_to_be16'
ndp->ptype.type = cpu_to_be16(ETH_P_NCSI);
^~~~~~~~~~~
net/ncsi/ncsi-manage.c:1134:32: note: each undeclared identifier is reported only once for each function it appears in
ndp->ptype.type = cpu_to_be16(ETH_P_NCSI);
^
include/uapi/linux/swab.h:100:54: note: in definition of macro '__swab16'
#define __swab16(x) (__u16)__builtin_bswap16((__u16)(x))
^
include/linux/byteorder/generic.h:95:21: note: in expansion of macro '__cpu_to_be16'
#define cpu_to_be16 __cpu_to_be16
^~~~~~~~~~~~~
>> net/ncsi/ncsi-manage.c:1134:20: note: in expansion of macro 'cpu_to_be16'
ndp->ptype.type = cpu_to_be16(ETH_P_NCSI);
^~~~~~~~~~~
vim +/ETH_P_NCSI +1134 net/ncsi/ncsi-manage.c
1128 register_inet6addr_notifier(&ncsi_inet6addr_notifier);
1129 #endif
1130 list_add_tail_rcu(&ndp->node, &ncsi_dev_list);
1131 spin_unlock_irqrestore(&ncsi_dev_lock, flags);
1132
1133 /* Register NCSI packet Rx handler */
> 1134 ndp->ptype.type = cpu_to_be16(ETH_P_NCSI);
1135 ndp->ptype.func = ncsi_rcv_rsp;
1136 ndp->ptype.dev = dev;
1137 dev_add_pack(&ndp->ptype);
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 55093 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 02/10] net/ncsi: NCSI command packet handler
2016-07-15 14:08 ` kbuild test robot
@ 2016-07-18 0:15 ` Gavin Shan
2016-07-19 1:48 ` Gavin Shan
0 siblings, 1 reply; 19+ messages in thread
From: Gavin Shan @ 2016-07-18 0:15 UTC (permalink / raw)
To: kbuild test robot
Cc: Gavin Shan, kbuild-all, netdev, davem, benh, joel, gerlitz.or,
weixue, yuvali, alexei.starovoitov, f.fainelli
On Fri, Jul 15, 2016 at 10:08:23PM +0800, kbuild test robot wrote:
>[auto build test ERROR on net-next/master]
>
>url: https://github.com/0day-ci/linux/commits/Gavin-Shan/NCSI-Support/20160715-190549
>config: i386-allmodconfig (attached as .config)
>compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430
>reproduce:
> # save the attached .config to linux build tree
> make ARCH=i386
>
>All error/warnings (new ones prefixed by >>):
>
> In file included from include/linux/swab.h:4:0,
> from include/uapi/linux/byteorder/little_endian.h:12,
> from include/linux/byteorder/little_endian.h:4,
> from arch/x86/include/uapi/asm/byteorder.h:4,
> from include/asm-generic/bitops/le.h:5,
> from arch/x86/include/asm/bitops.h:504,
> from include/linux/bitops.h:36,
> from include/linux/kernel.h:10,
> from include/linux/list.h:8,
> from include/linux/module.h:9,
> from net/ncsi/ncsi-cmd.c:10:
> net/ncsi/ncsi-cmd.c: In function 'ncsi_alloc_command':
>>> net/ncsi/ncsi-cmd.c:301:24: error: 'ETH_P_NCSI' undeclared (first use in this function)
> skb->protocol = htons(ETH_P_NCSI);
> ^
The ETH_P_NCSI definition in include/uapi/linux/if_ether.h was missed from this series. I
will fix it in next respin to address comments received from this series.
Thanks,
Gavin
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 04/10] net/ncsi: Package and channel management
2016-07-15 14:30 ` kbuild test robot
@ 2016-07-18 0:16 ` Gavin Shan
0 siblings, 0 replies; 19+ messages in thread
From: Gavin Shan @ 2016-07-18 0:16 UTC (permalink / raw)
To: kbuild test robot
Cc: Gavin Shan, kbuild-all, netdev, davem, benh, joel, gerlitz.or,
weixue, yuvali, alexei.starovoitov, f.fainelli
On Fri, Jul 15, 2016 at 10:30:00PM +0800, kbuild test robot wrote:
>[auto build test ERROR on net-next/master]
>
>url: https://github.com/0day-ci/linux/commits/Gavin-Shan/NCSI-Support/20160715-190549
>config: i386-allmodconfig (attached as .config)
>compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430
>reproduce:
> # save the attached .config to linux build tree
> make ARCH=i386
>
>All error/warnings (new ones prefixed by >>):
>
> In file included from include/linux/swab.h:4:0,
> from include/uapi/linux/byteorder/little_endian.h:12,
> from include/linux/byteorder/little_endian.h:4,
> from arch/x86/include/uapi/asm/byteorder.h:4,
> from include/asm-generic/bitops/le.h:5,
> from arch/x86/include/asm/bitops.h:504,
> from include/linux/bitops.h:36,
> from include/linux/kernel.h:10,
> from include/linux/list.h:8,
> from include/linux/module.h:9,
> from net/ncsi/ncsi-manage.c:10:
> net/ncsi/ncsi-manage.c: In function 'ncsi_register_dev':
>>> net/ncsi/ncsi-manage.c:1134:32: error: 'ETH_P_NCSI' undeclared (first use in this function)
> ndp->ptype.type = cpu_to_be16(ETH_P_NCSI);
> ^
Same to breakage caused by PATCH[02/10]. The ETH_P_NCSI definition in
include/uapi/linux/if_ether.h was missed from this series. I will fix
it in next respin to address comments received for this series.
Thanks,
Gavin
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 02/10] net/ncsi: NCSI command packet handler
2016-07-18 0:15 ` Gavin Shan
@ 2016-07-19 1:48 ` Gavin Shan
0 siblings, 0 replies; 19+ messages in thread
From: Gavin Shan @ 2016-07-19 1:48 UTC (permalink / raw)
To: Gavin Shan
Cc: kbuild test robot, kbuild-all, netdev, davem, benh, joel,
gerlitz.or, weixue, yuvali, alexei.starovoitov, f.fainelli
On Mon, Jul 18, 2016 at 10:15:21AM +1000, Gavin Shan wrote:
>On Fri, Jul 15, 2016 at 10:08:23PM +0800, kbuild test robot wrote:
>>[auto build test ERROR on net-next/master]
>>
>>url: https://github.com/0day-ci/linux/commits/Gavin-Shan/NCSI-Support/20160715-190549
>>config: i386-allmodconfig (attached as .config)
>>compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430
>>reproduce:
>> # save the attached .config to linux build tree
>> make ARCH=i386
>>
>>All error/warnings (new ones prefixed by >>):
>>
>> In file included from include/linux/swab.h:4:0,
>> from include/uapi/linux/byteorder/little_endian.h:12,
>> from include/linux/byteorder/little_endian.h:4,
>> from arch/x86/include/uapi/asm/byteorder.h:4,
>> from include/asm-generic/bitops/le.h:5,
>> from arch/x86/include/asm/bitops.h:504,
>> from include/linux/bitops.h:36,
>> from include/linux/kernel.h:10,
>> from include/linux/list.h:8,
>> from include/linux/module.h:9,
>> from net/ncsi/ncsi-cmd.c:10:
>> net/ncsi/ncsi-cmd.c: In function 'ncsi_alloc_command':
>>>> net/ncsi/ncsi-cmd.c:301:24: error: 'ETH_P_NCSI' undeclared (first use in this function)
>> skb->protocol = htons(ETH_P_NCSI);
>> ^
>
>The ETH_P_NCSI definition in include/uapi/linux/if_ether.h was missed from this series. I
>will fix it in next respin to address comments received from this series.
>
I will send followup v3 shortly since David marked this series as "change requested"
in patchwork.
Thanks,
Gavin
^ permalink raw reply [flat|nested] 19+ messages in thread
* RE: [PATCH net-next v2 07/10] net/faraday: Read MAC address from chip
2016-07-15 10:44 ` [PATCH net-next v2 07/10] net/faraday: Read MAC address from chip Gavin Shan
@ 2016-07-19 10:50 ` David Laight
2016-07-19 10:57 ` Benjamin Herrenschmidt
0 siblings, 1 reply; 19+ messages in thread
From: David Laight @ 2016-07-19 10:50 UTC (permalink / raw)
To: 'Gavin Shan', netdev
Cc: davem, benh, joel, gerlitz.or, weixue, yuvali,
alexei.starovoitov, f.fainelli
From: Gavin Shan
> Sent: 15 July 2016 11:44
> The device is assigned with random MAC address. It isn't reasonable.
> An valid MAC address might have been provided by (uboot) firmware by
> device-tree or in chip. It's reasonable to use it to maintain consistency.
>
> This uses the MAC address from device-tree or that in the chip if it's
> valid. Otherwise, a random MAC address is given as before.
...
> + m = ioread32(priv->base + FTGMAC100_OFFSET_MAC_MADR);
> + l = ioread32(priv->base + FTGMAC100_OFFSET_MAC_LADR);
> +
> + mac[0] = (m >> 8) & 0xff;
> + mac[1] = m & 0xff;
> + mac[2] = (l >> 24) & 0xff;
> + mac[3] = (l >> 16) & 0xff;
> + mac[4] = (l >> 8) & 0xff;
> + mac[5] = l & 0xff;
> +
> + if (!is_valid_ether_addr(mac)) {
> + mac[5] = (m >> 8) & 0xff;
> + mac[4] = m & 0xff;
> + mac[3] = (l >> 24) & 0xff;
> + mac[2] = (l >> 16) & 0xff;
> + mac[1] = (l >> 8) & 0xff;
> + mac[0] = l & 0xff;
> + }
...
That is horrid, not all byte reversed addresses will be invalid.
David
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 07/10] net/faraday: Read MAC address from chip
2016-07-19 10:50 ` David Laight
@ 2016-07-19 10:57 ` Benjamin Herrenschmidt
2016-07-19 23:40 ` Gavin Shan
0 siblings, 1 reply; 19+ messages in thread
From: Benjamin Herrenschmidt @ 2016-07-19 10:57 UTC (permalink / raw)
To: David Laight, 'Gavin Shan', netdev
Cc: davem, joel, gerlitz.or, weixue, yuvali, alexei.starovoitov, f.fainelli
On Tue, 2016-07-19 at 10:50 +0000, David Laight wrote:
> > + if (!is_valid_ether_addr(mac)) {
> > + mac[5] = (m >> 8) & 0xff;
> > + mac[4] = m & 0xff;
> > + mac[3] = (l >> 24) & 0xff;
> > + mac[2] = (l >> 16) & 0xff;
> > + mac[1] = (l >> 8) & 0xff;
> > + mac[0] = l & 0xff;
> > + }
> ...
>
> That is horrid, not all byte reversed addresses will be invalid.
Right, that's just a hack for a broken vendor uboot we had here, Gavin,
drop that part of the patch please.
Cheers,
Ben.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next v2 07/10] net/faraday: Read MAC address from chip
2016-07-19 10:57 ` Benjamin Herrenschmidt
@ 2016-07-19 23:40 ` Gavin Shan
0 siblings, 0 replies; 19+ messages in thread
From: Gavin Shan @ 2016-07-19 23:40 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: David Laight, 'Gavin Shan',
netdev, davem, joel, gerlitz.or, weixue, yuvali,
alexei.starovoitov, f.fainelli
On Tue, Jul 19, 2016 at 08:57:53PM +1000, Benjamin Herrenschmidt wrote:
>On Tue, 2016-07-19 at 10:50 +0000, David Laight wrote:
>> > + if (!is_valid_ether_addr(mac)) {
>> > + mac[5] = (m >> 8) & 0xff;
>> > + mac[4] = m & 0xff;
>> > + mac[3] = (l >> 24) & 0xff;
>> > + mac[2] = (l >> 16) & 0xff;
>> > + mac[1] = (l >> 8) & 0xff;
>> > + mac[0] = l & 0xff;
>> > + }
>> ...
>>
>> That is horrid, not all byte reversed addresses will be invalid.
>
>Right, that's just a hack for a broken vendor uboot we had here, Gavin,
>drop that part of the patch please.
>
Sure, I'll drop it in v4 or a followup patch. v3 is being reviewed this
moment.
Thanks,
Gavin
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2016-07-19 23:40 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-15 10:44 [PATCH net-next v2 00/10] NCSI Support Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 01/10] net/ncsi: Resource management Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 02/10] net/ncsi: NCSI command packet handler Gavin Shan
2016-07-15 14:08 ` kbuild test robot
2016-07-18 0:15 ` Gavin Shan
2016-07-19 1:48 ` Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 03/10] net/ncsi: NCSI response " Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 04/10] net/ncsi: Package and channel management Gavin Shan
2016-07-15 14:30 ` kbuild test robot
2016-07-18 0:16 ` Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 05/10] net/ncsi: NCSI AEN packet handler Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 06/10] net/faraday: Helper functions to create or destroy MDIO interface Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 07/10] net/faraday: Read MAC address from chip Gavin Shan
2016-07-19 10:50 ` David Laight
2016-07-19 10:57 ` Benjamin Herrenschmidt
2016-07-19 23:40 ` Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 08/10] net/faraday: Support NCSI mode Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 09/10] net/faraday: Match driver according to compatible property Gavin Shan
2016-07-15 10:44 ` [PATCH net-next v2 10/10] net/faraday: Mask PHY interrupt with NCSI mode Gavin Shan
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.