New driver for IBM System i/p VNIC protocol
Subsystems (3)
PATCH 1/1 New driver for IBM System i/p VNIC protocol
This is a new device driver for a high performance SR-IOV assisted virtual network for IBM System p and IBM System i systems. The SR-IOV VF will be attached to the VIOS partition and mapped to the Linux client via the hypervisors VNIC protocol that this driver implements.
This driver is able to perform basic tx and rx, new features and improvements will be added as they are being developed and tested.
--- 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(+)
drivers/net/ethernet/ibm/Kconfig +10 -0
@@ -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
drivers/net/ethernet/ibm/Makefile +1 -0
@@ -3,5 +3,6 @@ # obj-$(CONFIG_IBMVETH) += ibmveth.o +obj-$(CONFIG_VNIC) += vnic.o obj-$(CONFIG_IBM_EMAC) += emac/ obj-$(CONFIG_EHEA) += ehea/
drivers/net/ethernet/ibm/vnic.h +947 -0
947 lines not on this page · raw message
drivers/net/ethernet/ibm/vnic.c +3594 -0
2,033 lines not on this page · raw message
arch/powerpc/include/asm/hvcall.h +20 -0
Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.
Quoted 1 line · Santiago Leon
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
Quoted 4,633 lines
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 <[email protected]>--- 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(+)drivers/net/ethernet/ibm/Kconfig +10
-0@@ -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_IBMdrivers/net/ethernet/ibm/Makefile +1
-0@@ -3,5 +3,6 @@ # obj-$(CONFIG_IBMVETH) += ibmveth.o +obj-$(CONFIG_VNIC) += vnic.o obj-$(CONFIG_IBM_EMAC) += emac/ obj-$(CONFIG_EHEA) += ehea/drivers/net/ethernet/ibm/vnic.h +947
-0@@ -0,0 +1,947 @@ +/**************************************************************************/ +/* */ +/* IBM System i and System p Virtual NIC Device Driver */ +/* Copyright (C) 2003 IBM Corp. */ +/* Santiago Leon ([email protected]) */ +/* */ +/* 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__)) +drivers/net/ethernet/ibm/vnic.c +3594
-0@@ -0,0 +1,3594 @@ +/**************************************************************************/ +/* */ +/* IBM System i and System p Virtual NIC Device Driver */ +/* Copyright (C) 2003 IBM Corp. */ +/* Santiago Leon ([email protected]) */ +/* */ +/* 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 <[email protected]>" ); +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 ;Lines of this file are not shown. View the raw message.
arch/powerpc/include/asm/hvcall.h +20
-1Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Part of the diff is not shown here. View the raw message.
David Miller
maintainerFrom: Santiago Leon <[email protected]>
Date: Mon, 07 Apr 2014 13:10:22 -0500
Quoted 3 lines
This is a new device driver for a high performance SR-IOV assisted virtual network for IBM System p and IBM System i systems. The SR-IOV VF will be attached to the VIOS partition and mapped to the Linux client via the hypervisors VNIC protocol that this driver implements.
This driver is able to perform basic tx and rx, new features and improvements will be added as they are being developed and tested.
Please format your commit message to ~80 columns, it is ascii text and just because your mail client formats it automatically doesn't mean it will do so when this gets committed into the tree.
drivers/net/ethernet/ibm/vnic.h line 54
Quoted 11 lines
drivers/net/ethernet/ibm/vnic.h +11
-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 )));
What are the endianness of these buffers? Use the appropriate big-endian or little-endian types as needed. Compile with sparse warnings enabled to make sure all accesses to these members use the proper conversion interfaces.
drivers/net/ethernet/ibm/vnic.h line 749
Quoted 4 lines
drivers/net/ethernet/ibm/vnic.h +4
-0+}; + + +struct vnic_crq_queue {
Please only put one empty line instead of two between declarations, functions, etc. Please audit your entire submission for this problem.
drivers/net/ethernet/ibm/vnic.h line 915
Quoted 3 lines
drivers/net/ethernet/ibm/vnic.h +3
-0+/* partner capabilities */ +long min_tx_queues; +long min_rx_queues;
Is this a fixed type? If so use u32, u64, or similar instead of 'long' which is a variably sized type.
drivers/net/ethernet/ibm/vnic.h line 946
Quoted 5 lines
drivers/net/ethernet/ibm/vnic.h +5
-0+#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__))
Please, absolutely, do not define your own debugging facilities.
The kernel has a plethora of debugging facilities that can satisfy any objective you are striving for.
drivers/net/ethernet/ibm/vnic.c line 82
Quoted 3 lines
drivers/net/ethernet/ibm/vnic.c +3
-0+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" );
Do not define module parameters for these kinds of things, it is the worst user experience possible. Instead use existing, or newly created, facilities via ethtool or other network configuration interfaces to tweak these values.
drivers/net/ethernet/ibm/vnic.c line 151
Quoted 3 lines
drivers/net/ethernet/ibm/vnic.c +3
-0+static long h_reg_sub_crq (unsigned long unit_address,unsigned long token, +unsigned long length,unsigned long *number, +unsigned long *irq)
Arguments appearing on the second and subsequent lines of function declarations, definitions, and call sites, shall start precisely at the first column after the openning parenthesis on the first line.
You should use the appropriate number of TAB and space characters necessary to achieve this.
drivers/net/ethernet/ibm/vnic.c line 223
Quoted 3 lines
drivers/net/ethernet/ibm/vnic.c +3
-0+u64 * handle_array = + (u64 *)((u8 *)(adapter->login_rsp_buf) + + adapter->login_rsp_buf->off_rxadd_subcrqs);
Pointers are to be declared as "type *", not "type* ".
drivers/net/ethernet/ibm/vnic.c line 226
Quoted 2 lines
drivers/net/ethernet/ibm/vnic.c +2
-0+mb (); +for (i =0 ; i < count; ++i) {
What kind of memory barrier is really necessary here? What actions must complete before the memory barrier, and what operations after the memory barrier depend upon them.
Any memory barrier without a detailed comment explaining the constraints is a bug.
drivers/net/ethernet/ibm/vnic.c line 303
Quoted 1 line
drivers/net/ethernet/ibm/vnic.c +1
-0+if (pool->rx_buff) {
There must be a between "if" and "(".
drivers/net/ethernet/ibm/vnic.c line 329
Quoted 12 lines
drivers/net/ethernet/ibm/vnic.c +12
-0+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);
Please to not put empty lines in the middle of a series of variable declarations.
An empty line should only occur before the first line of actual code and after the last local variable declaration.
Review
Quoted 1 line
(quoted diff) +1
-0+ ENTER;
As mentioned, please get rid of these custom debugging hacks.
drivers/net/ethernet/ibm/vnic.c line 335
Quoted 3 lines
drivers/net/ethernet/ibm/vnic.c +3
-0+ adapter->napi = +kmalloc (sizeof (struct napi_struct ) * adapter->req_rx_queues, +GFP_KERNEL );
Please us kcalloc().
And that's all I have time for today, you can expect that it will take several revisions of review before this driver is in a state where it can actually be intergrated.
Davem, thanks for your input. Here's a new version with all your comments (including clean sparse checking of endianness).
This is a new device driver for a high performance SR-IOV assisted virtual network for IBM System p and IBM System i systems. The SR-IOV VF will be attached to the VIOS partition and mapped to the Linux client via the hypervisors VNIC protocol that this driver implements.
This driver is able to perform basic tx and rx, new features and improvements will be added as they are being developed and tested.
--- 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 | 3588 ++++++++++++++++++++++++++++++++++++++ drivers/net/ethernet/ibm/vnic.h | 937 +++++++++ 5 files changed, 4556 insertions(+)
drivers/net/ethernet/ibm/Kconfig +10 -0
@@ -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
drivers/net/ethernet/ibm/Makefile +1 -0
@@ -3,5 +3,6 @@ # obj-$(CONFIG_IBMVETH) += ibmveth.o +obj-$(CONFIG_VNIC) += vnic.o obj-$(CONFIG_IBM_EMAC) += emac/ obj-$(CONFIG_EHEA) += ehea/
drivers/net/ethernet/ibm/vnic.h +937 -0
937 lines not on this page · raw message
drivers/net/ethernet/ibm/vnic.c +3588 -0
2,043 lines not on this page · raw message
arch/powerpc/include/asm/hvcall.h +20 -0
Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.