VirtualBox

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

Last change on this file since 9713 was 9594, checked in by vboxsync, 16 years ago

If IntNet is used as a service (for lwIP) then taking it down on suspend/poweroff is way too early. The service and the users of it come later in the notification order and thus have no chance of doing proper connection shutdown. This fixes the 10 seconds suspend/poweroff delay when using iSCSI over IntNet.

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