VirtualBox

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

Last change on this file since 45761 was 45025, checked in by vboxsync, 12 years ago

Update PDMDEVREG initialization comment so they refer to pfnMemSetup instead of pfnIOCtl.

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

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