1 /* 2 * Abstract virtio based memory device 3 * 4 * Copyright (C) 2023 Red Hat, Inc. 5 * 6 * Authors: 7 * David Hildenbrand <david@redhat.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2. 10 * See the COPYING file in the top-level directory. 11 */ 12 13 #include "qemu/osdep.h" 14 #include "hw/virtio/virtio-md-pci.h" 15 #include "hw/mem/memory-device.h" 16 #include "qapi/error.h" 17 #include "qemu/error-report.h" 18 19 void virtio_md_pci_pre_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) 20 { 21 DeviceState *dev = DEVICE(vmd); 22 HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); 23 MemoryDeviceState *md = MEMORY_DEVICE(vmd); 24 Error *local_err = NULL; 25 26 if (!bus_handler && dev->hotplugged) { 27 /* 28 * Without a bus hotplug handler, we cannot control the plug/unplug 29 * order. We should never reach this point when hotplugging on x86, 30 * however, better add a safety net. 31 */ 32 error_setg(errp, "hotplug of virtio based memory devices not supported" 33 " on this bus."); 34 return; 35 } 36 /* 37 * First, see if we can plug this memory device at all. If that 38 * succeeds, branch of to the actual hotplug handler. 39 */ 40 memory_device_pre_plug(md, ms, NULL, &local_err); 41 if (!local_err && bus_handler) { 42 hotplug_handler_pre_plug(bus_handler, dev, &local_err); 43 } 44 error_propagate(errp, local_err); 45 } 46 47 void virtio_md_pci_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) 48 { 49 DeviceState *dev = DEVICE(vmd); 50 HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); 51 MemoryDeviceState *md = MEMORY_DEVICE(vmd); 52 Error *local_err = NULL; 53 54 /* 55 * Plug the memory device first and then branch off to the actual 56 * hotplug handler. If that one fails, we can easily undo the memory 57 * device bits. 58 */ 59 memory_device_plug(md, ms); 60 if (bus_handler) { 61 hotplug_handler_plug(bus_handler, dev, &local_err); 62 if (local_err) { 63 memory_device_unplug(md, ms); 64 } 65 } 66 error_propagate(errp, local_err); 67 } 68 69 void virtio_md_pci_unplug_request(VirtIOMDPCI *vmd, MachineState *ms, 70 Error **errp) 71 { 72 VirtIOMDPCIClass *vmdc = VIRTIO_MD_PCI_GET_CLASS(vmd); 73 DeviceState *dev = DEVICE(vmd); 74 HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); 75 HotplugHandlerClass *hdc; 76 Error *local_err = NULL; 77 78 if (!vmdc->unplug_request_check) { 79 error_setg(errp, "this virtio based memory devices cannot be unplugged"); 80 return; 81 } 82 83 if (!bus_handler) { 84 error_setg(errp, "hotunplug of virtio based memory devices not" 85 "supported on this bus"); 86 return; 87 } 88 89 vmdc->unplug_request_check(vmd, &local_err); 90 if (local_err) { 91 error_propagate(errp, local_err); 92 return; 93 } 94 95 /* 96 * Forward the async request or turn it into a sync request (handling it 97 * like qdev_unplug()). 98 */ 99 hdc = HOTPLUG_HANDLER_GET_CLASS(bus_handler); 100 if (hdc->unplug_request) { 101 hotplug_handler_unplug_request(bus_handler, dev, &local_err); 102 } else { 103 virtio_md_pci_unplug(vmd, ms, &local_err); 104 if (!local_err) { 105 object_unparent(OBJECT(dev)); 106 } 107 } 108 } 109 110 void virtio_md_pci_unplug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp) 111 { 112 DeviceState *dev = DEVICE(vmd); 113 HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev); 114 MemoryDeviceState *md = MEMORY_DEVICE(vmd); 115 Error *local_err = NULL; 116 117 /* Unplug the memory device while it is still realized. */ 118 memory_device_unplug(md, ms); 119 120 if (bus_handler) { 121 hotplug_handler_unplug(bus_handler, dev, &local_err); 122 if (local_err) { 123 /* Not expected to fail ... but still try to recover. */ 124 memory_device_plug(md, ms); 125 error_propagate(errp, local_err); 126 return; 127 } 128 } else { 129 /* Very unexpected, but let's just try to do the right thing. */ 130 warn_report("Unexpected unplug of virtio based memory device"); 131 qdev_unrealize(dev); 132 } 133 } 134 135 static const TypeInfo virtio_md_pci_info = { 136 .name = TYPE_VIRTIO_MD_PCI, 137 .parent = TYPE_VIRTIO_PCI, 138 .instance_size = sizeof(VirtIOMDPCI), 139 .class_size = sizeof(VirtIOMDPCIClass), 140 .abstract = true, 141 .interfaces = (InterfaceInfo[]) { 142 { TYPE_MEMORY_DEVICE }, 143 { } 144 }, 145 }; 146 147 static void virtio_md_pci_register(void) 148 { 149 type_register_static(&virtio_md_pci_info); 150 } 151 type_init(virtio_md_pci_register) 152