s390x/virtio-ccw: add support for virtio based memory devices

Let's implement support for abstract virtio based memory devices, using
the virtio-pci implementation as an orientation. Wire them up in the
machine hotplug handler, taking care of s390x page size limitations.

As we neither support virtio-mem or virtio-pmem yet, the code is
effectively unused. We'll implement support for virtio-mem based on this
next.

Note that we won't wire up the virtio-pci variant (should currently be
impossible due to lack of support for MSI-X), but we'll add a safety net
to reject plugging them in the pre-plug handler.

Message-ID: <20241219144115.2820241-14-david@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
This commit is contained in:
David Hildenbrand 2024-12-19 15:41:14 +01:00
parent df2ac211a6
commit 88d86f6f1e
7 changed files with 274 additions and 1 deletions

View file

@ -2376,6 +2376,9 @@ F: include/hw/virtio/virtio-crypto.h
virtio based memory device
M: David Hildenbrand <david@redhat.com>
S: Supported
F: hw/s390x/virtio-ccw-md.c
F: hw/s390x/virtio-ccw-md.h
F: hw/s390x/virtio-ccw-md-stubs.c
F: hw/virtio/virtio-md-pci.c
F: include/hw/virtio/virtio-md-pci.h
F: stubs/virtio-md-pci.c

View file

@ -50,8 +50,11 @@ endif
virtio_ss.add(when: 'CONFIG_VHOST_SCSI', if_true: files('vhost-scsi-ccw.c'))
virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-ccw.c'))
virtio_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs-ccw.c'))
virtio_ss.add(when: 'CONFIG_VIRTIO_MD', if_true: files('virtio-ccw-md.c'))
s390x_ss.add_all(when: 'CONFIG_VIRTIO_CCW', if_true: virtio_ss)
s390x_ss.add(when: 'CONFIG_VIRTIO_MD', if_false: files('virtio-ccw-md-stubs.c'))
hw_arch += {'s390x': s390x_ss}
hw_s390x_modules = {}

View file

@ -46,6 +46,8 @@
#include "qapi/visitor.h"
#include "hw/s390x/cpu-topology.h"
#include "kvm/kvm_s390x.h"
#include "hw/virtio/virtio-md-pci.h"
#include "hw/s390x/virtio-ccw-md.h"
#include CONFIG_DEVICES
static Error *pv_mig_blocker;
@ -546,11 +548,39 @@ static void s390_machine_reset(MachineState *machine, ResetType type)
s390_ipl_clear_reset_request();
}
static void s390_machine_device_pre_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) {
virtio_ccw_md_pre_plug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) {
error_setg(errp,
"PCI-attached virtio based memory devices not supported");
}
}
static void s390_machine_device_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
S390CcwMachineState *s390ms = S390_CCW_MACHINE(hotplug_dev);
if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
s390_cpu_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) {
/*
* At this point, the device is realized and set all memdevs mapped, so
* qemu_maxrampagesize() will pick up the page sizes of these memdevs
* as well. Before we plug the device and expose any RAM memory regions
* to the system, make sure we don't exceed the previously set max page
* size. While only relevant for KVM, there is not really any use case
* for this with TCG, so we'll unconditionally reject it.
*/
if (qemu_maxrampagesize() != s390ms->max_pagesize) {
error_setg(errp, "Memory device uses a bigger page size than"
" initial memory");
return;
}
virtio_ccw_md_plug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp);
}
}
@ -560,9 +590,20 @@ static void s390_machine_device_unplug_request(HotplugHandler *hotplug_dev,
if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
error_setg(errp, "CPU hot unplug not supported on this machine");
return;
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) {
virtio_ccw_md_unplug_request(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev),
errp);
}
}
static void s390_machine_device_unplug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW)) {
virtio_ccw_md_unplug(VIRTIO_MD_CCW(dev), MACHINE(hotplug_dev), errp);
}
}
static CpuInstanceProperties s390_cpu_index_to_props(MachineState *ms,
unsigned cpu_index)
{
@ -609,7 +650,9 @@ static const CPUArchIdList *s390_possible_cpu_arch_ids(MachineState *ms)
static HotplugHandler *s390_get_hotplug_handler(MachineState *machine,
DeviceState *dev)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_CCW) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MD_PCI)) {
return HOTPLUG_HANDLER(machine);
}
return NULL;
@ -769,8 +812,10 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data)
mc->possible_cpu_arch_ids = s390_possible_cpu_arch_ids;
/* it is overridden with 'host' cpu *in kvm_arch_init* */
mc->default_cpu_type = S390_CPU_TYPE_NAME("qemu");
hc->pre_plug = s390_machine_device_pre_plug;
hc->plug = s390_machine_device_plug;
hc->unplug_request = s390_machine_device_unplug_request;
hc->unplug = s390_machine_device_unplug;
nc->nmi_monitor_handler = s390_nmi;
mc->default_ram_id = "s390.ram";
mc->default_nic = "virtio-net-ccw";

View file

@ -0,0 +1,24 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/s390x/virtio-ccw-md.h"
void virtio_ccw_md_pre_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
{
error_setg(errp, "virtio based memory devices not supported");
}
void virtio_ccw_md_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
{
error_setg(errp, "virtio based memory devices not supported");
}
void virtio_ccw_md_unplug_request(VirtIOMDCcw *vmd, MachineState *ms,
Error **errp)
{
error_setg(errp, "virtio based memory devices not supported");
}
void virtio_ccw_md_unplug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
{
error_setg(errp, "virtio based memory devices not supported");
}

153
hw/s390x/virtio-ccw-md.c Normal file
View file

