VirtualBox

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

Last change on this file since 6001 was 6001, checked in by vboxsync, 17 years ago

Add configuration option to activate an IntNet interface early, before
the VM is about to be powered up. Needed by lwIP.

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