aboutsummaryrefslogtreecommitdiff
path: root/hw/virtio/virtio-md-pci.c
blob: 62bfb7920bf0b0b00a9df740ba16f05374bde02a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
 * Abstract virtio based memory device
 *
 * Copyright (C) 2023 Red Hat, Inc.
 *
 * Authors:
 *  David Hildenbrand <david@redhat.com>
 *
 * This work is licensed under the terms of the GNU GPL, version 2.
 * See the COPYING file in the top-level directory.
 */

#include "qemu/osdep.h"
#include "hw/virtio/virtio-md-pci.h"
#include "hw/mem/memory-device.h"
#include "qapi/error.h"
#include "qemu/error-report.h"

void virtio_md_pci_pre_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp)
{
    DeviceState *dev = DEVICE(vmd);
    HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
    MemoryDeviceState *md = MEMORY_DEVICE(vmd);
    Error *local_err = NULL;

    if (!bus_handler && dev->hotplugged) {
        /*
         * Without a bus hotplug handler, we cannot control the plug/unplug
         * order. We should never reach this point when hotplugging on x86,
         * however, better add a safety net.
         */
        error_setg(errp, "hotplug of virtio based memory devices not supported"
                   " on this bus.");
        return;
    }
    /*
     * First, see if we can plug this memory device at all. If that
     * succeeds, branch of to the actual hotplug handler.
     */
    memory_device_pre_plug(md, ms, NULL, &local_err);
    if (!local_err && bus_handler) {
        hotplug_handler_pre_plug(bus_handler, dev, &local_err);
    }
    error_propagate(errp, local_err);
}

void virtio_md_pci_plug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp)
{
    DeviceState *dev = DEVICE(vmd);
    HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
    MemoryDeviceState *md = MEMORY_DEVICE(vmd);
    Error *local_err = NULL;

    /*
     * Plug the memory device first and then branch off to the actual
     * hotplug handler. If that one fails, we can easily undo the memory
     * device bits.
     */
    memory_device_plug(md, ms);
    if (bus_handler) {
        hotplug_handler_plug(bus_handler, dev, &local_err);
        if (local_err) {
            memory_device_unplug(md, ms);
        }
    }
    error_propagate(errp, local_err);
}

void virtio_md_pci_unplug_request(VirtIOMDPCI *vmd, MachineState *ms,
                                  Error **errp)
{
    VirtIOMDPCIClass *vmdc = VIRTIO_MD_PCI_GET_CLASS(vmd);
    DeviceState *dev = DEVICE(vmd);
    HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
    HotplugHandlerClass *hdc;
    Error *local_err = NULL;

    if (!vmdc->unplug_request_check) {
        error_setg(errp, "this virtio based memory devices cannot be unplugged");
        return;
    }

    if (!bus_handler) {
        error_setg(errp, "hotunplug of virtio based memory devices not"
                   "supported on this bus");
        return;
    }

    vmdc->unplug_request_check(vmd, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        return;
    }

    /*
     * Forward the async request or turn it into a sync request (handling it
     * like qdev_unplug()).
     */
    hdc = HOTPLUG_HANDLER_GET_CLASS(bus_handler);
    if (hdc->unplug_request) {
        hotplug_handler_unplug_request(bus_handler, dev, &local_err);
    } else {
        virtio_md_pci_unplug(vmd, ms, &local_err);
        if (!local_err) {
            object_unparent(OBJECT(dev));
        }
    }
}

void virtio_md_pci_unplug(VirtIOMDPCI *vmd, MachineState *ms, Error **errp)
{
    DeviceState *dev = DEVICE(vmd);
    HotplugHandler *bus_handler = qdev_get_bus_hotplug_handler(dev);
    MemoryDeviceState *md = MEMORY_DEVICE(vmd);
    Error *local_err = NULL;

    /* Unplug the memory device while it is still realized. */
    memory_device_unplug(md, ms);

    if (bus_handler) {
        hotplug_handler_unplug(bus_handler, dev, &local_err);
        if (local_err) {
            /* Not expected to fail ... but still try to recover. */
            memory_device_plug(md, ms);
            error_propagate(errp, local_err);
            return;
        }
    } else {
        /* Very unexpected, but let's just try to do the right thing. */
        warn_report("Unexpected unplug of virtio based memory device");
        qdev_unrealize(dev);
    }
}

static const TypeInfo virtio_md_pci_info = {
    .name = TYPE_VIRTIO_MD_PCI,
    .parent = TYPE_VIRTIO_PCI,
    .instance_size = sizeof(VirtIOMDPCI),
    .class_size = sizeof(VirtIOMDPCIClass),
    .abstract = true,
    .interfaces = (InterfaceInfo[]) {
        { TYPE_MEMORY_DEVICE },
        { }
    },
};

static void virtio_md_pci_register(void)
{
    type_register_static(&virtio_md_pci_info);
}
type_init(virtio_md_pci_register)