VirtualBox

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

Last change on this file since 48749 was 47499, checked in by vboxsync, 11 years ago

Devices/Network: Temporarily disconnect network devices after the host resumed for NAT and bridged networks to let the guest pick up changed networks

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