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
virtio_md_pci_pre_plug(VirtIOMDPCI * vmd,MachineState * ms,Error ** errp)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, &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
virtio_md_pci_plug(VirtIOMDPCI * vmd,MachineState * ms,Error ** errp)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
virtio_md_pci_unplug_request(VirtIOMDPCI * vmd,MachineState * ms,Error ** errp)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
virtio_md_pci_unplug(VirtIOMDPCI * vmd,MachineState * ms,Error ** errp)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
virtio_md_pci_register(void)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