]>
Commit | Line | Data |
---|---|---|
0d9d4872 DH |
1 | /* |
2 | * QEMU device plug/unplug handling | |
3 | * | |
4 | * Copyright (C) 2019 Red Hat Inc. | |
5 | * | |
6 | * Authors: | |
7 | * David Hildenbrand <[email protected]> | |
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 "qapi/qmp/qdict.h" | |
16 | #include "qapi/qmp/qstring.h" | |
17 | ||
613ebbec | 18 | static void device_del_start(QTestState *qtest, const char *id) |
0d9d4872 | 19 | { |
613ebbec DH |
20 | qtest_qmp_send(qtest, |
21 | "{'execute': 'device_del', 'arguments': { 'id': %s } }", id); | |
22 | } | |
23 | ||
24 | static void device_del_finish(QTestState *qtest) | |
25 | { | |
26 | QDict *resp = qtest_qmp_receive(qtest); | |
0d9d4872 | 27 | |
0d9d4872 DH |
28 | g_assert(qdict_haskey(resp, "return")); |
29 | qobject_unref(resp); | |
30 | } | |
31 | ||
613ebbec DH |
32 | static void device_del_request(QTestState *qtest, const char *id) |
33 | { | |
34 | device_del_start(qtest, id); | |
35 | device_del_finish(qtest); | |
36 | } | |
37 | ||
0d9d4872 DH |
38 | static void system_reset(QTestState *qtest) |
39 | { | |
40 | QDict *resp; | |
41 | ||
42 | resp = qtest_qmp(qtest, "{'execute': 'system_reset'}"); | |
43 | g_assert(qdict_haskey(resp, "return")); | |
44 | qobject_unref(resp); | |
45 | } | |
46 | ||
47 | static void wait_device_deleted_event(QTestState *qtest, const char *id) | |
48 | { | |
49 | QDict *resp, *data; | |
50 | QString *qstr; | |
51 | ||
52 | /* | |
53 | * Other devices might get removed along with the removed device. Skip | |
54 | * these. The device of interest will be the last one. | |
55 | */ | |
56 | for (;;) { | |
57 | resp = qtest_qmp_eventwait_ref(qtest, "DEVICE_DELETED"); | |
58 | data = qdict_get_qdict(resp, "data"); | |
59 | if (!data || !qdict_get(data, "device")) { | |
60 | qobject_unref(resp); | |
61 | continue; | |
62 | } | |
63 | qstr = qobject_to(QString, qdict_get(data, "device")); | |
64 | g_assert(qstr); | |
65 | if (!strcmp(qstring_get_str(qstr), id)) { | |
66 | qobject_unref(resp); | |
67 | break; | |
68 | } | |
69 | qobject_unref(resp); | |
70 | } | |
71 | } | |
72 | ||
73 | static void test_pci_unplug_request(void) | |
74 | { | |
75 | QTestState *qtest = qtest_initf("-device virtio-mouse-pci,id=dev0"); | |
76 | ||
77 | /* | |
78 | * Request device removal. As the guest is not running, the request won't | |
79 | * be processed. However during system reset, the removal will be | |
80 | * handled, removing the device. | |
81 | */ | |
82 | device_del_request(qtest, "dev0"); | |
83 | system_reset(qtest); | |
84 | wait_device_deleted_event(qtest, "dev0"); | |
85 | ||
86 | qtest_quit(qtest); | |
87 | } | |
88 | ||
613ebbec DH |
89 | static void test_ccw_unplug(void) |
90 | { | |
91 | QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0"); | |
92 | ||
93 | /* | |
94 | * The DEVICE_DELETED events will be sent before the command | |
95 | * completes. | |
96 | */ | |
97 | device_del_start(qtest, "dev0"); | |
98 | wait_device_deleted_event(qtest, "dev0"); | |
99 | device_del_finish(qtest); | |
100 | ||
101 | qtest_quit(qtest); | |
102 | } | |
103 | ||
c76480e5 DH |
104 | static void test_spapr_cpu_unplug_request(void) |
105 | { | |
106 | QTestState *qtest; | |
107 | ||
108 | qtest = qtest_initf("-cpu power9_v2.0 -smp 1,maxcpus=2 " | |
109 | "-device power9_v2.0-spapr-cpu-core,core-id=1,id=dev0"); | |
110 | ||
111 | /* similar to test_pci_unplug_request */ | |
112 | device_del_request(qtest, "dev0"); | |
113 | system_reset(qtest); | |
114 | wait_device_deleted_event(qtest, "dev0"); | |
115 | ||
116 | qtest_quit(qtest); | |
117 | } | |
118 | ||
36880704 DH |
119 | static void test_spapr_memory_unplug_request(void) |
120 | { | |
121 | QTestState *qtest; | |
122 | ||
123 | qtest = qtest_initf("-m 256M,slots=1,maxmem=768M " | |
124 | "-object memory-backend-ram,id=mem0,size=512M " | |
125 | "-device pc-dimm,id=dev0,memdev=mem0"); | |
126 | ||
127 | /* similar to test_pci_unplug_request */ | |
128 | device_del_request(qtest, "dev0"); | |
129 | system_reset(qtest); | |
130 | wait_device_deleted_event(qtest, "dev0"); | |
131 | ||
132 | qtest_quit(qtest); | |
133 | } | |
134 | ||
9bcb5b29 GK |
135 | static void test_spapr_phb_unplug_request(void) |
136 | { | |
137 | QTestState *qtest; | |
138 | ||
139 | qtest = qtest_initf("-device spapr-pci-host-bridge,index=1,id=dev0"); | |
140 | ||
141 | /* similar to test_pci_unplug_request */ | |
142 | device_del_request(qtest, "dev0"); | |
143 | system_reset(qtest); | |
144 | wait_device_deleted_event(qtest, "dev0"); | |
145 | ||
146 | qtest_quit(qtest); | |
147 | } | |
148 | ||
0d9d4872 DH |
149 | int main(int argc, char **argv) |
150 | { | |
613ebbec DH |
151 | const char *arch = qtest_get_arch(); |
152 | ||
0d9d4872 DH |
153 | g_test_init(&argc, &argv, NULL); |
154 | ||
155 | /* | |
156 | * We need a system that will process unplug requests during system resets | |
157 | * and does not do PCI surprise removal. This holds for x86 ACPI, | |
158 | * s390x and spapr. | |
159 | */ | |
160 | qtest_add_func("/device-plug/pci-unplug-request", | |
161 | test_pci_unplug_request); | |
162 | ||
613ebbec DH |
163 | if (!strcmp(arch, "s390x")) { |
164 | qtest_add_func("/device-plug/ccw-unplug", | |
165 | test_ccw_unplug); | |
166 | } | |
167 | ||
c76480e5 DH |
168 | if (!strcmp(arch, "ppc64")) { |
169 | qtest_add_func("/device-plug/spapr-cpu-unplug-request", | |
170 | test_spapr_cpu_unplug_request); | |
36880704 DH |
171 | qtest_add_func("/device-plug/spapr-memory-unplug-request", |
172 | test_spapr_memory_unplug_request); | |
9bcb5b29 GK |
173 | qtest_add_func("/device-plug/spapr-phb-unplug-request", |
174 | test_spapr_phb_unplug_request); | |
c76480e5 DH |
175 | } |
176 | ||
0d9d4872 DH |
177 | return g_test_run(); |
178 | } |