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