master
c 250 lines 6.84 KB
Raw
1 /*
2 * QEMU device plug/unplug handling
3 *
4 * Copyright (C) 2019 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 or later.
10 * See the COPYING file in the top-level directory.
11 */
12
13 #include "qemu/osdep.h"
14 #include "libqtest.h"
15 #include "qobject/qdict.h"
16 #include "qobject/qstring.h"
17
18 static void wait_device_deleted_event(QTestState *qtest, const char *id)
19 {
20 QDict *resp, *data;
21 QString *qstr;
22
23 /*
24 * Other devices might get removed along with the removed device. Skip
25 * these. The device of interest will be the last one.
26 */
27 for (;;) {
28 resp = qtest_qmp_eventwait_ref(qtest, "DEVICE_DELETED");
29 data = qdict_get_qdict(resp, "data");
30 if (!data || !qdict_get(data, "device")) {
31 qobject_unref(resp);
32 continue;
33 }
34 qstr = qobject_to(QString, qdict_get(data, "device"));
35 g_assert(qstr);
36 if (!strcmp(qstring_get_str(qstr), id)) {
37 qobject_unref(resp);
38 break;
39 }
40 qobject_unref(resp);
41 }
42 }
43
44 static void process_device_remove(QTestState *qtest, const char *id)
45 {
46 /*
47 * Request device removal. As the guest is not running, the request won't
48 * be processed. However during system reset, the removal will be
49 * handled, removing the device.
50 */
51 qtest_qmp_device_del_send(qtest, id);
52 qtest_system_reset_nowait(qtest);
53 wait_device_deleted_event(qtest, id);
54 }
55
56 static void test_pci_unplug_request(void)
57 {
58 QTestState *qtest;
59 const char *arch = qtest_get_arch();
60 const char *machine_addition = "";
61
62 if (!qtest_has_device("virtio-mouse-pci")) {
63 g_test_skip("Device virtio-mouse-pci not available");
64 return;
65 }
66
67 if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
68 if (!qtest_has_machine("pc")) {
69 g_test_skip("Machine 'pc' is not available");
70 return;
71 }
72 machine_addition = "-machine pc";
73 }
74
75 qtest = qtest_initf("%s -device virtio-mouse-pci,id=dev0",
76 machine_addition);
77
78 process_device_remove(qtest, "dev0");
79
80 qtest_quit(qtest);
81 }
82
83 static void test_q35_pci_unplug_request(void)
84 {
85 QTestState *qtest;
86
87 if (!qtest_has_device("virtio-mouse-pci")) {
88 g_test_skip("Device virtio-mouse-pci not available");
89 return;
90 }
91
92 qtest = qtest_initf("-machine q35 "
93 "-device pcie-root-port,id=p1 "
94 "-device pcie-pci-bridge,bus=p1,id=b1 "
95 "-device virtio-mouse-pci,bus=b1,id=dev0");
96
97 process_device_remove(qtest, "dev0");
98
99 qtest_quit(qtest);
100 }
101
102 static void test_pci_unplug_json_request(void)
103 {
104 QTestState *qtest;
105 const char *arch = qtest_get_arch();
106 const char *machine_addition = "";
107
108 if (!qtest_has_device("virtio-mouse-pci")) {
109 g_test_skip("Device virtio-mouse-pci not available");
110 return;
111 }
112
113 if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
114 if (!qtest_has_machine("pc")) {
115 g_test_skip("Machine 'pc' is not available");
116 return;
117 }
118 machine_addition = "-machine pc";
119 }
120
121 qtest = qtest_initf(
122 "%s -device \"{'driver': 'virtio-mouse-pci', 'id': 'dev0'}\"",
123 machine_addition);
124
125 process_device_remove(qtest, "dev0");
126
127 qtest_quit(qtest);
128 }
129
130 static void test_q35_pci_unplug_json_request(void)
131 {
132 QTestState *qtest;
133 const char *port = "-device \"{'driver': 'pcie-root-port', "
134 "'id': 'p1'}\"";
135
136 const char *bridge = "-device \"{'driver': 'pcie-pci-bridge', "
137 "'id': 'b1', "
138 "'bus': 'p1'}\"";
139
140 const char *device = "-device \"{'driver': 'virtio-mouse-pci', "
141 "'bus': 'b1', "
142 "'id': 'dev0'}\"";
143
144 if (!qtest_has_device("virtio-mouse-pci")) {
145 g_test_skip("Device virtio-mouse-pci not available");
146 return;
147 }
148
149 qtest = qtest_initf("-machine q35 %s %s %s", port, bridge, device);
150
151 process_device_remove(qtest, "dev0");
152
153 qtest_quit(qtest);
154 }
155
156 static void test_ccw_unplug(void)
157 {
158 QTestState *qtest;
159
160 if (!qtest_has_device("virtio-balloon-ccw")) {
161 g_test_skip("Device virtio-balloon-ccw not available");
162 return;
163 }
164
165 qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0");
166
167 qtest_qmp_device_del_send(qtest, "dev0");
168 wait_device_deleted_event(qtest, "dev0");
169
170 qtest_quit(qtest);
171 }
172
173 static void test_spapr_cpu_unplug_request(void)
174 {
175 QTestState *qtest;
176
177 qtest = qtest_initf("-cpu power9_v2.2 -smp 1,maxcpus=2 "
178 "-device power9_v2.2-spapr-cpu-core,core-id=1,id=dev0");
179
180 /* similar to test_pci_unplug_request */
181 process_device_remove(qtest, "dev0");
182
183 qtest_quit(qtest);
184 }
185
186 static void test_spapr_memory_unplug_request(void)
187 {
188 QTestState *qtest;
189
190 qtest = qtest_initf("-m 256M,slots=1,maxmem=768M "
191 "-object memory-backend-ram,id=mem0,size=512M "
192 "-device pc-dimm,id=dev0,memdev=mem0");
193
194 /* similar to test_pci_unplug_request */
195 process_device_remove(qtest, "dev0");
196
197 qtest_quit(qtest);
198 }
199
200 static void test_spapr_phb_unplug_request(void)
201 {
202 QTestState *qtest;
203
204 qtest = qtest_initf("-device spapr-pci-host-bridge,index=1,id=dev0");
205
206 /* similar to test_pci_unplug_request */
207 process_device_remove(qtest, "dev0");
208
209 qtest_quit(qtest);
210 }
211
212 int main(int argc, char **argv)
213 {
214 const char *arch = qtest_get_arch();
215
216 g_test_init(&argc, &argv, NULL);
217
218 /*
219 * We need a system that will process unplug requests during system resets
220 * and does not do PCI surprise removal. This holds for x86 ACPI,
221 * s390x and spapr.
222 */
223 qtest_add_func("/device-plug/pci-unplug-request",
224 test_pci_unplug_request);
225 qtest_add_func("/device-plug/pci-unplug-json-request",
226 test_pci_unplug_json_request);
227
228 if (!strcmp(arch, "s390x")) {
229 qtest_add_func("/device-plug/ccw-unplug",
230 test_ccw_unplug);
231 }
232
233 if (!strcmp(arch, "ppc64")) {
234 qtest_add_func("/device-plug/spapr-cpu-unplug-request",
235 test_spapr_cpu_unplug_request);
236 qtest_add_func("/device-plug/spapr-memory-unplug-request",
237 test_spapr_memory_unplug_request);
238 qtest_add_func("/device-plug/spapr-phb-unplug-request",
239 test_spapr_phb_unplug_request);
240 }
241
242 if (!strcmp(arch, "x86_64") && qtest_has_machine("q35")) {
243 qtest_add_func("/device-plug/q35-pci-unplug-request",
244 test_q35_pci_unplug_request);
245 qtest_add_func("/device-plug/q35-pci-unplug-json-request",
246 test_q35_pci_unplug_json_request);
247 }
248
249 return g_test_run();
250 }