VirtualBox

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

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

net: moved the wait/notify mechanism for receive buffers into the device

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