diff options
69 files changed, 1617 insertions, 647 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 682e4ed481..57f3d3f690 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1193,6 +1193,7 @@ S: Odd Fixes F: hw/mips/fuloong2e.c F: hw/isa/vt82c686.c F: hw/pci-host/bonito.c +F: hw/usb/vt82c686-uhci-pci.c F: include/hw/isa/vt82c686.h Loongson-3 virtual platforms diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst index 5e3a31c123..62a64a8c41 100644 --- a/docs/system/deprecated.rst +++ b/docs/system/deprecated.rst @@ -21,15 +21,6 @@ deprecated. System emulator command line arguments -------------------------------------- -``-usbdevice`` (since 2.10.0) -''''''''''''''''''''''''''''' - -The ``-usbdevice DEV`` argument is now a synonym for setting -the ``-device usb-DEV`` argument instead. The deprecated syntax -would automatically enable USB support on the machine type. -If using the new syntax, USB support must be explicitly -enabled via the ``-machine usb=on`` argument. - ``-drive file=json:{...{'driver':'file'}}`` (since 3.0) ''''''''''''''''''''''''''''''''''''''''''''''''''''''' diff --git a/docs/system/removed-features.rst b/docs/system/removed-features.rst index 83148dcfda..82e7fcc517 100644 --- a/docs/system/removed-features.rst +++ b/docs/system/removed-features.rst @@ -38,6 +38,14 @@ or ``-display default,show-cursor=on`` instead. QEMU 5.0 introduced an alternative syntax to specify the size of the translation block cache, ``-accel tcg,tb-size=``. +``-usbdevice audio`` (removed in 6.0) +''''''''''''''''''''''''''''''''''''' + +This option lacked the possibility to specify an audio backend device. +Use ``-device usb-audio`` now instead (and specify a corresponding USB +host controller or ``-usb`` if necessary). + + QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 3d2072cf75..d28979efb8 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -962,10 +962,14 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) blkcfg.wce = blk_enable_write_cache(s->blk); virtio_stw_p(vdev, &blkcfg.num_queues, s->conf.num_queues); if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_DISCARD)) { + uint32_t discard_granularity = conf->discard_granularity; + if (discard_granularity == -1 || !s->conf.report_discard_granularity) { + discard_granularity = blk_size; + } virtio_stl_p(vdev, &blkcfg.max_discard_sectors, s->conf.max_discard_sectors); virtio_stl_p(vdev, &blkcfg.discard_sector_alignment, - blk_size >> BDRV_SECTOR_BITS); + discard_granularity >> BDRV_SECTOR_BITS); /* * We support only one segment per request since multiple segments * are not widely used and there are no userspace APIs that allow @@ -1299,6 +1303,8 @@ static Property virtio_blk_properties[] = { IOThread *), DEFINE_PROP_BIT64("discard", VirtIOBlock, host_features, VIRTIO_BLK_F_DISCARD, true), + DEFINE_PROP_BOOL("report-discard-granularity", VirtIOBlock, + conf.report_discard_granularity, true), DEFINE_PROP_BIT64("write-zeroes", VirtIOBlock, host_features, VIRTIO_BLK_F_WRITE_ZEROES, true), DEFINE_PROP_UINT32("max-discard-sectors", VirtIOBlock, diff --git a/hw/core/machine.c b/hw/core/machine.c index 4386f57b5c..257a664ea2 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -39,6 +39,8 @@ GlobalProperty hw_compat_5_2[] = { { "ICH9-LPC", "smm-compat", "on"}, { "PIIX4_PM", "smm-compat", "on"}, + { "virtio-blk-device", "report-discard-granularity", "off" }, + { "virtio-net-pci", "vectors", "3"}, }; const size_t hw_compat_5_2_len = G_N_ELEMENTS(hw_compat_5_2); diff --git a/hw/isa/Kconfig b/hw/isa/Kconfig index c7f07854f7..2691eae2f0 100644 --- a/hw/isa/Kconfig +++ b/hw/isa/Kconfig @@ -47,6 +47,7 @@ config VT82C686 select ACPI_SMBUS select SERIAL_ISA select FDC + select USB_UHCI config SMC37C669 bool diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 9a4474a084..24b3a0ff66 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1275,8 +1275,8 @@ static void gem_transmit(CadenceGEMState *s) /* Send the packet somewhere */ if (s->phy_loop || (s->regs[GEM_NWCTRL] & GEM_NWCTRL_LOCALLOOP)) { - gem_receive(qemu_get_queue(s->nic), s->tx_packet, - total_bytes); + qemu_receive_packet(qemu_get_queue(s->nic), s->tx_packet, + total_bytes); } else { qemu_send_packet(qemu_get_queue(s->nic), s->tx_packet, total_bytes); diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 205c0decc5..533a8304d0 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -506,7 +506,7 @@ static void dp8393x_do_transmit_packets(dp8393xState *s) s->regs[SONIC_TCR] |= SONIC_TCR_CRSL; if (nc->info->can_receive(nc)) { s->loopback_packet = 1; - nc->info->receive(nc, s->tx_buffer, tx_len); + qemu_receive_packet(nc, s->tx_buffer, tx_len); } } else { /* Transmit packet */ diff --git a/hw/net/e1000.c b/hw/net/e1000.c index d8da2f6528..4f75b44cfc 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -546,7 +546,7 @@ 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) { - nc->info->receive(nc, buf, size); + qemu_receive_packet(nc, buf, size); } else { qemu_send_packet(nc, buf, size); } @@ -670,6 +670,9 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) msh = tp->tso_props.hdr_len + tp->tso_props.mss; do { bytes = split_size; + if (tp->size >= msh) { + goto eop; + } if (tp->size + bytes > msh) bytes = msh - tp->size; @@ -695,6 +698,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) tp->size += split_size; } +eop: if (!(txd_lower & E1000_TXD_CMD_EOP)) return; if (!(tp->cptse && tp->size < tp->tso_props.hdr_len)) { diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index abc796285a..6aff424cbe 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -680,7 +680,7 @@ 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(qemu_get_queue(s->nic), s->txp->data, s->txp->len); + qemu_receive_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len); } else { qemu_send_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len); } diff --git a/hw/net/msf2-emac.c b/hw/net/msf2-emac.c index 32ba9e8412..3e6206044f 100644 --- a/hw/net/msf2-emac.c +++ b/hw/net/msf2-emac.c @@ -158,7 +158,7 @@ static void msf2_dma_tx(MSF2EmacState *s) * R_CFG1 bit 0 is set. */ if (s->regs[R_CFG1] & R_CFG1_LB_EN_MASK) { - nc->info->receive(nc, buf, size); + qemu_receive_packet(nc, buf, size); } else { qemu_send_packet(nc, buf, size); } diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c index da262edc3e..1f9aa59eca 100644 --- a/hw/net/net_tx_pkt.c +++ b/hw/net/net_tx_pkt.c @@ -553,7 +553,7 @@ static inline void net_tx_pkt_sendv(struct NetTxPkt *pkt, NetClientState *nc, const struct iovec *iov, int iov_cnt) { if (pkt->is_loopback) { - nc->info->receive_iov(nc, iov, iov_cnt); + qemu_receive_packet_iov(nc, iov, iov_cnt); } else { qemu_sendv_packet(nc, iov, iov_cnt); } diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c index f3f18d8598..dcd3fc4948 100644 --- a/hw/net/pcnet.c +++ b/hw/net/pcnet.c @@ -1250,7 +1250,7 @@ txagain: 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(qemu_get_queue(s->nic), s->buffer, s->xmit_pos); + qemu_receive_packet(qemu_get_queue(s->nic), s->buffer, s->xmit_pos); s->looptest = 0; } else { if (s->nic) { diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 4675ac878e..90b4fc63ce 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -1795,7 +1795,7 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, } DPRINTF("+++ transmit loopback mode\n"); - rtl8139_do_receive(qemu_get_queue(s->nic), buf, size, do_interrupt); + qemu_receive_packet(qemu_get_queue(s->nic), buf, size); if (iov) { g_free(buf2); diff --git a/hw/net/sungem.c b/hw/net/sungem.c index 33c3722df6..3684a4d733 100644 --- a/hw/net/sungem.c +++ b/hw/net/sungem.c @@ -306,7 +306,7 @@ static void sungem_send_packet(SunGEMState *s, const uint8_t *buf, NetClientState *nc = qemu_get_queue(s->nic); if (s->macregs[MAC_XIFCFG >> 2] & MAC_XIFCFG_LBCK) { - nc->info->receive(nc, buf, size); + qemu_receive_packet(nc, buf, size); } else { qemu_send_packet(nc, buf, size); } diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 5c815b4f0c..8431808ea0 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -296,9 +296,8 @@ static int net_init(struct XenLegacyDevice *xendev) netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf, "xen", NULL, netdev); - snprintf(qemu_get_queue(netdev->nic)->info_str, - sizeof(qemu_get_queue(netdev->nic)->info_str), - "nic: xenbus vif macaddr=%s", netdev->mac); + qemu_get_queue(netdev->nic)->info_str = g_strdup_printf( + "nic: xenbus vif macaddr=%s", netdev->mac); /* fill info */ xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1); diff --git a/hw/rdma/vmw/pvrdma.h b/hw/rdma/vmw/pvrdma.h index 1d36a76f1e..d08965d3e2 100644 --- a/hw/rdma/vmw/pvrdma.h +++ b/hw/rdma/vmw/pvrdma.h @@ -26,7 +26,6 @@ #include "../rdma_backend_defs.h" #include "../rdma_rm_defs.h" -#include "standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_ring.h" #include "standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h" #include "pvrdma_dev_ring.h" #include "qom/object.h" @@ -64,10 +63,10 @@ typedef struct DSRInfo { union pvrdma_cmd_req *req; union pvrdma_cmd_resp *rsp; - struct pvrdma_ring *async_ring_state; + PvrdmaRingState *async_ring_state; PvrdmaRing async; - struct pvrdma_ring *cq_ring_state; + PvrdmaRingState *cq_ring_state; PvrdmaRing cq; } DSRInfo; diff --git a/hw/rdma/vmw/pvrdma_cmd.c b/hw/rdma/vmw/pvrdma_cmd.c index 692125ac26..f59879e257 100644 --- a/hw/rdma/vmw/pvrdma_cmd.c +++ b/hw/rdma/vmw/pvrdma_cmd.c @@ -262,7 +262,7 @@ static int create_cq_ring(PCIDevice *pci_dev , PvrdmaRing **ring, r = g_malloc(sizeof(*r)); *ring = r; - r->ring_state = (struct pvrdma_ring *) + r->ring_state = (PvrdmaRingState *) rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); if (!r->ring_state) { @@ -398,7 +398,7 @@ static int create_qp_rings(PCIDevice *pci_dev, uint64_t pdir_dma, *rings = sr; /* Create send ring */ - sr->ring_state = (struct pvrdma_ring *) + sr->ring_state = (PvrdmaRingState *) rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); if (!sr->ring_state) { rdma_error_report("Failed to map to QP ring state"); @@ -639,7 +639,7 @@ static int create_srq_ring(PCIDevice *pci_dev, PvrdmaRing **ring, r = g_malloc(sizeof(*r)); *ring = r; - r->ring_state = (struct pvrdma_ring *) + r->ring_state = (PvrdmaRingState *) rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); if (!r->ring_state) { rdma_error_report("Failed to map tp SRQ ring state"); diff --git a/hw/rdma/vmw/pvrdma_dev_ring.c b/hw/rdma/vmw/pvrdma_dev_ring.c index f0bcde74b0..074ac59b84 100644 --- a/hw/rdma/vmw/pvrdma_dev_ring.c +++ b/hw/rdma/vmw/pvrdma_dev_ring.c @@ -22,11 +22,10 @@ #include "trace.h" #include "../rdma_utils.h" -#include "standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_ring.h" #include "pvrdma_dev_ring.h" int pvrdma_ring_init(PvrdmaRing *ring, const char *name, PCIDevice *dev, - struct pvrdma_ring *ring_state, uint32_t max_elems, + PvrdmaRingState *ring_state, uint32_t max_elems, size_t elem_sz, dma_addr_t *tbl, uint32_t npages) { int i; @@ -73,48 +72,54 @@ out: void *pvrdma_ring_next_elem_read(PvrdmaRing *ring) { - int e; - unsigned int idx = 0, offset; + unsigned int idx, offset; + const uint32_t tail = qatomic_read(&ring->ring_state->prod_tail); + const uint32_t head = qatomic_read(&ring->ring_state->cons_head); - e = pvrdma_idx_ring_has_data(ring->ring_state, ring->max_elems, &idx); - if (e <= 0) { + if (tail & ~((ring->max_elems << 1) - 1) || + head & ~((ring->max_elems << 1) - 1) || + tail == head) { trace_pvrdma_ring_next_elem_read_no_data(ring->name); return NULL; } + idx = head & (ring->max_elems - 1); offset = idx * ring->elem_sz; return ring->pages[offset / TARGET_PAGE_SIZE] + (offset % TARGET_PAGE_SIZE); } void pvrdma_ring_read_inc(PvrdmaRing *ring) { - pvrdma_idx_ring_inc(&ring->ring_state->cons_head, ring->max_elems); + uint32_t idx = qatomic_read(&ring->ring_state->cons_head); + + idx = (idx + 1) & ((ring->max_elems << 1) - 1); + qatomic_set(&ring->ring_state->cons_head, idx); } void *pvrdma_ring_next_elem_write(PvrdmaRing *ring) { - int idx; - unsigned int offset, tail; + unsigned int idx, offset; + const uint32_t tail = qatomic_read(&ring->ring_state->prod_tail); + const uint32_t head = qatomic_read(&ring->ring_state->cons_head); - idx = pvrdma_idx_ring_has_space(ring->ring_state, ring->max_elems, &tail); - if (idx <= 0) { + if (tail & ~((ring->max_elems << 1) - 1) || + head & ~((ring->max_elems << 1) - 1) || + tail == (head ^ ring->max_elems)) { rdma_error_report("CQ is full"); return NULL; } - idx = pvrdma_idx(&ring->ring_state->prod_tail, ring->max_elems); - if (idx < 0 || tail != idx) { - rdma_error_report("Invalid idx %d", idx); - return NULL; - } - + idx = tail & (ring->max_elems - 1); offset = idx * ring->elem_sz; return ring->pages[offset / TARGET_PAGE_SIZE] + (offset % TARGET_PAGE_SIZE); } void pvrdma_ring_write_inc(PvrdmaRing *ring) { - pvrdma_idx_ring_inc(&ring->ring_state->prod_tail, ring->max_elems); + uint32_t idx = qatomic_read(&ring->ring_state->prod_tail); + + idx = (idx + 1) & ((ring->max_elems << 1) - 1); + qatomic_set(&ring->ring_state->prod_tail, idx); } void pvrdma_ring_free(PvrdmaRing *ring) diff --git a/hw/rdma/vmw/pvrdma_dev_ring.h b/hw/rdma/vmw/pvrdma_dev_ring.h index 5f2a0cf9b9..d231588ce0 100644 --- a/hw/rdma/vmw/pvrdma_dev_ring.h +++ b/hw/rdma/vmw/pvrdma_dev_ring.h @@ -19,18 +19,23 @@ #define MAX_RING_NAME_SZ 32 +typedef struct PvrdmaRingState { + int prod_tail; /* producer tail */ + int cons_head; /* consumer head */ +} PvrdmaRingState; + typedef struct PvrdmaRing { char name[MAX_RING_NAME_SZ]; PCIDevice *dev; uint32_t max_elems; size_t elem_sz; - struct pvrdma_ring *ring_state; /* used only for unmap */ + PvrdmaRingState *ring_state; /* used only for unmap */ int npages; void **pages; } PvrdmaRing; int pvrdma_ring_init(PvrdmaRing *ring, const char *name, PCIDevice *dev, - struct pvrdma_ring *ring_state, uint32_t max_elems, + PvrdmaRingState *ring_state, uint32_t max_elems, size_t elem_sz, dma_addr_t *tbl, uint32_t npages); void *pvrdma_ring_next_elem_read(PvrdmaRing *ring); void pvrdma_ring_read_inc(PvrdmaRing *ring); diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c index 8593570332..84ae8024fc 100644 --- a/hw/rdma/vmw/pvrdma_main.c +++ b/hw/rdma/vmw/pvrdma_main.c @@ -85,7 +85,7 @@ static void free_dev_ring(PCIDevice *pci_dev, PvrdmaRing *ring, rdma_pci_dma_unmap(pci_dev, ring_state, TARGET_PAGE_SIZE); } -static int init_dev_ring(PvrdmaRing *ring, struct pvrdma_ring **ring_state, +static int init_dev_ring(PvrdmaRing *ring, PvrdmaRingState **ring_state, const char *name, PCIDevice *pci_dev, dma_addr_t dir_addr, uint32_t num_pages) { @@ -114,7 +114,7 @@ static int init_dev_ring(PvrdmaRing *ring, struct pvrdma_ring **ring_state, /* RX ring is the second */ (*ring_state)++; rc = pvrdma_ring_init(ring, name, pci_dev, - (struct pvrdma_ring *)*ring_state, + (PvrdmaRingState *)*ring_state, (num_pages - 1) * TARGET_PAGE_SIZE / sizeof(struct pvrdma_cqne), sizeof(struct pvrdma_cqne), diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig index 40093d7ea6..53f8283ffd 100644 --- a/hw/usb/Kconfig +++ b/hw/usb/Kconfig @@ -66,11 +66,22 @@ config USB_TABLET_WACOM default y depends on USB +config USB_STORAGE_CORE + bool + depends on USB + select SCSI + +config USB_STORAGE_CLASSIC + bool + default y + depends on USB + select USB_STORAGE_CORE + config USB_STORAGE_BOT bool default y depends on USB - select SCSI + select USB_STORAGE_CORE config USB_STORAGE_UAS bool diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 064f94e9c3..07083349f5 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -19,8 +19,6 @@ static void usb_qdev_unrealize(DeviceState *qdev); static Property usb_props[] = { DEFINE_PROP_STRING("port", USBDevice, port_path), DEFINE_PROP_STRING("serial", USBDevice, serial), - DEFINE_PROP_BIT("full-path", USBDevice, flags, - USB_DEV_FLAG_FULL_PATH, true), DEFINE_PROP_BIT("msos-desc", USBDevice, flags, USB_DEV_FLAG_MSOS_DESC_ENABLE, true), DEFINE_PROP_STRING("pcap", USBDevice, pcap_filename), @@ -312,13 +310,13 @@ typedef struct LegacyUSBFactory { const char *name; const char *usbdevice_name; - USBDevice *(*usbdevice_init)(const char *params); + USBDevice *(*usbdevice_init)(void); } LegacyUSBFactory; static GSList *legacy_usb_factory; void usb_legacy_register(const char *typename, const char *usbdevice_name, - USBDevice *(*usbdevice_init)(const char *params)) + USBDevice *(*usbdevice_init)(void)) { if (usbdevice_name) { LegacyUSBFactory *f = g_malloc0(sizeof(*f)); @@ -596,11 +594,8 @@ static char *usb_get_dev_path(DeviceState *qdev) { USBDevice *dev = USB_DEVICE(qdev); DeviceState *hcd = qdev->parent_bus->parent; - char *id = NULL; + char *id = qdev_get_dev_path(hcd); - if (dev->flags & (1 << USB_DEV_FLAG_FULL_PATH)) { - id = qdev_get_dev_path(hcd); - } if (id) { char *ret = g_strdup_printf("%s/%s", id, dev->port->path); g_free(id); @@ -663,27 +658,17 @@ void hmp_info_usb(Monitor *mon, const QDict *qdict) } /* handle legacy -usbdevice cmd line option */ -USBDevice *usbdevice_create(const char *cmdline) +USBDevice *usbdevice_create(const char *driver) { USBBus *bus = usb_bus_find(-1 /* any */); LegacyUSBFactory *f = NULL; Error *err = NULL; GSList *i; - char driver[32]; - const char *params; - int len; USBDevice *dev; - params = strchr(cmdline,':'); - if (params) { - params++; - len = params - cmdline; - if (len > sizeof(driver)) - len = sizeof(driver); - pstrcpy(driver, len, cmdline); - } else { - params = ""; - pstrcpy(driver, sizeof(driver), cmdline); + if (strchr(driver, ':')) { + error_report("usbdevice parameters are not supported anymore"); + return NULL; } for (i = legacy_usb_factory; i; i = i->next) { @@ -707,15 +692,7 @@ USBDevice *usbdevice_create(const char *cmdline) return NULL; } - if (f->usbdevice_init) { - dev = f->usbdevice_init(params); - } else { - if (*params) { - error_report("usbdevice %s accepts no params", driver); - return NULL; - } - dev = usb_new(f->name); - } + dev = f->usbdevice_init ? f->usbdevice_init() : usb_new(f->name); if (!dev) { error_report("Failed to create USB device '%s'", f->name); return NULL; diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c index e1486f81e0..f5cb246792 100644 --- a/hw/usb/dev-audio.c +++ b/hw/usb/dev-audio.c @@ -1024,7 +1024,6 @@ static const TypeInfo usb_audio_info = { static void usb_audio_register_types(void) { type_register_static(&usb_audio_info); - usb_legacy_register(TYPE_USB_AUDIO, "audio", NULL); } type_init(usb_audio_register_types) diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index b58c4eb908..63047d79cf 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -614,7 +614,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); } -static USBDevice *usb_braille_init(const char *unused) +static USBDevice *usb_braille_init(void) { USBDevice *dev; Chardev *cdrv; diff --git a/hw/usb/dev-storage-bot.c b/hw/usb/dev-storage-bot.c new file mode 100644 index 0000000000..6aad026d11 --- /dev/null +++ b/hw/usb/dev-storage-bot.c @@ -0,0 +1,63 @@ +/* + * USB Mass Storage Device emulation + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the LGPL. + */ + +#include "qemu/osdep.h" +#include "qemu/typedefs.h" +#include "qapi/error.h" +#include "hw/usb.h" +#include "hw/usb/desc.h" +#include "hw/usb/msd.h" + +static const struct SCSIBusInfo usb_msd_scsi_info_bot = { + .tcq = false, + .max_target = 0, + .max_lun = 15, + + .transfer_data = usb_msd_transfer_data, + .complete = usb_msd_command_complete, + .cancel = usb_msd_request_cancelled, + .load_request = usb_msd_load_request, +}; + +static void usb_msd_bot_realize(USBDevice *dev, Error **errp) +{ + MSDState *s = USB_STORAGE_DEV(dev); + DeviceState *d = DEVICE(dev); + + usb_desc_create_serial(dev); + usb_desc_init(dev); + if (d->hotplugged) { + s->dev.auto_attach = 0; + } + + scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), + &usb_msd_scsi_info_bot, NULL); + usb_msd_handle_reset(dev); +} + +static void usb_msd_class_bot_initfn(ObjectClass *klass, void *data) +{ + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + + uc->realize = usb_msd_bot_realize; + uc->attached_settable = true; +} + +static const TypeInfo bot_info = { + .name = "usb-bot", + .parent = TYPE_USB_STORAGE, + .class_init = usb_msd_class_bot_initfn, +}; + +static void register_types(void) +{ + type_register_static(&bot_info); +} + +type_init(register_types) diff --git a/hw/usb/dev-storage-classic.c b/hw/usb/dev-storage-classic.c new file mode 100644 index 0000000000..00cb34b22f --- /dev/null +++ b/hw/usb/dev-storage-classic.c @@ -0,0 +1,156 @@ +/* + * USB Mass Storage Device emulation + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the LGPL. + */ + +#include "qemu/osdep.h" +#include "qemu/typedefs.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "hw/usb.h" +#include "hw/usb/desc.h" +#include "hw/usb/msd.h" +#include "sysemu/sysemu.h" +#include "sysemu/block-backend.h" + +static const struct SCSIBusInfo usb_msd_scsi_info_storage = { + .tcq = false, + .max_target = 0, + .max_lun = 0, + + .transfer_data = usb_msd_transfer_data, + .complete = usb_msd_command_complete, + .cancel = usb_msd_request_cancelled, + .load_request = usb_msd_load_request, +}; + +static void usb_msd_storage_realize(USBDevice *dev, Error **errp) +{ + MSDState *s = USB_STORAGE_DEV(dev); + BlockBackend *blk = s->conf.blk; + SCSIDevice *scsi_dev; + + if (!blk) { + error_setg(errp, "drive property not set"); + return; + } + + if (!blkconf_blocksizes(&s->conf, errp)) { + return; + } + + if (!blkconf_apply_backend_options(&s->conf, !blk_supports_write_perm(blk), + true, errp)) { + return; + } + + /* + * Hack alert: this pretends to be a block device, but it's really + * a SCSI bus that can serve only a single device, which it + * creates automatically. But first it needs to detach from its + * blockdev, or else scsi_bus_legacy_add_drive() dies when it + * attaches again. We also need to take another reference so that + * blk_detach_dev() doesn't free blk while we still need it. + * + * The hack is probably a bad idea. + */ + blk_ref(blk); + blk_detach_dev(blk, DEVICE(s)); + s->conf.blk = NULL; + + usb_desc_create_serial(dev); + usb_desc_init(dev); + scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), + &usb_msd_scsi_info_storage, NULL); + scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable, + s->conf.bootindex, s->conf.share_rw, + s->conf.rerror, s->conf.werror, + dev->serial, + errp); + blk_unref(blk); + if (!scsi_dev) { + return; + } + usb_msd_handle_reset(dev); + s->scsi_dev = scsi_dev; +} + +static Property msd_properties[] = { + DEFINE_BLOCK_PROPERTIES(MSDState, conf), + DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf), + DEFINE_PROP_BOOL("removable", MSDState, removable, false), + DEFINE_PROP_BOOL("commandlog", MSDState, commandlog, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + + uc->realize = usb_msd_storage_realize; + device_class_set_props(dc, msd_properties); +} + +static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + USBDevice *dev = USB_DEVICE(obj); + MSDState *s = USB_STORAGE_DEV(dev); + + visit_type_int32(v, name, &s->conf.bootindex, errp); +} + +static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + USBDevice *dev = USB_DEVICE(obj); + MSDState *s = USB_STORAGE_DEV(dev); + int32_t boot_index; + Error *local_err = NULL; + + if (!visit_type_int32(v, name, &boot_index, errp)) { + return; + } + /* check whether bootindex is present in fw_boot_order list */ + check_boot_index(boot_index, &local_err); + if (local_err) { + goto out; + } + /* change bootindex to a new one */ + s->conf.bootindex = boot_index; + + if (s->scsi_dev) { + object_property_set_int(OBJECT(s->scsi_dev), "bootindex", boot_index, + &error_abort); + } + +out: + error_propagate(errp, local_err); +} + +static void usb_msd_instance_init(Object *obj) +{ + object_property_add(obj, "bootindex", "int32", + usb_msd_get_bootindex, + usb_msd_set_bootindex, NULL, NULL); + object_property_set_int(obj, "bootindex", -1, NULL); +} + +static const TypeInfo msd_info = { + .name = "usb-storage", + .parent = TYPE_USB_STORAGE, + .class_init = usb_msd_class_storage_initfn, + .instance_init = usb_msd_instance_init, +}; + +static void register_types(void) +{ + type_register_static(&msd_info); +} + +type_init(register_types) diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index a5f76fc001..dca62d544f 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -14,13 +14,11 @@ #include "qemu/option.h" #include "qemu/config-file.h" #include "hw/usb.h" +#include "hw/usb/msd.h" #include "desc.h" #include "hw/qdev-properties.h" #include "hw/scsi/scsi.h" #include "migration/vmstate.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" -#include "qapi/visitor.h" #include "qemu/cutils.h" #include "qom/object.h" #include "trace.h" @@ -29,43 +27,6 @@ #define MassStorageReset 0xff #define GetMaxLun 0xfe -enum USBMSDMode { - USB_MSDM_CBW, /* Command Block. */ - USB_MSDM_DATAOUT, /* Transfer data to device. */ - USB_MSDM_DATAIN, /* Transfer data from device. */ - USB_MSDM_CSW /* Command Status. */ -}; - -struct usb_msd_csw { - uint32_t sig; - uint32_t tag; - uint32_t residue; - uint8_t status; -}; - -struct MSDState { - USBDevice dev; - enum USBMSDMode mode; - uint32_t scsi_off; - uint32_t scsi_len; - uint32_t data_len; - struct usb_msd_csw csw; - SCSIRequest *req; - SCSIBus bus; - /* For async completion. */ - USBPacket *packet; - /* usb-storage only */ - BlockConf conf; - bool removable; - bool commandlog; - SCSIDevice *scsi_dev; -}; -typedef struct MSDState MSDState; - -#define TYPE_USB_STORAGE "usb-storage-dev" -DECLARE_INSTANCE_CHECKER(MSDState, USB_STORAGE_DEV, - TYPE_USB_STORAGE) - struct usb_msd_cbw { uint32_t sig; uint32_t tag; @@ -259,7 +220,7 @@ static void usb_msd_packet_complete(MSDState *s) usb_packet_complete(&s->dev, p); } -static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) +void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) { MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); USBPacket *p = s->packet; @@ -277,7 +238,7 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) } } -static void usb_msd_command_complete(SCSIRequest *req, size_t resid) +void usb_msd_command_complete(SCSIRequest *req, size_t resid) { MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); USBPacket *p = s->packet; @@ -320,7 +281,7 @@ static void usb_msd_command_complete(SCSIRequest *req, size_t resid) s->req = NULL; } -static void usb_msd_request_cancelled(SCSIRequest *req) +void usb_msd_request_cancelled(SCSIRequest *req) { MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); @@ -337,7 +298,7 @@ static void usb_msd_request_cancelled(SCSIRequest *req) } } -static void usb_msd_handle_reset(USBDevice *dev) +void usb_msd_handle_reset(USBDevice *dev) { MSDState *s = (MSDState *)dev; @@ -352,6 +313,7 @@ static void usb_msd_handle_reset(USBDevice *dev) usb_msd_packet_complete(s); } + memset(&s->csw, 0, sizeof(s->csw)); s->mode = USB_MSDM_CBW; } @@ -565,7 +527,7 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) } } -static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req) +void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req) { MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); @@ -576,95 +538,6 @@ static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req) return NULL; } -static const struct SCSIBusInfo usb_msd_scsi_info_storage = { - .tcq = false, - .max_target = 0, - .max_lun = 0, - - .transfer_data = usb_msd_transfer_data, - .complete = usb_msd_command_complete, - .cancel = usb_msd_request_cancelled, - .load_request = usb_msd_load_request, -}; - -static const struct SCSIBusInfo usb_msd_scsi_info_bot = { - .tcq = false, - .max_target = 0, - .max_lun = 15, - - .transfer_data = usb_msd_transfer_data, - .complete = usb_msd_command_complete, - .cancel = usb_msd_request_cancelled, - .load_request = usb_msd_load_request, -}; - -static void usb_msd_storage_realize(USBDevice *dev, Error **errp) -{ - MSDState *s = USB_STORAGE_DEV(dev); - BlockBackend *blk = s->conf.blk; - SCSIDevice *scsi_dev; - - if (!blk) { - error_setg(errp, "drive property not set"); - return; - } - - if (!blkconf_blocksizes(&s->conf, errp)) { - return; - } - - if (!blkconf_apply_backend_options(&s->conf, !blk_supports_write_perm(blk), - true, errp)) { - return; - } - - /* - * Hack alert: this pretends to be a block device, but it's really - * a SCSI bus that can serve only a single device, which it - * creates automatically. But first it needs to detach from its - * blockdev, or else scsi_bus_legacy_add_drive() dies when it - * attaches again. We also need to take another reference so that - * blk_detach_dev() doesn't free blk while we still need it. - * - * The hack is probably a bad idea. - */ - blk_ref(blk); - blk_detach_dev(blk, DEVICE(s)); - s->conf.blk = NULL; - - usb_desc_create_serial(dev); - usb_desc_init(dev); - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &usb_msd_scsi_info_storage, NULL); - scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable, - s->conf.bootindex, s->conf.share_rw, - s->conf.rerror, s->conf.werror, - dev->serial, - errp); - blk_unref(blk); - if (!scsi_dev) { - return; - } - usb_msd_handle_reset(dev); - s->scsi_dev = scsi_dev; -} - -static void usb_msd_bot_realize(USBDevice *dev, Error **errp) -{ - MSDState *s = USB_STORAGE_DEV(dev); - DeviceState *d = DEVICE(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - if (d->hotplugged) { - s->dev.auto_attach = 0; - } - - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &usb_msd_scsi_info_bot, NULL); - usb_msd_handle_reset(dev); -} - static const VMStateDescription vmstate_usb_msd = { .name = "usb-storage", .version_id = 1, @@ -683,14 +556,6 @@ static const VMStateDescription vmstate_usb_msd = { } }; -static Property msd_properties[] = { - DEFINE_BLOCK_PROPERTIES(MSDState, conf), - DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf), - DEFINE_PROP_BOOL("removable", MSDState, removable, false), - DEFINE_PROP_BOOL("commandlog", MSDState, commandlog, false), - DEFINE_PROP_END_OF_LIST(), -}; - static void usb_msd_class_initfn_common(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -708,52 +573,6 @@ static void usb_msd_class_initfn_common(ObjectClass *klass, void *data) dc->vmsd = &vmstate_usb_msd; } -static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_msd_storage_realize; - device_class_set_props(dc, msd_properties); -} - -static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - USBDevice *dev = USB_DEVICE(obj); - MSDState *s = USB_STORAGE_DEV(dev); - - visit_type_int32(v, name, &s->conf.bootindex, errp); -} - -static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - USBDevice *dev = USB_DEVICE(obj); - MSDState *s = USB_STORAGE_DEV(dev); - int32_t boot_index; - Error *local_err = NULL; - - if (!visit_type_int32(v, name, &boot_index, errp)) { - return; - } - /* check whether bootindex is present in fw_boot_order list */ - check_boot_index(boot_index, &local_err); - if (local_err) { - goto out; - } - /* change bootindex to a new one */ - s->conf.bootindex = boot_index; - - if (s->scsi_dev) { - object_property_set_int(OBJECT(s->scsi_dev), "bootindex", boot_index, - &error_abort); - } - -out: - error_propagate(errp, local_err); -} - static const TypeInfo usb_storage_dev_type_info = { .name = TYPE_USB_STORAGE, .parent = TYPE_USB_DEVICE, @@ -762,40 +581,9 @@ static const TypeInfo usb_storage_dev_type_info = { .class_init = usb_msd_class_initfn_common, }; -static void usb_msd_instance_init(Object *obj) -{ - object_property_add(obj, "bootindex", "int32", - usb_msd_get_bootindex, - usb_msd_set_bootindex, NULL, NULL); - object_property_set_int(obj, "bootindex", -1, NULL); -} - -static void usb_msd_class_bot_initfn(ObjectClass *klass, void *data) -{ - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_msd_bot_realize; - uc->attached_settable = true; -} - -static const TypeInfo msd_info = { - .name = "usb-storage", - .parent = TYPE_USB_STORAGE, - .class_init = usb_msd_class_storage_initfn, - .instance_init = usb_msd_instance_init, -}; - -static const TypeInfo bot_info = { - .name = "usb-bot", - .parent = TYPE_USB_STORAGE, - .class_init = usb_msd_class_bot_initfn, -}; - static void usb_msd_register_types(void) { type_register_static(&usb_storage_dev_type_info); - type_register_static(&msd_info); - type_register_static(&bot_info); } type_init(usb_msd_register_types) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 5969eb86b3..0cb02a6432 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -40,6 +40,7 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qom/object.h" +#include "hcd-uhci.h" #define FRAME_TIMER_FREQ 1000 @@ -50,8 +51,6 @@ #define MAX_FRAMES_PER_TICK (QH_VALID / 2) -#define NB_PORTS 2 - enum { TD_RESULT_STOP_FRAME = 10, TD_RESULT_COMPLETE, @@ -62,20 +61,8 @@ enum { typedef struct UHCIState UHCIState; typedef struct UHCIAsync UHCIAsync; -typedef struct UHCIQueue UHCIQueue; -typedef struct UHCIInfo UHCIInfo; typedef struct UHCIPCIDeviceClass UHCIPCIDeviceClass; -struct UHCIInfo { - const char *name; - uint16_t vendor_id; - uint16_t device_id; - uint8_t revision; - uint8_t irq_pin; - void (*realize)(PCIDevice *dev, Error **errp); - bool unplug; -}; - struct UHCIPCIDeviceClass { PCIDeviceClass parent_class; UHCIInfo info; @@ -107,43 +94,6 @@ struct UHCIQueue { int8_t valid; }; -typedef struct UHCIPort { - USBPort port; - uint16_t ctrl; -} UHCIPort; - -struct UHCIState { - PCIDevice dev; - MemoryRegion io_bar; - USBBus bus; /* Note unused when we're a companion controller */ - uint16_t cmd; /* cmd register */ - uint16_t status; - uint16_t intr; /* interrupt enable register */ - uint16_t frnum; /* frame number */ - uint32_t fl_base_addr; /* frame list base address */ - uint8_t sof_timing; - uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */ - int64_t expire_time; - QEMUTimer *frame_timer; - QEMUBH *bh; - uint32_t frame_bytes; - uint32_t frame_bandwidth; - bool completions_only; - UHCIPort ports[NB_PORTS]; - - /* Interrupts that should be raised at the end of the current frame. */ - uint32_t pending_int_mask; - - /* Active packets */ - QTAILQ_HEAD(, UHCIQueue) queues; - uint8_t num_ports_vmstate; - - /* Properties */ - char *masterbus; - uint32_t firstport; - uint32_t maxframes; -}; - typedef struct UHCI_TD { uint32_t link; uint32_t ctrl; /* see TD_CTRL_xxx */ @@ -160,10 +110,6 @@ static void uhci_async_cancel(UHCIAsync *async); static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td); static void uhci_resume(void *opaque); -#define TYPE_UHCI "pci-uhci-usb" -DECLARE_INSTANCE_CHECKER(UHCIState, UHCI, - TYPE_UHCI) - static inline int32_t uhci_queue_token(UHCI_TD *td) { if ((td->token & (0xf << 15)) == 0) { @@ -1213,7 +1159,7 @@ static USBPortOps uhci_port_ops = { static USBBusOps uhci_bus_ops = { }; -static void usb_uhci_common_realize(PCIDevice *dev, Error **errp) +void usb_uhci_common_realize(PCIDevice *dev, Error **errp) { Error *err = NULL; PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); @@ -1261,21 +1207,6 @@ static void usb_uhci_common_realize(PCIDevice *dev, Error **errp) pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar); } -static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp) -{ - UHCIState *s = UHCI(dev); - uint8_t *pci_conf = s->dev.config; - - /* USB misc control 1/2 */ - pci_set_long(pci_conf + 0x40,0x00001000); - /* PM capability */ - pci_set_long(pci_conf + 0x80,0x00020001); - /* USB legacy support */ - pci_set_long(pci_conf + 0xc0,0x00002000); - - usb_uhci_common_realize(dev, errp); -} - static void usb_uhci_exit(PCIDevice *dev) { UHCIState *s = UHCI(dev); @@ -1335,7 +1266,7 @@ static const TypeInfo uhci_pci_type_info = { }, }; -static void uhci_data_class_init(ObjectClass *klass, void *data) +void uhci_data_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -1373,14 +1304,6 @@ static UHCIInfo uhci_info[] = { .irq_pin = 3, .unplug = true, },{ - .name = "vt82c686b-usb-uhci", - .vendor_id = PCI_VENDOR_ID_VIA, - .device_id = PCI_DEVICE_ID_VIA_UHCI, - .revision = 0x01, - .irq_pin = 3, - .realize = usb_uhci_vt82c686b_realize, - .unplug = true, - },{ .name = "ich9-usb-uhci1", /* 00:1d.0 */ .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, diff --git a/hw/usb/hcd-uhci.h b/hw/usb/hcd-uhci.h new file mode 100644 index 0000000000..e61d8fcb19 --- /dev/null +++ b/hw/usb/hcd-uhci.h @@ -0,0 +1,93 @@ +/* + * USB UHCI controller emulation + * + * Copyright (c) 2005 Fabrice Bellard + * + * Copyright (c) 2008 Max Krasnyansky + * Magor rewrite of the UHCI data structures parser and frame processor + * Support for fully async operation and multiple outstanding transactions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef HW_USB_HCD_UHCI_H +#define HW_USB_HCD_UHCI_H + +#include "exec/memory.h" +#include "qemu/timer.h" +#include "hw/pci/pci.h" +#include "hw/usb.h" + +typedef struct UHCIQueue UHCIQueue; + +#define NB_PORTS 2 + +typedef struct UHCIPort { + USBPort port; + uint16_t ctrl; +} UHCIPort; + +typedef struct UHCIState { + PCIDevice dev; + MemoryRegion io_bar; + USBBus bus; /* Note unused when we're a companion controller */ + uint16_t cmd; /* cmd register */ + uint16_t status; + uint16_t intr; /* interrupt enable register */ + uint16_t frnum; /* frame number */ + uint32_t fl_base_addr; /* frame list base address */ + uint8_t sof_timing; + uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */ + int64_t expire_time; + QEMUTimer *frame_timer; + QEMUBH *bh; + uint32_t frame_bytes; + uint32_t frame_bandwidth; + bool completions_only; + UHCIPort ports[NB_PORTS]; + + /* Interrupts that should be raised at the end of the current frame. */ + uint32_t pending_int_mask; + + /* Active packets */ + QTAILQ_HEAD(, UHCIQueue) queues; + uint8_t num_ports_vmstate; + + /* Properties */ + char *masterbus; + uint32_t firstport; + uint32_t maxframes; +} UHCIState; + +#define TYPE_UHCI "pci-uhci-usb" +DECLARE_INSTANCE_CHECKER(UHCIState, UHCI, TYPE_UHCI) + +typedef struct UHCIInfo { + const char *name; + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision; + uint8_t irq_pin; + void (*realize)(PCIDevice *dev, Error **errp); + bool unplug; +} UHCIInfo; + +void uhci_data_class_init(ObjectClass *klass, void *data); +void usb_uhci_common_realize(PCIDevice *dev, Error **errp); + +#endif diff --git a/hw/usb/meson.build b/hw/usb/meson.build index 653192cff6..fb7a74e73a 100644 --- a/hw/usb/meson.build +++ b/hw/usb/meson.build @@ -32,6 +32,7 @@ softmmu_ss.add(when: 'CONFIG_USB_DWC3', if_true: files('hcd-dwc3.c')) softmmu_ss.add(when: 'CONFIG_TUSB6010', if_true: files('tusb6010.c')) softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('chipidea.c')) softmmu_ss.add(when: 'CONFIG_IMX_USBPHY', if_true: files('imx-usb-phy.c')) +softmmu_ss.add(when: 'CONFIG_VT82C686', if_true: files('vt82c686-uhci-pci.c')) specific_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-usb2-ctrl-regs.c')) specific_ss.add(when: 'CONFIG_XLNX_USB_SUBSYS', if_true: files('xlnx-usb-subsystem.c')) @@ -39,7 +40,9 @@ specific_ss.add(when: 'CONFIG_XLNX_USB_SUBSYS', if_true: files('xlnx-usb-subsyst softmmu_ss.add(when: 'CONFIG_USB', if_true: files('dev-hub.c')) softmmu_ss.add(when: 'CONFIG_USB', if_true: files('dev-hid.c')) softmmu_ss.add(when: 'CONFIG_USB_TABLET_WACOM', if_true: files('dev-wacom.c')) -softmmu_ss.add(when: 'CONFIG_USB_STORAGE_BOT', if_true: files('dev-storage.c')) +softmmu_ss.add(when: 'CONFIG_USB_STORAGE_CORE', if_true: files('dev-storage.c')) +softmmu_ss.add(when: 'CONFIG_USB_STORAGE_BOT', if_true: files('dev-storage-bot.c')) +softmmu_ss.add(when: 'CONFIG_USB_STORAGE_CLASSIC', if_true: files('dev-storage-classic.c')) softmmu_ss.add(when: 'CONFIG_USB_STORAGE_UAS', if_true: files('dev-uas.c')) softmmu_ss.add(when: 'CONFIG_USB_AUDIO', if_true: files('dev-audio.c')) softmmu_ss.add(when: 'CONFIG_USB_SERIAL', if_true: files('dev-serial.c')) diff --git a/hw/usb/u2f.c b/hw/usb/u2f.c index bc09191f06..56001249a4 100644 --- a/hw/usb/u2f.c +++ b/hw/usb/u2f.c @@ -346,7 +346,6 @@ static const TypeInfo u2f_key_info = { static void u2f_key_register_types(void) { type_register_static(&u2f_key_info); - usb_legacy_register(TYPE_U2F_KEY, "u2f-key", NULL); } type_init(u2f_key_register_types) diff --git a/hw/usb/vt82c686-uhci-pci.c b/hw/usb/vt82c686-uhci-pci.c new file mode 100644 index 0000000000..b109c21603 --- /dev/null +++ b/hw/usb/vt82c686-uhci-pci.c @@ -0,0 +1,43 @@ +#include "qemu/osdep.h" +#include "hcd-uhci.h" + +static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp) +{ + UHCIState *s = UHCI(dev); + uint8_t *pci_conf = s->dev.config; + + /* USB misc control 1/2 */ + pci_set_long(pci_conf + 0x40, 0x00001000); + /* PM capability */ + pci_set_long(pci_conf + 0x80, 0x00020001); + /* USB legacy support */ + pci_set_long(pci_conf + 0xc0, 0x00002000); + + usb_uhci_common_realize(dev, errp); +} + +static UHCIInfo uhci_info[] = { + { + .name = "vt82c686b-usb-uhci", + .vendor_id = PCI_VENDOR_ID_VIA, + .device_id = PCI_DEVICE_ID_VIA_UHCI, + .revision = 0x01, + .irq_pin = 3, + .realize = usb_uhci_vt82c686b_realize, + .unplug = true, + } +}; + +static const TypeInfo vt82c686b_usb_uhci_type_info = { + .parent = TYPE_UHCI, + .name = "vt82c686b-usb-uhci", + .class_init = uhci_data_class_init, + .class_data = uhci_info, +}; + +static void vt82c686b_usb_uhci_register_types(void) +{ + type_register_static(&vt82c686b_usb_uhci_type_info); +} + +type_init(vt82c686b_usb_uhci_register_types) diff --git a/hw/virtio/virtio-net-pci.c b/hw/virtio/virtio-net-pci.c index 292d13d278..aa0b3caecb 100644 --- a/hw/virtio/virtio-net-pci.c +++ b/hw/virtio/virtio-net-pci.c @@ -41,7 +41,8 @@ struct VirtIONetPCI { static Property virtio_net_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), DEFINE_PROP_END_OF_LIST(), }; @@ -50,6 +51,13 @@ static void virtio_net_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) DeviceState *qdev = DEVICE(vpci_dev); VirtIONetPCI *dev = VIRTIO_NET_PCI(vpci_dev); DeviceState *vdev = DEVICE(&dev->vdev); + VirtIONet *net = VIRTIO_NET(vdev); + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = 2 * MAX(net->nic_conf.peers.queues, 1) + + 1 /* Config interrupt */ + + 1 /* Control vq */; + } virtio_net_set_netclient_name(&dev->vdev, qdev->id, object_get_typename(OBJECT(qdev))); diff --git a/include/hw/usb.h b/include/hw/usb.h index abfbfc5284..436e07b304 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -216,7 +216,6 @@ struct USBEndpoint { }; enum USBDeviceFlags { - USB_DEV_FLAG_FULL_PATH, USB_DEV_FLAG_IS_HOST, USB_DEV_FLAG_MSOS_DESC_ENABLE, USB_DEV_FLAG_MSOS_DESC_IN_USE, @@ -500,7 +499,7 @@ void usb_bus_new(USBBus *bus, size_t bus_size, void usb_bus_release(USBBus *bus); USBBus *usb_bus_find(int busnr); void usb_legacy_register(const char *typename, const char *usbdevice_name, - USBDevice *(*usbdevice_init)(const char *params)); + USBDevice *(*usbdevice_init)(void)); USBDevice *usb_new(const char *name); bool usb_realize_and_unref(USBDevice *dev, USBBus *bus, Error **errp); USBDevice *usb_create_simple(USBBus *bus, const char *name); diff --git a/include/hw/usb/msd.h b/include/hw/usb/msd.h new file mode 100644 index 0000000000..7538c54569 --- /dev/null +++ b/include/hw/usb/msd.h @@ -0,0 +1,54 @@ +/* + * USB Mass Storage Device emulation + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the LGPL. + */ + +#include "hw/usb.h" +#include "hw/scsi/scsi.h" + +enum USBMSDMode { + USB_MSDM_CBW, /* Command Block. */ + USB_MSDM_DATAOUT, /* Transfer data to device. */ + USB_MSDM_DATAIN, /* Transfer data from device. */ + USB_MSDM_CSW /* Command Status. */ +}; + +struct usb_msd_csw { + uint32_t sig; + uint32_t tag; + uint32_t residue; + uint8_t status; +}; + +struct MSDState { + USBDevice dev; + enum USBMSDMode mode; + uint32_t scsi_off; + uint32_t scsi_len; + uint32_t data_len; + struct usb_msd_csw csw; + SCSIRequest *req; + SCSIBus bus; + /* For async completion. */ + USBPacket *packet; + /* usb-storage only */ + BlockConf conf; + bool removable; + bool commandlog; + SCSIDevice *scsi_dev; +}; + +typedef struct MSDState MSDState; +#define TYPE_USB_STORAGE "usb-storage-dev" +DECLARE_INSTANCE_CHECKER(MSDState, USB_STORAGE_DEV, + TYPE_USB_STORAGE) + +void usb_msd_transfer_data(SCSIRequest *req, uint32_t len); +void usb_msd_command_complete(SCSIRequest *req, size_t resid); +void usb_msd_request_cancelled(SCSIRequest *req); +void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req); +void usb_msd_handle_reset(USBDevice *dev); diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h index 214ab74822..29655a406d 100644 --- a/include/hw/virtio/virtio-blk.h +++ b/include/hw/virtio/virtio-blk.h @@ -41,6 +41,7 @@ struct VirtIOBlkConf uint16_t num_queues; uint16_t queue_size; bool seg_max_adjust; + bool report_discard_granularity; uint32_t max_discard_sectors; uint32_t max_write_zeroes_sectors; bool x_enable_wce_if_config_wce; diff --git a/include/net/net.h b/include/net/net.h index 919facaad2..a02949f6db 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -5,6 +5,8 @@ #include "qapi/qapi-types-net.h" #include "net/queue.h" #include "hw/qdev-properties-system.h" +#include "qapi/clone-visitor.h" +#include "qapi/qapi-visit-net.h" #define MAC_FMT "%02X:%02X:%02X:%02X:%02X:%02X" #define MAC_ARG(x) ((uint8_t *)(x))[0], ((uint8_t *)(x))[1], \ @@ -92,7 +94,8 @@ struct NetClientState { NetQueue *incoming_queue; char *model; char *name; - char info_str[256]; + char *info_str; + NetdevInfo *stored_config; unsigned receive_disabled : 1; NetClientDestructor *destructor; unsigned int queue_index; @@ -144,12 +147,17 @@ void *qemu_get_nic_opaque(NetClientState *nc); void qemu_del_net_client(NetClientState *nc); typedef void (*qemu_nic_foreach)(NICState *nic, void *opaque); void qemu_foreach_nic(qemu_nic_foreach func, void *opaque); +int qemu_can_receive_packet(NetClientState *nc); int qemu_can_send_packet(NetClientState *nc); ssize_t qemu_sendv_packet(NetClientState *nc, const struct iovec *iov, int iovcnt); ssize_t qemu_sendv_packet_async(NetClientState *nc, const struct iovec *iov, int iovcnt, NetPacketSent *sent_cb); ssize_t qemu_send_packet(NetClientState *nc, const uint8_t *buf, int size); +ssize_t qemu_receive_packet(NetClientState *nc, const uint8_t *buf, int size); +ssize_t qemu_receive_packet_iov(NetClientState *nc, + const struct iovec *iov, + int iovcnt); ssize_t qemu_send_packet_raw(NetClientState *nc, const uint8_t *buf, int size); ssize_t qemu_send_packet_async(NetClientState *nc, const uint8_t *buf, int size, NetPacketSent *sent_cb); diff --git a/include/net/queue.h b/include/net/queue.h index c0269bb1dc..9f2f289d77 100644 --- a/include/net/queue.h +++ b/include/net/queue.h @@ -55,6 +55,14 @@ void qemu_net_queue_append_iov(NetQueue *queue, void qemu_del_net_queue(NetQueue *queue); +ssize_t qemu_net_queue_receive(NetQueue *queue, + const uint8_t *data, + size_t size); + +ssize_t qemu_net_queue_receive_iov(NetQueue *queue, + const struct iovec *iov, + int iovcnt); + ssize_t qemu_net_queue_send(NetQueue *queue, NetClientState *sender, unsigned flags, diff --git a/include/qapi/hmp-output-visitor.h b/include/qapi/hmp-output-visitor.h new file mode 100644 index 0000000000..541e4002e3 --- /dev/null +++ b/include/qapi/hmp-output-visitor.h @@ -0,0 +1,30 @@ +/* + * HMP string output Visitor + * + * Copyright Yandex N.V., 2021 + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef HMP_OUTPUT_VISITOR_H +#define HMP_OUTPUT_VISITOR_H + +#include "qapi/visitor.h" + +typedef struct HMPOutputVisitor HMPOutputVisitor; + +/** + * Create a HMP string output visitor for @obj + * + * Flattens dicts/structures, only shows arrays borders. + * + * Errors are not expected to happen. + * + * The caller is responsible for freeing the visitor with + * visit_free(). + */ +Visitor *hmp_output_visitor_new(char **result); + +#endif diff --git a/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_ring.h b/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_ring.h deleted file mode 100644 index 7b4062a1a1..0000000000 --- a/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_ring.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2012-2016 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of EITHER the GNU General Public License - * version 2 as published by the Free Software Foundation or the BSD - * 2-Clause License. This program is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED - * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License version 2 for more details at - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html. - * - * You should have received a copy of the GNU General Public License - * along with this program available in the file COPYING in the main - * directory of this source tree. - * - * The BSD 2-Clause License - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __PVRDMA_RING_H__ -#define __PVRDMA_RING_H__ - -#include "standard-headers/linux/types.h" - -#define PVRDMA_INVALID_IDX -1 /* Invalid index. */ - -struct pvrdma_ring { - int prod_tail; /* Producer tail. */ - int cons_head; /* Consumer head. */ -}; - -struct pvrdma_ring_state { - struct pvrdma_ring tx; /* Tx ring. */ - struct pvrdma_ring rx; /* Rx ring. */ -}; - -static inline int pvrdma_idx_valid(uint32_t idx, uint32_t max_elems) -{ - /* Generates fewer instructions than a less-than. */ - return (idx & ~((max_elems << 1) - 1)) == 0; -} - -static inline int32_t pvrdma_idx(int *var, uint32_t max_elems) -{ - const unsigned int idx = qatomic_read(var); - - if (pvrdma_idx_valid(idx, max_elems)) - return idx & (max_elems - 1); - return PVRDMA_INVALID_IDX; -} - -static inline void pvrdma_idx_ring_inc(int *var, uint32_t max_elems) -{ - uint32_t idx = qatomic_read(var) + 1; /* Increment. */ - - idx &= (max_elems << 1) - 1; /* Modulo size, flip gen. */ - qatomic_set(var, idx); -} - -static inline int32_t pvrdma_idx_ring_has_space(const struct pvrdma_ring *r, - uint32_t max_elems, uint32_t *out_tail) -{ - const uint32_t tail = qatomic_read(&r->prod_tail); - const uint32_t head = qatomic_read(&r->cons_head); - - if (pvrdma_idx_valid(tail, max_elems) && - pvrdma_idx_valid(head, max_elems)) { - *out_tail = tail & (max_elems - 1); - return tail != (head ^ max_elems); - } - return PVRDMA_INVALID_IDX; -} - -static inline int32_t pvrdma_idx_ring_has_data(const struct pvrdma_ring *r, - uint32_t max_elems, uint32_t *out_head) -{ - const uint32_t tail = qatomic_read(&r->prod_tail); - const uint32_t head = qatomic_read(&r->cons_head); - - if (pvrdma_idx_valid(tail, max_elems) && - pvrdma_idx_valid(head, max_elems)) { - *out_head = head & (max_elems - 1); - return tail != head; - } - return PVRDMA_INVALID_IDX; -} - -#endif /* __PVRDMA_RING_H__ */ diff --git a/migration/migration.c b/migration/migration.c index a5ddf43559..36768391b6 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -323,7 +323,7 @@ static int migrate_send_rp_message(MigrationIncomingState *mis, int ret = 0; trace_migrate_send_rp_message((int)message_type, len); - qemu_mutex_lock(&mis->rp_mutex); + QEMU_LOCK_GUARD(&mis->rp_mutex); /* * It's possible that the file handle got lost due to network @@ -331,7 +331,7 @@ static int migrate_send_rp_message(MigrationIncomingState *mis, */ if (!mis->to_src_file) { ret = -EIO; - goto error; + return ret; } qemu_put_be16(mis->to_src_file, (unsigned int)message_type); @@ -342,8 +342,6 @@ static int migrate_send_rp_message(MigrationIncomingState *mis, /* It's possible that qemu file got error during sending */ ret = qemu_file_get_error(mis->to_src_file); -error: - qemu_mutex_unlock(&mis->rp_mutex); return ret; } diff --git a/migration/multifd.c b/migration/multifd.c index 1a1e589064..03527c564c 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -739,7 +739,16 @@ static void multifd_tls_outgoing_handshake(QIOTask *task, } else { trace_multifd_tls_outgoing_handshake_complete(ioc); } - multifd_channel_connect(p, ioc, err); + + if (!multifd_channel_connect(p, ioc, err)) { + /* + * Error happen, mark multifd_send_thread status as 'quit' although it + * is not created, and then tell who pay attention to me. + */ + p->quit = true; + qemu_sem_post(&multifd_send_state->channels_ready); + qemu_sem_post(&p->sem_sync); + } } static void *multifd_tls_handshake_thread(void *opaque) @@ -798,9 +807,9 @@ static bool multifd_channel_connect(MultiFDSendParams *p, * function after the TLS handshake, * so we mustn't call multifd_send_thread until then */ - return false; - } else { return true; + } else { + return false; } } else { /* update for tls qio channel */ @@ -808,10 +817,10 @@ static bool multifd_channel_connect(MultiFDSendParams *p, qemu_thread_create(&p->thread, p->name, multifd_send_thread, p, QEMU_THREAD_JOINABLE); } - return false; + return true; } - return true; + return false; } static void multifd_new_send_channel_cleanup(MultiFDSendParams *p, @@ -844,7 +853,7 @@ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque) p->c = QIO_CHANNEL(sioc); qio_channel_set_delay(p->c, false); p->running = true; - if (multifd_channel_connect(p, sioc, local_err)) { + if (!multifd_channel_connect(p, sioc, local_err)) { goto cleanup; } return; diff --git a/migration/ram.c b/migration/ram.c index 72143da0ac..52537f14ac 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -819,7 +819,7 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, { bool ret; - qemu_mutex_lock(&rs->bitmap_mutex); + QEMU_LOCK_GUARD(&rs->bitmap_mutex); /* * Clear dirty bitmap if needed. This _must_ be called before we @@ -852,7 +852,6 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, if (ret) { rs->migration_dirty_pages--; } - qemu_mutex_unlock(&rs->bitmap_mutex); return ret; } @@ -3275,7 +3274,7 @@ static void decompress_data_with_multi_threads(QEMUFile *f, int idx, thread_count; thread_count = migrate_decompress_threads(); - qemu_mutex_lock(&decomp_done_lock); + QEMU_LOCK_GUARD(&decomp_done_lock); while (true) { for (idx = 0; idx < thread_count; idx++) { if (decomp_param[idx].done) { @@ -3295,7 +3294,6 @@ static void decompress_data_with_multi_threads(QEMUFile *f, qemu_cond_wait(&decomp_done_cond, &decomp_done_lock); } } - qemu_mutex_unlock(&decomp_done_lock); } /* diff --git a/monitor/monitor.c b/monitor/monitor.c index e94f532cf5..640496e562 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -349,7 +349,7 @@ monitor_qapi_event_queue_no_reenter(QAPIEvent event, QDict *qdict) evconf = &monitor_qapi_event_conf[event]; trace_monitor_protocol_event_queue(event, qdict, evconf->rate); - qemu_mutex_lock(&monitor_lock); + QEMU_LOCK_GUARD(&monitor_lock); if (!evconf->rate) { /* Unthrottled event */ @@ -391,8 +391,6 @@ monitor_qapi_event_queue_no_reenter(QAPIEvent event, QDict *qdict) timer_mod_ns(evstate->timer, now + evconf->rate); } } - - qemu_mutex_unlock(&monitor_lock); } void qapi_event_emit(QAPIEvent event, QDict *qdict) @@ -447,7 +445,7 @@ static void monitor_qapi_event_handler(void *opaque) MonitorQAPIEventConf *evconf = &monitor_qapi_event_conf[evstate->event]; trace_monitor_protocol_event_handler(evstate->event, evstate->qdict); - qemu_mutex_lock(&monitor_lock); + QEMU_LOCK_GUARD(&monitor_lock); if (evstate->qdict) { int64_t now = qemu_clock_get_ns(monitor_get_event_clock()); @@ -462,8 +460,6 @@ static void monitor_qapi_event_handler(void *opaque) timer_free(evstate->timer); g_free(evstate); } - - qemu_mutex_unlock(&monitor_lock); } static unsigned int qapi_event_throttle_hash(const void *key) diff --git a/monitor/qmp.c b/monitor/qmp.c index 2326bd7f9b..2b0308f933 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -76,7 +76,7 @@ static void monitor_qmp_cleanup_req_queue_locked(MonitorQMP *mon) static void monitor_qmp_cleanup_queue_and_resume(MonitorQMP *mon) { - qemu_mutex_lock(&mon->qmp_queue_lock); + QEMU_LOCK_GUARD(&mon->qmp_queue_lock); /* * Same condition as in monitor_qmp_dispatcher_co(), but before @@ -103,7 +103,6 @@ static void monitor_qmp_cleanup_queue_and_resume(MonitorQMP *mon) monitor_resume(&mon->common); } - qemu_mutex_unlock(&mon->qmp_queue_lock); } void qmp_send_response(MonitorQMP *mon, const QDict *rsp) @@ -179,7 +178,7 @@ static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void) Monitor *mon; MonitorQMP *qmp_mon; - qemu_mutex_lock(&monitor_lock); + QEMU_LOCK_GUARD(&monitor_lock); QTAILQ_FOREACH(mon, &mon_list, entry) { if (!monitor_is_qmp(mon)) { @@ -205,8 +204,6 @@ static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void) QTAILQ_INSERT_TAIL(&mon_list, mon, entry); } - qemu_mutex_unlock(&monitor_lock); - return req_obj; } @@ -376,30 +373,30 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err) req_obj->err = err; /* Protect qmp_requests and fetching its length. */ - qemu_mutex_lock(&mon->qmp_queue_lock); + WITH_QEMU_LOCK_GUARD(&mon->qmp_queue_lock) { - /* - * Suspend the monitor when we can't queue more requests after - * this one. Dequeuing in monitor_qmp_dispatcher_co() or - * monitor_qmp_cleanup_queue_and_resume() will resume it. - * Note that when OOB is disabled, we queue at most one command, - * for backward compatibility. - */ - if (!qmp_oob_enabled(mon) || - mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) { - monitor_suspend(&mon->common); - } + /* + * Suspend the monitor when we can't queue more requests after + * this one. Dequeuing in monitor_qmp_dispatcher_co() or + * monitor_qmp_cleanup_queue_and_resume() will resume it. + * Note that when OOB is disabled, we queue at most one command, + * for backward compatibility. + */ + if (!qmp_oob_enabled(mon) || + mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) { + monitor_suspend(&mon->common); + } - /* - * Put the request to the end of queue so that requests will be - * handled in time order. Ownership for req_obj, req, - * etc. will be delivered to the handler side. - */ - trace_monitor_qmp_in_band_enqueue(req_obj, mon, - mon->qmp_requests->length); - assert(mon->qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX); - g_queue_push_tail(mon->qmp_requests, req_obj); - qemu_mutex_unlock(&mon->qmp_queue_lock); + /* + * Put the request to the end of queue so that requests will be + * handled in time order. Ownership for req_obj, req, + * etc. will be delivered to the handler side. + */ + trace_monitor_qmp_in_band_enqueue(req_obj, mon, + mon->qmp_requests->length); + assert(mon->qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX); + g_queue_push_tail(mon->qmp_requests, req_obj); + } /* Kick the dispatcher routine */ if (!qatomic_xchg(&qmp_dispatcher_co_busy, true)) { diff --git a/net/l2tpv3.c b/net/l2tpv3.c index e4d4218db6..b7e1d84674 100644 --- a/net/l2tpv3.c +++ b/net/l2tpv3.c @@ -723,8 +723,12 @@ int net_init_l2tpv3(const Netdev *netdev, l2tpv3_read_poll(s, true); - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "l2tpv3: connected"); + /* Store startup parameters */ + nc->stored_config = g_new0(NetdevInfo, 1); + nc->stored_config->type = NET_BACKEND_L2TPV3; + + QAPI_CLONE_MEMBERS(NetdevL2TPv3Options, + &nc->stored_config->u.l2tpv3, l2tpv3); return 0; outerr: qemu_del_net_client(nc); @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu-common.h" #include "net/net.h" #include "clients.h" @@ -35,7 +36,6 @@ #include "monitor/monitor.h" #include "qemu/help_option.h" #include "qapi/qapi-commands-net.h" -#include "qapi/qapi-visit-net.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" @@ -55,6 +55,7 @@ #include "sysemu/sysemu.h" #include "net/filter.h" #include "qapi/string-output-visitor.h" +#include "qapi/hmp-output-visitor.h" /* Net bridge is currently not supported for W32. */ #if !defined(_WIN32) @@ -129,11 +130,12 @@ char *qemu_mac_strdup_printf(const uint8_t *macaddr) void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6]) { - snprintf(nc->info_str, sizeof(nc->info_str), - "model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x", - nc->model, - macaddr[0], macaddr[1], macaddr[2], - macaddr[3], macaddr[4], macaddr[5]); + g_free(nc->info_str); + nc->info_str = g_strdup_printf( + "model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x", + nc->model, + macaddr[0], macaddr[1], macaddr[2], + macaddr[3], macaddr[4], macaddr[5]); } static int mac_table[256] = {0}; @@ -352,6 +354,8 @@ static void qemu_free_net_client(NetClientState *nc) } g_free(nc->name); g_free(nc->model); + g_free(nc->info_str); + qapi_free_NetdevInfo(nc->stored_config); if (nc->destructor) { nc->destructor(nc); } @@ -528,6 +532,17 @@ int qemu_set_vnet_be(NetClientState *nc, bool is_be) #endif } +int qemu_can_receive_packet(NetClientState *nc) +{ + if (nc->receive_disabled) { + return 0; + } else if (nc->info->can_receive && + !nc->info->can_receive(nc)) { + return 0; + } + return 1; +} + int qemu_can_send_packet(NetClientState *sender) { int vm_running = runstate_is_running(); @@ -540,13 +555,7 @@ int qemu_can_send_packet(NetClientState *sender) return 1; } - if (sender->peer->receive_disabled) { - return 0; - } else if (sender->peer->info->can_receive && - !sender->peer->info->can_receive(sender->peer)) { - return 0; - } - return 1; + return qemu_can_receive_packet(sender->peer); } static ssize_t filter_receive_iov(NetClientState *nc, @@ -679,6 +688,25 @@ ssize_t qemu_send_packet(NetClientState *nc, const uint8_t *buf, int size) return qemu_send_packet_async(nc, buf, size, NULL); } +ssize_t qemu_receive_packet(NetClientState *nc, const uint8_t *buf, int size) +{ + if (!qemu_can_receive_packet(nc)) { + return 0; + } + + return qemu_net_queue_receive(nc->incoming_queue, buf, size); +} + +ssize_t qemu_receive_packet_iov(NetClientState *nc, const struct iovec *iov, + int iovcnt) +{ + if (!qemu_can_receive_packet(nc)) { + return 0; + } + + return qemu_net_queue_receive_iov(nc->incoming_queue, iov, iovcnt); +} + ssize_t qemu_send_packet_raw(NetClientState *nc, const uint8_t *buf, int size) { return qemu_send_packet_async_with_flags(nc, QEMU_NET_PACKET_FLAG_RAW, @@ -1133,6 +1161,11 @@ void netdev_add(QemuOpts *opts, Error **errp) void qmp_netdev_add(Netdev *netdev, Error **errp) { + if (!id_wellformed(netdev->id)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id", "an identifier"); + return; + } + net_client_init1(netdev, true, errp); } @@ -1189,14 +1222,42 @@ static void netfilter_print_info(Monitor *mon, NetFilterState *nf) monitor_printf(mon, "\n"); } +static char *generate_info_str(NetClientState *nc) +{ + NetdevInfo *ni = nc->stored_config; + char *ret_out = NULL; + Visitor *v; + + /* Use legacy field info_str for NIC and hubports */ + if ((nc->info->type == NET_CLIENT_DRIVER_NIC) || + (nc->info->type == NET_CLIENT_DRIVER_HUBPORT)) { + return g_strdup(nc->info_str ? nc->info_str : ""); + } + + if (!ni) { + return g_malloc0(1); + } + + v = hmp_output_visitor_new(&ret_out); + if (visit_type_NetdevInfo(v, "", &ni, NULL)) { + visit_complete(v, &ret_out); + } + visit_free(v); + + return ret_out; +} + void print_net_client(Monitor *mon, NetClientState *nc) { NetFilterState *nf; + char *info_str = generate_info_str(nc); monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name, nc->queue_index, NetClientDriver_str(nc->info->type), - nc->info_str); + info_str); + g_free(info_str); + if (!QTAILQ_EMPTY(&nc->filters)) { monitor_printf(mon, "filters:\n"); } @@ -1259,6 +1320,34 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name, return filter_list; } +NetdevInfoList *qmp_query_netdev(Error **errp) +{ + NetdevInfoList *list = NULL; + NetClientState *nc; + + QTAILQ_FOREACH(nc, &net_clients, next) { + /* + * Only look at netdevs (backend network devices), not for each queue + * or NIC / hubport + */ + if (nc->stored_config) { + NetdevInfo *element = QAPI_CLONE(NetdevInfo, nc->stored_config); + + g_free(element->id); /* Need to dealloc empty id after clone */ + element->id = g_strdup(nc->name); + + element->has_peer_id = nc->peer != NULL; + if (element->has_peer_id) { + element->peer_id = g_strdup(nc->peer->name); + } + + QAPI_LIST_PREPEND(list, element); + } + } + + return list; +} + void hmp_info_network(Monitor *mon, const QDict *qdict) { NetClientState *nc, *peer; diff --git a/net/netmap.c b/net/netmap.c index 350f097f91..ad59d4ade4 100644 --- a/net/netmap.c +++ b/net/netmap.c @@ -427,6 +427,13 @@ int net_init_netmap(const Netdev *netdev, pstrcpy(s->ifname, sizeof(s->ifname), netmap_opts->ifname); netmap_read_poll(s, true); /* Initially only poll for reads. */ + /* Store startup parameters */ + nc->stored_config = g_new0(NetdevInfo, 1); + nc->stored_config->type = NET_BACKEND_NETMAP; + + QAPI_CLONE_MEMBERS(NetdevNetmapOptions, + &nc->stored_config->u.netmap, netmap_opts); + return 0; } diff --git a/net/queue.c b/net/queue.c index 19e32c80fd..c872d51df8 100644 --- a/net/queue.c +++ b/net/queue.c @@ -182,6 +182,28 @@ static ssize_t qemu_net_queue_deliver_iov(NetQueue *queue, return ret; } +ssize_t qemu_net_queue_receive(NetQueue *queue, + const uint8_t *data, + size_t size) +{ + if (queue->delivering) { + return 0; + } + + return qemu_net_queue_deliver(queue, NULL, 0, data, size); +} + +ssize_t qemu_net_queue_receive_iov(NetQueue *queue, + const struct iovec *iov, + int iovcnt) +{ + if (queue->delivering) { + return 0; + } + + return qemu_net_queue_deliver_iov(queue, NULL, 0, iov, iovcnt); +} + ssize_t qemu_net_queue_send(NetQueue *queue, NetClientState *sender, unsigned flags, diff --git a/net/slirp.c b/net/slirp.c index be914c0be0..9454a673d6 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -377,6 +377,9 @@ static int net_slirp_init(NetClientState *peer, const char *model, int shift; char *end; struct slirp_config_str *config; + NetdevUserOptions *stored; + StringList **stored_hostfwd; + StringList **stored_guestfwd; if (!ipv4 && (vnetwork || vhost || vnameserver)) { error_setg(errp, "IPv4 disabled but netmask/host/dns provided"); @@ -552,9 +555,114 @@ static int net_slirp_init(NetClientState *peer, const char *model, nc = qemu_new_net_client(&net_slirp_info, peer, model, name); - snprintf(nc->info_str, sizeof(nc->info_str), - "net=%s,restrict=%s", inet_ntoa(net), - restricted ? "on" : "off"); + /* Store startup parameters */ + nc->stored_config = g_new0(NetdevInfo, 1); + nc->stored_config->type = NET_BACKEND_USER; + stored = &nc->stored_config->u.user; + + if (vhostname) { + stored->has_hostname = true; + stored->hostname = g_strdup(vhostname); + } + + stored->has_q_restrict = true; + stored->q_restrict = restricted; + + stored->has_ipv4 = true; + stored->ipv4 = ipv4; + + stored->has_ipv6 = true; + stored->ipv6 = ipv6; + + if (ipv4) { + uint8_t *net_bytes = (uint8_t *)&net; + uint8_t *mask_bytes = (uint8_t *)&mask; + + stored->has_net = true; + stored->net = g_strdup_printf("%d.%d.%d.%d/%d.%d.%d.%d", + net_bytes[0], net_bytes[1], + net_bytes[2], net_bytes[3], + mask_bytes[0], mask_bytes[1], + mask_bytes[2], mask_bytes[3]); + + stored->has_host = true; + stored->host = g_strdup(inet_ntoa(host)); + } + + if (tftp_export) { + stored->has_tftp = true; + stored->tftp = g_strdup(tftp_export); + } + + if (bootfile) { + stored->has_bootfile = true; + stored->bootfile = g_strdup(bootfile); + } + + if (vdhcp_start) { + stored->has_dhcpstart = true; + stored->dhcpstart = g_strdup(vdhcp_start); + } + + if (ipv4) { + stored->has_dns = true; + stored->dns = g_strdup(inet_ntoa(dns)); + } + + if (dnssearch) { + stored->has_dnssearch = true; + StringList **stored_list = &stored->dnssearch; + + for (int i = 0; dnssearch[i]; i++) { + String *element = g_new0(String, 1); + + element->str = g_strdup(dnssearch[i]); + QAPI_LIST_APPEND(stored_list, element); + } + } + + if (vdomainname) { + stored->has_domainname = true; + stored->domainname = g_strdup(vdomainname); + } + + if (ipv6) { + char addrstr[INET6_ADDRSTRLEN]; + const char *res; + + stored->has_ipv6_prefix = true; + stored->ipv6_prefix = g_strdup(vprefix6); + + stored->has_ipv6_prefixlen = true; + stored->ipv6_prefixlen = vprefix6_len; + + res = inet_ntop(AF_INET6, &ip6_host, + addrstr, sizeof(addrstr)); + + stored->has_ipv6_host = true; + stored->ipv6_host = g_strdup(res); + + res = inet_ntop(AF_INET6, &ip6_dns, + addrstr, sizeof(addrstr)); + + stored->has_ipv6_dns = true; + stored->ipv6_dns = g_strdup(res); + } + + if (smb_export) { + stored->has_smb = true; + stored->smb = g_strdup(smb_export); + } + + if (vsmbserver) { + stored->has_smbserver = true; + stored->smbserver = g_strdup(vsmbserver); + } + + if (tftp_server_name) { + stored->has_tftp_server_name = true; + stored->tftp_server_name = g_strdup(tftp_server_name); + } s = DO_UPCAST(SlirpState, nc, nc); @@ -581,15 +689,25 @@ static int net_slirp_init(NetClientState *peer, const char *model, s->poll_notifier.notify = net_slirp_poll_notify; main_loop_poll_add_notifier(&s->poll_notifier); + stored_hostfwd = &stored->hostfwd; + stored_guestfwd = &stored->guestfwd; + for (config = slirp_configs; config; config = config->next) { + String *element = g_new0(String, 1); + + element->str = g_strdup(config->str); if (config->flags & SLIRP_CFG_HOSTFWD) { if (slirp_hostfwd(s, config->str, errp) < 0) { goto error; } + stored->has_hostfwd = true; + QAPI_LIST_APPEND(stored_hostfwd, element); } else { if (slirp_guestfwd(s, config->str, errp) < 0) { goto error; } + stored->has_guestfwd = true; + QAPI_LIST_APPEND(stored_guestfwd, element); } } #ifndef _WIN32 diff --git a/net/socket.c b/net/socket.c index 15b410e8d8..c0de10c0c0 100644 --- a/net/socket.c +++ b/net/socket.c @@ -180,7 +180,6 @@ static void net_socket_send(void *opaque) s->fd = -1; net_socket_rs_init(&s->rs, net_socket_rs_finalize, false); s->nc.link_down = true; - memset(s->nc.info_str, 0, sizeof(s->nc.info_str)); return; } @@ -342,6 +341,7 @@ static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer, NetSocketState *s; SocketAddress *sa; SocketAddressType sa_type; + NetdevSocketOptions *stored; sa = socket_local_address(fd, errp); if (!sa) { @@ -385,19 +385,24 @@ static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer, net_socket_rs_init(&s->rs, net_socket_rs_finalize, false); net_socket_read_poll(s, true); + /* Store startup parameters */ + nc->stored_config = g_new0(NetdevInfo, 1); + nc->stored_config->type = NET_BACKEND_SOCKET; + stored = &nc->stored_config->u.socket; + + stored->has_fd = true; + stored->fd = g_strdup_printf("%d", fd); + /* mcast: save bound address as dst */ if (is_connected && mcast != NULL) { + stored->has_mcast = true; + stored->mcast = g_strdup(mcast); + s->dgram_dst = saddr; - snprintf(nc->info_str, sizeof(nc->info_str), - "socket: fd=%d (cloned mcast=%s:%d)", - fd, inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); } else { if (sa_type == SOCKET_ADDRESS_TYPE_UNIX) { s->dgram_dst.sin_family = AF_UNIX; } - - snprintf(nc->info_str, sizeof(nc->info_str), - "socket: fd=%d %s", fd, SocketAddressType_str(sa_type)); } return s; @@ -428,11 +433,10 @@ static NetSocketState *net_socket_fd_init_stream(NetClientState *peer, { NetClientState *nc; NetSocketState *s; + NetdevSocketOptions *stored; nc = qemu_new_net_client(&net_socket_info, peer, model, name); - snprintf(nc->info_str, sizeof(nc->info_str), "socket: fd=%d", fd); - s = DO_UPCAST(NetSocketState, nc, nc); s->fd = fd; @@ -447,6 +451,15 @@ static NetSocketState *net_socket_fd_init_stream(NetClientState *peer, } else { qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s); } + + /* Store startup parameters */ + nc->stored_config = g_new0(NetdevInfo, 1); + nc->stored_config->type = NET_BACKEND_SOCKET; + stored = &nc->stored_config->u.socket; + + stored->has_fd = true; + stored->fd = g_strdup_printf("%d", fd); + return s; } @@ -483,6 +496,7 @@ static void net_socket_accept(void *opaque) struct sockaddr_in saddr; socklen_t len; int fd; + NetdevSocketOptions *stored; for(;;) { len = sizeof(saddr); @@ -498,9 +512,12 @@ static void net_socket_accept(void *opaque) s->fd = fd; s->nc.link_down = false; net_socket_connect(s); - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "socket: connection from %s:%d", - inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + + /* Store additional startup parameters (extend net_socket_listen_init) */ + stored = &s->nc.stored_config->u.socket; + + stored->has_fd = true; + stored->fd = g_strdup_printf("%d", fd); } static int net_socket_listen_init(NetClientState *peer, @@ -513,6 +530,7 @@ static int net_socket_listen_init(NetClientState *peer, NetSocketState *s; struct sockaddr_in saddr; int fd, ret; + NetdevSocketOptions *stored; if (parse_host_port(&saddr, host_str, errp) < 0) { return -1; @@ -549,6 +567,15 @@ static int net_socket_listen_init(NetClientState *peer, net_socket_rs_init(&s->rs, net_socket_rs_finalize, false); qemu_set_fd_handler(s->listen_fd, net_socket_accept, NULL, s); + + /* Store startup parameters */ + nc->stored_config = g_new0(NetdevInfo, 1); + nc->stored_config->type = NET_BACKEND_SOCKET; + stored = &nc->stored_config->u.socket; + + stored->has_listen = true; + stored->listen = g_strdup(host_str); + return 0; } @@ -561,6 +588,7 @@ static int net_socket_connect_init(NetClientState *peer, NetSocketState *s; int fd, connected, ret; struct sockaddr_in saddr; + NetdevSocketOptions *stored; if (parse_host_port(&saddr, host_str, errp) < 0) { return -1; @@ -598,9 +626,12 @@ static int net_socket_connect_init(NetClientState *peer, return -1; } - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "socket: connect to %s:%d", - inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + /* Store additional startup parameters (extend net_socket_fd_init) */ + stored = &s->nc.stored_config->u.socket; + + stored->has_connect = true; + stored->connect = g_strdup(host_str); + return 0; } @@ -615,6 +646,7 @@ static int net_socket_mcast_init(NetClientState *peer, int fd; struct sockaddr_in saddr; struct in_addr localaddr, *param_localaddr; + NetdevSocketOptions *stored; if (parse_host_port(&saddr, host_str, errp) < 0) { return -1; @@ -643,11 +675,20 @@ static int net_socket_mcast_init(NetClientState *peer, s->dgram_dst = saddr; - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "socket: mcast=%s:%d", - inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); - return 0; + /* Store additional startup parameters (extend net_socket_fd_init) */ + stored = &s->nc.stored_config->u.socket; + + if (!stored->has_mcast) { + stored->has_mcast = true; + stored->mcast = g_strdup(host_str); + } + if (localaddr_str) { + stored->has_localaddr = true; + stored->localaddr = g_strdup(localaddr_str); + } + + return 0; } static int net_socket_udp_init(NetClientState *peer, @@ -660,6 +701,7 @@ static int net_socket_udp_init(NetClientState *peer, NetSocketState *s; int fd, ret; struct sockaddr_in laddr, raddr; + NetdevSocketOptions *stored; if (parse_host_port(&laddr, lhost, errp) < 0) { return -1; @@ -698,9 +740,15 @@ static int net_socket_udp_init(NetClientState *peer, s->dgram_dst = raddr; - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "socket: udp=%s:%d", - inet_ntoa(raddr.sin_addr), ntohs(raddr.sin_port)); + /* Store additional startup parameters (extend net_socket_fd_init) */ + stored = &s->nc.stored_config->u.socket; + + stored->has_localaddr = true; + stored->localaddr = g_strdup(lhost); + + stored->has_udp = true; + stored->udp = g_strdup(rhost); + return 0; } diff --git a/net/tap-win32.c b/net/tap-win32.c index 2b5dcda36e..21e451107b 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -768,6 +768,7 @@ static int tap_win32_init(NetClientState *peer, const char *model, NetClientState *nc; TAPState *s; tap_win32_overlapped_t *handle; + NetdevTapOptions *stored; if (tap_win32_open(&handle, ifname) < 0) { printf("tap: Could not open '%s'\n", ifname); @@ -778,8 +779,13 @@ static int tap_win32_init(NetClientState *peer, const char *model, s = DO_UPCAST(TAPState, nc, nc); - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "tap: ifname=%s", ifname); + /* Store startup parameters */ + nc->stored_config = g_new0(NetdevInfo, 1); + nc->stored_config->type = NET_BACKEND_TAP; + stored = &nc->stored_config->u.tap; + + stored->has_ifname = true; + stored->ifname = g_strdup(ifname); s->handle = handle; @@ -590,6 +590,7 @@ int net_init_bridge(const Netdev *netdev, const char *name, const char *helper, *br; TAPState *s; int fd, vnet_hdr; + NetdevBridgeOptions *stored; assert(netdev->type == NET_CLIENT_DRIVER_BRIDGE); bridge = &netdev->u.bridge; @@ -609,8 +610,20 @@ int net_init_bridge(const Netdev *netdev, const char *name, } s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr); - snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s", helper, - br); + /* Store startup parameters */ + s->nc.stored_config = g_new0(NetdevInfo, 1); + s->nc.stored_config->type = NET_BACKEND_BRIDGE; + stored = &s->nc.stored_config->u.bridge; + + if (br) { + stored->has_br = true; + stored->br = g_strdup(br); + } + + if (helper) { + stored->has_helper = true; + stored->helper = g_strdup(helper); + } return 0; } @@ -656,11 +669,13 @@ static void 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, Error **errp) + int vnet_hdr, int fd, NetdevInfo **common_stored, + Error **errp) { Error *err = NULL; TAPState *s = net_tap_fd_init(peer, model, name, fd, vnet_hdr); int vhostfd; + NetdevTapOptions *stored; tap_set_sndbuf(s->fd, tap, &err); if (err) { @@ -668,15 +683,59 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, return; } + /* Store startup parameters */ + if (!*common_stored) { + *common_stored = g_new0(NetdevInfo, 1); + (*common_stored)->type = NET_BACKEND_TAP; + s->nc.stored_config = *common_stored; + } + stored = &(*common_stored)->u.tap; + + if (tap->has_sndbuf && !stored->has_sndbuf) { + stored->has_sndbuf = true; + stored->sndbuf = tap->sndbuf; + } + + if (vnet_hdr && !stored->has_vnet_hdr) { + stored->has_vnet_hdr = true; + stored->vnet_hdr = true; + } + if (tap->has_fd || tap->has_fds) { - snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd); + if (!stored->has_fds) { + stored->has_fds = true; + stored->fds = g_strdup_printf("%d", fd); + } else { + char *tmp_s = stored->fds; + stored->fds = g_strdup_printf("%s:%d", stored->fds, fd); + g_free(tmp_s); + } } else if (tap->has_helper) { - snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s", - tap->helper); + if (!stored->has_helper) { + stored->has_helper = true; + stored->helper = g_strdup(tap->helper); + } + + if (!stored->has_br) { + stored->has_br = true; + stored->br = tap->has_br ? g_strdup(tap->br) : + g_strdup(DEFAULT_BRIDGE_INTERFACE); + } } else { - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "ifname=%s,script=%s,downscript=%s", ifname, script, - downscript); + if (ifname && !stored->has_ifname) { + stored->has_ifname = true; + stored->ifname = g_strdup(ifname); + } + + if (script && !stored->has_script) { + stored->has_script = true; + stored->script = g_strdup(script); + } + + if (downscript && !stored->has_downscript) { + stored->has_downscript = true; + stored->downscript = g_strdup(downscript); + } if (strcmp(downscript, "no") != 0) { snprintf(s->down_script, sizeof(s->down_script), "%s", downscript); @@ -689,9 +748,20 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, vhostfdname || (tap->has_vhostforce && tap->vhostforce)) { VhostNetOptions options; + stored->has_vhost = true; + stored->vhost = true; + + if (tap->has_vhostforce && tap->vhostforce) { + stored->has_vhostforce = true; + stored->vhostforce = true; + } + options.backend_type = VHOST_BACKEND_TYPE_KERNEL; options.net_backend = &s->nc; if (tap->has_poll_us) { + stored->has_poll_us = true; + stored->poll_us = tap->poll_us; + options.busyloop_timeout = tap->poll_us; } else { options.busyloop_timeout = 0; @@ -731,6 +801,15 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } options.opaque = (void *)(uintptr_t)vhostfd; + if (!stored->has_vhostfds) { + stored->has_vhostfds = true; + stored->vhostfds = g_strdup_printf("%d", vhostfd); + } else { + char *tmp_s = stored->vhostfds; + stored->vhostfds = g_strdup_printf("%s:%d", stored->fds, vhostfd); + g_free(tmp_s); + } + s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { if (tap->has_vhostforce && tap->vhostforce) { @@ -783,6 +862,7 @@ int net_init_tap(const Netdev *netdev, const char *name, const char *vhostfdname; char ifname[128]; int ret = 0; + NetdevInfo *common_stored = NULL; /* will store configuration */ assert(netdev->type == NET_CLIENT_DRIVER_TAP); tap = &netdev->u.tap; @@ -829,7 +909,7 @@ int net_init_tap(const Netdev *netdev, const char *name, net_init_tap_one(tap, peer, "tap", name, NULL, script, downscript, - vhostfdname, vnet_hdr, fd, &err); + vhostfdname, vnet_hdr, fd, &common_stored, &err); if (err) { error_propagate(errp, err); close(fd); @@ -892,7 +972,7 @@ int net_init_tap(const Netdev *netdev, const char *name, net_init_tap_one(tap, peer, "tap", name, ifname, script, downscript, tap->has_vhostfds ? vhost_fds[i] : NULL, - vnet_hdr, fd, &err); + vnet_hdr, fd, &common_stored, &err); if (err) { error_propagate(errp, err); ret = -1; @@ -935,7 +1015,7 @@ free_fail: net_init_tap_one(tap, peer, "bridge", name, ifname, script, downscript, vhostfdname, - vnet_hdr, fd, &err); + vnet_hdr, fd, &common_stored, &err); if (err) { error_propagate(errp, err); close(fd); @@ -981,7 +1061,8 @@ free_fail: net_init_tap_one(tap, peer, "tap", name, ifname, i >= 1 ? "no" : script, i >= 1 ? "no" : downscript, - vhostfdname, vnet_hdr, fd, &err); + vhostfdname, vnet_hdr, fd, + &common_stored, &err); if (err) { error_propagate(errp, err); close(fd); @@ -84,6 +84,7 @@ static int net_vde_init(NetClientState *peer, const char *model, VDECONN *vde; char *init_group = (char *)group; char *init_sock = (char *)sock; + NetdevVdeOptions *stored; struct vde_open_args args = { .port = port, @@ -99,15 +100,33 @@ static int net_vde_init(NetClientState *peer, const char *model, nc = qemu_new_net_client(&net_vde_info, peer, model, name); - snprintf(nc->info_str, sizeof(nc->info_str), "sock=%s,fd=%d", - sock, vde_datafd(vde)); - s = DO_UPCAST(VDEState, nc, nc); s->vde = vde; qemu_set_fd_handler(vde_datafd(s->vde), vde_to_qemu, NULL, s); + /* Store startup parameters */ + nc->stored_config = g_new0(NetdevInfo, 1); + nc->stored_config->type = NET_BACKEND_VDE; + stored = &nc->stored_config->u.vde; + + if (sock) { + stored->has_sock = true; + stored->sock = g_strdup(sock); + } + + stored->has_port = true; + stored->port = port; + + if (group) { + stored->has_group = true; + stored->group = g_strdup(group); + } + + stored->has_mode = true; + stored->mode = mode; + return 0; } diff --git a/net/vhost-user.c b/net/vhost-user.c index ffbd94d944..e443c4b2b5 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -311,14 +311,15 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event) } static int net_vhost_user_init(NetClientState *peer, const char *device, - const char *name, Chardev *chr, - int queues) + const char *name, const char *chardev, + Chardev *chr, int queues) { Error *err = NULL; NetClientState *nc, *nc0 = NULL; NetVhostUserState *s = NULL; VhostUserState *user; int i; + NetdevVhostUserOptions *stored; assert(name); assert(queues > 0); @@ -326,8 +327,6 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, user = g_new0(struct VhostUserState, 1); for (i = 0; i < queues; i++) { nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name); - snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s", - i, chr->label); nc->queue_index = i; if (!nc0) { nc0 = nc; @@ -355,6 +354,16 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, assert(s->vhost_net); + /* Store startup parameters */ + nc0->stored_config = g_new0(NetdevInfo, 1); + nc0->stored_config->type = NET_BACKEND_VHOST_USER; + stored = &nc0->stored_config->u.vhost_user; + + stored->chardev = g_strdup(chardev); + + stored->has_queues = true; + stored->queues = queues; + return 0; err: @@ -446,5 +455,6 @@ int net_init_vhost_user(const Netdev *netdev, const char *name, return -1; } - return net_vhost_user_init(peer, "vhost_user", name, chr, queues); + return net_vhost_user_init(peer, "vhost_user", name, + vhost_user_opts->chardev, chr, queues); } diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index fe659ec9e2..5a28bbcd7b 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -184,9 +184,22 @@ static int net_vhost_vdpa_init(NetClientState *peer, const char *device, VhostVDPAState *s; int vdpa_device_fd = -1; int ret = 0; + NetdevVhostVDPAOptions *stored; + assert(name); nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name); - snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA); + + /* Store startup parameters */ + nc->stored_config = g_new0(NetdevInfo, 1); + nc->stored_config->type = NET_BACKEND_VHOST_VDPA; + stored = &nc->stored_config->u.vhost_vdpa; + + stored->has_vhostdev = true; + stored->vhostdev = g_strdup(vhostdev); + + stored->has_queues = true; + stored->queues = 1; /* TODO: change when support multiqueue */ + nc->queue_index = 0; s = DO_UPCAST(VhostVDPAState, nc, nc); vdpa_device_fd = qemu_open_old(vhostdev, O_RDWR); diff --git a/qapi/hmp-output-visitor.c b/qapi/hmp-output-visitor.c new file mode 100644 index 0000000000..8036605f97 --- /dev/null +++ b/qapi/hmp-output-visitor.c @@ -0,0 +1,193 @@ +/* + * HMP string output Visitor + * + * Copyright Yandex N.V., 2021 + * + * 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/osdep.h" +#include "qemu/cutils.h" +#include "qapi/hmp-output-visitor.h" +#include "qapi/visitor-impl.h" + +struct HMPOutputVisitor { + Visitor visitor; + char **result; + GString *buffer; + bool is_continue; +}; + +static HMPOutputVisitor *to_hov(Visitor *v) +{ + return container_of(v, HMPOutputVisitor, visitor); +} + +static void hmp_output_append_formatted(Visitor *v, const char *fmt, ...) +{ + HMPOutputVisitor *ov = to_hov(v); + va_list args; + + if (ov->is_continue) { + g_string_append(ov->buffer, ","); + } else { + ov->is_continue = true; + } + + va_start(args, fmt); + g_string_append_vprintf(ov->buffer, fmt, args); + va_end(args); +} + +static void hmp_output_skip_comma(Visitor *v) +{ + HMPOutputVisitor *ov = to_hov(v); + + ov->is_continue = false; +} + +static bool hmp_output_start_struct(Visitor *v, const char *name, + void **obj, size_t unused, Error **errp) +{ + return true; +} + +static void hmp_output_end_struct(Visitor *v, void **obj) {} + +static bool hmp_output_start_list(Visitor *v, const char *name, + GenericList **listp, size_t size, + Error **errp) +{ + hmp_output_append_formatted(v, "%s=[", name); + /* First element in array without comma before it */ + hmp_output_skip_comma(v); + + return true; +} + +static GenericList *hmp_output_next_list(Visitor *v, GenericList *tail, + size_t size) +{ + return tail->next; +} + +static void hmp_output_end_list(Visitor *v, void **obj) +{ + /* Don't need comma after last array element */ + hmp_output_skip_comma(v); + hmp_output_append_formatted(v, "]"); +} + +static bool hmp_output_type_int64(Visitor *v, const char *name, + int64_t *obj, Error **errp) +{ + hmp_output_append_formatted(v, "%s=%" PRId64, name, *obj); + + return true; +} + +static bool hmp_output_type_uint64(Visitor *v, const char *name, + uint64_t *obj, Error **errp) +{ + hmp_output_append_formatted(v, "%s=%" PRIu64, name, *obj); + + return true; +} + +static bool hmp_output_type_bool(Visitor *v, const char *name, bool *obj, + Error **errp) +{ + hmp_output_append_formatted(v, "%s=%s", name, *obj ? "true" : "false"); + + return true; +} + +static bool hmp_output_type_str(Visitor *v, const char *name, char **obj, + Error **errp) +{ + /* Skip already printed or unused fields */ + if (!*obj || g_str_equal(name, "id") || g_str_equal(name, "type")) { + return true; + } + + /* Do not print stub name for StringList elements */ + if (g_str_equal(name, "str")) { + hmp_output_append_formatted(v, "%s", *obj); + } else { + hmp_output_append_formatted(v, "%s=%s", name, *obj); + } + + return true; +} + +static bool hmp_output_type_number(Visitor *v, const char *name, + double *obj, Error **errp) +{ + hmp_output_append_formatted(v, "%s=%.17g", name, *obj); + + return true; +} + +/* TODO: remove this function? */ +static bool hmp_output_type_any(Visitor *v, const char *name, + QObject **obj, Error **errp) +{ + return true; +} + +static bool hmp_output_type_null(Visitor *v, const char *name, + QNull **obj, Error **errp) +{ + hmp_output_append_formatted(v, "%s=NULL", name); + + return true; +} + +static void hmp_output_complete(Visitor *v, void *opaque) +{ + HMPOutputVisitor *ov = to_hov(v); + + *ov->result = g_string_free(ov->buffer, false); + ov->buffer = NULL; +} + +static void hmp_output_free(Visitor *v) +{ + HMPOutputVisitor *ov = to_hov(v); + + if (ov->buffer) { + g_string_free(ov->buffer, true); + } + g_free(v); +} + +Visitor *hmp_output_visitor_new(char **result) +{ + HMPOutputVisitor *v; + + v = g_malloc0(sizeof(*v)); + + v->visitor.type = VISITOR_OUTPUT; + v->visitor.start_struct = hmp_output_start_struct; + v->visitor.end_struct = hmp_output_end_struct; + v->visitor.start_list = hmp_output_start_list; + v->visitor.next_list = hmp_output_next_list; + v->visitor.end_list = hmp_output_end_list; + v->visitor.type_int64 = hmp_output_type_int64; + v->visitor.type_uint64 = hmp_output_type_uint64; + v->visitor.type_bool = hmp_output_type_bool; + v->visitor.type_str = hmp_output_type_str; + v->visitor.type_number = hmp_output_type_number; + v->visitor.type_any = hmp_output_type_any; + v->visitor.type_null = hmp_output_type_null; + v->visitor.complete = hmp_output_complete; + v->visitor.free = hmp_output_free; + + v->result = result; + v->buffer = g_string_new(""); + v->is_continue = false; + + return &v->visitor; +} diff --git a/qapi/meson.build b/qapi/meson.build index fcb15a78f1..d4424ae6e7 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -8,6 +8,7 @@ util_ss.add(files( 'qobject-output-visitor.c', 'string-input-visitor.c', 'string-output-visitor.c', + 'hmp-output-visitor.c', )) if have_system or have_tools util_ss.add(files( diff --git a/qapi/net.json b/qapi/net.json index c31748c87f..87361ebd9a 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -714,3 +714,83 @@ ## { 'event': 'FAILOVER_NEGOTIATED', 'data': {'device-id': 'str'} } + +## +# @NetBackend: +# +# Available netdev backend drivers. +# +# Since: 6.0 +## +{ 'enum': 'NetBackend', + 'data': [ 'bridge', 'l2tpv3', 'netmap', 'socket', 'tap', 'user', 'vde', + 'vhost-user', 'vhost-vdpa' ] } + +## +# @NetdevInfo: +# +# Configuration of a network backend device (netdev). +# +# @id: Device identifier. +# +# @type: Specify the driver used for interpreting remaining arguments. +# +# @peer-id: The connected frontend network device name (absent if no frontend +# is connected). +# +# Since: 6.0 +## +{ 'union': 'NetdevInfo', + 'base': { 'id': 'str', + 'type': 'NetBackend', + '*peer-id': 'str' }, + 'discriminator': 'type', + 'data': { + 'bridge': 'NetdevBridgeOptions', + 'l2tpv3': 'NetdevL2TPv3Options', + 'netmap': 'NetdevNetmapOptions', + 'socket': 'NetdevSocketOptions', + 'tap': 'NetdevTapOptions', + 'user': 'NetdevUserOptions', + 'vde': 'NetdevVdeOptions', + 'vhost-user': 'NetdevVhostUserOptions', + 'vhost-vdpa': 'NetdevVhostVDPAOptions' } } + +## +# @query-netdev: +# +# Get a list of @NetdevInfo for all virtual network backend devices (netdevs). +# +# Returns: a list of @NetdevInfo describing each netdev. +# +# Since: 6.0 +# +# Example: +# +# -> { "execute": "query-netdev" } +# <- { "return": [ +# { +# "ipv6": true, +# "ipv4": true, +# "host": "10.0.2.2", +# "ipv6-dns": "fec0::3", +# "ipv6-prefix": "fec0::", +# "net": "10.0.2.0/255.255.255.0", +# "ipv6-host": "fec0::2", +# "type": "user", +# "peer-id": "net0", +# "dns": "10.0.2.3", +# "hostfwd": [ +# { +# "str": "tcp::20004-:22" +# } +# ], +# "ipv6-prefixlen": 64, +# "id": "netdev0", +# "restrict": false +# } +# ] +# } +# +## +{ 'command': 'query-netdev', 'returns': ['NetdevInfo'] } diff --git a/qemu-options.hx b/qemu-options.hx index 622d3bfa5a..fe83ea09b2 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1705,7 +1705,7 @@ ERST DEFHEADING() -DEFHEADING(USB options:) +DEFHEADING(USB convenience options:) DEF("usb", 0, QEMU_OPTION_usb, "-usb enable on-board USB host controller (if not enabled by default)\n", @@ -1723,9 +1723,31 @@ DEF("usbdevice", HAS_ARG, QEMU_OPTION_usbdevice, QEMU_ARCH_ALL) SRST ``-usbdevice devname`` - Add the USB device devname. Note that this option is deprecated, - please use ``-device usb-...`` instead. See the chapter about + Add the USB device devname, and enable an on-board USB controller + if possible and necessary (just like it can be done via + ``-machine usb=on``). Note that this option is mainly intended for + the user's convenience only. More fine-grained control can be + achieved by selecting a USB host controller (if necessary) and the + desired USB device via the ``-device`` option instead. For example, + instead of using ``-usbdevice mouse`` it is possible to use + ``-device qemu-xhci -device usb-mouse`` to connect the USB mouse + to a USB 3.0 controller instead (at least on machines that support + PCI and do not have an USB controller enabled by default yet). + For more details, see the chapter about :ref:`Connecting USB devices` in the System Emulation Users Guide. + Possible devices for devname are: + + ``braille`` + Braille device. This will use BrlAPI to display the braille + output on a real or fake device (i.e. it also creates a + corresponding ``braille`` chardev automatically beside the + ``usb-braille`` USB device). + + ``ccid`` + Smartcard reader device + + ``keyboard`` + Standard USB keyboard. Will override the PS/2 keyboard (if present). ``mouse`` Virtual Mouse. This will override the PS/2 mouse emulation when @@ -1737,9 +1759,10 @@ SRST position without having to grab the mouse. Also overrides the PS/2 mouse emulation when activated. - ``braille`` - Braille device. This will use BrlAPI to display the braille - output on a real or fake device. + ``wacom-tablet`` + Wacom PenPartner USB tablet. + + ERST DEFHEADING() diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index fa6f2b6272..1050e36169 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -215,8 +215,7 @@ sed -e '1h;2,$H;$!d;g' -e 's/[^};]*pvrdma[^(| ]*([^)]*);//g' \ "$linux/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h" > \ "$tmp_pvrdma_verbs"; -for i in "$linux/drivers/infiniband/hw/vmw_pvrdma/pvrdma_ring.h" \ - "$linux/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h" \ +for i in "$linux/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h" \ "$tmp_pvrdma_verbs"; do \ cp_portable "$i" \ "$output/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/" diff --git a/softmmu/vl.c b/softmmu/vl.c index b7673b9613..a750dae6b1 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -3180,8 +3180,6 @@ void qemu_init(int argc, char **argv, char **envp) qemu_opts_parse_noisily(olist, "usb=on", false); break; case QEMU_OPTION_usbdevice: - error_report("'-usbdevice' is deprecated, please use " - "'-device usb-...' instead"); olist = qemu_find_opts("machine"); qemu_opts_parse_noisily(olist, "usb=on", false); add_device_config(DEV_USB, optarg); diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 2688e1bfad..66ee9fbf45 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -30,6 +30,9 @@ qtests_generic = [ if config_host.has_key('CONFIG_MODULES') qtests_generic += [ 'modules-test' ] endif +if slirp.found() + qtests_generic += [ 'test-query-netdev' ] +endif qtests_pci = \ (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) + \ diff --git a/tests/qtest/test-query-netdev.c b/tests/qtest/test-query-netdev.c new file mode 100644 index 0000000000..1118537a9f --- /dev/null +++ b/tests/qtest/test-query-netdev.c @@ -0,0 +1,120 @@ +/* + * QTest testcase for the query-netdev + * + * Copyright Yandex N.V., 2019 + * + * 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/osdep.h" + +#include "libqos/libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" + +/* + * Events can get in the way of responses we are actually waiting for. + */ +GCC_FMT_ATTR(2, 3) +static QObject *wait_command(QTestState *who, const char *command, ...) +{ + va_list ap; + QDict *response; + QObject *result; + + va_start(ap, command); + qtest_qmp_vsend(who, command, ap); + va_end(ap); + + response = qtest_qmp_receive(who); + + result = qdict_get(response, "return"); + g_assert(result); + qobject_ref(result); + qobject_unref(response); + + return result; +} + +static void qmp_query_netdev_no_error(QTestState *qts, size_t netdevs_count) +{ + QObject *resp; + QList *netdevs; + + resp = wait_command(qts, "{'execute': 'query-netdev'}"); + + netdevs = qobject_to(QList, resp); + g_assert(netdevs); + g_assert(qlist_size(netdevs) == netdevs_count); + + qobject_unref(resp); +} + +static void test_query_netdev(void) +{ + const char *arch = qtest_get_arch(); + QObject *resp; + QTestState *state; + + /* Choosing machine for platforms without default one */ + if (g_str_equal(arch, "arm") || + g_str_equal(arch, "aarch64")) { + state = qtest_init( + "-nodefaults " + "-M virt " + "-netdev user,id=slirp0"); + } else if (g_str_equal(arch, "tricore")) { + state = qtest_init( + "-nodefaults " + "-M tricore_testboard " + "-netdev user,id=slirp0"); + } else if (g_str_equal(arch, "avr")) { + state = qtest_init( + "-nodefaults " + "-M mega2560 " + "-netdev user,id=slirp0"); + } else if (g_str_equal(arch, "rx")) { + state = qtest_init( + "-nodefaults " + "-M gdbsim-r5f562n8 " + "-netdev user,id=slirp0"); + } else { + state = qtest_init( + "-nodefaults " + "-netdev user,id=slirp0"); + } + g_assert(state); + + qmp_query_netdev_no_error(state, 1); + + resp = wait_command(state, + "{'execute': 'netdev_add', 'arguments': {" + " 'id': 'slirp1'," + " 'type': 'user'}}"); + qobject_unref(resp); + + qmp_query_netdev_no_error(state, 2); + + resp = wait_command(state, + "{'execute': 'netdev_del', 'arguments': {" + " 'id': 'slirp1'}}"); + qobject_unref(resp); + + qmp_query_netdev_no_error(state, 1); + + qtest_quit(state); +} + +int main(int argc, char **argv) +{ + int ret = 0; + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/net/qapi/query_netdev", test_query_netdev); + + ret = g_test_run(); + + return ret; +} diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index bad3a60993..e025b54c05 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -2128,7 +2128,7 @@ static void test_qemu_strtosz_float(void) str = "12.345M"; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, (uint64_t) (12.345 * MiB)); + g_assert_cmpint(res, ==, (uint64_t) (12.345 * MiB + 0.5)); g_assert(endptr == str + 7); } diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c index 523ee64fb7..3e13997406 100644 --- a/tools/virtiofsd/fuse_virtio.c +++ b/tools/virtiofsd/fuse_virtio.c @@ -792,7 +792,13 @@ static void fv_queue_set_started(VuDev *dev, int qidx, bool started) assert(0); } } else { + /* + * Temporarily drop write-lock taken in virtio_loop() so that + * the queue thread doesn't block in virtio_send_msg(). + */ + vu_dispatch_unlock(vud); fv_queue_cleanup_thread(vud, qidx); + vu_dispatch_wrlock(vud); } } diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index fc7e1b1e8e..b144320e48 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -37,6 +37,8 @@ #include "qemu/osdep.h" #include "qemu/timer.h" +#include "qemu-version.h" +#include "qemu-common.h" #include "fuse_virtio.h" #include "fuse_log.h" #include "fuse_lowlevel.h" @@ -221,22 +223,27 @@ static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st, static int xattr_map_client(const struct lo_data *lo, const char *client_name, char **out_name); -static int is_dot_or_dotdot(const char *name) +static bool is_dot_or_dotdot(const char *name) { return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); } /* Is `path` a single path component that is not "." or ".."? */ -static int is_safe_path_component(const char *path) +static bool is_safe_path_component(const char *path) { if (strchr(path, '/')) { - return 0; + return false; } return !is_dot_or_dotdot(path); } +static bool is_empty(const char *name) +{ + return name[0] == '\0'; +} + static struct lo_data *lo_data(fuse_req_t req) { return (struct lo_data *)fuse_req_userdata(req); @@ -1083,6 +1090,11 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", parent, name); + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + /* * Don't use is_safe_path_component(), allow "." and ".." for NFS export * support. @@ -1174,6 +1186,11 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, struct fuse_entry_param e; struct lo_cred old = {}; + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -1246,6 +1263,11 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, char procname[64]; int saverr; + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -1308,8 +1330,7 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, return NULL; } - res = do_statx(lo, dir->fd, name, &attr, - AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, &mnt_id); + res = do_statx(lo, dir->fd, name, &attr, AT_SYMLINK_NOFOLLOW, &mnt_id); lo_inode_put(lo, &dir); if (res == -1) { return NULL; @@ -1324,6 +1345,11 @@ static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) struct lo_inode *inode; struct lo_data *lo = lo_data(req); + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -1353,6 +1379,11 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, struct lo_inode *newinode = NULL; struct lo_data *lo = lo_data(req); + if (is_empty(name) || is_empty(newname)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { fuse_reply_err(req, EINVAL); return; @@ -1406,6 +1437,11 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) struct lo_inode *inode; struct lo_data *lo = lo_data(req); + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -3666,6 +3702,11 @@ static void fuse_lo_data_cleanup(struct lo_data *lo) free(lo->source); } +static void qemu_version(void) +{ + printf("virtiofsd version " QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n"); +} + int main(int argc, char *argv[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); @@ -3737,6 +3778,7 @@ int main(int argc, char *argv[]) ret = 0; goto err_out1; } else if (opts.show_version) { + qemu_version(); fuse_lowlevel_version(); ret = 0; goto err_out1; diff --git a/util/cutils.c b/util/cutils.c index d89a40a8c3..c442882b88 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -275,10 +275,9 @@ static int do_strtosz(const char *nptr, const char **end, int retval; const char *endptr, *f; unsigned char c; - bool mul_required = false, hex = false; - uint64_t val; + bool hex = false; + uint64_t val, valf = 0; int64_t mul; - double fraction = 0.0; /* Parse integral portion as decimal. */ retval = qemu_strtou64(nptr, &endptr, 10, &val); @@ -308,17 +307,19 @@ static int do_strtosz(const char *nptr, const char **end, * without fractional digits. If we see an exponent, treat * the entire input as invalid instead. */ + double fraction; + f = endptr; retval = qemu_strtod_finite(f, &endptr, &fraction); if (retval) { - fraction = 0.0; endptr++; } else if (memchr(f, 'e', endptr - f) || memchr(f, 'E', endptr - f)) { endptr = nptr; retval = -EINVAL; goto out; - } else if (fraction != 0) { - mul_required = true; + } else { + /* Extract into a 64-bit fixed-point fraction. */ + valf = (uint64_t)(fraction * 0x1p64); } } c = *endptr; @@ -333,16 +334,35 @@ static int do_strtosz(const char *nptr, const char **end, mul = suffix_mul(default_suffix, unit); assert(mul > 0); } - if (mul == 1 && mul_required) { - endptr = nptr; - retval = -EINVAL; - goto out; - } - if (val > (UINT64_MAX - ((uint64_t) (fraction * mul))) / mul) { - retval = -ERANGE; - goto out; + if (mul == 1) { + /* When a fraction is present, a scale is required. */ + if (valf != 0) { + endptr = nptr; + retval = -EINVAL; + goto out; + } + } else { + uint64_t valh, tmp; + + /* Compute exact result: 64.64 x 64.0 -> 128.64 fixed point */ + mulu64(&val, &valh, val, mul); + mulu64(&valf, &tmp, valf, mul); + val += tmp; + valh += val < tmp; + + /* Round 0.5 upward. */ + tmp = valf >> 63; + val += tmp; + valh += val < tmp; + + /* Report overflow. */ + if (valh != 0) { + retval = -ERANGE; + goto out; + } } - *result = val * mul + (uint64_t) (fraction * mul); + + *result = val; retval = 0; out: |