VirtualBox

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

Last change on this file since 47396 was 46904, checked in by vboxsync, 12 years ago

IntNet, VirtioNet, NetFilter: Large frame support + drop oversized + UFO fix (#6821)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 76.4 KB
Line 
1/* $Id: DevVirtioNet.cpp 46904 2013-07-02 12:59:56Z 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 65535 + 18 /**< Max IP packet size + Ethernet header with VLAN tag */
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 Log5(("%s vnetTransmitPendingPackets: complete frame is %u bytes.\n",
1158 INSTANCE(pThis), uSize));
1159 Assert(uSize <= VNET_MAX_FRAME_SIZE);
1160 if (pThis->pDrv)
1161 {
1162 VNETHDR Hdr;
1163 PDMNETWORKGSO Gso, *pGso;
1164
1165 PDMDevHlpPhysRead(pThis->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[0].addr,
1166 &Hdr, sizeof(Hdr));
1167
1168 STAM_REL_COUNTER_INC(&pThis->StatTransmitPackets);
1169
1170 STAM_PROFILE_START(&pThis->StatTransmitSend, a);
1171
1172 pGso = vnetSetupGsoCtx(&Gso, &Hdr);
1173 /** @todo Optimize away the extra copying! (lazy bird) */
1174 PPDMSCATTERGATHER pSgBuf;
1175 int rc = pThis->pDrv->pfnAllocBuf(pThis->pDrv, uSize, pGso, &pSgBuf);
1176 if (RT_SUCCESS(rc))
1177 {
1178 Assert(pSgBuf->cSegs == 1);
1179 /* Assemble a complete frame. */
1180 for (unsigned int i = 1; i < elem.nOut; i++)
1181 {
1182 PDMDevHlpPhysRead(pThis->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[i].addr,
1183 ((uint8_t*)pSgBuf->aSegs[0].pvSeg) + uOffset,
1184 elem.aSegsOut[i].cb);
1185 uOffset += elem.aSegsOut[i].cb;
1186 }
1187 pSgBuf->cbUsed = uSize;
1188 vnetPacketDump(pThis, (uint8_t*)pSgBuf->aSegs[0].pvSeg, uSize, "--> Outgoing");
1189 if (pGso)
1190 {
1191 /* Some guests (RHEL) may report HdrLen excluding transport layer header! */
1192 /*
1193 * We cannot use cdHdrs provided by the guest because of different ways
1194 * it gets filled out by different versions of kernels.
1195 */
1196 //if (pGso->cbHdrs < Hdr.u16CSumStart + Hdr.u16CSumOffset + 2)
1197 {
1198 Log4(("%s vnetTransmitPendingPackets: HdrLen before adjustment %d.\n",
1199 INSTANCE(pThis), pGso->cbHdrsTotal));
1200 switch (pGso->u8Type)
1201 {
1202 case PDMNETWORKGSOTYPE_IPV4_TCP:
1203 case PDMNETWORKGSOTYPE_IPV6_TCP:
1204 pGso->cbHdrsTotal = Hdr.u16CSumStart +
1205 ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + Hdr.u16CSumStart))->th_off * 4;
1206 pGso->cbHdrsSeg = pGso->cbHdrsTotal;
1207 break;
1208 case PDMNETWORKGSOTYPE_IPV4_UDP:
1209 pGso->cbHdrsTotal = (uint8_t)(Hdr.u16CSumStart + sizeof(RTNETUDP));
1210 pGso->cbHdrsSeg = Hdr.u16CSumStart;
1211 break;
1212 }
1213 /* Update GSO structure embedded into the frame */
1214 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
1215 ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg = pGso->cbHdrsSeg;
1216 Log4(("%s vnetTransmitPendingPackets: adjusted HdrLen to %d.\n",
1217 INSTANCE(pThis), pGso->cbHdrsTotal));
1218 }
1219 Log2(("%s vnetTransmitPendingPackets: gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u"
1220 " off1=0x%x off2=0x%x\n", INSTANCE(pThis), pGso->u8Type,
1221 pGso->cbHdrsTotal, pGso->cbHdrsSeg, pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
1222 STAM_REL_COUNTER_INC(&pThis->StatTransmitGSO);
1223 }
1224 else if (Hdr.u8Flags & VNETHDR_F_NEEDS_CSUM)
1225 {
1226 STAM_REL_COUNTER_INC(&pThis->StatTransmitCSum);
1227 /*
1228 * This is not GSO frame but checksum offloading is requested.
1229 */
1230 vnetCompleteChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, uSize,
1231 Hdr.u16CSumStart, Hdr.u16CSumOffset);
1232 }
1233
1234 rc = pThis->pDrv->pfnSendBuf(pThis->pDrv, pSgBuf, false);
1235 }
1236 else
1237 {
1238 Log4(("virtio-net: failed to allocate SG buffer: size=%u rc=%Rrc\n", uSize, rc));
1239 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
1240 STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
1241 /* Stop trying to fetch TX descriptors until we get more bandwidth. */
1242 break;
1243 }
1244
1245 STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
1246 STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
1247 }
1248 }
1249 /* Remove this descriptor chain from the available ring */
1250 vqueueSkip(&pThis->VPCI, pQueue);
1251 vqueuePut(&pThis->VPCI, pQueue, &elem, sizeof(VNETHDR) + uOffset);
1252 vqueueSync(&pThis->VPCI, pQueue);
1253 STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
1254 }
1255 vpciSetWriteLed(&pThis->VPCI, false);
1256
1257 if (pDrv)
1258 pDrv->pfnEndXmit(pDrv);
1259 ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
1260}
1261
1262/**
1263 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
1264 */
1265static DECLCALLBACK(void) vnetNetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
1266{
1267 PVNETSTATE pThis = RT_FROM_MEMBER(pInterface, VNETSTATE, INetworkDown);
1268 vnetTransmitPendingPackets(pThis, pThis->pTxQueue, false /*fOnWorkerThread*/);
1269}
1270
1271#ifdef VNET_TX_DELAY
1272
1273static DECLCALLBACK(void) vnetQueueTransmit(void *pvState, PVQUEUE pQueue)
1274{
1275 PVNETSTATE pThis = (PVNETSTATE)pvState;
1276
1277 if (TMTimerIsActive(pThis->CTX_SUFF(pTxTimer)))
1278 {
1279 int rc = TMTimerStop(pThis->CTX_SUFF(pTxTimer));
1280 Log3(("%s vnetQueueTransmit: Got kicked with notification disabled, "
1281 "re-enable notification and flush TX queue\n", INSTANCE(pThis)));
1282 vnetTransmitPendingPackets(pThis, pQueue, false /*fOnWorkerThread*/);
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, true);
1288 vnetCsLeave(pThis);
1289 }
1290 }
1291 else
1292 {
1293 if (RT_FAILURE(vnetCsEnter(pThis, VERR_SEM_BUSY)))
1294 LogRel(("vnetQueueTransmit: Failed to enter critical section!/n"));
1295 else
1296 {
1297 vringSetNotification(&pThis->VPCI, &pThis->pTxQueue->VRing, false);
1298 TMTimerSetMicro(pThis->CTX_SUFF(pTxTimer), VNET_TX_DELAY);
1299 pThis->u64NanoTS = RTTimeNanoTS();
1300 vnetCsLeave(pThis);
1301 }
1302 }
1303}
1304
1305/**
1306 * @callback_method_impl{FNTMTIMERDEV, Transmit Delay Timer handler.}
1307 */
1308static DECLCALLBACK(void) vnetTxTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
1309{
1310 PVNETSTATE pThis = (PVNETSTATE)pvUser;
1311
1312 uint32_t u32MicroDiff = (uint32_t)((RTTimeNanoTS() - pThis->u64NanoTS)/1000);
1313 if (u32MicroDiff < pThis->u32MinDiff)
1314 pThis->u32MinDiff = u32MicroDiff;
1315 if (u32MicroDiff > pThis->u32MaxDiff)
1316 pThis->u32MaxDiff = u32MicroDiff;
1317 pThis->u32AvgDiff = (pThis->u32AvgDiff * pThis->u32i + u32MicroDiff) / (pThis->u32i + 1);
1318 pThis->u32i++;
1319 Log3(("vnetTxTimer: Expired, diff %9d usec, avg %9d usec, min %9d usec, max %9d usec\n",
1320 u32MicroDiff, pThis->u32AvgDiff, pThis->u32MinDiff, pThis->u32MaxDiff));
1321
1322// Log3(("%s vnetTxTimer: Expired\n", INSTANCE(pThis)));
1323 vnetTransmitPendingPackets(pThis, pThis->pTxQueue, false /*fOnWorkerThread*/);
1324 if (RT_FAILURE(vnetCsEnter(pThis, VERR_SEM_BUSY)))
1325 {
1326 LogRel(("vnetTxTimer: Failed to enter critical section!/n"));
1327 return;
1328 }
1329 vringSetNotification(&pThis->VPCI, &pThis->pTxQueue->VRing, true);
1330 vnetCsLeave(pThis);
1331}
1332
1333#else /* !VNET_TX_DELAY */
1334
1335static DECLCALLBACK(void) vnetQueueTransmit(void *pvState, PVQUEUE pQueue)
1336{
1337 PVNETSTATE pThis = (PVNETSTATE)pvState;
1338
1339 vnetTransmitPendingPackets(pThis, pQueue, false /*fOnWorkerThread*/);
1340}
1341
1342#endif /* !VNET_TX_DELAY */
1343
1344static uint8_t vnetControlRx(PVNETSTATE pThis, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1345{
1346 uint8_t u8Ack = VNET_OK;
1347 uint8_t fOn, fDrvWasPromisc = pThis->fPromiscuous | pThis->fAllMulti;
1348 PDMDevHlpPhysRead(pThis->VPCI.CTX_SUFF(pDevIns),
1349 pElem->aSegsOut[1].addr,
1350 &fOn, sizeof(fOn));
1351 Log(("%s vnetControlRx: uCommand=%u fOn=%u\n", INSTANCE(pThis), pCtlHdr->u8Command, fOn));
1352 switch (pCtlHdr->u8Command)
1353 {
1354 case VNET_CTRL_CMD_RX_MODE_PROMISC:
1355 pThis->fPromiscuous = !!fOn;
1356 break;
1357 case VNET_CTRL_CMD_RX_MODE_ALLMULTI:
1358 pThis->fAllMulti = !!fOn;
1359 break;
1360 default:
1361 u8Ack = VNET_ERROR;
1362 }
1363 if (fDrvWasPromisc != (pThis->fPromiscuous | pThis->fAllMulti) && pThis->pDrv)
1364 pThis->pDrv->pfnSetPromiscuousMode(pThis->pDrv,
1365 (pThis->fPromiscuous | pThis->fAllMulti));
1366
1367 return u8Ack;
1368}
1369
1370static uint8_t vnetControlMac(PVNETSTATE pThis, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1371{
1372 uint32_t nMacs = 0;
1373
1374 if (pCtlHdr->u8Command != VNET_CTRL_CMD_MAC_TABLE_SET
1375 || pElem->nOut != 3
1376 || pElem->aSegsOut[1].cb < sizeof(nMacs)
1377 || pElem->aSegsOut[2].cb < sizeof(nMacs))
1378 {
1379 Log(("%s vnetControlMac: Segment layout is wrong "
1380 "(u8Command=%u nOut=%u cb1=%u cb2=%u)\n", INSTANCE(pThis),
1381 pCtlHdr->u8Command, pElem->nOut,
1382 pElem->aSegsOut[1].cb, pElem->aSegsOut[2].cb));
1383 return VNET_ERROR;
1384 }
1385
1386 /* Load unicast addresses */
1387 PDMDevHlpPhysRead(pThis->VPCI.CTX_SUFF(pDevIns),
1388 pElem->aSegsOut[1].addr,
1389 &nMacs, sizeof(nMacs));
1390
1391 if (pElem->aSegsOut[1].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
1392 {
1393 Log(("%s vnetControlMac: The unicast mac segment is too small "
1394 "(nMacs=%u cb=%u)\n", INSTANCE(pThis), pElem->aSegsOut[1].cb));
1395 return VNET_ERROR;
1396 }
1397
1398 if (nMacs > VNET_MAC_FILTER_LEN)
1399 {
1400 Log(("%s vnetControlMac: MAC table is too big, have to use promiscuous"
1401 " mode (nMacs=%u)\n", INSTANCE(pThis), nMacs));
1402 pThis->fPromiscuous = true;
1403 }
1404 else
1405 {
1406 if (nMacs)
1407 PDMDevHlpPhysRead(pThis->VPCI.CTX_SUFF(pDevIns),
1408 pElem->aSegsOut[1].addr + sizeof(nMacs),
1409 pThis->aMacFilter, nMacs * sizeof(RTMAC));
1410 pThis->nMacFilterEntries = nMacs;
1411#ifdef DEBUG
1412 Log(("%s vnetControlMac: unicast macs:\n", INSTANCE(pThis)));
1413 for(unsigned i = 0; i < nMacs; i++)
1414 Log((" %RTmac\n", &pThis->aMacFilter[i]));
1415#endif /* DEBUG */
1416 }
1417
1418 /* Load multicast addresses */
1419 PDMDevHlpPhysRead(pThis->VPCI.CTX_SUFF(pDevIns),
1420 pElem->aSegsOut[2].addr,
1421 &nMacs, sizeof(nMacs));
1422
1423 if (pElem->aSegsOut[2].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
1424 {
1425 Log(("%s vnetControlMac: The multicast mac segment is too small "
1426 "(nMacs=%u cb=%u)\n", INSTANCE(pThis), pElem->aSegsOut[2].cb));
1427 return VNET_ERROR;
1428 }
1429
1430 if (nMacs > VNET_MAC_FILTER_LEN - pThis->nMacFilterEntries)
1431 {
1432 Log(("%s vnetControlMac: MAC table is too big, have to use allmulti"
1433 " mode (nMacs=%u)\n", INSTANCE(pThis), nMacs));
1434 pThis->fAllMulti = true;
1435 }
1436 else
1437 {
1438 if (nMacs)
1439 PDMDevHlpPhysRead(pThis->VPCI.CTX_SUFF(pDevIns),
1440 pElem->aSegsOut[2].addr + sizeof(nMacs),
1441 &pThis->aMacFilter[pThis->nMacFilterEntries],
1442 nMacs * sizeof(RTMAC));
1443#ifdef DEBUG
1444 Log(("%s vnetControlMac: multicast macs:\n", INSTANCE(pThis)));
1445 for(unsigned i = 0; i < nMacs; i++)
1446 Log((" %RTmac\n",
1447 &pThis->aMacFilter[i+pThis->nMacFilterEntries]));
1448#endif /* DEBUG */
1449 pThis->nMacFilterEntries += nMacs;
1450 }
1451
1452 return VNET_OK;
1453}
1454
1455static uint8_t vnetControlVlan(PVNETSTATE pThis, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
1456{
1457 uint8_t u8Ack = VNET_OK;
1458 uint16_t u16Vid;
1459
1460 if (pElem->nOut != 2 || pElem->aSegsOut[1].cb != sizeof(u16Vid))
1461 {
1462 Log(("%s vnetControlVlan: Segment layout is wrong "
1463 "(u8Command=%u nOut=%u cb=%u)\n", INSTANCE(pThis),
1464 pCtlHdr->u8Command, pElem->nOut, pElem->aSegsOut[1].cb));
1465 return VNET_ERROR;
1466 }
1467
1468 PDMDevHlpPhysRead(pThis->VPCI.CTX_SUFF(pDevIns),
1469 pElem->aSegsOut[1].addr,
1470 &u16Vid, sizeof(u16Vid));
1471
1472 if (u16Vid >= VNET_MAX_VID)
1473 {
1474 Log(("%s vnetControlVlan: VLAN ID is out of range "
1475 "(VID=%u)\n", INSTANCE(pThis), u16Vid));
1476 return VNET_ERROR;
1477 }
1478
1479 Log(("%s vnetControlVlan: uCommand=%u VID=%u\n", INSTANCE(pThis),
1480 pCtlHdr->u8Command, u16Vid));
1481
1482 switch (pCtlHdr->u8Command)
1483 {
1484 case VNET_CTRL_CMD_VLAN_ADD:
1485 ASMBitSet(pThis->aVlanFilter, u16Vid);
1486 break;
1487 case VNET_CTRL_CMD_VLAN_DEL:
1488 ASMBitClear(pThis->aVlanFilter, u16Vid);
1489 break;
1490 default:
1491 u8Ack = VNET_ERROR;
1492 }
1493
1494 return u8Ack;
1495}
1496
1497
1498static DECLCALLBACK(void) vnetQueueControl(void *pvState, PVQUEUE pQueue)
1499{
1500 PVNETSTATE pThis = (PVNETSTATE)pvState;
1501 uint8_t u8Ack;
1502 VQUEUEELEM elem;
1503 while (vqueueGet(&pThis->VPCI, pQueue, &elem))
1504 {
1505 unsigned int uOffset = 0;
1506 if (elem.nOut < 1 || elem.aSegsOut[0].cb < sizeof(VNETCTLHDR))
1507 {
1508 Log(("%s vnetQueueControl: The first 'out' segment is not the "
1509 "header! (%u < 1 || %u < %u).\n", INSTANCE(pThis), elem.nOut,
1510 elem.aSegsOut[0].cb,sizeof(VNETCTLHDR)));
1511 break; /* Skip the element and hope the next one is good. */
1512 }
1513 else if ( elem.nIn < 1
1514 || elem.aSegsIn[elem.nIn - 1].cb < sizeof(VNETCTLACK))
1515 {
1516 Log(("%s vnetQueueControl: The last 'in' segment is too small "
1517 "to hold the acknowledge! (%u < 1 || %u < %u).\n",
1518 INSTANCE(pThis), elem.nIn, elem.aSegsIn[elem.nIn - 1].cb,
1519 sizeof(VNETCTLACK)));
1520 break; /* Skip the element and hope the next one is good. */
1521 }
1522 else
1523 {
1524 VNETCTLHDR CtlHdr;
1525 PDMDevHlpPhysRead(pThis->VPCI.CTX_SUFF(pDevIns),
1526 elem.aSegsOut[0].addr,
1527 &CtlHdr, sizeof(CtlHdr));
1528 switch (CtlHdr.u8Class)
1529 {
1530 case VNET_CTRL_CLS_RX_MODE:
1531 u8Ack = vnetControlRx(pThis, &CtlHdr, &elem);
1532 break;
1533 case VNET_CTRL_CLS_MAC:
1534 u8Ack = vnetControlMac(pThis, &CtlHdr, &elem);
1535 break;
1536 case VNET_CTRL_CLS_VLAN:
1537 u8Ack = vnetControlVlan(pThis, &CtlHdr, &elem);
1538 break;
1539 default:
1540 u8Ack = VNET_ERROR;
1541 }
1542 Log(("%s Processed control message %u, ack=%u.\n", INSTANCE(pThis),
1543 CtlHdr.u8Class, u8Ack));
1544 PDMDevHlpPCIPhysWrite(pThis->VPCI.CTX_SUFF(pDevIns),
1545 elem.aSegsIn[elem.nIn - 1].addr,
1546 &u8Ack, sizeof(u8Ack));
1547 }
1548 vqueuePut(&pThis->VPCI, pQueue, &elem, sizeof(u8Ack));
1549 vqueueSync(&pThis->VPCI, pQueue);
1550 }
1551}
1552
1553
1554/* -=-=-=-=- Saved state -=-=-=-=- */
1555
1556/**
1557 * Saves the configuration.
1558 *
1559 * @param pThis The VNET state.
1560 * @param pSSM The handle to the saved state.
1561 */
1562static void vnetSaveConfig(PVNETSTATE pThis, PSSMHANDLE pSSM)
1563{
1564 SSMR3PutMem(pSSM, &pThis->macConfigured, sizeof(pThis->macConfigured));
1565}
1566
1567
1568/**
1569 * @callback_method_impl{FNSSMDEVLIVEEXEC}
1570 */
1571static DECLCALLBACK(int) vnetLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1572{
1573 PVNETSTATE pThis = PDMINS_2_DATA(pDevIns, PVNETSTATE);
1574 vnetSaveConfig(pThis, pSSM);
1575 return VINF_SSM_DONT_CALL_AGAIN;
1576}
1577
1578
1579/**
1580 * @callback_method_impl{FNSSMDEVSAVEPREP}
1581 */
1582static DECLCALLBACK(int) vnetSavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1583{
1584 PVNETSTATE pThis = PDMINS_2_DATA(pDevIns, PVNETSTATE);
1585
1586 int rc = vnetCsRxEnter(pThis, VERR_SEM_BUSY);
1587 if (RT_UNLIKELY(rc != VINF_SUCCESS))
1588 return rc;
1589 vnetCsRxLeave(pThis);
1590 return VINF_SUCCESS;
1591}
1592
1593
1594/**
1595 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1596 */
1597static DECLCALLBACK(int) vnetSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1598{
1599 PVNETSTATE pThis = PDMINS_2_DATA(pDevIns, PVNETSTATE);
1600
1601 /* Save config first */
1602 vnetSaveConfig(pThis, pSSM);
1603
1604 /* Save the common part */
1605 int rc = vpciSaveExec(&pThis->VPCI, pSSM);
1606 AssertRCReturn(rc, rc);
1607 /* Save device-specific part */
1608 rc = SSMR3PutMem( pSSM, pThis->config.mac.au8, sizeof(pThis->config.mac));
1609 AssertRCReturn(rc, rc);
1610 rc = SSMR3PutBool(pSSM, pThis->fPromiscuous);
1611 AssertRCReturn(rc, rc);
1612 rc = SSMR3PutBool(pSSM, pThis->fAllMulti);
1613 AssertRCReturn(rc, rc);
1614 rc = SSMR3PutU32( pSSM, pThis->nMacFilterEntries);
1615 AssertRCReturn(rc, rc);
1616 rc = SSMR3PutMem( pSSM, pThis->aMacFilter,
1617 pThis->nMacFilterEntries * sizeof(RTMAC));
1618 AssertRCReturn(rc, rc);
1619 rc = SSMR3PutMem( pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
1620 AssertRCReturn(rc, rc);
1621 Log(("%s State has been saved\n", INSTANCE(pThis)));
1622 return VINF_SUCCESS;
1623}
1624
1625
1626/**
1627 * @callback_method_impl{FNSSMDEVLOADPREP, Serializes the receive thread, it may
1628 * be working inside the critsect. }
1629 */
1630static DECLCALLBACK(int) vnetLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1631{
1632 PVNETSTATE pThis = PDMINS_2_DATA(pDevIns, PVNETSTATE);
1633
1634 int rc = vnetCsRxEnter(pThis, VERR_SEM_BUSY);
1635 if (RT_UNLIKELY(rc != VINF_SUCCESS))
1636 return rc;
1637 vnetCsRxLeave(pThis);
1638 return VINF_SUCCESS;
1639}
1640
1641
1642/**
1643 * Takes down the link temporarily if it's current status is up.
1644 *
1645 * This is used during restore and when replumbing the network link.
1646 *
1647 * The temporary link outage is supposed to indicate to the OS that all network
1648 * connections have been lost and that it for instance is appropriate to
1649 * renegotiate any DHCP lease.
1650 *
1651 * @param pThis The Virtual I/O network device state.
1652 */
1653static void vnetTempLinkDown(PVNETSTATE pThis)
1654{
1655 if (STATUS & VNET_S_LINK_UP)
1656 {
1657 STATUS &= ~VNET_S_LINK_UP;
1658 vpciRaiseInterrupt(&pThis->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1659 /* Restore the link back in 5 seconds. */
1660 int rc = TMTimerSetMillies(pThis->pLinkUpTimer, pThis->cMsLinkUpDelay);
1661 AssertRC(rc);
1662 }
1663}
1664
1665
1666/**
1667 * @callback_method_impl{FNSSMDEVLOADEXEC}
1668 */
1669static DECLCALLBACK(int) vnetLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1670{
1671 PVNETSTATE pThis = PDMINS_2_DATA(pDevIns, PVNETSTATE);
1672 int rc;
1673
1674 /* config checks */
1675 RTMAC macConfigured;
1676 rc = SSMR3GetMem(pSSM, &macConfigured, sizeof(macConfigured));
1677 AssertRCReturn(rc, rc);
1678 if (memcmp(&macConfigured, &pThis->macConfigured, sizeof(macConfigured))
1679 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
1680 LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n", INSTANCE(pThis), &pThis->macConfigured, &macConfigured));
1681
1682 rc = vpciLoadExec(&pThis->VPCI, pSSM, uVersion, uPass, VNET_N_QUEUES);
1683 AssertRCReturn(rc, rc);
1684
1685 if (uPass == SSM_PASS_FINAL)
1686 {
1687 rc = SSMR3GetMem( pSSM, pThis->config.mac.au8,
1688 sizeof(pThis->config.mac));
1689 AssertRCReturn(rc, rc);
1690
1691 if (uVersion > VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1)
1692 {
1693 rc = SSMR3GetBool(pSSM, &pThis->fPromiscuous);
1694 AssertRCReturn(rc, rc);
1695 rc = SSMR3GetBool(pSSM, &pThis->fAllMulti);
1696 AssertRCReturn(rc, rc);
1697 rc = SSMR3GetU32(pSSM, &pThis->nMacFilterEntries);
1698 AssertRCReturn(rc, rc);
1699 rc = SSMR3GetMem(pSSM, pThis->aMacFilter,
1700 pThis->nMacFilterEntries * sizeof(RTMAC));
1701 AssertRCReturn(rc, rc);
1702 /* Clear the rest. */
1703 if (pThis->nMacFilterEntries < VNET_MAC_FILTER_LEN)
1704 memset(&pThis->aMacFilter[pThis->nMacFilterEntries],
1705 0,
1706 (VNET_MAC_FILTER_LEN - pThis->nMacFilterEntries)
1707 * sizeof(RTMAC));
1708 rc = SSMR3GetMem(pSSM, pThis->aVlanFilter,
1709 sizeof(pThis->aVlanFilter));
1710 AssertRCReturn(rc, rc);
1711 }
1712 else
1713 {
1714 pThis->fPromiscuous = true;
1715 pThis->fAllMulti = false;
1716 pThis->nMacFilterEntries = 0;
1717 memset(pThis->aMacFilter, 0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
1718 memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
1719 if (pThis->pDrv)
1720 pThis->pDrv->pfnSetPromiscuousMode(pThis->pDrv, true);
1721 }
1722 }
1723
1724 return rc;
1725}
1726
1727
1728/**
1729 * @callback_method_impl{FNSSMDEVLOADDONE, Link status adjustments after
1730 * loading.}
1731 */
1732static DECLCALLBACK(int) vnetLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1733{
1734 PVNETSTATE pThis = PDMINS_2_DATA(pDevIns, PVNETSTATE);
1735
1736 if (pThis->pDrv)
1737 pThis->pDrv->pfnSetPromiscuousMode(pThis->pDrv,
1738 (pThis->fPromiscuous | pThis->fAllMulti));
1739 /*
1740 * Indicate link down to the guest OS that all network connections have
1741 * been lost, unless we've been teleported here.
1742 */
1743 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
1744 vnetTempLinkDown(pThis);
1745
1746 return VINF_SUCCESS;
1747}
1748
1749
1750/* -=-=-=-=- PCI Device -=-=-=-=- */
1751
1752/**
1753 * @callback_method_impl{FNPCIIOREGIONMAP}
1754 */
1755static DECLCALLBACK(int) vnetMap(PPCIDEVICE pPciDev, int iRegion,
1756 RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
1757{
1758 PVNETSTATE pThis = PDMINS_2_DATA(pPciDev->pDevIns, PVNETSTATE);
1759 int rc;
1760
1761 if (enmType != PCI_ADDRESS_SPACE_IO)
1762 {
1763 /* We should never get here */
1764 AssertMsgFailed(("Invalid PCI address space param in map callback"));
1765 return VERR_INTERNAL_ERROR;
1766 }
1767
1768 pThis->VPCI.IOPortBase = (RTIOPORT)GCPhysAddress;
1769 rc = PDMDevHlpIOPortRegister(pPciDev->pDevIns, pThis->VPCI.IOPortBase,
1770 cb, 0, vnetIOPortOut, vnetIOPortIn,
1771 NULL, NULL, "VirtioNet");
1772#ifdef VNET_GC_SUPPORT
1773 AssertRCReturn(rc, rc);
1774 rc = PDMDevHlpIOPortRegisterR0(pPciDev->pDevIns, pThis->VPCI.IOPortBase,
1775 cb, 0, "vnetIOPortOut", "vnetIOPortIn",
1776 NULL, NULL, "VirtioNet");
1777 AssertRCReturn(rc, rc);
1778 rc = PDMDevHlpIOPortRegisterRC(pPciDev->pDevIns, pThis->VPCI.IOPortBase,
1779 cb, 0, "vnetIOPortOut", "vnetIOPortIn",
1780 NULL, NULL, "VirtioNet");
1781#endif
1782 AssertRC(rc);
1783 return rc;
1784}
1785
1786
1787/* -=-=-=-=- PDMDEVREG -=-=-=-=- */
1788
1789/**
1790 * @interface_method_impl{PDMDEVREG,pfnDetach}
1791 */
1792static DECLCALLBACK(void) vnetDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1793{
1794 PVNETSTATE pThis = PDMINS_2_DATA(pDevIns, PVNETSTATE);
1795 Log(("%s vnetDetach:\n", INSTANCE(pThis)));
1796
1797 AssertLogRelReturnVoid(iLUN == 0);
1798
1799 int rc = vnetCsEnter(pThis, VERR_SEM_BUSY);
1800 if (RT_FAILURE(rc))
1801 {
1802 LogRel(("vnetDetach failed to enter critical section!\n"));
1803 return;
1804 }
1805
1806 /*
1807 * Zero some important members.
1808 */
1809 pThis->pDrvBase = NULL;
1810 pThis->pDrv = NULL;
1811
1812 vnetCsLeave(pThis);
1813}
1814
1815
1816/**
1817 * @interface_method_impl{PDMDEVREG,pfnAttach}
1818 */
1819static DECLCALLBACK(int) vnetAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1820{
1821 PVNETSTATE pThis = PDMINS_2_DATA(pDevIns, PVNETSTATE);
1822 LogFlow(("%s vnetAttach:\n", INSTANCE(pThis)));
1823
1824 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
1825
1826 int rc = vnetCsEnter(pThis, VERR_SEM_BUSY);
1827 if (RT_FAILURE(rc))
1828 {
1829 LogRel(("vnetAttach failed to enter critical section!\n"));
1830 return rc;
1831 }
1832
1833 /*
1834 * Attach the driver.
1835 */
1836 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->VPCI.IBase, &pThis->pDrvBase, "Network Port");
1837 if (RT_SUCCESS(rc))
1838 {
1839 if (rc == VINF_NAT_DNS)
1840 {
1841#ifdef RT_OS_LINUX
1842 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1843 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"));
1844#else
1845 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
1846 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"));
1847#endif
1848 }
1849 pThis->pDrv = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMINETWORKUP);
1850 AssertMsgStmt(pThis->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
1851 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
1852 }
1853 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
1854 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
1855 {
1856 /* This should never happen because this function is not called
1857 * if there is no driver to attach! */
1858 Log(("%s No attached driver!\n", INSTANCE(pThis)));
1859 }
1860
1861 /*
1862 * Temporary set the link down if it was up so that the guest
1863 * will know that we have change the configuration of the
1864 * network card
1865 */
1866 if (RT_SUCCESS(rc))
1867 vnetTempLinkDown(pThis);
1868
1869 vnetCsLeave(pThis);
1870 return rc;
1871
1872}
1873
1874
1875/**
1876 * @interface_method_impl{PDMDEVREG,pfnSuspend}
1877 */
1878static DECLCALLBACK(void) vnetSuspend(PPDMDEVINS pDevIns)
1879{
1880 /* Poke thread waiting for buffer space. */
1881 vnetWakeupReceive(pDevIns);
1882}
1883
1884
1885/**
1886 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
1887 */
1888static DECLCALLBACK(void) vnetPowerOff(PPDMDEVINS pDevIns)
1889{
1890 /* Poke thread waiting for buffer space. */
1891 vnetWakeupReceive(pDevIns);
1892}
1893
1894
1895/**
1896 * @interface_method_impl{PDMDEVREG,pfnRelocate}
1897 */
1898static DECLCALLBACK(void) vnetRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1899{
1900 PVNETSTATE pThis = PDMINS_2_DATA(pDevIns, PVNETSTATE);
1901 vpciRelocate(pDevIns, offDelta);
1902 pThis->pCanRxQueueRC = PDMQueueRCPtr(pThis->pCanRxQueueR3);
1903#ifdef VNET_TX_DELAY
1904 pThis->pTxTimerRC = TMTimerRCPtr(pThis->pTxTimerR3);
1905#endif /* VNET_TX_DELAY */
1906 // TBD
1907}
1908
1909
1910/**
1911 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1912 */
1913static DECLCALLBACK(int) vnetDestruct(PPDMDEVINS pDevIns)
1914{
1915 PVNETSTATE pThis = PDMINS_2_DATA(pDevIns, PVNETSTATE);
1916 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1917
1918 LogRel(("TxTimer stats (avg/min/max): %7d usec %7d usec %7d usec\n",
1919 pThis->u32AvgDiff, pThis->u32MinDiff, pThis->u32MaxDiff));
1920 Log(("%s Destroying instance\n", INSTANCE(pThis)));
1921 if (pThis->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
1922 {
1923 RTSemEventSignal(pThis->hEventMoreRxDescAvail);
1924 RTSemEventDestroy(pThis->hEventMoreRxDescAvail);
1925 pThis->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1926 }
1927
1928 // if (PDMCritSectIsInitialized(&pThis->csRx))
1929 // PDMR3CritSectDelete(&pThis->csRx);
1930
1931 return vpciDestruct(&pThis->VPCI);
1932}
1933
1934
1935/**
1936 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1937 */
1938static DECLCALLBACK(int) vnetConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1939{
1940 PVNETSTATE pThis = PDMINS_2_DATA(pDevIns, PVNETSTATE);
1941 int rc;
1942 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1943
1944 /* Initialize the instance data suffiencently for the destructor not to blow up. */
1945 pThis->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
1946
1947 /* Do our own locking. */
1948 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
1949 AssertRCReturn(rc, rc);
1950
1951 /* Initialize PCI part. */
1952 pThis->VPCI.IBase.pfnQueryInterface = vnetQueryInterface;
1953 rc = vpciConstruct(pDevIns, &pThis->VPCI, iInstance,
1954 VNET_NAME_FMT, VNET_PCI_SUBSYSTEM_ID,
1955 VNET_PCI_CLASS, VNET_N_QUEUES);
1956 pThis->pRxQueue = vpciAddQueue(&pThis->VPCI, 256, vnetQueueReceive, "RX ");
1957 pThis->pTxQueue = vpciAddQueue(&pThis->VPCI, 256, vnetQueueTransmit, "TX ");
1958 pThis->pCtlQueue = vpciAddQueue(&pThis->VPCI, 16, vnetQueueControl, "CTL");
1959
1960 Log(("%s Constructing new instance\n", INSTANCE(pThis)));
1961
1962 /*
1963 * Validate configuration.
1964 */
1965 if (!CFGMR3AreValuesValid(pCfg, "MAC\0" "CableConnected\0" "LineSpeed\0" "LinkUpDelay\0"))
1966 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1967 N_("Invalid configuration for VirtioNet device"));
1968
1969 /* Get config params */
1970 rc = CFGMR3QueryBytes(pCfg, "MAC", pThis->macConfigured.au8,
1971 sizeof(pThis->macConfigured));
1972 if (RT_FAILURE(rc))
1973 return PDMDEV_SET_ERROR(pDevIns, rc,
1974 N_("Configuration error: Failed to get MAC address"));
1975 rc = CFGMR3QueryBool(pCfg, "CableConnected", &pThis->fCableConnected);
1976 if (RT_FAILURE(rc))
1977 return PDMDEV_SET_ERROR(pDevIns, rc,
1978 N_("Configuration error: Failed to get the value of 'CableConnected'"));
1979 rc = CFGMR3QueryU32Def(pCfg, "LinkUpDelay", (uint32_t*)&pThis->cMsLinkUpDelay, 5000); /* ms */
1980 if (RT_FAILURE(rc))
1981 return PDMDEV_SET_ERROR(pDevIns, rc,
1982 N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
1983 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
1984 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
1985 {
1986 LogRel(("%s WARNING! Link up delay is set to %u seconds!\n",
1987 INSTANCE(pThis), pThis->cMsLinkUpDelay / 1000));
1988 }
1989 Log(("%s Link up delay is set to %u seconds\n",
1990 INSTANCE(pThis), pThis->cMsLinkUpDelay / 1000));
1991
1992
1993 vnetPrintFeatures(pThis, vnetIoCb_GetHostFeatures(pThis), "Device supports the following features");
1994
1995 /* Initialize PCI config space */
1996 memcpy(pThis->config.mac.au8, pThis->macConfigured.au8, sizeof(pThis->config.mac.au8));
1997 pThis->config.uStatus = 0;
1998
1999 /* Initialize state structure */
2000 pThis->u32PktNo = 1;
2001
2002 /* Interfaces */
2003 pThis->INetworkDown.pfnWaitReceiveAvail = vnetNetworkDown_WaitReceiveAvail;
2004 pThis->INetworkDown.pfnReceive = vnetNetworkDown_Receive;
2005 pThis->INetworkDown.pfnReceiveGso = vnetNetworkDown_ReceiveGso;
2006 pThis->INetworkDown.pfnXmitPending = vnetNetworkDown_XmitPending;
2007
2008 pThis->INetworkConfig.pfnGetMac = vnetGetMac;
2009 pThis->INetworkConfig.pfnGetLinkState = vnetGetLinkState;
2010 pThis->INetworkConfig.pfnSetLinkState = vnetSetLinkState;
2011
2012 /* Initialize critical section. */
2013 // char szTmp[sizeof(pThis->VPCI.szInstance) + 2];
2014 // RTStrPrintf(szTmp, sizeof(szTmp), "%sRX", pThis->VPCI.szInstance);
2015 // rc = PDMDevHlpCritSectInit(pDevIns, &pThis->csRx, szTmp);
2016 // if (RT_FAILURE(rc))
2017 // return rc;
2018
2019 /* Map our ports to IO space. */
2020 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0,
2021 VPCI_CONFIG + sizeof(VNetPCIConfig),
2022 PCI_ADDRESS_SPACE_IO, vnetMap);
2023 if (RT_FAILURE(rc))
2024 return rc;
2025
2026
2027 /* Register save/restore state handlers. */
2028 rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIO_SAVEDSTATE_VERSION, sizeof(VNETSTATE), NULL,
2029 NULL, vnetLiveExec, NULL,
2030 vnetSavePrep, vnetSaveExec, NULL,
2031 vnetLoadPrep, vnetLoadExec, vnetLoadDone);
2032 if (RT_FAILURE(rc))
2033 return rc;
2034
2035 /* Create the RX notifier signaller. */
2036 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 1, 0,
2037 vnetCanRxQueueConsumer, true, "VNet-Rcv", &pThis->pCanRxQueueR3);
2038 if (RT_FAILURE(rc))
2039 return rc;
2040 pThis->pCanRxQueueR0 = PDMQueueR0Ptr(pThis->pCanRxQueueR3);
2041 pThis->pCanRxQueueRC = PDMQueueRCPtr(pThis->pCanRxQueueR3);
2042
2043 /* Create Link Up Timer */
2044 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetLinkUpTimer, pThis,
2045 TMTIMER_FLAGS_NO_CRIT_SECT,
2046 "VirtioNet Link Up Timer", &pThis->pLinkUpTimer);
2047 if (RT_FAILURE(rc))
2048 return rc;
2049
2050#ifdef VNET_TX_DELAY
2051 /* Create Transmit Delay Timer */
2052 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetTxTimer, pThis,
2053 TMTIMER_FLAGS_NO_CRIT_SECT,
2054 "VirtioNet TX Delay Timer", &pThis->pTxTimerR3);
2055 if (RT_FAILURE(rc))
2056 return rc;
2057 pThis->pTxTimerR0 = TMTimerR0Ptr(pThis->pTxTimerR3);
2058 pThis->pTxTimerRC = TMTimerRCPtr(pThis->pTxTimerR3);
2059
2060 pThis->u32i = pThis->u32AvgDiff = pThis->u32MaxDiff = 0;
2061 pThis->u32MinDiff = ~0;
2062#endif /* VNET_TX_DELAY */
2063
2064 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->VPCI.IBase, &pThis->pDrvBase, "Network Port");
2065 if (RT_SUCCESS(rc))
2066 {
2067 if (rc == VINF_NAT_DNS)
2068 {
2069 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
2070 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"));
2071 }
2072 pThis->pDrv = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMINETWORKUP);
2073 AssertMsgReturn(pThis->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
2074 VERR_PDM_MISSING_INTERFACE_BELOW);
2075 }
2076 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
2077 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME )
2078 {
2079 /* No error! */
2080 Log(("%s This adapter is not attached to any network!\n", INSTANCE(pThis)));
2081 }
2082 else
2083 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
2084
2085 rc = RTSemEventCreate(&pThis->hEventMoreRxDescAvail);
2086 if (RT_FAILURE(rc))
2087 return rc;
2088
2089 rc = vnetIoCb_Reset(pThis);
2090 AssertRC(rc);
2091
2092 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Public/Net/VNet%u/BytesReceived", iInstance);
2093 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Public/Net/VNet%u/BytesTransmitted", iInstance);
2094
2095 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/VNet%d/ReceiveBytes", iInstance);
2096 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/VNet%d/TransmitBytes", iInstance);
2097 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveGSO, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of received GSO packets", "/Devices/VNet%d/Packets/ReceiveGSO", iInstance);
2098 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitPackets, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of sent packets", "/Devices/VNet%d/Packets/Transmit", iInstance);
2099 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitGSO, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of sent GSO packets", "/Devices/VNet%d/Packets/Transmit-Gso", iInstance);
2100 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitCSum, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of completed TX checksums", "/Devices/VNet%d/Packets/Transmit-Csum", iInstance);
2101#if defined(VBOX_WITH_STATISTICS)
2102 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/VNet%d/Receive/Total", iInstance);
2103 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveStore, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive storing", "/Devices/VNet%d/Receive/Store", iInstance);
2104 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/VNet%d/RxOverflow", iInstance);
2105 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups", "/Devices/VNet%d/RxOverflowWakeup", iInstance);
2106 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC", "/Devices/VNet%d/Transmit/Total", iInstance);
2107 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitSend, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC", "/Devices/VNet%d/Transmit/Send", iInstance);
2108#endif /* VBOX_WITH_STATISTICS */
2109
2110 return VINF_SUCCESS;
2111}
2112
2113/**
2114 * The device registration structure.
2115 */
2116const PDMDEVREG g_DeviceVirtioNet =
2117{
2118 /* Structure version. PDM_DEVREG_VERSION defines the current version. */
2119 PDM_DEVREG_VERSION,
2120 /* Device name. */
2121 "virtio-net",
2122 /* Name of guest context module (no path).
2123 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
2124 "VBoxDDGC.gc",
2125 /* Name of ring-0 module (no path).
2126 * Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
2127 "VBoxDDR0.r0",
2128 /* The description of the device. The UTF-8 string pointed to shall, like this structure,
2129 * remain unchanged from registration till VM destruction. */
2130 "Virtio Ethernet.\n",
2131
2132 /* Flags, combination of the PDM_DEVREG_FLAGS_* \#defines. */
2133#ifdef VNET_GC_SUPPORT
2134 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
2135#else
2136 PDM_DEVREG_FLAGS_DEFAULT_BITS,
2137#endif
2138 /* Device class(es), combination of the PDM_DEVREG_CLASS_* \#defines. */
2139 PDM_DEVREG_CLASS_NETWORK,
2140 /* Maximum number of instances (per VM). */
2141 ~0U,
2142 /* Size of the instance data. */
2143 sizeof(VNETSTATE),
2144
2145 /* pfnConstruct */
2146 vnetConstruct,
2147 /* pfnDestruct */
2148 vnetDestruct,
2149 /* pfnRelocate */
2150 vnetRelocate,
2151 /* pfnMemSetup. */
2152 NULL,
2153 /* pfnPowerOn */
2154 NULL,
2155 /* pfnReset */
2156 NULL,
2157 /* pfnSuspend */
2158 vnetSuspend,
2159 /* pfnResume */
2160 NULL,
2161 /* pfnAttach */
2162 vnetAttach,
2163 /* pfnDetach */
2164 vnetDetach,
2165 /* pfnQueryInterface */
2166 NULL,
2167 /* pfnInitComplete */
2168 NULL,
2169 /* pfnPowerOff */
2170 vnetPowerOff,
2171 /* pfnSoftReset */
2172 NULL,
2173
2174 /* u32VersionEnd */
2175 PDM_DEVREG_VERSION
2176};
2177
2178#endif /* IN_RING3 */
2179#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

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