VirtualBox

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

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

DevirtioNet.cpp: cleanups.

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

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