[10733] | 1 | /* $Id: tstIntNetR0.cpp 104159 2024-04-04 15:35:39Z vboxsync $ */
|
---|
[1] | 2 | /** @file
|
---|
[10733] | 3 | * Internal networking - Usermode testcase for the kernel mode bits.
|
---|
[1] | 4 | *
|
---|
[10733] | 5 | * This is a bit hackish as we're mixing context here, however it is
|
---|
| 6 | * very useful when making changes to the internal networking service.
|
---|
[1] | 7 | */
|
---|
| 8 |
|
---|
| 9 | /*
|
---|
[98103] | 10 | * Copyright (C) 2006-2023 Oracle and/or its affiliates.
|
---|
[1] | 11 | *
|
---|
[96407] | 12 | * This file is part of VirtualBox base platform packages, as
|
---|
| 13 | * available from https://www.virtualbox.org.
|
---|
| 14 | *
|
---|
| 15 | * This program is free software; you can redistribute it and/or
|
---|
| 16 | * modify it under the terms of the GNU General Public License
|
---|
| 17 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 18 | * License.
|
---|
| 19 | *
|
---|
| 20 | * This program is distributed in the hope that it will be useful, but
|
---|
| 21 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 23 | * General Public License for more details.
|
---|
| 24 | *
|
---|
| 25 | * You should have received a copy of the GNU General Public License
|
---|
| 26 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 27 | *
|
---|
| 28 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[1] | 29 | */
|
---|
| 30 |
|
---|
| 31 |
|
---|
[57358] | 32 | /*********************************************************************************************************************************
|
---|
| 33 | * Header Files *
|
---|
| 34 | *********************************************************************************************************************************/
|
---|
[1] | 35 | #define IN_INTNET_TESTCASE
|
---|
| 36 | #define IN_INTNET_R3
|
---|
| 37 | #include <VBox/cdefs.h>
|
---|
| 38 | #undef INTNETR0DECL
|
---|
| 39 | #define INTNETR0DECL INTNETR3DECL
|
---|
[10681] | 40 | #undef DECLR0CALLBACKMEMBER
|
---|
| 41 | #define DECLR0CALLBACKMEMBER(type, name, args) DECLR3CALLBACKMEMBER(type, name, args)
|
---|
[10819] | 42 | #include <VBox/types.h>
|
---|
| 43 | typedef void *MYPSUPDRVSESSION;
|
---|
| 44 | #define PSUPDRVSESSION MYPSUPDRVSESSION
|
---|
| 45 |
|
---|
[1] | 46 | #include <VBox/intnet.h>
|
---|
| 47 | #include <VBox/sup.h>
|
---|
| 48 | #include <VBox/err.h>
|
---|
[28623] | 49 | #include <iprt/asm.h>
|
---|
| 50 | #include <iprt/getopt.h>
|
---|
| 51 | #include <iprt/initterm.h>
|
---|
| 52 | #include <iprt/mem.h>
|
---|
| 53 | #include <iprt/mp.h>
|
---|
[1] | 54 | #include <iprt/stream.h>
|
---|
| 55 | #include <iprt/thread.h>
|
---|
| 56 | #include <iprt/time.h>
|
---|
[28623] | 57 | #include <iprt/test.h>
|
---|
[1] | 58 |
|
---|
| 59 |
|
---|
[57358] | 60 | /*********************************************************************************************************************************
|
---|
| 61 | * Structures and Typedefs *
|
---|
| 62 | *********************************************************************************************************************************/
|
---|
[1] | 63 | /**
|
---|
| 64 | * Security objectype.
|
---|
| 65 | */
|
---|
| 66 | typedef enum SUPDRVOBJTYPE
|
---|
| 67 | {
|
---|
| 68 | /** The usual invalid object. */
|
---|
| 69 | SUPDRVOBJTYPE_INVALID = 0,
|
---|
| 70 | /** Internal network. */
|
---|
| 71 | SUPDRVOBJTYPE_INTERNAL_NETWORK,
|
---|
| 72 | /** Internal network interface. */
|
---|
| 73 | SUPDRVOBJTYPE_INTERNAL_NETWORK_INTERFACE,
|
---|
| 74 | /** The first invalid object type in this end. */
|
---|
| 75 | SUPDRVOBJTYPE_END,
|
---|
| 76 | /** The usual 32-bit type size hack. */
|
---|
| 77 | SUPDRVOBJTYPE_32_BIT_HACK = 0x7ffffff
|
---|
| 78 | } SUPDRVOBJTYPE;
|
---|
| 79 |
|
---|
| 80 | /**
|
---|
| 81 | * Object destructor callback.
|
---|
| 82 | * This is called for reference counted objectes when the count reaches 0.
|
---|
| 83 | *
|
---|
| 84 | * @param pvObj The object pointer.
|
---|
| 85 | * @param pvUser1 The first user argument.
|
---|
| 86 | * @param pvUser2 The second user argument.
|
---|
| 87 | */
|
---|
[85121] | 88 | typedef DECLCALLBACKTYPE(void, FNSUPDRVDESTRUCTOR,(void *pvObj, void *pvUser1, void *pvUser2));
|
---|
[1] | 89 | /** Pointer to a FNSUPDRVDESTRUCTOR(). */
|
---|
| 90 | typedef FNSUPDRVDESTRUCTOR *PFNSUPDRVDESTRUCTOR;
|
---|
| 91 |
|
---|
| 92 |
|
---|
| 93 | /**
|
---|
| 94 | * Dummy
|
---|
| 95 | */
|
---|
| 96 | typedef struct OBJREF
|
---|
| 97 | {
|
---|
| 98 | PFNSUPDRVDESTRUCTOR pfnDestructor;
|
---|
| 99 | void *pvUser1;
|
---|
| 100 | void *pvUser2;
|
---|
| 101 | uint32_t volatile cRefs;
|
---|
| 102 | } OBJREF, *POBJREF;
|
---|
| 103 |
|
---|
[10819] | 104 |
|
---|
[57358] | 105 | /*********************************************************************************************************************************
|
---|
| 106 | * Global Variables *
|
---|
| 107 | *********************************************************************************************************************************/
|
---|
[28623] | 108 | /** The test handle.*/
|
---|
| 109 | static RTTEST g_hTest = NIL_RTTEST;
|
---|
| 110 | /** The size (in bytes) of the large transfer tests. */
|
---|
| 111 | static uint32_t g_cbTransfer = _1M * 384;
|
---|
[1] | 112 | /** Fake session handle. */
|
---|
[66828] | 113 | const PSUPDRVSESSION g_pSession = (PSUPDRVSESSION)(uintptr_t)0xdeadface;
|
---|
[1] | 114 |
|
---|
| 115 |
|
---|
[39091] | 116 | INTNETR3DECL(void *) SUPR0ObjRegister(PSUPDRVSESSION pSession, SUPDRVOBJTYPE enmType,
|
---|
| 117 | PFNSUPDRVDESTRUCTOR pfnDestructor, void *pvUser1, void *pvUser2)
|
---|
[1] | 118 | {
|
---|
[28623] | 119 | RTTEST_CHECK_RET(g_hTest, pSession == g_pSession, NULL);
|
---|
| 120 | POBJREF pRef = (POBJREF)RTTestGuardedAllocTail(g_hTest, sizeof(OBJREF));
|
---|
[1] | 121 | if (!pRef)
|
---|
| 122 | return NULL;
|
---|
| 123 | pRef->cRefs = 1;
|
---|
| 124 | pRef->pfnDestructor = pfnDestructor;
|
---|
| 125 | pRef->pvUser1 = pvUser1;
|
---|
| 126 | pRef->pvUser2 = pvUser2;
|
---|
[39091] | 127 | NOREF(enmType);
|
---|
[1] | 128 | return pRef;
|
---|
| 129 | }
|
---|
| 130 |
|
---|
[15505] | 131 | INTNETR3DECL(int) SUPR0ObjAddRefEx(void *pvObj, PSUPDRVSESSION pSession, bool fNoBlocking)
|
---|
[1] | 132 | {
|
---|
[28623] | 133 | RTTEST_CHECK_RET(g_hTest, pSession == g_pSession, VERR_INVALID_PARAMETER);
|
---|
[1] | 134 | POBJREF pRef = (POBJREF)pvObj;
|
---|
| 135 | ASMAtomicIncU32(&pRef->cRefs);
|
---|
[39091] | 136 | NOREF(fNoBlocking);
|
---|
[1] | 137 | return VINF_SUCCESS;
|
---|
| 138 | }
|
---|
| 139 |
|
---|
[15505] | 140 | INTNETR3DECL(int) SUPR0ObjAddRef(void *pvObj, PSUPDRVSESSION pSession)
|
---|
| 141 | {
|
---|
| 142 | return SUPR0ObjAddRefEx(pvObj, pSession, false);
|
---|
| 143 | }
|
---|
| 144 |
|
---|
[1] | 145 | INTNETR3DECL(int) SUPR0ObjRelease(void *pvObj, PSUPDRVSESSION pSession)
|
---|
| 146 | {
|
---|
[28623] | 147 | RTTEST_CHECK_RET(g_hTest, pSession == g_pSession, VERR_INVALID_PARAMETER);
|
---|
[1] | 148 | POBJREF pRef = (POBJREF)pvObj;
|
---|
| 149 | if (!ASMAtomicDecU32(&pRef->cRefs))
|
---|
| 150 | {
|
---|
| 151 | pRef->pfnDestructor(pRef, pRef->pvUser1, pRef->pvUser2);
|
---|
[28623] | 152 | RTTestGuardedFree(g_hTest, pRef);
|
---|
[15842] | 153 | return VINF_OBJECT_DESTROYED;
|
---|
[1] | 154 | }
|
---|
| 155 | return VINF_SUCCESS;
|
---|
| 156 | }
|
---|
| 157 |
|
---|
| 158 | INTNETR3DECL(int) SUPR0ObjVerifyAccess(void *pvObj, PSUPDRVSESSION pSession, const char *pszObjName)
|
---|
| 159 | {
|
---|
[28623] | 160 | RTTEST_CHECK_RET(g_hTest, pSession == g_pSession, VERR_INVALID_PARAMETER);
|
---|
[39091] | 161 | NOREF(pvObj); NOREF(pszObjName);
|
---|
[1] | 162 | return VINF_SUCCESS;
|
---|
| 163 | }
|
---|
| 164 |
|
---|
[1481] | 165 | INTNETR3DECL(int) SUPR0MemAlloc(PSUPDRVSESSION pSession, uint32_t cb, PRTR0PTR ppvR0, PRTR3PTR ppvR3)
|
---|
[1] | 166 | {
|
---|
[28623] | 167 | RTTEST_CHECK_RET(g_hTest, pSession == g_pSession, VERR_INVALID_PARAMETER);
|
---|
| 168 | void *pv = RTTestGuardedAllocTail(g_hTest, cb);
|
---|
[1] | 169 | if (!pv)
|
---|
| 170 | return VERR_NO_MEMORY;
|
---|
[1481] | 171 | *ppvR0 = (RTR0PTR)pv;
|
---|
[1] | 172 | if (ppvR3)
|
---|
| 173 | *ppvR3 = pv;
|
---|
| 174 | return VINF_SUCCESS;
|
---|
| 175 | }
|
---|
| 176 |
|
---|
[1483] | 177 | INTNETR3DECL(int) SUPR0MemFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr)
|
---|
[1] | 178 | {
|
---|
[28623] | 179 | RTTEST_CHECK_RET(g_hTest, pSession == g_pSession, VERR_INVALID_PARAMETER);
|
---|
| 180 | RTTestGuardedFree(g_hTest, (void *)uPtr);
|
---|
[1] | 181 | return VINF_SUCCESS;
|
---|
| 182 | }
|
---|
| 183 |
|
---|
[28623] | 184 | /* Fake non-existing ring-0 APIs. */
|
---|
| 185 | #define RTThreadIsInInterrupt(hThread) false
|
---|
| 186 | #define RTThreadPreemptIsEnabled(hThread) true
|
---|
| 187 | #define RTMpCpuId() 0
|
---|
[1] | 188 |
|
---|
[28623] | 189 | /* No CLI/POPF, please. */
|
---|
[40806] | 190 | #include <iprt/spinlock.h>
|
---|
| 191 | #undef RTSPINLOCK_FLAGS_INTERRUPT_SAFE
|
---|
| 192 | #define RTSPINLOCK_FLAGS_INTERRUPT_SAFE RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE
|
---|
[1] | 193 |
|
---|
[28623] | 194 |
|
---|
[1] | 195 | /* ugly but necessary for making R0 code compilable for R3. */
|
---|
[1640] | 196 | #undef LOG_GROUP
|
---|
[1] | 197 | #include "../SrvIntNetR0.cpp"
|
---|
| 198 |
|
---|
[28025] | 199 |
|
---|
| 200 | /**
|
---|
| 201 | * Sends the data @a pvBuf points to.
|
---|
| 202 | */
|
---|
[28706] | 203 | static int tstIntNetSendBuf(PINTNETRINGBUF pRingBuf, INTNETIFHANDLE hIf,
|
---|
[28025] | 204 | PSUPDRVSESSION pSession, void const *pvBuf, size_t cbBuf)
|
---|
| 205 | {
|
---|
[104159] | 206 | union
|
---|
| 207 | {
|
---|
| 208 | uint8_t abBuf[sizeof(INTNETSG) + sizeof(INTNETSEG)];
|
---|
| 209 | INTNETSG SG;
|
---|
| 210 | } u;
|
---|
| 211 | IntNetSgInitTemp(&u.SG, (void *)pvBuf, (uint32_t)cbBuf);
|
---|
| 212 | int rc = intnetR0RingWriteFrame(pRingBuf, &u.SG, NULL);
|
---|
[28025] | 213 | if (RT_SUCCESS(rc))
|
---|
[28706] | 214 | rc = IntNetR0IfSend(hIf, pSession);
|
---|
[28025] | 215 | return rc;
|
---|
| 216 | }
|
---|
| 217 |
|
---|
| 218 |
|
---|
[26574] | 219 | typedef struct MYARGS
|
---|
[1] | 220 | {
|
---|
[28623] | 221 | PINTNETBUF pBuf;
|
---|
| 222 | INTNETIFHANDLE hIf;
|
---|
| 223 | RTMAC Mac;
|
---|
[29234] | 224 | uint32_t cbFrame;
|
---|
[28623] | 225 | uint64_t u64Start;
|
---|
| 226 | uint64_t u64End;
|
---|
[97299] | 227 | uint32_t cbSent;
|
---|
| 228 | uint32_t cFramesSent;
|
---|
[26574] | 229 | } MYARGS, *PMYARGS;
|
---|
[1] | 230 |
|
---|
| 231 |
|
---|
[28623] | 232 | /**
|
---|
| 233 | * Frame header used when testing.
|
---|
| 234 | */
|
---|
| 235 | #pragma pack(1)
|
---|
| 236 | typedef struct MYFRAMEHDR
|
---|
| 237 | {
|
---|
| 238 | RTMAC SrcMac;
|
---|
| 239 | RTMAC DstMac;
|
---|
| 240 | uint32_t iFrame;
|
---|
| 241 | uint32_t auEos[3];
|
---|
| 242 | } MYFRAMEHDR;
|
---|
| 243 | #pragma pack()
|
---|
[1] | 244 |
|
---|
| 245 | /**
|
---|
| 246 | * Send thread.
|
---|
[29234] | 247 | * This is constantly sending frames to the other interface.
|
---|
[1] | 248 | */
|
---|
[57442] | 249 | static DECLCALLBACK(int) SendThread(RTTHREAD hThreadSelf, void *pvArg)
|
---|
[1] | 250 | {
|
---|
[26574] | 251 | PMYARGS pArgs = (PMYARGS)pvArg;
|
---|
[28623] | 252 | int rc;
|
---|
[39091] | 253 | NOREF(hThreadSelf);
|
---|
[1] | 254 |
|
---|
| 255 | /*
|
---|
[28623] | 256 | * Send g_cbTransfer of data.
|
---|
[1] | 257 | */
|
---|
[29234] | 258 | uint8_t abBuf[16384] = {0};
|
---|
[28623] | 259 | MYFRAMEHDR *pHdr = (MYFRAMEHDR *)&abBuf[0];
|
---|
| 260 | uint32_t iFrame = 0;
|
---|
| 261 | uint32_t cbSent = 0;
|
---|
[97299] | 262 | uint32_t cErrors = 0;
|
---|
[28623] | 263 |
|
---|
| 264 | pHdr->SrcMac = pArgs->Mac;
|
---|
| 265 | pHdr->DstMac = pArgs->Mac;
|
---|
| 266 | pHdr->DstMac.au16[2] = (pArgs->Mac.au16[2] + 1) % 2;
|
---|
| 267 |
|
---|
[1] | 268 | pArgs->u64Start = RTTimeNanoTS();
|
---|
[28623] | 269 | for (; cbSent < g_cbTransfer; iFrame++)
|
---|
[1] | 270 | {
|
---|
[29234] | 271 | const unsigned cb = pArgs->cbFrame
|
---|
| 272 | ? pArgs->cbFrame
|
---|
| 273 | : iFrame % 1519 + sizeof(RTMAC) * 2 + sizeof(unsigned);
|
---|
| 274 | pHdr->iFrame = iFrame;
|
---|
[28025] | 275 |
|
---|
[104159] | 276 | union
|
---|
| 277 | {
|
---|
| 278 | uint8_t abBuf[sizeof(INTNETSG) + sizeof(INTNETSEG)];
|
---|
| 279 | INTNETSG SG;
|
---|
| 280 | } u;
|
---|
| 281 | IntNetSgInitTemp(&u.SG, abBuf, cb);
|
---|
| 282 | RTTEST_CHECK_RC_OK(g_hTest, rc = intnetR0RingWriteFrame(&pArgs->pBuf->Send, &u.SG, NULL));
|
---|
[5283] | 283 | if (RT_SUCCESS(rc))
|
---|
[28706] | 284 | RTTEST_CHECK_RC_OK(g_hTest, rc = IntNetR0IfSend(pArgs->hIf, g_pSession));
|
---|
[97299] | 285 | if (RT_FAILURE(rc) && ++cErrors > 64)
|
---|
| 286 | {
|
---|
| 287 | RTTestFailed(g_hTest, "Aborting xmit after >64 errors");
|
---|
| 288 | break;
|
---|
| 289 | }
|
---|
| 290 |
|
---|
[1] | 291 | cbSent += cb;
|
---|
| 292 | }
|
---|
[97299] | 293 | pArgs->cbSent = cbSent;
|
---|
| 294 | pArgs->cFramesSent = iFrame;
|
---|
[1] | 295 |
|
---|
| 296 | /*
|
---|
| 297 | * Termination frames.
|
---|
| 298 | */
|
---|
[28623] | 299 | pHdr->iFrame = 0xffffdead;
|
---|
| 300 | pHdr->auEos[0] = 0xffffdead;
|
---|
| 301 | pHdr->auEos[1] = 0xffffdead;
|
---|
| 302 | pHdr->auEos[2] = 0xffffdead;
|
---|
[1] | 303 | for (unsigned c = 0; c < 20; c++)
|
---|
| 304 | {
|
---|
[28706] | 305 | RTTEST_CHECK_RC_OK(g_hTest, rc = tstIntNetSendBuf(&pArgs->pBuf->Send, pArgs->hIf, g_pSession,
|
---|
[28623] | 306 | abBuf, sizeof(RTMAC) * 2 + sizeof(unsigned) * 4));
|
---|
[1] | 307 | RTThreadSleep(1);
|
---|
| 308 | }
|
---|
| 309 |
|
---|
[28623] | 310 | RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
|
---|
| 311 | "sender thread %.6Rhxs terminating.\n"
|
---|
| 312 | "iFrame=%u cb=%'u\n",
|
---|
| 313 | &pArgs->Mac, iFrame, cbSent);
|
---|
[1] | 314 | return 0;
|
---|
| 315 | }
|
---|
| 316 |
|
---|
| 317 |
|
---|
| 318 | /** Ignore lost frames. It only makes things worse to bitch about it. */
|
---|
| 319 | #define IGNORE_LOST_FRAMES
|
---|
| 320 |
|
---|
| 321 | /**
|
---|
| 322 | * Receive thread.
|
---|
| 323 | * This is reading stuff from the network.
|
---|
| 324 | */
|
---|
[57442] | 325 | static DECLCALLBACK(int) ReceiveThread(RTTHREAD hThreadSelf, void *pvArg)
|
---|
[1] | 326 | {
|
---|
[26574] | 327 | uint32_t cbReceived = 0;
|
---|
| 328 | uint32_t cLostFrames = 0;
|
---|
| 329 | uint32_t iFrame = UINT32_MAX;
|
---|
| 330 | PMYARGS pArgs = (PMYARGS)pvArg;
|
---|
[39091] | 331 | NOREF(hThreadSelf);
|
---|
| 332 |
|
---|
[1] | 333 | for (;;)
|
---|
| 334 | {
|
---|
| 335 | /*
|
---|
| 336 | * Read data.
|
---|
| 337 | */
|
---|
[28714] | 338 | while (IntNetRingHasMoreToRead(&pArgs->pBuf->Recv))
|
---|
[1] | 339 | {
|
---|
[29234] | 340 | uint8_t abBuf[16384 + 1024];
|
---|
[28623] | 341 | MYFRAMEHDR *pHdr = (MYFRAMEHDR *)&abBuf[0];
|
---|
[28714] | 342 | uint32_t cb = IntNetRingReadAndSkipFrame(&pArgs->pBuf->Recv, abBuf);
|
---|
[1] | 343 |
|
---|
| 344 | /* check for termination frame. */
|
---|
[28623] | 345 | if ( pHdr->iFrame == 0xffffdead
|
---|
| 346 | && pHdr->auEos[0] == 0xffffdead
|
---|
| 347 | && pHdr->auEos[1] == 0xffffdead
|
---|
| 348 | && pHdr->auEos[2] == 0xffffdead)
|
---|
[1] | 349 | {
|
---|
| 350 | pArgs->u64End = RTTimeNanoTS();
|
---|
[28623] | 351 | RTThreadSleep(10);
|
---|
| 352 | RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
|
---|
| 353 | "receiver thread %.6Rhxs terminating.\n"
|
---|
| 354 | " iFrame=%u cb=%'u c=%'u %'uKB/s %'ufps cLost=%'u \n",
|
---|
| 355 | &pArgs->Mac, iFrame, cbReceived, iFrame - cLostFrames,
|
---|
[93307] | 356 | (unsigned)((double)cbReceived * 1000000000.0 / 1024 / (double)(pArgs->u64End - pArgs->u64Start)),
|
---|
| 357 | (unsigned)((double)(iFrame - cLostFrames) * 1000000000.0 / (double)(pArgs->u64End - pArgs->u64Start)),
|
---|
[28623] | 358 | cLostFrames);
|
---|
[1] | 359 | return VINF_SUCCESS;
|
---|
| 360 | }
|
---|
| 361 |
|
---|
| 362 | /* validate frame header */
|
---|
[28623] | 363 | if ( pHdr->DstMac.au16[0] != pArgs->Mac.au16[0]
|
---|
| 364 | || pHdr->DstMac.au16[1] != pArgs->Mac.au16[1]
|
---|
| 365 | || pHdr->DstMac.au16[2] != pArgs->Mac.au16[2]
|
---|
| 366 | || pHdr->SrcMac.au16[0] != pArgs->Mac.au16[0]
|
---|
| 367 | || pHdr->SrcMac.au16[1] != pArgs->Mac.au16[1]
|
---|
| 368 | || pHdr->SrcMac.au16[2] != (pArgs->Mac.au16[2] + 1) % 2)
|
---|
[1] | 369 | {
|
---|
[28623] | 370 | RTTestFailed(g_hTest, "receiver thread %.6Rhxs received frame header: %.16Rhxs\n", &pArgs->Mac, abBuf);
|
---|
[1] | 371 | }
|
---|
| 372 |
|
---|
| 373 | /* frame stuff and stats. */
|
---|
[28623] | 374 | int32_t off = pHdr->iFrame - (iFrame + 1);
|
---|
[1] | 375 | if (off)
|
---|
| 376 | {
|
---|
| 377 | if (off > 0)
|
---|
| 378 | {
|
---|
[28623] | 379 | #ifndef IGNORE_LOST_FRAMES
|
---|
| 380 | RTTestFailed(g_hTest, "receiver thread %.6Rhxs: iFrame=%#x *puFrame=%#x off=%d\n",
|
---|
| 381 | &pArgs->Mac, iFrame, pHdr->iFrame, off);
|
---|
| 382 | #endif
|
---|
| 383 | cLostFrames += off;
|
---|
[1] | 384 | }
|
---|
| 385 | else
|
---|
| 386 | {
|
---|
[28623] | 387 | cLostFrames++;
|
---|
| 388 | RTTestFailed(g_hTest, "receiver thread %.6Rhxs: iFrame=%#x *puFrame=%#x off=%d\n",
|
---|
| 389 | &pArgs->Mac, iFrame, pHdr->iFrame, off);
|
---|
[1] | 390 | }
|
---|
| 391 | }
|
---|
[28623] | 392 | iFrame = pHdr->iFrame;
|
---|
[1] | 393 | cbReceived += cb;
|
---|
| 394 | }
|
---|
[28623] | 395 |
|
---|
| 396 | /*
|
---|
| 397 | * Wait for data.
|
---|
| 398 | */
|
---|
[28706] | 399 | int rc = IntNetR0IfWait(pArgs->hIf, g_pSession, RT_INDEFINITE_WAIT);
|
---|
[28623] | 400 | switch (rc)
|
---|
| 401 | {
|
---|
| 402 | case VERR_INTERRUPTED:
|
---|
| 403 | case VINF_SUCCESS:
|
---|
| 404 | break;
|
---|
| 405 | case VERR_SEM_DESTROYED:
|
---|
| 406 | RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
|
---|
| 407 | "receiver thread %.6Rhxs terminating. iFrame=%u cb=%'u c=%'u cLost=%'u\n",
|
---|
| 408 | &pArgs->Mac, iFrame, cbReceived, iFrame - cLostFrames, cLostFrames);
|
---|
| 409 | return VINF_SUCCESS;
|
---|
| 410 |
|
---|
| 411 | default:
|
---|
| 412 | RTTestFailed(g_hTest, "receiver thread %.6Rhxs got odd return value %Rrc! iFrame=%u cb=%'u c=%'u cLost=%'u\n",
|
---|
| 413 | &pArgs->Mac, rc, iFrame, cbReceived, iFrame - cLostFrames, cLostFrames);
|
---|
| 414 | return rc;
|
---|
| 415 | }
|
---|
| 416 |
|
---|
[1] | 417 | }
|
---|
| 418 | }
|
---|
| 419 |
|
---|
[28623] | 420 |
|
---|
| 421 | /**
|
---|
[97299] | 422 | * Drains the interface buffer before starting a new bi-directional run.
|
---|
| 423 | *
|
---|
| 424 | * We may have termination frames from previous runs pending in the buffer.
|
---|
| 425 | */
|
---|
| 426 | static void tstDrainInterfaceBuffer(PMYARGS pArgs)
|
---|
| 427 | {
|
---|
| 428 | uint8_t abBuf[16384 + 1024];
|
---|
| 429 | while (IntNetRingHasMoreToRead(&pArgs->pBuf->Recv))
|
---|
| 430 | IntNetRingReadAndSkipFrame(&pArgs->pBuf->Recv, abBuf);
|
---|
| 431 | }
|
---|
| 432 |
|
---|
| 433 |
|
---|
| 434 | /**
|
---|
[28623] | 435 | * Test state.
|
---|
| 436 | */
|
---|
| 437 | typedef struct TSTSTATE
|
---|
[1] | 438 | {
|
---|
[28623] | 439 | PINTNETBUF pBuf0;
|
---|
| 440 | INTNETIFHANDLE hIf0;
|
---|
| 441 |
|
---|
| 442 | PINTNETBUF pBuf1;
|
---|
| 443 | INTNETIFHANDLE hIf1;
|
---|
| 444 | } TSTSTATE;
|
---|
| 445 | typedef TSTSTATE *PTSTSTATE;
|
---|
| 446 |
|
---|
| 447 |
|
---|
| 448 | /**
|
---|
| 449 | * Open two internal network interfaces.
|
---|
| 450 | *
|
---|
| 451 | * @returns IPRT status of the first failure.
|
---|
| 452 | * @param pThis The test instance.
|
---|
| 453 | */
|
---|
| 454 | static int tstOpenInterfaces(PTSTSTATE pThis, const char *pszNetwork, uint32_t cbSend, uint32_t cbRecv)
|
---|
| 455 | {
|
---|
| 456 | pThis->hIf0 = INTNET_HANDLE_INVALID;
|
---|
[97339] | 457 | RTTESTI_CHECK_RC_OK_RET(IntNetR0Open(g_pSession, pszNetwork, kIntNetTrunkType_None, "", 0/*fFlags*/, cbSend, cbRecv,
|
---|
[97338] | 458 | NULL /*pfnRecvAvail*/, NULL /*pvUser*/, &pThis->hIf0), rcCheck);
|
---|
[28623] | 459 | RTTESTI_CHECK_RET(pThis->hIf0 != INTNET_HANDLE_INVALID, VERR_INTERNAL_ERROR);
|
---|
[28711] | 460 | RTTESTI_CHECK_RC_RET(IntNetR0IfGetBufferPtrs(pThis->hIf0, g_pSession, &pThis->pBuf0, NULL), VINF_SUCCESS, rcCheck);
|
---|
[28623] | 461 | RTTESTI_CHECK_RET(pThis->pBuf0, VERR_INTERNAL_ERROR);
|
---|
| 462 |
|
---|
| 463 |
|
---|
| 464 | pThis->hIf1 = INTNET_HANDLE_INVALID;
|
---|
[97339] | 465 | RTTESTI_CHECK_RC_OK_RET(IntNetR0Open(g_pSession, pszNetwork, kIntNetTrunkType_None, "", 0/*fFlags*/, cbSend, cbRecv,
|
---|
[97338] | 466 | NULL /*pfnRecvAvail*/, NULL /*pvUser*/, &pThis->hIf1), rcCheck);
|
---|
[28623] | 467 | RTTESTI_CHECK_RET(pThis->hIf1 != INTNET_HANDLE_INVALID, VERR_INTERNAL_ERROR);
|
---|
[28711] | 468 | RTTESTI_CHECK_RC_RET(IntNetR0IfGetBufferPtrs(pThis->hIf1, g_pSession, &pThis->pBuf1, NULL), VINF_SUCCESS, rcCheck);
|
---|
[28623] | 469 | RTTESTI_CHECK_RET(pThis->pBuf1, VERR_INTERNAL_ERROR);
|
---|
| 470 |
|
---|
| 471 | return VINF_SUCCESS;
|
---|
| 472 | }
|
---|
| 473 |
|
---|
| 474 | /**
|
---|
| 475 | * Close the interfaces.
|
---|
| 476 | *
|
---|
| 477 | * @param pThis The test instance.
|
---|
| 478 | */
|
---|
| 479 | static void tstCloseInterfaces(PTSTSTATE pThis)
|
---|
| 480 | {
|
---|
| 481 | int rc;
|
---|
[28706] | 482 | RTTESTI_CHECK_RC_OK(rc = IntNetR0IfClose(pThis->hIf0, g_pSession));
|
---|
[28623] | 483 | if (RT_SUCCESS(rc))
|
---|
[10740] | 484 | {
|
---|
[28623] | 485 | pThis->hIf0 = INTNET_HANDLE_INVALID;
|
---|
| 486 | pThis->pBuf0 = NULL;
|
---|
| 487 | }
|
---|
[10740] | 488 |
|
---|
[28706] | 489 | RTTESTI_CHECK_RC_OK(rc = IntNetR0IfClose(pThis->hIf1, g_pSession));
|
---|
[28623] | 490 | if (RT_SUCCESS(rc))
|
---|
| 491 | {
|
---|
| 492 | pThis->hIf1 = INTNET_HANDLE_INVALID;
|
---|
| 493 | pThis->pBuf1 = NULL;
|
---|
| 494 | }
|
---|
[10740] | 495 |
|
---|
[28623] | 496 | /* The network should be dead now. */
|
---|
[28706] | 497 | RTTESTI_CHECK(IntNetR0GetNetworkCount() == 0);
|
---|
[28623] | 498 | }
|
---|
| 499 |
|
---|
| 500 | /**
|
---|
| 501 | * Do the bi-directional transfer test.
|
---|
| 502 | */
|
---|
[29234] | 503 | static void tstBidirectionalTransfer(PTSTSTATE pThis, uint32_t cbFrame)
|
---|
[28623] | 504 | {
|
---|
[97299] | 505 | RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "-------------------------------------------------------------\n");
|
---|
| 506 |
|
---|
| 507 | /*
|
---|
| 508 | * Reset statistics.
|
---|
| 509 | */
|
---|
| 510 | pThis->pBuf0->cStatYieldsOk.c = 0;
|
---|
| 511 | pThis->pBuf0->cStatYieldsNok.c = 0;
|
---|
| 512 | pThis->pBuf0->cStatLost.c = 0;
|
---|
| 513 | pThis->pBuf0->cStatBadFrames.c = 0;
|
---|
| 514 | pThis->pBuf0->Recv.cStatFrames.c = 0;
|
---|
| 515 | pThis->pBuf0->Recv.cbStatWritten.c = 0;
|
---|
| 516 | pThis->pBuf0->Recv.cOverflows.c = 0;
|
---|
| 517 | pThis->pBuf0->Send.cStatFrames.c = 0;
|
---|
| 518 | pThis->pBuf0->Send.cbStatWritten.c = 0;
|
---|
| 519 | pThis->pBuf0->Send.cOverflows.c = 0;
|
---|
| 520 | pThis->pBuf1->cStatYieldsOk.c = 0;
|
---|
| 521 | pThis->pBuf1->cStatYieldsNok.c = 0;
|
---|
| 522 | pThis->pBuf1->cStatLost.c = 0;
|
---|
| 523 | pThis->pBuf1->cStatBadFrames.c = 0;
|
---|
| 524 | pThis->pBuf1->Recv.cStatFrames.c = 0;
|
---|
| 525 | pThis->pBuf1->Recv.cbStatWritten.c = 0;
|
---|
| 526 | pThis->pBuf1->Recv.cOverflows.c = 0;
|
---|
| 527 | pThis->pBuf1->Send.cStatFrames.c = 0;
|
---|
| 528 | pThis->pBuf1->Send.cbStatWritten.c = 0;
|
---|
| 529 | pThis->pBuf1->Send.cOverflows.c = 0;
|
---|
| 530 |
|
---|
| 531 | /*
|
---|
| 532 | * Do the benchmarking.
|
---|
| 533 | */
|
---|
[28623] | 534 | MYARGS Args0;
|
---|
| 535 | RT_ZERO(Args0);
|
---|
| 536 | Args0.hIf = pThis->hIf0;
|
---|
| 537 | Args0.pBuf = pThis->pBuf0;
|
---|
| 538 | Args0.Mac.au16[0] = 0x8086;
|
---|
| 539 | Args0.Mac.au16[1] = 0;
|
---|
| 540 | Args0.Mac.au16[2] = 0;
|
---|
[29234] | 541 | Args0.cbFrame = cbFrame;
|
---|
[97299] | 542 | tstDrainInterfaceBuffer(&Args0);
|
---|
| 543 | //RTTESTI_CHECK_RC_OK(IntNetR0IfSetMacAddress(pThis->hIf0, g_pSession, &Args0.Mac));
|
---|
[28623] | 544 |
|
---|
| 545 | MYARGS Args1;
|
---|
| 546 | RT_ZERO(Args1);
|
---|
| 547 | Args1.hIf = pThis->hIf1;
|
---|
| 548 | Args1.pBuf = pThis->pBuf1;
|
---|
| 549 | Args1.Mac.au16[0] = 0x8086;
|
---|
| 550 | Args1.Mac.au16[1] = 0;
|
---|
| 551 | Args1.Mac.au16[2] = 1;
|
---|
[29234] | 552 | Args1.cbFrame = cbFrame;
|
---|
[97299] | 553 | tstDrainInterfaceBuffer(&Args1);
|
---|
| 554 | //RTTESTI_CHECK_RC_OK(IntNetR0IfSetMacAddress(pThis->hIf1, g_pSession, &Args1.Mac));
|
---|
[28623] | 555 |
|
---|
| 556 | RTTHREAD ThreadRecv0 = NIL_RTTHREAD;
|
---|
| 557 | RTTHREAD ThreadRecv1 = NIL_RTTHREAD;
|
---|
| 558 | RTTHREAD ThreadSend0 = NIL_RTTHREAD;
|
---|
| 559 | RTTHREAD ThreadSend1 = NIL_RTTHREAD;
|
---|
| 560 | RTTESTI_CHECK_RC_OK_RETV(RTThreadCreate(&ThreadRecv0, ReceiveThread, &Args0, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "RECV0"));
|
---|
| 561 | RTTESTI_CHECK_RC_OK_RETV(RTThreadCreate(&ThreadRecv1, ReceiveThread, &Args1, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "RECV1"));
|
---|
| 562 | RTTESTI_CHECK_RC_OK_RETV(RTThreadCreate(&ThreadSend0, SendThread, &Args0, 0, RTTHREADTYPE_EMULATION, RTTHREADFLAGS_WAITABLE, "SEND0"));
|
---|
| 563 | RTTESTI_CHECK_RC_OK_RETV(RTThreadCreate(&ThreadSend1, SendThread, &Args1, 0, RTTHREADTYPE_EMULATION, RTTHREADFLAGS_WAITABLE, "SEND1"));
|
---|
| 564 |
|
---|
| 565 | int rc2 = VINF_SUCCESS;
|
---|
| 566 | int rc;
|
---|
[97299] | 567 | RTTESTI_CHECK_RC_OK(rc = RTThreadWait(ThreadSend0, RT_MS_5MIN, &rc2));
|
---|
[28623] | 568 | if (RT_SUCCESS(rc))
|
---|
| 569 | {
|
---|
| 570 | RTTESTI_CHECK_RC_OK(rc2);
|
---|
| 571 | ThreadSend0 = NIL_RTTHREAD;
|
---|
[97299] | 572 | RTTESTI_CHECK_RC_OK(rc = RTThreadWait(ThreadSend1, RT_MS_5MIN, RT_SUCCESS(rc2) ? &rc2 : NULL));
|
---|
[28623] | 573 | if (RT_SUCCESS(rc))
|
---|
[10740] | 574 | {
|
---|
[28623] | 575 | ThreadSend1 = NIL_RTTHREAD;
|
---|
| 576 | RTTESTI_CHECK_RC_OK(rc2);
|
---|
| 577 | }
|
---|
| 578 | }
|
---|
| 579 | if (RTTestErrorCount(g_hTest) == 0)
|
---|
| 580 | {
|
---|
| 581 | /*
|
---|
| 582 | * Wait a bit for the receivers to finish up.
|
---|
| 583 | */
|
---|
| 584 | unsigned cYields = 100000;
|
---|
[28714] | 585 | while ( ( IntNetRingHasMoreToRead(&pThis->pBuf0->Recv)
|
---|
| 586 | || IntNetRingHasMoreToRead(&pThis->pBuf1->Recv))
|
---|
[28623] | 587 | && cYields-- > 0)
|
---|
| 588 | RTThreadYield();
|
---|
[10740] | 589 |
|
---|
[28623] | 590 | /*
|
---|
| 591 | * Wait for the threads to finish up...
|
---|
| 592 | */
|
---|
[97299] | 593 | RTTESTI_CHECK_RC_OK(rc = RTThreadWait(ThreadRecv0, RT_MS_5SEC, &rc2));
|
---|
[28623] | 594 | if (RT_SUCCESS(rc))
|
---|
| 595 | {
|
---|
| 596 | RTTESTI_CHECK_RC_OK(rc2);
|
---|
| 597 | ThreadRecv0 = NIL_RTTHREAD;
|
---|
[10740] | 598 | }
|
---|
| 599 |
|
---|
[97299] | 600 | RTTESTI_CHECK_RC_OK(rc = RTThreadWait(ThreadRecv1, RT_MS_5MIN, &rc2));
|
---|
[28623] | 601 | if (RT_SUCCESS(rc))
|
---|
| 602 | {
|
---|
| 603 | RTTESTI_CHECK_RC_OK(rc2);
|
---|
| 604 | ThreadRecv1 = NIL_RTTHREAD;
|
---|
| 605 | }
|
---|
[97299] | 606 |
|
---|
| 607 | /*
|
---|
| 608 | * Report the results.
|
---|
| 609 | */
|
---|
| 610 | uint64_t cNsElapsed = RT_MAX(Args0.u64End, Args1.u64End) - RT_MIN(Args0.u64Start, Args1.u64Start);
|
---|
| 611 | uint64_t cbSent = (uint64_t)Args0.cbSent + Args1.cbSent;
|
---|
| 612 | uint64_t cKbps = (uint64_t)((double)(cbSent / 1024) / ((double)cNsElapsed / 1000000000.0));
|
---|
| 613 | uint64_t cFrames = (uint64_t)Args0.cFramesSent + Args1.cFramesSent;
|
---|
[97300] | 614 | uint64_t cFps = (uint64_t)((double)cFrames / ((double)cNsElapsed / 1000000000.0));
|
---|
[97299] | 615 | RTTestValue(g_hTest, "frame size", cbFrame, RTTESTUNIT_BYTES);
|
---|
| 616 | RTTestValue(g_hTest, "xmit time", cNsElapsed, RTTESTUNIT_NS);
|
---|
| 617 | RTTestValue(g_hTest, "bytes sent", cbSent, RTTESTUNIT_BYTES);
|
---|
| 618 | RTTestValue(g_hTest, "speed", cKbps, RTTESTUNIT_KILOBYTES_PER_SEC);
|
---|
| 619 | RTTestValue(g_hTest, "frames sent", cFrames, RTTESTUNIT_FRAMES);
|
---|
| 620 | RTTestValue(g_hTest, "fps", cFps, RTTESTUNIT_FRAMES_PER_SEC);
|
---|
| 621 | RTTestValue(g_hTest, "overflows",
|
---|
| 622 | pThis->pBuf0->Send.cOverflows.c + pThis->pBuf1->Send.cOverflows.c, RTTESTUNIT_OCCURRENCES);
|
---|
| 623 |
|
---|
[28623] | 624 | }
|
---|
| 625 |
|
---|
[1] | 626 | /*
|
---|
[28623] | 627 | * Give them a chance to complete...
|
---|
[1] | 628 | */
|
---|
[97299] | 629 | RTThreadWait(ThreadRecv0, RT_MS_5MIN, NULL);
|
---|
| 630 | RTThreadWait(ThreadRecv1, RT_MS_5MIN, NULL);
|
---|
| 631 | RTThreadWait(ThreadSend0, RT_MS_5MIN, NULL);
|
---|
| 632 | RTThreadWait(ThreadSend1, RT_MS_5MIN, NULL);
|
---|
[1] | 633 |
|
---|
[28623] | 634 |
|
---|
[1] | 635 | /*
|
---|
[28623] | 636 | * Display statistics.
|
---|
[1] | 637 | */
|
---|
[28623] | 638 | RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
|
---|
| 639 | "Buf0: Yields-OK=%llu Yields-NOK=%llu Lost=%llu Bad=%llu\n",
|
---|
| 640 | pThis->pBuf0->cStatYieldsOk.c,
|
---|
| 641 | pThis->pBuf0->cStatYieldsNok.c,
|
---|
| 642 | pThis->pBuf0->cStatLost.c,
|
---|
| 643 | pThis->pBuf0->cStatBadFrames.c);
|
---|
| 644 | RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
|
---|
| 645 | "Buf0.Recv: Frames=%llu Bytes=%llu Overflows=%llu\n",
|
---|
[83815] | 646 | pThis->pBuf0->Recv.cStatFrames.c,
|
---|
[28623] | 647 | pThis->pBuf0->Recv.cbStatWritten.c,
|
---|
| 648 | pThis->pBuf0->Recv.cOverflows.c);
|
---|
| 649 | RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
|
---|
| 650 | "Buf0.Send: Frames=%llu Bytes=%llu Overflows=%llu\n",
|
---|
[83815] | 651 | pThis->pBuf0->Send.cStatFrames.c,
|
---|
[28623] | 652 | pThis->pBuf0->Send.cbStatWritten.c,
|
---|
| 653 | pThis->pBuf0->Send.cOverflows.c);
|
---|
[1] | 654 |
|
---|
[28623] | 655 | RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
|
---|
| 656 | "Buf1: Yields-OK=%llu Yields-NOK=%llu Lost=%llu Bad=%llu\n",
|
---|
| 657 | pThis->pBuf1->cStatYieldsOk.c,
|
---|
| 658 | pThis->pBuf1->cStatYieldsNok.c,
|
---|
| 659 | pThis->pBuf1->cStatLost.c,
|
---|
| 660 | pThis->pBuf1->cStatBadFrames.c);
|
---|
| 661 | RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
|
---|
| 662 | "Buf1.Recv: Frames=%llu Bytes=%llu Overflows=%llu\n",
|
---|
[83815] | 663 | pThis->pBuf1->Recv.cStatFrames.c,
|
---|
[28623] | 664 | pThis->pBuf1->Recv.cbStatWritten.c,
|
---|
| 665 | pThis->pBuf1->Recv.cOverflows.c);
|
---|
| 666 | RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
|
---|
| 667 | "Buf1.Send: Frames=%llu Bytes=%llu Overflows=%llu\n",
|
---|
[83815] | 668 | pThis->pBuf1->Send.cStatFrames.c,
|
---|
[28623] | 669 | pThis->pBuf1->Send.cbStatWritten.c,
|
---|
| 670 | pThis->pBuf1->Send.cOverflows.c);
|
---|
[10847] | 671 |
|
---|
[28623] | 672 | }
|
---|
[10847] | 673 |
|
---|
[28623] | 674 | /**
|
---|
| 675 | * Performs a simple broadcast test.
|
---|
| 676 | *
|
---|
| 677 | * @param pThis The test instance.
|
---|
| 678 | * @param fHeadGuard Whether to use a head or tail guard.
|
---|
| 679 | */
|
---|
| 680 | static void doBroadcastTest(PTSTSTATE pThis, bool fHeadGuard)
|
---|
| 681 | {
|
---|
| 682 | static uint16_t const s_au16Frame[7] = { /* dst:*/ 0xffff, 0xffff, 0xffff, /*src:*/0x8086, 0, 0, 0x0800 };
|
---|
[1] | 683 |
|
---|
[28706] | 684 | RTTESTI_CHECK_RC_RETV(tstIntNetSendBuf(&pThis->pBuf0->Send, pThis->hIf0,
|
---|
[28623] | 685 | g_pSession, &s_au16Frame, sizeof(s_au16Frame)),
|
---|
| 686 | VINF_SUCCESS);
|
---|
[1] | 687 |
|
---|
[28623] | 688 | /* No echo, please */
|
---|
[28706] | 689 | RTTESTI_CHECK_RC_RETV(IntNetR0IfWait(pThis->hIf0, g_pSession, 1), VERR_TIMEOUT);
|
---|
[1] | 690 |
|
---|
[28623] | 691 | /* The other interface should see it though. But Wait should only return once, thank you. */
|
---|
[28706] | 692 | RTTESTI_CHECK_RC_RETV(IntNetR0IfWait(pThis->hIf1, g_pSession, 1), VINF_SUCCESS);
|
---|
| 693 | RTTESTI_CHECK_RC_RETV(IntNetR0IfWait(pThis->hIf1, g_pSession, 0), VERR_TIMEOUT);
|
---|
[1] | 694 |
|
---|
[28623] | 695 | /* Receive the data. */
|
---|
| 696 | const unsigned cbExpect = RT_ALIGN(sizeof(s_au16Frame) + sizeof(INTNETHDR), sizeof(INTNETHDR));
|
---|
[28714] | 697 | RTTESTI_CHECK_MSG(IntNetRingGetReadable(&pThis->pBuf1->Recv) == cbExpect,
|
---|
| 698 | ("%#x vs. %#x\n", IntNetRingGetReadable(&pThis->pBuf1->Recv), cbExpect));
|
---|
[1] | 699 |
|
---|
[28623] | 700 | void *pvBuf;
|
---|
| 701 | RTTESTI_CHECK_RC_OK_RETV(RTTestGuardedAlloc(g_hTest, sizeof(s_au16Frame), 1, fHeadGuard, &pvBuf));
|
---|
| 702 | uint32_t cb;
|
---|
[28714] | 703 | RTTESTI_CHECK_MSG_RETV((cb = IntNetRingReadAndSkipFrame(&pThis->pBuf1->Recv, pvBuf)) == sizeof(s_au16Frame),
|
---|
[28623] | 704 | ("%#x vs. %#x\n", cb, sizeof(s_au16Frame)));
|
---|
[1] | 705 |
|
---|
[28623] | 706 | if (memcmp(pvBuf, &s_au16Frame, sizeof(s_au16Frame)))
|
---|
| 707 | RTTestIFailed("Got invalid data!\n"
|
---|
| 708 | "received: %.*Rhxs\n"
|
---|
| 709 | "expected: %.*Rhxs\n",
|
---|
| 710 | cb, pvBuf, sizeof(s_au16Frame), &s_au16Frame);
|
---|
| 711 | }
|
---|
[1] | 712 |
|
---|
[28623] | 713 | /**
|
---|
| 714 | * Performs a simple unicast test.
|
---|
| 715 | *
|
---|
| 716 | * @param pThis The test instance.
|
---|
| 717 | * @param fHeadGuard Whether to use a head or tail guard.
|
---|
| 718 | */
|
---|
| 719 | static void doUnicastTest(PTSTSTATE pThis, bool fHeadGuard)
|
---|
| 720 | {
|
---|
| 721 | static uint16_t const s_au16Frame[7] = { /* dst:*/ 0x8086, 0, 0, /*src:*/0x8086, 0, 1, 0x0800 };
|
---|
[1] | 722 |
|
---|
[28706] | 723 | RTTESTI_CHECK_RC_RETV(tstIntNetSendBuf(&pThis->pBuf1->Send, pThis->hIf1,
|
---|
[28623] | 724 | g_pSession, s_au16Frame, sizeof(s_au16Frame)),
|
---|
| 725 | VINF_SUCCESS);
|
---|
[10557] | 726 |
|
---|
[28623] | 727 | /* No echo, please */
|
---|
[28706] | 728 | RTTESTI_CHECK_RC_RETV(IntNetR0IfWait(pThis->hIf1, g_pSession, 1), VERR_TIMEOUT);
|
---|
[10557] | 729 |
|
---|
[28623] | 730 | /* The other interface should see it though. But Wait should only return once, thank you. */
|
---|
[28706] | 731 | RTTESTI_CHECK_RC_RETV(IntNetR0IfWait(pThis->hIf0, g_pSession, 1), VINF_SUCCESS);
|
---|
| 732 | RTTESTI_CHECK_RC_RETV(IntNetR0IfWait(pThis->hIf0, g_pSession, 0), VERR_TIMEOUT);
|
---|
[10557] | 733 |
|
---|
[28623] | 734 | /* Receive the data. */
|
---|
| 735 | const unsigned cbExpect = RT_ALIGN(sizeof(s_au16Frame) + sizeof(INTNETHDR), sizeof(INTNETHDR));
|
---|
[28714] | 736 | RTTESTI_CHECK_MSG(IntNetRingGetReadable(&pThis->pBuf0->Recv) == cbExpect,
|
---|
| 737 | ("%#x vs. %#x\n", IntNetRingGetReadable(&pThis->pBuf0->Recv), cbExpect));
|
---|
[1] | 738 |
|
---|
[28623] | 739 | void *pvBuf;
|
---|
| 740 | RTTESTI_CHECK_RC_OK_RETV(RTTestGuardedAlloc(g_hTest, sizeof(s_au16Frame), 1, fHeadGuard, &pvBuf));
|
---|
| 741 | uint32_t cb;
|
---|
[28714] | 742 | RTTESTI_CHECK_MSG_RETV((cb = IntNetRingReadAndSkipFrame(&pThis->pBuf0->Recv, pvBuf)) == sizeof(s_au16Frame),
|
---|
[28623] | 743 | ("%#x vs. %#x\n", cb, sizeof(s_au16Frame)));
|
---|
[1] | 744 |
|
---|
[28623] | 745 | if (memcmp(pvBuf, &s_au16Frame, sizeof(s_au16Frame)))
|
---|
| 746 | RTTestIFailed("Got invalid data!\n"
|
---|
| 747 | "received: %.*Rhxs\n"
|
---|
| 748 | "expected: %.*Rhxs\n",
|
---|
| 749 | cb, pvBuf, sizeof(s_au16Frame), s_au16Frame);
|
---|
| 750 | }
|
---|
[10557] | 751 |
|
---|
[28623] | 752 | static void doTest(PTSTSTATE pThis, uint32_t cbRecv, uint32_t cbSend)
|
---|
| 753 | {
|
---|
[10557] | 754 |
|
---|
[28623] | 755 | /*
|
---|
| 756 | * Create an INTNET instance.
|
---|
| 757 | */
|
---|
[28706] | 758 | RTTestISub("IntNetR0Init");
|
---|
| 759 | RTTESTI_CHECK_RC_RETV(IntNetR0Init(), VINF_SUCCESS);
|
---|
[10557] | 760 |
|
---|
[28623] | 761 | /*
|
---|
| 762 | * Create two interfaces and activate them.
|
---|
| 763 | */
|
---|
| 764 | RTTestISub("Network creation");
|
---|
| 765 | int rc = tstOpenInterfaces(pThis, "test", cbSend, cbRecv);
|
---|
| 766 | if (RT_FAILURE(rc))
|
---|
| 767 | return;
|
---|
[28706] | 768 | RTTESTI_CHECK_RC(IntNetR0IfSetActive(pThis->hIf0, g_pSession, true), VINF_SUCCESS);
|
---|
| 769 | RTTESTI_CHECK_RC(IntNetR0IfSetActive(pThis->hIf1, g_pSession, true), VINF_SUCCESS);
|
---|
[10557] | 770 |
|
---|
[28623] | 771 | /*
|
---|
| 772 | * Test basic waiting.
|
---|
| 773 | */
|
---|
[28706] | 774 | RTTestISub("IntNetR0IfWait");
|
---|
| 775 | RTTESTI_CHECK_RC(IntNetR0IfWait(pThis->hIf0, g_pSession, 1), VERR_TIMEOUT);
|
---|
| 776 | RTTESTI_CHECK_RC(IntNetR0IfWait(pThis->hIf0, g_pSession, 0), VERR_TIMEOUT);
|
---|
| 777 | RTTESTI_CHECK_RC(IntNetR0IfWait(pThis->hIf1, g_pSession, 1), VERR_TIMEOUT);
|
---|
| 778 | RTTESTI_CHECK_RC(IntNetR0IfWait(pThis->hIf1, g_pSession, 0), VERR_TIMEOUT);
|
---|
[28623] | 779 |
|
---|
| 780 | /*
|
---|
| 781 | * Broadcast send and receive.
|
---|
| 782 | * (This establishes the MAC address of the 1st interface.)
|
---|
| 783 | */
|
---|
| 784 | RTTestISub("Broadcast");
|
---|
| 785 | doBroadcastTest(pThis, false /*fHeadGuard*/);
|
---|
| 786 | doBroadcastTest(pThis, true /*fHeadGuard*/);
|
---|
| 787 |
|
---|
| 788 | /*
|
---|
| 789 | * Unicast send and receive.
|
---|
| 790 | * (This establishes the MAC address of the 2nd interface.)
|
---|
| 791 | */
|
---|
| 792 | RTTestISub("Unicast");
|
---|
| 793 | doUnicastTest(pThis, false /*fHeadGuard*/);
|
---|
| 794 | doUnicastTest(pThis, true /*fHeadGuard*/);
|
---|
| 795 |
|
---|
| 796 | /*
|
---|
| 797 | * Do the big bi-directional transfer test if the basics worked out.
|
---|
| 798 | */
|
---|
| 799 | if (!RTTestIErrorCount())
|
---|
[1] | 800 | {
|
---|
[97294] | 801 | RTTestISubF("bi-dir benchmark, xbuf=%u rbuf=%u xfer=%u",
|
---|
[28623] | 802 | pThis->pBuf0->cbSend, pThis->pBuf0->cbRecv, g_cbTransfer);
|
---|
[29234] | 803 | tstBidirectionalTransfer(pThis, 256);
|
---|
| 804 |
|
---|
[97299] | 805 | /* Only doing up to half the xmit buffer size as it is easy to get into a
|
---|
| 806 | bad frame position from a previous run and run into overflow issues. */
|
---|
| 807 | /** @todo fix the code so it skips to a more optimal buffer position? */
|
---|
| 808 | for (uint32_t cbFrame = 64; cbFrame < cbSend / 2 - 64; cbFrame += 16)
|
---|
[29234] | 809 | {
|
---|
[97299] | 810 | RTTestISubF("bi-dir benchmark, xbuf=%u rbuf=%u xmit=%u frm=%u",
|
---|
[29234] | 811 | pThis->pBuf0->cbSend, pThis->pBuf0->cbRecv, g_cbTransfer, cbFrame);
|
---|
| 812 | tstBidirectionalTransfer(pThis, cbFrame);
|
---|
| 813 | }
|
---|
[1] | 814 | }
|
---|
| 815 |
|
---|
| 816 | /*
|
---|
| 817 | * Destroy the service.
|
---|
| 818 | */
|
---|
[28623] | 819 | tstCloseInterfaces(pThis);
|
---|
[28706] | 820 | IntNetR0Term();
|
---|
[28623] | 821 | }
|
---|
[1] | 822 |
|
---|
[28623] | 823 |
|
---|
| 824 | int main(int argc, char **argv)
|
---|
| 825 | {
|
---|
| 826 | int rc = RTTestInitAndCreate("tstIntNetR0", &g_hTest);
|
---|
| 827 | if (rc)
|
---|
| 828 | return rc;
|
---|
| 829 |
|
---|
[1] | 830 | /*
|
---|
[28623] | 831 | * Parse the arguments.
|
---|
[1] | 832 | */
|
---|
[28623] | 833 | static RTGETOPTDEF const s_aOptions[] =
|
---|
| 834 | {
|
---|
| 835 | { "--recv-buffer", 'r', RTGETOPT_REQ_UINT32 },
|
---|
| 836 | { "--send-buffer", 's', RTGETOPT_REQ_UINT32 },
|
---|
| 837 | { "--transfer-size", 'l', RTGETOPT_REQ_UINT32 },
|
---|
| 838 | };
|
---|
[1] | 839 |
|
---|
[28623] | 840 | uint32_t cbSend = 1536*2 + 4;
|
---|
| 841 | uint32_t cbRecv = 0x8000;
|
---|
| 842 |
|
---|
| 843 | int ch;
|
---|
| 844 | RTGETOPTUNION Value;
|
---|
| 845 | RTGETOPTSTATE GetState;
|
---|
| 846 | RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
|
---|
| 847 | while ((ch = RTGetOpt(&GetState, &Value)))
|
---|
| 848 | switch (ch)
|
---|
| 849 | {
|
---|
| 850 | case 'l':
|
---|
| 851 | g_cbTransfer = Value.u32;
|
---|
| 852 | break;
|
---|
| 853 |
|
---|
| 854 | case 'r':
|
---|
| 855 | cbRecv = Value.u32;
|
---|
| 856 | break;
|
---|
| 857 |
|
---|
| 858 | case 's':
|
---|
| 859 | cbSend = Value.u32;
|
---|
| 860 | break;
|
---|
| 861 |
|
---|
| 862 | default:
|
---|
| 863 | return RTGetOptPrintError(ch, &Value);
|
---|
| 864 | }
|
---|
| 865 |
|
---|
| 866 | /*
|
---|
| 867 | * Do the testing and report summary.
|
---|
| 868 | */
|
---|
| 869 | TSTSTATE This;
|
---|
| 870 | RT_ZERO(This);
|
---|
| 871 | doTest(&This, cbRecv, cbSend);
|
---|
| 872 |
|
---|
| 873 | return RTTestSummaryAndDestroy(g_hTest);
|
---|
[1] | 874 | }
|
---|
| 875 |
|
---|