lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Mon, 07 Apr 2014 13:29:59 -0500
From:	Dan Williams <dcbw@...hat.com>
To:	Santiago Leon <santil@...ux.vnet.ibm.com>
Cc:	netdev@...r.kernel.org
Subject: Re: [PATCH] New driver for IBM System i/p VNIC protocol

On Mon, 2014-04-07 at 13:10 -0500, Santiago Leon wrote:
> 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. 

Should this driver implement .ndo_get_phys_port_id so that userspace is
aware which VFs are attached to the same PF?

Dan

> 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


--
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

Powered by Openwall GNU/*/Linux - Powered by OpenVZ