VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DevVirtioNet.cpp@ 41820

最後變更 在這個檔案從41820是 41820,由 vboxsync 提交於 13 年 前

virtio: fixed GSO header issue causing heavy loss of packets (#4807)

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 76.0 KB
 
1/* $Id: DevVirtioNet.cpp 41820 2012-06-19 07:37:40Z vboxsync $ */
2/** @file
3 * DevVirtioNet - Virtio Network Device
4 */
5
6/*
7 * Copyright (C) 2009-2011 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19#define LOG_GROUP LOG_GROUP_DEV_VIRTIO_NET
20#define VNET_GC_SUPPORT
21#define VNET_WITH_GSO
22#define VNET_WITH_MERGEABLE_RX_BUFS
23
24#include <VBox/vmm/pdmdev.h>
25#include <VBox/vmm/pdmnetifs.h>
26#include <iprt/asm.h>
27#include <iprt/net.h>
28#include <iprt/semaphore.h>
29#ifdef IN_RING3
30# include <iprt/mem.h>
31# include <iprt/uuid.h>
32#endif /* IN_RING3 */
33#include "VBoxDD.h"
34#include "../VirtIO/Virtio.h"
35
36
37#ifndef VBOX_DEVICE_STRUCT_TESTCASE
38
39#define INSTANCE(pState) pState->VPCI.szInstance
40#define STATUS pState->config.uStatus
41
42#ifdef IN_RING3
43
44#define VNET_PCI_SUBSYSTEM_ID 1 + VIRTIO_NET_ID
45#define VNET_PCI_CLASS 0x0200
46#define VNET_N_QUEUES 3
47#define VNET_NAME_FMT "VNet%d"
48
49#if 0
50/* Virtio Block Device */
51#define VNET_PCI_SUBSYSTEM_ID 1 + VIRTIO_BLK_ID
52#define VNET_PCI_CLASS 0x0180
53#define VNET_N_QUEUES 2
54#define VNET_NAME_FMT "VBlk%d"
55#endif
56
57#endif /* IN_RING3 */
58
59/* Forward declarations ******************************************************/
60RT_C_DECLS_BEGIN
61PDMBOTHCBDECL(int) vnetIOPortIn (PPDMDEVINS pDevIns, void *pvUser, RTIOPORT port, uint32_t *pu32, unsigned cb);
62PDMBOTHCBDECL(int) vnetIOPortOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT port, uint32_t u32, unsigned cb);
63RT_C_DECLS_END
64
65#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
66
67
68#define VNET_TX_DELAY 150 /* 150 microseconds */
69#define VNET_MAX_FRAME_SIZE 65536 // TODO: Is it the right limit?
70#define VNET_MAC_FILTER_LEN 32
71#define VNET_MAX_VID (1 << 12)
72
73/* Virtio net features */
74#define VNET_F_CSUM 0x00000001 /* Host handles pkts w/ partial csum */
75#define VNET_F_GUEST_CSUM 0x00000002 /* Guest handles pkts w/ partial csum */
76#define VNET_F_MAC 0x00000020 /* Host has given MAC address. */
77#define VNET_F_GSO 0x00000040 /* Host handles pkts w/ any GSO type */
78#define VNET_F_GUEST_TSO4 0x00000080 /* Guest can handle TSOv4 in. */
79#define VNET_F_GUEST_TSO6 0x00000100 /* Guest can handle TSOv6 in. */
80#define VNET_F_GUEST_ECN 0x00000200 /* Guest can handle TSO[6] w/ ECN in. */
81#define VNET_F_GUEST_UFO 0x00000400 /* Guest can handle UFO in. */
82#define VNET_F_HOST_TSO4 0x00000800 /* Host can handle TSOv4 in. */
83#define VNET_F_HOST_TSO6 0x00001000 /* Host can handle TSOv6 in. */
84#define VNET_F_HOST_ECN 0x00002000 /* Host can handle TSO[6] w/ ECN in. */
85#define VNET_F_HOST_UFO 0x00004000 /* Host can handle UFO in. */
86#define VNET_F_MRG_RXBUF 0x00008000 /* Host can merge receive buffers. */
87#define VNET_F_STATUS 0x00010000 /* virtio_net_config.status available */
88#define VNET_F_CTRL_VQ 0x00020000 /* Control channel available */
89#define VNET_F_CTRL_RX 0x00040000 /* Control channel RX mode support */
90#define VNET_F_CTRL_VLAN 0x00080000 /* Control channel VLAN filtering */
91
92#define VNET_S_LINK_UP 1
93
94
95#ifdef _MSC_VER
96struct VNetPCIConfig
97#else /* !_MSC_VER */
98struct __attribute__ ((__packed__)) VNetPCIConfig
99#endif /* !_MSC_VER */
100{
101 RTMAC mac;
102 uint16_t uStatus;
103};
104AssertCompileMemberOffset(struct VNetPCIConfig, uStatus, 6);
105
106/**
107 * Device state structure. Holds the current state of device.
108 *
109 * @extends VPCISTATE
110 * @implements PDMINETWORKDOWN
111 * @implements PDMINETWORKCONFIG
112 */
113struct VNetState_st
114{
115 /* VPCISTATE must be the first member! */
116 VPCISTATE VPCI;
117
118// PDMCRITSECT csRx; /**< Protects RX queue. */
119
120 PDMINETWORKDOWN INetworkDown;
121 PDMINETWORKCONFIG INetworkConfig;
122 R3PTRTYPE(PPDMIBASE) pDrvBase; /**< Attached network driver. */
123 R3PTRTYPE(PPDMINETWORKUP) pDrv; /**< Connector of attached network driver. */
124
125 R3PTRTYPE(PPDMQUEUE) pCanRxQueueR3; /**< Rx wakeup signaller - R3. */
126 R0PTRTYPE(PPDMQUEUE) pCanRxQueueR0; /**< Rx wakeup signaller - R0. */
127 RCPTRTYPE(PPDMQUEUE) pCanRxQueueRC; /**< Rx wakeup signaller - RC. */
128# if HC_ARCH_BITS == 64
129 uint32_t padding;
130# endif
131
132 /**< Link Up(/Restore) Timer. */
133 PTMTIMERR3 pLinkUpTimer;
134
135#ifdef VNET_TX_DELAY
136 /**< Transmit Delay Timer - R3. */
137 PTMTIMERR3 pTxTimerR3;
138 /**< Transmit Delay Timer - R0. */
139 PTMTIMERR0 pTxTimerR0;
140 /**< Transmit Delay Timer - GC. */
141 PTMTIMERRC pTxTimerRC;
142
143# if HC_ARCH_BITS == 64
144 uint32_t padding2;
145# endif
146
147 uint32_t u32i;
148 uint32_t u32AvgDiff;
149 uint32_t u32MinDiff;
150 uint32_t u32MaxDiff;
151 uint64_t u64NanoTS;
152#endif /* VNET_TX_DELAY */
153
154 /** Indicates transmission in progress -- only one thread is allowed. */
155 uint32_t uIsTransmitting;
156
157 /** PCI config area holding MAC address as well as TBD. */
158 struct VNetPCIConfig config;
159 /** MAC address obtained from the configuration. */
160 RTMAC macConfigured;
161 /** True if physical cable is attached in configuration. */
162 bool fCableConnected;
163 /** Link up delay (in milliseconds). */
164 uint32_t cMsLinkUpDelay;
165
166 uint32_t alignment;
167
168 /** Number of packet being sent/received to show in debug log. */
169 uint32_t u32PktNo;
170
171 /** N/A: */
172 bool volatile fMaybeOutOfSpace;
173
174 /** Promiscuous mode -- RX filter accepts all packets. */
175 bool fPromiscuous;
176 /** AllMulti mode -- RX filter accepts all multicast packets. */
177 bool fAllMulti;
178 /** The number of actually used slots in aMacTable. */
179 uint32_t nMacFilterEntries;
180 /** Array of MAC addresses accepted by RX filter. */
181 RTMAC aMacFilter[VNET_MAC_FILTER_LEN];
182 /** Bit array of VLAN filter, one bit per VLAN ID. */
183 uint8_t aVlanFilter[VNET_MAX_VID / sizeof(uint8_t)];
184
185 R3PTRTYPE(PVQUEUE) pRxQueue;
186 R3PTRTYPE(PVQUEUE) pTxQueue;
187 R3PTRTYPE(PVQUEUE) pCtlQueue;
188 /* Receive-blocking-related fields ***************************************/
189
190 /** EMT: Gets signalled when more RX descriptors become available. */
191 RTSEMEVENT hEventMoreRxDescAvail;
192
193 /* Statistic fields ******************************************************/
194
195 STAMCOUNTER StatReceiveBytes;
196 STAMCOUNTER StatTransmitBytes;
197 STAMCOUNTER StatReceiveGSO;
198 STAMCOUNTER StatTransmitPackets;
199 STAMCOUNTER StatTransmitGSO;
200 STAMCOUNTER StatTransmitCSum;
201#if defined(VBOX_WITH_STATISTICS)
202 STAMPROFILE StatReceive;
203 STAMPROFILE StatReceiveStore;
204 STAMPROFILEADV StatTransmit;
205 STAMPROFILE StatTransmitSend;
206 STAMPROFILE StatRxOverflow;
207 STAMCOUNTER StatRxOverflowWakeup;
208#endif /* VBOX_WITH_STATISTICS */
209
210};
211typedef struct VNetState_st VNETSTATE;
212typedef VNETSTATE *PVNETSTATE;
213
214#ifndef VBOX_DEVICE_STRUCT_TESTCASE
215
216#define VNETHDR_F_NEEDS_CSUM 1 // Use u16CSumStart, u16CSumOffset
217
218#define VNETHDR_GSO_NONE 0 // Not a GSO frame
219#define VNETHDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO)
220#define VNETHDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO)
221#define VNETHDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP
222#define VNETHDR_GSO_ECN 0x80 // TCP has ECN set
223
224struct VNetHdr
225{
226 uint8_t u8Flags;
227 uint8_t u8GSOType;
228 uint16_t u16HdrLen;
229 uint16_t u16GSOSize;
230 uint16_t u16CSumStart;
231 uint16_t u16CSumOffset;
232};
233typedef struct VNetHdr VNETHDR;
234typedef VNETHDR *PVNETHDR;
235AssertCompileSize(VNETHDR, 10);
236
237struct VNetHdrMrx
238{
239 VNETHDR Hdr;
240 uint16_t u16NumBufs;
241};
242typedef struct VNetHdrMrx VNETHDRMRX;
243typedef VNETHDRMRX *PVNETHDRMRX;
244AssertCompileSize(VNETHDRMRX, 12);
245
246AssertCompileMemberOffset(VNETSTATE, VPCI, 0);
247
248#define VNET_OK 0
249#define VNET_ERROR 1
250typedef uint8_t VNETCTLACK;
251
252#define VNET_CTRL_CLS_RX_MODE 0
253#define VNET_CTRL_CMD_RX_MODE_PROMISC 0
254#define VNET_CTRL_CMD_RX_MODE_ALLMULTI 1
255
256#define VNET_CTRL_CLS_MAC 1
257#define VNET_CTRL_CMD_MAC_TABLE_SET 0
258
259#define VNET_CTRL_CLS_VLAN 2
260#define VNET_CTRL_CMD_VLAN_ADD 0
261#define VNET_CTRL_CMD_VLAN_DEL 1
262
263
264struct VNetCtlHdr
265{
266 uint8_t u8Class;
267 uint8_t u8Command;
268};
269typedef struct VNetCtlHdr VNETCTLHDR;
270typedef VNETCTLHDR *PVNETCTLHDR;
271AssertCompileSize(VNETCTLHDR, 2);
272
273/* Returns true if large packets are written into several RX buffers. */
274DECLINLINE(bool) vnetMergeableRxBuffers(PVNETSTATE pState)
275{
276 return !!(pState->VPCI.uGuestFeatures & VNET_F_MRG_RXBUF);
277}
278
279DECLINLINE(int) vnetCsEnter(PVNETSTATE pState, int rcBusy)
280{
281 return vpciCsEnter(&pState->VPCI, rcBusy);
282}
283
284DECLINLINE(void) vnetCsLeave(PVNETSTATE pState)
285{
286 vpciCsLeave(&pState->VPCI);
287}
288
289DECLINLINE(int) vnetCsRxEnter(PVNETSTATE pState, int rcBusy)
290{
291 // STAM_PROFILE_START(&pState->CTXSUFF(StatCsRx), a);
292 // int rc = PDMCritSectEnter(&pState->csRx, rcBusy);
293 // STAM_PROFILE_STOP(&pState->CTXSUFF(StatCsRx), a);
294 // return rc;
295 return VINF_SUCCESS;
296}
297
298DECLINLINE(void) vnetCsRxLeave(PVNETSTATE pState)
299{
300 // PDMCritSectLeave(&pState->csRx);
301}
302
303/**
304 * Dump a packet to debug log.
305 *
306 * @param pState The device state structure.
307 * @param cpPacket The packet.
308 * @param cb The size of the packet.
309 * @param cszText A string denoting direction of packet transfer.
310 */
311DECLINLINE(void) vnetPacketDump(PVNETSTATE pState, const uint8_t *cpPacket, size_t cb, const char *cszText)
312{
313#ifdef DEBUG
314 Log(("%s %s packet #%d (%d bytes):\n",
315 INSTANCE(pState), cszText, ++pState->u32PktNo, cb));
316 Log3(("%.*Rhxd\n", cb, cpPacket));
317#endif
318}
319
320
321
322PDMBOTHCBDECL(uint32_t) vnetGetHostFeatures(void *pvState)
323{
324 /* We support:
325 * - Host-provided MAC address
326 * - Link status reporting in config space
327 * - Control queue
328 * - RX mode setting
329 * - MAC filter table
330 * - VLAN filter
331 */
332 return VNET_F_MAC
333 | VNET_F_STATUS
334 | VNET_F_CTRL_VQ
335 | VNET_F_CTRL_RX
336 | VNET_F_CTRL_VLAN
337#ifdef VNET_WITH_GSO
338 | VNET_F_CSUM
339 | VNET_F_HOST_TSO4
340 | VNET_F_HOST_TSO6
341 | VNET_F_HOST_UFO
342#endif
343#ifdef VNET_WITH_MERGEABLE_RX_BUFS
344 | VNET_F_MRG_RXBUF
345#endif
346 ;
347}
348
349PDMBOTHCBDECL(uint32_t) vnetGetHostMinimalFeatures(void *pvState)
350{
351 return VNET_F_MAC;
352}
353
354PDMBOTHCBDECL(void) vnetSetHostFeatures(void *pvState, uint32_t uFeatures)
355{
356 // TODO: Nothing to do here yet
357 VNETSTATE *pState = (VNETSTATE *)pvState;
358 LogFlow(("%s vnetSetHostFeatures: uFeatures=%x\n", INSTANCE(pState), uFeatures));
359}
360
361PDMBOTHCBDECL(int) vnetGetConfig(void *pvState, uint32_t port, uint32_t cb, void *data)
362{
363 VNETSTATE *pState = (VNETSTATE *)pvState;
364 if (port + cb > sizeof(struct VNetPCIConfig))
365 {
366 Log(("%s vnetGetConfig: Read beyond the config structure is attempted (port=%RTiop cb=%x).\n", INSTANCE(pState), port, cb));
367 return VERR_IOM_IOPORT_UNUSED;
368 }
369 memcpy(data, ((uint8_t*)&pState->config) + port, cb);
370 return VINF_SUCCESS;
371}
372
373PDMBOTHCBDECL(int) vnetSetConfig(void *pvState, uint32_t port, uint32_t cb, void *data)
374{
375 VNETSTATE *pState = (VNETSTATE *)pvState;
376 if (port + cb > sizeof(struct VNetPCIConfig))
377 {
378 Log(("%s vnetGetConfig: Write beyond the config structure is attempted (port=%RTiop cb=%x).\n", INSTANCE(pState), port, cb));
379 if (port < sizeof(struct VNetPCIConfig))
380 memcpy(((uint8_t*)&pState->config) + port, data,
381 sizeof(struct VNetPCIConfig) - port);
382 return VINF_SUCCESS;
383 }
384 memcpy(((uint8_t*)&pState->config) + port, data, cb);
385 return VINF_SUCCESS;
386}
387
388/**
389 * Hardware reset. Revert all registers to initial values.
390 *
391 * @param pState The device state structure.
392 */
393PDMBOTHCBDECL(int) vnetReset(void *pvState)
394{
395 VNETSTATE *pState = (VNETSTATE*)pvState;
396 Log(("%s Reset triggered\n", INSTANCE(pState)));
397
398 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
399 if (RT_UNLIKELY(rc != VINF_SUCCESS))
400 {
401 LogRel(("vnetReset failed to enter RX critical section!\n"));
402 return rc;
403 }
404 vpciReset(&pState->VPCI);
405 vnetCsRxLeave(pState);
406
407 // TODO: Implement reset
408 if (pState->fCableConnected)
409 STATUS = VNET_S_LINK_UP;
410 else
411 STATUS = 0;
412
413 /*
414 * By default we pass all packets up since the older guests cannot control
415 * virtio mode.
416 */
417 pState->fPromiscuous = true;
418 pState->fAllMulti = false;
419 pState->nMacFilterEntries = 0;
420 memset(pState->aMacFilter, 0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
421 memset(pState->aVlanFilter, 0, sizeof(pState->aVlanFilter));
422 pState->uIsTransmitting = 0;
423#ifndef IN_RING3
424 return VINF_IOM_R3_IOPORT_WRITE;
425#else
426 if (pState->pDrv)
427 pState->pDrv->pfnSetPromiscuousMode(pState->pDrv, true);
428 return VINF_SUCCESS;
429#endif
430}
431
432#ifdef IN_RING3
433
434/**
435 * Wakeup the RX thread.
436 */
437static void vnetWakeupReceive(PPDMDEVINS pDevIns)
438{
439 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE *);
440 if ( pState->fMaybeOutOfSpace
441 && pState->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
442 {
443 STAM_COUNTER_INC(&pState->StatRxOverflowWakeup);
444 Log(("%s Waking up Out-of-RX-space semaphore\n", INSTANCE(pState)));
445 RTSemEventSignal(pState->hEventMoreRxDescAvail);
446 }
447}
448
449/**
450 * Link Up Timer handler.
451 *
452 * @param pDevIns Pointer to device instance structure.
453 * @param pTimer Pointer to the timer.
454 * @param pvUser NULL.
455 * @thread EMT
456 */
457static DECLCALLBACK(void) vnetLinkUpTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
458{
459 VNETSTATE *pState = (VNETSTATE *)pvUser;
460
461 int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
462 if (RT_UNLIKELY(rc != VINF_SUCCESS))
463 return;
464 STATUS |= VNET_S_LINK_UP;
465 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
466 vnetWakeupReceive(pDevIns);
467 vnetCsLeave(pState);
468}
469
470
471
472
473/**
474 * Handler for the wakeup signaller queue.
475 */
476static DECLCALLBACK(bool) vnetCanRxQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
477{
478 vnetWakeupReceive(pDevIns);
479 return true;
480}
481
482#endif /* IN_RING3 */
483
484/**
485 * This function is called when the driver becomes ready.
486 *
487 * @param pState The device state structure.
488 */
489PDMBOTHCBDECL(void) vnetReady(void *pvState)
490{
491 VNETSTATE *pState = (VNETSTATE*)pvState;
492 Log(("%s Driver became ready, waking up RX thread...\n", INSTANCE(pState)));
493#ifdef IN_RING3
494 vnetWakeupReceive(pState->VPCI.CTX_SUFF(pDevIns));
495#else
496 PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pState->CTX_SUFF(pCanRxQueue));
497 if (pItem)
498 PDMQueueInsert(pState->CTX_SUFF(pCanRxQueue), pItem);
499#endif
500}
501
502/**
503 * Port I/O Handler for IN operations.
504 *
505 * @returns VBox status code.
506 *
507 * @param pDevIns The device instance.
508 * @param pvUser Pointer to the device state structure.
509 * @param port Port number used for the IN operation.
510 * @param pu32 Where to store the result.
511 * @param cb Number of bytes read.
512 * @thread EMT
513 */
514PDMBOTHCBDECL(int) vnetIOPortIn(PPDMDEVINS pDevIns, void *pvUser,
515 RTIOPORT port, uint32_t *pu32, unsigned cb)
516{
517 return vpciIOPortIn(pDevIns, pvUser, port, pu32, cb,
518 vnetGetHostFeatures,
519 vnetGetConfig);
520}
521
522
523/**
524 * Port I/O Handler for OUT operations.
525 *
526 * @returns VBox status code.
527 *
528 * @param pDevIns The device instance.
529 * @param pvUser User argument.
530 * @param Port Port number used for the IN operation.
531 * @param u32 The value to output.
532 * @param cb The value size in bytes.
533 * @thread EMT
534 */
535PDMBOTHCBDECL(int) vnetIOPortOut(PPDMDEVINS pDevIns, void *pvUser,
536 RTIOPORT port, uint32_t u32, unsigned cb)
537{
538 return vpciIOPortOut(pDevIns, pvUser, port, u32, cb,
539 vnetGetHostMinimalFeatures,
540 vnetGetHostFeatures,
541 vnetSetHostFeatures,
542 vnetReset,
543 vnetReady,
544 vnetSetConfig);
545}
546
547
548#ifdef IN_RING3
549
550/**
551 * Check if the device can receive data now.
552 * This must be called before the pfnRecieve() method is called.
553 *
554 * @remarks As a side effect this function enables queue notification
555 * if it cannot receive because the queue is empty.
556 * It disables notification if it can receive.
557 *
558 * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
559 * @param pInterface Pointer to the interface structure containing the called function pointer.
560 * @thread RX
561 */
562static int vnetCanReceive(VNETSTATE *pState)
563{
564 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
565 AssertRCReturn(rc, rc);
566
567 LogFlow(("%s vnetCanReceive\n", INSTANCE(pState)));
568 if (!(pState->VPCI.uStatus & VPCI_STATUS_DRV_OK))
569 rc = VERR_NET_NO_BUFFER_SPACE;
570 else if (!vqueueIsReady(&pState->VPCI, pState->pRxQueue))
571 rc = VERR_NET_NO_BUFFER_SPACE;
572 else if (vqueueIsEmpty(&pState->VPCI, pState->pRxQueue))
573 {
574 vringSetNotification(&pState->VPCI, &pState->pRxQueue->VRing, true);
575 rc = VERR_NET_NO_BUFFER_SPACE;
576 }
577 else
578 {
579 vringSetNotification(&pState->VPCI, &pState->pRxQueue->VRing, false);
580 rc = VINF_SUCCESS;
581 }
582
583 LogFlow(("%s vnetCanReceive -> %Rrc\n", INSTANCE(pState), rc));
584 vnetCsRxLeave(pState);
585 return rc;
586}
587
588/**
589 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
590 */
591static DECLCALLBACK(int) vnetNetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
592{
593 VNETSTATE *pState = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkDown);
594 LogFlow(("%s vnetNetworkDown_WaitReceiveAvail(cMillies=%u)\n", INSTANCE(pState), cMillies));
595 int rc = vnetCanReceive(pState);
596
597 if (RT_SUCCESS(rc))
598 return VINF_SUCCESS;
599 if (RT_UNLIKELY(cMillies == 0))
600 return VERR_NET_NO_BUFFER_SPACE;
601
602 rc = VERR_INTERRUPTED;
603 ASMAtomicXchgBool(&pState->fMaybeOutOfSpace, true);
604 STAM_PROFILE_START(&pState->StatRxOverflow, a);
605
606 VMSTATE enmVMState;
607 while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pState->VPCI.CTX_SUFF(pDevIns))) == VMSTATE_RUNNING
608 || enmVMState == VMSTATE_RUNNING_LS))
609 {
610 int rc2 = vnetCanReceive(pState);
611 if (RT_SUCCESS(rc2))
612 {
613 rc = VINF_SUCCESS;
614 break;
615 }
616 Log(("%s vnetNetworkDown_WaitReceiveAvail: waiting cMillies=%u...\n",
617 INSTANCE(pState), cMillies));
618 RTSemEventWait(pState->hEventMoreRxDescAvail, cMillies);
619 }
620 STAM_PROFILE_STOP(&pState->StatRxOverflow, a);
621 ASMAtomicXchgBool(&pState->fMaybeOutOfSpace, false);
622
623 LogFlow(("%s vnetNetworkDown_WaitReceiveAvail -> %d\n", INSTANCE(pState), rc));
624 return rc;
625}
626
627
628/**
629 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
630 */
631static DECLCALLBACK(void *) vnetQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
632{
633 VNETSTATE *pThis = RT_FROM_MEMBER(pInterface, VNETSTATE, VPCI.IBase);
634 Assert(&pThis->VPCI.IBase == pInterface);
635
636 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThis->INetworkDown);
637 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig);
638 return vpciQueryInterface(pInterface, pszIID);
639}
640
641/**
642 * Returns true if it is a broadcast packet.
643 *
644 * @returns true if destination address indicates broadcast.
645 * @param pvBuf The ethernet packet.
646 */
647DECLINLINE(bool) vnetIsBroadcast(const void *pvBuf)
648{
649 static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
650 return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
651}
652
653/**
654 * Returns true if it is a multicast packet.
655 *
656 * @remarks returns true for broadcast packets as well.
657 * @returns true if destination address indicates multicast.
658 * @param pvBuf The ethernet packet.
659 */
660DECLINLINE(bool) vnetIsMulticast(const void *pvBuf)
661{
662 return (*(char*)pvBuf) & 1;
663}
664
665/**
666 * Determines if the packet is to be delivered to upper layer.
667 *
668 * @returns true if packet is intended for this node.
669 * @param pState Pointer to the state structure.
670 * @param pvBuf The ethernet packet.
671 * @param cb Number of bytes available in the packet.
672 */
673static bool vnetAddressFilter(PVNETSTATE pState, const void *pvBuf, size_t cb)
674{
675 if (pState->fPromiscuous)
676 return true;
677
678 /* Ignore everything outside of our VLANs */
679 uint16_t *u16Ptr = (uint16_t*)pvBuf;
680 /* Compare TPID with VLAN Ether Type */
681 if ( u16Ptr[6] == RT_H2BE_U16(0x8100)
682 && !ASMBitTest(pState->aVlanFilter, RT_BE2H_U16(u16Ptr[7]) & 0xFFF))
683 {
684 Log4(("%s vnetAddressFilter: not our VLAN, returning false\n", INSTANCE(pState)));
685 return false;
686 }
687
688 if (vnetIsBroadcast(pvBuf))
689 return true;
690
691 if (pState->fAllMulti && vnetIsMulticast(pvBuf))
692 return true;
693
694 if (!memcmp(pState->config.mac.au8, pvBuf, sizeof(RTMAC)))
695 return true;
696 Log4(("%s vnetAddressFilter: %RTmac (conf) != %RTmac (dest)\n",
697 INSTANCE(pState), pState->config.mac.au8, pvBuf));
698
699 for (unsigned i = 0; i < pState->nMacFilterEntries; i++)
700 if (!memcmp(&pState->aMacFilter[i], pvBuf, sizeof(RTMAC)))
701 return true;
702
703 Log2(("%s vnetAddressFilter: failed all tests, returning false, packet dump follows:\n", INSTANCE(pState)));
704 vnetPacketDump(pState, (const uint8_t*)pvBuf, cb, "<-- Incoming");
705
706 return false;
707}
708
709/**
710 * Pad and store received packet.
711 *
712 * @remarks Make sure that the packet appears to upper layer as one coming
713 * from real Ethernet: pad it and insert FCS.
714 *
715 * @returns VBox status code.
716 * @param pState The device state structure.
717 * @param pvBuf The available data.
718 * @param cb Number of bytes available in the buffer.
719 * @thread RX
720 */
721static int vnetHandleRxPacket(PVNETSTATE pState, const void *pvBuf, size_t cb,
722 PCPDMNETWORKGSO pGso)
723{
724 VNETHDRMRX Hdr;
725 PVNETHDRMRX pHdr;
726 unsigned uHdrLen;
727 RTGCPHYS addrHdrMrx = 0;
728
729 if (pGso)
730 {
731 Log2(("%s vnetHandleRxPacket: gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u"
732 " off1=0x%x off2=0x%x\n", INSTANCE(pState), pGso->u8Type,
733 pGso->cbHdrsTotal, pGso->cbHdrsSeg, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
734 Hdr.Hdr.u8Flags = VNETHDR_F_NEEDS_CSUM;
735 switch (pGso->u8Type)
736 {
737 case PDMNETWORKGSOTYPE_IPV4_TCP:
738 Hdr.Hdr.u8GSOType = VNETHDR_GSO_TCPV4;
739 Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
740 break;
741 case PDMNETWORKGSOTYPE_IPV6_TCP:
742 Hdr.Hdr.u8GSOType = VNETHDR_GSO_TCPV6;
743 Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
744 break;
745 case PDMNETWORKGSOTYPE_IPV4_UDP:
746 Hdr.Hdr.u8GSOType = VNETHDR_GSO_UDP;
747 Hdr.Hdr.u16CSumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
748 break;
749 default:
750 return VERR_INVALID_PARAMETER;
751 }
752 Hdr.Hdr.u16HdrLen = pGso->cbHdrsTotal;
753 Hdr.Hdr.u16GSOSize = pGso->cbMaxSeg;
754 Hdr.Hdr.u16CSumStart = pGso->offHdr2;
755 STAM_REL_COUNTER_INC(&pState->StatReceiveGSO);
756 }
757 else
758 {
759 Hdr.Hdr.u8Flags = 0;
760 Hdr.Hdr.u8GSOType = VNETHDR_GSO_NONE;
761 }
762
763 if (vnetMergeableRxBuffers(pState))
764 uHdrLen = sizeof(VNETHDRMRX);
765 else
766 uHdrLen = sizeof(VNETHDR);
767
768 vnetPacketDump(pState, (const uint8_t*)pvBuf, cb, "<-- Incoming");
769
770 unsigned int uOffset = 0;
771 unsigned int nElem;
772 for (nElem = 0; uOffset < cb; nElem++)
773 {
774 VQUEUEELEM elem;
775 unsigned int nSeg = 0, uElemSize = 0, cbReserved = 0;
776
777 if (!vqueueGet(&pState->VPCI, pState->pRxQueue, &elem))
778 {
779 /*
780 * @todo: It is possible to run out of RX buffers if only a few
781 * were added and we received a big packet.
782 */
783 Log(("%s vnetHandleRxPacket: Suddenly there is no space in receive queue!\n", INSTANCE(pState)));
784 return VERR_INTERNAL_ERROR;
785 }
786
787 if (elem.nIn < 1)
788 {
789 Log(("%s vnetHandleRxPacket: No writable descriptors in receive queue!\n", INSTANCE(pState)));
790 return VERR_INTERNAL_ERROR;
791 }
792
793 if (nElem == 0)
794 {
795 if (vnetMergeableRxBuffers(pState))
796 {
797 addrHdrMrx = elem.aSegsIn[nSeg].addr;
798 cbReserved = uHdrLen;
799 }
800 else
801 {
802 /* The very first segment of the very first element gets the header. */
803 if (elem.aSegsIn[nSeg].cb != sizeof(VNETHDR))
804 {
805 Log(("%s vnetHandleRxPacket: The first descriptor does match the header size!\n", INSTANCE(pState)));
806 return VERR_INTERNAL_ERROR;
807 }
808 elem.aSegsIn[nSeg++].pv = &Hdr;
809 }
810 uElemSize += uHdrLen;
811 }
812 while (nSeg < elem.nIn && uOffset < cb)
813 {
814 unsigned int uSize = (unsigned int)RT_MIN(elem.aSegsIn[nSeg].cb - (nSeg?0:cbReserved),
815 cb - uOffset);
816 elem.aSegsIn[nSeg++].pv = (uint8_t*)pvBuf + uOffset;
817 uOffset += uSize;
818 uElemSize += uSize;
819 }
820 STAM_PROFILE_START(&pState->StatReceiveStore, a);
821 vqueuePut(&pState->VPCI, pState->pRxQueue, &elem, uElemSize, cbReserved);
822 STAM_PROFILE_STOP(&pState->StatReceiveStore, a);
823 if (!vnetMergeableRxBuffers(pState))
824 break;
825 cbReserved = 0;
826 }
827 if (vnetMergeableRxBuffers(pState))
828 {
829 Hdr.u16NumBufs = nElem;
830 int rc = PDMDevHlpPhysWrite(pState->VPCI.CTX_SUFF(pDevIns), addrHdrMrx,
831 &Hdr, sizeof(Hdr));
832 if (RT_FAILURE(rc))
833 {
834 Log(("%s vnetHandleRxPacket: Failed to write merged RX buf header: %Rrc\n",
835 INSTANCE(pState), rc));
836 return rc;
837 }
838 }
839 vqueueSync(&pState->VPCI, pState->pRxQueue);
840 if (uOffset < cb)
841 {
842 Log(("%s vnetHandleRxPacket: Packet did not fit into RX queue (packet size=%u)!\n",
843 INSTANCE(pState), cb));
844 return VERR_TOO_MUCH_DATA;
845 }
846
847 return VINF_SUCCESS;
848}
849
850/**
851 * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
852 */
853static DECLCALLBACK(int) vnetNetworkDown_ReceiveGso(PPDMINETWORKDOWN pInterface,
854 const void *pvBuf, size_t cb,
855 PCPDMNETWORKGSO pGso)
856{
857 VNETSTATE *pState = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkDown);
858
859 Log2(("%s vnetNetworkDown_ReceiveGso: pvBuf=%p cb=%u pGso=%p\n",
860 INSTANCE(pState), pvBuf, cb, pGso));
861 int rc = vnetCanReceive(pState);
862 if (RT_FAILURE(rc))
863 return rc;
864
865 /* Drop packets if VM is not running or cable is disconnected. */
866 VMSTATE enmVMState = PDMDevHlpVMState(pState->VPCI.CTX_SUFF(pDevIns));
867 if (( enmVMState != VMSTATE_RUNNING
868 && enmVMState != VMSTATE_RUNNING_LS)
869 || !(STATUS & VNET_S_LINK_UP))
870 return VINF_SUCCESS;
871
872 STAM_PROFILE_START(&pState->StatReceive, a);
873 vpciSetReadLed(&pState->VPCI, true);
874 if (vnetAddressFilter(pState, pvBuf, cb))
875 {
876 rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
877 if (RT_SUCCESS(rc))
878 {
879 rc = vnetHandleRxPacket(pState, pvBuf, cb, pGso);
880 STAM_REL_COUNTER_ADD(&pState->StatReceiveBytes, cb);
881 vnetCsRxLeave(pState);
882 }
883 }
884 vpciSetReadLed(&pState->VPCI, false);
885 STAM_PROFILE_STOP(&pState->StatReceive, a);
886 return rc;
887}
888
889/**
890 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
891 */
892static DECLCALLBACK(int) vnetNetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
893{
894 return vnetNetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
895}
896
897/**
898 * Gets the current Media Access Control (MAC) address.
899 *
900 * @returns VBox status code.
901 * @param pInterface Pointer to the interface structure containing the called function pointer.
902 * @param pMac Where to store the MAC address.
903 * @thread EMT
904 */
905static DECLCALLBACK(int) vnetGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
906{
907 VNETSTATE *pState = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkConfig);
908 memcpy(pMac, pState->config.mac.au8, sizeof(RTMAC));
909 return VINF_SUCCESS;
910}
911
912/**
913 * Gets the new link state.
914 *
915 * @returns The current link state.
916 * @param pInterface Pointer to the interface structure containing the called function pointer.
917 * @thread EMT
918 */
919static DECLCALLBACK(PDMNETWORKLINKSTATE) vnetGetLinkState(PPDMINETWORKCONFIG pInterface)
920{
921 VNETSTATE *pState = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkConfig);
922 if (STATUS & VNET_S_LINK_UP)
923 return PDMNETWORKLINKSTATE_UP;
924 return PDMNETWORKLINKSTATE_DOWN;
925}
926
927
928/**
929 * Sets the new link state.
930 *
931 * @returns VBox status code.
932 * @param pInterface Pointer to the interface structure containing the called function pointer.
933 * @param enmState The new link state
934 */
935static DECLCALLBACK(int) vnetSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
936{
937 VNETSTATE *pState = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkConfig);
938 bool fOldUp = !!(STATUS & VNET_S_LINK_UP);
939 bool fNewUp = enmState == PDMNETWORKLINKSTATE_UP;
940
941 if (fNewUp != fOldUp)
942 {
943 if (fNewUp)
944 {
945 Log(("%s Link is up\n", INSTANCE(pState)));
946 STATUS |= VNET_S_LINK_UP;
947 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
948 }
949 else
950 {
951 Log(("%s Link is down\n", INSTANCE(pState)));
952 STATUS &= ~VNET_S_LINK_UP;
953 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
954 }
955 if (pState->pDrv)
956 pState->pDrv->pfnNotifyLinkChanged(pState->pDrv, enmState);
957 }
958 return VINF_SUCCESS;
959}
960
961static DECLCALLBACK(void) vnetQueueReceive(void *pvState, PVQUEUE pQueue)
962{
963 VNETSTATE *pState = (VNETSTATE*)pvState;
964 Log(("%s Receive buffers has been added, waking up receive thread.\n", INSTANCE(pState)));
965 vnetWakeupReceive(pState->VPCI.CTX_SUFF(pDevIns));
966}
967
968/**
969 * Sets up the GSO context according to the Virtio header.
970 *
971 * @param pGso The GSO context to setup.
972 * @param pCtx The context descriptor.
973 */
974DECLINLINE(PPDMNETWORKGSO) vnetSetupGsoCtx(PPDMNETWORKGSO pGso, VNETHDR const *pHdr)
975{
976 pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
977
978 if (pHdr->u8GSOType & VNETHDR_GSO_ECN)
979 {
980 AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
981 return NULL;
982 }
983 switch (pHdr->u8GSOType & ~VNETHDR_GSO_ECN)
984 {
985 case VNETHDR_GSO_TCPV4:
986 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
987 pGso->cbHdrsSeg = pHdr->u16HdrLen;
988 break;
989 case VNETHDR_GSO_TCPV6:
990 pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
991 pGso->cbHdrsSeg = pHdr->u16HdrLen;
992 break;
993 case VNETHDR_GSO_UDP:
994 pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
995 pGso->cbHdrsSeg = pHdr->u16CSumStart;
996 break;
997 default:
998 return NULL;
999 }
1000 if (pHdr->u8Flags & VNETHDR_F_NEEDS_CSUM)
1001 pGso->offHdr2 = pHdr->u16CSumStart;
1002 else
1003 {
1004 AssertMsgFailed(("GSO without checksum offloading!\n"));
1005 return NULL;
1006 }
1007 pGso->offHdr1 = sizeof(RTNETETHERHDR);
1008 pGso->cbHdrsTotal = pHdr->u16HdrLen;
1009 pGso->cbMaxSeg = pHdr->u16GSOSize;
1010 return pGso;
1011}
1012
1013DECLINLINE(uint16_t) vnetCSum16(const void *pvBuf, size_t cb)
1014{
1015 uint32_t csum = 0;
1016 uint16_t *pu16 = (uint16_t *)pvBuf;
1017
1018 while (cb > 1)
1019 {
1020 csum += *pu16++;
1021 cb -= 2;
1022 }
1023 if (cb)
1024 csum += *(uint8_t*)pu16;
1025 while (csum >> 16)
1026 csum = (csum >> 16) + (csum & 0xFFFF);
1027 return ~csum;
1028}
1029
1030DECLINLINE(void) vnetCompleteChecksum(uint8_t *pBuf, unsigned cbSize, uint16_t uStart, uint16_t uOffset)
1031{
1032 *(uint16_t*)(pBuf + uStart + uOffset) = vnetCSum16(pBuf + uStart, cbSize - uStart);
1033}
1034
1035static void vnetTransmitPendingPackets(PVNETSTATE pState, PVQUEUE pQueue, bool fOnWorkerThread)
1036{
1037 /*
1038 * Only one thread is allowed to transmit at a time, others should skip
1039 * transmission as the packets will be picked up by the transmitting
1040 * thread.
1041 */
1042 if (!ASMAtomicCmpXchgU32(&pState->uIsTransmitting, 1, 0))
1043 return;
1044
1045 if ((pState->VPCI.uStatus & VPCI_STATUS_DRV_OK) == 0)
1046 {
1047 Log(("%s Ignoring transmit requests from non-existent driver (status=0x%x).\n",
1048 INSTANCE(pState), pState->VPCI.uStatus));
1049 return;
1050 }
1051
1052 PPDMINETWORKUP pDrv = pState->pDrv;
1053 if (pDrv)
1054 {
1055 int rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
1056 Assert(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN);
1057 if (rc == VERR_TRY_AGAIN)
1058 {
1059 ASMAtomicWriteU32(&pState->uIsTransmitting, 0);
1060 return;
1061 }
1062 }
1063
1064 unsigned int uHdrLen;
1065 if (vnetMergeableRxBuffers(pState))
1066 uHdrLen = sizeof(VNETHDRMRX);
1067 else
1068 uHdrLen = sizeof(VNETHDR);
1069
1070 Log3(("%s vnetTransmitPendingPackets: About to transmit %d pending packets\n", INSTANCE(pState),
1071 vringReadAvailIndex(&pState->VPCI, &pState->pTxQueue->VRing) - pState->pTxQueue->uNextAvailIndex));
1072
1073 vpciSetWriteLed(&pState->VPCI, true);
1074
1075 VQUEUEELEM elem;
1076 /*
1077 * Do not remove descriptors from available ring yet, try to allocate the
1078 * buffer first.
1079 */
1080 while (vqueuePeek(&pState->VPCI, pQueue, &elem))
1081 {
1082 unsigned int uOffset = 0;
1083 if (elem.nOut < 2 || elem.aSegsOut[0].cb != uHdrLen)
1084 {
1085 Log(("%s vnetQueueTransmit: The first segment is not the header! (%u < 2 || %u != %u).\n",
1086 INSTANCE(pState), elem.nOut, elem.aSegsOut[0].cb, uHdrLen));
1087 break; /* For now we simply ignore the header, but it must be there anyway! */
1088 }
1089 else
1090 {
1091 unsigned int uSize = 0;
1092 STAM_PROFILE_ADV_START(&pState->StatTransmit, a);
1093 /* Compute total frame size. */
1094 for (unsigned int i = 1; i < elem.nOut; i++)
1095 uSize += elem.aSegsOut[i].cb;
1096 Assert(uSize <= VNET_MAX_FRAME_SIZE);
1097 if (pState->pDrv)
1098 {
1099 VNETHDR Hdr;
1100 PDMNETWORKGSO Gso, *pGso;
1101
1102 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[0].addr,
1103 &Hdr, sizeof(Hdr));
1104
1105 STAM_REL_COUNTER_INC(&pState->StatTransmitPackets);
1106
1107 STAM_PROFILE_START(&pState->StatTransmitSend, a);
1108
1109 pGso = vnetSetupGsoCtx(&Gso, &Hdr);
1110 /** @todo Optimize away the extra copying! (lazy bird) */
1111 PPDMSCATTERGATHER pSgBuf;
1112 int rc = pState->pDrv->pfnAllocBuf(pState->pDrv, uSize, pGso, &pSgBuf);
1113 if (RT_SUCCESS(rc))
1114 {
1115 Assert(pSgBuf->cSegs == 1);
1116 /* Assemble a complete frame. */
1117 for (unsigned int i = 1; i < elem.nOut; i++)
1118 {
1119 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[i].addr,
1120 ((uint8_t*)pSgBuf->aSegs[0].pvSeg) + uOffset,
1121 elem.aSegsOut[i].cb);
1122 uOffset += elem.aSegsOut[i].cb;
1123 }
1124 pSgBuf->cbUsed = uSize;
1125 vnetPacketDump(pState, (uint8_t*)pSgBuf->aSegs[0].pvSeg, uSize, "--> Outgoing");
1126 if (pGso)
1127 {
1128 /* Some guests (RHEL) may report HdrLen excluding transport layer header! */
1129 /*
1130 * We cannot use cdHdrs provided by the guest because of different ways
1131 * it gets filled out by different versions of kernels.
1132 */
1133 //if (pGso->cbHdrs < Hdr.u16CSumStart + Hdr.u16CSumOffset + 2)
1134 {
1135 Log4(("%s vnetTransmitPendingPackets: HdrLen before adjustment %d.\n",
1136 INSTANCE(pState), pGso->cbHdrsTotal));
1137 switch (pGso->u8Type)
1138 {
1139 case PDMNETWORKGSOTYPE_IPV4_TCP:
1140 case PDMNETWORKGSOTYPE_IPV6_TCP:
1141 pGso->cbHdrsTotal = Hdr.u16CSumStart +
1142 ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + Hdr.u16CSumStart))->th_off * 4;
1143 pGso->cbHdrsSeg = pGso->cbHdrsTotal;
1144 break;
1145 case PDMNETWORKGSOTYPE_IPV4_UDP:
1146 pGso->cbHdrsTotal = Hdr.u16CSumStart + sizeof(RTNETUDP);
1147 pGso->cbHdrsSeg = Hdr.u16CSumStart;
1148 break;
1149 }
1150 /* Update GSO structure embedded into the frame */
1151 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
1152 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg = pGso->cbHdrsTotal;
1153 Log4(("%s vnetTransmitPendingPackets: adjusted HdrLen to %d.\n",
1154 INSTANCE(pState), pGso->cbHdrsTotal));
1155 }
1156 Log2(("%s vnetTransmitPendingPackets: gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u"
1157 " off1=0x%x off2=0x%x\n", INSTANCE(pState), pGso->u8Type,
1158 pGso->cbHdrsTotal, pGso->cbHdrsSeg, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
1159 STAM_REL_COUNTER_INC(&pState->StatTransmitGSO);
1160 }
1161 else if (Hdr.u8Flags & VNETHDR_F_NEEDS_CSUM)
1162 {
1163 STAM_REL_COUNTER_INC(&pState->StatTransmitCSum);
1164 /*
1165 * This is not GSO frame but checksum offloading is requested.
1166 */
1167 vnetCompleteChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, uSize,
1168 Hdr.u16CSumStart, Hdr.u16CSumOffset);
1169 }
1170
1171 rc = pState->pDrv->pfnSendBuf(pState->pDrv, pSgBuf, false);
1172 }
1173 else
1174 {
1175 Log4(("virtio-net: failed to allocate SG buffer: size=%u rc=%Rrc\n", uSize, rc));
1176 STAM_PROFILE_STOP(&pState->StatTransmitSend, a);
1177 STAM_PROFILE_ADV_STOP(&pState->StatTransmit, a);
1178 /* Stop trying to fetch TX descriptors until we get more bandwidth. */
1179 break;
1180 }
1181
1182 STAM_PROFILE_STOP(&pState->StatTransmitSend, a);
1183 STAM_REL_COUNTER_ADD(&pState->StatTransmitBytes, uOffset);
1184 }
1185 }
1186 /* Remove this descriptor chain from the available ring */
1187 vqueueSkip(&pState->VPCI, pQueue);
1188 vqueuePut(&pState->VPCI, pQueue, &elem, sizeof(VNETHDR) + uOffset);
1189 vqueueSync(&pState->VPCI, pQueue);
1190 STAM_PROFILE_ADV_STOP(&pState->StatTransmit, a);
1191 }
1192 vpciSetWriteLed(&pState->VPCI, false);
1193
1194 if (pDrv)
1195 pDrv->pfnEndXmit(pDrv);
1196 ASMAtomicWriteU32(&pState->uIsTransmitting, 0);
1197}
1198
1199/**
1200 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
1201 */
1202static DECLCALLBACK(void) vnetNetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
1203{
1204 VNETSTATE *pThis = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkDown);
1205 vnetTransmitPendingPackets(pThis, pThis->pTxQueue, false /*fOnWorkerThread*/);
1206}
1207
1208#ifdef VNET_TX_DELAY
1209
1210static DECLCALLBACK(void) vnetQueueTransmit(void *pvState, PVQUEUE pQueue)
1211{
1212 VNETSTATE *pState = (VNETSTATE*)pvState;
1213
1214 if (TMTimerIsActive(pState->CTX_SUFF(pTxTimer)))
1215 {
1216 int rc = TMTimerStop(pState->CTX_SUFF(pTxTimer));
1217 Log3(("%s vnetQueueTransmit: Got kicked with notification disabled, "
1218 "re-enable notification and flush TX queue\n", INSTANCE(pState)));
1219 vnetTransmitPendingPackets(pState, pQueue, false /*fOnWorkerThread*/);
1220 if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
1221 LogRel(("vnetQueueTransmit: Failed to enter critical section!/n"));
1222 else
1223 {
1224 vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, true);
1225 vnetCsLeave(pState);
1226 }
1227 }
1228 else
1229 {
1230 if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
1231 LogRel(("vnetQueueTransmit: Failed to enter critical section!/n"));
1232 else
1233 {
1234 vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, false);
1235 TMTimerSetMicro(pState->CTX_SUFF(pTxTimer), VNET_TX_DELAY);
1236 pState->u64NanoTS = RTTimeNanoTS();
1237 vnetCsLeave(pState);
1238 }
1239 }
1240}
1241
1242/**
1243 * Transmit Delay Timer handler.
1244 *
1245 * @remarks We only get here when the timer expires.
1246 *
1247 * @param pDevIns Pointer to device instance structure.
1248 * @param pTimer Pointer to the timer.
1249 * @param pvUser NULL.
1250 * @thread EMT
1251 */
1252static DECLCALLBACK(void) vnetTxTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
1253{
1254 VNETSTATE *pState = (VNETSTATE*)pvUser;
1255
1256 uint32_t u32MicroDiff = (uint32_t)((RTTimeNanoTS() - pState->u64NanoTS)/1000);
1257 if (u32MicroDiff < pState->u32MinDiff)
1258 pState->u32MinDiff = u32MicroDiff;
1259 if (u32MicroDiff > pState->u32MaxDiff)
1260 pState->u32MaxDiff = u32MicroDiff;
1261 pState->u32AvgDiff = (pState->u32AvgDiff * pState->u32i + u32MicroDiff) / (pState->u32i + 1);
1262 pState->u32i++;
1263 Log3(("vnetTxTimer: Expired, diff %9d usec, avg %9d usec, min %9d usec, max %9d usec\n",
1264 u32MicroDiff, pState->u32AvgDiff, pState->u32MinDiff, pState->u32MaxDiff));
1265
1266// Log3(("%s vnetTxTimer: Expired\n", INSTANCE(pState)));
1267 vnetTransmitPendingPackets(pState, pState->pTxQueue, false /*fOnWorkerThread*/);
1268 if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
1269 {
1270 LogRel(("vnetTxTimer: Failed to enter critical section!/n"));
1271 return;
1272 }
1273 vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, true);
1274 vnetCsLeave(pState);
1275}
1276
1277#else /* !VNET_TX_DELAY */
1278
1279static DECLCALLBACK(void) vnetQueueTransmit(void *pvState, PVQUEUE pQueue)
1280{
1281 VNETSTATE *pState = (VNETSTATE*)pvState;
1282
1283 vnetTransmitPendingPackets(pState, pQueue, false /*fOnWorkerThread*/);
1284}
1285
1286#endif /* !VNET_TX_DELAY */
1287
1288static uint8_t vnetControlRx(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1289{
1290 uint8_t u8Ack = VNET_OK;
1291 uint8_t fOn, fDrvWasPromisc = pState->fPromiscuous | pState->fAllMulti;
1292 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1293 pElem->aSegsOut[1].addr,
1294 &fOn, sizeof(fOn));
1295 Log(("%s vnetControlRx: uCommand=%u fOn=%u\n", INSTANCE(pState), pCtlHdr->u8Command, fOn));
1296 switch (pCtlHdr->u8Command)
1297 {
1298 case VNET_CTRL_CMD_RX_MODE_PROMISC:
1299 pState->fPromiscuous = !!fOn;
1300 break;
1301 case VNET_CTRL_CMD_RX_MODE_ALLMULTI:
1302 pState->fAllMulti = !!fOn;
1303 break;
1304 default:
1305 u8Ack = VNET_ERROR;
1306 }
1307 if (fDrvWasPromisc != (pState->fPromiscuous | pState->fAllMulti) && pState->pDrv)
1308 pState->pDrv->pfnSetPromiscuousMode(pState->pDrv,
1309 (pState->fPromiscuous | pState->fAllMulti));
1310
1311 return u8Ack;
1312}
1313
1314static uint8_t vnetControlMac(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1315{
1316 uint32_t nMacs = 0;
1317
1318 if (pCtlHdr->u8Command != VNET_CTRL_CMD_MAC_TABLE_SET
1319 || pElem->nOut != 3
1320 || pElem->aSegsOut[1].cb < sizeof(nMacs)
1321 || pElem->aSegsOut[2].cb < sizeof(nMacs))
1322 {
1323 Log(("%s vnetControlMac: Segment layout is wrong "
1324 "(u8Command=%u nOut=%u cb1=%u cb2=%u)\n", INSTANCE(pState),
1325 pCtlHdr->u8Command, pElem->nOut,
1326 pElem->aSegsOut[1].cb, pElem->aSegsOut[2].cb));
1327 return VNET_ERROR;
1328 }
1329
1330 /* Load unicast addresses */
1331 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1332 pElem->aSegsOut[1].addr,
1333 &nMacs, sizeof(nMacs));
1334
1335 if (pElem->aSegsOut[1].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
1336 {
1337 Log(("%s vnetControlMac: The unicast mac segment is too small "
1338 "(nMacs=%u cb=%u)\n", INSTANCE(pState), pElem->aSegsOut[1].cb));
1339 return VNET_ERROR;
1340 }
1341
1342 if (nMacs > VNET_MAC_FILTER_LEN)
1343 {
1344 Log(("%s vnetControlMac: MAC table is too big, have to use promiscuous"
1345 " mode (nMacs=%u)\n", INSTANCE(pState), nMacs));
1346 pState->fPromiscuous = true;
1347 }
1348 else
1349 {
1350 if (nMacs)
1351 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1352 pElem->aSegsOut[1].addr + sizeof(nMacs),
1353 pState->aMacFilter, nMacs * sizeof(RTMAC));
1354 pState->nMacFilterEntries = nMacs;
1355#ifdef DEBUG
1356 Log(("%s vnetControlMac: unicast macs:\n", INSTANCE(pState)));
1357 for(unsigned i = 0; i < nMacs; i++)
1358 Log((" %RTmac\n", &pState->aMacFilter[i]));
1359#endif /* DEBUG */
1360 }
1361
1362 /* Load multicast addresses */
1363 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1364 pElem->aSegsOut[2].addr,
1365 &nMacs, sizeof(nMacs));
1366
1367 if (pElem->aSegsOut[2].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
1368 {
1369 Log(("%s vnetControlMac: The multicast mac segment is too small "
1370 "(nMacs=%u cb=%u)\n", INSTANCE(pState), pElem->aSegsOut[2].cb));
1371 return VNET_ERROR;
1372 }
1373
1374 if (nMacs > VNET_MAC_FILTER_LEN - pState->nMacFilterEntries)
1375 {
1376 Log(("%s vnetControlMac: MAC table is too big, have to use allmulti"
1377 " mode (nMacs=%u)\n", INSTANCE(pState), nMacs));
1378 pState->fAllMulti = true;
1379 }
1380 else
1381 {
1382 if (nMacs)
1383 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1384 pElem->aSegsOut[2].addr + sizeof(nMacs),
1385 &pState->aMacFilter[pState->nMacFilterEntries],
1386 nMacs * sizeof(RTMAC));
1387#ifdef DEBUG
1388 Log(("%s vnetControlMac: multicast macs:\n", INSTANCE(pState)));
1389 for(unsigned i = 0; i < nMacs; i++)
1390 Log((" %RTmac\n",
1391 &pState->aMacFilter[i+pState->nMacFilterEntries]));
1392#endif /* DEBUG */
1393 pState->nMacFilterEntries += nMacs;
1394 }
1395
1396 return VNET_OK;
1397}
1398
1399static uint8_t vnetControlVlan(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1400{
1401 uint8_t u8Ack = VNET_OK;
1402 uint16_t u16Vid;
1403
1404 if (pElem->nOut != 2 || pElem->aSegsOut[1].cb != sizeof(u16Vid))
1405 {
1406 Log(("%s vnetControlVlan: Segment layout is wrong "
1407 "(u8Command=%u nOut=%u cb=%u)\n", INSTANCE(pState),
1408 pCtlHdr->u8Command, pElem->nOut, pElem->aSegsOut[1].cb));
1409 return VNET_ERROR;
1410 }
1411
1412 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1413 pElem->aSegsOut[1].addr,
1414 &u16Vid, sizeof(u16Vid));
1415
1416 if (u16Vid >= VNET_MAX_VID)
1417 {
1418 Log(("%s vnetControlVlan: VLAN ID is out of range "
1419 "(VID=%u)\n", INSTANCE(pState), u16Vid));
1420 return VNET_ERROR;
1421 }
1422
1423 Log(("%s vnetControlVlan: uCommand=%u VID=%u\n", INSTANCE(pState),
1424 pCtlHdr->u8Command, u16Vid));
1425
1426 switch (pCtlHdr->u8Command)
1427 {
1428 case VNET_CTRL_CMD_VLAN_ADD:
1429 ASMBitSet(pState->aVlanFilter, u16Vid);
1430 break;
1431 case VNET_CTRL_CMD_VLAN_DEL:
1432 ASMBitClear(pState->aVlanFilter, u16Vid);
1433 break;
1434 default:
1435 u8Ack = VNET_ERROR;
1436 }
1437
1438 return u8Ack;
1439}
1440
1441
1442static DECLCALLBACK(void) vnetQueueControl(void *pvState, PVQUEUE pQueue)
1443{
1444 VNETSTATE *pState = (VNETSTATE*)pvState;
1445 uint8_t u8Ack;
1446 VQUEUEELEM elem;
1447 while (vqueueGet(&pState->VPCI, pQueue, &elem))
1448 {
1449 unsigned int uOffset = 0;
1450 if (elem.nOut < 1 || elem.aSegsOut[0].cb < sizeof(VNETCTLHDR))
1451 {
1452 Log(("%s vnetQueueControl: The first 'out' segment is not the "
1453 "header! (%u < 1 || %u < %u).\n", INSTANCE(pState), elem.nOut,
1454 elem.aSegsOut[0].cb,sizeof(VNETCTLHDR)));
1455 break; /* Skip the element and hope the next one is good. */
1456 }
1457 else if ( elem.nIn < 1
1458 || elem.aSegsIn[elem.nIn - 1].cb < sizeof(VNETCTLACK))
1459 {
1460 Log(("%s vnetQueueControl: The last 'in' segment is too small "
1461 "to hold the acknowledge! (%u < 1 || %u < %u).\n",
1462 INSTANCE(pState), elem.nIn, elem.aSegsIn[elem.nIn - 1].cb,
1463 sizeof(VNETCTLACK)));
1464 break; /* Skip the element and hope the next one is good. */
1465 }
1466 else
1467 {
1468 VNETCTLHDR CtlHdr;
1469 PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
1470 elem.aSegsOut[0].addr,
1471 &CtlHdr, sizeof(CtlHdr));
1472 switch (CtlHdr.u8Class)
1473 {
1474 case VNET_CTRL_CLS_RX_MODE:
1475 u8Ack = vnetControlRx(pState, &CtlHdr, &elem);
1476 break;
1477 case VNET_CTRL_CLS_MAC:
1478 u8Ack = vnetControlMac(pState, &CtlHdr, &elem);
1479 break;
1480 case VNET_CTRL_CLS_VLAN:
1481 u8Ack = vnetControlVlan(pState, &CtlHdr, &elem);
1482 break;
1483 default:
1484 u8Ack = VNET_ERROR;
1485 }
1486 Log(("%s Processed control message %u, ack=%u.\n", INSTANCE(pState),
1487 CtlHdr.u8Class, u8Ack));
1488 PDMDevHlpPhysWrite(pState->VPCI.CTX_SUFF(pDevIns),
1489 elem.aSegsIn[elem.nIn - 1].addr,
1490 &u8Ack, sizeof(u8Ack));
1491 }
1492 vqueuePut(&pState->VPCI, pQueue, &elem, sizeof(u8Ack));
1493 vqueueSync(&pState->VPCI, pQueue);
1494 }
1495}
1496
1497/**
1498 * Saves the configuration.
1499 *
1500 * @param pState The VNET state.
1501 * @param pSSM The handle to the saved state.
1502 */
1503static void vnetSaveConfig(VNETSTATE *pState, PSSMHANDLE pSSM)
1504{
1505 SSMR3PutMem(pSSM, &pState->macConfigured, sizeof(pState->macConfigured));
1506}
1507
1508/**
1509 * Live save - save basic configuration.
1510 *
1511 * @returns VBox status code.
1512 * @param pDevIns The device instance.
1513 * @param pSSM The handle to the saved state.
1514 * @param uPass
1515 */
1516static DECLCALLBACK(int) vnetLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1517{
1518 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1519 vnetSaveConfig(pState, pSSM);
1520 return VINF_SSM_DONT_CALL_AGAIN;
1521}
1522
1523/**
1524 * Prepares for state saving.
1525 *
1526 * @returns VBox status code.
1527 * @param pDevIns The device instance.
1528 * @param pSSM The handle to the saved state.
1529 */
1530static DECLCALLBACK(int) vnetSavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1531{
1532 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1533
1534 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
1535 if (RT_UNLIKELY(rc != VINF_SUCCESS))
1536 return rc;
1537 vnetCsRxLeave(pState);
1538 return VINF_SUCCESS;
1539}
1540
1541/**
1542 * Saves the state of device.
1543 *
1544 * @returns VBox status code.
1545 * @param pDevIns The device instance.
1546 * @param pSSM The handle to the saved state.
1547 */
1548static DECLCALLBACK(int) vnetSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1549{
1550 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1551
1552 /* Save config first */
1553 vnetSaveConfig(pState, pSSM);
1554
1555 /* Save the common part */
1556 int rc = vpciSaveExec(&pState->VPCI, pSSM);
1557 AssertRCReturn(rc, rc);
1558 /* Save device-specific part */
1559 rc = SSMR3PutMem( pSSM, pState->config.mac.au8, sizeof(pState->config.mac));
1560 AssertRCReturn(rc, rc);
1561 rc = SSMR3PutBool(pSSM, pState->fPromiscuous);
1562 AssertRCReturn(rc, rc);
1563 rc = SSMR3PutBool(pSSM, pState->fAllMulti);
1564 AssertRCReturn(rc, rc);
1565 rc = SSMR3PutU32( pSSM, pState->nMacFilterEntries);
1566 AssertRCReturn(rc, rc);
1567 rc = SSMR3PutMem( pSSM, pState->aMacFilter,
1568 pState->nMacFilterEntries * sizeof(RTMAC));
1569 AssertRCReturn(rc, rc);
1570 rc = SSMR3PutMem( pSSM, pState->aVlanFilter, sizeof(pState->aVlanFilter));
1571 AssertRCReturn(rc, rc);
1572 Log(("%s State has been saved\n", INSTANCE(pState)));
1573 return VINF_SUCCESS;
1574}
1575
1576
1577/**
1578 * Serializes the receive thread, it may be working inside the critsect.
1579 *
1580 * @returns VBox status code.
1581 * @param pDevIns The device instance.
1582 * @param pSSM The handle to the saved state.
1583 */
1584static DECLCALLBACK(int) vnetLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1585{
1586 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1587
1588 int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
1589 if (RT_UNLIKELY(rc != VINF_SUCCESS))
1590 return rc;
1591 vnetCsRxLeave(pState);
1592 return VINF_SUCCESS;
1593}
1594
1595/**
1596 * Takes down the link temporarily if it's current status is up.
1597 *
1598 * This is used during restore and when replumbing the network link.
1599 *
1600 * The temporary link outage is supposed to indicate to the OS that all network
1601 * connections have been lost and that it for instance is appropriate to
1602 * renegotiate any DHCP lease.
1603 *
1604 * @param pThis The PCNet instance data.
1605 */
1606static void vnetTempLinkDown(PVNETSTATE pState)
1607{
1608 if (STATUS & VNET_S_LINK_UP)
1609 {
1610 STATUS &= ~VNET_S_LINK_UP;
1611 vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1612 /* Restore the link back in 5 seconds. */
1613 int rc = TMTimerSetMillies(pState->pLinkUpTimer, pState->cMsLinkUpDelay);
1614 AssertRC(rc);
1615 }
1616}
1617
1618
1619/**
1620 * Restore previously saved state of device.
1621 *
1622 * @returns VBox status code.
1623 * @param pDevIns The device instance.
1624 * @param pSSM The handle to the saved state.
1625 * @param uVersion The data unit version number.
1626 * @param uPass The data pass.
1627 */
1628static DECLCALLBACK(int) vnetLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1629{
1630 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1631 int rc;
1632
1633 /* config checks */
1634 RTMAC macConfigured;
1635 rc = SSMR3GetMem(pSSM, &macConfigured, sizeof(macConfigured));
1636 AssertRCReturn(rc, rc);
1637 if (memcmp(&macConfigured, &pState->macConfigured, sizeof(macConfigured))
1638 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
1639 LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n", INSTANCE(pState), &pState->macConfigured, &macConfigured));
1640
1641 rc = vpciLoadExec(&pState->VPCI, pSSM, uVersion, uPass, VNET_N_QUEUES);
1642 AssertRCReturn(rc, rc);
1643
1644 if (uPass == SSM_PASS_FINAL)
1645 {
1646 rc = SSMR3GetMem( pSSM, pState->config.mac.au8,
1647 sizeof(pState->config.mac));
1648 AssertRCReturn(rc, rc);
1649
1650 if (uVersion > VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1)
1651 {
1652 rc = SSMR3GetBool(pSSM, &pState->fPromiscuous);
1653 AssertRCReturn(rc, rc);
1654 rc = SSMR3GetBool(pSSM, &pState->fAllMulti);
1655 AssertRCReturn(rc, rc);
1656 rc = SSMR3GetU32(pSSM, &pState->nMacFilterEntries);
1657 AssertRCReturn(rc, rc);
1658 rc = SSMR3GetMem(pSSM, pState->aMacFilter,
1659 pState->nMacFilterEntries * sizeof(RTMAC));
1660 AssertRCReturn(rc, rc);
1661 /* Clear the rest. */
1662 if (pState->nMacFilterEntries < VNET_MAC_FILTER_LEN)
1663 memset(&pState->aMacFilter[pState->nMacFilterEntries],
1664 0,
1665 (VNET_MAC_FILTER_LEN - pState->nMacFilterEntries)
1666 * sizeof(RTMAC));
1667 rc = SSMR3GetMem(pSSM, pState->aVlanFilter,
1668 sizeof(pState->aVlanFilter));
1669 AssertRCReturn(rc, rc);
1670 }
1671 else
1672 {
1673 pState->fPromiscuous = true;
1674 pState->fAllMulti = false;
1675 pState->nMacFilterEntries = 0;
1676 memset(pState->aMacFilter, 0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
1677 memset(pState->aVlanFilter, 0, sizeof(pState->aVlanFilter));
1678 if (pState->pDrv)
1679 pState->pDrv->pfnSetPromiscuousMode(pState->pDrv, true);
1680 }
1681 }
1682
1683 return rc;
1684}
1685
1686/**
1687 * Link status adjustments after loading.
1688 *
1689 * @returns VBox status code.
1690 * @param pDevIns The device instance.
1691 * @param pSSM The handle to the saved state.
1692 */
1693static DECLCALLBACK(int) vnetLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1694{
1695 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1696
1697 if (pState->pDrv)
1698 pState->pDrv->pfnSetPromiscuousMode(pState->pDrv,
1699 (pState->fPromiscuous | pState->fAllMulti));
1700 /*
1701 * Indicate link down to the guest OS that all network connections have
1702 * been lost, unless we've been teleported here.
1703 */
1704 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
1705 vnetTempLinkDown(pState);
1706
1707 return VINF_SUCCESS;
1708}
1709
1710/**
1711 * Map PCI I/O region.
1712 *
1713 * @return VBox status code.
1714 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
1715 * @param iRegion The region number.
1716 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
1717 * I/O port, else it's a physical address.
1718 * This address is *NOT* relative to pci_mem_base like earlier!
1719 * @param cb Region size.
1720 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
1721 * @thread EMT
1722 */
1723static DECLCALLBACK(int) vnetMap(PPCIDEVICE pPciDev, int iRegion,
1724 RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
1725{
1726 int rc;
1727 VNETSTATE *pState = PDMINS_2_DATA(pPciDev->pDevIns, VNETSTATE*);
1728
1729 if (enmType != PCI_ADDRESS_SPACE_IO)
1730 {
1731 /* We should never get here */
1732 AssertMsgFailed(("Invalid PCI address space param in map callback"));
1733 return VERR_INTERNAL_ERROR;
1734 }
1735
1736 pState->VPCI.addrIOPort = (RTIOPORT)GCPhysAddress;
1737 rc = PDMDevHlpIOPortRegister(pPciDev->pDevIns, pState->VPCI.addrIOPort,
1738 cb, 0, vnetIOPortOut, vnetIOPortIn,
1739 NULL, NULL, "VirtioNet");
1740#ifdef VNET_GC_SUPPORT
1741 AssertRCReturn(rc, rc);
1742 rc = PDMDevHlpIOPortRegisterR0(pPciDev->pDevIns, pState->VPCI.addrIOPort,
1743 cb, 0, "vnetIOPortOut", "vnetIOPortIn",
1744 NULL, NULL, "VirtioNet");
1745 AssertRCReturn(rc, rc);
1746 rc = PDMDevHlpIOPortRegisterRC(pPciDev->pDevIns, pState->VPCI.addrIOPort,
1747 cb, 0, "vnetIOPortOut", "vnetIOPortIn",
1748 NULL, NULL, "VirtioNet");
1749#endif
1750 AssertRC(rc);
1751 return rc;
1752}
1753
1754
1755/* -=-=-=-=- PDMDEVREG -=-=-=-=- */
1756
1757/**
1758 * Detach notification.
1759 *
1760 * One port on the network card has been disconnected from the network.
1761 *
1762 * @param pDevIns The device instance.
1763 * @param iLUN The logical unit which is being detached.
1764 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1765 */
1766static DECLCALLBACK(void) vnetDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1767{
1768 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1769 Log(("%s vnetDetach:\n", INSTANCE(pState)));
1770
1771 AssertLogRelReturnVoid(iLUN == 0);
1772
1773 int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
1774 if (RT_FAILURE(rc))
1775 {
1776 LogRel(("vnetDetach failed to enter critical section!\n"));
1777 return;
1778 }
1779
1780 /*
1781 * Zero some important members.
1782 */
1783 pState->pDrvBase = NULL;
1784 pState->pDrv = NULL;
1785
1786 vnetCsLeave(pState);
1787}
1788
1789/**
1790 * Attach the Network attachment.
1791 *
1792 * One port on the network card has been connected to a network.
1793 *
1794 * @returns VBox status code.
1795 * @param pDevIns The device instance.
1796 * @param iLUN The logical unit which is being attached.
1797 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1798 *
1799 * @remarks This code path is not used during construction.
1800 */
1801static DECLCALLBACK(int) vnetAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1802{
1803 VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1804 LogFlow(("%s vnetAttach:\n", INSTANCE(pState)));
1805
1806 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
1807
1808 int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
1809 if (RT_FAILURE(rc))
1810 {
1811 LogRel(("vnetAttach failed to enter critical section!\n"));
1812 return rc;
1813 }
1814
1815 /*
1816 * Attach the driver.
1817 */
1818 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
1819 if (RT_SUCCESS(rc))
1820 {
1821 if (rc == VINF_NAT_DNS)
1822 {
1823#ifdef RT_OS_LINUX
1824 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1825 N_("A Domain Name Server (DNS) for NAT networking could not be determined. Please check your /etc/resolv.conf for <tt>nameserver</tt> entries. Either add one manually (<i>man resolv.conf</i>) or ensure that your host is correctly connected to an ISP. If you ignore this warning the guest will not be able to perform nameserver lookups and it will probably observe delays if trying so"));
1826#else
1827 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1828 N_("A Domain Name Server (DNS) for NAT networking could not be determined. Ensure that your host is correctly connected to an ISP. If you ignore this warning the guest will not be able to perform nameserver lookups and it will probably observe delays if trying so"));
1829#endif
1830 }
1831 pState->pDrv = PDMIBASE_QUERY_INTERFACE(pState->pDrvBase, PDMINETWORKUP);
1832 AssertMsgStmt(pState->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
1833 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
1834 }
1835 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
1836 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
1837 {
1838 /* This should never happen because this function is not called
1839 * if there is no driver to attach! */
1840 Log(("%s No attached driver!\n", INSTANCE(pState)));
1841 }
1842
1843 /*
1844 * Temporary set the link down if it was up so that the guest
1845 * will know that we have change the configuration of the
1846 * network card
1847 */
1848 if (RT_SUCCESS(rc))
1849 vnetTempLinkDown(pState);
1850
1851 vnetCsLeave(pState);
1852 return rc;
1853
1854}
1855
1856/**
1857 * @copydoc FNPDMDEVSUSPEND
1858 */
1859static DECLCALLBACK(void) vnetSuspend(PPDMDEVINS pDevIns)
1860{
1861 /* Poke thread waiting for buffer space. */
1862 vnetWakeupReceive(pDevIns);
1863}
1864
1865/**
1866 * @copydoc FNPDMDEVPOWEROFF
1867 */
1868static DECLCALLBACK(void) vnetPowerOff(PPDMDEVINS pDevIns)
1869{
1870 /* Poke thread waiting for buffer space. */
1871 vnetWakeupReceive(pDevIns);
1872}
1873
1874/**
1875 * Device relocation callback.
1876 *
1877 * When this callback is called the device instance data, and if the
1878 * device have a GC component, is being relocated, or/and the selectors
1879 * have been changed. The device must use the chance to perform the
1880 * necessary pointer relocations and data updates.
1881 *
1882 * Before the GC code is executed the first time, this function will be
1883 * called with a 0 delta so GC pointer calculations can be one in one place.
1884 *
1885 * @param pDevIns Pointer to the device instance.
1886 * @param offDelta The relocation delta relative to the old location.
1887 *
1888 * @remark A relocation CANNOT fail.
1889 */
1890static DECLCALLBACK(void) vnetRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1891{
1892 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1893 vpciRelocate(pDevIns, offDelta);
1894 pState->pCanRxQueueRC = PDMQueueRCPtr(pState->pCanRxQueueR3);
1895#ifdef VNET_TX_DELAY
1896 pState->pTxTimerRC = TMTimerRCPtr(pState->pTxTimerR3);
1897#endif /* VNET_TX_DELAY */
1898 // TBD
1899}
1900
1901/**
1902 * Destruct a device instance.
1903 *
1904 * We need to free non-VM resources only.
1905 *
1906 * @returns VBox status.
1907 * @param pDevIns The device instance data.
1908 * @thread EMT
1909 */
1910static DECLCALLBACK(int) vnetDestruct(PPDMDEVINS pDevIns)
1911{
1912 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1913 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1914
1915 LogRel(("TxTimer stats (avg/min/max): %7d usec %7d usec %7d usec\n",
1916 pState->u32AvgDiff, pState->u32MinDiff, pState->u32MaxDiff));
1917 Log(("%s Destroying instance\n", INSTANCE(pState)));
1918 if (pState->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
1919 {
1920 RTSemEventSignal(pState->hEventMoreRxDescAvail);
1921 RTSemEventDestroy(pState->hEventMoreRxDescAvail);
1922 pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1923 }
1924
1925 // if (PDMCritSectIsInitialized(&pState->csRx))
1926 // PDMR3CritSectDelete(&pState->csRx);
1927
1928 return vpciDestruct(&pState->VPCI);
1929}
1930
1931/**
1932 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1933 */
1934static DECLCALLBACK(int) vnetConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1935{
1936 VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
1937 int rc;
1938 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1939
1940 /* Initialize PCI part first. */
1941 pState->VPCI.IBase.pfnQueryInterface = vnetQueryInterface;
1942 rc = vpciConstruct(pDevIns, &pState->VPCI, iInstance,
1943 VNET_NAME_FMT, VNET_PCI_SUBSYSTEM_ID,
1944 VNET_PCI_CLASS, VNET_N_QUEUES);
1945 pState->pRxQueue = vpciAddQueue(&pState->VPCI, 256, vnetQueueReceive, "RX ");
1946 pState->pTxQueue = vpciAddQueue(&pState->VPCI, 256, vnetQueueTransmit, "TX ");
1947 pState->pCtlQueue = vpciAddQueue(&pState->VPCI, 16, vnetQueueControl, "CTL");
1948
1949 Log(("%s Constructing new instance\n", INSTANCE(pState)));
1950
1951 pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1952
1953 /*
1954 * Validate configuration.
1955 */
1956 if (!CFGMR3AreValuesValid(pCfg, "MAC\0" "CableConnected\0" "LineSpeed\0" "LinkUpDelay\0"))
1957 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1958 N_("Invalid configuration for VirtioNet device"));
1959
1960 /* Get config params */
1961 rc = CFGMR3QueryBytes(pCfg, "MAC", pState->macConfigured.au8,
1962 sizeof(pState->macConfigured));
1963 if (RT_FAILURE(rc))
1964 return PDMDEV_SET_ERROR(pDevIns, rc,
1965 N_("Configuration error: Failed to get MAC address"));
1966 rc = CFGMR3QueryBool(pCfg, "CableConnected", &pState->fCableConnected);
1967 if (RT_FAILURE(rc))
1968 return PDMDEV_SET_ERROR(pDevIns, rc,
1969 N_("Configuration error: Failed to get the value of 'CableConnected'"));
1970 rc = CFGMR3QueryU32Def(pCfg, "LinkUpDelay", (uint32_t*)&pState->cMsLinkUpDelay, 5000); /* ms */
1971 if (RT_FAILURE(rc))
1972 return PDMDEV_SET_ERROR(pDevIns, rc,
1973 N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
1974 Assert(pState->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
1975 if (pState->cMsLinkUpDelay > 5000 || pState->cMsLinkUpDelay < 100)
1976 {
1977 LogRel(("%s WARNING! Link up delay is set to %u seconds!\n",
1978 INSTANCE(pState), pState->cMsLinkUpDelay / 1000));
1979 }
1980 Log(("%s Link up delay is set to %u seconds\n",
1981 INSTANCE(pState), pState->cMsLinkUpDelay / 1000));
1982
1983 /* Initialize PCI config space */
1984 memcpy(pState->config.mac.au8, pState->macConfigured.au8, sizeof(pState->config.mac.au8));
1985 pState->config.uStatus = 0;
1986
1987 /* Initialize state structure */
1988 pState->u32PktNo = 1;
1989
1990 /* Interfaces */
1991 pState->INetworkDown.pfnWaitReceiveAvail = vnetNetworkDown_WaitReceiveAvail;
1992 pState->INetworkDown.pfnReceive = vnetNetworkDown_Receive;
1993 pState->INetworkDown.pfnReceiveGso = vnetNetworkDown_ReceiveGso;
1994 pState->INetworkDown.pfnXmitPending = vnetNetworkDown_XmitPending;
1995
1996 pState->INetworkConfig.pfnGetMac = vnetGetMac;
1997 pState->INetworkConfig.pfnGetLinkState = vnetGetLinkState;
1998 pState->INetworkConfig.pfnSetLinkState = vnetSetLinkState;
1999
2000 /* Initialize critical section. */
2001 // char szTmp[sizeof(pState->VPCI.szInstance) + 2];
2002 // RTStrPrintf(szTmp, sizeof(szTmp), "%sRX", pState->VPCI.szInstance);
2003 // rc = PDMDevHlpCritSectInit(pDevIns, &pState->csRx, szTmp);
2004 // if (RT_FAILURE(rc))
2005 // return rc;
2006
2007 /* Map our ports to IO space. */
2008 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0,
2009 VPCI_CONFIG + sizeof(VNetPCIConfig),
2010 PCI_ADDRESS_SPACE_IO, vnetMap);
2011 if (RT_FAILURE(rc))
2012 return rc;
2013
2014
2015 /* Register save/restore state handlers. */
2016 rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIO_SAVEDSTATE_VERSION, sizeof(VNETSTATE), NULL,
2017 NULL, vnetLiveExec, NULL,
2018 vnetSavePrep, vnetSaveExec, NULL,
2019 vnetLoadPrep, vnetLoadExec, vnetLoadDone);
2020 if (RT_FAILURE(rc))
2021 return rc;
2022
2023 /* Create the RX notifier signaller. */
2024 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 1, 0,
2025 vnetCanRxQueueConsumer, true, "VNet-Rcv", &pState->pCanRxQueueR3);
2026 if (RT_FAILURE(rc))
2027 return rc;
2028 pState->pCanRxQueueR0 = PDMQueueR0Ptr(pState->pCanRxQueueR3);
2029 pState->pCanRxQueueRC = PDMQueueRCPtr(pState->pCanRxQueueR3);
2030
2031 /* Create Link Up Timer */
2032 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetLinkUpTimer, pState,
2033 TMTIMER_FLAGS_NO_CRIT_SECT,
2034 "VirtioNet Link Up Timer", &pState->pLinkUpTimer);
2035 if (RT_FAILURE(rc))
2036 return rc;
2037
2038#ifdef VNET_TX_DELAY
2039 /* Create Transmit Delay Timer */
2040 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetTxTimer, pState,
2041 TMTIMER_FLAGS_NO_CRIT_SECT,
2042 "VirtioNet TX Delay Timer", &pState->pTxTimerR3);
2043 if (RT_FAILURE(rc))
2044 return rc;
2045 pState->pTxTimerR0 = TMTimerR0Ptr(pState->pTxTimerR3);
2046 pState->pTxTimerRC = TMTimerRCPtr(pState->pTxTimerR3);
2047
2048 pState->u32i = pState->u32AvgDiff = pState->u32MaxDiff = 0;
2049 pState->u32MinDiff = ~0;
2050#endif /* VNET_TX_DELAY */
2051
2052 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
2053 if (RT_SUCCESS(rc))
2054 {
2055 if (rc == VINF_NAT_DNS)
2056 {
2057 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
2058 N_("A Domain Name Server (DNS) for NAT networking could not be determined. Ensure that your host is correctly connected to an ISP. If you ignore this warning the guest will not be able to perform nameserver lookups and it will probably observe delays if trying so"));
2059 }
2060 pState->pDrv = PDMIBASE_QUERY_INTERFACE(pState->pDrvBase, PDMINETWORKUP);
2061 AssertMsgReturn(pState->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
2062 VERR_PDM_MISSING_INTERFACE_BELOW);
2063 }
2064 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
2065 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME )
2066 {
2067 /* No error! */
2068 Log(("%s This adapter is not attached to any network!\n", INSTANCE(pState)));
2069 }
2070 else
2071 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
2072
2073 rc = RTSemEventCreate(&pState->hEventMoreRxDescAvail);
2074 if (RT_FAILURE(rc))
2075 return rc;
2076
2077 rc = vnetReset(pState);
2078 AssertRC(rc);
2079
2080 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/VNet%d/Bytes/Receive", iInstance);
2081 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/VNet%d/Bytes/Transmit", iInstance);
2082 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveGSO, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of received GSO packets", "/Devices/VNet%d/Packets/ReceiveGSO", iInstance);
2083 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitPackets, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of sent packets", "/Devices/VNet%d/Packets/Transmit", iInstance);
2084 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitGSO, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of sent GSO packets", "/Devices/VNet%d/Packets/Transmit-Gso", iInstance);
2085 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitCSum, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of completed TX checksums", "/Devices/VNet%d/Packets/Transmit-Csum", iInstance);
2086#if defined(VBOX_WITH_STATISTICS)
2087 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/VNet%d/Receive/Total", iInstance);
2088 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveStore, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive storing", "/Devices/VNet%d/Receive/Store", iInstance);
2089 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/VNet%d/RxOverflow", iInstance);
2090 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflowWakeup, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups", "/Devices/VNet%d/RxOverflowWakeup", iInstance);
2091 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC", "/Devices/VNet%d/Transmit/Total", iInstance);
2092 PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitSend, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC", "/Devices/VNet%d/Transmit/Send", iInstance);
2093#endif /* VBOX_WITH_STATISTICS */
2094
2095 return VINF_SUCCESS;
2096}
2097
2098/**
2099 * The device registration structure.
2100 */
2101const PDMDEVREG g_DeviceVirtioNet =
2102{
2103 /* Structure version. PDM_DEVREG_VERSION defines the current version. */
2104 PDM_DEVREG_VERSION,
2105 /* Device name. */
2106 "virtio-net",
2107 /* Name of guest context module (no path).
2108 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
2109 "VBoxDDGC.gc",
2110 /* Name of ring-0 module (no path).
2111 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
2112 "VBoxDDR0.r0",
2113 /* The description of the device. The UTF-8 string pointed to shall, like this structure,
2114 * remain unchanged from registration till VM destruction. */
2115 "Virtio Ethernet.\n",
2116
2117 /* Flags, combination of the PDM_DEVREG_FLAGS_* \#defines. */
2118#ifdef VNET_GC_SUPPORT
2119 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
2120#else
2121 PDM_DEVREG_FLAGS_DEFAULT_BITS,
2122#endif
2123 /* Device class(es), combination of the PDM_DEVREG_CLASS_* \#defines. */
2124 PDM_DEVREG_CLASS_NETWORK,
2125 /* Maximum number of instances (per VM). */
2126 ~0U,
2127 /* Size of the instance data. */
2128 sizeof(VNETSTATE),
2129
2130 /* Construct instance - required. */
2131 vnetConstruct,
2132 /* Destruct instance - optional. */
2133 vnetDestruct,
2134 /* Relocation command - optional. */
2135 vnetRelocate,
2136 /* I/O Control interface - optional. */
2137 NULL,
2138 /* Power on notification - optional. */
2139 NULL,
2140 /* Reset notification - optional. */
2141 NULL,
2142 /* Suspend notification - optional. */
2143 vnetSuspend,
2144 /* Resume notification - optional. */
2145 NULL,
2146 /* Attach command - optional. */
2147 vnetAttach,
2148 /* Detach notification - optional. */
2149 vnetDetach,
2150 /* Query a LUN base interface - optional. */
2151 NULL,
2152 /* Init complete notification - optional. */
2153 NULL,
2154 /* Power off notification - optional. */
2155 vnetPowerOff,
2156 /* pfnSoftReset */
2157 NULL,
2158 /* u32VersionEnd */
2159 PDM_DEVREG_VERSION
2160};
2161
2162#endif /* IN_RING3 */
2163#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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