diff options
Diffstat (limited to 'private/ntos/ndis/testprot/tpdrvr/perf.c')
-rw-r--r-- | private/ntos/ndis/testprot/tpdrvr/perf.c | 2162 |
1 files changed, 2162 insertions, 0 deletions
diff --git a/private/ntos/ndis/testprot/tpdrvr/perf.c b/private/ntos/ndis/testprot/tpdrvr/perf.c new file mode 100644 index 000000000..255494430 --- /dev/null +++ b/private/ntos/ndis/testprot/tpdrvr/perf.c @@ -0,0 +1,2162 @@ +// ------------------------------------------------ +// +// Copyright (c) 1990 Microsoft Corporation +// +// Module Name: +// +// perf.c +// +// Abstract: +// +// This file contains the source for the netcard performance tests +// +// Author: +// +// Tim Wynsma (timothyw) 4-27-1994 +// +// Environment: +// +// Kernel mode +// +// +// Changes: +// 5-18-1994 (timothyw) +// Requested changes to performance function and output (part 1) +// 6-08-1994 (timothyw) +// Changed perf tests to client/server model (part 2) +// +// -------------------------------------------------- + + +#include <ndis.h> + +#include "tpdefs.h" +#include "media.h" +#include "tpprocs.h" +#include "string.h" + + +// +// local constants.. +// + +#define MINIMUM_PERF_PACKET 60 +#define PACKETS_PER_BURST 5 + +#define PERFMODE_SEND 0 // client sends to any address +#define PERFMODE_SENDTOSRV 1 // client sends to server +#define PERFMODE_SENDWITHACK 2 // client sends to server, server ACKS +#define PERFMODE_SENDANDRCV 3 // client sends to server, server sends to client +#define PERFMODE_RECEIVE 4 // server sends to client +#define PERFMODE_REQANDRCV 5 // server sends to client when get REQ message +#define PERFMODE_SHUTDOWN 6 // client shuts down server + +#define REQ_DATA 0 +#define REQ_INITGO 1 +#define REQ_ACK 2 +#define REQ_RES 3 + +// +// defines for the packet signature of each type, and for the packet +// types themselves +// + +// "ID" portion of packet signature. This is added to base to get actual signature + +#define PERF_DATA_ID 0x00000000 // test data message +#define PERF_ACKREQ_ID 0x00000001 // ACK or REQ message +#define PERF_START_ID 0x00000002 // INIT or GO message +#define PERF_DONE_ID 0x00000003 // REQRES or SRVDONE message +#define PERF_STOP_ID 0x00000004 // STOPSRV or SRVDOWN message +#define PERF_NOGO_ID 0x00000005 // NOGO message +#define PERF_RESULTS_ID 0x00000006 // RETRES message +#define PERF_ID_MASK 0x00000007 // mask for ID + +#define PERF_BASE 0x76543210 // base signature +#define PERF_SERVER 0x00000008 // offset from base of server ids + +// signatures used by client (messages sent to server) + +#define PERF_CLTDATA_SIGNATURE (PERF_BASE + PERF_DATA_ID) +#define PERF_REQ_SIGNATURE (PERF_BASE + PERF_ACKREQ_ID) +#define PERF_INIT_SIGNATURE (PERF_BASE + PERF_START_ID) +#define PERF_REQRES_SIGNATURE (PERF_BASE + PERF_DONE_ID) +#define PERF_STOPSRV_SIGNATURE (PERF_BASE + PERF_STOP_ID) + +// signatures used by server (messages sent to client) + +#define PERF_SRVDATA_SIGNATURE (PERF_BASE + PERF_SERVER + PERF_DATA_ID) +#define PERF_ACK_SIGNATURE (PERF_BASE + PERF_SERVER + PERF_ACKREQ_ID) +#define PERF_GO_SIGNATURE (PERF_BASE + PERF_SERVER + PERF_START_ID) +#define PERF_SRVDONE_SIGNATURE (PERF_BASE + PERF_SERVER + PERF_DONE_ID) +#define PERF_SRVDOWN_SIGNATURE (PERF_BASE + PERF_SERVER + PERF_STOP_ID) +#define PERF_NOGO_SIGNATURE (PERF_BASE + PERF_SERVER + PERF_NOGO_ID) +#define PERF_RETRES_SIGNATURE (PERF_BASE + PERF_SERVER + PERF_RESULTS_ID) + +// +// structures of special (info) packet types used by performance tests +// structures MUST be packed + +#include <packon.h> + +typedef struct _INFO_PACKET_INFO +{ + ULONG Signature; + ULONG PacketSize; + ULONG Mode; + ULONG Length; + ULONG Count; + ULONG Delay; + UCHAR Address[ADDRESS_LENGTH]; + ULONG CheckSum; +} INFO_PACKET_INFO; + +typedef INFO_PACKET_INFO UNALIGNED *PINFO_PACKET_INFO; + + +typedef struct _RESULTS_PACKET_INFO +{ + ULONG Signature; + ULONG PacketSize; + ULONG PacketsSent; + ULONG SendErrors; + ULONG PacketsReceived; + ULONG ElapsedTime; + ULONG SelfReceives; + ULONG Restarts; + ULONG CheckSum; +} RESULTS_PACKET_INFO; +typedef RESULTS_PACKET_INFO UNALIGNED *PRESULTS_PACKET_INFO; + + +typedef struct _DATA_PACKET_INFO +{ + ULONG Signature; + ULONG PacketSize; + ULONG CheckSum; +} DATA_PACKET_INFO; + +typedef DATA_PACKET_INFO UNALIGNED *PDATA_PACKET_INFO; + +typedef struct _PERF_PACKET +{ + MEDIA_HEADER media; + union + { + INFO_PACKET_INFO info; + DATA_PACKET_INFO data; + RESULTS_PACKET_INFO results; + } u; +} PERF_PACKET; +typedef PERF_PACKET UNALIGNED *PPERF_PACKET; + +#include <packoff.h> + +// +// local functions +// + +PTP_REQUEST_HANDLE +TpPerfAllocatePacket( IN POPEN_BLOCK OpenP, + IN ULONG PacketSize ); + +NDIS_STATUS +TpPerfInitialize( IN OUT POPEN_BLOCK OpenP); + +VOID +TpPerfDeallocate( IN OUT POPEN_BLOCK OpenP); + + +NDIS_STATUS +TpPerfSend( IN POPEN_BLOCK OpenP ); + +VOID +TpPerfSendDpc( IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SysArg1, + IN PVOID SysArg2 ); + +VOID +TpPerfRestart( IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SysArg1, + IN PVOID SysArg2 ); + +VOID +TpPerformEndDpc( IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SysArg1, + IN PVOID SysArg2 ); + +VOID +TpPerfWriteResults( IN PPERF_BLOCK Perform, + IN PRESULTS_PACKET_INFO Info); + + +VOID +TpPerfSetPacketData(POPEN_BLOCK OpenP, + PUCHAR TmpBuf, + ULONG Signature, + PUCHAR DestAddr, + ULONG PacketSize); + +VOID +TpPerfTestCompleted(PPERF_BLOCK Perform); + + +NDIS_STATUS +TpPerfLowPriorityReceive( POPEN_BLOCK OpenP, + PINFO_PACKET_INFO ReceivePacketInfo, + ULONG MessageId); + + +// --------------------------------------------------------------- +// +// Function: TpPerfServer +// +// Arguments: OpenP -- pointer to open block for instance +// +// Returns: Completion status +// +// Descript: This function starts up the PerformServer command +// +// --------------------------------------------------------------- + +NDIS_STATUS +TpPerfServer( POPEN_BLOCK OpenP ) +{ + NDIS_STATUS Status; + PTP_REQUEST_HANDLE RequestHandle; + + // + // Allocate the performance structure, and do necessary initializations + // + + Status = TpPerfInitialize(OpenP); + if (Status != NDIS_STATUS_SUCCESS) + { + return Status; + } + OpenP->Perform->IsServer = TRUE; + OpenP->Perform->MaskId = PERF_BASE; + + // + // yes, I know that OpenP->Perform->ServerAddress is 00-00-00-00-00-00 + // at this point + // + RequestHandle = TpPerfAllocatePacket( OpenP, MINIMUM_PERF_PACKET); + if (RequestHandle == NULL) + { + TpPerfDeallocate(OpenP); + return NDIS_STATUS_RESOURCES; + } + OpenP->Perform->GoInitReq = RequestHandle; + + RequestHandle = TpPerfAllocatePacket( OpenP, MINIMUM_PERF_PACKET); + if (RequestHandle == NULL) + { + TpPerfDeallocate(OpenP); + return NDIS_STATUS_RESOURCES; + } + OpenP->Perform->AckReq = RequestHandle; + + RequestHandle = TpPerfAllocatePacket( OpenP, MINIMUM_PERF_PACKET); + if (RequestHandle == NULL) + { + TpPerfDeallocate(OpenP); + return NDIS_STATUS_RESOURCES; + } + OpenP->Perform->ResReq = RequestHandle; + + TpAddReference( OpenP ); + + return NDIS_STATUS_PENDING; +} + + +// --------------------------------------------------------------- +// +// Function: TpPerfClient +// +// Arguments: OpenP -- pointer to open block for instance +// CmdArgs -- Arguments given in tpctl to PerformClient command +// +// Returns: Completion status +// +// Descript: This function starts up the PerformClient command +// +// --------------------------------------------------------------- + + +NDIS_STATUS +TpPerfClient( POPEN_BLOCK OpenP, + PCMD_ARGS CmdArgs ) +{ + PUCHAR p, q, s, t; + ULONG i; + PTP_REQUEST_HANDLE RequestHandle; + NDIS_STATUS Status; + PPERF_BLOCK Perform; + PPERF_RESULTS OutputBuffer; + + // + // Allocate the performance structure, and do necessary initializations + // + + Status = TpPerfInitialize(OpenP); + if (Status != NDIS_STATUS_SUCCESS) + { + return Status; + } + Perform = OpenP->Perform; + Perform->IsServer = FALSE; + Perform->MaskId = PERF_BASE + PERF_SERVER; + + // + // set so no data is avail if aborted + // + OutputBuffer = MmGetSystemAddressForMdl( Perform->PerformIrp->MdlAddress ); + OutputBuffer->ResultsExist = FALSE; + + // Now, deal with the arguments.. + + Perform->NumberOfPackets = CmdArgs->ARGS.TPPERF.PerfNumPackets; + Perform->PacketDelay = CmdArgs->ARGS.TPPERF.PerfDelay; + Perform->PerformMode = CmdArgs->ARGS.TPPERF.PerfMode; + + if ( CmdArgs->ARGS.TPPERF.PerfPacketSize > OpenP->Media->MaxPacketLen ) + { + Perform->PacketSize = OpenP->Media->MaxPacketLen; + IF_TPDBG ( TP_DEBUG_IOCTL_ARGS ) + { + TpPrint1("TpPerfClient: Invalid PacketSize, using %d\n", Perform->PacketSize); + } + } + else if ( CmdArgs->ARGS.TPPERF.PerfPacketSize < MINIMUM_PERF_PACKET ) + { + Perform->PacketSize = MINIMUM_PERF_PACKET; + IF_TPDBG ( TP_DEBUG_IOCTL_ARGS ) + { + TpPrint1("TpPerfClient: Invalid PacketSize, using %d\n", Perform->PacketSize); + } + } + else + { + Perform->PacketSize = CmdArgs->ARGS.TPPERF.PerfPacketSize; + } + + p = Perform->ServerAddress; + q = CmdArgs->ARGS.TPPERF.PerfServerAddr; + s = Perform->ClientAddress; + t = CmdArgs->ARGS.TPPERF.PerfSendAddr; + + + for ( i=0 ; i < OpenP->Media->AddressLen; i++ ) + { + *p++ = *q++; + *s++ = *t++; + } + + // + // only PERFMODE_SEND does not use a info packet (it assumes its sending to + // never-never land) + // + + if (Perform->PerformMode != PERFMODE_SEND) + { + // + // NULL_ADDRESS is not a valid server address + // + if ( RtlCompareMemory( Perform->ServerAddress, + NULL_ADDRESS, + OpenP->Media->AddressLen ) == OpenP->Media->AddressLen ) + { + TpPrint0("TpPerfClient: server address may not equal NULL_ADDRESS\n"); + TpPerfDeallocate(OpenP); + return NDIS_STATUS_FAILURE; + } + + Perform->WhichReq = REQ_INITGO; + + // + // Set up the info send packet and request. + // + + RequestHandle = TpPerfAllocatePacket( OpenP, MINIMUM_PERF_PACKET); + if (RequestHandle == NULL) + { + TpPerfDeallocate(OpenP); + return NDIS_STATUS_RESOURCES; + } + Perform->GoInitReq = RequestHandle; + + TpPerfSetPacketData(OpenP, + RequestHandle->u.PERF_REQ.Buffer, + ((Perform->PerformMode < PERFMODE_SHUTDOWN) + ? PERF_INIT_SIGNATURE + : PERF_STOPSRV_SIGNATURE), + Perform->ServerAddress, + MINIMUM_PERF_PACKET); + + // + // if needed, Set up the data request send packet and request. + // + if (Perform->PerformMode == PERFMODE_REQANDRCV) + { + RequestHandle = TpPerfAllocatePacket( OpenP, MINIMUM_PERF_PACKET); + if (RequestHandle == NULL) + { + TpPerfDeallocate(OpenP); + return NDIS_STATUS_RESOURCES; + } + Perform->AckReq = RequestHandle; + + TpPerfSetPacketData(OpenP, + RequestHandle->u.PERF_REQ.Buffer, + PERF_REQ_SIGNATURE, + Perform->ServerAddress, + MINIMUM_PERF_PACKET); + } + + // + // If needed, set up the request server results send packet and request. + // + + if (Perform->PerformMode < PERFMODE_SHUTDOWN) + { + RequestHandle = TpPerfAllocatePacket( OpenP, MINIMUM_PERF_PACKET); + if (RequestHandle == NULL) + { + TpPerfDeallocate(OpenP); + return NDIS_STATUS_RESOURCES; + } + Perform->ResReq = RequestHandle; + + TpPerfSetPacketData(OpenP, + RequestHandle->u.PERF_REQ.Buffer, + PERF_REQRES_SIGNATURE, + Perform->ServerAddress, + MINIMUM_PERF_PACKET); + } + } + else + { + Perform->WhichReq = REQ_DATA; + } + + // + // if necessary, set up the data send packet and request + // + + if (Perform->PerformMode <= PERFMODE_SENDANDRCV) + { + RequestHandle = TpPerfAllocatePacket( OpenP, Perform->PacketSize); + if (RequestHandle == NULL) + { + TpPerfDeallocate(OpenP); + return NDIS_STATUS_RESOURCES; + } + Perform->DataReq = RequestHandle; + + TpPerfSetPacketData(OpenP, + RequestHandle->u.PERF_REQ.Buffer, + PERF_CLTDATA_SIGNATURE, + Perform->ServerAddress, + Perform->PacketSize); + + if (Perform->PerformMode == PERFMODE_SENDWITHACK) + { + Perform->SendBurstCount = PACKETS_PER_BURST; + } + else + { + Perform->SendBurstCount = Perform->NumberOfPackets+1; + } + Perform->ReceiveBurstCount = Perform->NumberOfPackets+1; + } + else if (Perform->PerformMode == PERFMODE_REQANDRCV) + { + Perform->ReceiveBurstCount = PACKETS_PER_BURST; + } + else + { + Perform->ReceiveBurstCount = Perform->NumberOfPackets+1; + } + + TpAddReference( OpenP ); + + // + // We will be probably be sending more than one packet, so queue TpPerfSendDpc + // and return Pending to the user, the DPC will send the packets, + // and after all the packets have been sent complete the request. + // + + if ( !KeInsertQueueDpc( &Perform->PerformSendDpc, NULL, NULL )) + { + IF_TPDBG ( TP_DEBUG_DPC ) + { + TpPrint0("TpPerfSend failed to queue the TpPerfSendDpc.\n"); + } + + NdisAcquireSpinLock( &OpenP->SpinLock ); + if ( Perform->PerformIrp != NULL ) + { + Perform->PerformIrp->IoStatus.Status = NDIS_STATUS_FAILURE; + } + NdisReleaseSpinLock( &OpenP->SpinLock ); + + TpPerfDeallocate(OpenP); + return NDIS_STATUS_FAILURE; + } + return NDIS_STATUS_PENDING; +} + + + +// --------------------------------------------- +// +// Function: TpPerfInitialize +// +// Arguments: OpenP -- ptr to current open instance +// +// Returns: Status +// +// Descript: This function allocates the PERF_BLOCK structure, and initializes +// necessary components of it.. +// +// --------------------------------------------- + +NDIS_STATUS +TpPerfInitialize( IN OUT POPEN_BLOCK OpenP) +{ + NDIS_STATUS Status; + + // Sanity check + + IF_TPDBG (TP_DEBUG_RESOURCES) + { + if (OpenP->Perform != NULL) + { + TpPrint0("TpPerfInitialize: OpenP->Perform is not NULL !\n"); + TpBreakPoint(); + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + // + // First allocate the Performance struct. + // + + if ( (NdisAllocateMemory( (PVOID *)&OpenP->Perform, + sizeof( PERF_BLOCK ), + 0, + HighestAddress) ) != NDIS_STATUS_SUCCESS) + { + IF_TPDBG (TP_DEBUG_RESOURCES) + { + TpPrint0("TpPerfInitialize: failed to allocate PERF_BLOCK struct\n"); + } + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // zero everything for starters.. + // + + NdisZeroMemory( OpenP->Perform, sizeof( PERF_BLOCK )); + + // + // Allocate the Send PacketPool. + // + + NdisAllocatePacketPool( &Status, + &OpenP->Perform->PacketHandle, + NUMBER_OF_POOL_PACKETS, + sizeof( PROTOCOL_RESERVED ) ); + + if ( Status != NDIS_STATUS_SUCCESS ) + { + IF_TPDBG (TP_DEBUG_RESOURCES) + { + TpPrint0("TpPerfInitialize: could not allocate Packet Pool\n"); + } + NdisFreeMemory( OpenP->Perform,0,0 ); + OpenP->Perform = NULL; + return Status; + } + + // + // Initialize the Perform DPCs + // + + KeInitializeDpc(&OpenP->Perform->PerformSendDpc, // performance send + TpPerfSendDpc, + (PVOID)OpenP ); + + KeInitializeDpc(&OpenP->Perform->PerformEndDpc, + TpPerformEndDpc, + (PVOID)OpenP ); + + KeInitializeDpc(&OpenP->Perform->PerformRestartDpc, + TpPerfRestart, + (PVOID)OpenP); + + + KeInitializeTimer( &OpenP->Perform->PerformTimer ); + + // + // other things we can initialize here + // + + OpenP->Perform->PerformIrp = OpenP->Irp; + OpenP->Irp = NULL; + + OpenP->Perform->Active = TRUE; + OpenP->PerformanceTest = TRUE; + + return NDIS_STATUS_SUCCESS; +} + + + +// ------------------------------------------------- +// +// Function: TpPerfDeallocate +// +// Arguments: OpenP -- ptr to current open instance +// +// Returns: none +// +// Descript: This function frees up everything if allocations fail, or when +// we are ending performance testing +// +// ------------------------------------------------- + + +VOID +TpPerfDeallocate( IN OUT POPEN_BLOCK OpenP) +{ + OpenP->PerformanceTest = FALSE; + + if (OpenP->Perform->GoInitReq) + { + TpFuncFreePacket( OpenP->Perform->GoInitReq->u.SEND_REQ.Packet, + OpenP->Perform->GoInitReq->u.SEND_REQ.PacketSize ); + NdisFreeMemory( OpenP->Perform->GoInitReq,0,0 ); + } + + if (OpenP->Perform->AckReq) + { + TpFuncFreePacket( OpenP->Perform->AckReq->u.SEND_REQ.Packet, + OpenP->Perform->AckReq->u.SEND_REQ.PacketSize ); + NdisFreeMemory( OpenP->Perform->AckReq,0,0 ); + } + + if (OpenP->Perform->ResReq) + { + TpFuncFreePacket( OpenP->Perform->ResReq->u.SEND_REQ.Packet, + OpenP->Perform->ResReq->u.SEND_REQ.PacketSize ); + NdisFreeMemory( OpenP->Perform->ResReq,0,0 ); + } + + if (OpenP->Perform->DataReq) + { + TpFuncFreePacket( OpenP->Perform->DataReq->u.SEND_REQ.Packet, + OpenP->Perform->DataReq->u.SEND_REQ.PacketSize ); + NdisFreeMemory( OpenP->Perform->DataReq,0,0 ); + } + + NdisFreePacketPool( OpenP->Perform->PacketHandle ); + + NdisFreeMemory( OpenP->Perform,0,0 ); + OpenP->Perform = NULL; +} + + + + +// --------------------------------------------- +// +// Function: TpPerfAllocatePacket +// +// Arguments: OpenP -- ptr to current open instance +// PacketSize -- size of packet being allocated -- +// range is checked by caller +// +// Returns: ptr to request structure for packet if successful, else NULL +// +// Descript: This function allocates all send packets used in performance tests +// +// --------------------------------------------- + + +PTP_REQUEST_HANDLE +TpPerfAllocatePacket( IN POPEN_BLOCK OpenP, + IN ULONG PacketSize ) + +{ + NDIS_STATUS Status; + PPROTOCOL_RESERVED ProtRes; + PNDIS_PACKET Packet; + PNDIS_BUFFER Buffer; + PUCHAR TmpBuf; + PTP_REQUEST_HANDLE RequestHandle; + + // + // first, check to make sure media type is ok. + // This makes sure that this check is done in "main" thread, not + // in a DPC somewhere + + switch( OpenP->Media->MediumType ) + { + case NdisMediumDix: + case NdisMedium802_3: + case NdisMedium802_5: + case NdisMediumFddi: + case NdisMediumArcnet878_2: + break; + + default: + IF_TPDBG ( TP_DEBUG_RESOURCES ) + { + TpPrint0("TpPerfAllocatePacket: Unsupported MAC Type\n"); + } + return (PTP_REQUEST_HANDLE)NULL; + } + + + NdisAllocatePacket( &Status, + &Packet, + OpenP->Perform->PacketHandle ); + + if ( Status != NDIS_STATUS_SUCCESS ) + { + TpPrint1("TpPerfAllocatePacket: NdisAllocatePacket failed %s\n", TpGetStatus( Status )); + return (PTP_REQUEST_HANDLE)NULL; + } + + ProtRes = PROT_RES( Packet ); + ProtRes->Pool.PacketHandle = &OpenP->Perform->PacketHandle; + ProtRes->InstanceCounters = NULL; + + // + // start of things partially copied from TpFuncInitPacketHeader + // + + + Status = NdisAllocateMemory((PVOID *)&TmpBuf, + PacketSize, + 0, + HighestAddress ); + + if ( Status != NDIS_STATUS_SUCCESS ) + { + IF_TPDBG ( TP_DEBUG_RESOURCES ) + { + TpPrint0("TpFuncInitPacketHeader: failed to allocate TmpBuf\n"); + } + return (PTP_REQUEST_HANDLE)NULL; + } + NdisZeroMemory( (PVOID)TmpBuf,PacketSize ); + + // data is in 2 or three buffers. The first is always 14 bytes, and the second is + // always 46 bytes. (giving a total of 60 bytes). If Packetsize > 60 bytes, the + // third buffer is created with a size of (Packetsize-60) bytes + + // first, the "media header" = 14 bytes + + NdisAllocateBuffer( &Status, + &Buffer, + NULL, // pool handle, not currently used in NT + TmpBuf, + sizeof (MEDIA_HEADER)); + + if ( Status != NDIS_STATUS_SUCCESS ) + { + TpPrint0("TpPerfAllocatePacket: failed to create the MDL\n"); + TpFuncFreePacket( Packet, PacketSize ); + return (PTP_REQUEST_HANDLE)NULL; + } + + // + // And chain it to the back of the packet. + // + + NdisChainBufferAtBack( Packet,Buffer ); + + // next, the "protocol" info (plus some data) = 46 bytes (46+14=60) + + NdisAllocateBuffer( &Status, + &Buffer, + NULL, // pool handle, not currently used in NT + TmpBuf+sizeof(MEDIA_HEADER), + MINIMUM_PERF_PACKET - sizeof(MEDIA_HEADER)); + + if ( Status != NDIS_STATUS_SUCCESS ) + { + TpPrint0("TpPerfAllocatePacket: failed to create the MDL\n"); + TpFuncFreePacket( Packet, PacketSize ); + return (PTP_REQUEST_HANDLE)NULL; + } + + // + // And chain it to the back of the packet. + // + + NdisChainBufferAtBack( Packet,Buffer ); + + // finally, if we need more than 60 bytes, add a buffer with the rest.. + + if (PacketSize > MINIMUM_PERF_PACKET) + { + NdisAllocateBuffer( &Status, + &Buffer, + NULL, // pool handle, not currently used in NT + TmpBuf+MINIMUM_PERF_PACKET, + PacketSize-MINIMUM_PERF_PACKET); + + if ( Status != NDIS_STATUS_SUCCESS ) + { + TpPrint0("TpPerfAllocatePacket: failed to create the MDL\n"); + TpFuncFreePacket( Packet, PacketSize ); + return (PTP_REQUEST_HANDLE)NULL; + } + + // + // And chain it to the back of the packet. + // + + NdisChainBufferAtBack( Packet,Buffer ); + } + + Status = NdisAllocateMemory((PVOID *)&RequestHandle, + sizeof( TP_REQUEST_HANDLE ), + 0, + HighestAddress ); + + if ( Status != NDIS_STATUS_SUCCESS ) + { + TpPrint0("TpPerfAllocatePacket: unable to allocate Request Handle.\n"); + return (PTP_REQUEST_HANDLE)NULL; + } + else + { + NdisZeroMemory( RequestHandle,sizeof( TP_REQUEST_HANDLE )); + } + + RequestHandle->Signature = SEND_REQUEST_HANDLE_SIGNATURE; + RequestHandle->Open = OpenP; + RequestHandle->RequestPended = TRUE; + RequestHandle->Irp = OpenP->Perform->PerformIrp; + + RequestHandle->u.PERF_REQ.Packet = Packet; + RequestHandle->u.PERF_REQ.PacketSize = PacketSize; + RequestHandle->u.PERF_REQ.SendPacket = TRUE; + RequestHandle->u.PERF_REQ.Buffer = TmpBuf; + + ProtRes = PROT_RES( Packet ); + ProtRes->RequestHandle = RequestHandle; + + // + // Set the check sum in the PROTOCOL RESERVED Section of the + // packet header to ensure it is not touched while the packet + // is in the hands of the MAC. + // + + ProtRes->CheckSum = TpSetCheckSum( (PUCHAR)ProtRes, + sizeof( PROTOCOL_RESERVED ) - sizeof( ULONG ) ); + + return RequestHandle; + +} + +// ----------------------------------------------------- +// +// Function: TpPerfSetPacketData +// +// Arguments: OpenP -- ptr to current open instance +// TmpBuf -- ptr to memory allocated for packet data +// Signature -- packet type signature to use +// DestAddr -- address to which packet will be sent +// PacketSize -- bytes allocated for packet +// +// Returns: None +// +// Descript: stuffs data into a packet +// +// ----------------------------------------------------- + +VOID +TpPerfSetPacketData(POPEN_BLOCK OpenP, + PUCHAR TmpBuf, + ULONG Signature, + PUCHAR DestAddr, + ULONG PacketSize) +{ + PPERF_PACKET TmpBuffer = (PPERF_PACKET)TmpBuf; + PUCHAR p; + PUCHAR q; + PUCHAR SrcAddr = OpenP->StationAddress; + USHORT DataSizeShort; + USHORT i; + PPERF_BLOCK Perform = OpenP->Perform; + + + if (DestAddr != NULL) // only do this on first pass + { + switch( OpenP->Media->MediumType ) + { + case NdisMediumDix: + case NdisMedium802_3: + p = TmpBuffer->media.e.DestAddress; + q = TmpBuffer->media.e.SrcAddress; + DataSizeShort = (USHORT)( PacketSize - OpenP->Media->HeaderSize ); + TmpBuffer->media.e.PacketSize_Hi = (UCHAR)( DataSizeShort >> 8 ); + TmpBuffer->media.e.PacketSize_Lo = (UCHAR)DataSizeShort; + break; + + case NdisMedium802_5: + TmpBuffer->media.tr.AC = 0x10; + TmpBuffer->media.tr.FC = 0x40; + p = TmpBuffer->media.tr.DestAddress; + q = TmpBuffer->media.tr.SrcAddress; + break; + + case NdisMediumFddi: + TmpBuffer->media.fddi.FC = 0x57; + p = TmpBuffer->media.fddi.DestAddress; + q = TmpBuffer->media.fddi.SrcAddress; + break; + + case NdisMediumArcnet878_2: + TmpBuffer->media.a.ProtocolID = ARCNET_DEFAULT_PROTOCOLID; + p = TmpBuffer->media.a.DestAddress; + q = TmpBuffer->media.a.SrcAddress; + break; + + default: + TpBreakPoint(); + } + + for ( i = 0 ; i < OpenP->Media->AddressLen ; i++ ) + { + *p++ = *DestAddr++; + *q++ = *SrcAddr++; + } + } + + // + // initialize the packet information header + // + + if ((Signature == PERF_SRVDATA_SIGNATURE) || (Signature == PERF_CLTDATA_SIGNATURE)) + { + ULONG DataFieldSize; + PUCHAR DataField; + + TmpBuffer->u.data.Signature = Signature; + TmpBuffer->u.data.PacketSize = PacketSize; + TmpBuffer->u.data.CheckSum = TpSetCheckSum( (PUCHAR)&TmpBuffer->u.data, + sizeof(DATA_PACKET_INFO) - sizeof(ULONG) ); + DataField = TmpBuf + sizeof (MEDIA_HEADER) + sizeof (DATA_PACKET_INFO); + DataFieldSize = PacketSize - (sizeof (MEDIA_HEADER) + sizeof (DATA_PACKET_INFO) ); + for ( i = 0 ; i < DataFieldSize ; i++ ) + { + *DataField++ = (UCHAR)i; + } + } + else if (Signature == PERF_RETRES_SIGNATURE) + { + TmpBuffer->u.results.Signature = PERF_RETRES_SIGNATURE; + TmpBuffer->u.results.PacketSize = PacketSize; + TmpBuffer->u.results.PacketsSent = Perform->SendCount; + TmpBuffer->u.results.SendErrors = Perform->SendFailCount; + TmpBuffer->u.results.PacketsReceived = Perform->ReceiveCount; + TmpBuffer->u.results.ElapsedTime = Perform->PerfSendTotalTime.LowPart; + TmpBuffer->u.results.SelfReceives = Perform->SelfReceiveCount; + TmpBuffer->u.results.Restarts = Perform->RestartCount; + + TmpBuffer->u.results.CheckSum = TpSetCheckSum( (PUCHAR)&TmpBuffer->u.results, + sizeof(RESULTS_PACKET_INFO) - sizeof(ULONG) ); + } + else + { + + TmpBuffer->u.info.Signature = Signature; + if (DestAddr != NULL) + { + ULONG i; + PUCHAR r,s; + + TmpBuffer->u.info.PacketSize = PacketSize; + TmpBuffer->u.info.Mode = Perform->PerformMode; + TmpBuffer->u.info.Length = Perform->PacketSize; + TmpBuffer->u.info.Count = Perform->NumberOfPackets; + TmpBuffer->u.info.Delay = Perform->PacketDelay; + r = TmpBuffer->u.info.Address; + s = Perform->ClientAddress; + for (i=0; i < ADDRESS_LENGTH; i++) + { + *r++ = *s++; + } + } + TmpBuffer->u.info.CheckSum = TpSetCheckSum( (PUCHAR)&TmpBuffer->u.info, + sizeof(INFO_PACKET_INFO) - sizeof(ULONG) ); + } +} + + + + +// ------------------------------------------ +// +// Function: TpPerfSendDpc +// +// Arguments: Dpc -- ignored +// DeferredContext -- actually ptr to open instance +// SysArg1 -- ignored +// SysArg2 -- ignored +// +// Returns: none +// +// Descript: This function is used to start the sending of packets +// Further packets are sent via TpPerfSendComplete +// +// ------------------------------------------- + + +VOID +TpPerfSendDpc( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SysArg1, + IN PVOID SysArg2 + ) + +{ + POPEN_BLOCK OpenP = ((POPEN_BLOCK)DeferredContext); + NDIS_STATUS Status; + PTP_REQUEST_HANDLE RequestHandle; + + UNREFERENCED_PARAMETER( Dpc ); + UNREFERENCED_PARAMETER( SysArg1 ); + UNREFERENCED_PARAMETER( SysArg2 ); + + + switch(OpenP->Perform->WhichReq) + { + case REQ_DATA: + RequestHandle = OpenP->Perform->DataReq; + ++OpenP->Perform->SendCount; + OpenP->Perform->PerfSendTotalTime = + RtlLargeIntegerNegate(KeQueryPerformanceCounter(NULL)); + break; + case REQ_INITGO: + RequestHandle = OpenP->Perform->GoInitReq; + break; + case REQ_ACK: + RequestHandle = OpenP->Perform->AckReq; + break; + case REQ_RES: + RequestHandle = OpenP->Perform->ResReq; + break; + } + + ++OpenP->Perform->PacketsPending; + + NdisSend( &Status,OpenP->NdisBindingHandle, RequestHandle->u.PERF_REQ.Packet ); + + if ( Status != NDIS_STATUS_PENDING ) + { + TpPerfSendComplete( OpenP, RequestHandle->u.PERF_REQ.Packet, Status ); + } + +} + + + + +// ----------------------------------------------- +// +// Function: TpPerfSendComplete +// +// Arguments: ProtocolBindingContext -- actually ptr to open instance +// Packet -- the packet that was just sent +// Status -- final status of the send operation +// +// Returns: none +// +// Descript: This function is called after the netcard driver actually +// sends the packet. It is responsible for sending the next +// packet (if there is one to be sent) +// +// ----------------------------------------------- + + +VOID +TpPerfSendComplete( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status + ) + + +{ + POPEN_BLOCK OpenP = ((POPEN_BLOCK)ProtocolBindingContext); + PPERF_BLOCK Perform = OpenP->Perform; + PPROTOCOL_RESERVED ProtRes; + PTP_REQUEST_HANDLE RequestHandle; + PNDIS_BUFFER Buffer; + LARGE_INTEGER DueTime; + ULONG MessageId; + + + + ProtRes = PROT_RES( Packet ); + RequestHandle = ProtRes->RequestHandle; + +senddidnotpend: + + if ( Perform->Active == TRUE ) + { + // + // Make sure it is one of our packets + // + if (( RequestHandle->Signature == SEND_REQUEST_HANDLE_SIGNATURE ) && + ( RequestHandle->u.PERF_REQ.SendPacket == TRUE )) + { + // + // Packet was sent by the PERF command, decrement the + // counter tracking the number of outstanding functional packets, + // and if the send succeeded increment the completion counter. + // + + --Perform->PacketsPending; + + // + // doesn't do any good to reverse logic here (at least for x86) + // so just put up with the 1 jump + // + + if (Status != NDIS_STATUS_SUCCESS) + { + // + // If we are running on TokenRing the following two "failures" + // are not considered failures NDIS_STATUS_NOT_RECOGNIZED - + // no one on the ring recognized the address as theirs, or + // NDIS_STATUS_NOT_COPIED - no one on the ring copied the + // packet, so we need to special case this and not count + // these as failures. + // + // SanjeevK : Even FDDI returns the same errors as 802.5 + // + + if ( ( NdisMediumArray[OpenP->MediumIndex] == NdisMedium802_5 ) || + ( NdisMediumArray[OpenP->MediumIndex] == NdisMediumFddi ) || + ( NdisMediumArray[OpenP->MediumIndex] == NdisMediumArcnet878_2) ) + { + if (( Status != NDIS_STATUS_NOT_RECOGNIZED ) && + ( Status != NDIS_STATUS_NOT_COPIED )) + { + ++Perform->SendFailCount; + } + } + else + { + ++Perform->SendFailCount; + } + } + + // + // not checking the checksum of the PROTOCOL_RESERVED section + // here, because that stuff was done in functional testing + // + + MessageId = ((PPERF_PACKET)RequestHandle->u.PERF_REQ.Buffer)->u.info.Signature + - PERF_BASE; + + // + // deal with performance test messages first + // + + if ((MessageId & PERF_ID_MASK) == PERF_DATA_ID) + { + if ( ++Perform->PacketsSent < Perform->NumberOfPackets ) + { + // + // if in a mode where never wait for ack or req, don't + // bother with spin-lock, etc, even though this causes + // repetitious code... + // + if ((Perform->PerformMode != PERFMODE_SENDWITHACK) && + (Perform->PerformMode != PERFMODE_REQANDRCV)) + { + ++Perform->PacketsPending; + ++Perform->SendCount; + + if (!Perform->PacketDelay) + { + NdisSend( &Status,OpenP->NdisBindingHandle,Packet ); + if ( Status == NDIS_STATUS_PENDING ) + { + return; + } + goto senddidnotpend; // avoid recursion + } + // + // delay code + // + else + { + KeStallExecutionProcessor(10 * Perform->PacketDelay); + NdisSend( &Status,OpenP->NdisBindingHandle,Packet ); + if ( Status == NDIS_STATUS_PENDING ) + { + return; + } + goto senddidnotpend; // avoid recursion + } + } + + // + // send another packet if SendBurstCount has not run out + // otherwise, wait for REQ or ACK (and setup timeout?) + // + NdisAcquireSpinLock( &OpenP->SpinLock ); + if (--Perform->SendBurstCount != 0) + { + NdisReleaseSpinLock( &OpenP->SpinLock ); + ++Perform->PacketsPending; + ++Perform->SendCount; + + if (!Perform->PacketDelay) + { + NdisSend( &Status,OpenP->NdisBindingHandle,Packet ); + if ( Status == NDIS_STATUS_PENDING ) + { + return; + } + goto senddidnotpend; // avoid recursion + } + // + // delay code + // + else + { + KeStallExecutionProcessor(10 * Perform->PacketDelay); + NdisSend( &Status,OpenP->NdisBindingHandle,Packet ); + if ( Status == NDIS_STATUS_PENDING ) + { + return; + } + goto senddidnotpend; // avoid recursion + } + } + else + { + NdisReleaseSpinLock( &OpenP->SpinLock ); + DueTime.HighPart = 0xFFFFFFFF; // So it will be relative. + DueTime.LowPart = (ULONG)(-(ONE_TENTH_SECOND)); + + if ( KeSetTimer(&Perform->PerformTimer, + DueTime, + &Perform->PerformRestartDpc )) + { + IF_TPDBG ( TP_DEBUG_DPC ) + { + TpPrint0( + "TpPerfSendComplete: set PerformTimer while timer existed(4).\n"); + } + } + } + } + else + { + TpPerfTestCompleted(Perform); + } + return; + } + + // + // deal with all other messages + // + switch ( MessageId) + { + // + // info messages sent by server. Nothing special to do + // when send is complete + // + case PERF_ACKREQ_ID: // client: REQ message + case (PERF_ACKREQ_ID + PERF_SERVER): // server: ACK message + case PERF_START_ID: // client: INIT message + case PERF_DONE_ID: // client: REQRES message + case (PERF_DONE_ID + PERF_SERVER): // server: SRVDONE message + case PERF_STOP_ID: // client: STOPSRV message + case (PERF_NOGO_ID + PERF_SERVER): // server: NOGO message + return; // client waits for server message + // server waits for client response + + // + // we are a server, and we just got done sending a GO message + // if we are a sender, send the first performance packet + // + case (PERF_START_ID + PERF_SERVER): // server: GO message + if (Perform->PerformMode >= PERFMODE_SENDANDRCV) + { + // + // server needs to send data to client. start it up + // + Perform->WhichReq = REQ_DATA; + + DueTime.HighPart = 0xFFFFFFFF; // So it will be relative. + DueTime.LowPart = (ULONG)(-(ONE_HUNDREDTH_SECOND*2)); + + if ( KeSetTimer(&Perform->PerformTimer, + DueTime, + &Perform->PerformSendDpc )) + { + IF_TPDBG ( TP_DEBUG_DPC ) + { + TpPrint0( + "TpPerfSendComplete: set PerformTimer while timer existed(1).\n"); + } + } + } + return; + + // + // we are a server, and we just got done sending the final results + // to the client. finish shutting down from this test + // + + case (PERF_RESULTS_ID + PERF_SERVER): // server: sent results + if (Perform->DataReq) // clean up, then wait for INIT message + { + TpFuncFreePacket( Perform->DataReq->u.SEND_REQ.Packet, + Perform->DataReq->u.SEND_REQ.PacketSize ); + NdisFreeMemory( Perform->DataReq,0,0 ); + Perform->DataReq = NULL; + } + return; + + // + // we are a server, and we just got done acknowledging a shut-down request + // from the client. finish up with the shutdown. + // + case (PERF_STOP_ID + PERF_SERVER): // server: acknowledged shutdown + // request cleansup, then exit + DueTime.HighPart = 0xFFFFFFFF; // So it will be relative. + DueTime.LowPart = (ULONG)(-(ONE_SECOND)); + + if ( KeSetTimer(&Perform->PerformTimer, + DueTime, + &Perform->PerformEndDpc )) + { + IF_TPDBG ( TP_DEBUG_DPC ) + { + TpPrint0( + "TpPerfSendComplete: set PerformTimer while timer existed(2).\n"); + } + } + return; + + default: // illegal message + TpPrint0("TpPerfSendComplete: unknown message\n"); + TpBreakPoint(); + return; + + + } + } // if (RequestSignature == .. + + else + { + // + // this is not one of ours. we should never, ever get here... + // + TpPrint0("TpPerfSendComplete: Not one of ours--why are we here?"); + NdisUnchainBufferAtFront( Packet,&Buffer ); + NdisFreeMemory( MmGetMdlVirtualAddress( Buffer ),0,0 ); + TpFreeBuffer( Buffer ); + NdisFreePacket( Packet ); + NdisFreeMemory( RequestHandle,0,0 ); + return; + } + } // if (Perform->Active) + +} + + +// ----------------------------------------------------------- +// +// TpPerfTestCompleted +// +// Arguments: OpenP -- ptr to current open instance +// +// Returns: none +// +// Descript: This code deals with cleanup that needs to be done at the +// end of a send test +// +// ---------------------------------------------------------- + +VOID +TpPerfTestCompleted(PPERF_BLOCK Perform) +{ + LARGE_INTEGER scale; + LARGE_INTEGER ltemp; + LARGE_INTEGER DueTime; + PKDPC DpcPtr; + + Perform->PerfSendTotalTime = RtlLargeIntegerAdd( Perform->PerfSendTotalTime, + KeQueryPerformanceCounter(&scale)); + + Perform->PerfSendTotalTime = RtlExtendedIntegerMultiply(Perform->PerfSendTotalTime, 1000); + Perform->PerfSendTotalTime = RtlLargeIntegerDivide(Perform->PerfSendTotalTime, + scale, <emp); + + if (Perform->IsServer) + { + DpcPtr = &Perform->PerformSendDpc; + Perform->WhichReq = REQ_ACK; // SRVDONE message + } + + // + // must be client.. + // + else if (Perform->PerformMode == PERFMODE_SEND) + { + // + // Write the statistics to the send results outputbuffer. + // + + TpPerfWriteResults( Perform, NULL ); + DpcPtr = &Perform->PerformEndDpc; + } + + else + { + if (Perform->PerformMode == PERFMODE_SENDANDRCV) + { + if (!Perform->Testing) + { + Perform->Testing = TRUE; + return; + } + } + Perform->WhichReq = REQ_RES; + DpcPtr = &Perform->PerformSendDpc; + } + + DueTime.HighPart = 0xFFFFFFFF; // So it will be relative. + DueTime.LowPart = (ULONG)(-(ONE_SECOND)); + + + if ( KeSetTimer(&Perform->PerformTimer, + DueTime, + DpcPtr )) + { + IF_TPDBG ( TP_DEBUG_DPC ) + { + TpPrint0( "TpPerfTestCompleted: set PerformTimer while timer existed.\n"); + } + } +} + +// ------------------------------------------------------- +// +// Function: TpPerfReceive +// +// Arguments: ProtocolBindingContext -- actually ptr to current open instance +// LookaheadBuffer -- ptr to actual data received (after header) +// LookaheadBufferSize -- valid bytes in LookaheadBuffer +// PacketSize -- total size of packet (excluding header) +// +// Returns: Status +// +// Descript: This function deals with packets received by this netcard open instance +// Some packets are counted, some just thrown away, some result in other +// packets being sent +// +// ------------------------------------------------------- + + + +NDIS_STATUS +TpPerfReceive( + IN NDIS_HANDLE ProtocolBindingContext, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ) + +{ + POPEN_BLOCK OpenP = ((POPEN_BLOCK)ProtocolBindingContext); + PPERF_BLOCK Perform = OpenP->Perform; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PINFO_PACKET_INFO ReceivePacketInfo; + PNDIS_PACKET Packet; + ULONG MessageId; + + // + // if we are not active (ie, shutting down) skip everything + // + + if (Perform->Active) + { + // + // The LookAhead Buffer has been adjusted to point to the beginning of the + // PACKET_INFO structure + // + ReceivePacketInfo = (PINFO_PACKET_INFO)LookaheadBuffer; + + // + // All valid messages have a signature of 0x7654321X. + // Using the MaskId, convert all valid messages to 0x0000000X. + // invalid messages will have other bits set. Messages that we + // managed to send to ourselves will be in range 0x08 to 0x0f. + // messages we expect will be in range 0x00 to 0x07 + // + MessageId = ReceivePacketInfo->Signature ^ Perform->MaskId; + // + // trivially discard all unrecognized messages + // + if (MessageId < (PERF_SERVER + PERF_ID_MASK)) + { + // + // first, deal with performance test messages which were received + // + if ( MessageId == PERF_DATA_ID) + { + ++Perform->ReceiveCount; + + // + // Check to see if we need to send an ACK (or a REQ ) + // note that the info packet will already be set up correctly + // + if (--Perform->ReceiveBurstCount != 0) + { + return NDIS_STATUS_SUCCESS; + } + else + { + Perform->ReceiveBurstCount = PACKETS_PER_BURST; + Packet = Perform->AckReq->u.PERF_REQ.Packet; + ++Perform->PacketsPending; + + NdisSend( &Status,OpenP->NdisBindingHandle,Packet ); + if ( Status == NDIS_STATUS_PENDING ) + { + return NDIS_STATUS_SUCCESS; + } + TpPerfSendComplete( OpenP, Packet, Status ); + return NDIS_STATUS_SUCCESS; + } + } + // + // second, deal with ACK and REQ messages + // + else if (MessageId == PERF_ACKREQ_ID) + { + NdisAcquireSpinLock( &OpenP->SpinLock ); + if (Perform->SendBurstCount == 0) + { + Perform->SendBurstCount = PACKETS_PER_BURST; + NdisReleaseSpinLock( &OpenP->SpinLock ); + KeCancelTimer(&Perform->PerformTimer); + + Packet = Perform->DataReq->u.PERF_REQ.Packet; + ++Perform->SendCount; + ++Perform->PacketsPending; + + NdisSend( &Status,OpenP->NdisBindingHandle,Packet ); + if ( Status != NDIS_STATUS_PENDING ) + { + TpPerfSendComplete( OpenP, Packet, Status ); + } + } + else + { + Perform->SendBurstCount += PACKETS_PER_BURST; + NdisReleaseSpinLock( &OpenP->SpinLock ); + } + return NDIS_STATUS_SUCCESS; + } + // + // deal with other valid messages + // + else if ((MessageId & PERF_SERVER) == 0) // check other valid messages + { + return TpPerfLowPriorityReceive(OpenP, ReceivePacketInfo, MessageId); + } + // + // deal with messages that we probably sent to ourselves + // while it it POSSIBLE that we got a random message that will + // fit this criteria, we are ignoring that for now + // + else + { + Perform->SelfReceiveCount++; + return NDIS_STATUS_SUCCESS; + } + } + } + return NDIS_STATUS_SUCCESS; // don't fail.. +} + + +// ------------------------------------------------ +// +// Function: TpPerfLowPriorityReceive +// +// Arguments: OpenP -- ptr to current open instance +// ReceivePacketInfo -- data received from other end of wire +// MessageId -- which message we received +// +// Returns: Status +// +// Descript: This function does the initialization required +// when the server receives the PERF_INIT message +// +// ------------------------------------------------- + +NDIS_STATUS +TpPerfLowPriorityReceive( POPEN_BLOCK OpenP, + PINFO_PACKET_INFO ReceivePacketInfo, + ULONG MessageId) +{ + PPERF_BLOCK Perform = OpenP->Perform; + PUCHAR r,s; + ULONG i; + PTP_REQUEST_HANDLE RequestHandle; + PNDIS_PACKET Packet; + LARGE_INTEGER DueTime; + PKDPC DpcPtr; + + + switch(MessageId) + { + case PERF_DONE_ID: // REQRES or SRVDONE message + if (Perform->IsServer) + { + // + // client sent request for final results of test (on server side) + // test had better be complete. Shut down test and send message + // to client with those results + // + TpPerfSetPacketData(OpenP, + Perform->ResReq->u.PERF_REQ.Buffer, + PERF_RETRES_SIGNATURE, + NULL, + MINIMUM_PERF_PACKET); + + Perform->WhichReq = REQ_RES; + DpcPtr = &Perform->PerformSendDpc; + DueTime.LowPart = (ULONG)(-(ONE_SECOND)); + Perform->Testing = FALSE; + } + else + { + // + // server is done sending data. if only server was sending, get + // stats now . if both were sending, get stats now if we are also + // done. Otherwise, set flags to get data when we are done sending + // + if (Perform->PerformMode == PERFMODE_SENDANDRCV) + { + if (!Perform->Testing) + { + Perform->Testing = TRUE; + return NDIS_STATUS_SUCCESS; + } + } + Perform->WhichReq = REQ_RES; + DpcPtr = &Perform->PerformSendDpc; + DueTime.LowPart = (ULONG)(-(ONE_SECOND)); + } + break; + + + case PERF_STOP_ID: // STOPSRV or SRVDOWN message + if (Perform->IsServer) + { + // + // client just sent message to server telling server to + // shut down, and go back to tpctl for next command + // + TpPerfSetPacketData(OpenP, + Perform->GoInitReq->u.PERF_REQ.Buffer, + PERF_SRVDOWN_SIGNATURE, + ReceivePacketInfo->Address, + MINIMUM_PERF_PACKET); + + Perform->WhichReq = REQ_INITGO; + DpcPtr = &Perform->PerformSendDpc; + DueTime.LowPart = (ULONG)(-(ONE_SECOND)); + } + else + { + // + // server is shutting down. We need to do the same + // + DpcPtr = &Perform->PerformEndDpc; + DueTime.LowPart = (ULONG)(-(ONE_SECOND)); + } + break; + + + case PERF_NOGO_ID: // NOGO message + // + // server just sent message that it is unable to perform the + // requested test. Clean up and exit (to tpctl) + // + DpcPtr = &Perform->PerformEndDpc; + DueTime.LowPart = (ULONG)(-(ONE_SECOND)); + break; + + + case PERF_RESULTS_ID: // RETRES message + // + // just received final results of this test from the server + // send data to tpctl, cleanup, and exit + // + // + // Write the statistics to the send results outputbuffer. + // + + TpPerfWriteResults( Perform, (PRESULTS_PACKET_INFO)ReceivePacketInfo); + DpcPtr = &Perform->PerformEndDpc; + DueTime.LowPart = (ULONG)(-(ONE_SECOND)); + break; + + case PERF_START_ID: // INIT or GO message + if (!Perform->IsServer) + { + if (Perform->DataReq) + { + Perform->WhichReq = REQ_DATA; + DpcPtr = &Perform->PerformSendDpc; + DueTime.LowPart = (ULONG)(-(ONE_HUNDREDTH_SECOND)); + break; + } + return NDIS_STATUS_SUCCESS; + } + else + { + if (Perform->Testing) // Got 2nd request, not done with 1st + { + TpPrint0("TpPerfReceive: Server got INIT while already running!\n"); + if (Perform->DataReq) + { + TpFuncFreePacket( Perform->DataReq->u.SEND_REQ.Packet, + Perform->DataReq->u.SEND_REQ.PacketSize ); + NdisFreeMemory( Perform->DataReq,0,0 ); + } + } + // + // copy info we will need from message + // + Perform->PerformMode = ReceivePacketInfo->Mode; + Perform->PacketSize = ReceivePacketInfo->Length; + Perform->NumberOfPackets = ReceivePacketInfo->Count; + Perform->PacketDelay = ReceivePacketInfo->Delay; + r = Perform->ClientAddress; + s = ReceivePacketInfo->Address; + for (i=0; i < ADDRESS_LENGTH; i++) + { + *r++ = *s++; + } + // + // initialize counters + // + Perform->SendCount = 0; + Perform->SendFailCount = 0; + Perform->ReceiveCount = 0; + Perform->PacketsSent = 0; + Perform->PerformEndDpcCount = 0; + Perform->PacketsPending = 0; + Perform->Testing = FALSE; + Perform->SelfReceiveCount = 0; + Perform->SendBurstCount = 0; + Perform->ReceiveBurstCount = 0; + Perform->RestartCount = 0; + // + // if we will be sending test data (not just info messages), then + // set up the necessary buffer. If it fails, send a NOGO message + // + if (Perform->PerformMode >= PERFMODE_SENDANDRCV) + { + RequestHandle = TpPerfAllocatePacket( OpenP, Perform->PacketSize); + if (RequestHandle == NULL) + { + TpPrint0("TpPerfReceive: Server unable to allocate data packet\n"); + RequestHandle = Perform->GoInitReq; + Packet = RequestHandle->u.PERF_REQ.Packet; + + TpPerfSetPacketData(OpenP, + RequestHandle->u.PERF_REQ.Buffer, + PERF_NOGO_SIGNATURE, + Perform->ClientAddress, + MINIMUM_PERF_PACKET); + + Perform->WhichReq = REQ_INITGO; + DpcPtr = &Perform->PerformSendDpc; + DueTime.LowPart = (ULONG)(-(ONE_SECOND)); + break; + } + Perform->DataReq = RequestHandle; + + TpPerfSetPacketData(OpenP, + RequestHandle->u.PERF_REQ.Buffer, + PERF_SRVDATA_SIGNATURE, + Perform->ClientAddress, + Perform->PacketSize); + + if (Perform->PerformMode == PERFMODE_REQANDRCV) + { + Perform->SendBurstCount = PACKETS_PER_BURST; + } + else + { + Perform->SendBurstCount = Perform->NumberOfPackets+1; + } + Perform->ReceiveBurstCount = Perform->NumberOfPackets+1; + } + + else if (Perform->PerformMode == PERFMODE_SENDWITHACK) + { + Perform->ReceiveBurstCount = PACKETS_PER_BURST; + } + else + { + Perform->ReceiveBurstCount = Perform->NumberOfPackets+1; + } + + // + // all set--initialize the AckReq message, and + // send the client the GO message + // + Perform->Testing = TRUE; + + switch(Perform->PerformMode) + { + case PERFMODE_SENDTOSRV: + case PERFMODE_SENDWITHACK: + TpPerfSetPacketData(OpenP, + Perform->AckReq->u.PERF_REQ.Buffer, + PERF_ACK_SIGNATURE, + Perform->ClientAddress, + MINIMUM_PERF_PACKET); + break; + default: + TpPerfSetPacketData(OpenP, + Perform->AckReq->u.PERF_REQ.Buffer, + PERF_SRVDONE_SIGNATURE, + Perform->ClientAddress, + MINIMUM_PERF_PACKET); + break; + } + TpPerfSetPacketData(OpenP, + Perform->ResReq->u.PERF_REQ.Buffer, + PERF_RETRES_SIGNATURE, + Perform->ClientAddress, + MINIMUM_PERF_PACKET); + + TpPerfSetPacketData(OpenP, + Perform->GoInitReq->u.PERF_REQ.Buffer, + PERF_GO_SIGNATURE, + Perform->ClientAddress, + MINIMUM_PERF_PACKET); + + DpcPtr = &Perform->PerformSendDpc; + DueTime.LowPart = (ULONG)(-(ONE_SECOND)); + Perform->WhichReq = REQ_INITGO; + break; + } + + default: + TpPrint0("TpPerfReceive: Client received unrecognized message\n"); + return NDIS_STATUS_NOT_RECOGNIZED; + } + + // + // drop thru to here if need to fire something off with the timer... + // + + DueTime.HighPart = 0xFFFFFFFF; // So it will be relative. + + if ( KeSetTimer(&Perform->PerformTimer, + DueTime, + DpcPtr )) + { + IF_TPDBG ( TP_DEBUG_DPC ) + { + TpPrint0( "TpPerfLowPriorityReceive: set PerformTimer while timer existed.\n"); + } + } + return NDIS_STATUS_SUCCESS; +} + + +// -------------------------------------------- +// +// Function: TpPerformEndDpc +// +// Arguments: Dpc -- not used +// DeferredContext -- actually ptr to current open instance +// SysArg1 -- not used +// SysArg2 -- not used +// +// Returns: none +// +// Descript: This function is called when it is time to shut down +// the current performance command +// +// ------------------------------------------- + +VOID +TpPerformEndDpc( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SysArg1, + IN PVOID SysArg2 + ) +{ + POPEN_BLOCK OpenP = ((POPEN_BLOCK)DeferredContext); + PPERF_BLOCK Perform = OpenP->Perform; + LARGE_INTEGER DueTime; + + UNREFERENCED_PARAMETER( Dpc ); + UNREFERENCED_PARAMETER( SysArg1 ); + UNREFERENCED_PARAMETER( SysArg2 ); + + // + // See if we have any outstanding packets left to complete. If we do, + // then we will reset the time to queue this dpc routine again in one + // second, if after ten requeue the packet has still no completed we + // assume it will never complete and return the results and finish. + // + + NdisAcquireSpinLock( &OpenP->SpinLock ); + + if ((( Perform->PerformIrp != NULL ) && + ( Perform->PerformIrp->Cancel == FALSE )) && + (( Perform->PacketsPending != 0 ) && + ( Perform->PerformEndDpcCount++ < 10 ))) + { + NdisReleaseSpinLock( &OpenP->SpinLock ); + + DueTime.HighPart = -1; // So it will be relative. + DueTime.LowPart = (ULONG)(-(ONE_SECOND)); + + if ( KeSetTimer(&Perform->PerformTimer, + DueTime, + &Perform->PerformEndDpc )) + { + IF_TPDBG ( TP_DEBUG_DPC ) + { + TpPrint0("TpPerformEndDpc: set PerformTimer while timer existed.\n"); + } + } + return; + } + + + // + // and if the IoStatus.Status has not been set, then set it. + // + + if ( (Perform->PerformIrp != NULL) && + (Perform->PerformIrp->IoStatus.Status == NDIS_STATUS_PENDING )) + { + Perform->PerformIrp->IoStatus.Status = NDIS_STATUS_SUCCESS; + } + + NdisReleaseSpinLock( &OpenP->SpinLock ); + + // + // Now set the sending flag to indicate that we are no longer + // SENDing packets. + // + + Perform->Active = FALSE; + + // + // and decrement the reference count on the OpenBlock stating this + // instance of an async test is no longer running, and the adapter + // may be closed if requested. + // + + + if (Perform->PerformIrp != NULL) + { + TpRemoveReference( OpenP ); + IoMarkIrpPending( Perform->PerformIrp ); + + IoAcquireCancelSpinLock( &Perform->PerformIrp->CancelIrql ); + IoSetCancelRoutine( Perform->PerformIrp,NULL ); + IoReleaseCancelSpinLock( Perform->PerformIrp->CancelIrql ); + + IoCompleteRequest( Perform->PerformIrp,IO_NETWORK_INCREMENT ); + + Perform->PerformIrp = NULL; + } + TpPerfDeallocate(OpenP); +} + +// -------------------------------------------- +// +// Function: TpPerfRestart +// +// Arguments: Dpc -- not used +// DeferredContext -- actually ptr to current open instance +// SysArg1 -- not used +// SysArg2 -- not used +// +// Returns: none +// +// Descript: This function is called when it is necessary to restart +// a send without there having been a REQ or ACK received +// +// ------------------------------------------- + +VOID +TpPerfRestart( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SysArg1, + IN PVOID SysArg2 + ) +{ + POPEN_BLOCK OpenP = ((POPEN_BLOCK)DeferredContext); + PPERF_BLOCK Perform = OpenP->Perform; + NDIS_STATUS Status; + + NdisAcquireSpinLock( &OpenP->SpinLock ); + if (Perform->SendBurstCount == 0) + { + Perform->SendBurstCount = 1; + NdisReleaseSpinLock( &OpenP->SpinLock ); + Perform->RestartCount++; + ++Perform->SendCount; + ++Perform->PacketsPending; + + NdisSend( &Status,OpenP->NdisBindingHandle, Perform->DataReq->u.PERF_REQ.Packet ); + if ( Status != NDIS_STATUS_PENDING ) + { + TpPerfSendComplete( OpenP, Perform->DataReq->u.PERF_REQ.Packet, Status ); + } + } +} + + +// ----------------------------------------------------------------- +// +// Function: TpPerfWriteResults +// +// Arguments: Perform -- ptr to perform block structure +// Info -- ptr to data received from server (may be NULL) +// +// Returns: none +// +// Descript: This function writes the Performance test statistics into the +// output buffer passed in by the ioctl call. NOTE: the +// OpenP->SpinLock must be held when making this call +// +// ------------------------------------------------------------------ + + +VOID +TpPerfWriteResults( IN PPERF_BLOCK Perform, + IN PRESULTS_PACKET_INFO Info) +{ + PPERF_RESULTS OutputBuffer; + + // + // Get the output buffer out of the MDL stored in the IRP, and map + // it so we may write the statistics to it. + // + + if (( Perform->PerformIrp != NULL ) && + ( Perform->PerformIrp->Cancel == FALSE )) + { + OutputBuffer = MmGetSystemAddressForMdl( Perform->PerformIrp->MdlAddress ); + + // + // Write the statistics to the outbuffer + // + + OutputBuffer->Signature = PERF_RESULTS_SIGNATURE; + OutputBuffer->ResultsExist = TRUE; + OutputBuffer->Mode = Perform->PerformMode; + OutputBuffer->PacketSize = Perform->PacketSize; + OutputBuffer->PacketCount = Perform->NumberOfPackets; + OutputBuffer->Milliseconds = Perform->PerfSendTotalTime.LowPart; + OutputBuffer->Sends = Perform->SendCount; + OutputBuffer->SendFails = Perform->SendFailCount; + OutputBuffer->Receives = Perform->ReceiveCount; + OutputBuffer->SelfReceives = Perform->SelfReceiveCount; + OutputBuffer->Restarts = Perform->RestartCount; + + if (Info != NULL) + { + OutputBuffer->S_Milliseconds = Info->ElapsedTime; + OutputBuffer->S_Sends = Info->PacketsSent; + OutputBuffer->S_SendFails = Info->SendErrors; + OutputBuffer->S_Receives = Info->PacketsReceived; + OutputBuffer->S_SelfReceives = Info->SelfReceives; + OutputBuffer->S_Restarts = Info->Restarts; + } + } +} + + +NDIS_STATUS +TpPerfAbort(POPEN_BLOCK OpenP) +{ + LARGE_INTEGER DueTime; + PPERF_BLOCK Perform = OpenP->Perform; + + // + // We want to stop any active client and/or server on this open + // instance from running the performance routines, so clear the + // Active flag, queue up the EndDpc function, and wait for it + // to finish + // + + Perform->Active = FALSE; + + DueTime.HighPart = 0xFFFFFFFF; // So it will be relative. + DueTime.LowPart = (ULONG)(-(ONE_SECOND)); + + for(;;) + { + KeCancelTimer(&Perform->PerformTimer); + if ( KeSetTimer(&Perform->PerformTimer, + DueTime, + &Perform->PerformEndDpc )) + { + IF_TPDBG ( TP_DEBUG_DPC ) + { + TpPrint0( "TpPerfAbort: set PerformTimer while timer existed.\n"); + } + } + else + { + break; + } + } + + // + // And wait for them to finish. + // + + while ( OpenP->PerformanceTest == TRUE ) + { + /* NULL */ ; + } + return NDIS_STATUS_SUCCESS; +} + + + |