diff options
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; @@ -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 @@ -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(ð->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); @@ -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); @@ -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); @@ -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); @@ -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(); } @@ -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 { @@ -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; +} |