aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2015-10-29 09:49:52 +0000
committerPeter Maydell <peter.maydell@linaro.org>2015-10-29 09:49:52 +0000
commit7bc8e0c967a4ef77657174d28af775691e18b4ce (patch)
treeeb44098ade96974f811b5861135cdd8a9746b587 /hw
parent331c5e2091009f170554fed4ef884aeea871e4bb (diff)
parent3f1e1478db2d67098d98f2c3acf5a4946b7fb643 (diff)
downloadqemu-7bc8e0c967a4ef77657174d28af775691e18b4ce.zip
qemu-7bc8e0c967a4ef77657174d28af775691e18b4ce.tar.gz
qemu-7bc8e0c967a4ef77657174d28af775691e18b4ce.tar.bz2
Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging
virtio, pc, memory: fixes+features for 2.5 New features: This enables hotplug for multifunction devices. Patches are very small, so I think it's OK to merge at this stage. There's also some new infrastructure for vhost-user testing not enabled yet so it's harmless to merge. I've reverted the "gap between DIMMs" workaround, as it seems too risky, and applied my own patch in virtio, but not in dataplane code. This means that dataplane is broken for some complex DIMM configurations for now. Waiting for Stefan to review the dataplane fix. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # gpg: Signature made Thu 29 Oct 2015 09:36:16 GMT using RSA key ID D28D5469 # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" * remotes/mst/tags/for_upstream: enable multi-function hot-add remove function during multi-function hot-add tests/vhost-user-bridge: add vhost-user bridge application Revert "memhp: extend address auto assignment to support gaps" Revert "pc: memhp: force gaps between DIMM's GPA" virtio: drop virtqueue_map_sg virtio-scsi: convert to virtqueue_map virtio-serial: convert to virtio_map virtio-blk: convert to virtqueue_map virtio: switch to virtio_map virtio: introduce virtio_map mmap-alloc: fix error handling pc: memhp: do not emit inserting event for coldplugged DIMMs vhost-user-test: fix up rhel6 build vhost-user: cleanup msg size math vhost-user: cleanup struct size math Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/acpi/memory_hotplug.c8
-rw-r--r--hw/block/virtio-blk.c5
-rw-r--r--hw/char/virtio-serial-bus.c5
-rw-r--r--hw/i386/pc.c5
-rw-r--r--hw/i386/pc_piix.c1
-rw-r--r--hw/i386/pc_q35.c1
-rw-r--r--hw/mem/pc-dimm.c15
-rw-r--r--hw/pci/pci.c40
-rw-r--r--hw/pci/pci_host.c15
-rw-r--r--hw/pci/pcie.c39
-rw-r--r--hw/ppc/spapr.c2
-rw-r--r--hw/scsi/virtio-scsi.c16
-rw-r--r--hw/virtio/vhost-user.c22
-rw-r--r--hw/virtio/virtio.c52
14 files changed, 149 insertions, 77 deletions
diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c
index 2ff0d5c..ce428df 100644
--- a/hw/acpi/memory_hotplug.c
+++ b/hw/acpi/memory_hotplug.c
@@ -238,10 +238,12 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
mdev->dimm = dev;
mdev->is_enabled = true;
- mdev->is_inserting = true;
+ if (dev->hotplugged) {
+ mdev->is_inserting = true;
- /* do ACPI magic */
- acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS);
+ /* do ACPI magic */
+ acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS);
+ }
return;
}
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 8beb26b..3e230de 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -839,10 +839,7 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f,
req->next = s->rq;
s->rq = req;
- virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr,
- req->elem.in_num, 1);
- virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr,
- req->elem.out_num, 0);
+ virtqueue_map(&req->elem);
}
return 0;
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index be97058..497b0af 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -705,10 +705,7 @@ static int fetch_active_ports_list(QEMUFile *f, int version_id,
qemu_get_buffer(f, (unsigned char *)&port->elem,
sizeof(port->elem));
- virtqueue_map_sg(port->elem.in_sg, port->elem.in_addr,
- port->elem.in_num, 1);
- virtqueue_map_sg(port->elem.out_sg, port->elem.out_addr,
- port->elem.out_num, 1);
+ virtqueue_map(&port->elem);
/*
* Port was throttled on source machine. Let's
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 3d958ba..0cb8afd 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1616,7 +1616,6 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
HotplugHandlerClass *hhc;
Error *local_err = NULL;
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
- PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
PCDIMMDevice *dimm = PC_DIMM(dev);
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
MemoryRegion *mr = ddc->get_memory_region(dimm);
@@ -1632,8 +1631,7 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
goto out;
}
- pc_dimm_memory_plug(dev, &pcms->hotplug_memory, mr, align,
- pcmc->inter_dimm_gap, &local_err);
+ pc_dimm_memory_plug(dev, &pcms->hotplug_memory, mr, align, &local_err);
if (local_err) {
goto out;
}
@@ -1953,7 +1951,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
PCMachineClass *pcmc = PC_MACHINE_CLASS(oc);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
- pcmc->inter_dimm_gap = true;
pcmc->get_hotplug_handler = mc->get_hotplug_handler;
mc->get_hotplug_handler = pc_get_hotpug_handler;
mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 9d4425a..393dcc4 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -487,7 +487,6 @@ static void pc_i440fx_2_4_machine_options(MachineClass *m)
m->alias = NULL;
m->is_default = 0;
pcmc->broken_reserved_end = true;
- pcmc->inter_dimm_gap = false;
SET_MACHINE_COMPAT(m, PC_COMPAT_2_4);
}
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 3744abd..2f8f396 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -385,7 +385,6 @@ static void pc_q35_2_4_machine_options(MachineClass *m)
pc_q35_2_5_machine_options(m);
m->alias = NULL;
pcmc->broken_reserved_end = true;
- pcmc->inter_dimm_gap = false;
SET_MACHINE_COMPAT(m, PC_COMPAT_2_4);
}
diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c
index 2bae994..80f424b 100644
--- a/hw/mem/pc-dimm.c
+++ b/hw/mem/pc-dimm.c
@@ -33,8 +33,7 @@ typedef struct pc_dimms_capacity {
} pc_dimms_capacity;
void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms,
- MemoryRegion *mr, uint64_t align, bool gap,
- Error **errp)
+ MemoryRegion *mr, uint64_t align, Error **errp)
{
int slot;
MachineState *machine = MACHINE(qdev_get_machine());
@@ -50,7 +49,7 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms,
addr = pc_dimm_get_free_addr(hpms->base,
memory_region_size(&hpms->mr),
- !addr ? NULL : &addr, align, gap,
+ !addr ? NULL : &addr, align,
memory_region_size(mr), &local_err);
if (local_err) {
goto out;
@@ -295,8 +294,8 @@ static int pc_dimm_built_list(Object *obj, void *opaque)
uint64_t pc_dimm_get_free_addr(uint64_t address_space_start,
uint64_t address_space_size,
- uint64_t *hint, uint64_t align, bool gap,
- uint64_t size, Error **errp)
+ uint64_t *hint, uint64_t align, uint64_t size,
+ Error **errp)
{
GSList *list = NULL, *item;
uint64_t new_addr, ret = 0;
@@ -341,15 +340,13 @@ uint64_t pc_dimm_get_free_addr(uint64_t address_space_start,
goto out;
}
- if (ranges_overlap(dimm->addr, dimm_size, new_addr,
- size + (gap ? 1 : 0))) {
+ if (ranges_overlap(dimm->addr, dimm_size, new_addr, size)) {
if (hint) {
DeviceState *d = DEVICE(dimm);
error_setg(errp, "address range conflicts with '%s'", d->id);
goto out;
}
- new_addr = QEMU_ALIGN_UP(dimm->addr + dimm_size + (gap ? 1 : 0),
- align);
+ new_addr = QEMU_ALIGN_UP(dimm->addr + dimm_size, align);
}
}
ret = new_addr;
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index b0bf540..168b9cc 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -847,6 +847,9 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
PCIConfigWriteFunc *config_write = pc->config_write;
Error *local_err = NULL;
AddressSpace *dma_as;
+ DeviceState *dev = DEVICE(pci_dev);
+
+ pci_dev->bus = bus;
if (devfn < 0) {
for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices);
@@ -864,9 +867,17 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
PCI_SLOT(devfn), PCI_FUNC(devfn), name,
bus->devices[devfn]->name);
return NULL;
+ } else if (dev->hotplugged &&
+ pci_get_function_0(pci_dev)) {
+ error_setg(errp, "PCI: slot %d function 0 already ocuppied by %s,"
+ " new func %s cannot be exposed to guest.",
+ PCI_SLOT(devfn),
+ bus->devices[PCI_DEVFN(PCI_SLOT(devfn), 0)]->name,
+ name);
+
+ return NULL;
}
- pci_dev->bus = bus;
pci_dev->devfn = devfn;
dma_as = pci_device_iommu_address_space(pci_dev);
@@ -2454,6 +2465,33 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range)
pci_for_each_device_under_bus(bus, pci_dev_get_w64, range);
}
+static bool pcie_has_upstream_port(PCIDevice *dev)
+{
+ PCIDevice *parent_dev = pci_bridge_get_device(dev->bus);
+
+ /* Device associated with an upstream port.
+ * As there are several types of these, it's easier to check the
+ * parent device: upstream ports are always connected to
+ * root or downstream ports.
+ */
+ return parent_dev &&
+ pci_is_express(parent_dev) &&
+ parent_dev->exp.exp_cap &&
+ (pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_DOWNSTREAM);
+}
+
+PCIDevice *pci_get_function_0(PCIDevice *pci_dev)
+{
+ if(pcie_has_upstream_port(pci_dev)) {
+ /* With an upstream PCIe port, we only support 1 device at slot 0 */
+ return pci_dev->bus->devices[0];
+ } else {
+ /* Other bus types might support multiple devices at slots 0-31 */
+ return pci_dev->bus->devices[PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 0)];
+ }
+}
+
static const TypeInfo pci_device_type_info = {
.name = TYPE_PCI_DEVICE,
.parent = TYPE_DEVICE,
diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c
index 3e26f92..49f59a5 100644
--- a/hw/pci/pci_host.c
+++ b/hw/pci/pci_host.c
@@ -20,6 +20,7 @@
#include "hw/pci/pci.h"
#include "hw/pci/pci_host.h"
+#include "hw/pci/pci_bus.h"
#include "trace.h"
/* debug PCI */
@@ -52,6 +53,13 @@ void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr,
uint32_t limit, uint32_t val, uint32_t len)
{
assert(len <= 4);
+ /* non-zero functions are only exposed when function 0 is present,
+ * allowing direct removal of unexposed functions.
+ */
+ if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) {
+ return;
+ }
+
trace_pci_cfg_write(pci_dev->name, PCI_SLOT(pci_dev->devfn),
PCI_FUNC(pci_dev->devfn), addr, val);
pci_dev->config_write(pci_dev, addr, val, MIN(len, limit - addr));
@@ -63,6 +71,13 @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr,
uint32_t ret;
assert(len <= 4);
+ /* non-zero functions are only exposed when function 0 is present,
+ * allowing direct removal of unexposed functions.
+ */
+ if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) {
+ return ~0x0;
+ }
+
ret = pci_dev->config_read(pci_dev, addr, MIN(len, limit - addr));
trace_pci_cfg_read(pci_dev->name, PCI_SLOT(pci_dev->devfn),
PCI_FUNC(pci_dev->devfn), addr, ret);
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 6e28985..32c65c2 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -249,25 +249,43 @@ void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
return;
}
- /* TODO: multifunction hot-plug.
- * Right now, only a device of function = 0 is allowed to be
- * hot plugged/unplugged.
+ /* To enable multifunction hot-plug, we just ensure the function
+ * 0 added last. When function 0 is added, we set the sltsta and
+ * inform OS via event notification.
*/
- assert(PCI_FUNC(pci_dev->devfn) == 0);
+ if (pci_get_function_0(pci_dev)) {
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_PDS);
+ pcie_cap_slot_event(PCI_DEVICE(hotplug_dev),
+ PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP);
+ }
+}
- pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
- PCI_EXP_SLTSTA_PDS);
- pcie_cap_slot_event(PCI_DEVICE(hotplug_dev),
- PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP);
+static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque)
+{
+ object_unparent(OBJECT(dev));
}
void pcie_cap_slot_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
uint8_t *exp_cap;
+ PCIDevice *pci_dev = PCI_DEVICE(dev);
+ PCIBus *bus = pci_dev->bus;
pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp);
+ /* In case user cancel the operation of multi-function hot-add,
+ * remove the function that is unexposed to guest individually,
+ * without interaction with guest.
+ */
+ if (pci_dev->devfn &&
+ !bus->devices[0]) {
+ pcie_unplug_device(bus, pci_dev, NULL);
+
+ return;
+ }
+
pcie_cap_slot_push_attention_button(PCI_DEVICE(hotplug_dev));
}
@@ -378,11 +396,6 @@ void pcie_cap_slot_reset(PCIDevice *dev)
hotplug_event_update_event_status(dev);
}
-static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque)
-{
- object_unparent(OBJECT(dev));
-}
-
void pcie_cap_slot_write_config(PCIDevice *dev,
uint32_t addr, uint32_t val, int len)
{
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index e1202ce..288b57e 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -2157,7 +2157,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
goto out;
}
- pc_dimm_memory_plug(dev, &ms->hotplug_memory, mr, align, false, &local_err);
+ pc_dimm_memory_plug(dev, &ms->hotplug_memory, mr, align, &local_err);
if (local_err) {
goto out;
}
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 20885fb..46553e1 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -205,20 +205,8 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
assert(n < vs->conf.num_queues);
req = virtio_scsi_init_req(s, vs->cmd_vqs[n]);
qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
- /* TODO: add a way for SCSIBusInfo's load_request to fail,
- * and fail migration instead of asserting here.
- * When we do, we might be able to re-enable NDEBUG below.
- */
-#ifdef NDEBUG
-#error building with NDEBUG is not supported
-#endif
- assert(req->elem.in_num <= ARRAY_SIZE(req->elem.in_sg));
- assert(req->elem.out_num <= ARRAY_SIZE(req->elem.out_sg));
-
- virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr,
- req->elem.in_num, 1);
- virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr,
- req->elem.out_num, 0);
+
+ virtqueue_map(&req->elem);
if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size,
sizeof(VirtIOSCSICmdResp) + vs->sense_size) < 0) {
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 0aa8e0d..83c84f1 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -201,7 +201,7 @@ static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base,
.request = VHOST_USER_SET_LOG_BASE,
.flags = VHOST_USER_VERSION,
.payload.u64 = base,
- .size = sizeof(m.payload.u64),
+ .size = sizeof(msg.payload.u64),
};
if (shmfd && log->fd != -1) {
@@ -265,8 +265,8 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev,
return -1;
}
- msg.size = sizeof(m.payload.memory.nregions);
- msg.size += sizeof(m.payload.memory.padding);
+ msg.size = sizeof(msg.payload.memory.nregions);
+ msg.size += sizeof(msg.payload.memory.padding);
msg.size += fd_num * sizeof(VhostUserMemoryRegion);
vhost_user_write(dev, &msg, fds, fd_num);
@@ -281,7 +281,7 @@ static int vhost_user_set_vring_addr(struct vhost_dev *dev,
.request = VHOST_USER_SET_VRING_ADDR,
.flags = VHOST_USER_VERSION,
.payload.addr = *addr,
- .size = sizeof(*addr),
+ .size = sizeof(msg.payload.addr),
};
vhost_user_write(dev, &msg, NULL, 0);
@@ -304,7 +304,7 @@ static int vhost_set_vring(struct vhost_dev *dev,
.request = request,
.flags = VHOST_USER_VERSION,
.payload.state = *ring,
- .size = sizeof(*ring),
+ .size = sizeof(msg.payload.state),
};
vhost_user_write(dev, &msg, NULL, 0);
@@ -346,7 +346,7 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev,
.request = VHOST_USER_GET_VRING_BASE,
.flags = VHOST_USER_VERSION,
.payload.state = *ring,
- .size = sizeof(*ring),
+ .size = sizeof(msg.payload.state),
};
vhost_user_write(dev, &msg, NULL, 0);
@@ -361,7 +361,7 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev,
return -1;
}
- if (msg.size != sizeof(m.payload.state)) {
+ if (msg.size != sizeof(msg.payload.state)) {
error_report("Received bad msg size.");
return -1;
}
@@ -381,7 +381,7 @@ static int vhost_set_vring_file(struct vhost_dev *dev,
.request = request,
.flags = VHOST_USER_VERSION,
.payload.u64 = file->index & VHOST_USER_VRING_IDX_MASK,
- .size = sizeof(m.payload.u64),
+ .size = sizeof(msg.payload.u64),
};
if (ioeventfd_enabled() && file->fd > 0) {
@@ -413,7 +413,7 @@ static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64)
.request = request,
.flags = VHOST_USER_VERSION,
.payload.u64 = u64,
- .size = sizeof(m.payload.u64),
+ .size = sizeof(msg.payload.u64),
};
vhost_user_write(dev, &msg, NULL, 0);
@@ -456,7 +456,7 @@ static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64)
return -1;
}
- if (msg.size != sizeof(m.payload.u64)) {
+ if (msg.size != sizeof(msg.payload.u64)) {
error_report("Received bad msg size.");
return -1;
}
@@ -592,7 +592,7 @@ static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr)
msg.request = VHOST_USER_SEND_RARP;
msg.flags = VHOST_USER_VERSION;
memcpy((char *)&msg.payload.u64, mac_addr, 6);
- msg.size = sizeof(m.payload.u64);
+ msg.size = sizeof(msg.payload.u64);
err = vhost_user_write(dev, &msg, NULL, 0);
return err;
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index d0bc72e..939f802 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -448,28 +448,59 @@ int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes,
return in_bytes <= in_total && out_bytes <= out_total;
}
-void virtqueue_map_sg(struct iovec *sg, hwaddr *addr,
- size_t num_sg, int is_write)
+static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr,
+ unsigned int *num_sg, unsigned int max_size,
+ int is_write)
{
unsigned int i;
hwaddr len;
- if (num_sg > VIRTQUEUE_MAX_SIZE) {
- error_report("virtio: map attempt out of bounds: %zd > %d",
- num_sg, VIRTQUEUE_MAX_SIZE);
- exit(1);
- }
+ /* Note: this function MUST validate input, some callers
+ * are passing in num_sg values received over the network.
+ */
+ /* TODO: teach all callers that this can fail, and return failure instead
+ * of asserting here.
+ * When we do, we might be able to re-enable NDEBUG below.
+ */
+#ifdef NDEBUG
+#error building with NDEBUG is not supported
+#endif
+ assert(*num_sg <= max_size);
- for (i = 0; i < num_sg; i++) {
+ for (i = 0; i < *num_sg; i++) {
len = sg[i].iov_len;
sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write);
- if (sg[i].iov_base == NULL || len != sg[i].iov_len) {
+ if (!sg[i].iov_base) {
error_report("virtio: error trying to map MMIO memory");
exit(1);
}
+ if (len == sg[i].iov_len) {
+ continue;
+ }
+ if (*num_sg >= max_size) {
+ error_report("virtio: memory split makes iovec too large");
+ exit(1);
+ }
+ memmove(sg + i + 1, sg + i, sizeof(*sg) * (*num_sg - i));
+ memmove(addr + i + 1, addr + i, sizeof(*addr) * (*num_sg - i));
+ assert(len < sg[i + 1].iov_len);
+ sg[i].iov_len = len;
+ addr[i + 1] += len;
+ sg[i + 1].iov_len -= len;
+ ++*num_sg;
}
}
+void virtqueue_map(VirtQueueElement *elem)
+{
+ virtqueue_map_iovec(elem->in_sg, elem->in_addr, &elem->in_num,
+ MIN(ARRAY_SIZE(elem->in_sg), ARRAY_SIZE(elem->in_addr)),
+ 1);
+ virtqueue_map_iovec(elem->out_sg, elem->out_addr, &elem->out_num,
+ MIN(ARRAY_SIZE(elem->out_sg), ARRAY_SIZE(elem->out_addr)),
+ 0);
+}
+
int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
{
unsigned int i, head, max;
@@ -531,8 +562,7 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
} while ((i = virtqueue_next_desc(vdev, desc_pa, i, max)) != max);
/* Now map what we have collected */
- virtqueue_map_sg(elem->in_sg, elem->in_addr, elem->in_num, 1);
- virtqueue_map_sg(elem->out_sg, elem->out_addr, elem->out_num, 0);
+ virtqueue_map(elem);
elem->index = head;