virtio,pc,pci: features, fixes, cleanups
Features: SR-IOV emulation for pci virtio-mem-pci support for s390 interleave support for cxl big endian support for vdpa svq new QAPI events for vhost-user Also vIOMMU reset order fixups are in. Fixes, cleanups all over the place. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> -----BEGIN PGP SIGNATURE----- iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAme4b8sPHG1zdEByZWRo YXQuY29tAAoJECgfDbjSjVRpHKcIAKPJsVqPdda2dJ7b7FdyRT0Q+uwezXqaGHd4 7Lzih1wsxYNkwIAyPtEb76/21qiS7BluqlUCfCB66R9xWjP5/KfvAFj4/r4AEduE fxAgYzotNpv55zcRbcflMyvQ42WGiZZHC+o5Lp7vDXUP3pIyHrl0Ydh5WmcD+hwS BjXvda58TirQpPJ7rUL+sSfLih17zQkkDcfv5/AgorDy1wK09RBKwMx/gq7wG8yJ twy8eBY2CmfmFD7eTM+EKqBD2T0kwLEeLfS/F/tl5Fyg6lAiYgYtCbGLpAmWErsg XZvfZmwqL7CNzWexGvPFnnLyqwC33WUP0k0kT88Y5wh3/h98blw= =tej8 -----END PGP SIGNATURE----- Merge tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu into staging virtio,pc,pci: features, fixes, cleanups Features: SR-IOV emulation for pci virtio-mem-pci support for s390 interleave support for cxl big endian support for vdpa svq new QAPI events for vhost-user Also vIOMMU reset order fixups are in. Fixes, cleanups all over the place. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # -----BEGIN PGP SIGNATURE----- # # iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAme4b8sPHG1zdEByZWRo # YXQuY29tAAoJECgfDbjSjVRpHKcIAKPJsVqPdda2dJ7b7FdyRT0Q+uwezXqaGHd4 # 7Lzih1wsxYNkwIAyPtEb76/21qiS7BluqlUCfCB66R9xWjP5/KfvAFj4/r4AEduE # fxAgYzotNpv55zcRbcflMyvQ42WGiZZHC+o5Lp7vDXUP3pIyHrl0Ydh5WmcD+hwS # BjXvda58TirQpPJ7rUL+sSfLih17zQkkDcfv5/AgorDy1wK09RBKwMx/gq7wG8yJ # twy8eBY2CmfmFD7eTM+EKqBD2T0kwLEeLfS/F/tl5Fyg6lAiYgYtCbGLpAmWErsg # XZvfZmwqL7CNzWexGvPFnnLyqwC33WUP0k0kT88Y5wh3/h98blw= # =tej8 # -----END PGP SIGNATURE----- # gpg: Signature made Fri 21 Feb 2025 20:21:31 HKT # gpg: using RSA key 5D09FD0871C8F85B94CA8A0D281F0DB8D28D5469 # gpg: issuer "mst@redhat.com" # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" [full] # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" [full] # Primary key fingerprint: 0270 606B 6F3C DF3D 0B17 0970 C350 3912 AFBE 8E67 # Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA 8A0D 281F 0DB8 D28D 5469 * tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu: (41 commits) docs/devel/reset: Document reset expectations for DMA and IOMMU hw/vfio/common: Add a trace point in vfio_reset_handler hw/arm/smmuv3: Move reset to exit phase hw/i386/intel-iommu: Migrate to 3-phase reset hw/virtio/virtio-iommu: Migrate to 3-phase reset vhost-user-snd: correct the calculation of config_size net: vhost-user: add QAPI events to report connection state hw/virtio/virtio-nsm: Respond with correct length vdpa: Fix endian bugs in shadow virtqueue MAINTAINERS: add more files to `vhost` cryptodev/vhost: allocate CryptoDevBackendVhost using g_mem0() vhost-iova-tree: Update documentation vhost-iova-tree, svq: Implement GPA->IOVA & partial IOVA->HVA trees vhost-iova-tree: Implement an IOVA-only tree amd_iommu: Use correct bitmask to set capability BAR amd_iommu: Use correct DTE field for interrupt passthrough hw/virtio: reset virtio balloon stats on machine reset mem/cxl_type3: support 3, 6, 12 and 16 interleave ways hw/mem/cxl_type3: Ensure errp is set on realization failure hw/mem/cxl_type3: Fix special_ops memory leak on msix_init_exclusive_bar() failure ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
b69801dd6b
11
MAINTAINERS
11
MAINTAINERS
@ -2221,12 +2221,16 @@ M: Michael S. Tsirkin <mst@redhat.com>
|
|||||||
R: Stefano Garzarella <sgarzare@redhat.com>
|
R: Stefano Garzarella <sgarzare@redhat.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: hw/*/*vhost*
|
F: hw/*/*vhost*
|
||||||
F: docs/interop/vhost-user.json
|
F: docs/interop/vhost-user*
|
||||||
F: docs/interop/vhost-user.rst
|
F: docs/system/devices/vhost-user*
|
||||||
F: contrib/vhost-user-*/
|
F: contrib/vhost-user-*/
|
||||||
F: backends/vhost-user.c
|
F: backends/*vhost*
|
||||||
F: include/system/vhost-user-backend.h
|
F: include/system/vhost-user-backend.h
|
||||||
|
F: include/hw/virtio/vhost*
|
||||||
|
F: include/*/vhost*
|
||||||
F: subprojects/libvhost-user/
|
F: subprojects/libvhost-user/
|
||||||
|
F: block/export/vhost-user*
|
||||||
|
F: util/vhost-user-server.c
|
||||||
|
|
||||||
vhost-shadow-virtqueue
|
vhost-shadow-virtqueue
|
||||||
R: Eugenio Pérez <eperezma@redhat.com>
|
R: Eugenio Pérez <eperezma@redhat.com>
|
||||||
@ -2255,6 +2259,7 @@ F: include/hw/virtio/virtio-balloon.h
|
|||||||
F: system/balloon.c
|
F: system/balloon.c
|
||||||
F: include/system/balloon.h
|
F: include/system/balloon.h
|
||||||
F: tests/qtest/virtio-balloon-test.c
|
F: tests/qtest/virtio-balloon-test.c
|
||||||
|
F: tests/functional/test_virtio_balloon.py
|
||||||
|
|
||||||
virtio-9p
|
virtio-9p
|
||||||
M: Christian Schoenebeck <qemu_oss@crudebyte.com>
|
M: Christian Schoenebeck <qemu_oss@crudebyte.com>
|
||||||
|
@ -53,7 +53,7 @@ cryptodev_vhost_init(
|
|||||||
CryptoDevBackendVhost *crypto;
|
CryptoDevBackendVhost *crypto;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
crypto = g_new(CryptoDevBackendVhost, 1);
|
crypto = g_new0(CryptoDevBackendVhost, 1);
|
||||||
crypto->dev.max_queues = 1;
|
crypto->dev.max_queues = 1;
|
||||||
crypto->dev.nvqs = 1;
|
crypto->dev.nvqs = 1;
|
||||||
crypto->dev.vqs = crypto->vqs;
|
crypto->dev.vqs = crypto->vqs;
|
||||||
|
@ -277,13 +277,6 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name
|
|||||||
better reflects the way this property affects all random data within
|
better reflects the way this property affects all random data within
|
||||||
the device tree blob, not just the ``kaslr-seed`` node.
|
the device tree blob, not just the ``kaslr-seed`` node.
|
||||||
|
|
||||||
``pc-i440fx-2.4`` up to ``pc-i440fx-2.12`` (since 9.1)
|
|
||||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
|
||||||
|
|
||||||
These old machine types are quite neglected nowadays and thus might have
|
|
||||||
various pitfalls with regards to live migration. Use a newer machine type
|
|
||||||
instead.
|
|
||||||
|
|
||||||
PPC 405 ``ref405ep`` machine (since 9.1)
|
PPC 405 ``ref405ep`` machine (since 9.1)
|
||||||
''''''''''''''''''''''''''''''''''''''''
|
''''''''''''''''''''''''''''''''''''''''
|
||||||
|
|
||||||
|
@ -972,6 +972,11 @@ from Linux in 2021, and is not supported anymore by QEMU either.
|
|||||||
System emulator machines
|
System emulator machines
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
Note: Versioned machine types that have been introduced in a QEMU version
|
||||||
|
that has initially been released more than 6 years before are considered
|
||||||
|
obsolete and will be removed without further notice in this document.
|
||||||
|
Please use newer machine types instead.
|
||||||
|
|
||||||
``s390-virtio`` (removed in 2.6)
|
``s390-virtio`` (removed in 2.6)
|
||||||
''''''''''''''''''''''''''''''''
|
''''''''''''''''''''''''''''''''
|
||||||
|
|
||||||
@ -1006,12 +1011,6 @@ mips ``fulong2e`` machine alias (removed in 6.0)
|
|||||||
|
|
||||||
This machine has been renamed ``fuloong2e``.
|
This machine has been renamed ``fuloong2e``.
|
||||||
|
|
||||||
``pc-0.10`` up to ``pc-i440fx-2.3`` (removed in 4.0 up to 9.0)
|
|
||||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
|
||||||
|
|
||||||
These machine types were very old and likely could not be used for live
|
|
||||||
migration from old QEMU versions anymore. Use a newer machine type instead.
|
|
||||||
|
|
||||||
Raspberry Pi ``raspi2`` and ``raspi3`` machines (removed in 6.2)
|
Raspberry Pi ``raspi2`` and ``raspi3`` machines (removed in 6.2)
|
||||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||||
|
|
||||||
|
@ -143,6 +143,11 @@ The *exit* phase is executed only when the last reset operation ends. Therefore
|
|||||||
the object does not need to care how many of reset controllers it has and how
|
the object does not need to care how many of reset controllers it has and how
|
||||||
many of them have started a reset.
|
many of them have started a reset.
|
||||||
|
|
||||||
|
DMA capable devices are expected to cancel all outstanding DMA operations
|
||||||
|
during either 'enter' or 'hold' phases. IOMMUs are expected to reset during
|
||||||
|
the 'exit' phase and this sequencing makes sure no outstanding DMA request
|
||||||
|
will fault.
|
||||||
|
|
||||||
|
|
||||||
Handling reset in a resettable object
|
Handling reset in a resettable object
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
@ -52,9 +52,11 @@ setting up a BAR for a VF.
|
|||||||
...
|
...
|
||||||
|
|
||||||
/* Add and initialize the SR/IOV capability */
|
/* Add and initialize the SR/IOV capability */
|
||||||
pcie_sriov_pf_init(d, 0x200, "your_virtual_dev",
|
if (!pcie_sriov_pf_init(d, 0x200, "your_virtual_dev",
|
||||||
vf_devid, initial_vfs, total_vfs,
|
vf_devid, initial_vfs, total_vfs,
|
||||||
fun_offset, stride);
|
fun_offset, stride, errp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Set up individual VF BARs (parameters as for normal BARs) */
|
/* Set up individual VF BARs (parameters as for normal BARs) */
|
||||||
pcie_sriov_pf_init_vf_bar( ... )
|
pcie_sriov_pf_init_vf_bar( ... )
|
||||||
|
@ -924,7 +924,12 @@ static void smmu_base_realize(DeviceState *dev, Error **errp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smmu_base_reset_hold(Object *obj, ResetType type)
|
/*
|
||||||
|
* Make sure the IOMMU is reset in 'exit' phase after
|
||||||
|
* all outstanding DMA requests have been quiesced during
|
||||||
|
* the 'enter' or 'hold' reset phases
|
||||||
|
*/
|
||||||
|
static void smmu_base_reset_exit(Object *obj, ResetType type)
|
||||||
{
|
{
|
||||||
SMMUState *s = ARM_SMMU(obj);
|
SMMUState *s = ARM_SMMU(obj);
|
||||||
|
|
||||||
@ -949,7 +954,7 @@ static void smmu_base_class_init(ObjectClass *klass, void *data)
|
|||||||
device_class_set_props(dc, smmu_dev_properties);
|
device_class_set_props(dc, smmu_dev_properties);
|
||||||
device_class_set_parent_realize(dc, smmu_base_realize,
|
device_class_set_parent_realize(dc, smmu_base_realize,
|
||||||
&sbc->parent_realize);
|
&sbc->parent_realize);
|
||||||
rc->phases.hold = smmu_base_reset_hold;
|
rc->phases.exit = smmu_base_reset_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo smmu_base_info = {
|
static const TypeInfo smmu_base_info = {
|
||||||
|
@ -1870,13 +1870,19 @@ static void smmu_init_irq(SMMUv3State *s, SysBusDevice *dev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smmu_reset_hold(Object *obj, ResetType type)
|
/*
|
||||||
|
* Make sure the IOMMU is reset in 'exit' phase after
|
||||||
|
* all outstanding DMA requests have been quiesced during
|
||||||
|
* the 'enter' or 'hold' reset phases
|
||||||
|
*/
|
||||||
|
static void smmu_reset_exit(Object *obj, ResetType type)
|
||||||
{
|
{
|
||||||
SMMUv3State *s = ARM_SMMUV3(obj);
|
SMMUv3State *s = ARM_SMMUV3(obj);
|
||||||
SMMUv3Class *c = ARM_SMMUV3_GET_CLASS(s);
|
SMMUv3Class *c = ARM_SMMUV3_GET_CLASS(s);
|
||||||
|
|
||||||
if (c->parent_phases.hold) {
|
trace_smmu_reset_exit();
|
||||||
c->parent_phases.hold(obj, type);
|
if (c->parent_phases.exit) {
|
||||||
|
c->parent_phases.exit(obj, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
smmuv3_init_regs(s);
|
smmuv3_init_regs(s);
|
||||||
@ -1999,7 +2005,7 @@ static void smmuv3_class_init(ObjectClass *klass, void *data)
|
|||||||
SMMUv3Class *c = ARM_SMMUV3_CLASS(klass);
|
SMMUv3Class *c = ARM_SMMUV3_CLASS(klass);
|
||||||
|
|
||||||
dc->vmsd = &vmstate_smmuv3;
|
dc->vmsd = &vmstate_smmuv3;
|
||||||
resettable_class_set_parent_phases(rc, NULL, smmu_reset_hold, NULL,
|
resettable_class_set_parent_phases(rc, NULL, NULL, smmu_reset_exit,
|
||||||
&c->parent_phases);
|
&c->parent_phases);
|
||||||
device_class_set_parent_realize(dc, smmu_realize,
|
device_class_set_parent_realize(dc, smmu_realize,
|
||||||
&c->parent_realize);
|
&c->parent_realize);
|
||||||
|
@ -56,6 +56,7 @@ smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x"
|
|||||||
smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s"
|
smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s"
|
||||||
smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s"
|
smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s"
|
||||||
smmuv3_inv_notifiers_iova(const char *name, int asid, int vmid, uint64_t iova, uint8_t tg, uint64_t num_pages, int stage) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" stage=%d"
|
smmuv3_inv_notifiers_iova(const char *name, int asid, int vmid, uint64_t iova, uint8_t tg, uint64_t num_pages, int stage) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" stage=%d"
|
||||||
|
smmu_reset_exit(void) ""
|
||||||
|
|
||||||
# strongarm.c
|
# strongarm.c
|
||||||
strongarm_uart_update_parameters(const char *label, int speed, char parity, int data_bits, int stop_bits) "%s speed=%d parity=%c data=%d stop=%d"
|
strongarm_uart_update_parameters(const char *label, int speed, char parity, int data_bits, int stop_bits) "%s speed=%d parity=%c data=%d stop=%d"
|
||||||
|
@ -243,8 +243,13 @@ static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk,
|
|||||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, INTERLEAVE_4K, 1);
|
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, INTERLEAVE_4K, 1);
|
||||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY,
|
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY,
|
||||||
POISON_ON_ERR_CAP, 0);
|
POISON_ON_ERR_CAP, 0);
|
||||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 3_6_12_WAY, 0);
|
if (type == CXL2_TYPE3_DEVICE) {
|
||||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 16_WAY, 0);
|
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 3_6_12_WAY, 1);
|
||||||
|
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 16_WAY, 1);
|
||||||
|
} else {
|
||||||
|
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 3_6_12_WAY, 0);
|
||||||
|
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, 16_WAY, 0);
|
||||||
|
}
|
||||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, UIO, 0);
|
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, UIO, 0);
|
||||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY,
|
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY,
|
||||||
UIO_DECODER_COUNT, 0);
|
UIO_DECODER_COUNT, 0);
|
||||||
|
@ -352,10 +352,8 @@ static void device_reg_init_common(CXLDeviceState *cxl_dstate)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mailbox_reg_init_common(CXLDeviceState *cxl_dstate)
|
static void mailbox_reg_init_common(CXLDeviceState *cxl_dstate, int msi_n)
|
||||||
{
|
{
|
||||||
const uint8_t msi_n = 9;
|
|
||||||
|
|
||||||
/* 2048 payload size */
|
/* 2048 payload size */
|
||||||
ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP,
|
ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP,
|
||||||
PAYLOAD_SIZE, CXL_MAILBOX_PAYLOAD_SHIFT);
|
PAYLOAD_SIZE, CXL_MAILBOX_PAYLOAD_SHIFT);
|
||||||
@ -382,7 +380,7 @@ static void memdev_reg_init_common(CXLDeviceState *cxl_dstate)
|
|||||||
cxl_dstate->memdev_status = memdev_status_reg;
|
cxl_dstate->memdev_status = memdev_status_reg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cxl_device_register_init_t3(CXLType3Dev *ct3d)
|
void cxl_device_register_init_t3(CXLType3Dev *ct3d, int msi_n)
|
||||||
{
|
{
|
||||||
CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate;
|
CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate;
|
||||||
uint64_t *cap_h = cxl_dstate->caps_reg_state64;
|
uint64_t *cap_h = cxl_dstate->caps_reg_state64;
|
||||||
@ -398,7 +396,7 @@ void cxl_device_register_init_t3(CXLType3Dev *ct3d)
|
|||||||
device_reg_init_common(cxl_dstate);
|
device_reg_init_common(cxl_dstate);
|
||||||
|
|
||||||
cxl_device_cap_init(cxl_dstate, MAILBOX, 2, CXL_DEV_MAILBOX_VERSION);
|
cxl_device_cap_init(cxl_dstate, MAILBOX, 2, CXL_DEV_MAILBOX_VERSION);
|
||||||
mailbox_reg_init_common(cxl_dstate);
|
mailbox_reg_init_common(cxl_dstate, msi_n);
|
||||||
|
|
||||||
cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000,
|
cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000,
|
||||||
CXL_MEM_DEV_STATUS_VERSION);
|
CXL_MEM_DEV_STATUS_VERSION);
|
||||||
@ -408,7 +406,7 @@ void cxl_device_register_init_t3(CXLType3Dev *ct3d)
|
|||||||
CXL_MAILBOX_MAX_PAYLOAD_SIZE);
|
CXL_MAILBOX_MAX_PAYLOAD_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cxl_device_register_init_swcci(CSWMBCCIDev *sw)
|
void cxl_device_register_init_swcci(CSWMBCCIDev *sw, int msi_n)
|
||||||
{
|
{
|
||||||
CXLDeviceState *cxl_dstate = &sw->cxl_dstate;
|
CXLDeviceState *cxl_dstate = &sw->cxl_dstate;
|
||||||
uint64_t *cap_h = cxl_dstate->caps_reg_state64;
|
uint64_t *cap_h = cxl_dstate->caps_reg_state64;
|
||||||
@ -423,7 +421,7 @@ void cxl_device_register_init_swcci(CSWMBCCIDev *sw)
|
|||||||
device_reg_init_common(cxl_dstate);
|
device_reg_init_common(cxl_dstate);
|
||||||
|
|
||||||
cxl_device_cap_init(cxl_dstate, MAILBOX, 2, 1);
|
cxl_device_cap_init(cxl_dstate, MAILBOX, 2, 1);
|
||||||
mailbox_reg_init_common(cxl_dstate);
|
mailbox_reg_init_common(cxl_dstate, msi_n);
|
||||||
|
|
||||||
cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000, 1);
|
cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000, 1);
|
||||||
memdev_reg_init_common(cxl_dstate);
|
memdev_reg_init_common(cxl_dstate);
|
||||||
|
@ -17,10 +17,12 @@
|
|||||||
#include "hw/qdev-properties.h"
|
#include "hw/qdev-properties.h"
|
||||||
#include "hw/cxl/cxl.h"
|
#include "hw/cxl/cxl.h"
|
||||||
|
|
||||||
|
#define CXL_SWCCI_MSIX_MBOX 3
|
||||||
|
|
||||||
static void cswmbcci_reset(DeviceState *dev)
|
static void cswmbcci_reset(DeviceState *dev)
|
||||||
{
|
{
|
||||||
CSWMBCCIDev *cswmb = CXL_SWITCH_MAILBOX_CCI(dev);
|
CSWMBCCIDev *cswmb = CXL_SWITCH_MAILBOX_CCI(dev);
|
||||||
cxl_device_register_init_swcci(cswmb);
|
cxl_device_register_init_swcci(cswmb, CXL_SWCCI_MSIX_MBOX);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cswbcci_realize(PCIDevice *pci_dev, Error **errp)
|
static void cswbcci_realize(PCIDevice *pci_dev, Error **errp)
|
||||||
|
@ -1309,15 +1309,15 @@ static int amdvi_int_remap_msi(AMDVIState *iommu,
|
|||||||
ret = -AMDVI_IR_ERR;
|
ret = -AMDVI_IR_ERR;
|
||||||
break;
|
break;
|
||||||
case AMDVI_IOAPIC_INT_TYPE_NMI:
|
case AMDVI_IOAPIC_INT_TYPE_NMI:
|
||||||
pass = dte[3] & AMDVI_DEV_NMI_PASS_MASK;
|
pass = dte[2] & AMDVI_DEV_NMI_PASS_MASK;
|
||||||
trace_amdvi_ir_delivery_mode("nmi");
|
trace_amdvi_ir_delivery_mode("nmi");
|
||||||
break;
|
break;
|
||||||
case AMDVI_IOAPIC_INT_TYPE_INIT:
|
case AMDVI_IOAPIC_INT_TYPE_INIT:
|
||||||
pass = dte[3] & AMDVI_DEV_INT_PASS_MASK;
|
pass = dte[2] & AMDVI_DEV_INT_PASS_MASK;
|
||||||
trace_amdvi_ir_delivery_mode("init");
|
trace_amdvi_ir_delivery_mode("init");
|
||||||
break;
|
break;
|
||||||
case AMDVI_IOAPIC_INT_TYPE_EINT:
|
case AMDVI_IOAPIC_INT_TYPE_EINT:
|
||||||
pass = dte[3] & AMDVI_DEV_EINT_PASS_MASK;
|
pass = dte[2] & AMDVI_DEV_EINT_PASS_MASK;
|
||||||
trace_amdvi_ir_delivery_mode("eint");
|
trace_amdvi_ir_delivery_mode("eint");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -1593,9 +1593,9 @@ static void amdvi_pci_realize(PCIDevice *pdev, Error **errp)
|
|||||||
/* reset AMDVI specific capabilities, all r/o */
|
/* reset AMDVI specific capabilities, all r/o */
|
||||||
pci_set_long(pdev->config + s->capab_offset, AMDVI_CAPAB_FEATURES);
|
pci_set_long(pdev->config + s->capab_offset, AMDVI_CAPAB_FEATURES);
|
||||||
pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_BAR_LOW,
|
pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_BAR_LOW,
|
||||||
AMDVI_BASE_ADDR & ~(0xffff0000));
|
AMDVI_BASE_ADDR & MAKE_64BIT_MASK(14, 18));
|
||||||
pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_BAR_HIGH,
|
pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_BAR_HIGH,
|
||||||
(AMDVI_BASE_ADDR & ~(0xffff)) >> 16);
|
AMDVI_BASE_ADDR >> 32);
|
||||||
pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_RANGE,
|
pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_RANGE,
|
||||||
0xff000000);
|
0xff000000);
|
||||||
pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_MISC, 0);
|
pci_set_long(pdev->config + s->capab_offset + AMDVI_CAPAB_MISC, 0);
|
||||||
|
@ -187,7 +187,7 @@
|
|||||||
AMDVI_CAPAB_FLAG_HTTUNNEL | AMDVI_CAPAB_EFR_SUP)
|
AMDVI_CAPAB_FLAG_HTTUNNEL | AMDVI_CAPAB_EFR_SUP)
|
||||||
|
|
||||||
/* AMDVI default address */
|
/* AMDVI default address */
|
||||||
#define AMDVI_BASE_ADDR 0xfed80000
|
#define AMDVI_BASE_ADDR 0xfed80000ULL
|
||||||
|
|
||||||
/* page management constants */
|
/* page management constants */
|
||||||
#define AMDVI_PAGE_SHIFT 12
|
#define AMDVI_PAGE_SHIFT 12
|
||||||
|
@ -4697,10 +4697,11 @@ static void vtd_init(IntelIOMMUState *s)
|
|||||||
/* Should not reset address_spaces when reset because devices will still use
|
/* Should not reset address_spaces when reset because devices will still use
|
||||||
* the address space they got at first (won't ask the bus again).
|
* the address space they got at first (won't ask the bus again).
|
||||||
*/
|
*/
|
||||||
static void vtd_reset(DeviceState *dev)
|
static void vtd_reset_exit(Object *obj, ResetType type)
|
||||||
{
|
{
|
||||||
IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev);
|
IntelIOMMUState *s = INTEL_IOMMU_DEVICE(obj);
|
||||||
|
|
||||||
|
trace_vtd_reset_exit();
|
||||||
vtd_init(s);
|
vtd_init(s);
|
||||||
vtd_address_space_refresh_all(s);
|
vtd_address_space_refresh_all(s);
|
||||||
}
|
}
|
||||||
@ -4864,8 +4865,13 @@ static void vtd_class_init(ObjectClass *klass, void *data)
|
|||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
X86IOMMUClass *x86_class = X86_IOMMU_DEVICE_CLASS(klass);
|
X86IOMMUClass *x86_class = X86_IOMMU_DEVICE_CLASS(klass);
|
||||||
|
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||||
|
|
||||||
device_class_set_legacy_reset(dc, vtd_reset);
|
/*
|
||||||
|
* Use 'exit' reset phase to make sure all DMA requests
|
||||||
|
* have been quiesced during 'enter' or 'hold' phase
|
||||||
|
*/
|
||||||
|
rc->phases.exit = vtd_reset_exit;
|
||||||
dc->vmsd = &vtd_vmstate;
|
dc->vmsd = &vtd_vmstate;
|
||||||
device_class_set_props(dc, vtd_properties);
|
device_class_set_props(dc, vtd_properties);
|
||||||
dc->hotpluggable = false;
|
dc->hotpluggable = false;
|
||||||
|
@ -451,11 +451,44 @@ static HotplugHandler *microvm_get_hotplug_handler(MachineState *machine,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void microvm_machine_done(Notifier *notifier, void *data)
|
||||||
|
{
|
||||||
|
MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState,
|
||||||
|
machine_done);
|
||||||
|
X86MachineState *x86ms = X86_MACHINE(mms);
|
||||||
|
|
||||||
|
acpi_setup_microvm(mms);
|
||||||
|
dt_setup_microvm(mms);
|
||||||
|
fw_cfg_add_e820(x86ms->fw_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void microvm_powerdown_req(Notifier *notifier, void *data)
|
||||||
|
{
|
||||||
|
MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState,
|
||||||
|
powerdown_req);
|
||||||
|
X86MachineState *x86ms = X86_MACHINE(mms);
|
||||||
|
|
||||||
|
if (x86ms->acpi_dev) {
|
||||||
|
Object *obj = OBJECT(x86ms->acpi_dev);
|
||||||
|
AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj);
|
||||||
|
adevc->send_event(ACPI_DEVICE_IF(x86ms->acpi_dev),
|
||||||
|
ACPI_POWER_DOWN_STATUS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void microvm_machine_state_init(MachineState *machine)
|
static void microvm_machine_state_init(MachineState *machine)
|
||||||
{
|
{
|
||||||
MicrovmMachineState *mms = MICROVM_MACHINE(machine);
|
MicrovmMachineState *mms = MICROVM_MACHINE(machine);
|
||||||
X86MachineState *x86ms = X86_MACHINE(machine);
|
X86MachineState *x86ms = X86_MACHINE(machine);
|
||||||
|
|
||||||
|
/* State */
|
||||||
|
mms->kernel_cmdline_fixed = false;
|
||||||
|
|
||||||
|
mms->machine_done.notify = microvm_machine_done;
|
||||||
|
qemu_add_machine_init_done_notifier(&mms->machine_done);
|
||||||
|
mms->powerdown_req.notify = microvm_powerdown_req;
|
||||||
|
qemu_register_powerdown_notifier(&mms->powerdown_req);
|
||||||
|
|
||||||
microvm_memory_init(mms);
|
microvm_memory_init(mms);
|
||||||
|
|
||||||
x86_cpus_init(x86ms, CPU_VERSION_LATEST);
|
x86_cpus_init(x86ms, CPU_VERSION_LATEST);
|
||||||
@ -581,31 +614,6 @@ static void microvm_machine_set_auto_kernel_cmdline(Object *obj, bool value,
|
|||||||
mms->auto_kernel_cmdline = value;
|
mms->auto_kernel_cmdline = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void microvm_machine_done(Notifier *notifier, void *data)
|
|
||||||
{
|
|
||||||
MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState,
|
|
||||||
machine_done);
|
|
||||||
X86MachineState *x86ms = X86_MACHINE(mms);
|
|
||||||
|
|
||||||
acpi_setup_microvm(mms);
|
|
||||||
dt_setup_microvm(mms);
|
|
||||||
fw_cfg_add_e820(x86ms->fw_cfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void microvm_powerdown_req(Notifier *notifier, void *data)
|
|
||||||
{
|
|
||||||
MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState,
|
|
||||||
powerdown_req);
|
|
||||||
X86MachineState *x86ms = X86_MACHINE(mms);
|
|
||||||
|
|
||||||
if (x86ms->acpi_dev) {
|
|
||||||
Object *obj = OBJECT(x86ms->acpi_dev);
|
|
||||||
AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj);
|
|
||||||
adevc->send_event(ACPI_DEVICE_IF(x86ms->acpi_dev),
|
|
||||||
ACPI_POWER_DOWN_STATUS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void microvm_machine_initfn(Object *obj)
|
static void microvm_machine_initfn(Object *obj)
|
||||||
{
|
{
|
||||||
MicrovmMachineState *mms = MICROVM_MACHINE(obj);
|
MicrovmMachineState *mms = MICROVM_MACHINE(obj);
|
||||||
@ -617,14 +625,6 @@ static void microvm_machine_initfn(Object *obj)
|
|||||||
mms->isa_serial = true;
|
mms->isa_serial = true;
|
||||||
mms->option_roms = true;
|
mms->option_roms = true;
|
||||||
mms->auto_kernel_cmdline = true;
|
mms->auto_kernel_cmdline = true;
|
||||||
|
|
||||||
/* State */
|
|
||||||
mms->kernel_cmdline_fixed = false;
|
|
||||||
|
|
||||||
mms->machine_done.notify = microvm_machine_done;
|
|
||||||
qemu_add_machine_init_done_notifier(&mms->machine_done);
|
|
||||||
mms->powerdown_req.notify = microvm_powerdown_req;
|
|
||||||
qemu_register_powerdown_notifier(&mms->powerdown_req);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalProperty microvm_properties[] = {
|
GlobalProperty microvm_properties[] = {
|
||||||
|
@ -1241,6 +1241,9 @@ void pc_basic_device_init(struct PCMachineState *pcms,
|
|||||||
/* Super I/O */
|
/* Super I/O */
|
||||||
pc_superio_init(isa_bus, create_fdctrl, pcms->i8042_enabled,
|
pc_superio_init(isa_bus, create_fdctrl, pcms->i8042_enabled,
|
||||||
pcms->vmport != ON_OFF_AUTO_ON, &error_fatal);
|
pcms->vmport != ON_OFF_AUTO_ON, &error_fatal);
|
||||||
|
|
||||||
|
pcms->machine_done.notify = pc_machine_done;
|
||||||
|
qemu_add_machine_init_done_notifier(&pcms->machine_done);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus)
|
void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus)
|
||||||
@ -1714,9 +1717,6 @@ static void pc_machine_initfn(Object *obj)
|
|||||||
if (pcmc->pci_enabled) {
|
if (pcmc->pci_enabled) {
|
||||||
cxl_machine_init(obj, &pcms->cxl_devices_state);
|
cxl_machine_init(obj, &pcms->cxl_devices_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
pcms->machine_done.notify = pc_machine_done;
|
|
||||||
qemu_add_machine_init_done_notifier(&pcms->machine_done);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pc_machine_reset(MachineState *machine, ResetType type)
|
static void pc_machine_reset(MachineState *machine, ResetType type)
|
||||||
|
@ -68,6 +68,7 @@ vtd_frr_new(int index, uint64_t hi, uint64_t lo) "index %d high 0x%"PRIx64" low
|
|||||||
vtd_warn_invalid_qi_tail(uint16_t tail) "tail 0x%"PRIx16
|
vtd_warn_invalid_qi_tail(uint16_t tail) "tail 0x%"PRIx16
|
||||||
vtd_warn_ir_vector(uint16_t sid, int index, int vec, int target) "sid 0x%"PRIx16" index %d vec %d (should be: %d)"
|
vtd_warn_ir_vector(uint16_t sid, int index, int vec, int target) "sid 0x%"PRIx16" index %d vec %d (should be: %d)"
|
||||||
vtd_warn_ir_trigger(uint16_t sid, int index, int trig, int target) "sid 0x%"PRIx16" index %d trigger %d (should be: %d)"
|
vtd_warn_ir_trigger(uint16_t sid, int index, int trig, int target) "sid 0x%"PRIx16" index %d trigger %d (should be: %d)"
|
||||||
|
vtd_reset_exit(void) ""
|
||||||
|
|
||||||
# amd_iommu.c
|
# amd_iommu.c
|
||||||
amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32
|
amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32
|
||||||
|
@ -30,6 +30,14 @@
|
|||||||
#include "hw/cxl/cxl.h"
|
#include "hw/cxl/cxl.h"
|
||||||
#include "hw/pci/msix.h"
|
#include "hw/pci/msix.h"
|
||||||
|
|
||||||
|
/* type3 device private */
|
||||||
|
enum CXL_T3_MSIX_VECTOR {
|
||||||
|
CXL_T3_MSIX_PCIE_DOE_TABLE_ACCESS = 0,
|
||||||
|
CXL_T3_MSIX_EVENT_START = 2,
|
||||||
|
CXL_T3_MSIX_MBOX = CXL_T3_MSIX_EVENT_START + CXL_EVENT_TYPE_MAX,
|
||||||
|
CXL_T3_MSIX_VECTOR_NR
|
||||||
|
};
|
||||||
|
|
||||||
#define DWORD_BYTE 4
|
#define DWORD_BYTE 4
|
||||||
#define CXL_CAPACITY_MULTIPLIER (256 * MiB)
|
#define CXL_CAPACITY_MULTIPLIER (256 * MiB)
|
||||||
|
|
||||||
@ -843,7 +851,6 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
|
|||||||
ComponentRegisters *regs = &cxl_cstate->crb;
|
ComponentRegisters *regs = &cxl_cstate->crb;
|
||||||
MemoryRegion *mr = ®s->component_registers;
|
MemoryRegion *mr = ®s->component_registers;
|
||||||
uint8_t *pci_conf = pci_dev->config;
|
uint8_t *pci_conf = pci_dev->config;
|
||||||
unsigned short msix_num = 10;
|
|
||||||
int i, rc;
|
int i, rc;
|
||||||
uint16_t count;
|
uint16_t count;
|
||||||
|
|
||||||
@ -884,31 +891,32 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
|
|||||||
&ct3d->cxl_dstate.device_registers);
|
&ct3d->cxl_dstate.device_registers);
|
||||||
|
|
||||||
/* MSI(-X) Initialization */
|
/* MSI(-X) Initialization */
|
||||||
rc = msix_init_exclusive_bar(pci_dev, msix_num, 4, NULL);
|
rc = msix_init_exclusive_bar(pci_dev, CXL_T3_MSIX_VECTOR_NR, 4, errp);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
goto err_address_space_free;
|
goto err_free_special_ops;
|
||||||
}
|
}
|
||||||
for (i = 0; i < msix_num; i++) {
|
for (i = 0; i < CXL_T3_MSIX_VECTOR_NR; i++) {
|
||||||
msix_vector_use(pci_dev, i);
|
msix_vector_use(pci_dev, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DOE Initialization */
|
/* DOE Initialization */
|
||||||
pcie_doe_init(pci_dev, &ct3d->doe_cdat, 0x190, doe_cdat_prot, true, 0);
|
pcie_doe_init(pci_dev, &ct3d->doe_cdat, 0x190, doe_cdat_prot, true,
|
||||||
|
CXL_T3_MSIX_PCIE_DOE_TABLE_ACCESS);
|
||||||
|
|
||||||
cxl_cstate->cdat.build_cdat_table = ct3_build_cdat_table;
|
cxl_cstate->cdat.build_cdat_table = ct3_build_cdat_table;
|
||||||
cxl_cstate->cdat.free_cdat_table = ct3_free_cdat_table;
|
cxl_cstate->cdat.free_cdat_table = ct3_free_cdat_table;
|
||||||
cxl_cstate->cdat.private = ct3d;
|
cxl_cstate->cdat.private = ct3d;
|
||||||
if (!cxl_doe_cdat_init(cxl_cstate, errp)) {
|
if (!cxl_doe_cdat_init(cxl_cstate, errp)) {
|
||||||
goto err_free_special_ops;
|
goto err_msix_uninit;
|
||||||
}
|
}
|
||||||
|
|
||||||
pcie_cap_deverr_init(pci_dev);
|
pcie_cap_deverr_init(pci_dev);
|
||||||
/* Leave a bit of room for expansion */
|
/* Leave a bit of room for expansion */
|
||||||
rc = pcie_aer_init(pci_dev, PCI_ERR_VER, 0x200, PCI_ERR_SIZEOF, NULL);
|
rc = pcie_aer_init(pci_dev, PCI_ERR_VER, 0x200, PCI_ERR_SIZEOF, errp);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
goto err_release_cdat;
|
goto err_release_cdat;
|
||||||
}
|
}
|
||||||
cxl_event_init(&ct3d->cxl_dstate, 2);
|
cxl_event_init(&ct3d->cxl_dstate, CXL_T3_MSIX_EVENT_START);
|
||||||
|
|
||||||
/* Set default value for patrol scrub attributes */
|
/* Set default value for patrol scrub attributes */
|
||||||
ct3d->patrol_scrub_attrs.scrub_cycle_cap =
|
ct3d->patrol_scrub_attrs.scrub_cycle_cap =
|
||||||
@ -935,9 +943,10 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
|
|||||||
|
|
||||||
err_release_cdat:
|
err_release_cdat:
|
||||||
cxl_doe_cdat_release(cxl_cstate);
|
cxl_doe_cdat_release(cxl_cstate);
|
||||||
|
err_msix_uninit:
|
||||||
|
msix_uninit_exclusive_bar(pci_dev);
|
||||||
err_free_special_ops:
|
err_free_special_ops:
|
||||||
g_free(regs->special_ops);
|
g_free(regs->special_ops);
|
||||||
err_address_space_free:
|
|
||||||
if (ct3d->dc.host_dc) {
|
if (ct3d->dc.host_dc) {
|
||||||
cxl_destroy_dc_regions(ct3d);
|
cxl_destroy_dc_regions(ct3d);
|
||||||
address_space_destroy(&ct3d->dc.host_dc_as);
|
address_space_destroy(&ct3d->dc.host_dc_as);
|
||||||
@ -959,6 +968,7 @@ static void ct3_exit(PCIDevice *pci_dev)
|
|||||||
|
|
||||||
pcie_aer_exit(pci_dev);
|
pcie_aer_exit(pci_dev);
|
||||||
cxl_doe_cdat_release(cxl_cstate);
|
cxl_doe_cdat_release(cxl_cstate);
|
||||||
|
msix_uninit_exclusive_bar(pci_dev);
|
||||||
g_free(regs->special_ops);
|
g_free(regs->special_ops);
|
||||||
if (ct3d->dc.host_dc) {
|
if (ct3d->dc.host_dc) {
|
||||||
cxl_destroy_dc_regions(ct3d);
|
cxl_destroy_dc_regions(ct3d);
|
||||||
@ -1090,10 +1100,17 @@ static bool cxl_type3_dpa(CXLType3Dev *ct3d, hwaddr host_addr, uint64_t *dpa)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
*dpa = dpa_base +
|
if (iw < 8) {
|
||||||
((MAKE_64BIT_MASK(0, 8 + ig) & hpa_offset) |
|
*dpa = dpa_base +
|
||||||
((MAKE_64BIT_MASK(8 + ig + iw, 64 - 8 - ig - iw) & hpa_offset)
|
((MAKE_64BIT_MASK(0, 8 + ig) & hpa_offset) |
|
||||||
>> iw));
|
((MAKE_64BIT_MASK(8 + ig + iw, 64 - 8 - ig - iw) & hpa_offset)
|
||||||
|
>> iw));
|
||||||
|
} else {
|
||||||
|
*dpa = dpa_base +
|
||||||
|
((MAKE_64BIT_MASK(0, 8 + ig) & hpa_offset) |
|
||||||
|
((((MAKE_64BIT_MASK(ig + iw, 64 - ig - iw) & hpa_offset)
|
||||||
|
>> (ig + iw)) / 3) << (ig + 8)));
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1202,7 +1219,7 @@ static void ct3d_reset(DeviceState *dev)
|
|||||||
|
|
||||||
pcie_cap_fill_link_ep_usp(PCI_DEVICE(dev), ct3d->width, ct3d->speed);
|
pcie_cap_fill_link_ep_usp(PCI_DEVICE(dev), ct3d->width, ct3d->speed);
|
||||||
cxl_component_register_init_common(reg_state, write_msk, CXL2_TYPE3_DEVICE);
|
cxl_component_register_init_common(reg_state, write_msk, CXL2_TYPE3_DEVICE);
|
||||||
cxl_device_register_init_t3(ct3d);
|
cxl_device_register_init_t3(ct3d, CXL_T3_MSIX_MBOX);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bring up an endpoint to target with MCTP over VDM.
|
* Bring up an endpoint to target with MCTP over VDM.
|
||||||
|
10
hw/net/igb.c
10
hw/net/igb.c
@ -446,9 +446,13 @@ static void igb_pci_realize(PCIDevice *pci_dev, Error **errp)
|
|||||||
|
|
||||||
pcie_ari_init(pci_dev, 0x150);
|
pcie_ari_init(pci_dev, 0x150);
|
||||||
|
|
||||||
pcie_sriov_pf_init(pci_dev, IGB_CAP_SRIOV_OFFSET, TYPE_IGBVF,
|
if (!pcie_sriov_pf_init(pci_dev, IGB_CAP_SRIOV_OFFSET, TYPE_IGBVF,
|
||||||
IGB_82576_VF_DEV_ID, IGB_MAX_VF_FUNCTIONS, IGB_MAX_VF_FUNCTIONS,
|
IGB_82576_VF_DEV_ID, IGB_MAX_VF_FUNCTIONS,
|
||||||
IGB_VF_OFFSET, IGB_VF_STRIDE);
|
IGB_MAX_VF_FUNCTIONS, IGB_VF_OFFSET, IGB_VF_STRIDE,
|
||||||
|
errp)) {
|
||||||
|
igb_cleanup_msix(s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pcie_sriov_pf_init_vf_bar(pci_dev, IGBVF_MMIO_BAR_IDX,
|
pcie_sriov_pf_init_vf_bar(pci_dev, IGBVF_MMIO_BAR_IDX,
|
||||||
PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH,
|
PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH,
|
||||||
|
@ -1352,18 +1352,25 @@ exit:
|
|||||||
|
|
||||||
static bool virtio_net_load_ebpf(VirtIONet *n, Error **errp)
|
static bool virtio_net_load_ebpf(VirtIONet *n, Error **errp)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
if (!virtio_net_attach_ebpf_to_backend(n->nic, -1)) {
|
||||||
|
return true;
|
||||||
if (virtio_net_attach_ebpf_to_backend(n->nic, -1)) {
|
|
||||||
trace_virtio_net_rss_load(n, n->nr_ebpf_rss_fds, n->ebpf_rss_fds);
|
|
||||||
if (n->ebpf_rss_fds) {
|
|
||||||
ret = virtio_net_load_ebpf_fds(n, errp);
|
|
||||||
} else {
|
|
||||||
ret = ebpf_rss_load(&n->ebpf_rss, errp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
trace_virtio_net_rss_load(n, n->nr_ebpf_rss_fds, n->ebpf_rss_fds);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If user explicitly gave QEMU RSS FDs to use, then
|
||||||
|
* failing to use them must be considered a fatal
|
||||||
|
* error. If no RSS FDs were provided, QEMU is trying
|
||||||
|
* eBPF on a "best effort" basis only, so report a
|
||||||
|
* warning and allow fallback to software RSS.
|
||||||
|
*/
|
||||||
|
if (n->ebpf_rss_fds) {
|
||||||
|
return virtio_net_load_ebpf_fds(n, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
ebpf_rss_load(&n->ebpf_rss, &error_warn);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_net_unload_ebpf(VirtIONet *n)
|
static void virtio_net_unload_ebpf(VirtIONet *n)
|
||||||
@ -3913,23 +3920,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
|
|||||||
net_rx_pkt_init(&n->rx_pkt);
|
net_rx_pkt_init(&n->rx_pkt);
|
||||||
|
|
||||||
if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) {
|
if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) {
|
||||||
Error *err = NULL;
|
virtio_net_load_ebpf(n, errp);
|
||||||
if (!virtio_net_load_ebpf(n, &err)) {
|
|
||||||
/*
|
|
||||||
* If user explicitly gave QEMU RSS FDs to use, then
|
|
||||||
* failing to use them must be considered a fatal
|
|
||||||
* error. If no RSS FDs were provided, QEMU is trying
|
|
||||||
* eBPF on a "best effort" basis only, so report a
|
|
||||||
* warning and allow fallback to software RSS.
|
|
||||||
*/
|
|
||||||
if (n->ebpf_rss_fds) {
|
|
||||||
error_propagate(errp, err);
|
|
||||||
} else {
|
|
||||||
warn_report("unable to load eBPF RSS: %s",
|
|
||||||
error_get_pretty(err));
|
|
||||||
error_free(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8481,7 +8481,8 @@ out:
|
|||||||
return pow2ceil(bar_size);
|
return pow2ceil(bar_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset)
|
static bool nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
uint16_t vf_dev_id = n->params.use_intel_id ?
|
uint16_t vf_dev_id = n->params.use_intel_id ?
|
||||||
PCI_DEVICE_ID_INTEL_NVME : PCI_DEVICE_ID_REDHAT_NVME;
|
PCI_DEVICE_ID_INTEL_NVME : PCI_DEVICE_ID_REDHAT_NVME;
|
||||||
@ -8490,12 +8491,16 @@ static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset)
|
|||||||
le16_to_cpu(cap->vifrsm),
|
le16_to_cpu(cap->vifrsm),
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
|
|
||||||
pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id,
|
if (!pcie_sriov_pf_init(pci_dev, offset, "nvme", vf_dev_id,
|
||||||
n->params.sriov_max_vfs, n->params.sriov_max_vfs,
|
n->params.sriov_max_vfs, n->params.sriov_max_vfs,
|
||||||
NVME_VF_OFFSET, NVME_VF_STRIDE);
|
NVME_VF_OFFSET, NVME_VF_STRIDE, errp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
pcie_sriov_pf_init_vf_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
|
pcie_sriov_pf_init_vf_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
|
||||||
PCI_BASE_ADDRESS_MEM_TYPE_64, bar_size);
|
PCI_BASE_ADDRESS_MEM_TYPE_64, bar_size);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset)
|
static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset)
|
||||||
@ -8620,6 +8625,11 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs &&
|
||||||
|
!nvme_init_sriov(n, pci_dev, 0x120, errp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize);
|
nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize);
|
||||||
|
|
||||||
pcie_cap_deverr_init(pci_dev);
|
pcie_cap_deverr_init(pci_dev);
|
||||||
@ -8649,10 +8659,6 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
|
|||||||
nvme_init_pmr(n, pci_dev);
|
nvme_init_pmr(n, pci_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pci_is_vf(pci_dev) && n->params.sriov_max_vfs) {
|
|
||||||
nvme_init_sriov(n, pci_dev, 0x120);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
#include "hw/pci/msi.h"
|
#include "hw/pci/msi.h"
|
||||||
#include "hw/pci/msix.h"
|
#include "hw/pci/msix.h"
|
||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
@ -260,6 +261,14 @@ static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr,
|
|||||||
static void msix_pba_mmio_write(void *opaque, hwaddr addr,
|
static void msix_pba_mmio_write(void *opaque, hwaddr addr,
|
||||||
uint64_t val, unsigned size)
|
uint64_t val, unsigned size)
|
||||||
{
|
{
|
||||||
|
PCIDevice *dev = opaque;
|
||||||
|
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"PCI [%s:%02x:%02x.%x] attempt to write to MSI-X "
|
||||||
|
"PBA at 0x%" FMT_PCIBUS ", ignoring.\n",
|
||||||
|
pci_root_bus_path(dev), pci_dev_bus_num(dev),
|
||||||
|
PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
|
||||||
|
addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const MemoryRegionOps msix_pba_mmio_ops = {
|
static const MemoryRegionOps msix_pba_mmio_ops = {
|
||||||
|
22
hw/pci/pci.c
22
hw/pci/pci.c
@ -803,10 +803,17 @@ static bool migrate_is_not_pcie(void *opaque, int version_id)
|
|||||||
return !pci_is_express((PCIDevice *)opaque);
|
return !pci_is_express((PCIDevice *)opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pci_post_load(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
pcie_sriov_pf_post_load(opaque);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const VMStateDescription vmstate_pci_device = {
|
const VMStateDescription vmstate_pci_device = {
|
||||||
.name = "PCIDevice",
|
.name = "PCIDevice",
|
||||||
.version_id = 2,
|
.version_id = 2,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
|
.post_load = pci_post_load,
|
||||||
.fields = (const VMStateField[]) {
|
.fields = (const VMStateField[]) {
|
||||||
VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice),
|
VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice),
|
||||||
VMSTATE_BUFFER_UNSAFE_INFO_TEST(config, PCIDevice,
|
VMSTATE_BUFFER_UNSAFE_INFO_TEST(config, PCIDevice,
|
||||||
@ -1391,6 +1398,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
|
|||||||
assert(hdr_type != PCI_HEADER_TYPE_BRIDGE || region_num < 2);
|
assert(hdr_type != PCI_HEADER_TYPE_BRIDGE || region_num < 2);
|
||||||
|
|
||||||
r = &pci_dev->io_regions[region_num];
|
r = &pci_dev->io_regions[region_num];
|
||||||
|
assert(!r->size);
|
||||||
r->addr = PCI_BAR_UNMAPPED;
|
r->addr = PCI_BAR_UNMAPPED;
|
||||||
r->size = size;
|
r->size = size;
|
||||||
r->type = type;
|
r->type = type;
|
||||||
@ -2963,7 +2971,17 @@ MSIMessage pci_get_msi_message(PCIDevice *dev, int vector)
|
|||||||
|
|
||||||
void pci_set_power(PCIDevice *d, bool state)
|
void pci_set_power(PCIDevice *d, bool state)
|
||||||
{
|
{
|
||||||
pci_set_enabled(d, state);
|
/*
|
||||||
|
* Don't change the enabled state of VFs when powering on/off the device.
|
||||||
|
*
|
||||||
|
* When powering on, VFs must not be enabled immediately but they must
|
||||||
|
* wait until the guest configures SR-IOV.
|
||||||
|
* When powering off, their corresponding PFs will be reset and disable
|
||||||
|
* VFs.
|
||||||
|
*/
|
||||||
|
if (!pci_is_vf(d)) {
|
||||||
|
pci_set_enabled(d, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pci_set_enabled(PCIDevice *d, bool state)
|
void pci_set_enabled(PCIDevice *d, bool state)
|
||||||
@ -2977,7 +2995,7 @@ void pci_set_enabled(PCIDevice *d, bool state)
|
|||||||
memory_region_set_enabled(&d->bus_master_enable_region,
|
memory_region_set_enabled(&d->bus_master_enable_region,
|
||||||
(pci_get_word(d->config + PCI_COMMAND)
|
(pci_get_word(d->config + PCI_COMMAND)
|
||||||
& PCI_COMMAND_MASTER) && d->enabled);
|
& PCI_COMMAND_MASTER) && d->enabled);
|
||||||
if (!d->enabled) {
|
if (qdev_is_realized(&d->qdev)) {
|
||||||
pci_device_reset(d);
|
pci_device_reset(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,23 +20,37 @@
|
|||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
static PCIDevice *register_vf(PCIDevice *pf, int devfn,
|
static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs)
|
||||||
const char *name, uint16_t vf_num);
|
{
|
||||||
static void unregister_vfs(PCIDevice *dev);
|
for (uint16_t i = 0; i < total_vfs; i++) {
|
||||||
|
PCIDevice *vf = dev->exp.sriov_pf.vf[i];
|
||||||
|
object_unparent(OBJECT(vf));
|
||||||
|
object_unref(OBJECT(vf));
|
||||||
|
}
|
||||||
|
g_free(dev->exp.sriov_pf.vf);
|
||||||
|
dev->exp.sriov_pf.vf = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
|
bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
|
||||||
const char *vfname, uint16_t vf_dev_id,
|
const char *vfname, uint16_t vf_dev_id,
|
||||||
uint16_t init_vfs, uint16_t total_vfs,
|
uint16_t init_vfs, uint16_t total_vfs,
|
||||||
uint16_t vf_offset, uint16_t vf_stride)
|
uint16_t vf_offset, uint16_t vf_stride,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
BusState *bus = qdev_get_parent_bus(&dev->qdev);
|
||||||
|
int32_t devfn = dev->devfn + vf_offset;
|
||||||
uint8_t *cfg = dev->config + offset;
|
uint8_t *cfg = dev->config + offset;
|
||||||
uint8_t *wmask;
|
uint8_t *wmask;
|
||||||
|
|
||||||
|
if (total_vfs &&
|
||||||
|
(uint32_t)devfn + (uint32_t)(total_vfs - 1) * vf_stride >= PCI_DEVFN_MAX) {
|
||||||
|
error_setg(errp, "VF addr overflows");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1,
|
pcie_add_capability(dev, PCI_EXT_CAP_ID_SRIOV, 1,
|
||||||
offset, PCI_EXT_CAP_SRIOV_SIZEOF);
|
offset, PCI_EXT_CAP_SRIOV_SIZEOF);
|
||||||
dev->exp.sriov_cap = offset;
|
dev->exp.sriov_cap = offset;
|
||||||
dev->exp.sriov_pf.num_vfs = 0;
|
|
||||||
dev->exp.sriov_pf.vfname = g_strdup(vfname);
|
|
||||||
dev->exp.sriov_pf.vf = NULL;
|
dev->exp.sriov_pf.vf = NULL;
|
||||||
|
|
||||||
pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset);
|
pci_set_word(cfg + PCI_SRIOV_VF_OFFSET, vf_offset);
|
||||||
@ -69,13 +83,37 @@ void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
|
|||||||
pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, 0x553);
|
pci_set_word(wmask + PCI_SRIOV_SYS_PGSIZE, 0x553);
|
||||||
|
|
||||||
qdev_prop_set_bit(&dev->qdev, "multifunction", true);
|
qdev_prop_set_bit(&dev->qdev, "multifunction", true);
|
||||||
|
|
||||||
|
dev->exp.sriov_pf.vf = g_new(PCIDevice *, total_vfs);
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < total_vfs; i++) {
|
||||||
|
PCIDevice *vf = pci_new(devfn, vfname);
|
||||||
|
vf->exp.sriov_vf.pf = dev;
|
||||||
|
vf->exp.sriov_vf.vf_number = i;
|
||||||
|
|
||||||
|
if (!qdev_realize(&vf->qdev, bus, errp)) {
|
||||||
|
object_unparent(OBJECT(vf));
|
||||||
|
object_unref(vf);
|
||||||
|
unparent_vfs(dev, i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set vid/did according to sr/iov spec - they are not used */
|
||||||
|
pci_config_set_vendor_id(vf->config, 0xffff);
|
||||||
|
pci_config_set_device_id(vf->config, 0xffff);
|
||||||
|
|
||||||
|
dev->exp.sriov_pf.vf[i] = vf;
|
||||||
|
devfn += vf_stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcie_sriov_pf_exit(PCIDevice *dev)
|
void pcie_sriov_pf_exit(PCIDevice *dev)
|
||||||
{
|
{
|
||||||
unregister_vfs(dev);
|
uint8_t *cfg = dev->config + dev->exp.sriov_cap;
|
||||||
g_free((char *)dev->exp.sriov_pf.vfname);
|
|
||||||
dev->exp.sriov_pf.vfname = NULL;
|
unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF));
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num,
|
void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num,
|
||||||
@ -141,80 +179,36 @@ void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PCIDevice *register_vf(PCIDevice *pf, int devfn, const char *name,
|
|
||||||
uint16_t vf_num)
|
|
||||||
{
|
|
||||||
PCIDevice *dev = pci_new(devfn, name);
|
|
||||||
dev->exp.sriov_vf.pf = pf;
|
|
||||||
dev->exp.sriov_vf.vf_number = vf_num;
|
|
||||||
PCIBus *bus = pci_get_bus(pf);
|
|
||||||
Error *local_err = NULL;
|
|
||||||
|
|
||||||
qdev_realize(&dev->qdev, &bus->qbus, &local_err);
|
|
||||||
if (local_err) {
|
|
||||||
error_report_err(local_err);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set vid/did according to sr/iov spec - they are not used */
|
|
||||||
pci_config_set_vendor_id(dev->config, 0xffff);
|
|
||||||
pci_config_set_device_id(dev->config, 0xffff);
|
|
||||||
|
|
||||||
return dev;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void register_vfs(PCIDevice *dev)
|
static void register_vfs(PCIDevice *dev)
|
||||||
{
|
{
|
||||||
uint16_t num_vfs;
|
uint16_t num_vfs;
|
||||||
uint16_t i;
|
uint16_t i;
|
||||||
uint16_t sriov_cap = dev->exp.sriov_cap;
|
uint16_t sriov_cap = dev->exp.sriov_cap;
|
||||||
uint16_t vf_offset =
|
|
||||||
pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_OFFSET);
|
|
||||||
uint16_t vf_stride =
|
|
||||||
pci_get_word(dev->config + sriov_cap + PCI_SRIOV_VF_STRIDE);
|
|
||||||
int32_t devfn = dev->devfn + vf_offset;
|
|
||||||
|
|
||||||
assert(sriov_cap > 0);
|
assert(sriov_cap > 0);
|
||||||
num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF);
|
num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF);
|
||||||
if (num_vfs > pci_get_word(dev->config + sriov_cap + PCI_SRIOV_TOTAL_VF)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->exp.sriov_pf.vf = g_new(PCIDevice *, num_vfs);
|
|
||||||
|
|
||||||
trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn),
|
trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn),
|
||||||
PCI_FUNC(dev->devfn), num_vfs);
|
PCI_FUNC(dev->devfn), num_vfs);
|
||||||
for (i = 0; i < num_vfs; i++) {
|
for (i = 0; i < num_vfs; i++) {
|
||||||
dev->exp.sriov_pf.vf[i] = register_vf(dev, devfn,
|
pci_set_enabled(dev->exp.sriov_pf.vf[i], true);
|
||||||
dev->exp.sriov_pf.vfname, i);
|
|
||||||
if (!dev->exp.sriov_pf.vf[i]) {
|
|
||||||
num_vfs = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
devfn += vf_stride;
|
|
||||||
}
|
}
|
||||||
dev->exp.sriov_pf.num_vfs = num_vfs;
|
|
||||||
|
pci_set_word(dev->wmask + sriov_cap + PCI_SRIOV_NUM_VF, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unregister_vfs(PCIDevice *dev)
|
static void unregister_vfs(PCIDevice *dev)
|
||||||
{
|
{
|
||||||
uint16_t num_vfs = dev->exp.sriov_pf.num_vfs;
|
uint8_t *cfg = dev->config + dev->exp.sriov_cap;
|
||||||
uint16_t i;
|
uint16_t i;
|
||||||
|
|
||||||
trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn),
|
trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn),
|
||||||
PCI_FUNC(dev->devfn), num_vfs);
|
PCI_FUNC(dev->devfn));
|
||||||
for (i = 0; i < num_vfs; i++) {
|
for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) {
|
||||||
Error *err = NULL;
|
pci_set_enabled(dev->exp.sriov_pf.vf[i], false);
|
||||||
PCIDevice *vf = dev->exp.sriov_pf.vf[i];
|
|
||||||
if (!object_property_set_bool(OBJECT(vf), "realized", false, &err)) {
|
|
||||||
error_reportf_err(err, "Failed to unplug: ");
|
|
||||||
}
|
|
||||||
object_unparent(OBJECT(vf));
|
|
||||||
object_unref(OBJECT(vf));
|
|
||||||
}
|
}
|
||||||
g_free(dev->exp.sriov_pf.vf);
|
|
||||||
dev->exp.sriov_pf.vf = NULL;
|
pci_set_word(dev->wmask + dev->exp.sriov_cap + PCI_SRIOV_NUM_VF, 0xffff);
|
||||||
dev->exp.sriov_pf.num_vfs = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcie_sriov_config_write(PCIDevice *dev, uint32_t address,
|
void pcie_sriov_config_write(PCIDevice *dev, uint32_t address,
|
||||||
@ -235,15 +229,29 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address,
|
|||||||
PCI_FUNC(dev->devfn), off, val, len);
|
PCI_FUNC(dev->devfn), off, val, len);
|
||||||
|
|
||||||
if (range_covers_byte(off, len, PCI_SRIOV_CTRL)) {
|
if (range_covers_byte(off, len, PCI_SRIOV_CTRL)) {
|
||||||
if (dev->exp.sriov_pf.num_vfs) {
|
if (val & PCI_SRIOV_CTRL_VFE) {
|
||||||
if (!(val & PCI_SRIOV_CTRL_VFE)) {
|
register_vfs(dev);
|
||||||
unregister_vfs(dev);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (val & PCI_SRIOV_CTRL_VFE) {
|
unregister_vfs(dev);
|
||||||
register_vfs(dev);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else if (range_covers_byte(off, len, PCI_SRIOV_NUM_VF)) {
|
||||||
|
uint8_t *cfg = dev->config + sriov_cap;
|
||||||
|
uint8_t *wmask = dev->wmask + sriov_cap;
|
||||||
|
uint16_t num_vfs = pci_get_word(cfg + PCI_SRIOV_NUM_VF);
|
||||||
|
uint16_t wmask_val = PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI;
|
||||||
|
|
||||||
|
if (num_vfs <= pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)) {
|
||||||
|
wmask_val |= PCI_SRIOV_CTRL_VFE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_set_word(wmask + PCI_SRIOV_CTRL, wmask_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pcie_sriov_pf_post_load(PCIDevice *dev)
|
||||||
|
{
|
||||||
|
if (dev->exp.sriov_cap) {
|
||||||
|
register_vfs(dev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,6 +268,8 @@ void pcie_sriov_pf_reset(PCIDevice *dev)
|
|||||||
unregister_vfs(dev);
|
unregister_vfs(dev);
|
||||||
|
|
||||||
pci_set_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF, 0);
|
pci_set_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF, 0);
|
||||||
|
pci_set_word(dev->wmask + sriov_cap + PCI_SRIOV_CTRL,
|
||||||
|
PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default is to use 4K pages, software can modify it
|
* Default is to use 4K pages, software can modify it
|
||||||
@ -306,7 +316,7 @@ PCIDevice *pcie_sriov_get_pf(PCIDevice *dev)
|
|||||||
PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n)
|
PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n)
|
||||||
{
|
{
|
||||||
assert(!pci_is_vf(dev));
|
assert(!pci_is_vf(dev));
|
||||||
if (n < dev->exp.sriov_pf.num_vfs) {
|
if (n < pcie_sriov_num_vfs(dev)) {
|
||||||
return dev->exp.sriov_pf.vf[n];
|
return dev->exp.sriov_pf.vf[n];
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -314,5 +324,10 @@ PCIDevice *pcie_sriov_get_vf_at_index(PCIDevice *dev, int n)
|
|||||||
|
|
||||||
uint16_t pcie_sriov_num_vfs(PCIDevice *dev)
|
uint16_t pcie_sriov_num_vfs(PCIDevice *dev)
|
||||||
{
|
{
|
||||||
return dev->exp.sriov_pf.num_vfs;
|
uint16_t sriov_cap = dev->exp.sriov_cap;
|
||||||
|
uint8_t *cfg = dev->config + sriov_cap;
|
||||||
|
|
||||||
|
return sriov_cap &&
|
||||||
|
(pci_get_word(cfg + PCI_SRIOV_CTRL) & PCI_SRIOV_CTRL_VFE) ?
|
||||||
|
pci_get_word(cfg + PCI_SRIOV_NUM_VF) : 0;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ msix_write_config(char *name, bool enabled, bool masked) "dev %s enabled %d mask
|
|||||||
|
|
||||||
# hw/pci/pcie_sriov.c
|
# hw/pci/pcie_sriov.c
|
||||||
sriov_register_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: creating %d vf devs"
|
sriov_register_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: creating %d vf devs"
|
||||||
sriov_unregister_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: Unregistering %d vf devs"
|
sriov_unregister_vfs(const char *name, int slot, int function) "%s %02x:%x: Unregistering vf devs"
|
||||||
sriov_config_write(const char *name, int slot, int fun, uint32_t offset, uint32_t val, uint32_t len) "%s %02x:%x: sriov offset 0x%x val 0x%x len %d"
|
sriov_config_write(const char *name, int slot, int fun, uint32_t offset, uint32_t val, uint32_t len) "%s %02x:%x: sriov offset 0x%x val 0x%x len %d"
|
||||||
|
|
||||||
# pcie.c
|
# pcie.c
|
||||||
|
@ -1283,8 +1283,7 @@ static void spapr_dt_pci_device_cb(PCIBus *bus, PCIDevice *pdev,
|
|||||||
PciWalkFdt *p = opaque;
|
PciWalkFdt *p = opaque;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (p->err) {
|
if (p->err || !pdev->enabled) {
|
||||||
/* Something's already broken, don't keep going */
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1550,7 +1549,9 @@ static void spapr_pci_pre_plug(HotplugHandler *plug_handler,
|
|||||||
* hotplug, we do not allow functions to be hotplugged to a
|
* hotplug, we do not allow functions to be hotplugged to a
|
||||||
* slot that already has function 0 present
|
* slot that already has function 0 present
|
||||||
*/
|
*/
|
||||||
if (plugged_dev->hotplugged && bus->devices[PCI_DEVFN(slotnr, 0)] &&
|
if (plugged_dev->hotplugged &&
|
||||||
|
!pci_is_vf(pdev) &&
|
||||||
|
bus->devices[PCI_DEVFN(slotnr, 0)] &&
|
||||||
PCI_FUNC(pdev->devfn) != 0) {
|
PCI_FUNC(pdev->devfn) != 0) {
|
||||||
error_setg(errp, "PCI: slot %d function 0 already occupied by %s,"
|
error_setg(errp, "PCI: slot %d function 0 already occupied by %s,"
|
||||||
" additional functions can no longer be exposed to guest.",
|
" additional functions can no longer be exposed to guest.",
|
||||||
@ -1572,6 +1573,14 @@ static void spapr_pci_plug(HotplugHandler *plug_handler,
|
|||||||
SpaprDrc *drc = drc_from_dev(phb, pdev);
|
SpaprDrc *drc = drc_from_dev(phb, pdev);
|
||||||
uint32_t slotnr = PCI_SLOT(pdev->devfn);
|
uint32_t slotnr = PCI_SLOT(pdev->devfn);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If DR or the PCI device is disabled we don't need to do anything
|
||||||
|
* in the case of hotplug or coldplug callbacks.
|
||||||
|
*/
|
||||||
|
if (!pdev->enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
g_assert(drc);
|
g_assert(drc);
|
||||||
|
|
||||||
if (IS_PCI_BRIDGE(plugged_dev)) {
|
if (IS_PCI_BRIDGE(plugged_dev)) {
|
||||||
@ -1647,6 +1656,11 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler,
|
|||||||
SpaprDrc *drc = drc_from_dev(phb, pdev);
|
SpaprDrc *drc = drc_from_dev(phb, pdev);
|
||||||
|
|
||||||
g_assert(drc);
|
g_assert(drc);
|
||||||
|
|
||||||
|
if (!drc->dev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
g_assert(drc->dev == plugged_dev);
|
g_assert(drc->dev == plugged_dev);
|
||||||
|
|
||||||
if (!spapr_drc_unplug_requested(drc)) {
|
if (!spapr_drc_unplug_requested(drc)) {
|
||||||
|
@ -971,14 +971,7 @@ static void s390_pcihost_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|||||||
"this device");
|
"this device");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
|
||||||
PCIDevice *pdev = PCI_DEVICE(dev);
|
|
||||||
|
|
||||||
if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
|
|
||||||
error_setg(errp, "multifunction not supported in s390");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
|
|
||||||
S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev);
|
S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev);
|
||||||
|
|
||||||
if (!s390_pci_alloc_idx(s, pbdev)) {
|
if (!s390_pci_alloc_idx(s, pbdev)) {
|
||||||
@ -1069,6 +1062,18 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|||||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
||||||
pdev = PCI_DEVICE(dev);
|
pdev = PCI_DEVICE(dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Multifunction is not supported due to the lack of CLP. However,
|
||||||
|
* do not check for multifunction capability for SR-IOV devices because
|
||||||
|
* SR-IOV devices automatically add the multifunction capability whether
|
||||||
|
* the user intends to use the functions other than the PF.
|
||||||
|
*/
|
||||||
|
if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION &&
|
||||||
|
!pdev->exp.sriov_cap) {
|
||||||
|
error_setg(errp, "multifunction not supported in s390");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!dev->id) {
|
if (!dev->id) {
|
||||||
/* In the case the PCI device does not define an id */
|
/* In the case the PCI device does not define an id */
|
||||||
/* we generate one based on the PCI address */
|
/* we generate one based on the PCI address */
|
||||||
@ -1080,6 +1085,16 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|||||||
|
|
||||||
pbdev = s390_pci_find_dev_by_target(s, dev->id);
|
pbdev = s390_pci_find_dev_by_target(s, dev->id);
|
||||||
if (!pbdev) {
|
if (!pbdev) {
|
||||||
|
/*
|
||||||
|
* VFs are automatically created by PF, and creating zpci for them
|
||||||
|
* will result in unexpected usage of fids. Currently QEMU does not
|
||||||
|
* support multifunction for s390x so we don't need zpci for VFs
|
||||||
|
* anyway.
|
||||||
|
*/
|
||||||
|
if (pci_is_vf(pdev)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pbdev = s390_pci_device_new(s, dev->id, errp);
|
pbdev = s390_pci_device_new(s, dev->id, errp);
|
||||||
if (!pbdev) {
|
if (!pbdev) {
|
||||||
return;
|
return;
|
||||||
@ -1167,7 +1182,10 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|||||||
int32_t devfn;
|
int32_t devfn;
|
||||||
|
|
||||||
pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev));
|
pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev));
|
||||||
g_assert(pbdev);
|
if (!pbdev) {
|
||||||
|
g_assert(pci_is_vf(pci_dev));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED,
|
s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED,
|
||||||
pbdev->fh, pbdev->fid);
|
pbdev->fh, pbdev->fid);
|
||||||
@ -1206,7 +1224,11 @@ static void s390_pcihost_unplug_request(HotplugHandler *hotplug_dev,
|
|||||||
* we've checked the PCI device already (to prevent endless recursion).
|
* we've checked the PCI device already (to prevent endless recursion).
|
||||||
*/
|
*/
|
||||||
pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev));
|
pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev));
|
||||||
g_assert(pbdev);
|
if (!pbdev) {
|
||||||
|
g_assert(pci_is_vf(PCI_DEVICE(dev)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pbdev->pci_unplug_request_processed = true;
|
pbdev->pci_unplug_request_processed = true;
|
||||||
qdev_unplug(DEVICE(pbdev), errp);
|
qdev_unplug(DEVICE(pbdev), errp);
|
||||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
|
||||||
|
@ -1404,6 +1404,7 @@ void vfio_reset_handler(void *opaque)
|
|||||||
{
|
{
|
||||||
VFIODevice *vbasedev;
|
VFIODevice *vbasedev;
|
||||||
|
|
||||||
|
trace_vfio_reset_handler();
|
||||||
QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) {
|
QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) {
|
||||||
if (vbasedev->dev->realized) {
|
if (vbasedev->dev->realized) {
|
||||||
vbasedev->ops->vfio_compute_needs_reset(vbasedev);
|
vbasedev->ops->vfio_compute_needs_reset(vbasedev);
|
||||||
|
@ -120,6 +120,7 @@ vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype
|
|||||||
vfio_legacy_dma_unmap_overflow_workaround(void) ""
|
vfio_legacy_dma_unmap_overflow_workaround(void) ""
|
||||||
vfio_get_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64
|
vfio_get_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64
|
||||||
vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64
|
vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64
|
||||||
|
vfio_reset_handler(void) ""
|
||||||
|
|
||||||
# platform.c
|
# platform.c
|
||||||
vfio_platform_realize(char *name, char *compat) "vfio device %s, compat = %s"
|
vfio_platform_realize(char *name, char *compat) "vfio device %s, compat = %s"
|
||||||
|
@ -108,7 +108,7 @@ virtio_pci_notify_write(uint64_t addr, uint64_t val, unsigned int size) "0x%" PR
|
|||||||
virtio_pci_notify_write_pio(uint64_t addr, uint64_t val, unsigned int size) "0x%" PRIx64" = 0x%" PRIx64 " (%d)"
|
virtio_pci_notify_write_pio(uint64_t addr, uint64_t val, unsigned int size) "0x%" PRIx64" = 0x%" PRIx64 " (%d)"
|
||||||
|
|
||||||
# hw/virtio/virtio-iommu.c
|
# hw/virtio/virtio-iommu.c
|
||||||
virtio_iommu_device_reset(void) "reset!"
|
virtio_iommu_device_reset_exit(void) "reset!"
|
||||||
virtio_iommu_system_reset(void) "system reset!"
|
virtio_iommu_system_reset(void) "system reset!"
|
||||||
virtio_iommu_get_features(uint64_t features) "device supports features=0x%"PRIx64
|
virtio_iommu_get_features(uint64_t features) "device supports features=0x%"PRIx64
|
||||||
virtio_iommu_device_status(uint8_t status) "driver status = %d"
|
virtio_iommu_device_status(uint8_t status) "driver status = %d"
|
||||||
|
@ -28,12 +28,18 @@ struct VhostIOVATree {
|
|||||||
|
|
||||||
/* IOVA address to qemu memory maps. */
|
/* IOVA address to qemu memory maps. */
|
||||||
IOVATree *iova_taddr_map;
|
IOVATree *iova_taddr_map;
|
||||||
|
|
||||||
|
/* Allocated IOVA addresses */
|
||||||
|
IOVATree *iova_map;
|
||||||
|
|
||||||
|
/* GPA->IOVA address memory maps */
|
||||||
|
IOVATree *gpa_iova_map;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new IOVA tree
|
* Create a new VhostIOVATree
|
||||||
*
|
*
|
||||||
* Returns the new IOVA tree
|
* Returns the new VhostIOVATree.
|
||||||
*/
|
*/
|
||||||
VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last)
|
VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last)
|
||||||
{
|
{
|
||||||
@ -44,25 +50,29 @@ VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last)
|
|||||||
tree->iova_last = iova_last;
|
tree->iova_last = iova_last;
|
||||||
|
|
||||||
tree->iova_taddr_map = iova_tree_new();
|
tree->iova_taddr_map = iova_tree_new();
|
||||||
|
tree->iova_map = iova_tree_new();
|
||||||
|
tree->gpa_iova_map = gpa_tree_new();
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete an iova tree
|
* Delete a VhostIOVATree
|
||||||
*/
|
*/
|
||||||
void vhost_iova_tree_delete(VhostIOVATree *iova_tree)
|
void vhost_iova_tree_delete(VhostIOVATree *iova_tree)
|
||||||
{
|
{
|
||||||
iova_tree_destroy(iova_tree->iova_taddr_map);
|
iova_tree_destroy(iova_tree->iova_taddr_map);
|
||||||
|
iova_tree_destroy(iova_tree->iova_map);
|
||||||
|
iova_tree_destroy(iova_tree->gpa_iova_map);
|
||||||
g_free(iova_tree);
|
g_free(iova_tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the IOVA address stored from a memory address
|
* Find the IOVA address stored from a memory address
|
||||||
*
|
*
|
||||||
* @tree: The iova tree
|
* @tree: The VhostIOVATree
|
||||||
* @map: The map with the memory address
|
* @map: The map with the memory address
|
||||||
*
|
*
|
||||||
* Return the stored mapping, or NULL if not found.
|
* Returns the stored IOVA->HVA mapping, or NULL if not found.
|
||||||
*/
|
*/
|
||||||
const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree,
|
const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree,
|
||||||
const DMAMap *map)
|
const DMAMap *map)
|
||||||
@ -71,40 +81,111 @@ const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate a new mapping
|
* Allocate a new IOVA range and add the mapping to the IOVA->HVA tree
|
||||||
*
|
*
|
||||||
* @tree: The iova tree
|
* @tree: The VhostIOVATree
|
||||||
* @map: The iova map
|
* @map: The IOVA mapping
|
||||||
|
* @taddr: The translated address (HVA)
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* - IOVA_OK if the map fits in the container
|
* - IOVA_OK if the map fits in the container
|
||||||
* - IOVA_ERR_INVALID if the map does not make sense (like size overflow)
|
* - IOVA_ERR_INVALID if the map does not make sense (like size overflow)
|
||||||
* - IOVA_ERR_NOMEM if tree cannot allocate more space.
|
* - IOVA_ERR_NOMEM if tree cannot allocate more space.
|
||||||
*
|
*
|
||||||
* It returns assignated iova in map->iova if return value is VHOST_DMA_MAP_OK.
|
* It returns an assigned IOVA in map->iova if the return value is IOVA_OK.
|
||||||
*/
|
*/
|
||||||
int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map)
|
int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map, hwaddr taddr)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* Some vhost devices do not like addr 0. Skip first page */
|
/* Some vhost devices do not like addr 0. Skip first page */
|
||||||
hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size();
|
hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size();
|
||||||
|
|
||||||
if (map->translated_addr + map->size < map->translated_addr ||
|
if (taddr + map->size < taddr || map->perm == IOMMU_NONE) {
|
||||||
map->perm == IOMMU_NONE) {
|
|
||||||
return IOVA_ERR_INVALID;
|
return IOVA_ERR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate a node in IOVA address */
|
/* Allocate a node in the IOVA-only tree */
|
||||||
return iova_tree_alloc_map(tree->iova_taddr_map, map, iova_first,
|
ret = iova_tree_alloc_map(tree->iova_map, map, iova_first, tree->iova_last);
|
||||||
tree->iova_last);
|
if (unlikely(ret != IOVA_OK)) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert a node in the IOVA->HVA tree */
|
||||||
|
map->translated_addr = taddr;
|
||||||
|
return iova_tree_insert(tree->iova_taddr_map, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove existing mappings from iova tree
|
* Remove existing mappings from the IOVA-only and IOVA->HVA trees
|
||||||
*
|
*
|
||||||
* @iova_tree: The vhost iova tree
|
* @iova_tree: The VhostIOVATree
|
||||||
* @map: The map to remove
|
* @map: The map to remove
|
||||||
*/
|
*/
|
||||||
void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map)
|
void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map)
|
||||||
{
|
{
|
||||||
iova_tree_remove(iova_tree->iova_taddr_map, map);
|
iova_tree_remove(iova_tree->iova_taddr_map, map);
|
||||||
|
iova_tree_remove(iova_tree->iova_map, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the IOVA address stored from a guest memory address (GPA)
|
||||||
|
*
|
||||||
|
* @tree: The VhostIOVATree
|
||||||
|
* @map: The map with the guest memory address
|
||||||
|
*
|
||||||
|
* Returns the stored GPA->IOVA mapping, or NULL if not found.
|
||||||
|
*/
|
||||||
|
const DMAMap *vhost_iova_tree_find_gpa(const VhostIOVATree *tree,
|
||||||
|
const DMAMap *map)
|
||||||
|
{
|
||||||
|
return iova_tree_find_iova(tree->gpa_iova_map, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new IOVA range and add the mapping to the GPA->IOVA tree
|
||||||
|
*
|
||||||
|
* @tree: The VhostIOVATree
|
||||||
|
* @map: The IOVA mapping
|
||||||
|
* @taddr: The translated address (GPA)
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - IOVA_OK if the map fits both containers
|
||||||
|
* - IOVA_ERR_INVALID if the map does not make sense (like size overflow)
|
||||||
|
* - IOVA_ERR_NOMEM if the IOVA-only tree cannot allocate more space
|
||||||
|
*
|
||||||
|
* It returns an assigned IOVA in map->iova if the return value is IOVA_OK.
|
||||||
|
*/
|
||||||
|
int vhost_iova_tree_map_alloc_gpa(VhostIOVATree *tree, DMAMap *map, hwaddr taddr)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Some vhost devices don't like addr 0. Skip first page */
|
||||||
|
hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size();
|
||||||
|
|
||||||
|
if (taddr + map->size < taddr || map->perm == IOMMU_NONE) {
|
||||||
|
return IOVA_ERR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate a node in the IOVA-only tree */
|
||||||
|
ret = iova_tree_alloc_map(tree->iova_map, map, iova_first, tree->iova_last);
|
||||||
|
if (unlikely(ret != IOVA_OK)) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert a node in the GPA->IOVA tree */
|
||||||
|
map->translated_addr = taddr;
|
||||||
|
return gpa_tree_insert(tree->gpa_iova_map, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove existing mappings from the IOVA-only and GPA->IOVA trees
|
||||||
|
*
|
||||||
|
* @tree: The VhostIOVATree
|
||||||
|
* @map: The map to remove
|
||||||
|
*/
|
||||||
|
void vhost_iova_tree_remove_gpa(VhostIOVATree *iova_tree, DMAMap map)
|
||||||
|
{
|
||||||
|
iova_tree_remove(iova_tree->gpa_iova_map, map);
|
||||||
|
iova_tree_remove(iova_tree->iova_map, map);
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,13 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostIOVATree, vhost_iova_tree_delete);
|
|||||||
|
|
||||||
const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *iova_tree,
|
const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *iova_tree,
|
||||||
const DMAMap *map);
|
const DMAMap *map);
|
||||||
int vhost_iova_tree_map_alloc(VhostIOVATree *iova_tree, DMAMap *map);
|
int vhost_iova_tree_map_alloc(VhostIOVATree *iova_tree, DMAMap *map,
|
||||||
|
hwaddr taddr);
|
||||||
void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map);
|
void vhost_iova_tree_remove(VhostIOVATree *iova_tree, DMAMap map);
|
||||||
|
const DMAMap *vhost_iova_tree_find_gpa(const VhostIOVATree *iova_tree,
|
||||||
|
const DMAMap *map);
|
||||||
|
int vhost_iova_tree_map_alloc_gpa(VhostIOVATree *iova_tree, DMAMap *map,
|
||||||
|
hwaddr taddr);
|
||||||
|
void vhost_iova_tree_remove_gpa(VhostIOVATree *iova_tree, DMAMap map);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -78,24 +78,39 @@ uint16_t vhost_svq_available_slots(const VhostShadowVirtqueue *svq)
|
|||||||
* @vaddr: Translated IOVA addresses
|
* @vaddr: Translated IOVA addresses
|
||||||
* @iovec: Source qemu's VA addresses
|
* @iovec: Source qemu's VA addresses
|
||||||
* @num: Length of iovec and minimum length of vaddr
|
* @num: Length of iovec and minimum length of vaddr
|
||||||
|
* @gpas: Descriptors' GPAs, if backed by guest memory
|
||||||
*/
|
*/
|
||||||
static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq,
|
static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq,
|
||||||
hwaddr *addrs, const struct iovec *iovec,
|
hwaddr *addrs, const struct iovec *iovec,
|
||||||
size_t num)
|
size_t num, const hwaddr *gpas)
|
||||||
{
|
{
|
||||||
if (num == 0) {
|
if (num == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < num; ++i) {
|
for (size_t i = 0; i < num; ++i) {
|
||||||
DMAMap needle = {
|
|
||||||
.translated_addr = (hwaddr)(uintptr_t)iovec[i].iov_base,
|
|
||||||
.size = iovec[i].iov_len,
|
|
||||||
};
|
|
||||||
Int128 needle_last, map_last;
|
Int128 needle_last, map_last;
|
||||||
size_t off;
|
size_t off;
|
||||||
|
const DMAMap *map;
|
||||||
|
DMAMap needle;
|
||||||
|
|
||||||
|
/* Check if the descriptor is backed by guest memory */
|
||||||
|
if (gpas) {
|
||||||
|
/* Search the GPA->IOVA tree */
|
||||||
|
needle = (DMAMap) {
|
||||||
|
.translated_addr = gpas[i],
|
||||||
|
.size = iovec[i].iov_len,
|
||||||
|
};
|
||||||
|
map = vhost_iova_tree_find_gpa(svq->iova_tree, &needle);
|
||||||
|
} else {
|
||||||
|
/* Search the IOVA->HVA tree */
|
||||||
|
needle = (DMAMap) {
|
||||||
|
.translated_addr = (hwaddr)(uintptr_t)iovec[i].iov_base,
|
||||||
|
.size = iovec[i].iov_len,
|
||||||
|
};
|
||||||
|
map = vhost_iova_tree_find_iova(svq->iova_tree, &needle);
|
||||||
|
}
|
||||||
|
|
||||||
const DMAMap *map = vhost_iova_tree_find_iova(svq->iova_tree, &needle);
|
|
||||||
/*
|
/*
|
||||||
* Map cannot be NULL since iova map contains all guest space and
|
* Map cannot be NULL since iova map contains all guest space and
|
||||||
* qemu already has a physical address mapped
|
* qemu already has a physical address mapped
|
||||||
@ -130,6 +145,7 @@ static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq,
|
|||||||
* @sg: Cache for hwaddr
|
* @sg: Cache for hwaddr
|
||||||
* @iovec: The iovec from the guest
|
* @iovec: The iovec from the guest
|
||||||
* @num: iovec length
|
* @num: iovec length
|
||||||
|
* @addr: Descriptors' GPAs, if backed by guest memory
|
||||||
* @more_descs: True if more descriptors come in the chain
|
* @more_descs: True if more descriptors come in the chain
|
||||||
* @write: True if they are writeable descriptors
|
* @write: True if they are writeable descriptors
|
||||||
*
|
*
|
||||||
@ -137,7 +153,8 @@ static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq,
|
|||||||
*/
|
*/
|
||||||
static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg,
|
static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg,
|
||||||
const struct iovec *iovec, size_t num,
|
const struct iovec *iovec, size_t num,
|
||||||
bool more_descs, bool write)
|
const hwaddr *addr, bool more_descs,
|
||||||
|
bool write)
|
||||||
{
|
{
|
||||||
uint16_t i = svq->free_head, last = svq->free_head;
|
uint16_t i = svq->free_head, last = svq->free_head;
|
||||||
unsigned n;
|
unsigned n;
|
||||||
@ -149,7 +166,7 @@ static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = vhost_svq_translate_addr(svq, sg, iovec, num);
|
ok = vhost_svq_translate_addr(svq, sg, iovec, num, addr);
|
||||||
if (unlikely(!ok)) {
|
if (unlikely(!ok)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -165,17 +182,18 @@ static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg,
|
|||||||
descs[i].len = cpu_to_le32(iovec[n].iov_len);
|
descs[i].len = cpu_to_le32(iovec[n].iov_len);
|
||||||
|
|
||||||
last = i;
|
last = i;
|
||||||
i = cpu_to_le16(svq->desc_next[i]);
|
i = svq->desc_next[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
svq->free_head = le16_to_cpu(svq->desc_next[last]);
|
svq->free_head = svq->desc_next[last];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool vhost_svq_add_split(VhostShadowVirtqueue *svq,
|
static bool vhost_svq_add_split(VhostShadowVirtqueue *svq,
|
||||||
const struct iovec *out_sg, size_t out_num,
|
const struct iovec *out_sg, size_t out_num,
|
||||||
|
const hwaddr *out_addr,
|
||||||
const struct iovec *in_sg, size_t in_num,
|
const struct iovec *in_sg, size_t in_num,
|
||||||
unsigned *head)
|
const hwaddr *in_addr, unsigned *head)
|
||||||
{
|
{
|
||||||
unsigned avail_idx;
|
unsigned avail_idx;
|
||||||
vring_avail_t *avail = svq->vring.avail;
|
vring_avail_t *avail = svq->vring.avail;
|
||||||
@ -191,13 +209,14 @@ static bool vhost_svq_add_split(VhostShadowVirtqueue *svq,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = vhost_svq_vring_write_descs(svq, sgs, out_sg, out_num, in_num > 0,
|
ok = vhost_svq_vring_write_descs(svq, sgs, out_sg, out_num, out_addr,
|
||||||
false);
|
in_num > 0, false);
|
||||||
if (unlikely(!ok)) {
|
if (unlikely(!ok)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = vhost_svq_vring_write_descs(svq, sgs, in_sg, in_num, false, true);
|
ok = vhost_svq_vring_write_descs(svq, sgs, in_sg, in_num, in_addr, false,
|
||||||
|
true);
|
||||||
if (unlikely(!ok)) {
|
if (unlikely(!ok)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -228,10 +247,12 @@ static void vhost_svq_kick(VhostShadowVirtqueue *svq)
|
|||||||
smp_mb();
|
smp_mb();
|
||||||
|
|
||||||
if (virtio_vdev_has_feature(svq->vdev, VIRTIO_RING_F_EVENT_IDX)) {
|
if (virtio_vdev_has_feature(svq->vdev, VIRTIO_RING_F_EVENT_IDX)) {
|
||||||
uint16_t avail_event = *(uint16_t *)(&svq->vring.used->ring[svq->vring.num]);
|
uint16_t avail_event = le16_to_cpu(
|
||||||
|
*(uint16_t *)(&svq->vring.used->ring[svq->vring.num]));
|
||||||
needs_kick = vring_need_event(avail_event, svq->shadow_avail_idx, svq->shadow_avail_idx - 1);
|
needs_kick = vring_need_event(avail_event, svq->shadow_avail_idx, svq->shadow_avail_idx - 1);
|
||||||
} else {
|
} else {
|
||||||
needs_kick = !(svq->vring.used->flags & VRING_USED_F_NO_NOTIFY);
|
needs_kick =
|
||||||
|
!(svq->vring.used->flags & cpu_to_le16(VRING_USED_F_NO_NOTIFY));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!needs_kick) {
|
if (!needs_kick) {
|
||||||
@ -247,8 +268,9 @@ static void vhost_svq_kick(VhostShadowVirtqueue *svq)
|
|||||||
* Return -EINVAL if element is invalid, -ENOSPC if dev queue is full
|
* Return -EINVAL if element is invalid, -ENOSPC if dev queue is full
|
||||||
*/
|
*/
|
||||||
int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
|
int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
|
||||||
size_t out_num, const struct iovec *in_sg, size_t in_num,
|
size_t out_num, const hwaddr *out_addr,
|
||||||
VirtQueueElement *elem)
|
const struct iovec *in_sg, size_t in_num,
|
||||||
|
const hwaddr *in_addr, VirtQueueElement *elem)
|
||||||
{
|
{
|
||||||
unsigned qemu_head;
|
unsigned qemu_head;
|
||||||
unsigned ndescs = in_num + out_num;
|
unsigned ndescs = in_num + out_num;
|
||||||
@ -258,7 +280,8 @@ int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
|
|||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = vhost_svq_add_split(svq, out_sg, out_num, in_sg, in_num, &qemu_head);
|
ok = vhost_svq_add_split(svq, out_sg, out_num, out_addr, in_sg, in_num,
|
||||||
|
in_addr, &qemu_head);
|
||||||
if (unlikely(!ok)) {
|
if (unlikely(!ok)) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -274,8 +297,8 @@ int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
|
|||||||
static int vhost_svq_add_element(VhostShadowVirtqueue *svq,
|
static int vhost_svq_add_element(VhostShadowVirtqueue *svq,
|
||||||
VirtQueueElement *elem)
|
VirtQueueElement *elem)
|
||||||
{
|
{
|
||||||
return vhost_svq_add(svq, elem->out_sg, elem->out_num, elem->in_sg,
|
return vhost_svq_add(svq, elem->out_sg, elem->out_num, elem->out_addr,
|
||||||
elem->in_num, elem);
|
elem->in_sg, elem->in_num, elem->in_addr, elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -365,7 +388,7 @@ static bool vhost_svq_more_used(VhostShadowVirtqueue *svq)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
svq->shadow_used_idx = cpu_to_le16(*(volatile uint16_t *)used_idx);
|
svq->shadow_used_idx = le16_to_cpu(*(volatile uint16_t *)used_idx);
|
||||||
|
|
||||||
return svq->last_used_idx != svq->shadow_used_idx;
|
return svq->last_used_idx != svq->shadow_used_idx;
|
||||||
}
|
}
|
||||||
@ -383,7 +406,7 @@ static bool vhost_svq_enable_notification(VhostShadowVirtqueue *svq)
|
|||||||
{
|
{
|
||||||
if (virtio_vdev_has_feature(svq->vdev, VIRTIO_RING_F_EVENT_IDX)) {
|
if (virtio_vdev_has_feature(svq->vdev, VIRTIO_RING_F_EVENT_IDX)) {
|
||||||
uint16_t *used_event = (uint16_t *)&svq->vring.avail->ring[svq->vring.num];
|
uint16_t *used_event = (uint16_t *)&svq->vring.avail->ring[svq->vring.num];
|
||||||
*used_event = svq->shadow_used_idx;
|
*used_event = cpu_to_le16(svq->shadow_used_idx);
|
||||||
} else {
|
} else {
|
||||||
svq->vring.avail->flags &= ~cpu_to_le16(VRING_AVAIL_F_NO_INTERRUPT);
|
svq->vring.avail->flags &= ~cpu_to_le16(VRING_AVAIL_F_NO_INTERRUPT);
|
||||||
}
|
}
|
||||||
@ -408,7 +431,7 @@ static uint16_t vhost_svq_last_desc_of_chain(const VhostShadowVirtqueue *svq,
|
|||||||
uint16_t num, uint16_t i)
|
uint16_t num, uint16_t i)
|
||||||
{
|
{
|
||||||
for (uint16_t j = 0; j < (num - 1); ++j) {
|
for (uint16_t j = 0; j < (num - 1); ++j) {
|
||||||
i = le16_to_cpu(svq->desc_next[i]);
|
i = svq->desc_next[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
@ -683,7 +706,7 @@ void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev,
|
|||||||
svq->desc_state = g_new0(SVQDescState, svq->vring.num);
|
svq->desc_state = g_new0(SVQDescState, svq->vring.num);
|
||||||
svq->desc_next = g_new0(uint16_t, svq->vring.num);
|
svq->desc_next = g_new0(uint16_t, svq->vring.num);
|
||||||
for (unsigned i = 0; i < svq->vring.num - 1; i++) {
|
for (unsigned i = 0; i < svq->vring.num - 1; i++) {
|
||||||
svq->desc_next[i] = cpu_to_le16(i + 1);
|
svq->desc_next[i] = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,8 +118,9 @@ uint16_t vhost_svq_available_slots(const VhostShadowVirtqueue *svq);
|
|||||||
void vhost_svq_push_elem(VhostShadowVirtqueue *svq,
|
void vhost_svq_push_elem(VhostShadowVirtqueue *svq,
|
||||||
const VirtQueueElement *elem, uint32_t len);
|
const VirtQueueElement *elem, uint32_t len);
|
||||||
int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
|
int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
|
||||||
size_t out_num, const struct iovec *in_sg, size_t in_num,
|
size_t out_num, const hwaddr *out_addr,
|
||||||
VirtQueueElement *elem);
|
const struct iovec *in_sg, size_t in_num,
|
||||||
|
const hwaddr *in_addr, VirtQueueElement *elem);
|
||||||
size_t vhost_svq_poll(VhostShadowVirtqueue *svq, size_t num);
|
size_t vhost_svq_poll(VhostShadowVirtqueue *svq, size_t num);
|
||||||
|
|
||||||
void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd);
|
void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd);
|
||||||
|
@ -16,6 +16,18 @@
|
|||||||
#include "standard-headers/linux/virtio_ids.h"
|
#include "standard-headers/linux/virtio_ids.h"
|
||||||
#include "standard-headers/linux/virtio_snd.h"
|
#include "standard-headers/linux/virtio_snd.h"
|
||||||
|
|
||||||
|
static const VirtIOFeature feature_sizes[] = {
|
||||||
|
{.flags = 1ULL << VIRTIO_SND_F_CTLS,
|
||||||
|
.end = endof(struct virtio_snd_config, controls)},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VirtIOConfigSizeParams cfg_size_params = {
|
||||||
|
.min_size = endof(struct virtio_snd_config, chmaps),
|
||||||
|
.max_size = sizeof(struct virtio_snd_config),
|
||||||
|
.feature_sizes = feature_sizes
|
||||||
|
};
|
||||||
|
|
||||||
static const VMStateDescription vu_snd_vmstate = {
|
static const VMStateDescription vu_snd_vmstate = {
|
||||||
.name = "vhost-user-snd",
|
.name = "vhost-user-snd",
|
||||||
.unmigratable = 1,
|
.unmigratable = 1,
|
||||||
@ -23,16 +35,20 @@ static const VMStateDescription vu_snd_vmstate = {
|
|||||||
|
|
||||||
static const Property vsnd_properties[] = {
|
static const Property vsnd_properties[] = {
|
||||||
DEFINE_PROP_CHR("chardev", VHostUserBase, chardev),
|
DEFINE_PROP_CHR("chardev", VHostUserBase, chardev),
|
||||||
|
DEFINE_PROP_BIT64("controls", VHostUserBase,
|
||||||
|
parent_obj.host_features, VIRTIO_SND_F_CTLS, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
static void vu_snd_base_realize(DeviceState *dev, Error **errp)
|
static void vu_snd_base_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
VHostUserBase *vub = VHOST_USER_BASE(dev);
|
VHostUserBase *vub = VHOST_USER_BASE(dev);
|
||||||
VHostUserBaseClass *vubs = VHOST_USER_BASE_GET_CLASS(dev);
|
VHostUserBaseClass *vubs = VHOST_USER_BASE_GET_CLASS(dev);
|
||||||
|
VirtIODevice *vdev = &vub->parent_obj;
|
||||||
|
|
||||||
vub->virtio_id = VIRTIO_ID_SOUND;
|
vub->virtio_id = VIRTIO_ID_SOUND;
|
||||||
vub->num_vqs = 4;
|
vub->num_vqs = 4;
|
||||||
vub->config_size = sizeof(struct virtio_snd_config);
|
vub->config_size = virtio_get_config_size(&cfg_size_params,
|
||||||
|
vdev->host_features);
|
||||||
vub->vq_size = 64;
|
vub->vq_size = 64;
|
||||||
|
|
||||||
vubs->parent_realize(dev, errp);
|
vubs->parent_realize(dev, errp);
|
||||||
|
@ -360,14 +360,20 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener,
|
|||||||
llsize = int128_sub(llend, int128_make64(iova));
|
llsize = int128_sub(llend, int128_make64(iova));
|
||||||
if (s->shadow_data) {
|
if (s->shadow_data) {
|
||||||
int r;
|
int r;
|
||||||
|
hwaddr gpa = section->offset_within_address_space;
|
||||||
|
|
||||||
mem_region.translated_addr = (hwaddr)(uintptr_t)vaddr,
|
|
||||||
mem_region.size = int128_get64(llsize) - 1,
|
mem_region.size = int128_get64(llsize) - 1,
|
||||||
mem_region.perm = IOMMU_ACCESS_FLAG(true, section->readonly),
|
mem_region.perm = IOMMU_ACCESS_FLAG(true, section->readonly),
|
||||||
|
|
||||||
r = vhost_iova_tree_map_alloc(s->iova_tree, &mem_region);
|
r = vhost_iova_tree_map_alloc_gpa(s->iova_tree, &mem_region, gpa);
|
||||||
if (unlikely(r != IOVA_OK)) {
|
if (unlikely(r != IOVA_OK)) {
|
||||||
error_report("Can't allocate a mapping (%d)", r);
|
error_report("Can't allocate a mapping (%d)", r);
|
||||||
|
|
||||||
|
if (mem_region.translated_addr == gpa) {
|
||||||
|
error_report("Insertion to GPA->IOVA tree failed");
|
||||||
|
/* Remove the mapping from the IOVA-only tree */
|
||||||
|
goto fail_map;
|
||||||
|
}
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,7 +392,7 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener,
|
|||||||
|
|
||||||
fail_map:
|
fail_map:
|
||||||
if (s->shadow_data) {
|
if (s->shadow_data) {
|
||||||
vhost_iova_tree_remove(s->iova_tree, mem_region);
|
vhost_iova_tree_remove_gpa(s->iova_tree, mem_region);
|
||||||
}
|
}
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@ -440,21 +446,18 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener,
|
|||||||
|
|
||||||
if (s->shadow_data) {
|
if (s->shadow_data) {
|
||||||
const DMAMap *result;
|
const DMAMap *result;
|
||||||
const void *vaddr = memory_region_get_ram_ptr(section->mr) +
|
|
||||||
section->offset_within_region +
|
|
||||||
(iova - section->offset_within_address_space);
|
|
||||||
DMAMap mem_region = {
|
DMAMap mem_region = {
|
||||||
.translated_addr = (hwaddr)(uintptr_t)vaddr,
|
.translated_addr = section->offset_within_address_space,
|
||||||
.size = int128_get64(llsize) - 1,
|
.size = int128_get64(llsize) - 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
result = vhost_iova_tree_find_iova(s->iova_tree, &mem_region);
|
result = vhost_iova_tree_find_gpa(s->iova_tree, &mem_region);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
/* The memory listener map wasn't mapped */
|
/* The memory listener map wasn't mapped */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
iova = result->iova;
|
iova = result->iova;
|
||||||
vhost_iova_tree_remove(s->iova_tree, *result);
|
vhost_iova_tree_remove_gpa(s->iova_tree, *result);
|
||||||
}
|
}
|
||||||
vhost_vdpa_iotlb_batch_begin_once(s);
|
vhost_vdpa_iotlb_batch_begin_once(s);
|
||||||
/*
|
/*
|
||||||
@ -1142,16 +1145,23 @@ static void vhost_vdpa_svq_unmap_rings(struct vhost_dev *dev,
|
|||||||
*
|
*
|
||||||
* @v: Vhost-vdpa device
|
* @v: Vhost-vdpa device
|
||||||
* @needle: The area to search iova
|
* @needle: The area to search iova
|
||||||
|
* @taddr: The translated address (HVA)
|
||||||
* @errorp: Error pointer
|
* @errorp: Error pointer
|
||||||
*/
|
*/
|
||||||
static bool vhost_vdpa_svq_map_ring(struct vhost_vdpa *v, DMAMap *needle,
|
static bool vhost_vdpa_svq_map_ring(struct vhost_vdpa *v, DMAMap *needle,
|
||||||
Error **errp)
|
hwaddr taddr, Error **errp)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = vhost_iova_tree_map_alloc(v->shared->iova_tree, needle);
|
r = vhost_iova_tree_map_alloc(v->shared->iova_tree, needle, taddr);
|
||||||
if (unlikely(r != IOVA_OK)) {
|
if (unlikely(r != IOVA_OK)) {
|
||||||
error_setg(errp, "Cannot allocate iova (%d)", r);
|
error_setg(errp, "Cannot allocate iova (%d)", r);
|
||||||
|
|
||||||
|
if (needle->translated_addr == taddr) {
|
||||||
|
error_append_hint(errp, "Insertion to IOVA->HVA tree failed");
|
||||||
|
/* Remove the mapping from the IOVA-only tree */
|
||||||
|
vhost_iova_tree_remove(v->shared->iova_tree, *needle);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1192,11 +1202,11 @@ static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev,
|
|||||||
vhost_svq_get_vring_addr(svq, &svq_addr);
|
vhost_svq_get_vring_addr(svq, &svq_addr);
|
||||||
|
|
||||||
driver_region = (DMAMap) {
|
driver_region = (DMAMap) {
|
||||||
.translated_addr = svq_addr.desc_user_addr,
|
|
||||||
.size = driver_size - 1,
|
.size = driver_size - 1,
|
||||||
.perm = IOMMU_RO,
|
.perm = IOMMU_RO,
|
||||||
};
|
};
|
||||||
ok = vhost_vdpa_svq_map_ring(v, &driver_region, errp);
|
ok = vhost_vdpa_svq_map_ring(v, &driver_region, svq_addr.desc_user_addr,
|
||||||
|
errp);
|
||||||
if (unlikely(!ok)) {
|
if (unlikely(!ok)) {
|
||||||
error_prepend(errp, "Cannot create vq driver region: ");
|
error_prepend(errp, "Cannot create vq driver region: ");
|
||||||
return false;
|
return false;
|
||||||
@ -1206,11 +1216,11 @@ static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev,
|
|||||||
addr->avail_user_addr = driver_region.iova + avail_offset;
|
addr->avail_user_addr = driver_region.iova + avail_offset;
|
||||||
|
|
||||||
device_region = (DMAMap) {
|
device_region = (DMAMap) {
|
||||||
.translated_addr = svq_addr.used_user_addr,
|
|
||||||
.size = device_size - 1,
|
.size = device_size - 1,
|
||||||
.perm = IOMMU_RW,
|
.perm = IOMMU_RW,
|
||||||
};
|
};
|
||||||
ok = vhost_vdpa_svq_map_ring(v, &device_region, errp);
|
ok = vhost_vdpa_svq_map_ring(v, &device_region, svq_addr.used_user_addr,
|
||||||
|
errp);
|
||||||
if (unlikely(!ok)) {
|
if (unlikely(!ok)) {
|
||||||
error_prepend(errp, "Cannot create vq device region: ");
|
error_prepend(errp, "Cannot create vq device region: ");
|
||||||
vhost_vdpa_svq_unmap_ring(v, driver_region.translated_addr);
|
vhost_vdpa_svq_unmap_ring(v, driver_region.translated_addr);
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "migration/misc.h"
|
#include "migration/misc.h"
|
||||||
|
#include "system/reset.h"
|
||||||
#include "hw/virtio/virtio-bus.h"
|
#include "hw/virtio/virtio-bus.h"
|
||||||
#include "hw/virtio/virtio-access.h"
|
#include "hw/virtio/virtio-access.h"
|
||||||
|
|
||||||
@ -910,6 +910,8 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
reset_stats(s);
|
reset_stats(s);
|
||||||
|
s->stats_last_update = 0;
|
||||||
|
qemu_register_resettable(OBJECT(dev));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_balloon_device_unrealize(DeviceState *dev)
|
static void virtio_balloon_device_unrealize(DeviceState *dev)
|
||||||
@ -917,6 +919,7 @@ static void virtio_balloon_device_unrealize(DeviceState *dev)
|
|||||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||||
VirtIOBalloon *s = VIRTIO_BALLOON(dev);
|
VirtIOBalloon *s = VIRTIO_BALLOON(dev);
|
||||||
|
|
||||||
|
qemu_unregister_resettable(OBJECT(dev));
|
||||||
if (s->free_page_bh) {
|
if (s->free_page_bh) {
|
||||||
qemu_bh_delete(s->free_page_bh);
|
qemu_bh_delete(s->free_page_bh);
|
||||||
object_unref(OBJECT(s->iothread));
|
object_unref(OBJECT(s->iothread));
|
||||||
@ -987,6 +990,27 @@ static void virtio_balloon_set_status(VirtIODevice *vdev, uint8_t status)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ResettableState *virtio_balloon_get_reset_state(Object *obj)
|
||||||
|
{
|
||||||
|
VirtIOBalloon *s = VIRTIO_BALLOON(obj);
|
||||||
|
return &s->reset_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_balloon_reset_enter(Object *obj, ResetType type)
|
||||||
|
{
|
||||||
|
VirtIOBalloon *s = VIRTIO_BALLOON(obj);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When waking up from standby/suspend-to-ram, do not reset stats.
|
||||||
|
*/
|
||||||
|
if (type == RESET_TYPE_WAKEUP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_stats(s);
|
||||||
|
s->stats_last_update = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void virtio_balloon_instance_init(Object *obj)
|
static void virtio_balloon_instance_init(Object *obj)
|
||||||
{
|
{
|
||||||
VirtIOBalloon *s = VIRTIO_BALLOON(obj);
|
VirtIOBalloon *s = VIRTIO_BALLOON(obj);
|
||||||
@ -1038,6 +1062,7 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data)
|
|||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||||
|
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||||
|
|
||||||
device_class_set_props(dc, virtio_balloon_properties);
|
device_class_set_props(dc, virtio_balloon_properties);
|
||||||
dc->vmsd = &vmstate_virtio_balloon;
|
dc->vmsd = &vmstate_virtio_balloon;
|
||||||
@ -1050,6 +1075,9 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data)
|
|||||||
vdc->get_features = virtio_balloon_get_features;
|
vdc->get_features = virtio_balloon_get_features;
|
||||||
vdc->set_status = virtio_balloon_set_status;
|
vdc->set_status = virtio_balloon_set_status;
|
||||||
vdc->vmsd = &vmstate_virtio_balloon_device;
|
vdc->vmsd = &vmstate_virtio_balloon_device;
|
||||||
|
|
||||||
|
rc->get_state = virtio_balloon_get_reset_state;
|
||||||
|
rc->phases.enter = virtio_balloon_reset_enter;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo virtio_balloon_info = {
|
static const TypeInfo virtio_balloon_info = {
|
||||||
|
@ -1504,11 +1504,11 @@ static void virtio_iommu_device_unrealize(DeviceState *dev)
|
|||||||
virtio_cleanup(vdev);
|
virtio_cleanup(vdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_iommu_device_reset(VirtIODevice *vdev)
|
static void virtio_iommu_device_reset_exit(Object *obj, ResetType type)
|
||||||
{
|
{
|
||||||
VirtIOIOMMU *s = VIRTIO_IOMMU(vdev);
|
VirtIOIOMMU *s = VIRTIO_IOMMU(obj);
|
||||||
|
|
||||||
trace_virtio_iommu_device_reset();
|
trace_virtio_iommu_device_reset_exit();
|
||||||
|
|
||||||
if (s->domains) {
|
if (s->domains) {
|
||||||
g_tree_destroy(s->domains);
|
g_tree_destroy(s->domains);
|
||||||
@ -1668,6 +1668,7 @@ static void virtio_iommu_class_init(ObjectClass *klass, void *data)
|
|||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||||
|
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||||
|
|
||||||
device_class_set_props(dc, virtio_iommu_properties);
|
device_class_set_props(dc, virtio_iommu_properties);
|
||||||
dc->vmsd = &vmstate_virtio_iommu;
|
dc->vmsd = &vmstate_virtio_iommu;
|
||||||
@ -1675,7 +1676,12 @@ static void virtio_iommu_class_init(ObjectClass *klass, void *data)
|
|||||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||||
vdc->realize = virtio_iommu_device_realize;
|
vdc->realize = virtio_iommu_device_realize;
|
||||||
vdc->unrealize = virtio_iommu_device_unrealize;
|
vdc->unrealize = virtio_iommu_device_unrealize;
|
||||||
vdc->reset = virtio_iommu_device_reset;
|
|
||||||
|
/*
|
||||||
|
* Use 'exit' reset phase to make sure all DMA requests
|
||||||
|
* have been quiesced during 'enter' or 'hold' phase
|
||||||
|
*/
|
||||||
|
rc->phases.exit = virtio_iommu_device_reset_exit;
|
||||||
vdc->get_config = virtio_iommu_get_config;
|
vdc->get_config = virtio_iommu_get_config;
|
||||||
vdc->set_config = virtio_iommu_set_config;
|
vdc->set_config = virtio_iommu_set_config;
|
||||||
vdc->get_features = virtio_iommu_get_features;
|
vdc->get_features = virtio_iommu_get_features;
|
||||||
|
@ -1596,7 +1596,7 @@ static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
|
|||||||
g_free(req.iov_base);
|
g_free(req.iov_base);
|
||||||
g_free(res.iov_base);
|
g_free(res.iov_base);
|
||||||
virtqueue_push(vq, out_elem, 0);
|
virtqueue_push(vq, out_elem, 0);
|
||||||
virtqueue_push(vq, in_elem, in_elem->in_sg->iov_len);
|
virtqueue_push(vq, in_elem, sz);
|
||||||
virtio_notify(vdev, vq);
|
virtio_notify(vdev, vq);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -264,8 +264,8 @@ void cxl_device_register_block_init(Object *obj, CXLDeviceState *dev,
|
|||||||
typedef struct CXLType3Dev CXLType3Dev;
|
typedef struct CXLType3Dev CXLType3Dev;
|
||||||
typedef struct CSWMBCCIDev CSWMBCCIDev;
|
typedef struct CSWMBCCIDev CSWMBCCIDev;
|
||||||
/* Set up default values for the register block */
|
/* Set up default values for the register block */
|
||||||
void cxl_device_register_init_t3(CXLType3Dev *ct3d);
|
void cxl_device_register_init_t3(CXLType3Dev *ct3d, int msi_n);
|
||||||
void cxl_device_register_init_swcci(CSWMBCCIDev *sw);
|
void cxl_device_register_init_swcci(CSWMBCCIDev *sw, int msi_n);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CXL r3.1 Section 8.2.8.1: CXL Device Capabilities Array Register
|
* CXL r3.1 Section 8.2.8.1: CXL Device Capabilities Array Register
|
||||||
|
@ -16,9 +16,7 @@
|
|||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
|
|
||||||
typedef struct PCIESriovPF {
|
typedef struct PCIESriovPF {
|
||||||
uint16_t num_vfs; /* Number of virtual functions created */
|
|
||||||
uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */
|
uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */
|
||||||
const char *vfname; /* Reference to the device type used for the VFs */
|
|
||||||
PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */
|
PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */
|
||||||
} PCIESriovPF;
|
} PCIESriovPF;
|
||||||
|
|
||||||
@ -27,10 +25,11 @@ typedef struct PCIESriovVF {
|
|||||||
uint16_t vf_number; /* Logical VF number of this function */
|
uint16_t vf_number; /* Logical VF number of this function */
|
||||||
} PCIESriovVF;
|
} PCIESriovVF;
|
||||||
|
|
||||||
void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
|
bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
|
||||||
const char *vfname, uint16_t vf_dev_id,
|
const char *vfname, uint16_t vf_dev_id,
|
||||||
uint16_t init_vfs, uint16_t total_vfs,
|
uint16_t init_vfs, uint16_t total_vfs,
|
||||||
uint16_t vf_offset, uint16_t vf_stride);
|
uint16_t vf_offset, uint16_t vf_stride,
|
||||||
|
Error **errp);
|
||||||
void pcie_sriov_pf_exit(PCIDevice *dev);
|
void pcie_sriov_pf_exit(PCIDevice *dev);
|
||||||
|
|
||||||
/* Set up a VF bar in the SR/IOV bar area */
|
/* Set up a VF bar in the SR/IOV bar area */
|
||||||
@ -58,6 +57,8 @@ void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize);
|
|||||||
void pcie_sriov_config_write(PCIDevice *dev, uint32_t address,
|
void pcie_sriov_config_write(PCIDevice *dev, uint32_t address,
|
||||||
uint32_t val, int len);
|
uint32_t val, int len);
|
||||||
|
|
||||||
|
void pcie_sriov_pf_post_load(PCIDevice *dev);
|
||||||
|
|
||||||
/* Reset SR/IOV */
|
/* Reset SR/IOV */
|
||||||
void pcie_sriov_pf_reset(PCIDevice *dev);
|
void pcie_sriov_pf_reset(PCIDevice *dev);
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#define QEMU_VIRTIO_BALLOON_H
|
#define QEMU_VIRTIO_BALLOON_H
|
||||||
|
|
||||||
#include "standard-headers/linux/virtio_balloon.h"
|
#include "standard-headers/linux/virtio_balloon.h"
|
||||||
|
#include "hw/resettable.h"
|
||||||
#include "hw/virtio/virtio.h"
|
#include "hw/virtio/virtio.h"
|
||||||
#include "system/iothread.h"
|
#include "system/iothread.h"
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
@ -71,6 +72,9 @@ struct VirtIOBalloon {
|
|||||||
|
|
||||||
bool qemu_4_0_config_size;
|
bool qemu_4_0_config_size;
|
||||||
uint32_t poison_val;
|
uint32_t poison_val;
|
||||||
|
|
||||||
|
/* State of the resettable container */
|
||||||
|
ResettableState reset_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -40,6 +40,28 @@ typedef struct DMAMap {
|
|||||||
} QEMU_PACKED DMAMap;
|
} QEMU_PACKED DMAMap;
|
||||||
typedef gboolean (*iova_tree_iterator)(DMAMap *map);
|
typedef gboolean (*iova_tree_iterator)(DMAMap *map);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gpa_tree_new:
|
||||||
|
*
|
||||||
|
* Create a new GPA->IOVA tree.
|
||||||
|
*
|
||||||
|
* Returns: the tree point on success, or NULL otherwise.
|
||||||
|
*/
|
||||||
|
IOVATree *gpa_tree_new(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gpa_tree_insert:
|
||||||
|
*
|
||||||
|
* @tree: The GPA->IOVA tree we're inserting the mapping to
|
||||||
|
* @map: The GPA->IOVA mapping to insert
|
||||||
|
*
|
||||||
|
* Inserts a GPA range to the GPA->IOVA tree. If there are overlapped
|
||||||
|
* ranges, IOVA_ERR_OVERLAP will be returned.
|
||||||
|
*
|
||||||
|
* Return: 0 if successful, < 0 otherwise.
|
||||||
|
*/
|
||||||
|
int gpa_tree_insert(IOVATree *tree, const DMAMap *map);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iova_tree_new:
|
* iova_tree_new:
|
||||||
*
|
*
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "chardev/char-fe.h"
|
#include "chardev/char-fe.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qapi-commands-net.h"
|
#include "qapi/qapi-commands-net.h"
|
||||||
|
#include "qapi/qapi-events-net.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
@ -271,6 +272,7 @@ static void chr_closed_bh(void *opaque)
|
|||||||
if (err) {
|
if (err) {
|
||||||
error_report_err(err);
|
error_report_err(err);
|
||||||
}
|
}
|
||||||
|
qapi_event_send_netdev_vhost_user_disconnected(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void net_vhost_user_event(void *opaque, QEMUChrEvent event)
|
static void net_vhost_user_event(void *opaque, QEMUChrEvent event)
|
||||||
@ -300,6 +302,7 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event)
|
|||||||
net_vhost_user_watch, s);
|
net_vhost_user_watch, s);
|
||||||
qmp_set_link(name, true, &err);
|
qmp_set_link(name, true, &err);
|
||||||
s->started = true;
|
s->started = true;
|
||||||
|
qapi_event_send_netdev_vhost_user_connected(name, chr->label);
|
||||||
break;
|
break;
|
||||||
case CHR_EVENT_CLOSED:
|
case CHR_EVENT_CLOSED:
|
||||||
/* a close event may happen during a read/write, but vhost
|
/* a close event may happen during a read/write, but vhost
|
||||||
|
@ -510,14 +510,20 @@ static int vhost_vdpa_cvq_map_buf(struct vhost_vdpa *v, void *buf, size_t size,
|
|||||||
bool write)
|
bool write)
|
||||||
{
|
{
|
||||||
DMAMap map = {};
|
DMAMap map = {};
|
||||||
|
hwaddr taddr = (hwaddr)(uintptr_t)buf;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
map.translated_addr = (hwaddr)(uintptr_t)buf;
|
|
||||||
map.size = size - 1;
|
map.size = size - 1;
|
||||||
map.perm = write ? IOMMU_RW : IOMMU_RO,
|
map.perm = write ? IOMMU_RW : IOMMU_RO,
|
||||||
r = vhost_iova_tree_map_alloc(v->shared->iova_tree, &map);
|
r = vhost_iova_tree_map_alloc(v->shared->iova_tree, &map, taddr);
|
||||||
if (unlikely(r != IOVA_OK)) {
|
if (unlikely(r != IOVA_OK)) {
|
||||||
error_report("Cannot map injected element");
|
error_report("Cannot map injected element");
|
||||||
|
|
||||||
|
if (map.translated_addr == taddr) {
|
||||||
|
error_report("Insertion to IOVA->HVA tree failed");
|
||||||
|
/* Remove the mapping from the IOVA-only tree */
|
||||||
|
goto dma_map_err;
|
||||||
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,7 +649,7 @@ static ssize_t vhost_vdpa_net_cvq_add(VhostVDPAState *s,
|
|||||||
VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
|
VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = vhost_svq_add(svq, out_sg, out_num, in_sg, in_num, NULL);
|
r = vhost_svq_add(svq, out_sg, out_num, NULL, in_sg, in_num, NULL, NULL);
|
||||||
if (unlikely(r != 0)) {
|
if (unlikely(r != 0)) {
|
||||||
if (unlikely(r == -ENOSPC)) {
|
if (unlikely(r == -ENOSPC)) {
|
||||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n",
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n",
|
||||||
|
@ -1031,3 +1031,43 @@
|
|||||||
##
|
##
|
||||||
{ 'event': 'NETDEV_STREAM_DISCONNECTED',
|
{ 'event': 'NETDEV_STREAM_DISCONNECTED',
|
||||||
'data': { 'netdev-id': 'str' } }
|
'data': { 'netdev-id': 'str' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @NETDEV_VHOST_USER_CONNECTED:
|
||||||
|
#
|
||||||
|
# Emitted when the vhost-user chardev is connected
|
||||||
|
#
|
||||||
|
# @netdev-id: QEMU netdev id that is connected
|
||||||
|
#
|
||||||
|
# @chardev-id: The character device id used by the QEMU netdev
|
||||||
|
#
|
||||||
|
# Since: 10.0
|
||||||
|
#
|
||||||
|
# .. qmp-example::
|
||||||
|
#
|
||||||
|
# <- { "timestamp": {"seconds": 1739538638, "microseconds": 354181 },
|
||||||
|
# "event": "NETDEV_VHOST_USER_CONNECTED",
|
||||||
|
# "data": { "netdev-id": "netdev0", "chardev-id": "chr0" } }
|
||||||
|
#
|
||||||
|
##
|
||||||
|
{ 'event': 'NETDEV_VHOST_USER_CONNECTED',
|
||||||
|
'data': { 'netdev-id': 'str', 'chardev-id': 'str' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @NETDEV_VHOST_USER_DISCONNECTED:
|
||||||
|
#
|
||||||
|
# Emitted when the vhost-user chardev is disconnected
|
||||||
|
#
|
||||||
|
# @netdev-id: QEMU netdev id that is disconnected
|
||||||
|
#
|
||||||
|
# Since: 10.0
|
||||||
|
#
|
||||||
|
# .. qmp-example::
|
||||||
|
#
|
||||||
|
# <- { "timestamp": { "seconds": 1739538634, "microseconds": 920450 },
|
||||||
|
# "event": "NETDEV_VHOST_USER_DISCONNECTED",
|
||||||
|
# "data": { "netdev-id": "netdev0" } }
|
||||||
|
#
|
||||||
|
##
|
||||||
|
{ 'event': 'NETDEV_VHOST_USER_DISCONNECTED',
|
||||||
|
'data': { 'netdev-id': 'str' } }
|
||||||
|
@ -46,6 +46,7 @@ test_timeouts = {
|
|||||||
'riscv64_tuxrun' : 120,
|
'riscv64_tuxrun' : 120,
|
||||||
's390x_ccw_virtio' : 420,
|
's390x_ccw_virtio' : 420,
|
||||||
'sh4_tuxrun' : 240,
|
'sh4_tuxrun' : 240,
|
||||||
|
'virtio_balloon': 120,
|
||||||
'x86_64_kvm_xen' : 180,
|
'x86_64_kvm_xen' : 180,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,6 +286,7 @@ tests_x86_64_system_thorough = [
|
|||||||
'linux_initrd',
|
'linux_initrd',
|
||||||
'multiprocess',
|
'multiprocess',
|
||||||
'netdev_ethtool',
|
'netdev_ethtool',
|
||||||
|
'virtio_balloon',
|
||||||
'virtio_gpu',
|
'virtio_gpu',
|
||||||
'x86_64_hotplug_blk',
|
'x86_64_hotplug_blk',
|
||||||
'x86_64_hotplug_cpu',
|
'x86_64_hotplug_cpu',
|
||||||
|
161
tests/functional/test_virtio_balloon.py
Executable file
161
tests/functional/test_virtio_balloon.py
Executable file
@ -0,0 +1,161 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# virtio-balloon tests
|
||||||
|
#
|
||||||
|
# This work is licensed under the terms of the GNU GPL, version 2 or
|
||||||
|
# later. See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from qemu_test import QemuSystemTest, Asset
|
||||||
|
from qemu_test import wait_for_console_pattern
|
||||||
|
from qemu_test import exec_command_and_wait_for_pattern
|
||||||
|
|
||||||
|
UNSET_STATS_VALUE = 18446744073709551615
|
||||||
|
|
||||||
|
|
||||||
|
class VirtioBalloonx86(QemuSystemTest):
|
||||||
|
|
||||||
|
ASSET_KERNEL = Asset(
|
||||||
|
('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
|
||||||
|
'/31/Server/x86_64/os/images/pxeboot/vmlinuz'),
|
||||||
|
'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129')
|
||||||
|
|
||||||
|
ASSET_INITRD = Asset(
|
||||||
|
('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
|
||||||
|
'/31/Server/x86_64/os/images/pxeboot/initrd.img'),
|
||||||
|
'277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b')
|
||||||
|
|
||||||
|
ASSET_DISKIMAGE = Asset(
|
||||||
|
('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
|
||||||
|
'/31/Cloud/x86_64/images/Fedora-Cloud-Base-31-1.9.x86_64.qcow2'),
|
||||||
|
'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0')
|
||||||
|
|
||||||
|
DEFAULT_KERNEL_PARAMS = ('root=/dev/vda1 console=ttyS0 net.ifnames=0 '
|
||||||
|
'rd.rescue')
|
||||||
|
|
||||||
|
def wait_for_console_pattern(self, success_message, vm=None):
|
||||||
|
wait_for_console_pattern(
|
||||||
|
self,
|
||||||
|
success_message,
|
||||||
|
failure_message="Kernel panic - not syncing",
|
||||||
|
vm=vm,
|
||||||
|
)
|
||||||
|
|
||||||
|
def mount_root(self):
|
||||||
|
self.wait_for_console_pattern('Entering emergency mode.')
|
||||||
|
prompt = '# '
|
||||||
|
self.wait_for_console_pattern(prompt)
|
||||||
|
|
||||||
|
exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot',
|
||||||
|
prompt)
|
||||||
|
exec_command_and_wait_for_pattern(self, 'chroot /sysroot',
|
||||||
|
prompt)
|
||||||
|
exec_command_and_wait_for_pattern(self, "modprobe virtio-balloon",
|
||||||
|
prompt)
|
||||||
|
|
||||||
|
def assert_initial_stats(self):
|
||||||
|
ret = self.vm.qmp('qom-get',
|
||||||
|
{'path': '/machine/peripheral/balloon',
|
||||||
|
'property': 'guest-stats'})['return']
|
||||||
|
when = ret.get('last-update')
|
||||||
|
assert when == 0
|
||||||
|
stats = ret.get('stats')
|
||||||
|
for name, val in stats.items():
|
||||||
|
assert val == UNSET_STATS_VALUE
|
||||||
|
|
||||||
|
def assert_running_stats(self, then):
|
||||||
|
ret = self.vm.qmp('qom-get',
|
||||||
|
{'path': '/machine/peripheral/balloon',
|
||||||
|
'property': 'guest-stats'})['return']
|
||||||
|
when = ret.get('last-update')
|
||||||
|
now = time.time()
|
||||||
|
|
||||||
|
assert when > then and when < now
|
||||||
|
stats = ret.get('stats')
|
||||||
|
# Stat we expect this particular Kernel to have set
|
||||||
|
expectData = [
|
||||||
|
"stat-available-memory",
|
||||||
|
"stat-disk-caches",
|
||||||
|
"stat-free-memory",
|
||||||
|
"stat-htlb-pgalloc",
|
||||||
|
"stat-htlb-pgfail",
|
||||||
|
"stat-major-faults",
|
||||||
|
"stat-minor-faults",
|
||||||
|
"stat-swap-in",
|
||||||
|
"stat-swap-out",
|
||||||
|
"stat-total-memory",
|
||||||
|
]
|
||||||
|
for name, val in stats.items():
|
||||||
|
if name in expectData:
|
||||||
|
assert val != UNSET_STATS_VALUE
|
||||||
|
else:
|
||||||
|
assert val == UNSET_STATS_VALUE
|
||||||
|
|
||||||
|
def test_virtio_balloon_stats(self):
|
||||||
|
self.set_machine('q35')
|
||||||
|
kernel_path = self.ASSET_KERNEL.fetch()
|
||||||
|
initrd_path = self.ASSET_INITRD.fetch()
|
||||||
|
diskimage_path = self.ASSET_DISKIMAGE.fetch()
|
||||||
|
|
||||||
|
self.vm.set_console()
|
||||||
|
self.vm.add_args("-S")
|
||||||
|
self.vm.add_args("-cpu", "max")
|
||||||
|
self.vm.add_args("-m", "2G")
|
||||||
|
# Slow down BIOS phase with boot menu, so that after a system
|
||||||
|
# reset, we can reliably catch the clean stats again in BIOS
|
||||||
|
# phase before the guest OS launches
|
||||||
|
self.vm.add_args("-boot", "menu=on")
|
||||||
|
self.vm.add_args("-machine", "q35,accel=kvm:tcg")
|
||||||
|
self.vm.add_args("-device", "virtio-balloon,id=balloon")
|
||||||
|
self.vm.add_args('-drive',
|
||||||
|
f'file={diskimage_path},if=none,id=drv0,snapshot=on')
|
||||||
|
self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' +
|
||||||
|
'drive=drv0,id=virtio-disk0,bootindex=1')
|
||||||
|
|
||||||
|
self.vm.add_args(
|
||||||
|
"-kernel",
|
||||||
|
kernel_path,
|
||||||
|
"-initrd",
|
||||||
|
initrd_path,
|
||||||
|
"-append",
|
||||||
|
self.DEFAULT_KERNEL_PARAMS
|
||||||
|
)
|
||||||
|
self.vm.launch()
|
||||||
|
|
||||||
|
# Poll stats at 100ms
|
||||||
|
self.vm.qmp('qom-set',
|
||||||
|
{'path': '/machine/peripheral/balloon',
|
||||||
|
'property': 'guest-stats-polling-interval',
|
||||||
|
'value': 100 })
|
||||||
|
|
||||||
|
# We've not run any guest code yet, neither BIOS or guest,
|
||||||
|
# so stats should be all default values
|
||||||
|
self.assert_initial_stats()
|
||||||
|
|
||||||
|
self.vm.qmp('cont')
|
||||||
|
|
||||||
|
then = time.time()
|
||||||
|
self.mount_root()
|
||||||
|
self.assert_running_stats(then)
|
||||||
|
|
||||||
|
# Race window between these two commands, where we
|
||||||
|
# rely on '-boot menu=on' to (hopefully) ensure we're
|
||||||
|
# still executing the BIOS when QEMU processes the
|
||||||
|
# 'stop', and thus have not loaded the virtio-balloon
|
||||||
|
# driver in the guest
|
||||||
|
self.vm.qmp('system_reset')
|
||||||
|
self.vm.qmp('stop')
|
||||||
|
|
||||||
|
# If the above assumption held, we're in BIOS now and
|
||||||
|
# stats should be all back at their default values
|
||||||
|
self.assert_initial_stats()
|
||||||
|
self.vm.qmp('cont')
|
||||||
|
|
||||||
|
then = time.time()
|
||||||
|
self.mount_root()
|
||||||
|
self.assert_running_stats(then)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
QemuSystemTest.main()
|
@ -328,8 +328,6 @@ bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry)
|
|||||||
|
|
||||||
g_assert(dev->msix_enabled);
|
g_assert(dev->msix_enabled);
|
||||||
pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off);
|
pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off);
|
||||||
qpci_io_writel(dev, dev->msix_pba_bar, dev->msix_pba_off + off,
|
|
||||||
pba_entry & ~(1 << bit_n));
|
|
||||||
return (pba_entry & (1 << bit_n)) != 0;
|
return (pba_entry & (1 << bit_n)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,3 +257,49 @@ void iova_tree_destroy(IOVATree *tree)
|
|||||||
g_tree_destroy(tree->tree);
|
g_tree_destroy(tree->tree);
|
||||||
g_free(tree);
|
g_free(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int gpa_tree_compare(gconstpointer a, gconstpointer b, gpointer data)
|
||||||
|
{
|
||||||
|
const DMAMap *m1 = a, *m2 = b;
|
||||||
|
|
||||||
|
if (m1->translated_addr > m2->translated_addr + m2->size) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m1->translated_addr + m1->size < m2->translated_addr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overlapped */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IOVATree *gpa_tree_new(void)
|
||||||
|
{
|
||||||
|
IOVATree *gpa_tree = g_new0(IOVATree, 1);
|
||||||
|
|
||||||
|
gpa_tree->tree = g_tree_new_full(gpa_tree_compare, NULL, g_free, NULL);
|
||||||
|
|
||||||
|
return gpa_tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gpa_tree_insert(IOVATree *tree, const DMAMap *map)
|
||||||
|
{
|
||||||
|
DMAMap *new;
|
||||||
|
|
||||||
|
if (map->translated_addr + map->size < map->translated_addr ||
|
||||||
|
map->perm == IOMMU_NONE) {
|
||||||
|
return IOVA_ERR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't allow inserting ranges that overlap with existing ones */
|
||||||
|
if (iova_tree_find(tree, map)) {
|
||||||
|
return IOVA_ERR_OVERLAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
new = g_new0(DMAMap, 1);
|
||||||
|
memcpy(new, map, sizeof(*new));
|
||||||
|
iova_tree_insert_internal(tree->tree, new);
|
||||||
|
|
||||||
|
return IOVA_OK;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user