VirtualBox

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

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

internal networking fixes.

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