From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:49469) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TWY49-0007EA-92 for qemu-devel@nongnu.org; Thu, 08 Nov 2012 14:47:11 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TWY43-0007JB-3i for qemu-devel@nongnu.org; Thu, 08 Nov 2012 14:47:05 -0500 Received: from chello084112167138.7.11.vie.surfer.at ([84.112.167.138]:51187 helo=wiesinger.com) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TWY42-00074l-4d for qemu-devel@nongnu.org; Thu, 08 Nov 2012 14:46:59 -0500 Message-ID: <509C0652.5030509@wiesinger.com> Date: Thu, 08 Nov 2012 20:21:54 +0100 From: Gerhard Wiesinger MIME-Version: 1.0 References: <1334665961-16826-1-git-send-email-dmitry.fleytman@ravellosystems.com> <1334665961-16826-8-git-send-email-dmitry.fleytman@ravellosystems.com> <20120430081807.GB32638@redhat.com> In-Reply-To: <20120430081807.GB32638@redhat.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [Qemu-devel] [PATCH 7/7 V6] VMXNET3 paravirtualized device implementation Device "vmxnet3" added. List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Dmitry Fleytman , Alex Fishman , Izik Eidus , Yan Vugenfirer , Dmitry Fleytman Cc: Anthony Liguori , yvugenfi@redhat.com, qemu-devel@nongnu.org, "Michael S. Tsirkin" Hello, Can you fix the minor issues and submit another patch V7 that it gets accepted. Thank you. Ciao, Gerhard On 30.04.2012 10:18, Michael S. Tsirkin wrote: > On Tue, Apr 17, 2012 at 03:32:41PM +0300, Dmitry Fleytman wrote: >> From: Dmitry Fleytman >> >> Signed-off-by: Dmitry Fleytman >> Signed-off-by: Yan Vugenfirer > noticed I didn't review this patch in the series, > here comes. > >> --- >> Makefile.objs | 1 + >> default-configs/pci.mak | 1 + >> hw/pci.h | 1 + >> hw/vmxnet3.c | 2435 +++++++++++++++++++++++++++++++++++++++++++++++ >> hw/vmxnet3.h | 762 +++++++++++++++ >> 5 files changed, 3200 insertions(+), 0 deletions(-) >> create mode 100644 hw/vmxnet3.c >> create mode 100644 hw/vmxnet3.h >> >> diff --git a/Makefile.objs b/Makefile.objs >> index f308b57..b1dd451 100644 >> --- a/Makefile.objs >> +++ b/Makefile.objs >> @@ -284,6 +284,7 @@ hw-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o >> hw-obj-$(CONFIG_PCNET_COMMON) += pcnet.o >> hw-obj-$(CONFIG_E1000_PCI) += e1000.o >> hw-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o >> +hw-obj-$(CONFIG_VMXNET3_PCI) += vmxnet3.o vmxnet_utils.o vmxnet_pkt.o >> >> hw-obj-$(CONFIG_SMC91C111) += smc91c111.o >> hw-obj-$(CONFIG_LAN9118) += lan9118.o >> diff --git a/default-configs/pci.mak b/default-configs/pci.mak >> index 9d3e1db..2d7748d 100644 >> --- a/default-configs/pci.mak >> +++ b/default-configs/pci.mak >> @@ -12,6 +12,7 @@ CONFIG_PCNET_COMMON=y >> CONFIG_LSI_SCSI_PCI=y >> CONFIG_RTL8139_PCI=y >> CONFIG_E1000_PCI=y >> +CONFIG_VMXNET3_PCI=y >> CONFIG_IDE_CORE=y >> CONFIG_IDE_QDEV=y >> CONFIG_IDE_PCI=y >> diff --git a/hw/pci.h b/hw/pci.h >> index 4f19fdb..fee8250 100644 >> --- a/hw/pci.h >> +++ b/hw/pci.h >> @@ -60,6 +60,7 @@ >> #define PCI_DEVICE_ID_VMWARE_NET 0x0720 >> #define PCI_DEVICE_ID_VMWARE_SCSI 0x0730 >> #define PCI_DEVICE_ID_VMWARE_IDE 0x1729 >> +#define PCI_DEVICE_ID_VMWARE_VMXNET3 0x07B0 >> >> /* Intel (0x8086) */ >> #define PCI_DEVICE_ID_INTEL_82551IT 0x1209 >> diff --git a/hw/vmxnet3.c b/hw/vmxnet3.c >> new file mode 100644 >> index 0000000..d58a74a >> --- /dev/null >> +++ b/hw/vmxnet3.c >> @@ -0,0 +1,2435 @@ >> +/* >> + * QEMU VMWARE VMXNET3 paravirtual NIC >> + * >> + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) >> + * >> + * Developed by Daynix Computing LTD (http://www.daynix.com) >> + * >> + * Authors: >> + * Dmitry Fleytman >> + * Tamir Shomer >> + * Yan Vugenfirer >> + * >> + * This work is licensed under the terms of the GNU GPL, version 2 or later. >> + * See the COPYING file in the top-level directory. >> + * >> + */ >> + >> +#include "hw.h" >> +#include "pci.h" >> +#include "net.h" >> +#include "virtio-net.h" >> +#include "net/tap.h" >> +#include "net/checksum.h" >> +#include "sysemu.h" >> +#include "iov.h" >> +#include "bswap.h" >> +#include "msix.h" >> +#include "msi.h" >> + >> +#include "vmxnet3.h" >> +#include "vmxnet_debug.h" >> +#include "vmware_utils.h" >> +#include "vmxnet_utils.h" >> +#include "vmxnet_pkt.h" >> + >> +#define PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION 0x1 >> +#define VMXNET3_MSIX_BAR_SIZE 0x2000 >> + >> +#define VMXNET3_BAR0_IDX (0) >> +#define VMXNET3_BAR1_IDX (1) >> +#define VMXNET3_MSIX_BAR_IDX (2) >> + >> +/* Link speed in Mbps should be shifted by 16 */ >> +#define VMXNET3_LINK_SPEED (1000 << 16) >> + >> +/* Link status: 1 - up, 0 - down. */ >> +#define VMXNET3_LINK_STATUS_UP 0x1 >> + >> +/* Least significant bit should be set for revision and version */ >> +#define VMXNET3_DEVICE_VERSION 0x1 >> +#define VMXNET3_DEVICE_REVISION 0x1 >> + >> +/* Macros for rings descriptors access */ >> +#define VMXNET3_READ_TX_QUEUE_DESCR8(dpa, field) \ >> + (vmw_shmem_ld8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field))) >> + >> +#define VMXNET3_WRITE_TX_QUEUE_DESCR8(dpa, field, value) \ >> + (vmw_shmem_st8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field, value))) >> + >> +#define VMXNET3_READ_TX_QUEUE_DESCR32(dpa, field) \ >> + (vmw_shmem_ld32(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field))) >> + >> +#define VMXNET3_WRITE_TX_QUEUE_DESCR32(dpa, field, value) \ >> + (vmw_shmem_st32(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value)) >> + >> +#define VMXNET3_READ_TX_QUEUE_DESCR64(dpa, field) \ >> + (vmw_shmem_ld64(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field))) >> + >> +#define VMXNET3_WRITE_TX_QUEUE_DESCR64(dpa, field, value) \ >> + (vmw_shmem_st64(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value)) >> + >> +#define VMXNET3_READ_RX_QUEUE_DESCR64(dpa, field) \ >> + (vmw_shmem_ld64(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field))) >> + >> +#define VMXNET3_READ_RX_QUEUE_DESCR32(dpa, field) \ >> + (vmw_shmem_ld32(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field))) >> + >> +#define VMXNET3_WRITE_RX_QUEUE_DESCR64(dpa, field, value) \ >> + (vmw_shmem_st64(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value)) >> + >> +#define VMXNET3_WRITE_RX_QUEUE_DESCR8(dpa, field, value) \ >> + (vmw_shmem_st8(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value)) >> + >> +/* Macros for guest driver shared area access */ >> +#define VMXNET3_READ_DRV_SHARED64(shpa, field) \ >> + (vmw_shmem_ld64(shpa + offsetof(struct Vmxnet3_DriverShared, field))) >> + >> +#define VMXNET3_READ_DRV_SHARED32(shpa, field) \ >> + (vmw_shmem_ld32(shpa + offsetof(struct Vmxnet3_DriverShared, field))) >> + >> +#define VMXNET3_WRITE_DRV_SHARED32(shpa, field, val) \ >> + (vmw_shmem_st32(shpa + offsetof(struct Vmxnet3_DriverShared, field), val)) >> + >> +#define VMXNET3_READ_DRV_SHARED16(shpa, field) \ >> + (vmw_shmem_ld16(shpa + offsetof(struct Vmxnet3_DriverShared, field))) >> + >> +#define VMXNET3_READ_DRV_SHARED8(shpa, field) \ >> + (vmw_shmem_ld8(shpa + offsetof(struct Vmxnet3_DriverShared, field))) >> + >> +#define VMXNET3_READ_DRV_SHARED(shpa, field, b, l) \ >> + (vmw_shmem_read(shpa + offsetof(struct Vmxnet3_DriverShared, field), b, l)) >> + >> +/* Cyclic ring abstraction */ >> +typedef struct { >> + target_phys_addr_t pa; >> + size_t size; >> + size_t cell_size; >> + size_t next; >> + uint8_t gen; >> +} Vmxnet3Ring; >> + >> +static inline void vmxnet3_ring_init(Vmxnet3Ring *ring, >> + target_phys_addr_t pa, >> + size_t size, >> + size_t cell_size, >> + bool zero_region) >> +{ >> + ring->pa = pa; >> + ring->size = size; >> + ring->cell_size = cell_size; >> + ring->gen = VMXNET3_INIT_GEN; >> + ring->next = 0; >> + >> + if (zero_region) { >> + vmw_shmem_set(pa, 0, size*cell_size); > spaces around * > >> + } >> +} >> + >> +#define vmxnet3_ring_dump(macro, ring_name, ridx, r) \ >> + macro("%s#%d: base %" PRIx64 " size %lu cell_size %lu gen %d next %lu", \ >> + (ring_name), (ridx), \ >> + (r)->pa, (r)->size, (r)->cell_size, (r)->gen, (r)->next) > make macros upper case > >> + >> +static inline void vmxnet3_ring_inc(Vmxnet3Ring *ring) >> +{ >> + if (++ring->next >= ring->size) { >> + ring->next = 0; >> + ring->gen ^= 1; >> + } >> +} >> + >> +static inline void vmxnet3_ring_dec(Vmxnet3Ring *ring) >> +{ >> + if (0 == ring->next--) { >> + ring->next = ring->size - 1; >> + ring->gen ^= 1; >> + } >> +} >> + >> +static inline target_phys_addr_t vmxnet3_ring_curr_cell_pa(Vmxnet3Ring *ring) >> +{ >> + return ring->pa + ring->next * ring->cell_size; >> +} >> + >> +static inline void vmxnet3_ring_read_curr_cell(Vmxnet3Ring *ring, void *buff) >> +{ >> + vmw_shmem_read(vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size); >> +} >> + >> +static inline void vmxnet3_ring_write_curr_cell(Vmxnet3Ring *ring, void *buff) >> +{ >> + vmw_shmem_write(vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size); >> +} >> + >> +static inline size_t vmxnet3_ring_curr_cell_idx(Vmxnet3Ring *ring) >> +{ >> + return ring->next; >> +} >> + >> +static inline uint8_t vmxnet3_ring_curr_gen(Vmxnet3Ring *ring) >> +{ >> + return ring->gen; >> +} >> + >> +/* Debug trace-related functions */ >> +static inline void >> +vmxnet3_dump_tx_descr(struct Vmxnet3_TxDesc *descr) >> +{ >> + VMW_PKPRN("TX DESCR: " >> + "addr %" PRIx64 ", len: %d, gen: %d, rsvd: %d, " >> + "dtype: %d, ext1: %d, msscof: %d, hlen: %d, om: %d, " >> + "eop: %d, cq: %d, ext2: %d, ti: %d, tci: %d", >> + le64_to_cpu(descr->addr), descr->len, descr->gen, descr->rsvd, >> + descr->dtype, descr->ext1, descr->msscof, descr->hlen, descr->om, >> + descr->eop, descr->cq, descr->ext2, descr->ti, descr->tci); >> +} >> + >> +static inline void >> +vmxnet3_dump_virt_hdr(struct virtio_net_hdr *vhdr) >> +{ >> + VMW_PKPRN("VHDR: flags 0x%x, gso_type: 0x%x, hdr_len: %d, gso_size: %d, " >> + "csum_start: %d, csum_offset: %d", >> + vhdr->flags, vhdr->gso_type, vhdr->hdr_len, vhdr->gso_size, >> + vhdr->csum_start, vhdr->csum_offset); >> +} >> + >> +static inline void >> +vmxnet3_dump_rx_descr(struct Vmxnet3_RxDesc *descr) >> +{ >> + VMW_PKPRN("RX DESCR: addr %" PRIx64 ", len: %d, gen: %d, rsvd: %d, " >> + "dtype: %d, ext1: %d, btype: %d", >> + le64_to_cpu(descr->addr), descr->len, descr->gen, >> + descr->rsvd, descr->dtype, descr->ext1, descr->btype); >> +} >> + >> +/* Device state and helper functions */ >> +#define VMXNET3_RX_RINGS_PER_QUEUE (2) >> + >> +typedef struct { >> + Vmxnet3Ring tx_ring; >> + Vmxnet3Ring comp_ring; >> + >> + uint8_t intr_idx; >> + target_phys_addr_t tx_stats_pa; >> + struct UPT1_TxStats txq_stats; >> +} Vmxnet3TxqDescr; >> + >> +typedef struct { >> + Vmxnet3Ring rx_ring[VMXNET3_RX_RINGS_PER_QUEUE]; >> + Vmxnet3Ring comp_ring; >> + uint8_t intr_idx; >> + target_phys_addr_t rx_stats_pa; >> + struct UPT1_RxStats rxq_stats; >> +} Vmxnet3RxqDescr; >> + >> +typedef struct { >> + bool is_masked; >> + bool is_pending; >> + bool is_asserted; >> +} Vmxnet3IntState; >> + >> +typedef struct { >> + PCIDevice dev; >> + NICState *nic; >> + NICConf conf; >> + MemoryRegion bar0; >> + MemoryRegion bar1; >> + MemoryRegion msix_bar; >> + >> + Vmxnet3RxqDescr rxq_descr[VMXNET3_DEVICE_MAX_RX_QUEUES]; >> + Vmxnet3TxqDescr txq_descr[VMXNET3_DEVICE_MAX_TX_QUEUES]; >> + >> + /* Whether MSI-X support was installed successfully */ >> + uint8_t msix_used; >> + /* Whether MSI support was installed successfully */ >> + uint8_t msi_used; >> + target_phys_addr_t drv_shmem; >> + target_phys_addr_t temp_shared_guest_driver_memory; >> + >> + uint8_t txq_num; >> + >> + /* This boolean tells whether RX packet being indicated has to */ >> + /* be split into head and body chunks from different RX rings */ >> + bool rx_packets_compound; >> + >> + bool rx_vlan_stripping; >> + bool lro_supported; >> + >> + uint8_t rxq_num; >> + >> + /* Network MTU */ >> + uint32_t mtu; >> + >> + /* Maximum number of fragments for indicated TX packets */ >> + uint32_t max_tx_frags; >> + >> + /* Maximum number of fragments for indicated RX packets */ >> + uint16_t max_rx_frags; >> + >> + /* Index for events interrupt */ >> + uint8_t event_int_idx; >> + >> + /* Whether automatic interrupts masking enabled */ >> + uint8_t auto_int_masking; >> + >> + bool peer_has_vhdr; >> + >> + /* TX packets to QEMU interface */ >> + VmxnetTxPktH tx_pkt; >> + uint32_t offload_mode; >> + uint32_t cso_or_gso_size; >> + uint16 tci; >> + bool needs_vlan; >> + >> + VmxnetRxPktH rx_pkt; >> + >> + bool tx_sop; >> + bool skip_current_tx_pkt; >> + >> + uint32_t device_active; >> + uint32_t last_command; >> + >> + uint32_t link_status_and_speed; >> + >> + Vmxnet3IntState interrupt_states[VMXNET3_MAX_INTRS]; >> + >> + uint32_t temp_mac; /* To store the low part first */ >> + >> + MACAddr perm_mac; >> + uint32_t vlan_table[VMXNET3_VFT_SIZE]; >> + uint32_t rx_mode; >> + MACAddr *mcast_list; >> + uint32_t mcast_list_len; >> + uint32_t mcast_list_buff_size; /* needed for live migration. */ >> +} VMXNET3State; >> + >> +/* Interrupt management */ >> + >> +/* >> + *This function returns sign whether interrupt line is in asserted state >> + * This depends on the type of interrupt used. For INTX interrupt line will >> + * be asserted until explicit deassertion, for MSI(X) interrupt line will >> + * be deasserted automatically due to notification semantics of the MSI(X) >> + * interrupts >> + */ >> +static bool _vmxnet3_assert_interrupt_line(VMXNET3State *s, uint32_t int_idx) >> +{ >> + if (s->msix_used && msix_enabled(&s->dev)) { >> + VMW_IRPRN("Sending MSI-X notification for vector %u", int_idx); >> + msix_notify(&s->dev, int_idx); >> + return false; >> + } >> + if (s->msi_used && msi_enabled(&s->dev)) { >> + VMW_IRPRN("Sending MSI notification for vector %u", int_idx); >> + msi_notify(&s->dev, int_idx); >> + return false; >> + } >> + >> + VMW_IRPRN("Asserting line for interrupt %u", int_idx); >> + qemu_set_irq(s->dev.irq[int_idx], 1); >> + return true; >> +} >> + >> +static void _vmxnet3_deassert_interrupt_line(VMXNET3State *s, int lidx) >> +{ >> + /* >> + * This function should never be called for MSI(X) interrupts >> + * because deassertion never required for message interrupts >> + */ >> + assert(!s->msix_used || !msix_enabled(&s->dev)); >> + /* >> + * This function should never be called for MSI(X) interrupts >> + * because deassertion never required for message interrupts >> + */ >> + assert(!s->msi_used || !msi_enabled(&s->dev)); >> + >> + VMW_IRPRN("Deasserting line for interrupt %u", lidx); >> + qemu_set_irq(s->dev.irq[lidx], 0); >> +} >> + >> +static void vmxnet3_update_interrupt_line_state(VMXNET3State *s, int lidx) >> +{ >> + if (!s->interrupt_states[lidx].is_pending && >> + s->interrupt_states[lidx].is_asserted) { >> + VMW_IRPRN("New interrupt line state for index %d is DOWN", lidx); >> + _vmxnet3_deassert_interrupt_line(s, lidx); >> + s->interrupt_states[lidx].is_asserted = false; >> + return; >> + } >> + >> + if (s->interrupt_states[lidx].is_pending && >> + !s->interrupt_states[lidx].is_masked && >> + !s->interrupt_states[lidx].is_asserted) { >> + VMW_IRPRN("New interrupt line state for index %d is UP", lidx); >> + s->interrupt_states[lidx].is_asserted = >> + _vmxnet3_assert_interrupt_line(s, lidx); >> + s->interrupt_states[lidx].is_pending = false; >> + return; >> + } >> +} >> + >> +static void vmxnet3_trigger_interrupt(VMXNET3State *s, int lidx) >> +{ >> + s->interrupt_states[lidx].is_pending = true; >> + vmxnet3_update_interrupt_line_state(s, lidx); >> + >> + if (s->msix_used && msix_enabled(&s->dev) && s->auto_int_masking) { >> + goto do_automask; >> + } >> + >> + if (s->msi_used && msi_enabled(&s->dev) && s->auto_int_masking) { >> + goto do_automask; >> + } >> + >> + return; >> + >> +do_automask: >> + s->interrupt_states[lidx].is_masked = true; >> + vmxnet3_update_interrupt_line_state(s, lidx); >> +} >> + >> +static bool vmxnet3_interrupt_asserted(VMXNET3State *s, int lidx) >> +{ >> + return s->interrupt_states[lidx].is_asserted; >> +} >> + >> +static void vmxnet3_clear_interrupt(VMXNET3State *s, int int_idx) >> +{ >> + s->interrupt_states[int_idx].is_pending = false; >> + if (s->auto_int_masking) { >> + s->interrupt_states[int_idx].is_masked = true; >> + } >> + vmxnet3_update_interrupt_line_state(s, int_idx); >> +} >> + >> +static void >> +vmxnet3_on_interrupt_mask_changed(VMXNET3State *s, int lidx, bool is_masked) >> +{ >> + s->interrupt_states[lidx].is_masked = is_masked; >> + vmxnet3_update_interrupt_line_state(s, lidx); >> +} >> + >> +static bool vmxnet3_verify_driver_magic(target_phys_addr_t dshmem) >> +{ >> + return (VMXNET3_REV1_MAGIC == VMXNET3_READ_DRV_SHARED32(dshmem, magic)); >> +} >> + >> +#define _GET_BYTE(x, byte_num) (((x) >> (byte_num)*8) & 0xFF) >> +#define _MAKE_BYTE(byte_num, val) (((uint32_t)((val) & 0xFF)) << (byte_num)*8) >> + >> +static void vmxnet3_set_variable_mac(VMXNET3State *s, uint32_t h, uint32_t l) >> +{ >> + s->conf.macaddr.a[0] = _GET_BYTE(l, 0); >> + s->conf.macaddr.a[1] = _GET_BYTE(l, 1); >> + s->conf.macaddr.a[2] = _GET_BYTE(l, 2); >> + s->conf.macaddr.a[3] = _GET_BYTE(l, 3); >> + s->conf.macaddr.a[4] = _GET_BYTE(h, 0); >> + s->conf.macaddr.a[5] = _GET_BYTE(h, 1); >> + >> + VMW_CFPRN("Variable MAC: " VMXNET_MF, VMXNET_MA(s->conf.macaddr.a)); >> + >> + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); >> +} >> + >> +static uint64_t vmxnet3_get_mac_low(MACAddr *addr) >> +{ >> + return _MAKE_BYTE(0, addr->a[0]) | >> + _MAKE_BYTE(1, addr->a[1]) | >> + _MAKE_BYTE(2, addr->a[2]) | >> + _MAKE_BYTE(3, addr->a[3]); >> +} >> + >> +static uint64_t vmxnet3_get_mac_high(MACAddr *addr) >> +{ >> + return _MAKE_BYTE(0, addr->a[4]) | >> + _MAKE_BYTE(1, addr->a[5]); >> +} >> + >> +static void >> +vmxnet3_inc_tx_consumption_counter(VMXNET3State *s, int qidx) >> +{ >> + vmxnet3_ring_inc(&s->txq_descr[qidx].tx_ring); >> +} >> + >> +static inline void >> +vmxnet3_inc_rx_consumption_counter(VMXNET3State *s, int qidx, int ridx) >> +{ >> + vmxnet3_ring_inc(&s->rxq_descr[qidx].rx_ring[ridx]); >> +} >> + >> +static inline void >> +vmxnet3_inc_tx_completion_counter(VMXNET3State *s, int qidx) >> +{ >> + vmxnet3_ring_inc(&s->txq_descr[qidx].comp_ring); >> +} >> + >> +static void >> +vmxnet3_inc_rx_completion_counter(VMXNET3State *s, int qidx) >> +{ >> + vmxnet3_ring_inc(&s->rxq_descr[qidx].comp_ring); >> +} >> + >> +static void >> +vmxnet3_dec_rx_completion_counter(VMXNET3State *s, int qidx) >> +{ >> + vmxnet3_ring_dec(&s->rxq_descr[qidx].comp_ring); >> +} >> + >> +static inline void vmxnet3_flush_shmem_changes(void) >> +{ >> + /* >> + * Flush shared memory changes >> + * Needed before transferring control to guest > what does 'transferring control to guest' mean? > >> + */ >> + smp_wmb(); >> +} > Don't use wrappers like this. They just hide bugs. For example > it's not helpful before an interrupt in the function below. > >> + >> +static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32 tx_ridx) >> +{ >> + struct Vmxnet3_TxCompDesc txcq_descr; >> + >> + vmxnet3_ring_dump(VMW_RIPRN, "TXC", qidx, &s->txq_descr[qidx].comp_ring); >> + >> + txcq_descr.txdIdx = tx_ridx; >> + txcq_descr.gen = vmxnet3_ring_curr_gen(&s->txq_descr[qidx].comp_ring); >> + >> + vmxnet3_ring_write_curr_cell(&s->txq_descr[qidx].comp_ring, &txcq_descr); >> + vmxnet3_inc_tx_completion_counter(s, qidx); >> + >> + vmxnet3_flush_shmem_changes(); >> + vmxnet3_trigger_interrupt(s, s->txq_descr[qidx].intr_idx); >> +} >> + >> +static bool >> +vmxnet3_setup_tx_offloads(VMXNET3State *s) >> +{ >> + switch (s->offload_mode) { >> + case VMXNET3_OM_NONE: >> + vmxnet_tx_pkt_build_vheader(s->tx_pkt, false, false, 0); >> + break; >> + >> + case VMXNET3_OM_CSUM: >> + vmxnet_tx_pkt_build_vheader(s->tx_pkt, false, true, 0); >> + VMW_PKPRN("L4 CSO requested\n"); >> + break; >> + >> + case VMXNET3_OM_TSO: >> + vmxnet_tx_pkt_build_vheader(s->tx_pkt, true, true, >> + s->cso_or_gso_size); >> + vmxnet_tx_pkt_update_ip_checksums(s->tx_pkt); >> + VMW_PKPRN("GSO offload requested."); >> + break; >> + >> + default: >> + assert(false); >> + return false; >> + } >> + >> + return true; >> +} >> + >> +static void >> +vmxnet3_tx_retrieve_metadata(VMXNET3State *s, >> + const struct Vmxnet3_TxDesc *txd) >> +{ >> + s->offload_mode = txd->om; >> + s->cso_or_gso_size = txd->msscof; >> + s->tci = txd->tci; >> + s->needs_vlan = txd->ti; >> +} >> + >> +typedef enum { >> + VMXNET3_PKT_STATUS_OK, >> + VMXNET3_PKT_STATUS_ERROR, >> + VMXNET3_PKT_STATUS_DISCARD,/* only for tx */ >> + VMXNET3_PKT_STATUS_OUT_OF_BUF /* only for rx */ >> +} Vmxnet3PktStatus; >> + >> +static void >> +vmxnet3_on_tx_done_update_stats(VMXNET3State *s, int qidx, >> + Vmxnet3PktStatus status) >> +{ >> + size_t tot_len = vmxnet_tx_pkt_get_total_len(s->tx_pkt); >> + struct UPT1_TxStats *stats = &s->txq_descr[qidx].txq_stats; >> + >> + switch (status) { >> + case VMXNET3_PKT_STATUS_OK: { > don't put {} around cases: they align incorrectly > if it's too big move to a function. > >> + switch (vmxnet_tx_pkt_get_packet_type(s->tx_pkt)) { >> + case ETH_PKT_BCAST: >> + stats->bcastPktsTxOK++; >> + stats->bcastBytesTxOK += tot_len; >> + break; >> + case ETH_PKT_MCAST: >> + stats->mcastPktsTxOK++; >> + stats->mcastBytesTxOK += tot_len; >> + break; >> + case ETH_PKT_UCAST: >> + stats->ucastPktsTxOK++; >> + stats->ucastBytesTxOK += tot_len; >> + break; >> + default: >> + assert(false); >> + } >> + >> + if (VMXNET3_OM_TSO == s->offload_mode) { > Don't do Yoda style like this > >> + /* >> + * According to VMWARE headers this statistic is a number >> + * of packets after segmentation but since we don't have >> + * this information in QEMU model, the best we can do is to >> + * provide number of non-segmented packets >> + */ >> + stats->TSOPktsTxOK++; >> + stats->TSOBytesTxOK += tot_len; >> + } >> + } >> + break; >> + >> + case VMXNET3_PKT_STATUS_DISCARD: { >> + stats->pktsTxDiscard++; >> + } >> + break; >> + >> + case VMXNET3_PKT_STATUS_ERROR: { >> + stats->pktsTxError++; >> + } >> + break; >> + >> + default: >> + assert(false); >> + } >> +} >> + >> +static void >> +vmxnet3_on_rx_done_update_stats(VMXNET3State *s, >> + int qidx, >> + Vmxnet3PktStatus status) >> +{ >> + struct UPT1_RxStats *stats = &s->rxq_descr[qidx].rxq_stats; >> + size_t tot_len = vmxnet_rx_pkt_get_total_len(s->rx_pkt); >> + >> + switch (status) { >> + case VMXNET3_PKT_STATUS_OUT_OF_BUF: >> + stats->pktsRxOutOfBuf++; >> + break; >> + >> + case VMXNET3_PKT_STATUS_ERROR: >> + stats->pktsRxError++; >> + break; >> + case VMXNET3_PKT_STATUS_OK: >> + switch (vmxnet_rx_pkt_get_packet_type(s->rx_pkt)) { >> + case ETH_PKT_BCAST: >> + stats->bcastPktsRxOK++; >> + stats->bcastBytesRxOK += tot_len; >> + break; >> + case ETH_PKT_MCAST: >> + stats->mcastPktsRxOK++; >> + stats->mcastBytesRxOK += tot_len; >> + break; >> + case ETH_PKT_UCAST: >> + stats->ucastPktsRxOK++; >> + stats->ucastBytesRxOK += tot_len; >> + break; >> + default: >> + assert(false); >> + } >> + >> + if (tot_len > s->mtu) { >> + stats->LROPktsRxOK++; >> + stats->LROBytesRxOK += tot_len; >> + } >> + break; >> + default: >> + assert(false); >> + } >> +} >> + >> +static inline bool >> +vmxnet3_pop_next_tx_descr(VMXNET3State *s, >> + int qidx, >> + struct Vmxnet3_TxDesc *txd, >> + uint32_t *descr_idx) >> +{ >> + Vmxnet3Ring *ring = &s->txq_descr[qidx].tx_ring; >> + >> + vmxnet3_ring_read_curr_cell(ring, txd); >> + if (txd->gen == vmxnet3_ring_curr_gen(ring)) { >> + vmxnet3_ring_dump(VMW_RIPRN, "TX", qidx, ring); >> + *descr_idx = vmxnet3_ring_curr_cell_idx(ring); >> + vmxnet3_inc_tx_consumption_counter(s, qidx); >> + return true; >> + } >> + >> + return false; >> +} >> + >> +static bool >> +vmxnet3_send_packet(VMXNET3State *s, uint32_t qidx) >> +{ >> + size_t bytes_sent = 0; >> + bool res = true; > why = true? don't initialize just because. > >> + Vmxnet3PktStatus status = VMXNET3_PKT_STATUS_OK; >> + >> + res = vmxnet3_setup_tx_offloads(s); >> + if (!res) { >> + status = VMXNET3_PKT_STATUS_ERROR; >> + goto func_exit; >> + } >> + >> + /* debug prints */ >> + vmxnet3_dump_virt_hdr(vmxnet_tx_pkt_get_vhdr(s->tx_pkt)); >> + vmxnet_tx_pkt_dump(s->tx_pkt); >> + >> + bytes_sent = vmxnet_tx_pkt_send(s->tx_pkt, &s->nic->nc); >> + if (!bytes_sent) { >> + res = false; >> + status = VMXNET3_PKT_STATUS_DISCARD; >> + goto func_exit; >> + } >> + >> +func_exit: >> + vmxnet3_on_tx_done_update_stats(s, qidx, status); >> + return res; >> +} >> + >> +static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx) >> +{ >> + struct Vmxnet3_TxDesc txd; >> + uint32_t txd_idx; >> + uint32_t data_len; >> + target_phys_addr_t data_pa; >> + >> + for (;;) { >> + if (!vmxnet3_pop_next_tx_descr(s, qidx, &txd, &txd_idx)) { >> + break; >> + } >> + >> + vmxnet3_dump_tx_descr(&txd); >> + >> + if (!s->skip_current_tx_pkt) { >> + data_len = (txd.len > 0) ? txd.len : VMXNET3_MAX_TX_BUF_SIZE; >> + data_pa = le64_to_cpu(txd.addr); >> + >> + if (!vmxnet_tx_pkt_add_raw_fragment(s->tx_pkt, >> + data_pa, >> + data_len)) { >> + s->skip_current_tx_pkt = true; >> + } >> + } >> + >> + if (s->tx_sop) { >> + vmxnet3_tx_retrieve_metadata(s, &txd); >> + s->tx_sop = false; >> + } >> + >> + if (txd.eop) { >> + if (!s->skip_current_tx_pkt) { >> + vmxnet_tx_pkt_parse(s->tx_pkt); >> + >> + if (s->needs_vlan) { >> + vmxnet_tx_pkt_setup_vlan_header(s->tx_pkt, s->tci); >> + } >> + >> + vmxnet3_send_packet(s, qidx); >> + } else { >> + vmxnet3_on_tx_done_update_stats(s, qidx, >> + VMXNET3_PKT_STATUS_ERROR); >> + } >> + >> + vmxnet3_complete_packet(s, qidx, txd_idx); >> + s->tx_sop = true; >> + s->skip_current_tx_pkt = false; >> + vmxnet_tx_pkt_reset(s->tx_pkt); >> + } >> + } >> +} >> + >> +static inline void >> +vmxnet3_read_next_rx_descr(VMXNET3State *s, int qidx, int ridx, >> + struct Vmxnet3_RxDesc *dbuf, uint32_t *didx) >> +{ >> + Vmxnet3Ring *ring = &s->rxq_descr[qidx].rx_ring[ridx]; >> + *didx = vmxnet3_ring_curr_cell_idx(ring); >> + vmxnet3_ring_read_curr_cell(ring, dbuf); >> +} >> + >> +static inline uint8_t >> +vmxnet3_get_rx_ring_gen(VMXNET3State *s, int qidx, int ridx) >> +{ >> + return s->rxq_descr[qidx].rx_ring[ridx].gen; >> +} >> + >> +static inline target_phys_addr_t >> +vmxnet3_pop_rxc_descr(VMXNET3State *s, int qidx, uint32_t *descr_gen) >> +{ >> + uint8_t ring_gen; >> + struct Vmxnet3_RxCompDesc rxcd; >> + >> + target_phys_addr_t daddr = >> + vmxnet3_ring_curr_cell_pa(&s->rxq_descr[qidx].comp_ring); >> + >> + cpu_physical_memory_read(daddr, &rxcd, sizeof(struct Vmxnet3_RxCompDesc)); >> + ring_gen = vmxnet3_ring_curr_gen(&s->rxq_descr[qidx].comp_ring); >> + >> + if (rxcd.gen != ring_gen) { >> + *descr_gen = ring_gen; >> + vmxnet3_inc_rx_completion_counter(s, qidx); >> + return daddr; >> + } >> + >> + return 0; >> +} >> + >> +static inline void >> +vmxnet3_revert_rxc_descr(VMXNET3State *s, int qidx) >> +{ >> + vmxnet3_dec_rx_completion_counter(s, qidx); >> +} >> + >> +#define RXQ_IDX (0) >> +#define RX_HEAD_BODY_RING (0) >> +#define RX_BODY_ONLY_RING (1) >> + >> +static bool >> +vmxnet3_get_next_head_rx_descr(VMXNET3State *s, >> + struct Vmxnet3_RxDesc *descr_buf, >> + uint32_t *descr_idx, >> + uint32_t *ridx) >> +{ >> + for (;;) { >> + uint32_t ring_gen; >> + vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, >> + descr_buf, descr_idx); >> + >> + /* If no more free descriptors - return */ >> + ring_gen = vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_HEAD_BODY_RING); >> + if (descr_buf->gen != ring_gen) { >> + return false; >> + } >> + >> + /* Mark current descriptor as used/skipped */ >> + vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_HEAD_BODY_RING); >> + >> + /* If this is what we are looking for - return */ >> + if (VMXNET3_RXD_BTYPE_HEAD == descr_buf->btype) { >> + *ridx = RX_HEAD_BODY_RING; >> + return true; >> + } >> + } >> +} >> + >> +static bool >> +vmxnet3_get_next_body_rx_descr(VMXNET3State *s, >> + struct Vmxnet3_RxDesc *d, >> + uint32_t *didx, >> + uint32_t *ridx) >> +{ >> + vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, d, didx); >> + >> + /* Try to find corresponding descriptor in head/body ring */ >> + if ((d->gen == vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_HEAD_BODY_RING)) && >> + (VMXNET3_RXD_BTYPE_BODY == d->btype)) { >> + vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_HEAD_BODY_RING); >> + *ridx = RX_HEAD_BODY_RING; >> + return true; >> + } >> + >> + /* >> + * If there is no free descriptors on head/body ring or next free >> + * descriptor is a head descriptor switch to body only ring >> + */ >> + vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_BODY_ONLY_RING, d, didx); >> + >> + /* If no more free descriptors - return */ >> + if (d->gen == vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_BODY_ONLY_RING)) { >> + assert(VMXNET3_RXD_BTYPE_BODY == d->btype); >> + *ridx = RX_BODY_ONLY_RING; >> + vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_BODY_ONLY_RING); >> + return true; >> + } >> + >> + return false; >> +} >> + >> +static inline bool >> +vmxnet3_get_next_rx_descr(VMXNET3State *s, bool is_head, >> + struct Vmxnet3_RxDesc *descr_buf, >> + uint32_t *descr_idx, >> + uint32_t *ridx) >> +{ >> + if (is_head || !s->rx_packets_compound) { >> + return vmxnet3_get_next_head_rx_descr(s, descr_buf, descr_idx, ridx); >> + } else { >> + return vmxnet3_get_next_body_rx_descr(s, descr_buf, descr_idx, ridx); >> + } >> +} >> + >> +static void vmxnet3_rx_update_descr(VmxnetRxPktH pkt, >> + struct Vmxnet3_RxCompDesc *rxcd) >> +{ >> + int csum_ok, is_gso; >> + bool isip4, isip6, istcp, isudp; >> + struct virtio_net_hdr *vhdr; >> + uint8_t offload_type; >> + >> + if (vmxnet_rx_pkt_is_vlan_stripped(pkt)) { >> + rxcd->ts = 1; >> + rxcd->tci = vmxnet_rx_pkt_get_vlan_tag(pkt); >> + } >> + >> + if (!vmxnet_rx_pkt_has_virt_hdr(pkt)) { >> + goto nocsum; >> + } >> + >> + vhdr = vmxnet_rx_pkt_get_vhdr(pkt); >> + /* >> + * Checksum is valid when lower level tell so or when lower level >> + * requires checksum offload telling that packet produced/bridged >> + * locally and did travel over network after last checksum calculation >> + * or production >> + */ >> + csum_ok = VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_DATA_VALID) || >> + VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_NEEDS_CSUM); >> + >> + offload_type = vhdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN; >> + is_gso = (offload_type != VIRTIO_NET_HDR_GSO_NONE) ? 1 : 0; >> + >> + if (!csum_ok && !is_gso) { >> + goto nocsum; >> + } >> + >> + vmxnet_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); >> + if ((!istcp && !isudp) || (!isip4 && !isip6)) { >> + goto nocsum; >> + } >> + >> + rxcd->cnc = 0; >> + rxcd->v4 = isip4 ? 1 : 0; >> + rxcd->v6 = isip6 ? 1 : 0; >> + rxcd->tcp = istcp ? 1 : 0; >> + rxcd->udp = isudp ? 1 : 0; >> + rxcd->fcs = rxcd->tuc = rxcd->ipc = 1; >> + return; >> + >> +nocsum: >> + rxcd->cnc = 1; >> + return; >> +} >> + >> +static void >> +vmxnet3_physical_memory_writev(const struct iovec *iov, >> + size_t start_iov_off, >> + target_phys_addr_t target_addr, >> + size_t bytes_to_copy) >> +{ >> + size_t curr_off = 0; >> + size_t copied = 0; >> + >> + while (bytes_to_copy) { >> + if (start_iov_off < (curr_off + iov->iov_len)) { >> + size_t chunk_len = >> + MIN((curr_off + iov->iov_len) - start_iov_off, bytes_to_copy); >> + >> + cpu_physical_memory_write(target_addr + copied, >> + iov->iov_base + start_iov_off - curr_off, >> + chunk_len); >> + >> + copied += chunk_len; >> + start_iov_off += chunk_len; >> + curr_off = start_iov_off; >> + bytes_to_copy -= chunk_len; >> + } else { >> + curr_off += iov->iov_len; >> + } >> + iov++; >> + } >> +} >> + >> +static bool >> +vmxnet3_indicate_packet(VMXNET3State *s) >> +{ >> + struct Vmxnet3_RxDesc rxd; >> + bool is_head = true; >> + uint32_t rxd_idx; >> + uint32_t rx_ridx; >> + >> + struct Vmxnet3_RxCompDesc rxcd; >> + uint32_t new_rxcd_gen = VMXNET3_INIT_GEN; >> + target_phys_addr_t new_rxcd_pa = 0; >> + target_phys_addr_t ready_rxcd_pa = 0; >> + struct iovec *data = vmxnet_rx_pkt_get_iovec(s->rx_pkt); >> + size_t bytes_copied = 0; >> + size_t bytes_left = vmxnet_rx_pkt_get_total_len(s->rx_pkt); >> + uint16_t num_frags = 0; >> + size_t chunk_size; >> + >> + vmxnet_rx_pkt_dump(s->rx_pkt); >> + >> + while (bytes_left > 0) { >> + >> + /* cannot add more frags to packet */ >> + if (num_frags == s->max_rx_frags) { >> + break; >> + } >> + >> + new_rxcd_pa = vmxnet3_pop_rxc_descr(s, RXQ_IDX, &new_rxcd_gen); >> + if (!new_rxcd_pa) { >> + break; >> + } >> + >> + if (!vmxnet3_get_next_rx_descr(s, is_head, &rxd, &rxd_idx, &rx_ridx)) { >> + break; >> + } >> + >> + chunk_size = MIN(bytes_left, rxd.len); >> + vmxnet3_physical_memory_writev(data, bytes_copied, >> + le64_to_cpu(rxd.addr), chunk_size); >> + bytes_copied += chunk_size; >> + bytes_left -= chunk_size; >> + >> + vmxnet3_dump_rx_descr(&rxd); >> + >> + if (0 != ready_rxcd_pa) { >> + cpu_physical_memory_write(ready_rxcd_pa, &rxcd, sizeof(rxcd)); >> + } >> + >> + memset(&rxcd, 0, sizeof(struct Vmxnet3_RxCompDesc)); >> + rxcd.rxdIdx = rxd_idx; >> + rxcd.len = chunk_size; >> + rxcd.sop = is_head; >> + rxcd.gen = new_rxcd_gen; >> + rxcd.rqID = RXQ_IDX + rx_ridx*s->rxq_num; >> + >> + if (0 == bytes_left) { >> + vmxnet3_rx_update_descr(s->rx_pkt, &rxcd); >> + } >> + >> + VMW_RIPRN("RX Completion descriptor: rxRing: %lu rxIdx %lu len %lu " >> + "sop %d csum_correct %lu", >> + (unsigned long) rx_ridx, >> + (unsigned long) rxcd.rxdIdx, >> + (unsigned long) rxcd.len, >> + (int) rxcd.sop, >> + (unsigned long) rxcd.tuc); >> + >> + is_head = false; >> + ready_rxcd_pa = new_rxcd_pa; >> + new_rxcd_pa = 0; >> + } >> + >> + if (0 != ready_rxcd_pa) { >> + rxcd.eop = 1; >> + rxcd.err = (0 != bytes_left); >> + cpu_physical_memory_write(ready_rxcd_pa, &rxcd, sizeof(rxcd)); >> + vmxnet3_flush_shmem_changes(); >> + } >> + >> + if (0 != new_rxcd_pa) { >> + vmxnet3_revert_rxc_descr(s, RXQ_IDX); >> + } >> + >> + vmxnet3_trigger_interrupt(s, s->rxq_descr[RXQ_IDX].intr_idx); >> + >> + if (bytes_left == 0) { >> + vmxnet3_on_rx_done_update_stats(s, RXQ_IDX, VMXNET3_PKT_STATUS_OK); >> + return true; >> + } else if (num_frags == s->max_rx_frags) { >> + vmxnet3_on_rx_done_update_stats(s, RXQ_IDX, VMXNET3_PKT_STATUS_ERROR); >> + return false; >> + } else { >> + vmxnet3_on_rx_done_update_stats(s, RXQ_IDX, >> + VMXNET3_PKT_STATUS_OUT_OF_BUF); >> + return false; >> + } >> +} >> + >> +static void >> +vmxnet3_io_bar0_write(void *opaque, target_phys_addr_t addr, >> + uint64_t val, unsigned size) >> +{ >> + VMXNET3State *s = opaque; >> + >> + if (IS_MULTIREG_ADDR(addr, VMXNET3_REG_TXPROD, >> + VMXNET3_DEVICE_MAX_TX_QUEUES, VMXNET3_REG_ALIGN)) { >> + int tx_queue_idx = >> + MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_TXPROD, VMXNET3_REG_ALIGN); >> + assert(tx_queue_idx <= s->txq_num); >> + vmxnet3_process_tx_queue(s, tx_queue_idx); >> + return; >> + } >> + >> + if (IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR, >> + VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) { >> + int l = MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_IMR, VMXNET3_REG_ALIGN); >> + >> + VMW_CBPRN("Interrupt mask for line %d written: 0x%" PRIx64, l, val); >> + >> + vmxnet3_on_interrupt_mask_changed(s, l, val); >> + return; >> + } >> + >> + if (IS_MULTIREG_ADDR(addr, VMXNET3_REG_RXPROD, >> + VMXNET3_DEVICE_MAX_RX_QUEUES, VMXNET3_REG_ALIGN) || >> + IS_MULTIREG_ADDR(addr, VMXNET3_REG_RXPROD2, >> + VMXNET3_DEVICE_MAX_RX_QUEUES, VMXNET3_REG_ALIGN)) { >> + return; >> + } >> + >> + VMW_WRPRN("BAR0 unknown write [%" PRIx64 "] = %" PRIx64 ", size %d", >> + (uint64_t) addr, val, size); >> +} >> + >> +static uint64_t >> +vmxnet3_io_bar0_read(void *opaque, target_phys_addr_t addr, unsigned size) >> +{ >> + if (IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR, >> + VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) { >> + assert(false); >> + } >> + >> + VMW_CBPRN("BAR0 unknown read [%" PRIx64 "], size %d", addr, size); >> + return 0; >> +} >> + >> +static void vmxnet3_reset(VMXNET3State *s) >> +{ >> + VMW_CBPRN("Resetting vmxnet3..."); >> + >> + vmxnet_tx_pkt_reset(s->tx_pkt); >> + s->tx_sop = true; >> + s->skip_current_tx_pkt = false; >> +} >> + >> +static void vmxnet3_deactivate_device(VMXNET3State *s) >> +{ >> + VMW_CBPRN("Deactivating vmxnet3..."); >> + s->device_active = false; >> +} >> + >> +static void vmxnet3_update_rx_mode(VMXNET3State *s) >> +{ >> + s->rx_mode = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, >> + devRead.rxFilterConf.rxMode); >> + VMW_CFPRN("RX mode: 0x%08X", s->rx_mode); >> +} >> + >> +static void vmxnet3_update_vlan_filters(VMXNET3State *s) >> +{ >> + int i; >> + >> + /* Copy configuration from shared memory */ >> + VMXNET3_READ_DRV_SHARED(s->drv_shmem, >> + devRead.rxFilterConf.vfTable, >> + s->vlan_table, >> + sizeof(s->vlan_table)); >> + >> + /* Invert byte order when needed */ >> + for (i = 0; i < ARRAY_SIZE(s->vlan_table); i++) { >> + s->vlan_table[i] = le32_to_cpu(s->vlan_table[i]); >> + } >> + >> + /* Dump configuration for debugging purposes */ >> + VMW_CFPRN("Configured VLANs:"); >> + for (i = 0; i < sizeof(s->vlan_table) * 8; i++) { >> + if (VMXNET3_VFTABLE_ENTRY_IS_SET(s->vlan_table, i)) { >> + VMW_CFPRN("\tVLAN %d is present", i); >> + } >> + } >> +} >> + >> +static void vmxnet3_update_mcast_filters(VMXNET3State *s) >> +{ >> + uint16_t list_bytes = >> + VMXNET3_READ_DRV_SHARED16(s->drv_shmem, >> + devRead.rxFilterConf.mfTableLen); >> + >> + s->mcast_list_len = list_bytes / sizeof(s->mcast_list[0]); >> + >> + s->mcast_list = g_realloc(s->mcast_list, list_bytes); >> + if (NULL == s->mcast_list) { >> + if (0 == s->mcast_list_len) { >> + VMW_CFPRN("Current multicast list is empty"); >> + } else { >> + VMW_ERPRN("Failed to allocate multicast list of %d elements", >> + s->mcast_list_len); >> + } >> + s->mcast_list_len = 0; >> + } else { >> + int i; >> + target_phys_addr_t mcast_list_pa = >> + VMXNET3_READ_DRV_SHARED64(s->drv_shmem, >> + devRead.rxFilterConf.mfTablePA); >> + >> + cpu_physical_memory_read(mcast_list_pa, s->mcast_list, list_bytes); >> + VMW_CFPRN("Current multicast list len is %d:", s->mcast_list_len); >> + for (i = 0; i < s->mcast_list_len; i++) { >> + VMW_CFPRN("\t" VMXNET_MF, VMXNET_MA(s->mcast_list[i].a)); >> + } >> + } >> +} >> + >> +static void vmxnet3_setup_rx_filtering(VMXNET3State *s) >> +{ >> + vmxnet3_update_rx_mode(s); >> + vmxnet3_update_vlan_filters(s); >> + vmxnet3_update_mcast_filters(s); >> +} >> + >> +static uint32_t vmxnet3_get_interrupt_config(VMXNET3State *s) >> +{ >> + uint32_t interrupt_mode = VMXNET3_IT_AUTO | (VMXNET3_IMM_AUTO << 2); >> + VMW_CFPRN("Interrupt config is 0x%X", interrupt_mode); >> + return interrupt_mode; >> +} >> + >> +static void vmxnet3_fill_stats(VMXNET3State *s) >> +{ >> + int i; >> + for (i = 0; i < s->txq_num; i++) { >> + cpu_physical_memory_write(s->txq_descr[i].tx_stats_pa, >> + &s->txq_descr[i].txq_stats, >> + sizeof(s->txq_descr[i].txq_stats)); >> + } >> + >> + for (i = 0; i < s->rxq_num; i++) { >> + cpu_physical_memory_write(s->rxq_descr[i].rx_stats_pa, >> + &s->rxq_descr[i].rxq_stats, >> + sizeof(s->rxq_descr[i].rxq_stats)); >> + } >> +} >> + >> +static void vmxnet3_adjust_by_guest_type(VMXNET3State *s) >> +{ >> + struct Vmxnet3_GOSInfo gos; >> + >> + VMXNET3_READ_DRV_SHARED(s->drv_shmem, devRead.misc.driverInfo.gos, >> + &gos, sizeof(gos)); >> + s->rx_packets_compound = >> + (VMXNET3_GOS_TYPE_WIN == gos.gosType) ? false : true; >> + >> + VMW_CFPRN("Guest type specifics: RXCOMPOUND: %d", s->rx_packets_compound); >> +} >> + >> +static void >> +vmxnet3_dump_conf_descr(const char *name, >> + struct Vmxnet3_VariableLenConfDesc *pm_descr) >> +{ >> + VMW_CFPRN("%s descriptor dump: Version %u, Length %u", >> + name, pm_descr->confVer, pm_descr->confLen); >> + >> +}; >> + >> +static void vmxnet3_update_pm_state(VMXNET3State *s) >> +{ >> + struct Vmxnet3_VariableLenConfDesc pm_descr; >> + >> + pm_descr.confLen = >> + VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.pmConfDesc.confLen); >> + pm_descr.confVer = >> + VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.pmConfDesc.confVer); >> + pm_descr.confPA = >> + VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.pmConfDesc.confPA); >> + >> + vmxnet3_dump_conf_descr("PM State", &pm_descr); >> +} >> + >> +static void vmxnet3_update_features(VMXNET3State *s) >> +{ >> + uint32_t guest_features; >> + int rxcso_supported; >> + >> + guest_features = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, >> + devRead.misc.uptFeatures); >> + >> + rxcso_supported = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_RXCSUM); >> + s->rx_vlan_stripping = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_RXVLAN); >> + s->lro_supported = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_LRO); >> + >> + VMW_CFPRN("Features configuration: LRO: %d, RXCSUM: %d, VLANSTRIP: %d", >> + s->lro_supported, rxcso_supported, >> + s->rx_vlan_stripping); >> + if (s->peer_has_vhdr) { >> + tap_set_offload(s->nic->nc.peer, >> + rxcso_supported, >> + s->lro_supported, >> + s->lro_supported, >> + 0, >> + 0); >> + } >> +} >> + >> +static void vmxnet3_activate_device(VMXNET3State *s) >> +{ >> + int i; >> + static const uint32_t VMXNET3_DEF_TX_THRESHOLD = 1; >> + target_phys_addr_t qdescr_table_pa; >> + uint64_t pa; >> + uint32_t size; >> + >> + /* Verify configuration consistency */ >> + if (!vmxnet3_verify_driver_magic(s->drv_shmem)) { >> + VMW_ERPRN("Device configuration received from driver is invalid"); >> + return; >> + } >> + >> + vmxnet3_adjust_by_guest_type(s); >> + vmxnet3_update_features(s); >> + vmxnet3_update_pm_state(s); >> + vmxnet3_setup_rx_filtering(s); >> + /* Cache fields from shared memory */ >> + s->mtu = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.misc.mtu); >> + VMW_CFPRN("MTU is %u", s->mtu); >> + >> + s->max_rx_frags = >> + VMXNET3_READ_DRV_SHARED16(s->drv_shmem, devRead.misc.maxNumRxSG); >> + >> + VMW_CFPRN("Max RX fragments is %u", s->max_rx_frags); >> + >> + s->event_int_idx = >> + VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.intrConf.eventIntrIdx); >> + VMW_CFPRN("Events interrupt line is %u", s->event_int_idx); >> + >> + s->auto_int_masking = >> + VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.intrConf.autoMask); >> + VMW_CFPRN("Automatic interrupt masking is %d", (int)s->auto_int_masking); >> + >> + s->txq_num = >> + VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numTxQueues); >> + s->rxq_num = >> + VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numRxQueues); >> + >> + VMW_CFPRN("Number of TX/RX queues %u/%u", s->txq_num, s->rxq_num); >> + assert(s->txq_num <= VMXNET3_DEVICE_MAX_TX_QUEUES); >> + >> + qdescr_table_pa = >> + VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.misc.queueDescPA); >> + VMW_CFPRN("TX queues descriptors table is at 0x%" PRIx64, qdescr_table_pa); >> + >> + /* >> + * Worst-case scenario is a packet that holds all TX rings space so >> + * we calculate total size of all TX rings for max TX fragments number >> + */ >> + s->max_tx_frags = 0; >> + >> + /* TX queues */ >> + for (i = 0; i < s->txq_num; i++) { >> + target_phys_addr_t qdescr_pa = >> + qdescr_table_pa + i*sizeof(struct Vmxnet3_TxQueueDesc); >> + >> + /* Read interrupt number for this TX queue */ >> + s->txq_descr[i].intr_idx = >> + VMXNET3_READ_TX_QUEUE_DESCR8(qdescr_pa, conf.intrIdx); >> + >> + VMW_CFPRN("TX Queue %d interrupt: %d", i, s->txq_descr[i].intr_idx); >> + >> + /* Read rings memory locations for TX queues */ >> + pa = VMXNET3_READ_TX_QUEUE_DESCR64(qdescr_pa, conf.txRingBasePA); >> + size = VMXNET3_READ_TX_QUEUE_DESCR32(qdescr_pa, conf.txRingSize); >> + >> + vmxnet3_ring_init(&s->txq_descr[i].tx_ring, pa, size, >> + sizeof(struct Vmxnet3_TxDesc), false); >> + vmxnet3_ring_dump(VMW_CFPRN, "TX", i, &s->txq_descr[i].tx_ring); >> + >> + s->max_tx_frags += size; >> + >> + /* TXC ring */ >> + pa = VMXNET3_READ_TX_QUEUE_DESCR64(qdescr_pa, conf.compRingBasePA); >> + size = VMXNET3_READ_TX_QUEUE_DESCR32(qdescr_pa, conf.compRingSize); >> + vmxnet3_ring_init(&s->txq_descr[i].comp_ring, pa, size, >> + sizeof(struct Vmxnet3_TxCompDesc), true); >> + vmxnet3_ring_dump(VMW_CFPRN, "TXC", i, &s->txq_descr[i].comp_ring); >> + >> + s->txq_descr[i].tx_stats_pa = >> + qdescr_pa + offsetof(struct Vmxnet3_TxQueueDesc, stats); >> + >> + memset(&s->txq_descr[i].txq_stats, 0, >> + sizeof(s->txq_descr[i].txq_stats)); >> + >> + /* Fill device-managed parameters for queues */ >> + VMXNET3_WRITE_TX_QUEUE_DESCR32(qdescr_pa, >> + ctrl.txThreshold, >> + VMXNET3_DEF_TX_THRESHOLD); >> + } >> + >> + /* Preallocate TX packet wrapper */ >> + VMW_CFPRN("Max TX fragments is %u", s->max_tx_frags); >> + vmxnet_tx_pkt_init(&s->tx_pkt, s->max_tx_frags, s->peer_has_vhdr); >> + vmxnet_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr); >> + >> + /* Read rings memory locations for RX queues */ >> + for (i = 0; i < s->rxq_num; i++) { >> + int j; >> + target_phys_addr_t qd_pa = >> + qdescr_table_pa + s->txq_num*sizeof(struct Vmxnet3_TxQueueDesc) + >> + i*sizeof(struct Vmxnet3_RxQueueDesc); >> + >> + /* Read interrupt number for this RX queue */ >> + s->rxq_descr[i].intr_idx = >> + VMXNET3_READ_TX_QUEUE_DESCR8(qd_pa, conf.intrIdx); >> + >> + VMW_CFPRN("RX Queue %d interrupt: %d", i, s->rxq_descr[i].intr_idx); >> + >> + /* Read rings memory locations */ >> + for (j = 0; j < VMXNET3_RX_RINGS_PER_QUEUE; j++) { >> + /* RX rings */ >> + pa = VMXNET3_READ_RX_QUEUE_DESCR64(qd_pa, conf.rxRingBasePA[j]); >> + size = VMXNET3_READ_RX_QUEUE_DESCR32(qd_pa, conf.rxRingSize[j]); >> + vmxnet3_ring_init(&s->rxq_descr[i].rx_ring[j], pa, size, >> + sizeof(struct Vmxnet3_RxDesc), false); >> + VMW_CFPRN("RX queue %d:%d: Base: %" PRIx64 ", Size: %d", >> + i, j, pa, size); >> + } >> + >> + /* RXC ring */ >> + pa = VMXNET3_READ_RX_QUEUE_DESCR64(qd_pa, conf.compRingBasePA); >> + size = VMXNET3_READ_RX_QUEUE_DESCR32(qd_pa, conf.compRingSize); >> + vmxnet3_ring_init(&s->rxq_descr[i].comp_ring, pa, size, >> + sizeof(struct Vmxnet3_RxCompDesc), true); >> + VMW_CFPRN("RXC queue %d: Base: %" PRIx64 ", Size: %d", i, pa, size); >> + >> + s->rxq_descr[i].rx_stats_pa = >> + qd_pa + offsetof(struct Vmxnet3_RxQueueDesc, stats); >> + memset(&s->rxq_descr[i].rxq_stats, 0, >> + sizeof(s->rxq_descr[i].rxq_stats)); >> + } >> + >> + vmxnet3_flush_shmem_changes(); >> + >> + /* Revert MAC address to the permanent one on device reactivation */ >> + memcpy(&s->conf.macaddr.a, &s->perm_mac.a, sizeof(s->perm_mac.a)); >> + VMW_CFPRN("MAC address set to: " VMXNET_MF, VMXNET_MA(s->conf.macaddr.a)); >> + >> + s->device_active = true; >> +} >> + >> +static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd) >> +{ >> + s->last_command = cmd; >> + >> + switch (cmd) { >> + case VMXNET3_CMD_GET_PERM_MAC_HI: >> + VMW_CBPRN("Set: Get upper part of permanent MAC"); >> + break; >> + >> + case VMXNET3_CMD_GET_PERM_MAC_LO: >> + VMW_CBPRN("Set: Get lower part of permanent MAC"); >> + break; >> + >> + case VMXNET3_CMD_GET_STATS: >> + VMW_CBPRN("Set: Get device statistics"); >> + vmxnet3_fill_stats(s); >> + break; >> + >> + case VMXNET3_CMD_ACTIVATE_DEV: >> + VMW_CBPRN("Set: Activating vmxnet3 device"); >> + vmxnet3_activate_device(s); >> + break; >> + >> + case VMXNET3_CMD_UPDATE_RX_MODE: >> + VMW_CBPRN("Set: Update rx mode"); >> + vmxnet3_update_rx_mode(s); >> + break; >> + >> + case VMXNET3_CMD_UPDATE_VLAN_FILTERS: >> + VMW_CBPRN("Set: Update VLAN filters"); >> + vmxnet3_update_vlan_filters(s); >> + break; >> + >> + case VMXNET3_CMD_UPDATE_MAC_FILTERS: >> + VMW_CBPRN("Set: Update MAC filters"); >> + vmxnet3_update_mcast_filters(s); >> + break; >> + >> + case VMXNET3_CMD_UPDATE_FEATURE: >> + VMW_CBPRN("Set: Update features"); >> + vmxnet3_update_features(s); >> + break; >> + >> + case VMXNET3_CMD_UPDATE_PMCFG: >> + VMW_CBPRN("Set: Update power management config"); >> + vmxnet3_update_pm_state(s); >> + break; >> + >> + case VMXNET3_CMD_GET_LINK: >> + VMW_CBPRN("Set: Get link"); >> + break; >> + >> + case VMXNET3_CMD_RESET_DEV: >> + VMW_CBPRN("Set: Reset device"); >> + vmxnet3_reset(s); >> + break; >> + >> + case VMXNET3_CMD_QUIESCE_DEV: >> + VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - pause the device"); >> + vmxnet3_deactivate_device(s); >> + break; >> + >> + case VMXNET3_CMD_GET_CONF_INTR: >> + VMW_CBPRN("Set: VMXNET3_CMD_GET_CONF_INTR - interrupt configuration"); >> + break; >> + >> + default: >> + VMW_CBPRN("Received unknown command: %" PRIx64, cmd); >> + break; >> + } >> +} >> + >> +static uint64_t vmxnet3_get_command_status(VMXNET3State *s) >> +{ >> + uint64_t ret; >> + >> + switch (s->last_command) { >> + case VMXNET3_CMD_ACTIVATE_DEV: >> + ret = (s->device_active) ? 0 : -1; >> + VMW_CFPRN("Device active: %" PRIx64, ret); >> + break; >> + >> + case VMXNET3_CMD_GET_LINK: >> + ret = s->link_status_and_speed; >> + VMW_CFPRN("Link and speed: %" PRIx64, ret); >> + break; >> + >> + case VMXNET3_CMD_GET_PERM_MAC_LO: >> + ret = vmxnet3_get_mac_low(&s->perm_mac); >> + break; >> + >> + case VMXNET3_CMD_GET_PERM_MAC_HI: >> + ret = vmxnet3_get_mac_high(&s->perm_mac); >> + break; >> + >> + case VMXNET3_CMD_GET_CONF_INTR: >> + ret = vmxnet3_get_interrupt_config(s); >> + break; >> + >> + default: >> + VMW_WRPRN("Received request for unknown command: %x", s->last_command); >> + ret = -1; >> + break; >> + } >> + >> + return ret; >> +} >> + >> +static void vmxnet3_set_events(VMXNET3State *s, uint32_t val) >> +{ >> + uint32_t events; >> + >> + VMW_CBPRN("Setting events: 0x%x", val); >> + events = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, ecr) | val; >> + VMXNET3_WRITE_DRV_SHARED32(s->drv_shmem, ecr, events); >> +} >> + >> +static void vmxnet3_ack_events(VMXNET3State *s, uint32_t val) >> +{ >> + uint32_t events; >> + >> + VMW_CBPRN("Clearing events: 0x%x", val); >> + events = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, ecr) & ~val; >> + VMXNET3_WRITE_DRV_SHARED32(s->drv_shmem, ecr, events); >> +} >> + >> +static void >> +vmxnet3_io_bar1_write(void *opaque, >> + target_phys_addr_t addr, >> + uint64_t val, >> + unsigned size) >> +{ >> + VMXNET3State *s = opaque; >> + >> + switch (addr) { >> + /* Vmxnet3 Revision Report Selection */ >> + case VMXNET3_REG_VRRS: >> + VMW_CBPRN("Write BAR1 [VMXNET3_REG_VRRS] = %" PRIx64 ", size %d", >> + val, size); >> + break; >> + >> + /* UPT Version Report Selection */ >> + case VMXNET3_REG_UVRS: >> + VMW_CBPRN("Write BAR1 [VMXNET3_REG_UVRS] = %" PRIx64 ", size %d", >> + val, size); >> + break; >> + >> + /* Driver Shared Address Low */ >> + case VMXNET3_REG_DSAL: >> + VMW_CBPRN("Write BAR1 [VMXNET3_REG_DSAL] = %" PRIx64 ", size %d", >> + val, size); >> + /* >> + * Guest driver will first write the low part of the shared >> + * memory address. We save it to temp variable and set the >> + * shared address only after we get the high part >> + */ >> + if (0 == val) { >> + s->device_active = false; >> + } >> + s->temp_shared_guest_driver_memory = val; >> + s->drv_shmem = 0; >> + break; >> + >> + /* Driver Shared Address High */ >> + case VMXNET3_REG_DSAH: >> + VMW_CBPRN("Write BAR1 [VMXNET3_REG_DSAH] = %" PRIx64 ", size %d", >> + val, size); >> + /* >> + * Set the shared memory between guest driver and device. >> + * We already should have low address part. >> + */ >> + s->drv_shmem = s->temp_shared_guest_driver_memory | (val << 32); >> + break; >> + >> + /* Command */ >> + case VMXNET3_REG_CMD: >> + VMW_CBPRN("Write BAR1 [VMXNET3_REG_CMD] = %" PRIx64 ", size %d", >> + val, size); >> + vmxnet3_handle_command(s, val); >> + break; >> + >> + /* MAC Address Low */ >> + case VMXNET3_REG_MACL: >> + VMW_CBPRN("Write BAR1 [VMXNET3_REG_MACL] = %" PRIx64 ", size %d", >> + val, size); >> + s->temp_mac = val; >> + break; >> + >> + /* MAC Address High */ >> + case VMXNET3_REG_MACH: >> + VMW_CBPRN("Write BAR1 [VMXNET3_REG_MACH] = %" PRIx64 ", size %d", >> + val, size); >> + vmxnet3_set_variable_mac(s, val, s->temp_mac); >> + break; >> + >> + /* Interrupt Cause Register */ >> + case VMXNET3_REG_ICR: >> + VMW_CBPRN("Write BAR1 [VMXNET3_REG_ICR] = %" PRIx64 ", size %d", >> + val, size); >> + assert(false); >> + break; >> + >> + /* Event Cause Register */ >> + case VMXNET3_REG_ECR: >> + VMW_CBPRN("Write BAR1 [VMXNET3_REG_ECR] = %" PRIx64 ", size %d", >> + val, size); >> + vmxnet3_ack_events(s, val); >> + break; >> + >> + default: >> + VMW_CBPRN("Unknown Write to BAR1 [%" PRIx64 "] = %" PRIx64 ", size %d", >> + addr, val, size); >> + break; >> + } >> +} >> + >> +static uint64_t >> +vmxnet3_io_bar1_read(void *opaque, target_phys_addr_t addr, unsigned size) >> +{ >> + VMXNET3State *s = opaque; >> + uint64_t ret = 0; >> + >> + switch (addr) { >> + /* Vmxnet3 Revision Report Selection */ >> + case VMXNET3_REG_VRRS: >> + VMW_CBPRN("Read BAR1 [VMXNET3_REG_VRRS], size %d", size); >> + ret = VMXNET3_DEVICE_REVISION; >> + break; >> + >> + /* UPT Version Report Selection */ >> + case VMXNET3_REG_UVRS: >> + VMW_CBPRN("Read BAR1 [VMXNET3_REG_UVRS], size %d", size); >> + ret = VMXNET3_DEVICE_VERSION; >> + break; >> + >> + /* Command */ >> + case VMXNET3_REG_CMD: >> + VMW_CBPRN("Read BAR1 [VMXNET3_REG_CMD], size %d", size); >> + ret = vmxnet3_get_command_status(s); >> + break; >> + >> + /* MAC Address Low */ >> + case VMXNET3_REG_MACL: >> + VMW_CBPRN("Read BAR1 [VMXNET3_REG_MACL], size %d", size); >> + ret = vmxnet3_get_mac_low(&s->conf.macaddr); >> + break; >> + >> + /* MAC Address High */ >> + case VMXNET3_REG_MACH: >> + VMW_CBPRN("Read BAR1 [VMXNET3_REG_MACH], size %d", size); >> + ret = vmxnet3_get_mac_high(&s->conf.macaddr); >> + break; >> + >> + /* >> + * Interrupt Cause Register >> + * Used for legacy interrupts only so interrupt index always 0 >> + */ >> + case VMXNET3_REG_ICR: >> + VMW_CBPRN("Read BAR1 [VMXNET3_REG_ICR], size %d", size); >> + if (vmxnet3_interrupt_asserted(s, 0)) { >> + vmxnet3_clear_interrupt(s, 0); >> + ret = true; >> + } else { >> + ret = false; >> + } >> + break; >> + >> + default: >> + VMW_CBPRN("Unknow read BAR1[%" PRIx64 "], %d bytes", addr, size); >> + break; >> + } >> + >> + return ret; >> +} >> + >> +static int >> +vmxnet3_can_receive(VLANClientState *nc) >> +{ >> + VMXNET3State *s = DO_UPCAST(NICState, nc, nc)->opaque; >> + return s->device_active && >> + VMXNET_FLAG_IS_SET(s->link_status_and_speed, VMXNET3_LINK_STATUS_UP); >> +} >> + >> +static inline bool >> +vmxnet3_is_registered_vlan(VMXNET3State *s, const void *data) >> +{ >> + uint16_t vlan_tag = eth_get_pkt_tci(data) & VLAN_VID_MASK; >> + if (IS_SPECIAL_VLAN_ID(vlan_tag)) { >> + return true; >> + } >> + >> + return VMXNET3_VFTABLE_ENTRY_IS_SET(s->vlan_table, vlan_tag); >> +} >> + >> +static bool >> +vmxnet3_is_allowed_mcast_group(VMXNET3State *s, const uint8_t *group_mac) >> +{ >> + int i; >> + for (i = 0; i < s->mcast_list_len; i++) { >> + if (!memcmp(group_mac, s->mcast_list[i].a, sizeof(s->mcast_list[i]))) { >> + return true; >> + } >> + } >> + return false; >> +} >> + >> +static bool >> +vmxnet3_rx_filter_may_indicate(VMXNET3State *s, const void *data, >> + size_t size) >> +{ >> + struct eth_header *ehdr = PKT_GET_ETH_HDR(data); >> + >> + if (VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_PROMISC)) { >> + return true; >> + } >> + >> + if (!vmxnet3_is_registered_vlan(s, data)) { >> + return false; >> + } >> + >> + switch (vmxnet_rx_pkt_get_packet_type(s->rx_pkt)) { >> + case ETH_PKT_UCAST: >> + if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_UCAST)) { >> + return false; >> + } >> + if (memcmp(s->conf.macaddr.a, ehdr->h_dest, ETH_ALEN)) { >> + return false; >> + } >> + break; >> + >> + case ETH_PKT_BCAST: >> + if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_BCAST)) { >> + return false; >> + } >> + break; >> + >> + case ETH_PKT_MCAST: >> + if (VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_ALL_MULTI)) { >> + return true; >> + } >> + if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_MCAST)) { >> + return false; >> + } >> + if (!vmxnet3_is_allowed_mcast_group(s, ehdr->h_dest)) { >> + return false; >> + } >> + break; >> + >> + default: >> + assert(false); >> + } >> + >> + return true; >> +} >> + >> +static ssize_t >> +vmxnet3_receive(VLANClientState *nc, const uint8_t *buf, size_t size) >> +{ >> + VMXNET3State *s = DO_UPCAST(NICState, nc, nc)->opaque; >> + size_t bytes_indicated; >> + >> + if (!vmxnet3_can_receive(&s->nic->nc)) { >> + VMW_PKPRN("Cannot receive now"); >> + return -1; >> + } >> + >> + if (s->peer_has_vhdr) { >> + vmxnet_rx_pkt_set_vhdr(s->rx_pkt, (struct virtio_net_hdr *)buf); >> + buf += sizeof(struct virtio_net_hdr); >> + size -= sizeof(struct virtio_net_hdr); >> + } >> + >> + vmxnet_rx_pkt_set_packet_type(s->rx_pkt, >> + get_eth_packet_type(PKT_GET_ETH_HDR(buf))); >> + >> + if (vmxnet3_rx_filter_may_indicate(s, buf, size)) { >> + vmxnet_rx_pkt_attach_data(s->rx_pkt, buf, size, s->rx_vlan_stripping); >> + bytes_indicated = vmxnet3_indicate_packet(s) ? size : -1; >> + if (bytes_indicated < size) { >> + VMW_PKPRN("RX: %lu of %lu bytes indicated", bytes_indicated, size); >> + } >> + } else { >> + VMW_PKPRN("Packet dropped by RX filter"); >> + bytes_indicated = size; >> + } >> + >> + assert(size > 0); >> + assert(bytes_indicated != 0); >> + return bytes_indicated; >> +} >> + >> +static void vmxnet3_cleanup(VLANClientState *nc) >> +{ >> + VMXNET3State *s = DO_UPCAST(NICState, nc, nc)->opaque; >> + s->nic = NULL; >> +} >> + >> +static void vmxnet3_set_link_status(VLANClientState *nc) >> +{ >> + VMXNET3State *s = DO_UPCAST(NICState, nc, nc)->opaque; >> + >> + if (nc->link_down) { >> + s->link_status_and_speed &= ~VMXNET3_LINK_STATUS_UP; >> + } else { >> + s->link_status_and_speed |= VMXNET3_LINK_STATUS_UP; >> + } >> + >> + vmxnet3_set_events(s, VMXNET3_ECR_LINK); >> + vmxnet3_trigger_interrupt(s, s->event_int_idx); >> +} >> + >> +static NetClientInfo net_vmxnet3_info = { >> + .type = NET_CLIENT_TYPE_NIC, >> + .size = sizeof(NICState), >> + .can_receive = vmxnet3_can_receive, >> + .receive = vmxnet3_receive, >> + .cleanup = vmxnet3_cleanup, >> + .link_status_changed = vmxnet3_set_link_status, >> +}; >> + >> +static bool vmxnet3_peer_has_vnet_hdr(VMXNET3State *s) >> +{ >> + VLANClientState *peer = s->nic->nc.peer; >> + >> + if ((NULL != peer) && >> + (NET_CLIENT_TYPE_TAP == peer->info->type) && >> + tap_has_vnet_hdr(peer)) { >> + return true; >> + } >> + >> + VMW_WRPRN("Peer has no virtio extension. Task offloads will be emulated."); >> + return false; >> +} >> + >> +static void vmxnet3_net_uninit(VMXNET3State *s) >> +{ >> + if (NULL != s->mcast_list) { >> + g_free(s->mcast_list); >> + } >> + >> + vmxnet_tx_pkt_reset(s->tx_pkt); >> + vmxnet_tx_pkt_uninit(s->tx_pkt); >> + vmxnet_rx_pkt_uninit(s->rx_pkt); >> +} >> + >> +static void vmxnet3_net_init(VMXNET3State *s) >> +{ >> + VMW_CBPRN("vmxnet3_net_init called..."); >> + >> + qemu_macaddr_default_if_unset(&s->conf.macaddr); >> + >> + /* Windows guest will query the address that was set on init */ >> + memcpy(&s->perm_mac.a, &s->conf.macaddr.a, sizeof(s->perm_mac.a)); >> + >> + s->mcast_list = NULL; >> + s->mcast_list_len = 0; >> + >> + s->link_status_and_speed = VMXNET3_LINK_SPEED | VMXNET3_LINK_STATUS_UP; >> + >> + VMW_CFPRN("Permanent MAC: " MAC_FMT, MAC_ARG(s->perm_mac.a)); >> + >> + s->nic = qemu_new_nic(&net_vmxnet3_info, &s->conf, >> + object_get_typename(OBJECT(s)), >> + s->dev.qdev.id, s); >> + >> + s->peer_has_vhdr = vmxnet3_peer_has_vnet_hdr(s); >> + s->tx_sop = true; >> + s->skip_current_tx_pkt = false; >> + s->tx_pkt = NULL; >> + s->rx_pkt = NULL; >> + s->rx_vlan_stripping = false; >> + s->lro_supported = false; >> + >> + if (s->peer_has_vhdr) { >> + tap_set_vnet_hdr_len(s->nic->nc.peer, sizeof(struct virtio_net_hdr)); >> + tap_using_vnet_hdr(s->nic->nc.peer, 1); >> + } >> + >> + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); >> +} >> + >> +static void >> +vmxnet3_unuse_msix_vectors(VMXNET3State *s, int num_vectors) >> +{ >> + int i; >> + for (i = 0; i < num_vectors; i++) { >> + msix_vector_unuse(&s->dev, i); >> + } >> +} >> + >> +static bool >> +vmxnet3_use_msix_vectors(VMXNET3State *s, int num_vectors) >> +{ >> + int i; >> + for (i = 0; i < num_vectors; i++) { >> + int res = msix_vector_use(&s->dev, i); >> + if (0 > res) { >> + VMW_WRPRN("Failed to use MSI-X vector %d, error %d", i, res); >> + vmxnet3_unuse_msix_vectors(s, i); >> + return false; >> + } >> + } >> + return true; >> +} >> + >> +static bool >> +vmxnet3_init_msix(VMXNET3State *s) >> +{ >> + int res = msix_init(&s->dev, VMXNET3_MAX_INTRS, >> + &s->msix_bar, VMXNET3_MSIX_BAR_IDX, 0); >> + if (0 > res) { >> + VMW_WRPRN("Failed to initialize MSI-X, error %d", res); >> + s->msix_used = false; >> + } else { >> + if (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) { >> + VMW_WRPRN("Failed to use MSI-X vectors, error %d", res); >> + msix_uninit(&s->dev, &s->msix_bar); >> + s->msix_used = false; >> + } else { >> + s->msix_used = true; >> + } >> + } >> + return s->msix_used; >> +} >> + >> +static void >> +vmxnet3_cleanup_msix(VMXNET3State *s) >> +{ >> + if (s->msix_used) { >> + msix_vector_unuse(&s->dev, VMXNET3_MAX_INTRS); >> + msix_uninit(&s->dev, &s->msix_bar); >> + } >> +} >> + >> +static bool >> +vmxnet3_init_msi(VMXNET3State *s) >> +{ >> +#define VMXNET3_MSI_NUM_VECTORS (1) >> +#define VMXNET3_MSI_OFFSET (0x50) >> +#define VMXNET3_USE_64BIT (true) >> +#define VMXNET3_PER_VECTOR_MASK (false) >> + >> + int res; >> + res = msi_init(&s->dev, VMXNET3_MSI_OFFSET, VMXNET3_MSI_NUM_VECTORS, >> + VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK); >> + if (0 > res) { >> + VMW_WRPRN("Failed to initialize MSI, error %d", res); >> + s->msi_used = false; >> + } else { >> + s->msi_used = true; >> + } >> + >> + return s->msi_used; >> +} >> + >> +static void >> +vmxnet3_cleanup_msi(VMXNET3State *s) >> +{ >> + if (s->msi_used) { >> + msi_uninit(&s->dev); >> + } >> +} >> + >> +static void >> +vmxnet3_msix_save(QEMUFile *f, void *opaque) >> +{ >> + msix_save(&((VMXNET3State *)opaque)->dev, f); >> +} >> + >> +static int >> +vmxnet3_msix_load(QEMUFile *f, void *opaque, int version_id) >> +{ >> + msix_load(&((VMXNET3State *)opaque)->dev, f); >> + return 0; >> +} >> + >> +static int vmxnet3_pci_init(PCIDevice *dev) >> +{ >> + static const MemoryRegionOps b0_ops = { >> + .read = vmxnet3_io_bar0_read, >> + .write = vmxnet3_io_bar0_write, >> + .endianness = DEVICE_LITTLE_ENDIAN, >> + .impl = { >> + .min_access_size = 4, >> + .max_access_size = 4, >> + }, >> + }; >> + >> + static const MemoryRegionOps b1_ops = { >> + .read = vmxnet3_io_bar1_read, >> + .write = vmxnet3_io_bar1_write, >> + .endianness = DEVICE_LITTLE_ENDIAN, >> + .impl = { >> + .min_access_size = 4, >> + .max_access_size = 4, >> + }, >> + }; >> + >> + VMXNET3State *s = DO_UPCAST(VMXNET3State, dev, dev); >> + int i; >> + >> + VMW_CBPRN("Starting init..."); >> + >> + memory_region_init_io(&s->bar0, &b0_ops, s, >> + "vmxnet3-b0", VMXNET3_PT_REG_SIZE); >> + pci_register_bar(&s->dev, VMXNET3_BAR0_IDX, >> + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0); >> + >> + memory_region_init_io(&s->bar1, &b1_ops, s, >> + "vmxnet3-b1", VMXNET3_VD_REG_SIZE); >> + pci_register_bar(&s->dev, VMXNET3_BAR1_IDX, >> + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar1); >> + >> + memory_region_init(&s->msix_bar, "vmxnet3-msix-bar", >> + VMXNET3_MSIX_BAR_SIZE); >> + pci_register_bar(&s->dev, VMXNET3_MSIX_BAR_IDX, >> + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->msix_bar); >> + >> + for (i = 0; i < ARRAY_SIZE(s->interrupt_states); i++) { >> + s->interrupt_states[i].is_asserted = false; >> + s->interrupt_states[i].is_pending = false; >> + s->interrupt_states[i].is_masked = true; >> + } >> + >> + /* Interrupt pin A */ >> + s->dev.config[PCI_INTERRUPT_PIN] = 0x01; >> + >> + if (!vmxnet3_init_msix(s)) { >> + VMW_WRPRN("Failed to initialize MSI-X, configuration is inconsistent."); >> + } >> + >> + if (!vmxnet3_init_msi(s)) { >> + VMW_WRPRN("Failed to initialize MSI, configuration is inconsistent."); >> + } >> + >> + vmxnet3_net_init(s); >> + >> + register_savevm(&dev->qdev, "vmxnet3-msix", -1, 1, >> + vmxnet3_msix_save, vmxnet3_msix_load, s); >> + >> + add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet-phy@0"); >> + >> + return 0; >> +} >> + >> + >> +static int vmxnet3_pci_uninit(PCIDevice *dev) >> +{ >> + VMXNET3State *s = DO_UPCAST(VMXNET3State, dev, dev); >> + >> + VMW_CBPRN("Starting uninit..."); >> + >> + unregister_savevm(&dev->qdev, "vmxnet3-msix", s); >> + >> + vmxnet3_net_uninit(s); >> + >> + vmxnet3_cleanup_msix(s); >> + >> + vmxnet3_cleanup_msi(s); >> + >> + memory_region_destroy(&s->bar0); >> + memory_region_destroy(&s->bar1); >> + memory_region_destroy(&s->msix_bar); >> + >> + return 0; >> +} >> + >> +static void vmxnet3_qdev_reset(DeviceState *dev) >> +{ >> + VMXNET3State *s = DO_UPCAST(VMXNET3State, dev.qdev, dev); >> + VMW_CBPRN("Starting QDEV reset..."); >> + vmxnet3_reset(s); >> +} >> + >> +static bool vmxnet3_mc_list_needed(void *opaque) >> +{ >> + return true; >> +} >> + >> +static int vmxnet3_mcast_list_pre_load(void *opaque) >> +{ >> + VMXNET3State *s = opaque; >> + >> + s->mcast_list = g_malloc(s->mcast_list_buff_size); >> + >> + return 0; >> +} >> + >> + >> +static void vmxnet3_pre_save(void *opaque) >> +{ >> + VMXNET3State *s = opaque; >> + >> + s->mcast_list_buff_size = s->mcast_list_len * sizeof(MACAddr); >> +} >> + >> +static const VMStateDescription vmxtate_vmxnet3_mcast_list = { >> + .name = "vmxnet3/mcast_list", >> + .version_id = 1, >> + .minimum_version_id = 1, >> + .minimum_version_id_old = 1, >> + .pre_load = vmxnet3_mcast_list_pre_load, >> + .fields = (VMStateField[]) { >> + VMSTATE_VBUFFER_UINT32(mcast_list, VMXNET3State, 0, NULL, 0, >> + mcast_list_buff_size), >> + VMSTATE_END_OF_LIST() >> + } >> +}; >> + >> +static void vmxnet3_get_ring_from_file(QEMUFile *f, Vmxnet3Ring *r) >> +{ >> +#if TARGET_PHYS_ADDR_BITS == 64 >> + r->pa = qemu_get_be64(f); >> +#else >> + r->pa = qemu_get_be32(f); >> +#endif >> + r->size = qemu_get_be32(f); >> + r->cell_size = qemu_get_be32(f); >> + r->next = qemu_get_be32(f); >> + r->gen = qemu_get_byte(f); >> +} >> + >> +static void vmxnet3_put_ring_to_file(QEMUFile *f, Vmxnet3Ring *r) >> +{ >> +#if TARGET_PHYS_ADDR_BITS == 64 >> + qemu_put_be64(f, r->pa); >> +#else >> + qemu_put_be32(f, r->pa); >> +#endif >> + qemu_put_be32(f, r->size); >> + qemu_put_be32(f, r->cell_size); >> + qemu_put_be32(f, r->next); >> + qemu_put_byte(f, r->gen); >> +} >> + >> +static void vmxnet3_get_tx_stats_from_file(QEMUFile *f, >> + struct UPT1_TxStats *tx_stat) >> +{ >> + tx_stat->TSOPktsTxOK = qemu_get_be64(f); >> + tx_stat->TSOBytesTxOK = qemu_get_be64(f); >> + tx_stat->ucastPktsTxOK = qemu_get_be64(f); >> + tx_stat->ucastBytesTxOK = qemu_get_be64(f); >> + tx_stat->mcastPktsTxOK = qemu_get_be64(f); >> + tx_stat->mcastBytesTxOK = qemu_get_be64(f); >> + tx_stat->bcastPktsTxOK = qemu_get_be64(f); >> + tx_stat->bcastBytesTxOK = qemu_get_be64(f); >> + tx_stat->pktsTxError = qemu_get_be64(f); >> + tx_stat->pktsTxDiscard = qemu_get_be64(f); >> +} >> + >> +static void vmxnet3_put_tx_stats_to_file(QEMUFile *f, >> + struct UPT1_TxStats *tx_stat) >> +{ >> + qemu_put_be64(f, tx_stat->TSOPktsTxOK); >> + qemu_put_be64(f, tx_stat->TSOBytesTxOK); >> + qemu_put_be64(f, tx_stat->ucastPktsTxOK); >> + qemu_put_be64(f, tx_stat->ucastBytesTxOK); >> + qemu_put_be64(f, tx_stat->mcastPktsTxOK); >> + qemu_put_be64(f, tx_stat->mcastBytesTxOK); >> + qemu_put_be64(f, tx_stat->bcastPktsTxOK); >> + qemu_put_be64(f, tx_stat->bcastBytesTxOK); >> + qemu_put_be64(f, tx_stat->pktsTxError); >> + qemu_put_be64(f, tx_stat->pktsTxDiscard); >> +} >> + >> +static int vmxnet3_get_txq_descr(QEMUFile *f, void *pv, size_t size) >> +{ >> + Vmxnet3TxqDescr *r = pv; >> + >> + vmxnet3_get_ring_from_file(f, &r->tx_ring); >> + vmxnet3_get_ring_from_file(f, &r->comp_ring); >> + r->intr_idx = qemu_get_byte(f); >> + >> +#if TARGET_PHYS_ADDR_BITS == 64 >> + r->tx_stats_pa = qemu_get_be64(f); >> +#else >> + r->tx_stats_pa = qemu_get_be32(f); >> +#endif >> + >> + vmxnet3_get_tx_stats_from_file(f, &r->txq_stats); >> + >> + return 0; >> +} >> + >> +static void vmxnet3_put_txq_descr(QEMUFile *f, void *pv, size_t size) >> +{ >> + Vmxnet3TxqDescr *r = pv; >> + >> + vmxnet3_put_ring_to_file(f, &r->tx_ring); >> + vmxnet3_put_ring_to_file(f, &r->comp_ring); >> + qemu_put_byte(f, r->intr_idx); >> +#if TARGET_PHYS_ADDR_BITS == 64 >> + qemu_put_be64(f, r->tx_stats_pa); >> +#else >> + qemu_put_be32(f, r->tx_stats_pa); >> +#endif >> + vmxnet3_put_tx_stats_to_file(f, &r->txq_stats); >> +} >> + >> +const VMStateInfo txq_descr_info = { >> + .name = "txq_descr", >> + .get = vmxnet3_get_txq_descr, >> + .put = vmxnet3_put_txq_descr >> +}; >> + >> +static void vmxnet3_get_rx_stats_from_file(QEMUFile *f, >> + struct UPT1_RxStats *rx_stat) >> +{ >> + rx_stat->LROPktsRxOK = qemu_get_be64(f); >> + rx_stat->LROBytesRxOK = qemu_get_be64(f); >> + rx_stat->ucastPktsRxOK = qemu_get_be64(f); >> + rx_stat->ucastBytesRxOK = qemu_get_be64(f); >> + rx_stat->mcastPktsRxOK = qemu_get_be64(f); >> + rx_stat->mcastBytesRxOK = qemu_get_be64(f); >> + rx_stat->bcastPktsRxOK = qemu_get_be64(f); >> + rx_stat->bcastBytesRxOK = qemu_get_be64(f); >> + rx_stat->pktsRxOutOfBuf = qemu_get_be64(f); >> + rx_stat->pktsRxError = qemu_get_be64(f); >> +} >> + >> +static void vmxnet3_put_rx_stats_to_file(QEMUFile *f, >> + struct UPT1_RxStats *rx_stat) >> +{ >> + qemu_put_be64(f, rx_stat->LROPktsRxOK); >> + qemu_put_be64(f, rx_stat->LROBytesRxOK); >> + qemu_put_be64(f, rx_stat->ucastPktsRxOK); >> + qemu_put_be64(f, rx_stat->ucastBytesRxOK); >> + qemu_put_be64(f, rx_stat->mcastPktsRxOK); >> + qemu_put_be64(f, rx_stat->mcastBytesRxOK); >> + qemu_put_be64(f, rx_stat->bcastPktsRxOK); >> + qemu_put_be64(f, rx_stat->bcastBytesRxOK); >> + qemu_put_be64(f, rx_stat->pktsRxOutOfBuf); >> + qemu_put_be64(f, rx_stat->pktsRxError); >> +} >> + >> +static int vmxnet3_get_rxq_descr(QEMUFile *f, void *pv, size_t size) >> +{ >> + Vmxnet3RxqDescr *r = pv; >> + int i; >> + >> + for (i = 0; i < VMXNET3_RX_RINGS_PER_QUEUE; i++) { >> + vmxnet3_get_ring_from_file(f, &r->rx_ring[i]); >> + } >> + >> + vmxnet3_get_ring_from_file(f, &r->comp_ring); >> + r->intr_idx = qemu_get_byte(f); >> +#if TARGET_PHYS_ADDR_BITS == 64 >> + r->rx_stats_pa = qemu_get_be64(f); >> +#else >> + r->rx_stats_pa = qemu_get_be32(f); >> +#endif >> + >> + vmxnet3_get_rx_stats_from_file(f, &r->rxq_stats); >> + >> + return 0; >> +} >> + >> +static void vmxnet3_put_rxq_descr(QEMUFile *f, void *pv, size_t size) >> +{ >> + Vmxnet3RxqDescr *r = pv; >> + int i; >> + >> + for (i = 0; i < VMXNET3_RX_RINGS_PER_QUEUE; i++) { >> + vmxnet3_put_ring_to_file(f, &r->rx_ring[i]); >> + } >> + >> + vmxnet3_put_ring_to_file(f, &r->comp_ring); >> + qemu_put_byte(f, r->intr_idx); >> +#if TARGET_PHYS_ADDR_BITS == 64 >> + qemu_put_be64(f, r->rx_stats_pa); >> +#else >> + qemu_put_be32(f, r->rx_stats_pa); >> +#endif >> + vmxnet3_put_rx_stats_to_file(f, &r->rxq_stats); >> +} >> + >> +static int vmxnet3_post_load(void *opaque, int version_id) >> +{ >> + VMXNET3State *s = opaque; >> + >> + vmxnet_tx_pkt_init(&s->tx_pkt, s->max_tx_frags, s->peer_has_vhdr); >> + vmxnet_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr); >> + >> + if (s->msix_used) { >> + if (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) { >> + VMW_WRPRN("Failed to re-use MSI-X vectors"); >> + msix_uninit(&s->dev, &s->msix_bar); >> + s->msix_used = false; >> + return -1; >> + } >> + } >> + >> + return 0; >> +} >> + >> +const VMStateInfo rxq_descr_info = { >> + .name = "rxq_descr", >> + .get = vmxnet3_get_rxq_descr, >> + .put = vmxnet3_put_rxq_descr >> +}; >> + >> +static int vmxnet3_get_int_state(QEMUFile *f, void *pv, size_t size) >> +{ >> + Vmxnet3IntState *r = pv; >> + >> + r->is_masked = qemu_get_byte(f); >> + r->is_pending = qemu_get_byte(f); >> + r->is_asserted = qemu_get_byte(f); >> + >> + return 0; >> +} >> + >> +static void vmxnet3_put_int_state(QEMUFile *f, void *pv, size_t size) >> +{ >> + Vmxnet3IntState *r = pv; >> + >> + qemu_put_byte(f, r->is_masked); >> + qemu_put_byte(f, r->is_pending); >> + qemu_put_byte(f, r->is_asserted); >> +} >> + >> +const VMStateInfo int_state_info = { >> + .name = "int_state", >> + .get = vmxnet3_get_int_state, >> + .put = vmxnet3_put_int_state >> +}; >> + >> +static const VMStateDescription vmstate_vmxnet3 = { >> + .name = "vmxnet3", >> + .version_id = 1, >> + .minimum_version_id = 1, >> + .minimum_version_id_old = 1, >> + .pre_save = vmxnet3_pre_save, >> + .post_load = vmxnet3_post_load, >> + .fields = (VMStateField[]) { >> + VMSTATE_PCI_DEVICE(dev, VMXNET3State), >> + VMSTATE_BOOL(rx_packets_compound, VMXNET3State), >> + VMSTATE_BOOL(rx_vlan_stripping, VMXNET3State), >> + VMSTATE_BOOL(lro_supported, VMXNET3State), >> + VMSTATE_UINT32(rx_mode, VMXNET3State), >> + VMSTATE_UINT32(mcast_list_len, VMXNET3State), >> + VMSTATE_UINT32(mcast_list_buff_size, VMXNET3State), >> + VMSTATE_UINT32_ARRAY(vlan_table, VMXNET3State, VMXNET3_VFT_SIZE), >> + VMSTATE_UINT32(mtu, VMXNET3State), >> + VMSTATE_UINT16(max_rx_frags, VMXNET3State), >> + VMSTATE_UINT32(max_tx_frags, VMXNET3State), >> + VMSTATE_UINT8(event_int_idx, VMXNET3State), >> + VMSTATE_UINT8(auto_int_masking, VMXNET3State), >> + VMSTATE_UINT8(txq_num, VMXNET3State), >> + VMSTATE_UINT8(rxq_num, VMXNET3State), >> + VMSTATE_UINT32(device_active, VMXNET3State), >> + VMSTATE_UINT32(last_command, VMXNET3State), >> + VMSTATE_UINT32(link_status_and_speed, VMXNET3State), >> + VMSTATE_UINT32(temp_mac, VMXNET3State), >> +#if TARGET_PHYS_ADDR_BITS == 64 >> + VMSTATE_UINT64(drv_shmem, VMXNET3State), >> + VMSTATE_UINT64(temp_shared_guest_driver_memory, VMXNET3State), >> +#else >> + VMSTATE_UINT32(drv_shmem, VMXNET3State), >> + VMSTATE_UINT32(temp_shared_guest_driver_memory, VMXNET3State), >> +#endif >> + >> + VMSTATE_ARRAY(txq_descr, VMXNET3State, >> + VMXNET3_DEVICE_MAX_TX_QUEUES, 0, txq_descr_info, >> + Vmxnet3TxqDescr), >> + VMSTATE_ARRAY(rxq_descr, VMXNET3State, >> + VMXNET3_DEVICE_MAX_RX_QUEUES, 0, rxq_descr_info, >> + Vmxnet3RxqDescr), >> + VMSTATE_ARRAY(interrupt_states, VMXNET3State, VMXNET3_MAX_INTRS, >> + 0, int_state_info, Vmxnet3IntState), >> + >> + VMSTATE_END_OF_LIST() >> + }, >> + .subsections = (VMStateSubsection[]) { >> + { >> + .vmsd = &vmxtate_vmxnet3_mcast_list, >> + .needed = vmxnet3_mc_list_needed >> + }, >> + { >> + /* empty element. */ >> + } >> + } >> +}; >> + >> +static void >> +vmxnet3_write_config(PCIDevice *pci, uint32_t addr, uint32_t val, int len) >> +{ >> + pci_default_write_config(pci, addr, val, len); >> + msix_write_config(pci, addr, val, len); >> + msi_write_config(pci, addr, val, len); >> +} >> + >> +static Property vmxnet3_properties[] = { >> + DEFINE_NIC_PROPERTIES(VMXNET3State, conf), >> + DEFINE_PROP_END_OF_LIST(), >> +}; >> + >> +static void vmxnet3_class_init(ObjectClass *class, void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(class); >> + PCIDeviceClass *c = PCI_DEVICE_CLASS(class); >> + >> + c->init = vmxnet3_pci_init; >> + c->exit = vmxnet3_pci_uninit; >> + c->vendor_id = PCI_VENDOR_ID_VMWARE; >> + c->device_id = PCI_DEVICE_ID_VMWARE_VMXNET3; >> + c->revision = PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION; >> + c->class_id = PCI_CLASS_NETWORK_ETHERNET; >> + c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE; >> + c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3; >> + c->config_write = vmxnet3_write_config, >> + dc->desc = "VMWare Paravirtualized Ethernet v3"; >> + dc->reset = vmxnet3_qdev_reset; >> + dc->vmsd = &vmstate_vmxnet3; >> + dc->props = vmxnet3_properties; >> +} >> + >> +static TypeInfo vmxnet3_info = { >> + .name = "vmxnet3", >> + .parent = TYPE_PCI_DEVICE, >> + .instance_size = sizeof(VMXNET3State), >> + .class_init = vmxnet3_class_init, >> +}; >> + >> +static void vmxnet3_register_types(void) >> +{ >> + VMW_CBPRN("vmxnet3_register_types called..."); >> + type_register_static(&vmxnet3_info); >> +} >> + >> +type_init(vmxnet3_register_types) >> diff --git a/hw/vmxnet3.h b/hw/vmxnet3.h >> new file mode 100644 >> index 0000000..b6089ba >> --- /dev/null >> +++ b/hw/vmxnet3.h >> @@ -0,0 +1,762 @@ >> +/* >> + * QEMU VMWARE VMXNET3 paravirtual NIC >> + * >> + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) >> + * >> + * Developed by Daynix Computing LTD (http://www.daynix.com) >> + * >> + * Authors: >> + * Dmitry Fleytman >> + * Tamir Shomer >> + * Yan Vugenfirer >> + * >> + * This work is licensed under the terms of the GNU GPL, version 2 or later. >> + * See the COPYING file in the top-level directory. >> + * >> + */ >> + >> +#ifndef _QEMU_VMXNET3_H >> +#define _QEMU_VMXNET3_H >> + >> +#define VMXNET_VERSION_3 >> + >> +#define VMXNET3_DEVICE_MAX_TX_QUEUES 8 >> +#define VMXNET3_DEVICE_MAX_RX_QUEUES 8 /* Keep this value as a power of 2 */ >> + >> +/* >> + * VMWARE headers we got from Linux kernel do not fully comply QEMU coding >> + * standards in sense of types and defines used. >> + * Since we didn't want to change VMWARE code, following set of typedefs >> + * and defines needed to compile these headers with QEMU introduced. >> + */ > No need for this now. > You can export headers and put them under linux-headers. >