VirtualBox

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

Last change on this file since 10690 was 10663, checked in by vboxsync, 16 years ago

Created tstIntNet-1 for checking that capturing an interface works on darwin.

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

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