]>
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 <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 "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 | ||
0d9d4872 DH |
104 | int main(int argc, char **argv) |
105 | { | |
613ebbec DH |
106 | const char *arch = qtest_get_arch(); |
107 | ||
0d9d4872 DH |
108 | g_test_init(&argc, &argv, NULL); |
109 | ||
110 | /* | |
111 | * We need a system that will process unplug requests during system resets | |
112 | * and does not do PCI surprise removal. This holds for x86 ACPI, | |
113 | * s390x and spapr. | |
114 | */ | |
115 | qtest_add_func("/device-plug/pci-unplug-request", | |
116 | test_pci_unplug_request); | |
117 | ||
613ebbec DH |
118 | if (!strcmp(arch, "s390x")) { |
119 | qtest_add_func("/device-plug/ccw-unplug", | |
120 | test_ccw_unplug); | |
121 | } | |
122 | ||
0d9d4872 DH |
123 | return g_test_run(); |
124 | } |