VirtualBox

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

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

no "\n", ".", nor "!" at end of an error message

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

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