aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.objs2
-rw-r--r--arch_init.c3
-rwxr-xr-xconfigure2
-rw-r--r--hmp.c2
-rw-r--r--hw/cadence_gem.c17
-rw-r--r--hw/dp8393x.c17
-rw-r--r--hw/ds1338.c2
-rw-r--r--hw/e1000.c35
-rw-r--r--hw/eepro100.c18
-rw-r--r--hw/etraxfs_eth.c11
-rw-r--r--hw/i2c.c4
-rw-r--r--hw/i2c.h1
-rw-r--r--hw/isa.h2
-rw-r--r--hw/lan9118.c16
-rw-r--r--hw/lance.c2
-rw-r--r--hw/lm832x.c2
-rw-r--r--hw/max7310.c2
-rw-r--r--hw/mcf_fec.c12
-rw-r--r--hw/milkymist-minimac2.c10
-rw-r--r--hw/mipsnet.c10
-rw-r--r--hw/musicpal.c6
-rw-r--r--hw/ne2000-isa.c4
-rw-r--r--hw/ne2000.c13
-rw-r--r--hw/opencores_eth.c12
-rw-r--r--hw/pc_piix.c4
-rw-r--r--hw/pcnet-pci.c4
-rw-r--r--hw/pcnet.c13
-rw-r--r--hw/pxa2xx.c2
-rw-r--r--hw/qdev-properties-system.c46
-rw-r--r--hw/qdev-properties.h6
-rw-r--r--hw/rtl8139.c22
-rw-r--r--hw/s390x/ipl.c6
-rw-r--r--hw/s390x/s390-virtio-bus.c4
-rw-r--r--hw/s390x/s390-virtio.c8
-rw-r--r--hw/smc91c111.c10
-rw-r--r--hw/spapr_llan.c8
-rw-r--r--hw/stellaris_enet.c11
-rw-r--r--hw/usb/dev-network.c16
-rw-r--r--hw/vhost.c82
-rw-r--r--hw/vhost.h2
-rw-r--r--hw/vhost_net.c86
-rw-r--r--hw/vhost_net.h4
-rw-r--r--hw/virtio-net.c508
-rw-r--r--hw/virtio-net.h28
-rw-r--r--hw/virtio.c17
-rw-r--r--hw/virtio.h3
-rw-r--r--hw/wm8750.c2
-rw-r--r--hw/xen_nic.c19
-rw-r--r--hw/xgmac.c10
-rw-r--r--hw/xilinx_axienet.c10
-rw-r--r--hw/xilinx_ethlite.c12
-rw-r--r--include/net/net.h26
-rw-r--r--include/net/tap.h6
-rw-r--r--net/net.c198
-rw-r--r--net/tap-aix.c19
-rw-r--r--net/tap-bsd.c18
-rw-r--r--net/tap-haiku.c18
-rw-r--r--net/tap-linux.c71
-rw-r--r--net/tap-linux.h4
-rw-r--r--net/tap-solaris.c18
-rw-r--r--net/tap-win32.c18
-rw-r--r--net/tap.c333
-rw-r--r--net/tap_int.h6
-rw-r--r--qapi-schema.json5
-rw-r--r--qmp-commands.hx6
-rw-r--r--savevm.c161
-rw-r--r--target-ppc/cpu.h6
-rw-r--r--target-ppc/helper.h3
-rw-r--r--target-ppc/mem_helper.c21
-rw-r--r--target-ppc/mmu_helper.c9
-rw-r--r--target-ppc/translate.c35
-rw-r--r--target-ppc/translate_init.c10
-rw-r--r--target-s390x/cpu.c2
-rw-r--r--target-s390x/cpu.h23
-rw-r--r--target-s390x/helper.c17
-rw-r--r--target-s390x/interrupt.c2
-rw-r--r--target-s390x/kvm.c13
-rw-r--r--tests/Makefile3
-rw-r--r--tests/libqtest.c7
-rw-r--r--tests/test-xbzrle.c196
-rw-r--r--xbzrle.c173
81 files changed, 1798 insertions, 777 deletions
diff --git a/Makefile.objs b/Makefile.objs
index 68eb0cef1a..21e9c911f5 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -59,7 +59,7 @@ common-obj-$(CONFIG_LINUX) += fsdev/
common-obj-y += migration.o migration-tcp.o
common-obj-y += qemu-char.o #aio.o
common-obj-y += block-migration.o
-common-obj-y += page_cache.o
+common-obj-y += page_cache.o xbzrle.o
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
diff --git a/arch_init.c b/arch_init.c
index dada6ded1a..8da868b988 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -851,9 +851,6 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
} else if (flags & RAM_SAVE_FLAG_XBZRLE) {
- if (!migrate_use_xbzrle()) {
- return -EINVAL;
- }
void *host = host_from_stream_offset(f, addr, flags);
if (!host) {
return -EINVAL;
diff --git a/configure b/configure
index b7635e4fec..0657b1a1b5 100755
--- a/configure
+++ b/configure
@@ -1180,7 +1180,7 @@ fi
z_version=`cut -f3 -d. $source_path/VERSION`
if test -z "$werror" ; then
- if test "$z_version" = "50" -a \
+ if test -d "$source_path/.git" -a \
"$linux" = "yes" ; then
werror="yes"
else
diff --git a/hmp.c b/hmp.c
index 249b89b7e3..1689e6f1fd 100644
--- a/hmp.c
+++ b/hmp.c
@@ -892,7 +892,7 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict)
qapi_free_MigrationCapabilityStatusList(caps);
if (err) {
- monitor_printf(mon, "migrate_set_parameter: %s\n",
+ monitor_printf(mon, "migrate_set_capability: %s\n",
error_get_pretty(err));
error_free(err);
}
diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c
index b77423d449..ab86c1702d 100644
--- a/hw/cadence_gem.c
+++ b/hw/cadence_gem.c
@@ -389,10 +389,10 @@ static void gem_init_register_masks(GemState *s)
*/
static void phy_update_link(GemState *s)
{
- DB_PRINT("down %d\n", s->nic->nc.link_down);
+ DB_PRINT("down %d\n", qemu_get_queue(s->nic)->link_down);
/* Autonegotiation status mirrors link status. */
- if (s->nic->nc.link_down) {
+ if (qemu_get_queue(s->nic)->link_down) {
s->phy_regs[PHY_REG_STATUS] &= ~(PHY_REG_STATUS_ANEGCMPL |
PHY_REG_STATUS_LINK);
s->phy_regs[PHY_REG_INT_ST] |= PHY_REG_INT_ST_LINKC;
@@ -409,7 +409,7 @@ static int gem_can_receive(NetClientState *nc)
{
GemState *s;
- s = DO_UPCAST(NICState, nc, nc)->opaque;
+ s = qemu_get_nic_opaque(nc);
DB_PRINT("\n");
@@ -612,7 +612,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
uint8_t rxbuf[2048];
uint8_t *rxbuf_ptr;
- s = DO_UPCAST(NICState, nc, nc)->opaque;
+ s = qemu_get_nic_opaque(nc);
/* Do nothing if receive is not enabled. */
if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) {
@@ -908,9 +908,10 @@ static void gem_transmit(GemState *s)
/* Send the packet somewhere */
if (s->phy_loop) {
- gem_receive(&s->nic->nc, tx_packet, total_bytes);
+ gem_receive(qemu_get_queue(s->nic), tx_packet, total_bytes);
} else {
- qemu_send_packet(&s->nic->nc, tx_packet, total_bytes);
+ qemu_send_packet(qemu_get_queue(s->nic), tx_packet,
+ total_bytes);
}
/* Prepare for next packet */
@@ -1151,7 +1152,7 @@ static const MemoryRegionOps gem_ops = {
static void gem_cleanup(NetClientState *nc)
{
- GemState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ GemState *s = qemu_get_nic_opaque(nc);
DB_PRINT("\n");
s->nic = NULL;
@@ -1160,7 +1161,7 @@ static void gem_cleanup(NetClientState *nc)
static void gem_set_link(NetClientState *nc)
{
DB_PRINT("\n");
- phy_update_link(DO_UPCAST(NICState, nc, nc)->opaque);
+ phy_update_link(qemu_get_nic_opaque(nc));
}
static NetClientInfo net_gem_info = {
diff --git a/hw/dp8393x.c b/hw/dp8393x.c
index b5014501df..808157b38b 100644
--- a/hw/dp8393x.c
+++ b/hw/dp8393x.c
@@ -339,6 +339,7 @@ static void do_receiver_disable(dp8393xState *s)
static void do_transmit_packets(dp8393xState *s)
{
+ NetClientState *nc = qemu_get_queue(s->nic);
uint16_t data[12];
int width, size;
int tx_len, len;
@@ -408,13 +409,13 @@ static void do_transmit_packets(dp8393xState *s)
if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
/* Loopback */
s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
- if (s->nic->nc.info->can_receive(&s->nic->nc)) {
+ if (nc->info->can_receive(nc)) {
s->loopback_packet = 1;
- s->nic->nc.info->receive(&s->nic->nc, s->tx_buffer, tx_len);
+ nc->info->receive(nc, s->tx_buffer, tx_len);
}
} else {
/* Transmit packet */
- qemu_send_packet(&s->nic->nc, s->tx_buffer, tx_len);
+ qemu_send_packet(nc, s->tx_buffer, tx_len);
}
s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
@@ -675,7 +676,7 @@ static const MemoryRegionOps dp8393x_ops = {
static int nic_can_receive(NetClientState *nc)
{
- dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ dp8393xState *s = qemu_get_nic_opaque(nc);
if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
return 0;
@@ -724,7 +725,7 @@ static int receive_filter(dp8393xState *s, const uint8_t * buf, int size)
static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
{
- dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ dp8393xState *s = qemu_get_nic_opaque(nc);
uint16_t data[10];
int packet_type;
uint32_t available, address;
@@ -860,7 +861,7 @@ static void nic_reset(void *opaque)
static void nic_cleanup(NetClientState *nc)
{
- dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ dp8393xState *s = qemu_get_nic_opaque(nc);
memory_region_del_subregion(s->address_space, &s->mmio);
memory_region_destroy(&s->mmio);
@@ -899,11 +900,11 @@ void dp83932_init(NICInfo *nd, hwaddr base, int it_shift,
s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
s->conf.macaddr = nd->macaddr;
- s->conf.peer = nd->netdev;
+ s->conf.peers.ncs[0] = nd->netdev;
s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
qemu_register_reset(nic_reset, s);
nic_reset(s);
diff --git a/hw/ds1338.c b/hw/ds1338.c
index 379220638e..6f70538eb3 100644
--- a/hw/ds1338.c
+++ b/hw/ds1338.c
@@ -198,7 +198,7 @@ static int ds1338_init(I2CSlave *i2c)
static void ds1338_reset(DeviceState *dev)
{
- DS1338State *s = FROM_I2C_SLAVE(DS1338State, I2C_SLAVE_FROM_QDEV(dev));
+ DS1338State *s = FROM_I2C_SLAVE(DS1338State, I2C_SLAVE(dev));
/* The clock is running and synchronized with the host */
s->offset = 0;
diff --git a/hw/e1000.c b/hw/e1000.c
index ee85c53d38..bb150c67dc 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -166,12 +166,7 @@ static void
set_phy_ctrl(E1000State *s, int index, uint16_t val)
{
if ((val & MII_CR_AUTO_NEG_EN) && (val & MII_CR_RESTART_AUTO_NEG)) {
- /* no need auto-negotiation if link was down */
- if (s->nic->nc.link_down) {
- s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE;
- return;
- }
- s->nic->nc.link_down = true;
+ qemu_get_queue(s->nic)->link_down = true;
e1000_link_down(s);
s->phy_reg[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE;
DBGOUT(PHY, "Start link auto negotiation\n");
@@ -183,7 +178,7 @@ static void
e1000_autoneg_timer(void *opaque)
{
E1000State *s = opaque;
- s->nic->nc.link_down = false;
+ qemu_get_queue(s->nic)->link_down = false;
e1000_link_up(s);
s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE;
DBGOUT(PHY, "Auto negotiation is completed\n");
@@ -296,7 +291,7 @@ static void e1000_reset(void *opaque)
d->rxbuf_min_shift = 1;
memset(&d->tx, 0, sizeof d->tx);
- if (d->nic->nc.link_down) {
+ if (qemu_get_queue(d->nic)->link_down) {
e1000_link_down(d);
}
@@ -324,7 +319,7 @@ set_rx_control(E1000State *s, int index, uint32_t val)
s->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1;
DBGOUT(RX, "RCTL: %d, mac_reg[RCTL] = 0x%x\n", s->mac_reg[RDT],
s->mac_reg[RCTL]);
- qemu_flush_queued_packets(&s->nic->nc);
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
}
static void
@@ -475,10 +470,11 @@ fcs_len(E1000State *s)
static void
e1000_send_packet(E1000State *s, const uint8_t *buf, int size)
{
+ NetClientState *nc = qemu_get_queue(s->nic);
if (s->phy_reg[PHY_CTRL] & MII_CR_LOOPBACK) {
- s->nic->nc.info->receive(&s->nic->nc, buf, size);
+ nc->info->receive(nc, buf, size);
} else {
- qemu_send_packet(&s->nic->nc, buf, size);
+ qemu_send_packet(nc, buf, size);
}
}
@@ -752,7 +748,7 @@ receive_filter(E1000State *s, const uint8_t *buf, int size)
static void
e1000_set_link_status(NetClientState *nc)
{
- E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ E1000State *s = qemu_get_nic_opaque(nc);
uint32_t old_status = s->mac_reg[STATUS];
if (nc->link_down) {
@@ -786,7 +782,7 @@ static bool e1000_has_rxbufs(E1000State *s, size_t total_size)
static int
e1000_can_receive(NetClientState *nc)
{
- E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ E1000State *s = qemu_get_nic_opaque(nc);
return (s->mac_reg[RCTL] & E1000_RCTL_EN) && e1000_has_rxbufs(s, 1);
}
@@ -802,7 +798,7 @@ static uint64_t rx_desc_base(E1000State *s)
static ssize_t
e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
- E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ E1000State *s = qemu_get_nic_opaque(nc);
struct e1000_rx_desc desc;
dma_addr_t base;
unsigned int n, rdt;
@@ -963,7 +959,7 @@ set_rdt(E1000State *s, int index, uint32_t val)
{
s->mac_reg[index] = val & 0xffff;
if (e1000_has_rxbufs(s, 1)) {
- qemu_flush_queued_packets(&s->nic->nc);
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
}
}
@@ -1117,10 +1113,11 @@ static bool is_version_1(void *opaque, int version_id)
static int e1000_post_load(void *opaque, int version_id)
{
E1000State *s = opaque;
+ NetClientState *nc = qemu_get_queue(s->nic);
/* nc.link_down can't be migrated, so infer link_down according
* to link status bit in mac_reg[STATUS] */
- s->nic->nc.link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0;
+ nc->link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0;
return 0;
}
@@ -1238,7 +1235,7 @@ e1000_mmio_setup(E1000State *d)
static void
e1000_cleanup(NetClientState *nc)
{
- E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ E1000State *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
@@ -1252,7 +1249,7 @@ pci_e1000_uninit(PCIDevice *dev)
qemu_free_timer(d->autoneg_timer);
memory_region_destroy(&d->mmio);
memory_region_destroy(&d->io);
- qemu_del_net_client(&d->nic->nc);
+ qemu_del_nic(d->nic);
}
static NetClientInfo net_e1000_info = {
@@ -1299,7 +1296,7 @@ static int pci_e1000_init(PCIDevice *pci_dev)
d->nic = qemu_new_nic(&net_e1000_info, &d->conf,
object_get_typename(OBJECT(d)), d->dev.qdev.id, d);
- qemu_format_nic_info_str(&d->nic->nc, macaddr);
+ qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr);
add_boot_device_path(d->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
diff --git a/hw/eepro100.c b/hw/eepro100.c
index 6bbefb505f..5d237968e7 100644
--- a/hw/eepro100.c
+++ b/hw/eepro100.c
@@ -828,7 +828,7 @@ static void tx_command(EEPRO100State *s)
}
}
TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size)));
- qemu_send_packet(&s->nic->nc, buf, size);
+ qemu_send_packet(qemu_get_queue(s->nic), buf, size);
s->statistics.tx_good_frames++;
/* Transmit with bad status would raise an CX/TNO interrupt.
* (82557 only). Emulation never has bad status. */
@@ -1036,7 +1036,7 @@ static void eepro100_ru_command(EEPRO100State * s, uint8_t val)
}
set_ru_state(s, ru_ready);
s->ru_offset = e100_read_reg4(s, SCBPointer);
- qemu_flush_queued_packets(&s->nic->nc);
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
TRACE(OTHER, logout("val=0x%02x (rx start)\n", val));
break;
case RX_RESUME:
@@ -1619,7 +1619,7 @@ static const MemoryRegionOps eepro100_ops = {
static int nic_can_receive(NetClientState *nc)
{
- EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ EEPRO100State *s = qemu_get_nic_opaque(nc);
TRACE(RXTX, logout("%p\n", s));
return get_ru_state(s) == ru_ready;
#if 0
@@ -1633,7 +1633,7 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
* - Magic packets should set bit 30 in power management driver register.
* - Interesting packets should set bit 29 in power management driver register.
*/
- EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ EEPRO100State *s = qemu_get_nic_opaque(nc);
uint16_t rfd_status = 0xa000;
#if defined(CONFIG_PAD_RECEIVED_FRAMES)
uint8_t min_buf[60];
@@ -1835,7 +1835,7 @@ static const VMStateDescription vmstate_eepro100 = {
static void nic_cleanup(NetClientState *nc)
{
- EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ EEPRO100State *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
@@ -1849,7 +1849,7 @@ static void pci_nic_uninit(PCIDevice *pci_dev)
memory_region_destroy(&s->flash_bar);
vmstate_unregister(&pci_dev->qdev, s->vmstate, s);
eeprom93xx_free(&pci_dev->qdev, s->eeprom);
- qemu_del_net_client(&s->nic->nc);
+ qemu_del_nic(s->nic);
}
static NetClientInfo net_eepro100_info = {
@@ -1895,14 +1895,14 @@ static int e100_nic_init(PCIDevice *pci_dev)
s->nic = qemu_new_nic(&net_eepro100_info, &s->conf,
object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
- TRACE(OTHER, logout("%s\n", s->nic->nc.info_str));
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+ TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str));
qemu_register_reset(nic_reset, s);
s->vmstate = g_malloc(sizeof(vmstate_eepro100));
memcpy(s->vmstate, &vmstate_eepro100, sizeof(vmstate_eepro100));
- s->vmstate->name = s->nic->nc.model;
+ s->vmstate->name = qemu_get_queue(s->nic)->model;
vmstate_register(&pci_dev->qdev, -1, s->vmstate, s);
add_boot_device_path(s->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c
index 0b474c0843..ad36411193 100644
--- a/hw/etraxfs_eth.c
+++ b/hw/etraxfs_eth.c
@@ -523,7 +523,7 @@ static int eth_can_receive(NetClientState *nc)
static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
- struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct fs_eth *eth = qemu_get_nic_opaque(nc);
int use_ma0 = eth->regs[RW_REC_CTRL] & 1;
int use_ma1 = eth->regs[RW_REC_CTRL] & 2;
int r_bcast = eth->regs[RW_REC_CTRL] & 8;
@@ -555,13 +555,13 @@ static int eth_tx_push(void *opaque, unsigned char *buf, int len, bool eop)
struct fs_eth *eth = opaque;
D(printf("%s buf=%p len=%d\n", __func__, buf, len));
- qemu_send_packet(&eth->nic->nc, buf, len);
+ qemu_send_packet(qemu_get_queue(eth->nic), buf, len);
return len;
}
static void eth_set_link(NetClientState *nc)
{
- struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct fs_eth *eth = qemu_get_nic_opaque(nc);
D(printf("%s %d\n", __func__, nc->link_down));
eth->phy.link = !nc->link_down;
}
@@ -578,7 +578,7 @@ static const MemoryRegionOps eth_ops = {
static void eth_cleanup(NetClientState *nc)
{
- struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct fs_eth *eth = qemu_get_nic_opaque(nc);
/* Disconnect the client. */
eth->dma_out->client.push = NULL;
@@ -616,7 +616,8 @@ static int fs_eth_init(SysBusDevice *dev)
qemu_macaddr_default_if_unset(&s->conf.macaddr);
s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf,
object_get_typename(OBJECT(s)), dev->qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+
tdk_init(&s->phy);
mdio_attach(&s->mdio_bus, &s->phy, s->phyaddr);
diff --git a/hw/i2c.c b/hw/i2c.c
index 119e96bc0e..ec314a40d1 100644
--- a/hw/i2c.c
+++ b/hw/i2c.c
@@ -92,7 +92,7 @@ int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv)
QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
DeviceState *qdev = kid->child;
- I2CSlave *candidate = I2C_SLAVE_FROM_QDEV(qdev);
+ I2CSlave *candidate = I2C_SLAVE(qdev);
if (candidate->address == address) {
slave = candidate;
break;
@@ -204,7 +204,7 @@ const VMStateDescription vmstate_i2c_slave = {
static int i2c_slave_qdev_init(DeviceState *dev)
{
- I2CSlave *s = I2C_SLAVE_FROM_QDEV(dev);
+ I2CSlave *s = I2C_SLAVE(dev);
I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(s);
return sc->init(s);
diff --git a/hw/i2c.h b/hw/i2c.h
index 883b5c588d..0e80d5a9cb 100644
--- a/hw/i2c.h
+++ b/hw/i2c.h
@@ -59,7 +59,6 @@ void i2c_nack(i2c_bus *bus);
int i2c_send(i2c_bus *bus, uint8_t data);
int i2c_recv(i2c_bus *bus);
-#define I2C_SLAVE_FROM_QDEV(dev) DO_UPCAST(I2CSlave, qdev, dev)
#define FROM_I2C_SLAVE(type, dev) DO_UPCAST(type, i2c, dev)
DeviceState *i2c_create_slave(i2c_bus *bus, const char *name, uint8_t addr);
diff --git a/hw/isa.h b/hw/isa.h
index 62e89d3bcd..7a8874abfd 100644
--- a/hw/isa.h
+++ b/hw/isa.h
@@ -82,7 +82,7 @@ void isa_register_portio_list(ISADevice *dev, uint16_t start,
static inline ISABus *isa_bus_from_device(ISADevice *d)
{
- return DO_UPCAST(ISABus, qbus, d->qdev.parent_bus);
+ return ISA_BUS(qdev_get_parent_bus(DEVICE(d)));
}
extern hwaddr isa_mem_base;
diff --git a/hw/lan9118.c b/hw/lan9118.c
index 6596979d8b..0e844e535c 100644
--- a/hw/lan9118.c
+++ b/hw/lan9118.c
@@ -341,7 +341,7 @@ static void lan9118_update(lan9118_state *s)
static void lan9118_mac_changed(lan9118_state *s)
{
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
}
static void lan9118_reload_eeprom(lan9118_state *s)
@@ -373,7 +373,7 @@ static void phy_update_irq(lan9118_state *s)
static void phy_update_link(lan9118_state *s)
{
/* Autonegotiation status mirrors link status. */
- if (s->nic->nc.link_down) {
+ if (qemu_get_queue(s->nic)->link_down) {
s->phy_status &= ~0x0024;
s->phy_int |= PHY_INT_DOWN;
} else {
@@ -386,7 +386,7 @@ static void phy_update_link(lan9118_state *s)
static void lan9118_set_link(NetClientState *nc)
{
- phy_update_link(DO_UPCAST(NICState, nc, nc)->opaque);
+ phy_update_link(qemu_get_nic_opaque(nc));
}
static void phy_reset(lan9118_state *s)
@@ -512,7 +512,7 @@ static int lan9118_filter(lan9118_state *s, const uint8_t *addr)
static ssize_t lan9118_receive(NetClientState *nc, const uint8_t *buf,
size_t size)
{
- lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ lan9118_state *s = qemu_get_nic_opaque(nc);
int fifo_len;
int offset;
int src_pos;
@@ -657,9 +657,9 @@ static void do_tx_packet(lan9118_state *s)
/* FIXME: Honor TX disable, and allow queueing of packets. */
if (s->phy_control & 0x4000) {
/* This assumes the receive routine doesn't touch the VLANClient. */
- lan9118_receive(&s->nic->nc, s->txp->data, s->txp->len);
+ lan9118_receive(qemu_get_queue(s->nic), s->txp->data, s->txp->len);
} else {
- qemu_send_packet(&s->nic->nc, s->txp->data, s->txp->len);
+ qemu_send_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len);
}
s->txp->fifo_used = 0;
@@ -1306,7 +1306,7 @@ static const MemoryRegionOps lan9118_16bit_mem_ops = {
static void lan9118_cleanup(NetClientState *nc)
{
- lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ lan9118_state *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
@@ -1335,7 +1335,7 @@ static int lan9118_init1(SysBusDevice *dev)
s->nic = qemu_new_nic(&net_lan9118_info, &s->conf,
object_get_typename(OBJECT(dev)), dev->qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
s->eeprom[0] = 0xa5;
for (i = 0; i < 6; i++) {
s->eeprom[i + 1] = s->conf.macaddr.a[i];
diff --git a/hw/lance.c b/hw/lance.c
index a5997fd64e..4b92425299 100644
--- a/hw/lance.c
+++ b/hw/lance.c
@@ -87,7 +87,7 @@ static const MemoryRegionOps lance_mem_ops = {
static void lance_cleanup(NetClientState *nc)
{
- PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque;
+ PCNetState *d = qemu_get_nic_opaque(nc);
pcnet_common_cleanup(d);
}
diff --git a/hw/lm832x.c b/hw/lm832x.c
index af49dd68bf..94b8ae06d8 100644
--- a/hw/lm832x.c
+++ b/hw/lm832x.c
@@ -476,7 +476,7 @@ static int lm8323_init(I2CSlave *i2c)
void lm832x_key_event(DeviceState *dev, int key, int state)
{
- LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, I2C_SLAVE_FROM_QDEV(dev));
+ LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, I2C_SLAVE(dev));
if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR))
return;
diff --git a/hw/max7310.c b/hw/max7310.c
index de2221ba01..c2df0b49eb 100644
--- a/hw/max7310.c
+++ b/hw/max7310.c
@@ -25,7 +25,7 @@ typedef struct {
static void max7310_reset(DeviceState *dev)
{
- MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, I2C_SLAVE_FROM_QDEV(dev));
+ MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, I2C_SLAVE(dev));
s->level &= s->direction;
s->direction = 0xff;
s->polarity = 0xf0;
diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c
index 2423f64bf6..8e60f09fbb 100644
--- a/hw/mcf_fec.c
+++ b/hw/mcf_fec.c
@@ -174,7 +174,7 @@ static void mcf_fec_do_tx(mcf_fec_state *s)
if (bd.flags & FEC_BD_L) {
/* Last buffer in frame. */
DPRINTF("Sending packet\n");
- qemu_send_packet(&s->nic->nc, frame, len);
+ qemu_send_packet(qemu_get_queue(s->nic), frame, len);
ptr = frame;
frame_size = 0;
s->eir |= FEC_INT_TXF;
@@ -353,13 +353,13 @@ static void mcf_fec_write(void *opaque, hwaddr addr,
static int mcf_fec_can_receive(NetClientState *nc)
{
- mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ mcf_fec_state *s = qemu_get_nic_opaque(nc);
return s->rx_enabled;
}
static ssize_t mcf_fec_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
- mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ mcf_fec_state *s = qemu_get_nic_opaque(nc);
mcf_fec_bd bd;
uint32_t flags = 0;
uint32_t addr;
@@ -441,7 +441,7 @@ static const MemoryRegionOps mcf_fec_ops = {
static void mcf_fec_cleanup(NetClientState *nc)
{
- mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ mcf_fec_state *s = qemu_get_nic_opaque(nc);
memory_region_del_subregion(s->sysmem, &s->iomem);
memory_region_destroy(&s->iomem);
@@ -472,9 +472,9 @@ void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd,
memory_region_add_subregion(sysmem, base, &s->iomem);
s->conf.macaddr = nd->macaddr;
- s->conf.peer = nd->netdev;
+ s->conf.peers.ncs[0] = nd->netdev;
s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf, nd->model, nd->name, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
}
diff --git a/hw/milkymist-minimac2.c b/hw/milkymist-minimac2.c
index 43d6c195eb..9992dcceaf 100644
--- a/hw/milkymist-minimac2.c
+++ b/hw/milkymist-minimac2.c
@@ -257,7 +257,7 @@ static void minimac2_tx(MilkymistMinimac2State *s)
trace_milkymist_minimac2_tx_frame(txcount - 12);
/* send packet, skipping preamble and sfd */
- qemu_send_packet_raw(&s->nic->nc, buf + 8, txcount - 12);
+ qemu_send_packet_raw(qemu_get_queue(s->nic), buf + 8, txcount - 12);
s->regs[R_TXCOUNT] = 0;
@@ -280,7 +280,7 @@ static void update_rx_interrupt(MilkymistMinimac2State *s)
static ssize_t minimac2_rx(NetClientState *nc, const uint8_t *buf, size_t size)
{
- MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ MilkymistMinimac2State *s = qemu_get_nic_opaque(nc);
uint32_t r_count;
uint32_t r_state;
@@ -410,7 +410,7 @@ static const MemoryRegionOps minimac2_ops = {
static int minimac2_can_rx(NetClientState *nc)
{
- MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ MilkymistMinimac2State *s = qemu_get_nic_opaque(nc);
if (s->regs[R_STATE0] == STATE_LOADED) {
return 1;
@@ -424,7 +424,7 @@ static int minimac2_can_rx(NetClientState *nc)
static void minimac2_cleanup(NetClientState *nc)
{
- MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ MilkymistMinimac2State *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
@@ -480,7 +480,7 @@ static int milkymist_minimac2_init(SysBusDevice *dev)
qemu_macaddr_default_if_unset(&s->conf.macaddr);
s->nic = qemu_new_nic(&net_milkymist_minimac2_info, &s->conf,
object_get_typename(OBJECT(dev)), dev->qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
return 0;
}
diff --git a/hw/mipsnet.c b/hw/mipsnet.c
index feac8159ee..ff6bf7fdcb 100644
--- a/hw/mipsnet.c
+++ b/hw/mipsnet.c
@@ -64,7 +64,7 @@ static int mipsnet_buffer_full(MIPSnetState *s)
static int mipsnet_can_receive(NetClientState *nc)
{
- MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ MIPSnetState *s = qemu_get_nic_opaque(nc);
if (s->busy)
return 0;
@@ -73,7 +73,7 @@ static int mipsnet_can_receive(NetClientState *nc)
static ssize_t mipsnet_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
- MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ MIPSnetState *s = qemu_get_nic_opaque(nc);
trace_mipsnet_receive(size);
if (!mipsnet_can_receive(nc))
@@ -173,7 +173,7 @@ static void mipsnet_ioport_write(void *opaque, hwaddr addr,
if (s->tx_written == s->tx_count) {
/* Send buffer. */
trace_mipsnet_send(s->tx_count);
- qemu_send_packet(&s->nic->nc, s->tx_buffer, s->tx_count);
+ qemu_send_packet(qemu_get_queue(s->nic), s->tx_buffer, s->tx_count);
s->tx_count = s->tx_written = 0;
s->intctl |= MIPSNET_INTCTL_TXDONE;
s->busy = 1;
@@ -211,7 +211,7 @@ static const VMStateDescription vmstate_mipsnet = {
static void mipsnet_cleanup(NetClientState *nc)
{
- MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ MIPSnetState *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
@@ -241,7 +241,7 @@ static int mipsnet_sysbus_init(SysBusDevice *dev)
s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf,
object_get_typename(OBJECT(dev)), dev->qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
return 0;
}
diff --git a/hw/musicpal.c b/hw/musicpal.c
index 7ac0a918fb..272cb80303 100644
--- a/hw/musicpal.c
+++ b/hw/musicpal.c
@@ -190,7 +190,7 @@ static int eth_can_receive(NetClientState *nc)
static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
- mv88w8618_eth_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
uint32_t desc_addr;
mv88w8618_rx_desc desc;
int i;
@@ -257,7 +257,7 @@ static void eth_send(mv88w8618_eth_state *s, int queue_index)
len = desc.bytes;
if (len < 2048) {
cpu_physical_memory_read(desc.buffer, buf, len);
- qemu_send_packet(&s->nic->nc, buf, len);
+ qemu_send_packet(qemu_get_queue(s->nic), buf, len);
}
desc.cmdstat &= ~MP_ETH_TX_OWN;
s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index);
@@ -369,7 +369,7 @@ static const MemoryRegionOps mv88w8618_eth_ops = {
static void eth_cleanup(NetClientState *nc)
{
- mv88w8618_eth_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
diff --git a/hw/ne2000-isa.c b/hw/ne2000-isa.c
index 7c11229f1a..342c6bdad1 100644
--- a/hw/ne2000-isa.c
+++ b/hw/ne2000-isa.c
@@ -38,7 +38,7 @@ typedef struct ISANE2000State {
static void isa_ne2000_cleanup(NetClientState *nc)
{
- NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ NE2000State *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
@@ -77,7 +77,7 @@ static int isa_ne2000_initfn(ISADevice *dev)
s->nic = qemu_new_nic(&net_ne2000_isa_info, &s->c,
object_get_typename(OBJECT(dev)), dev->qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a);
return 0;
}
diff --git a/hw/ne2000.c b/hw/ne2000.c
index 872115c454..3dd1c844e8 100644
--- a/hw/ne2000.c
+++ b/hw/ne2000.c
@@ -167,7 +167,7 @@ static int ne2000_buffer_full(NE2000State *s)
int ne2000_can_receive(NetClientState *nc)
{
- NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ NE2000State *s = qemu_get_nic_opaque(nc);
if (s->cmd & E8390_STOP)
return 1;
@@ -178,7 +178,7 @@ int ne2000_can_receive(NetClientState *nc)
ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
{
- NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ NE2000State *s = qemu_get_nic_opaque(nc);
int size = size_;
uint8_t *p;
unsigned int total_len, next, avail, len, index, mcast_idx;
@@ -300,7 +300,8 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val)
index -= NE2000_PMEM_SIZE;
/* fail safe: check range on the transmitted length */
if (index + s->tcnt <= NE2000_PMEM_END) {
- qemu_send_packet(&s->nic->nc, s->mem + index, s->tcnt);
+ qemu_send_packet(qemu_get_queue(s->nic), s->mem + index,
+ s->tcnt);
}
/* signal end of transfer */
s->tsr = ENTSR_PTX;
@@ -705,7 +706,7 @@ void ne2000_setup_io(NE2000State *s, unsigned size)
static void ne2000_cleanup(NetClientState *nc)
{
- NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ NE2000State *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
@@ -737,7 +738,7 @@ static int pci_ne2000_init(PCIDevice *pci_dev)
s->nic = qemu_new_nic(&net_ne2000_info, &s->c,
object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a);
add_boot_device_path(s->c.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
@@ -750,7 +751,7 @@ static void pci_ne2000_exit(PCIDevice *pci_dev)
NE2000State *s = &d->ne2000;
memory_region_destroy(&s->io);
- qemu_del_net_client(&s->nic->nc);
+ qemu_del_nic(s->nic);
}
static Property ne2000_properties[] = {
diff --git a/hw/opencores_eth.c b/hw/opencores_eth.c
index 746a959f6b..f9ba5eeaba 100644
--- a/hw/opencores_eth.c
+++ b/hw/opencores_eth.c
@@ -313,7 +313,7 @@ static void open_eth_int_source_write(OpenEthState *s,
static void open_eth_set_link_status(NetClientState *nc)
{
- OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ OpenEthState *s = qemu_get_nic_opaque(nc);
if (GET_REGBIT(s, MIICOMMAND, SCANSTAT)) {
SET_REGFIELD(s, MIISTATUS, LINKFAIL, nc->link_down);
@@ -339,12 +339,12 @@ static void open_eth_reset(void *opaque)
s->rx_desc = 0x40;
mii_reset(&s->mii);
- open_eth_set_link_status(&s->nic->nc);
+ open_eth_set_link_status(qemu_get_queue(s->nic));
}
static int open_eth_can_receive(NetClientState *nc)
{
- OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ OpenEthState *s = qemu_get_nic_opaque(nc);
return GET_REGBIT(s, MODER, RXEN) &&
(s->regs[TX_BD_NUM] < 0x80) &&
@@ -354,7 +354,7 @@ static int open_eth_can_receive(NetClientState *nc)
static ssize_t open_eth_receive(NetClientState *nc,
const uint8_t *buf, size_t size)
{
- OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ OpenEthState *s = qemu_get_nic_opaque(nc);
size_t maxfl = GET_REGFIELD(s, PACKETLEN, MAXFL);
size_t minfl = GET_REGFIELD(s, PACKETLEN, MINFL);
size_t fcsl = 4;
@@ -499,7 +499,7 @@ static void open_eth_start_xmit(OpenEthState *s, desc *tx)
if (tx_len > len) {
memset(buf + len, 0, tx_len - len);
}
- qemu_send_packet(&s->nic->nc, buf, tx_len);
+ qemu_send_packet(qemu_get_queue(s->nic), buf, tx_len);
if (tx->len_flags & TXD_WR) {
s->tx_desc = 0;
@@ -606,7 +606,7 @@ static void open_eth_mii_command_host_write(OpenEthState *s, uint32_t val)
} else {
s->regs[MIIRX_DATA] = 0xffff;
}
- SET_REGFIELD(s, MIISTATUS, LINKFAIL, s->nic->nc.link_down);
+ SET_REGFIELD(s, MIISTATUS, LINKFAIL, qemu_get_queue(s->nic)->link_down);
}
}
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index ba09714d6c..0af436cfaf 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -313,6 +313,10 @@ static QEMUMachine pc_i440fx_machine_v1_4 = {
.driver = "virtio-net-pci",\
.property = "ctrl_mac_addr",\
.value = "off", \
+ },{ \
+ .driver = "virtio-net-pci", \
+ .property = "mq", \
+ .value = "off", \
}
static QEMUMachine pc_machine_v1_3 = {
diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c
index a94f642136..df63b22463 100644
--- a/hw/pcnet-pci.c
+++ b/hw/pcnet-pci.c
@@ -266,7 +266,7 @@ static void pci_physical_memory_read(void *dma_opaque, hwaddr addr,
static void pci_pcnet_cleanup(NetClientState *nc)
{
- PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque;
+ PCNetState *d = qemu_get_nic_opaque(nc);
pcnet_common_cleanup(d);
}
@@ -279,7 +279,7 @@ static void pci_pcnet_uninit(PCIDevice *dev)
memory_region_destroy(&d->io_bar);
qemu_del_timer(d->state.poll_timer);
qemu_free_timer(d->state.poll_timer);
- qemu_del_net_client(&d->state.nic->nc);
+ qemu_del_nic(d->state.nic);
}
static NetClientInfo net_pci_pcnet_info = {
diff --git a/hw/pcnet.c b/hw/pcnet.c
index 30f100007a..e0de1e3458 100644
--- a/hw/pcnet.c
+++ b/hw/pcnet.c
@@ -1006,7 +1006,7 @@ static int pcnet_tdte_poll(PCNetState *s)
int pcnet_can_receive(NetClientState *nc)
{
- PCNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ PCNetState *s = qemu_get_nic_opaque(nc);
if (CSR_STOP(s) || CSR_SPND(s))
return 0;
@@ -1017,7 +1017,7 @@ int pcnet_can_receive(NetClientState *nc)
ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
{
- PCNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ PCNetState *s = qemu_get_nic_opaque(nc);
int is_padr = 0, is_bcast = 0, is_ladr = 0;
uint8_t buf1[60];
int remaining;
@@ -1199,7 +1199,7 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
void pcnet_set_link_status(NetClientState *nc)
{
- PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque;
+ PCNetState *d = qemu_get_nic_opaque(nc);
d->lnkst = nc->link_down ? 0 : 0x40;
}
@@ -1261,11 +1261,12 @@ static void pcnet_transmit(PCNetState *s)
if (BCR_SWSTYLE(s) == 1)
add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS);
s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC;
- pcnet_receive(&s->nic->nc, s->buffer, s->xmit_pos);
+ pcnet_receive(qemu_get_queue(s->nic), s->buffer, s->xmit_pos);
s->looptest = 0;
} else
if (s->nic)
- qemu_send_packet(&s->nic->nc, s->buffer, s->xmit_pos);
+ qemu_send_packet(qemu_get_queue(s->nic), s->buffer,
+ s->xmit_pos);
s->csr[0] &= ~0x0008; /* clear TDMD */
s->csr[4] |= 0x0004; /* set TXSTRT */
@@ -1730,7 +1731,7 @@ int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info)
qemu_macaddr_default_if_unset(&s->conf.macaddr);
s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0");
diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c
index 2367c6a4a4..373d0616ba 100644
--- a/hw/pxa2xx.c
+++ b/hw/pxa2xx.c
@@ -1468,7 +1468,7 @@ PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base,
s = FROM_SYSBUS(PXA2xxI2CState, i2c_dev);
/* FIXME: Should the slave device really be on a separate bus? */
dev = i2c_create_slave(i2c_init_bus(NULL, "dummy"), "pxa2xx-i2c-slave", 0);
- s->slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, I2C_SLAVE_FROM_QDEV(dev));
+ s->slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, I2C_SLAVE(dev));
s->slave->host = s;
return s;
diff --git a/hw/qdev-properties-system.c b/hw/qdev-properties-system.c
index ce0f7933e6..ce3af22193 100644
--- a/hw/qdev-properties-system.c
+++ b/hw/qdev-properties-system.c
@@ -173,16 +173,47 @@ PropertyInfo qdev_prop_chr = {
static int parse_netdev(DeviceState *dev, const char *str, void **ptr)
{
- NetClientState *netdev = qemu_find_netdev(str);
+ NICPeers *peers_ptr = (NICPeers *)ptr;
+ NICConf *conf = container_of(peers_ptr, NICConf, peers);
+ NetClientState **ncs = peers_ptr->ncs;
+ NetClientState *peers[MAX_QUEUE_NUM];
+ int queues, i = 0;
+ int ret;
- if (netdev == NULL) {
- return -ENOENT;
+ queues = qemu_find_net_clients_except(str, peers,
+ NET_CLIENT_OPTIONS_KIND_NIC,
+ MAX_QUEUE_NUM);
+ if (queues == 0) {
+ ret = -ENOENT;
+ goto err;
}
- if (netdev->peer) {
- return -EEXIST;
+
+ if (queues > MAX_QUEUE_NUM) {
+ ret = -E2BIG;
+ goto err;
+ }
+
+ for (i = 0; i < queues; i++) {
+ if (peers[i] == NULL) {
+ ret = -ENOENT;
+ goto err;
+ }
+
+ if (peers[i]->peer) {
+ ret = -EEXIST;
+ goto err;
+ }
+
+ ncs[i] = peers[i];
+ ncs[i]->queue_index = i;
}
- *ptr = netdev;
+
+ conf->queues = queues;
+
return 0;
+
+err:
+ return ret;
}
static const char *print_netdev(void *ptr)
@@ -249,7 +280,8 @@ static void set_vlan(Object *obj, Visitor *v, void *opaque,
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
- NetClientState **ptr = qdev_get_prop_ptr(dev, prop);
+ NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
+ NetClientState **ptr = &peers_ptr->ncs[0];
Error *local_err = NULL;
int32_t id;
NetClientState *hubport;
diff --git a/hw/qdev-properties.h b/hw/qdev-properties.h
index ddcf774506..20c67f3443 100644
--- a/hw/qdev-properties.h
+++ b/hw/qdev-properties.h
@@ -31,7 +31,7 @@ extern PropertyInfo qdev_prop_pci_host_devaddr;
.name = (_name), \
.info = &(_prop), \
.offset = offsetof(_state, _field) \
- + type_check(_type,typeof_field(_state, _field)), \
+ + type_check(_type, typeof_field(_state, _field)), \
}
#define DEFINE_PROP_DEFAULT(_name, _state, _field, _defval, _prop, _type) { \
.name = (_name), \
@@ -77,9 +77,9 @@ extern PropertyInfo qdev_prop_pci_host_devaddr;
#define DEFINE_PROP_STRING(_n, _s, _f) \
DEFINE_PROP(_n, _s, _f, qdev_prop_string, char*)
#define DEFINE_PROP_NETDEV(_n, _s, _f) \
- DEFINE_PROP(_n, _s, _f, qdev_prop_netdev, NetClientState*)
+ DEFINE_PROP(_n, _s, _f, qdev_prop_netdev, NICPeers)
#define DEFINE_PROP_VLAN(_n, _s, _f) \
- DEFINE_PROP(_n, _s, _f, qdev_prop_vlan, NetClientState*)
+ DEFINE_PROP(_n, _s, _f, qdev_prop_vlan, NICPeers)
#define DEFINE_PROP_DRIVE(_n, _s, _f) \
DEFINE_PROP(_n, _s, _f, qdev_prop_drive, BlockDriverState *)
#define DEFINE_PROP_MACADDR(_n, _s, _f) \
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index cfbf3f47c1..d7716beb9e 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -786,7 +786,7 @@ static bool rtl8139_cp_rx_valid(RTL8139State *s)
static int rtl8139_can_receive(NetClientState *nc)
{
- RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ RTL8139State *s = qemu_get_nic_opaque(nc);
int avail;
/* Receive (drop) packets if card is disabled. */
@@ -808,7 +808,7 @@ static int rtl8139_can_receive(NetClientState *nc)
static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt)
{
- RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ RTL8139State *s = qemu_get_nic_opaque(nc);
/* size is the length of the buffer passed to the driver */
int size = size_;
const uint8_t *dot1q_buf = NULL;
@@ -1259,7 +1259,7 @@ static void rtl8139_reset(DeviceState *d)
//s->BasicModeStatus |= 0x0040; /* UTP medium */
s->BasicModeStatus |= 0x0020; /* autonegotiation completed */
/* preserve link state */
- s->BasicModeStatus |= s->nic->nc.link_down ? 0 : 0x04;
+ s->BasicModeStatus |= qemu_get_queue(s->nic)->link_down ? 0 : 0x04;
s->NWayAdvert = 0x05e1; /* all modes, full duplex */
s->NWayLPAR = 0x05e1; /* all modes, full duplex */
@@ -1787,7 +1787,7 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size,
}
DPRINTF("+++ transmit loopback mode\n");
- rtl8139_do_receive(&s->nic->nc, buf, size, do_interrupt);
+ rtl8139_do_receive(qemu_get_queue(s->nic), buf, size, do_interrupt);
if (iov) {
g_free(buf2);
@@ -1796,9 +1796,9 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size,
else
{
if (iov) {
- qemu_sendv_packet(&s->nic->nc, iov, 3);
+ qemu_sendv_packet(qemu_get_queue(s->nic), iov, 3);
} else {
- qemu_send_packet(&s->nic->nc, buf, size);
+ qemu_send_packet(qemu_get_queue(s->nic), buf, size);
}
}
}
@@ -3230,7 +3230,7 @@ static int rtl8139_post_load(void *opaque, int version_id)
/* nc.link_down can't be migrated, so infer link_down according
* to link status bit in BasicModeStatus */
- s->nic->nc.link_down = (s->BasicModeStatus & 0x04) == 0;
+ qemu_get_queue(s->nic)->link_down = (s->BasicModeStatus & 0x04) == 0;
return 0;
}
@@ -3429,7 +3429,7 @@ static void rtl8139_timer(void *opaque)
static void rtl8139_cleanup(NetClientState *nc)
{
- RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ RTL8139State *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
@@ -3446,12 +3446,12 @@ static void pci_rtl8139_uninit(PCIDevice *dev)
}
qemu_del_timer(s->timer);
qemu_free_timer(s->timer);
- qemu_del_net_client(&s->nic->nc);
+ qemu_del_nic(s->nic);
}
static void rtl8139_set_link_status(NetClientState *nc)
{
- RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ RTL8139State *s = qemu_get_nic_opaque(nc);
if (nc->link_down) {
s->BasicModeStatus &= ~0x04;
@@ -3503,7 +3503,7 @@ static int pci_rtl8139_init(PCIDevice *dev)
s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf,
object_get_typename(OBJECT(dev)), dev->qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
s->cplus_txbuffer = NULL;
s->cplus_txbuffer_len = 0;
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 86e84153ad..206d552e16 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -58,10 +58,12 @@ typedef struct S390IPLState {
static void s390_ipl_cpu(uint64_t pswaddr)
{
- CPUS390XState *env = &S390_CPU(qemu_get_cpu(0))->env;
+ S390CPU *cpu = S390_CPU(qemu_get_cpu(0));
+ CPUS390XState *env = &cpu->env;
+
env->psw.addr = pswaddr;
env->psw.mask = IPL_PSW_MASK;
- s390_add_running_cpu(env);
+ s390_add_running_cpu(cpu);
}
static int s390_ipl_init(SysBusDevice *dev)
diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c
index 32f63b07ea..d4677814ca 100644
--- a/hw/s390x/s390-virtio-bus.c
+++ b/hw/s390x/s390-virtio-bus.c
@@ -113,12 +113,10 @@ VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size)
static void s390_virtio_irq(S390CPU *cpu, int config_change, uint64_t token)
{
- CPUS390XState *env = &cpu->env;
-
if (kvm_enabled()) {
kvm_s390_virtio_irq(cpu, config_change, token);
} else {
- cpu_inject_ext(env, VIRTIO_EXT_CODE, config_change, token);
+ cpu_inject_ext(cpu, VIRTIO_EXT_CODE, config_change, token);
}
}
diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c
index 2a1d9ac2da..e25c330320 100644
--- a/hw/s390x/s390-virtio.c
+++ b/hw/s390x/s390-virtio.c
@@ -130,8 +130,10 @@ static void s390_virtio_register_hcalls(void)
*/
static unsigned s390_running_cpus;
-void s390_add_running_cpu(CPUS390XState *env)
+void s390_add_running_cpu(S390CPU *cpu)
{
+ CPUS390XState *env = &cpu->env;
+
if (env->halted) {
s390_running_cpus++;
env->halted = 0;
@@ -139,8 +141,10 @@ void s390_add_running_cpu(CPUS390XState *env)
}
}
-unsigned s390_del_running_cpu(CPUS390XState *env)
+unsigned s390_del_running_cpu(S390CPU *cpu)
{
+ CPUS390XState *env = &cpu->env;
+
if (env->halted == 0) {
assert(s390_running_cpus >= 1);
s390_running_cpus--;
diff --git a/hw/smc91c111.c b/hw/smc91c111.c
index fe2389bf25..67fd074d85 100644
--- a/hw/smc91c111.c
+++ b/hw/smc91c111.c
@@ -237,7 +237,7 @@ static void smc91c111_do_tx(smc91c111_state *s)
smc91c111_release_packet(s, packetnum);
else if (s->tx_fifo_done_len < NUM_PACKETS)
s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
- qemu_send_packet(&s->nic->nc, p, len);
+ qemu_send_packet(qemu_get_queue(s->nic), p, len);
}
s->tx_fifo_len = 0;
smc91c111_update(s);
@@ -631,7 +631,7 @@ static uint32_t smc91c111_readl(void *opaque, hwaddr offset)
static int smc91c111_can_receive(NetClientState *nc)
{
- smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ smc91c111_state *s = qemu_get_nic_opaque(nc);
if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
return 1;
@@ -642,7 +642,7 @@ static int smc91c111_can_receive(NetClientState *nc)
static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
- smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ smc91c111_state *s = qemu_get_nic_opaque(nc);
int status;
int packetsize;
uint32_t crc;
@@ -731,7 +731,7 @@ static const MemoryRegionOps smc91c111_mem_ops = {
static void smc91c111_cleanup(NetClientState *nc)
{
- smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ smc91c111_state *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
@@ -754,7 +754,7 @@ static int smc91c111_init1(SysBusDevice *dev)
qemu_macaddr_default_if_unset(&s->conf.macaddr);
s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf,
object_get_typename(OBJECT(dev)), dev->qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
/* ??? Save/restore. */
return 0;
}
diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c
index db34b485aa..6ef29362f5 100644
--- a/hw/spapr_llan.c
+++ b/hw/spapr_llan.c
@@ -85,7 +85,7 @@ typedef struct VIOsPAPRVLANDevice {
static int spapr_vlan_can_receive(NetClientState *nc)
{
- VIOsPAPRVLANDevice *dev = DO_UPCAST(NICState, nc, nc)->opaque;
+ VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc);
return (dev->isopen && dev->rx_bufs > 0);
}
@@ -93,7 +93,7 @@ static int spapr_vlan_can_receive(NetClientState *nc)
static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf,
size_t size)
{
- VIOsPAPRDevice *sdev = DO_UPCAST(NICState, nc, nc)->opaque;
+ VIOsPAPRDevice *sdev = qemu_get_nic_opaque(nc);
VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
vlan_bd_t rxq_bd = vio_ldq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF);
vlan_bd_t bd;
@@ -199,7 +199,7 @@ static int spapr_vlan_init(VIOsPAPRDevice *sdev)
dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf,
object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev);
- qemu_format_nic_info_str(&dev->nic->nc, dev->nicconf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a);
return 0;
}
@@ -462,7 +462,7 @@ static target_ulong h_send_logical_lan(PowerPCCPU *cpu, sPAPREnvironment *spapr,
p += VLAN_BD_LEN(bufs[i]);
}
- qemu_send_packet(&dev->nic->nc, lbuf, total_len);
+ qemu_send_packet(qemu_get_queue(dev->nic), lbuf, total_len);
return H_SUCCESS;
}
diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c
index 5e9053fa26..6c701fb67b 100644
--- a/hw/stellaris_enet.c
+++ b/hw/stellaris_enet.c
@@ -80,7 +80,7 @@ static void stellaris_enet_update(stellaris_enet_state *s)
/* TODO: Implement MAC address filtering. */
static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
- stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ stellaris_enet_state *s = qemu_get_nic_opaque(nc);
int n;
uint8_t *p;
uint32_t crc;
@@ -122,7 +122,7 @@ static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, si
static int stellaris_enet_can_receive(NetClientState *nc)
{
- stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ stellaris_enet_state *s = qemu_get_nic_opaque(nc);
if ((s->rctl & SE_RCTL_RXEN) == 0)
return 1;
@@ -259,7 +259,8 @@ static void stellaris_enet_write(void *opaque, hwaddr offset,
memset(&s->tx_fifo[s->tx_frame_len], 0, 60 - s->tx_frame_len);
s->tx_fifo_len = 60;
}
- qemu_send_packet(&s->nic->nc, s->tx_fifo, s->tx_frame_len);
+ qemu_send_packet(qemu_get_queue(s->nic), s->tx_fifo,
+ s->tx_frame_len);
s->tx_frame_len = -1;
s->ris |= SE_INT_TXEMP;
stellaris_enet_update(s);
@@ -383,7 +384,7 @@ static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id)
static void stellaris_enet_cleanup(NetClientState *nc)
{
- stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ stellaris_enet_state *s = qemu_get_nic_opaque(nc);
unregister_savevm(&s->busdev.qdev, "stellaris_enet", s);
@@ -412,7 +413,7 @@ static int stellaris_enet_init(SysBusDevice *dev)
s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf,
object_get_typename(OBJECT(dev)), dev->qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
stellaris_enet_reset(s);
register_savevm(&s->busdev.qdev, "stellaris_enet", -1, 1,
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index 9dede4c68d..a01a5e793a 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1012,7 +1012,7 @@ static int rndis_keepalive_response(USBNetState *s,
static void usb_net_reset_in_buf(USBNetState *s)
{
s->in_ptr = s->in_len = 0;
- qemu_flush_queued_packets(&s->nic->nc);
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
}
static int rndis_parse(USBNetState *s, uint8_t *data, int length)
@@ -1196,7 +1196,7 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p)
if (!is_rndis(s)) {
if (p->iov.size < 64) {
- qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr);
+ qemu_send_packet(qemu_get_queue(s->nic), s->out_buf, s->out_ptr);
s->out_ptr = 0;
}
return;
@@ -1209,7 +1209,7 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p)
uint32_t offs = 8 + le32_to_cpu(msg->DataOffset);
uint32_t size = le32_to_cpu(msg->DataLength);
if (offs + size <= len)
- qemu_send_packet(&s->nic->nc, s->out_buf + offs, size);
+ qemu_send_packet(qemu_get_queue(s->nic), s->out_buf + offs, size);
}
s->out_ptr -= len;
memmove(s->out_buf, &s->out_buf[len], s->out_ptr);
@@ -1261,7 +1261,7 @@ static void usb_net_handle_data(USBDevice *dev, USBPacket *p)
static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
- USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ USBNetState *s = qemu_get_nic_opaque(nc);
uint8_t *in_buf = s->in_buf;
size_t total_size = size;
@@ -1308,7 +1308,7 @@ static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t siz
static int usbnet_can_receive(NetClientState *nc)
{
- USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ USBNetState *s = qemu_get_nic_opaque(nc);
if (is_rndis(s) && s->rndis_state != RNDIS_DATA_INITIALIZED) {
return 1;
@@ -1319,7 +1319,7 @@ static int usbnet_can_receive(NetClientState *nc)
static void usbnet_cleanup(NetClientState *nc)
{
- USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ USBNetState *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
@@ -1330,7 +1330,7 @@ static void usb_net_handle_destroy(USBDevice *dev)
/* TODO: remove the nd_table[] entry */
rndis_clear_responsequeue(s);
- qemu_del_net_client(&s->nic->nc);
+ qemu_del_nic(s->nic);
}
static NetClientInfo net_usbnet_info = {
@@ -1361,7 +1361,7 @@ static int usb_net_initfn(USBDevice *dev)
qemu_macaddr_default_if_unset(&s->conf.macaddr);
s->nic = qemu_new_nic(&net_usbnet_info, &s->conf,
object_get_typename(OBJECT(s)), s->dev.qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
snprintf(s->usbstring_mac, sizeof(s->usbstring_mac),
"%02x%02x%02x%02x%02x%02x",
0x40,
diff --git a/hw/vhost.c b/hw/vhost.c
index 0dd2a9aa40..8d41fdb53f 100644
--- a/hw/vhost.c
+++ b/hw/vhost.c
@@ -616,14 +616,17 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
{
hwaddr s, l, a;
int r;
+ int vhost_vq_index = idx - dev->vq_index;
struct vhost_vring_file file = {
- .index = idx,
+ .index = vhost_vq_index
};
struct vhost_vring_state state = {
- .index = idx,
+ .index = vhost_vq_index
};
struct VirtQueue *vvq = virtio_get_queue(vdev, idx);
+ assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
+
vq->num = state.num = virtio_queue_get_num(vdev, idx);
r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
if (r) {
@@ -666,11 +669,12 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
goto fail_alloc_ring;
}
- r = vhost_virtqueue_set_addr(dev, vq, idx, dev->log_enabled);
+ r = vhost_virtqueue_set_addr(dev, vq, vhost_vq_index, dev->log_enabled);
if (r < 0) {
r = -errno;
goto fail_alloc;
}
+
file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq));
r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
if (r) {
@@ -706,9 +710,10 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev,
unsigned idx)
{
struct vhost_vring_state state = {
- .index = idx,
+ .index = idx - dev->vq_index
};
int r;
+ assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
r = ioctl(dev->control, VHOST_GET_VRING_BASE, &state);
if (r < 0) {
fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r);
@@ -864,7 +869,9 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
}
for (i = 0; i < hdev->nvqs; ++i) {
- r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, true);
+ r = vdev->binding->set_host_notifier(vdev->binding_opaque,
+ hdev->vq_index + i,
+ true);
if (r < 0) {
fprintf(stderr, "vhost VQ %d notifier binding failed: %d\n", i, -r);
goto fail_vq;
@@ -874,7 +881,9 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
return 0;
fail_vq:
while (--i >= 0) {
- r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false);
+ r = vdev->binding->set_host_notifier(vdev->binding_opaque,
+ hdev->vq_index + i,
+ false);
if (r < 0) {
fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r);
fflush(stderr);
@@ -895,7 +904,9 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
int i, r;
for (i = 0; i < hdev->nvqs; ++i) {
- r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false);
+ r = vdev->binding->set_host_notifier(vdev->binding_opaque,
+ hdev->vq_index + i,
+ false);
if (r < 0) {
fprintf(stderr, "vhost VQ %d notifier cleanup failed: %d\n", i, -r);
fflush(stderr);
@@ -909,8 +920,9 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
*/
bool vhost_virtqueue_pending(struct vhost_dev *hdev, int n)
{
- struct vhost_virtqueue *vq = hdev->vqs + n;
+ struct vhost_virtqueue *vq = hdev->vqs + n - hdev->vq_index;
assert(hdev->started);
+ assert(n >= hdev->vq_index && n < hdev->vq_index + hdev->nvqs);
return event_notifier_test_and_clear(&vq->masked_notifier);
}
@@ -919,15 +931,16 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n,
bool mask)
{
struct VirtQueue *vvq = virtio_get_queue(vdev, n);
- int r;
+ int r, index = n - hdev->vq_index;
assert(hdev->started);
+ assert(n >= hdev->vq_index && n < hdev->vq_index + hdev->nvqs);
struct vhost_vring_file file = {
- .index = n,
+ .index = index
};
if (mask) {
- file.fd = event_notifier_get_fd(&hdev->vqs[n].masked_notifier);
+ file.fd = event_notifier_get_fd(&hdev->vqs[index].masked_notifier);
} else {
file.fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vvq));
}
@@ -942,20 +955,6 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
hdev->started = true;
- if (!vdev->binding->set_guest_notifiers) {
- fprintf(stderr, "binding does not support guest notifiers\n");
- r = -ENOSYS;
- goto fail;
- }
-
- r = vdev->binding->set_guest_notifiers(vdev->binding_opaque,
- hdev->nvqs,
- true);
- if (r < 0) {
- fprintf(stderr, "Error binding guest notifier: %d\n", -r);
- goto fail_notifiers;
- }
-
r = vhost_dev_set_features(hdev, hdev->log_enabled);
if (r < 0) {
goto fail_features;
@@ -967,9 +966,9 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
}
for (i = 0; i < hdev->nvqs; ++i) {
r = vhost_virtqueue_start(hdev,
- vdev,
- hdev->vqs + i,
- i);
+ vdev,
+ hdev->vqs + i,
+ hdev->vq_index + i);
if (r < 0) {
goto fail_vq;
}
@@ -992,15 +991,13 @@ fail_log:
fail_vq:
while (--i >= 0) {
vhost_virtqueue_stop(hdev,
- vdev,
- hdev->vqs + i,
- i);
+ vdev,
+ hdev->vqs + i,
+ hdev->vq_index + i);
}
+ i = hdev->nvqs;
fail_mem:
fail_features:
- vdev->binding->set_guest_notifiers(vdev->binding_opaque, hdev->nvqs, false);
-fail_notifiers:
-fail:
hdev->started = false;
return r;
@@ -1009,29 +1006,22 @@ fail:
/* Host notifiers must be enabled at this point. */
void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)
{
- int i, r;
+ int i;
for (i = 0; i < hdev->nvqs; ++i) {
vhost_virtqueue_stop(hdev,
- vdev,
- hdev->vqs + i,
- i);
+ vdev,
+ hdev->vqs + i,
+ hdev->vq_index + i);
}
for (i = 0; i < hdev->n_mem_sections; ++i) {
vhost_sync_dirty_bitmap(hdev, &hdev->mem_sections[i],
0, (hwaddr)~0x0ull);
}
- r = vdev->binding->set_guest_notifiers(vdev->binding_opaque,
- hdev->nvqs,
- false);
- if (r < 0) {
- fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r);
- fflush(stderr);
- }
- assert (r >= 0);
hdev->started = false;
g_free(hdev->log);
hdev->log = NULL;
hdev->log_size = 0;
}
+
diff --git a/hw/vhost.h b/hw/vhost.h
index 44c61a5877..f062d48807 100644
--- a/hw/vhost.h
+++ b/hw/vhost.h
@@ -35,6 +35,8 @@ struct vhost_dev {
MemoryRegionSection *mem_sections;
struct vhost_virtqueue *vqs;
int nvqs;
+ /* the first virtuque which would be used by this vhost dev */
+ int vq_index;
unsigned long long features;
unsigned long long acked_features;
unsigned long long backend_features;
diff --git a/hw/vhost_net.c b/hw/vhost_net.c
index d3a04caef6..8693ac27f6 100644
--- a/hw/vhost_net.c
+++ b/hw/vhost_net.c
@@ -140,12 +140,21 @@ bool vhost_net_query(VHostNetState *net, VirtIODevice *dev)
return vhost_dev_query(&net->dev, dev);
}
-int vhost_net_start(struct vhost_net *net,
- VirtIODevice *dev)
+static int vhost_net_start_one(struct vhost_net *net,
+ VirtIODevice *dev,
+ int vq_index)
{
struct vhost_vring_file file = { };
int r;
+ if (net->dev.started) {
+ return 0;
+ }
+
+ net->dev.nvqs = 2;
+ net->dev.vqs = net->vqs;
+ net->dev.vq_index = vq_index;
+
r = vhost_dev_enable_notifiers(&net->dev, dev);
if (r < 0) {
goto fail_notifiers;
@@ -181,11 +190,15 @@ fail_notifiers:
return r;
}
-void vhost_net_stop(struct vhost_net *net,
- VirtIODevice *dev)
+static void vhost_net_stop_one(struct vhost_net *net,
+ VirtIODevice *dev)
{
struct vhost_vring_file file = { .fd = -1 };
+ if (!net->dev.started) {
+ return;
+ }
+
for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
int r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file);
assert(r >= 0);
@@ -195,6 +208,61 @@ void vhost_net_stop(struct vhost_net *net,
vhost_dev_disable_notifiers(&net->dev, dev);
}
+int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
+ int total_queues)
+{
+ int r, i = 0;
+
+ if (!dev->binding->set_guest_notifiers) {
+ error_report("binding does not support guest notifiers\n");
+ r = -ENOSYS;
+ goto err;
+ }
+
+ for (i = 0; i < total_queues; i++) {
+ r = vhost_net_start_one(tap_get_vhost_net(ncs[i].peer), dev, i * 2);
+
+ if (r < 0) {
+ goto err;
+ }
+ }
+
+ r = dev->binding->set_guest_notifiers(dev->binding_opaque,
+ total_queues * 2,
+ true);
+ if (r < 0) {
+ error_report("Error binding guest notifier: %d\n", -r);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ while (--i >= 0) {
+ vhost_net_stop_one(tap_get_vhost_net(ncs[i].peer), dev);
+ }
+ return r;
+}
+
+void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs,
+ int total_queues)
+{
+ int i, r;
+
+ r = dev->binding->set_guest_notifiers(dev->binding_opaque,
+ total_queues * 2,
+ false);
+ if (r < 0) {
+ fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r);
+ fflush(stderr);
+ }
+ assert(r >= 0);
+
+ for (i = 0; i < total_queues; i++) {
+ vhost_net_stop_one(tap_get_vhost_net(ncs[i].peer), dev);
+ }
+}
+
void vhost_net_cleanup(struct vhost_net *net)
{
vhost_dev_cleanup(&net->dev);
@@ -224,13 +292,15 @@ bool vhost_net_query(VHostNetState *net, VirtIODevice *dev)
return false;
}
-int vhost_net_start(struct vhost_net *net,
- VirtIODevice *dev)
+int vhost_net_start(VirtIODevice *dev,
+ NetClientState *ncs,
+ int total_queues)
{
return -ENOSYS;
}
-void vhost_net_stop(struct vhost_net *net,
- VirtIODevice *dev)
+void vhost_net_stop(VirtIODevice *dev,
+ NetClientState *ncs,
+ int total_queues)
{
}
diff --git a/hw/vhost_net.h b/hw/vhost_net.h
index 88912b85fd..2d936bb5f5 100644
--- a/hw/vhost_net.h
+++ b/hw/vhost_net.h
@@ -9,8 +9,8 @@ typedef struct vhost_net VHostNetState;
VHostNetState *vhost_net_init(NetClientState *backend, int devfd, bool force);
bool vhost_net_query(VHostNetState *net, VirtIODevice *dev);
-int vhost_net_start(VHostNetState *net, VirtIODevice *dev);
-void vhost_net_stop(VHostNetState *net, VirtIODevice *dev);
+int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, int total_queues);
+void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, int total_queues);
void vhost_net_cleanup(VHostNetState *net);
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index dfb9687d2f..e37358a40c 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -26,28 +26,33 @@
#define MAC_TABLE_ENTRIES 64
#define MAX_VLAN (1 << 12) /* Per 802.1Q definition */
+typedef struct VirtIONetQueue {
+ VirtQueue *rx_vq;
+ VirtQueue *tx_vq;
+ QEMUTimer *tx_timer;
+ QEMUBH *tx_bh;
+ int tx_waiting;
+ struct {
+ VirtQueueElement elem;
+ ssize_t len;
+ } async_tx;
+ struct VirtIONet *n;
+} VirtIONetQueue;
+
typedef struct VirtIONet
{
VirtIODevice vdev;
uint8_t mac[ETH_ALEN];
uint16_t status;
- VirtQueue *rx_vq;
- VirtQueue *tx_vq;
+ VirtIONetQueue vqs[MAX_QUEUE_NUM];
VirtQueue *ctrl_vq;
NICState *nic;
- QEMUTimer *tx_timer;
- QEMUBH *tx_bh;
uint32_t tx_timeout;
int32_t tx_burst;
- int tx_waiting;
uint32_t has_vnet_hdr;
size_t host_hdr_len;
size_t guest_hdr_len;
uint8_t has_ufo;
- struct {
- VirtQueueElement elem;
- ssize_t len;
- } async_tx;
int mergeable_rx_bufs;
uint8_t promisc;
uint8_t allmulti;
@@ -65,8 +70,23 @@ typedef struct VirtIONet
} mac_table;
uint32_t *vlans;
DeviceState *qdev;
+ int multiqueue;
+ uint16_t max_queues;
+ uint16_t curr_queues;
} VirtIONet;
+static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc)
+{
+ VirtIONet *n = qemu_get_nic_opaque(nc);
+
+ return &n->vqs[nc->queue_index];
+}
+
+static int vq2q(int queue_index)
+{
+ return queue_index / 2;
+}
+
/* TODO
* - we could suppress RX interrupt if we were so inclined.
*/
@@ -82,6 +102,7 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
struct virtio_net_config netcfg;
stw_p(&netcfg.status, n->status);
+ stw_p(&netcfg.max_virtqueue_pairs, n->max_queues);
memcpy(netcfg.mac, n->mac, ETH_ALEN);
memcpy(config, &netcfg, sizeof(netcfg));
}
@@ -96,7 +117,7 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
if (!(n->vdev.guest_features >> VIRTIO_NET_F_CTRL_MAC_ADDR & 1) &&
memcmp(netcfg.mac, n->mac, ETH_ALEN)) {
memcpy(n->mac, netcfg.mac, ETH_ALEN);
- qemu_format_nic_info_str(&n->nic->nc, n->mac);
+ qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
}
}
@@ -108,34 +129,38 @@ static bool virtio_net_started(VirtIONet *n, uint8_t status)
static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
{
- if (!n->nic->nc.peer) {
+ NetClientState *nc = qemu_get_queue(n->nic);
+ int queues = n->multiqueue ? n->max_queues : 1;
+
+ if (!nc->peer) {
return;
}
- if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+ if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
return;
}
- if (!tap_get_vhost_net(n->nic->nc.peer)) {
+ if (!tap_get_vhost_net(nc->peer)) {
return;
}
+
if (!!n->vhost_started == virtio_net_started(n, status) &&
- !n->nic->nc.peer->link_down) {
+ !nc->peer->link_down) {
return;
}
if (!n->vhost_started) {
int r;
- if (!vhost_net_query(tap_get_vhost_net(n->nic->nc.peer), &n->vdev)) {
+ if (!vhost_net_query(tap_get_vhost_net(nc->peer), &n->vdev)) {
return;
}
n->vhost_started = 1;
- r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer), &n->vdev);
+ r = vhost_net_start(&n->vdev, n->nic->ncs, queues);
if (r < 0) {
error_report("unable to start vhost net: %d: "
"falling back on userspace virtio", -r);
n->vhost_started = 0;
}
} else {
- vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), &n->vdev);
+ vhost_net_stop(&n->vdev, n->nic->ncs, queues);
n->vhost_started = 0;
}
}
@@ -143,32 +168,45 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
{
VirtIONet *n = to_virtio_net(vdev);
+ VirtIONetQueue *q;
+ int i;
+ uint8_t queue_status;
virtio_net_vhost_status(n, status);
- if (!n->tx_waiting) {
- return;
- }
+ for (i = 0; i < n->max_queues; i++) {
+ q = &n->vqs[i];
- if (virtio_net_started(n, status) && !n->vhost_started) {
- if (n->tx_timer) {
- qemu_mod_timer(n->tx_timer,
- qemu_get_clock_ns(vm_clock) + n->tx_timeout);
+ if ((!n->multiqueue && i != 0) || i >= n->curr_queues) {
+ queue_status = 0;
} else {
- qemu_bh_schedule(n->tx_bh);
+ queue_status = status;
}
- } else {
- if (n->tx_timer) {
- qemu_del_timer(n->tx_timer);
+
+ if (!q->tx_waiting) {
+ continue;
+ }
+
+ if (virtio_net_started(n, queue_status) && !n->vhost_started) {
+ if (q->tx_timer) {
+ qemu_mod_timer(q->tx_timer,
+ qemu_get_clock_ns(vm_clock) + n->tx_timeout);
+ } else {
+ qemu_bh_schedule(q->tx_bh);
+ }
} else {
- qemu_bh_cancel(n->tx_bh);
+ if (q->tx_timer) {
+ qemu_del_timer(q->tx_timer);
+ } else {
+ qemu_bh_cancel(q->tx_bh);
+ }
}
}
}
static void virtio_net_set_link_status(NetClientState *nc)
{
- VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
+ VirtIONet *n = qemu_get_nic_opaque(nc);
uint16_t old_status = n->status;
if (nc->link_down)
@@ -193,6 +231,8 @@ static void virtio_net_reset(VirtIODevice *vdev)
n->nomulti = 0;
n->nouni = 0;
n->nobcast = 0;
+ /* multiqueue is disabled by default */
+ n->curr_queues = 1;
/* Flush any MAC and VLAN filter table state */
n->mac_table.in_use = 0;
@@ -206,13 +246,16 @@ static void virtio_net_reset(VirtIODevice *vdev)
static void peer_test_vnet_hdr(VirtIONet *n)
{
- if (!n->nic->nc.peer)
+ NetClientState *nc = qemu_get_queue(n->nic);
+ if (!nc->peer) {
return;
+ }
- if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP)
+ if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
return;
+ }
- n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer);
+ n->has_vnet_hdr = tap_has_vnet_hdr(nc->peer);
}
static int peer_has_vnet_hdr(VirtIONet *n)
@@ -225,28 +268,81 @@ static int peer_has_ufo(VirtIONet *n)
if (!peer_has_vnet_hdr(n))
return 0;
- n->has_ufo = tap_has_ufo(n->nic->nc.peer);
+ n->has_ufo = tap_has_ufo(qemu_get_queue(n->nic)->peer);
return n->has_ufo;
}
static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs)
{
+ int i;
+ NetClientState *nc;
+
n->mergeable_rx_bufs = mergeable_rx_bufs;
n->guest_hdr_len = n->mergeable_rx_bufs ?
sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr);
- if (peer_has_vnet_hdr(n) &&
- tap_has_vnet_hdr_len(n->nic->nc.peer, n->guest_hdr_len)) {
- tap_set_vnet_hdr_len(n->nic->nc.peer, n->guest_hdr_len);
- n->host_hdr_len = n->guest_hdr_len;
+ for (i = 0; i < n->max_queues; i++) {
+ nc = qemu_get_subqueue(n->nic, i);
+
+ if (peer_has_vnet_hdr(n) &&
+ tap_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) {
+ tap_set_vnet_hdr_len(nc->peer, n->guest_hdr_len);
+ n->host_hdr_len = n->guest_hdr_len;
+ }
+ }
+}
+
+static int peer_attach(VirtIONet *n, int index)
+{
+ NetClientState *nc = qemu_get_subqueue(n->nic, index);
+
+ if (!nc->peer) {
+ return 0;
+ }
+
+ if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+ return 0;
+ }
+
+ return tap_enable(nc->peer);
+}
+
+static int peer_detach(VirtIONet *n, int index)
+{
+ NetClientState *nc = qemu_get_subqueue(n->nic, index);
+
+ if (!nc->peer) {
+ return 0;
+ }
+
+ if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+ return 0;
+ }
+
+ return tap_disable(nc->peer);
+}
+
+static void virtio_net_set_queues(VirtIONet *n)
+{
+ int i;
+
+ for (i = 0; i < n->max_queues; i++) {
+ if (i < n->curr_queues) {
+ assert(!peer_attach(n, i));
+ } else {
+ assert(!peer_detach(n, i));
+ }
}
}
+static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue, int ctrl);
+
static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features)
{
VirtIONet *n = to_virtio_net(vdev);
+ NetClientState *nc = qemu_get_queue(n->nic);
features |= (1 << VIRTIO_NET_F_MAC);
@@ -267,14 +363,13 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features)
features &= ~(0x1 << VIRTIO_NET_F_HOST_UFO);
}
- if (!n->nic->nc.peer ||
- n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+ if (!nc->peer || nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
return features;
}
- if (!tap_get_vhost_net(n->nic->nc.peer)) {
+ if (!tap_get_vhost_net(nc->peer)) {
return features;
}
- return vhost_net_get_features(tap_get_vhost_net(n->nic->nc.peer), features);
+ return vhost_net_get_features(tap_get_vhost_net(nc->peer), features);
}
static uint32_t virtio_net_bad_features(VirtIODevice *vdev)
@@ -295,25 +390,33 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev)
static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
{
VirtIONet *n = to_virtio_net(vdev);
+ int i;
+
+ virtio_net_set_multiqueue(n, !!(features & (1 << VIRTIO_NET_F_MQ)),
+ !!(features & (1 << VIRTIO_NET_F_CTRL_VQ)));
virtio_net_set_mrg_rx_bufs(n, !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF)));
if (n->has_vnet_hdr) {
- tap_set_offload(n->nic->nc.peer,
+ tap_set_offload(qemu_get_subqueue(n->nic, 0)->peer,
(features >> VIRTIO_NET_F_GUEST_CSUM) & 1,
(features >> VIRTIO_NET_F_GUEST_TSO4) & 1,
(features >> VIRTIO_NET_F_GUEST_TSO6) & 1,
(features >> VIRTIO_NET_F_GUEST_ECN) & 1,
(features >> VIRTIO_NET_F_GUEST_UFO) & 1);
}
- if (!n->nic->nc.peer ||
- n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
- return;
- }
- if (!tap_get_vhost_net(n->nic->nc.peer)) {
- return;
+
+ for (i = 0; i < n->max_queues; i++) {
+ NetClientState *nc = qemu_get_subqueue(n->nic, i);
+
+ if (!nc->peer || nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+ continue;
+ }
+ if (!tap_get_vhost_net(nc->peer)) {
+ continue;
+ }
+ vhost_net_ack_features(tap_get_vhost_net(nc->peer), features);
}
- vhost_net_ack_features(tap_get_vhost_net(n->nic->nc.peer), features);
}
static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
@@ -358,7 +461,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
}
s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac));
assert(s == sizeof(n->mac));
- qemu_format_nic_info_str(&n->nic->nc, n->mac);
+ qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
return VIRTIO_NET_OK;
}
@@ -451,6 +554,38 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
return VIRTIO_NET_OK;
}
+static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd,
+ VirtQueueElement *elem)
+{
+ struct virtio_net_ctrl_mq s;
+
+ if (elem->out_num != 2 ||
+ elem->out_sg[1].iov_len != sizeof(struct virtio_net_ctrl_mq)) {
+ error_report("virtio-net ctrl invalid steering command");
+ return VIRTIO_NET_ERR;
+ }
+
+ if (cmd != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
+ return VIRTIO_NET_ERR;
+ }
+
+ memcpy(&s, elem->out_sg[1].iov_base, sizeof(struct virtio_net_ctrl_mq));
+
+ if (s.virtqueue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
+ s.virtqueue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX ||
+ s.virtqueue_pairs > n->max_queues ||
+ !n->multiqueue) {
+ return VIRTIO_NET_ERR;
+ }
+
+ n->curr_queues = s.virtqueue_pairs;
+ /* stop the backend before changing the number of queues to avoid handling a
+ * disabled queue */
+ virtio_net_set_status(&n->vdev, n->vdev.status);
+ virtio_net_set_queues(n);
+
+ return VIRTIO_NET_OK;
+}
static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIONet *n = to_virtio_net(vdev);
@@ -480,6 +615,8 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt);
} else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) {
status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt);
+ } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) {
+ status = virtio_net_handle_mq(n, ctrl.cmd, &elem);
}
s = iov_from_buf(elem.in_sg, elem.in_num, 0, &status, sizeof(status));
@@ -495,42 +632,52 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIONet *n = to_virtio_net(vdev);
+ int queue_index = vq2q(virtio_get_queue_index(vq));
- qemu_flush_queued_packets(&n->nic->nc);
+ qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index));
}
static int virtio_net_can_receive(NetClientState *nc)
{
- VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
+ VirtIONet *n = qemu_get_nic_opaque(nc);
+ VirtIONetQueue *q = virtio_net_get_subqueue(nc);
+
if (!n->vdev.vm_running) {
return 0;
}
- if (!virtio_queue_ready(n->rx_vq) ||
- !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
+ if (nc->queue_index >= n->curr_queues) {
+ return 0;
+ }
+
+ if (!virtio_queue_ready(q->rx_vq) ||
+ !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
return 0;
+ }
return 1;
}
-static int virtio_net_has_buffers(VirtIONet *n, int bufsize)
+static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize)
{
- if (virtio_queue_empty(n->rx_vq) ||
+ VirtIONet *n = q->n;
+ if (virtio_queue_empty(q->rx_vq) ||
(n->mergeable_rx_bufs &&
- !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) {
- virtio_queue_set_notification(n->rx_vq, 1);
+ !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
+ virtio_queue_set_notification(q->rx_vq, 1);
/* To avoid a race condition where the guest has made some buffers
* available after the above check but before notification was
* enabled, check for available buffers again.
*/
- if (virtio_queue_empty(n->rx_vq) ||
+ if (virtio_queue_empty(q->rx_vq) ||
(n->mergeable_rx_bufs &&
- !virtqueue_avail_bytes(n->rx_vq, bufsize, 0)))
+ !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) {
return 0;
+ }
}
- virtio_queue_set_notification(n->rx_vq, 0);
+ virtio_queue_set_notification(q->rx_vq, 0);
return 1;
}
@@ -632,18 +779,21 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
- VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
+ VirtIONet *n = qemu_get_nic_opaque(nc);
+ VirtIONetQueue *q = virtio_net_get_subqueue(nc);
struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE];
struct virtio_net_hdr_mrg_rxbuf mhdr;
unsigned mhdr_cnt = 0;
size_t offset, i, guest_offset;
- if (!virtio_net_can_receive(&n->nic->nc))
+ if (!virtio_net_can_receive(nc)) {
return -1;
+ }
/* hdr_len refers to the header we supply to the guest */
- if (!virtio_net_has_buffers(n, size + n->guest_hdr_len - n->host_hdr_len))
+ if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) {
return 0;
+ }
if (!receive_filter(n, buf, size))
return size;
@@ -657,7 +807,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t
total = 0;
- if (virtqueue_pop(n->rx_vq, &elem) == 0) {
+ if (virtqueue_pop(q->rx_vq, &elem) == 0) {
if (i == 0)
return -1;
error_report("virtio-net unexpected empty queue: "
@@ -710,7 +860,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t
}
/* signal other side */
- virtqueue_fill(n->rx_vq, &elem, total, i++);
+ virtqueue_fill(q->rx_vq, &elem, total, i++);
}
if (mhdr_cnt) {
@@ -720,44 +870,47 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t
&mhdr.num_buffers, sizeof mhdr.num_buffers);
}
- virtqueue_flush(n->rx_vq, i);
- virtio_notify(&n->vdev, n->rx_vq);
+ virtqueue_flush(q->rx_vq, i);
+ virtio_notify(&n->vdev, q->rx_vq);
return size;
}
-static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq);
+static int32_t virtio_net_flush_tx(VirtIONetQueue *q);
static void virtio_net_tx_complete(NetClientState *nc, ssize_t len)
{
- VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
+ VirtIONet *n = qemu_get_nic_opaque(nc);
+ VirtIONetQueue *q = virtio_net_get_subqueue(nc);
- virtqueue_push(n->tx_vq, &n->async_tx.elem, 0);
- virtio_notify(&n->vdev, n->tx_vq);
+ virtqueue_push(q->tx_vq, &q->async_tx.elem, 0);
+ virtio_notify(&n->vdev, q->tx_vq);
- n->async_tx.elem.out_num = n->async_tx.len = 0;
+ q->async_tx.elem.out_num = q->async_tx.len = 0;
- virtio_queue_set_notification(n->tx_vq, 1);
- virtio_net_flush_tx(n, n->tx_vq);
+ virtio_queue_set_notification(q->tx_vq, 1);
+ virtio_net_flush_tx(q);
}
/* TX */
-static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
+static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
{
+ VirtIONet *n = q->n;
VirtQueueElement elem;
int32_t num_packets = 0;
+ int queue_index = vq2q(virtio_get_queue_index(q->tx_vq));
if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
return num_packets;
}
assert(n->vdev.vm_running);
- if (n->async_tx.elem.out_num) {
- virtio_queue_set_notification(n->tx_vq, 0);
+ if (q->async_tx.elem.out_num) {
+ virtio_queue_set_notification(q->tx_vq, 0);
return num_packets;
}
- while (virtqueue_pop(vq, &elem)) {
+ while (virtqueue_pop(q->tx_vq, &elem)) {
ssize_t ret, len;
unsigned int out_num = elem.out_num;
struct iovec *out_sg = &elem.out_sg[0];
@@ -787,19 +940,19 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
len = n->guest_hdr_len;
- ret = qemu_sendv_packet_async(&n->nic->nc, out_sg, out_num,
- virtio_net_tx_complete);
+ ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
+ out_sg, out_num, virtio_net_tx_complete);
if (ret == 0) {
- virtio_queue_set_notification(n->tx_vq, 0);
- n->async_tx.elem = elem;
- n->async_tx.len = len;
+ virtio_queue_set_notification(q->tx_vq, 0);
+ q->async_tx.elem = elem;
+ q->async_tx.len = len;
return -EBUSY;
}
len += ret;
- virtqueue_push(vq, &elem, 0);
- virtio_notify(&n->vdev, vq);
+ virtqueue_push(q->tx_vq, &elem, 0);
+ virtio_notify(&n->vdev, q->tx_vq);
if (++num_packets >= n->tx_burst) {
break;
@@ -811,22 +964,23 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIONet *n = to_virtio_net(vdev);
+ VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
/* This happens when device was stopped but VCPU wasn't. */
if (!n->vdev.vm_running) {
- n->tx_waiting = 1;
+ q->tx_waiting = 1;
return;
}
- if (n->tx_waiting) {
+ if (q->tx_waiting) {
virtio_queue_set_notification(vq, 1);
- qemu_del_timer(n->tx_timer);
- n->tx_waiting = 0;
- virtio_net_flush_tx(n, vq);
+ qemu_del_timer(q->tx_timer);
+ q->tx_waiting = 0;
+ virtio_net_flush_tx(q);
} else {
- qemu_mod_timer(n->tx_timer,
+ qemu_mod_timer(q->tx_timer,
qemu_get_clock_ns(vm_clock) + n->tx_timeout);
- n->tx_waiting = 1;
+ q->tx_waiting = 1;
virtio_queue_set_notification(vq, 0);
}
}
@@ -834,48 +988,51 @@ static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq)
static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIONet *n = to_virtio_net(vdev);
+ VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))];
- if (unlikely(n->tx_waiting)) {
+ if (unlikely(q->tx_waiting)) {
return;
}
- n->tx_waiting = 1;
+ q->tx_waiting = 1;
/* This happens when device was stopped but VCPU wasn't. */
if (!n->vdev.vm_running) {
return;
}
virtio_queue_set_notification(vq, 0);
- qemu_bh_schedule(n->tx_bh);
+ qemu_bh_schedule(q->tx_bh);
}
static void virtio_net_tx_timer(void *opaque)
{
- VirtIONet *n = opaque;
+ VirtIONetQueue *q = opaque;
+ VirtIONet *n = q->n;
assert(n->vdev.vm_running);
- n->tx_waiting = 0;
+ q->tx_waiting = 0;
/* Just in case the driver is not ready on more */
if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
return;
- virtio_queue_set_notification(n->tx_vq, 1);
- virtio_net_flush_tx(n, n->tx_vq);
+ virtio_queue_set_notification(q->tx_vq, 1);
+ virtio_net_flush_tx(q);
}
static void virtio_net_tx_bh(void *opaque)
{
- VirtIONet *n = opaque;
+ VirtIONetQueue *q = opaque;
+ VirtIONet *n = q->n;
int32_t ret;
assert(n->vdev.vm_running);
- n->tx_waiting = 0;
+ q->tx_waiting = 0;
/* Just in case the driver is not ready on more */
if (unlikely(!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)))
return;
- ret = virtio_net_flush_tx(n, n->tx_vq);
+ ret = virtio_net_flush_tx(q);
if (ret == -EBUSY) {
return; /* Notification re-enable handled by tx_complete */
}
@@ -883,24 +1040,61 @@ static void virtio_net_tx_bh(void *opaque)
/* If we flush a full burst of packets, assume there are
* more coming and immediately reschedule */
if (ret >= n->tx_burst) {
- qemu_bh_schedule(n->tx_bh);
- n->tx_waiting = 1;
+ qemu_bh_schedule(q->tx_bh);
+ q->tx_waiting = 1;
return;
}
/* If less than a full burst, re-enable notification and flush
* anything that may have come in while we weren't looking. If
* we find something, assume the guest is still active and reschedule */
- virtio_queue_set_notification(n->tx_vq, 1);
- if (virtio_net_flush_tx(n, n->tx_vq) > 0) {
- virtio_queue_set_notification(n->tx_vq, 0);
- qemu_bh_schedule(n->tx_bh);
- n->tx_waiting = 1;
+ virtio_queue_set_notification(q->tx_vq, 1);
+ if (virtio_net_flush_tx(q) > 0) {
+ virtio_queue_set_notification(q->tx_vq, 0);
+ qemu_bh_schedule(q->tx_bh);
+ q->tx_waiting = 1;
}
}
+static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue, int ctrl)
+{
+ VirtIODevice *vdev = &n->vdev;
+ int i, max = multiqueue ? n->max_queues : 1;
+
+ n->multiqueue = multiqueue;
+
+ for (i = 2; i <= n->max_queues * 2 + 1; i++) {
+ virtio_del_queue(vdev, i);
+ }
+
+ for (i = 1; i < max; i++) {
+ n->vqs[i].rx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_rx);
+ if (n->vqs[i].tx_timer) {
+ n->vqs[i].tx_vq =
+ virtio_add_queue(vdev, 256, virtio_net_handle_tx_timer);
+ n->vqs[i].tx_timer = qemu_new_timer_ns(vm_clock,
+ virtio_net_tx_timer,
+ &n->vqs[i]);
+ } else {
+ n->vqs[i].tx_vq =
+ virtio_add_queue(vdev, 256, virtio_net_handle_tx_bh);
+ n->vqs[i].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[i]);
+ }
+
+ n->vqs[i].tx_waiting = 0;
+ n->vqs[i].n = n;
+ }
+
+ if (ctrl) {
+ n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
+ }
+
+ virtio_net_set_queues(n);
+}
+
static void virtio_net_save(QEMUFile *f, void *opaque)
{
+ int i;
VirtIONet *n = opaque;
/* At this point, backend must be stopped, otherwise
@@ -909,7 +1103,7 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
virtio_save(&n->vdev, f);
qemu_put_buffer(f, n->mac, ETH_ALEN);
- qemu_put_be32(f, n->tx_waiting);
+ qemu_put_be32(f, n->vqs[0].tx_waiting);
qemu_put_be32(f, n->mergeable_rx_bufs);
qemu_put_be16(f, n->status);
qemu_put_byte(f, n->promisc);
@@ -925,13 +1119,19 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
qemu_put_byte(f, n->nouni);
qemu_put_byte(f, n->nobcast);
qemu_put_byte(f, n->has_ufo);
+ if (n->max_queues > 1) {
+ qemu_put_be16(f, n->max_queues);
+ qemu_put_be16(f, n->curr_queues);
+ for (i = 1; i < n->curr_queues; i++) {
+ qemu_put_be32(f, n->vqs[i].tx_waiting);
+ }
+ }
}
static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
{
VirtIONet *n = opaque;
- int i;
- int ret;
+ int ret, i, link_down;
if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION)
return -EINVAL;
@@ -942,7 +1142,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
}
qemu_get_buffer(f, n->mac, ETH_ALEN);
- n->tx_waiting = qemu_get_be32(f);
+ n->vqs[0].tx_waiting = qemu_get_be32(f);
virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f));
@@ -984,7 +1184,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
}
if (n->has_vnet_hdr) {
- tap_set_offload(n->nic->nc.peer,
+ tap_set_offload(qemu_get_queue(n->nic)->peer,
(n->vdev.guest_features >> VIRTIO_NET_F_GUEST_CSUM) & 1,
(n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO4) & 1,
(n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO6) & 1,
@@ -1012,6 +1212,20 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
}
}
+ if (n->max_queues > 1) {
+ if (n->max_queues != qemu_get_be16(f)) {
+ error_report("virtio-net: different max_queues ");
+ return -1;
+ }
+
+ n->curr_queues = qemu_get_be16(f);
+ for (i = 1; i < n->curr_queues; i++) {
+ n->vqs[i].tx_waiting = qemu_get_be32(f);
+ }
+ }
+
+ virtio_net_set_queues(n);
+
/* Find the first multicast entry in the saved MAC filter */
for (i = 0; i < n->mac_table.in_use; i++) {
if (n->mac_table.macs[i * ETH_ALEN] & 1) {
@@ -1022,14 +1236,17 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
/* nc.link_down can't be migrated, so infer link_down according
* to link status bit in n->status */
- n->nic->nc.link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0;
+ link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0;
+ for (i = 0; i < n->max_queues; i++) {
+ qemu_get_subqueue(n->nic, i)->link_down = link_down;
+ }
return 0;
}
static void virtio_net_cleanup(NetClientState *nc)
{
- VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
+ VirtIONet *n = qemu_get_nic_opaque(nc);
n->nic = NULL;
}
@@ -1046,16 +1263,18 @@ static NetClientInfo net_virtio_info = {
static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
{
VirtIONet *n = to_virtio_net(vdev);
+ NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
assert(n->vhost_started);
- return vhost_net_virtqueue_pending(tap_get_vhost_net(n->nic->nc.peer), idx);
+ return vhost_net_virtqueue_pending(tap_get_vhost_net(nc->peer), idx);
}
static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx,
bool mask)
{
VirtIONet *n = to_virtio_net(vdev);
+ NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx));
assert(n->vhost_started);
- vhost_net_virtqueue_mask(tap_get_vhost_net(n->nic->nc.peer),
+ vhost_net_virtqueue_mask(tap_get_vhost_net(nc->peer),
vdev, idx, mask);
}
@@ -1063,6 +1282,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
virtio_net_conf *net)
{
VirtIONet *n;
+ int i;
n = (VirtIONet *)virtio_common_init("virtio-net", VIRTIO_ID_NET,
sizeof(struct virtio_net_config),
@@ -1077,7 +1297,11 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
n->vdev.set_status = virtio_net_set_status;
n->vdev.guest_notifier_mask = virtio_net_guest_notifier_mask;
n->vdev.guest_notifier_pending = virtio_net_guest_notifier_pending;
- n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
+ n->vqs[0].rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
+ n->max_queues = conf->queues;
+ n->curr_queues = 1;
+ n->vqs[0].n = n;
+ n->tx_timeout = net->txtimer;
if (net->tx && strcmp(net->tx, "timer") && strcmp(net->tx, "bh")) {
error_report("virtio-net: "
@@ -1087,12 +1311,14 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
}
if (net->tx && !strcmp(net->tx, "timer")) {
- n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_timer);
- n->tx_timer = qemu_new_timer_ns(vm_clock, virtio_net_tx_timer, n);
- n->tx_timeout = net->txtimer;
+ n->vqs[0].tx_vq = virtio_add_queue(&n->vdev, 256,
+ virtio_net_handle_tx_timer);
+ n->vqs[0].tx_timer = qemu_new_timer_ns(vm_clock, virtio_net_tx_timer,
+ &n->vqs[0]);
} else {
- n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_bh);
- n->tx_bh = qemu_bh_new(virtio_net_tx_bh, n);
+ n->vqs[0].tx_vq = virtio_add_queue(&n->vdev, 256,
+ virtio_net_handle_tx_bh);
+ n->vqs[0].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[0]);
}
n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl);
qemu_macaddr_default_if_unset(&conf->macaddr);
@@ -1102,15 +1328,17 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
n->nic = qemu_new_nic(&net_virtio_info, conf, object_get_typename(OBJECT(dev)), dev->id, n);
peer_test_vnet_hdr(n);
if (peer_has_vnet_hdr(n)) {
- tap_using_vnet_hdr(n->nic->nc.peer, 1);
+ for (i = 0; i < n->max_queues; i++) {
+ tap_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true);
+ }
n->host_hdr_len = sizeof(struct virtio_net_hdr);
} else {
n->host_hdr_len = 0;
}
- qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(n->nic), conf->macaddr.a);
- n->tx_waiting = 0;
+ n->vqs[0].tx_waiting = 0;
n->tx_burst = net->txburst;
virtio_net_set_mrg_rx_bufs(n, 0);
n->promisc = 1; /* for compatibility */
@@ -1131,24 +1359,30 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
void virtio_net_exit(VirtIODevice *vdev)
{
VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev);
+ int i;
/* This will stop vhost backend if appropriate. */
virtio_net_set_status(vdev, 0);
- qemu_purge_queued_packets(&n->nic->nc);
-
unregister_savevm(n->qdev, "virtio-net", n);
g_free(n->mac_table.macs);
g_free(n->vlans);
- if (n->tx_timer) {
- qemu_del_timer(n->tx_timer);
- qemu_free_timer(n->tx_timer);
- } else {
- qemu_bh_delete(n->tx_bh);
+ for (i = 0; i < n->max_queues; i++) {
+ VirtIONetQueue *q = &n->vqs[i];
+ NetClientState *nc = qemu_get_subqueue(n->nic, i);
+
+ qemu_purge_queued_packets(nc);
+
+ if (q->tx_timer) {
+ qemu_del_timer(q->tx_timer);
+ qemu_free_timer(q->tx_timer);
+ } else {
+ qemu_bh_delete(q->tx_bh);
+ }
}
- qemu_del_net_client(&n->nic->nc);
+ qemu_del_nic(n->nic);
virtio_cleanup(&n->vdev);
}
diff --git a/hw/virtio-net.h b/hw/virtio-net.h
index c0bb284df2..f5fea6e9bc 100644
--- a/hw/virtio-net.h
+++ b/hw/virtio-net.h
@@ -43,6 +43,8 @@
#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */
#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */
#define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */
+#define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow
+ * Steering */
#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */
@@ -73,6 +75,8 @@ struct virtio_net_config
uint8_t mac[ETH_ALEN];
/* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */
uint16_t status;
+ /* Max virtqueue pairs supported by the device */
+ uint16_t max_virtqueue_pairs;
} QEMU_PACKED;
/*
@@ -147,6 +151,26 @@ struct virtio_net_ctrl_mac {
#define VIRTIO_NET_CTRL_VLAN_ADD 0
#define VIRTIO_NET_CTRL_VLAN_DEL 1
+/*
+ * Control Multiqueue
+ *
+ * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET
+ * enables multiqueue, specifying the number of the transmit and
+ * receive queues that will be used. After the command is consumed and acked by
+ * the device, the device will not steer new packets on receive virtqueues
+ * other than specified nor read from transmit virtqueues other than specified.
+ * Accordingly, driver should not transmit new packets on virtqueues other than
+ * specified.
+ */
+struct virtio_net_ctrl_mq {
+ uint16_t virtqueue_pairs;
+};
+
+#define VIRTIO_NET_CTRL_MQ 4
+ #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0
+ #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1
+ #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000
+
#define DEFINE_VIRTIO_NET_FEATURES(_state, _field) \
DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \
DEFINE_PROP_BIT("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \
@@ -166,5 +190,7 @@ struct virtio_net_ctrl_mac {
DEFINE_PROP_BIT("ctrl_rx", _state, _field, VIRTIO_NET_F_CTRL_RX, true), \
DEFINE_PROP_BIT("ctrl_vlan", _state, _field, VIRTIO_NET_F_CTRL_VLAN, true), \
DEFINE_PROP_BIT("ctrl_rx_extra", _state, _field, VIRTIO_NET_F_CTRL_RX_EXTRA, true), \
- DEFINE_PROP_BIT("ctrl_mac_addr", _state, _field, VIRTIO_NET_F_CTRL_MAC_ADDR, true)
+ DEFINE_PROP_BIT("ctrl_mac_addr", _state, _field, VIRTIO_NET_F_CTRL_MAC_ADDR, true), \
+ DEFINE_PROP_BIT("mq", _state, _field, VIRTIO_NET_F_MQ, true)
+
#endif
diff --git a/hw/virtio.c b/hw/virtio.c
index ca170c319e..e259348518 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -73,6 +73,8 @@ struct VirtQueue
/* Notification enabled? */
bool notification;
+ uint16_t queue_index;
+
int inuse;
uint16_t vector;
@@ -701,6 +703,15 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
return &vdev->vq[i];
}
+void virtio_del_queue(VirtIODevice *vdev, int n)
+{
+ if (n < 0 || n >= VIRTIO_PCI_QUEUE_MAX) {
+ abort();
+ }
+
+ vdev->vq[n].vring.num = 0;
+}
+
void virtio_irq(VirtQueue *vq)
{
trace_virtio_irq(vq);
@@ -922,6 +933,7 @@ void virtio_init(VirtIODevice *vdev, const char *name,
for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
vdev->vq[i].vector = VIRTIO_NO_VECTOR;
vdev->vq[i].vdev = vdev;
+ vdev->vq[i].queue_index = i;
}
vdev->name = name;
@@ -1009,6 +1021,11 @@ VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n)
return vdev->vq + n;
}
+uint16_t virtio_get_queue_index(VirtQueue *vq)
+{
+ return vq->queue_index;
+}
+
static void virtio_queue_guest_notifier_read(EventNotifier *n)
{
VirtQueue *vq = container_of(n, VirtQueue, guest_notifier);
diff --git a/hw/virtio.h b/hw/virtio.h
index 9cc7b85671..a29a54d4f3 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -181,6 +181,8 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
void (*handle_output)(VirtIODevice *,
VirtQueue *));
+void virtio_del_queue(VirtIODevice *vdev, int n);
+
void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem,
unsigned int len);
void virtqueue_flush(VirtQueue *vq, unsigned int count);
@@ -278,6 +280,7 @@ hwaddr virtio_queue_get_ring_size(VirtIODevice *vdev, int n);
uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n);
void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx);
VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n);
+uint16_t virtio_get_queue_index(VirtQueue *vq);
int virtio_queue_get_id(VirtQueue *vq);
EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq);
void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign,
diff --git a/hw/wm8750.c b/hw/wm8750.c
index bb85064c9b..d3ea5ba8f5 100644
--- a/hw/wm8750.c
+++ b/hw/wm8750.c
@@ -632,7 +632,7 @@ static void wm8750_fini(I2CSlave *i2c)
void wm8750_data_req_set(DeviceState *dev,
void (*data_req)(void *, int, int), void *opaque)
{
- WM8750State *s = FROM_I2C_SLAVE(WM8750State, I2C_SLAVE_FROM_QDEV(dev));
+ WM8750State *s = FROM_I2C_SLAVE(WM8750State, I2C_SLAVE(dev));
s->data_req = data_req;
s->opaque = opaque;
}
diff --git a/hw/xen_nic.c b/hw/xen_nic.c
index dc12110dba..34961c287a 100644
--- a/hw/xen_nic.c
+++ b/hw/xen_nic.c
@@ -185,9 +185,11 @@ static void net_tx_packets(struct XenNetDev *netdev)
}
memcpy(tmpbuf, page + txreq.offset, txreq.size);
net_checksum_calculate(tmpbuf, txreq.size);
- qemu_send_packet(&netdev->nic->nc, tmpbuf, txreq.size);
+ qemu_send_packet(qemu_get_queue(netdev->nic), tmpbuf,
+ txreq.size);
} else {
- qemu_send_packet(&netdev->nic->nc, page + txreq.offset, txreq.size);
+ qemu_send_packet(qemu_get_queue(netdev->nic),
+ page + txreq.offset, txreq.size);
}
xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1);
net_tx_response(netdev, &txreq, NETIF_RSP_OKAY);
@@ -234,7 +236,7 @@ static void net_rx_response(struct XenNetDev *netdev,
static int net_rx_ok(NetClientState *nc)
{
- struct XenNetDev *netdev = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct XenNetDev *netdev = qemu_get_nic_opaque(nc);
RING_IDX rc, rp;
if (netdev->xendev.be_state != XenbusStateConnected) {
@@ -255,7 +257,7 @@ static int net_rx_ok(NetClientState *nc)
static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size)
{
- struct XenNetDev *netdev = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct XenNetDev *netdev = qemu_get_nic_opaque(nc);
netif_rx_request_t rxreq;
RING_IDX rc, rp;
void *page;
@@ -324,12 +326,11 @@ static int net_init(struct XenDevice *xendev)
return -1;
}
- netdev->conf.peer = NULL;
-
netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf,
"xen", NULL, netdev);
- snprintf(netdev->nic->nc.info_str, sizeof(netdev->nic->nc.info_str),
+ snprintf(qemu_get_queue(netdev->nic)->info_str,
+ sizeof(qemu_get_queue(netdev->nic)->info_str),
"nic: xenbus vif macaddr=%s", netdev->mac);
/* fill info */
@@ -405,7 +406,7 @@ static void net_disconnect(struct XenDevice *xendev)
netdev->rxs = NULL;
}
if (netdev->nic) {
- qemu_del_net_client(&netdev->nic->nc);
+ qemu_del_nic(netdev->nic);
netdev->nic = NULL;
}
}
@@ -414,7 +415,7 @@ static void net_event(struct XenDevice *xendev)
{
struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
net_tx_packets(netdev);
- qemu_flush_queued_packets(&netdev->nic->nc);
+ qemu_flush_queued_packets(qemu_get_queue(netdev->nic));
}
static int net_free(struct XenDevice *xendev)
diff --git a/hw/xgmac.c b/hw/xgmac.c
index 00dae7789c..50722988b9 100644
--- a/hw/xgmac.c
+++ b/hw/xgmac.c
@@ -235,7 +235,7 @@ static void xgmac_enet_send(struct XgmacState *s)
frame_size += len;
if (bd.ctl_stat & 0x20000000) {
/* Last buffer in frame. */
- qemu_send_packet(&s->nic->nc, frame, len);
+ qemu_send_packet(qemu_get_queue(s->nic), frame, len);
ptr = frame;
frame_size = 0;
s->regs[DMA_STATUS] |= DMA_STATUS_TI | DMA_STATUS_NIS;
@@ -310,7 +310,7 @@ static const MemoryRegionOps enet_mem_ops = {
static int eth_can_rx(NetClientState *nc)
{
- struct XgmacState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct XgmacState *s = qemu_get_nic_opaque(nc);
/* RX enabled? */
return s->regs[DMA_CONTROL] & DMA_CONTROL_SR;
@@ -318,7 +318,7 @@ static int eth_can_rx(NetClientState *nc)
static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
{
- struct XgmacState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct XgmacState *s = qemu_get_nic_opaque(nc);
static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
0xff, 0xff, 0xff};
int unicast, broadcast, multicast;
@@ -366,7 +366,7 @@ out:
static void eth_cleanup(NetClientState *nc)
{
- struct XgmacState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct XgmacState *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
@@ -391,7 +391,7 @@ static int xgmac_enet_init(SysBusDevice *dev)
qemu_macaddr_default_if_unset(&s->conf.macaddr);
s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf,
object_get_typename(OBJECT(dev)), dev->qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) |
s->conf.macaddr.a[4];
diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c
index 51c2896e44..34e344ce2c 100644
--- a/hw/xilinx_axienet.c
+++ b/hw/xilinx_axienet.c
@@ -617,7 +617,7 @@ static const MemoryRegionOps enet_ops = {
static int eth_can_rx(NetClientState *nc)
{
- struct XilinxAXIEnet *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
/* RX enabled? */
return !axienet_rx_resetting(s) && axienet_rx_enabled(s);
@@ -640,7 +640,7 @@ static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1)
static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
{
- struct XilinxAXIEnet *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
0xff, 0xff, 0xff};
static const unsigned char sa_ipmcast[3] = {0x01, 0x00, 0x52};
@@ -785,7 +785,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
static void eth_cleanup(NetClientState *nc)
{
/* FIXME. */
- struct XilinxAXIEnet *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
g_free(s->rxmem);
g_free(s);
}
@@ -826,7 +826,7 @@ axienet_stream_push(StreamSlave *obj, uint8_t *buf, size_t size, uint32_t *hdr)
buf[write_off + 1] = csum & 0xff;
}
- qemu_send_packet(&s->nic->nc, buf, size);
+ qemu_send_packet(qemu_get_queue(s->nic), buf, size);
s->stats.tx_bytes += size;
s->regs[R_IS] |= IS_TX_COMPLETE;
@@ -853,7 +853,7 @@ static int xilinx_enet_init(SysBusDevice *dev)
qemu_macaddr_default_if_unset(&s->conf.macaddr);
s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf,
object_get_typename(OBJECT(dev)), dev->qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
tdk_init(&s->TEMAC.phy);
mdio_attach(&s->TEMAC.mdio_bus, &s->TEMAC.phy, s->c_phyaddr);
diff --git a/hw/xilinx_ethlite.c b/hw/xilinx_ethlite.c
index 11dfbc3ac1..21c6f8c49c 100644
--- a/hw/xilinx_ethlite.c
+++ b/hw/xilinx_ethlite.c
@@ -118,7 +118,7 @@ eth_write(void *opaque, hwaddr addr,
D(qemu_log("%s addr=" TARGET_FMT_plx " val=%x\n",
__func__, addr * 4, value));
if ((value & (CTRL_P | CTRL_S)) == CTRL_S) {
- qemu_send_packet(&s->nic->nc,
+ qemu_send_packet(qemu_get_queue(s->nic),
(void *) &s->regs[base],
s->regs[base + R_TX_LEN0]);
D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0]));
@@ -139,7 +139,7 @@ eth_write(void *opaque, hwaddr addr,
case R_RX_CTRL0:
case R_RX_CTRL1:
if (!(value & CTRL_S)) {
- qemu_flush_queued_packets(&s->nic->nc);
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
}
case R_TX_LEN0:
case R_TX_LEN1:
@@ -167,7 +167,7 @@ static const MemoryRegionOps eth_ops = {
static int eth_can_rx(NetClientState *nc)
{
- struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
unsigned int rxbase = s->rxbuf * (0x800 / 4);
return !(s->regs[rxbase + R_RX_CTRL0] & CTRL_S);
@@ -175,7 +175,7 @@ static int eth_can_rx(NetClientState *nc)
static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
{
- struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
unsigned int rxbase = s->rxbuf * (0x800 / 4);
/* DA filter. */
@@ -201,7 +201,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
static void eth_cleanup(NetClientState *nc)
{
- struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
s->nic = NULL;
}
@@ -228,7 +228,7 @@ static int xilinx_ethlite_init(SysBusDevice *dev)
qemu_macaddr_default_if_unset(&s->conf.macaddr);
s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf,
object_get_typename(OBJECT(dev)), dev->qdev.id, s);
- qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
return 0;
}
diff --git a/include/net/net.h b/include/net/net.h
index 4a92b6c3d2..43a045e052 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -9,24 +9,32 @@
#include "migration/vmstate.h"
#include "qapi-types.h"
+#define MAX_QUEUE_NUM 1024
+
struct MACAddr {
uint8_t a[6];
};
/* qdev nic properties */
+typedef struct NICPeers {
+ NetClientState *ncs[MAX_QUEUE_NUM];
+} NICPeers;
+
typedef struct NICConf {
MACAddr macaddr;
- NetClientState *peer;
+ NICPeers peers;
int32_t bootindex;
+ int32_t queues;
} NICConf;
#define DEFINE_NIC_PROPERTIES(_state, _conf) \
DEFINE_PROP_MACADDR("mac", _state, _conf.macaddr), \
- DEFINE_PROP_VLAN("vlan", _state, _conf.peer), \
- DEFINE_PROP_NETDEV("netdev", _state, _conf.peer), \
+ DEFINE_PROP_VLAN("vlan", _state, _conf.peers), \
+ DEFINE_PROP_NETDEV("netdev", _state, _conf.peers), \
DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1)
+
/* Net clients */
typedef void (NetPoll)(NetClientState *, bool enable);
@@ -35,6 +43,7 @@ typedef ssize_t (NetReceive)(NetClientState *, const uint8_t *, size_t);
typedef ssize_t (NetReceiveIOV)(NetClientState *, const struct iovec *, int);
typedef void (NetCleanup) (NetClientState *);
typedef void (LinkStatusChanged)(NetClientState *);
+typedef void (NetClientDestructor)(NetClientState *);
typedef struct NetClientInfo {
NetClientOptionsKind type;
@@ -58,16 +67,20 @@ struct NetClientState {
char *name;
char info_str[256];
unsigned receive_disabled : 1;
+ NetClientDestructor *destructor;
+ unsigned int queue_index;
};
typedef struct NICState {
- NetClientState nc;
+ NetClientState ncs[MAX_QUEUE_NUM];
NICConf *conf;
void *opaque;
bool peer_deleted;
} NICState;
NetClientState *qemu_find_netdev(const char *id);
+int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
+ NetClientOptionsKind type, int max);
NetClientState *qemu_new_net_client(NetClientInfo *info,
NetClientState *peer,
const char *model,
@@ -77,6 +90,11 @@ NICState *qemu_new_nic(NetClientInfo *info,
const char *model,
const char *name,
void *opaque);
+void qemu_del_nic(NICState *nic);
+NetClientState *qemu_get_subqueue(NICState *nic, int queue_index);
+NetClientState *qemu_get_queue(NICState *nic);
+NICState *qemu_get_nic(NetClientState *nc);
+void *qemu_get_nic_opaque(NetClientState *nc);
void qemu_del_net_client(NetClientState *nc);
NetClientState *qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id,
const char *client_str);
diff --git a/include/net/tap.h b/include/net/tap.h
index bb7efb5439..a994f20447 100644
--- a/include/net/tap.h
+++ b/include/net/tap.h
@@ -29,12 +29,14 @@
#include "qemu-common.h"
#include "qapi-types.h"
-int tap_has_ufo(NetClientState *nc);
+bool tap_has_ufo(NetClientState *nc);
int tap_has_vnet_hdr(NetClientState *nc);
int tap_has_vnet_hdr_len(NetClientState *nc, int len);
-void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr);
+void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr);
void tap_set_offload(NetClientState *nc, int csum, int tso4, int tso6, int ecn, int ufo);
void tap_set_vnet_hdr_len(NetClientState *nc, int len);
+int tap_enable(NetClientState *nc);
+int tap_disable(NetClientState *nc);
int tap_get_fd(NetClientState *nc);
diff --git a/net/net.c b/net/net.c
index 2f0ab3a121..98068625d4 100644
--- a/net/net.c
+++ b/net/net.c
@@ -182,17 +182,18 @@ static char *assign_name(NetClientState *nc1, const char *model)
return g_strdup(buf);
}
-NetClientState *qemu_new_net_client(NetClientInfo *info,
- NetClientState *peer,
- const char *model,
- const char *name)
+static void qemu_net_client_destructor(NetClientState *nc)
{
- NetClientState *nc;
-
- assert(info->size >= sizeof(NetClientState));
-
- nc = g_malloc0(info->size);
+ g_free(nc);
+}
+static void qemu_net_client_setup(NetClientState *nc,
+ NetClientInfo *info,
+ NetClientState *peer,
+ const char *model,
+ const char *name,
+ NetClientDestructor *destructor)
+{
nc->info = info;
nc->model = g_strdup(model);
if (name) {
@@ -209,6 +210,21 @@ NetClientState *qemu_new_net_client(NetClientInfo *info,
QTAILQ_INSERT_TAIL(&net_clients, nc, next);
nc->send_queue = qemu_new_net_queue(nc);
+ nc->destructor = destructor;
+}
+
+NetClientState *qemu_new_net_client(NetClientInfo *info,
+ NetClientState *peer,
+ const char *model,
+ const char *name)
+{
+ NetClientState *nc;
+
+ assert(info->size >= sizeof(NetClientState));
+
+ nc = g_malloc0(info->size);
+ qemu_net_client_setup(nc, info, peer, model, name,
+ qemu_net_client_destructor);
return nc;
}
@@ -220,27 +236,58 @@ NICState *qemu_new_nic(NetClientInfo *info,
void *opaque)
{
NetClientState *nc;
+ NetClientState **peers = conf->peers.ncs;
NICState *nic;
+ int i;
assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC);
assert(info->size >= sizeof(NICState));
- nc = qemu_new_net_client(info, conf->peer, model, name);
+ nc = qemu_new_net_client(info, peers[0], model, name);
+ nc->queue_index = 0;
- nic = DO_UPCAST(NICState, nc, nc);
+ nic = qemu_get_nic(nc);
nic->conf = conf;
nic->opaque = opaque;
+ for (i = 1; i < conf->queues; i++) {
+ qemu_net_client_setup(&nic->ncs[i], info, peers[i], model, nc->name,
+ NULL);
+ nic->ncs[i].queue_index = i;
+ }
+
return nic;
}
+NetClientState *qemu_get_subqueue(NICState *nic, int queue_index)
+{
+ return &nic->ncs[queue_index];
+}
+
+NetClientState *qemu_get_queue(NICState *nic)
+{
+ return qemu_get_subqueue(nic, 0);
+}
+
+NICState *qemu_get_nic(NetClientState *nc)
+{
+ NetClientState *nc0 = nc - nc->queue_index;
+
+ return DO_UPCAST(NICState, ncs[0], nc0);
+}
+
+void *qemu_get_nic_opaque(NetClientState *nc)
+{
+ NICState *nic = qemu_get_nic(nc);
+
+ return nic->opaque;
+}
+
static void qemu_cleanup_net_client(NetClientState *nc)
{
QTAILQ_REMOVE(&net_clients, nc, next);
- if (nc->info->cleanup) {
- nc->info->cleanup(nc);
- }
+ nc->info->cleanup(nc);
}
static void qemu_free_net_client(NetClientState *nc)
@@ -253,37 +300,72 @@ static void qemu_free_net_client(NetClientState *nc)
}
g_free(nc->name);
g_free(nc->model);
- g_free(nc);
+ if (nc->destructor) {
+ nc->destructor(nc);
+ }
}
void qemu_del_net_client(NetClientState *nc)
{
+ NetClientState *ncs[MAX_QUEUE_NUM];
+ int queues, i;
+
+ /* If the NetClientState belongs to a multiqueue backend, we will change all
+ * other NetClientStates also.
+ */
+ queues = qemu_find_net_clients_except(nc->name, ncs,
+ NET_CLIENT_OPTIONS_KIND_NIC,
+ MAX_QUEUE_NUM);
+ assert(queues != 0);
+
/* If there is a peer NIC, delete and cleanup client, but do not free. */
if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
- NICState *nic = DO_UPCAST(NICState, nc, nc->peer);
+ NICState *nic = qemu_get_nic(nc->peer);
if (nic->peer_deleted) {
return;
}
nic->peer_deleted = true;
- /* Let NIC know peer is gone. */
- nc->peer->link_down = true;
+
+ for (i = 0; i < queues; i++) {
+ ncs[i]->peer->link_down = true;
+ }
+
if (nc->peer->info->link_status_changed) {
nc->peer->info->link_status_changed(nc->peer);
}
- qemu_cleanup_net_client(nc);
+
+ for (i = 0; i < queues; i++) {
+ qemu_cleanup_net_client(ncs[i]);
+ }
+
return;
}
+ assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC);
+
+ for (i = 0; i < queues; i++) {
+ qemu_cleanup_net_client(ncs[i]);
+ qemu_free_net_client(ncs[i]);
+ }
+}
+
+void qemu_del_nic(NICState *nic)
+{
+ int i, queues = nic->conf->queues;
+
/* If this is a peer NIC and peer has already been deleted, free it now. */
- if (nc->peer && nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
- NICState *nic = DO_UPCAST(NICState, nc, nc);
- if (nic->peer_deleted) {
- qemu_free_net_client(nc->peer);
+ if (nic->peer_deleted) {
+ for (i = 0; i < queues; i++) {
+ qemu_free_net_client(qemu_get_subqueue(nic, i)->peer);
}
}
- qemu_cleanup_net_client(nc);
- qemu_free_net_client(nc);
+ for (i = queues - 1; i >= 0; i--) {
+ NetClientState *nc = qemu_get_subqueue(nic, i);
+
+ qemu_cleanup_net_client(nc);
+ qemu_free_net_client(nc);
+ }
}
void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
@@ -292,7 +374,9 @@ void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
QTAILQ_FOREACH(nc, &net_clients, next) {
if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
- func(DO_UPCAST(NICState, nc, nc), opaque);
+ if (nc->queue_index == 0) {
+ func(qemu_get_nic(nc), opaque);
+ }
}
}
}
@@ -482,6 +566,27 @@ NetClientState *qemu_find_netdev(const char *id)
return NULL;
}
+int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
+ NetClientOptionsKind type, int max)
+{
+ NetClientState *nc;
+ int ret = 0;
+
+ QTAILQ_FOREACH(nc, &net_clients, next) {
+ if (nc->info->type == type) {
+ continue;
+ }
+ if (!strcmp(nc->name, id)) {
+ if (ret < max) {
+ ncs[ret] = nc;
+ }
+ ret++;
+ }
+ }
+
+ return ret;
+}
+
static int nic_get_free_idx(void)
{
int index;
@@ -846,8 +951,10 @@ void qmp_netdev_del(const char *id, Error **errp)
void print_net_client(Monitor *mon, NetClientState *nc)
{
- monitor_printf(mon, "%s: type=%s,%s\n", nc->name,
- NetClientOptionsKind_lookup[nc->info->type], nc->info_str);
+ monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name,
+ nc->queue_index,
+ NetClientOptionsKind_lookup[nc->info->type],
+ nc->info_str);
}
void do_info_network(Monitor *mon, const QDict *qdict)
@@ -878,20 +985,23 @@ void do_info_network(Monitor *mon, const QDict *qdict)
void qmp_set_link(const char *name, bool up, Error **errp)
{
- NetClientState *nc = NULL;
+ NetClientState *ncs[MAX_QUEUE_NUM];
+ NetClientState *nc;
+ int queues, i;
- QTAILQ_FOREACH(nc, &net_clients, next) {
- if (!strcmp(nc->name, name)) {
- goto done;
- }
- }
-done:
- if (!nc) {
+ queues = qemu_find_net_clients_except(name, ncs,
+ NET_CLIENT_OPTIONS_KIND_MAX,
+ MAX_QUEUE_NUM);
+
+ if (queues == 0) {
error_set(errp, QERR_DEVICE_NOT_FOUND, name);
return;
}
+ nc = ncs[0];
- nc->link_down = !up;
+ for (i = 0; i < queues; i++) {
+ ncs[i]->link_down = !up;
+ }
if (nc->info->link_status_changed) {
nc->info->link_status_changed(nc);
@@ -911,10 +1021,18 @@ done:
void net_cleanup(void)
{
- NetClientState *nc, *next_vc;
+ NetClientState *nc;
- QTAILQ_FOREACH_SAFE(nc, &net_clients, next, next_vc) {
- qemu_del_net_client(nc);
+ /* We may del multiple entries during qemu_del_net_client(),
+ * so QTAILQ_FOREACH_SAFE() is also not safe here.
+ */
+ while (!QTAILQ_EMPTY(&net_clients)) {
+ nc = QTAILQ_FIRST(&net_clients);
+ if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+ qemu_del_nic(qemu_get_nic(nc));
+ } else {
+ qemu_del_net_client(nc);
+ }
}
}
diff --git a/net/tap-aix.c b/net/tap-aix.c
index aff6c527e9..804d16448d 100644
--- a/net/tap-aix.c
+++ b/net/tap-aix.c
@@ -25,7 +25,8 @@
#include "tap_int.h"
#include <stdio.h>
-int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
+ int vnet_hdr_required, int mq_required)
{
fprintf(stderr, "no tap on AIX\n");
return -1;
@@ -59,3 +60,19 @@ void tap_fd_set_offload(int fd, int csum, int tso4,
int tso6, int ecn, int ufo)
{
}
+
+int tap_fd_enable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_disable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_get_ifname(int fd, char *ifname)
+{
+ return -1;
+}
+
diff --git a/net/tap-bsd.c b/net/tap-bsd.c
index 01c705b4c0..bcdb2682b5 100644
--- a/net/tap-bsd.c
+++ b/net/tap-bsd.c
@@ -33,7 +33,8 @@
#include <net/if_tap.h>
#endif
-int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
+ int vnet_hdr_required, int mq_required)
{
int fd;
#ifdef TAPGIFNAME
@@ -145,3 +146,18 @@ void tap_fd_set_offload(int fd, int csum, int tso4,
int tso6, int ecn, int ufo)
{
}
+
+int tap_fd_enable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_disable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_get_ifname(int fd, char *ifname)
+{
+ return -1;
+}
diff --git a/net/tap-haiku.c b/net/tap-haiku.c
index 08cc034cee..e5ce436d24 100644
--- a/net/tap-haiku.c
+++ b/net/tap-haiku.c
@@ -25,7 +25,8 @@
#include "tap_int.h"
#include <stdio.h>
-int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
+ int vnet_hdr_required, int mq_required)
{
fprintf(stderr, "no tap on Haiku\n");
return -1;
@@ -59,3 +60,18 @@ void tap_fd_set_offload(int fd, int csum, int tso4,
int tso6, int ecn, int ufo)
{
}
+
+int tap_fd_enable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_disable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_get_ifname(int fd, char *ifname)
+{
+ return -1;
+}
diff --git a/net/tap-linux.c b/net/tap-linux.c
index 059f5f34ab..a9531892a6 100644
--- a/net/tap-linux.c
+++ b/net/tap-linux.c
@@ -36,7 +36,8 @@
#define PATH_NET_TUN "/dev/net/tun"
-int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
+ int vnet_hdr_required, int mq_required)
{
struct ifreq ifr;
int fd, ret;
@@ -76,6 +77,20 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
ioctl(fd, TUNSETVNETHDRSZ, &len);
}
+ if (mq_required) {
+ unsigned int features;
+
+ if ((ioctl(fd, TUNGETFEATURES, &features) != 0) ||
+ !(features & IFF_MULTI_QUEUE)) {
+ error_report("multiqueue required, but no kernel "
+ "support for IFF_MULTI_QUEUE available");
+ close(fd);
+ return -1;
+ } else {
+ ifr.ifr_flags |= IFF_MULTI_QUEUE;
+ }
+ }
+
if (ifname[0] != '\0')
pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname);
else
@@ -164,7 +179,7 @@ int tap_probe_vnet_hdr_len(int fd, int len)
if (ioctl(fd, TUNSETVNETHDRSZ, &orig) == -1) {
fprintf(stderr, "TUNGETVNETHDRSZ ioctl() failed: %s. Exiting.\n",
strerror(errno));
- assert(0);
+ abort();
return -errno;
}
return 1;
@@ -175,7 +190,7 @@ void tap_fd_set_vnet_hdr_len(int fd, int len)
if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) {
fprintf(stderr, "TUNSETVNETHDRSZ ioctl() failed: %s. Exiting.\n",
strerror(errno));
- assert(0);
+ abort();
}
}
@@ -209,3 +224,53 @@ void tap_fd_set_offload(int fd, int csum, int tso4,
}
}
}
+
+/* Enable a specific queue of tap. */
+int tap_fd_enable(int fd)
+{
+ struct ifreq ifr;
+ int ret;
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ ifr.ifr_flags = IFF_ATTACH_QUEUE;
+ ret = ioctl(fd, TUNSETQUEUE, (void *) &ifr);
+
+ if (ret != 0) {
+ error_report("could not enable queue");
+ }
+
+ return ret;
+}
+
+/* Disable a specific queue of tap/ */
+int tap_fd_disable(int fd)
+{
+ struct ifreq ifr;
+ int ret;
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ ifr.ifr_flags = IFF_DETACH_QUEUE;
+ ret = ioctl(fd, TUNSETQUEUE, (void *) &ifr);
+
+ if (ret != 0) {
+ error_report("could not disable queue");
+ }
+
+ return ret;
+}
+
+int tap_fd_get_ifname(int fd, char *ifname)
+{
+ struct ifreq ifr;
+
+ if (ioctl(fd, TUNGETIFF, &ifr) != 0) {
+ error_report("TUNGETIFF ioctl() failed: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ pstrcpy(ifname, sizeof(ifr.ifr_name), ifr.ifr_name);
+ return 0;
+}
diff --git a/net/tap-linux.h b/net/tap-linux.h
index cb2a6d480a..65087e1419 100644
--- a/net/tap-linux.h
+++ b/net/tap-linux.h
@@ -29,6 +29,7 @@
#define TUNSETSNDBUF _IOW('T', 212, int)
#define TUNGETVNETHDRSZ _IOR('T', 215, int)
#define TUNSETVNETHDRSZ _IOW('T', 216, int)
+#define TUNSETQUEUE _IOW('T', 217, int)
#endif
@@ -36,6 +37,9 @@
#define IFF_TAP 0x0002
#define IFF_NO_PI 0x1000
#define IFF_VNET_HDR 0x4000
+#define IFF_MULTI_QUEUE 0x0100
+#define IFF_ATTACH_QUEUE 0x0200
+#define IFF_DETACH_QUEUE 0x0400
/* Features for GSO (TUNSETOFFLOAD). */
#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */
diff --git a/net/tap-solaris.c b/net/tap-solaris.c
index 486a7ea838..9c7278f1bf 100644
--- a/net/tap-solaris.c
+++ b/net/tap-solaris.c
@@ -173,7 +173,8 @@ static int tap_alloc(char *dev, size_t dev_size)
return tap_fd;
}
-int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
+ int vnet_hdr_required, int mq_required)
{
char dev[10]="";
int fd;
@@ -225,3 +226,18 @@ void tap_fd_set_offload(int fd, int csum, int tso4,
int tso6, int ecn, int ufo)
{
}
+
+int tap_fd_enable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_disable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_get_ifname(int fd, char *ifname)
+{
+ return -1;
+}
diff --git a/net/tap-win32.c b/net/tap-win32.c
index 265369c3c5..91e9e844a0 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -722,9 +722,9 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
return 0;
}
-int tap_has_ufo(NetClientState *nc)
+bool tap_has_ufo(NetClientState *nc)
{
- return 0;
+ return false;
}
int tap_has_vnet_hdr(NetClientState *nc)
@@ -741,7 +741,7 @@ void tap_fd_set_vnet_hdr_len(int fd, int len)
{
}
-void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr)
+void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr)
{
}
@@ -762,5 +762,15 @@ int tap_has_vnet_hdr_len(NetClientState *nc, int len)
void tap_set_vnet_hdr_len(NetClientState *nc, int len)
{
- assert(0);
+ abort();
+}
+
+int tap_enable(NetClientState *nc)
+{
+ abort();
+}
+
+int tap_disable(NetClientState *nc)
+{
+ abort();
}
diff --git a/net/tap.c b/net/tap.c
index eb40c42d7d..1bf760995d 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -55,10 +55,11 @@ typedef struct TAPState {
char down_script[1024];
char down_script_arg[128];
uint8_t buf[TAP_BUFSIZE];
- unsigned int read_poll : 1;
- unsigned int write_poll : 1;
- unsigned int using_vnet_hdr : 1;
- unsigned int has_ufo: 1;
+ bool read_poll;
+ bool write_poll;
+ bool using_vnet_hdr;
+ bool has_ufo;
+ bool enabled;
VHostNetState *vhost_net;
unsigned host_vnet_hdr_len;
} TAPState;
@@ -72,21 +73,21 @@ static void tap_writable(void *opaque);
static void tap_update_fd_handler(TAPState *s)
{
qemu_set_fd_handler2(s->fd,
- s->read_poll ? tap_can_send : NULL,
- s->read_poll ? tap_send : NULL,
- s->write_poll ? tap_writable : NULL,
+ s->read_poll && s->enabled ? tap_can_send : NULL,
+ s->read_poll && s->enabled ? tap_send : NULL,
+ s->write_poll && s->enabled ? tap_writable : NULL,
s);
}
-static void tap_read_poll(TAPState *s, int enable)
+static void tap_read_poll(TAPState *s, bool enable)
{
- s->read_poll = !!enable;
+ s->read_poll = enable;
tap_update_fd_handler(s);
}
-static void tap_write_poll(TAPState *s, int enable)
+static void tap_write_poll(TAPState *s, bool enable)
{
- s->write_poll = !!enable;
+ s->write_poll = enable;
tap_update_fd_handler(s);
}
@@ -94,7 +95,7 @@ static void tap_writable(void *opaque)
{
TAPState *s = opaque;
- tap_write_poll(s, 0);
+ tap_write_poll(s, false);
qemu_flush_queued_packets(&s->nc);
}
@@ -108,7 +109,7 @@ static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt
} while (len == -1 && errno == EINTR);
if (len == -1 && errno == EAGAIN) {
- tap_write_poll(s, 1);
+ tap_write_poll(s, true);
return 0;
}
@@ -186,7 +187,7 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
static void tap_send_completed(NetClientState *nc, ssize_t len)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- tap_read_poll(s, 1);
+ tap_read_poll(s, true);
}
static void tap_send(void *opaque)
@@ -209,12 +210,12 @@ static void tap_send(void *opaque)
size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed);
if (size == 0) {
- tap_read_poll(s, 0);
+ tap_read_poll(s, false);
}
} while (size > 0 && qemu_can_send_packet(&s->nc));
}
-int tap_has_ufo(NetClientState *nc)
+bool tap_has_ufo(NetClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
@@ -253,12 +254,10 @@ void tap_set_vnet_hdr_len(NetClientState *nc, int len)
s->host_vnet_hdr_len = len;
}
-void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr)
+void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- using_vnet_hdr = using_vnet_hdr != 0;
-
assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
assert(!!s->host_vnet_hdr_len == using_vnet_hdr);
@@ -290,8 +289,8 @@ static void tap_cleanup(NetClientState *nc)
if (s->down_script[0])
launch_script(s->down_script, s->down_script_arg, s->fd);
- tap_read_poll(s, 0);
- tap_write_poll(s, 0);
+ tap_read_poll(s, false);
+ tap_write_poll(s, false);
close(s->fd);
s->fd = -1;
}
@@ -337,8 +336,9 @@ static TAPState *net_tap_fd_init(NetClientState *peer,
s->fd = fd;
s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;
- s->using_vnet_hdr = 0;
+ s->using_vnet_hdr = false;
s->has_ufo = tap_probe_has_ufo(s->fd);
+ s->enabled = true;
tap_set_offload(&s->nc, 0, 0, 0, 0, 0);
/*
* Make sure host header length is set correctly in tap:
@@ -347,7 +347,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer,
if (tap_probe_vnet_hdr_len(s->fd, s->host_vnet_hdr_len)) {
tap_fd_set_vnet_hdr_len(s->fd, s->host_vnet_hdr_len);
}
- tap_read_poll(s, 1);
+ tap_read_poll(s, true);
s->vhost_net = NULL;
return s;
}
@@ -558,17 +558,10 @@ int net_init_bridge(const NetClientOptions *opts, const char *name,
static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr,
const char *setup_script, char *ifname,
- size_t ifname_sz)
+ size_t ifname_sz, int mq_required)
{
int fd, vnet_hdr_required;
- if (tap->has_ifname) {
- pstrcpy(ifname, ifname_sz, tap->ifname);
- } else {
- assert(ifname_sz > 0);
- ifname[0] = '\0';
- }
-
if (tap->has_vnet_hdr) {
*vnet_hdr = tap->vnet_hdr;
vnet_hdr_required = *vnet_hdr;
@@ -577,7 +570,8 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr,
vnet_hdr_required = 0;
}
- TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required));
+ TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required,
+ mq_required));
if (fd < 0) {
return -1;
}
@@ -593,27 +587,118 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr,
return fd;
}
+#define MAX_TAP_QUEUES 1024
+
+static int net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
+ const char *model, const char *name,
+ const char *ifname, const char *script,
+ const char *downscript, const char *vhostfdname,
+ int vnet_hdr, int fd)
+{
+ TAPState *s;
+
+ s = net_tap_fd_init(peer, model, name, fd, vnet_hdr);
+ if (!s) {
+ close(fd);
+ return -1;
+ }
+
+ if (tap_set_sndbuf(s->fd, tap) < 0) {
+ return -1;
+ }
+
+ if (tap->has_fd || tap->has_fds) {
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
+ } else if (tap->has_helper) {
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s",
+ tap->helper);
+ } else {
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "ifname=%s,script=%s,downscript=%s", ifname, script,
+ downscript);
+
+ if (strcmp(downscript, "no") != 0) {
+ snprintf(s->down_script, sizeof(s->down_script), "%s", downscript);
+ snprintf(s->down_script_arg, sizeof(s->down_script_arg),
+ "%s", ifname);
+ }
+ }
+
+ if (tap->has_vhost ? tap->vhost :
+ vhostfdname || (tap->has_vhostforce && tap->vhostforce)) {
+ int vhostfd;
+
+ if (tap->has_vhostfd) {
+ vhostfd = monitor_handle_fd_param(cur_mon, vhostfdname);
+ if (vhostfd == -1) {
+ return -1;
+ }
+ } else {
+ vhostfd = -1;
+ }
+
+ s->vhost_net = vhost_net_init(&s->nc, vhostfd,
+ tap->has_vhostforce && tap->vhostforce);
+ if (!s->vhost_net) {
+ error_report("vhost-net requested but could not be initialized");
+ return -1;
+ }
+ } else if (tap->has_vhostfd || tap->has_vhostfds) {
+ error_report("vhostfd= is not valid without vhost");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_fds(char *str, char *fds[], int max)
+{
+ char *ptr = str, *this;
+ size_t len = strlen(str);
+ int i = 0;
+
+ while (i < max && ptr < str + len) {
+ this = strchr(ptr, ':');
+
+ if (this == NULL) {
+ fds[i] = g_strdup(ptr);
+ } else {
+ fds[i] = g_strndup(ptr, this - ptr);
+ }
+
+ i++;
+ if (this == NULL) {
+ break;
+ } else {
+ ptr = this + 1;
+ }
+ }
+
+ return i;
+}
+
int net_init_tap(const NetClientOptions *opts, const char *name,
NetClientState *peer)
{
const NetdevTapOptions *tap;
-
- int fd, vnet_hdr = 0;
- const char *model;
- TAPState *s;
-
+ int fd, vnet_hdr = 0, i = 0, queues;
/* for the no-fd, no-helper case */
const char *script = NULL; /* suppress wrong "uninit'd use" gcc warning */
+ const char *downscript = NULL;
+ const char *vhostfdname;
char ifname[128];
assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
tap = opts->tap;
+ queues = tap->has_queues ? tap->queues : 1;
+ vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL;
if (tap->has_fd) {
if (tap->has_ifname || tap->has_script || tap->has_downscript ||
- tap->has_vnet_hdr || tap->has_helper) {
+ tap->has_vnet_hdr || tap->has_helper || tap->has_queues ||
+ tap->has_fds) {
error_report("ifname=, script=, downscript=, vnet_hdr=, "
- "and helper= are invalid with fd=");
+ "helper=, queues=, and fds= are invalid with fd=");
return -1;
}
@@ -626,13 +711,61 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
vnet_hdr = tap_probe_vnet_hdr(fd);
- model = "tap";
+ if (net_init_tap_one(tap, peer, "tap", NULL, NULL,
+ script, downscript,
+ vhostfdname, vnet_hdr, fd)) {
+ return -1;
+ }
+ } else if (tap->has_fds) {
+ char *fds[MAX_TAP_QUEUES];
+ char *vhost_fds[MAX_TAP_QUEUES];
+ int nfds, nvhosts;
+
+ if (tap->has_ifname || tap->has_script || tap->has_downscript ||
+ tap->has_vnet_hdr || tap->has_helper || tap->has_queues ||
+ tap->has_fd) {
+ error_report("ifname=, script=, downscript=, vnet_hdr=, "
+ "helper=, queues=, and fd= are invalid with fds=");
+ return -1;
+ }
+ nfds = get_fds(tap->fds, fds, MAX_TAP_QUEUES);
+ if (tap->has_vhostfds) {
+ nvhosts = get_fds(tap->vhostfds, vhost_fds, MAX_TAP_QUEUES);
+ if (nfds != nvhosts) {
+ error_report("The number of fds passed does not match the "
+ "number of vhostfds passed");
+ return -1;
+ }
+ }
+
+ for (i = 0; i < nfds; i++) {
+ fd = monitor_handle_fd_param(cur_mon, fds[i]);
+ if (fd == -1) {
+ return -1;
+ }
+
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ if (i == 0) {
+ vnet_hdr = tap_probe_vnet_hdr(fd);
+ } else if (vnet_hdr != tap_probe_vnet_hdr(fd)) {
+ error_report("vnet_hdr not consistent across given tap fds");
+ return -1;
+ }
+
+ if (net_init_tap_one(tap, peer, "tap", name, ifname,
+ script, downscript,
+ tap->has_vhostfds ? vhost_fds[i] : NULL,
+ vnet_hdr, fd)) {
+ return -1;
+ }
+ }
} else if (tap->has_helper) {
if (tap->has_ifname || tap->has_script || tap->has_downscript ||
- tap->has_vnet_hdr) {
+ tap->has_vnet_hdr || tap->has_queues || tap->has_fds) {
error_report("ifname=, script=, downscript=, and vnet_hdr= "
- "are invalid with helper=");
+ "queues=, and fds= are invalid with helper=");
return -1;
}
@@ -642,74 +775,45 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
}
fcntl(fd, F_SETFL, O_NONBLOCK);
-
vnet_hdr = tap_probe_vnet_hdr(fd);
- model = "bridge";
-
- } else {
- script = tap->has_script ? tap->script : DEFAULT_NETWORK_SCRIPT;
- fd = net_tap_init(tap, &vnet_hdr, script, ifname, sizeof ifname);
- if (fd == -1) {
+ if (net_init_tap_one(tap, peer, "bridge", name, ifname,
+ script, downscript, vhostfdname,
+ vnet_hdr, fd)) {
return -1;
}
-
- model = "tap";
- }
-
- s = net_tap_fd_init(peer, model, name, fd, vnet_hdr);
- if (!s) {
- close(fd);
- return -1;
- }
-
- if (tap_set_sndbuf(s->fd, tap) < 0) {
- return -1;
- }
-
- if (tap->has_fd) {
- snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
- } else if (tap->has_helper) {
- snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s",
- tap->helper);
} else {
- const char *downscript;
-
+ script = tap->has_script ? tap->script : DEFAULT_NETWORK_SCRIPT;
downscript = tap->has_downscript ? tap->downscript :
- DEFAULT_NETWORK_DOWN_SCRIPT;
+ DEFAULT_NETWORK_DOWN_SCRIPT;
- snprintf(s->nc.info_str, sizeof(s->nc.info_str),
- "ifname=%s,script=%s,downscript=%s", ifname, script,
- downscript);
-
- if (strcmp(downscript, "no") != 0) {
- snprintf(s->down_script, sizeof(s->down_script), "%s", downscript);
- snprintf(s->down_script_arg, sizeof(s->down_script_arg), "%s", ifname);
+ if (tap->has_ifname) {
+ pstrcpy(ifname, sizeof ifname, tap->ifname);
+ } else {
+ ifname[0] = '\0';
}
- }
-
- if (tap->has_vhost ? tap->vhost :
- tap->has_vhostfd || (tap->has_vhostforce && tap->vhostforce)) {
- int vhostfd;
- if (tap->has_vhostfd) {
- vhostfd = monitor_handle_fd_param(cur_mon, tap->vhostfd);
- if (vhostfd == -1) {
+ for (i = 0; i < queues; i++) {
+ fd = net_tap_init(tap, &vnet_hdr, i >= 1 ? "no" : script,
+ ifname, sizeof ifname, queues > 1);
+ if (fd == -1) {
return -1;
}
- } else {
- vhostfd = -1;
- }
- s->vhost_net = vhost_net_init(&s->nc, vhostfd,
- tap->has_vhostforce && tap->vhostforce);
- if (!s->vhost_net) {
- error_report("vhost-net requested but could not be initialized");
- return -1;
+ if (queues > 1 && i == 0 && !tap->has_ifname) {
+ if (tap_fd_get_ifname(fd, ifname)) {
+ error_report("Fail to get ifname");
+ return -1;
+ }
+ }
+
+ if (net_init_tap_one(tap, peer, "tap", name, ifname,
+ i >= 1 ? "no" : script,
+ i >= 1 ? "no" : downscript,
+ vhostfdname, vnet_hdr, fd)) {
+ return -1;
+ }
}
- } else if (tap->has_vhostfd) {
- error_report("vhostfd= is not valid without vhost");
- return -1;
}
return 0;
@@ -721,3 +825,38 @@ VHostNetState *tap_get_vhost_net(NetClientState *nc)
assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
return s->vhost_net;
}
+
+int tap_enable(NetClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ int ret;
+
+ if (s->enabled) {
+ return 0;
+ } else {
+ ret = tap_fd_enable(s->fd);
+ if (ret == 0) {
+ s->enabled = true;
+ tap_update_fd_handler(s);
+ }
+ return ret;
+ }
+}
+
+int tap_disable(NetClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ int ret;
+
+ if (s->enabled == 0) {
+ return 0;
+ } else {
+ ret = tap_fd_disable(s->fd);
+ if (ret == 0) {
+ qemu_purge_queued_packets(nc);
+ s->enabled = false;
+ tap_update_fd_handler(s);
+ }
+ return ret;
+ }
+}
diff --git a/net/tap_int.h b/net/tap_int.h
index 1dffe12a45..86bb224bc8 100644
--- a/net/tap_int.h
+++ b/net/tap_int.h
@@ -32,7 +32,8 @@
#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
-int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required);
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
+ int vnet_hdr_required, int mq_required);
ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen);
@@ -42,5 +43,8 @@ int tap_probe_vnet_hdr_len(int fd, int len);
int tap_probe_has_ufo(int fd);
void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo);
void tap_fd_set_vnet_hdr_len(int fd, int len);
+int tap_fd_enable(int fd);
+int tap_fd_disable(int fd);
+int tap_fd_get_ifname(int fd, char *ifname);
#endif /* QEMU_TAP_H */
diff --git a/qapi-schema.json b/qapi-schema.json
index 3a4817b391..cdd8384915 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2533,6 +2533,7 @@
'data': {
'*ifname': 'str',
'*fd': 'str',
+ '*fds': 'str',
'*script': 'str',
'*downscript': 'str',
'*helper': 'str',
@@ -2540,7 +2541,9 @@
'*vnet_hdr': 'bool',
'*vhost': 'bool',
'*vhostfd': 'str',
- '*vhostforce': 'bool' } }
+ '*vhostfds': 'str',
+ '*vhostforce': 'bool',
+ '*queues': 'uint32'} }
##
# @NetdevSocketOptions
diff --git a/qmp-commands.hx b/qmp-commands.hx
index f90efe590c..bbb21f3583 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2601,10 +2601,8 @@ Arguments:
Example:
-> { "execute": "query-migrate-capabilities" }
-<- { "return": {
- "capabilities" : [ { "capability" : "xbzrle", "state" : false } ]
- }
- }
+<- { "return": [ { "state": false, "capability": "xbzrle" } ] }
+
EQMP
{
diff --git a/savevm.c b/savevm.c
index 304d1effe5..4eb29b2ae2 100644
--- a/savevm.c
+++ b/savevm.c
@@ -81,7 +81,7 @@ static void qemu_announce_self_iter(NICState *nic, void *opaque)
len = announce_self_create(buf, nic->conf->macaddr.a);
- qemu_send_packet_raw(&nic->nc, buf, len);
+ qemu_send_packet_raw(qemu_get_queue(nic), buf, len);
}
@@ -2388,162 +2388,3 @@ void vmstate_register_ram_global(MemoryRegion *mr)
{
vmstate_register_ram(mr, NULL);
}
-
-/*
- page = zrun nzrun
- | zrun nzrun page
-
- zrun = length
-
- nzrun = length byte...
-
- length = uleb128 encoded integer
- */
-int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen,
- uint8_t *dst, int dlen)
-{
- uint32_t zrun_len = 0, nzrun_len = 0;
- int d = 0, i = 0;
- long res, xor;
- uint8_t *nzrun_start = NULL;
-
- g_assert(!(((uintptr_t)old_buf | (uintptr_t)new_buf | slen) %
- sizeof(long)));
-
- while (i < slen) {
- /* overflow */
- if (d + 2 > dlen) {
- return -1;
- }
-
- /* not aligned to sizeof(long) */
- res = (slen - i) % sizeof(long);
- while (res && old_buf[i] == new_buf[i]) {
- zrun_len++;
- i++;
- res--;
- }
-
- /* word at a time for speed */
- if (!res) {
- while (i < slen &&
- (*(long *)(old_buf + i)) == (*(long *)(new_buf + i))) {
- i += sizeof(long);
- zrun_len += sizeof(long);
- }
-
- /* go over the rest */
- while (i < slen && old_buf[i] == new_buf[i]) {
- zrun_len++;
- i++;
- }
- }
-
- /* buffer unchanged */
- if (zrun_len == slen) {
- return 0;
- }
-
- /* skip last zero run */
- if (i == slen) {
- return d;
- }
-
- d += uleb128_encode_small(dst + d, zrun_len);
-
- zrun_len = 0;
- nzrun_start = new_buf + i;
-
- /* overflow */
- if (d + 2 > dlen) {
- return -1;
- }
- /* not aligned to sizeof(long) */
- res = (slen - i) % sizeof(long);
- while (res && old_buf[i] != new_buf[i]) {
- i++;
- nzrun_len++;
- res--;
- }
-
- /* word at a time for speed, use of 32-bit long okay */
- if (!res) {
- /* truncation to 32-bit long okay */
- long mask = (long)0x0101010101010101ULL;
- while (i < slen) {
- xor = *(long *)(old_buf + i) ^ *(long *)(new_buf + i);
- if ((xor - mask) & ~xor & (mask << 7)) {
- /* found the end of an nzrun within the current long */
- while (old_buf[i] != new_buf[i]) {
- nzrun_len++;
- i++;
- }
- break;
- } else {
- i += sizeof(long);
- nzrun_len += sizeof(long);
- }
- }
- }
-
- d += uleb128_encode_small(dst + d, nzrun_len);
- /* overflow */
- if (d + nzrun_len > dlen) {
- return -1;
- }
- memcpy(dst + d, nzrun_start, nzrun_len);
- d += nzrun_len;
- nzrun_len = 0;
- }
-
- return d;
-}
-
-int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen)
-{
- int i = 0, d = 0;
- int ret;
- uint32_t count = 0;
-
- while (i < slen) {
-
- /* zrun */
- if ((slen - i) < 2) {
- return -1;
- }
-
- ret = uleb128_decode_small(src + i, &count);
- if (ret < 0 || (i && !count)) {
- return -1;
- }
- i += ret;
- d += count;
-
- /* overflow */
- if (d > dlen) {
- return -1;
- }
-
- /* nzrun */
- if ((slen - i) < 2) {
- return -1;
- }
-
- ret = uleb128_decode_small(src + i, &count);
- if (ret < 0 || !count) {
- return -1;
- }
- i += ret;
-
- /* overflow */
- if (d + count > dlen || i + count > slen) {
- return -1;
- }
-
- memcpy(dst + d, src + i, count);
- d += count;
- i += count;
- }
-
- return d;
-}
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 953146eeba..8c081dbec5 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -1857,10 +1857,8 @@ enum {
PPC_CACHE = 0x0000000200000000ULL,
/* icbi instruction */
PPC_CACHE_ICBI = 0x0000000400000000ULL,
- /* dcbz instruction with fixed cache line size */
+ /* dcbz instruction */
PPC_CACHE_DCBZ = 0x0000000800000000ULL,
- /* dcbz instruction with tunable cache line size */
- PPC_CACHE_DCBZT = 0x0000001000000000ULL,
/* dcba instruction */
PPC_CACHE_DCBA = 0x0000002000000000ULL,
/* Freescale cache locking instructions */
@@ -1928,7 +1926,7 @@ enum {
| PPC_MEM_TLBIE | PPC_MEM_TLBSYNC \
| PPC_MEM_SYNC | PPC_MEM_EIEIO \
| PPC_CACHE | PPC_CACHE_ICBI \
- | PPC_CACHE_DCBZ | PPC_CACHE_DCBZT \
+ | PPC_CACHE_DCBZ \
| PPC_CACHE_DCBA | PPC_CACHE_LOCK \
| PPC_EXTERN | PPC_SEGMENT | PPC_6xx_TLB \
| PPC_74xx_TLB | PPC_40x_TLB | PPC_SEGMENT_64B \
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index 83139d5225..18e039452f 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -25,8 +25,7 @@ DEF_HELPER_3(stmw, void, env, tl, i32)
DEF_HELPER_4(lsw, void, env, tl, i32, i32)
DEF_HELPER_5(lswx, void, env, tl, i32, i32, i32)
DEF_HELPER_4(stsw, void, env, tl, i32, i32)
-DEF_HELPER_2(dcbz, void, env, tl)
-DEF_HELPER_2(dcbz_970, void, env, tl)
+DEF_HELPER_3(dcbz, void, env, tl, i32)
DEF_HELPER_2(icbi, void, env, tl)
DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32)
diff --git a/target-ppc/mem_helper.c b/target-ppc/mem_helper.c
index 902b1cd823..ba383c8f11 100644
--- a/target-ppc/mem_helper.c
+++ b/target-ppc/mem_helper.c
@@ -136,18 +136,21 @@ static void do_dcbz(CPUPPCState *env, target_ulong addr, int dcache_line_size)
}
}
-void helper_dcbz(CPUPPCState *env, target_ulong addr)
+void helper_dcbz(CPUPPCState *env, target_ulong addr, uint32_t is_dcbzl)
{
- do_dcbz(env, addr, env->dcache_line_size);
-}
+ int dcbz_size = env->dcache_line_size;
-void helper_dcbz_970(CPUPPCState *env, target_ulong addr)
-{
- if (((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) {
- do_dcbz(env, addr, 32);
- } else {
- do_dcbz(env, addr, env->dcache_line_size);
+#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
+ if (!is_dcbzl &&
+ (env->excp_model == POWERPC_EXCP_970) &&
+ ((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) {
+ dcbz_size = 32;
}
+#endif
+
+ /* XXX add e500mc support */
+
+ do_dcbz(env, addr, dcbz_size);
}
void helper_icbi(CPUPPCState *env, target_ulong addr)
diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c
index 0aee7a9063..1cc1c1649a 100644
--- a/target-ppc/mmu_helper.c
+++ b/target-ppc/mmu_helper.c
@@ -587,7 +587,7 @@ static inline int find_pte2(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h,
}
r = pte64_check(ctx, pte0, pte1, h, rw, type);
- LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " "
+ LOG_MMU("Load pte from %016" HWADDR_PRIx " => " TARGET_FMT_lx " "
TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n",
pteg_off + (i * 16), pte0, pte1, (int)(pte0 & 1), h,
(int)((pte0 >> 1) & 1), ctx->ptem);
@@ -602,7 +602,7 @@ static inline int find_pte2(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h,
pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4);
}
r = pte32_check(ctx, pte0, pte1, h, rw, type);
- LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " "
+ LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " "
TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n",
pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h,
(int)((pte0 >> 6) & 1), ctx->ptem);
@@ -633,7 +633,7 @@ static inline int find_pte2(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h,
}
if (good != -1) {
done:
- LOG_MMU("found PTE at addr " TARGET_FMT_lx " prot=%01x ret=%d\n",
+ LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n",
ctx->raddr, ctx->prot, ret);
/* Update page flags */
pte1 = ctx->raddr;
@@ -2260,8 +2260,9 @@ void helper_store_601_batu(CPUPPCState *env, uint32_t nr, target_ulong value)
void helper_store_601_batl(CPUPPCState *env, uint32_t nr, target_ulong value)
{
+#if !defined(FLUSH_ALL_TLBS)
target_ulong mask;
-#if defined(FLUSH_ALL_TLBS)
+#else
int do_inval;
#endif
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 798b7acfc9..2ac5794add 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -4118,29 +4118,21 @@ static void gen_dcbtst(DisasContext *ctx)
/* dcbz */
static void gen_dcbz(DisasContext *ctx)
{
- TCGv t0;
- gen_set_access_type(ctx, ACCESS_CACHE);
- /* NIP cannot be restored if the memory exception comes from an helper */
- gen_update_nip(ctx, ctx->nip - 4);
- t0 = tcg_temp_new();
- gen_addr_reg_index(ctx, t0);
- gen_helper_dcbz(cpu_env, t0);
- tcg_temp_free(t0);
-}
+ TCGv tcgv_addr;
+ TCGv_i32 tcgv_is_dcbzl;
+ int is_dcbzl = ctx->opcode & 0x00200000 ? 1 : 0;
-static void gen_dcbz_970(DisasContext *ctx)
-{
- TCGv t0;
gen_set_access_type(ctx, ACCESS_CACHE);
/* NIP cannot be restored if the memory exception comes from an helper */
gen_update_nip(ctx, ctx->nip - 4);
- t0 = tcg_temp_new();
- gen_addr_reg_index(ctx, t0);
- if (ctx->opcode & 0x00200000)
- gen_helper_dcbz(cpu_env, t0);
- else
- gen_helper_dcbz_970(cpu_env, t0);
- tcg_temp_free(t0);
+ tcgv_addr = tcg_temp_new();
+ tcgv_is_dcbzl = tcg_const_i32(is_dcbzl);
+
+ gen_addr_reg_index(ctx, tcgv_addr);
+ gen_helper_dcbz(cpu_env, tcgv_addr, tcgv_is_dcbzl);
+
+ tcg_temp_free(tcgv_addr);
+ tcg_temp_free_i32(tcgv_is_dcbzl);
}
/* dst / dstt */
@@ -8648,8 +8640,7 @@ GEN_HANDLER(dcbi, 0x1F, 0x16, 0x0E, 0x03E00001, PPC_CACHE),
GEN_HANDLER(dcbst, 0x1F, 0x16, 0x01, 0x03E00001, PPC_CACHE),
GEN_HANDLER(dcbt, 0x1F, 0x16, 0x08, 0x02000001, PPC_CACHE),
GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x02000001, PPC_CACHE),
-GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03E00001, PPC_CACHE_DCBZ),
-GEN_HANDLER2(dcbz_970, "dcbz", 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZT),
+GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZ),
GEN_HANDLER(dst, 0x1F, 0x16, 0x0A, 0x01800001, PPC_ALTIVEC),
GEN_HANDLER(dstst, 0x1F, 0x16, 0x0B, 0x02000001, PPC_ALTIVEC),
GEN_HANDLER(dss, 0x1F, 0x16, 0x19, 0x019FF801, PPC_ALTIVEC),
@@ -9698,7 +9689,7 @@ static inline void gen_intermediate_code_internal(CPUPPCState *env,
}
LOG_DISAS("translate opcode %08x (%02x %02x %02x) (%s)\n",
ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode),
- opc3(ctx.opcode), little_endian ? "little" : "big");
+ opc3(ctx.opcode), ctx.le_mode ? "little" : "big");
if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) {
tcg_gen_debug_insn_start(ctx.nip);
}
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index e143af532a..e2021c4a05 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -6298,7 +6298,7 @@ static void init_proc_7457 (CPUPPCState *env)
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
PPC_FLOAT_STFIWX | \
- PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
PPC_MEM_SYNC | PPC_MEM_EIEIO | \
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
PPC_64B | PPC_ALTIVEC | \
@@ -6394,7 +6394,7 @@ static void init_proc_970 (CPUPPCState *env)
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
PPC_FLOAT_STFIWX | \
- PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
PPC_MEM_SYNC | PPC_MEM_EIEIO | \
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
PPC_64B | PPC_ALTIVEC | \
@@ -6496,7 +6496,7 @@ static void init_proc_970FX (CPUPPCState *env)
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
PPC_FLOAT_STFIWX | \
- PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
PPC_MEM_SYNC | PPC_MEM_EIEIO | \
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
PPC_64B | PPC_ALTIVEC | \
@@ -6586,7 +6586,7 @@ static void init_proc_970GX (CPUPPCState *env)
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
PPC_FLOAT_STFIWX | \
- PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
PPC_MEM_SYNC | PPC_MEM_EIEIO | \
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
PPC_64B | PPC_ALTIVEC | \
@@ -6677,7 +6677,7 @@ static void init_proc_970MP (CPUPPCState *env)
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
PPC_FLOAT_STFIWX | \
- PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
PPC_MEM_SYNC | PPC_MEM_EIEIO | \
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
PPC_64B | PPC_ALTIVEC | \
diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c
index a0c4479f39..d765e7b984 100644
--- a/target-s390x/cpu.c
+++ b/target-s390x/cpu.c
@@ -70,7 +70,7 @@ static void s390_cpu_reset(CPUState *s)
log_cpu_state(env, 0);
}
- s390_del_running_cpu(env);
+ s390_del_running_cpu(cpu);
scc->parent_reset(s);
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 9be4a475a3..01e59b99f0 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -375,8 +375,8 @@ static inline void kvm_s390_interrupt_internal(S390CPU *cpu, int type,
}
#endif
S390CPU *s390_cpu_addr2state(uint16_t cpu_addr);
-void s390_add_running_cpu(CPUS390XState *env);
-unsigned s390_del_running_cpu(CPUS390XState *env);
+void s390_add_running_cpu(S390CPU *cpu);
+unsigned s390_del_running_cpu(S390CPU *cpu);
/* service interrupts are floating therefore we must not pass an cpustate */
void s390_sclp_extint(uint32_t parm);
@@ -385,11 +385,11 @@ void s390_sclp_extint(uint32_t parm);
extern const hwaddr virtio_size;
#else
-static inline void s390_add_running_cpu(CPUS390XState *env)
+static inline void s390_add_running_cpu(S390CPU *cpu)
{
}
-static inline unsigned s390_del_running_cpu(CPUS390XState *env)
+static inline unsigned s390_del_running_cpu(S390CPU *cpu)
{
return 0;
}
@@ -975,9 +975,11 @@ static inline uint64_t time2tod(uint64_t ns) {
return (ns << 9) / 125;
}
-static inline void cpu_inject_ext(CPUS390XState *env, uint32_t code, uint32_t param,
+static inline void cpu_inject_ext(S390CPU *cpu, uint32_t code, uint32_t param,
uint64_t param64)
{
+ CPUS390XState *env = &cpu->env;
+
if (env->ext_index == MAX_EXT_QUEUE - 1) {
/* ugh - can't queue anymore. Let's drop. */
return;
@@ -994,10 +996,11 @@ static inline void cpu_inject_ext(CPUS390XState *env, uint32_t code, uint32_t pa
cpu_interrupt(env, CPU_INTERRUPT_HARD);
}
-static inline void cpu_inject_io(CPUS390XState *env, uint16_t subchannel_id,
+static inline void cpu_inject_io(S390CPU *cpu, uint16_t subchannel_id,
uint16_t subchannel_number,
uint32_t io_int_parm, uint32_t io_int_word)
{
+ CPUS390XState *env = &cpu->env;
int isc = ffs(io_int_word << 2) - 1;
if (env->io_index[isc] == MAX_IO_QUEUE - 1) {
@@ -1017,8 +1020,10 @@ static inline void cpu_inject_io(CPUS390XState *env, uint16_t subchannel_id,
cpu_interrupt(env, CPU_INTERRUPT_HARD);
}
-static inline void cpu_inject_crw_mchk(CPUS390XState *env)
+static inline void cpu_inject_crw_mchk(S390CPU *cpu)
{
+ CPUS390XState *env = &cpu->env;
+
if (env->mchk_index == MAX_MCHK_QUEUE - 1) {
/* ugh - can't queue anymore. Let's drop. */
return;
@@ -1090,7 +1095,7 @@ static inline void s390_io_interrupt(S390CPU *cpu,
kvm_s390_io_interrupt(cpu, subchannel_id, subchannel_nr, io_int_parm,
io_int_word);
} else {
- cpu_inject_io(&cpu->env, subchannel_id, subchannel_nr, io_int_parm,
+ cpu_inject_io(cpu, subchannel_id, subchannel_nr, io_int_parm,
io_int_word);
}
}
@@ -1100,7 +1105,7 @@ static inline void s390_crw_mchk(S390CPU *cpu)
if (kvm_enabled()) {
kvm_s390_crw_mchk(cpu);
} else {
- cpu_inject_crw_mchk(&cpu->env);
+ cpu_inject_crw_mchk(cpu);
}
}
diff --git a/target-s390x/helper.c b/target-s390x/helper.c
index 857c89725c..3180b90ed8 100644
--- a/target-s390x/helper.c
+++ b/target-s390x/helper.c
@@ -387,7 +387,7 @@ int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong orig_vaddr,
int prot;
DPRINTF("%s: address 0x%" PRIx64 " rw %d mmu_idx %d\n",
- __func__, _vaddr, rw, mmu_idx);
+ __func__, orig_vaddr, rw, mmu_idx);
orig_vaddr &= TARGET_PAGE_MASK;
vaddr = orig_vaddr;
@@ -404,8 +404,8 @@ int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong orig_vaddr,
/* check out of RAM access */
if (raddr > (ram_size + virtio_size)) {
- DPRINTF("%s: aaddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__,
- (uint64_t)aaddr, (uint64_t)ram_size);
+ DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__,
+ (uint64_t)raddr, (uint64_t)ram_size);
trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_LATER);
return 1;
}
@@ -441,8 +441,9 @@ hwaddr cpu_get_phys_page_debug(CPUS390XState *env,
void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr)
{
if (mask & PSW_MASK_WAIT) {
+ S390CPU *cpu = s390_env_get_cpu(env);
if (!(mask & (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK))) {
- if (s390_del_running_cpu(env) == 0) {
+ if (s390_del_running_cpu(cpu) == 0) {
#ifndef CONFIG_USER_ONLY
qemu_system_shutdown_request();
#endif
@@ -737,10 +738,12 @@ static void do_mchk_interrupt(CPUS390XState *env)
void do_interrupt(CPUS390XState *env)
{
+ S390CPU *cpu = s390_env_get_cpu(env);
+
qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n",
__func__, env->exception_index, env->psw.addr);
- s390_add_running_cpu(env);
+ s390_add_running_cpu(cpu);
/* handle machine checks */
if ((env->psw.mask & PSW_MASK_MCHECK) &&
(env->exception_index == -1)) {
@@ -755,12 +758,12 @@ void do_interrupt(CPUS390XState *env)
/* code is already in env */
env->exception_index = EXCP_EXT;
} else if (env->pending_int & INTERRUPT_TOD) {
- cpu_inject_ext(env, 0x1004, 0, 0);
+ cpu_inject_ext(cpu, 0x1004, 0, 0);
env->exception_index = EXCP_EXT;
env->pending_int &= ~INTERRUPT_EXT;
env->pending_int &= ~INTERRUPT_TOD;
} else if (env->pending_int & INTERRUPT_CPUTIMER) {
- cpu_inject_ext(env, 0x1005, 0, 0);
+ cpu_inject_ext(cpu, 0x1005, 0, 0);
env->exception_index = EXCP_EXT;
env->pending_int &= ~INTERRUPT_EXT;
env->pending_int &= ~INTERRUPT_TOD;
diff --git a/target-s390x/interrupt.c b/target-s390x/interrupt.c
index e51519dbd7..6d6580de3a 100644
--- a/target-s390x/interrupt.c
+++ b/target-s390x/interrupt.c
@@ -24,7 +24,7 @@ void s390_sclp_extint(uint32_t parm)
#endif
} else {
env->psw.addr += 4;
- cpu_inject_ext(env, EXT_SERVICE, parm, 0);
+ cpu_inject_ext(dummy_cpu, EXT_SERVICE, parm, 0);
}
}
#endif
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index 2c24182001..3929771182 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -570,12 +570,10 @@ static int handle_diag(CPUS390XState *env, struct kvm_run *run, int ipb_code)
static int s390_cpu_restart(S390CPU *cpu)
{
- CPUS390XState *env = &cpu->env;
-
kvm_s390_interrupt(cpu, KVM_S390_RESTART, 0);
- s390_add_running_cpu(env);
+ s390_add_running_cpu(cpu);
qemu_cpu_kick(CPU(cpu));
- dprintf("DONE: SIGP cpu restart: %p\n", env);
+ dprintf("DONE: SIGP cpu restart: %p\n", &cpu->env);
return 0;
}
@@ -591,7 +589,7 @@ static int s390_cpu_initial_reset(S390CPU *cpu)
CPUS390XState *env = &cpu->env;
int i;
- s390_del_running_cpu(env);
+ s390_del_running_cpu(cpu);
if (kvm_vcpu_ioctl(CPU(cpu), KVM_S390_INITIAL_RESET, NULL) < 0) {
perror("cannot init reset vcpu");
}
@@ -701,7 +699,6 @@ static bool is_special_wait_psw(CPUState *cs)
static int handle_intercept(S390CPU *cpu)
{
- CPUS390XState *env = &cpu->env;
CPUState *cs = CPU(cpu);
struct kvm_run *run = cs->kvm_run;
int icpt_code = run->s390_sieic.icptcode;
@@ -714,14 +711,14 @@ static int handle_intercept(S390CPU *cpu)
r = handle_instruction(cpu, run);
break;
case ICPT_WAITPSW:
- if (s390_del_running_cpu(env) == 0 &&
+ if (s390_del_running_cpu(cpu) == 0 &&
is_special_wait_psw(cs)) {
qemu_system_shutdown_request();
}
r = EXCP_HALTED;
break;
case ICPT_CPU_STOP:
- if (s390_del_running_cpu(env) == 0) {
+ if (s390_del_running_cpu(cpu) == 0) {
qemu_system_shutdown_request();
}
r = EXCP_HALTED;
diff --git a/tests/Makefile b/tests/Makefile
index c681cebd18..abe9c2a6c4 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -50,6 +50,8 @@ check-unit-y += tests/test-hbitmap$(EXESUF)
check-unit-y += tests/test-x86-cpuid$(EXESUF)
# all code tested by test-x86-cpuid is inside topology.h
gcov-files-test-x86-cpuid-y =
+check-unit-y += tests/test-xbzrle$(EXESUF)
+gcov-files-test-xbzrle-y = xbzrle.c
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -98,6 +100,7 @@ tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemu
tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a
tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a
tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o
+tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o xbzrle.o page_cache.o libqemuutil.a
tests/test-qapi-types.c tests/test-qapi-types.h :\
$(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
diff --git a/tests/libqtest.c b/tests/libqtest.c
index 913fa0535c..762dec4ac0 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -39,7 +39,8 @@ struct QTestState
int qmp_fd;
bool irq_level[MAX_IRQ];
GString *rx;
- gchar *pid_file;
+ gchar *pid_file; /* QEMU PID file */
+ int child_pid; /* Child process created to execute QEMU */
char *socket_path, *qmp_socket_path;
};
@@ -144,6 +145,7 @@ QTestState *qtest_init(const char *extra_args)
s->rx = g_string_new("");
s->pid_file = pid_file;
+ s->child_pid = pid;
for (i = 0; i < MAX_IRQ; i++) {
s->irq_level[i] = false;
}
@@ -165,8 +167,9 @@ void qtest_quit(QTestState *s)
pid_t pid = qtest_qemu_pid(s);
if (pid != -1) {
+ /* kill QEMU, but wait for the child created by us to run system() */
kill(pid, SIGTERM);
- waitpid(pid, &status, 0);
+ waitpid(s->child_pid, &status, 0);
}
unlink(s->pid_file);
diff --git a/tests/test-xbzrle.c b/tests/test-xbzrle.c
new file mode 100644
index 0000000000..db93b0a3d2
--- /dev/null
+++ b/tests/test-xbzrle.c
@@ -0,0 +1,196 @@
+/*
+ * Xor Based Zero Run Length Encoding unit tests.
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ * Orit Wasserman <owasserm@redhat.com>
+ *
+ * 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <sys/time.h>
+#include <assert.h>
+#include "qemu-common.h"
+#include "include/migration/migration.h"
+
+#define PAGE_SIZE 4096
+
+static void test_uleb(void)
+{
+ uint32_t i, val;
+ uint8_t buf[2];
+ int encode_ret, decode_ret;
+
+ for (i = 0; i <= 0x3fff; i++) {
+ encode_ret = uleb128_encode_small(&buf[0], i);
+ decode_ret = uleb128_decode_small(&buf[0], &val);
+ g_assert(encode_ret == decode_ret);
+ g_assert(i == val);
+ }
+
+ /* decode invalid value */
+ buf[0] = 0x80;
+ buf[1] = 0x80;
+
+ decode_ret = uleb128_decode_small(&buf[0], &val);
+ g_assert(decode_ret == -1);
+ g_assert(val == 0);
+}
+
+static void test_encode_decode_zero(void)
+{
+ uint8_t *buffer = g_malloc0(PAGE_SIZE);
+ uint8_t *compressed = g_malloc0(PAGE_SIZE);
+ int i = 0;
+ int dlen = 0;
+ int diff_len = g_test_rand_int_range(0, PAGE_SIZE - 1006);
+
+ for (i = diff_len; i > 0; i--) {
+ buffer[1000 + i] = i;
+ }
+
+ buffer[1000 + diff_len + 3] = 103;
+ buffer[1000 + diff_len + 5] = 105;
+
+ /* encode zero page */
+ dlen = xbzrle_encode_buffer(buffer, buffer, PAGE_SIZE, compressed,
+ PAGE_SIZE);
+ g_assert(dlen == 0);
+
+ g_free(buffer);
+ g_free(compressed);
+}
+
+static void test_encode_decode_unchanged(void)
+{
+ uint8_t *compressed = g_malloc0(PAGE_SIZE);
+ uint8_t *test = g_malloc0(PAGE_SIZE);
+ int i = 0;
+ int dlen = 0;
+ int diff_len = g_test_rand_int_range(0, PAGE_SIZE - 1006);
+
+ for (i = diff_len; i > 0; i--) {
+ test[1000 + i] = i + 4;
+ }
+
+ test[1000 + diff_len + 3] = 107;
+ test[1000 + diff_len + 5] = 109;
+
+ /* test unchanged buffer */
+ dlen = xbzrle_encode_buffer(test, test, PAGE_SIZE, compressed,
+ PAGE_SIZE);
+ g_assert(dlen == 0);
+
+ g_free(test);
+ g_free(compressed);
+}
+
+static void test_encode_decode_1_byte(void)
+{
+ uint8_t *buffer = g_malloc0(PAGE_SIZE);
+ uint8_t *test = g_malloc0(PAGE_SIZE);
+ uint8_t *compressed = g_malloc(PAGE_SIZE);
+ int dlen = 0, rc = 0;
+ uint8_t buf[2];
+
+ test[PAGE_SIZE - 1] = 1;
+
+ dlen = xbzrle_encode_buffer(buffer, test, PAGE_SIZE, compressed,
+ PAGE_SIZE);
+ g_assert(dlen == (uleb128_encode_small(&buf[0], 4095) + 2));
+
+ rc = xbzrle_decode_buffer(compressed, dlen, buffer, PAGE_SIZE);
+ g_assert(rc == PAGE_SIZE);
+ g_assert(memcmp(test, buffer, PAGE_SIZE) == 0);
+
+ g_free(buffer);
+ g_free(compressed);
+ g_free(test);
+}
+
+static void test_encode_decode_overflow(void)
+{
+ uint8_t *compressed = g_malloc0(PAGE_SIZE);
+ uint8_t *test = g_malloc0(PAGE_SIZE);
+ uint8_t *buffer = g_malloc0(PAGE_SIZE);
+ int i = 0, rc = 0;
+
+ for (i = 0; i < PAGE_SIZE / 2 - 1; i++) {
+ test[i * 2] = 1;
+ }
+
+ /* encode overflow */
+ rc = xbzrle_encode_buffer(buffer, test, PAGE_SIZE, compressed,
+ PAGE_SIZE);
+ g_assert(rc == -1);
+
+ g_free(buffer);
+ g_free(compressed);
+ g_free(test);
+}
+
+static void encode_decode_range(void)
+{
+ uint8_t *buffer = g_malloc0(PAGE_SIZE);
+ uint8_t *compressed = g_malloc(PAGE_SIZE);
+ uint8_t *test = g_malloc0(PAGE_SIZE);
+ int i = 0, rc = 0;
+ int dlen = 0;
+
+ int diff_len = g_test_rand_int_range(0, PAGE_SIZE - 1006);
+
+ for (i = diff_len; i > 0; i--) {
+ buffer[1000 + i] = i;
+ test[1000 + i] = i + 4;
+ }
+
+ buffer[1000 + diff_len + 3] = 103;
+ test[1000 + diff_len + 3] = 107;
+
+ buffer[1000 + diff_len + 5] = 105;
+ test[1000 + diff_len + 5] = 109;
+
+ /* test encode/decode */
+ dlen = xbzrle_encode_buffer(test, buffer, PAGE_SIZE, compressed,
+ PAGE_SIZE);
+
+ rc = xbzrle_decode_buffer(compressed, dlen, test, PAGE_SIZE);
+ g_assert(rc < PAGE_SIZE);
+ g_assert(memcmp(test, buffer, PAGE_SIZE) == 0);
+
+ g_free(buffer);
+ g_free(compressed);
+ g_free(test);
+}
+
+static void test_encode_decode(void)
+{
+ int i;
+
+ for (i = 0; i < 10000; i++) {
+ encode_decode_range();
+ }
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ g_test_rand_int();
+ g_test_add_func("/xbzrle/uleb", test_uleb);
+ g_test_add_func("/xbzrle/encode_decode_zero", test_encode_decode_zero);
+ g_test_add_func("/xbzrle/encode_decode_unchanged",
+ test_encode_decode_unchanged);
+ g_test_add_func("/xbzrle/encode_decode_1_byte", test_encode_decode_1_byte);
+ g_test_add_func("/xbzrle/encode_decode_overflow",
+ test_encode_decode_overflow);
+ g_test_add_func("/xbzrle/encode_decode", test_encode_decode);
+
+ return g_test_run();
+}
diff --git a/xbzrle.c b/xbzrle.c
new file mode 100644
index 0000000000..fbcb35d0e3
--- /dev/null
+++ b/xbzrle.c
@@ -0,0 +1,173 @@
+/*
+ * Xor Based Zero Run Length Encoding
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ * Orit Wasserman <owasserm@redhat.com>
+ *
+ * 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 "qemu-common.h"
+#include "include/migration/migration.h"
+
+/*
+ page = zrun nzrun
+ | zrun nzrun page
+
+ zrun = length
+
+ nzrun = length byte...
+
+ length = uleb128 encoded integer
+ */
+int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen,
+ uint8_t *dst, int dlen)
+{
+ uint32_t zrun_len = 0, nzrun_len = 0;
+ int d = 0, i = 0;
+ long res, xor;
+ uint8_t *nzrun_start = NULL;
+
+ g_assert(!(((uintptr_t)old_buf | (uintptr_t)new_buf | slen) %
+ sizeof(long)));
+
+ while (i < slen) {
+ /* overflow */
+ if (d + 2 > dlen) {
+ return -1;
+ }
+
+ /* not aligned to sizeof(long) */
+ res = (slen - i) % sizeof(long);
+ while (res && old_buf[i] == new_buf[i]) {
+ zrun_len++;
+ i++;
+ res--;
+ }
+
+ /* word at a time for speed */
+ if (!res) {
+ while (i < slen &&
+ (*(long *)(old_buf + i)) == (*(long *)(new_buf + i))) {
+ i += sizeof(long);
+ zrun_len += sizeof(long);
+ }
+
+ /* go over the rest */
+ while (i < slen && old_buf[i] == new_buf[i]) {
+ zrun_len++;
+ i++;
+ }
+ }
+
+ /* buffer unchanged */
+ if (zrun_len == slen) {
+ return 0;
+ }
+
+ /* skip last zero run */
+ if (i == slen) {
+ return d;
+ }
+
+ d += uleb128_encode_small(dst + d, zrun_len);
+
+ zrun_len = 0;
+ nzrun_start = new_buf + i;
+
+ /* overflow */
+ if (d + 2 > dlen) {
+ return -1;
+ }
+ /* not aligned to sizeof(long) */
+ res = (slen - i) % sizeof(long);
+ while (res && old_buf[i] != new_buf[i]) {
+ i++;
+ nzrun_len++;
+ res--;
+ }
+
+ /* word at a time for speed, use of 32-bit long okay */
+ if (!res) {
+ /* truncation to 32-bit long okay */
+ long mask = (long)0x0101010101010101ULL;
+ while (i < slen) {
+ xor = *(long *)(old_buf + i) ^ *(long *)(new_buf + i);
+ if ((xor - mask) & ~xor & (mask << 7)) {
+ /* found the end of an nzrun within the current long */
+ while (old_buf[i] != new_buf[i]) {
+ nzrun_len++;
+ i++;
+ }
+ break;
+ } else {
+ i += sizeof(long);
+ nzrun_len += sizeof(long);
+ }
+ }
+ }
+
+ d += uleb128_encode_small(dst + d, nzrun_len);
+ /* overflow */
+ if (d + nzrun_len > dlen) {
+ return -1;
+ }
+ memcpy(dst + d, nzrun_start, nzrun_len);
+ d += nzrun_len;
+ nzrun_len = 0;
+ }
+
+ return d;
+}
+
+int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen)
+{
+ int i = 0, d = 0;
+ int ret;
+ uint32_t count = 0;
+
+ while (i < slen) {
+
+ /* zrun */
+ if ((slen - i) < 2) {
+ return -1;
+ }
+
+ ret = uleb128_decode_small(src + i, &count);
+ if (ret < 0 || (i && !count)) {
+ return -1;
+ }
+ i += ret;
+ d += count;
+
+ /* overflow */
+ if (d > dlen) {
+ return -1;
+ }
+
+ /* nzrun */
+ if ((slen - i) < 2) {
+ return -1;
+ }
+
+ ret = uleb128_decode_small(src + i, &count);
+ if (ret < 0 || !count) {
+ return -1;
+ }
+ i += ret;
+
+ /* overflow */
+ if (d + count > dlen || i + count > slen) {
+ return -1;
+ }
+
+ memcpy(dst + d, src + i, count);
+ d += count;
+ i += count;
+ }
+
+ return d;
+}