[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <5342EA0E.2080502@linux.vnet.ibm.com>
Date: Mon, 07 Apr 2014 13:10:22 -0500
From: Santiago Leon <santil@...ux.vnet.ibm.com>
To: netdev@...r.kernel.org
Subject: [PATCH] New driver for IBM System i/p VNIC protocol
This is a new device driver for a high performance SR-IOV assisted virtual network for IBM System p and IBM System i systems. The SR-IOV VF will be attached to the VIOS partition and mapped to the Linux client via the hypervisors VNIC protocol that this driver implements.
This driver is able to perform basic tx and rx, new features and improvements will be added as they are being developed and tested.
Signed-off-by: Santiago Leon <santil@...ux.vnet.ibm.com>
---
arch/powerpc/include/asm/hvcall.h | 20
drivers/net/ethernet/ibm/Kconfig | 10
drivers/net/ethernet/ibm/Makefile | 1
drivers/net/ethernet/ibm/vnic.c | 3594
++++++++++++++++++++++++++++++++++++++
drivers/net/ethernet/ibm/vnic.h | 947 ++++++++++
5 files changed, 4572 insertions(+)
--- a/drivers/net/ethernet/ibm/Kconfig 2014-03-30 22:40:15.000000000 -0500
+++ b/drivers/net/ethernet/ibm/Kconfig 2014-04-07 09:45:10.000000000 -0500
@@ -39,4 +39,14 @@ config EHEA
To compile the driver as a module, choose M here. The module
will be called ehea.
+config VNIC
+ tristate "IBM Virtual NIC support"
+ depends on PPC_PSERIES
+ ---help---
+ This driver supports Virtual NIC adapters on IBM i and IBM System p
+ systems.
+
+ To compile this driver as a module, choose M here. The module will
+ be called vnic.
+
endif # NET_VENDOR_IBM
--- a/drivers/net/ethernet/ibm/Makefile 2014-03-30 22:40:15.000000000 -0500
+++ b/drivers/net/ethernet/ibm/Makefile 2014-04-07 09:45:10.000000000 -0500
@@ -3,5 +3,6 @@
#
obj-$(CONFIG_IBMVETH) += ibmveth.o
+obj-$(CONFIG_VNIC) += vnic.o
obj-$(CONFIG_IBM_EMAC) += emac/
obj-$(CONFIG_EHEA) += ehea/
--- a/drivers/net/ethernet/ibm/vnic.h 1969-12-31 18:00:00.000000000 -0600
+++ b/drivers/net/ethernet/ibm/vnic.h 2014-04-07 09:45:10.000000000 -0500
@@ -0,0 +1,947 @@
+/**************************************************************************/
+/* */
+/* IBM System i and System p Virtual NIC Device Driver */
+/* Copyright (C) 2003 IBM Corp. */
+/* Santiago Leon (santil@...ibm.com) */
+/* */
+/* 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. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */
+/* USA */
+/* */
+/* This module contains the implementation of a virtual ethernet device */
+/* for use with IBM i/pSeries LPAR Linux. It utilizes the logical LAN */
+/* option of the RS/6000 Platform Architechture to interface with virtual */
+/* ethernet NICs that are presented to the partition by the hypervisor. */
+/* */
+/**************************************************************************/
+
+#define VNIC_NAME "vnic"
+#define VNIC_DRIVER_VERSION "0.037"
+#define VNIC_INVALID_MAP -1
+#define VNIC_STATS_TIMEOUT 1
+#define VMIC_IO_ENTITLEMENT_DEFAULT 610305 /* basic structures plus
+ 100 2k buffers */
+
+/* Initial module_parameters */
+#define VNIC_RX_WEIGHT 16
+#define VNIC_BUFFS_PER_POOL 100 /* when changing this, update
+ VMIC_IO_ENTITLEMENT_DEFAULT */
+#define VNIC_MAX_TX_QUEUES 4
+#define VNIC_DEBUG 0
+
+
+struct vnic_login_buffer {
+ u32 len;
+ u32 version;
+#define INITIAL_VERSION_LB 1
+ u32 num_txcomp_subcrqs;
+ u32 off_txcomp_subcrqs;
+ u32 num_rxcomp_subcrqs;
+ u32 off_rxcomp_subcrqs;
+ u32 login_rsp_ioba;
+ u32 login_rsp_len;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_login_rsp_buffer {
+ u32 len;
+ u32 version;
+#define INITIAL_VERSION_LRB 1
+ u32 num_txsubm_subcrqs;
+ u32 off_txsubm_subcrqs;
+ u32 num_rxadd_subcrqs;
+ u32 off_rxadd_subcrqs;
+ u32 off_rxadd_buff_size;
+ u32 num_supp_tx_desc;
+ u32 off_supp_tx_desc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_query_ip_offload_buffer {
+ u32 len;
+ u32 version;
+#define INITIAL_VERSION_IOB 1
+ u8 ipv4_chksum;
+ u8 ipv6_chksum;
+ u8 tcp_ipv4_chksum;
+ u8 tcp_ipv6_chksum;
+ u8 udp_ipv4_chksum;
+ u8 udp_ipv6_chksum;
+ u8 large_tx_ipv4;
+ u8 large_tx_ipv6;
+ u8 large_rx_ipv4;
+ u8 large_rx_ipv6;
+ u8 reserved1[14];
+ u16 max_ipv4_header_size;
+ u16 max_ipv6_header_size;
+ u16 max_tcp_header_size;
+ u16 max_udp_header_size;
+ u32 max_large_tx_size;
+ u32 max_large_rx_size;
+ u8 reserved2[16];
+ u8 ipv6_extension_header;
+#define IPV6_EH_NOT_SUPPORTED 0x00
+#define IPV6_EH_SUPPORTED_LIM 0x01
+#define IPV6_EH_SUPPORTED 0xFF
+ u8 tcp_pseudosum_req;
+#define TCP_PS_NOT_REQUIRED 0x00
+#define TCP_PS_REQUIRED 0x01
+ u8 reserved3[30];
+ u16 num_ipv6_ext_headers;
+ u32 off_ipv6_ext_headers;
+ u8 reserved4[154];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_control_ip_offload_buffer {
+ u32 len;
+ u32 version;
+#define INITIAL_VERSION_IOB 1
+ u8 ipv4_chksum;
+ u8 ipv6_chksum;
+ u8 tcp_ipv4_chksum;
+ u8 tcp_ipv6_chksum;
+ u8 udp_ipv4_chksum;
+ u8 udp_ipv6_chksum;
+ u8 large_tx_ipv4;
+ u8 large_tx_ipv6;
+ u8 bad_packet_rx;
+ u8 large_rx_ipv4;
+ u8 large_rx_ipv6;
+ u8 reserved4[111];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_fw_component {
+ u8 name[48];
+ u32 trace_buff_size;
+ u8 correlator;
+ u8 trace_level;
+ u8 parent_correlator;
+ u8 error_check_level;
+ u8 trace_on;
+ u8 reserved[7];
+ u8 description[192];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_fw_trace_entry {
+ u32 trace_id;
+ u8 num_valid_data;
+ u8 reserved[3];
+ u64 pmc_registers;
+ u64 timebase;
+ u64 trace_data[5];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_statistics {
+ u32 version;
+ u32 promiscuous;
+ u64 rx_packets;
+ u64 rx_bytes;
+ u64 tx_packets;
+ u64 tx_bytes;
+ u64 ucast_tx_packets;
+ u64 ucast_rx_packets;
+ u64 mcast_tx_packets;
+ u64 mcast_rx_packets;
+ u64 bcast_tx_packets;
+ u64 bcast_rx_packets;
+ u64 align_errors;
+ u64 fcs_errors;
+ u64 single_collision_frames;
+ u64 multi_collision_frames;
+ u64 sqe_test_errors;
+ u64 deferred_tx;
+ u64 late_collisions;
+ u64 excess_collisions;
+ u64 internal_mac_tx_errors;
+ u64 carrier_sense;
+ u64 too_long_frames;
+ u64 internal_mac_rx_errors;
+ u8 reserved[72];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_acl_buffer {
+ u32 len;
+ u32 version;
+#define INITIAL_VERSION_IOB 1
+ u8 mac_acls_restrict;
+ u8 vlan_acls_restrict;
+ u8 reserved1[22];
+ u32 num_mac_addrs;
+ u32 offset_mac_addrs;
+ u32 num_vlan_ids;
+ u32 offset_vlan_ids;
+ u8 reserved2[80];
+}__attribute__((packed, aligned (8)));
+
+#define VNIC_TX_DESC_VERSIONS 3
+struct vnic_tx_desc_v0 {
+ u8 type;
+ u8 version;
+#define VNIC_TX_DESC_V0 0
+ u8 flags1;
+#define VNIC_LARGE_TX 0x80
+#define VNIC_IP_CHKSUM 0x40
+#define VNIC_TCP_CHKSUM 0x20
+#define VNIC_INS_VLANH 0x10
+#define VNIC_UDP_CHKSUM 0x08
+#define VNIC_MULTI_TX_DESC 0x04
+#define VNIC_LAST_FRAG 0x02
+#define VNIC_TX_COMP_EVENT 0x01
+ u8 flags2;
+#define IPV6 0x80
+ u16 tcp_udp_h_off;
+ u16 vlan_h;
+ u8 reserved;
+ u8 mss_msb;
+ u16 mss;
+ u32 correlator;
+ u32 ioba1;
+ u32 len1;
+ u32 ioba2;
+ u32 len2;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_tx_comp_desc {
+ u8 type;
+ u8 num_comps;
+ u16 rcs[5];
+ u32 correlators[5];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_tx_desc_v1 {
+ u8 type;
+ u8 version;
+#define VNIC_TX_DESC_V1 1
+ u8 flags1; /* same as V0 */
+ u8 flags2; /* same as V0 */
+ u16 tcp_udp_h_off;
+ u16 vlan_h;
+ u8 reserved;
+ u8 mss[3];
+ u32 correlator;
+ u32 ioba1;
+ u32 len1;
+ u8 dest_mac[6];
+ u8 ethertype;
+}__attribute__((packed, aligned (8)));
+
+#define VNIC_MAX_FRAGS_PER_CRQ 3
+struct vnic_tx_desc_v2 {
+ u8 type;
+ u8 version;
+#define VNIC_TX_DESC_V2 2
+ u8 flags1; /* only VNIC_LAST_FRAG and VNIC_TX_COMP_EVENT allowed here */
+ u8 reserved;
+ u32 ioba1;
+ u32 len1;
+ u32 correlator;
+ u32 ioba2;
+ u32 len2;
+ u32 ioba3;
+ u32 len3;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_rx_desc {
+ u8 type;
+ u8 flags;
+#define VNIC_IP_CHKSUM_GOOD 0x80
+#define VNIC_TCP_UDP_CHKSUM_GOOD 0x40
+#define VNIC_END_FRAME 0x20
+#define VNIC_EXACT_MC 0x10
+#define VNIC_CSUM_FIELD 0x08
+ u16 off_frame_data;
+ u32 len;
+ u64 correlator;
+ u16 csum;
+ u8 reserved[14];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_generic_scrq {
+ u8 type;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_rx_buff_add_desc {
+ u8 type;
+ u8 reserved1[7];
+ u64 correlator;
+ u32 ioba;
+ u32 len;
+ u8 reserved2[8];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_rc {
+ volatile u8 code; /* one of enum vnic_rc_codes */
+ volatile u8 detailed_data[3];
+}__attribute__((packed, aligned (4)));
+
+struct vnic_generic_crq {
+ u8 type;
+ u8 cmd;
+ u8 params[10];
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_version_exchange {
+ u8 type;
+ u8 cmd;
+ u16 version;
+#define VNIC_INITIAL_VERSION 1
+ u8 reserved[8];
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_capability {
+ u8 type;
+ u8 cmd;
+ u16 capability; /* one of vnic_capabilities */
+ struct vnic_rc rc;
+ u32 number; /*FIX: should be u64, but I'm getting the least
+ significant word first */
+}__attribute__((packed, aligned (8)));
+
+struct vnic_login {
+ u8 type;
+ u8 cmd;
+ u8 reserved[6];
+ u32 ioba;
+ u32 len;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_phys_parms {
+ u8 type;
+ u8 cmd;
+ u8 flags1;
+#define VNIC_EXTERNAL_LOOPBACK 0x80
+#define VNIC_INTERNAL_LOOPBACK 0x40
+#define VNIC_PROMISC 0x20
+#define VNIC_PHYS_LINK_ACTIVE 0x10
+#define VNIC_AUTONEG_DUPLEX 0x08
+#define VNIC_FULL_DUPLEX 0x04
+#define VNIC_HALF_DUPLEX 0x02
+#define VNIC_CAN_CHG_PHYS_PARMS 0x01
+ u8 flags2;
+#define VNIC_LOGICAL_LNK_ACTIVE 0x80
+ u32 speed;
+#define VNIC_AUTONEG 0x80
+#define VNIC_10MBPS 0x40
+#define VNIC_100MBPS 0x20
+#define VNIC_1GBPS 0x10
+#define VNIC_10GBPS 0x08
+ u32 mtu;
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_logical_link_state {
+ u8 type;
+ u8 cmd;
+ u8 link_state;
+#define VNIC_LOGICAL_LNK_DN 0x00
+#define VNIC_LOGICAL_LNK_UP 0x01
+#define VNIC_LOGICAL_LNK_QUERY 0xff
+ u8 reserved[9];
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_query_ip_offload {
+ u8 type;
+ u8 cmd;
+ u8 reserved[2];
+ u32 len;
+ u32 ioba;
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_control_ip_offload {
+ u8 type;
+ u8 cmd;
+ u8 reserved[2];
+ u32 ioba;
+ u32 len;
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_request_dump_size {
+ u8 type;
+ u8 cmd;
+ u8 reserved[6];
+ u32 len;
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_request_dump {
+ u8 type;
+ u8 cmd;
+ u8 reserved1[2];
+ u32 ioba;
+ u32 len;
+ u8 reserved2[4];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_request_dump_rsp {
+ u8 type;
+ u8 cmd;
+ u8 reserved[6];
+ u32 dumped_len;
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_request_ras_comp_num {
+ u8 type;
+ u8 cmd;
+ u8 reserved1[2];
+ u32 num_components;
+ u8 reserved2[4];
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_request_ras_comps {
+ u8 type;
+ u8 cmd;
+ u8 reserved[2];
+ u32 ioba;
+ u32 len;
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_control_ras {
+ u8 type;
+ u8 cmd;
+ u8 correlator;
+ u8 level;
+ u8 op;
+#define VNIC_TRACE_LEVEL 1
+#define VNIC_ERROR_LEVEL 2
+#define VNIC_TRACE_PAUSE 3
+#define VNIC_TRACE_RESUME 4
+#define VNIC_TRACE_ON 5
+#define VNIC_TRACE_OFF 6
+#define VNIC_CHG_TRACE_BUFF_SZ 7
+ u8 trace_buff_sz[3];
+ u8 reserved[4];
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_collect_fw_trace {
+ u8 type;
+ u8 cmd;
+ u8 correlator;
+ u8 reserved;
+ u32 ioba;
+ u32 len;
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_request_statistics {
+ u8 type;
+ u8 cmd;
+ u8 flags;
+#define VNIC_PHYSICAL_PORT 0x80
+ u8 reserved1;
+ u32 ioba;
+ u32 len;
+ u8 reserved[4];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_request_debug_stats {
+ u8 type;
+ u8 cmd;
+ u8 reserved[2];
+ u32 ioba;
+ u32 len;
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_error_indication {
+ u8 type;
+ u8 cmd;
+ u8 flags;
+#define VNIC_FATAL_ERROR 0x80
+ u8 reserved1;
+ u32 error_id;
+ u32 detail_error_sz;
+ u16 error_cause;
+ u8 reserved2[2];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_request_error_info {
+ u8 type;
+ u8 cmd;
+ u8 reserved[2];
+ u32 ioba;
+ u32 len;
+ u32 error_id;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_request_error_rsp {
+ u8 type;
+ u8 cmd;
+ u8 reserved[2];
+ u32 error_id;
+ u32 len;
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_link_state_indication {
+ u8 type;
+ u8 cmd;
+ u8 reserved1[2];
+ u8 phys_link_state;
+ u8 logical_link_state;
+ u8 reserved2[10];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_change_mac_addr {
+ u8 type;
+ u8 cmd;
+ u8 mac_addr[6];
+ struct vnic_rc rc;
+ u8 reserved[4];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_multicast_ctrl {
+ u8 type;
+ u8 cmd;
+ u8 mac_addr[6];
+ u8 flags;
+#define VNIC_ENABLE_MC 0x80
+#define VNIC_DISABLE_MC 0x40
+#define VNIC_ENABLE_ALL 0x20
+#define VNIC_DISABLE_ALL 0x10
+ u8 reserved1;
+ u16 reserved2; /* was num_enabled_mc_addr; */
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_get_vpd_size_rsp {
+ u8 type;
+ u8 cmd;
+ u8 reserved[2];
+ u64 len;
+ struct vnic_rc rc;
+}__attribute__((packed, aligned (8)));
+
+struct vnic_get_vpd {
+ u8 type;
+ u8 cmd;
+ u8 reserved1[2];
+ u32 ioba;
+ u32 len;
+ u8 reserved[4];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_acl_change_indication {
+ u8 type;
+ u8 cmd;
+ u16 change_type;
+#define VNIC_MAC_ACL 0;
+#define VNIC_VLAN_ACL 1;
+ u8 reserved[12];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_acl_query {
+ u8 type;
+ u8 cmd;
+ u8 reserved1[2];
+ u32 ioba;
+ u32 len;
+ u8 reserved2[4];
+}__attribute__((packed, aligned (8)));
+
+struct vnic_tune {
+ u8 type;
+ u8 cmd;
+ u8 reserved1[2];
+ u32 ioba;
+ u32 len;
+ u8 reserved2[4];
+}__attribute__((packed, aligned (8)));
+
+union vnic_crq {
+ struct vnic_generic_crq generic;
+ struct vnic_version_exchange version_exchange;
+ struct vnic_version_exchange version_exchange_rsp;
+ struct vnic_capability query_capability;
+ struct vnic_capability query_capability_rsp;
+ struct vnic_capability request_capability;
+ struct vnic_capability request_capability_rsp;
+ struct vnic_login login;
+ struct vnic_generic_crq login_rsp;
+ struct vnic_phys_parms query_phys_parms;
+ struct vnic_phys_parms query_phys_parms_rsp;
+ struct vnic_phys_parms query_phys_capabilities;
+ struct vnic_phys_parms query_phys_capabilities_rsp;
+ struct vnic_phys_parms set_phys_parms;
+ struct vnic_phys_parms set_phys_parms_rsp;
+ struct vnic_logical_link_state logical_link_state;
+ struct vnic_logical_link_state logical_link_state_rsp;
+ struct vnic_query_ip_offload query_ip_offload;
+ struct vnic_query_ip_offload query_ip_offload_rsp;
+ struct vnic_control_ip_offload control_ip_offload;
+ struct vnic_control_ip_offload control_ip_offload_rsp;
+ struct vnic_request_dump_size request_dump_size;
+ struct vnic_request_dump_size request_dump_size_rsp;
+ struct vnic_request_dump request_dump;
+ struct vnic_request_dump_rsp request_dump_rsp;
+ struct vnic_request_ras_comp_num request_ras_comp_num;
+ struct vnic_request_ras_comp_num request_ras_comp_num_rsp;
+ struct vnic_request_ras_comps request_ras_comps;
+ struct vnic_request_ras_comps request_ras_comps_rsp;
+ struct vnic_control_ras control_ras;
+ struct vnic_control_ras control_ras_rsp;
+ struct vnic_collect_fw_trace collect_fw_trace;
+ struct vnic_collect_fw_trace collect_fw_trace_rsp;
+ struct vnic_request_statistics request_statistics;
+ struct vnic_generic_crq request_statistics_rsp;
+ struct vnic_request_debug_stats request_debug_stats;
+ struct vnic_request_debug_stats request_debug_stats_rsp;
+ struct vnic_error_indication error_indication;
+ struct vnic_request_error_info request_error_info;
+ struct vnic_request_error_rsp request_error_rsp;
+ struct vnic_link_state_indication link_state_indication;
+ struct vnic_change_mac_addr change_mac_addr;
+ struct vnic_change_mac_addr change_mac_addr_rsp;
+ struct vnic_multicast_ctrl multicast_ctrl;
+ struct vnic_multicast_ctrl multicast_ctrl_rsp;
+ struct vnic_generic_crq get_vpd_size;
+ struct vnic_get_vpd_size_rsp get_vpd_size_rsp;
+ struct vnic_get_vpd get_vpd;
+ struct vnic_generic_crq get_vpd_rsp;
+ struct vnic_acl_change_indication acl_change_indication;
+ struct vnic_acl_query acl_query;
+ struct vnic_generic_crq acl_query_rsp;
+ struct vnic_tune tune;
+ struct vnic_generic_crq tune_rsp;
+};
+
+enum vnic_rc_codes {
+ SUCCESS = 0,
+ PARTIALSUCCESS = 1,
+ PERMISSION = 2,
+ NOMEMORY = 3,
+ PARAMETER = 4,
+ UNKNOWNCOMMAND = 5,
+ ABORTED = 6,
+ INVALIDSTATE = 7,
+ INVALIDIOBA = 8,
+ INVALIDLENGTH = 9,
+ UNSUPPORTEDOPTION = 10,
+};
+
+enum vnic_capabilities {
+ MIN_TX_QUEUES = 1,
+ MIN_RX_QUEUES = 2,
+ MIN_RX_ADD_QUEUES = 3,
+ MAX_TX_QUEUES = 4,
+ MAX_RX_QUEUES = 5,
+ MAX_RX_ADD_QUEUES = 6,
+ REQ_TX_QUEUES = 7,
+ REQ_RX_QUEUES = 8,
+ REQ_RX_ADD_QUEUES = 9,
+ MIN_TX_ENTRIES_PER_SUBCRQ = 10,
+ MIN_RX_ADD_ENTRIES_PER_SUBCRQ = 11,
+ MAX_TX_ENTRIES_PER_SUBCRQ = 12,
+ MAX_RX_ADD_ENTRIES_PER_SUBCRQ = 13,
+ REQ_TX_ENTRIES_PER_SUBCRQ = 14,
+ REQ_RX_ADD_ENTRIES_PER_SUBCRQ = 15,
+ TCP_IP_OFFLOAD = 16,
+ PROMISC_REQUESTED = 17,
+ PROMISC_SUPPORTED = 18,
+ MIN_MTU = 19,
+ MAX_MTU = 20,
+ REQ_MTU = 21,
+ MAX_MULTICAST_FILTERS = 22,
+ VLAN_HEADER_INSERTION = 23,
+ MAX_TX_SG_ENTRIES = 25,
+ RX_SG_SUPPORTED = 26,
+ RX_SG_REQUESTED = 27,
+};
+
+enum vnic_error_cause {
+ ADAPTER_PROBLEM = 0,
+ BUS_PROBLEM = 1,
+ FW_PROBLEM = 2,
+ DD_PROBLEM = 3,
+ EEH_RECOVERY = 4,
+ FW_UPDATED = 5,
+ LOW_MEMORY = 6,
+};
+
+enum vnic_commands {
+ VERSION_EXCHANGE = 0x01,
+ VERSION_EXCHANGE_RSP = 0x81,
+ QUERY_CAPABILITY = 0x02,
+ QUERY_CAPABILITY_RSP = 0x82,
+ REQUEST_CAPABILITY = 0x03,
+ REQUEST_CAPABILITY_RSP = 0x83,
+ LOGIN = 0x04,
+ LOGIN_RSP = 0x84,
+ QUERY_PHYS_PARMS = 0x05,
+ QUERY_PHYS_PARMS_RSP = 0x85,
+ QUERY_PHYS_CAPABILITIES = 0x06,
+ QUERY_PHYS_CAPABILITIES_RSP = 0x86,
+ SET_PHYS_PARMS = 0x07,
+ SET_PHYS_PARMS_RSP = 0x87,
+ ERROR_INDICATION = 0x08,
+ REQUEST_ERROR_INFO = 0x09,
+ REQUEST_ERROR_RSP = 0x89,
+ REQUEST_DUMP_SIZE = 0x0A,
+ REQUEST_DUMP_SIZE_RSP = 0x8A,
+ REQUEST_DUMP = 0x0B,
+ REQUEST_DUMP_RSP = 0x8B,
+ LOGICAL_LINK_STATE = 0x0C,
+ LOGICAL_LINK_STATE_RSP = 0x8C,
+ REQUEST_STATISTICS = 0x0D,
+ REQUEST_STATISTICS_RSP = 0x8D,
+ REQUEST_RAS_COMP_NUM = 0x0E,
+ REQUEST_RAS_COMP_NUM_RSP = 0x8E,
+ REQUEST_RAS_COMPS = 0x0F,
+ REQUEST_RAS_COMPS_RSP = 0x8F,
+ CONTROL_RAS = 0x10,
+ CONTROL_RAS_RSP = 0x90,
+ COLLECT_FW_TRACE = 0x11,
+ COLLECT_FW_TRACE_RSP = 0x91,
+ LINK_STATE_INDICATION = 0x12,
+ CHANGE_MAC_ADDR = 0x13,
+ CHANGE_MAC_ADDR_RSP = 0x93,
+ MULTICAST_CTRL = 0x14,
+ MULTICAST_CTRL_RSP = 0x94,
+ GET_VPD_SIZE = 0x15,
+ GET_VPD_SIZE_RSP = 0x95,
+ GET_VPD = 0x16,
+ GET_VPD_RSP = 0x96,
+ TUNE = 0x17,
+ TUNE_RSP = 0x97,
+ QUERY_IP_OFFLOAD = 0x18,
+ QUERY_IP_OFFLOAD_RSP = 0x98,
+ CONTROL_IP_OFFLOAD = 0x19,
+ CONTROL_IP_OFFLOAD_RSP = 0x99,
+ ACL_CHANGE_INDICATION = 0x1A,
+ ACL_QUERY = 0x1B,
+ ACL_QUERY_RSP = 0x9B,
+ REQUEST_DEBUG_STATS = 0x1C,
+ REQUEST_DEBUG_STATS_RSP = 0x9C,
+};
+
+enum vnic_crq_type {
+ VNIC_CRQ_CMD = 0x80,
+ VNIC_CRQ_CMD_RSP = 0x80,
+ VNIC_CRQ_INIT_CMD = 0xC0,
+ VNIC_CRQ_INIT_RSP = 0xC0,
+ VNIC_CRQ_XPORT_EVENT = 0xFF,
+};
+
+enum ibmvfc_crq_format {
+ VNIC_CRQ_INIT = 0x01,
+ VNIC_CRQ_INIT_COMPLETE = 0x02,
+ VNIC_PARTITION_MIGRATED = 0x06,
+};
+
+
+struct vnic_crq_queue {
+ union vnic_crq *msgs;
+ int size, cur;
+ dma_addr_t msg_token;
+ spinlock_t lock;
+};
+
+union sub_crq {
+ struct vnic_generic_scrq generic;
+ struct vnic_tx_comp_desc tx_comp;
+ struct vnic_tx_desc_v0 v0;
+ struct vnic_tx_desc_v1 v1;
+ struct vnic_tx_desc_v2 v2;
+ struct vnic_rx_desc rx_comp;
+ struct vnic_rx_buff_add_desc rx_add;
+};
+
+struct vnic_sub_crq_queue {
+ union sub_crq *msgs;
+ int size, cur;
+ dma_addr_t msg_token;
+ unsigned long crq_num;
+ unsigned long hw_irq;
+ unsigned int irq;
+ unsigned int pool_index;
+ int scrq_num;
+ spinlock_t lock;
+ struct sk_buff *rx_skb_top;
+ struct vnic_adapter *adapter;
+};
+
+struct vnic_tx_buff {
+ struct sk_buff *skb;
+ dma_addr_t data_dma[VNIC_MAX_FRAGS_PER_CRQ];
+ unsigned int data_len[VNIC_MAX_FRAGS_PER_CRQ];
+ int index;
+ int pool_index;
+ int last_frag;
+ int used_bounce;
+};
+
+struct vnic_tx_pool {
+ struct vnic_tx_buff* tx_buff;
+ int *free_map;
+ int consumer_index;
+ int producer_index;
+ wait_queue_head_t vnic_tx_comp_q;
+ struct task_struct *work_thread;
+};
+
+struct vnic_rx_buff {
+ struct sk_buff *skb;
+ dma_addr_t dma;
+ int size;
+ int pool_index;
+};
+
+struct vnic_rx_pool {
+ struct vnic_rx_buff *rx_buff;
+ int size;
+ int index;
+ int buff_size;
+ atomic_t available;
+ int *free_map;
+ int next_free;
+ int next_alloc;
+ int active;
+};
+
+struct vnic_error_buff {
+ char *buff;
+ dma_addr_t dma;
+ int len;
+ struct list_head list;
+ u32 error_id;
+};
+
+struct vnic_fw_comp_internal {
+ struct vnic_adapter *adapter;
+ int num;
+ struct debugfs_blob_wrapper desc_blob;
+ int paused;
+};
+
+struct vnic_inflight_cmd {
+ union vnic_crq crq;
+ struct list_head list;
+};
+
+struct vnic_adapter {
+ struct vio_dev *vdev;
+ struct net_device *netdev;
+ struct vnic_crq_queue crq;
+ u8 mac_addr[ETH_ALEN];
+ struct vnic_query_ip_offload_buffer ip_offload_buf;
+ dma_addr_t ip_offload_tok;
+ struct vnic_control_ip_offload_buffer ip_offload_ctrl;
+ dma_addr_t ip_offload_ctrl_tok;
+ int migrated;
+ u32 msg_enable;
+ void *bounce_buffer;
+ int bounce_buffer_size;
+ dma_addr_t bounce_buffer_dma;
+
+ /* Statistics */
+ struct net_device_stats net_stats;
+ struct vnic_statistics stats;
+ dma_addr_t stats_token;
+ struct completion stats_done;
+ spinlock_t stats_lock;
+ int replenish_no_mem;
+ int replenish_add_buff_success;
+ int replenish_add_buff_failure;
+ int replenish_task_cycles;
+ int tx_send_failed;
+ int tx_map_failed;
+
+ int phys_link_state;
+ int logical_link_state;
+
+ /* login data */
+ struct vnic_login_buffer *login_buf;
+ dma_addr_t login_buf_token;
+ int login_buf_sz;
+
+ struct vnic_login_rsp_buffer *login_rsp_buf;
+ dma_addr_t login_rsp_buf_token;
+ int login_rsp_buf_sz;
+
+ atomic_t running_cap_queries;
+
+ struct vnic_sub_crq_queue **tx_scrq;
+ struct vnic_sub_crq_queue **rx_scrq;
+ int requested_caps;
+
+ /* rx structs */
+ struct napi_struct *napi;
+ struct vnic_rx_pool *rx_pool;
+ long promisc;
+
+ struct vnic_tx_pool *tx_pool;
+ int closing;
+ struct completion init_done;
+
+ struct list_head errors;
+ spinlock_t error_list_lock;
+
+ /* debugfs */
+ struct dentry *debugfs_dir;
+ struct dentry *debugfs_dump;
+ struct completion fw_done;
+ char *dump_data;
+ dma_addr_t dump_data_token;
+ int dump_data_size;
+ int ras_comp_num;
+ struct vnic_fw_component *ras_comps;
+ struct vnic_fw_comp_internal *ras_comp_int;
+ dma_addr_t ras_comps_tok;
+ struct dentry *ras_comps_ent;
+
+ /* in-flight commands that allocate and/or map memory*/
+ struct list_head inflight;
+ spinlock_t inflight_lock;
+
+ /* partner capabilities */
+ long min_tx_queues;
+ long min_rx_queues;
+ long min_rx_add_queues;
+ long max_tx_queues;
+ long max_rx_queues;
+ long max_rx_add_queues;
+ long req_tx_queues;
+ long req_rx_queues;
+ long req_rx_add_queues;
+ long min_tx_entries_per_subcrq;
+ long min_rx_add_entries_per_subcrq;
+ long max_tx_entries_per_subcrq;
+ long max_rx_add_entries_per_subcrq;
+ long req_tx_entries_per_subcrq;
+ long req_rx_add_entries_per_subcrq;
+ long tcp_ip_offload;
+ long promisc_requested;
+ long promisc_supported;
+ long min_mtu;
+ long max_mtu;
+ long req_mtu;
+ long max_multicast_filters;
+ long vlan_header_insertion;
+ long max_tx_sg_entries;
+ long rx_sg_supported;
+ long rx_sg_requested;
+};
+
+#define DBG_CMD(CMD) do { if (vnic_debug) CMD; } while (0)
+#define ENTER DBG_CMD(printk(KERN_INFO VNIC_NAME": Entering %s\n", __func__))
+#define LEAVE DBG_CMD(printk(KERN_INFO VNIC_NAME": Leaving %s\n", __func__))
+#define vnic_dbg(adapter, ...) \
+ DBG_CMD(dev_info(&adapter->vdev->dev, ##__VA_ARGS__))
+
--- a/drivers/net/ethernet/ibm/vnic.c 1969-12-31 18:00:00.000000000 -0600
+++ b/drivers/net/ethernet/ibm/vnic.c 2014-04-07 11:19:14.000000000 -0500
@@ -0,0 +1,3594 @@
+/**************************************************************************/
+/* */
+/* IBM System i and System p Virtual NIC Device Driver */
+/* Copyright (C) 2003 IBM Corp. */
+/* Santiago Leon (santil@...ibm.com) */
+/* */
+/* 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. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */
+/* USA */
+/* */
+/* This module contains the implementation of a virtual ethernet device */
+/* for use with IBM i/pSeries LPAR Linux. It utilizes the logical LAN */
+/* option of the RS/6000 Platform Architecture to interface with virtual */
+/* ethernet NICs that are presented to the partition by the hypervisor. */
+/* */
+/**************************************************************************/
+/*
+ * TODO:
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/completion.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/mii.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/ethtool.h>
+#include <linux/proc_fs.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/interrupt.h>
+#include <net/net_namespace.h>
+#include <asm/hvcall.h>
+#include <asm/atomic.h>
+#include <asm/vio.h>
+#include <asm/iommu.h>
+#include <asm/uaccess.h>
+#include <asm/firmware.h>
+#include <linux/seq_file.h>
+
+#include "vnic.h"
+
+
+static const char vnic_driver_name[] = "vnic";
+static const char vnic_driver_string[] = "IBM System i/p Virtual NIC Driver";
+
+MODULE_AUTHOR("Santiago Leon <santil@...ibm.com>");
+MODULE_DESCRIPTION("IBM System i/p Virtual NIC Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(VNIC_DRIVER_VERSION);
+
+static int vnic_debug = VNIC_DEBUG;
+module_param(vnic_debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(vnic_debug, "VNIC debugging message enable");
+
+static int vnic_rx_weight = VNIC_RX_WEIGHT;
+module_param(vnic_rx_weight, int, S_IRUGO);
+MODULE_PARM_DESC(vnic_rx_weight, "VNIC rx weight for NAPI");
+
+static int vnic_buffs_per_pool = VNIC_BUFFS_PER_POOL;
+module_param(vnic_buffs_per_pool, int, S_IRUGO);
+MODULE_PARM_DESC(vnic_buffs_per_pool, "VNIC number of buffers per rx pool");
+
+static int vnic_max_tx_queues = VNIC_MAX_TX_QUEUES;
+module_param(vnic_max_tx_queues, int, S_IRUGO);
+MODULE_PARM_DESC(vnic_max_tx_queues, "VNIC max number of tx queues");
+
+static int vnic_req_mtu = 0;
+module_param(vnic_req_mtu, int, 0);
+MODULE_PARM_DESC(vnic_req_mtu, "VNIC requested MTU, (0 = max allowed by hw)");
+
+
+static int vnic_version = VNIC_INITIAL_VERSION;
+static int vnic_remove(struct vio_dev *);
+static void release_sub_crqs(struct vnic_adapter *);
+static int vnic_reset_crq(struct vnic_adapter *);
+static int vnic_send_crq_init(struct vnic_adapter *);
+static int vnic_reenable_crq_queue(struct vnic_adapter *);
+static int vnic_send_crq(struct vnic_adapter *, union vnic_crq *);
+static int send_subcrq(struct vnic_adapter *adapter, u64 remote_handle,
+ union sub_crq *sub_crq);
+static irqreturn_t vnic_interrupt_rx(int irq, void *instance);
+static int enable_scrq_irq(struct vnic_adapter *, struct vnic_sub_crq_queue *);
+static int disable_scrq_irq(struct vnic_adapter *, struct vnic_sub_crq_queue *);
+static int pending_scrq(struct vnic_adapter *, struct vnic_sub_crq_queue *);
+static union sub_crq *vnic_next_scrq(struct vnic_adapter *,
+ struct vnic_sub_crq_queue *);
+static int vnic_tx_work(void* data);
+int vnic_poll(struct napi_struct *, int);
+
+struct vnic_stat {
+ char name[ETH_GSTRING_LEN];
+ int offset;
+};
+
+#define VNIC_STAT_OFF(stat) (offsetof(struct vnic_adapter, stats) + \
+ offsetof(struct vnic_statistics, stat))
+#define VNIC_GET_STAT(a, off) *((u64 *)(((unsigned long)(a)) + off))
+
+struct vnic_stat vnic_stats[] = {
+ { "rx_packets", VNIC_STAT_OFF(rx_packets) },
+ { "rx_bytes", VNIC_STAT_OFF(rx_bytes) },
+ { "tx_packets", VNIC_STAT_OFF(tx_packets) },
+ { "tx_bytes", VNIC_STAT_OFF(tx_bytes) },
+ { "ucast_tx_packets", VNIC_STAT_OFF(ucast_tx_packets) },
+ { "ucast_rx_packets", VNIC_STAT_OFF(ucast_rx_packets) },
+ { "mcast_tx_packets", VNIC_STAT_OFF(mcast_tx_packets) },
+ { "mcast_rx_packets", VNIC_STAT_OFF(mcast_rx_packets) },
+ { "bcast_tx_packets", VNIC_STAT_OFF(bcast_tx_packets) },
+ { "bcast_rx_packets", VNIC_STAT_OFF(bcast_rx_packets) },
+ { "align_errors", VNIC_STAT_OFF(align_errors) },
+ { "fcs_errors", VNIC_STAT_OFF(fcs_errors) },
+ { "single_collision_frames", VNIC_STAT_OFF(single_collision_frames) },
+ { "multi_collision_frames", VNIC_STAT_OFF(multi_collision_frames) },
+ { "sqe_test_errors", VNIC_STAT_OFF(sqe_test_errors) },
+ { "deferred_tx", VNIC_STAT_OFF(deferred_tx) },
+ { "late_collisions", VNIC_STAT_OFF(late_collisions) },
+ { "excess_collisions", VNIC_STAT_OFF(excess_collisions) },
+ { "internal_mac_tx_errors", VNIC_STAT_OFF(internal_mac_tx_errors) },
+ { "carrier_sense", VNIC_STAT_OFF(carrier_sense) },
+ { "too_long_frames", VNIC_STAT_OFF(too_long_frames) },
+ { "internal_mac_rx_errors", VNIC_STAT_OFF(internal_mac_rx_errors) },
+};
+
+static long h_reg_sub_crq(unsigned long unit_address, unsigned long token,
+ unsigned long length, unsigned long *number,
+ unsigned long *irq)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ rc = plpar_hcall(H_REG_SUB_CRQ, retbuf, unit_address, token, length);
+
+ *number = retbuf[0];
+ *irq = retbuf[1];
+
+ return rc;
+}
+
+/*
+ * net_device_ops functions
+*/
+static void init_rx_pool(struct vnic_adapter *adapter,
+ struct vnic_rx_pool *rx_pool, int num, int index,
+ int buff_size, int active)
+{
+ vnic_dbg(adapter, "Initializing rx_pool %d, %d buffs, %d bytes each\n",
+ index, num, buff_size);
+ rx_pool->size = num;
+ rx_pool->index = index;
+ rx_pool->buff_size = buff_size;
+ rx_pool->active = active;
+}
+
+static int alloc_rx_pool(struct vnic_adapter *adapter,
+ struct vnic_rx_pool *pool)
+{
+ struct device* dev = &adapter->vdev->dev;
+ int i;
+
+ pool->free_map = kzalloc(sizeof(int) * pool->size, GFP_KERNEL);
+ if (!pool->free_map) {
+ dev_err(dev, "Couldn't alloc free map\n");
+ return -ENOMEM;
+ }
+
+ pool->rx_buff = kzalloc(sizeof(struct vnic_rx_buff) * pool->size,
+ GFP_KERNEL);
+ if (!pool->rx_buff) {
+ dev_err(dev, "Couldn't alloc rx buffers\n");
+ kfree(pool->free_map);
+ return -ENOMEM;
+ }
+
+ for(i = 0; i < pool->size; ++i)
+ pool->free_map[i] = i;
+
+ atomic_set(&pool->available, 0);
+ pool->next_alloc = 0;
+ pool->next_free = 0;
+
+ return 0;
+}
+
+static void replenish_rx_pool(struct vnic_adapter *adapter,
+ struct vnic_rx_pool *pool)
+{
+ int i;
+ int count = pool->size - atomic_read(&pool->available);
+ int buffers_added = 0;
+ struct sk_buff *skb;
+ int index;
+ unsigned long lpar_rc;
+ dma_addr_t dma_addr;
+ struct device* dev = &adapter->vdev->dev;
+ union sub_crq sub_crq;
+ u64* handle_array =
+ (u64 *)((u8*)(adapter->login_rsp_buf) +
+ adapter->login_rsp_buf->off_rxadd_subcrqs);
+
+ mb();
+ for (i = 0; i < count; ++i) {
+ skb = alloc_skb(pool->buff_size, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(dev, "Couldn't replenish rx buff\n");
+ adapter->replenish_no_mem++;
+ break;
+ }
+
+ index = pool->free_map[pool->next_free];
+
+ if (pool->rx_buff[index].skb != NULL)
+ dev_err(dev, "Inconsistent free_map!\n");
+
+ dma_addr = dma_map_single(dev, skb->data, pool->buff_size,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&adapter->vdev->dev, dma_addr))
+ goto failure;
+
+ pool->free_map[pool->next_free] = VNIC_INVALID_MAP;
+ pool->rx_buff[index].dma = dma_addr;
+ pool->rx_buff[index].skb = skb;
+ pool->rx_buff[index].pool_index = pool->index;
+ pool->rx_buff[index].size = pool->buff_size;
+
+ sub_crq.rx_add.type = VNIC_CRQ_CMD;
+ sub_crq.rx_add.correlator = (u64)&pool->rx_buff[index];
+ sub_crq.rx_add.ioba = dma_addr;
+ sub_crq.rx_add.len = pool->buff_size;
+
+ lpar_rc = send_subcrq(adapter, handle_array[pool->index],
+ &sub_crq);
+ if (lpar_rc != H_SUCCESS)
+ goto failure;
+
+ buffers_added++;
+ adapter->replenish_add_buff_success++;
+ pool->next_free = (pool->next_free + 1) % pool->size;
+ }
+ atomic_add(buffers_added, &(pool->available));
+ return;
+
+failure:
+ pool->free_map[pool->next_free] = index;
+ pool->rx_buff[index].skb = NULL;
+ if (!dma_mapping_error(dev, dma_addr))
+ dma_unmap_single(dev, dma_addr, pool->buff_size,
+ DMA_FROM_DEVICE);
+
+ dev_kfree_skb_any(skb);
+ adapter->replenish_add_buff_failure++;
+ atomic_add(buffers_added, &(pool->available));
+ LEAVE;
+}
+
+static void replenish_pools(struct vnic_adapter *adapter)
+{
+ int i;
+ if (adapter->migrated)
+ return;
+
+ adapter->replenish_task_cycles++;
+ for (i = 0; i < adapter->login_rsp_buf->num_rxadd_subcrqs; i++)
+ if(adapter->rx_pool[i].active)
+ replenish_rx_pool(adapter, &adapter->rx_pool[i]);
+
+}
+
+static void free_rx_pool(struct vnic_adapter *adapter,
+ struct vnic_rx_pool *pool)
+{
+ struct device* dev = &adapter->vdev->dev;
+ int i;
+
+ if (pool->free_map)
+ kfree(pool->free_map);
+ pool->free_map = NULL;
+
+ if(pool->rx_buff) {
+ for (i = 0; i < pool->size; i++) {
+ if(pool->rx_buff[i].skb) {
+ dma_unmap_single(dev, pool->rx_buff[i].dma,
+ pool->rx_buff[i].size,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_any(pool->rx_buff[i].skb);
+ pool->rx_buff[i].skb = NULL;
+ }
+ }
+ kfree(pool->rx_buff);
+ pool->rx_buff = NULL;
+ }
+}
+
+static int vnic_open(struct net_device *netdev)
+{
+ struct vnic_adapter *adapter = netdev_priv(netdev);
+ struct device* dev = &adapter->vdev->dev;
+ union vnic_crq crq;
+
+ int i, j;
+ int rxadd_subcrqs = adapter->login_rsp_buf->num_rxadd_subcrqs;
+ int tx_subcrqs = adapter->login_rsp_buf->num_txsubm_subcrqs;
+ u64* size_array =
+ (u64 *)((u8*)(adapter->login_rsp_buf) +
+ adapter->login_rsp_buf->off_rxadd_buff_size);
+
+ ENTER;
+
+ adapter->napi =
+ kmalloc(sizeof(struct napi_struct) * adapter->req_rx_queues,
+ GFP_KERNEL);
+
+ if (!adapter->napi) {
+ dev_err(dev, "Couldn't alloc napi structs\n");
+ goto alloc_napi_failed;
+ }
+
+ for (i = 0 ; i < adapter->req_rx_queues; i++ ) {
+ netif_napi_add(netdev, &adapter->napi[i], vnic_poll,
+ vnic_rx_weight);
+ napi_enable(&adapter->napi[i]);
+ }
+
+ adapter->rx_pool = kmalloc(sizeof(struct vnic_rx_pool) * rxadd_subcrqs, GFP_KERNEL);
+ if (!adapter->rx_pool) {
+ dev_err(dev, "Couldn't alloc rx pool array\n");
+ goto rx_pool_arr_alloc_failed;
+ }
+
+ for (i = 0 ; i < rxadd_subcrqs; i++) {
+ if(!adapter->rx_pool->active)
+ continue;
+ init_rx_pool(adapter, &adapter->rx_pool[i], vnic_buffs_per_pool,
+ i, size_array[i], 1);
+ if (alloc_rx_pool(adapter, &adapter->rx_pool[i])) {
+ dev_err(dev, "Couldn't alloc rx pool\n");
+ goto rx_pool_alloc_failed;
+ }
+ }
+
+ adapter->tx_pool =
+ kmalloc(sizeof(struct vnic_tx_pool) * rxadd_subcrqs, GFP_KERNEL);
+ if (!adapter->tx_pool) {
+ dev_err(dev, "Couldn't alloc tx pool array\n");
+ goto tx_pool_arr_alloc_failed;
+ }
+
+ for (i = 0 ; i < tx_subcrqs; i++) {
+ adapter->tx_pool[i].tx_buff =
+ kzalloc(sizeof(struct vnic_tx_buff) *
+ adapter->max_tx_entries_per_subcrq, GFP_KERNEL);
+ if (!adapter->tx_pool[i].tx_buff) {
+ dev_err(dev, "Couldn't alloc tx pool\n");
+ goto tx_pool_alloc_failed;
+ }
+ adapter->tx_pool[i].free_map =
+ kmalloc(sizeof(int) * adapter->max_tx_entries_per_subcrq,
+ GFP_KERNEL);
+ if (!adapter->tx_pool[i].free_map) {
+ dev_err(dev, "Couldn't alloc tx free map\n");
+ goto tx_fm_alloc_failed;
+ }
+ for (j = 0; j < adapter->max_tx_entries_per_subcrq; j++) {
+ adapter->tx_pool[i].free_map[j] = j;
+ }
+ adapter->tx_pool[i].consumer_index = 0;
+ adapter->tx_pool[i].producer_index = 0;
+
+ init_waitqueue_head(&adapter->tx_pool[i].vnic_tx_comp_q);
+ adapter->tx_pool[i].work_thread =
+ kthread_run(vnic_tx_work, adapter->tx_scrq[i], "%s_%s_%d",
+ VNIC_NAME, adapter->netdev->name, i);
+ if (IS_ERR(adapter->tx_pool[i].work_thread)) {
+ dev_err(dev, "Couldn't create kernel thread: %ld\n",
+ PTR_ERR(adapter->tx_pool[i].work_thread));
+ goto thread_failed;
+ }
+ }
+
+ adapter->bounce_buffer_size =
+ (netdev->mtu + ETH_HLEN - 1) / PAGE_SIZE + 1;
+ adapter->bounce_buffer = kmalloc(adapter->bounce_buffer_size,
+ GFP_KERNEL);
+ if (!adapter->bounce_buffer) {
+ dev_err(dev, "Couldn't alloc tx bounce buffer\n");
+ goto bounce_alloc_failed;
+ }
+
+ adapter->bounce_buffer_dma = dma_map_single(dev, adapter->bounce_buffer,
+ adapter->bounce_buffer_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, adapter->bounce_buffer_dma)) {
+ dev_err(dev, "Couldn't map tx bounce buffer\n");
+ goto bounce_map_failed;
+ }
+
+ replenish_pools(adapter);
+
+ /* We're ready to receive frames, enable the sub-crq interrupts and
+ set the logical link state to up */
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ enable_scrq_irq(adapter, adapter->rx_scrq[i]);
+
+ for (i = 0; i < adapter->req_tx_queues; i++)
+ enable_scrq_irq(adapter, adapter->tx_scrq[i]);
+
+ memset(&crq, 0, sizeof(crq));
+ crq.logical_link_state.type = VNIC_CRQ_CMD;
+ crq.logical_link_state.cmd = LOGICAL_LINK_STATE;
+ crq.logical_link_state.link_state = VNIC_LOGICAL_LNK_UP;
+ vnic_send_crq(adapter, &crq);
+
+ netif_start_queue(netdev);
+ return 0;
+
+bounce_map_failed:
+ kfree(adapter->bounce_buffer);
+bounce_alloc_failed:
+ i = tx_subcrqs;
+thread_failed:
+ for (j = 0; j < i; j++)
+ kthread_stop(adapter->tx_pool[j].work_thread);
+tx_fm_alloc_failed:
+ i = tx_subcrqs;
+tx_pool_alloc_failed:
+ for (j = 0; j < i; j++)
+ kfree(adapter->tx_pool[j].tx_buff);
+ kfree(adapter->tx_pool);
+ adapter->tx_pool = NULL;
+tx_pool_arr_alloc_failed:
+ i = rxadd_subcrqs;
+rx_pool_alloc_failed:
+ for (j = 0; j < i; j++)
+ free_rx_pool(adapter, &adapter->rx_pool[j]);
+ kfree(adapter->rx_pool);
+ adapter->rx_pool = NULL;
+rx_pool_arr_alloc_failed:
+ for (i = 0 ; i < adapter->req_rx_queues; i++ )
+ napi_enable(&adapter->napi[i]);
+alloc_napi_failed:
+ return -ENOMEM;
+}
+
+static int vnic_close(struct net_device *netdev)
+{
+ struct vnic_adapter *adapter = netdev_priv(netdev);
+ struct device* dev = &adapter->vdev->dev;
+ int i;
+
+ adapter->closing = 1;
+ for (i = 0; i < adapter->req_tx_queues; i++) {
+ wake_up(&adapter->tx_pool[i].vnic_tx_comp_q);
+ kthread_stop(adapter->tx_pool[i].work_thread);
+ }
+
+ for (i = 0 ; i < adapter->req_rx_queues; i++ )
+ napi_disable(&adapter->napi[i]);
+
+ netif_stop_queue(netdev);
+
+ if (adapter->bounce_buffer != NULL) {
+ if (!dma_mapping_error(dev, adapter->bounce_buffer_dma)) {
+ dma_unmap_single(&adapter->vdev->dev,
+ adapter->bounce_buffer_dma,
+ adapter->bounce_buffer_size,
+ DMA_BIDIRECTIONAL);
+ adapter->bounce_buffer_dma = DMA_ERROR_CODE;
+ }
+ kfree(adapter->bounce_buffer);
+ adapter->bounce_buffer = NULL;
+ }
+
+ for (i = 0; i < adapter->login_rsp_buf->num_txsubm_subcrqs; i++)
+ kfree(adapter->tx_pool[i].tx_buff);
+ kfree(adapter->tx_pool);
+ adapter->tx_pool = NULL;
+
+ for (i = 0; i < adapter->login_rsp_buf->num_rxadd_subcrqs; i++)
+ free_rx_pool(adapter, &adapter->rx_pool[i]);
+ kfree(adapter->rx_pool);
+ adapter->rx_pool = NULL;
+
+ adapter->closing = 0;
+
+ return 0;
+}
+
+static int vnic_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct vnic_adapter *adapter = netdev_priv(netdev);
+ struct device* dev = &adapter->vdev->dev;
+ dma_addr_t data_dma_addr;
+ union sub_crq tx_crq;
+ unsigned long lpar_rc;
+ unsigned int tx_dropped = 0;
+ unsigned int tx_bytes = 0;
+ unsigned int tx_packets = 0;
+ unsigned int tx_send_failed = 0;
+ unsigned int tx_map_failed = 0;
+ unsigned int nr_frags;
+ int ret = 0;
+ int index;
+ int i;
+ int used_bounce = 0;
+ struct vnic_tx_buff *tx_buff = NULL;
+ struct skb_frag_struct *frag = NULL;
+ int queue_num = skb_get_queue_mapping(skb);
+ struct vnic_tx_pool *tx_pool = &adapter->tx_pool[queue_num];
+ struct vnic_control_ip_offload_buffer *offload =
+ &adapter->ip_offload_ctrl;
+ struct netdev_queue *txq =
+ netdev_get_tx_queue(netdev,skb_get_queue_mapping(skb));
+ u64* handle_array =
+ (u64 *)((u8*)(adapter->login_rsp_buf) +
+ adapter->login_rsp_buf->off_txsubm_subcrqs);
+ u8 offload_flags = 0;
+
+ if (adapter->migrated) {
+ tx_send_failed++;
+ tx_dropped++;
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ }
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ if (offload->tcp_ipv4_chksum
+ && ip_hdr(skb)->protocol == IPPROTO_TCP)
+ offload_flags |= VNIC_TCP_CHKSUM;
+ else if (offload->udp_ipv4_chksum
+ && ip_hdr(skb)->protocol == IPPROTO_UDP)
+ offload_flags |= VNIC_UDP_CHKSUM;
+ else if (skb_checksum_help(skb)) {
+ dev_err(dev, "tx: failed to checksum packet\n");
+ tx_dropped++;
+ goto out;
+ }
+ }
+
+ /* If the hardware can't handle the number of memory segments, try
+ to linearize it */
+ if (skb_shinfo(skb)->nr_frags > adapter->max_tx_sg_entries) {
+ if (skb_linearize(skb)) {
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+ }
+
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ if (!nr_frags) {
+ data_dma_addr = dma_map_single(dev, skb->data, skb->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, data_dma_addr)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(dev, "tx: unable to map xmit buffer\n");
+ skb_copy_from_linear_data(skb, adapter->bounce_buffer,
+ skb->len);
+ data_dma_addr = adapter->bounce_buffer_dma;
+ used_bounce = 1;
+ tx_map_failed++;
+ tx_dropped++;
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ }
+
+ index = tx_pool->free_map[tx_pool->consumer_index];
+ tx_pool->consumer_index =
+ ++index % adapter->max_tx_entries_per_subcrq;
+ tx_buff = &tx_pool->tx_buff[index];
+ tx_buff->skb = skb;
+ tx_buff->data_dma[0] = data_dma_addr;
+ tx_buff->data_len[0] = skb->len;
+ tx_buff->index = index;
+ tx_buff->pool_index = 0;
+ tx_buff->last_frag = 1;
+ tx_buff->used_bounce = 1;
+
+ memset(&tx_crq, 0, sizeof(tx_crq));
+ tx_crq.v0.type = VNIC_CRQ_CMD;
+ tx_crq.v0.version = VNIC_TX_DESC_V0;
+ tx_crq.v0.correlator = index;
+ tx_crq.v0.ioba1 = data_dma_addr;
+ tx_crq.v0.len1 = skb->len;
+ tx_crq.v0.flags1 = VNIC_TX_COMP_EVENT | VNIC_LAST_FRAG;
+ if (adapter->vlan_header_insertion) {
+ tx_crq.v0.flags1 |= VNIC_INS_VLANH;
+ tx_crq.v0.vlan_h = skb->vlan_tci;
+ }
+ if (skb_shinfo(skb)->gso_size) {
+ tx_crq.v0.flags1 |= VNIC_LARGE_TX;
+ tx_crq.v0.flags2 = skb->network_header;
+ tx_crq.v0.mss = skb_shinfo(skb)->gso_size;
+ }
+ if (ip_hdr(skb)->version == 6)
+ tx_crq.v0.flags2 |= IPV6;
+ if (offload_flags) {
+ tx_crq.v0.flags1 |= offload_flags;
+ tx_crq.v0.tcp_udp_h_off = skb->transport_header;
+ }
+
+ lpar_rc = send_subcrq(adapter, handle_array[0], &tx_crq);
+
+ if (lpar_rc != H_SUCCESS) {
+ dev_err(dev, "tx failed with code %ld\n", lpar_rc);
+ if (!used_bounce)
+ dma_unmap_single(dev, data_dma_addr, skb->len,
+ DMA_TO_DEVICE);
+
+ if (tx_pool->consumer_index == 0)
+ tx_pool->consumer_index =
+ adapter->max_tx_entries_per_subcrq - 1;
+ else
+ tx_pool->consumer_index--;
+
+ tx_send_failed++;
+ tx_dropped++;
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ } else {
+ tx_packets++;
+ tx_bytes += skb->len;
+ txq->trans_start = jiffies;
+ ret = NETDEV_TX_OK;
+ }
+ goto out;
+ }
+
+ /* nr_frags > 0 */
+ for (i = 0; i < nr_frags; i++) {
+ void *data;
+ frag = &skb_shinfo(skb)->frags[i];
+ data = skb_frag_address(frag);
+ data_dma_addr = dma_map_single(dev, data, frag->size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, data_dma_addr)) {
+ dev_err(dev, "tx: unable to map xmit buffer\n");
+ tx_map_failed++;
+ tx_dropped++;
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ }
+
+ if (!(i % 2)) { /* 0 and even frags */
+ index = tx_pool->free_map[tx_pool->consumer_index];
+ tx_pool->consumer_index =
+ ++index % adapter->max_tx_entries_per_subcrq;
+
+ tx_buff = &tx_pool->tx_buff[index];
+ tx_buff->skb = skb;
+ tx_buff->data_dma[0] = data_dma_addr;
+ tx_buff->data_len[0] = frag->size;
+ tx_buff->index = index;
+ tx_buff->pool_index = 0;
+
+ memset(&tx_crq, 0, sizeof(tx_crq));
+ tx_crq.v0.type = VNIC_CRQ_CMD;
+ tx_crq.v0.version = VNIC_TX_DESC_V0;
+ tx_crq.v0.correlator = index;
+ tx_crq.v0.ioba1 = data_dma_addr;
+ tx_crq.v0.len1 = frag->size;
+ tx_crq.v0.flags1 = VNIC_TX_COMP_EVENT;
+ if (adapter->vlan_header_insertion) {
+ tx_crq.v0.flags1 |= VNIC_INS_VLANH;
+ tx_crq.v0.vlan_h = skb->vlan_tci;
+ }
+ if (skb_shinfo(skb)->gso_size) {
+ tx_crq.v0.flags1 |= VNIC_LARGE_TX;
+ tx_crq.v0.flags2 = skb->network_header;
+ tx_crq.v0.mss = skb_shinfo(skb)->gso_size;
+ }
+ if (ip_hdr(skb)->version == 6)
+ tx_crq.v0.flags2 |= IPV6;
+ if (offload_flags) {
+ tx_crq.v0.flags1 |= offload_flags;
+ tx_crq.v0.tcp_udp_h_off = skb->transport_header;
+ }
+
+ if (i == nr_frags - 1) {
+ tx_buff->last_frag = 1;
+ tx_crq.v0.flags1 |= VNIC_LAST_FRAG;
+ lpar_rc =
+ send_subcrq(adapter, handle_array[0],
+ &tx_crq);
+ if (lpar_rc != H_SUCCESS) {
+ dev_err(dev,
+ "tx failed with code %ld\n",
+ lpar_rc);
+ dma_unmap_single(dev, data_dma_addr,
+ skb->len,
+ DMA_TO_DEVICE);
+ if (tx_pool->consumer_index == 0)
+ tx_pool-> consumer_index =
+ adapter->
+ max_tx_entries_per_subcrq -
+ 1;
+ else
+ tx_pool->consumer_index--;
+
+ tx_send_failed++;
+ tx_dropped++;
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ } else {
+ tx_packets++;
+ tx_bytes += skb->len;
+ txq->trans_start = jiffies;
+ ret = NETDEV_TX_OK;
+ }
+ }
+ } else { /* odd frags */
+ tx_crq.v0.ioba1 = data_dma_addr;
+ tx_crq.v0.len1 = frag->size;
+ if (i == nr_frags - 1) {
+ tx_buff->last_frag = 1;
+ tx_crq.v0.flags1 |= VNIC_LAST_FRAG;
+ }
+ lpar_rc = send_subcrq(adapter, handle_array[0], &tx_crq);
+ if (lpar_rc != H_SUCCESS) {
+ dev_err(dev, "tx failed with code %ld\n",
+ lpar_rc);
+ dma_unmap_single(dev, data_dma_addr, skb->len,
+ DMA_TO_DEVICE);
+ if (tx_pool->consumer_index == 0)
+ tx_pool->consumer_index =
+ adapter->max_tx_entries_per_subcrq - 1;
+ else
+ tx_pool->consumer_index--;
+
+ tx_send_failed++;
+ tx_dropped++;
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ } else {
+ tx_packets++;
+ tx_bytes += skb->len;
+ txq->trans_start = jiffies;
+ ret = NETDEV_TX_OK;
+ }
+ }
+ }
+
+out: adapter->net_stats.tx_dropped += tx_dropped;
+ adapter->net_stats.tx_bytes += tx_bytes;
+ adapter->net_stats.tx_packets += tx_packets;
+ adapter->tx_send_failed += tx_send_failed;
+ adapter->tx_map_failed += tx_map_failed;
+
+ return ret;
+}
+
+
+static void vnic_set_multi(struct net_device *netdev)
+{
+ struct vnic_adapter *adapter = netdev_priv(netdev);
+ union vnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_capability.type = VNIC_CRQ_CMD;
+ crq.request_capability.cmd = REQUEST_CAPABILITY;
+
+ if (netdev->flags & IFF_PROMISC) {
+ if (!adapter->promisc_supported)
+ return;
+ crq.request_capability.capability = PROMISC_REQUESTED;
+ crq.request_capability.number = 1;
+ vnic_send_crq(adapter, &crq);
+ } else {
+ crq.request_capability.capability = PROMISC_REQUESTED;
+ crq.request_capability.number = 0;
+ vnic_send_crq(adapter, &crq);
+
+ if (netdev->flags & IFF_ALLMULTI) {
+ /* Accept all multicast. */
+ memset(&crq, 0, sizeof(crq));
+ crq.multicast_ctrl.type = VNIC_CRQ_CMD;
+ crq.multicast_ctrl.cmd = MULTICAST_CTRL;
+ crq.multicast_ctrl.flags = VNIC_ENABLE_ALL;
+ vnic_send_crq(adapter, &crq);
+ } else if (netdev_mc_empty(netdev)) {
+ /* Reject all multicast. */
+ memset(&crq, 0, sizeof(crq));
+ crq.multicast_ctrl.type = VNIC_CRQ_CMD;
+ crq.multicast_ctrl.cmd = MULTICAST_CTRL;
+ crq.multicast_ctrl.flags = VNIC_DISABLE_ALL;
+ vnic_send_crq(adapter, &crq);
+ } else {
+ /* Accept one or more multicast(s). */
+ int i;
+
+ for (i = 0; i < netdev_mc_count(netdev); i++) {
+ memset(&crq, 0, sizeof(crq));
+ crq.multicast_ctrl.type = VNIC_CRQ_CMD;
+ crq.multicast_ctrl.cmd = MULTICAST_CTRL;
+ crq.multicast_ctrl.flags = VNIC_ENABLE_MC;
+ vnic_send_crq(adapter, &crq);
+ }
+ }
+ }
+}
+
+static int vnic_set_mac(struct net_device *netdev, void *p)
+{
+ struct vnic_adapter *adapter = netdev_priv(netdev);
+ struct sockaddr *addr = p;
+ union vnic_crq crq;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.change_mac_addr.type = VNIC_CRQ_CMD;
+ crq.change_mac_addr.cmd = CHANGE_MAC_ADDR;
+ memcpy(&crq.change_mac_addr.mac_addr, addr->sa_data, ETH_ALEN);
+ vnic_send_crq(adapter, &crq);
+
+ return 0;
+}
+
+static int vnic_mii_ioctl(struct net_device *netdev, struct ifreq *ifr,
+ int cmd)
+{
+ struct mii_ioctl_data *data = if_mii(ifr);
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ break;
+ case SIOCGMIIREG:
+ switch (data->reg_num & 0x1F) {
+ case MII_BMCR:
+ break;
+ case MII_BMSR:
+ break;
+ case MII_PHYSID1:
+ break;
+ case MII_PHYSID2:
+ break;
+ case MII_ADVERTISE:
+ break;
+ case MII_LPA:
+ break;
+ case MII_EXPANSION:
+ break;
+ case MII_CTRL1000:
+ break;
+ case MII_STAT1000:
+ break;
+ case MII_ESTATUS:
+ break;
+ default:
+ return -EIO;
+ }
+ break;
+ case SIOCSMIIREG:
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int vnic_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
+ case SIOCSMIIREG:
+ return vnic_mii_ioctl(netdev, ifr, cmd);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int vnic_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct vnic_adapter *adapter = netdev_priv(netdev);
+
+ if (new_mtu > adapter->req_mtu)
+ return -EINVAL;
+
+ netdev->mtu = new_mtu;
+ return 0;
+}
+
+void vnic_tx_timeout(struct net_device *dev)
+{
+ struct vnic_adapter *adapter = netdev_priv(dev);
+ int rc;
+
+ /* Adapter timed out, resetting it */
+ release_sub_crqs(adapter);
+ if ((rc = vnic_reset_crq(adapter)))
+ dev_err(&adapter->vdev->dev, "Adapter timeout, reset failed\n");
+ else
+ vnic_send_crq_init(adapter);
+}
+
+struct net_device_stats *vnic_get_stats(struct net_device *dev)
+{
+ struct vnic_adapter *adapter = netdev_priv(dev);
+
+ /* only return the current stats */
+ return &adapter->net_stats;
+
+}
+
+static void remove_buff_from_pool(struct vnic_adapter *adapter,
+ struct vnic_rx_buff *rx_buff)
+{
+ struct device* dev = &adapter->vdev->dev;
+ struct vnic_rx_pool *pool = &adapter->rx_pool[rx_buff->pool_index];
+
+ rx_buff->skb = NULL;
+ dma_unmap_single(dev, rx_buff->dma, rx_buff->size, DMA_FROM_DEVICE);
+
+ pool->free_map[pool->next_alloc] = (int)(rx_buff - pool->rx_buff);
+ pool->next_alloc = (pool->next_alloc + 1) % pool->size;
+
+ atomic_dec(&(pool->available));
+}
+
+int vnic_poll(struct napi_struct *napi, int budget)
+{
+ struct net_device *netdev = napi->dev;
+ struct vnic_adapter *adapter = netdev_priv(netdev);
+ int frames_processed = 0;
+ int scrq_num = (int)(napi - adapter->napi);
+
+restart_poll:
+ do {
+ struct sk_buff *skb;
+ struct vnic_rx_buff *rx_buff;
+ union sub_crq *next;
+ int length;
+ int offset;
+ int csum;
+ u8 flags;
+ struct sk_buff *rx_top =
+ adapter->rx_scrq[scrq_num]->rx_skb_top;
+
+ if (!pending_scrq(adapter, adapter->rx_scrq[scrq_num]))
+ break;
+
+ next = vnic_next_scrq(adapter, adapter->rx_scrq[scrq_num]);
+ rx_buff = (struct vnic_rx_buff*)next->rx_comp.correlator;
+ length = next->rx_comp.len;
+ offset = next->rx_comp.off_frame_data;
+ flags = next->rx_comp.flags;
+ csum = next->rx_comp.csum;
+ skb = rx_buff->skb;
+
+ /* free the entry */
+ next->rx_comp.type = 0;
+ remove_buff_from_pool(adapter, rx_buff);
+
+ /* handle large rx offload */
+ if (!(flags & VNIC_END_FRAME)) {
+ /* this is the first or middle frame */
+ if (!rx_top) {
+ /* first frame */
+ adapter->rx_scrq[scrq_num]->rx_skb_top = skb;
+ skb_add_rx_frag(skb, 0, virt_to_page(skb->data),
+ ((long)skb->data & !PAGE_MASK) +
+ offset, length, length);
+ } else {
+ /* middle frame */
+ skb_add_rx_frag(skb,
+ skb_shinfo(rx_top)->nr_frags,
+ virt_to_page(skb->data),
+ ((long)skb->data & !PAGE_MASK) +
+ offset, length, length);
+ /* get a reference to the data, free the skb */
+ atomic_inc(&(skb_shinfo(skb)->dataref));
+ dev_kfree_skb_any(skb);
+ }
+ } else {
+ if (rx_top) {
+ /* last frame */
+ skb_add_rx_frag(skb,
+ skb_shinfo(rx_top)->nr_frags,
+ virt_to_page(skb->data),
+ ((long)skb->data & !PAGE_MASK) +
+ offset, length, length);
+ atomic_inc(&(skb_shinfo(skb)->dataref));
+ dev_kfree_skb_any(skb);
+ skb = rx_top;
+ adapter->rx_scrq[scrq_num]->rx_skb_top = NULL;
+ } else {
+ /* no multi-frame packet */
+ skb_reserve(skb, offset);
+ skb_put(skb, length);
+ }
+ }
+
+ skb->protocol = eth_type_trans(skb, netdev);
+
+ if (flags & VNIC_IP_CHKSUM_GOOD
+ && flags & VNIC_TCP_UDP_CHKSUM_GOOD) {
+ if (flags & VNIC_CSUM_FIELD) {
+ skb->csum = csum;
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ } else
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+
+ length = skb->len;
+ napi_gro_receive(napi, skb); /* send it up */
+
+ adapter->net_stats.rx_packets++;
+ adapter->net_stats.rx_bytes += length;
+ frames_processed++;
+ } while (frames_processed < budget);
+
+ replenish_pools(adapter);
+
+ if (frames_processed < budget) {
+ enable_scrq_irq(adapter, adapter->rx_scrq[scrq_num]);
+ napi_complete(napi);
+ if (pending_scrq(adapter, adapter->rx_scrq[scrq_num]) &&
+ napi_reschedule(napi)) {
+ disable_scrq_irq(adapter, adapter->rx_scrq[scrq_num]);
+ goto restart_poll;
+ }
+ }
+
+ return frames_processed;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void vnic_netpoll_controller(struct net_device *dev)
+{
+ struct vnic_adapter *adapter = netdev_priv(dev);
+ int i;
+
+ replenish_pools(netdev_priv(dev));
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ vnic_interrupt_rx(adapter->rx_scrq[i]->irq,
+ adapter->rx_scrq[i]);
+}
+#endif
+
+
+static const struct net_device_ops vnic_netdev_ops = {
+ .ndo_open = vnic_open,
+ .ndo_stop = vnic_close,
+ .ndo_start_xmit = vnic_xmit,
+ .ndo_set_rx_mode = vnic_set_multi,
+ .ndo_set_mac_address = vnic_set_mac,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = vnic_ioctl,
+ .ndo_change_mtu = vnic_change_mtu,
+ .ndo_tx_timeout = vnic_tx_timeout,
+ .ndo_get_stats = vnic_get_stats,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = vnic_netpoll_controller,
+#endif
+};
+
+/*
+ * ethtool functions
+*/
+static int vnic_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ return 0;
+}
+static int vnic_set_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ return 0;
+
+}
+
+static void vnic_get_drvinfo (struct net_device *dev,
+ struct ethtool_drvinfo *info) {
+ strncpy(info->driver, vnic_driver_name, sizeof(info->driver) - 1);
+ strncpy(info->version, VNIC_DRIVER_VERSION,
+ sizeof(info->version) - 1);
+}
+
+static u32 vnic_get_msglevel(struct net_device *netdev)
+{
+ struct vnic_adapter *adapter = netdev_priv(netdev);
+ return adapter->msg_enable;
+}
+
+static void vnic_set_msglevel(struct net_device *netdev, u32 data)
+{
+ struct vnic_adapter *adapter = netdev_priv(netdev);
+ adapter->msg_enable = data;
+}
+
+static u32 vnic_get_link(struct net_device *netdev)
+{
+ struct vnic_adapter *adapter = netdev_priv(netdev);
+
+ /* Don't need to send a query because we request a logical link up at
+ init and then we wait for link state indications */
+ return adapter->logical_link_state;
+}
+
+static void vnic_get_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ ring->rx_max_pending = 0;
+ ring->tx_max_pending = 0;
+ ring->rx_mini_max_pending = 0;
+ ring->rx_jumbo_max_pending = 0;
+ ring->rx_pending = 0;
+ ring->tx_pending = 0;
+ ring->rx_mini_pending = 0;
+ ring->rx_jumbo_pending = 0;
+}
+
+static void vnic_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ int i;
+
+ if (stringset != ETH_SS_STATS)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(vnic_stats); i++, data += ETH_GSTRING_LEN)
+ memcpy(data, vnic_stats[i].name, ETH_GSTRING_LEN);
+}
+
+static int vnic_get_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ARRAY_SIZE(vnic_stats);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void vnic_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ int i;
+ struct vnic_adapter *adapter = netdev_priv(dev);
+ union vnic_crq crq;
+
+ ENTER;
+ memset(&crq, 0, sizeof(crq));
+ crq.request_statistics.type = VNIC_CRQ_CMD;
+ crq.request_statistics.cmd = REQUEST_STATISTICS;
+ crq.request_statistics.ioba = adapter->stats_token;
+ crq.request_statistics.len = sizeof(struct vnic_statistics);
+ vnic_send_crq(adapter, &crq);
+
+ /* Wait for data to be written */
+ init_completion(&adapter->stats_done);
+ wait_for_completion(&adapter->stats_done);
+
+ for (i = 0; i < ARRAY_SIZE(vnic_stats); i++)
+ data[i] = VNIC_GET_STAT(adapter, vnic_stats[i].offset);
+
+ LEAVE;
+}
+
+static const struct ethtool_ops vnic_ethtool_ops = {
+ .get_settings = vnic_get_settings,
+ .set_settings = vnic_set_settings,
+ .get_drvinfo = vnic_get_drvinfo,
+ .get_msglevel = vnic_get_msglevel,
+ .set_msglevel = vnic_set_msglevel,
+ .get_link = vnic_get_link,
+ .get_ringparam = vnic_get_ringparam,
+ .get_strings = vnic_get_strings,
+ .get_sset_count = vnic_get_sset_count,
+ .get_ethtool_stats = vnic_get_ethtool_stats,
+};
+
+
+/*
+ * vio bus functions
+*/
+
+static void release_sub_crq_queue(struct vnic_adapter *adapter,
+ struct vnic_sub_crq_queue *scrq)
+{
+ struct device* dev = &adapter->vdev->dev;
+ long rc;
+
+ ENTER;
+ vnic_dbg(adapter, "Releasing sub-CRQ\n");
+
+ /* Close the sub-crqs */
+ do {
+ rc = plpar_hcall_norets(H_FREE_SUB_CRQ,
+ adapter->vdev->unit_address,
+ scrq->crq_num);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+ dma_unmap_single(dev, scrq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
+ free_page((unsigned long)scrq->msgs);
+ kfree(scrq);
+ LEAVE;
+}
+
+static struct vnic_sub_crq_queue *init_sub_crq_queue(struct vnic_adapter
+ *adapter)
+{
+ struct device* dev = &adapter->vdev->dev;
+ struct vnic_sub_crq_queue *scrq;
+ int rc;
+
+ ENTER;
+ scrq = kmalloc(sizeof(*scrq), GFP_ATOMIC);
+ if (!scrq) {
+ dev_warn(dev, "Couldn't allocate crq queue\n");
+ return NULL;
+ }
+
+ scrq->msgs = (union sub_crq *)get_zeroed_page(GFP_KERNEL);
+ if(!scrq->msgs) {
+ dev_warn(dev, "Couldn't allocate crq queue messages page\n");
+ goto zero_page_failed;
+ }
+
+ scrq->msg_token = dma_map_single(dev, scrq->msgs, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, scrq->msg_token)) {
+ dev_warn(dev, "Couldn't map crq queue messages page\n");
+ goto map_failed;
+ }
+
+ rc = h_reg_sub_crq(adapter->vdev->unit_address, scrq->msg_token,
+ PAGE_SIZE, &scrq->crq_num, &scrq->hw_irq);
+
+ if (rc == H_RESOURCE)
+ rc = vnic_reset_crq(adapter);
+
+ if (rc == H_CLOSED)
+ dev_warn(dev, "Partner adapter not ready, waiting.\n");
+ else if (rc) {
+ dev_warn(dev, "Error %d registering sub-crq\n", rc);
+ goto reg_failed;
+ }
+
+ scrq->irq = irq_create_mapping(NULL, scrq->hw_irq);
+ if (scrq->irq == NO_IRQ) {
+ dev_err(dev, "Error maping irq\n");
+ goto map_irq_failed;
+ }
+
+ scrq->adapter = adapter;
+ scrq->size = PAGE_SIZE / sizeof(*scrq->msgs);
+ scrq->cur = 0;
+ scrq->rx_skb_top = NULL;
+ spin_lock_init(&scrq->lock);
+
+ vnic_dbg(adapter, "sub-crq initialized, num %lx, hw_irq=%lx, irq=%x\n",
+ scrq->crq_num, scrq->hw_irq, scrq->irq);
+
+ LEAVE;
+ return scrq;
+
+map_irq_failed:
+ do {
+ rc = plpar_hcall_norets(H_FREE_SUB_CRQ,
+ adapter->vdev->unit_address,
+ scrq->crq_num);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+reg_failed:
+ dma_unmap_single(dev, scrq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
+map_failed:
+ free_page((unsigned long)scrq->msgs);
+zero_page_failed:
+ kfree(scrq);
+ LEAVE;
+ return NULL;
+}
+
+static void release_sub_crqs(struct vnic_adapter *adapter)
+{
+ int i;
+
+ if (adapter->tx_scrq) {
+ for (i = 0; i < adapter->req_tx_queues; i++)
+ if (adapter->tx_scrq[i]) {
+ free_irq(adapter->tx_scrq[i]->irq,
+ adapter->tx_scrq[i]);
+ release_sub_crq_queue(adapter,
+ adapter->tx_scrq[i]);
+ }
+ adapter->tx_scrq = NULL;
+ }
+
+ if (adapter->rx_scrq) {
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ if (adapter->rx_scrq[i]) {
+ free_irq(adapter->rx_scrq[i]->irq,
+ adapter->rx_scrq[i]);
+ release_sub_crq_queue(adapter,
+ adapter->rx_scrq[i]);
+ }
+ adapter->rx_scrq = NULL;
+ }
+
+ adapter->requested_caps = 0;
+}
+
+static int disable_scrq_irq(struct vnic_adapter *adapter,
+ struct vnic_sub_crq_queue *scrq)
+{
+ struct device* dev = &adapter->vdev->dev;
+ unsigned long rc;
+
+ rc = plpar_hcall_norets(H_VIOCTL, adapter->vdev->unit_address,
+ DISABLE_VIO_INTERRUPT, scrq->hw_irq, 0, 0);
+ if (rc)
+ dev_err(dev, "Couldn't disable scrq irq 0x%lx. rc=%ld\n",
+ scrq->hw_irq, rc);
+ return rc;
+}
+
+static int enable_scrq_irq(struct vnic_adapter *adapter,
+ struct vnic_sub_crq_queue *scrq)
+{
+ struct device* dev = &adapter->vdev->dev;
+ unsigned long rc;
+
+ if (scrq->hw_irq > 0x100000000) {
+ dev_err(dev, "bad hw_irq = %lx\n", scrq->hw_irq);
+ return 1;
+ }
+
+ rc = plpar_hcall_norets(H_VIOCTL, adapter->vdev->unit_address,
+ ENABLE_VIO_INTERRUPT, scrq->hw_irq, 0, 0);
+ if (rc)
+ dev_err(dev, "Couldn't enable scrq irq 0x%lx. rc=%ld\n",
+ scrq->hw_irq, rc);
+ return rc;
+}
+
+static int vnic_complete_tx(struct vnic_adapter *adapter,
+ struct vnic_sub_crq_queue* scrq)
+{
+ struct device* dev = &adapter->vdev->dev;
+ union sub_crq *next;
+ struct vnic_tx_buff *txbuff;
+ int index;
+ int i,j;
+
+
+restart_loop:
+ while (pending_scrq(adapter, scrq)){
+ unsigned int pool = scrq->pool_index;
+
+ next = vnic_next_scrq(adapter, scrq);
+ for (i = 0; i < next->tx_comp.num_comps; i++) {
+ if (next->tx_comp.rcs[i]) {
+ dev_err(dev, "tx error %x\n",
+ next->tx_comp.rcs[i]);
+ continue;
+ }
+ index = next->tx_comp.correlators[i];
+ txbuff = &adapter->tx_pool[pool].tx_buff[index];
+
+ for (j = 0; j < VNIC_MAX_FRAGS_PER_CRQ; j++) {
+ if (!txbuff->data_dma[j])
+ continue;
+
+ if (txbuff->used_bounce)
+ dma_unmap_single(dev,
+ txbuff->data_dma[j],
+ txbuff->data_len[j],
+ DMA_TO_DEVICE);
+ txbuff->data_dma[j] = 0;
+ txbuff->used_bounce = 0;
+ }
+
+ if (txbuff->last_frag)
+ dev_kfree_skb(txbuff->skb);
+
+ adapter->tx_pool[pool].free_map[adapter->tx_pool[pool].
+ producer_index] = index;
+ adapter->tx_pool[pool].producer_index =
+ ++adapter->tx_pool[pool].producer_index %
+ adapter->max_tx_entries_per_subcrq;
+ }
+ }
+
+ enable_scrq_irq(adapter, scrq);
+
+ if (pending_scrq(adapter, scrq)) {
+ disable_scrq_irq(adapter, scrq);
+ goto restart_loop;
+ }
+
+ return 0;
+}
+
+static int vnic_tx_work(void* data)
+{
+ int rc;
+ struct vnic_sub_crq_queue *scrq = data;
+ struct vnic_adapter *adapter = scrq->adapter;
+
+ ENTER;
+
+ set_user_nice(current, -20);
+
+ while (1) {
+ rc =
+ wait_event_interruptible(adapter->tx_pool[scrq->pool_index].
+ vnic_tx_comp_q,
+ pending_scrq(adapter, scrq));
+ BUG_ON(rc);
+
+ if (kthread_should_stop())
+ break;
+
+ vnic_complete_tx(adapter, scrq);
+ }
+
+ LEAVE;
+ return 0;
+}
+
+static irqreturn_t vnic_interrupt_tx(int irq, void *instance)
+{
+ struct vnic_sub_crq_queue *scrq = instance;
+ struct vnic_adapter *adapter = scrq->adapter;
+
+ disable_scrq_irq(adapter, scrq);
+ wake_up(&adapter->tx_pool[scrq->pool_index].vnic_tx_comp_q);
+
+ return IRQ_HANDLED;
+}
+
+
+static irqreturn_t vnic_interrupt_rx(int irq, void *instance)
+{
+ struct vnic_sub_crq_queue *scrq = instance;
+ struct vnic_adapter *adapter = scrq->adapter;
+
+ if (napi_schedule_prep(&adapter->napi[scrq->scrq_num])) {
+ disable_scrq_irq(adapter, scrq);
+ __napi_schedule(&adapter->napi[scrq->scrq_num]);
+ }
+ return IRQ_HANDLED;
+}
+
+static void init_sub_crqs(struct vnic_adapter *adapter, int retry)
+{
+ struct device* dev = &adapter->vdev->dev;
+ int i, j, more = 0, total_queues, registered_queues = 0;
+ struct vnic_sub_crq_queue **allqueues;
+ union vnic_crq crq;
+ int rc;
+
+ if (!retry) {
+ /* Sub-CRQ entries are 32 byte long */
+ int entries_page = PAGE_SIZE / (sizeof(u64) * 4);
+
+ if (adapter->min_tx_entries_per_subcrq > entries_page ||
+ adapter->min_rx_add_entries_per_subcrq > entries_page ) {
+ dev_err(dev, "Fatal, invalid entries per sub-crq\n");
+ goto allqueues_failed;
+ }
+
+ /* Get the minimum between the queried max and the entries
+ that fit in our PAGE_SIZE */
+ adapter->req_tx_entries_per_subcrq =
+ adapter->max_tx_entries_per_subcrq >
+ entries_page ? entries_page : adapter->
+ max_tx_entries_per_subcrq;
+ adapter->req_rx_add_entries_per_subcrq =
+ adapter->max_rx_add_entries_per_subcrq >
+ entries_page ? entries_page : adapter->
+ max_rx_add_entries_per_subcrq;
+
+ /* Choosing the maximum number of queues supported by firmware*/
+ adapter->req_tx_queues = adapter->min_tx_queues;
+ adapter->req_rx_queues = adapter->min_rx_queues;
+ adapter->req_rx_add_queues = adapter->min_rx_add_queues;
+
+ adapter->req_mtu = adapter->max_mtu;
+ if (vnic_req_mtu) {
+ if(vnic_req_mtu > adapter->max_mtu
+ || vnic_req_mtu < adapter->min_mtu)
+ dev_warn(dev, "Module param req_mtu out of "
+ "range, ignoring it\n");
+ else
+ adapter->req_mtu = vnic_req_mtu;
+ }
+ }
+
+ total_queues =
+ adapter->req_tx_queues + adapter->req_rx_queues;
+
+ allqueues = kmalloc(sizeof(*allqueues) * total_queues, GFP_ATOMIC);
+ if (!allqueues) {
+ dev_err(dev, "Fatal, couldn't allocate sub-crqs\n");
+ goto allqueues_failed;
+ }
+
+ for (i = 0; i < total_queues; i++) {
+ allqueues[i] = init_sub_crq_queue(adapter);
+ if (!allqueues[i]) {
+ dev_warn(dev, "Couldn't allocate all sub-crqs\n");
+ break;
+ }
+ registered_queues++;
+ }
+
+ /* Make sure we were able to register the minimum number of queues */
+ if (registered_queues <
+ adapter->min_tx_queues + adapter->min_rx_queues) {
+ dev_err(dev, "Fatal: Couldn't init min number of sub-crqs\n");
+ goto tx_failed;
+ }
+
+ /* Distribute the failed allocated queues*/
+ for (i = 0 ; i < total_queues - registered_queues + more ; i++) {
+ vnic_dbg(adapter, "Reducing number of queues\n");
+ switch(i % 3) {
+ case 0:
+ if (adapter->req_rx_queues > adapter->min_rx_queues)
+ adapter->req_rx_queues--;
+ else
+ more++;
+ break;
+ case 1:
+ if (adapter->req_tx_queues > adapter->min_tx_queues)
+ adapter->req_tx_queues--;
+ else
+ more++;
+ break;
+ }
+ }
+
+ adapter->tx_scrq =
+ kmalloc(sizeof(*adapter->tx_scrq) * adapter->req_tx_queues,
+ GFP_ATOMIC);
+ if (!adapter->tx_scrq) {
+ dev_err(dev, "Fatal, couldn't allocate tx_scrq\n");
+ goto tx_failed;
+ }
+ for (i = 0; i < adapter->req_tx_queues; i++) {
+ adapter->tx_scrq[i] = allqueues[i];
+ adapter->tx_scrq[i]->pool_index = i;
+ rc = request_irq(adapter->tx_scrq[i]->irq, vnic_interrupt_tx, 0,
+ "vnic_tx", adapter->tx_scrq[i]);
+ if (rc) {
+ dev_err(dev, "Couldn't register tx irq 0x%x. rc=%d\n",
+ adapter->tx_scrq[i]->irq, rc);
+ goto req_tx_irq_failed;
+ }
+ }
+
+ adapter->rx_scrq =
+ kmalloc(sizeof(*adapter->rx_scrq) * adapter->req_rx_queues,
+ GFP_ATOMIC);
+ if (!adapter->rx_scrq) {
+ dev_err(dev, "Fatal, couldn't allocate rx_scrq\n");
+ goto rx_failed;
+ }
+ for (i = 0; i < adapter->req_rx_queues; i++) {
+ adapter->rx_scrq[i] = allqueues[i + adapter->req_tx_queues];
+ adapter->rx_scrq[i]->scrq_num = i;
+ rc = request_irq(adapter->rx_scrq[i]->irq, vnic_interrupt_rx, 0,
+ "vnic_rx", adapter->rx_scrq[i]);
+ if (rc) {
+ dev_err(dev, "Couldn't register rx irq 0x%x. rc=%d\n",
+ adapter->rx_scrq[i]->irq, rc);
+ goto req_rx_irq_failed;
+ }
+ }
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_capability.type = VNIC_CRQ_CMD;
+ crq.request_capability.cmd = REQUEST_CAPABILITY;
+
+ crq.request_capability.capability = REQ_TX_QUEUES;
+ crq.request_capability.number = adapter->req_tx_queues;
+ vnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability = REQ_RX_QUEUES;
+ crq.request_capability.number = adapter->req_rx_queues;
+ vnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability = REQ_RX_ADD_QUEUES;
+ crq.request_capability.number = adapter->req_rx_add_queues;
+ vnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability = REQ_TX_ENTRIES_PER_SUBCRQ;
+ crq.request_capability.number = adapter->req_tx_entries_per_subcrq;
+ vnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability = REQ_RX_ADD_ENTRIES_PER_SUBCRQ;
+ crq.request_capability.number = adapter->req_rx_add_entries_per_subcrq;
+ vnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability = REQ_MTU;
+ crq.request_capability.number = adapter->req_mtu;
+ vnic_send_crq(adapter, &crq);
+
+ kfree(allqueues);
+ LEAVE;
+ return;
+
+req_rx_irq_failed:
+ for (j = 0; j < i; j++)
+ free_irq(adapter->rx_scrq[j]->irq, adapter->rx_scrq[j]);
+ i = adapter->req_tx_queues;
+req_tx_irq_failed:
+ for (j = 0; j < i; j++)
+ free_irq(adapter->tx_scrq[j]->irq, adapter->tx_scrq[j]);
+ kfree(adapter->rx_scrq);
+ adapter->rx_scrq = NULL;
+rx_failed:
+ kfree(adapter->tx_scrq);
+ adapter->tx_scrq = NULL;
+tx_failed:
+ for (i = 0; i < registered_queues; i++)
+ release_sub_crq_queue(adapter, allqueues[i]);
+ kfree(allqueues);
+allqueues_failed:
+ vnic_remove(adapter->vdev);
+ return;
+}
+
+
+static int pending_scrq(struct vnic_adapter *adapter,
+ struct vnic_sub_crq_queue *scrq)
+{
+ union sub_crq *entry = &scrq->msgs[scrq->cur];
+ if (entry->generic.type & VNIC_CRQ_CMD_RSP || adapter->closing)
+ return 1;
+ else
+ return 0;
+}
+
+static union sub_crq *vnic_next_scrq(struct vnic_adapter *adapter,
+ struct vnic_sub_crq_queue *scrq)
+{
+ union sub_crq *entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(&scrq->lock, flags);
+ entry = &scrq->msgs[scrq->cur];
+ if (entry->generic.type & VNIC_CRQ_CMD_RSP) {
+ if (++scrq->cur == scrq->size)
+ scrq->cur = 0;
+ } else
+ entry = NULL;
+ spin_unlock_irqrestore(&scrq->lock, flags);
+
+ return entry;
+}
+
+static union vnic_crq *vnic_next_crq(struct vnic_adapter *adapter)
+{
+ struct vnic_crq_queue *queue = &adapter->crq;
+ union vnic_crq *crq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ crq = &queue->msgs[queue->cur];
+ if (crq->generic.type & VNIC_CRQ_CMD_RSP) {
+ if (++queue->cur == queue->size)
+ queue->cur = 0;
+ } else
+ crq = NULL;
+
+ spin_unlock_irqrestore(&queue->lock, flags);
+
+ return crq;
+}
+
+
+static int send_subcrq(struct vnic_adapter *adapter, u64 remote_handle,
+ union sub_crq *sub_crq)
+{
+ unsigned int ua = adapter->vdev->unit_address;
+ struct device* dev = &adapter->vdev->dev;
+ int rc;
+
+ u64 *u64_crq = (u64 *)sub_crq;
+
+ vnic_dbg(adapter, "Sending sCRQ %016lx: %016lx %016lx %016lx %016lx\n",
+ (long unsigned int)remote_handle,
+ (long unsigned int)u64_crq[0], (long unsigned int)u64_crq[1],
+ (long unsigned int)u64_crq[2], (long unsigned int)u64_crq[3]);
+
+ mb();
+
+ if ((rc =
+ plpar_hcall_norets(H_SEND_SUB_CRQ, ua, remote_handle, u64_crq[0],
+ u64_crq[1], u64_crq[2], u64_crq[3]))) {
+ if (rc == H_CLOSED)
+ dev_warn(dev, "CRQ Queue closed\n");
+ dev_err(dev, "Send error (rc=%d)\n", rc);
+ }
+ return rc;
+}
+
+static int vnic_send_crq(struct vnic_adapter *adapter, union vnic_crq *crq)
+{
+ unsigned int ua = adapter->vdev->unit_address;
+ struct device* dev = &adapter->vdev->dev;
+ int rc;
+
+ u64 *u64_crq = (u64 *)crq;
+ vnic_dbg(adapter, "Sending CRQ: %016lx %016lx\n",
+ (long unsigned int)u64_crq[0], (long unsigned int)u64_crq[1]);
+
+ mb();
+
+ if ((rc = plpar_hcall_norets(H_SEND_CRQ, ua, u64_crq[0], u64_crq[1]))) {
+ if (rc == H_CLOSED)
+ dev_warn(dev, "CRQ Queue closed\n");
+ dev_warn(dev, "Send error (rc=%d)\n", rc);
+ }
+ return rc;
+}
+
+static int vnic_send_crq_init(struct vnic_adapter *adapter)
+{
+ union vnic_crq crq;
+ memset(&crq, 0, sizeof(crq));
+ crq.generic.type = VNIC_CRQ_INIT_CMD;
+ crq.generic.cmd = VNIC_CRQ_INIT;
+ vnic_dbg(adapter, "Sending CRQ init\n");
+ return vnic_send_crq(adapter, &crq);
+}
+
+static int vnic_send_crq_init_complete(struct vnic_adapter *adapter)
+{
+ union vnic_crq crq;
+ memset(&crq, 0, sizeof(crq));
+ crq.generic.type = VNIC_CRQ_INIT_CMD;
+ crq.generic.cmd = VNIC_CRQ_INIT_COMPLETE;
+ vnic_dbg(adapter, "Sending CRQ init complete\n");
+ return vnic_send_crq(adapter, &crq);
+}
+
+static int send_version_xchg(struct vnic_adapter *adapter)
+{
+ union vnic_crq crq;
+ ENTER;
+ memset(&crq, 0, sizeof(crq));
+ crq.version_exchange.type = VNIC_CRQ_CMD;
+ crq.version_exchange.cmd = VERSION_EXCHANGE;
+ crq.version_exchange.version = vnic_version;
+ LEAVE;
+ return vnic_send_crq(adapter, &crq);
+}
+
+static void send_login(struct vnic_adapter *adapter)
+{
+ struct device* dev = &adapter->vdev->dev;
+ union vnic_crq crq;
+ struct vnic_login_buffer *login_buffer;
+ size_t buffer_size;
+ dma_addr_t buffer_token;
+ struct vnic_login_rsp_buffer *login_rsp_buffer;
+ size_t rsp_buffer_size;
+ dma_addr_t rsp_buffer_token;
+ long unsigned int *tx_list_p;
+ long unsigned int *rx_list_p;
+ struct vnic_inflight_cmd *inflight_cmd;
+ unsigned long flags;
+ int i;
+
+ ENTER;
+ buffer_size =
+ sizeof(struct vnic_login_buffer) +
+ sizeof(u64) * (adapter->req_tx_queues + adapter->req_rx_queues);
+
+ login_buffer = kmalloc(buffer_size, GFP_ATOMIC);
+ if (!login_buffer) {
+ dev_err(dev, "Couldn't allocate login buffer\n");
+ goto buf_alloc_failed;
+ }
+
+ buffer_token = dma_map_single(dev, login_buffer, buffer_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, buffer_token)) {
+ dev_err(dev, "Couldn't map login buffer\n");
+ goto buf_map_failed;
+ }
+
+ rsp_buffer_size =
+ sizeof(struct vnic_login_rsp_buffer) +
+ sizeof(u64) * (adapter->req_tx_queues +
+ adapter->req_rx_queues *
+ adapter->req_rx_add_queues + adapter->
+ req_rx_add_queues) +
+ sizeof(u8) * (VNIC_TX_DESC_VERSIONS);
+
+ login_rsp_buffer = kmalloc(rsp_buffer_size, GFP_ATOMIC);
+ if (!login_rsp_buffer) {
+ dev_err(dev, "Couldn't allocate login rsp buffer\n");
+ goto buf_rsp_alloc_failed;
+ }
+
+ rsp_buffer_token = dma_map_single(dev, login_rsp_buffer,
+ rsp_buffer_size, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, rsp_buffer_token)) {
+ dev_err(dev, "Couldn't map login rsp buffer\n");
+ goto buf_rsp_map_failed;
+ }
+
+ inflight_cmd = kmalloc(sizeof(inflight_cmd), GFP_ATOMIC);
+ if (!inflight_cmd) {
+ dev_err(dev, "Couldn't allocate inflight_cmd\n");
+ goto inflight_alloc_failed;
+ }
+
+ adapter->login_buf = login_buffer;
+ adapter->login_buf_token = buffer_token;
+ adapter->login_buf_sz = buffer_size;
+ adapter->login_rsp_buf = login_rsp_buffer;
+ adapter->login_rsp_buf_token = rsp_buffer_token;
+ adapter->login_rsp_buf_sz = rsp_buffer_size;
+
+ login_buffer->len = buffer_size;
+ login_buffer->version = INITIAL_VERSION_LB;
+ login_buffer->num_txcomp_subcrqs = adapter->req_tx_queues;
+ login_buffer->off_txcomp_subcrqs = sizeof(struct vnic_login_buffer);
+ login_buffer->num_rxcomp_subcrqs = adapter->req_rx_queues;
+ login_buffer->off_rxcomp_subcrqs = sizeof(struct vnic_login_buffer) +
+ sizeof(u64) * adapter->req_tx_queues;
+ login_buffer->login_rsp_ioba = rsp_buffer_token;
+ login_buffer->login_rsp_len = rsp_buffer_size;
+
+ tx_list_p =
+ (unsigned long *)((char*)login_buffer +
+ sizeof(struct vnic_login_buffer));
+ rx_list_p =
+ (unsigned long *)((char*)login_buffer +
+ sizeof(struct vnic_login_buffer) + sizeof(u64) *
+ adapter->req_tx_queues);
+
+ for (i = 0; i < adapter->req_tx_queues; i++)
+ if (adapter->tx_scrq[i])
+ tx_list_p[i] = adapter->tx_scrq[i]->crq_num;
+
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ if (adapter->rx_scrq[i])
+ rx_list_p[i] = adapter->rx_scrq[i]->crq_num;
+
+ vnic_dbg(adapter, "Login Buffer:\n");
+ for (i = 0; i < (adapter->login_buf_sz - 1) / 8 + 1; i++) {
+ vnic_dbg(adapter, "%016lx \n",
+ ((long unsigned int*)(adapter->login_buf))[i]);
+ }
+
+ memset(&crq, 0, sizeof(crq));
+ crq.login.type = VNIC_CRQ_CMD;
+ crq.login.cmd = LOGIN;
+ crq.login.ioba = buffer_token;
+ crq.login.len = buffer_size;
+
+ memcpy(&inflight_cmd->crq, &crq, sizeof(crq));
+
+ spin_lock_irqsave(&adapter->inflight_lock, flags);
+ list_add_tail(&inflight_cmd->list, &adapter->inflight);
+ spin_unlock_irqrestore(&adapter->inflight_lock, flags);
+
+ vnic_send_crq(adapter, &crq);
+
+ LEAVE;
+ return;
+
+inflight_alloc_failed:
+ dma_unmap_single(dev, rsp_buffer_token, rsp_buffer_size, DMA_FROM_DEVICE);
+buf_rsp_map_failed:
+ kfree(login_rsp_buffer);
+buf_rsp_alloc_failed:
+ dma_unmap_single(dev, buffer_token, buffer_size, DMA_TO_DEVICE);
+buf_map_failed:
+ kfree(login_buffer);
+buf_alloc_failed:
+ return;
+}
+
+static void send_cap_queries(struct vnic_adapter *adapter)
+{
+ union vnic_crq crq;
+
+ ENTER;
+ atomic_set(&adapter->running_cap_queries, 0);
+ memset(&crq, 0, sizeof(crq));
+ crq.query_capability.type = VNIC_CRQ_CMD;
+ crq.query_capability.cmd = QUERY_CAPABILITY;
+
+ crq.query_capability.capability = MIN_TX_QUEUES;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = MIN_RX_QUEUES;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = MIN_RX_ADD_QUEUES;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = MAX_TX_QUEUES;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = MAX_RX_QUEUES;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = MAX_RX_ADD_QUEUES;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = MIN_TX_ENTRIES_PER_SUBCRQ;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = MIN_RX_ADD_ENTRIES_PER_SUBCRQ;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = MAX_TX_ENTRIES_PER_SUBCRQ;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = MAX_RX_ADD_ENTRIES_PER_SUBCRQ;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = TCP_IP_OFFLOAD;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = PROMISC_SUPPORTED;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = MIN_MTU;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = MAX_MTU;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = MAX_MULTICAST_FILTERS;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = VLAN_HEADER_INSERTION;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = MAX_TX_SG_ENTRIES;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = RX_SG_SUPPORTED;
+ atomic_inc(&adapter->running_cap_queries);
+ vnic_send_crq(adapter, &crq);
+
+ return;
+}
+
+static void handle_query_ip_offload_rsp(struct vnic_adapter *adapter)
+{
+ struct device* dev = &adapter->vdev->dev;
+ struct vnic_query_ip_offload_buffer* buf = &adapter->ip_offload_buf;
+ union vnic_crq crq;
+ int i;
+
+ dma_unmap_single(dev, adapter->ip_offload_tok,
+ sizeof(adapter->ip_offload_buf),
+ DMA_FROM_DEVICE);
+
+ vnic_dbg(adapter, "Query IP Offload Buffer:\n");
+ for (i = 0; i < (sizeof(adapter->ip_offload_buf) - 1) / 8 + 1; i++)
+ vnic_dbg(adapter, "%016lx \n", ((long unsigned int*)(buf))[i]);
+
+ vnic_dbg(adapter, "ipv4_chksum = %d\n", buf->ipv4_chksum);
+ vnic_dbg(adapter, "ipv6_chksum = %d\n", buf->ipv6_chksum);
+ vnic_dbg(adapter, "tcp_ipv4_chksum = %d\n", buf->tcp_ipv4_chksum);
+ vnic_dbg(adapter, "tcp_ipv6_chksum = %d\n", buf->tcp_ipv6_chksum);
+ vnic_dbg(adapter, "udp_ipv4_chksum = %d\n", buf->udp_ipv4_chksum);
+ vnic_dbg(adapter, "udp_ipv6_chksum = %d\n", buf->udp_ipv6_chksum);
+ vnic_dbg(adapter, "large_tx_ipv4 = %d\n", buf->large_tx_ipv4);
+ vnic_dbg(adapter, "large_tx_ipv6 = %d\n", buf->large_tx_ipv6);
+ vnic_dbg(adapter, "large_rx_ipv4 = %d\n", buf->large_rx_ipv4);
+ vnic_dbg(adapter, "large_rx_ipv6 = %d\n", buf->large_rx_ipv6);
+ vnic_dbg(adapter, "max_ipv4_hdr_sz = %d\n", buf->max_ipv4_header_size);
+ vnic_dbg(adapter, "max_ipv6_hdr_sz = %d\n", buf->max_ipv6_header_size);
+ vnic_dbg(adapter, "max_tcp_hdr_size = %d\n", buf->max_tcp_header_size);
+ vnic_dbg(adapter, "max_udp_hdr_size = %d\n", buf->max_udp_header_size);
+ vnic_dbg(adapter, "max_large_tx_size = %d\n", buf->max_large_tx_size);
+ vnic_dbg(adapter, "max_large_rx_size = %d\n", buf->max_large_rx_size);
+ vnic_dbg(adapter, "ipv6_ext_hdr = %d\n", buf->ipv6_extension_header);
+ vnic_dbg(adapter, "tcp_pseudosum_req = %d\n", buf->tcp_pseudosum_req);
+ vnic_dbg(adapter, "num_ipv6_ext_hd = %d\n", buf->num_ipv6_ext_headers);
+ vnic_dbg(adapter, "off_ipv6_ext_hd = %d\n", buf->off_ipv6_ext_headers);
+
+ adapter->ip_offload_ctrl_tok =
+ dma_map_single(dev, &adapter->ip_offload_ctrl,
+ sizeof(adapter->ip_offload_ctrl), DMA_TO_DEVICE);
+
+ if (dma_mapping_error(dev, adapter->ip_offload_ctrl_tok)) {
+ dev_err(dev, "Couldn't map ip offload control buffer\n");
+ return;
+ }
+
+ adapter->ip_offload_ctrl.version = INITIAL_VERSION_IOB;
+ adapter->ip_offload_ctrl.tcp_ipv4_chksum = buf->tcp_ipv4_chksum;
+ adapter->ip_offload_ctrl.udp_ipv4_chksum = buf->udp_ipv4_chksum;
+ adapter->ip_offload_ctrl.tcp_ipv6_chksum = buf->tcp_ipv6_chksum;
+ adapter->ip_offload_ctrl.udp_ipv6_chksum = buf->udp_ipv6_chksum;
+ adapter->ip_offload_ctrl.large_tx_ipv4 = buf->large_tx_ipv4;
+ adapter->ip_offload_ctrl.large_tx_ipv6 = buf->large_tx_ipv6;
+ adapter->ip_offload_ctrl.large_rx_ipv4 = buf->large_rx_ipv4;
+ adapter->ip_offload_ctrl.large_rx_ipv6 = buf->large_rx_ipv6;
+
+ adapter->netdev->features = NETIF_F_SG | NETIF_F_GSO;
+
+ if (buf->tcp_ipv4_chksum || buf->udp_ipv4_chksum)
+ adapter->netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
+
+ if (buf->tcp_ipv6_chksum || buf->udp_ipv6_chksum)
+ adapter->netdev->features |= NETIF_F_SG | NETIF_F_IPV6_CSUM;
+
+ if (buf->large_tx_ipv4)
+ adapter->netdev->features |= NETIF_F_TSO;
+
+ if (buf->large_tx_ipv6)
+ adapter->netdev->features |= NETIF_F_TSO6;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ip_offload.type = VNIC_CRQ_CMD;
+ crq.control_ip_offload.cmd = CONTROL_IP_OFFLOAD;
+ crq.control_ip_offload.len = sizeof(adapter->ip_offload_ctrl);
+ crq.control_ip_offload.ioba = adapter->ip_offload_ctrl_tok;
+ vnic_send_crq(adapter, &crq);
+}
+
+static void handle_error_info_rsp(union vnic_crq *crq,
+ struct vnic_adapter *adapter)
+{
+ struct device* dev = &adapter->vdev->dev;
+ struct vnic_error_buff *error_buff;
+ int found = 0;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&adapter->error_list_lock, flags);
+ list_for_each_entry(error_buff, &adapter->errors, list)
+ if (error_buff->error_id == crq->request_error_rsp.error_id) {
+ found = 1;
+ list_del(&error_buff->list);
+ break;
+ }
+ spin_unlock_irqrestore(&adapter->error_list_lock, flags);
+
+ if (!found) {
+ dev_err(dev, "Couldn't find error id %x\n",
+ crq->request_error_rsp.error_id);
+ return;
+ }
+
+ dev_err(dev, "Detailed info for error id %x:",
+ crq->request_error_rsp.error_id);
+
+ for (i = 0 ; i < error_buff->len; i++) {
+ printk("%02x", (int)error_buff->buff[i]);
+ if (i % 8 == 7)
+ printk(" ");
+ }
+ printk("\n");
+
+ dma_unmap_single(dev, error_buff->dma, error_buff->len,
+ DMA_FROM_DEVICE);
+ kfree(error_buff->buff);
+ kfree(error_buff);
+}
+
+static void handle_dump_size_rsp(union vnic_crq *crq,
+ struct vnic_adapter *adapter)
+{
+ union vnic_crq newcrq;
+ struct device* dev = &adapter->vdev->dev;
+ int len = crq->request_dump_size_rsp.len;
+ struct vnic_inflight_cmd *inflight_cmd;
+ unsigned long flags;
+
+
+ /* allocate and map buffer */
+ adapter->dump_data = kmalloc(len, GFP_KERNEL);
+ if(!adapter->dump_data) {
+ dev_err(dev, "Couldn't allocate dump data\n");
+ complete(&adapter->fw_done);
+ return;
+ }
+
+ adapter->dump_data_token = dma_map_single(dev, adapter->dump_data, len,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(dev, adapter->dump_data_token)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(dev, "Couldn't map dump data\n");
+ kfree(adapter->dump_data);
+ complete(&adapter->fw_done);
+ return;
+ }
+
+ inflight_cmd = kmalloc(sizeof(struct vnic_inflight_cmd), GFP_ATOMIC);
+ if (!inflight_cmd) {
+ dev_err(dev, "Couldn't allocate inflight_cmd\n");
+ dma_unmap_single(dev, adapter->dump_data_token, len,
+ DMA_FROM_DEVICE);
+ kfree(adapter->dump_data);
+ complete(&adapter->fw_done);
+ return;
+ }
+
+ memset(&newcrq, 0, sizeof(newcrq));
+ newcrq.request_dump.type = VNIC_CRQ_CMD;
+ newcrq.request_dump.cmd = REQUEST_DUMP;
+ newcrq.request_dump.ioba = adapter->dump_data_token;
+ newcrq.request_dump.len = adapter->dump_data_size;
+
+ memcpy(&inflight_cmd->crq, &newcrq, sizeof(newcrq));
+
+ spin_lock_irqsave(&adapter->inflight_lock, flags);
+ list_add_tail(&inflight_cmd->list, &adapter->inflight);
+ spin_unlock_irqrestore(&adapter->inflight_lock, flags);
+
+ vnic_send_crq(adapter, &newcrq);
+}
+
+static void handle_error_indication(union vnic_crq *crq,
+ struct vnic_adapter *adapter)
+{
+ struct device* dev = &adapter->vdev->dev;
+ union vnic_crq new_crq;
+ struct vnic_error_buff *error_buff;
+ unsigned long flags;
+ int detail_len = crq->error_indication.detail_error_sz;
+ struct vnic_inflight_cmd *inflight_cmd;
+
+ dev_err(dev, "Firmware reports %serror id %x, cause %d\n",
+ crq->error_indication.flags & VNIC_FATAL_ERROR ? "FATAL ": "",
+ crq->error_indication.error_id,
+ crq->error_indication.error_cause);
+
+ error_buff = kmalloc(sizeof(struct vnic_error_buff), GFP_ATOMIC);
+ if (!error_buff) {
+ dev_err(dev, "Couldn't allocate error buffer struct\n");
+ return;
+ }
+
+ error_buff->buff = kmalloc(detail_len, GFP_ATOMIC);
+ if (!error_buff->buff) {
+ dev_err(dev, "Couldn't allocate error buffer\n");
+ kfree(error_buff);
+ return;
+ }
+
+ error_buff->dma = dma_map_single(dev, error_buff->buff, detail_len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, error_buff->dma)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(dev, "Couldn't map error buffer\n");
+ kfree(error_buff->buff);
+ kfree(error_buff);
+ return;
+ }
+
+ inflight_cmd = kmalloc(sizeof(struct vnic_inflight_cmd), GFP_ATOMIC);
+ if (!inflight_cmd) {
+ dev_err(dev, "Couldn't allocate inflight_cmd\n");
+ dma_unmap_single(dev, error_buff->dma, detail_len,
+ DMA_FROM_DEVICE);
+ kfree(error_buff->buff);
+ kfree(error_buff);
+ return;
+ }
+
+ error_buff->len = detail_len;
+ error_buff->error_id = crq->error_indication.error_id;
+
+ spin_lock_irqsave(&adapter->error_list_lock, flags);
+ list_add_tail(&error_buff->list, &adapter->errors);
+ spin_unlock_irqrestore(&adapter->error_list_lock, flags);
+
+ memset(&new_crq, 0, sizeof(new_crq));
+ new_crq.request_error_info.type = VNIC_CRQ_CMD;
+ new_crq.request_error_info.cmd = REQUEST_ERROR_INFO;
+ new_crq.request_error_info.ioba = error_buff->dma;
+ new_crq.request_error_info.len = detail_len;
+ new_crq.request_error_info.error_id = crq->error_indication.error_id;
+
+ memcpy(&inflight_cmd->crq, &crq, sizeof(crq));
+
+ spin_lock_irqsave(&adapter->inflight_lock, flags);
+ list_add_tail(&inflight_cmd->list, &adapter->inflight);
+ spin_unlock_irqrestore(&adapter->inflight_lock, flags);
+
+ vnic_send_crq(adapter, &new_crq);
+
+ return;
+}
+
+static void handle_change_mac_rsp(union vnic_crq *crq,
+ struct vnic_adapter *adapter)
+{
+ struct device* dev = &adapter->vdev->dev;
+ struct net_device *netdev = adapter->netdev;
+ long rc;
+
+ if ((rc = crq->change_mac_addr_rsp.rc.code)) {
+ dev_err(dev, "Error %ld in CHANGE_MAC_ADDR_RSP\n", rc);
+ return;
+ }
+ memcpy(netdev->dev_addr, &crq->change_mac_addr_rsp.mac_addr, ETH_ALEN);
+}
+
+static void handle_request_cap_rsp(union vnic_crq *crq,
+ struct vnic_adapter *adapter)
+{
+ struct device* dev = &adapter->vdev->dev;
+ long* req_value;
+ char* name;
+
+ switch (crq->request_capability_rsp.capability) {
+ case REQ_TX_QUEUES:
+ req_value = &adapter->req_tx_queues;
+ name = "tx";
+ break;
+ case REQ_RX_QUEUES:
+ req_value = &adapter->req_rx_queues;
+ name = "rx";
+ break;
+ case REQ_RX_ADD_QUEUES:
+ req_value = &adapter->req_rx_add_queues;
+ name = "rx_add";
+ break;
+ case REQ_TX_ENTRIES_PER_SUBCRQ:
+ req_value = &adapter->req_tx_entries_per_subcrq;
+ name = "tx_entries_per_subcrq";
+ break;
+ case REQ_RX_ADD_ENTRIES_PER_SUBCRQ:
+ req_value = &adapter->req_rx_add_entries_per_subcrq;
+ name = "rx_add_entries_per_subcrq";
+ break;
+ case REQ_MTU:
+ req_value = &adapter->req_mtu;
+ name = "mtu";
+ break;
+ case PROMISC_REQUESTED:
+ req_value = &adapter->promisc;
+ name = "promisc";
+ return;
+ default:
+ dev_err(dev, "Got invalid cap request rsp %d\n",
+ crq->request_capability.capability);
+ return;
+ }
+
+ switch (crq->request_capability_rsp.rc.code) {
+ case SUCCESS:
+ break;
+ case PARTIALSUCCESS:
+ dev_info(dev, "req=%ld, rsp=%ld in %s queue, retrying.\n",
+ *req_value,
+ (unsigned long)crq->request_capability_rsp.number,
+ name);
+ release_sub_crqs(adapter);
+ *req_value = crq->request_capability_rsp.number;
+ init_sub_crqs(adapter, 1);
+ return;
+ default:
+ dev_err(dev, "Error %d in request cap rsp\n",
+ crq->request_capability_rsp.rc.code);
+ return;
+ }
+
+ /* Done receiving requested capabilities, query IP offload support */
+ if (++adapter->requested_caps == 6){
+ union vnic_crq newcrq;
+ int buf_sz = sizeof(struct vnic_query_ip_offload_buffer);
+ struct vnic_query_ip_offload_buffer* ip_offload_buf =
+ &adapter->ip_offload_buf;
+
+ adapter->ip_offload_tok = dma_map_single(dev, ip_offload_buf,
+ buf_sz,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(dev, adapter->ip_offload_tok)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(dev, "Couldn't map offload buffer\n");
+ return;
+ }
+
+ memset(&newcrq, 0, sizeof(newcrq));
+ newcrq.query_ip_offload.type = VNIC_CRQ_CMD;
+ newcrq.query_ip_offload.cmd = QUERY_IP_OFFLOAD;
+ newcrq.query_ip_offload.len = buf_sz;
+ newcrq.query_ip_offload.ioba = adapter->ip_offload_tok;
+
+ vnic_send_crq(adapter, &newcrq);
+ }
+}
+
+
+static int handle_login_rsp(union vnic_crq *login_rsp_crq,
+ struct vnic_adapter *adapter)
+{
+ struct device* dev = &adapter->vdev->dev;
+ int i;
+ struct vnic_login_buffer *login = adapter->login_buf;
+ struct vnic_login_rsp_buffer *login_rsp = adapter->login_rsp_buf;
+ union vnic_crq crq;
+
+ ENTER;
+
+ dma_unmap_single(dev, adapter->login_buf_token, adapter->login_buf_sz,
+ DMA_BIDIRECTIONAL);
+ dma_unmap_single(dev, adapter->login_rsp_buf_token,
+ adapter->login_rsp_buf_sz, DMA_BIDIRECTIONAL);
+
+ vnic_dbg(adapter, "Login Response Buffer:\n");
+ for (i = 0; i < (adapter->login_rsp_buf_sz - 1) / 8 + 1; i++) {
+ vnic_dbg(adapter, "%016lx \n",
+ ((long unsigned int*)(adapter->login_rsp_buf))[i]);
+ }
+
+ /* Sanity checks */
+ if (login->num_txcomp_subcrqs != login_rsp->num_txsubm_subcrqs
+ || login->num_rxcomp_subcrqs * adapter->req_rx_add_queues !=
+ login_rsp->num_rxadd_subcrqs) {
+ dev_err(dev, "FATAL: Inconsistent login and login rsp\n");
+ vnic_remove(adapter->vdev);
+ return -EIO;
+ }
+
+ complete(&adapter->init_done);
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_ras_comp_num.type = VNIC_CRQ_CMD;
+ crq.request_ras_comp_num.cmd = REQUEST_RAS_COMP_NUM;
+ vnic_send_crq(adapter, &crq);
+
+ LEAVE;
+ return 0;
+}
+
+static void handle_query_cap_rsp(union vnic_crq *crq,
+ struct vnic_adapter *adapter)
+{
+ struct device* dev = &adapter->vdev->dev;
+ long rc;
+
+ atomic_dec(&adapter->running_cap_queries);
+ vnic_dbg(adapter, "Outstanding queries: %d\n",
+ atomic_read(&adapter->running_cap_queries));
+ if ((rc = crq->query_capability.rc.code)) {
+ dev_err(dev, "Error %ld in QUERY_CAP_RSP\n", rc);
+ goto out;
+ }
+
+ switch (crq->query_capability.capability) {
+ case MIN_TX_QUEUES:
+ adapter->min_tx_queues = crq->query_capability.number;
+ vnic_dbg(adapter, "min_tx_queues = %ld\n",
+ adapter->min_tx_queues);
+ break;
+ case MIN_RX_QUEUES:
+ adapter->min_rx_queues = crq->query_capability.number;
+ vnic_dbg(adapter, "min_rx_queues = %ld\n",
+ adapter->min_rx_queues);
+ break;
+ case MIN_RX_ADD_QUEUES:
+ adapter->min_rx_add_queues = crq->query_capability.number;
+ vnic_dbg(adapter, "min_rx_add_queues = %ld\n",
+ adapter->min_rx_add_queues);
+ break;
+ case MAX_TX_QUEUES:
+ adapter->max_tx_queues = crq->query_capability.number;
+ vnic_dbg(adapter, "max_tx_queues = %ld\n",
+ adapter->max_tx_queues);
+ if (adapter->max_tx_queues > vnic_max_tx_queues)
+ dev_warn(dev, "Adapter's max_tx_queues is greater "
+ "than module parameter. Set module paramenter"
+ " to %ld, for optimum performance\n",
+ adapter->max_tx_queues);
+ break;
+ case MAX_RX_QUEUES:
+ adapter->max_rx_queues = crq->query_capability.number;
+ vnic_dbg(adapter, "max_rx_queues = %ld\n",
+ adapter->max_rx_queues);
+ break;
+ case MAX_RX_ADD_QUEUES:
+ adapter->max_rx_add_queues = crq->query_capability.number;
+ vnic_dbg(adapter, "max_rx_add_queues = %ld\n",
+ adapter->max_rx_add_queues);
+ break;
+ case MIN_TX_ENTRIES_PER_SUBCRQ:
+ adapter->min_tx_entries_per_subcrq =
+ crq->query_capability.number;
+ vnic_dbg(adapter, "min_tx_entries_per_subcrq = %ld\n",
+ adapter->min_tx_entries_per_subcrq);
+ break;
+ case MIN_RX_ADD_ENTRIES_PER_SUBCRQ:
+ adapter->min_rx_add_entries_per_subcrq =
+ crq->query_capability.number;
+ vnic_dbg(adapter, "min_rx_add_entrs_per_subcrq = %ld\n",
+ adapter->min_rx_add_entries_per_subcrq);
+ break;
+ case MAX_TX_ENTRIES_PER_SUBCRQ:
+ adapter->max_tx_entries_per_subcrq =
+ crq->query_capability.number;
+ vnic_dbg(adapter, "max_tx_entries_per_subcrq = %ld\n",
+ adapter->max_tx_entries_per_subcrq);
+ break;
+ case MAX_RX_ADD_ENTRIES_PER_SUBCRQ:
+ adapter->max_rx_add_entries_per_subcrq =
+ crq->query_capability.number;
+ vnic_dbg(adapter, "max_rx_add_entrs_per_subcrq = %ld\n",
+ adapter->max_rx_add_entries_per_subcrq);
+ break;
+ case TCP_IP_OFFLOAD:
+ adapter->tcp_ip_offload = crq->query_capability.number;
+ vnic_dbg(adapter, "tcp_ip_offload = %ld\n",
+ adapter->tcp_ip_offload);
+ break;
+ case PROMISC_SUPPORTED:
+ adapter->promisc_supported = crq->query_capability.number;
+ vnic_dbg(adapter, "promisc_supported = %ld\n",
+ adapter->promisc_supported);
+ break;
+ case MIN_MTU:
+ adapter->min_mtu = crq->query_capability.number;
+ vnic_dbg(adapter, "min_mtu = %ld\n", adapter->min_mtu);
+ break;
+ case MAX_MTU:
+ adapter->max_mtu = crq->query_capability.number;
+ vnic_dbg(adapter, "max_mtu = %ld\n", adapter->max_mtu);
+ break;
+ case MAX_MULTICAST_FILTERS:
+ adapter->max_multicast_filters = crq->query_capability.number;
+ vnic_dbg(adapter, "max_multicast_filters = %ld\n",
+ adapter->max_multicast_filters);
+ break;
+ case VLAN_HEADER_INSERTION:
+ adapter->vlan_header_insertion = crq->query_capability.number;
+ if (adapter->vlan_header_insertion)
+ adapter->netdev->features |= NETIF_F_HW_VLAN_STAG_TX;
+ vnic_dbg(adapter, "vlan_header_insertion = %ld\n",
+ adapter->vlan_header_insertion);
+ break;
+ case MAX_TX_SG_ENTRIES:
+ adapter->max_tx_sg_entries = crq->query_capability.number;
+ vnic_dbg(adapter, "max_tx_sg_entries = %ld\n",
+ adapter->max_tx_sg_entries);
+ break;
+ case RX_SG_SUPPORTED:
+ adapter->rx_sg_supported = crq->query_capability.number;
+ vnic_dbg(adapter, "rx_sg_supported = %ld\n",
+ adapter->rx_sg_supported);
+ break;
+ default:
+ dev_err(dev, "Got invalid cap rsp %d\n",
+ crq->query_capability.capability);
+ }
+
+out:
+ if (atomic_read(&adapter->running_cap_queries) == 0)
+ init_sub_crqs(adapter, 0);
+ /* We're done querying the capabilities, initialize sub-crqs */
+}
+
+
+static void handle_control_ras_rsp(union vnic_crq *crq,
+ struct vnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ u8 correlator = crq->control_ras_rsp.correlator;
+ int found = 0;
+ int i;
+
+ if (crq->control_ras_rsp.rc.code) {
+ dev_warn(dev, "Control ras failed rc=%d\n",
+ crq->control_ras_rsp.rc.code);
+ return;
+ }
+
+ for (i = 0; i < adapter->ras_comp_num ; i++) {
+ if (adapter->ras_comps[i].correlator == correlator) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ dev_warn(dev, "Correlator not found on control_ras_rsp\n");
+ return;
+ }
+
+ switch (crq->control_ras_rsp.op) {
+ case VNIC_TRACE_LEVEL:
+ adapter->ras_comps[i].trace_level = crq->control_ras.level;
+ break;
+ case VNIC_ERROR_LEVEL:
+ adapter->ras_comps[i].error_check_level =
+ crq->control_ras.level;
+ break;
+ case VNIC_TRACE_PAUSE:
+ adapter->ras_comp_int[i].paused = 1;
+ break;
+ case VNIC_TRACE_RESUME:
+ adapter->ras_comp_int[i].paused = 0;
+ break;
+ case VNIC_TRACE_ON:
+ adapter->ras_comps[i].trace_on = 1;
+ break;
+ case VNIC_TRACE_OFF:
+ adapter->ras_comps[i].trace_on = 0;
+ break;
+ case VNIC_CHG_TRACE_BUFF_SZ:
+ /* trace_buff_sz is 3 bytes, stuff it into an int */
+ ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[0] = 0;
+ ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[1] =
+ crq->control_ras_rsp.trace_buff_sz[0];
+ ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[2] =
+ crq->control_ras_rsp.trace_buff_sz[1];
+ ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[3] =
+ crq->control_ras_rsp.trace_buff_sz[2];
+ break;
+ default:
+ dev_err(dev, "invalid op %d on control_ras_rsp",
+ crq->control_ras_rsp.op);
+ }
+}
+
+static int vnic_fw_comp_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+ssize_t trace_read(struct file *file, char __user *user_buf, size_t len,
+ loff_t *ppos)
+{
+ struct vnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct vnic_adapter *adapter = ras_comp_int->adapter;
+ struct device *dev = &adapter->vdev->dev;
+ int num = ras_comp_int->num;
+ struct vnic_fw_trace_entry *trace;
+ dma_addr_t trace_tok;
+ union vnic_crq crq;
+
+
+ if (*ppos >= adapter->ras_comps[num].trace_buff_size)
+ return 0;
+
+ trace = dma_alloc_coherent(dev, adapter->ras_comps[num].trace_buff_size,
+ &trace_tok, GFP_KERNEL);
+ if (!trace) {
+ dev_err(dev, "Couldn't alloc trace buffer\n");
+ return 0;
+ }
+
+ memset(&crq, 0, sizeof(crq));
+ crq.collect_fw_trace.type = VNIC_CRQ_CMD;
+ crq.collect_fw_trace.cmd = COLLECT_FW_TRACE;
+ crq.collect_fw_trace.correlator = adapter->ras_comps[num].correlator;
+ crq.collect_fw_trace.ioba = trace_tok;
+ crq.collect_fw_trace.len = adapter->ras_comps[num].trace_buff_size;
+ vnic_send_crq(adapter, &crq);
+
+ init_completion(&adapter->fw_done);
+ wait_for_completion(&adapter->fw_done);
+
+ if (*ppos + len > adapter->ras_comps[num].trace_buff_size)
+ len = adapter->ras_comps[num].trace_buff_size - *ppos;
+
+ copy_to_user(user_buf, &((u8 *)trace)[*ppos], len);
+
+ dma_free_coherent(dev, adapter->ras_comps[num].trace_buff_size,
+ trace, trace_tok);
+ *ppos += len;
+ return len;
+}
+
+static const struct file_operations trace_ops= {
+ .owner = THIS_MODULE,
+ .open = vnic_fw_comp_open,
+ .read = trace_read,
+};
+
+ssize_t paused_read(struct file *file, char __user *user_buf, size_t len,
+ loff_t *ppos)
+{
+ struct vnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct vnic_adapter *adapter = ras_comp_int->adapter;
+ char buff[5]; /* 1 or 0 plus \n and \0 */
+ int num = ras_comp_int->num;
+ int size;
+
+ size = sprintf(buff, "%d\n", adapter->ras_comp_int[num].paused);
+
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t paused_write(struct file *file, const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct vnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct vnic_adapter *adapter = ras_comp_int->adapter;
+ char buff[9]; /* decimal max int plus \n and \0 */
+ unsigned long val;
+ union vnic_crq crq;
+ int num = ras_comp_int->num;
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = simple_strtoul(buff, NULL, 10);
+
+ adapter->ras_comp_int[num].paused = val ? 1 : 0;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.type = VNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator = adapter->ras_comps[num].correlator;
+ crq.control_ras.op = val ? VNIC_TRACE_PAUSE : VNIC_TRACE_RESUME;
+ vnic_send_crq(adapter, &crq);
+
+ return len;
+}
+
+static const struct file_operations paused_ops= {
+ .owner = THIS_MODULE,
+ .open = vnic_fw_comp_open,
+ .read = paused_read,
+ .write = paused_write,
+};
+
+ssize_t tracing_read(struct file *file, char __user *user_buf, size_t len,
+ loff_t *ppos)
+{
+ struct vnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct vnic_adapter *adapter = ras_comp_int->adapter;
+ char buff[5]; /* 1 or 0 plus \n and \0 */
+ int num = ras_comp_int->num;
+ int size;
+
+ size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_on);
+
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t tracing_write(struct file *file, const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct vnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct vnic_adapter *adapter = ras_comp_int->adapter;
+ char buff[9]; /* decimal max int plus \n and \0 */
+ unsigned long val;
+ union vnic_crq crq;
+ int num = ras_comp_int->num;
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = simple_strtoul(buff, NULL, 10);
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.type = VNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator = adapter->ras_comps[num].correlator;
+ crq.control_ras.op = val ? VNIC_TRACE_ON : VNIC_TRACE_OFF;
+
+ return len;
+}
+
+static const struct file_operations tracing_ops= {
+ .owner = THIS_MODULE,
+ .open = vnic_fw_comp_open,
+ .read = tracing_read,
+ .write = tracing_write,
+};
+
+ssize_t error_level_read(struct file *file, char __user *user_buf, size_t len,
+ loff_t *ppos)
+{
+ struct vnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct vnic_adapter *adapter = ras_comp_int->adapter;
+ char buff[5]; /* decimal max char plus \n and \0 */
+ int num = ras_comp_int->num;
+ int size;
+
+ size = sprintf(buff, "%d\n",
+ adapter->ras_comps[num].error_check_level);
+
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t error_level_write(struct file *file, const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct vnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct vnic_adapter *adapter = ras_comp_int->adapter;
+ char buff[9]; /* decimal max int plus \n and \0 */
+ unsigned long val;
+ union vnic_crq crq;
+ int num = ras_comp_int->num;
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = simple_strtoul(buff, NULL, 10);
+
+ if (val > 9)
+ val = 9;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.type = VNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator = adapter->ras_comps[num].correlator;
+ crq.control_ras.op = VNIC_ERROR_LEVEL;
+ crq.control_ras.level = val;
+ vnic_send_crq(adapter, &crq);
+
+ return len;
+}
+
+static const struct file_operations error_level_ops= {
+ .owner = THIS_MODULE,
+ .open = vnic_fw_comp_open,
+ .read = error_level_read,
+ .write = error_level_write,
+};
+
+ssize_t trace_level_read(struct file *file, char __user *user_buf, size_t len,
+ loff_t *ppos)
+{
+ struct vnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct vnic_adapter *adapter = ras_comp_int->adapter;
+ char buff[5]; /* decimal max char plus \n and \0 */
+ int num = ras_comp_int->num;
+ int size;
+
+ size = sprintf(buff, "%d\n",
+ adapter->ras_comps[num].trace_level);
+
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t trace_level_write(struct file *file, const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct vnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct vnic_adapter *adapter = ras_comp_int->adapter;
+ char buff[9]; /* decimal max int plus \n and \0 */
+ unsigned long val;
+ union vnic_crq crq;
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = simple_strtoul(buff, NULL, 10);
+
+ if (val > 9)
+ val = 9;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.type = VNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator =
+ adapter->ras_comps[ras_comp_int->num].correlator;
+ crq.control_ras.op = VNIC_TRACE_LEVEL;
+ crq.control_ras.level = val;
+ vnic_send_crq(adapter, &crq);
+
+ return len;
+}
+
+static const struct file_operations trace_level_ops= {
+ .owner = THIS_MODULE,
+ .open = vnic_fw_comp_open,
+ .read = trace_level_read,
+ .write = trace_level_write,
+};
+
+ssize_t trace_buff_size_read(struct file *file, char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct vnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct vnic_adapter *adapter = ras_comp_int->adapter;
+ char buff[9]; /* decimal max int plus \n and \0 */
+ int num = ras_comp_int->num;
+ int size;
+
+ size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_buff_size);
+
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t trace_buff_size_write(struct file *file,
+ const char __user *user_buf, size_t len,
+ loff_t *ppos)
+{
+ struct vnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct vnic_adapter *adapter = ras_comp_int->adapter;
+ char buff[9]; /* decimal max int plus \n and \0 */
+ unsigned long val;
+ union vnic_crq crq;
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = simple_strtoul(buff, NULL, 10);
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.type = VNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator =
+ adapter->ras_comps[ras_comp_int->num].correlator;
+ crq.control_ras.op = VNIC_CHG_TRACE_BUFF_SZ;
+ /* trace_buff_sz is 3 bytes, stuff an int into it */
+ crq.control_ras.trace_buff_sz[0] = ((u8*)(&val))[5];
+ crq.control_ras.trace_buff_sz[1] = ((u8*)(&val))[6];
+ crq.control_ras.trace_buff_sz[2] = ((u8*)(&val))[7];
+ vnic_send_crq(adapter, &crq);
+
+ return len;
+}
+
+static const struct file_operations trace_size_ops= {
+ .owner = THIS_MODULE,
+ .open = vnic_fw_comp_open,
+ .read = trace_buff_size_read,
+ .write = trace_buff_size_write,
+};
+
+static void handle_request_ras_comps_rsp(union vnic_crq *crq,
+ struct vnic_adapter *adapter)
+{
+ struct device* dev = &adapter->vdev->dev;
+ struct dentry *dir_ent;
+ struct dentry *ent;
+ int i;
+
+ debugfs_remove_recursive(adapter->ras_comps_ent);
+
+ adapter->ras_comps_ent = debugfs_create_dir("ras_comps",
+ adapter->debugfs_dir);
+ if (!adapter->ras_comps_ent || IS_ERR(adapter->ras_comps_ent)) {
+ dev_info(dev, "debugfs create ras_comps dir failed\n");
+ return;
+ }
+
+ for (i = 0 ; i < adapter->ras_comp_num; i++) {
+ dir_ent = debugfs_create_dir(adapter->ras_comps[i].name,
+ adapter->ras_comps_ent);
+ if (!dir_ent || IS_ERR(dir_ent)) {
+ dev_info(dev, "debugfs create %s dir failed\n",
+ adapter->ras_comps[i].name);
+ continue;
+ }
+
+ adapter->ras_comp_int[i].adapter = adapter;
+ adapter->ras_comp_int[i].num = i;
+ adapter->ras_comp_int[i].desc_blob.data =
+ &adapter->ras_comps[i].description;
+ adapter->ras_comp_int[i].desc_blob.size =
+ sizeof(adapter->ras_comps[i].description);
+
+ /* Don't need to remember the dentry's because the debugfs dir
+ gets removed recursively */
+ ent = debugfs_create_blob("description", S_IRUGO, dir_ent,
+ &adapter->ras_comp_int[i].desc_blob);
+ ent = debugfs_create_file("trace_buf_size", S_IRUGO | S_IWUSR,
+ dir_ent, &adapter->ras_comp_int[i],
+ &trace_size_ops);
+ ent = debugfs_create_file("trace_level",
+ S_IRUGO | (adapter->ras_comps[i].
+ trace_level !=
+ 0xFF ? S_IWUSR : 0),
+ dir_ent, &adapter->ras_comp_int[i],
+ &trace_level_ops);
+ ent = debugfs_create_file("error_level",
+ S_IRUGO | (adapter->ras_comps[i].
+ error_check_level !=
+ 0xFF ? S_IWUSR : 0),
+ dir_ent, &adapter->ras_comp_int[i],
+ &trace_level_ops);
+ ent = debugfs_create_file("tracing", S_IRUGO | S_IWUSR,
+ dir_ent, &adapter->ras_comp_int[i],
+ &tracing_ops);
+ ent = debugfs_create_file("paused", S_IRUGO | S_IWUSR,
+ dir_ent, &adapter->ras_comp_int[i],
+ &paused_ops);
+ ent = debugfs_create_file("trace", S_IRUGO, dir_ent,
+ &adapter->ras_comp_int[i],
+ &trace_ops);
+ }
+}
+
+static void handle_request_ras_comp_num_rsp(union vnic_crq *crq,
+ struct vnic_adapter *adapter)
+{
+ struct device* dev = &adapter->vdev->dev;
+ union vnic_crq newcrq;
+ int len = adapter->ras_comp_num * sizeof(struct vnic_fw_component);
+
+ adapter->ras_comps = dma_alloc_coherent(dev, len,
+ &adapter->ras_comps_tok,
+ GFP_KERNEL);
+ if (!adapter->ras_comps) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(dev, "Couldn't alloc fw comps buffer\n");
+ return;
+ }
+
+ adapter->ras_comp_int =
+ kmalloc(sizeof(struct vnic_fw_comp_internal) *
+ adapter->ras_comp_num, GFP_KERNEL);
+
+ if (!adapter->ras_comp_int) {
+ dev_err(dev, "Couldn't alloc comp blobs buffer\n");
+ dma_free_coherent(dev, len, adapter->ras_comps,
+ adapter->ras_comps_tok);
+ }
+
+ memset(&newcrq, 0, sizeof(newcrq));
+ newcrq.request_ras_comps.type = VNIC_CRQ_CMD;
+ newcrq.request_ras_comps.cmd = REQUEST_RAS_COMPS;
+ newcrq.request_ras_comps.ioba = adapter->ras_comps_tok;
+ newcrq.request_ras_comps.len = len;
+ vnic_send_crq(adapter, &newcrq);
+}
+
+static void vnic_free_inflight(struct vnic_adapter *adapter)
+{
+ struct device* dev = &adapter->vdev->dev;
+ struct vnic_inflight_cmd *inflight_cmd;
+ struct vnic_error_buff *error_buff;
+ unsigned long flags;
+ unsigned long flags2;
+
+ spin_lock_irqsave(&adapter->inflight_lock, flags);
+ list_for_each_entry(inflight_cmd, &adapter->inflight, list) {
+ switch (inflight_cmd->crq.generic.cmd) {
+ case LOGIN:
+ dma_unmap_single(dev, adapter->login_buf_token,
+ adapter->login_buf_sz,
+ DMA_BIDIRECTIONAL);
+ dma_unmap_single(dev, adapter->login_rsp_buf_token,
+ adapter->login_rsp_buf_sz,
+ DMA_BIDIRECTIONAL);
+ kfree(adapter->login_rsp_buf);
+ kfree(adapter->login_buf);
+ break;
+ case REQUEST_DUMP:
+ complete(&adapter->fw_done);
+ break;
+ case REQUEST_ERROR_INFO:
+ spin_lock_irqsave(&adapter->error_list_lock, flags2);
+ list_for_each_entry(error_buff,
+ &adapter->errors, list) {
+ dma_unmap_single(dev, error_buff->dma,
+ error_buff->len,
+ DMA_FROM_DEVICE);
+ kfree(error_buff->buff);
+ list_del(&error_buff->list);
+ kfree(error_buff);
+ }
+ spin_unlock_irqrestore(&adapter->error_list_lock,
+ flags2);
+
+ break;
+ }
+ list_del(&inflight_cmd->list);
+ kfree(inflight_cmd);
+ }
+ spin_unlock_irqrestore(&adapter->inflight_lock, flags);
+
+
+}
+
+static void vnic_handle_crq(union vnic_crq *crq, struct vnic_adapter *adapter)
+{
+ struct vnic_generic_crq *gen_crq = &crq->generic;
+ struct device* dev = &adapter->vdev->dev;
+ long rc;
+
+ vnic_dbg(adapter, "Handling CRQ: %016lx %016lx\n",
+ ((long unsigned int*)crq)[0], ((long unsigned int*)crq)[1]);
+ switch (gen_crq->type) {
+ case VNIC_CRQ_INIT_RSP:
+ switch (gen_crq->cmd) {
+ case VNIC_CRQ_INIT:
+ dev_info(dev, "Partner initialized\n");
+ /* Send back a response */
+ rc = vnic_send_crq_init_complete(adapter);
+ if (rc == 0)
+ send_version_xchg(adapter);
+ else
+ dev_err(dev, "Can't send initrsp rc=%ld\n", rc);
+ break;
+ case VNIC_CRQ_INIT_COMPLETE:
+ dev_info(dev, "Partner initialization complete\n");
+ send_version_xchg(adapter);
+ break;
+ default:
+ dev_err(dev, "Unknown crq cmd: %d\n", gen_crq->cmd);
+ }
+ return;
+ case VNIC_CRQ_XPORT_EVENT:
+ if (gen_crq->cmd == VNIC_PARTITION_MIGRATED) {
+ dev_info(dev, "Re-enabling adapter\n");
+ adapter->migrated = 1;
+ vnic_free_inflight(adapter);
+ release_sub_crqs(adapter);
+ if ((rc = vnic_reenable_crq_queue(adapter)))
+ dev_err(dev, "Error after enable rc=%ld\n", rc);
+ adapter->migrated = 0;
+ if ((rc = vnic_send_crq_init(adapter)))
+ dev_err(dev, "Error sending init rc=%ld\n", rc);
+ } else {
+ /* The adapter lost the connection */
+ dev_err(dev, "Virtual Adapter failed (rc=%d)\n",
+ gen_crq->cmd);
+ vnic_free_inflight(adapter);
+ release_sub_crqs(adapter);
+ }
+ return;
+ case VNIC_CRQ_CMD_RSP:
+ break;
+ default:
+ dev_err(dev, "Got an invalid msg type 0x%02x\n", gen_crq->type);
+ return;
+ }
+
+ switch (gen_crq->cmd) {
+ case VERSION_EXCHANGE_RSP:
+ if ((rc = crq->version_exchange_rsp.rc.code)) {
+ dev_err(dev, "Error %ld in VERSION_EXCHG_RSP\n", rc);
+ break;
+ }
+ dev_info(dev, "Partner protocol version is %d\n",
+ crq->version_exchange_rsp.version);
+ if (crq->version_exchange_rsp.version < vnic_version)
+ vnic_version = crq->version_exchange_rsp.version;
+ send_cap_queries(adapter);
+ break;
+ case QUERY_CAPABILITY_RSP:
+ handle_query_cap_rsp(crq, adapter);
+ break;
+ case REQUEST_CAPABILITY_RSP:
+ handle_request_cap_rsp(crq, adapter);
+ break;
+ case LOGIN_RSP:
+ vnic_dbg(adapter, "Got Login Response\n");
+ handle_login_rsp(crq, adapter);
+ break;
+ case LOGICAL_LINK_STATE_RSP:
+ vnic_dbg(adapter, "Got Logical Link State Response\n");
+ adapter->logical_link_state =
+ crq->logical_link_state_rsp.link_state;
+ break;
+ case LINK_STATE_INDICATION:
+ vnic_dbg(adapter, "Got Logical Link State Indication\n");
+ adapter->phys_link_state =
+ crq->link_state_indication.phys_link_state;
+ adapter->logical_link_state =
+ crq->link_state_indication.logical_link_state;
+ break;
+ case CHANGE_MAC_ADDR_RSP:
+ vnic_dbg(adapter, "Got MAC address change Response\n");
+ handle_change_mac_rsp(crq, adapter);
+ break;
+ case ERROR_INDICATION:
+ vnic_dbg(adapter, "Got Error Indication\n");
+ handle_error_indication(crq, adapter);
+ break;
+ case REQUEST_ERROR_RSP:
+ vnic_dbg(adapter, "Got Error Detail Response\n");
+ handle_error_info_rsp(crq, adapter);
+ break;
+ case REQUEST_STATISTICS_RSP:
+ vnic_dbg(adapter, "Got Statistics Response\n");
+ complete(&adapter->stats_done);
+ break;
+ case REQUEST_DUMP_SIZE_RSP:
+ vnic_dbg(adapter, "Got Request Dump Size Response\n");
+ handle_dump_size_rsp(crq, adapter);
+ break;
+ case REQUEST_DUMP_RSP:
+ vnic_dbg(adapter, "Got Request Dump Response\n");
+ complete(&adapter->fw_done);
+ break;
+ case QUERY_IP_OFFLOAD_RSP:
+ vnic_dbg(adapter, "Got Query IP offload Response\n");
+ handle_query_ip_offload_rsp(adapter);
+ break;
+ case MULTICAST_CTRL_RSP:
+ vnic_dbg(adapter, "Got multicast control Response\n");
+ break;
+ case CONTROL_IP_OFFLOAD_RSP:
+ vnic_dbg(adapter, "Got Control IP offload Response\n");
+ dma_unmap_single(dev, adapter->ip_offload_ctrl_tok,
+ sizeof(adapter->ip_offload_ctrl),
+ DMA_TO_DEVICE);
+ /* We're done with the queries, perform the login */
+ send_login(adapter);
+ break;
+ case REQUEST_RAS_COMP_NUM_RSP:
+ vnic_dbg(adapter, "Got Request RAS Comp Num Response\n");
+ adapter->ras_comp_num =
+ crq->request_ras_comp_num_rsp.num_components;
+ handle_request_ras_comp_num_rsp(crq, adapter);
+ break;
+ case REQUEST_RAS_COMPS_RSP:
+ vnic_dbg(adapter, "Got Request RAS Comps Response\n");
+ handle_request_ras_comps_rsp(crq, adapter);
+ break;
+ case CONTROL_RAS_RSP:
+ vnic_dbg(adapter, "Got Control RAS Response\n");
+ handle_control_ras_rsp(crq, adapter);
+ break;
+ case COLLECT_FW_TRACE_RSP:
+ vnic_dbg(adapter, "Got Collect firmware trace Response\n");
+ complete(&adapter->fw_done);
+ break;
+ default:
+ dev_err(dev, "Got an invalid cmd type 0x%02x\n", gen_crq->cmd);
+ }
+}
+
+static irqreturn_t vnic_interrupt(int irq, void *instance)
+{
+ struct vnic_adapter *adapter = instance;
+ struct vio_dev *vdev = adapter->vdev;
+ union vnic_crq *crq;
+ int done = 0;
+
+ vio_disable_interrupts(vdev);
+ while (!done) {
+ /* Pull all the valid messages off the CRQ */
+ while ((crq = vnic_next_crq(adapter)) != NULL) {
+ vnic_handle_crq(crq, adapter);
+ crq->generic.type = 0;
+ }
+ vio_enable_interrupts(vdev);
+ if ((crq = vnic_next_crq(adapter)) != NULL) {
+ vio_disable_interrupts(vdev);
+ vnic_handle_crq(crq, adapter);
+ crq->generic.type = 0;
+ } else
+ done = 1;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int vnic_reenable_crq_queue(struct vnic_adapter *adapter)
+{
+ int rc;
+ struct vio_dev *vdev = adapter->vdev;
+
+ do {
+ rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address);
+ } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+ if (rc)
+ dev_err(&vdev->dev, "Error enabling adapter (rc=%d)\n", rc);
+
+ return rc;
+}
+
+
+static int vnic_reset_crq(struct vnic_adapter *adapter)
+{
+ int rc;
+ struct vio_dev *vdev = adapter->vdev;
+ struct vnic_crq_queue *crq = &adapter->crq;
+ struct device *dev = &adapter->vdev->dev;
+
+ /* Close the CRQ */
+ do {
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+ /* Clean out the queue */
+ memset(crq->msgs, 0, PAGE_SIZE);
+ crq->cur = 0;
+
+ /* And re-open it again */
+ rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
+ crq->msg_token, PAGE_SIZE);
+
+ if (rc == H_CLOSED)
+ /* Adapter is good, but other end is not ready */
+ dev_warn(dev, "Partner adapter not ready\n");
+ else if (rc != 0)
+ dev_warn(dev, "Couldn't register crq (rc=%d)\n", rc);
+
+ return rc;
+}
+
+static void vnic_release_crq_queue(struct vnic_adapter *adapter)
+{
+ long rc;
+ struct vio_dev *vdev = adapter->vdev;
+ struct vnic_crq_queue *crq = &adapter->crq;
+
+ vnic_dbg(adapter, "Releasing CRQ\n");
+ free_irq(vdev->irq, adapter);
+ do {
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+ dma_unmap_single(&vdev->dev, crq->msg_token, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ free_page((unsigned long)crq->msgs);
+}
+
+
+static int vnic_init_crq_queue(struct vnic_adapter *adapter)
+{
+ int rc, retrc = -ENOMEM;
+ struct vnic_crq_queue *crq = &adapter->crq;
+ struct vio_dev *vdev = adapter->vdev;
+ struct device *dev = &adapter->vdev->dev;
+
+ ENTER;
+ crq->msgs = (union vnic_crq *)get_zeroed_page(GFP_KERNEL);
+ /* Should we allocate more than one page? */
+
+ if (!crq->msgs)
+ return -ENOMEM;
+
+ crq->size = PAGE_SIZE / sizeof(*crq->msgs);
+ crq->msg_token = dma_map_single(dev, crq->msgs, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, crq->msg_token))
+ goto map_failed;
+
+ retrc = rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
+ crq->msg_token, PAGE_SIZE);
+
+ if (rc == H_RESOURCE)
+ /* maybe kexecing and resource is busy. try a reset */
+ retrc = rc = vnic_reset_crq(adapter);
+
+ if (rc == H_CLOSED)
+ dev_warn(dev, "Partner adapter not ready\n");
+ else if (rc) {
+ dev_warn(dev, "Error %d opening adapter\n", rc);
+ goto reg_crq_failed;
+ }
+
+ retrc = 0;
+
+ vnic_dbg(adapter, "registering irq 0x%x\n", vdev->irq);
+ rc = request_irq(vdev->irq, vnic_interrupt, 0, VNIC_NAME, adapter);
+ if (rc) {
+ dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n", vdev->irq,
+ rc);
+ goto req_irq_failed;
+ }
+
+ if ((rc = vio_enable_interrupts(vdev))) {
+ dev_err(dev, "Error %d enabling interrupts\n", rc);
+ goto req_irq_failed;
+ }
+
+
+ crq->cur = 0;
+ spin_lock_init(&crq->lock);
+
+ LEAVE;
+ return retrc;
+
+req_irq_failed:
+ do {
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+reg_crq_failed:
+ dma_unmap_single(dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
+map_failed:
+ free_page((unsigned long)crq->msgs);
+ return retrc;
+
+}
+
+/* debugfs for dump */
+static int vnic_dump_show(struct seq_file *seq, void *v)
+{
+ struct net_device *netdev = seq->private;
+ struct vnic_adapter *adapter = netdev_priv(netdev);
+ struct device* dev = &adapter->vdev->dev;
+ union vnic_crq crq;
+
+ ENTER;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_dump_size.type = VNIC_CRQ_CMD;
+ crq.request_dump_size.cmd = REQUEST_DUMP_SIZE;
+ vnic_send_crq(adapter, &crq);
+
+ init_completion(&adapter->fw_done);
+ wait_for_completion(&adapter->fw_done);
+
+ seq_write(seq, adapter->dump_data, adapter->dump_data_size);
+
+ dma_unmap_single(dev, adapter->dump_data_token, adapter->dump_data_size,
+ DMA_BIDIRECTIONAL);
+
+ kfree(adapter->dump_data);
+
+ LEAVE;
+
+ return 0;
+}
+
+static int vnic_dump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, vnic_dump_show, inode->i_private);
+}
+
+static const struct file_operations vnic_dump_ops = {
+ .owner = THIS_MODULE,
+ .open = vnic_dump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int vnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
+{
+ struct net_device *netdev;
+ struct vnic_adapter *adapter;
+ unsigned char *mac_addr_p;
+ struct dentry *ent;
+ char buf[16];
+ int rc;
+
+ dev_dbg(&dev->dev, "entering vnic_probe for UA 0x%x\n", dev->unit_address);
+
+ mac_addr_p = (unsigned char *) vio_get_attribute(dev,
+ VETH_MAC_ADDR, NULL);
+ if(!mac_addr_p) {
+ dev_err(&dev->dev, "(%s:%3.3d) ERROR: Can't find MAC_ADDR "
+ "attribute\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ netdev = alloc_etherdev_mq(sizeof(struct vnic_adapter),
+ vnic_max_tx_queues);
+
+ if(!netdev)
+ return -ENOMEM;
+
+ adapter = netdev_priv(netdev);
+ dev_set_drvdata(&dev->dev, netdev);
+ adapter->vdev = dev;
+ adapter->netdev = netdev;
+
+ memcpy(&adapter->mac_addr, mac_addr_p, netdev->addr_len);
+ memcpy(netdev->dev_addr, &adapter->mac_addr, netdev->addr_len);
+ netdev->irq = dev->irq;
+ netdev->netdev_ops = &vnic_netdev_ops;
+ netdev->ethtool_ops = &vnic_ethtool_ops;
+ SET_NETDEV_DEV(netdev, &dev->dev);
+ spin_lock_init(&adapter->stats_lock);
+
+ if ((rc = vnic_init_crq_queue(adapter))) {
+ dev_err(&dev->dev, "Couldn't initialize crq. rc=%d\n", rc);
+ goto free_netdev;
+ }
+
+ INIT_LIST_HEAD(&adapter->errors);
+ INIT_LIST_HEAD(&adapter->inflight);
+ spin_lock_init(&adapter->error_list_lock);
+ spin_lock_init(&adapter->inflight_lock);
+
+ adapter->stats_token = dma_map_single(&dev->dev, &adapter->stats,
+ sizeof(struct vnic_statistics),
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&dev->dev, adapter->stats_token)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(&dev->dev, "Couldn't map stats buffer\n");
+ goto free_crq;
+ }
+
+ snprintf(buf, sizeof buf, "vnic_%x", dev->unit_address);
+ ent = debugfs_create_dir(buf, NULL);
+ if (!ent || IS_ERR(ent)) {
+ dev_info(&dev->dev, "debugfs create directory failed\n");
+ adapter->debugfs_dir = NULL;
+ } else {
+ adapter->debugfs_dir = ent;
+ ent = debugfs_create_file("dump", S_IRUGO, adapter->debugfs_dir,
+ netdev, &vnic_dump_ops);
+ if (!ent || IS_ERR(ent)) {
+ dev_info(&dev->dev, "debugfs create dump file failed\n");
+ adapter->debugfs_dump = NULL;
+ }
+ else
+ adapter->debugfs_dump = ent;
+ }
+ vnic_send_crq_init(adapter);
+
+ init_completion(&adapter->init_done);
+ wait_for_completion(&adapter->init_done);
+
+ netdev->real_num_tx_queues = adapter->req_tx_queues;
+
+ rc = register_netdev(netdev);
+ if(rc) {
+ dev_err(&dev->dev, "failed to register netdev rc=%d\n", rc);
+ goto free_debugfs;
+ }
+ dev_info(&dev->dev, "vnic registered\n");
+
+ return 0;
+
+free_debugfs:
+ if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir))
+ debugfs_remove_recursive(adapter->debugfs_dir);
+free_crq:
+ vnic_release_crq_queue(adapter);
+free_netdev:
+ free_netdev(netdev);
+ return rc;
+}
+
+static int vnic_remove(struct vio_dev *dev)
+{
+ struct net_device *netdev = dev_get_drvdata(&dev->dev);
+ struct vnic_adapter *adapter = netdev_priv(netdev);
+
+ ENTER;
+
+ unregister_netdev(netdev);
+
+ release_sub_crqs(adapter);
+
+ vnic_release_crq_queue(adapter);
+
+ if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir))
+ debugfs_remove_recursive(adapter->debugfs_dir);
+
+ if (adapter->ras_comps)
+ dma_free_coherent(&dev->dev,
+ adapter->ras_comp_num *
+ sizeof(struct vnic_fw_component),
+ adapter->ras_comps, adapter->ras_comps_tok);
+
+ if (adapter->ras_comp_int)
+ kfree(adapter->ras_comp_int);
+
+ free_netdev(netdev);
+ dev_set_drvdata(&dev->dev, NULL);
+
+ LEAVE;
+
+ return 0;
+}
+
+static unsigned long vnic_get_desired_dma(struct vio_dev *vdev)
+{
+ struct net_device *netdev = dev_get_drvdata(&vdev->dev);
+ struct vnic_adapter *adapter;
+ unsigned long ret = 0;
+ struct iommu_table *tbl;
+ int i;
+
+ tbl = get_iommu_table_base(&vdev->dev);
+
+ /* netdev inits at probe time along with the structures we need below*/
+ if (netdev == NULL)
+ return IOMMU_PAGE_ALIGN(VMIC_IO_ENTITLEMENT_DEFAULT, tbl);
+
+ adapter = netdev_priv(netdev);
+
+ ret += PAGE_SIZE; /* the crq message queue */
+ ret += adapter->bounce_buffer_size;
+ ret += IOMMU_PAGE_ALIGN(sizeof(struct vnic_statistics), tbl);
+
+ for (i = 0; i < adapter->req_tx_queues + adapter->req_rx_queues; i++)
+ ret += PAGE_SIZE; /* the scrq message queue */
+
+ for (i = 0; i < adapter->login_rsp_buf->num_rxadd_subcrqs; i++)
+ ret +=
+ adapter->rx_pool[i].size *
+ IOMMU_PAGE_ALIGN(adapter->rx_pool[i].buff_size, tbl);
+
+ return ret;
+}
+
+static int vnic_resume(struct device *dev)
+{
+ struct net_device *netdev = dev_get_drvdata(dev);
+ struct vnic_adapter *adapter = netdev_priv(netdev);
+ int i;
+
+ /* kick the interrupt handlers just in case we lost an interrupt */
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ vnic_interrupt_rx(adapter->rx_scrq[i]->irq,
+ adapter->rx_scrq[i]);
+
+ return 0;
+}
+
+static struct vio_device_id vnic_device_table[] = {
+ { "network", "IBM,vnic"},
+ { "", "" }
+};
+MODULE_DEVICE_TABLE(vio, vnic_device_table);
+
+static struct dev_pm_ops vnic_pm_ops = {
+ .resume = vnic_resume
+};
+
+static struct vio_driver vnic_driver = {
+ .id_table = vnic_device_table,
+ .probe = vnic_probe,
+ .remove = vnic_remove,
+ .get_desired_dma = vnic_get_desired_dma,
+ .driver = {
+ .name = vnic_driver_name,
+ .owner = THIS_MODULE,
+ .pm = &vnic_pm_ops,
+ }
+};
+
+
+/*
+ * module functions
+*/
+
+static int __init vnic_module_init(void)
+{
+ printk(KERN_INFO "%s: %s %s\n", vnic_driver_name, vnic_driver_string, VNIC_DRIVER_VERSION);
+
+ return vio_register_driver(&vnic_driver);
+}
+
+static void __exit vnic_module_exit(void)
+{
+ vio_unregister_driver(&vnic_driver);
+}
+
+module_init(vnic_module_init);
+module_exit(vnic_module_exit);
--- a/arch/powerpc/include/asm/hvcall.h 2014-03-30 22:40:15.000000000 -0500
+++ b/arch/powerpc/include/asm/hvcall.h 2014-04-07 09:45:10.000000000 -0500
@@ -258,11 +258,16 @@
#define H_DEL_CONN 0x288
#define H_JOIN 0x298
#define H_VASI_STATE 0x2A4
+#define H_VIOCTL 0x2A8
#define H_ENABLE_CRQ 0x2B0
#define H_GET_EM_PARMS 0x2B8
#define H_SET_MPP 0x2D0
#define H_GET_MPP 0x2D4
+#define H_REG_SUB_CRQ 0x2DC
#define H_HOME_NODE_ASSOCIATIVITY 0x2EC
+#define H_FREE_SUB_CRQ 0x2E0
+#define H_SEND_SUB_CRQ 0x2E4
+#define H_SEND_SUB_CRQ_INDIRECT 0x2E8
#define H_BEST_ENERGY 0x2F4
#define H_XIRR_X 0x2FC
#define H_RANDOM 0x300
@@ -271,6 +276,21 @@
#define H_SET_MODE 0x31C
#define MAX_HCALL_OPCODE H_SET_MODE
+/* H_VIOCTL functions */
+#define GET_VIOA_DUMP_SIZE 0x01
+#define GET_VIOA_DUMP 0x02
+#define GET_ILLAN_NUM_VLAN_IDS 0x03
+#define GET_ILLAN_VLAN_ID_LIST 0x04
+#define GET_ILLAN_SWITCH_ID 0x05
+#define DISABLE_MIGRATION 0x06
+#define ENABLE_MIGRATION 0x07
+#define GET_PARTNER_INFO 0x08
+#define GET_PARTNER_WWPN_LIST 0x09
+#define DISABLE_ALL_VIO_INTS 0x0A
+#define DISABLE_VIO_INTERRUPT 0x0B
+#define ENABLE_VIO_INTERRUPT 0x0C
+
+
/* Platform specific hcalls, used by KVM */
#define H_RTAS 0xf000
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists