xref: /openbmc/qemu/hw/virtio/virtio-md-pci.c (revision 05caa062)
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, &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