From 52ad325e23810908e3aafa2312dbbd08cacdf0bf Mon Sep 17 00:00:00 2001 From: ftillier Date: Fri, 6 Oct 2006 20:26:00 +0000 Subject: [PATCH] [VNIC] Initial checkin of VNIC code. Not yet fully functional. git-svn-id: svn://openib.tc.cornell.edu/gen1@515 ad392aa1-c5ef-ae45-8dd8-e69d62a5ef86 --- trunk/ulp/dirs | 3 +- trunk/ulp/inic/dirs | 2 + trunk/ulp/inic/kernel/SOURCES | 75 + trunk/ulp/inic/kernel/inic.rc | 47 + trunk/ulp/inic/kernel/makefile | 7 + trunk/ulp/inic/kernel/netvnic.inf | 146 ++ trunk/ulp/inic/kernel/vnic_adapter.c | 1007 ++++++++++++ trunk/ulp/inic/kernel/vnic_adapter.h | 216 +++ trunk/ulp/inic/kernel/vnic_config.h | 238 +++ trunk/ulp/inic/kernel/vnic_control.c | 1980 +++++++++++++++++++++++ trunk/ulp/inic/kernel/vnic_control.h | 123 ++ trunk/ulp/inic/kernel/vnic_controlpkt.h | 284 ++++ trunk/ulp/inic/kernel/vnic_data.c | 1383 ++++++++++++++++ trunk/ulp/inic/kernel/vnic_data.h | 204 +++ trunk/ulp/inic/kernel/vnic_debug.h | 106 ++ trunk/ulp/inic/kernel/vnic_driver.c | 1877 +++++++++++++++++++++ trunk/ulp/inic/kernel/vnic_driver.h | 187 +++ trunk/ulp/inic/kernel/vnic_ib.c | 900 +++++++++++ trunk/ulp/inic/kernel/vnic_ib.h | 209 +++ trunk/ulp/inic/kernel/vnic_netpath.c | 237 +++ trunk/ulp/inic/kernel/vnic_trailer.h | 107 ++ trunk/ulp/inic/kernel/vnic_util.h | 82 + trunk/ulp/inic/kernel/vnic_viport.c | 1025 ++++++++++++ trunk/ulp/inic/kernel/vnic_viport.h | 286 ++++ 24 files changed, 10730 insertions(+), 1 deletion(-) create mode 100644 trunk/ulp/inic/dirs create mode 100644 trunk/ulp/inic/kernel/SOURCES create mode 100644 trunk/ulp/inic/kernel/inic.rc create mode 100644 trunk/ulp/inic/kernel/makefile create mode 100644 trunk/ulp/inic/kernel/netvnic.inf create mode 100644 trunk/ulp/inic/kernel/vnic_adapter.c create mode 100644 trunk/ulp/inic/kernel/vnic_adapter.h create mode 100644 trunk/ulp/inic/kernel/vnic_config.h create mode 100644 trunk/ulp/inic/kernel/vnic_control.c create mode 100644 trunk/ulp/inic/kernel/vnic_control.h create mode 100644 trunk/ulp/inic/kernel/vnic_controlpkt.h create mode 100644 trunk/ulp/inic/kernel/vnic_data.c create mode 100644 trunk/ulp/inic/kernel/vnic_data.h create mode 100644 trunk/ulp/inic/kernel/vnic_debug.h create mode 100644 trunk/ulp/inic/kernel/vnic_driver.c create mode 100644 trunk/ulp/inic/kernel/vnic_driver.h create mode 100644 trunk/ulp/inic/kernel/vnic_ib.c create mode 100644 trunk/ulp/inic/kernel/vnic_ib.h create mode 100644 trunk/ulp/inic/kernel/vnic_netpath.c create mode 100644 trunk/ulp/inic/kernel/vnic_trailer.h create mode 100644 trunk/ulp/inic/kernel/vnic_util.h create mode 100644 trunk/ulp/inic/kernel/vnic_viport.c create mode 100644 trunk/ulp/inic/kernel/vnic_viport.h diff --git a/trunk/ulp/dirs b/trunk/ulp/dirs index 87be0553..e5ba0c88 100644 --- a/trunk/ulp/dirs +++ b/trunk/ulp/dirs @@ -3,4 +3,5 @@ DIRS=\ dapl \ ipoib \ srp \ - wsd + wsd \ + inic diff --git a/trunk/ulp/inic/dirs b/trunk/ulp/inic/dirs new file mode 100644 index 00000000..ed41dcf4 --- /dev/null +++ b/trunk/ulp/inic/dirs @@ -0,0 +1,2 @@ +DIRS=\ + kernel diff --git a/trunk/ulp/inic/kernel/SOURCES b/trunk/ulp/inic/kernel/SOURCES new file mode 100644 index 00000000..d56ea8cf --- /dev/null +++ b/trunk/ulp/inic/kernel/SOURCES @@ -0,0 +1,75 @@ +######################################################################## +# +# Copyright(c) Infinicon Systems All rights reserved. +# +######################################################################## + + +# The TARGETNAME. This is name of the item being built (without the +# extension. +TARGETNAME=vnic + +######################################################################## +# The path where all binaries are built. +# +TARGETPATH=..\..\..\bin\kernel\obj$(BUILD_ALT_DIR) + +######################################################################## +# The type of item that is being built. This is manditory. +# Value Meaning +# DYNLINK - A DLL. +# DRIVER - A kernel device driver. +# EXPORT_DRIVER - A kernel device driver with exports. +# PROGRAM - A windows executable. +# PROGLIB - A windows library. +# MINPORT - A miniport driver. +# GDI_DRIVER - A video driver. +# LIBRARY - A library +TARGETTYPE=MINIPORT + +######################################################################## +# The type of driver being built. This affects the include paths. +# Comment out for non-WDM drivers. +#DRIVERTYPE=WDM + +# +######################################################################## +# All the source files in this project. +# +SOURCES= inic.rc \ + vnic_driver.c \ + vnic_adapter.c \ + vnic_ib.c \ + vnic_control.c \ + vnic_data.c \ + vnic_netpath.c \ + vnic_viport.c \ + + + + +INCLUDES=..;..\..\..\inc;..\..\..\inc\kernel; + +C_DEFINES=$(C_DEFINES) -DNDIS_MINIPORT_DRIVER -DNDIS_WDM=1 \ + -DDEPRECATE_DDK_FUNCTIONS -DNDIS51_MINIPORT -DNEED_CL_OBJ -DBINARY_COMPATIBLE=0 + +TARGETLIBS= \ + $(DDK_LIB_PATH)\ntoskrnl.lib \ + $(DDK_LIB_PATH)\hal.lib \ + $(DDK_LIB_PATH)\ndis.lib \ + $(TARGETPATH)\*\complib.lib + +#!if !defined(DDK_TARGET_OS) || "$(DDK_TARGET_OS)"=="Win2K" +# +# The driver is built in the Win2K build environment +# - use the library version of safe strings +# +#TARGETLIBS= $(TARGETLIBS) $(DDK_LIB_PATH)\ntstrsafe.lib +#!endif + +######################################################################## +# Set the warning levels to maximum. +MSC_WARNING_LEVEL= /W4 +# + + diff --git a/trunk/ulp/inic/kernel/inic.rc b/trunk/ulp/inic/kernel/inic.rc new file mode 100644 index 00000000..66c5b30d --- /dev/null +++ b/trunk/ulp/inic/kernel/inic.rc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK + +#ifdef _DEBUG_ +#define VER_FILEDESCRIPTION_STR "Virtual Nic NDIS Miniport (Debug)" +#else +#define VER_FILEDESCRIPTION_STR "Virtual Nic NDIS Miniport" +#endif + +#define VER_INTERNALNAME_STR "vnic.sys" +#define VER_ORIGINALFILENAME_STR "vnic.sys" + +#include diff --git a/trunk/ulp/inic/kernel/makefile b/trunk/ulp/inic/kernel/makefile new file mode 100644 index 00000000..9c985f57 --- /dev/null +++ b/trunk/ulp/inic/kernel/makefile @@ -0,0 +1,7 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/trunk/ulp/inic/kernel/netvnic.inf b/trunk/ulp/inic/kernel/netvnic.inf new file mode 100644 index 00000000..a4f72d39 --- /dev/null +++ b/trunk/ulp/inic/kernel/netvnic.inf @@ -0,0 +1,146 @@ +; SilverStorm Technologies Ethernet over Infiniband NIC. +; Copyright 2006 SilverStorm Technologies all Rights Reserved. + +[Version] +Signature="$Windows NT$" +Class=Net +ClassGUID={4d36e972-e325-11ce-bfc1-08002be10318} +Provider=%Inf_Provider% +DriverVer=01/12/2006,3.0.0039.0 + +[ControlFlags] +ExcludeFromSelect = IBA\V00066aP00000030 + +; ================= Device Install section ===================== + +[DestinationDirs] +DefaultDestDir=12 + +[SourceDisksNames.x86] +1=%DiskId%,,,\x86 + +[SourceDisksNames.amd64] +1=%DiskId%,,,\amd64 + +[SourceDisksNames.ia64] +1=%DiskId%,,,\ia64 + +[SourceDisksFiles] +vnic.sys=1 + +[Manufacturer] +%Inf_Provider% = VNIC.DeviceSection,ntx86,ntamd64,ntia64 + +[VNIC.DeviceSection] +; empty since we don't support W9x/Me + +[VNIC.DeviceSection.ntx86] +%VNIC.DeviceDesc% = VNIC.DDInstall,IBA\V00066aP00000030S00066as00000030v0001, \ + IBA\V00066aP00000030S00066as00000030, \ + IBA\V00066aP00000030v0001, \ + IBA\V00066aP00000030 + +[VNIC.DeviceSection.ntamd64] +%VNIC.DeviceDesc% = VNIC.DDInstall,IBA\V00066aP00000030S00066as00000030v0001, \ + IBA\V00066aP00000030S00066as00000030, \ + IBA\V00066aP00000030v0001, \ + IBA\V00066aP00000030 + +[VNIC.DeviceSection.ntia64] +%VNIC.DeviceDesc% = VNIC.DDInstall,IBA\V00066aP00000030S00066as00000030v0001, \ + IBA\V00066aP00000030S00066as00000030, \ + IBA\V00066aP00000030v0001, \ + IBA\V00066aP00000030 + +[VNIC.DDInstall.ntx86] +Characteristics = %CHARACTERISTICS% +AddReg = VNIC.AddReg +CopyFiles = VNIC.CopyFiles + +[VNIC.DDInstall.ntamd64] +Characteristics = %CHARACTERISTICS% +AddReg = VNIC.AddReg +CopyFiles = VNIC.CopyFiles + +[VNIC.DDInstall.ntia64] +Characteristics = %CHARACTERISTICS% +AddReg = VNIC.AddReg +CopyFiles = VNIC.CopyFiles + +[VNIC.DDInstall.ntx86.Services] +AddService = vnic,%SPSVCINST_ASSOCSERVICE%,VNIC.ServiceInstall,VNIC.EventLogInstall + +[VNIC.DDInstall.ntamd64.Services] +AddService = vnic,%SPSVCINST_ASSOCSERVICE%,VNIC.ServiceInstall,VNIC.EventLogInstall + +[VNIC.DDInstall.ntia64.Services] +AddService = vnic,%SPSVCINST_ASSOCSERVICE%,VNIC.ServiceInstall,VNIC.EventLogInstall + +[VNIC.CopyFiles] +vnic.sys + +[VNIC.AddReg] +HKR, Ndi, Service, 0, "ipoib" +HKR, Ndi\Interfaces, UpperRange, 0, "ndis5" +HKR, Ndi\Interfaces, LowerRange, 0, "ethernet" + + +; ============= Service Install section ============== + +[VNIC.ServiceInstall] +DisplayName = %VNIC.ServiceDesc% +ServiceType = %SERVICE_KERNEL_DRIVER% +StartType = %SERVICE_DEMAND_START% +ErrorControl = %SERVICE_ERROR_NORMAL% +ServiceBinary = %12%\vnic.sys +LoadOrderGroup = NDIS +AddReg = VNIC.ParamsReg + +[VNIC.ParamsReg] +HKR,"Params\PnpInterface",%InternalBus%,%REG_DWORD%,1 +HKR,"Params\PnpInterface",%PNPBus%,%REG_DWORD%,1 + +HKR, Params, MinMtu,, "1500" +HKR, Params, MaxMtu,, "9500" +HKR, Params, MinHostPoolSz,, "64" +HKR, Params, HostRecvPoolEntries,, "256" +HKR, Params, MinEiocPoolSz,, "64" +HKR, Params, MaxEiocPoolSz,, "256" +HKR, Params, MinHostKickTimeout,, "50" +HKR, Params, MaxHostKickTimeout,, "200" +HKR, Params, MinHostKickEntries,, "1" +HKR, Params, MaxHostKickEntries,, "64" +HKR, Params, MinHostKickBytes,, "0" +HKR, Params, MaxHostKickBytes,, "5000" +HKR, Params, MinHostUpdateSz,, "8" +HKR, Params, MaxHostUpdateSz,, "32" +HKR, Params, MinEiocUpdateSz,, "8" +HKR, Params, MaxEiocUpdateSz,, "32" +HKR, Params, UseRxCsum, %REG_DWORD%,1 +HKR, Params, UseTxCsum, %REG_DWORD%,1 + +[VNIC.EventLogInstall] +AddReg = VNIC.EventLogAddReg + +[VNIC.EventLogAddReg] +HKR,,EventMessageFile,%REG_EXPAND_SZ%,"%%SystemRoot%%\System32\netevent.dll" +HKR,,TypesSupported,%REG_DWORD%,7 + +[Strings] +NetClassGuid = "{4d36e972-e325-11ce-bfc1-08002be10318}" +Inf_Provider = "SilverStorm Technologies" +VNIC.DeviceDesc = "Ethernet over InfiniBand Virtual NIC" +VNIC.ServiceDesc = "Virtual NIC" +DiskId = "SilverStorm Ethernet over InfiniBand Virtual NIC installation disk" +InternalBus = 0 +PNPBus = 15 +SPSVCINST_NULL = 0x0 +SPSVCINST_ASSOCSERVICE = 0x00000002 +SERVICE_KERNEL_DRIVER = 1 +SERVICE_DEMAND_START = 3 +SERVICE_ERROR_NORMAL = 1 +REG_DWORD = 0x00010001 +REG_DWORD_NO_CLOBBER = 0x00010003 +REG_EXPAND_SZ = 0x00020000 +CHARACTERISTICS = 0x81 ; NCF_VIRTUAL | NCF_HAS_UI + diff --git a/trunk/ulp/inic/kernel/vnic_adapter.c b/trunk/ulp/inic/kernel/vnic_adapter.c new file mode 100644 index 00000000..624a6b1a --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_adapter.c @@ -0,0 +1,1007 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + +#include +#include +#include +#include "vnic_adapter.h" + +//#include "vnic_driver.h" + +extern struct _vnic_globals g_vnic; + +NDIS_STATUS +vnic_get_adapter_params( + IN NDIS_HANDLE h_handle, + OUT vnic_params_t* const p_params ); + +NDIS_STATUS +vnic_get_adapter_interface( + IN NDIS_HANDLE h_handle, + IN vnic_adapter_t *p_adapter); + +static ib_api_status_t +__path_record_add( + IN vnic_adapter_t *p_adapter, + IN ib_path_rec_t *p_path_rec ); + +static ib_api_status_t +__path_record_remove( + IN vnic_adapter_t *p_adapter, + IN ib_path_rec_t *p_path_rec ); + +static BOOLEAN +__path_records_match( + IN ib_path_rec_t *p_path1, + IN ib_path_rec_t *p_path2 ); + +static void +__path_records_cleanup( + IN vnic_adapter_t *p_adapter ); + +static ib_api_status_t +_vnic_get_ca_info( + IN vnic_adapter_t *p_adapter ); + +static inline uint8_t +_get_ioc_num_from_iocguid( + IN ib_net64_t *p_iocguid ); + +static BOOLEAN +__sid_valid( + IN vnic_adapter_t *p_adapter, + IN ib_net64_t sid ); + +static void +_vnic_viport_free( + IN vnic_adapter_t* const p_adapter ); + +ib_api_status_t +vnic_create_adapter( + IN NDIS_HANDLE h_handle, + OUT vnic_adapter_t** const pp_adapter) +{ + NDIS_STATUS status; + ib_api_status_t ib_status; + vnic_adapter_t *p_adapter; + KIRQL irql; + + VNIC_ENTER( VNIC_DBG_ADAPTER ); + + status = NdisAllocateMemoryWithTag( &p_adapter, sizeof(vnic_adapter_t), 'pada'); + + if ( status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE_EXIT(VNIC_DBG_ERROR,("Failed to allocate adapter\n")); + return IB_INSUFFICIENT_MEMORY; + } + + NdisZeroMemory( p_adapter, sizeof(vnic_adapter_t) ); + + NdisAllocateSpinLock( &p_adapter->lock ); + + status = vnic_get_adapter_params( h_handle, &p_adapter->params ); + + if ( status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + (" vnic_get_adapter_params failed with status %d\n", status)); + NdisFreeMemory( p_adapter, sizeof(vnic_adapter_t), 0 ); + return IB_INVALID_PARAMETER; + } + + status = vnic_get_adapter_interface( h_handle, p_adapter ); + if ( status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("failed status %x\n", status ) ); + NdisFreeMemory( p_adapter, sizeof(vnic_adapter_t), 0 ); + return IB_INVALID_PARAMETER; + } + + /*Open AL */ + ib_status = p_adapter->ifc.open_al( &p_adapter->h_al ); + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("ib_open_al returned %s\n", p_adapter->ifc.get_err_str( ib_status )) ); + NdisFreeMemory( p_adapter, sizeof(vnic_adapter_t), 0 ); + return ib_status; + } + + /* ca is opened here */ + ib_status = _vnic_get_ca_info( p_adapter ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE ( VNIC_DBG_ERROR, + ("_get_ca_info return status %s\n", p_adapter->ifc.get_err_str( ib_status )) ); + NdisFreeMemory( p_adapter, sizeof(vnic_adapter_t), 0 ); + return ib_status; + } + + KeAcquireSpinLock( &g_vnic.lock, &irql ); + InsertTailList( &g_vnic.adapter_list, &p_adapter->list_entry ); + InterlockedIncrement( &g_vnic.adapters ); + KeReleaseSpinLock( &g_vnic.lock, irql ); + + p_adapter->h_handle = h_handle; + *pp_adapter = p_adapter; + + VNIC_EXIT( VNIC_DBG_ADAPTER ); + return IB_SUCCESS; +} + + +void +vnic_destroy_adapter( + IN vnic_adapter_t *p_adapter) +{ + ib_api_status_t ib_status = IB_SUCCESS; + KIRQL irql; + + VNIC_ENTER( VNIC_DBG_ADAPTER ); + + ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL ); + + KeAcquireSpinLock( &g_vnic.lock, &irql ); + RemoveEntryList( &p_adapter->list_entry ); + InterlockedDecrement( &g_vnic.adapters ); + KeReleaseSpinLock( &g_vnic.lock, irql ); + + if( p_adapter->h_pnp ) + { + ib_status = + p_adapter->ifc.dereg_pnp( p_adapter->h_pnp, ib_sync_destroy ); + } + + vnic_viport_cleanup( p_adapter ); + + if ( p_adapter->ca.region.h_mr ) + { + ib_status = p_adapter->ifc.dereg_mr(p_adapter->ca.region.h_mr ); + ASSERT( ib_status == IB_SUCCESS ); + } + + if( p_adapter->ca.hPd ) + { + ib_status = p_adapter->ifc.dealloc_pd( p_adapter->ca.hPd, NULL ); + ASSERT( ib_status == IB_SUCCESS ); + } + + if ( p_adapter->h_ca ) + { + ib_status = p_adapter->ifc.close_ca( p_adapter->h_ca, NULL ); + ASSERT( ib_status == IB_SUCCESS ); + } + + if ( p_adapter->h_al ) + { + ib_status = p_adapter->ifc.close_al( p_adapter->h_al ); + ASSERT( ib_status == IB_SUCCESS ); + } + + if ( p_adapter->p_svc_entries ) + cl_free ( p_adapter->p_svc_entries ); + + NdisFreeSpinLock( &p_adapter->lock ); + NdisFreeMemory( p_adapter, sizeof(vnic_adapter_t), 0 ); + + VNIC_EXIT( VNIC_DBG_ADAPTER ); +} + + +static ib_api_status_t +_vnic_get_ca_info( + IN vnic_adapter_t *p_adapter ) +{ + ib_api_status_t ib_status = IB_SUCCESS; + ib_al_ifc_t *p_ifc = &p_adapter->ifc; + uint32_t attr_size; + ib_ca_attr_t *p_ca_attrs; + uint32_t num; + uint64_t start_addr = 0; + + ib_status = p_ifc->open_ca( p_adapter->h_al, + p_adapter->ifc_data.ca_guid, + ib_asyncEvent, + p_adapter, + &p_adapter->h_ca ); + + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("Failed to open hca\n") ); + return IB_INSUFFICIENT_RESOURCES; + } + + ib_status = p_ifc->query_ca( p_adapter->h_ca, NULL , &attr_size ); + if( ib_status != IB_INSUFFICIENT_MEMORY ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("ib_query_ca failed status %s\n", + p_ifc->get_err_str( ib_status )) ); + return IB_INSUFFICIENT_RESOURCES; + } + + ASSERT( attr_size ); + + p_ca_attrs = cl_zalloc( attr_size ); + if ( p_ca_attrs == NULL ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("Allocate %d bytes failed for Channel adapter\n", attr_size )); + return IB_INSUFFICIENT_MEMORY; + } + + ib_status = p_ifc->query_ca( p_adapter->h_ca, p_ca_attrs , &attr_size ); + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("Query failed for channel adapter\n") ); + cl_free ( p_ca_attrs ); + return ib_status; + } + + p_adapter->ca.numPorts = p_ca_attrs->num_ports; + if( p_adapter->ca.numPorts > VNIC_CA_MAX_PORTS ) + p_adapter->ca.numPorts = VNIC_CA_MAX_PORTS; + + for( num = 0; num < p_adapter->ca.numPorts; num++ ) + p_adapter->ca.portGuids[num] = p_ca_attrs->p_port_attr[num].port_guid; + + p_adapter->ca.caGuid = p_adapter->ifc_data.ca_guid; + + cl_free ( p_ca_attrs ); + + ib_status = p_adapter->ifc.alloc_pd( p_adapter->h_ca, + IB_PDT_NORMAL, p_adapter, &p_adapter->ca.hPd ); + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT ( VNIC_DBG_ERROR, + ("alloc PD failed status %s(%d)\n", + p_adapter->ifc.get_err_str(ib_status), ib_status )); + return ib_status; + } + + if ( ( ib_status = ibregion_physInit( p_adapter, + &p_adapter->ca.region, + p_adapter->ca.hPd, + &start_addr, + MAX_PHYS_MEMORY ) ) != IB_SUCCESS ) + { + VNIC_TRACE_EXIT ( VNIC_DBG_ERROR, + ("phys region init failed status %s(%d)\n", + p_adapter->ifc.get_err_str(ib_status), ib_status )); + + p_adapter->ifc.dealloc_pd( p_adapter->ca.hPd, NULL ); + } + + return ib_status; +} + +static BOOLEAN +_vnic_params_sanity_check(vnic_params_t *p_params) +{ + DEFAULT_PARAM( p_params->MaxAddressEntries, MAX_ADDRESS_ENTRIES ); + DEFAULT_PARAM( p_params->MinAddressEntries, MIN_ADDRESS_ENTRIES ); + + DEFAULT_PARAM( p_params->ViportStatsInterval, VIPORT_STATS_INTERVAL ); + DEFAULT_PARAM( p_params->ViportHbInterval, VIPORT_HEARTBEAT_INTERVAL ); + DEFAULT_PARAM( p_params->ViportHbTimeout, VIPORT_HEARTBEAT_TIMEOUT ); + + DEFAULT_PARAM( p_params->ControlRspTimeout, CONTROL_RSP_TIMEOUT ); + DEFAULT_PARAM( p_params->ControlReqRetryCount, CONTROL_REQ_RETRY_COUNT ); + + DEFAULT_PARAM( p_params->RetryCount, RETRY_COUNT ); + DEFAULT_PARAM( p_params->MinRnrTimer, MIN_RNR_TIMER ); + + DEFAULT_PARAM( p_params->MaxViportsPerNetpath, MAX_VIPORTS_PER_NETPATH ); + DEFAULT_PARAM( p_params->DefaultViportsPerNetpath, DEFAULT_VIPORTS_PER_NETPATH ); + + DEFAULT_PARAM( p_params->DefaultPkey, DEFAULT_PKEY ); + DEFAULT_PARAM( p_params->NotifyBundleSz, NOTIFY_BUNDLE_SZ ); + DEFAULT_PARAM( p_params->DefaultNoPathTimeout, DEFAULT_NO_PATH_TIMEOUT ); + DEFAULT_PARAM( p_params->DefaultPrimaryConnectTimeout, DEFAULT_PRI_CON_TIMEOUT ); + DEFAULT_PARAM( p_params->DefaultPrimaryReconnectTimeout, DEFAULT_PRI_RECON_TIMEOUT ); + DEFAULT_PARAM( p_params->DefaultPrimarySwitchTimeout, DEFAULT_PRI_SWITCH_TIMEOUT ); + DEFAULT_PARAM( p_params->DefaultPreferPrimary, DEFAULT_PREFER_PRIMARY ); + + U32_RANGE( p_params->MaxAddressEntries ); + U32_RANGE( p_params->MinAddressEntries ); + RANGE_CHECK( p_params->MinMtu, MIN_MTU, MAX_MTU ); + RANGE_CHECK( p_params->MaxMtu, MIN_MTU, MAX_MTU ); + U32_RANGE( p_params->HostRecvPoolEntries ); + U32_RANGE( p_params->MinHostPoolSz ); + U32_RANGE( p_params->MinEiocPoolSz ); + U32_RANGE( p_params->MaxEiocPoolSz ); + U32_ZERO_RANGE( p_params->MinHostKickTimeout ); + U32_ZERO_RANGE( p_params->MaxHostKickTimeout ); + U32_ZERO_RANGE( p_params->MinHostKickEntries ); + U32_ZERO_RANGE( p_params->MaxHostKickEntries ); + U32_ZERO_RANGE( p_params->MinHostKickBytes ); + U32_ZERO_RANGE( p_params->MaxHostKickBytes ); + U32_RANGE( p_params->MinHostUpdateSz ); + U32_RANGE( p_params->MaxHostUpdateSz ); + U32_RANGE( p_params->MinEiocUpdateSz ); + U32_RANGE( p_params->MaxEiocUpdateSz ); + U8_RANGE( p_params->NotifyBundleSz ); + U32_ZERO_RANGE( p_params->ViportStatsInterval ); + U32_ZERO_RANGE( p_params->ViportHbInterval ); + U32_ZERO_RANGE( p_params->ViportHbTimeout ); + U32_RANGE( p_params->ControlRspTimeout ); + U8_RANGE( p_params->ControlReqRetryCount ); + ZERO_RANGE_CHECK( p_params->RetryCount, 0, 7 ); + ZERO_RANGE_CHECK( p_params->MinRnrTimer, 0, 31 ); + U32_RANGE( p_params->DefaultViportsPerNetpath ); + U8_RANGE( p_params->MaxViportsPerNetpath ); + U16_ZERO_RANGE( p_params->DefaultPkey ); + U32_RANGE( p_params->DefaultNoPathTimeout ); + U32_RANGE( p_params->DefaultPrimaryConnectTimeout ); + U32_RANGE( p_params->DefaultPrimaryReconnectTimeout ); + U32_RANGE( p_params->DefaultPrimarySwitchTimeout ); + BOOLEAN_RANGE( p_params->DefaultPreferPrimary ); + BOOLEAN_RANGE( p_params->UseRxCsum ); + BOOLEAN_RANGE( p_params->UseTxCsum ); + + LESS_THAN_OR_EQUAL( p_params->MinAddressEntries, p_params->MaxAddressEntries ); + LESS_THAN_OR_EQUAL( p_params->MinMtu, p_params->MaxMtu ); + LESS_THAN_OR_EQUAL( p_params->MinHostPoolSz, p_params->HostRecvPoolEntries ); + POWER_OF_2( p_params->HostRecvPoolEntries ); + POWER_OF_2( p_params->MinHostPoolSz ); + POWER_OF_2( p_params->NotifyBundleSz ); + LESS_THAN( p_params->NotifyBundleSz, p_params->MinEiocPoolSz ); + LESS_THAN_OR_EQUAL( p_params->MinEiocPoolSz, p_params->MaxEiocPoolSz ); + POWER_OF_2( p_params->MinEiocPoolSz ); + POWER_OF_2( p_params->MaxEiocPoolSz ); + LESS_THAN_OR_EQUAL( p_params->MinHostKickTimeout, p_params->MaxHostKickTimeout ); + LESS_THAN_OR_EQUAL( p_params->MinHostKickEntries, p_params->MaxHostKickEntries ); + LESS_THAN_OR_EQUAL( p_params->MinHostKickBytes, p_params->MaxHostKickBytes ); + LESS_THAN_OR_EQUAL( p_params->MinHostUpdateSz, p_params->MaxHostUpdateSz ); + POWER_OF_2( p_params->MinHostUpdateSz ); + POWER_OF_2( p_params->MaxHostUpdateSz ); + LESS_THAN( p_params->MinHostUpdateSz, p_params->MinHostPoolSz ); + LESS_THAN( p_params->MaxHostUpdateSz, p_params->HostRecvPoolEntries ); + LESS_THAN_OR_EQUAL( p_params->MinEiocUpdateSz, p_params->MaxEiocUpdateSz ); + POWER_OF_2( p_params->MinEiocUpdateSz ); + POWER_OF_2( p_params->MaxEiocUpdateSz ); + LESS_THAN( p_params->MinEiocUpdateSz, p_params->MinEiocPoolSz ); + LESS_THAN( p_params->MaxEiocUpdateSz, p_params->MaxEiocPoolSz ); + LESS_THAN_OR_EQUAL( p_params->DefaultViportsPerNetpath, p_params->MaxViportsPerNetpath ); + + return TRUE; + +} +NDIS_STATUS +vnic_get_adapter_params( + IN NDIS_HANDLE h_handle, + OUT vnic_params_t* const p_params ) +{ + NDIS_STATUS status; + NDIS_HANDLE h_config; + NDIS_CONFIGURATION_PARAMETER *p_reg_prm; + //NDIS_STRING keyword; + + VNIC_ENTER( VNIC_DBG_ADAPTER ); + + CL_ASSERT(p_params ); + + /* prepare params for default initialization */ + cl_memset( p_params, 0xff, sizeof (vnic_params_t) ); + + NdisOpenConfiguration( &status, &h_config, h_handle ); + if( status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("NdisOpenConfiguration returned 0x%.8x\n", status) ); + return status; + } + + status = NDIS_STATUS_FAILURE; + p_reg_prm = NULL; + + //RtlInitUnicodeString( &keyword, L"DebugFlags" ); + + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + //if( status == NDIS_STATUS_SUCCESS ) + // g_vnic_dbg_lvl = p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MinMtu" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MinMtu = ( status != NDIS_STATUS_SUCCESS ) ? MIN_MTU: + p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MaxMtu" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MaxMtu =( status != NDIS_STATUS_SUCCESS )? MAX_MTU: + p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"UseRxCsum" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->UseRxCsum = ( status != NDIS_STATUS_SUCCESS ) ? + TRUE : ( p_reg_prm->ParameterData.IntegerData )? TRUE : FALSE; + + //RtlInitUnicodeString( &keyword, L"UseTxCsum" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + /* turn it on by default, if not present */ + p_params->UseTxCsum = ( status != NDIS_STATUS_SUCCESS ) ? + TRUE : ( p_reg_prm->ParameterData.IntegerData )? TRUE : FALSE; + + //RtlInitUnicodeString( &keyword, L"MinEiocUpdateSz" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MinEiocUpdateSz = ( status != NDIS_STATUS_SUCCESS ) ? + MIN_EIOC_UPDATE_SZ : p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MaxEiocUpdateSz" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MaxEiocUpdateSz = ( status != NDIS_STATUS_SUCCESS ) ? + MAX_EIOC_UPDATE_SZ : p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MinHostUpdateSz" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MinHostUpdateSz = ( status != NDIS_STATUS_SUCCESS ) ? + MIN_HOST_UPDATE_SZ : p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MaxHostUpdateSz" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MaxHostUpdateSz = ( status != NDIS_STATUS_SUCCESS ) ? + MAX_HOST_UPDATE_SZ : p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MinHostKickBytes" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MinHostKickBytes = ( status != NDIS_STATUS_SUCCESS ) ? + MIN_HOST_KICK_BYTES : p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MaxHostKickBytes" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MaxHostKickBytes = ( status != NDIS_STATUS_SUCCESS ) ? + MAX_HOST_KICK_BYTES : p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MinHostKickEntries" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MinHostKickEntries = ( status != NDIS_STATUS_SUCCESS ) ? + MIN_HOST_KICK_ENTRIES : p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MaxHostKickEntries" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MaxHostKickEntries = ( status != NDIS_STATUS_SUCCESS ) ? + MAX_HOST_KICK_ENTRIES : p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MinHostKickTimeout" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MinHostKickTimeout = ( status != NDIS_STATUS_SUCCESS ) ? + MIN_HOST_KICK_TIMEOUT : p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MaxHostKickTimeout" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MaxHostKickTimeout = ( status != NDIS_STATUS_SUCCESS ) ? + MAX_HOST_KICK_TIMEOUT : p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MinEiocPoolSz" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MinEiocPoolSz = ( status != NDIS_STATUS_SUCCESS ) ? + MIN_EIOC_POOL_SZ : p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MaxEiocPoolSz" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MaxEiocPoolSz = ( status != NDIS_STATUS_SUCCESS ) ? + MAX_EIOC_POOL_SZ : p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"MinHostPoolSz" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->MinHostPoolSz = ( status != NDIS_STATUS_SUCCESS ) ? + MIN_HOST_POOL_SZ : p_reg_prm->ParameterData.IntegerData; + + //RtlInitUnicodeString( &keyword, L"HostRecvPoolEntries" ); + //NdisReadConfiguration( + // &status, &p_reg_prm, h_config, &keyword, NdisParameterInteger ); + + p_params->HostRecvPoolEntries = ( status != NDIS_STATUS_SUCCESS ) ? + HOST_RECV_POOL_ENTRIES : p_reg_prm->ParameterData.IntegerData; + + NdisCloseConfiguration( h_config ); + + status = ( _vnic_params_sanity_check(p_params)? + NDIS_STATUS_SUCCESS: NDIS_STATUS_FAILURE ); + + VNIC_EXIT( VNIC_DBG_ADAPTER ); + return status; +} + +ib_api_status_t +vnic_viport_allocate( + IN vnic_adapter_t* const p_adapter, + IN OUT viport_t** const pp_viport ) +{ + viport_t *p_viport; + NDIS_STATUS status; + VNIC_ENTER( VNIC_DBG_ADAPTER ); + + NdisAcquireSpinLock( &p_adapter->lock ); + status = NdisAllocateMemoryWithTag( &p_viport, sizeof(viport_t), 'trop' ); + + if( status != NDIS_STATUS_SUCCESS ) + { + NdisReleaseSpinLock( &p_adapter->lock ); + VNIC_TRACE_EXIT(VNIC_DBG_ERROR, + ( "Failed allocating Viport structure\n" )); + return IB_ERROR; + } + + NdisZeroMemory( p_viport, sizeof(viport_t) ); + + KeInitializeSpinLock( &p_viport->lock ); + InitializeListHead( &p_viport->listPtrs ); + cl_qlist_init( &p_viport->send_pending_list ); + + KeInitializeEvent( &p_viport->conn_event, SynchronizationEvent, FALSE ); + + p_viport->p_adapter = p_adapter; + + viport_get_adapter_name( p_viport ); + + viport_config_defaults ( p_viport ); + VNIC_TRACE( VNIC_DBG_PNP, ("Configure Viport default values\n") ); + + control_construct( &p_viport->control, p_viport ); + data_construct( &p_viport->data, p_viport ); + + *pp_viport = p_viport; + + NdisReleaseSpinLock( &p_adapter->lock ); + VNIC_EXIT( VNIC_DBG_ADAPTER ); + return IB_SUCCESS; +} +static void +_vnic_viport_free( + IN vnic_adapter_t* const p_adapter ) +{ + viport_t *p_viport = p_adapter->p_viport; + VNIC_ENTER( VNIC_DBG_ADAPTER ); + + if ( p_viport ) + { + p_adapter->p_viport = NULL; + NdisFreeMemory( p_viport, sizeof(viport_t), 0 ); + } + p_adapter->state = INIC_UNINITIALIZED; + + VNIC_EXIT( VNIC_DBG_ADAPTER ); +} + + +NDIS_STATUS +vnic_set_mcast( + IN vnic_adapter_t* const p_adapter, + IN mac_addr_t* const p_mac_array, + IN const uint8_t mc_count ) +{ + NDIS_STATUS status; + + VNIC_ENTER( VNIC_DBG_MCAST ); + + /* Copy the MC address list into the adapter. */ + if( mc_count ) + { + RtlCopyMemory( + p_adapter->mcast_array, p_mac_array, mc_count * MAC_ADDR_LEN ); + } + p_adapter->mc_count = mc_count; + + if( !p_adapter->p_currentPath->pViport ) + return NDIS_STATUS_SUCCESS; + + p_adapter->pending_set = TRUE; + status = viport_setMulticast( p_adapter->p_currentPath->pViport ); + if( status != NDIS_STATUS_PENDING ) + { + p_adapter->pending_set = FALSE; + } + + VNIC_EXIT( VNIC_DBG_MCAST ); + return status; +} + + +static BOOLEAN +__path_records_match( + IN ib_path_rec_t *p_path1, + IN ib_path_rec_t *p_path2 ) +{ + if ( p_path1->dgid.unicast.prefix != p_path2->dgid.unicast.prefix ) + return FALSE; + if ( p_path1->dgid.unicast.interface_id != p_path2->dgid.unicast.interface_id ) + return FALSE; + if ( p_path1->dlid != p_path2->dlid ) + return FALSE; + if ( p_path1->pkey != p_path2->pkey ) + return FALSE; + if ( p_path1->rate != p_path2->rate ) + return FALSE; + + return TRUE; +} + +static BOOLEAN +__sid_valid( + IN vnic_adapter_t *p_adapter, + IN ib_net64_t sid ) +{ + vnic_sid_t svc_id; + svc_id.as_uint64 = sid; + if( ( svc_id.s.base_id & 0x10 ) != 0x10 ) + return FALSE; + if( svc_id.s.oui[0] != 0x00 && + svc_id.s.oui[1] != 0x06 && + svc_id.s.oui[2] != 0x6a ) + return FALSE; + if ( svc_id.s.type != CONTROL_SID && + svc_id.s.type != DATA_SID ) + return FALSE; + if ( svc_id.s.ioc_num != _get_ioc_num_from_iocguid( &p_adapter->ifc_data.guid ) ) + return FALSE; + return TRUE; +} +static inline uint8_t +_get_ioc_num_from_iocguid( + IN ib_net64_t *p_iocguid ) +{ + return ( (vnic_ioc_guid_t *)p_iocguid)->s.ioc_num; +} + +static ib_api_status_t +__path_record_add( + IN vnic_adapter_t *p_adapter, + IN ib_path_rec_t *p_path_rec ) +{ + + NdisAcquireSpinLock( &p_adapter->lock ); + + if ( !__path_records_match( &p_adapter->path_record.path_rec, p_path_rec ) ) + { + p_adapter->path_record.path_rec = *p_path_rec; + p_adapter->path_record.num_entries++; + } + + NdisReleaseSpinLock( &p_adapter->lock ); + return IB_SUCCESS; +} + +static ib_api_status_t +__path_record_remove( + IN vnic_adapter_t *p_adapter, + IN ib_path_rec_t *p_path_rec ) +{ + + NdisAcquireSpinLock( &p_adapter->lock ); + + if ( __path_records_match( &p_adapter->path_record.path_rec, p_path_rec ) ) + { + --p_adapter->path_record.num_entries; + cl_memclr( &p_adapter->path_record.path_rec, sizeof( ib_path_rec_t )); + } + + NdisReleaseSpinLock( &p_adapter->lock ); + return IB_SUCCESS; +} + +static void +__path_records_cleanup( + vnic_adapter_t *p_adapter ) +{ + + NdisAcquireSpinLock( &p_adapter->lock ); + + cl_memclr( &p_adapter->path_record.path_rec, sizeof( ib_path_rec_t )); + p_adapter->path_record.num_entries = 0; + + NdisReleaseSpinLock( &p_adapter->lock ); + return; +} +ib_api_status_t +__vnic_pnp_cb( + IN ib_pnp_rec_t *p_pnp_rec ) +{ + ib_api_status_t ib_status = IB_SUCCESS; + ib_pnp_ioc_rec_t *p_ioc_rec; + ib_pnp_ioc_path_rec_t *p_ioc_path; + + vnic_adapter_t * __ptr64 p_adapter = (vnic_adapter_t * __ptr64)p_pnp_rec->pnp_context; + + VNIC_ENTER( VNIC_DBG_PNP ); + + CL_ASSERT( p_adapter ); + + switch( p_pnp_rec->pnp_event ) + { + case IB_PNP_IOC_ADD: + p_ioc_rec = (ib_pnp_ioc_rec_t*)p_pnp_rec; + + VNIC_TRACE( VNIC_DBG_PNP, ("IB_PNP_IOC_ADD for %s.\n", p_ioc_rec->info.profile.id_string) ); + + if( p_adapter->ifc_data.ca_guid != p_ioc_rec->ca_guid ) + { + VNIC_TRACE_EXIT( VNIC_DBG_WARN, ("Invalid CA GUID.\n") ); + ib_status = IB_INVALID_GUID; + break; + } + if( p_adapter->ifc_data.guid != p_ioc_rec->info.profile.ioc_guid ) + { + VNIC_TRACE_EXIT( VNIC_DBG_WARN, ("Invalid IOC GUID.\n") ); + ib_status = IB_INVALID_GUID; + break; + } + /* get ioc profile data */ + NdisAcquireSpinLock( &p_adapter->lock ); + + InterlockedExchange( (volatile LONG*)&p_adapter->pnp_state, IB_PNP_IOC_ADD ); + + p_adapter->ioc_info = p_ioc_rec->info; + p_adapter->num_svc_entries = p_ioc_rec->info.profile.num_svc_entries; + + CL_ASSERT(p_adapter->num_svc_entries >= 2 ); + + if( !__sid_valid( p_adapter, p_ioc_rec->svc_entry_array[0].id ) ) + { + NdisReleaseSpinLock( &p_adapter->lock ); + + VNIC_TRACE_EXIT( VNIC_DBG_WARN, + ("Invalid Service ID %#I64x\n",p_ioc_rec->svc_entry_array[0].id ) ); + + ib_status = IB_INVALID_GUID; // should it be set INVALID_SERVICE_TYPE ? + break; + } + + p_adapter->p_svc_entries = + cl_zalloc( sizeof(ib_svc_entry_t) * + p_adapter->ioc_info.profile.num_svc_entries ); + + if( p_adapter->p_svc_entries == NULL ) + { + NdisReleaseSpinLock( &p_adapter->lock ); + + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, ("Insufficient Memory.\n") ); + ib_status = IB_INSUFFICIENT_MEMORY; + break; + } + + cl_memcpy( p_adapter->p_svc_entries, + p_ioc_rec->svc_entry_array, + sizeof(ib_svc_entry_t) * p_adapter->ioc_info.profile.num_svc_entries); + + VNIC_TRACE( VNIC_DBG_PNP, + ("Found %d Service Entries.\n", p_adapter->ioc_info.profile.num_svc_entries)); + NdisReleaseSpinLock( &p_adapter->lock ); + break; + + case IB_PNP_IOC_REMOVE: + CL_ASSERT( p_pnp_rec->guid == p_adapter->ifc_data.guid ); + + p_ioc_rec = (ib_pnp_ioc_rec_t*)p_pnp_rec; + + InterlockedExchange( (volatile LONG*)&p_adapter->pnp_state, IB_PNP_IOC_REMOVE ); + + VNIC_TRACE( VNIC_DBG_PNP, ("IB_PNP_IOC_REMOVE for %s.\n", + p_adapter->ioc_info.profile.id_string) ); + + NdisMIndicateStatus( p_adapter->h_handle, + NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0 ); + NdisMIndicateStatusComplete( p_adapter->h_handle ); + vnic_viport_cleanup( p_adapter ); + break; + + case IB_PNP_IOC_PATH_ADD: + /* path for our IOC ? */ + if ( p_pnp_rec->guid != p_adapter->ifc_data.guid ) + { + VNIC_TRACE( VNIC_DBG_PNP, + ("Getting path for wrong IOC\n") ); + ib_status = IB_INVALID_GUID; + break; + } + p_ioc_path = (ib_pnp_ioc_path_rec_t*)p_pnp_rec; + + if( p_adapter->state != INIC_UNINITIALIZED ) + break; + + ib_status = __path_record_add( p_adapter, &p_ioc_path->path ); + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("Failed to add path record\n") ); + break; + } + + netpath_init( &p_adapter->primaryPath, p_adapter ); + p_adapter->p_currentPath = &p_adapter->primaryPath; + + ib_status = vnic_viport_allocate( p_adapter, &p_adapter->p_viport ); + + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE ( VNIC_DBG_ERROR, + ("Failed status %s\n", p_adapter->ifc.get_err_str( ib_status )) ); + _vnic_viport_free( p_adapter ); + return ib_status; + } + + netpath_addPath( p_adapter->p_currentPath, p_adapter->p_viport ); + viport_setPath( p_adapter->p_viport, + &p_adapter->path_record.path_rec, + &p_adapter->ioc_info.profile.ioc_guid ); + + ib_status = viport_control_connect( p_adapter->p_viport ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("Control path connect failed status s%\n", + p_adapter->ifc.get_err_str( ib_status )) ); + vnic_viport_cleanup( p_adapter ); + break; + } + + ib_status = viport_data_connect( p_adapter->p_viport ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("Data path connect failed status %s\n", + p_adapter->ifc.get_err_str( ib_status )) ); + vnic_viport_cleanup( p_adapter ); + break; + } + + p_adapter->state = INIC_REGISTERED; + break; + + case IB_PNP_IOC_PATH_REMOVE: + p_ioc_path = (ib_pnp_ioc_path_rec_t*)p_pnp_rec; + + VNIC_TRACE( VNIC_DBG_PNP, + ("IB_PNP_IOC_PATH_REMOVE (slid:%d dlid:%d) for %s.\n", + ntoh16( p_ioc_path->path.slid ), + ntoh16( p_ioc_path->path.dlid ), + p_adapter->ioc_info.profile.id_string)); + + ib_status = __path_record_remove( p_adapter, &p_ioc_path->path ); + + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("Failed to remove path record\n") ); + } + break; + + default: + VNIC_TRACE( VNIC_DBG_PNP, + (" Received unhandled PnP event %#x\n", p_pnp_rec->pnp_event ) ); + break; + } + + VNIC_EXIT( VNIC_DBG_PNP ); + return ib_status; +} + + +NDIS_STATUS +vnic_get_adapter_interface( + IN NDIS_HANDLE h_handle, + IN vnic_adapter_t *p_adapter) +{ + + NTSTATUS status; + ib_al_ifc_data_t data; + IO_STACK_LOCATION io_stack; + DEVICE_OBJECT *p_pdo; + + VNIC_ENTER( VNIC_DBG_ADAPTER ); + + NdisMGetDeviceProperty( h_handle, &p_pdo, NULL, NULL, NULL, NULL ); + + data.size = sizeof(ioc_ifc_data_t); + data.type = &GUID_IOC_INTERFACE_DATA; + data.version = IOC_INTERFACE_DATA_VERSION; + data.p_data = &p_adapter->ifc_data; + + io_stack.MinorFunction = IRP_MN_QUERY_INTERFACE; + io_stack.Parameters.QueryInterface.Version = AL_INTERFACE_VERSION; + io_stack.Parameters.QueryInterface.Size = sizeof(ib_al_ifc_t); + io_stack.Parameters.QueryInterface.Interface = (INTERFACE*)&p_adapter->ifc; + io_stack.Parameters.QueryInterface.InterfaceSpecificData = &data; + io_stack.Parameters.QueryInterface.InterfaceType = &GUID_IB_AL_INTERFACE; + + status = cl_fwd_query_ifc( p_pdo, &io_stack ); + + if( !NT_SUCCESS( status ) ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("Query interface for VNIC interface returned %08x.\n", status) ); + return status; + } + /* + * Dereference the interface now so that the bus driver doesn't fail a + * query remove IRP. We will always get unloaded before the bus driver + * since we're a child device. + */ + p_adapter->ifc.wdm.InterfaceDereference( + p_adapter->ifc.wdm.Context ); + + VNIC_EXIT( VNIC_DBG_ADAPTER ); + + return NDIS_STATUS_SUCCESS; +} + + +void +vnic_viport_cleanup( + IN vnic_adapter_t *p_adapter ) +{ + VNIC_ENTER( VNIC_DBG_ADAPTER ); + + if ( p_adapter->p_viport ) + viport_cleanup(p_adapter->p_viport ); + p_adapter->p_viport = NULL; + + VNIC_EXIT( VNIC_DBG_ADAPTER ); +} diff --git a/trunk/ulp/inic/kernel/vnic_adapter.h b/trunk/ulp/inic/kernel/vnic_adapter.h new file mode 100644 index 00000000..dcb0b251 --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_adapter.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + +#if !defined _VNIC_ADAPTER_H_ +#define _VNIC_ADAPTER_H_ + +#include +#include +#include +#include +#include "vnic_ib.h" +#include "vnic_controlpkt.h" +#include "vnic_config.h" +#include "vnic_control.h" +#include "vnic_data.h" +#include "vnic_viport.h" +#include + +typedef struct _pending_oid +{ + NDIS_OID oid; + PVOID p_buf; + ULONG buf_len; + PULONG p_bytes_used; + PULONG p_bytes_needed; + +} pending_oid_t; + +typedef struct _ipv4_address_item +{ + union _net_address_item_address + { + ULONG as_ulong; + UCHAR as_bytes[4]; + } address; + +} ipv4_address_item_t; + +typedef struct _vnic_params { + uint32_t MaxAddressEntries; + uint32_t MinAddressEntries; + uint32_t MinMtu; + uint32_t MaxMtu; + uint32_t HostRecvPoolEntries; + uint32_t MinHostPoolSz; + uint32_t MinEiocPoolSz; + uint32_t MaxEiocPoolSz; + uint32_t MinHostKickTimeout; + uint32_t MaxHostKickTimeout; + uint32_t MinHostKickEntries; + uint32_t MaxHostKickEntries; + uint32_t MinHostKickBytes; + uint32_t MaxHostKickBytes; + uint32_t MinHostUpdateSz; + uint32_t MaxHostUpdateSz; + uint32_t MinEiocUpdateSz; + uint32_t MaxEiocUpdateSz; + uint32_t NotifyBundleSz; + uint32_t ViportStatsInterval; + uint32_t ViportHbInterval; + uint32_t ViportHbTimeout; + uint32_t ControlRspTimeout; + uint32_t ControlReqRetryCount; + uint32_t RetryCount; + uint32_t MinRnrTimer; + uint32_t MaxViportsPerNetpath; + uint32_t DefaultViportsPerNetpath; + uint32_t DefaultPkey; + uint32_t DefaultNoPathTimeout; + uint32_t DefaultPrimaryConnectTimeout; + uint32_t DefaultPrimaryReconnectTimeout; + uint32_t DefaultPrimarySwitchTimeout; + uint32_t DefaultPreferPrimary; + uint32_t UseRxCsum; + uint32_t UseTxCsum; + mac_addr_t conf_mac; +} vnic_params_t; + + +typedef struct _vnic_adapter { + LIST_ENTRY list_entry; + NDIS_HANDLE h_handle; + PDRIVER_OBJECT p_drv_obj; +// cl_obj_t obj; + NDIS_SPIN_LOCK lock; + ib_al_ifc_t ifc; + ioc_ifc_data_t ifc_data; + ib_ioc_info_t ioc_info; + vnic_path_record_t path_record; + ib_al_handle_t h_al; + ib_ca_handle_t h_ca; + ib_pnp_handle_t h_pnp; + ib_pnp_event_t pnp_state; + IbCa_t ca; + struct _viport *p_viport; + InicState_t state; + struct Netpath primaryPath; + struct Netpath secondaryPath; + struct Netpath *p_currentPath; + vnic_params_t params; + int open; + int macSet; + int mc_count; + mac_addr_t mcast_array[MAX_MCAST]; + LONG xmitStarted; + LONG carrier; + uint32_t packet_filter; + BOOLEAN hung; + BOOLEAN pending_set; + BOOLEAN pending_query; + pending_oid_t query_oid; + pending_oid_t set_oid; + uint8_t num_svc_entries; + ib_svc_entry_t *p_svc_entries; + char name[65]; + InicNPEvent_t np_event[INICNP_NUM_EVENTS]; +#ifdef VNIC_STATISTIC + struct { + uint64_t startTime; + uint64_t connTime; + uint64_t disconnRef; /* Intermediate time */ + uint64_t disconnTime; + uint32_t disconnNum; + uint64_t xmitTime; + uint32_t xmitNum; + uint32_t xmitFail; + uint64_t recvTime; + uint32_t recvNum; + uint64_t xmitRef; /* Intermediate time */ + uint64_t xmitOffTime; + uint32_t xmitOffNum; + uint64_t carrierRef; /* Intermediate time */ + uint64_t carrierOffTime; + uint32_t carrierOffNum; + } statistics; +#endif /* VNIC_STATISTIC */ + +} vnic_adapter_t; + +ib_api_status_t +vnic_create_adapter( + IN NDIS_HANDLE h_handle, + OUT vnic_adapter_t** const pp_adapter); + +void +vnic_destroy_adapter( + IN vnic_adapter_t* p_adapter); + +ib_api_status_t +vnic_construct_adapter( + IN vnic_adapter_t *p_adapter); + +NDIS_STATUS +vnic_set_mcast( + IN vnic_adapter_t* const p_adapter, + IN mac_addr_t* const p_mac_array, + IN const uint8_t mc_count ); + +ib_api_status_t +vnic_viport_allocate( + IN vnic_adapter_t* const p_adapter, + IN OUT viport_t** const pp_viport ); + +void +vnic_viport_cleanup( + IN vnic_adapter_t *p_adapter ); + +void +vnic_resume_set_oids( + IN vnic_adapter_t* const p_adapter ); + +void +vnic_resume_oids( + IN vnic_adapter_t* const p_adapter ); + +ib_api_status_t +__vnic_pnp_cb( + IN ib_pnp_rec_t *p_pnp_rec ); + +ib_api_status_t +ibregion_physInit( + IN struct _vnic_adapter* p_adapter, + OUT IbRegion_t *pRegion, + IN ib_pd_handle_t hPd, + IN uint64_t *p_vaddr, + IN uint64_t len ); + +#endif /* !defined _VNIC_ADAPTER_H_ */ diff --git a/trunk/ulp/inic/kernel/vnic_config.h b/trunk/ulp/inic/kernel/vnic_config.h new file mode 100644 index 00000000..9a054cae --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_config.h @@ -0,0 +1,238 @@ +#ifndef _VNIC_CONFIG_H_ +#define _VNIC_CONFIG_H_ + +#include "vnic_util.h" +/* These are hard, compile time limits. + * Lower runtime overrides may be in effect + */ +#define INIC_CLASS_SUBCLASS 0x2000066A +#define INIC_PROTOCOL 0 +#define INIC_PROT_VERSION 1 + +#define INIC_MAJORVERSION 1 +#define INIC_MINORVERSION 1 + +#define MAX_ADDRESS_ENTRIES 64 /* max entries to negotiate with remote */ +#define MIN_ADDRESS_ENTRIES 16 /* min entries remote can return to us that we agree with */ +#define MAX_ADDR_ARRAY 32 /* address array we can handle. for now */ +#define MIN_MTU 1500 /* Minimum Negotiated payload size */ +#define MAX_MTU 9500 /* max Jumbo frame payload size */ +#define ETH_VLAN_HLEN 18 /* ethernet header with VLAN tag */ + +#define HOST_RECV_POOL_ENTRIES 512 /* TBD: Abritrary */ +#define MIN_HOST_POOL_SZ 64 /* TBD: Abritrary */ +#define MIN_EIOC_POOL_SZ 64 /* TBD: Abritrary */ +#define MAX_EIOC_POOL_SZ 256 /* TBD: Abritrary */ + +#define MIN_HOST_KICK_TIMEOUT 10 /* TBD: Arbitrary */ +#define MAX_HOST_KICK_TIMEOUT 100 /* In uSec */ + +#define MIN_HOST_KICK_ENTRIES 1 /* TBD: Arbitrary */ +#define MAX_HOST_KICK_ENTRIES 128 /* TBD: Arbitrary */ + +#define MIN_HOST_KICK_BYTES 0 +#define MAX_HOST_KICK_BYTES 5000 + +#define MIN_HOST_UPDATE_SZ 8 /* TBD: Arbitrary */ +#define MAX_HOST_UPDATE_SZ 32 /* TBD: Arbitrary */ +#define MIN_EIOC_UPDATE_SZ 8 /* TBD: Arbitrary */ +#define MAX_EIOC_UPDATE_SZ 32 /* TBD: Arbitrary */ + +#define NOTIFY_BUNDLE_SZ 32 + +#define MAX_PARAM_VALUE 0x40000000 + +#define DEFAULT_VIPORTS_PER_NETPATH 1 +#define MAX_VIPORTS_PER_NETPATH 1 + +#define INIC_USE_RX_CSUM TRUE +#define INIC_USE_TX_CSUM TRUE +#define DEFAULT_NO_PATH_TIMEOUT 10000 /* TBD: Arbitrary */ +#define DEFAULT_PRI_CON_TIMEOUT 10000 /* TBD: Arbitrary */ +#define DEFAULT_PRI_RECON_TIMEOUT 10000 /* TBD: Arbitrary */ +#define DEFAULT_PRI_SWITCH_TIMEOUT 10000 /* TBD: Arbitrary */ +#define DEFAULT_PREFER_PRIMARY TRUE + +/* timeouts: !! all data defined in milliseconds, + some later will be converted to microseconds */ +#define VIPORT_STATS_INTERVAL 5000 /* 5 sec */ +#define VIPORT_HEARTBEAT_INTERVAL 2000 /* 2 seconds */ +#define VIPORT_HEARTBEAT_TIMEOUT 64000 /* 64 sec */ +#define CONTROL_RSP_TIMEOUT 2000 /* 2 sec */ + +#define _100NS_IN_1MS (10000) +inline uint64_t +get_time_stamp_ms( void ) +{ + return( (KeQueryInterruptTime() / _100NS_IN_1MS ) ); +} + +/* InfiniBand Connection Parameters */ +#define CONTROL_REQ_RETRY_COUNT 4 +#define RETRY_COUNT 3 +#define MIN_RNR_TIMER 22 /* 20 ms */ +#define DEFAULT_PKEY 0 /* Pkey table index */ + +/* phys memory size to register with HCA*/ +#define MEM_REG_SIZE 0xFFFFFFFFFFFFFFFF + +/* link speed in 100 bits/sec units */ +#define LINK_SPEED_1GBIT_x100BPS 10000000 +#define LINK_SPEED_10GBIT_x100BPS 100000000 + /* VEx does not report it's link speed, so set it 1Gb/s so far */ +#define DEFAULT_LINK_SPEED_x100BPS LINK_SPEED_1GBIT_x100BPS + +#define DEFAULT_PARAM(x,y) if(x == MAXU32) { \ + x = y; } +#define POWER_OF_2(x) if (!IsPowerOf2(x)) { \ + VNIC_TRACE( VNIC_DBG_WARN, (" %s (%d) must be a power of 2\n",#x,x) ); \ + x = SetMinPowerOf2(x); \ + } +#define LESS_THAN(lo, hi) if (lo >= hi) { \ + VNIC_TRACE( VNIC_DBG_ERROR, (" %s (%d) must be less than %s (%d)\n",#lo,lo,#hi,hi) ); \ + lo = hi >> 1; \ + } +#define LESS_THAN_OR_EQUAL(lo, hi) if (lo > hi) { \ + VNIC_TRACE( VNIC_DBG_WARN, (" %s (%d) cannot be greater than %s (%d)\n",#lo,lo,#hi,hi) ); \ + lo = hi; \ + } +#define RANGE_CHECK(x, min, max) if ((x < min) || (x > max)) { \ + VNIC_TRACE( VNIC_DBG_WARN, (" %s (%d) must be between %d and %d\n",#x,x,min,max) ); \ + if (x < min) \ + x = min; \ + else \ + x = max; \ + } +#define ZERO_RANGE_CHECK(x, min, max) if (x > max) { \ + VNIC_TRACE( VNIC_DBG_WARN, (" %s (%d) must be between %d and %d\n",#x,x,min,max) ); \ + x = max; \ + } + +#define BOOLEAN_RANGE(x) ZERO_RANGE_CHECK(x, 0, 1) +#define U32_ZERO_RANGE(x) ZERO_RANGE_CHECK(x, 0, 0x7FFFFFFF) +#define U32_RANGE(x) RANGE_CHECK(x, 1, 0x7FFFFFFF) +#define U16_ZERO_RANGE(x) ZERO_RANGE_CHECK(x, 0, 0xFFFF) +#define U16_RANGE(x) RANGE_CHECK(x, 1, 0xFFFF) +#define U8_ZERO_RANGE(x) ZERO_RANGE_CHECK(x, 0, 0xFF) +#define U8_RANGE(x) RANGE_CHECK(x, 1, 0xFF) + + +typedef struct { + uint64_t ioc_guid; + uint64_t portGuid; + uint64_t port; + uint64_t hca; + uint64_t instance; + char ioc_string[65]; + char ioc_guid_set; + char ioc_string_set; +} PathParam_t; + +typedef struct _vnic_globals { + NDIS_HANDLE ndis_handle; // ndis wrapper handle + volatile LONG dev_reg; // dev register counter + volatile LONG adapters; // created adapters counter + KSPIN_LOCK lock; + LIST_ENTRY adapter_list; + uint8_t host_name[IB_NODE_DESCRIPTION_SIZE + 1]; +} vnic_globals_t; +/* +struct members: + lock: + take this lock before access to adapter_list + adapter_list: + head of list of virtual adapters initialized by driver +*/ + +typedef struct IbConfig { + ib_path_rec_t pathInfo; + uint64_t sid; + Inic_ConnectionData_t connData; + uint32_t retryCount; + uint32_t rnrRetryCount; + uint8_t minRnrTimer; + uint32_t numSends; + uint32_t numRecvs; + uint32_t recvScatter; /* 1 */ + uint32_t sendGather; /* 1 or 2 */ + uint32_t overrides; +} IbConfig_t; + +typedef struct ControlConfig { + IbConfig_t ibConfig; + uint32_t numRecvs; + uint8_t inicInstance; + uint16_t maxAddressEntries; + uint16_t minAddressEntries; + uint32_t rspTimeout; + uint8_t reqRetryCount; + uint32_t overrides; +} ControlConfig_t; + +typedef struct DataConfig { + IbConfig_t ibConfig; + uint64_t pathId; + uint32_t numRecvs; + uint32_t hostRecvPoolEntries; + Inic_RecvPoolConfig_t hostMin; + Inic_RecvPoolConfig_t hostMax; + Inic_RecvPoolConfig_t eiocMin; + Inic_RecvPoolConfig_t eiocMax; + uint32_t notifyBundle; + uint32_t overrides; +} DataConfig_t; + +typedef struct ViportConfig { + struct _viport *pViport; + ControlConfig_t controlConfig; + DataConfig_t dataConfig; + uint32_t hca; + uint32_t port; + uint32_t statsInterval; + uint32_t hbInterval; /* heartbeat interval */ + uint32_t hbTimeout; /* heartbeat timeout */ + uint64_t portGuid; + uint64_t guid; + size_t pathIdx; + char ioc_string[65]; + +#define HB_INTERVAL_OVERRIDE 0x1 +#define GUID_OVERRIDE 0x2 +#define STRING_OVERRIDE 0x4 +#define HCA_OVERRIDE 0x8 +#define PORT_OVERRIDE 0x10 +#define PORTGUID_OVERRIDE 0x20 + uint32_t overrides; +} ViportConfig_t; + +/* + * primaryConnectTimeout - If the secondary connects first, how long do we + * give the primary? + * primaryReconnectTimeout - Same as above, but used when recovering when + * both paths fail + * primaryReconnectTimeout - How long do we wait before switching to the + * primary when it comes back? + */ +#define IFNAMSIZ 65 +typedef struct InicConfig { + //struct Inic *pInic; + char name[IFNAMSIZ]; + uint32_t noPathTimeout; + uint32_t primaryConnectTimeout; + uint32_t primaryReconnectTimeout; + uint32_t primarySwitchTimeout; + int preferPrimary; + BOOLEAN useRxCsum; + BOOLEAN useTxCsum; +#define USE_RX_CSUM_OVERRIDE 0x1 +#define USE_TX_CSUM_OVERRIDE 0x2 + uint32_t overrides; +} InicConfig_t; + +typedef enum { + INIC_UNINITIALIZED, + INIC_REGISTERED, +} InicState_t; + +#endif /* _VNIC_CONFIG_H_ */ + diff --git a/trunk/ulp/inic/kernel/vnic_control.c b/trunk/ulp/inic/kernel/vnic_control.c new file mode 100644 index 00000000..9ca242fa --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_control.c @@ -0,0 +1,1980 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + +#include "vnic_adapter.h" + +static void +control_recv( Control_t *pControl, RecvIo_t *pRecvIo ); + +static void +control_recvComplete( + IN Io_t *pIo ); + +static ib_api_status_t +control_send( Control_t *pControl ); + +static void +control_sendComplete( Io_t *pIo ); +static void +control_timeout( void * context ); + +static void +control_timer( Control_t *pControl, int timeout ); + +static void +control_timerStop( Control_t *pControl ); + +static void +control_initHdr( Control_t *pControl, uint8_t cmd ); + +static RecvIo_t * +control_getRsp( Control_t *pControl ); + +static void +copyRecvPoolConfig( Inic_RecvPoolConfig_t *pSrc, + Inic_RecvPoolConfig_t *pDst ); + +static BOOLEAN +checkRecvPoolConfigValue( + IN void *pSrc, + IN void *pDst, + IN void *pMax, + IN void *pMin, + IN char *name ); + +static BOOLEAN checkRecvPoolConfig( + Inic_RecvPoolConfig_t *pSrc, + Inic_RecvPoolConfig_t *pDst, + Inic_RecvPoolConfig_t *pMax, + Inic_RecvPoolConfig_t *pMin); + +static void +__control_logControlPacket( + Inic_ControlPacket_t *pPkt ); + +static inline char* +control_ifcfg_name( Control_t *pControl ) +{ + if (! pControl) + return "NCtl"; + return (pControl->p_viport->p_adapter->name ); +} + + +void +control_construct( + IN Control_t *pControl, + IN viport_t *pViport ) +{ + VNIC_ENTER( VNIC_DBG_CTRL ); + + cl_memclr(pControl, sizeof(Control_t)); + + pControl->p_viport = pViport; + + pControl->reqOutstanding = FALSE; + pControl->seqNum = 0; + + pControl->pResponse = NULL; + pControl->pInfo = NULL; + + pControl->p_viport->addrs_query_done = TRUE; + + InitializeListHead(&pControl->failureList); + KeInitializeSpinLock(&pControl->ioLock); + + cl_timer_construct( &pControl->timer ); + + ibqp_construct( &pControl->qp, pViport ); + + VNIC_EXIT( VNIC_DBG_CTRL ); +} + + +ib_api_status_t +control_init( + IN Control_t *pControl, + IN viport_t *pViport, + IN ControlConfig_t *pConfig, + IN uint64_t guid ) +{ + ib_api_status_t ib_status; + ib_pd_handle_t hPd; + Inic_ControlPacket_t *pkt; + Io_t *pIo; + int sz; + unsigned int i; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + pControl->p_conf = pConfig; + + hPd = pViport->p_adapter->ca.hPd; + + cl_timer_init( &pControl->timer, control_timeout, pControl ); + + ib_status = ibqp_init( + &pControl->qp, guid, &pConfig->ibConfig ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, ("%s: ibqp_init returned %s\n", + pViport->p_adapter->ifc.get_err_str( ib_status )) ); + goto failure; + } + + sz = (sizeof(RecvIo_t) * pConfig->numRecvs ) + + (sizeof(Inic_ControlPacket_t) * (pConfig->numRecvs + 1)); + + pControl->pLocalStorage = cl_zalloc( sz ); + + if ( pControl->pLocalStorage == NULL ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("%s: Failed allocating space for local storage\n", + control_ifcfg_name(pControl)) ); + + ibqp_cleanup(&pControl->qp); + ib_status = IB_INSUFFICIENT_MEMORY; + goto failure; + } + + pControl->pRecvIos = (RecvIo_t *)pControl->pLocalStorage; + + pkt = (Inic_ControlPacket_t *)(pControl->pLocalStorage + + sizeof(SendIo_t) * pConfig->numRecvs); + + sz = sizeof(Inic_ControlPacket_t) * (pConfig->numRecvs + 1); + + ib_status = ibregion_init( pViport, &pControl->region, hPd, + pkt, sz, IB_AC_LOCAL_WRITE ); + if ( ib_status != IB_SUCCESS ) + { + /* NOTE: I'm allowing recvs into the send buffer as well + * as the receive buffers. I'm doing this to combine them + * into a single region, and conserve a region. + */ + VNIC_TRACE_EXIT( VNIC_DBG_ERROR|VNIC_DBG_CTRL, + ("%s: Failed setting up control space region\n", + control_ifcfg_name(pControl)) ); + ibqp_cleanup( &pControl->qp ); + cl_free( pControl->pLocalStorage ); + goto failure; + } + pIo = &pControl->sendIo.io; + pIo->pViport = pViport; + pIo->pRoutine = control_sendComplete; + + pIo->wrq.p_next = NULL; + pIo->wrq.wr_type = WR_SEND; + pIo->wrq.send_opt = IB_SEND_OPT_SIGNALED; + pIo->wrq.wr_id = (uint64_t)(pIo); + pIo->wrq.num_ds = 1; + pIo->wrq.ds_array = &pControl->sendIo.dsList; + pIo->wrq.ds_array[0].length = sizeof(Inic_ControlPacket_t); + pIo->wrq.ds_array[0].lkey = pControl->region.lkey; + pIo->wrq.ds_array[0].vaddr = (uint64_t)(pkt++); + + for (i = 0; i < pConfig->numRecvs; i++ ) + { + pIo = &pControl->pRecvIos[i].io; + pIo->pViport = pViport; + pIo->pRoutine = control_recvComplete; + + pIo->r_wrq.wr_id = (uint64_t)(pIo); + pIo->r_wrq.p_next = NULL; + pIo->r_wrq.num_ds = 1; + pIo->r_wrq.ds_array = &pControl->pRecvIos[i].dsList; + pIo->r_wrq.ds_array[0].length = sizeof(Inic_ControlPacket_t); + pIo->r_wrq.ds_array[0].vaddr = (uint64_t)(pkt++); + pIo->r_wrq.ds_array[0].lkey = pControl->region.lkey; + + if ( ibqp_postRecv( &pControl->qp, pIo ) != IB_SUCCESS ) + { + control_cleanup( pControl ); + ib_status = IB_ERROR; + goto failure; + } + } + +failure: + VNIC_EXIT( VNIC_DBG_CTRL ); + return ib_status; +} + + +void +control_cleanup( + IN Control_t *pControl ) +{ + VNIC_ENTER( VNIC_DBG_CTRL ); + + control_timerStop( pControl ); + ibqp_detach( &pControl->qp ); + ibqp_cleanup( &pControl->qp ); + ibregion_cleanup( pControl->p_viport, &pControl->region ); + + if ( pControl->pLocalStorage ) + { + cl_free( pControl->pLocalStorage ); + pControl->pLocalStorage = NULL; + } + + cl_timer_destroy( &pControl->timer ); + + VNIC_EXIT( VNIC_DBG_CTRL ); + return; +} + +void +control_processAsync( + IN Control_t *pControl ) +{ + RecvIo_t *pRecvIo; + Inic_ControlPacket_t *pPkt; + LIST_ENTRY *p_list_entry; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + pRecvIo = InterlockedExchangePointer( &pControl->pInfo, NULL ); + + if ( pRecvIo != NULL ) + { + VNIC_TRACE( VNIC_DBG_CTRL | VNIC_DBG_INFO, + ("%s: processing info packet\n", + control_ifcfg_name(pControl)) ); + + pPkt = control_packet( pRecvIo ); + + if ( pPkt->hdr.pktCmd == CMD_REPORT_STATUS ) + { + switch( ntoh32(pPkt->cmd.reportStatus.statusNumber) ) + { + case INIC_STATUS_LINK_UP: + VNIC_TRACE( VNIC_DBG_CTRL | VNIC_DBG_INFO, + ("%s: Link Up\n", + control_ifcfg_name(pControl)) ); + + viport_linkUp( pControl->p_viport ); + break; + + case INIC_STATUS_LINK_DOWN: + VNIC_TRACE( VNIC_DBG_CTRL | VNIC_DBG_INFO, + ("%s: Link Down\n", + control_ifcfg_name(pControl)) ); + + viport_linkDown( pControl->p_viport ); + break; + + default: + VNIC_TRACE( VNIC_DBG_CTRL | VNIC_DBG_ERROR, + ("%s: Asynchronous status received from EIOC\n", + control_ifcfg_name(pControl)) ); + __control_logControlPacket( pPkt ); + break; + } + } + + if ( pPkt->hdr.pktCmd != CMD_REPORT_STATUS || + pPkt->cmd.reportStatus.isFatal ) + { + viport_failure( pControl->p_viport ); + } + + control_recv( pControl, pRecvIo ); + } + + while ( !IsListEmpty( &pControl->failureList ) ) + { + + VNIC_TRACE( VNIC_DBG_CTRL | VNIC_DBG_INFO, + ("%s: processing error packet\n", + control_ifcfg_name(pControl)) ); + + p_list_entry = ExInterlockedRemoveHeadList( &pControl->failureList, &pControl->ioLock ); + pRecvIo = (RecvIo_t *)p_list_entry; + pPkt = control_packet( pRecvIo ); + + VNIC_TRACE( VNIC_DBG_CTRL | VNIC_DBG_INFO, + ("%s: Asynchronous error received from EIOC\n", + control_ifcfg_name(pControl)) ); + + __control_logControlPacket( pPkt ); + + if ( ( pPkt->hdr.pktType != TYPE_ERR ) || + ( pPkt->hdr.pktCmd != CMD_REPORT_STATUS ) || + ( pPkt->cmd.reportStatus.isFatal ) ) + { + viport_failure( pControl->p_viport ); + } + + control_recv( pControl, pRecvIo ); + } + + VNIC_EXIT( VNIC_DBG_CTRL ); +} + + +ib_api_status_t +control_initInicReq( + IN Control_t *pControl ) +{ + ControlConfig_t *p_conf = pControl->p_conf; + Inic_ControlPacket_t *pPkt; + Inic_CmdInitInicReq_t *pInitInicReq; + ib_api_status_t ib_status; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + control_initHdr( pControl, CMD_INIT_INIC ); + + pPkt = control_packet( &pControl->sendIo ); + pInitInicReq = &pPkt->cmd.initInicReq; + pInitInicReq->inicMajorVersion = hton16( INIC_MAJORVERSION ); + pInitInicReq->inicMinorVersion = hton16( INIC_MINORVERSION ); + pInitInicReq->inicInstance = p_conf->inicInstance; + pInitInicReq->numDataPaths = 1; + pInitInicReq->numAddressEntries = hton16( p_conf->maxAddressEntries ); + + ib_status = control_send( pControl ); + VNIC_EXIT( VNIC_DBG_CTRL ); + return ib_status; +} + + +BOOLEAN +control_initInicRsp( + IN Control_t *pControl, + IN uint32_t *pFeatures, + IN uint8_t *pMacAddress, + IN uint16_t *pNumAddrs, + IN uint16_t *pVlan ) +{ + RecvIo_t *pRecvIo; + ControlConfig_t *p_conf = pControl->p_conf; + Inic_ControlPacket_t *pPkt; + Inic_CmdInitInicRsp_t *pInitInicRsp; + uint8_t numDataPaths, + numLanSwitches; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + pRecvIo = control_getRsp( pControl ); + if (!pRecvIo) + return FALSE; + + pPkt = control_packet( pRecvIo ); + + if ( pPkt->hdr.pktCmd != CMD_INIT_INIC ) + { + + VNIC_TRACE( VNIC_DBG_CTRL | VNIC_DBG_ERROR, + ("%s: Sent control request:\n", + control_ifcfg_name(pControl)) ); + + __control_logControlPacket( control_lastReq( pControl ) ); + + VNIC_TRACE_EXIT( VNIC_DBG_CTRL | VNIC_DBG_ERROR, + ("%s: Received control response:\n", + control_ifcfg_name(pControl)) ); + + __control_logControlPacket( pPkt ); + goto failure; + } + pInitInicRsp = &pPkt->cmd.initInicRsp; + pControl->majVer = ntoh16( pInitInicRsp->inicMajorVersion ); + pControl->minVer = ntoh16( pInitInicRsp->inicMinorVersion ); + numDataPaths = pInitInicRsp->numDataPaths; + numLanSwitches = pInitInicRsp->numLanSwitches; + *pFeatures = ntoh32( pInitInicRsp->featuresSupported ); + *pNumAddrs = ntoh16( pInitInicRsp->numAddressEntries ); + + if ( ( pControl->majVer > INIC_MAJORVERSION ) || + (( pControl->majVer == INIC_MAJORVERSION ) && + ( pControl->minVer > INIC_MINORVERSION )) ) + { + VNIC_TRACE_EXIT( VNIC_DBG_CTRL | VNIC_DBG_ERROR, + ("%s: Unsupported version\n", + control_ifcfg_name(pControl)) ); + goto failure; + } + + if ( numDataPaths != 1 ) + { + VNIC_TRACE_EXIT( VNIC_DBG_CTRL | VNIC_DBG_ERROR, + ("%s: EIOC returned too many datapaths\n", + control_ifcfg_name(pControl)) ); + goto failure; + } + + if ( *pNumAddrs > p_conf->maxAddressEntries ) + { + VNIC_TRACE_EXIT( VNIC_DBG_CTRL | VNIC_DBG_ERROR, + ("%s: EIOC returned more Address entries than requested\n", + control_ifcfg_name(pControl)) ); + goto failure; + } + if ( *pNumAddrs < p_conf->minAddressEntries ) + { + VNIC_TRACE_EXIT( VNIC_DBG_CTRL | VNIC_DBG_ERROR, + ("%s: Not enough address entries\n", + control_ifcfg_name(pControl)) ); + goto failure; + } + if ( numLanSwitches < 1 ) + { + VNIC_TRACE_EXIT( VNIC_DBG_CTRL | VNIC_DBG_ERROR, + ("%s: EIOC returned no lan switches\n", + control_ifcfg_name(pControl)) ); + goto failure; + } + if ( numLanSwitches > 1 ) + { + VNIC_TRACE_EXIT( VNIC_DBG_CTRL | VNIC_DBG_ERROR, + ("%s: EIOC returned multiple lan switches\n", + control_ifcfg_name(pControl)) ); + goto failure; + } + + pControl->lanSwitch.lanSwitchNum = + pInitInicRsp->lanSwitch[0].lanSwitchNum ; + pControl->lanSwitch.numEnetPorts = + pInitInicRsp->lanSwitch[0].numEnetPorts ; + pControl->lanSwitch.defaultVlan = + ntoh16( pInitInicRsp->lanSwitch[0].defaultVlan ); + *pVlan = pControl->lanSwitch.defaultVlan; + cl_memcpy( pControl->lanSwitch.hwMacAddress, + pInitInicRsp->lanSwitch[0].hwMacAddress, MAC_ADDR_LEN ); + cl_memcpy( pMacAddress, + pInitInicRsp->lanSwitch[0].hwMacAddress, MAC_ADDR_LEN); + + control_recv( pControl, pRecvIo ); + + VNIC_EXIT( VNIC_DBG_CTRL ); + return TRUE; +failure: + viport_failure( pControl->p_viport ); + return FALSE; +} + + +ib_api_status_t +control_configDataPathReq( + IN Control_t *pControl, + IN uint64_t pathId, + IN Inic_RecvPoolConfig_t *pHost, + IN Inic_RecvPoolConfig_t *pEioc ) +{ + Inic_ControlPacket_t *pPkt; + Inic_CmdConfigDataPath_t *pConfigDataPath; + ib_api_status_t ib_status; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + control_initHdr( pControl, CMD_CONFIG_DATA_PATH ); + + pPkt = control_packet( &pControl->sendIo ); + pConfigDataPath = &pPkt->cmd.configDataPathReq; + NdisZeroMemory( pConfigDataPath, sizeof( Inic_CmdConfigDataPath_t ) ); + pConfigDataPath->dataPath = 0; + pConfigDataPath->pathIdentifier = pathId; + copyRecvPoolConfig( pHost, &pConfigDataPath->hostRecvPoolConfig ); + copyRecvPoolConfig( pEioc, &pConfigDataPath->eiocRecvPoolConfig ); + + ib_status = control_send( pControl ); + VNIC_EXIT( VNIC_DBG_CTRL ); + return ib_status; +} + + +BOOLEAN +control_configDataPathRsp( + IN Control_t *pControl, + IN Inic_RecvPoolConfig_t *pHost, + IN Inic_RecvPoolConfig_t *pEioc, + IN Inic_RecvPoolConfig_t *pMaxHost, + IN Inic_RecvPoolConfig_t *pMaxEioc, + IN Inic_RecvPoolConfig_t *pMinHost, + IN Inic_RecvPoolConfig_t *pMinEioc ) +{ + RecvIo_t *pRecvIo; + Inic_ControlPacket_t *pPkt; + Inic_CmdConfigDataPath_t *pConfigDataPath; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + pRecvIo = control_getRsp( pControl ); + if ( !pRecvIo ) + return FALSE; + + pPkt = control_packet( pRecvIo ); + + if ( pPkt->hdr.pktCmd != CMD_CONFIG_DATA_PATH ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Sent control request:\n", + control_ifcfg_name(pControl)) ); + + __control_logControlPacket( control_lastReq( pControl ) ); + + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Received control response:\n", + control_ifcfg_name( pControl )) ); + + __control_logControlPacket( pPkt ); + goto failure; + } + + pConfigDataPath = &pPkt->cmd.configDataPathRsp; + + if ( pConfigDataPath->dataPath != 0 ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Received CMD_CONFIG_DATA_PATH response for wrong data path: %u\n", + control_ifcfg_name( pControl ), pConfigDataPath->dataPath) ); + goto failure; + } + + if ( !checkRecvPoolConfig(&pConfigDataPath->hostRecvPoolConfig, + pHost, pMaxHost, pMinHost ) + || !checkRecvPoolConfig(&pConfigDataPath->eiocRecvPoolConfig, + pEioc, pMaxEioc, pMinEioc)) + { + goto failure; + } + + control_recv( pControl, pRecvIo ); + + VNIC_EXIT( VNIC_DBG_CTRL ); + return TRUE; +failure: + viport_failure( pControl->p_viport ); + VNIC_EXIT( VNIC_DBG_CTRL ); + return FALSE; +} + + +ib_api_status_t +control_exchangePoolsReq( + IN Control_t *pControl, + IN uint64_t addr, + IN uint32_t rkey ) +{ + Inic_CmdExchangePools_t *pExchangePools; + Inic_ControlPacket_t *pPkt; + ib_api_status_t ib_status; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + control_initHdr(pControl, CMD_EXCHANGE_POOLS ); + + pPkt = control_packet( &pControl->sendIo ); + pExchangePools = &pPkt->cmd.exchangePoolsReq; + NdisZeroMemory( pExchangePools, sizeof( Inic_CmdExchangePools_t ) ); + pExchangePools->dataPath = 0; + pExchangePools->poolRKey = rkey; + pExchangePools->poolAddr = hton64( addr ); + + ib_status = control_send( pControl ); + VNIC_EXIT( VNIC_DBG_CTRL ); + return ib_status; +} + + +BOOLEAN +control_exchangePoolsRsp( + IN Control_t *pControl, + IN OUT uint64_t *pAddr, + IN OUT uint32_t *pRkey ) +{ + RecvIo_t *pRecvIo; + Inic_ControlPacket_t *pPkt; + Inic_CmdExchangePools_t *pExchangePools; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + pRecvIo = control_getRsp( pControl ); + if ( !pRecvIo ) + return FALSE; + + pPkt = control_packet( pRecvIo ); + + if ( pPkt->hdr.pktCmd != CMD_EXCHANGE_POOLS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Sent control request:\n", + control_ifcfg_name(pControl)) ); + + __control_logControlPacket( control_lastReq(pControl ) ); + + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Received control response:\n", + control_ifcfg_name(pControl)) ); + + __control_logControlPacket( pPkt ); + goto failure; + } + + pExchangePools = &pPkt->cmd.exchangePoolsRsp; + *pRkey = pExchangePools->poolRKey; + *pAddr = ntoh64( pExchangePools->poolAddr ); + + if ( hton32( pExchangePools->dataPath ) != 0 ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("%s: Received CMD_EXCHANGE_POOLS response for wrong data path: %u\n", + control_ifcfg_name(pControl), pExchangePools->dataPath ) ); + goto failure; + } + + control_recv( pControl, pRecvIo ); + + VNIC_EXIT( VNIC_DBG_CTRL ); + return TRUE; + +failure: + viport_failure( pControl->p_viport ); + return FALSE; +} + + +ib_api_status_t +control_configLinkReq( + IN Control_t *pControl, + IN uint8_t flags, + IN uint16_t mtu ) +{ + Inic_CmdConfigLink_t *pConfigLinkReq; + Inic_ControlPacket_t *pPkt; + ib_api_status_t ib_status; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + control_initHdr( pControl, CMD_CONFIG_LINK ); + + pPkt = control_packet( &pControl->sendIo ); + pConfigLinkReq = &pPkt->cmd.configLinkReq; + NdisZeroMemory( pConfigLinkReq, sizeof( Inic_CmdConfigLink_t ) ); + pConfigLinkReq->lanSwitchNum = pControl->lanSwitch.lanSwitchNum; + pConfigLinkReq->cmdFlags = INIC_FLAG_SET_MTU; + + if ( flags & INIC_FLAG_ENABLE_NIC ) + { + pConfigLinkReq->cmdFlags |= INIC_FLAG_ENABLE_NIC; + } + else + { + pConfigLinkReq->cmdFlags |= INIC_FLAG_DISABLE_NIC; + } + + if (flags & INIC_FLAG_ENABLE_MCAST_ALL ) + { + pConfigLinkReq->cmdFlags |= INIC_FLAG_ENABLE_MCAST_ALL; + } + else + { + pConfigLinkReq->cmdFlags |= INIC_FLAG_DISABLE_MCAST_ALL; + } + if (flags & INIC_FLAG_ENABLE_PROMISC ) + { + pConfigLinkReq->cmdFlags |= INIC_FLAG_ENABLE_PROMISC; + /* The EIOU doesn't really do PROMISC mode. + * If PROMISC is set, it only receives unicast packets + * I also have to set MCAST_ALL if I want real + * PROMISC mode. + */ + pConfigLinkReq->cmdFlags &= ~INIC_FLAG_DISABLE_MCAST_ALL; + pConfigLinkReq->cmdFlags |= INIC_FLAG_ENABLE_MCAST_ALL; + } + else + { + pConfigLinkReq->cmdFlags |= INIC_FLAG_DISABLE_PROMISC; + } + pConfigLinkReq->mtuSize = hton16( mtu ); + + ib_status = control_send( pControl ); + VNIC_EXIT( VNIC_DBG_CTRL ); + return ib_status; +} + + +BOOLEAN +control_configLinkRsp( + IN Control_t *pControl, + IN OUT uint8_t *pFlags, + IN OUT uint16_t *pMtu ) +{ + RecvIo_t *pRecvIo; + Inic_ControlPacket_t *pPkt; + Inic_CmdConfigLink_t *pConfigLinkRsp; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + pRecvIo = control_getRsp( pControl ); + if ( !pRecvIo ) + return FALSE; + + pPkt = control_packet( pRecvIo ); + if ( pPkt->hdr.pktCmd != CMD_CONFIG_LINK ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Sent control request:\n", + control_ifcfg_name( pControl )) ); + + __control_logControlPacket( control_lastReq( pControl ) ); + + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Received control response:\n", + control_ifcfg_name( pControl )) ); + + __control_logControlPacket( pPkt ); + goto failure; + } + + pConfigLinkRsp = &pPkt->cmd.configLinkRsp; + + *pFlags = pConfigLinkRsp->cmdFlags; + *pMtu = ntoh16( pConfigLinkRsp->mtuSize ); + + control_recv( pControl, pRecvIo ); + + VNIC_EXIT( VNIC_DBG_CTRL ); + return TRUE; + +failure: + viport_failure( pControl->p_viport ); + VNIC_EXIT( VNIC_DBG_CTRL ); + return FALSE; +} + +/* control_configAddrsReq: + * Return values: + * -1: failure + * 0: incomplete (successful operation, but more address + * table entries to be updated) + * 1: complete + */ +ib_api_status_t +control_configAddrsReq( + IN Control_t *pControl, + IN Inic_AddressOp_t *pAddrs, + IN uint16_t num, + OUT int32_t *pAddrQueryDone ) +{ + Inic_CmdConfigAddresses_t *pConfigAddrsReq; + Inic_ControlPacket_t *pPkt; + uint16_t i; + uint8_t j; + ib_api_status_t ib_status; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + control_initHdr( pControl, CMD_CONFIG_ADDRESSES ); + + pPkt = control_packet( &pControl->sendIo ); + pConfigAddrsReq = &pPkt->cmd.configAddressesReq; + NdisZeroMemory( pConfigAddrsReq, sizeof( Inic_CmdConfigAddresses_t ) ); + pConfigAddrsReq->lanSwitchNum = pControl->lanSwitch.lanSwitchNum; + + for ( i=0, j = 0; ( i < num ) && ( j < 16 ); i++ ) + { + if ( !pAddrs[i].operation ) + continue; + + pConfigAddrsReq->listAddressOps[j].index = hton16(i); + pConfigAddrsReq->listAddressOps[j].operation = INIC_OP_SET_ENTRY; + pConfigAddrsReq->listAddressOps[j].valid = pAddrs[i].valid; + + cl_memcpy( pConfigAddrsReq->listAddressOps[j].address, + pAddrs[i].address, MAC_ADDR_LEN ); + pConfigAddrsReq->listAddressOps[j].vlan = hton16( pAddrs[i].vlan ); + pAddrs[i].operation = 0; + j++; + } + pConfigAddrsReq->numAddressOps = j; + + for ( ; i < num; i++ ) + { + if ( pAddrs[i].operation ) + break; + } + *pAddrQueryDone = (i == num); + + ib_status = control_send( pControl ); + VNIC_EXIT( VNIC_DBG_CTRL ); + return ib_status; +} + + +BOOLEAN +control_configAddrsRsp( + IN Control_t *pControl ) +{ + RecvIo_t *pRecvIo; + Inic_ControlPacket_t *pPkt; + Inic_CmdConfigAddresses_t *pConfigAddrsRsp; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + pRecvIo = control_getRsp( pControl ); + if ( !pRecvIo ) + return FALSE; + + pPkt = control_packet( pRecvIo ); + if ( pPkt->hdr.pktCmd != CMD_CONFIG_ADDRESSES ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Sent control request:\n", + control_ifcfg_name( pControl )) ); + + __control_logControlPacket( control_lastReq( pControl ) ); + + VNIC_TRACE_EXIT(VNIC_DBG_ERROR, + ("%s: Received control response:\n", + control_ifcfg_name( pControl )) ); + + __control_logControlPacket( pPkt ); + + goto failure; + } + + pConfigAddrsRsp = &pPkt->cmd.configAddressesRsp; + + control_recv( pControl, pRecvIo ); + VNIC_EXIT( VNIC_DBG_CTRL ); + return TRUE; + +failure: + viport_failure( pControl->p_viport ); + return FALSE; +} + + +ib_api_status_t +control_reportStatisticsReq( + IN Control_t *pControl ) +{ + Inic_ControlPacket_t *pPkt; + Inic_CmdReportStatisticsReq_t *pReportStatisticsReq; + ib_api_status_t ib_status; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + control_initHdr( pControl, CMD_REPORT_STATISTICS ); + + pPkt = control_packet( &pControl->sendIo ); + pReportStatisticsReq = &pPkt->cmd.reportStatisticsReq; + pReportStatisticsReq->lanSwitchNum = pControl->lanSwitch.lanSwitchNum; + + ib_status = control_send( pControl ); + VNIC_EXIT( VNIC_DBG_CTRL ); + return ib_status; +} + + +BOOLEAN +control_reportStatisticsRsp( + IN Control_t *pControl, + IN Inic_CmdReportStatisticsRsp_t *pStats ) +{ + RecvIo_t *pRecvIo; + Inic_ControlPacket_t *pPkt; + Inic_CmdReportStatisticsRsp_t *pRepStatRsp; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + pRecvIo = control_getRsp( pControl ); + if (!pRecvIo) + return FALSE; + + pPkt = control_packet( pRecvIo ); + if ( pPkt->hdr.pktCmd != CMD_REPORT_STATISTICS ) + { + VNIC_TRACE(VNIC_DBG_ERROR, + ("%s: Sent control request:\n", + control_ifcfg_name( pControl )) ); + + __control_logControlPacket( control_lastReq( pControl ) ); + + VNIC_TRACE_EXIT(VNIC_DBG_ERROR, + ("%s: Received control response:\n", + control_ifcfg_name( pControl ) ) ); + + __control_logControlPacket( pPkt ); + + goto failure; + } + + pRepStatRsp = &pPkt->cmd.reportStatisticsRsp; + pStats->ifInBroadcastPkts = ntoh64(pRepStatRsp->ifInBroadcastPkts); + pStats->ifInMulticastPkts = ntoh64(pRepStatRsp->ifInMulticastPkts); + pStats->ifInOctets = ntoh64(pRepStatRsp->ifInOctets); + pStats->ifInUcastPkts = ntoh64(pRepStatRsp->ifInUcastPkts); + pStats->ifInNUcastPkts = ntoh64(pRepStatRsp->ifInNUcastPkts); + pStats->ifInUnderrun = ntoh64(pRepStatRsp->ifInUnderrun); + pStats->ifInErrors = ntoh64(pRepStatRsp->ifInErrors); + pStats->ifOutErrors = ntoh64(pRepStatRsp->ifOutErrors); + pStats->ifOutOctets = ntoh64(pRepStatRsp->ifOutOctets); + pStats->ifOutUcastPkts = ntoh64(pRepStatRsp->ifOutUcastPkts); + pStats->ifOutMulticastPkts = ntoh64(pRepStatRsp->ifOutMulticastPkts); + pStats->ifOutBroadcastPkts = ntoh64(pRepStatRsp->ifOutBroadcastPkts); + pStats->ifOutNUcastPkts = ntoh64(pRepStatRsp->ifOutNUcastPkts); + pStats->ifOutOk = ntoh64(pRepStatRsp->ifOutOk); + pStats->ifInOk = ntoh64(pRepStatRsp->ifInOk); + pStats->ifOutUcastBytes = ntoh64(pRepStatRsp->ifOutUcastBytes); + pStats->ifOutMulticastBytes = ntoh64(pRepStatRsp->ifOutMulticastBytes); + pStats->ifOutBroadcastBytes = ntoh64(pRepStatRsp->ifOutBroadcastBytes); + pStats->ifInUcastBytes = ntoh64(pRepStatRsp->ifInUcastBytes); + pStats->ifInMulticastBytes = ntoh64(pRepStatRsp->ifInMulticastBytes); + pStats->ifInBroadcastBytes = ntoh64(pRepStatRsp->ifInBroadcastBytes); + pStats->ethernetStatus = ntoh64(pRepStatRsp->ethernetStatus); + + control_recv(pControl, pRecvIo); + + VNIC_EXIT( VNIC_DBG_CTRL ); + return TRUE; +failure: + viport_failure( pControl->p_viport ); + return FALSE; +} + + +ib_api_status_t +control_resetReq( + IN Control_t *pControl ) +{ + ib_api_status_t ib_status; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + control_initHdr( pControl, CMD_RESET ); + ib_status = control_send( pControl ); + + VNIC_EXIT( VNIC_DBG_CTRL ); + return ib_status; +} + +BOOLEAN +control_resetRsp( + IN Control_t *pControl ) +{ + RecvIo_t *pRecvIo; + Inic_ControlPacket_t *pPkt; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + pRecvIo = control_getRsp( pControl ); + if ( !pRecvIo ) return FALSE; + + pPkt = control_packet( pRecvIo ); + + if ( pPkt->hdr.pktCmd != CMD_RESET ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Sent control request:\n", + control_ifcfg_name( pControl )) ); + + __control_logControlPacket( control_lastReq( pControl ) ); + + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Received control response:\n", + control_ifcfg_name( pControl )) ); + + __control_logControlPacket( pPkt ); + + goto failure; + } + + control_recv( pControl, pRecvIo ); + VNIC_EXIT( VNIC_DBG_CTRL ); + return TRUE; +failure: + viport_failure( pControl->p_viport ); + VNIC_EXIT( VNIC_DBG_CTRL ); + return FALSE; +} + + +ib_api_status_t +control_heartbeatReq( + IN Control_t *pControl, + IN uint32_t hbInterval ) +{ + Inic_ControlPacket_t *pPkt; + Inic_CmdHeartbeat_t *pHeartbeatReq; + ib_api_status_t ib_status; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + control_initHdr(pControl, CMD_HEARTBEAT); + + pPkt = control_packet(&pControl->sendIo); + pHeartbeatReq = &pPkt->cmd.heartbeatReq; + + /* pass timeout for the target in microseconds */ + pHeartbeatReq->hbInterval = hton32( hbInterval*1000 ); + + ib_status = control_send( pControl ); + ASSERT( ib_status == IB_SUCCESS ); + VNIC_EXIT( VNIC_DBG_CTRL ); + return ib_status; +} + +BOOLEAN +control_heartbeatRsp( + IN Control_t *pControl ) +{ + RecvIo_t *pRecvIo; + Inic_ControlPacket_t *pPkt; + Inic_CmdHeartbeat_t *pHeartbeatRsp; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + pRecvIo = control_getRsp( pControl ); + + if (!pRecvIo) + return FALSE; + + pPkt = control_packet(pRecvIo); + + if ( pPkt->hdr.pktCmd != CMD_HEARTBEAT ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Sent control request:\n", + control_ifcfg_name(pControl)) ); + + __control_logControlPacket( control_lastReq(pControl) ); + + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Received control response:\n", + control_ifcfg_name(pControl)) ); + + __control_logControlPacket( pPkt ); + goto failure; + } + + pHeartbeatRsp = &pPkt->cmd.heartbeatRsp; + + control_recv( pControl, pRecvIo ); + VNIC_EXIT ( VNIC_DBG_CTRL ); + return TRUE; + +failure: + viport_failure( pControl->p_viport ); + VNIC_EXIT ( VNIC_DBG_CTRL ); + return FALSE; +} + +static void +control_recv( + IN Control_t *pControl, + IN RecvIo_t *pRecvIo ) +{ + VNIC_ENTER( VNIC_DBG_CTRL ); + + if ( ibqp_postRecv( &pControl->qp, &pRecvIo->io ) != IB_SUCCESS ) + viport_failure( pControl->p_viport ); + + VNIC_EXIT ( VNIC_DBG_CTRL ); +} + + +static void +control_recvComplete( + IN Io_t *pIo ) +{ + RecvIo_t *pRecvIo = (RecvIo_t *)pIo; + RecvIo_t *pLastRecvIo; + BOOLEAN status = FALSE; + Control_t *pControl = &pIo->pViport->control; + viport_t *p_viport = pIo->pViport; + Inic_ControlPacket_t *pPkt = control_packet(pRecvIo); + Inic_ControlHeader_t *pCHdr = &pPkt->hdr; + + switch ( pCHdr->pktType ) + { + case TYPE_INFO: + pLastRecvIo = InterlockedExchangePointer( &pControl->pInfo, pRecvIo ); + + control_processAsync( pControl ); + + if ( pLastRecvIo ) + { + control_recv( pControl, pLastRecvIo ); + } + return; + + case TYPE_RSP: + break; + + default: + //TODO: Should we ever reach this? Who processes the list entries? + ASSERT( pCHdr->pktType == TYPE_INFO || pCHdr->pktType == TYPE_RSP ); + ExInterlockedInsertTailList( &pRecvIo->io.listPtrs, + &pControl->failureList, + &pControl->ioLock ); + return; + } + + if( (pCHdr->pktSeqNum != pControl->seqNum) || + !InterlockedExchange( (volatile LONG*)&pControl->rspExpected, FALSE ) ) + { + return; + } + + InterlockedExchangePointer( &pControl->pResponse, pRecvIo ); + + switch ( pCHdr->pktCmd ) + { + case CMD_INIT_INIC: + status = control_initInicRsp( pControl, + &p_viport->featuresSupported, + p_viport->hwMacAddress, + &p_viport->numMacAddresses, + &p_viport->defaultVlan ); + if( status ) + { + InterlockedExchange( + (volatile LONG*)&p_viport->linkState, + (LONG)LINK_INITINICRSP ); + } + InterlockedOr( &p_viport->updates, SYNC_QUERY ); + break; + + case CMD_CONFIG_DATA_PATH: + status = control_configDataPathRsp( &p_viport->control, + data_hostPool( &p_viport->data ), + data_eiocPool( &p_viport->data ), + data_hostPoolMax( &p_viport->data ), + data_eiocPoolMax( &p_viport->data ), + data_hostPoolMin( &p_viport->data ), + data_eiocPoolMin( &p_viport->data )); + if( status ) + { + InterlockedExchange( + (volatile LONG*)&p_viport->linkState, + (LONG)LINK_CONFIGDATAPATHRSP ); + } + InterlockedOr( &p_viport->updates, SYNC_QUERY ); + break; + + case CMD_EXCHANGE_POOLS: + status = control_exchangePoolsRsp( &p_viport->control, + data_remotePoolAddr( &p_viport->data ), + data_remotePoolRkey( &p_viport->data ) ); + if( status ) + { + InterlockedExchange( + (volatile LONG*)&p_viport->linkState, + (LONG)LINK_XCHGPOOLRSP ); + } + InterlockedOr( &p_viport->updates, SYNC_QUERY ); + break; + + /* process other responses */ + case CMD_CONFIG_LINK: + status = control_configLinkRsp( &p_viport->control, + &p_viport->flags, + &p_viport->mtu ); + if( status ) + { + InterlockedExchange( + (volatile LONG*)&p_viport->linkState, + (LONG)LINK_CONFIGLINKRSP ); + + if( p_viport->flags & INIC_FLAG_ENABLE_NIC ) + { + InterlockedExchange( &p_viport->p_netpath->carrier, TRUE ); + } + else + { + InterlockedExchange( &p_viport->p_netpath->carrier, FALSE ); + } + InterlockedAnd( &p_viport->updates, ~NEED_LINK_CONFIG ); + } + break; + + case CMD_HEARTBEAT: + status = control_heartbeatRsp( pControl ); + if( status && + !p_viport->errored && + !p_viport->disconnect ) + { + status = FALSE; + viport_timer( p_viport, p_viport->port_config.hbInterval ); + } + // Don't signal any waiting thread or start processing other updates. + return; + + case CMD_CONFIG_ADDRESSES: + status = control_configAddrsRsp( pControl ); + if( status == TRUE ) + { + if( pControl->p_viport->addrs_query_done == 0 ) + { + // need more entries to send + // TODO: Handle a send failure. + control_configAddrsReq( pControl, + pControl->p_viport->macAddresses, + pControl->p_viport->numMacAddresses, + &pControl->p_viport->addrs_query_done ); + // Don't signal any waiting thread or start processing other updates. + return; + } + + InterlockedAnd( &p_viport->updates, ~NEED_ADDRESS_CONFIG ); + InterlockedExchange( (volatile LONG*)&p_viport->linkState, + (LONG)LINK_CONFIGADDRSRSP ); + } + break; + + case CMD_REPORT_STATISTICS: + status = control_reportStatisticsRsp( pControl, &p_viport->stats ); + if ( status ) + { + if( p_viport->stats.ethernetStatus > 0 ) + { + viport_linkUp( p_viport ); + } + else + { + viport_linkDown( p_viport ); + } + } + InterlockedAnd( &p_viport->updates, ~NEED_STATS ); + break; + + case CMD_RESET: + status = control_resetRsp( pControl ); + if( status ) + { + status = FALSE; + InterlockedExchange( + (volatile LONG*)&p_viport->linkState, + (LONG)LINK_RESETRSP ); + } + break; + + default: + break; + } + + if( _viport_process_query( p_viport, FALSE ) == STATUS_SUCCESS ) + { + /* Complete any pending set OID. */ + vnic_resume_set_oids( p_viport->p_adapter ); + } + + if( InterlockedAnd( &p_viport->updates, ~SYNC_QUERY ) & SYNC_QUERY ) + cl_event_signal( &p_viport->conn_event ); +} + + +static ib_api_status_t +control_send( + IN Control_t *pControl ) +{ + ib_api_status_t ib_status; + Inic_ControlPacket_t *pPkt = control_packet(&pControl->sendIo); + + VNIC_ENTER ( VNIC_DBG_CTRL ); + + ASSERT( !pControl->reqOutstanding ); + if ( InterlockedCompareExchange( (volatile LONG*)&pControl->reqOutstanding, + TRUE, FALSE ) == TRUE ) + { + /* IF WE HIT THIS WE ARE HOSED!!! + * by the time we detect this error, the send buffer has been + * overwritten, and if we retry we will send garbage data. + */ + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("IB Send never completed\n" ) ); + viport_failure( pControl->p_viport ); + return IB_ERROR; + } + +#ifdef _DEBUG_ + __control_logControlPacket( pPkt ); +#endif + + InterlockedExchange( (volatile LONG*)&pControl->rspExpected, + (LONG)pPkt->hdr.pktCmd ); + + control_timer( pControl, pControl->p_conf->rspTimeout ); + +#ifdef VNIC_STATISTIC + pControl->statistics.requestTime = cl_get_time_stamp(); +#endif /* VNIC_STATISTIC */ + + ib_status = ibqp_postSend( &pControl->qp, &pControl->sendIo.io ); + if( ib_status != IB_SUCCESS ) + { + InterlockedExchange((volatile LONG*)&pControl->reqOutstanding, FALSE ); + + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Failed to post send\n", control_ifcfg_name(pControl)) ); + viport_failure( pControl->p_viport ); + } + + VNIC_EXIT( VNIC_DBG_CTRL ); + return ib_status; +} + + +static void +control_sendComplete( + IN Io_t *pIo ) +{ + Control_t *pControl = &pIo->pViport->control; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + InterlockedExchange((volatile LONG*)&pControl->reqOutstanding, FALSE ); + + VNIC_EXIT( VNIC_DBG_CTRL ); + return; +} + +static void +control_timeout( + IN void *p_context ) +{ + Control_t *pControl; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + pControl = (Control_t *)p_context; + + InterlockedExchange( (LONG *)&pControl->timerstate, (LONG)TIMER_EXPIRED ); + + InterlockedExchange( (volatile LONG*)&pControl->rspExpected, FALSE ); + + VNIC_EXIT( VNIC_DBG_CTRL ); + return; +} + +static void +control_timer( + IN Control_t *pControl, + IN int timeout ) +{ + VNIC_ENTER( VNIC_DBG_CTRL ); + + InterlockedExchange( (LONG *)&pControl->timerstate, (LONG)TIMER_ACTIVE ); + + cl_timer_start(&pControl->timer, timeout); + + VNIC_EXIT( VNIC_DBG_CTRL ); + return; +} + +static void +control_timerStop( + IN Control_t *pControl ) +{ + VNIC_ENTER( VNIC_DBG_CTRL ); + + + if ( ( InterlockedExchange( (LONG *)&pControl->timerstate, + (LONG)TIMER_IDLE )) == TIMER_ACTIVE ) + { + cl_timer_stop( &pControl->timer ); + } + + VNIC_EXIT( VNIC_DBG_CTRL ); + return; +} + +static void +control_initHdr( + IN Control_t * pControl, + IN uint8_t cmd ) +{ + ControlConfig_t *p_conf; + Inic_ControlPacket_t *pPkt; + Inic_ControlHeader_t *pHdr; + + VNIC_ENTER( VNIC_DBG_CTRL ); + + p_conf = pControl->p_conf; + + pPkt = control_packet( &pControl->sendIo ); + pHdr = &pPkt->hdr; + + pHdr->pktType = TYPE_REQ; + pHdr->pktCmd = cmd; + pHdr->pktSeqNum = ++pControl->seqNum; + pControl->reqRetryCounter = 0; + pHdr->pktRetryCount = 0; + + VNIC_EXIT( VNIC_DBG_CTRL ); +} + +static RecvIo_t* +control_getRsp( + IN Control_t *pControl ) +{ + RecvIo_t *pRecvIo; + + VNIC_ENTER ( VNIC_DBG_CTRL ); + + pRecvIo = InterlockedExchangePointer( &pControl->pResponse, NULL ); + + if ( pRecvIo != NULL ) + { + control_timerStop(pControl); + return pRecvIo; + } + + if ( ( pControl->timerstate = + InterlockedCompareExchange( (LONG *)&pControl->timerstate, + (LONG)TIMER_IDLE, (LONG)TIMER_EXPIRED )) == TIMER_EXPIRED ) + { + Inic_ControlPacket_t *pPkt = control_packet( &pControl->sendIo ); + Inic_ControlHeader_t *pHdr = &pPkt->hdr; + + VNIC_TRACE( VNIC_DBG_CTRL| VNIC_DBG_ERROR, + ("%s: No response received from EIOC\n", + control_ifcfg_name(pControl)) ); +#ifdef VNIC_STATISTIC + pControl->statistics.timeoutNum++; +#endif /* VNIC_STATISTIC */ + + pControl->reqRetryCounter++; + + if ( pControl->reqRetryCounter >= pControl->p_conf->reqRetryCount ) + { + VNIC_TRACE( VNIC_DBG_CTRL| VNIC_DBG_ERROR, + ("%s: Control packet retry exceeded\n", + control_ifcfg_name(pControl)) ); + viport_failure(pControl->p_viport ); + } + else + { + pHdr->pktRetryCount = pControl->reqRetryCounter; + control_send( pControl ); + } + } + return NULL; +} + +static void +copyRecvPoolConfig( + IN Inic_RecvPoolConfig_t *pSrc, + IN OUT Inic_RecvPoolConfig_t *pDst ) +{ + + pDst->sizeRecvPoolEntry = hton32(pSrc->sizeRecvPoolEntry); + pDst->numRecvPoolEntries = hton32(pSrc->numRecvPoolEntries); + pDst->timeoutBeforeKick = hton32(pSrc->timeoutBeforeKick); + pDst->numRecvPoolEntriesBeforeKick = hton32(pSrc->numRecvPoolEntriesBeforeKick); + pDst->numRecvPoolBytesBeforeKick = hton32(pSrc->numRecvPoolBytesBeforeKick); + pDst->freeRecvPoolEntriesPerUpdate = hton32(pSrc->freeRecvPoolEntriesPerUpdate); + return; +} + +static BOOLEAN +checkRecvPoolConfigValue( + IN void *pSrc, + IN void *pDst, + IN void *pMax, + IN void *pMin, + IN char *name ) +{ + uint32_t value; + uint32_t *p_src = (uint32_t *)pSrc; + uint32_t *p_dst = (uint32_t *)pDst; + uint32_t *p_min = (uint32_t *)pMin; + uint32_t *p_max = (uint32_t *)pMax; + + UNREFERENCED_PARAMETER( name ); + + value = ntoh32( *p_src ); + + if (value > *p_max ) + { + VNIC_TRACE( VNIC_DBG_CTRL| VNIC_DBG_ERROR, + ("Value %s too large\n", name) ); + return FALSE; + } + else if (value < *p_min ) + { + VNIC_TRACE( VNIC_DBG_CTRL| VNIC_DBG_ERROR, + ("Value %s too small\n", name) ); + return FALSE; + } + + *p_dst = value; + return TRUE; +} + +static BOOLEAN +checkRecvPoolConfig( + IN Inic_RecvPoolConfig_t *pSrc, + IN Inic_RecvPoolConfig_t *pDst, + IN Inic_RecvPoolConfig_t *pMax, + IN Inic_RecvPoolConfig_t *pMin ) +{ + if (!checkRecvPoolConfigValue(&pSrc->sizeRecvPoolEntry, &pDst->sizeRecvPoolEntry, + &pMax->sizeRecvPoolEntry, &pMin->sizeRecvPoolEntry, "sizeRecvPoolEntry") + || !checkRecvPoolConfigValue(&pSrc->numRecvPoolEntries, &pDst->numRecvPoolEntries, + &pMax->numRecvPoolEntries, &pMin->numRecvPoolEntries, "numRecvPoolEntries") + || !checkRecvPoolConfigValue(&pSrc->timeoutBeforeKick, &pDst->timeoutBeforeKick, + &pMax->timeoutBeforeKick, &pMin->timeoutBeforeKick, "timeoutBeforeKick") + || !checkRecvPoolConfigValue(&pSrc->numRecvPoolEntriesBeforeKick, + &pDst->numRecvPoolEntriesBeforeKick, &pMax->numRecvPoolEntriesBeforeKick, + &pMin->numRecvPoolEntriesBeforeKick, "numRecvPoolEntriesBeforeKick") + || !checkRecvPoolConfigValue(&pSrc->numRecvPoolBytesBeforeKick, + &pDst->numRecvPoolBytesBeforeKick, &pMax->numRecvPoolBytesBeforeKick, + &pMin->numRecvPoolBytesBeforeKick, "numRecvPoolBytesBeforeKick") + || !checkRecvPoolConfigValue(&pSrc->freeRecvPoolEntriesPerUpdate, + &pDst->freeRecvPoolEntriesPerUpdate, &pMax->freeRecvPoolEntriesPerUpdate, + &pMin->freeRecvPoolEntriesPerUpdate, "freeRecvPoolEntriesPerUpdate")) + return FALSE; + + if ( !IsPowerOf2( pDst->numRecvPoolEntries ) ) + { + VNIC_TRACE( VNIC_DBG_CTRL| VNIC_DBG_ERROR, + ("numRecvPoolEntries (%d) must be power of 2\n", + pDst->numRecvPoolEntries) ); + return FALSE; + } + if ( !IsPowerOf2( pDst->freeRecvPoolEntriesPerUpdate ) ) + { + VNIC_TRACE( VNIC_DBG_CTRL| VNIC_DBG_ERROR, + ("freeRecvPoolEntriesPerUpdate (%d) must be power of 2\n", + pDst->freeRecvPoolEntriesPerUpdate) ); + return FALSE; + } + if ( pDst->freeRecvPoolEntriesPerUpdate >= pDst->numRecvPoolEntries ) + { + VNIC_TRACE( VNIC_DBG_CTRL| VNIC_DBG_ERROR, + ("freeRecvPoolEntriesPerUpdate (%d) must be less than numRecvPoolEntries (%d)\n", + pDst->freeRecvPoolEntriesPerUpdate, pDst->numRecvPoolEntries) ); + return FALSE; + } + if ( pDst->numRecvPoolEntriesBeforeKick >= pDst->numRecvPoolEntries ) + { + VNIC_TRACE( VNIC_DBG_CTRL| VNIC_DBG_ERROR, + ("numRecvPoolEntriesBeforeKick (%d) must be less than numRecvPoolEntries (%d)\n", + pDst->numRecvPoolEntriesBeforeKick, pDst->numRecvPoolEntries) ); + return FALSE; + } + + return TRUE; +} + +static void +__control_logControlPacket( + IN Inic_ControlPacket_t *pPkt ) +{ + char *type; + int i; + + switch( pPkt->hdr.pktType ) + { + case TYPE_INFO: + type = "TYPE_INFO"; + break; + case TYPE_REQ: + type = "TYPE_REQ"; + break; + case TYPE_RSP: + type = "TYPE_RSP"; + break; + case TYPE_ERR: + type = "TYPE_ERR"; + break; + default: + type = "UNKNOWN"; + } + switch( pPkt->hdr.pktCmd ) + { + case CMD_INIT_INIC: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ("ControlPacket: pktType = %s, pktCmd = CMD_INIT_INIC\n", type ) ); + VNIC_PRINT( VNIC_DBG_CTRL| VNIC_DBG_INFO, + (" pktSeqNum = %u, pktRetryCount = %u\n", + pPkt->hdr.pktSeqNum, + pPkt->hdr.pktRetryCount) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" inicMajorVersion = %u, inicMinorVersion = %u\n", + ntoh16(pPkt->cmd.initInicReq.inicMajorVersion), + ntoh16(pPkt->cmd.initInicReq.inicMinorVersion)) ); + if (pPkt->hdr.pktType == TYPE_REQ) + { + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" inicInstance = %u, numDataPaths = %u\n", + pPkt->cmd.initInicReq.inicInstance, + pPkt->cmd.initInicReq.numDataPaths) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT| VNIC_DBG_INFO, + (" numAddressEntries = %u\n", + ntoh16(pPkt->cmd.initInicReq.numAddressEntries)) ); + } + else + { + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" numLanSwitches = %u, numDataPaths = %u\n", + pPkt->cmd.initInicRsp.numLanSwitches, + pPkt->cmd.initInicRsp.numDataPaths) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" numAddressEntries = %u, featuresSupported = %08x\n", + ntoh16(pPkt->cmd.initInicRsp.numAddressEntries), + ntoh32(pPkt->cmd.initInicRsp.featuresSupported)) ); + if (pPkt->cmd.initInicRsp.numLanSwitches != 0) + { + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ("lanSwitch[0] lanSwitchNum = %u, numEnetPorts = %08x\n", + pPkt->cmd.initInicRsp.lanSwitch[0].lanSwitchNum, + pPkt->cmd.initInicRsp.lanSwitch[0].numEnetPorts) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" defaultVlan = %u, hwMacAddress = %02x:%02x:%02x:%02x:%02x:%02x\n", + ntoh16(pPkt->cmd.initInicRsp.lanSwitch[0].defaultVlan), + pPkt->cmd.initInicRsp.lanSwitch[0].hwMacAddress[0], + pPkt->cmd.initInicRsp.lanSwitch[0].hwMacAddress[1], + pPkt->cmd.initInicRsp.lanSwitch[0].hwMacAddress[2], + pPkt->cmd.initInicRsp.lanSwitch[0].hwMacAddress[3], + pPkt->cmd.initInicRsp.lanSwitch[0].hwMacAddress[4], + pPkt->cmd.initInicRsp.lanSwitch[0].hwMacAddress[5]) ); + } + } + break; + case CMD_CONFIG_DATA_PATH: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ( "ControlPacket: pktType = %s, pktCmd = CMD_CONFIG_DATA_PATH\n", type) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" pktSeqNum = %u, pktRetryCount = %u\n", + pPkt->hdr.pktSeqNum, + pPkt->hdr.pktRetryCount) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" pathIdentifier = %"PRIx64", dataPath = %u\n", + pPkt->cmd.configDataPathReq.pathIdentifier, + pPkt->cmd.configDataPathReq.dataPath) ); + + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ("Host Config sizeRecvPoolEntry = %u, numRecvPoolEntries = %u\n", + ntoh32(pPkt->cmd.configDataPathReq.hostRecvPoolConfig.sizeRecvPoolEntry), + ntoh32(pPkt->cmd.configDataPathReq.hostRecvPoolConfig.numRecvPoolEntries)) ); + + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" timeoutBeforeKick = %u, numRecvPoolEntriesBeforeKick = %u\n", + ntoh32(pPkt->cmd.configDataPathReq.hostRecvPoolConfig.timeoutBeforeKick), + ntoh32(pPkt->cmd.configDataPathReq.hostRecvPoolConfig.numRecvPoolEntriesBeforeKick)) ); + + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" numRecvPoolBytesBeforeKick = %u, freeRecvPoolEntriesPerUpdate = %u\n", + ntoh32(pPkt->cmd.configDataPathReq.hostRecvPoolConfig.numRecvPoolBytesBeforeKick), + ntoh32(pPkt->cmd.configDataPathReq.hostRecvPoolConfig.freeRecvPoolEntriesPerUpdate)) ); + + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ("Eioc Config sizeRecvPoolEntry = %u, numRecvPoolEntries = %u\n", + ntoh32(pPkt->cmd.configDataPathReq.eiocRecvPoolConfig.sizeRecvPoolEntry), + ntoh32(pPkt->cmd.configDataPathReq.eiocRecvPoolConfig.numRecvPoolEntries)) ); + + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" timeoutBeforeKick = %u, numRecvPoolEntriesBeforeKick = %u\n", + ntoh32(pPkt->cmd.configDataPathReq.eiocRecvPoolConfig.timeoutBeforeKick), + ntoh32(pPkt->cmd.configDataPathReq.eiocRecvPoolConfig.numRecvPoolEntriesBeforeKick)) ); + + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" numRecvPoolBytesBeforeKick = %u, freeRecvPoolEntriesPerUpdate = %u\n", + ntoh32(pPkt->cmd.configDataPathReq.eiocRecvPoolConfig.numRecvPoolBytesBeforeKick), + ntoh32(pPkt->cmd.configDataPathReq.eiocRecvPoolConfig.freeRecvPoolEntriesPerUpdate)) ); + break; + case CMD_EXCHANGE_POOLS: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ("ControlPacket: pktType = %s, pktCmd = CMD_EXCHANGE_POOLS\n", type ) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" pktSeqNum = %u, pktRetryCount = %u\n", + pPkt->hdr.pktSeqNum, + pPkt->hdr.pktRetryCount) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" datapath = %u\n", + pPkt->cmd.exchangePoolsReq.dataPath) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" poolRKey = %08x poolAddr = %"PRIx64"\n", + ntoh32(pPkt->cmd.exchangePoolsReq.poolRKey), + ntoh64(pPkt->cmd.exchangePoolsReq.poolAddr)) ); + break; + case CMD_CONFIG_ADDRESSES: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ( "ControlPacket: pktType = %s, pktCmd = CMD_CONFIG_ADDRESSES\n", type ) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" pktSeqNum = %u, pktRetryCount = %u\n", + pPkt->hdr.pktSeqNum, + pPkt->hdr.pktRetryCount) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" numAddressOps = %x, lanSwitchNum = %d\n", + pPkt->cmd.configAddressesReq.numAddressOps, + pPkt->cmd.configAddressesReq.lanSwitchNum) ); + for (i = 0; ( i < pPkt->cmd.configAddressesReq.numAddressOps) && (i < 16); i++) + { + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" listAddressOps[%u].index = %u\n", + i, ntoh16(pPkt->cmd.configAddressesReq.listAddressOps[i].index)) ); + + switch(pPkt->cmd.configAddressesReq.listAddressOps[i].operation) + { + case INIC_OP_GET_ENTRY: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" listAddressOps[%u].operation = INIC_OP_GET_ENTRY\n", i) ); + break; + case INIC_OP_SET_ENTRY: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" listAddressOps[%u].operation = INIC_OP_SET_ENTRY\n", i) ); + break; + default: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" listAddressOps[%u].operation = UNKNOWN(%d)\n", + i, pPkt->cmd.configAddressesReq.listAddressOps[i].operation) ); + break; + } + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" listAddressOps[%u].valid = %u\n", + i, pPkt->cmd.configAddressesReq.listAddressOps[i].valid) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" listAddressOps[%u].address = %02x:%02x:%02x:%02x:%02x:%02x\n", i, + pPkt->cmd.configAddressesReq.listAddressOps[i].address[0], + pPkt->cmd.configAddressesReq.listAddressOps[i].address[1], + pPkt->cmd.configAddressesReq.listAddressOps[i].address[2], + pPkt->cmd.configAddressesReq.listAddressOps[i].address[3], + pPkt->cmd.configAddressesReq.listAddressOps[i].address[4], + pPkt->cmd.configAddressesReq.listAddressOps[i].address[5]) ); + + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" listAddressOps[%u].vlan = %u\n", + i, ntoh16(pPkt->cmd.configAddressesReq.listAddressOps[i].vlan)) ); + } + break; + case CMD_CONFIG_LINK: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ("ControlPacket: pktType = %s, pktCmd = CMD_CONFIG_LINK\n", type) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" pktSeqNum = %u, pktRetryCount = %u\n", + pPkt->hdr.pktSeqNum, + pPkt->hdr.pktRetryCount) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" cmdFlags = %x\n", + pPkt->cmd.configLinkReq.cmdFlags) ); + + if ( pPkt->cmd.configLinkReq.cmdFlags & INIC_FLAG_ENABLE_NIC ) + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" INIC_FLAG_ENABLE_NIC\n") ); + + if ( pPkt->cmd.configLinkReq.cmdFlags & INIC_FLAG_DISABLE_NIC ) + + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" INIC_FLAG_DISABLE_NIC\n") ); + + if ( pPkt->cmd.configLinkReq.cmdFlags & INIC_FLAG_ENABLE_MCAST_ALL ) + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" INIC_FLAG_ENABLE_MCAST_ALL\n") ); + + if ( pPkt->cmd.configLinkReq.cmdFlags & INIC_FLAG_DISABLE_MCAST_ALL ) + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" INIC_FLAG_DISABLE_MCAST_ALL\n") ); + + if ( pPkt->cmd.configLinkReq.cmdFlags & INIC_FLAG_ENABLE_PROMISC ) + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" INIC_FLAG_ENABLE_PROMISC\n") ); + + if ( pPkt->cmd.configLinkReq.cmdFlags & INIC_FLAG_DISABLE_PROMISC ) + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" INIC_FLAG_DISABLE_PROMISC\n") ); + if ( pPkt->cmd.configLinkReq.cmdFlags & INIC_FLAG_SET_MTU ) + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" INIC_FLAG_SET_MTU\n") ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" lanSwitchNum = %x, mtuSize = %d\n", + pPkt->cmd.configLinkReq.lanSwitchNum, + ntoh16(pPkt->cmd.configLinkReq.mtuSize)) ); + if ( pPkt->hdr.pktType == TYPE_RSP ) + { + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" defaultVlan = %u, hwMacAddress = %02x:%02x:%02x:%02x:%02x:%02x\n", + ntoh16(pPkt->cmd.configLinkReq.defaultVlan), + pPkt->cmd.configLinkReq.hwMacAddress[0], + pPkt->cmd.configLinkReq.hwMacAddress[1], + pPkt->cmd.configLinkReq.hwMacAddress[2], + pPkt->cmd.configLinkReq.hwMacAddress[3], + pPkt->cmd.configLinkReq.hwMacAddress[4], + pPkt->cmd.configLinkReq.hwMacAddress[5]) ); + } + break; + case CMD_REPORT_STATISTICS: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ("ControlPacket: pktType = %s, pktCmd = CMD_REPORT_STATISTICS\n", type ) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" pktSeqNum = %u, pktRetryCount = %u\n", + pPkt->hdr.pktSeqNum, + pPkt->hdr.pktRetryCount) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" lanSwitchNum = %u\n", + pPkt->cmd.reportStatisticsReq.lanSwitchNum) ); + + if (pPkt->hdr.pktType == TYPE_REQ) + break; + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifInBroadcastPkts = %"PRIu64, + ntoh64(pPkt->cmd.reportStatisticsRsp.ifInBroadcastPkts)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifInMulticastPkts = %"PRIu64"\n", + ntoh64(pPkt->cmd.reportStatisticsRsp.ifInMulticastPkts)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifInOctets = %"PRIu64, + ntoh64(pPkt->cmd.reportStatisticsRsp.ifInOctets)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifInUcastPkts = %"PRIu64"\n", + ntoh64(pPkt->cmd.reportStatisticsRsp.ifInUcastPkts)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifInNUcastPkts = %"PRIu64, + ntoh64(pPkt->cmd.reportStatisticsRsp.ifInNUcastPkts)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifInUnderrun = %"PRIu64"\n", + ntoh64(pPkt->cmd.reportStatisticsRsp.ifInUnderrun)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifInErrors = %"PRIu64, + ntoh64(pPkt->cmd.reportStatisticsRsp.ifInErrors)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifOutErrors = %"PRIu64"\n", + ntoh64(pPkt->cmd.reportStatisticsRsp.ifOutErrors)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifOutOctets = %"PRIu64, + ntoh64(pPkt->cmd.reportStatisticsRsp.ifOutOctets)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifOutUcastPkts = %"PRIu64"\n", + ntoh64(pPkt->cmd.reportStatisticsRsp.ifOutUcastPkts)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifOutMulticastPkts = %"PRIu64, + ntoh64(pPkt->cmd.reportStatisticsRsp.ifOutMulticastPkts)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifOutBroadcastPkts = %"PRIu64"\n", + ntoh64(pPkt->cmd.reportStatisticsRsp.ifOutBroadcastPkts)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifOutNUcastPkts = %"PRIu64, + ntoh64(pPkt->cmd.reportStatisticsRsp.ifOutNUcastPkts)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifOutOk = %"PRIu64"\n", + ntoh64(pPkt->cmd.reportStatisticsRsp.ifOutOk)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifInOk = %"PRIu64, + ntoh64(pPkt->cmd.reportStatisticsRsp.ifInOk)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifOutUcastBytes = %"PRIu64"\n", + ntoh64(pPkt->cmd.reportStatisticsRsp.ifOutUcastBytes)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifOutMulticastBytes = %"PRIu64, + ntoh64(pPkt->cmd.reportStatisticsRsp.ifOutMulticastBytes)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifOutBroadcastBytes = %"PRIu64"\n", + ntoh64(pPkt->cmd.reportStatisticsRsp.ifOutBroadcastBytes)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifInUcastBytes = %"PRIu64, + ntoh64(pPkt->cmd.reportStatisticsRsp.ifInUcastBytes)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifInMulticastBytes = %"PRIu64"\n", + ntoh64(pPkt->cmd.reportStatisticsRsp.ifInMulticastBytes)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ifInBroadcastBytes = %"PRIu64, + ntoh64(pPkt->cmd.reportStatisticsRsp.ifInBroadcastBytes)) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" ethernetStatus = %"PRIu64"\n", + ntoh64(pPkt->cmd.reportStatisticsRsp.ethernetStatus)) ); + break; + case CMD_CLEAR_STATISTICS: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ("ControlPacket: pktType = %s, pktCmd = CMD_CLEAR_STATISTICS\n", type ) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" pktSeqNum = %u, pktRetryCount = %u\n", + pPkt->hdr.pktSeqNum, + pPkt->hdr.pktRetryCount) ); + break; + case CMD_REPORT_STATUS: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ("ControlPacket: pktType = %s, pktCmd = CMD_REPORT_STATUS\n", + type) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" pktSeqNum = %u, pktRetryCount = %u\n", + pPkt->hdr.pktSeqNum, + pPkt->hdr.pktRetryCount) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" lanSwitchNum = %u, isFatal = %u\n", + pPkt->cmd.reportStatus.lanSwitchNum, + pPkt->cmd.reportStatus.isFatal) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" statusNumber = %u, statusInfo = %u\n", + ntoh32(pPkt->cmd.reportStatus.statusNumber), + ntoh32(pPkt->cmd.reportStatus.statusInfo)) ); + pPkt->cmd.reportStatus.fileName[31] = '\0'; + pPkt->cmd.reportStatus.routine[31] = '\0'; + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" filename = %s, routine = %s\n", + pPkt->cmd.reportStatus.fileName, + pPkt->cmd.reportStatus.routine) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" lineNum = %u, errorParameter = %u\n", + ntoh32(pPkt->cmd.reportStatus.lineNum), + ntoh32(pPkt->cmd.reportStatus.errorParameter)) ); + pPkt->cmd.reportStatus.descText[127] = '\0'; + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" descText = %s\n", + pPkt->cmd.reportStatus.descText) ); + break; + case CMD_RESET: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ("ControlPacket: pktType = %s, pktCmd = CMD_RESET\n", type ) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" pktSeqNum = %u, pktRetryCount = %u\n", + pPkt->hdr.pktSeqNum, + pPkt->hdr.pktRetryCount) ); + break; + case CMD_HEARTBEAT: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ("ControlPacket: pktType = %s, pktCmd = CMD_HEARTBEAT\n", type ) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" pktSeqNum = %u, pktRetryCount = %u\n", + pPkt->hdr.pktSeqNum, + pPkt->hdr.pktRetryCount) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" hbInterval = %d\n", + ntoh32(pPkt->cmd.heartbeatReq.hbInterval)) ); + break; + default: + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + ("ControlPacket: pktType = %s, pktCmd = UNKNOWN (%u)\n", + type,pPkt->hdr.pktCmd) ); + VNIC_PRINT( VNIC_DBG_CTRL_PKT | VNIC_DBG_INFO, + (" pktSeqNum = %u, pktRetryCount = %u\n", + pPkt->hdr.pktSeqNum, + pPkt->hdr.pktRetryCount) ); + break; + } + return; +} diff --git a/trunk/ulp/inic/kernel/vnic_control.h b/trunk/ulp/inic/kernel/vnic_control.h new file mode 100644 index 00000000..f6c2ce36 --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_control.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + +#ifndef _VNIC_CONTROL_H_ +#define _VNIC_CONTROL_H_ + +#include "vnic_controlpkt.h" +#include "vnic_util.h" + +typedef enum { + TIMER_IDLE, + TIMER_ACTIVE, + TIMER_EXPIRED +}timerstate_t; + +typedef struct Control { + struct _viport *p_viport; + struct ControlConfig *p_conf; + IbRegion_t region; + IbQp_t qp; + uint8_t *pLocalStorage; + uint16_t majVer; + uint16_t minVer; + Inic_LanSwitchAttributes_t lanSwitch; + SendIo_t sendIo; + RecvIo_t *pRecvIos; + + timerstate_t timerstate; + cl_timer_t timer; + uint8_t reqRetryCounter; + uint8_t seqNum; + uint32_t reqOutstanding; + uint32_t rspExpected; + RecvIo_t *pResponse; + RecvIo_t *pInfo; + LIST_ENTRY failureList; + KSPIN_LOCK ioLock; + +#ifdef VNIC_STATISTIC + struct { + uint64_t requestTime; /* Intermediate value */ + uint64_t responseTime; + uint32_t responseNum; + uint64_t responseMax; + uint64_t responseMin; + uint32_t timeoutNum; + } statistics; +#endif /* VNIC_STATISTIC */ +} Control_t; + +void +control_construct( + IN Control_t *pControl, + IN struct _viport *pViport ); + +ib_api_status_t control_init(Control_t *pControl, struct _viport *pViport, + struct ControlConfig *p_conf, uint64_t guid); +void control_cleanup(Control_t *pControl); +void control_processAsync(Control_t *pControl); +ib_api_status_t control_initInicReq(Control_t *pControl); +BOOLEAN control_initInicRsp(Control_t *pControl, uint32_t *pFeatures, + uint8_t *pMacAddress, uint16_t *pNumAddrs, uint16_t *pVlan); +ib_api_status_t control_configDataPathReq(Control_t *pControl, uint64_t pathId, + struct Inic_RecvPoolConfig *pHost, + struct Inic_RecvPoolConfig *pEioc); +BOOLEAN control_configDataPathRsp(Control_t *pControl, + struct Inic_RecvPoolConfig *pHost, + struct Inic_RecvPoolConfig *pEioc, + struct Inic_RecvPoolConfig *pMaxHost, + struct Inic_RecvPoolConfig *pMaxEioc, + struct Inic_RecvPoolConfig *pMinHost, + struct Inic_RecvPoolConfig *pMinEioc); +ib_api_status_t control_exchangePoolsReq(Control_t *pControl, uint64_t addr, uint32_t rkey); +BOOLEAN control_exchangePoolsRsp(Control_t *pControl, uint64_t *pAddr, + uint32_t *pRkey); +ib_api_status_t control_configLinkReq(Control_t *pControl, uint8_t flags, uint16_t mtu); +BOOLEAN control_configLinkRsp(Control_t *pControl, uint8_t *pFlags, uint16_t *pMtu); +ib_api_status_t control_configAddrsReq(Control_t *pControl, Inic_AddressOp_t *pAddrs, + uint16_t num, int32_t *pAddrQueryDone); +BOOLEAN control_configAddrsRsp(Control_t *pControl); +ib_api_status_t control_reportStatisticsReq(Control_t *pControl); +BOOLEAN control_reportStatisticsRsp(Control_t *pControl, + struct Inic_CmdReportStatisticsRsp *pStats); +ib_api_status_t control_resetReq( Control_t *pControl ); +BOOLEAN control_resetRsp(Control_t *pControl); +ib_api_status_t control_heartbeatReq(Control_t *pControl, uint32_t hbInterval); +BOOLEAN control_heartbeatRsp(Control_t *pControl); + +#define control_packet(pIo) (Inic_ControlPacket_t *)(LONG_PTR)((pIo)->dsList.vaddr ) +#define control_lastReq(pControl) control_packet(&(pControl)->sendIo) +#define control_features(pControl) (pControl)->featuresSupported +#define control_getMacAddress(pControl,addr) \ + memcpy(addr,(pControl)->lanSwitch.hwMacAddress,MAC_ADDR_LEN) + +#endif /* _VNIC_CONTROL_H_ */ diff --git a/trunk/ulp/inic/kernel/vnic_controlpkt.h b/trunk/ulp/inic/kernel/vnic_controlpkt.h new file mode 100644 index 00000000..378d792e --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_controlpkt.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + +#ifndef _VNIC_CONTROLPKT_H_ +#define _VNIC_CONTROLPKT_H_ + +#include +typedef struct Inic_ConnectionData { + uint64_t pathId; + uint8_t inicInstance; + uint8_t pathNum; + uint8_t nodename[65]; + uint8_t reserved; + uint32_t featuresSupported; +} Inic_ConnectionData_t; + +typedef struct Inic_ControlHeader { + uint8_t pktType; + uint8_t pktCmd; + uint8_t pktSeqNum; + uint8_t pktRetryCount; + uint32_t reserved; /* for 64-bit alignmnet */ +} Inic_ControlHeader_t; + +/* ptkType values */ +#define TYPE_INFO 0 +#define TYPE_REQ 1 +#define TYPE_RSP 2 +#define TYPE_ERR 3 + +/* ptkCmd values */ +#define CMD_INIT_INIC 1 +#define CMD_CONFIG_DATA_PATH 2 +#define CMD_EXCHANGE_POOLS 3 +#define CMD_CONFIG_ADDRESSES 4 +#define CMD_CONFIG_LINK 5 +#define CMD_REPORT_STATISTICS 6 +#define CMD_CLEAR_STATISTICS 7 +#define CMD_REPORT_STATUS 8 +#define CMD_RESET 9 +#define CMD_HEARTBEAT 10 + +#define MAC_ADDR_LEN HW_ADDR_LEN + +/* pktCmd CMD_INIT_INIC, pktType TYPE_REQ data format */ +typedef struct Inic_CmdInitInicReq { + uint16_t inicMajorVersion; + uint16_t inicMinorVersion; + uint8_t inicInstance; + uint8_t numDataPaths; + uint16_t numAddressEntries; +} Inic_CmdInitInicReq_t; + +/* pktCmd CMD_INIT_INIC, pktType TYPE_RSP subdata format */ +typedef struct Inic_LanSwitchAttributes { + uint8_t lanSwitchNum; + uint8_t numEnetPorts; + uint16_t defaultVlan; + uint8_t hwMacAddress[MAC_ADDR_LEN]; +} Inic_LanSwitchAttributes_t; + +/* pktCmd CMD_INIT_INIC, pktType TYPE_RSP data format */ +typedef struct Inic_CmdInitInicRsp { + uint16_t inicMajorVersion; + uint16_t inicMinorVersion; + uint8_t numLanSwitches; + uint8_t numDataPaths; + uint16_t numAddressEntries; + uint32_t featuresSupported; + Inic_LanSwitchAttributes_t lanSwitch[1]; +} Inic_CmdInitInicRsp_t; + +/* featuresSupported values */ +#define INIC_FEAT_IPV4_HEADERS 0x00000001 +#define INIC_FEAT_IPV6_HEADERS 0x00000002 +#define INIC_FEAT_IPV4_CSUM_RX 0x00000004 +#define INIC_FEAT_IPV4_CSUM_TX 0x00000008 +#define INIC_FEAT_TCP_CSUM_RX 0x00000010 +#define INIC_FEAT_TCP_CSUM_TX 0x00000020 +#define INIC_FEAT_UDP_CSUM_RX 0x00000040 +#define INIC_FEAT_UDP_CSUM_TX 0x00000080 +#define INIC_FEAT_TCP_SEGMENT 0x00000100 +#define INIC_FEAT_IPV4_IPSEC_OFFLOAD 0x00000200 +#define INIC_FEAT_IPV6_IPSEC_OFFLOAD 0x00000400 +#define INIC_FEAT_FCS_PROPAGATE 0x00000800 +#define INIC_FEAT_PF_KICK 0x00001000 +#define INIC_FEAT_PF_FORCE_ROUTE 0x00002000 +#define INIC_FEAT_CHASH_OFFLOAD 0x00004000 +#define INIC_FEAT_RDMA_IMMED 0x00008000 +#define INIC_FEAT_IGNORE_VLAN 0x00010000 + +/* pktCmd CMD_CONFIG_DATA_PATH subdata format */ +typedef struct Inic_RecvPoolConfig { + uint32_t sizeRecvPoolEntry; + uint32_t numRecvPoolEntries; + uint32_t timeoutBeforeKick; + uint32_t numRecvPoolEntriesBeforeKick; + uint32_t numRecvPoolBytesBeforeKick; + uint32_t freeRecvPoolEntriesPerUpdate; +} Inic_RecvPoolConfig_t; + +/* pktCmd CMD_CONFIG_DATA_PATH data format */ +typedef struct Inic_CmdConfigDataPath { + uint64_t pathIdentifier; + uint8_t dataPath; + uint8_t reserved[3]; + Inic_RecvPoolConfig_t hostRecvPoolConfig; + Inic_RecvPoolConfig_t eiocRecvPoolConfig; +} Inic_CmdConfigDataPath_t; + +/* pktCmd CMD_EXCHANGE_POOLS data format */ +typedef struct Inic_CmdExchangePools { + uint8_t dataPath; + uint8_t reserved[3]; + uint32_t poolRKey; + uint64_t poolAddr; +} Inic_CmdExchangePools_t; + +/* pktCmd CMD_CONFIG_ADDRESSES subdata format */ +typedef struct Inic_AddressOp { + uint16_t index; + uint8_t operation; + uint8_t valid; + uint8_t address[6]; + uint16_t vlan; +} Inic_AddressOp_t; + +/* operation values */ +#define INIC_OP_SET_ENTRY 0x01 +#define INIC_OP_GET_ENTRY 0x02 + +/* pktCmd CMD_CONFIG_ADDRESSES data format */ +typedef struct Inic_CmdConfigAddresses { + uint8_t numAddressOps; + uint8_t lanSwitchNum; + Inic_AddressOp_t listAddressOps[1]; +} Inic_CmdConfigAddresses_t; + +/* CMD_CONFIG_LINK data format */ +typedef struct Inic_CmdConfigLink { + uint8_t cmdFlags; + uint8_t lanSwitchNum; + uint16_t mtuSize; + uint16_t defaultVlan; + uint8_t hwMacAddress[6]; +} Inic_CmdConfigLink_t; + +/* cmdFlags values */ +#define INIC_FLAG_ENABLE_NIC 0x01 +#define INIC_FLAG_DISABLE_NIC 0x02 +#define INIC_FLAG_ENABLE_MCAST_ALL 0x04 +#define INIC_FLAG_DISABLE_MCAST_ALL 0x08 +#define INIC_FLAG_ENABLE_PROMISC 0x10 +#define INIC_FLAG_DISABLE_PROMISC 0x20 +#define INIC_FLAG_SET_MTU 0x40 + +/* pktCmd CMD_REPORT_STATISTICS, pktType TYPE_REQ data format */ +typedef struct Inic_CmdReportStatisticsReq { + uint8_t lanSwitchNum; +} Inic_CmdReportStatisticsReq_t; + +/* pktCmd CMD_REPORT_STATISTICS, pktType TYPE_RSP data format */ +typedef struct Inic_CmdReportStatisticsRsp { + uint8_t lanSwitchNum; + uint8_t reserved[7]; /* for 64-bit alignment */ + uint64_t ifInBroadcastPkts; + uint64_t ifInMulticastPkts; + uint64_t ifInOctets; + uint64_t ifInUcastPkts; + uint64_t ifInNUcastPkts; /* ifInBroadcastPkts + ifInMulticastPkts */ + uint64_t ifInUnderrun; /* (OID_GEN_RCV_NO_BUFFER) */ + uint64_t ifInErrors; /* (OID_GEN_RCV_ERROR) */ + uint64_t ifOutErrors; /* (OID_GEN_XMIT_ERROR) */ + uint64_t ifOutOctets; + uint64_t ifOutUcastPkts; + uint64_t ifOutMulticastPkts; + uint64_t ifOutBroadcastPkts; + uint64_t ifOutNUcastPkts; /* ifOutBroadcastPkts + ifOutMulticastPkts */ + uint64_t ifOutOk; /* ifOutNUcastPkts + ifOutUcastPkts (OID_GEN_XMIT_OK)*/ + uint64_t ifInOk; /* ifInNUcastPkts + ifInUcastPkts (OID_GEN_RCV_OK) */ + uint64_t ifOutUcastBytes; /* (OID_GEN_DIRECTED_BYTES_XMT) */ + uint64_t ifOutMulticastBytes; /* (OID_GEN_MULTICAST_BYTES_XMT) */ + uint64_t ifOutBroadcastBytes; /* (OID_GEN_BROADCAST_BYTES_XMT) */ + uint64_t ifInUcastBytes; /* (OID_GEN_DIRECTED_BYTES_RCV) */ + uint64_t ifInMulticastBytes; /* (OID_GEN_MULTICAST_BYTES_RCV) */ + uint64_t ifInBroadcastBytes; /* (OID_GEN_BROADCAST_BYTES_RCV) */ + uint64_t ethernetStatus; /* OID_GEN_MEDIA_CONNECT_STATUS) */ +} Inic_CmdReportStatisticsRsp_t; + +/* pktCmd CMD_CLEAR_STATISTICS data format */ +typedef struct Inic_CmdClearStatistics { + uint8_t lanSwitchNum; +} Inic_CmdClearStatistics_t; + +/* pktCmd CMD_REPORT_STATUS data format */ +typedef struct Inic_CmdReportStatus { + uint8_t lanSwitchNum; + uint8_t isFatal; + uint8_t reserved[2]; /* for 32-bit alignment */ + uint32_t statusNumber; + uint32_t statusInfo; + uint8_t fileName[32]; + uint8_t routine[32]; + uint32_t lineNum; + uint32_t errorParameter; + uint8_t descText[128]; +} Inic_CmdReportStatus_t; + +/* pktCmd CMD_HEARTBEAT data format */ +typedef struct Inic_CmdHeartbeat { + uint32_t hbInterval; +} Inic_CmdHeartbeat_t; + +#define INIC_STATUS_LINK_UP 1 +#define INIC_STATUS_LINK_DOWN 2 +#define INIC_STATUS_ENET_AGGREGATION_CHANGE 3 +#define INIC_STATUS_EIOC_SHUTDOWN 4 +#define INIC_STATUS_CONTROL_ERROR 5 +#define INIC_STATUS_EIOC_ERROR 6 + +#define INIC_MAX_CONTROLPKTSZ 256 +#define INIC_MAX_CONTROLDATASZ \ + (INIC_MAX_CONTROLPKTSZ - sizeof(Inic_ControlHeader_t)) + +typedef struct Inic_ControlPacket { + Inic_ControlHeader_t hdr; + union { + Inic_CmdInitInicReq_t initInicReq; + Inic_CmdInitInicRsp_t initInicRsp; + Inic_CmdConfigDataPath_t configDataPathReq; + Inic_CmdConfigDataPath_t configDataPathRsp; + Inic_CmdExchangePools_t exchangePoolsReq; + Inic_CmdExchangePools_t exchangePoolsRsp; + Inic_CmdConfigAddresses_t configAddressesReq; + Inic_CmdConfigAddresses_t configAddressesRsp; + Inic_CmdConfigLink_t configLinkReq; + Inic_CmdConfigLink_t configLinkRsp; + Inic_CmdReportStatisticsReq_t reportStatisticsReq; + Inic_CmdReportStatisticsRsp_t reportStatisticsRsp; + Inic_CmdClearStatistics_t clearStatisticsReq; + Inic_CmdClearStatistics_t clearStatisticsRsp; + Inic_CmdReportStatus_t reportStatus; + Inic_CmdHeartbeat_t heartbeatReq; + Inic_CmdHeartbeat_t heartbeatRsp; + char cmdData[INIC_MAX_CONTROLDATASZ]; + } cmd; +} Inic_ControlPacket_t; +/* +typedef struct _mac_addr +{ + uint8_t addr[MAC_ADDR_LEN]; +} PACK_SUFFIX mac_addr_t; +*/ +#include + +#endif /* _VNIC_CONTROLPKT_H_ */ \ No newline at end of file diff --git a/trunk/ulp/inic/kernel/vnic_data.c b/trunk/ulp/inic/kernel/vnic_data.c new file mode 100644 index 00000000..66f60373 --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_data.c @@ -0,0 +1,1383 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ +#include +#include "vnic_driver.h" + +static void data_postRecvs(Data_t *pData); +static void _data_receivedKick(Io_t *pIo); +static void _data_xmitComplete(Io_t *pIo); +static void data_sendKickMessage(Data_t *pData); +static void _data_kickTimeoutHandler( void *context ); +static BOOLEAN data_allocXmitBuffer(Data_t *pData, + BufferPoolEntry_t **ppBpe, RdmaIo_t **ppRdmaIo, BOOLEAN *pLast); +static void data_checkXmitBuffers(Data_t *pData); +static void data_rdmaPacket(Data_t *pData, BufferPoolEntry_t *pBpe, RdmaIo_t *pRdmaIo); + +static NDIS_PACKET * +_data_recv_to_ndis_pkt( Data_t *pData, RdmaDest_t *pRdmaDest ); + +static void +_data_allocBuffers( + Data_t *pData, + BOOLEAN initialAllocation ); + +static void +_data_addFreeBuffer( + Data_t *pData, + int index, + RdmaDest_t *pRdmaDest ); + +static uint32_t +_data_incomingRecv( + Data_t *pData ); + +static void +_data_sendFreeRecvBuffers( + Data_t *pData ); + +static uint8_t +_tx_chksum_flags( + IN NDIS_PACKET* const p_packet ); + + +static void +_get_first_buffer( + IN NDIS_PACKET *p_packet, + IN OUT NDIS_BUFFER **pp_buf_desc, + OUT void **pp_buf, + OUT ULONG *p_packet_sz ); + +static void +_data_return_recv( + IN NDIS_PACKET *p_packet ); + +static void +_data_kickTimer_start( + IN Data_t *pData, + IN uint32_t microseconds ); + +static void +_data_kickTimer_stop( + IN Data_t *pData ); + +#define LOCAL_IO(x) PTR64((x)) + +#define INBOUND_COPY + +#ifdef VNIC_STATISTIC +int64_t recvRef; +#endif /* VNIC_STATISTIC */ + +void +data_construct( + IN Data_t *pData, + IN viport_t *pViport ) +{ + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + RtlZeroMemory( pData, sizeof(*pData) ); + + pData->p_viport = pViport; + pData->p_phy_region = &pViport->p_adapter->ca.region; + InitializeListHead( &pData->recvIos ); + KeInitializeSpinLock ( &pData->recvIosLock ); + KeInitializeSpinLock ( &pData->xmitBufLock ); + cl_timer_construct( &pData->kickTimer ); + + ibqp_construct( &pData->qp, pViport ); + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); +} + + +ib_api_status_t +data_init( + IN Data_t *pData, + IN DataConfig_t *p_conf, + IN uint64_t guid ) +{ + ib_api_status_t ib_status; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + ASSERT( pData->p_viport != NULL ); + pData->p_conf = p_conf; + + cl_timer_init( &pData->kickTimer, _data_kickTimeoutHandler, pData ); + + ib_status = ibqp_init(&pData->qp, guid, &p_conf->ibConfig ); + if( ib_status != IB_SUCCESS ) + VNIC_TRACE( VNIC_DBG_ERROR, ("data ibqp_init failed\n") ); + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); + return ib_status; +} + + +ib_api_status_t +data_connect( + IN Data_t *pData ) +{ + NDIS_STATUS status; + ib_api_status_t ib_status; + XmitPool_t *pXmitPool = &pData->xmitPool; + RecvPool_t *pRecvPool = &pData->recvPool; + RecvIo_t *pRecvIo; + SendIo_t *pSendIo; + RdmaIo_t *pRdmaIo; + RdmaDest_t *pRdmaDest; + uint8_t *pRegionData; + int sz, regionSz; + unsigned int i, j; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + pRecvPool->poolSz = pData->p_conf->hostRecvPoolEntries; + pRecvPool->eiocPoolSz = pData->hostPoolParms.numRecvPoolEntries; + + if ( pRecvPool->poolSz > pRecvPool->eiocPoolSz ) + { + pRecvPool->poolSz = pData->hostPoolParms.numRecvPoolEntries; + } + pRecvPool->szFreeBundle = + pData->hostPoolParms.freeRecvPoolEntriesPerUpdate; + pRecvPool->numFreeBufs = 0; + pRecvPool->numPostedBufs = 0; + pRecvPool->nextFullBuf = 0; + pRecvPool->nextFreeBuf = 0; + pRecvPool->kickOnFree = FALSE; + pRecvPool->bufferSz = pData->hostPoolParms.sizeRecvPoolEntry; + + pXmitPool->bufferSz = pData->eiocPoolParms.sizeRecvPoolEntry; + pXmitPool->poolSz = pData->eiocPoolParms.numRecvPoolEntries; + pXmitPool->notifyCount = 0; + pXmitPool->notifyBundle = pData->p_conf->notifyBundle; + pXmitPool->nextXmitPool = 0; + +#if TRUE // LIMIT_OUTSTANDING_SENDS + pXmitPool->numXmitBufs = pXmitPool->notifyBundle * 2; +#else /* !LIMIT_OUTSTANDING_SENDS */ + pXmitPool->numXmitBufs = pXmitPool->poolSz; +#endif /* LIMIT_OUTSTANDING_SENDS */ + + pXmitPool->nextXmitBuf = 0; + pXmitPool->lastCompBuf = pXmitPool->numXmitBufs - 1; + pXmitPool->kickCount = 0; + pXmitPool->kickByteCount = 0; + pXmitPool->sendKicks = + (BOOLEAN)(( pData->eiocPoolParms.numRecvPoolEntriesBeforeKick != 0 ) + || ( pData->eiocPoolParms.numRecvPoolBytesBeforeKick != 0 )); + pXmitPool->kickBundle = + pData->eiocPoolParms.numRecvPoolEntriesBeforeKick; + pXmitPool->kickByteBundle = + pData->eiocPoolParms.numRecvPoolBytesBeforeKick; + pXmitPool->needBuffers = TRUE; + + sz = sizeof(RdmaDest_t) * pRecvPool->poolSz; + sz += sizeof(RecvIo_t) * pData->p_conf->numRecvs; + sz += sizeof(RdmaIo_t) * pXmitPool->numXmitBufs; + + regionSz = 4 * pData->p_conf->numRecvs; + regionSz += sizeof(BufferPoolEntry_t) * pRecvPool->eiocPoolSz; + regionSz += sizeof(BufferPoolEntry_t) * pXmitPool->poolSz; + sz += regionSz; + + status = NdisAllocateMemoryWithTag( &pData->pLocalStorage, + (UINT)sz, + 'grtS' ); + if ( status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("Failed allocating %d bytes local storage\n", sz ) ); + ib_status = IB_INSUFFICIENT_MEMORY; + goto err1; + } + + NdisZeroMemory( pData->pLocalStorage, sz ); + pData->localStorageSz = sz; + + pRecvPool->pRecvBufs = (RdmaDest_t *)pData->pLocalStorage; + sz = sizeof(RdmaDest_t) * pRecvPool->poolSz; + pRecvIo = (RecvIo_t *)(pData->pLocalStorage + sz); + sz += sizeof(RecvIo_t) * pData->p_conf->numRecvs; + + pXmitPool->pXmitBufs = (RdmaIo_t *)(pData->pLocalStorage + sz); + sz += sizeof(RdmaIo_t) * pXmitPool->numXmitBufs; + + pRegionData = pData->pLocalStorage + sz; + sz += 4 * pData->p_conf->numRecvs; + + pRecvPool->bufPool = (BufferPoolEntry_t *)(pData->pLocalStorage + sz); + sz += sizeof(BufferPoolEntry_t) * pRecvPool->eiocPoolSz; + pXmitPool->bufPool = (BufferPoolEntry_t *)(pData->pLocalStorage + sz); + + ib_status = ibregion_init( pData->p_viport, &pData->region, + pData->p_viport->p_adapter->ca.hPd, pRegionData, regionSz, + ( IB_AC_LOCAL_WRITE | IB_AC_RDMA_WRITE ) ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, ("ib_region_init failed\n") ); + goto err2; + } + + pRdmaIo = &pData->freeBufsIo; + pRdmaIo->io.pViport = pData->p_viport; + pRdmaIo->io.pRoutine = NULL; + pRdmaIo->io.wrq.p_next = NULL; + pRdmaIo->io.wrq.wr_type = WR_RDMA_WRITE; + pRdmaIo->io.wrq.wr_id = PTR64( pRdmaIo ); + pRdmaIo->io.wrq.num_ds = 1; + pRdmaIo->io.wrq.ds_array = pRdmaIo->dsList; + pRdmaIo->dsList[0].lkey = pData->region.lkey; + pRdmaIo->io.wrq.send_opt = IB_SEND_OPT_SIGNALED; + + pSendIo = &pData->kickIo; + pSendIo->io.pViport = pData->p_viport; + pSendIo->io.pRoutine = NULL; + pSendIo->io.wrq.p_next = NULL; + pSendIo->io.wrq.wr_type = WR_SEND; + pSendIo->io.wrq.wr_id = PTR64( pSendIo ); + pSendIo->io.wrq.num_ds = 1; + pSendIo->io.wrq.ds_array = &pSendIo->dsList; + + pSendIo->io.wrq.send_opt = IB_SEND_OPT_SIGNALED; + + pSendIo->dsList.length = 0; + pSendIo->dsList.vaddr = PTR64( pRegionData ); + pSendIo->dsList.lkey = pData->region.lkey; + + for ( i = 0; i < pData->p_conf->numRecvs; i++ ) + { + pRecvIo[i].io.pViport = pData->p_viport; + pRecvIo[i].io.pRoutine = _data_receivedKick; + pRecvIo[i].io.r_wrq.wr_id = PTR64( &pRecvIo[i].io ); + pRecvIo[i].io.r_wrq.p_next = NULL; + pRecvIo[i].io.r_wrq.num_ds = 1; + pRecvIo[i].io.r_wrq.ds_array = &pRecvIo[i].dsList; + pRecvIo[i].dsList.length = 4; + pRecvIo[i].dsList.vaddr = PTR64( pRegionData ); + pRecvIo[i].dsList.lkey = pData->region.lkey; + + ExInterlockedInsertTailList( &pData->recvIos, &pRecvIo[i].io.listPtrs, &pData->recvIosLock ); + /* Do not need to move pointer since the receive info + * is not read. Note, we could reduce the amount + * of memory allocated and the size of the region. + * pRegionData += 4; + * */ + } + + sz = pRecvPool->poolSz * pRecvPool->bufferSz; + status = NdisAllocateMemoryWithTag(&pData->p_recv_bufs, + sz, 'fubr'); + if( status != NDIS_STATUS_SUCCESS ) + { + ib_status = IB_INSUFFICIENT_MEMORY; + goto err3; + } + NdisZeroMemory( pData->p_recv_bufs, sz ); + + pData->recv_bufs_sz = sz; + + ib_status = ibregion_init( pData->p_viport, &pData->rbuf_region, + pData->p_viport->p_adapter->ca.hPd, pData->p_recv_bufs, sz, + (IB_AC_LOCAL_WRITE | IB_AC_RDMA_WRITE) ); + if( ib_status != IB_SUCCESS ) + { + goto err4; + } + + NdisAllocatePacketPool( &status, + &pData->h_recv_pkt_pool, + pRecvPool->poolSz, + PROTOCOL_RESERVED_SIZE_IN_PACKET ); + + if( status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("Allocate packet pool failed status %#x\n", status )); + ib_status = IB_INSUFFICIENT_MEMORY; + goto err5; + } + + NdisAllocateBufferPool( + &status, &pData->h_recv_buf_pool, pRecvPool->poolSz ); + + if( status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("Allocate packet pool failed status %#x\n", status )); + ib_status = IB_INSUFFICIENT_MEMORY; + goto err6; + } + pData->recvPool.recv_pkt_array = + cl_zalloc( sizeof(NDIS_PACKET*)* pRecvPool->poolSz ); + if( !pData->recvPool.recv_pkt_array ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("Allocate packet array failed\n" ) ); + ib_status = IB_INSUFFICIENT_MEMORY; + goto err7; + } + + InitializeListHead( &pRecvPool->availRecvBufs ); + + for ( i = 0; i < pRecvPool->poolSz; i++ ) + { + /* Allocate a region for each possible receive + * buffer. Make sure each is large enough for + * the maximum packet size, but initially point + * each to a random piece of memory. As buffers + * are allocated, the region will be modified to + * reflect the memory space of the actual buffers. + */ + pRdmaDest = &pRecvPool->pRecvBufs[i]; + pRdmaDest->data = pData->p_recv_bufs + (i * pRecvPool->bufferSz ); + pRdmaDest->region = pData->rbuf_region; + InsertTailList( &pRecvPool->availRecvBufs, &pRdmaDest->listPtrs ); + } + + for ( i = 0; i < pXmitPool->numXmitBufs; i++ ) + { + pRdmaIo = &pXmitPool->pXmitBufs[i]; + pRdmaIo->index = (uint16_t)i; + pRdmaIo->io.pViport = pData->p_viport; + pRdmaIo->io.pRoutine = _data_xmitComplete; + pRdmaIo->io.wrq.p_next = NULL; + pRdmaIo->io.wrq.wr_type = WR_RDMA_WRITE; + pRdmaIo->io.wrq.wr_id = PTR64(pRdmaIo); + pRdmaIo->io.wrq.num_ds = MAX_NUM_SGE; // will set actual number when transmit + pRdmaIo->io.wrq.ds_array = pRdmaIo->dsList; + pRdmaIo->p_trailer = (ViportTrailer_t *)&pRdmaIo->data[0]; + for( j = 0; j < MAX_NUM_SGE; j++ ) + { + pRdmaIo->dsList[j].lkey = pData->p_phy_region->lkey; + } + } + + pXmitPool->rdmaRKey = pData->region.rkey; + pXmitPool->rdmaAddr = PTR64( pXmitPool->bufPool ); + + data_postRecvs( pData ); + + ib_status = ibqp_connect( &pData->qp ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, ("ibqp_connect returned %s\n", + pData->p_viport->p_adapter->ifc.get_err_str( ib_status )) ); +err7: + NdisFreeBufferPool( pData->h_recv_buf_pool ); +err6: + NdisFreePacketPool( pData->h_recv_pkt_pool ); +err5: + ibregion_cleanup( pData->p_viport, &pData->rbuf_region ); +err4: + NdisFreeMemory( pData->p_recv_bufs, pData->recv_bufs_sz, 0 ); + pData->p_recv_bufs = NULL; +err3: + ibregion_cleanup(pData->p_viport, &pData->region ); +err2: + NdisFreeMemory( pData->pLocalStorage, pData->localStorageSz, 0 ); + pData->pLocalStorage = NULL; +err1: + pRecvPool->poolSz = 0; + } + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); + return ib_status; +} + + +void +data_connected( + IN Data_t *pData ) +{ + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + pData->freeBufsIo.io.wrq.remote_ops.rkey = + pData->recvPool.eiocRdmaRkey; + + _data_allocBuffers(pData, TRUE); + _data_sendFreeRecvBuffers(pData); + pData->connected = TRUE; + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); + return; +} + +void +data_disconnect( + IN Data_t *pData ) +{ + RecvPool_t *pRecvPool = &pData->recvPool; + viport_t *p_viport = pData->p_viport; + NDIS_PACKET *p_packet; + cl_list_item_t *p_list_item; + unsigned int i; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + pData->connected = FALSE; + _data_kickTimer_stop ( pData ); + + ibqp_detach( &pData->qp ); + + ibregion_cleanup( pData->p_viport, &pData->rbuf_region ); + ibregion_cleanup( pData->p_viport, &pData->region ); + + for ( i = 0; i < pRecvPool->poolSz; i++ ) + { + p_packet = pRecvPool->pRecvBufs[i].p_packet; + pRecvPool->pRecvBufs[i].p_packet = NULL; + + if ( p_packet != NULL ) + { + _data_return_recv( p_packet ); + } + } + /* clear pending queue if any */ + if( cl_qlist_count( &p_viport->send_pending_list ) ) + { + for( p_list_item = cl_qlist_remove_head( &p_viport->send_pending_list ); + p_list_item != cl_qlist_end( &p_viport->send_pending_list ); + p_list_item = cl_qlist_remove_head( &p_viport->send_pending_list ) ) + { + p_packet = VNIC_PACKET_FROM_LIST_ITEM( p_list_item ); + NdisMSendComplete( pData->p_viport->p_adapter->h_handle, + p_packet, NDIS_STATUS_RESET_IN_PROGRESS ); + } + } + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); + return; +} + +BOOLEAN +data_xmitPacket( + IN Data_t *pData, + IN NDIS_PACKET* const p_packet ) +{ + XmitPool_t *p_xmitPool = &pData->xmitPool; + RdmaIo_t *pRdmaIo; + BufferPoolEntry_t *pBpe; + BOOLEAN last; + uint8_t *p_buf; + uint32_t buf_len; + NDIS_BUFFER *p_buf_desc; + eth_hdr_t* p_eth_hdr; + int pad; + SCATTER_GATHER_LIST *p_sgl; + uint32_t i; + PHYSICAL_ADDRESS phy_addr; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + if( !data_allocXmitBuffer( pData, &pBpe, &pRdmaIo, &last ) ) + { + return FALSE; + } + p_sgl = NDIS_PER_PACKET_INFO_FROM_PACKET( p_packet, + ScatterGatherListPacketInfo ); + if ( p_sgl == NULL ) + { + return FALSE; + } + + NdisGetFirstBufferFromPacketSafe( p_packet, + &p_buf_desc, + &p_buf, + &buf_len, + &pRdmaIo->packet_sz, + NormalPagePriority ); + + if( pRdmaIo->packet_sz > p_xmitPool->bufferSz ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("Outbound packet too large, size = %d\n", pRdmaIo->packet_sz ) ); + return FALSE; + } + + if ( p_sgl->NumberOfElements > (ULONG)MAX_NUM_SGE - 1 ) + { + VNIC_TRACE( VNIC_DBG_DATA | VNIC_DBG_INFO, + (" Xmit packet exceeded SGE limit - %d\n", + p_sgl->NumberOfElements ) ); + return FALSE; + } + + for( i=0; i < p_sgl->NumberOfElements; i++ ) + { + pRdmaIo->dsList[i].vaddr = p_sgl->Elements[i].Address.QuadPart; + pRdmaIo->dsList[i].length = p_sgl->Elements[i].Length; + } + + pRdmaIo->len = (uint32_t)ROUNDUPP2( + max(60, pRdmaIo->packet_sz), VIPORT_TRAILER_ALIGNMENT ); + pad = pRdmaIo->len - pRdmaIo->packet_sz; + + p_eth_hdr = (eth_hdr_t *)p_buf; + + pRdmaIo->p_trailer = (ViportTrailer_t *)&pRdmaIo->data[pad]; + cl_memclr( pRdmaIo->data, pad + sizeof( ViportTrailer_t ) ); + cl_memcpy( pRdmaIo->p_trailer->destMacAddr, p_eth_hdr->dst.addr, MAC_ADDR_LEN ); + + pRdmaIo->p_trailer->dataLength = + hton16( (uint16_t)max( 60, pRdmaIo->packet_sz ) ); + + NdisGetNextBuffer( p_buf_desc, &p_buf_desc ); + + /* should handle VLAN tag */ + if( pRdmaIo->packet_sz > 16 ) + { + if( p_eth_hdr->type == hton16(0x8100) ) + { + if( p_sgl->Elements[0].Length > sizeof(eth_hdr_t) ) + { + pRdmaIo->p_trailer->vLan = *(uint16_t *)((uint8_t *)p_eth_hdr + 14 ); + pRdmaIo->p_trailer->pktFlags |= PF_VLAN_INSERT; + } + else + { + if( p_buf_desc ) + { + NdisQueryBufferSafe( p_buf_desc, &p_buf, &buf_len, NormalPagePriority ); + + pad = sizeof(eth_hdr_t) - p_sgl->Elements[0].Length; + pRdmaIo->p_trailer->vLan = *(uint16_t *)(p_buf + pad + 2); + pRdmaIo->p_trailer->pktFlags |= PF_VLAN_INSERT; + } + } + } + else if( p_eth_hdr->type == ETH_PROT_TYPE_IP && + !( p_eth_hdr->dst.addr[0] & 0x01 ) ) + { + if( p_buf_desc ) + { + NdisQueryBufferSafe( p_buf_desc, &p_buf, &buf_len, NormalPagePriority ); + + if( ((ip_pkt_t*)p_buf)->hdr.prot == IP_PROT_UDP || + ((ip_pkt_t*)p_buf)->hdr.prot == IP_PROT_TCP ) + { + /* use socket src port + dest port to generate hash value + * for link aggregation distribution function. + */ + pRdmaIo->p_trailer->connectionHashAndValid = 0x40 | + ((uint8_t)((ip_pkt_t*)p_buf)->prot.tcp.src_port + + (uint8_t)((ip_pkt_t*)p_buf)->prot.tcp.dst_port ) & 0x3f; + } + } + } + } + + pRdmaIo->p_trailer->txChksumFlags = _tx_chksum_flags( p_packet ); + pRdmaIo->p_trailer->connectionHashAndValid |= CHV_VALID; + + if( last ) + pRdmaIo->p_trailer->pktFlags |= PF_KICK; + + /* fill last data segment with trailer and pad */ + phy_addr = MmGetPhysicalAddress( pRdmaIo->data ); + + pRdmaIo->dsList[p_sgl->NumberOfElements].vaddr = phy_addr.QuadPart; + pRdmaIo->dsList[p_sgl->NumberOfElements].length = pRdmaIo->len - + pRdmaIo->packet_sz + + sizeof( ViportTrailer_t ); + //pRdmaIo->io.wrq.ds_array = pRdmaIo->dsList; + pRdmaIo->io.wrq.num_ds =p_sgl->NumberOfElements + 1; + + data_rdmaPacket( pData, pBpe, pRdmaIo ); + + if( p_xmitPool->sendKicks ) + { + /* EIOC needs kicks to inform it of sent packets */ + + p_xmitPool->kickCount++; + p_xmitPool->kickByteCount += pRdmaIo->packet_sz; + if( ( p_xmitPool->kickCount >= p_xmitPool->kickBundle ) + || ( p_xmitPool->kickByteCount >= p_xmitPool->kickByteBundle ) ) + { + data_sendKickMessage( pData ); + } + else if( p_xmitPool->kickCount == 1 ) + { + _data_kickTimer_start( pData, pData->eiocPoolParms.timeoutBeforeKick ); + } + } + return TRUE; +} +static uint8_t +_tx_chksum_flags( + IN NDIS_PACKET* const p_packet ) + +{ + NDIS_TCP_IP_CHECKSUM_PACKET_INFO *p_packet_info; + ULONG packet_info; + uint8_t txChksumFlags = 0; + + if( NDIS_PROTOCOL_ID_TCP_IP == NDIS_GET_PACKET_PROTOCOL_TYPE(p_packet) ) + { + packet_info = PtrToUlong( NDIS_PER_PACKET_INFO_FROM_PACKET( p_packet, TcpIpChecksumPacketInfo)); + p_packet_info = ( NDIS_TCP_IP_CHECKSUM_PACKET_INFO *)&packet_info; + + if( p_packet_info && + p_packet_info->Transmit.NdisPacketChecksumV4 ) + { + txChksumFlags = TX_CHKSUM_FLAGS_CHECKSUM_V4 + | ( p_packet_info->Transmit.NdisPacketIpChecksum ? TX_CHKSUM_FLAGS_IP_CHECKSUM: 0 ) + | ( p_packet_info->Transmit.NdisPacketTcpChecksum ? TX_CHKSUM_FLAGS_TCP_CHECKSUM: 0 ) + | ( p_packet_info->Transmit.NdisPacketUdpChecksum ? TX_CHKSUM_FLAGS_UDP_CHECKSUM: 0 ); + } + } + + VNIC_TRACE( VNIC_DBG_DATA | VNIC_DBG_INFO , + ("txChksumFlags = %d: V4 %c, V6 %c, IP %c, TCP %c, UDP %c\n", + txChksumFlags, + ((txChksumFlags & TX_CHKSUM_FLAGS_CHECKSUM_V4 )? '+': '-'), + ((txChksumFlags & TX_CHKSUM_FLAGS_CHECKSUM_V6 )? '+': '-'), + ((txChksumFlags & TX_CHKSUM_FLAGS_IP_CHECKSUM )? '+': '-'), + ((txChksumFlags & TX_CHKSUM_FLAGS_TCP_CHECKSUM )? '+': '-'), + ((txChksumFlags & TX_CHKSUM_FLAGS_UDP_CHECKSUM )? '+': '-') )); + + return txChksumFlags; +} + +static void +_get_first_buffer( + IN NDIS_PACKET *p_packet, + IN OUT NDIS_BUFFER **pp_buf_desc, + OUT void **pp_buf, + OUT ULONG *p_packet_sz ) +{ + UINT buf_len; + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + NdisGetFirstBufferFromPacketSafe( p_packet, + pp_buf_desc, + pp_buf, + &buf_len, + p_packet_sz, + NormalPagePriority ); + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); +} + +static void +data_postRecvs( + IN Data_t *pData ) +{ + RecvIo_t *pRecvIo; + LIST_ENTRY *p_list_entry; + ib_api_status_t ib_status; + + VNIC_ENTER ( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + while( ( p_list_entry = ExInterlockedRemoveHeadList( &pData->recvIos, + &pData->recvIosLock )) + != NULL ) + { + pRecvIo = (RecvIo_t *)p_list_entry; + + ib_status = ibqp_postRecv( &pData->qp, &pRecvIo->io ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, ("ibqp_postRecv returned %s\n", + pData->p_viport->p_adapter->ifc.get_err_str( ib_status )) ); + viport_failure( pData->p_viport ); + return; + } + } + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); + return; +} + +static void +_data_receivedKick( + IN Io_t *pIo ) +{ + Data_t *pData = &pIo->pViport->data; + uint32_t num_pkts = 0; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + +#ifdef VNIC_STATISTIC + recvRef = cl_get_tick_count(); +#endif /* VNIC_STATISTIC */ + + ExInterlockedInsertTailList( &pData->recvIos, &pIo->listPtrs, &pData->recvIosLock ); + + data_postRecvs( pData ); + +#ifdef VNIC_STATISTIC + pData->statistics.kickRecvs++; +#endif /* VNIC_STATISTIC */ + + data_checkXmitBuffers( pData ); + + num_pkts = _data_incomingRecv( pData ); + + if( num_pkts ) + { + NdisMIndicateReceivePacket( pData->p_viport->p_adapter->h_handle, + pData->recvPool.recv_pkt_array, + num_pkts ); + pData->p_viport->stats.ifInOk++; + } + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); + return; +} + +static void +_data_xmitComplete( + IN Io_t *pIo ) +{ + RdmaIo_t *pRdmaIo = (RdmaIo_t *)pIo; + Data_t *pData = &pIo->pViport->data; + XmitPool_t *p_xmitPool = &pData->xmitPool; + vnic_adapter_t *p_adapter = pIo->pViport->p_adapter; + NDIS_PACKET *p_packet; + NDIS_STATUS ndis_status; + cl_list_item_t *p_list_item; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + while ( p_xmitPool->lastCompBuf != pRdmaIo->index ) + { + INC(p_xmitPool->lastCompBuf, 1, p_xmitPool->numXmitBufs); + p_packet = p_xmitPool->pXmitBufs[p_xmitPool->lastCompBuf].p_packet; + + p_xmitPool->pXmitBufs[p_xmitPool->lastCompBuf].p_packet = NULL; + + if( p_packet != NULL ) + { + if( pIo->wc_status != IB_WCS_SUCCESS ) + { + ndis_status = NDIS_STATUS_FAILURE; + p_adapter->p_viport->stats.ifOutErrors++; + } + else + { + ndis_status = NDIS_STATUS_SUCCESS; + p_adapter->p_viport->stats.ifOutOk++; + } + NDIS_SET_PACKET_STATUS( p_packet, ndis_status ); + NdisMSendComplete( pIo->pViport->p_adapter->h_handle, + p_packet, ndis_status ); + } + } + pIo->wc_status = IB_WCS_SUCCESS; + if( !p_adapter->packet_filter ) + { + if( cl_qlist_count( &pIo->pViport->send_pending_list ) ) + { + for( p_list_item = cl_qlist_remove_head( &pIo->pViport->send_pending_list ); + p_list_item != cl_qlist_end( &pIo->pViport->send_pending_list ); + p_list_item = cl_qlist_remove_head( &pIo->pViport->send_pending_list ) ) + { + p_packet = VNIC_PACKET_FROM_LIST_ITEM( p_list_item ); + NdisMSendComplete( p_adapter->h_handle, p_packet, + NDIS_STATUS_RESET_IN_PROGRESS ); + } + } + } + data_checkXmitBuffers(pData); + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); + return; +} + +static void +data_sendKickMessage( + IN Data_t *pData ) +{ + XmitPool_t *pPool = &pData->xmitPool; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + /* stop timer for BundleTimeout */ + _data_kickTimer_stop( pData ); + + pPool->kickCount = 0; + pPool->kickByteCount = 0; + + /* TBD: Keep track of when kick is outstanding, and + * don't reuse until complete + */ + if ( ibqp_postSend( &pData->qp, &pData->kickIo.io ) != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("Unable to send kick to EIOC\n") ); + viport_failure( pData->p_viport ); + } + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); +} + +static void +_data_kickTimeoutHandler( void * context ) +{ + Data_t* pData = (Data_t *)context; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + pData->kickTimerOn = FALSE; + data_sendKickMessage( pData ); + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + return; +} + +static BOOLEAN +data_allocXmitBuffer( + IN Data_t *pData, + OUT BufferPoolEntry_t **ppBpe, + OUT RdmaIo_t **ppRdmaIo, + OUT BOOLEAN *pLast ) +{ + XmitPool_t *p_xmitPool = &pData->xmitPool; + KIRQL flags; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + KeAcquireSpinLock( &pData->xmitBufLock, &flags ); + + *pLast = FALSE; + *ppRdmaIo = &p_xmitPool->pXmitBufs[p_xmitPool->nextXmitBuf]; + *ppBpe = &p_xmitPool->bufPool[p_xmitPool->nextXmitPool]; + + if ( (*ppBpe)->valid && p_xmitPool->nextXmitBuf != p_xmitPool->lastCompBuf ) + { + INC(p_xmitPool->nextXmitBuf, 1, p_xmitPool->numXmitBufs); + INC(p_xmitPool->nextXmitPool, 1, p_xmitPool->poolSz); + + if ( !p_xmitPool->bufPool[p_xmitPool->nextXmitPool].valid ) + { + VNIC_TRACE( VNIC_DBG_DATA | VNIC_DBG_INFO, + ("Just used the last EIOU receive buffer\n") ); + + *pLast = TRUE; + p_xmitPool->needBuffers = TRUE; + viport_stopXmit( pData->p_viport ); +#ifdef VNIC_STATISTIC + pData->statistics.kickReqs++; +#endif /* VNIC_STATISTIC */ + } + else if ( p_xmitPool->nextXmitBuf == p_xmitPool->lastCompBuf ) + { + VNIC_TRACE( VNIC_DBG_DATA | VNIC_DBG_INFO, + ("Just used our last xmit buffer\n") ); + + p_xmitPool->needBuffers = TRUE; + viport_stopXmit( pData->p_viport ); + } + + (*ppBpe)->valid = 0; + + KeReleaseSpinLock( &pData->xmitBufLock, flags ); + return TRUE; + } + else + { +#ifdef VNIC_STATISTIC + pData->statistics.noXmitBufs++; +#endif /* VNIC_STATISTIC */ + + VNIC_TRACE( VNIC_DBG_ERROR, + ("Out of xmit buffers\n") ); + + viport_stopXmit( pData->p_viport ); + + KeReleaseSpinLock( &pData->xmitBufLock, flags ); + return FALSE; + } +} + +static void +data_checkXmitBuffers( + IN Data_t *pData ) +{ + XmitPool_t *p_xmitPool = &pData->xmitPool; + KIRQL flags; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + KeAcquireSpinLock( &pData->xmitBufLock, &flags ); + + if ( pData->xmitPool.needBuffers + && p_xmitPool->bufPool[p_xmitPool->nextXmitPool].valid + && p_xmitPool->nextXmitBuf != p_xmitPool->lastCompBuf ) + { + pData->xmitPool.needBuffers = FALSE; + viport_restartXmit( pData->p_viport ); + + VNIC_TRACE( VNIC_DBG_DATA | VNIC_DBG_INFO, + ("There are free xmit buffers\n") ); + } + + KeReleaseSpinLock( &pData->xmitBufLock, flags ); + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); + return; +} + +static void +data_rdmaPacket( + IN Data_t *pData, + IN BufferPoolEntry_t *pBpe, + IN RdmaIo_t *pRdmaIo ) +{ + ib_send_wr_t *pWrq; + uint64_t remote_addr; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + pWrq = &pRdmaIo->io.wrq; + + remote_addr = ntoh64( pBpe->remoteAddr ); + remote_addr += pData->xmitPool.bufferSz; + remote_addr -= ( pRdmaIo->len + sizeof(ViportTrailer_t) ); + + pWrq->remote_ops.vaddr = remote_addr; + pWrq->remote_ops.rkey = pBpe->rKey; + + pData->xmitPool.notifyCount++; + + if( pData->xmitPool.notifyCount >= pData->xmitPool.notifyBundle ) + { + pData->xmitPool.notifyCount = 0; + pWrq->send_opt = IB_SEND_OPT_SIGNALED; + } + else + { + pWrq->send_opt &= ~IB_SEND_OPT_SIGNALED; + } + pWrq->send_opt = IB_SEND_OPT_SIGNALED; + + if( ibqp_postSend( &pData->qp, &pRdmaIo->io ) != IB_SUCCESS ) + { + VNIC_TRACE(VNIC_DBG_ERROR, + ("Failed sending data to EIOC\n") ); + viport_failure( pData->p_viport ); + return; + } +#ifdef VNIC_STATISTIC + pData->statistics.xmitNum++; +#endif /* VNIC_STATISTIC */ + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); +} + +static NDIS_PACKET * +_data_recv_to_ndis_pkt( + IN Data_t *pData, + IN RdmaDest_t *pRdmaDest ) +{ + struct ViportTrailer *pTrailer; + NDIS_PACKET *p_packet; + NDIS_STATUS ndis_status; + int start; + unsigned int len; + uint8_t rxChksumFlags; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO packet_info; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + pTrailer = pRdmaDest->pTrailer; + start = (int)data_offset(pData, pTrailer); + len = data_len(pData, pTrailer); + + NdisAllocatePacket( &ndis_status, + &p_packet, + pData->h_recv_pkt_pool ); + + if ( ndis_status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ( "NdisAllocatePacket failed %#x\n", ndis_status ) ); + return NULL; + } + NdisAllocateBuffer( &ndis_status, + &pRdmaDest->p_buf, + pData->h_recv_buf_pool, + pRdmaDest->data + start, + len ); + + if ( ndis_status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ( "NdisAllocateBuffer failed %#x\n", ndis_status ) ); + NdisFreePacket( p_packet ); + return NULL; + } + + NdisChainBufferAtFront( p_packet, pRdmaDest->p_buf ); + pRdmaDest->p_packet = p_packet; + + if ( pTrailer->pktFlags & PF_VLAN_INSERT ) + { + /* TODO: + * add OID_GEN_VLAN_ID + * handle VLAN tag insertion + * set packet header size = eth_hdr + 4 + */ + } + + NDIS_SET_PACKET_HEADER_SIZE( p_packet, sizeof(eth_hdr_t) ); + + rxChksumFlags = pTrailer->rxChksumFlags; + + VNIC_TRACE( VNIC_DBG_DATA | VNIC_DBG_INFO, + ("rxChksumFlags = %d, LOOP = %c, IP = %c, TCP = %c, UDP = %c\n", + rxChksumFlags, + (rxChksumFlags & RX_CHKSUM_FLAGS_LOOPBACK)? 'Y': 'N', + (rxChksumFlags & RX_CHKSUM_FLAGS_IP_CHECKSUM_SUCCEEDED)? 'Y': + (rxChksumFlags & RX_CHKSUM_FLAGS_IP_CHECKSUM_FAILED)? 'N': '-', + (rxChksumFlags & RX_CHKSUM_FLAGS_TCP_CHECKSUM_SUCCEEDED)? 'Y': + (rxChksumFlags & RX_CHKSUM_FLAGS_TCP_CHECKSUM_FAILED)? 'N': '-', + (rxChksumFlags & RX_CHKSUM_FLAGS_UDP_CHECKSUM_SUCCEEDED)? 'Y': + (rxChksumFlags & RX_CHKSUM_FLAGS_UDP_CHECKSUM_FAILED)? 'N': '-') ); + + packet_info.Value = 0; + + if( rxChksumFlags & RX_CHKSUM_FLAGS_IP_CHECKSUM_SUCCEEDED ) + packet_info.Receive.NdisPacketIpChecksumSucceeded = TRUE; + else if( rxChksumFlags & RX_CHKSUM_FLAGS_IP_CHECKSUM_FAILED ) + packet_info.Receive.NdisPacketIpChecksumFailed = TRUE; + + if( rxChksumFlags & RX_CHKSUM_FLAGS_TCP_CHECKSUM_SUCCEEDED ) + packet_info.Receive.NdisPacketTcpChecksumSucceeded = TRUE; + else if( rxChksumFlags & RX_CHKSUM_FLAGS_TCP_CHECKSUM_FAILED ) + packet_info.Receive.NdisPacketTcpChecksumFailed = TRUE; + + if( rxChksumFlags & RX_CHKSUM_FLAGS_TCP_CHECKSUM_SUCCEEDED ) + packet_info.Receive.NdisPacketUdpChecksumSucceeded = TRUE; + else if( rxChksumFlags & RX_CHKSUM_FLAGS_TCP_CHECKSUM_FAILED ) + packet_info.Receive.NdisPacketUdpChecksumFailed = TRUE; + + NDIS_PER_PACKET_INFO_FROM_PACKET( p_packet, TcpIpChecksumPacketInfo )= + (void *)(uintn_t)packet_info.Value; + + VNIC_RECV_FROM_PACKET( p_packet ) = pRdmaDest; + NDIS_SET_PACKET_STATUS( p_packet, NDIS_STATUS_SUCCESS ); + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); + return p_packet; +} + +/* NOTE: This routine is not reentrant */ +static void +_data_allocBuffers( + IN Data_t *pData, + IN BOOLEAN initialAllocation ) +{ + RecvPool_t *p_recvPool = &pData->recvPool; + RdmaDest_t *pRdmaDest; + LIST_ENTRY *p_list_entry; + int index; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + index = ADD(p_recvPool->nextFreeBuf, p_recvPool->numFreeBufs, p_recvPool->eiocPoolSz); + + while ( !IsListEmpty( &p_recvPool->availRecvBufs ) ) + { + p_list_entry = RemoveHeadList( &p_recvPool->availRecvBufs ); + pRdmaDest = (RdmaDest_t*)p_list_entry; + + if( initialAllocation ) + { + pRdmaDest->buf_sz = p_recvPool->bufferSz; + pRdmaDest->pTrailer = + (struct ViportTrailer*)(pRdmaDest->data + pRdmaDest->buf_sz + - sizeof(struct ViportTrailer)); + pRdmaDest->pTrailer->connectionHashAndValid = 0; + } + + pRdmaDest->p_packet = NULL; + _data_addFreeBuffer(pData, index, pRdmaDest); + index = NEXT(index,p_recvPool->eiocPoolSz); + } + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); + return; +} + +static void +_data_addFreeBuffer( + IN Data_t *pData, + IN int index, + IN RdmaDest_t *pRdmaDest ) +{ + RecvPool_t *p_recvPool = &pData->recvPool; + BufferPoolEntry_t *pBpe; + + pRdmaDest->pTrailer->connectionHashAndValid = 0; + pBpe = &p_recvPool->bufPool[index]; + + pBpe->rKey = pRdmaDest->region.rkey; + pBpe->remoteAddr = hton64( PTR64( pRdmaDest->data ) ); + pBpe->valid = (uint32_t)(pRdmaDest - &p_recvPool->pRecvBufs[0]) + 1; + ++p_recvPool->numFreeBufs; + + return; +} + +static uint32_t +_data_incomingRecv( + IN Data_t *pData ) +{ + RecvPool_t *p_recvPool = &pData->recvPool; + RdmaDest_t *pRdmaDest; + ViportTrailer_t *pTrailer; + BufferPoolEntry_t *pBpe; + NDIS_PACKET *p_packet = NULL; + uint32_t idx = 0; + BOOLEAN status = FALSE; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + while( !status ) + { + if ( p_recvPool->nextFullBuf == p_recvPool->nextFreeBuf ) + return idx; + + pBpe = &p_recvPool->bufPool[p_recvPool->nextFullBuf]; + pRdmaDest = &p_recvPool->pRecvBufs[pBpe->valid - 1]; + pTrailer = pRdmaDest->pTrailer; + + if ( ( pTrailer != NULL ) && + ( pTrailer->connectionHashAndValid & CHV_VALID ) ) + { + /* received a packet */ + if ( pTrailer->pktFlags & PF_KICK ) + { + p_recvPool->kickOnFree = TRUE; + } + /* we do not want to indicate packet if no filter is set */ + if( pData->p_viport->p_adapter->packet_filter ) + { + p_packet = _data_recv_to_ndis_pkt( pData, pRdmaDest ); + if ( p_packet != NULL ) + { + p_recvPool->recv_pkt_array[idx++] = p_packet; + } + } + else + { /* put back to free buffers pool */ + InsertTailList( &p_recvPool->availRecvBufs, + &pRdmaDest->listPtrs ); + } + pBpe->valid = 0; + INC( p_recvPool->nextFullBuf, 1, p_recvPool->eiocPoolSz ); + p_recvPool->numPostedBufs--; +#ifdef VNIC_STATISTIC + pData->statistics.recvNum++; +#endif /* VNIC_STATISTIC */ + } + else + break; + } + return idx; +} + + +void +vnic_return_packet( + IN NDIS_HANDLE adapter_context, + IN NDIS_PACKET * const p_packet ) +{ + + + vnic_adapter_t *p_adapter = (vnic_adapter_t *)adapter_context; + viport_t *p_viport = p_adapter->p_currentPath->pViport; + RdmaDest_t *p_rdma_dest = VNIC_RECV_FROM_PACKET( p_packet ); + + ASSERT( p_rdma_dest->p_packet == p_packet ); + _data_return_recv( p_packet ); + p_rdma_dest->p_packet = NULL; + + InsertTailList( &p_viport->data.recvPool.availRecvBufs, + &p_rdma_dest->listPtrs ); + + if( p_viport->data.connected == TRUE ) + { + _data_allocBuffers( &p_viport->data, FALSE ); + _data_sendFreeRecvBuffers( &p_viport->data ); + } +} +static void +_data_return_recv( + IN NDIS_PACKET *p_packet ) +{ + NDIS_BUFFER *p_buf; + /* Unchain the NDIS buffer. */ + NdisUnchainBufferAtFront( p_packet, &p_buf ); + CL_ASSERT( p_buf ); + /* Return the NDIS packet and NDIS buffer to their pools. */ + NdisFreeBuffer( p_buf ); + NdisFreePacket( p_packet ); +} + +static void +_data_sendFreeRecvBuffers( + IN Data_t *pData ) +{ + RecvPool_t *p_recvPool = &pData->recvPool; + ib_send_wr_t *pWrq = &pData->freeBufsIo.io.wrq; + BOOLEAN bufsSent = FALSE; + uint64_t rdmaAddr; + uint32_t offset; + uint32_t sz; + unsigned int numToSend, + nextIncrement; + + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + for ( numToSend = p_recvPool->szFreeBundle; + numToSend <= p_recvPool->numFreeBufs; + numToSend += p_recvPool->szFreeBundle ) + { + /* Handle multiple bundles as one when possible. */ + nextIncrement = numToSend + p_recvPool->szFreeBundle; + if ( ( nextIncrement <= p_recvPool->numFreeBufs ) + && ( p_recvPool->nextFreeBuf + nextIncrement <= p_recvPool->eiocPoolSz ) ) + { + continue; + } + + offset = p_recvPool->nextFreeBuf * sizeof(BufferPoolEntry_t); + sz = numToSend * sizeof(BufferPoolEntry_t); + rdmaAddr = p_recvPool->eiocRdmaAddr + offset; + + //pWrq->MessageLen = sz; + pWrq->ds_array->length = sz; + pWrq->ds_array->vaddr = PTR64((uint8_t *)p_recvPool->bufPool + offset); + pWrq->remote_ops.vaddr = rdmaAddr; + + if ( ibqp_postSend( &pData->qp, &pData->freeBufsIo.io ) != IB_SUCCESS ) + { + VNIC_TRACE(VNIC_DBG_ERROR, + ("Unable to rdma free buffers to EIOC\n") ); + + viport_failure( pData->p_viport ); + break; + } + + INC( p_recvPool->nextFreeBuf, numToSend, p_recvPool->eiocPoolSz ); + p_recvPool->numFreeBufs -= numToSend; + p_recvPool->numPostedBufs += numToSend; + bufsSent = TRUE; + } + + if( bufsSent ) + { + if( p_recvPool->kickOnFree ) + { + data_sendKickMessage( pData ); + } + } + if( p_recvPool->numPostedBufs == 0 ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("%s: Unable to allocate receive buffers\n", + pData->p_viport->p_adapter->name ) ); + + viport_failure( pData->p_viport ); + } + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); +} + + +void +data_cleanup( + IN Data_t *pData ) +{ + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + if( pData->recvPool.recv_pkt_array ) + { + cl_free( pData->recvPool.recv_pkt_array ); + pData->recvPool.recv_pkt_array = NULL; + pData->recvPool.poolSz = 0; + } + + if ( pData->pLocalStorage ) + { + NdisFreeMemory( pData->pLocalStorage, pData->localStorageSz, 0 ); + pData->pLocalStorage = NULL; + } + if( pData->h_recv_buf_pool ) + { + NdisFreeBufferPool( pData->h_recv_buf_pool ); + pData->h_recv_buf_pool = NULL; + } + if ( pData->h_recv_pkt_pool ) + { + NdisFreePacketPool( pData->h_recv_pkt_pool ); + pData->h_recv_pkt_pool = NULL; + } + // clear Qp struct for reuse + cl_memclr( &pData->qp, sizeof( IbQp_t) ); + + cl_timer_destroy( &pData->kickTimer ); + + VNIC_EXIT( VNIC_DBG_CTRL ); +} + + +static void +_data_kickTimer_start( + IN Data_t *pData, + IN uint32_t microseconds ) +{ + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + InterlockedExchange( (LONG *)&pData->kickTimerOn, TRUE ); + + usec_timer_start(&pData->kickTimer, microseconds ); + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); + return; +} + +static void +_data_kickTimer_stop( + IN Data_t *pData ) +{ + VNIC_ENTER( VNIC_DBG_DATA | VNIC_DBG_INFO ); + + if( InterlockedExchange( &pData->kickTimerOn, FALSE ) == TRUE ) + { + cl_timer_stop( &pData->kickTimer ); + } + + VNIC_EXIT( VNIC_DBG_DATA | VNIC_DBG_INFO ); +} diff --git a/trunk/ulp/inic/kernel/vnic_data.h b/trunk/ulp/inic/kernel/vnic_data.h new file mode 100644 index 00000000..1aec7efc --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_data.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + +#ifndef _VNIC_DATA_H_ +#define _VNIC_DATA_H_ + +#include "vnic_trailer.h" + +typedef struct RdmaDest { + LIST_ENTRY listPtrs; + IbRegion_t region; + NDIS_PACKET *p_packet; + NDIS_BUFFER *p_buf; + uint32_t buf_sz; + uint8_t *data; + struct ViportTrailer *pTrailer; +} RdmaDest_t; + +typedef struct BufferPoolEntry { + uint64_t remoteAddr; + net32_t rKey; + uint32_t valid; +} BufferPoolEntry_t; + +typedef struct RecvPool { + uint32_t bufferSz; + uint32_t poolSz; + uint32_t eiocPoolSz; + uint32_t eiocRdmaRkey; + uint64_t eiocRdmaAddr; + uint32_t nextFullBuf; + uint32_t nextFreeBuf; + uint32_t numFreeBufs; + uint32_t numPostedBufs; + uint32_t szFreeBundle; + BOOLEAN kickOnFree; + BufferPoolEntry_t *bufPool; + RdmaDest_t *pRecvBufs; + LIST_ENTRY availRecvBufs; + NDIS_PACKET **recv_pkt_array; +} RecvPool_t; + +typedef struct XmitPool { + uint32_t bufferSz; + uint32_t poolSz; + uint32_t notifyCount; + uint32_t notifyBundle; + uint32_t nextXmitBuf; + uint32_t lastCompBuf; + uint32_t numXmitBufs; + uint32_t nextXmitPool; + uint32_t kickCount; + uint32_t kickByteCount; + uint32_t kickBundle; + uint32_t kickByteBundle; + BOOLEAN needBuffers; + BOOLEAN sendKicks; + uint32_t rdmaRKey; + uint64_t rdmaAddr; + BufferPoolEntry_t *bufPool; + RdmaIo_t *pXmitBufs; +} XmitPool_t; + +typedef struct Data { + struct _viport *p_viport; + DataConfig_t *p_conf; + IbRegion_t *p_phy_region; + IbRegion_t region; + IbRegion_t rbuf_region; + IbQp_t qp; + uint8_t *pLocalStorage; + uint32_t localStorageSz; + uint8_t *p_recv_bufs; + uint32_t recv_bufs_sz; + Inic_RecvPoolConfig_t hostPoolParms; + Inic_RecvPoolConfig_t eiocPoolParms; + RecvPool_t recvPool; + XmitPool_t xmitPool; + RdmaIo_t freeBufsIo; + SendIo_t kickIo; + LIST_ENTRY recvIos; + KSPIN_LOCK recvIosLock; + KSPIN_LOCK xmitBufLock; + volatile LONG kickTimerOn; + BOOLEAN connected; + cl_timer_t kickTimer; + NDIS_HANDLE h_recv_pkt_pool; + NDIS_HANDLE h_recv_buf_pool; +#ifdef VNIC_STATISTIC + struct { + uint32_t xmitNum; + uint32_t recvNum; + uint32_t freeBufSends; + uint32_t freeBufNum; + uint32_t freeBufMin; + uint32_t kickRecvs; + uint32_t kickReqs; + uint32_t noXmitBufs; + uint64_t noXmitBufTime; + } statistics; +#endif /* VNIC_STATISTIC */ +} Data_t; + +void +vnic_return_packet( + IN NDIS_HANDLE adapter_context, + IN NDIS_PACKET* const p_packet ); + +void +data_construct( + IN Data_t *pData, + IN struct _viport *pViport ); + +ib_api_status_t +data_init( + Data_t *pData, + DataConfig_t *p_conf, + uint64_t guid ); + +ib_api_status_t +data_connect( + Data_t *pData ); + +void +data_connected( + Data_t *pData ); + +void +data_disconnect( + Data_t *pData ); + +BOOLEAN +data_xmitPacket( + Data_t *pData, + NDIS_PACKET* const p_pkt ); + +void +data_cleanup( + Data_t *pData ); + +#define data_pathId(pData) (pData)->p_conf->pathId +#define data_eiocPool(pData) &(pData)->eiocPoolParms +#define data_hostPool(pData) &(pData)->hostPoolParms +#define data_eiocPoolMin(pData) &(pData)->p_conf->eiocMin +#define data_hostPoolMin(pData) &(pData)->p_conf->hostMin +#define data_eiocPoolMax(pData) &(pData)->p_conf->eiocMax +#define data_hostPoolMax(pData) &(pData)->p_conf->hostMax +#define data_localPoolAddr(pData) (pData)->xmitPool.rdmaAddr +#define data_localPoolRkey(pData) (pData)->xmitPool.rdmaRKey +#define data_remotePoolAddr(pData) &(pData)->recvPool.eiocRdmaAddr +#define data_remotePoolRkey(pData) &(pData)->recvPool.eiocRdmaRkey +#define data_maxMtu(pData) MAX_PAYLOAD(min((pData)->recvPool.bufferSz, (pData)->xmitPool.bufferSz)) - ETH_VLAN_HLEN +#define data_len(pData, pTrailer) ntoh16(pTrailer->dataLength) +#define data_offset(pData, pTrailer) \ + pData->recvPool.bufferSz - sizeof(struct ViportTrailer) \ + - (uint32_t)ROUNDUPP2(data_len(pData, pTrailer), VIPORT_TRAILER_ALIGNMENT) \ + + pTrailer->dataAlignmentOffset + + +/* The following macros manipulate ring buffer indexes. + * The ring buffer size must be a power of 2. + */ +#define ADD(index, increment, size) (((index) + (increment))&((size) - 1)) +#define NEXT(index, size) ADD(index, 1, size) +#define INC(index, increment, size) (index) = ADD(index, increment, size) + +#define VNIC_RECV_FROM_PACKET( P ) \ + (((RdmaDest_t **)P->MiniportReservedEx)[1]) + +#define VNIC_LIST_ITEM_FROM_PACKET( P ) \ + ((cl_list_item_t*)P->MiniportReservedEx) + +#define VNIC_PACKET_FROM_LIST_ITEM( I ) \ + (PARENT_STRUCT( I, NDIS_PACKET, MiniportReservedEx )) + +#endif /* _VNIC_DATA_H_ */ \ No newline at end of file diff --git a/trunk/ulp/inic/kernel/vnic_debug.h b/trunk/ulp/inic/kernel/vnic_debug.h new file mode 100644 index 00000000..62e7f1d4 --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_debug.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + + +#ifndef _VNIC_DEBUG_H_ +#define _VNIC_DEBUG_H_ + + +#include + +/* + * Debug macros + */ +extern uint32_t g_vnic_dbg_lvl; + + +#define VNIC_DBG_INIT (0x00000001) +#define VNIC_DBG_PNP (0x00000002) +#define VNIC_DBG_SEND (0x00000004) +#define VNIC_DBG_RECV (0x00000008) +#define VNIC_DBG_STATUS (0x00000010) +#define VNIC_DBG_IB (0x00000020) +#define VNIC_DBG_BUF (0x00000040) +#define VNIC_DBG_MCAST (0x00000080) +#define VNIC_DBG_ALLOC (0x00000100) +#define VNIC_DBG_OID (0x00000200) +#define VNIC_DBG_DATA (0x00000400) +#define VNIC_DBG_CTRL (0x00000800) +#define VNIC_DBG_CTRL_PKT (0x00001000) +#define VNIC_DBG_CONF (0x00002000) +#define VNIC_DBG_VIPORT (0x00004000) +#define VNIC_DBG_ADAPTER (0x00008000) +#define VNIC_DBG_NETPATH (0x00010000) + +#define VNIC_DBG_FUNC (0x10000000) /* For function entry/exit */ +#define VNIC_DBG_INFO (0x20000000) /* For verbose information */ +#define VNIC_DBG_WARN (0x40000000) /* For warnings. */ +#define VNIC_DBG_ERROR CL_DBG_ERROR +#define VNIC_DBG_ALL CL_DBG_ALL + +#define VNIC_DEBUG_FLAGS ( VNIC_DBG_ERROR /*| VNIC_DBG_WARN | VNIC_DBG_INFO | VNIC_DBG_FUNC | VNIC_DBG_OID | VNIC_DBG_VIPORT | VNIC_DBG_CTRL | VNIC_DBG_DATA */) + +/* Enter and exit macros automatically add VNIC_DBG_FUNC bit */ +#define VNIC_ENTER( lvl ) \ + CL_ENTER( (lvl | VNIC_DBG_FUNC), g_vnic_dbg_lvl ) + +#define VNIC_EXIT( lvl ) \ + CL_EXIT( (lvl | VNIC_DBG_FUNC), g_vnic_dbg_lvl ) + +#define VNIC_TRACE( lvl, msg ) \ + CL_TRACE( (lvl), g_vnic_dbg_lvl, msg ) + +#define VNIC_TRACE_EXIT( lvl, msg ) \ + CL_TRACE_EXIT( (lvl), g_vnic_dbg_lvl, msg ) + +#define VNIC_PRINT( lvl, msg ) \ + CL_PRINT ( (lvl), g_vnic_dbg_lvl, msg ) + +#define VNIC_TRACE_BYTES( lvl, ptr, len ) \ + { \ + size_t _loop_; \ + for (_loop_ = 0; _loop_ < (len); ++_loop_) \ + { \ + CL_PRINT( (lvl), g_vnic_dbg_lvl, ("0x%.2X ", ((uint8_t*)(ptr))[_loop_])); \ + if ((_loop_ + 1)% 16 == 0) \ + { \ + CL_PRINT( (lvl), g_vnic_dbg_lvl, ("\n") ); \ + } \ + else if ((_loop_ % 4 + 1) == 0) \ + { \ + CL_PRINT( (lvl), g_vnic_dbg_lvl, (" ") ); \ + } \ + } \ + CL_PRINT( (lvl), g_vnic_dbg_lvl, ("\n") ); \ + } + + +#endif /* _VNIC_DEBUG_H_ */ diff --git a/trunk/ulp/inic/kernel/vnic_driver.c b/trunk/ulp/inic/kernel/vnic_driver.c new file mode 100644 index 00000000..67ecbb9a --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_driver.c @@ -0,0 +1,1877 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + + + +#include +#include "vnic_driver.h" + + +vnic_globals_t g_vnic; + +#define DEFAULT_HOST_NAME "VNIC Host" + +//uint32_t g_vnic_dbg_lvl = VNIC_DBG_ERROR | VNIC_DBG_INIT | VNIC_DBG_IB | VNIC_DBG_INFO; +uint32_t g_vnic_dbg_lvl = VNIC_DEBUG_FLAGS; + +static void +_vnic_complete_query( + IN vnic_adapter_t* const p_adapter, + IN pending_oid_t* const p_oid_info, + IN const NDIS_STATUS status, + IN const void* const p_buf, + IN const ULONG buf_len ); + +static NDIS_STATUS +__vnic_get_tcp_task_offload( + IN vnic_adapter_t* p_adapter, + IN pending_oid_t* const p_oid_info ); + +static NDIS_STATUS +__vnic_set_tcp_task_offload( + IN vnic_adapter_t* p_adapter, + IN void* const p_info_buf, + IN ULONG* const p_info_len ); + +static NDIS_STATUS +_vnic_process_packet_filter( + IN vnic_adapter_t* const p_adapter, + IN ULONG pkt_filter ); + +static void +__vnic_read_machine_name( void ); + +static NDIS_STATUS +__vnic_set_machine_name( + IN VOID *p_uni_array, + IN USHORT buf_size ); + +/* +p_drv_obj + Pointer to Driver Object for this device driver +p_registry_path + Pointer to unicode string containing path to this driver's registry area +return + STATUS_SUCCESS, NDIS_STATUS_BAD_CHARACTERISTICS, NDIS_STATUS_BAD_VERSION, + NDIS_STATUS_RESOURCES, or NDIS_STATUS_FAILURE +IRQL = PASSIVE_LEVEL +*/ +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT p_drv_obj, + IN PUNICODE_STRING p_registry_path ) +{ + NDIS_STATUS status; + NDIS_MINIPORT_CHARACTERISTICS characteristics; + + VNIC_ENTER( VNIC_DBG_INIT ); + +#ifdef _DEBUG_ + PAGED_CODE(); +#endif + + status = CL_INIT; + + if( !NT_SUCCESS( status ) ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("cl_init failed.\n") ); + return status; + } + + status = NDIS_STATUS_SUCCESS; + + KeInitializeSpinLock( &g_vnic.lock ); + InitializeListHead( &g_vnic.adapter_list ); + + g_vnic.adapters = 0; + g_vnic.ndis_handle = NULL; + + NdisMInitializeWrapper( &g_vnic.ndis_handle, p_drv_obj, p_registry_path, NULL ); + + if ( g_vnic.ndis_handle == NULL ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("NdisMInitializeWrapper failed\n")); + CL_DEINIT; + return NDIS_STATUS_FAILURE; + } + + cl_memclr( &characteristics, sizeof(characteristics) ); + + characteristics.MajorNdisVersion = MAJOR_NDIS_VERSION; + characteristics.MinorNdisVersion = MINOR_NDIS_VERSION; + characteristics.CheckForHangHandler = vnic_check_for_hang; + characteristics.HaltHandler = vnic_halt; + characteristics.InitializeHandler = vnic_initialize; + characteristics.QueryInformationHandler = vnic_oid_query_info; + characteristics.ResetHandler = vnic_reset; + characteristics.SetInformationHandler = vnic_oid_set_info; + characteristics.ReturnPacketHandler = vnic_return_packet; + characteristics.SendPacketsHandler = vnic_send_packets; + +#ifdef NDIS51_MINIPORT + characteristics.PnPEventNotifyHandler = vnic_pnp_notify; + characteristics.AdapterShutdownHandler = vnic_shutdown; +#endif + + status = NdisMRegisterMiniport( + g_vnic.ndis_handle, &characteristics, sizeof(characteristics) ); + + if( status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("NdisMRegisterMiniport failed with status of %d\n", status) ); + NdisTerminateWrapper( g_vnic.ndis_handle, NULL ); + CL_DEINIT; + } + else + { + NdisMRegisterUnloadHandler( g_vnic.ndis_handle, vnic_unload ); + + __vnic_read_machine_name(); + } + + VNIC_EXIT( VNIC_DBG_INIT ); + return status; +} + + +VOID +vnic_unload( + IN PDRIVER_OBJECT p_drv_obj ) +{ + VNIC_ENTER( VNIC_DBG_INIT ); + + UNREFERENCED_PARAMETER( p_drv_obj ); + CL_DEINIT; + + VNIC_EXIT( VNIC_DBG_INIT ); +} + + +//! Initialization function called for each IOC discovered +/* The MiniportInitialize function is a required function that sets up a +NIC (or virtual NIC) for network I/O operations, claims all hardware +resources necessary to the NIC in the registry, and allocates resources +the driver needs to carry out network I/O operations. +IRQL = PASSIVE_LEVEL + +@param p_open_status Pointer to a status field set if this function returns NDIS_STATUS_OPEN_ERROR +@param p_selected_medium_index Pointer to unsigned integer noting index into medium_array for this NIC +@param medium_array Array of mediums for this NIC +@param medium_array_size Number of elements in medium_array +@param h_handle Handle assigned by NDIS for this NIC +@param wrapper_config_context Handle used for Ndis initialization functions +@return NDIS_STATUS_SUCCESS, NDIS_STATUS_UNSUPPORTED_MEDIA, NDIS_STATUS_RESOURCES, +NDIS_STATUS_NOT_SUPPORTED +*/ +NDIS_STATUS +vnic_initialize( + OUT PNDIS_STATUS p_open_status, + OUT PUINT p_selected_medium_index, + IN PNDIS_MEDIUM medium_array, + IN UINT medium_array_size, + IN NDIS_HANDLE h_handle, + IN NDIS_HANDLE wrapper_config_context ) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + ib_api_status_t ib_status; + ib_pnp_req_t pnp_req; + UINT medium_index; + vnic_adapter_t *p_adapter; + ULONG buffer_size; + + VNIC_ENTER( VNIC_DBG_INIT ); + +#ifdef _DEBUG_ + PAGED_CODE(); +#endif + + UNUSED_PARAM( p_open_status ); + UNUSED_PARAM( wrapper_config_context ); + + /* Search for our medium */ + for( medium_index = 0; medium_index < medium_array_size; ++medium_index ) + { + /* Check to see if we found our medium */ + if( medium_array[medium_index] == NdisMedium802_3 ) + break; + } + + if( medium_index == medium_array_size ) /* Never found it */ + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, ("No supported media.\n") ); + return NDIS_STATUS_UNSUPPORTED_MEDIA; + } + + *p_selected_medium_index = medium_index; + + /* Create the adapter */ + ib_status = vnic_create_adapter( h_handle, &p_adapter ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("returned status %x\n", ib_status ) ); + return NDIS_STATUS_FAILURE; + } + + /* set NDIS features we support */ + NdisMSetAttributesEx( h_handle, + p_adapter, + 5, /*check for hung t-out */ + NDIS_ATTRIBUTE_BUS_MASTER | + NDIS_ATTRIBUTE_DESERIALIZE | + NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK | + NDIS_ATTRIBUTE_USES_SAFE_BUFFER_APIS, + NdisInterfacePNPBus ); + + buffer_size = sizeof(ViportTrailer_t) + + ROUNDUPP2(p_adapter->params.MaxMtu + sizeof(eth_hdr_t),VIPORT_TRAILER_ALIGNMENT ); + + status = NdisMInitializeScatterGatherDma( h_handle, TRUE, buffer_size ); + if ( status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("Init ScatterGatherDma failed status %#x\n", status ) ); + goto failure; + } + + /* Register for IOC events */ + pnp_req.pfn_pnp_cb = __vnic_pnp_cb; + pnp_req.pnp_class = IB_PNP_IOC | IB_PNP_FLAG_REG_SYNC; + pnp_req.pnp_context = p_adapter; + + ib_status = p_adapter->ifc.reg_pnp( p_adapter->h_al, &pnp_req, &p_adapter->h_pnp ); + + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("ib_reg_pnp returned %s\n", p_adapter->ifc.get_err_str( ib_status )) ); + status = NDIS_STATUS_FAILURE; + goto failure; + } + + if( p_adapter->state == INIC_UNINITIALIZED ) + { + status = NDIS_STATUS_FAILURE; +failure: + vnic_destroy_adapter( p_adapter ); + return status; + } + + VNIC_EXIT( VNIC_DBG_INIT ); + return status; + +} + + +//! Deallocates resources when the NIC is removed and halts the NIC.. +/* IRQL = DISPATCH_LEVEL + +@param adapter_context The adapter context allocated at start +*/ +void +vnic_halt( + IN NDIS_HANDLE adapter_context ) +{ + vnic_adapter_t *p_adapter; + + VNIC_ENTER( VNIC_DBG_INIT ); + CL_ASSERT( adapter_context ); + + p_adapter = (vnic_adapter_t*)adapter_context; + + vnic_destroy_adapter( p_adapter ); + + VNIC_EXIT( VNIC_DBG_INIT ); +} + + +//! Reports the state of the NIC, or monitors the responsiveness of an underlying device driver. +/* IRQL = DISPATCH_LEVEL + +@param adapter_context The adapter context allocated at start +@return TRUE if the driver determines that its NIC is not operating +*/ +BOOLEAN +vnic_check_for_hang( + IN NDIS_HANDLE adapter_context ) +{ + vnic_adapter_t *p_adapter; + + CL_ASSERT( adapter_context ); + p_adapter = (vnic_adapter_t*)adapter_context; + + if( p_adapter->p_viport ) + { + p_adapter->hung = + (BOOLEAN)( p_adapter->p_viport->errored != 0 ); + } + if( p_adapter->hung ) + { + VNIC_TRACE( VNIC_DBG_WARN, ("Adapter Hung\n")); + p_adapter->hung = FALSE; + return TRUE; + } + return FALSE; +} + + +//! Returns information about the capabilities and status of the driver and/or its NIC. +/* IRQL = DISPATCH_LEVEL + +@param adapter_context The adapter context allocated at start +@param oid Object ID representing the query operation to be carried out +@param info_buf Buffer containing any input for this query and location for output +@param info_buf_len Number of bytes available in info_buf +@param p_bytes_written Pointer to number of bytes written into info_buf +@param p_bytes_needed Pointer to number of bytes needed to satisfy this oid +@return NDIS_STATUS_SUCCESS, NDIS_STATUS_PENDING, NDIS_STATUS_INVALID_OID, +NDIS_STATUS_INVALID_LENGTH, NDIS_STATUS_NOT_ACCEPTED, NDIS_STATUS_NOT_SUPPORTED, +NDIS_STATUS_RESOURCES +*/ +NDIS_STATUS +vnic_oid_query_info( + IN NDIS_HANDLE adapter_context, + IN NDIS_OID oid, + IN PVOID info_buf, + IN ULONG info_buf_len, + OUT PULONG p_bytes_written, + OUT PULONG p_bytes_needed ) +{ + vnic_adapter_t *p_adapter; + NDIS_STATUS status; + USHORT version; + uint32_t info32; + uint64_t info64; + PVOID src_buf; + ULONG buf_len; + pending_oid_t oid_info; + + VNIC_ENTER( VNIC_DBG_OID ); + + oid_info.oid = oid; + oid_info.p_buf = info_buf; + oid_info.buf_len = info_buf_len; + oid_info.p_bytes_used = p_bytes_written; + oid_info.p_bytes_needed = p_bytes_needed; + + CL_ASSERT( adapter_context ); + p_adapter = (vnic_adapter_t *)adapter_context; + + CL_ASSERT( p_bytes_written ); + CL_ASSERT( p_bytes_needed ); + + status = NDIS_STATUS_SUCCESS; + src_buf = &info32; + buf_len = sizeof(info32); + + switch( oid ) + { + /* Required General */ + case OID_GEN_SUPPORTED_LIST: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("received query for OID_GEN_SUPPORTED_LIST\n") ); + src_buf = (PVOID)SUPPORTED_OIDS; + buf_len = sizeof(SUPPORTED_OIDS); + break; + + case OID_GEN_HARDWARE_STATUS: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("received query for OID_GEN_HARDWARE_STATUS\n") ); + + if( p_adapter->p_currentPath->carrier ) + { + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("returning NdisHardwareStatusReady\n") ); + info32 = NdisHardwareStatusReady; + } + else + { + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("returning NdisHardwareStatusInitializing\n") ); + info32 = NdisHardwareStatusNotReady; + } + break; + + case OID_GEN_MEDIA_SUPPORTED: + case OID_GEN_MEDIA_IN_USE: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("received query for OID_GEN_MEDIA_SUPPORTED " + "or OID_GEN_MEDIA_IN_USE\n") ); + info32 = NdisMedium802_3; + break; + + case OID_GEN_MAXIMUM_FRAME_SIZE: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("received query for OID_GEN_MAXIMUM_FRAME_SIZE\n") ); + if( !p_adapter->p_currentPath->carrier ) + { + info32 = p_adapter->params.MinMtu; + } + else + { + info32 = p_adapter->p_currentPath->pViport->mtu; + /*TODO: add VLAN tag size if support request */ + } + break; + + case OID_GEN_LINK_SPEED: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("received query for OID_GEN_LINK_SPEED\n") ); + + if( p_adapter->p_currentPath->carrier ) + { + info32 = DEFAULT_LINK_SPEED_x100BPS; /* x 100bps units */ + } + else + { + status = NDIS_STATUS_NOT_ACCEPTED; + } + break; + + case OID_GEN_TRANSMIT_BUFFER_SPACE: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("received query for OID_GEN_TRANSMIT_BUFFER_SPACE\n") ); + if ( !p_adapter->p_currentPath->carrier ) + { + p_adapter->pending_query = TRUE; + p_adapter->query_oid = oid_info; + + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("returning NDIS_STATUS_PENDING\n") ); + status = NDIS_STATUS_PENDING; + } + else + { + info32 = p_adapter->p_viport->data.xmitPool.bufferSz * + p_adapter->p_viport->data.xmitPool.poolSz; + } + break; + + case OID_GEN_RECEIVE_BUFFER_SPACE: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("received query for OID_GEN_RECEIVE_BUFFER_SPACE " + "or OID_GEN_RECEIVE_BUFFER_SPACE\n") ); + if ( !p_adapter->p_currentPath->carrier ) + { + p_adapter->pending_query = TRUE; + p_adapter->query_oid = oid_info; + + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("returning NDIS_STATUS_PENDING\n") ); + status = NDIS_STATUS_PENDING; + } + else + { + info32 = p_adapter->p_viport->data.recvPool.bufferSz * + p_adapter->p_viport->data.recvPool.poolSz; + } + break; + case OID_GEN_MAXIMUM_LOOKAHEAD: + case OID_GEN_CURRENT_LOOKAHEAD: + case OID_GEN_TRANSMIT_BLOCK_SIZE: + case OID_GEN_RECEIVE_BLOCK_SIZE: + case OID_GEN_MAXIMUM_TOTAL_SIZE: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("received query for OID_GEN_MAXIMUM_LOOKAHEAD " + "or OID_GEN_CURRENT_LOOKAHEAD or " + "OID_GEN_TRANSMIT_BLOCK_SIZE or " + "OID_GEN_RECEIVE_BLOCK_SIZE or " + "OID_GEN_MAXIMUM_TOTAL_SIZE\n") ); + if( !p_adapter->p_currentPath->carrier ) + { + info32 = p_adapter->params.MinMtu; + } + else + { + info32 = p_adapter->p_currentPath->pViport->mtu; + } + /*TODO: add VLAN tag size if support requested */ + info32 += sizeof(eth_hdr_t); + break; + + case OID_GEN_VENDOR_ID: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("received query for OID_GEN_VENDOR_ID\n") ); + + src_buf = (void*)VENDOR_ID; + buf_len = sizeof(VENDOR_ID); + break; + + case OID_GEN_VENDOR_DESCRIPTION: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("received query for OID_GEN_VENDOR_DESCRIPTION\n") ); + src_buf = VENDOR_DESCRIPTION; + buf_len = sizeof(VENDOR_DESCRIPTION); + break; + + case OID_GEN_VENDOR_DRIVER_VERSION: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_VENDOR_DRIVER_VERSION\n" ) ); + src_buf = &version; + buf_len = sizeof(version); + //TODO: Figure out what the right version is. + version = INIC_MAJORVERSION << 8 | INIC_MINORVERSION; + break; + + case OID_GEN_PHYSICAL_MEDIUM: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_PHYSICAL_MEDIUM\n" ) ); + info32 = NdisPhysicalMediumUnspecified; + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_CURRENT_PACKET_FILTER\n" ) ); + info32 = p_adapter->packet_filter; + break; + + case OID_GEN_DRIVER_VERSION: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_DRIVER_VERSION\n" ) ); + src_buf = &version; + buf_len = sizeof(version); + version = MAJOR_NDIS_VERSION << 8 | MINOR_NDIS_VERSION; + break; + + case OID_GEN_MAC_OPTIONS: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_MAC_OPTIONS\n" ) ); + info32 = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | + NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | + NDIS_MAC_OPTION_NO_LOOPBACK | + NDIS_MAC_OPTION_FULL_DUPLEX ; +//TODO: Figure out if we will support priority and VLANs. +// NDIS_MAC_OPTION_8021P_PRIORITY; +//#ifdef NDIS51_MINIPORT +// info |= NDIS_MAC_OPTION_8021Q_VLAN; +//#endif + break; + + case OID_GEN_MEDIA_CONNECT_STATUS: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_MEDIA_CONNECT_STATUS\n" ) ); + + info32 = ( p_adapter->carrier )? + NdisMediaStateConnected : + NdisMediaStateDisconnected; + break; + + case OID_GEN_MAXIMUM_SEND_PACKETS: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_MAXIMUM_SEND_PACKETS\n" ) ); + info32 = MAXLONG; // NDIS ignored it anyway + break; + + /* Required General Statistics */ + case OID_GEN_XMIT_OK: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_XMIT_OK\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifOutOk; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_RCV_OK: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_RCV_OK\n" ) ); + if ( !p_adapter->p_currentPath->carrier) + { + info32 = 0; + break; + } + if ( info_buf_len == sizeof(info32) ) + { + info32 = (uint32_t)p_adapter->p_viport->stats.ifInOk; + } + else + { + info64 = p_adapter->p_viport->stats.ifInOk; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_XMIT_ERROR: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_XMIT_ERROR\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifOutErrors; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_RCV_ERROR: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_RCV_ERROR\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifInErrors; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_RCV_NO_BUFFER: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_RCV_NO_BUFFER\n" ) ); + info32 = 0; + status = NDIS_STATUS_SUCCESS; + break; + + case OID_GEN_DIRECTED_BYTES_XMIT: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_DIRECTED_BYTES_XMIT\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifOutUcastBytes; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_DIRECTED_FRAMES_XMIT: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_DIRECTED_FRAMES_XMIT\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifOutNUcastPkts; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_MULTICAST_BYTES_XMIT: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_MULTICAST_BYTES_XMIT\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifOutMulticastBytes; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_MULTICAST_FRAMES_XMIT: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_MULTICAST_FRAMES_XMIT\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifOutMulticastPkts; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_BROADCAST_BYTES_XMIT: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_BROADCAST_BYTES_XMIT\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifOutBroadcastBytes; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_BROADCAST_FRAMES_XMIT: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_BROADCAST_FRAMES_XMIT\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifOutBroadcastPkts; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + case OID_GEN_DIRECTED_BYTES_RCV: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_DIRECTED_BYTES_RCV\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifInUcastBytes; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_DIRECTED_FRAMES_RCV: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_DIRECTED_FRAMES_RCV\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifInNUcastPkts; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_MULTICAST_BYTES_RCV: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_MULTICAST_BYTES_RCV\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifInMulticastBytes; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_MULTICAST_FRAMES_RCV: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_MULTICAST_FRAMES_RCV\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + + else + { + info64 = p_adapter->p_viport->stats.ifInMulticastPkts; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_BROADCAST_BYTES_RCV: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_BROADCAST_BYTES_RCV\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifInBroadcastBytes; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + case OID_GEN_BROADCAST_FRAMES_RCV: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_GEN_BROADCAST_FRAMES_RCV\n" ) ); + if ( !p_adapter->p_currentPath->carrier ) + { + info32 = 0; + } + else + { + info64 = p_adapter->p_viport->stats.ifInBroadcastPkts; + src_buf = &info64; + buf_len = sizeof(info64); + } + break; + + /* Required Ethernet operational characteristics */ + case OID_802_3_PERMANENT_ADDRESS: + case OID_802_3_CURRENT_ADDRESS: +#if defined( _DEBUG_ ) + if( oid == OID_802_3_PERMANENT_ADDRESS ) + { + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_802_3_PERMANENT_ADDRESS\n" ) ); + } + else + { + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_802_3_CURRENT_ADDRESS\n" ) ); + } +#endif /* defined( _DEBUG_ )*/ + if ( !p_adapter->macSet ) + { + p_adapter->pending_query = TRUE; + p_adapter->query_oid = oid_info; + + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("returning NDIS_STATUS_PENDING\n") ); + status = NDIS_STATUS_PENDING; + } + else + { + src_buf = &p_adapter->p_viport->hwMacAddress; + buf_len = HW_ADDR_LEN; + } + break; + + case OID_802_3_MULTICAST_LIST: + VNIC_TRACE( VNIC_DBG_INFO, + (" received query for OID_802_3_MULTICAST_LIST\n" ) ); + + if (!p_adapter->p_currentPath->carrier && + !(p_adapter->p_viport->flags & INIC_FLAG_ENABLE_NIC) ) + { + p_adapter->pending_query = TRUE; + p_adapter->query_oid = oid_info; + + VNIC_TRACE( VNIC_DBG_INFO, + ("returning NDIS_STATUS_PENDING\n") ); + status = NDIS_STATUS_PENDING; + } + else if ( p_adapter->mc_count > 0 ) + { + buf_len = p_adapter->mc_count * sizeof( mac_addr_t ); + src_buf = &p_adapter->mcast_array; + } + else + { + info32 = 0; + } + break; + + case OID_802_3_MAXIMUM_LIST_SIZE: + VNIC_TRACE( VNIC_DBG_INFO, + (" received query for OID_802_3_MAXIMUM_LIST_SIZE\n" ) ); + if ( !p_adapter->macSet ) + { + info32 = MAX_MCAST; + } + else + { + info32 = p_adapter->p_viport->numMacAddresses - MCAST_ADDR_START; + } + break; + case OID_802_3_MAC_OPTIONS: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_802_3_MAC_OPTIONS\n" ) ); + info32 = 0; + break; + + /* Required Ethernet stats */ + case OID_802_3_RCV_ERROR_ALIGNMENT: + case OID_802_3_XMIT_ONE_COLLISION: + case OID_802_3_XMIT_MORE_COLLISIONS: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_802_3_RCV_ERROR_ALIGNMENT or " + "OID_802_3_XMIT_ONE_COLLISION or " + "OID_802_3_XMIT_MORE_COLLISIONS\n" ) ); + info32 = 0; + break; + + case OID_TCP_TASK_OFFLOAD: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received query for OID_TCP_TASK_OFFLOAD\n" ) ); + + src_buf = NULL; + status = __vnic_get_tcp_task_offload( p_adapter, &oid_info ); + break; + + /* Optional General */ + case OID_GEN_SUPPORTED_GUIDS: +#ifdef NDIS51_MINIPORT + case OID_GEN_VLAN_ID: +#endif + + /* Optional General Stats */ + case OID_GEN_RCV_CRC_ERROR: + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + + /* Optional Ethernet Stats */ + case OID_802_3_XMIT_DEFERRED: + case OID_802_3_XMIT_MAX_COLLISIONS: + case OID_802_3_RCV_OVERRUN: + case OID_802_3_XMIT_UNDERRUN: + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + case OID_802_3_XMIT_TIMES_CRS_LOST: + case OID_802_3_XMIT_LATE_COLLISIONS: + case OID_PNP_CAPABILITIES: + status = NDIS_STATUS_NOT_SUPPORTED; + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received an unsupported oid of 0x%.8X!\n" , oid) ); + break; + + case OID_GEN_PROTOCOL_OPTIONS: + case OID_GEN_TRANSPORT_HEADER_OFFSET: +#ifdef NDIS51_MINIPORT + case OID_GEN_MACHINE_NAME: + case OID_GEN_RNDIS_CONFIG_PARAMETER: +#endif + default: + status = NDIS_STATUS_INVALID_OID; + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received an invalid oid of 0x%.8X!\n" , oid) ); + break; + } + + /* + * Complete the request as if it was handled asynchronously to maximize + * code reuse for when we really handle the requests asynchronously. + * Note that this requires the QueryInformation entry point to always + * return NDIS_STATUS_PENDING + */ + if( status != NDIS_STATUS_PENDING ) + { + _vnic_complete_query( + p_adapter, &oid_info, status, src_buf, buf_len ); + } + + VNIC_EXIT( VNIC_DBG_OID ); + return NDIS_STATUS_PENDING; +} + + +static void +_vnic_complete_query( + IN vnic_adapter_t* const p_adapter, + IN pending_oid_t* const p_oid_info, + IN const NDIS_STATUS status, + IN const void* const p_buf, + IN const ULONG buf_len ) +{ + NDIS_STATUS oid_status = status; + + VNIC_ENTER( VNIC_DBG_OID ); + + CL_ASSERT( status != NDIS_STATUS_PENDING ); + + if( status == NDIS_STATUS_SUCCESS ) + { + if( p_oid_info->buf_len < buf_len ) + { + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("Insufficient buffer space. " + "Returning NDIS_STATUS_INVALID_LENGTH.\n") ); + oid_status = NDIS_STATUS_INVALID_LENGTH; + *p_oid_info->p_bytes_needed = buf_len; + *p_oid_info->p_bytes_used = 0; + } + else if( p_oid_info->p_buf ) + { + /* Only copy if we have a distinct source buffer. */ + if( p_buf ) + { + NdisMoveMemory( p_oid_info->p_buf, p_buf, buf_len ); + *p_oid_info->p_bytes_used = buf_len; + } + } + else + { + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("Returning NDIS_NOT_ACCEPTED") ); + oid_status = NDIS_STATUS_NOT_ACCEPTED; + } + } + else + { + *p_oid_info->p_bytes_used = 0; + } + + p_adapter->pending_query = FALSE; + + NdisMQueryInformationComplete( p_adapter->h_handle, oid_status ); + + VNIC_EXIT( VNIC_DBG_OID ); +} + + +static NDIS_STATUS +__vnic_get_tcp_task_offload( + IN vnic_adapter_t* p_adapter, + IN pending_oid_t* const p_oid_info ) +{ + NDIS_TASK_OFFLOAD_HEADER *p_offload_hdr; + NDIS_TASK_OFFLOAD *p_offload_task; + NDIS_TASK_TCP_IP_CHECKSUM *p_offload_chksum; + //uint8_t port_num; + ULONG buf_len; + + buf_len = sizeof(NDIS_TASK_OFFLOAD_HEADER) + + sizeof(NDIS_TASK_OFFLOAD) + + sizeof(NDIS_TASK_TCP_IP_CHECKSUM) - 1; + + *(p_oid_info->p_bytes_needed) = buf_len; + + if( p_oid_info->buf_len < buf_len ) + return NDIS_STATUS_INVALID_LENGTH; + + p_offload_hdr = (NDIS_TASK_OFFLOAD_HEADER*)p_oid_info->p_buf; + if( p_offload_hdr->Version != NDIS_TASK_OFFLOAD_VERSION ) + return NDIS_STATUS_INVALID_DATA; + + if( p_offload_hdr->EncapsulationFormat.Encapsulation != + IEEE_802_3_Encapsulation ) + { + return NDIS_STATUS_INVALID_DATA; + } + + p_offload_hdr->OffsetFirstTask = sizeof(NDIS_TASK_OFFLOAD_HEADER); + p_offload_task = (NDIS_TASK_OFFLOAD*)(p_offload_hdr + 1); + p_offload_task->Version = NDIS_TASK_OFFLOAD_VERSION; + p_offload_task->Size = sizeof(NDIS_TASK_OFFLOAD); + p_offload_task->Task = TcpIpChecksumNdisTask; + p_offload_task->OffsetNextTask = 0; + p_offload_task->TaskBufferLength = sizeof(NDIS_TASK_TCP_IP_CHECKSUM); + p_offload_chksum = + (NDIS_TASK_TCP_IP_CHECKSUM*)p_offload_task->TaskBuffer; + + p_offload_chksum->V4Transmit.IpOptionsSupported = + p_adapter->params.UseTxCsum; + p_offload_chksum->V4Transmit.TcpOptionsSupported = + p_adapter->params.UseTxCsum; + p_offload_chksum->V4Transmit.TcpChecksum = + p_adapter->params.UseTxCsum; + p_offload_chksum->V4Transmit.UdpChecksum = + p_adapter->params.UseTxCsum; + p_offload_chksum->V4Transmit.IpChecksum = + p_adapter->params.UseTxCsum; + + p_offload_chksum->V4Receive.IpOptionsSupported = TRUE; + p_offload_chksum->V4Receive.TcpOptionsSupported = TRUE; + p_offload_chksum->V4Receive.TcpChecksum = + p_adapter->params.UseRxCsum; + p_offload_chksum->V4Receive.UdpChecksum = + p_adapter->params.UseRxCsum; + p_offload_chksum->V4Receive.IpChecksum = + p_adapter->params.UseRxCsum; + + p_offload_chksum->V6Transmit.IpOptionsSupported = FALSE; + p_offload_chksum->V6Transmit.TcpOptionsSupported = FALSE; + p_offload_chksum->V6Transmit.TcpChecksum = FALSE; + p_offload_chksum->V6Transmit.UdpChecksum = FALSE; + + p_offload_chksum->V6Receive.IpOptionsSupported = FALSE; + p_offload_chksum->V6Receive.TcpOptionsSupported = FALSE; + p_offload_chksum->V6Receive.TcpChecksum = FALSE; + p_offload_chksum->V6Receive.UdpChecksum = FALSE; + + *(p_oid_info->p_bytes_used) = buf_len; + + return NDIS_STATUS_SUCCESS; +} + +static NDIS_STATUS +__vnic_set_tcp_task_offload( + IN vnic_adapter_t* p_adapter, + IN void* const p_info_buf, + IN ULONG* const p_info_len ) +{ + NDIS_TASK_OFFLOAD_HEADER *p_offload_hdr; + NDIS_TASK_OFFLOAD *p_offload_task; + NDIS_TASK_TCP_IP_CHECKSUM *p_offload_chksum; + + VNIC_ENTER( VNIC_DBG_OID ); + + p_offload_hdr = (NDIS_TASK_OFFLOAD_HEADER*)p_info_buf; + + if( *p_info_len < sizeof(NDIS_TASK_OFFLOAD_HEADER) ) + return NDIS_STATUS_INVALID_LENGTH; + + if( p_offload_hdr->Version != NDIS_TASK_OFFLOAD_VERSION ) + return NDIS_STATUS_INVALID_DATA; + + if( p_offload_hdr->Size != sizeof(NDIS_TASK_OFFLOAD_HEADER) ) + return NDIS_STATUS_INVALID_LENGTH; + + if( !p_offload_hdr->OffsetFirstTask ) + return NDIS_STATUS_SUCCESS; + + if( p_offload_hdr->EncapsulationFormat.Encapsulation != + IEEE_802_3_Encapsulation ) + { + return NDIS_STATUS_INVALID_DATA; + } + + p_offload_task = (NDIS_TASK_OFFLOAD*) + (((UCHAR*)p_offload_hdr) + p_offload_hdr->OffsetFirstTask); + + if( *p_info_len < sizeof(NDIS_TASK_OFFLOAD_HEADER) + + offsetof( NDIS_TASK_OFFLOAD, TaskBuffer ) + + sizeof(NDIS_TASK_TCP_IP_CHECKSUM) ) + { + return NDIS_STATUS_INVALID_LENGTH; + } + + if( p_offload_task->Version != NDIS_TASK_OFFLOAD_VERSION ) + return NDIS_STATUS_INVALID_DATA; + p_offload_chksum = + (NDIS_TASK_TCP_IP_CHECKSUM*)p_offload_task->TaskBuffer; + + if( !p_adapter->params.UseTxCsum && + (p_offload_chksum->V4Transmit.IpOptionsSupported || + p_offload_chksum->V4Transmit.TcpOptionsSupported || + p_offload_chksum->V4Transmit.TcpChecksum || + p_offload_chksum->V4Transmit.UdpChecksum || + p_offload_chksum->V4Transmit.IpChecksum) ) + { + return NDIS_STATUS_NOT_SUPPORTED; + } + + if( !p_adapter->params.UseRxCsum && + (p_offload_chksum->V4Receive.IpOptionsSupported || + p_offload_chksum->V4Receive.TcpOptionsSupported || + p_offload_chksum->V4Receive.TcpChecksum || + p_offload_chksum->V4Receive.UdpChecksum || + p_offload_chksum->V4Receive.IpChecksum) ) + { + return NDIS_STATUS_NOT_SUPPORTED; + } + if( p_offload_chksum->V6Receive.IpOptionsSupported || + p_offload_chksum->V6Receive.TcpOptionsSupported || + p_offload_chksum->V6Receive.TcpChecksum || + p_offload_chksum->V6Receive.UdpChecksum || + p_offload_chksum->V6Transmit.IpOptionsSupported || + p_offload_chksum->V6Transmit.TcpOptionsSupported || + p_offload_chksum->V6Transmit.TcpChecksum || + p_offload_chksum->V6Transmit.UdpChecksum ) + { + return NDIS_STATUS_NOT_SUPPORTED; + } + VNIC_EXIT( VNIC_DBG_OID ); + + return NDIS_STATUS_SUCCESS; +} + + +//! Issues a hardware reset to the NIC and/or resets the driver's software state. +/* Tear down the connection and start over again. This is only called when there is a problem. +For example, if a send, query info, or set info had a time out. MiniportCheckForHang will +be called first. +IRQL = DISPATCH_LEVEL + +@param p_addr_resetPointer to BOOLLEAN that is set to TRUE if the NDIS +library should call MiniportSetInformation to restore addressing information to the current values. +@param adapter_context The adapter context allocated at start +@return NDIS_STATUS_SUCCESS, NDIS_STATUS_PENDING, NDIS_STATUS_NOT_RESETTABLE, +NDIS_STATUS_RESET_IN_PROGRESS, NDIS_STATUS_SOFT_ERRORS, NDIS_STATUS_HARD_ERRORS +*/ +NDIS_STATUS +vnic_reset( + OUT PBOOLEAN p_addr_reset, + IN NDIS_HANDLE adapter_context) +{ +// vnic_adapter_t* p_adapter; + + VNIC_ENTER( VNIC_DBG_INIT ); + CL_ASSERT( p_addr_reset ); + CL_ASSERT( adapter_context ); + //p_adapter = (vnic_adapter_t*)adapter_context; + *p_addr_reset = TRUE; + + UNREFERENCED_PARAMETER( adapter_context ); + + VNIC_EXIT( VNIC_DBG_INIT ); + return NDIS_STATUS_NOT_RESETTABLE; +} + + +//! Request changes in the state information that the miniport driver maintains +/* For example, this is used to set multicast addresses and the packet filter. +IRQL = DISPATCH_LEVEL + +@param adapter_context The adapter context allocated at start +@param oid Object ID representing the set operation to be carried out +@param info_buf Buffer containing input for this set and location for any output +@param info_buf_len Number of bytes available in info_buf +@param p_bytes_read Pointer to number of bytes read from info_buf +@param p_bytes_needed Pointer to number of bytes needed to satisfy this oid +@return NDIS_STATUS_SUCCESS, NDIS_STATUS_PENDING, NDIS_STATUS_INVALID_OID, +NDIS_STATUS_INVALID_LENGTH, NDIS_STATUS_INVALID_DATA, NDIS_STATUS_NOT_ACCEPTED, +NDIS_STATUS_NOT_SUPPORTED, NDIS_STATUS_RESOURCES +*/ +NDIS_STATUS +vnic_oid_set_info( + IN NDIS_HANDLE adapter_context, + IN NDIS_OID oid, + IN PVOID info_buf, + IN ULONG info_buf_len, + OUT PULONG p_bytes_read, + OUT PULONG p_bytes_needed ) +{ + vnic_adapter_t* p_adapter; + NDIS_STATUS status; + ULONG buf_len; + pending_oid_t oid_info; + + VNIC_ENTER( VNIC_DBG_OID ); + + CL_ASSERT( adapter_context ); + p_adapter = (vnic_adapter_t*)adapter_context; + + CL_ASSERT( p_bytes_read ); + CL_ASSERT( p_bytes_needed ); + CL_ASSERT( !p_adapter->pending_set ); + + status = NDIS_STATUS_SUCCESS; + *p_bytes_needed = 0; + buf_len = sizeof(ULONG); + + oid_info.oid = oid; + oid_info.p_buf = info_buf; + oid_info.buf_len = info_buf_len; + oid_info.p_bytes_used = p_bytes_read; + oid_info.p_bytes_needed = p_bytes_needed; + + /* do not set anything until IB path initialized and NIC is enabled */ + if( !p_adapter->p_currentPath->carrier ) + { + *p_bytes_read = 0; + return NDIS_STATUS_NOT_ACCEPTED; + } + + switch( oid ) + { + /* Required General */ + case OID_GEN_CURRENT_PACKET_FILTER: + VNIC_TRACE( VNIC_DBG_INFO, + (" received set for OID_GEN_CURRENT_PACKET_FILTER, %#x\n", *(uint32_t*)info_buf )); + if ( !p_adapter->p_currentPath->carrier ) + { + status = NDIS_STATUS_NOT_ACCEPTED; + break; + } + if( info_buf_len < sizeof( p_adapter->packet_filter ) ) + { + status = NDIS_STATUS_INVALID_LENGTH; + } + else if( !info_buf ) + { + status = NDIS_STATUS_INVALID_DATA; + } + else + { + p_adapter->set_oid = oid_info; + status = _vnic_process_packet_filter( p_adapter, *((uint32_t*)info_buf) ); + } + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received set for OID_GEN_CURRENT_LOOKAHEAD\n" )); + if( info_buf_len < buf_len ) + status = NDIS_STATUS_INVALID_LENGTH; + break; + + case OID_GEN_PROTOCOL_OPTIONS: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received set for OID_GEN_PROTOCOL_OPTIONS\n" )); + if( info_buf_len < buf_len ) + status = NDIS_STATUS_INVALID_LENGTH; + break; + +#ifdef NDIS51_MINIPORT + case OID_GEN_MACHINE_NAME: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received set for OID_GEN_MACHINE_NAME\n" ) ); + if( info_buf_len < buf_len ) + status = NDIS_STATUS_INVALID_LENGTH; + // else + // status = __vnic_set_machine_name( info_buf, + // (USHORT)info_buf_len ); + break; +#endif + + /* Required Ethernet operational characteristics */ + case OID_802_3_MULTICAST_LIST: + if( !p_adapter->p_currentPath->carrier ) + { + status = NDIS_STATUS_NOT_ACCEPTED; + break; + } + VNIC_TRACE( VNIC_DBG_INFO, + (" received set for OID_802_3_MULTICAST_LIST\n" ) ); + if( info_buf_len > MAX_MCAST * sizeof(mac_addr_t) ) + { + VNIC_TRACE( VNIC_DBG_INFO, + (" OID_802_3_MULTICAST_LIST - Multicast list full.\n" ) ); + status = NDIS_STATUS_MULTICAST_FULL; + *p_bytes_needed = MAX_MCAST * sizeof(mac_addr_t); + } + else if( info_buf_len % sizeof(mac_addr_t) ) + { + VNIC_TRACE( VNIC_DBG_INFO, + (" OID_802_3_MULTICAST_LIST - Invalid input buffer length.\n" ) ); + status = NDIS_STATUS_INVALID_DATA; + } + else if( info_buf == NULL && info_buf_len != 0 ) + { + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" OID_802_3_MULTICAST_LIST - Invalid input buffer.\n" ) ); + status = NDIS_STATUS_INVALID_DATA; + } + else + { + p_adapter->set_oid = oid_info; + status = vnic_set_mcast( p_adapter, (mac_addr_t*)info_buf, + (uint8_t)(info_buf_len / sizeof(mac_addr_t)) ); + } + break; + + case OID_TCP_TASK_OFFLOAD: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received set for OID_TCP_TASK_OFFLOAD\n" ) ); + buf_len = info_buf_len; + status = __vnic_set_tcp_task_offload( p_adapter, info_buf, &buf_len ); + break; + + /* Optional General */ + case OID_GEN_TRANSPORT_HEADER_OFFSET: + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + ("Set for OID_GEN_TRANSPORT_HEADER_OFFSET\n") ); + break; +#ifdef NDIS51_MINIPORT + case OID_GEN_RNDIS_CONFIG_PARAMETER: + case OID_GEN_VLAN_ID: +#endif + status = NDIS_STATUS_NOT_SUPPORTED; + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received an unsupported oid of 0x%.8X!\n" , oid)); + break; + + case OID_GEN_SUPPORTED_LIST: + case OID_GEN_HARDWARE_STATUS: + case OID_GEN_MEDIA_SUPPORTED: + case OID_GEN_MEDIA_IN_USE: + case OID_GEN_MAXIMUM_FRAME_SIZE: + case OID_GEN_LINK_SPEED: + case OID_GEN_TRANSMIT_BUFFER_SPACE: + case OID_GEN_RECEIVE_BUFFER_SPACE: + case OID_GEN_MAXIMUM_LOOKAHEAD: + case OID_GEN_TRANSMIT_BLOCK_SIZE: + case OID_GEN_RECEIVE_BLOCK_SIZE: + case OID_GEN_MAXIMUM_TOTAL_SIZE: + case OID_GEN_VENDOR_ID: + case OID_GEN_VENDOR_DESCRIPTION: + case OID_GEN_VENDOR_DRIVER_VERSION: + case OID_GEN_DRIVER_VERSION: + case OID_GEN_MAC_OPTIONS: + case OID_GEN_MEDIA_CONNECT_STATUS: + case OID_GEN_MAXIMUM_SEND_PACKETS: + case OID_GEN_SUPPORTED_GUIDS: + case OID_GEN_PHYSICAL_MEDIUM: + default: + status = NDIS_STATUS_INVALID_OID; + VNIC_TRACE( VNIC_DBG_OID | VNIC_DBG_INFO, + (" received an invalid oid of 0x%.8X!\n" , oid)); + break; + } + + if( status == NDIS_STATUS_SUCCESS ) + { + *p_bytes_read = buf_len; + } + else + { + if( status == NDIS_STATUS_INVALID_LENGTH ) + { + if ( !*p_bytes_needed ) + { + *p_bytes_needed = buf_len; + } + } + + *p_bytes_read = 0; + } + + VNIC_EXIT( VNIC_DBG_OID ); + return status; +} + + +//! Transfers some number of packets, specified as an array of packet pointers, over the network. +/* For a deserialized driver, these packets are completed asynchronously +using NdisMSendComplete. +IRQL <= DISPATCH_LEVEL + +@param adapter_context Pointer to vnic_adapter_t structure with per NIC state +@param packet_array Array of packets to send +@param numPackets Number of packets in the array +*/ +void +vnic_send_packets( + IN NDIS_HANDLE adapter_context, + IN PPNDIS_PACKET packet_array, + IN UINT num_packets ) +{ + vnic_adapter_t *p_adapter; + viport_t *p_viport; + UINT packet_num; + + VNIC_ENTER( VNIC_DBG_SEND ); + + CL_ASSERT( adapter_context ); + + p_adapter = (vnic_adapter_t*)adapter_context; + p_viport = p_adapter->p_viport; + + NdisAcquireSpinLock( &p_adapter->lock ); + + if( !p_adapter->carrier ) + { + NdisReleaseSpinLock( &p_adapter->lock ); + for( packet_num = 0; packet_num < num_packets; ++packet_num ) + { + NdisMSendComplete( p_adapter->h_handle, + packet_array[packet_num], NDIS_STATUS_ADAPTER_NOT_READY ); + } + VNIC_EXIT( VNIC_DBG_SEND ); + return; + } + NdisReleaseSpinLock( &p_adapter->lock ); + + for( packet_num = 0; packet_num < num_packets; ++packet_num ) + { + netpath_xmitPacket( p_adapter->p_currentPath, + packet_array[packet_num] ); + } + VNIC_EXIT( VNIC_DBG_SEND ); +} + +void +vnic_pnp_notify( + IN NDIS_HANDLE adapter_context, + IN NDIS_DEVICE_PNP_EVENT pnp_event, + IN PVOID info_buf, + IN ULONG info_buf_len ) +{ + vnic_adapter_t *p_adapter; + + VNIC_ENTER( VNIC_DBG_PNP ); + + UNUSED_PARAM( info_buf ); + UNUSED_PARAM( info_buf_len ); + + p_adapter = (vnic_adapter_t*)adapter_context; + + VNIC_TRACE( VNIC_DBG_PNP, ("Event %d\n", pnp_event) ); + if( pnp_event != NdisDevicePnPEventPowerProfileChanged ) + { + InterlockedExchange( (volatile LONG *)&p_adapter->pnp_state, IB_PNP_IOC_REMOVE ); + vnic_resume_oids( p_adapter ); + } + + VNIC_EXIT( VNIC_DBG_PNP ); +} + + +void +vnic_shutdown( + IN PVOID adapter_context ) +{ + VNIC_ENTER( VNIC_DBG_INIT ); + + UNUSED_PARAM( adapter_context ); + + VNIC_EXIT( VNIC_DBG_INIT ); +} + + +void +vnic_resume_set_oids( + IN vnic_adapter_t* const p_adapter ) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + BOOLEAN pending_set; + pending_oid_t set_oid = {0}; + + VNIC_ENTER( VNIC_DBG_OID ); + + NdisAcquireSpinLock( &p_adapter->lock ); + /* + * Set the status depending on our state. Fail OID requests that + * are pending while we reset the adapter. + */ + switch( p_adapter->pnp_state ) + { + case IB_PNP_IOC_ADD: + break; + + case IB_PNP_IOC_REMOVE: + default: + status = NDIS_STATUS_NOT_ACCEPTED; + break; + } + + pending_set = p_adapter->pending_set; + if( pending_set ) + { + set_oid = p_adapter->set_oid; + p_adapter->pending_set = FALSE; + } + NdisReleaseSpinLock( &p_adapter->lock ); + + ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL ); + + if( pending_set ) + { + switch( set_oid.oid ) + { + case OID_GEN_CURRENT_PACKET_FILTER: + /* Validation already performed in the SetInformation path. */ + p_adapter->packet_filter = *(PULONG)set_oid.p_buf; + NdisMSetInformationComplete( p_adapter->h_handle, status ); + break; + + case OID_GEN_MACHINE_NAME: + status = __vnic_set_machine_name ( p_adapter->set_oid.p_buf, (USHORT)p_adapter->set_oid.buf_len ); + NdisMSetInformationComplete( p_adapter->h_handle, status ); + break; + + case OID_802_3_MULTICAST_LIST: + NdisMSetInformationComplete( p_adapter->h_handle, status ); + break; + + default: + CL_ASSERT( set_oid.oid && 0 ); + break; + } + } + + VNIC_EXIT( VNIC_DBG_OID ); +} + + +void +vnic_resume_oids( + IN vnic_adapter_t* const p_adapter ) +{ + ULONG info; + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + BOOLEAN pending_query; + pending_oid_t query_oid = {0}; + KIRQL irql; + + uint8_t mac[HW_ADDR_LEN]; + + VNIC_ENTER( VNIC_DBG_OID ); + + NdisAcquireSpinLock( &p_adapter->lock ); + /* + * Set the status depending on our state. Fail OID requests that + * are pending while we reset the adapter. + */ + switch( p_adapter->pnp_state ) + { + case IB_PNP_IOC_ADD: + break; + + case IB_PNP_IOC_REMOVE: + default: + status = NDIS_STATUS_NOT_ACCEPTED; + break; + } + + pending_query = p_adapter->pending_query; + + if( pending_query ) + { + query_oid = p_adapter->query_oid; + p_adapter->pending_query = FALSE; + } + NdisReleaseSpinLock( &p_adapter->lock ); + + KeRaiseIrql( DISPATCH_LEVEL, &irql ); + + /* + * If we had a pending OID request for OID_GEN_LINK_SPEED, + * complete it now. Note that we hold the object lock since + * NdisMQueryInformationComplete is called at DISPATCH_LEVEL. + */ + if( pending_query ) + { + switch( query_oid.oid ) + { + case OID_802_3_CURRENT_ADDRESS: + case OID_802_3_PERMANENT_ADDRESS: + cl_memcpy( mac, p_adapter->p_viport->hwMacAddress, HW_ADDR_LEN ); + _vnic_complete_query( p_adapter, + &query_oid, + status, + mac, + HW_ADDR_LEN ); + break; + case OID_GEN_LINK_SPEED: + info = DEFAULT_LINK_SPEED_x100BPS; + _vnic_complete_query( p_adapter, + &query_oid, + status, + &info, + sizeof( info ) ); + break; + + case OID_GEN_MEDIA_CONNECT_STATUS: + info = ( p_adapter->carrier )? NdisMediaStateConnected : + NdisMediaStateDisconnected; + _vnic_complete_query( p_adapter, + &query_oid, + status, + &info, + sizeof( info ) ); + break; + case OID_802_3_MULTICAST_LIST: + ASSERT( p_adapter->p_viport ); + _vnic_complete_query( p_adapter, + &query_oid, + status, + &p_adapter->p_viport->macAddresses[MCAST_ADDR_START], + (p_adapter->p_viport->numMacAddresses - + MCAST_ADDR_START) * sizeof( mac_addr_t ) ); + break; + case OID_GEN_TRANSMIT_BUFFER_SPACE: + info = p_adapter->p_viport->data.xmitPool.bufferSz * + p_adapter->p_viport->data.xmitPool.numXmitBufs; + _vnic_complete_query( p_adapter, + &query_oid, + status, + &info, + sizeof( info ) ); + break; + case OID_GEN_RECEIVE_BUFFER_SPACE: + info = p_adapter->p_viport->data.recvPool.bufferSz * + p_adapter->p_viport->data.recvPool.poolSz; + _vnic_complete_query( p_adapter, + &query_oid, + status, + &info, + sizeof( info ) ); + break; + default: + CL_ASSERT( query_oid.oid == OID_GEN_LINK_SPEED || + query_oid.oid == OID_GEN_MEDIA_CONNECT_STATUS || + query_oid.oid == OID_802_3_MULTICAST_LIST || + query_oid.oid == OID_802_3_CURRENT_ADDRESS || + query_oid.oid == OID_802_3_PERMANENT_ADDRESS || + query_oid.oid == OID_GEN_RECEIVE_BUFFER_SPACE || + query_oid.oid == OID_GEN_TRANSMIT_BUFFER_SPACE ); + break; + } + } + + vnic_resume_set_oids( p_adapter ); + + KeLowerIrql( irql ); + + VNIC_EXIT( VNIC_DBG_OID ); +} + +static NDIS_STATUS +__vnic_set_machine_name( + IN VOID *p_uni_array, + IN USHORT buf_size ) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + uint8_t *p_src_buf = (uint8_t *)p_uni_array; + int i; + + VNIC_ENTER( VNIC_DBG_OID ); + + cl_memclr(g_vnic.host_name, 65 ); + for( i = 0; i < ( buf_size >1 ); i++ ) + { + g_vnic.host_name[i] = *(p_src_buf + i*2); + } + + return status; +} + +static void +__vnic_read_machine_name( void ) +{ + /* this code is borrowed from the bus_driver */ + + NTSTATUS nt_status; + /* Remember the terminating entry in the table below. */ + RTL_QUERY_REGISTRY_TABLE table[2]; + UNICODE_STRING hostNamePath; + UNICODE_STRING hostNameW; + ANSI_STRING hostName; + + VNIC_ENTER( VNIC_DBG_INIT ); + + /* Get the host name. */ + RtlInitUnicodeString( &hostNamePath, L"ComputerName\\ComputerName" ); + RtlInitUnicodeString( &hostNameW, NULL ); + + /* + * Clear the table. This clears all the query callback pointers, + * and sets up the terminating table entry. + */ + cl_memclr( table, sizeof(table) ); + + /* Setup the table entries. */ + table[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; + table[0].Name = L"ComputerName"; + table[0].EntryContext = &hostNameW; + table[0].DefaultType = REG_SZ; + table[0].DefaultData = &hostNameW; + table[0].DefaultLength = 0; + + /* Have at it! */ + nt_status = RtlQueryRegistryValues( RTL_REGISTRY_CONTROL, + hostNamePath.Buffer, table, NULL, NULL ); + if( NT_SUCCESS( nt_status ) ) + { + /* Convert the UNICODE host name to UTF-8 (ASCII). */ + hostName.Length = 0; + hostName.MaximumLength = sizeof(g_vnic.host_name) - 1; + hostName.Buffer = (PCHAR)g_vnic.host_name; + nt_status = RtlUnicodeStringToAnsiString( &hostName, &hostNameW, FALSE ); + RtlFreeUnicodeString( &hostNameW ); + } + else + { + VNIC_TRACE(VNIC_DBG_ERROR , ("Failed to get host name from registry\n") ); + /* Use the default name... */ + cl_memcpy( g_vnic.host_name, + DEFAULT_HOST_NAME, + min (sizeof( g_vnic.host_name), sizeof(DEFAULT_HOST_NAME) ) ); + } + + VNIC_EXIT( VNIC_DBG_INIT ); +} + +/* function: usec_timer_start + uses cl_timer* functionality (init/destroy/stop/start ) + except it takes expiration time parameter in microseconds. +*/ +cl_status_t +usec_timer_start( + IN cl_timer_t* const p_timer, + IN const uint32_t time_usec ) +{ + LARGE_INTEGER due_time; + + CL_ASSERT( p_timer ); + CL_ASSERT( p_timer->pfn_callback ); + CL_ASSERT( KeGetCurrentIrql() <= DISPATCH_LEVEL ); + + /* Due time is in 100 ns increments. Negative for relative time. */ + due_time.QuadPart = -(int64_t)(((uint64_t)time_usec) * 10); + + /* Store the timeout time in the timer object. in microseconds */ + p_timer->timeout_time = cl_get_time_stamp() + (((uint64_t)time_usec)); + + KeSetTimer( &p_timer->timer, due_time, &p_timer->dpc ); + return( CL_SUCCESS ); +} + + +static NDIS_STATUS +_vnic_process_packet_filter( + IN vnic_adapter_t* const p_adapter, + IN ULONG pkt_filter ) +{ + NDIS_STATUS status; + + VNIC_ENTER(VNIC_DBG_FUNC ); + + ASSERT( p_adapter ); + ASSERT( p_adapter->p_viport ); + ASSERT( !(p_adapter->p_viport->updates & SYNC_QUERY) ); + + InterlockedExchange((volatile LONG*)&p_adapter->packet_filter, pkt_filter ); + + ASSERT( (p_adapter->p_viport->updates & ~MCAST_OVERFLOW) == 0 ); + + if( pkt_filter & NDIS_PACKET_TYPE_ALL_MULTICAST ) + { + if( !p_adapter->p_viport->flags & INIC_FLAG_ENABLE_MCAST_ALL ) + { + p_adapter->p_viport->newFlags |= INIC_FLAG_ENABLE_MCAST_ALL; + // TODO: Shouldn't MCAST_OVERFLOW be a flag bit, not an update bit? + InterlockedOr( &p_adapter->p_viport->updates, + NEED_LINK_CONFIG | MCAST_OVERFLOW ); + } + } + if ( pkt_filter & NDIS_PACKET_TYPE_PROMISCUOUS ) + { + if( !p_adapter->p_viport->flags & INIC_FLAG_ENABLE_PROMISC ) + { + p_adapter->p_viport->newFlags |= INIC_FLAG_ENABLE_PROMISC; + InterlockedOr( &p_adapter->p_viport->updates, NEED_LINK_CONFIG ); + } + } + /* BROADCAST and MULTICAST flags VEx enable by default */ + p_adapter->pending_set = TRUE; + status = _viport_process_query( p_adapter->p_viport, FALSE ); + VNIC_TRACE( VNIC_DBG_INFO, + ("LINK CONFIG status %x\n", status )); + if( status != NDIS_STATUS_PENDING ) + p_adapter->pending_set = FALSE; + + VNIC_EXIT( VNIC_DBG_FUNC ); + return status; +} + diff --git a/trunk/ulp/inic/kernel/vnic_driver.h b/trunk/ulp/inic/kernel/vnic_driver.h new file mode 100644 index 00000000..132f7967 --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_driver.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + +#ifndef _VNIC_DRIVER_H_ +#define _VNIC_DRIVER_H_ + +#include "vnic_adapter.h" +#include "vnic_debug.h" + + +#if defined(NDIS50_MINIPORT) +#define MAJOR_NDIS_VERSION 5 +#define MINOR_NDIS_VERSION 0 +#elif defined (NDIS51_MINIPORT) +#define MAJOR_NDIS_VERSION 5 +#define MINOR_NDIS_VERSION 1 +#else +#error NDIS Version not defined, try defining NDIS50_MINIPORT or NDIS51_MINIPORT +#endif + +#include + +static const NDIS_OID SUPPORTED_OIDS[] = +{ + OID_GEN_SUPPORTED_LIST, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_LOOKAHEAD, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_CURRENT_LOOKAHEAD, + OID_GEN_DRIVER_VERSION, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_PROTOCOL_OPTIONS, // ? + OID_GEN_MAC_OPTIONS, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_MAXIMUM_SEND_PACKETS, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_PHYSICAL_MEDIUM, + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, + OID_GEN_DIRECTED_BYTES_XMIT, + OID_GEN_DIRECTED_FRAMES_XMIT, + OID_GEN_MULTICAST_BYTES_XMIT, + OID_GEN_MULTICAST_FRAMES_XMIT, + OID_GEN_BROADCAST_BYTES_XMIT, + OID_GEN_BROADCAST_FRAMES_XMIT, + OID_GEN_DIRECTED_BYTES_RCV, + OID_GEN_DIRECTED_FRAMES_RCV, + OID_GEN_MULTICAST_BYTES_RCV, + OID_GEN_MULTICAST_FRAMES_RCV, + OID_GEN_BROADCAST_BYTES_RCV, + OID_GEN_BROADCAST_FRAMES_RCV, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE, + OID_802_3_MAC_OPTIONS, + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS, + OID_TCP_TASK_OFFLOAD +}; + +static const unsigned char VENDOR_ID[] = {0x00, 0x06, 0x6A, 0x00}; +#define VENDOR_DESCRIPTION "Virtual Ethernet over InfiniBand" +#define DEFAULT_VNIC_NAME "VNIC" + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT p_drv_obj, + IN PUNICODE_STRING p_reg_path ); + +VOID +vnic_unload( + IN PDRIVER_OBJECT p_drv_obj ); + +NDIS_STATUS +vnic_initialize( + OUT PNDIS_STATUS p_open_err_status, + OUT PUINT p_selected_medium_index, + IN PNDIS_MEDIUM medium_array, + IN UINT medium_array_size, + IN NDIS_HANDLE h_handle, + IN NDIS_HANDLE wrapper_configuration_context ); + +BOOLEAN +vnic_check_for_hang( + IN NDIS_HANDLE adapter_context ); + +void +vnic_halt( + IN NDIS_HANDLE adapter_context ); + +NDIS_STATUS +vnic_oid_query_info( + IN NDIS_HANDLE adapter_context, + IN NDIS_OID oid, + IN PVOID info_buf, + IN ULONG info_buf_len, + OUT PULONG p_bytes_written, + OUT PULONG p_bytes_needed ); + +NDIS_STATUS +vnic_reset( + OUT PBOOLEAN p_addressing_reset, + IN NDIS_HANDLE adapter_context ); + +NDIS_STATUS +vnic_oid_set_info( + IN NDIS_HANDLE adapter_context, + IN NDIS_OID oid, + IN PVOID info_buf, + IN ULONG info_buf_length, + OUT PULONG p_bytes_read, + OUT PULONG p_bytes_needed ); + +void +vnic_send_packets( + IN NDIS_HANDLE adapter_context, + IN PPNDIS_PACKET packet_array, + IN UINT num_packets ); + +void +vnic_pnp_notify( + IN NDIS_HANDLE adapter_context, + IN NDIS_DEVICE_PNP_EVENT pnp_event, + IN PVOID info_buf, + IN ULONG info_buf_len ); + +void +vnic_shutdown( + IN PVOID adapter_context ); + +NDIS_STATUS +vnic_get_agapter_interface( + IN NDIS_HANDLE h_handle, + IN vnic_adapter_t *p_adapter); + + +/* same as cl_timer_start() except it takes timeout param in usec */ +/* need to run kicktimer */ +cl_status_t +usec_timer_start( + IN cl_timer_t* const p_timer, + IN const uint32_t time_usec ); + +#endif /* _VNIC_DRIVER_H_ */ \ No newline at end of file diff --git a/trunk/ulp/inic/kernel/vnic_ib.c b/trunk/ulp/inic/kernel/vnic_ib.c new file mode 100644 index 00000000..888c0981 --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_ib.c @@ -0,0 +1,900 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ +#include "vnic_adapter.h" +#include "vnic_util.h" + +#ifndef max_t +#define max_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; }) +#endif + + +static int inicIbInited = 0; + +extern struct _vnic_globals g_vnic; + +void +ib_asyncEvent( + ib_async_event_rec_t *pEventRecord ); + +static void +_ib_qpCompletion( + IN IbQp_t *pQp); +static void +_ibqp_connect_cb( + IN ib_cm_rep_rec_t *p_cm_rep ); + +static void +_ibqp_detach_cb( + IN ib_cm_drep_rec_t *p_drep_rec ); + +static void +_ibqp_dreq_cb( + IN ib_cm_dreq_rec_t *p_dreq_rec ); + +static void +_ibqp_mra_cb( + IN ib_cm_mra_rec_t *p_mra_rec ); + +static void +_ibqp_rej_cb( + IN ib_cm_rej_rec_t *p_rej_rec ); +static +void ib_workCompletion( + IN ib_cq_handle_t h_cq, + IN void *cqContext ); + + +uint8_t +ibca_findPortNum( + struct _viport *p_viport, + uint64_t guid ) +{ + uint8_t port; + + for (port = 0; port < p_viport->p_adapter->ca.numPorts; port++ ) + { + if (p_viport->p_adapter->ca.portGuids[port] == guid ) + { + return port+1; + } + } + return 0; +} + +ib_api_status_t +ibregion_physInit( + IN struct _vnic_adapter* p_adapter, + IN IbRegion_t *pRegion, + IN ib_pd_handle_t hPd, + IN uint64_t *p_vaddr, + IN uint64_t len ) +{ + ib_phys_create_t phys_mem; + ib_phys_range_t phys_range; + ib_api_status_t ib_status = IB_SUCCESS; + uint64_t vaddr = 0; + VNIC_ENTER ( VNIC_DBG_IB ); + + UNUSED_PARAM( p_vaddr ); + + phys_range.base_addr = *(p_vaddr); + phys_range.size = len; + phys_mem.access_ctrl = ( IB_AC_LOCAL_WRITE | IB_AC_RDMA_WRITE ); + phys_mem.buf_offset = 0; + phys_mem.hca_page_size = PAGE_SIZE; + phys_mem.length = len; + phys_mem.num_ranges = 1; + phys_mem.range_array = &phys_range; + + ib_status = p_adapter->ifc.reg_phys( hPd, + &phys_mem, + &vaddr, + &pRegion->lkey, + &pRegion->rkey, + &pRegion->h_mr ); + + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("ib_reg_phys failed status %s(%d)\n", + p_adapter->ifc.get_err_str(ib_status), ib_status ) ); + pRegion->h_mr = NULL; + } + pRegion->virtAddress = vaddr; + pRegion->len = len; + + VNIC_EXIT( VNIC_DBG_IB ); + return ib_status; +} + +ib_api_status_t +ibregion_init ( + IN viport_t *p_viport, + IN OUT IbRegion_t *pRegion, + IN ib_pd_handle_t hPd, + IN void* __ptr64 vaddr, + IN uint64_t len, + IN ib_access_t access_ctrl ) +{ + ib_api_status_t ib_status; + ib_mr_create_t create_mem; + + VNIC_ENTER ( VNIC_DBG_IB ); + + create_mem.length = len; + create_mem.vaddr = vaddr; + create_mem.access_ctrl = access_ctrl; + + ib_status = p_viport->p_adapter->ifc.reg_mem( hPd, + &create_mem, + &pRegion->lkey, + &pRegion->rkey, + &pRegion->h_mr ); + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("ib_reg_mem failed status %s(%d)\n", + p_viport->p_adapter->ifc.get_err_str( ib_status ), ib_status )); + pRegion->h_mr = NULL; + } + else + { + pRegion->len = len; + pRegion->virtAddress = (uint64_t)( vaddr ); + } + VNIC_EXIT ( VNIC_DBG_IB ); + return ib_status; +} + +void +ibregion_cleanup( + IN viport_t *p_viport, + IN IbRegion_t *pRegion ) +{ + void *mr_handle; + ib_api_status_t ib_status; + + VNIC_ENTER( VNIC_DBG_IB ); + + if( ( mr_handle = InterlockedExchangePointer( (void *)&pRegion->h_mr, NULL )) != NULL ) + { + ib_status = p_viport->p_adapter->ifc.dereg_mr( (ib_mr_handle_t)mr_handle ); + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("Dereg MR failed status %s(%d)\n", + p_viport->p_adapter->ifc.get_err_str(ib_status), ib_status ) ); + return; + } + } + + VNIC_EXIT( VNIC_DBG_IB ); +} + + +void +ibqp_construct( + IN OUT IbQp_t *pQp, + IN viport_t *pViport ) +{ + VNIC_ENTER ( VNIC_DBG_IB ); + + ASSERT( pQp->qpState == IB_UNINITTED ); + + pQp->qp = NULL; + pQp->cq = NULL; + pQp->pViport = pViport; + pQp->pCa = &pViport->p_adapter->ca; + + NdisAllocateSpinLock( &pQp->qpLock ); + + InitializeListHead( &pQp->listPtrs); +} + + +ib_api_status_t +ibqp_init( + IN OUT IbQp_t *pQp, + IN uint64_t guid, + IN IbConfig_t *p_conf ) +{ + ib_qp_create_t attribCreate; + ib_qp_mod_t attribMod; + ib_qp_attr_t qpAttribs; + + ib_cq_create_t cq_create; + ib_cq_handle_t h_cq; + + ib_api_status_t ib_status = IB_SUCCESS; + + VNIC_ENTER ( VNIC_DBG_IB ); + + if (pQp->qpState != IB_UNINITTED && pQp->qpState != IB_DETACHED ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("ibqp_init: out of state (%d)\n", pQp->qpState) ); + return IB_ERROR; + } + + InterlockedExchange( &pQp->qpState, IB_UNINITTED ); + + pQp->p_conf = p_conf; + + cq_create.size = p_conf->numSends + p_conf->numRecvs; + cq_create.pfn_comp_cb = ib_workCompletion; + cq_create.h_wait_obj = NULL; + + ib_status = pQp->pViport->p_adapter->ifc.create_cq( + pQp->pViport->p_adapter->h_ca, &cq_create, pQp, NULL, &h_cq ); + + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("Failed allocating completion queue\n") ); + goto err1; + } + + pQp->cq = h_cq; + + cl_memclr( &attribCreate, sizeof(attribCreate) ); + + attribCreate.qp_type = IB_QPT_RELIABLE_CONN; + attribCreate.sq_depth = p_conf->numSends; + attribCreate.rq_depth = p_conf->numRecvs; + attribCreate.sq_sge = p_conf->sendGather; + attribCreate.rq_sge = p_conf->recvScatter; + attribCreate.h_sq_cq = pQp->cq; + attribCreate.h_rq_cq = pQp->cq; + attribCreate.sq_signaled = FALSE; + + ib_status = pQp->pViport->p_adapter->ifc.create_qp( + pQp->pViport->p_adapter->ca.hPd, &attribCreate, pQp, NULL, &pQp->qp ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_IB, ("Create QP failed %s\n", + pQp->pViport->p_adapter->ifc.get_err_str( ib_status )) ); + goto err2; + } + ib_status = pQp->pViport->p_adapter->ifc.query_qp(pQp->qp, &qpAttribs); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, ("Query QP failed %s\n", + pQp->pViport->p_adapter->ifc.get_err_str( ib_status )) ); + goto err3; + } + pQp->qpNumber = qpAttribs.num; + pQp->portGuid = guid; + cl_memclr( &attribMod, sizeof(attribMod) ); + + attribMod.req_state = IB_QPS_INIT; + attribMod.state.init.primary_port = ibca_findPortNum( pQp->pViport, guid ); + attribMod.state.init.pkey_index = 0; + attribMod.state.init.access_ctrl = IB_AC_LOCAL_WRITE; + attribMod.state.init.access_ctrl |= (p_conf->sendGather > 1) ? IB_AC_RDMA_WRITE : 0; + + ib_status = pQp->pViport->p_adapter->ifc.modify_qp( pQp->qp, &attribMod ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_IB, ("Init QP failed %s\n", + pQp->pViport->p_adapter->ifc.get_err_str( ib_status )) ); + +err3: + pQp->pViport->p_adapter->ifc.destroy_qp( pQp->qp, NULL ); +err2: + pQp->pViport->p_adapter->ifc.destroy_cq( pQp->cq, NULL ); + } + else + { + InterlockedExchange( &pQp->qpState, IB_INITTED ); + } + +err1: + VNIC_EXIT ( VNIC_DBG_IB ); + return ib_status; +} + + +ib_api_status_t +ibqp_connect( + IN IbQp_t *pQp) +{ + IbCa_t *pCa; + viport_t *p_viport; + IbConfig_t *p_conf; + ib_api_status_t ib_status = IB_SUCCESS; + ib_cm_req_t conn_req; + + VNIC_ENTER( VNIC_DBG_IB ); + + if ( pQp->qpState != IB_INITTED ) + { + VNIC_TRACE_EXIT( VNIC_DBG_IB, + ("ibqp_connect: out of state (%d)\n",pQp->qpState) ); + return IB_INVALID_STATE; + } + + p_viport = pQp->pViport; + pCa = pQp->pCa; + p_conf = pQp->p_conf; + +#ifdef VNIC_STATISTIC + pQp->statistics.connectionTime = cl_get_time_stamp(); +#endif + + cl_memclr( &conn_req, sizeof(conn_req)); + + conn_req.h_al = p_viport->p_adapter->h_al; + conn_req.qp_type = IB_QPT_RELIABLE_CONN; + conn_req.h_qp = pQp->qp; + conn_req.p_req_pdata = (uint8_t*)&pQp->p_conf->connData; + conn_req.req_length = sizeof( Inic_ConnectionData_t ); + conn_req.svc_id = pQp->p_conf->sid; + conn_req.pkey = pQp->p_conf->pathInfo.pkey; + + conn_req.p_primary_path = &pQp->p_conf->pathInfo; + + conn_req.retry_cnt = (uint8_t)pQp->p_conf->retryCount; + conn_req.rnr_nak_timeout = pQp->p_conf->minRnrTimer; + conn_req.rnr_retry_cnt = (uint8_t)pQp->p_conf->rnrRetryCount; + conn_req.max_cm_retries = 5; + conn_req.remote_resp_timeout = ib_path_rec_pkt_life( &pQp->p_conf->pathInfo ) + 1; + conn_req.local_resp_timeout = ib_path_rec_pkt_life( &pQp->p_conf->pathInfo ) + 1; + + conn_req.compare_length = 0; + conn_req.resp_res = 0; + + conn_req.flow_ctrl = TRUE; + + conn_req.pfn_cm_req_cb = NULL; + conn_req.pfn_cm_rep_cb = _ibqp_connect_cb; + conn_req.pfn_cm_mra_cb = _ibqp_mra_cb; + conn_req.pfn_cm_rej_cb = _ibqp_rej_cb; + + InterlockedExchange( &pQp->qpState, IB_ATTACHING ); + + ib_status = p_viport->p_adapter->ifc.cm_req ( &conn_req ); + if ( ib_status != IB_SUCCESS && ib_status != IB_PENDING ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("Connect request return status %s\n", + p_viport->p_adapter->ifc.get_err_str( ib_status )) ); + InterlockedExchange( &pQp->qpState, IB_DETACHED ); + return ib_status; + } + + if( cl_event_wait_on( + &p_viport->conn_event, EVENT_NO_TIMEOUT, FALSE ) != CL_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, ("conn event timeout!\n") ); + return IB_TIMEOUT; + } + + if( pQp->qpState != IB_ATTACHED ) + { + VNIC_TRACE( VNIC_DBG_ERROR, ("QP connect failed\n") ); + ib_status = IB_ERROR; + } + + VNIC_EXIT( VNIC_DBG_IB ); + return ib_status; +} + + +static void +_ibqp_detach_cb( + IN ib_cm_drep_rec_t *p_drep_rec ) +{ + IbQp_t *pQp = (IbQp_t * __ptr64 )p_drep_rec->qp_context; + VNIC_ENTER( VNIC_DBG_IB ); + CL_ASSERT( p_drep_rec ); + + InterlockedExchange( &pQp->qpState, IB_DETACHED ); + + VNIC_EXIT( VNIC_DBG_IB ); +} + +static void +_ibqp_rej_cb( + IN ib_cm_rej_rec_t *p_rej_rec ) +{ + IbQp_t *pQp = (IbQp_t * __ptr64 )p_rej_rec->qp_context; + CL_ASSERT(p_rej_rec ); + + InterlockedExchange( &pQp->qpState, IB_DETACHED ); + switch ( p_rej_rec->rej_status ) + { + case IB_REJ_USER_DEFINED: + + VNIC_TRACE ( VNIC_DBG_IB | VNIC_DBG_ERROR, + ("Conn req user reject status %d\nARI: %s\n", + cl_ntoh16( p_rej_rec->rej_status ), + p_rej_rec->p_ari )); + break; + default: + VNIC_TRACE( VNIC_DBG_IB | VNIC_DBG_ERROR, + ("Conn req reject status %d\n", + cl_ntoh16( p_rej_rec->rej_status )) ); + } + viport_failure( pQp->pViport ); + cl_event_signal( &pQp->pViport->conn_event ); +} + +static void +_ibqp_mra_cb( + IN ib_cm_mra_rec_t *p_mra_rec ) +{ + VNIC_ENTER( VNIC_DBG_IB ); + CL_ASSERT( p_mra_rec ); + UNREFERENCED_PARAMETER( p_mra_rec ); + VNIC_EXIT( VNIC_DBG_IB ); +} + +static void +_ibqp_dreq_cb( + IN ib_cm_dreq_rec_t *p_dreq_rec ) +{ + ib_api_status_t ib_status = IB_SUCCESS; + ib_cm_drep_t cm_drep; + IbQp_t *pQp = (IbQp_t * __ptr64 )p_dreq_rec->qp_context; + + VNIC_ENTER( VNIC_DBG_IB ); + CL_ASSERT( p_dreq_rec ); + + cm_drep.drep_length = 0; + cm_drep.p_drep_pdata = NULL; + + ib_status = pQp->pViport->p_adapter->ifc.cm_drep( + p_dreq_rec->h_cm_dreq, &cm_drep ); + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("dreq_cb failed status %s(%d)\n", + pQp->pViport->p_adapter->ifc.get_err_str( ib_status ), ib_status)); + return; + } + + InterlockedExchange (&pQp->qpState, IB_DETACHED ); + + VNIC_EXIT( VNIC_DBG_IB ); +} + +void +ibqp_detach( + IN IbQp_t *pQp ) +{ + ib_cm_dreq_t cm_dreq; + ib_api_status_t ib_status = IB_SUCCESS; + + VNIC_ENTER( VNIC_DBG_IB ); + + NdisAcquireSpinLock( &pQp->qpLock ); + + if( pQp->qpState == IB_ATTACHED ) + { + InterlockedExchange ( &pQp->qpState, IB_DETACHING ); + + cm_dreq.h_qp = pQp->qp; + cm_dreq.qp_type = IB_QPT_RELIABLE_CONN; + cm_dreq.p_dreq_pdata = NULL; + cm_dreq.dreq_length = 0; + cm_dreq.pfn_cm_drep_cb = _ibqp_detach_cb; + ib_status = pQp->pViport->p_adapter->ifc.cm_dreq( &cm_dreq ); + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + (" cm_dreq failed status %s\n", + pQp->pViport->p_adapter->ifc.get_err_str( ib_status ))); + } + ib_status = pQp->pViport->p_adapter->ifc.destroy_qp( pQp->qp, NULL ); + } + NdisReleaseSpinLock( &pQp->qpLock ); + + VNIC_EXIT( VNIC_DBG_IB ); + return; +} + +void +ibqp_cleanup( + IN IbQp_t *pQp ) +{ + ib_api_status_t ib_status = IB_SUCCESS; + LONG qp_state; + + VNIC_ENTER( VNIC_DBG_IB ); + + qp_state = InterlockedExchange( &pQp->qpState, IB_UNINITTED ); + if ( qp_state != IB_UNINITTED && + qp_state != IB_DETACHED && + qp_state != IB_DETACHING ) + { + ib_status = pQp->pViport->p_adapter->ifc.destroy_qp( pQp->qp, NULL ); + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("destroy_qp failed status %s\n", + pQp->pViport->p_adapter->ifc.get_err_str( ib_status )) ); + } + } + + VNIC_EXIT( VNIC_DBG_IB ); + return; +} + +ib_api_status_t ibqp_postSend(IbQp_t *pQp, Io_t *pIo) +{ + ib_api_status_t ib_status; + +#ifdef VNIC_STATISTIC + int64_t postTime; +#endif /* VNIC_STATISTIC */ + + VNIC_ENTER( VNIC_DBG_IB ); + + NdisAcquireSpinLock( &pQp->qpLock ); + + if( pQp->qpState != IB_ATTACHED ) + { + ib_status = IB_INVALID_STATE; + goto failure; + } + +#ifdef VNIC_STATISTIC + pIo->time = postTime = cl_get_time_stamp(); +#endif /* VNIC_STATISTIC */ + if (pIo->wrq.wr_type == WR_RDMA_WRITE ) + pIo->type = RDMA; + else + pIo->type = SEND; + + ib_status = + pQp->pViport->p_adapter->ifc.post_send( pQp->qp, &pIo->wrq, NULL ); + +#ifdef VNIC_STATISTIC + postTime = cl_get_time_stamp() - postTime; +#endif /* VNIC_STATISTIC */ + + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, ("ib_post_send returned %s\n", + pQp->pViport->p_adapter->ifc.get_err_str( ib_status )) ); + } + else + { +#ifdef VNIC_STATISTIC + if (pIo->wrq.wr_type == WR_RDMA_WRITE) + { + pQp->statistics.rdmaPostTime += postTime; + pQp->statistics.rdmaPostIos++; + } + else + { + pQp->statistics.sendPostTime += postTime; + pQp->statistics.sendPostIos++; + } +#endif /* VNIC_STATISTIC */ + } + +failure: + NdisReleaseSpinLock( &pQp->qpLock ); + VNIC_EXIT( VNIC_DBG_IB ); + return ib_status; +} + + +ib_api_status_t +ibqp_postRecv(IbQp_t *pQp, Io_t *pIo) +{ + ib_api_status_t ib_status = IB_SUCCESS; +#ifdef VNIC_STATISTIC + int64_t postTime; +#endif /* VNIC_STATISTIC */ + + NdisAcquireSpinLock( &pQp->qpLock ); + /* can post recvieves before connecting queue pair */ + if( pQp->qpState != IB_INITTED && pQp->qpState != IB_ATTACHED ) + { + NdisReleaseSpinLock( &pQp->qpLock ); + return IB_INVALID_STATE; + } + pIo->type = RECV; + +#ifdef VNIC_STATISTIC + postTime = cl_get_time_stamp(); + if (pIo->time != 0) + { + pQp->statistics.recvCompTime += postTime - pIo->time; + pQp->statistics.recvCompIos++; + } +#endif /* VNIC_STATISTIC */ + + ib_status = + pQp->pViport->p_adapter->ifc.post_recv(pQp->qp, &pIo->r_wrq, NULL ); + +#ifdef VNIC_STATISTIC + postTime = (cl_get_time_stamp()) - postTime; +#endif /* VNIC_STATISTIC */ + + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("Post Recv failed status %s(%d)\n", + pQp->pViport->p_adapter->ifc.get_err_str(ib_status), ib_status )); + + NdisReleaseSpinLock( &pQp->qpLock ); + return FALSE; + } + else + { +#ifdef VNIC_STATISTIC + pQp->statistics.recvPostTime += postTime; + pQp->statistics.recvPostIos++; +#endif /* VNIC_STATISTIC */ + } + + NdisReleaseSpinLock( &pQp->qpLock ); + return ib_status; +} + + +static void +_ibqp_connect_cb( + IN ib_cm_rep_rec_t *p_cm_rep ) +{ + IbQp_t *pQp; + ib_api_status_t ib_status = IB_SUCCESS; + + union + { + ib_cm_rtu_t cm_rtu; + ib_cm_mra_t cm_mra; + ib_cm_rej_t cm_rej; + } u_reply; + + viport_t *p_viport; + + VNIC_ENTER( VNIC_DBG_IB ); + + pQp = (IbQp_t * __ptr64 )p_cm_rep->qp_context; + p_viport = pQp->pViport; + + ASSERT( pQp->qpState == IB_ATTACHING ); + + ib_status = p_viport->p_adapter->ifc.rearm_cq( pQp->cq, FALSE ); + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("Rearm CQ failed %s\n", + p_viport->p_adapter->ifc.get_err_str( ib_status )) ); + + cl_memclr( &u_reply.cm_rej, sizeof( u_reply.cm_rej ) ); + u_reply.cm_rej.rej_status = IB_REJ_INSUF_RESOURCES; + + p_viport->p_adapter->ifc.cm_rej( p_cm_rep->h_cm_rep, &u_reply.cm_rej ); + goto err; + } + + cl_memclr( &u_reply.cm_rtu, sizeof( u_reply.cm_rtu ) ); + u_reply.cm_rtu.access_ctrl = IB_AC_LOCAL_WRITE; + + if( pQp->p_conf->sendGather > 1 ) + u_reply.cm_rtu.access_ctrl |= ( IB_AC_RDMA_WRITE ); + + u_reply.cm_rtu.pfn_cm_dreq_cb = _ibqp_dreq_cb; + + ib_status = p_viport->p_adapter->ifc.cm_rtu ( p_cm_rep->h_cm_rep, &u_reply.cm_rtu ); + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("Send RTU failed\n") ); +err: + InterlockedExchange( &pQp->qpState, IB_DETACHED ); + viport_failure( p_viport ); + } + else + { + InterlockedExchange ( &pQp->qpState, IB_ATTACHED ); + } + + cl_event_signal( &p_viport->conn_event ); + VNIC_EXIT( VNIC_DBG_IB ); +} + + +#define WC_LIST_SIZE_TO_POLL 4 +static void +_ib_qpCompletion( + IN IbQp_t *pQp ) +{ + Io_t *pIo; + ib_wc_t wc[WC_LIST_SIZE_TO_POLL]; + ib_wc_t *p_free_wc; + ib_wc_t *p_done_wc; + ib_api_status_t ib_status = IB_SUCCESS; + int i; + +#ifdef VNIC_STATISTIC + int64_t compTime; + uint32_t compNum = 0; +#endif /* VNIC_STATISTIC */ + + VNIC_ENTER ( VNIC_DBG_IB ); + + if ( pQp->qpState != IB_ATTACHING && pQp->qpState != IB_ATTACHED ) + return; + +#ifdef VNIC_STATISTIC + compTime = cl_get_time_stamp(); + pQp->statistics.numCallbacks++; +#endif /* VNIC_STATISTIC */ + + for( i = 0; i < (WC_LIST_SIZE_TO_POLL - 1); i++ ) + { + wc[i].p_next = &wc[i + 1]; + } + wc[(WC_LIST_SIZE_TO_POLL - 1)].p_next = NULL; + + p_free_wc = wc; + p_done_wc = NULL; + + ib_status = pQp->pViport->p_adapter->ifc.poll_cq( pQp->cq, &p_free_wc, &p_done_wc ); + + if ( ib_status != IB_SUCCESS && ib_status != IB_NOT_FOUND ) + { + VNIC_TRACE ( VNIC_DBG_ERROR, + ("ib_poll_cq failed status %d(%s)\n", ib_status, + pQp->pViport->p_adapter->ifc.get_err_str( ib_status )) ); + return; + } + + while ( p_done_wc ) + { + pIo = (Io_t *)(uintn_t)p_done_wc->wr_id; + + if (pIo) + { + /* keep completion status for proper ndis packet return status */ + pIo->wc_status = p_done_wc->status; +#ifdef VNIC_STATISTIC + if (pIo->type == RECV) + { + pIo->time = compTime; + } + else if (pIo->type == RDMA) + { + pQp->statistics.rdmaCompTime += compTime - pIo->time; + pQp->statistics.rdmaCompIos++; + } + else if (pIo->type == SEND) + { + pQp->statistics.sendCompTime += compTime - pIo->time; + pQp->statistics.sendCompIos++; + } + pQp->statistics.numIos++; + if ( ++compNum > pQp->statistics.maxIos) + { + pQp->statistics.maxIos = compNum; + } +#endif /* VNIC_STATISTIC */ + VNIC_TRACE( VNIC_DBG_IB, + ("ib_workCompletion: WcType = %d, Status = %d, Length = %d\n", + p_done_wc->wc_type, + p_done_wc->status, + ( p_done_wc->wc_type == IB_WC_RECV )? p_done_wc->length : 0 ) ); + + if( pIo->pRoutine ) + { + (*pIo->pRoutine)( pIo ); + } + } + + if (p_done_wc->status != IB_WCS_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_IB, + ("Failed completion, type = %d, status = %d (%s)\n", + p_done_wc->wc_type, + p_done_wc->status, + pQp->pViport->p_adapter->ifc.get_wc_status_str (p_done_wc->status) ) ); + } + + p_done_wc = p_done_wc->p_next; + } + + ib_status = pQp->pViport->p_adapter->ifc.rearm_cq( pQp->cq, FALSE ); + + if ( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("Rearm CQ failed status %d(%s)\n", ib_status, + pQp->pViport->p_adapter->ifc.get_err_str( ib_status )) ); + } + return; +} + + +static +void ib_workCompletion( + IN ib_cq_handle_t h_cq, + IN void *cqContext ) +{ + IbQp_t *pQp; + + VNIC_ENTER ( VNIC_DBG_IB ); + UNREFERENCED_PARAMETER( h_cq ); + pQp = (IbQp_t *)cqContext; + _ib_qpCompletion(pQp); + + VNIC_EXIT ( VNIC_DBG_IB ); + return; +} + +void +ib_asyncEvent( + IN ib_async_event_rec_t *pEventRecord ) +{ + IbQp_t *pQp; + + VNIC_ENTER ( VNIC_DBG_IB ); + + if ( pEventRecord ) + { + switch ( pEventRecord->code ) + { + case IB_AE_QP_COMM: + case IB_AE_QP_FATAL: + + pQp = (IbQp_t * __ptr64 )pEventRecord->context; + if (pQp) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("Async Event %d QP State %#x\n", + pEventRecord->code, pQp->qpState )); + } + break; + default: + VNIC_TRACE( VNIC_DBG_ERROR, + ("Async Event %d received\n", pEventRecord->code) ); + break; + } + } + else + { + VNIC_TRACE( VNIC_DBG_ERROR, ("Unknown NULL event\n") ); + } + VNIC_EXIT ( VNIC_DBG_IB ); +} + + + + + + + diff --git a/trunk/ulp/inic/kernel/vnic_ib.h b/trunk/ulp/inic/kernel/vnic_ib.h new file mode 100644 index 00000000..2755fddd --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_ib.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ +#ifndef _VNIC_IB_H_ +#define _VNIC_IB_H_ + + +#include +#include +#include "vnic_trailer.h" +struct Io; + +typedef void (CompRoutine_t)(struct Io *pIo); + +#define MAX_HCAS 4 +#define MAX_NUM_SGE 8 + +#define MAX_PHYS_MEMORY 0xFFFFFFFFFFFFFFFF +#define CONTROL_SID 0 +#define DATA_SID 1 + +#include +typedef union _vnic_sid { + uint64_t as_uint64; + struct { + uint8_t base_id; /* base id for vnic is 0x10 */ + uint8_t oui[3]; /* OUI */ + uint16_t reserved; /* should be zero */ + uint8_t type; /* control or data */ + uint8_t ioc_num; /* ioc number */ + }s; +} vnic_sid_t; + +typedef union _vnic_ioc_guid { + uint64_t as_uint64; + struct { + uint8_t oui[3]; + uint8_t ioc_num; + uint32_t counter; /* SST device type: 8 bits, counter:24 bits */ + }s; +} vnic_ioc_guid_t; +#include + +typedef enum { + IB_UNINITTED = 0, + IB_INITTED, + IB_ATTACHING, + IB_ATTACHED, + IB_DETACHING, + IB_DETACHED, + IB_DISCONNECTED +} IbState_t; +#pragma warning ( disable : 4324 ) +typedef struct _vnic_path_record { + cl_list_item_t list_entry; + uint32_t num_entries; + ib_path_rec_t path_rec; +} vnic_path_record_t; +#pragma warning( default : 4324 ) + +typedef struct IbQp { + LIST_ENTRY listPtrs; + struct _viport *pViport; + struct IbConfig *p_conf; + NDIS_SPIN_LOCK qpLock; + volatile LONG qpState; + uint32_t qpNumber; + struct IbCa *pCa; + uint64_t portGuid; + ib_qp_handle_t qp; + ib_cq_handle_t cq; +#ifdef VNIC_STATISTIC + struct { + int64_t connectionTime; + int64_t rdmaPostTime; + uint32_t rdmaPostIos; + int64_t rdmaCompTime; + uint32_t rdmaCompIos; + int64_t sendPostTime; + uint32_t sendPostIos; + int64_t sendCompTime; + uint32_t sendCompIos; + int64_t recvPostTime; + uint32_t recvPostIos; + int64_t recvCompTime; + uint32_t recvCompIos; + uint32_t numIos; + uint32_t numCallbacks; + uint32_t maxIos; + } statistics; +#endif /* VNIC_STATISTIC */ +} IbQp_t; + +typedef struct IbRegion { + uint64_t virtAddress; + net64_t len; + ib_mr_handle_t h_mr; + net32_t lkey; + net32_t rkey; +} IbRegion_t; + + +#define VNIC_CA_MAX_PORTS 2 +typedef struct IbCa { + cl_list_item_t list_entry; + net64_t caGuid; + ib_pd_handle_t hPd; + IbRegion_t region; + uint32_t numPorts; + uint64_t portGuids[VNIC_CA_MAX_PORTS]; +} IbCa_t; + +typedef enum _OpType { RECV, RDMA, SEND }OpType_t; + +typedef struct Io { + LIST_ENTRY listPtrs; + struct _viport *pViport; + CompRoutine_t *pRoutine; + ib_send_wr_t wrq; + ib_recv_wr_t r_wrq; + ib_wc_status_t wc_status; +#ifdef VNIC_STATISTIC + int64_t time; +#endif /* VNIC_STATISTIC */ + OpType_t type; +} Io_t; + +typedef struct RdmaIo { + Io_t io; + ib_local_ds_t dsList[MAX_NUM_SGE]; + uint16_t index; + uint32_t len; + NDIS_PACKET *p_packet; + NDIS_BUFFER *p_buf; + ULONG packet_sz; + struct ViportTrailer *p_trailer; + uint8_t data[2* VIPORT_TRAILER_ALIGNMENT]; +} RdmaIo_t; + +typedef struct SendIo { + Io_t io; + ib_local_ds_t dsList; +} SendIo_t; + +typedef struct RecvIo { + Io_t io; + ib_local_ds_t dsList; +} RecvIo_t; + +void +ibqp_construct( + IN OUT IbQp_t *pQp, + IN struct _viport *pViport ); +ib_api_status_t ibqp_init(IbQp_t *pQp, uint64_t guid, struct IbConfig *p_conf); +ib_api_status_t ibqp_connect(IbQp_t *pQp); +void ibqp_detach(IbQp_t *pQp); +void ibqp_cleanup(IbQp_t *pQp); +ib_api_status_t ibqp_postSend(IbQp_t *pQp, Io_t *pIo); +ib_api_status_t ibqp_postRecv(IbQp_t *pQp, Io_t *pIo); + +uint8_t ibca_findPortNum( struct _viport *p_viport, uint64_t guid ); + +ib_api_status_t +ibregion_init( + IN struct _viport *p_viport, + OUT IbRegion_t *pRegion, + IN ib_pd_handle_t hPd, + IN void* __ptr64 vaddr, + IN uint64_t len, + IN ib_access_t access_ctrl ); + +void +ibregion_cleanup( + struct _viport *p_viport, + IbRegion_t *pRegion ); + +void ib_asyncEvent( ib_async_event_rec_t *pEventRecord ); + +#define ibpd_fromCa(pCa) (&(pCa)->pd) + + +#endif /* _VNIC_IB_H_ */ diff --git a/trunk/ulp/inic/kernel/vnic_netpath.c b/trunk/ulp/inic/kernel/vnic_netpath.c new file mode 100644 index 00000000..a20862e6 --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_netpath.c @@ -0,0 +1,237 @@ + +#include "vnic_adapter.h" + +extern vnic_globals_t g_vnic; + + +void +netpath_init( + IN Netpath_t *pNetpath, + IN vnic_adapter_t *p_adapter ) +{ + int i; + + VNIC_ENTER( VNIC_DBG_FUNC ); + + pNetpath->p_adapter = p_adapter; + pNetpath->carrier = 0; + pNetpath->pViport = NULL; + pNetpath->timerState = NETPATH_TS_IDLE; + + for ( i = 0; i < INICNP_NUM_EVENTS; i++ ) + { + p_adapter->np_event[i].p_adapter = p_adapter; + p_adapter->np_event[i].event_num = (uint8_t)i; + InitializeListHead( &p_adapter->np_event[i].listPtrs ); + } + + VNIC_EXIT( VNIC_DBG_FUNC ); + return; +} + + +BOOLEAN +netpath_addPath ( + Netpath_t *pNetpath, + viport_t *pViport ) +{ + + if( pNetpath->pViport ) + { + return FALSE; + } + else + { + pNetpath->pViport = pViport; + viport_setParent( pViport, pNetpath ); + return TRUE; + } +} + +BOOLEAN +netpath_removePath( + Netpath_t *pNetpath, + viport_t *pViport) +{ + if( pNetpath->pViport != pViport ) + { + return FALSE; + } + else + { + pNetpath->pViport = NULL; + viport_unsetParent( pViport, pNetpath ); + return TRUE; + } +} + + +BOOLEAN netpath_setUnicast(Netpath_t *pNetpath, uint8_t *pAddress) +{ + BOOLEAN ret = FALSE; + + // TODO: Not called anywhere!!! + VNIC_TRACE( VNIC_DBG_NETPATH | VNIC_DBG_INFO, + ("Set %s MAC to %02X:%02X:%02X:%02X:%02X:%02X\n", + netpath_to_string(pNetpath->p_adapter, pNetpath), + pAddress[0], + pAddress[1], + pAddress[2], + pAddress[3], + pAddress[4], + pAddress[5]) ); + + if (pNetpath->pViport) + { + ret = viport_setUnicast( pNetpath->pViport, pAddress ); + } + return ret; +} + + +int netpath_maxMtu(Netpath_t *pNetpath) +{ + int ret = MAX_PARAM_VALUE; + + if (pNetpath->pViport) { + ret = viport_maxMtu(pNetpath->pViport); + } + return ret; +} + +BOOLEAN +netpath_xmitPacket( + IN Netpath_t *pNetpath, + IN NDIS_PACKET * const p_packet ) +{ + BOOLEAN ret = FALSE; + + if ( pNetpath->pViport ) + { + ret = viport_xmitPacket(pNetpath->pViport, p_packet ); + } + return ret; +} + + +void +netpath_stopXmit( + IN Netpath_t *pNetpath ) + +{ + + VNIC_ENTER( VNIC_DBG_NETPATH ); + + if (pNetpath == pNetpath->p_adapter->p_currentPath ) + { + InterlockedCompareExchange( &pNetpath->p_adapter->xmitStarted, 0, 1 ); + } +#ifdef INIC_STATISTICS + if ( pNetpath->p_adapter->statistics.xmitRef == 0) + { + pNetpath->p_adapter->statistics.xmitRef = get_time_stamp_ms(); + } +#endif /* INIC_STATISTICS */ + return; +} + +void netpath_restartXmit( + IN Netpath_t *pNetpath ) +{ + VNIC_ENTER( VNIC_DBG_NETPATH ); + + if (pNetpath == pNetpath->p_adapter->p_currentPath ) + { + InterlockedCompareExchange( &pNetpath->p_adapter->xmitStarted, 1, 0 ); + } +#ifdef INIC_STATISTICS + if (pNetpath->p_adapter->statistics.xmitRef != 0) + { + pNetpath->p_adapter->statistics.xmitOffTime += + get_time_stamp_ms() - pNetpath->p_adapter->statistics.xmitRef; + pNetpath->p_adapter->statistics.xmitOffNum++; + pNetpath->p_adapter->statistics.xmitRef = 0; + } +#endif /* INIC_STATISTICS */ + return; +} + +// Viport on input calls this +void netpath_recvPacket( + IN Netpath_t *pNetpath, + IN NDIS_PACKET* p_packet ) +{ + #ifdef INIC_STATISTICS + extern cycles_t recvRef; +#endif /* INIC_STATISTICS */ + + VNIC_ENTER( VNIC_DBG_NETPATH ); + +#ifdef INIC_STATISTICS + pNetpath->p_adapter->statistics.recvTime += get_time_stamp_ms() - recvRef; + pNetpath->p_adapter->statistics.recvNum++; +#endif /* INIC_STATISTICS */ + + NdisMIndicateReceivePacket( pNetpath->p_adapter->h_handle, &p_packet, 1 ); + + return; +} + +void netpath_tx_timeout( + IN Netpath_t *pNetpath ) +{ + if ( pNetpath->pViport ) + { + viport_failure( pNetpath->pViport ); + } +} + +const char * +netpath_to_string( + IN vnic_adapter_t *p_adapter, + IN Netpath_t *pNetpath ) +{ + if ( !pNetpath ) + { + return "NULL"; + } + else if ( pNetpath == &p_adapter->primaryPath ) + { + return "PRIMARY"; + } + else if ( pNetpath == &p_adapter->secondaryPath ) + { + return "SECONDARY"; + } else + { + return "UNKNOWN"; + } +} + + +static BOOLEAN +__netpath_npevent_register(vnic_adapter_t *p_adapter, Netpath_t *pNetpath) +{ + VNIC_ENTER( VNIC_DBG_NETPATH ); + + UNUSED_PARAM( pNetpath ); + p_adapter->state = INIC_REGISTERED; + return TRUE; +} + +static const char * const inicNPEventStr[] = { + "PRIMARY CONNECTED", + "PRIMARY DISCONNECTED", + "PRIMARY CARRIER", + "PRIMARY NO CARRIER", + "PRIMARY TIMER EXPIRED", + "SETLINK", + + "SECONDARY CONNECTED", + "SECONDARY DISCONNECTED", + "SECONDARY CARRIER", + "SECONDARY NO CARRIER", + "SECONDARY TIMER EXPIRED", + "FREE INIC", +}; + diff --git a/trunk/ulp/inic/kernel/vnic_trailer.h b/trunk/ulp/inic/kernel/vnic_trailer.h new file mode 100644 index 00000000..d128780f --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_trailer.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + +#ifndef _VNIC_TRAILER_H_ +#define _VNIC_TRAILER_H_ + +/* pktFlags values */ +#define PF_CHASH_VALID 0x01 +#define PF_IPSEC_VALID 0x02 +#define PF_TCP_SEGMENT 0x04 +#define PF_KICK 0x08 +#define PF_VLAN_INSERT 0x10 +#define PF_PVID_OVERRIDDEN 0x20 +#define PF_FCS_INCLUDED 0x40 +#define PF_FORCE_ROUTE 0x80 + +/* txChksumFlags values */ +#define TX_CHKSUM_FLAGS_CHECKSUM_V4 0x01 +#define TX_CHKSUM_FLAGS_CHECKSUM_V6 0x02 +#define TX_CHKSUM_FLAGS_TCP_CHECKSUM 0x04 +#define TX_CHKSUM_FLAGS_UDP_CHECKSUM 0x08 +#define TX_CHKSUM_FLAGS_IP_CHECKSUM 0x10 + +/* rxChksumFlags values */ +#define RX_CHKSUM_FLAGS_TCP_CHECKSUM_FAILED 0x01 +#define RX_CHKSUM_FLAGS_UDP_CHECKSUM_FAILED 0x02 +#define RX_CHKSUM_FLAGS_IP_CHECKSUM_FAILED 0x04 +#define RX_CHKSUM_FLAGS_TCP_CHECKSUM_SUCCEEDED 0x08 +#define RX_CHKSUM_FLAGS_UDP_CHECKSUM_SUCCEEDED 0x10 +#define RX_CHKSUM_FLAGS_IP_CHECKSUM_SUCCEEDED 0x20 +#define RX_CHKSUM_FLAGS_LOOPBACK 0x40 +#define RX_CHKSUM_FLAGS_RESERVED 0x80 + +/* connectionHashAndValid values */ +#define CHV_VALID 0x80 +#define CHV_HASH_MASH 0x7f + +/* round down value to align, align must be a power of 2 */ +#ifndef ROUNDDOWNP2 +#define ROUNDDOWNP2(val, align) \ + (((uintn_t)(val)) & (~((uintn_t)(align)-1))) +#endif + +/* round up value to align, align must be a power of 2 */ +#ifndef ROUNDUPP2 +#define ROUNDUPP2(val, align) \ + (((uintn_t)(val) + (uintn_t)(align) - 1) & (~((uintn_t)(align)-1))) +#endif + +#define VIPORT_TRAILER_ALIGNMENT 32 +#define BUFFER_SIZE(len) (sizeof(ViportTrailer_t) + ROUNDUPP2((len), VIPORT_TRAILER_ALIGNMENT)) +#define MAX_PAYLOAD(len) ROUNDDOWNP2((len) - sizeof(ViportTrailer_t), VIPORT_TRAILER_ALIGNMENT) + +#include + +typedef struct ViportTrailer { + int8_t dataAlignmentOffset; + uint8_t rndisHeaderLength; /* reserved for use by Edp */ + uint16_t dataLength; + uint8_t pktFlags; + + uint8_t txChksumFlags; + + uint8_t rxChksumFlags; + + uint8_t ipSecFlags; + uint32_t tcpSeqNo; + uint32_t ipSecOffloadHandle; + uint32_t ipSecNextOffloadHandle; + uint8_t destMacAddr[6]; + uint16_t vLan; + uint16_t timeStamp; + uint8_t origin; + uint8_t connectionHashAndValid; +} ViportTrailer_t; + +#include + +#endif /* _VNIC_TRAILER_H_ */ \ No newline at end of file diff --git a/trunk/ulp/inic/kernel/vnic_util.h b/trunk/ulp/inic/kernel/vnic_util.h new file mode 100644 index 00000000..7586ee2e --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_util.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + +#ifndef _VNIC_UTIL_H_ +#define _VNIC_UTIL_H_ + +#include "vnic_debug.h" + +#define MAXU32 0xffffffff +#define MAXU64 ((uint64_t)(~0)) + +#define MODULE_NAME "VNIC" + +#define PTR64(what) ((uint64_t)(void * __ptr64)(what)) + +#define CONV2JIFFIES(time) (((time) * HZ) / 100) /* source time is 100ths of a sec */ +#define CONV2USEC(time) ((time) * 10000) /* source time is 100ths of a sec */ + +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif + +#define hton16(x) _byteswap_ushort(x) +#define hton32(x) _byteswap_ulong(x) +#define hton64(x) _byteswap_uint64(x) + +#define ntoh16(x) hton16(x) +#define ntoh32(x) hton32(x) +#define ntoh64(x) hton64(x) + +#define IsPowerOf2(value) (((value) & ((value - 1))) == 0) +/* round down to closest power of 2 value */ + +#define SetMinPowerOf2(_val) ((_val) & ( 1 << RtlFindMostSignificantBit( (uint64_t)(_val) ))) + +#define PRINT(level,x) VNIC_PRINT(level, x ) + +#define PRINT_CONDITIONAL(level,x,condition) if (condition){PRINT( level, x)} + +#ifdef _DEBUG_ +#define IB_FSTATUS(functionName) \ + if( (ib_status != IB_SUCCESS) && \ + (ib_status != IB_PENDING) ) \ + VNIC_PRINT( g_vnic_dbg_lvl,("[%s]: %d\n", functionName,ib_status)) + +#else +#define IB_FSTATUS(functionName) +#endif + +#define IB_TEST_FSTATUS( functionName ) IB_FSTATUS( functionName ); \ + if ((ib_status != IB_SUCCESS) && (ib_status != IB_PENDING)) goto failure + +#endif /* _VNIC_UTIL_H_ */ + diff --git a/trunk/ulp/inic/kernel/vnic_viport.c b/trunk/ulp/inic/kernel/vnic_viport.c new file mode 100644 index 00000000..388f6859 --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_viport.c @@ -0,0 +1,1025 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + +#include "iba/ib_al.h" +#include "vnic_util.h" +#include "vnic_driver.h" +#include "vnic_viport.h" +#include "vnic_control.h" +#include "vnic_data.h" +#include "vnic_config.h" +#include "vnic_controlpkt.h" + +extern vnic_globals_t g_vnic; + +static void viport_timeout( void *context ); +static ib_api_status_t viport_initMacAddresses( viport_t *p_viport ); +static void viport_statemachine( void *context ); + +uint32_t +viport_get_adapter_name( + IN viport_t *p_viport ) +{ + cl_memcpy(p_viport->p_adapter->name, DEFAULT_VNIC_NAME, sizeof(DEFAULT_VNIC_NAME) ); + return ( sizeof(DEFAULT_VNIC_NAME) ); +} + +BOOLEAN +viport_config_defaults( + IN viport_t *p_viport ) +{ + + vnic_adapter_t *p_adapter = p_viport->p_adapter; + ViportConfig_t *pConfig = &p_viport->port_config; + ControlConfig_t *pControlConfig = &p_viport->port_config.controlConfig; + DataConfig_t *pDataConfig = &p_viport->port_config.dataConfig; + + VNIC_ENTER( VNIC_DBG_VIPORT ); + + p_viport->state = VIPORT_DISCONNECTED; + p_viport->linkState = LINK_RETRYWAIT; + p_viport->newMtu = 1500; + p_viport->newFlags = 0; + + cl_timer_init( &p_viport->timer, viport_timeout, p_viport ); + + pConfig->statsInterval = p_adapter->params.ViportStatsInterval; + pConfig->hbInterval = p_adapter->params.ViportHbInterval; + pConfig->hbTimeout = p_adapter->params.ViportHbTimeout; + + cl_memcpy ( pConfig->ioc_string, + p_adapter->ioc_info.profile.id_string, + min( sizeof( p_adapter->ioc_info.profile.id_string ), + sizeof( pConfig->ioc_string )) ); + + pControlConfig->ibConfig.sid = + 0x10ULL << 56 | + 0x00ULL << 48 | + 0x06ULL << 40 | + 0x6aULL << 32 | + 0x00ULL << 24 | + 0x00ULL << 16 | + 0x00ULL << 8 | + ( (uint8_t)( p_adapter->ioc_info.profile.ioc_guid >> 24 ) & 0xFF ); + + pControlConfig->ibConfig.connData.pathId = 0; + pControlConfig->ibConfig.connData.inicInstance = 0; + pControlConfig->ibConfig.connData.pathNum = 0; + + pControlConfig->ibConfig.retryCount = p_adapter->params.RetryCount; + pControlConfig->ibConfig.rnrRetryCount = p_adapter->params.RetryCount; + pControlConfig->ibConfig.minRnrTimer = (uint8_t)p_adapter->params.MinRnrTimer; + pControlConfig->ibConfig.numRecvs = 5; /* Not configurable */ + pControlConfig->ibConfig.numSends = 1; /* Not configurable */ + pControlConfig->ibConfig.recvScatter = 1; /* Not configurable */ + pControlConfig->ibConfig.sendGather = 1; /* Not configurable */ + + /* indicate new features support capabilities */ + pControlConfig->ibConfig.connData.featuresSupported = + hton32((uint32_t)(INIC_FEAT_IGNORE_VLAN | INIC_FEAT_RDMA_IMMED )); + + cl_memcpy ( pControlConfig->ibConfig.connData.nodename, + g_vnic.host_name, + min( sizeof( g_vnic.host_name ), + sizeof( pControlConfig->ibConfig.connData.nodename )) ); + + pControlConfig->numRecvs = pControlConfig->ibConfig.numRecvs; + + pControlConfig->inicInstance = pControlConfig->ibConfig.connData.inicInstance; + pControlConfig->maxAddressEntries = (uint16_t)p_adapter->params.MaxAddressEntries; + pControlConfig->minAddressEntries = (uint16_t)p_adapter->params.MinAddressEntries; + pControlConfig->reqRetryCount = (uint8_t)p_adapter->params.ControlReqRetryCount; + pControlConfig->rspTimeout = p_adapter->params.ControlRspTimeout; + + pDataConfig->ibConfig.sid = + 0x10LL << 56 | + 0x00LL << 48 | + 0x06LL << 40 | + 0x6aLL << 32 | + 0x00LL << 24 | + 0x00LL << 16 | + 0x01LL << 8 | + ( (uint8_t)( p_adapter->ioc_info.profile.ioc_guid >> 24 ) & 0xFF ); + + pDataConfig->ibConfig.connData.pathId = get_time_stamp_ms(); + pDataConfig->ibConfig.connData.inicInstance = pControlConfig->inicInstance; + pDataConfig->ibConfig.connData.pathNum = 0; + + pDataConfig->ibConfig.retryCount = p_adapter->params.RetryCount; + pDataConfig->ibConfig.rnrRetryCount = p_adapter->params.RetryCount; + pDataConfig->ibConfig.minRnrTimer = (uint8_t)p_adapter->params.MinRnrTimer; + + /* + * NOTE: The numRecvs size assumes that the EIOC could + * RDMA enough packets to fill all of the host recv + * pool entries, plus send a kick message after each + * packet, plus RDMA new buffers for the size of + * the EIOC recv buffer pool, plus send kick messages + * after each MinHostUpdateSz of new buffers all + * before the Host can even pull off the first completed + * receive off the completion queue, and repost the + * receive. NOT LIKELY! + */ + pDataConfig->ibConfig.numRecvs = p_adapter->params.HostRecvPoolEntries + + ( p_adapter->params.MaxEiocPoolSz / p_adapter->params.MinHostUpdateSz ); + +#if TRUE //defined(LIMIT_OUTSTANDING_SENDS) + + pDataConfig->ibConfig.numSends = (2 * p_adapter->params.NotifyBundleSz ) + + ( p_adapter->params.HostRecvPoolEntries / p_adapter->params.MinEiocUpdateSz ) + 1; + +#else /* !defined(LIMIT_OUTSTANDING_SENDS) */ + /* + * NOTE: The numSends size assumes that the HOST could + * post RDMA sends for every single buffer in the EIOCs + * receive pool, and allocate a full complement of + * receive buffers on the host, and RDMA free buffers + * every MinEiocUpdateSz entries all before the HCA + * can complete a single RDMA transfer. VERY UNLIKELY, + * BUT NOT COMPLETELY IMPOSSIBLE IF THERE IS AN IB + * PROBLEM! + */ + pDataConfig->ibConfig.numSends = p_adapter->params.MaxEiocPoolSz + + ( p_adapter->params.HostRecvPoolEntries / p_adapter->params.MinEiocUpdateSz ) + 1; + +#endif /* !defined(LIMIT_OUTSTANDING_SENDS) */ + + pDataConfig->ibConfig.recvScatter = 1; /* Not configurable */ + pDataConfig->ibConfig.sendGather = MAX_NUM_SGE; /* Not configurable */ + + pDataConfig->numRecvs = pDataConfig->ibConfig.numRecvs; + pDataConfig->pathId = pDataConfig->ibConfig.connData.pathId; + + pDataConfig->hostMin.sizeRecvPoolEntry = + (uint32_t)BUFFER_SIZE(ETH_VLAN_HLEN + p_adapter->params.MinMtu); + pDataConfig->hostMax.sizeRecvPoolEntry = + (uint32_t)BUFFER_SIZE(ETH_VLAN_HLEN + p_adapter->params.MaxMtu); + pDataConfig->eiocMin.sizeRecvPoolEntry = + (uint32_t)BUFFER_SIZE(ETH_VLAN_HLEN + p_adapter->params.MinMtu); + pDataConfig->eiocMax.sizeRecvPoolEntry = MAX_PARAM_VALUE; + + pDataConfig->hostRecvPoolEntries = p_adapter->params.HostRecvPoolEntries; + pDataConfig->notifyBundle = p_adapter->params.NotifyBundleSz; + + pDataConfig->hostMin.numRecvPoolEntries = p_adapter->params.MinHostPoolSz; + pDataConfig->hostMax.numRecvPoolEntries = MAX_PARAM_VALUE; + pDataConfig->eiocMin.numRecvPoolEntries = p_adapter->params.MinEiocPoolSz; + pDataConfig->eiocMax.numRecvPoolEntries = p_adapter->params.MaxEiocPoolSz; + + pDataConfig->hostMin.timeoutBeforeKick = p_adapter->params.MinHostKickTimeout; + pDataConfig->hostMax.timeoutBeforeKick = p_adapter->params.MaxHostKickTimeout; + pDataConfig->eiocMin.timeoutBeforeKick = 0; + pDataConfig->eiocMax.timeoutBeforeKick = MAX_PARAM_VALUE; + + pDataConfig->hostMin.numRecvPoolEntriesBeforeKick = p_adapter->params.MinHostKickEntries; + pDataConfig->hostMax.numRecvPoolEntriesBeforeKick = p_adapter->params.MaxHostKickEntries; + pDataConfig->eiocMin.numRecvPoolEntriesBeforeKick = 0; + pDataConfig->eiocMax.numRecvPoolEntriesBeforeKick = MAX_PARAM_VALUE; + + pDataConfig->hostMin.numRecvPoolBytesBeforeKick = p_adapter->params.MinHostKickBytes; + pDataConfig->hostMax.numRecvPoolBytesBeforeKick = p_adapter->params.MaxHostKickBytes; + pDataConfig->eiocMin.numRecvPoolBytesBeforeKick = 0; + pDataConfig->eiocMax.numRecvPoolBytesBeforeKick = MAX_PARAM_VALUE; + + pDataConfig->hostMin.freeRecvPoolEntriesPerUpdate = p_adapter->params.MinHostUpdateSz; + pDataConfig->hostMax.freeRecvPoolEntriesPerUpdate = p_adapter->params.MaxHostUpdateSz; + pDataConfig->eiocMin.freeRecvPoolEntriesPerUpdate = p_adapter->params.MinEiocUpdateSz; + pDataConfig->eiocMax.freeRecvPoolEntriesPerUpdate = p_adapter->params.MaxEiocUpdateSz; + + VNIC_EXIT( VNIC_DBG_VIPORT ); + return TRUE; +} + +static BOOLEAN config_isValid(ViportConfig_t *pConfig) +{ + UNREFERENCED_PARAMETER( pConfig ); + return TRUE; +} + + +void +viport_cleanup( + viport_t *p_viport ) +{ + VNIC_ENTER( VNIC_DBG_VIPORT ); + + if( p_viport ) + { + InterlockedExchange( &p_viport->p_adapter->carrier, FALSE ); + InterlockedExchange( &p_viport->p_netpath->carrier, FALSE ); + viport_timerStop( p_viport ); + + data_disconnect( &p_viport->data ); + + control_cleanup( &p_viport->control ); + + data_cleanup( &p_viport->data ); + + if( p_viport->macAddresses != NULL ) + { + NdisFreeMemory( p_viport->macAddresses, + p_viport->numMacAddresses * sizeof(Inic_AddressOp_t), 0 ); + } + + cl_timer_destroy( &p_viport->timer ); + + NdisFreeMemory ( p_viport, sizeof(viport_t), 0 ); + } + VNIC_EXIT( VNIC_DBG_VIPORT ); + return; +} + + +void +viport_setPath( + IN viport_t *p_viport, + IN ib_path_rec_t *p_path, + IN uint64_t *p_guid ) +{ + + UNUSED_PARAM( p_guid ); + + VNIC_ENTER( VNIC_DBG_FUNC ); + + VNIC_TRACE( VNIC_DBG_INFO, + ("Using SLID=%d DLID=%d Target:%s\n", + cl_ntoh16( p_path->slid ), + cl_ntoh16( p_path->dlid ), + p_viport->p_adapter->ioc_info.profile.id_string) ); + + p_viport->portGuid = p_path->sgid.unicast.interface_id; + p_viport->iocGuid = *p_guid; + p_viport->port_config.dataConfig.ibConfig.pathInfo = *p_path; + p_viport->port_config.controlConfig.ibConfig.pathInfo = *p_path; + p_viport->port_config.controlConfig.ibConfig.sid = + p_viport->p_adapter->p_svc_entries[0].id; + p_viport->port_config.dataConfig.ibConfig.sid = + p_viport->p_adapter->p_svc_entries[1].id; + + VNIC_EXIT( VNIC_DBG_FUNC ); +} + +BOOLEAN +viport_setParent( + IN viport_t *p_viport, + IN Netpath_t *pNetpath ) +{ + VNIC_ENTER( VNIC_DBG_VIPORT ); + + + if(p_viport->p_netpath != NULL) + { + return FALSE; + } + + p_viport->p_netpath = pNetpath; + + VNIC_EXIT( VNIC_DBG_VIPORT ); + return TRUE; +} + +BOOLEAN +viport_unsetParent( + IN viport_t *p_viport, + IN Netpath_t *pNetpath ) +{ + VNIC_ENTER( VNIC_DBG_VIPORT ); + if( pNetpath->pViport == p_viport ) + viport_free( p_viport ); + VNIC_EXIT( VNIC_DBG_VIPORT ); + return TRUE; +} + +void +viport_free( + IN viport_t *p_viport ) +{ + VNIC_ENTER( VNIC_DBG_VIPORT ); + + viport_timerStop( p_viport ); + viport_disconnect(p_viport); + VNIC_EXIT( VNIC_DBG_VIPORT ); + return; +} + +void +viport_disconnect( + IN viport_t *p_viport ) +{ + VNIC_ENTER( VNIC_DBG_VIPORT ); + + InterlockedExchange( (volatile LONG *)&p_viport->disconnect, TRUE ); + viport_failure( p_viport ); + + VNIC_EXIT( VNIC_DBG_VIPORT ); + return; +} + + +NDIS_STATUS +viport_setLink( + IN viport_t *p_viport, + IN uint8_t flags, + IN uint16_t mtu ) +{ + KIRQL irql; + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + + VNIC_ENTER( VNIC_DBG_VIPORT ); + + if(mtu > data_maxMtu(&p_viport->data)) + { + viport_failure(p_viport); + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("Configuration error. Mtu of %d unsupported by %s\n", mtu, p_viport->p_adapter->name ) ); + return NDIS_STATUS_FAILURE; + } + + KeAcquireSpinLock( &p_viport->lock, &irql ); + + flags &= ( INIC_FLAG_ENABLE_NIC | + INIC_FLAG_ENABLE_MCAST_ALL | + INIC_FLAG_ENABLE_PROMISC ); + + if( (p_viport->newFlags != flags) || + (p_viport->newMtu != mtu)) + { + p_viport->newFlags = flags; + p_viport->newMtu = mtu; + InterlockedOr( &p_viport->updates, NEED_LINK_CONFIG ); + } + KeReleaseSpinLock( &p_viport->lock, irql ); + + status = _viport_process_query( p_viport, TRUE ); + VNIC_EXIT( VNIC_DBG_VIPORT ); + return status; +} + +BOOLEAN +viport_setUnicast( + IN viport_t *p_viport, + IN uint8_t *p_address ) +{ + KIRQL flags; + + VNIC_ENTER( VNIC_DBG_VIPORT ); + + if( !p_viport ) + return FALSE; + + KeAcquireSpinLock( &p_viport->lock, &flags ); + if( p_viport->macAddresses == NULL ) + { + KeReleaseSpinLock( &p_viport->lock, flags ); + return FALSE; + } + if( cl_memcmp(p_viport->macAddresses[UNICAST_ADDR].address, + p_address, MAC_ADDR_LEN ) ) + { + cl_memcpy( p_viport->macAddresses[UNICAST_ADDR].address, + p_address, MAC_ADDR_LEN ); + p_viport->macAddresses[UNICAST_ADDR].operation + = INIC_OP_SET_ENTRY; + InterlockedOr( &p_viport->updates, NEED_ADDRESS_CONFIG ); + } + KeReleaseSpinLock( &p_viport->lock, flags ); + + VNIC_EXIT( VNIC_DBG_VIPORT ); + return TRUE; +} + + +/* Returns flags for state machine operations. */ +NDIS_STATUS +viport_setMulticast( + IN viport_t *p_viport ) +{ + vnic_adapter_t *p_adapter = p_viport->p_adapter; + uint32_t updates = 0; + int i; + KIRQL flags; + NDIS_STATUS status; + + VNIC_ENTER( VNIC_DBG_VIPORT ); + + KeAcquireSpinLock( &p_viport->lock, &flags ); + + if( p_viport->macAddresses == NULL ) + { + KeReleaseSpinLock( &p_viport->lock, flags ); + return NDIS_STATUS_NOT_ACCEPTED; + } + + ASSERT( (p_viport->updates & ~MCAST_OVERFLOW) == 0 ); + + if( p_adapter->mc_count > p_viport->numMacAddresses - MCAST_ADDR_START ) + { + updates |= NEED_LINK_CONFIG | MCAST_OVERFLOW; + } + else if( p_adapter->mc_count == 0 ) + { + /* NDIS can send us a NULL array to clear all entries */ + if( InterlockedAnd( + &p_viport->updates, ~MCAST_OVERFLOW ) & MCAST_OVERFLOW ) + { + updates |= NEED_LINK_CONFIG; + } + /* invalidate all entries for the remote */ + for (i = MCAST_ADDR_START; + i < min( MAX_ADDR_ARRAY, p_viport->numMacAddresses ); i++ ) + { + p_viport->macAddresses[i].valid = 0; + p_viport->macAddresses[i].operation = INIC_OP_SET_ENTRY; + } + updates |= NEED_ADDRESS_CONFIG; + } + else + { + if( InterlockedAnd( + &p_viport->updates, ~MCAST_OVERFLOW ) & MCAST_OVERFLOW ) + { + updates |= NEED_LINK_CONFIG; + } + /* Brute force algorithm */ + for (i = MCAST_ADDR_START; + i < min( MAX_ADDR_ARRAY, p_adapter->mc_count + MCAST_ADDR_START ); + i++ ) + { + if( p_viport->macAddresses[i].valid && + NdisEqualMemory( p_viport->macAddresses[i].address, + p_adapter->mcast_array[i - MCAST_ADDR_START].addr, + MAC_ADDR_LEN ) ) + { + continue; + } + + NdisMoveMemory( &p_viport->macAddresses[i].address, + p_adapter->mcast_array[i - MCAST_ADDR_START].addr, + MAC_ADDR_LEN ); + + p_viport->macAddresses[i].valid = 1; + p_viport->macAddresses[i].operation = INIC_OP_SET_ENTRY; + + updates |= NEED_ADDRESS_CONFIG; + } + for (; i < min( MAX_ADDR_ARRAY, p_viport->numMacAddresses ); i++ ) + { + if( !p_viport->macAddresses[i].valid ) + continue; + + updates |= NEED_ADDRESS_CONFIG; + + p_viport->macAddresses[i].valid = 0; + p_viport->macAddresses[i].operation = INIC_OP_SET_ENTRY; + } + } + + /* + * Now that the mac array is setup, we can set the update bits + * to send the request. + */ + InterlockedOr( &p_viport->updates, updates ); + KeReleaseSpinLock( &p_viport->lock, flags ); + + status = _viport_process_query( p_viport, FALSE ); + + VNIC_EXIT( VNIC_DBG_VIPORT ); + return status; +} + + +NDIS_STATUS +viport_getStats( + IN viport_t *p_viport ) +{ + uint64_t stats_update_ms; + NDIS_STATUS status = STATUS_SUCCESS; + + VNIC_ENTER( VNIC_DBG_VIPORT ); + + stats_update_ms = get_time_stamp_ms(); + + if( stats_update_ms > p_viport->lastStatsTime + p_viport->port_config.statsInterval ) + { + p_viport->lastStatsTime = (uint32_t)stats_update_ms; + + InterlockedOr( &p_viport->updates, NEED_STATS ); + // TODO: Should the following call be synchronous? + status = _viport_process_query( p_viport, TRUE ); + if ( status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("Query NEED_STATS Failed\n") ); + } + } + + VNIC_EXIT( VNIC_DBG_VIPORT ); + return status; +} + + +BOOLEAN +viport_xmitPacket( + IN viport_t* const p_viport, + IN NDIS_PACKET* const p_packet ) +{ + BOOLEAN status = FALSE; + KIRQL flags; + cl_list_item_t *p_list_item; + + VNIC_ENTER( VNIC_DBG_VIPORT ); + + KeAcquireSpinLock( &p_viport->lock, &flags ); + + if( p_viport->p_adapter->xmitStarted ) + { + for( p_list_item = cl_qlist_remove_head( &p_viport->send_pending_list ); + p_list_item != cl_qlist_end( &p_viport->send_pending_list ); + p_list_item = cl_qlist_remove_head( &p_viport->send_pending_list ) ) + { + status = data_xmitPacket( &p_viport->data, + VNIC_PACKET_FROM_LIST_ITEM( p_list_item )); + } + status = data_xmitPacket( &p_viport->data, p_packet ); + } + else + { + cl_qlist_insert_tail( &p_viport->send_pending_list, + VNIC_LIST_ITEM_FROM_PACKET( p_packet ) ); + } + + KeReleaseSpinLock( &p_viport->lock, flags ); + + VNIC_EXIT( VNIC_DBG_VIPORT ); + return status; +} + +void viport_linkUp(viport_t *p_viport) +{ + VNIC_ENTER( VNIC_DBG_VIPORT ); + InterlockedExchange( &p_viport->p_adapter->carrier, TRUE ); + NdisMIndicateStatus( p_viport->p_adapter->h_handle, + NDIS_STATUS_MEDIA_CONNECT, NULL, 0 ); + NdisMIndicateStatusComplete( p_viport->p_adapter->h_handle ); + VNIC_EXIT( VNIC_DBG_VIPORT ); + return; +} + +void viport_linkDown(viport_t *p_viport) +{ + VNIC_ENTER( VNIC_DBG_VIPORT ); + InterlockedExchange( &p_viport->p_adapter->carrier, FALSE ); + NdisMIndicateStatus( p_viport->p_adapter->h_handle, + NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0 ); + NdisMIndicateStatusComplete( p_viport->p_adapter->h_handle ); + VNIC_EXIT( VNIC_DBG_VIPORT ); + return; +} + +void viport_stopXmit(viport_t *p_viport) +{ + VNIC_ENTER( VNIC_DBG_VIPORT ); + netpath_stopXmit( p_viport->p_netpath ); + VNIC_EXIT( VNIC_DBG_VIPORT ); + return; +} + +void viport_restartXmit(viport_t *p_viport) +{ + VNIC_ENTER( VNIC_DBG_VIPORT ); + netpath_restartXmit( p_viport->p_netpath ); + VNIC_EXIT( VNIC_DBG_VIPORT ); + return; +} + +void +viport_recvPacket( + IN viport_t *p_viport, + IN NDIS_PACKET *p_packet ) +{ + VNIC_ENTER( VNIC_DBG_VIPORT ); + netpath_recvPacket(p_viport->p_netpath, p_packet ); + VNIC_EXIT( VNIC_DBG_VIPORT ); + return; +} + + +void +viport_failure( + IN viport_t *p_viport ) +{ + VNIC_ENTER( VNIC_DBG_VIPORT ); + + InterlockedExchange( (volatile LONG*)&p_viport->errored, 1 ); + + VNIC_EXIT( VNIC_DBG_VIPORT ); +} + + +void +viport_timeout( + IN void *context ) +{ + viport_t *p_viport = (viport_t *)context; + + InterlockedExchange( &p_viport->timerActive, FALSE ); + if( p_viport && p_viport->data.connected ) + { + // TODO: What about send failure? + control_heartbeatReq( &p_viport->control, + p_viport->port_config.hbTimeout ); + } +} + + +void +viport_timer( + IN viport_t *p_viport, + IN int timeout ) +{ + VNIC_ENTER( VNIC_DBG_VIPORT );; + + InterlockedExchange( &p_viport->timerActive, TRUE ); + cl_timer_start( &p_viport->timer, (uint32_t)timeout ); + + VNIC_EXIT( VNIC_DBG_VIPORT ); +} + + +void +viport_timerStop( + IN viport_t *p_viport ) +{ + VNIC_ENTER( VNIC_DBG_VIPORT ); + + if( p_viport ) + { + if( InterlockedExchange( &p_viport->timerActive, FALSE ) == TRUE ) + { + cl_timer_stop( &p_viport->timer ); + } + } + + VNIC_EXIT( VNIC_DBG_VIPORT ); + return; +} + +static ib_api_status_t +viport_initMacAddresses( + IN viport_t *p_viport ) +{ + int i, size; + KIRQL flags; + NDIS_STATUS status; + VNIC_ENTER( VNIC_DBG_VIPORT ); + + size = p_viport->numMacAddresses * sizeof(Inic_AddressOp_t); + status = NdisAllocateMemoryWithTag( &p_viport->macAddresses, size , 'acam' ); + + if ( status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("Failed allocating MAC address table size %d\n", size) ); + return IB_INSUFFICIENT_MEMORY; + } + + NdisZeroMemory( p_viport->macAddresses, size ); + + KeAcquireSpinLock( &p_viport->lock, &flags ); + for( i = 0; i < p_viport->numMacAddresses; i++ ) + { + p_viport->macAddresses[i].index = (uint16_t)i; + p_viport->macAddresses[i].vlan = p_viport->defaultVlan; + } + + NdisFillMemory( p_viport->macAddresses[BROADCAST_ADDR].address, + MAC_ADDR_LEN, 0xFF ); + p_viport->macAddresses[BROADCAST_ADDR].valid = TRUE; + + NdisMoveMemory( p_viport->macAddresses[UNICAST_ADDR].address, + p_viport->hwMacAddress, MAC_ADDR_LEN ); + + p_viport->macAddresses[UNICAST_ADDR].valid = TRUE; + p_viport->p_adapter->macSet = TRUE; + + KeReleaseSpinLock( &p_viport->lock, flags ); + + VNIC_EXIT( VNIC_DBG_VIPORT ); + return IB_SUCCESS; +} + + +ib_api_status_t +viport_control_connect( + IN viport_t* const p_viport ) +{ + ib_api_status_t ib_status; + + VNIC_ENTER( VNIC_DBG_VIPORT ); + + ib_status = control_init( &p_viport->control, p_viport, + &p_viport->port_config.controlConfig, p_viport->portGuid ); + if( ib_status != IB_SUCCESS ) + { + VNIC_EXIT( VNIC_DBG_VIPORT ); + return ib_status; + } + + ib_status = ibqp_connect( &p_viport->control.qp ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_INFO, + ("control QP connect failed\n")); + goto err1; + } + + InterlockedExchange( (volatile LONG*)&p_viport->linkState, + (LONG)LINK_INITINICREQ ); + + ib_status = control_initInicReq( &p_viport->control ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_INFO, ("CMD_INIT_INIC REQ failed\n") ); + goto err2; + } + cl_event_wait_on( &p_viport->conn_event, + (p_viport->control.p_conf->rspTimeout << 11), TRUE ); + + if( p_viport->linkState != LINK_INITINICRSP ) + { + VNIC_TRACE( VNIC_DBG_INFO, + ("CMD_INIT_INIC RSP failed\n")); + ib_status = IB_INSUFFICIENT_RESOURCES; + goto err2; + } + + vnic_resume_oids( p_viport->p_adapter ); + + ib_status = viport_initMacAddresses( p_viport ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_INFO, + ("Init MAC Addresses failed\n")); +err2: + control_resetReq( &p_viport->control ); +err1: + control_cleanup( &p_viport->control ); + } + + VNIC_EXIT( VNIC_DBG_VIPORT ); + return ib_status; +} + +ib_api_status_t +viport_data_connect( + IN viport_t* const p_viport ) +{ + NDIS_STATUS status; + ib_api_status_t ib_status; + + VNIC_ENTER( VNIC_DBG_VIPORT ); + + ib_status = data_init( &p_viport->data, + &p_viport->port_config.dataConfig, + p_viport->portGuid ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, ("Data init returned %s\n", + p_viport->p_adapter->ifc.get_err_str( ib_status )) ); + return ib_status; + } + InterlockedExchange( (volatile LONG*)&p_viport->linkState, + (LONG)LINK_CONFIGDATAPATHREQ ); + + ib_status = control_configDataPathReq( &p_viport->control, + data_pathId(&p_viport->data ), data_hostPoolMax( &p_viport->data ), + data_eiocPoolMax( &p_viport->data ) ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("command CONFIGDATAPATH REQ failed\n")); + return ib_status; + } + cl_event_wait_on( &p_viport->conn_event, + (p_viport->control.p_conf->rspTimeout << 11), TRUE ); + + if( p_viport->linkState != LINK_CONFIGDATAPATHRSP ) + { + VNIC_TRACE( VNIC_DBG_ERROR, + ("failed to get CONFIGDATAPATH RSP\n")); + return IB_INSUFFICIENT_RESOURCES; + } + + ib_status = data_connect( &p_viport->data ); + if( ib_status != IB_SUCCESS ) + { + VNIC_EXIT( VNIC_DBG_VIPORT ); + return ib_status; + } + cl_event_wait_on( &p_viport->conn_event, + (p_viport->control.p_conf->rspTimeout << 11), TRUE ); + if( p_viport->data.qp.qpState != IB_ATTACHED ) + { + VNIC_EXIT( VNIC_DBG_VIPORT ); + return IB_ERROR; + } + InterlockedExchange( (volatile LONG*)&p_viport->linkState, + (LONG)LINK_XCHGPOOLREQ ); + ib_status = control_exchangePoolsReq( &p_viport->control, + data_localPoolAddr(&p_viport->data), + data_localPoolRkey(&p_viport->data) ); + if( ib_status != IB_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("command XCHGPOOL REQ failed\n")); + return ib_status; + } + cl_event_wait_on( &p_viport->conn_event, + (p_viport->control.p_conf->rspTimeout << 11), TRUE ); + + if( p_viport->linkState != LINK_XCHGPOOLRSP ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("failed to get LINK_XCHGPOOL RSP\n")); + return IB_ERROR; + } + + InterlockedExchange( (volatile LONG*)&p_viport->linkState, + (LONG)LINK_INITIALIZED ); + p_viport->state = VIPORT_CONNECTED; + + data_connected(&p_viport->data); + + InterlockedExchange( (volatile LONG *)&p_viport->mtu, 1500 ); + InterlockedExchange( (volatile LONG *)&p_viport->flags, 0 ); + status = viport_setLink( p_viport, INIC_FLAG_ENABLE_NIC, 1500 ); + if( status != NDIS_STATUS_SUCCESS ) + { + VNIC_TRACE_EXIT( VNIC_DBG_ERROR, + ("failed to set Link flags\n")); + return IB_ERROR; + } + + /* now we have to start periodic heartbeat timer */ + if( p_viport->port_config.hbInterval ) + { + viport_timer( p_viport, p_viport->port_config.hbInterval ); + } + + VNIC_EXIT( VNIC_DBG_VIPORT ); + return IB_SUCCESS; +} + + +NDIS_STATUS +_viport_process_query( + IN viport_t* const p_viport, + IN BOOLEAN sync ) +{ + NDIS_STATUS status; + ib_api_status_t ib_status; + KIRQL irql; + LinkState_t expected_state = 0; + + VNIC_ENTER( VNIC_DBG_VIPORT ); + + if ( p_viport->state != VIPORT_CONNECTED || + p_viport->errored != 0 ) + { + VNIC_TRACE_EXIT( VNIC_DBG_WARN, ("Invalid state or error.\n") ); + return NDIS_STATUS_NOT_ACCEPTED; + } + + // Check for updates. Note that unless sync is set to TRUE, this + // is the only way for this function to return success. + if( !InterlockedCompareExchange( &p_viport->updates, 0, 0 ) ) + { + VNIC_TRACE_EXIT( VNIC_DBG_VIPORT, ("No updates.\n") ); + return NDIS_STATUS_SUCCESS; + } + + if( sync ) + { + status = NDIS_STATUS_SUCCESS; + InterlockedOr( &p_viport->updates, SYNC_QUERY ); + } + else + { + status = NDIS_STATUS_PENDING; + } + + // Handle update bits one at a time. + if( p_viport->updates & NEED_ADDRESS_CONFIG ) + { + VNIC_TRACE( VNIC_DBG_INFO, + ("QUERY NEED_ADDRESS_CONFIG\n")); + KeAcquireSpinLock(&p_viport->lock, &irql ); + p_viport->linkState = LINK_CONFIGADDRSREQ; + ib_status = control_configAddrsReq( + &p_viport->control, p_viport->macAddresses, + p_viport->numMacAddresses, &p_viport->addrs_query_done ); + KeReleaseSpinLock( &p_viport->lock, irql ); + if ( ib_status != IB_SUCCESS ) + { + InterlockedAnd( &p_viport->updates, ~NEED_ADDRESS_CONFIG ); + VNIC_EXIT( VNIC_DBG_VIPORT ); + return NDIS_STATUS_FAILURE; + } + expected_state = LINK_CONFIGADDRSRSP; + } + else if( p_viport->updates & NEED_LINK_CONFIG ) + { + VNIC_TRACE( VNIC_DBG_INFO, + ("QUERY NEED_LINK_CONFIG\n")); + + KeAcquireSpinLock(&p_viport->lock, &irql ); + p_viport->linkState = LINK_CONFIGLINKREQ; + + if( (InterlockedAnd( + &p_viport->updates, ~MCAST_OVERFLOW ) & MCAST_OVERFLOW) ) + { + p_viport->newFlags |= INIC_FLAG_ENABLE_MCAST_ALL; + } + else + { + p_viport->newFlags &= ~INIC_FLAG_ENABLE_MCAST_ALL; + } + + if ( p_viport->mtu != p_viport->newMtu ) + p_viport->mtu = p_viport->newMtu; + + ib_status = control_configLinkReq( &p_viport->control, + p_viport->newFlags, p_viport->mtu ); + KeReleaseSpinLock( &p_viport->lock, irql ); + if( ib_status != IB_SUCCESS ) + { + InterlockedAnd( &p_viport->updates, ~NEED_LINK_CONFIG ); + VNIC_EXIT( VNIC_DBG_VIPORT ); + return NDIS_STATUS_FAILURE; + } + expected_state = LINK_CONFIGLINKRSP; + } + else if( p_viport->updates & NEED_STATS ) + { + // TODO: This is dead code. + VNIC_TRACE( VNIC_DBG_INFO, + ("QUERY NEED_STATS\n")); + + KeAcquireSpinLock( &p_viport->lock, &irql ); + p_viport->linkState = LINK_REPORTSTATREQ; + + ib_status = control_reportStatisticsReq( &p_viport->control ); + KeReleaseSpinLock( &p_viport->lock, irql ); + if( ib_status != IB_SUCCESS ) + { + InterlockedAnd( &p_viport->updates, ~NEED_STATS ); + VNIC_EXIT( VNIC_DBG_VIPORT ); + return NDIS_STATUS_FAILURE; + } + expected_state = LINK_REPORTSTATRSP; + } + + if( sync ) + { + cl_event_wait_on( &p_viport->conn_event, EVENT_NO_TIMEOUT, FALSE ); + + if( p_viport->linkState != expected_state ) + { + status = NDIS_STATUS_FAILURE; + VNIC_TRACE( VNIC_DBG_ERROR, + ("Link state error: expected %d but got %d\n", + expected_state, p_viport->linkState)); + } + } + VNIC_EXIT( VNIC_DBG_VIPORT ); + return status; +} + diff --git a/trunk/ulp/inic/kernel/vnic_viport.h b/trunk/ulp/inic/kernel/vnic_viport.h new file mode 100644 index 00000000..529d1244 --- /dev/null +++ b/trunk/ulp/inic/kernel/vnic_viport.h @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2005 SilverStorm Technologies. All rights reserved. + * + * This software is available to you under the OpenIB.org BSD license + * below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ +#ifndef _VNIC_VIPORT_H_ +#define _VNIC_VIPORT_H_ + +typedef struct _mc_list { + uint8_t mc_addr[MAC_ADDR_LEN]; +} mc_list_t; + +typedef enum { + VIPORT_DISCONNECTED, + VIPORT_CONNECTED +} viport_state_t; + +typedef enum { + LINK_UNINITIALIZED, + LINK_INITIALIZE, + LINK_INITIALIZECONTROL, + LINK_INITIALIZEDATA, + LINK_CONTROLCONNECT, + LINK_CONTROLCONNECTWAIT, + LINK_INITINICREQ, + LINK_INITINICRSP, + LINK_BEGINDATAPATH, + LINK_CONFIGDATAPATHREQ, + LINK_CONFIGDATAPATHRSP, + LINK_DATACONNECT, + LINK_DATACONNECTWAIT, + LINK_XCHGPOOLREQ, + LINK_XCHGPOOLRSP, + LINK_INITIALIZED, + LINK_IDLE, + LINK_IDLING, + LINK_CONFIGLINKREQ, + LINK_CONFIGLINKRSP, + LINK_CONFIGADDRSREQ, + LINK_CONFIGADDRSRSP, + LINK_REPORTSTATREQ, + LINK_REPORTSTATRSP, + LINK_HEARTBEATREQ, + LINK_HEARTBEATRSP, + LINK_RESET, + LINK_RESETRSP, + LINK_RESETCONTROL, + LINK_RESETCONTROLRSP, + LINK_DATADISCONNECT, + LINK_CONTROLDISCONNECT, + LINK_CLEANUPDATA, + LINK_CLEANUPCONTROL, + LINK_DISCONNECTED, + LINK_RETRYWAIT +} LinkState_t; + +/* index entries */ +#define BROADCAST_ADDR 0 +#define UNICAST_ADDR 1 +#define MCAST_ADDR_START 2 +#define MAX_MCAST (MAX_ADDR_ARRAY - MCAST_ADDR_START) + +#define currentMacAddress macAddresses[UNICAST_ADDR].address + +#define NEED_STATS 0x00000001 +#define NEED_ADDRESS_CONFIG 0x00000002 +#define NEED_LINK_CONFIG 0x00000004 +#define MCAST_OVERFLOW 0x00000008 +#define SYNC_QUERY 0x80000000 + +typedef enum { + NETPATH_TS_IDLE, + NETPATH_TS_ACTIVE, + NETPATH_TS_EXPIRED +} netpathTS_t; + + +typedef struct { + LIST_ENTRY listPtrs; + struct _vnic_adapter *p_adapter; + uint8_t event_num; +} InicNPEvent_t; + +typedef enum { + INICNP_CONNECTED = 0, + INICNP_DISCONNECTED, + INICNP_LINKUP, + INICNP_LINKDOWN, + INICNP_TIMEREXPIRED, + INICNP_UNIVERSAL1, + /* SECONDARYOFFSET MUST ALWAYS COME AT THE END */ + INICNP_SECONDARYOFFSET +} InicNPEventPos_t; + +#define INICNP_NUM_EVENTS (2 * INICNP_SECONDARYOFFSET) + +#define INIC_PRINP_CONNECTED INICNP_CONNECTED +#define INIC_PRINP_DISCONNECTED INICNP_DISCONNECTED +#define INIC_PRINP_LINKUP INICNP_LINKUP +#define INIC_PRINP_LINKDOWN INICNP_LINKDOWN +#define INIC_PRINP_TIMEREXPIRED INICNP_TIMEREXPIRED +#define INIC_NP_SETLINK INICNP_UNIVERSAL1 + +#define INIC_SECNP_CONNECTED (INICNP_CONNECTED + INICNP_SECONDARYOFFSET) +#define INIC_SECNP_DISCONNECTED (INICNP_DISCONNECTED + INICNP_SECONDARYOFFSET) +#define INIC_SECNP_LINKUP (INICNP_LINKUP + INICNP_SECONDARYOFFSET) +#define INIC_SECNP_LINKDOWN (INICNP_LINKDOWN + INICNP_SECONDARYOFFSET) +#define INIC_SECNP_TIMEREXPIRED (INICNP_TIMEREXPIRED + INICNP_SECONDARYOFFSET) +#define INIC_NP_FREEINIC (INICNP_UNIVERSAL1 + INICNP_SECONDARYOFFSET) + + +typedef struct Netpath { + volatile LONG carrier; + struct _vnic_adapter *p_adapter; + struct _viport *pViport; + size_t pathIdx; + uint64_t connectTime; + netpathTS_t timerState; +} Netpath_t; + +typedef enum { + WAIT, + DELAY, + NOW +} conn_wait_state_t; + +typedef struct _viport { + LIST_ENTRY listPtrs; + KSPIN_LOCK lock; + cl_obj_t obj; + cl_qlist_t send_pending_list; + struct _vnic_adapter *p_adapter; + struct Netpath *p_netpath; + struct ViportConfig port_config; + struct Control control; + struct Data data; + uint64_t iocGuid; + uint64_t portGuid; + + // connected/disconnected state of control and data QPs. + viport_state_t state; + + // State machine state? + LinkState_t linkState; + + Inic_CmdReportStatisticsRsp_t stats; + uint64_t lastStatsTime; + uint32_t featuresSupported; + uint8_t hwMacAddress[MAC_ADDR_LEN]; + uint16_t defaultVlan; + uint16_t numMacAddresses; + Inic_AddressOp_t *macAddresses; + int32_t addrs_query_done; + + // Indicates actions (to the VEx) that need to be taken. + volatile LONG updates; + // ??? + uint8_t flags; + // TODO: Can we eliminate newFlags? + uint8_t newFlags; + + uint16_t mtu; + uint16_t newMtu; + uint32_t errored; + uint32_t disconnect; + volatile LONG timerActive; + cl_timer_t timer; + cl_event_t conn_event; + +} viport_t; + + +BOOLEAN +viport_xmitPacket( + viport_t* const p_viport, + NDIS_PACKET* const p_pkt ); + +BOOLEAN +viport_config_defaults( + IN viport_t *p_viport ); + +uint32_t +viport_get_adapter_name( + IN viport_t *p_viport ); + +void viport_cleanup(viport_t *p_viport ); +void viport_setPath(viport_t *pViport, ib_path_rec_t *path, uint64_t *guid); +BOOLEAN viport_setParent(viport_t *pViport, struct Netpath *pNetpath); +BOOLEAN viport_unsetParent(viport_t *pViport, struct Netpath *pNetpath); +void viport_free(viport_t *pViport); +void viport_disconnect(viport_t *pViport); +NDIS_STATUS viport_setLink(viport_t *pViport, uint8_t flags, uint16_t mtu); +NDIS_STATUS viport_getStats(viport_t *pViport ); + +void viport_timer( viport_t *p_viport, int timeout ); +void viport_timerStop( viport_t *p_viport ); + +void viport_linkUp(viport_t *pViport); +void viport_linkDown(viport_t *pViport); +void viport_stopXmit(viport_t *pViport); +void viport_restartXmit(viport_t *pViport); +void viport_recvPacket(viport_t *pViport, NDIS_PACKET *pkt ); +void viport_failure(viport_t *pViport); +BOOLEAN viport_setUnicast(viport_t *pViport, uint8_t *pAddress); +NDIS_STATUS viport_setMulticast( viport_t *pViport ); +#define viport_portGuid(pViport) ((pViport)->portGuid) +#define viport_maxMtu(pViport) data_maxMtu(&(pViport)->data) + +#define viport_getHwAddr(pViport,pAddress) \ + cl_memcpy(pAddress, (pViport)->hwMacAddress, MAC_ADDR_LEN) + +#define viport_features(pViport) ((pViport)->featuresSupported) +#define viport_canTxCsum(pViport) (((pViport)->featuresSupported & \ + (INIC_FEAT_IPV4_CSUM_TX|INIC_FEAT_TCP_CSUM_TX|INIC_FEAT_UDP_CSUM_TX)) \ + == (INIC_FEAT_IPV4_CSUM_TX|INIC_FEAT_TCP_CSUM_TX|INIC_FEAT_UDP_CSUM_TX)) + + +void netpath_init( struct Netpath *pNetpath, struct _vnic_adapter *p_adapter ); +BOOLEAN netpath_addPath(struct Netpath *pNetpath, viport_t *pViport); +BOOLEAN netpath_removePath(struct Netpath *pNetpath, viport_t *pViport); +BOOLEAN netpath_getStats(struct Netpath *pNetpath ); +BOOLEAN netpath_setUnicast(struct Netpath *pNetpath, uint8_t *pAddress); +BOOLEAN netpath_setMulticast(struct Netpath *pNetpath, mc_list_t *mc_list, + int mc_count); +int netpath_maxMtu(struct Netpath *pNetpath); + +BOOLEAN +netpath_xmitPacket( + struct Netpath* pNetpath, + NDIS_PACKET* const p_pkt ); +void +netpath_recvPacket( + struct Netpath* pNetpath, + NDIS_PACKET* p_pkt ); + +void netpath_stopXmit(struct Netpath *pNetpath ); +void netpath_restartXmit(struct Netpath *pNetpath ); + +void netpath_kick(struct Netpath *pNetpath); +void netpath_timer(struct Netpath *pNetpath, int timeout); +void netpath_tx_timeout(struct Netpath *pNetpath); + +const char * netpath_to_string(struct _vnic_adapter *p_adapter, struct Netpath *pNetpath); +#define netpath_getHwAddr(pNetpath, pAddress) viport_getHwAddr((pNetpath)->pViport, pAddress) +#define netpath_isConnected(pNetpath) (pNetpath->state == NETPATH_CONNECTED) +#define netpath_canTxCsum(pNetpath) viport_canTxCsum(pNetpath->pViport) + +ib_api_status_t +viport_control_connect( + IN viport_t* const p_viport ); +ib_api_status_t +viport_data_connect( + IN viport_t* const p_viport ); + +NDIS_STATUS +_viport_process_query( + IN viport_t* const p_viport, + IN BOOLEAN sync ); + + +#endif /* _VNIC_VIPORT_H_ */ -- 2.41.0