VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DrvIntNet.cpp@ 571

Last change on this file since 571 was 559, checked in by vboxsync, 18 years ago

Added INetworkConnector callback for sending multiple packets

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.3 KB
Line 
1/** @file
2 *
3 * VBox network devices:
4 * Internal network transport driver
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DRV_INTNET
28#include <VBox/pdm.h>
29#include <VBox/cfgm.h>
30#include <VBox/intnet.h>
31#include <VBox/vmm.h>
32#include <VBox/err.h>
33
34#include <VBox/log.h>
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/thread.h>
38#include <iprt/semaphore.h>
39#include <iprt/string.h>
40#include <iprt/time.h>
41
42#include <string.h>
43
44#include "Builtins.h"
45
46
47/*******************************************************************************
48* Structures and Typedefs *
49*******************************************************************************/
50/**
51 * The state of the asynchronous thread.
52 */
53typedef enum ASYNCSTATE
54{
55 /** The thread is suspended. */
56 ASYNCSTATE_SUSPENDED = 1,
57 /** The thread is running. */
58 ASYNCSTATE_RUNNING,
59 /** The thread must (/has) terminate. */
60 ASYNCSTATE_TERMINATE,
61 /** The usual 32-bit type blowup. */
62 ASYNCSTATE_32BIT_HACK = 0x7fffffff
63} ASYNCSTATE;
64
65/**
66 * Block driver instance data.
67 */
68typedef struct DRVINTNET
69{
70 /** The network interface. */
71 PDMINETWORKCONNECTOR INetworkConnector;
72 /** The network interface. */
73 PPDMINETWORKPORT pPort;
74 /** Pointer to the driver instance. */
75 PPDMDRVINS pDrvIns;
76 /** Interface handle. */
77 INTNETIFHANDLE hIf;
78 /** Pointer to the communication buffer. */
79 PINTNETBUF pBuf;
80 /** The thread state. */
81 ASYNCSTATE volatile enmState;
82 /** Reader thread. */
83 RTTHREAD Thread;
84 /** Event semaphore the Thread waits on while the VM is suspended. */
85 RTSEMEVENT EventSuspended;
86 /** Indicates that we're in waiting for recieve space to become available. */
87 bool volatile fOutOfSpace;
88 /** Event semaphore the Thread sleeps on while polling for more
89 * buffer space to become available.
90 * @todo We really need the network device to signal this! */
91 RTSEMEVENT EventOutOfSpace;
92 /** Set if the link is down.
93 * When the link is down all incoming packets will be dropped. */
94 bool volatile fLinkDown;
95
96#ifdef VBOX_WITH_STATISTICS
97 /** Profiling packet transmit runs. */
98 STAMPROFILE StatTransmit;
99 /** Profiling packet receive runs. */
100 STAMPROFILEADV StatReceive;
101 /** Number of receive overflows. */
102 STAMPROFILE StatRecvOverflows;
103#endif /* VBOX_WITH_STATISTICS */
104
105#ifdef LOG_ENABLED
106 /** The nano ts of the last transfer. */
107 uint64_t u64LastTransferTS;
108 /** The nano ts of the last receive. */
109 uint64_t u64LastReceiveTS;
110#endif
111 /** The network name. */
112 char szNetwork[INTNET_MAX_NETWORK_NAME];
113} DRVINTNET, *PDRVINTNET;
114
115
116/** Converts a pointer to DRVINTNET::INetworkConnector to a PDRVINTNET. */
117#define PDMINETWORKCONNECTOR_2_DRVINTNET(pInterface) ( (PDRVINTNET)((uintptr_t)pInterface - RT_OFFSETOF(DRVINTNET, INetworkConnector)) )
118
119
120/**
121 * Send data to the network.
122 *
123 * @returns VBox status code.
124 * @param pInterface Pointer to the interface structure containing the called function pointer.
125 * @param pvBuf Data to send.
126 * @param cb Number of bytes to send.
127 * @thread EMT
128 */
129static DECLCALLBACK(int) drvIntNetSend(PPDMINETWORKCONNECTOR pInterface, const void *pvBuf, size_t cb)
130{
131 PDRVINTNET pThis = PDMINETWORKCONNECTOR_2_DRVINTNET(pInterface);
132 STAM_PROFILE_START(&pThis->StatTransmit, a);
133
134#ifdef LOG_ENABLED
135 uint64_t u64Now = RTTimeProgramNanoTS();
136 LogFlow(("drvIntNetSend: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
137 cb, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
138 pThis->u64LastTransferTS = u64Now;
139 Log2(("drvIntNetSend: pvBuf=%p cb=%#x\n"
140 "%.*Vhxd\n",
141 pvBuf, cb, cb, pvBuf));
142#endif
143
144 /** @todo copy to send buffer, this is not safe. */
145 INTNETIFSENDARGS SendArgs;
146 SendArgs.hIf = pThis->hIf;
147 SendArgs.pvFrame = pvBuf;
148 SendArgs.cbFrame = cb;
149 int rc = pThis->pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pThis->pDrvIns, VMMR0_DO_INTNET_IF_SEND, &SendArgs, sizeof(SendArgs));
150
151 STAM_PROFILE_STOP(&pThis->StatTransmit, a);
152 AssertRC(rc);
153 return rc;
154}
155
156
157/**
158 * Send multiple data packets to the network.
159 *
160 * @returns VBox status code.
161 * @param pInterface Pointer to the interface structure containing the called function pointer.
162 * @param cPackets Number of packets
163 * @param paPacket Packet description array
164 * @thread EMT
165 */
166static DECLCALLBACK(int) drvIntNetSendEx(PPDMINETWORKCONNECTOR pInterface, uint32_t cPackets, PPDMINETWORKPACKET paPacket)
167{
168 int rc = VERR_INVALID_PARAMETER;
169
170 for (uint32_t i=0;i<cPackets;i++)
171 {
172 rc = drvIntNetSend(pInterface, paPacket[i].pvBuf, paPacket[i].cb);
173 if (VBOX_FAILURE(rc))
174 break;
175 }
176 return rc;
177}
178
179/**
180 * Set promiscuous mode.
181 *
182 * This is called when the promiscuous mode is set. This means that there doesn't have
183 * to be a mode change when it's called.
184 *
185 * @param pInterface Pointer to the interface structure containing the called function pointer.
186 * @param fPromiscuous Set if the adaptor is now in promiscuous mode. Clear if it is not.
187 * @thread EMT
188 */
189static DECLCALLBACK(void) drvIntNetSetPromiscuousMode(PPDMINETWORKCONNECTOR pInterface, bool fPromiscuous)
190{
191 PDRVINTNET pThis = PDMINETWORKCONNECTOR_2_DRVINTNET(pInterface);
192 INTNETIFSETPROMISCUOUSMODEARGS SetArgs = {0};
193 SetArgs.hIf = pThis->hIf;
194 SetArgs.fPromiscuous = fPromiscuous;
195 int rc = pThis->pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pThis->pDrvIns, VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE, &SetArgs, sizeof(SetArgs));
196 LogFlow(("drvIntNetSetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
197 AssertRC(rc);
198}
199
200
201/**
202 * Notification on link status changes.
203 *
204 * @param pInterface Pointer to the interface structure containing the called function pointer.
205 * @param enmLinkState The new link state.
206 * @thread EMT
207 */
208static DECLCALLBACK(void) drvIntNetNotifyLinkChanged(PPDMINETWORKCONNECTOR pInterface, PDMNETWORKLINKSTATE enmLinkState)
209{
210 PDRVINTNET pThis = PDMINETWORKCONNECTOR_2_DRVINTNET(pInterface);
211 bool fLinkDown;
212 switch (enmLinkState)
213 {
214 case PDMNETWORKLINKSTATE_DOWN:
215 case PDMNETWORKLINKSTATE_DOWN_RESUME:
216 fLinkDown = true;
217 break;
218 default:
219 AssertMsgFailed(("enmLinkState=%d\n", enmLinkState));
220 case PDMNETWORKLINKSTATE_UP:
221 fLinkDown = false;
222 break;
223 }
224 LogFlow(("drvNATNotifyLinkChanged: enmLinkState=%d %d->%d\n", enmLinkState, pThis->fLinkDown, fLinkDown));
225 ASMAtomicXchgSize(&pThis->fLinkDown, fLinkDown);
226}
227
228
229/**
230 * More receive buffer has become available.
231 *
232 * This is called when the NIC frees up receive buffers.
233 *
234 * @param pInterface Pointer to the interface structure containing the called function pointer.
235 * @remark This function isn't called by pcnet nor yet.
236 * @thread EMT
237 */
238static DECLCALLBACK(void) drvIntNetNotifyCanReceive(PPDMINETWORKCONNECTOR pInterface)
239{
240 PDRVINTNET pThis = PDMINETWORKCONNECTOR_2_DRVINTNET(pInterface);
241 if (pThis->fOutOfSpace)
242 {
243 LogFlow(("drvIntNetNotifyCanReceive: signaling\n"));
244 RTSemEventSignal(pThis->EventOutOfSpace);
245 }
246}
247
248
249/**
250 * Wait for space to become available up the driver/device chain.
251 *
252 * @returns VINF_SUCCESS if space is available.
253 * @returns VERR_STATE_CHANGED if the state changed.
254 * @returns VBox status code on other errors.
255 * @param pThis Pointer to the instance data.
256 * @param cbFrame The frame size.
257 */
258static int drvIntNetAsyncIoWaitForSpace(PDRVINTNET pThis, size_t cbFrame)
259{
260 LogFlow(("drvIntNetAsyncIoWaitForSpace: cbFrame=%zu\n", cbFrame));
261 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
262 STAM_PROFILE_START(&pData->StatRecvOverflows, b);
263
264 ASMAtomicXchgSize(&pThis->fOutOfSpace, true);
265 int rc;
266 unsigned cYields = 0;
267 for (;;)
268 {
269 /* yield/sleep */
270 if ( !RTThreadYield()
271 || ++cYields % 100 == 0)
272 {
273 /** @todo we need a callback from the device which can wake us up here. */
274 rc = RTSemEventWait(pThis->EventOutOfSpace, 1);
275 if ( VBOX_FAILURE(rc)
276 && rc != VERR_TIMEOUT)
277 break;
278 }
279 if (pThis->enmState != ASYNCSTATE_RUNNING)
280 {
281 rc = VERR_STATE_CHANGED;
282 break;
283 }
284
285 /* retry */
286 size_t cbMax = pThis->pPort->pfnCanReceive(pThis->pPort);
287 if (cbMax >= cbFrame)
288 {
289 rc = VINF_SUCCESS;
290 break;
291 }
292 }
293 ASMAtomicXchgSize(&pThis->fOutOfSpace, false);
294
295 STAM_PROFILE_STOP(&pThis->StatRecvOverflows, b);
296 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
297 LogFlow(("drvIntNetAsyncIoWaitForSpace: returns %Vrc\n", rc));
298 return rc;
299}
300
301
302/**
303 * Executes async I/O (RUNNING mode).
304 *
305 * @returns VERR_STATE_CHANGED if the state changed.
306 * @returns Appropriate VBox status code (error) on fatal error.
307 * @param pThis The driver instance data.
308 */
309static int drvIntNetAsyncIoRun(PDRVINTNET pThis)
310{
311 PPDMDRVINS pDrvIns = pThis->pDrvIns;
312 LogFlow(("drvIntNetAsyncIoRun: pThis=%p\n", pThis));
313
314 /*
315 * The running loop - processing received data and waiting for more to arrive.
316 */
317 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
318 PINTNETBUF pBuf = pThis->pBuf;
319 PINTNETRINGBUF pRingBuf = &pThis->pBuf->Recv;
320 for (;;)
321 {
322 /*
323 * Process the receive buffer.
324 */
325 while (INTNETRingGetReadable(pRingBuf) > 0)
326 {
327 /*
328 * Check the state and then inspect the packet.
329 */
330 if (pThis->enmState != ASYNCSTATE_RUNNING)
331 {
332 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
333 LogFlow(("drvIntNetAsyncIoRun: returns VERR_STATE_CHANGED (state changed - #0)\n"));
334 return VERR_STATE_CHANGED;
335 }
336
337 PINTNETHDR pHdr = (PINTNETHDR)((uintptr_t)pBuf + pRingBuf->offRead);
338 Log2(("pHdr=%p offRead=%#x: %.8Rhxs\n", pHdr, pRingBuf->offRead, pHdr));
339 if ( pHdr->u16Type == INTNETHDR_TYPE_FRAME
340 && !pThis->fLinkDown)
341 {
342 /*
343 * Check if there is room for the frame and pass it up.
344 */
345 size_t cbFrame = pHdr->cbFrame;
346 size_t cbMax = pThis->pPort->pfnCanReceive(pThis->pPort);
347 if (cbMax >= cbFrame)
348 {
349#ifdef LOG_ENABLED
350 uint64_t u64Now = RTTimeProgramNanoTS();
351 LogFlow(("drvIntNetAsyncIoRun: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
352 cbFrame, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
353 pThis->u64LastReceiveTS = u64Now;
354 Log2(("drvIntNetAsyncIoRun: cbFrame=%#x\n"
355 "%.*Vhxd\n",
356 cbFrame, cbFrame, INTNETHdrGetFramePtr(pHdr, pBuf)));
357#endif
358 int rc = pThis->pPort->pfnReceive(pThis->pPort, INTNETHdrGetFramePtr(pHdr, pBuf), cbFrame);
359 AssertRC(rc);
360
361 /* skip to the next frame. */
362 INTNETRingSkipFrame(pBuf, pRingBuf);
363 }
364 else
365 {
366 /*
367 * Wait for sufficient space to become available and then retry.
368 */
369 int rc = drvIntNetAsyncIoWaitForSpace(pThis, cbFrame);
370 if (VBOX_FAILURE(rc))
371 {
372 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
373 LogFlow(("drvIntNetAsyncIoRun: returns %Vrc (wait-for-space)\n", rc));
374 return rc;
375 }
376 }
377 }
378 else
379 {
380 /*
381 * Link down or unknown frame - skip to the next frame.
382 */
383 AssertMsg(pHdr->u16Type == INTNETHDR_TYPE_FRAME, ("Unknown frame type %RX16! offRead=%#x\n",
384 pHdr->u16Type, pRingBuf->offRead));
385 INTNETRingSkipFrame(pBuf, pRingBuf);
386 }
387 } /* while more received data */
388
389 /*
390 * Wait for data, checking the state before we block.
391 */
392 if (pThis->enmState != ASYNCSTATE_RUNNING)
393 {
394 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
395 LogFlow(("drvIntNetAsyncIoRun: returns VINF_SUCCESS (state changed - #1)\n"));
396 return VERR_STATE_CHANGED;
397 }
398 INTNETIFWAITARGS WaitArgs;
399 WaitArgs.hIf = pThis->hIf;
400 WaitArgs.cMillies = 30000; /* don't wait forever, timeout now and then. */
401 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
402 int rc = pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pDrvIns, VMMR0_DO_INTNET_IF_WAIT, &WaitArgs, sizeof(WaitArgs));
403 if ( VBOX_FAILURE(rc)
404 && rc != VERR_TIMEOUT
405 && rc != VERR_INTERRUPTED)
406 {
407 LogFlow(("drvIntNetAsyncIoRun: returns %Vrc\n", rc));
408 return rc;
409 }
410 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
411 }
412}
413
414
415/**
416 * Asynchronous I/O thread for handling receive.
417 *
418 * @returns VINF_SUCCESS (ignored).
419 * @param ThreadSelf Thread handle.
420 * @param pvUser Pointer to a DRVINTNET structure.
421 */
422static DECLCALLBACK(int) drvIntNetAsyncIoThread(RTTHREAD ThreadSelf, void *pvUser)
423{
424 PDRVINTNET pThis = (PDRVINTNET)pvUser;
425 LogFlow(("drvIntNetAsyncIoThread: pThis=%p\n", pThis));
426 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
427
428 /*
429 * The main loop - acting on state.
430 */
431 for (;;)
432 {
433 ASYNCSTATE enmState = pThis->enmState;
434 switch (enmState)
435 {
436 case ASYNCSTATE_SUSPENDED:
437 {
438 int rc = RTSemEventWait(pThis->EventSuspended, 30000);
439 if ( VBOX_FAILURE(rc)
440 && rc != VERR_TIMEOUT)
441 {
442 LogFlow(("drvIntNetAsyncIoThread: returns %Vrc\n", rc));
443 return rc;
444 }
445 break;
446 }
447
448 case ASYNCSTATE_RUNNING:
449 {
450 int rc = drvIntNetAsyncIoRun(pThis);
451 if ( rc != VERR_STATE_CHANGED
452 && VBOX_FAILURE(rc))
453 {
454 LogFlow(("drvIntNetAsyncIoThread: returns %Vrc\n", rc));
455 return rc;
456 }
457 break;
458 }
459
460 default:
461 AssertMsgFailed(("Invalid state %d\n", enmState));
462 case ASYNCSTATE_TERMINATE:
463 LogFlow(("drvIntNetAsyncIoThread: returns VINF_SUCCESS\n"));
464 return VINF_SUCCESS;
465 }
466 }
467}
468
469
470/**
471 * Queries an interface to the driver.
472 *
473 * @returns Pointer to interface.
474 * @returns NULL if the interface was not supported by the driver.
475 * @param pInterface Pointer to this interface structure.
476 * @param enmInterface The requested interface identification.
477 * @thread Any thread.
478 */
479static DECLCALLBACK(void *) drvIntNetQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
480{
481 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
482 PDRVINTNET pThis = PDMINS2DATA(pDrvIns, PDRVINTNET);
483 switch (enmInterface)
484 {
485 case PDMINTERFACE_BASE:
486 return &pDrvIns->IBase;
487 case PDMINTERFACE_NETWORK_CONNECTOR:
488 return &pThis->INetworkConnector;
489 default:
490 return NULL;
491 }
492}
493
494
495/**
496 * Power Off notification.
497 *
498 * @param pDrvIns The driver instance.
499 */
500static DECLCALLBACK(void) drvIntNetPowerOff(PPDMDRVINS pDrvIns)
501{
502 LogFlow(("drvIntNetPowerOff\n"));
503 PDRVINTNET pThis = PDMINS2DATA(pDrvIns, PDRVINTNET);
504 ASMAtomicXchgSize(&pThis->enmState, ASYNCSTATE_SUSPENDED);
505}
506
507
508/**
509 * Resume notification.
510 *
511 * @param pDrvIns The driver instance.
512 */
513static DECLCALLBACK(void) drvIntNetResume(PPDMDRVINS pDrvIns)
514{
515 LogFlow(("drvIntNetPowerResume\n"));
516 PDRVINTNET pThis = PDMINS2DATA(pDrvIns, PDRVINTNET);
517 ASMAtomicXchgSize(&pThis->enmState, ASYNCSTATE_RUNNING);
518 RTSemEventSignal(pThis->EventSuspended);
519}
520
521
522/**
523 * Suspend notification.
524 *
525 * @param pDrvIns The driver instance.
526 */
527static DECLCALLBACK(void) drvIntNetSuspend(PPDMDRVINS pDrvIns)
528{
529 LogFlow(("drvIntNetPowerSuspend\n"));
530 PDRVINTNET pThis = PDMINS2DATA(pDrvIns, PDRVINTNET);
531 ASMAtomicXchgSize(&pThis->enmState, ASYNCSTATE_SUSPENDED);
532}
533
534
535/**
536 * Power On notification.
537 *
538 * @param pDrvIns The driver instance.
539 */
540static DECLCALLBACK(void) drvIntNetPowerOn(PPDMDRVINS pDrvIns)
541{
542 LogFlow(("drvIntNetPowerOn\n"));
543 PDRVINTNET pThis = PDMINS2DATA(pDrvIns, PDRVINTNET);
544 ASMAtomicXchgSize(&pThis->enmState, ASYNCSTATE_RUNNING);
545 RTSemEventSignal(pThis->EventSuspended);
546}
547
548
549/**
550 * Destruct a driver instance.
551 *
552 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
553 * resources can be freed correctly.
554 *
555 * @param pDrvIns The driver instance data.
556 */
557static DECLCALLBACK(void) drvIntNetDestruct(PPDMDRVINS pDrvIns)
558{
559 LogFlow(("drvIntNetDestruct\n"));
560 PDRVINTNET pThis = PDMINS2DATA(pDrvIns, PDRVINTNET);
561
562 /*
563 * Indicate to the thread that it's time to quit.
564 */
565 ASMAtomicXchgSize(&pThis->enmState, ASYNCSTATE_TERMINATE);
566 ASMAtomicXchgSize(&pThis->fLinkDown, true);
567 RTSEMEVENT EventOutOfSpace = pThis->EventOutOfSpace;
568 pThis->EventOutOfSpace = NIL_RTSEMEVENT;
569 RTSEMEVENT EventSuspended = pThis->EventSuspended;
570 pThis->EventSuspended = NIL_RTSEMEVENT;
571
572 /*
573 * Close the interface
574 */
575 if (pThis->hIf != INTNET_HANDLE_INVALID)
576 {
577 INTNETIFCLOSEARGS CloseArgs = {0};
578 CloseArgs.hIf = pThis->hIf;
579 pThis->hIf = INTNET_HANDLE_INVALID;
580 int rc = pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pDrvIns, VMMR0_DO_INTNET_IF_CLOSE, &CloseArgs, sizeof(CloseArgs));
581 AssertRC(rc);
582 }
583
584 /*
585 * Wait for the thread to terminate.
586 */
587 if (pThis->Thread != NIL_RTTHREAD)
588 {
589 if (EventOutOfSpace != NIL_RTSEMEVENT)
590 RTSemEventSignal(EventOutOfSpace);
591 if (EventSuspended != NIL_RTSEMEVENT)
592 RTSemEventSignal(EventSuspended);
593 int rc = RTThreadWait(pThis->Thread, 5000, NULL);
594 AssertRC(rc);
595 pThis->Thread = NIL_RTTHREAD;
596 }
597
598 /*
599 * Destroy the semaphores.
600 */
601 if (EventOutOfSpace != NIL_RTSEMEVENT)
602 RTSemEventDestroy(EventOutOfSpace);
603 if (EventSuspended != NIL_RTSEMEVENT)
604 RTSemEventDestroy(EventSuspended);
605}
606
607
608/**
609 * Construct a TAP network transport driver instance.
610 *
611 * @returns VBox status.
612 * @param pDrvIns The driver instance data.
613 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
614 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
615 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
616 * iInstance it's expected to be used a bit in this function.
617 */
618static DECLCALLBACK(int) drvIntNetConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
619{
620 PDRVINTNET pThis = PDMINS2DATA(pDrvIns, PDRVINTNET);
621
622 /*
623 * Init the static parts.
624 */
625 pThis->pDrvIns = pDrvIns;
626 pThis->hIf = INTNET_HANDLE_INVALID;
627 pThis->Thread = NIL_RTTHREAD;
628 pThis->EventSuspended = NIL_RTSEMEVENT;
629 pThis->EventOutOfSpace = NIL_RTSEMEVENT;
630 pThis->enmState = ASYNCSTATE_SUSPENDED;
631 /* IBase */
632 pDrvIns->IBase.pfnQueryInterface = drvIntNetQueryInterface;
633 /* INetwork */
634 pThis->INetworkConnector.pfnSend = drvIntNetSend;
635 pThis->INetworkConnector.pfnSendEx = drvIntNetSendEx;
636 pThis->INetworkConnector.pfnSetPromiscuousMode = drvIntNetSetPromiscuousMode;
637 pThis->INetworkConnector.pfnNotifyLinkChanged = drvIntNetNotifyLinkChanged;
638 pThis->INetworkConnector.pfnNotifyCanReceive = drvIntNetNotifyCanReceive;
639
640 /*
641 * Validate the config.
642 */
643 if (!CFGMR3AreValuesValid(pCfgHandle, "Network\0ReceiveBufferSize\0SendBufferSize\0"))
644 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
645
646 /*
647 * Check that no-one is attached to us.
648 */
649 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, NULL);
650 if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
651 {
652 AssertMsgFailed(("Configuration error: Cannot attach drivers to the TAP driver!\n"));
653 return VERR_PDM_DRVINS_NO_ATTACH;
654 }
655
656 /*
657 * Query the network port interface.
658 */
659 pThis->pPort = (PPDMINETWORKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_NETWORK_PORT);
660 if (!pThis->pPort)
661 {
662 AssertMsgFailed(("Configuration error: the above device/driver didn't export the network port interface!\n"));
663 return VERR_PDM_MISSING_INTERFACE_ABOVE;
664 }
665
666 /*
667 * Read the configuration.
668 */
669 INTNETOPENARGS OpenArgs;
670 memset(&OpenArgs, 0, sizeof(OpenArgs));
671 rc = CFGMR3QueryString(pCfgHandle, "Network", OpenArgs.szNetwork, sizeof(OpenArgs.szNetwork));
672 if (VBOX_FAILURE(rc))
673 {
674 AssertMsgFailed(("Configuration error: query for \"Network\" string return %Vra.\n", rc));
675 return rc;
676 }
677 strcpy(pThis->szNetwork, OpenArgs.szNetwork);
678
679 rc = CFGMR3QueryU32(pCfgHandle, "ReceiveBufferSize", &OpenArgs.cbRecv);
680 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
681 OpenArgs.cbRecv = _256K;
682 else if (VBOX_FAILURE(rc))
683 {
684 AssertMsgFailed(("Configuration error: query for \"ReceiveBufferSize\" uint32_t return %Vra.\n", rc));
685 return rc;
686 }
687
688 rc = CFGMR3QueryU32(pCfgHandle, "SendBufferSize", &OpenArgs.cbSend);
689 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
690 OpenArgs.cbSend = _4K;
691 else if (VBOX_FAILURE(rc))
692 {
693 AssertMsgFailed(("Configuration error: query for \"SendBufferSize\" uint32_t return %Vra.\n", rc));
694 return rc;
695 }
696
697 /*
698 * Create the event semaphores
699 */
700 rc = RTSemEventCreate(&pThis->EventSuspended);
701 if (VBOX_FAILURE(rc))
702 return rc;
703 rc = RTSemEventCreate(&pThis->EventOutOfSpace);
704 if (VBOX_FAILURE(rc))
705 return rc;
706
707 /*
708 * Create the interface.
709 */
710 OpenArgs.hIf = INTNET_HANDLE_INVALID;
711 rc = pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pDrvIns, VMMR0_DO_INTNET_OPEN, &OpenArgs, sizeof(OpenArgs));
712 if (VBOX_FAILURE(rc))
713 {
714 AssertMsgFailed(("Failed to open/create the network '%s', cbRecv=%RU32, cbSend=%RU32. rc=%VRc\n",
715 pThis->szNetwork, OpenArgs.cbRecv, OpenArgs.cbSend, rc));
716 return rc;
717 }
718 AssertRelease(OpenArgs.hIf != INTNET_HANDLE_INVALID);
719 pThis->hIf = OpenArgs.hIf;
720 Log(("IntNet%d: hIf=%RX32 '%s'\n", pDrvIns->iInstance, pThis->hIf, pThis->szNetwork));
721
722 /*
723 * Get default buffer.
724 */
725 INTNETIFGETRING3BUFFERARGS GetRing3BufferArgs = {0};
726 GetRing3BufferArgs.hIf = pThis->hIf;
727 GetRing3BufferArgs.pRing3Buf = NULL;
728 rc = pDrvIns->pDrvHlp->pfnSUPCallVMMR0Ex(pDrvIns, VMMR0_DO_INTNET_IF_GET_RING3_BUFFER, &GetRing3BufferArgs, sizeof(GetRing3BufferArgs));
729 if (VBOX_FAILURE(rc))
730 {
731 AssertMsgFailed(("Failed to get ring-3 buffer for the newly created interface to '%s'. rc=%VRc\n",
732 pThis->szNetwork, rc));
733 return rc;
734 }
735 AssertRelease(VALID_PTR(GetRing3BufferArgs.pRing3Buf));
736 pThis->pBuf = GetRing3BufferArgs.pRing3Buf;
737
738 /*
739 * Create the async I/O thread.
740 */
741 rc = RTThreadCreate(&pThis->Thread, drvIntNetAsyncIoThread, pThis, _128K, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "INTNET");
742 if (VBOX_FAILURE(rc))
743 {
744 AssertRC(rc);
745 return rc;
746 }
747
748 char szStatName[64];
749 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Bytes/Received", pDrvIns->iInstance);
750 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cbStatRecv, STAMTYPE_COUNTER, szStatName, STAMUNIT_BYTES, "Number of received bytes.");
751 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Bytes/Sent", pDrvIns->iInstance);
752 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cbStatSend, STAMTYPE_COUNTER, szStatName, STAMUNIT_BYTES, "Number of sent bytes.");
753 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Packets/Received", pDrvIns->iInstance);
754 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cStatRecvs, STAMTYPE_COUNTER, szStatName, STAMUNIT_OCCURENCES, "Number of received packets.");
755 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Packets/Sent", pDrvIns->iInstance);
756 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cStatSends, STAMTYPE_COUNTER, szStatName, STAMUNIT_OCCURENCES, "Number of sent packets.");
757 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Packets/Lost", pDrvIns->iInstance);
758 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cStatLost, STAMTYPE_COUNTER, szStatName, STAMUNIT_OCCURENCES, "Number of lost packets.");
759 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/YieldOk", pDrvIns->iInstance);
760 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cStatYieldsOk, STAMTYPE_COUNTER, szStatName, STAMUNIT_OCCURENCES, "Number of times yielding fixed an overflow.");
761 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/YieldNok", pDrvIns->iInstance);
762 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->pBuf->cStatYieldsNok, STAMTYPE_COUNTER, szStatName, STAMUNIT_OCCURENCES, "Number of times yielding didn't help fix an overflow.");
763
764#ifdef VBOX_WITH_STATISTICS
765 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Receive", pDrvIns->iInstance);
766 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->StatReceive, STAMTYPE_PROFILE, szStatName, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.");
767 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/RecvOverflows", pDrvIns->iInstance);
768 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->StatRecvOverflows, STAMTYPE_PROFILE, szStatName, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling packet receive overflows.");
769 RTStrPrintf(szStatName, sizeof(szStatName), "/Net/IntNet%d/Transmit", pDrvIns->iInstance);
770 pDrvIns->pDrvHlp->pfnSTAMRegister(pDrvIns, &pThis->StatTransmit, STAMTYPE_PROFILE, szStatName, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.");
771#endif
772
773 return rc;
774}
775
776
777/**
778 * Internal networking transport driver registration record.
779 */
780const PDMDRVREG g_DrvIntNet =
781{
782 /* u32Version */
783 PDM_DRVREG_VERSION,
784 /* szDriverName */
785 "IntNet",
786 /* pszDescription */
787 "Internal Networking Transport Driver",
788 /* fFlags */
789 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
790 /* fClass. */
791 PDM_DRVREG_CLASS_NETWORK,
792 /* cMaxInstances */
793 ~0,
794 /* cbInstance */
795 sizeof(DRVINTNET),
796 /* pfnConstruct */
797 drvIntNetConstruct,
798 /* pfnDestruct */
799 drvIntNetDestruct,
800 /* pfnIOCtl */
801 NULL,
802 /* pfnPowerOn */
803 drvIntNetPowerOn,
804 /* pfnReset */
805 NULL,
806 /* pfnSuspend */
807 drvIntNetSuspend,
808 /* pfnResume */
809 drvIntNetResume,
810 /* pfnDetach */
811 NULL,
812 /* pfnPowerOff */
813 drvIntNetPowerOff
814};
815
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette