1 /*
2 * Virtio CCW support for abstract virtio based memory device
3 *
4 * Copyright (C) 2024 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/s390x/virtio-ccw-md.h"
15 #include "hw/mem/memory-device.h"
16 #include "qapi/error.h"
17 #include "qemu/error-report.h"
18
virtio_ccw_md_pre_plug(VirtIOMDCcw * vmd,MachineState * ms,Error ** errp)19 void virtio_ccw_md_pre_plug(VirtIOMDCcw *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, but
30 * 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 /*
38 * First, see if we can plug this memory device at all. If that
39 * succeeds, branch of to the actual hotplug handler.
40 */
41 memory_device_pre_plug(md, ms, &local_err);
42 if (!local_err && bus_handler) {
43 hotplug_handler_pre_plug(bus_handler, dev, &local_err);
44 }
45 error_propagate(errp, local_err);
46 }
47
virtio_ccw_md_plug(VirtIOMDCcw * vmd,MachineState * ms,Error ** errp)48 void virtio_ccw_md_plug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
49 {
50 DeviceState *dev = DEVICE(vmd);
51 HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
52 MemoryDeviceState *md = MEMORY_DEVICE(vmd);
53 Error *local_err = NULL;
54
55 /*
56 * Plug the memory device first and then branch off to the actual
57 * hotplug handler. If that one fails, we can easily undo the memory
58 * device bits.
59 */
60 memory_device_plug(md, ms);
61 if (bus_handler) {
62 hotplug_handler_plug(bus_handler, dev, &local_err);
63 if (local_err) {
64 memory_device_unplug(md, ms);
65 }
66 }
67 error_propagate(errp, local_err);
68 }
69
virtio_ccw_md_unplug_request(VirtIOMDCcw * vmd,MachineState * ms,Error ** errp)70 void virtio_ccw_md_unplug_request(VirtIOMDCcw *vmd, MachineState *ms,
71 Error **errp)
72 {
73 VirtIOMDCcwClass *vmdc = VIRTIO_MD_CCW_GET_CLASS(vmd);
74 DeviceState *dev = DEVICE(vmd);
75 HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
76 HotplugHandlerClass *hdc;
77 Error *local_err = NULL;
78
79 if (!vmdc->unplug_request_check) {
80 error_setg(errp,
81 "this virtio based memory devices cannot be unplugged");
82 return;
83 }
84
85 if (!bus_handler) {
86 error_setg(errp, "hotunplug of virtio based memory devices not"
87 "supported on this bus");
88 return;
89 }
90
91 vmdc->unplug_request_check(vmd, &local_err);
92 if (local_err) {
93 error_propagate(errp, local_err);
94 return;
95 }
96
97 /*
98 * Forward the async request or turn it into a sync request (handling it
99 * like qdev_unplug()).
100 */
101 hdc = HOTPLUG_HANDLER_GET_CLASS(bus_handler);
102 if (hdc->unplug_request) {
103 hotplug_handler_unplug_request(bus_handler, dev, &local_err);
104 } else {
105 virtio_ccw_md_unplug(vmd, ms, &local_err);
106 if (!local_err) {
107 object_unparent(OBJECT(dev));
108 }
109 }
110 }
111
virtio_ccw_md_unplug(VirtIOMDCcw * vmd,MachineState * ms,Error ** errp)112 void virtio_ccw_md_unplug(VirtIOMDCcw *vmd, MachineState *ms, Error **errp)
113 {
114 DeviceState *dev = DEVICE(vmd);
115 HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
116 MemoryDeviceState *md = MEMORY_DEVICE(vmd);
117 Error *local_err = NULL;
118
119 /* Unplug the memory device while it is still realized. */
120 memory_device_unplug(md, ms);
121
122 if (bus_handler) {
123 hotplug_handler_unplug(bus_handler, dev, &local_err);
124 if (local_err) {
125 /* Not expected to fail ... but still try to recover. */
126 memory_device_plug(md, ms);
127 error_propagate(errp, local_err);
128 return;
129 }
130 } else {
131 /* Very unexpected, but let's just try to do the right thing. */
132 warn_report("Unexpected unplug of virtio based memory device");
133 qdev_unrealize(dev);
134 }
135 }
136
137 static const TypeInfo virtio_ccw_md_info = {
138 .name = TYPE_VIRTIO_MD_CCW,
139 .parent = TYPE_VIRTIO_CCW_DEVICE,
140 .instance_size = sizeof(VirtIOMDCcw),
141 .class_size = sizeof(VirtIOMDCcwClass),
142 .abstract = true,
143 .interfaces = (const InterfaceInfo[]) {
144 { TYPE_MEMORY_DEVICE },
145 { }
146 },
147 };
148
virtio_ccw_md_register(void)149 static void virtio_ccw_md_register(void)
150 {
151 type_register_static(&virtio_ccw_md_info);
152 }
153 type_init(virtio_ccw_md_register)
154