@ -0,0 +1,153 @@
/*
* Virtio CCW support for abstract virtio based memory device
*
* Copyright (C) 2024 Red Hat, Inc.
*
* Authors:
* David Hildenbrand <david@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "hw/s390x/virtio-ccw-md.h"
#include "hw/mem/memory-device.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
void virtio_ccw_md_pre_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
{
DeviceState *dev = DEVICE(vmd);
HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
MemoryDeviceState *md = MEMORY_DEVICE(vmd);
Error *local_err = NULL;
if (!bus_handler && dev->hotplugged) {
/*
* Without a bus hotplug handler, we cannot control the plug/unplug
* order. We should never reach this point when hotplugging, but
* better add a safety net.
*/
error_setg(errp, "hotplug of virtio based memory devices not supported"
" on this bus.");
return;
}
/*
* First, see if we can plug this memory device at all. If that
* succeeds, branch of to the actual hotplug handler.
*/
memory_device_pre_plug(md, ms, &local_err);
if (!local_err && bus_handler) {
hotplug_handler_pre_plug(bus_handler, dev, &local_err);
}
error_propagate(errp, local_err);
}
void virtio_ccw_md_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
{
DeviceState *dev = DEVICE(vmd);
HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
MemoryDeviceState *md = MEMORY_DEVICE(vmd);
Error *local_err = NULL;
/*
* Plug the memory device first and then branch off to the actual
* hotplug handler. If that one fails, we can easily undo the memory
* device bits.
*/
memory_device_plug(md, ms);
if (bus_handler) {
hotplug_handler_plug(bus_handler, dev, &local_err);
if (local_err) {
memory_device_unplug(md, ms);
}
}
error_propagate(errp, local_err);
}
void virtio_ccw_md_unplug_request(VirtIOMDCcw *vmd, MachineState *ms,
Error **errp)
{
VirtIOMDCcwClass *vmdc = VIRTIO_MD_CCW_GET_CLASS(vmd);
DeviceState *dev = DEVICE(vmd);
HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
HotplugHandlerClass *hdc;
Error *local_err = NULL;
if (!vmdc->unplug_request_check) {
error_setg(errp,
"this virtio based memory devices cannot be unplugged");
return;
}
if (!bus_handler) {
error_setg(errp, "hotunplug of virtio based memory devices not"
"supported on this bus");
return;
}
vmdc->unplug_request_check(vmd, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
/*
* Forward the async request or turn it into a sync request (handling it
* like qdev_unplug()).
*/
hdc = HOTPLUG_HANDLER_GET_CLASS(bus_handler);
if (hdc->unplug_request) {
hotplug_handler_unplug_request(bus_handler, dev, &local_err);
} else {
virtio_ccw_md_unplug(vmd, ms, &local_err);
if (!local_err) {
object_unparent(OBJECT(dev));
}
}
}
void virtio_ccw_md_unplug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
{
DeviceState *dev = DEVICE(vmd);
HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
MemoryDeviceState *md = MEMORY_DEVICE(vmd);
Error *local_err = NULL;
/* Unplug the memory device while it is still realized. */
memory_device_unplug(md, ms);
if (bus_handler) {
hotplug_handler_unplug(bus_handler, dev, &local_err);
if (local_err) {
/* Not expected to fail ... but still try to recover. */
memory_device_plug(md, ms);
error_propagate(errp, local_err);
return;
}
} else {
/* Very unexpected, but let's just try to do the right thing. */
warn_report("Unexpected unplug of virtio based memory device");
qdev_unrealize(dev);
}
}
static const TypeInfo virtio_ccw_md_info = {
.name = TYPE_VIRTIO_MD_CCW,
.parent = TYPE_VIRTIO_CCW_DEVICE,
.instance_size = sizeof(VirtIOMDCcw),
.class_size = sizeof(VirtIOMDCcwClass),
.abstract = true,
.interfaces = (InterfaceInfo[]) {
{ TYPE_MEMORY_DEVICE },
{ }
},
};
static void virtio_ccw_md_register(void)
{
type_register_static(&virtio_ccw_md_info);
}
type_init(virtio_ccw_md_register)

44
hw/s390x/virtio-ccw-md.h Normal file
View file

@ -0,0 +1,44 @@
/*
* Virtio CCW support for abstract virtio based memory device
*
* Copyright (C) 2024 Red Hat, Inc.
*
* Authors:
* David Hildenbrand <david@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2.
* See the COPYING file in the top-level directory.
*/
#ifndef HW_S390X_VIRTIO_CCW_MD_H
#define HW_S390X_VIRTIO_CCW_MD_H
#include "virtio-ccw.h"
#include "qom/object.h"
/*
* virtio-md-ccw: This extends VirtioCcwDevice.
*/
#define TYPE_VIRTIO_MD_CCW "virtio-md-ccw"
OBJECT_DECLARE_TYPE(VirtIOMDCcw, VirtIOMDCcwClass, VIRTIO_MD_CCW)
struct VirtIOMDCcwClass {
/* private */
VirtIOCCWDeviceClass parent;
/* public */
void (*unplug_request_check)(VirtIOMDCcw *vmd, Error **errp);
};
struct VirtIOMDCcw {
VirtioCcwDevice parent_obj;
};
void virtio_ccw_md_pre_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp);
void virtio_ccw_md_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp);
void virtio_ccw_md_unplug_request(VirtIOMDCcw *vmd, MachineState *ms,
Error **errp);
void virtio_ccw_md_unplug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp);
#endif /* HW_S390X_VIRTIO_CCW_MD_H */

View file

@ -29,6 +29,7 @@ config VIRTIO_MMIO
config VIRTIO_CCW
bool
select VIRTIO
select VIRTIO_MD_SUPPORTED
config VIRTIO_BALLOON
bool