1 | The following changes since commit 812b835fb4d23dd108b2f9802158472d50b73579: | 1 | The following changes since commit 6c769690ac845fa62642a5f93b4e4bd906adab95: |
---|---|---|---|
2 | 2 | ||
3 | Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2019-05-07' into staging (2019-05-09 16:31:12 +0100) | 3 | Merge remote-tracking branch 'remotes/vsementsov/tags/pull-simplebench-2021-05-04' into staging (2021-05-21 12:02:34 +0100) |
4 | 4 | ||
5 | are available in the Git repository at: | 5 | are available in the Git repository at: |
6 | 6 | ||
7 | https://github.com/stefanha/qemu.git tags/block-pull-request | 7 | https://gitlab.com/stefanha/qemu.git tags/block-pull-request |
8 | 8 | ||
9 | for you to fetch changes up to e84125761f78919fe63616d9888ea45e72dc956f: | 9 | for you to fetch changes up to 0a6f0c76a030710780ce10d6347a70f098024d21: |
10 | 10 | ||
11 | docs: add Security chapter to the documentation (2019-05-10 10:53:52 +0100) | 11 | coroutine-sleep: introduce qemu_co_sleep (2021-05-21 18:22:33 +0100) |
12 | 12 | ||
13 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
14 | Pull request | 14 | Pull request |
15 | 15 | ||
16 | (Resent due to an email preparation mistake.) | ||
17 | |||
16 | ---------------------------------------------------------------- | 18 | ---------------------------------------------------------------- |
17 | 19 | ||
18 | Andrey Shinkevich (1): | 20 | Paolo Bonzini (6): |
19 | block/io.c: fix for the allocation failure | 21 | coroutine-sleep: use a stack-allocated timer |
22 | coroutine-sleep: disallow NULL QemuCoSleepState** argument | ||
23 | coroutine-sleep: allow qemu_co_sleep_wake that wakes nothing | ||
24 | coroutine-sleep: move timer out of QemuCoSleepState | ||
25 | coroutine-sleep: replace QemuCoSleepState pointer with struct in the | ||
26 | API | ||
27 | coroutine-sleep: introduce qemu_co_sleep | ||
20 | 28 | ||
21 | Jules Irenge (3): | 29 | Philippe Mathieu-Daudé (1): |
22 | util/readline: add a space to fix errors by checkpatch tool | 30 | bitops.h: Improve find_xxx_bit() documentation |
23 | util: readline: replace tab indent by four spaces to fix checkpatch | ||
24 | errors | ||
25 | util/readline: Add braces to fix checkpatch errors | ||
26 | 31 | ||
27 | Nikita Alekseev (1): | 32 | Zenghui Yu (1): |
28 | block: Add coroutine_fn to bdrv_check_co_entry | 33 | multi-process: Initialize variables declared with g_auto* |
29 | 34 | ||
30 | Paolo Bonzini (1): | 35 | include/qemu/bitops.h | 15 ++++++-- |
31 | aio-posix: ensure poll mode is left when aio_notify is called | 36 | include/qemu/coroutine.h | 27 ++++++++----- |
32 | 37 | block/block-copy.c | 10 ++--- | |
33 | Stefan Hajnoczi (2): | 38 | block/nbd.c | 14 +++---- |
34 | docs: add Secure Coding Practices to developer docs | 39 | hw/remote/memory.c | 5 +-- |
35 | docs: add Security chapter to the documentation | 40 | hw/remote/proxy.c | 3 +- |
36 | 41 | util/qemu-coroutine-sleep.c | 75 +++++++++++++++++++------------------ | |
37 | Makefile | 2 +- | 42 | 7 files changed, 79 insertions(+), 70 deletions(-) |
38 | block.c | 2 +- | ||
39 | block/io.c | 2 +- | ||
40 | util/aio-posix.c | 12 +- | ||
41 | util/readline.c | 174 ++++++++++++++----------- | ||
42 | docs/devel/index.rst | 1 + | ||
43 | docs/devel/secure-coding-practices.rst | 106 +++++++++++++++ | ||
44 | docs/security.texi | 131 +++++++++++++++++++ | ||
45 | qemu-doc.texi | 3 + | ||
46 | 9 files changed, 347 insertions(+), 86 deletions(-) | ||
47 | create mode 100644 docs/devel/secure-coding-practices.rst | ||
48 | create mode 100644 docs/security.texi | ||
49 | 43 | ||
50 | -- | 44 | -- |
51 | 2.21.0 | 45 | 2.31.1 |
52 | 46 | ||
53 | diff view generated by jsdifflib |
1 | This new chapter in the QEMU documentation covers the security | 1 | From: Zenghui Yu <yuzenghui@huawei.com> |
---|---|---|---|
2 | requirements that QEMU is designed to meet and principles for securely | ||
3 | deploying QEMU. | ||
4 | 2 | ||
5 | It is just a starting point that can be extended in the future with more | 3 | Quote docs/devel/style.rst (section "Automatic memory deallocation"): |
6 | information. | ||
7 | 4 | ||
8 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 5 | * Variables declared with g_auto* MUST always be initialized, |
9 | Acked-by: Stefano Garzarella <sgarzare@redhat.com> | 6 | otherwise the cleanup function will use uninitialized stack memory |
10 | Reviewed-by: Alex Bennée <alex.bennee@linaro.org> | 7 | |
8 | Initialize @name properly to get rid of the compilation error (using | ||
9 | gcc-7.3.0 on CentOS): | ||
10 | |||
11 | ../hw/remote/proxy.c: In function 'pci_proxy_dev_realize': | ||
12 | /usr/include/glib-2.0/glib/glib-autocleanups.h:28:3: error: 'name' may be used uninitialized in this function [-Werror=maybe-uninitialized] | ||
13 | g_free (*pp); | ||
14 | ^~~~~~~~~~~~ | ||
15 | ../hw/remote/proxy.c:350:30: note: 'name' was declared here | ||
16 | g_autofree char *name; | ||
17 | ^~~~ | ||
18 | |||
19 | Signed-off-by: Zenghui Yu <yuzenghui@huawei.com> | ||
20 | Reviewed-by: Jagannathan Raman <jag.raman@oracle.com> | ||
11 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | 21 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> |
12 | Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> | 22 | Reviewed-by: Miroslav Rezanina <mrezanin@redhat.com> |
13 | Reviewed-by: Li Qiang <liq3ea@gmail.com> | 23 | Message-id: 20210312112143.1369-1-yuzenghui@huawei.com |
14 | Message-id: 20190509121820.16294-3-stefanha@redhat.com | ||
15 | Message-Id: <20190509121820.16294-3-stefanha@redhat.com> | ||
16 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 24 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> |
17 | --- | 25 | --- |
18 | Makefile | 2 +- | 26 | hw/remote/memory.c | 5 ++--- |
19 | docs/security.texi | 131 +++++++++++++++++++++++++++++++++++++++++++++ | 27 | hw/remote/proxy.c | 3 +-- |
20 | qemu-doc.texi | 3 ++ | 28 | 2 files changed, 3 insertions(+), 5 deletions(-) |
21 | 3 files changed, 135 insertions(+), 1 deletion(-) | ||
22 | create mode 100644 docs/security.texi | ||
23 | 29 | ||
24 | diff --git a/Makefile b/Makefile | 30 | diff --git a/hw/remote/memory.c b/hw/remote/memory.c |
25 | index XXXXXXX..XXXXXXX 100644 | 31 | index XXXXXXX..XXXXXXX 100644 |
26 | --- a/Makefile | 32 | --- a/hw/remote/memory.c |
27 | +++ b/Makefile | 33 | +++ b/hw/remote/memory.c |
28 | @@ -XXX,XX +XXX,XX @@ qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \ | 34 | @@ -XXX,XX +XXX,XX @@ void remote_sysmem_reconfig(MPQemuMsg *msg, Error **errp) |
29 | qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \ | 35 | |
30 | qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ | 36 | remote_sysmem_reset(); |
31 | qemu-monitor-info.texi docs/qemu-block-drivers.texi \ | 37 | |
32 | - docs/qemu-cpu-models.texi | 38 | - for (region = 0; region < msg->num_fds; region++) { |
33 | + docs/qemu-cpu-models.texi docs/security.texi | 39 | - g_autofree char *name; |
34 | 40 | + for (region = 0; region < msg->num_fds; region++, suffix++) { | |
35 | docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \ | 41 | + g_autofree char *name = g_strdup_printf("remote-mem-%u", suffix); |
36 | docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \ | 42 | subregion = g_new(MemoryRegion, 1); |
37 | diff --git a/docs/security.texi b/docs/security.texi | 43 | - name = g_strdup_printf("remote-mem-%u", suffix++); |
38 | new file mode 100644 | 44 | memory_region_init_ram_from_fd(subregion, NULL, |
39 | index XXXXXXX..XXXXXXX | 45 | name, sysmem_info->sizes[region], |
40 | --- /dev/null | 46 | true, msg->fds[region], |
41 | +++ b/docs/security.texi | 47 | diff --git a/hw/remote/proxy.c b/hw/remote/proxy.c |
42 | @@ -XXX,XX +XXX,XX @@ | ||
43 | +@node Security | ||
44 | +@chapter Security | ||
45 | + | ||
46 | +@section Overview | ||
47 | + | ||
48 | +This chapter explains the security requirements that QEMU is designed to meet | ||
49 | +and principles for securely deploying QEMU. | ||
50 | + | ||
51 | +@section Security Requirements | ||
52 | + | ||
53 | +QEMU supports many different use cases, some of which have stricter security | ||
54 | +requirements than others. The community has agreed on the overall security | ||
55 | +requirements that users may depend on. These requirements define what is | ||
56 | +considered supported from a security perspective. | ||
57 | + | ||
58 | +@subsection Virtualization Use Case | ||
59 | + | ||
60 | +The virtualization use case covers cloud and virtual private server (VPS) | ||
61 | +hosting, as well as traditional data center and desktop virtualization. These | ||
62 | +use cases rely on hardware virtualization extensions to execute guest code | ||
63 | +safely on the physical CPU at close-to-native speed. | ||
64 | + | ||
65 | +The following entities are untrusted, meaning that they may be buggy or | ||
66 | +malicious: | ||
67 | + | ||
68 | +@itemize | ||
69 | +@item Guest | ||
70 | +@item User-facing interfaces (e.g. VNC, SPICE, WebSocket) | ||
71 | +@item Network protocols (e.g. NBD, live migration) | ||
72 | +@item User-supplied files (e.g. disk images, kernels, device trees) | ||
73 | +@item Passthrough devices (e.g. PCI, USB) | ||
74 | +@end itemize | ||
75 | + | ||
76 | +Bugs affecting these entities are evaluated on whether they can cause damage in | ||
77 | +real-world use cases and treated as security bugs if this is the case. | ||
78 | + | ||
79 | +@subsection Non-virtualization Use Case | ||
80 | + | ||
81 | +The non-virtualization use case covers emulation using the Tiny Code Generator | ||
82 | +(TCG). In principle the TCG and device emulation code used in conjunction with | ||
83 | +the non-virtualization use case should meet the same security requirements as | ||
84 | +the virtualization use case. However, for historical reasons much of the | ||
85 | +non-virtualization use case code was not written with these security | ||
86 | +requirements in mind. | ||
87 | + | ||
88 | +Bugs affecting the non-virtualization use case are not considered security | ||
89 | +bugs at this time. Users with non-virtualization use cases must not rely on | ||
90 | +QEMU to provide guest isolation or any security guarantees. | ||
91 | + | ||
92 | +@section Architecture | ||
93 | + | ||
94 | +This section describes the design principles that ensure the security | ||
95 | +requirements are met. | ||
96 | + | ||
97 | +@subsection Guest Isolation | ||
98 | + | ||
99 | +Guest isolation is the confinement of guest code to the virtual machine. When | ||
100 | +guest code gains control of execution on the host this is called escaping the | ||
101 | +virtual machine. Isolation also includes resource limits such as throttling of | ||
102 | +CPU, memory, disk, or network. Guests must be unable to exceed their resource | ||
103 | +limits. | ||
104 | + | ||
105 | +QEMU presents an attack surface to the guest in the form of emulated devices. | ||
106 | +The guest must not be able to gain control of QEMU. Bugs in emulated devices | ||
107 | +could allow malicious guests to gain code execution in QEMU. At this point the | ||
108 | +guest has escaped the virtual machine and is able to act in the context of the | ||
109 | +QEMU process on the host. | ||
110 | + | ||
111 | +Guests often interact with other guests and share resources with them. A | ||
112 | +malicious guest must not gain control of other guests or access their data. | ||
113 | +Disk image files and network traffic must be protected from other guests unless | ||
114 | +explicitly shared between them by the user. | ||
115 | + | ||
116 | +@subsection Principle of Least Privilege | ||
117 | + | ||
118 | +The principle of least privilege states that each component only has access to | ||
119 | +the privileges necessary for its function. In the case of QEMU this means that | ||
120 | +each process only has access to resources belonging to the guest. | ||
121 | + | ||
122 | +The QEMU process should not have access to any resources that are inaccessible | ||
123 | +to the guest. This way the guest does not gain anything by escaping into the | ||
124 | +QEMU process since it already has access to those same resources from within | ||
125 | +the guest. | ||
126 | + | ||
127 | +Following the principle of least privilege immediately fulfills guest isolation | ||
128 | +requirements. For example, guest A only has access to its own disk image file | ||
129 | +@code{a.img} and not guest B's disk image file @code{b.img}. | ||
130 | + | ||
131 | +In reality certain resources are inaccessible to the guest but must be | ||
132 | +available to QEMU to perform its function. For example, host system calls are | ||
133 | +necessary for QEMU but are not exposed to guests. A guest that escapes into | ||
134 | +the QEMU process can then begin invoking host system calls. | ||
135 | + | ||
136 | +New features must be designed to follow the principle of least privilege. | ||
137 | +Should this not be possible for technical reasons, the security risk must be | ||
138 | +clearly documented so users are aware of the trade-off of enabling the feature. | ||
139 | + | ||
140 | +@subsection Isolation mechanisms | ||
141 | + | ||
142 | +Several isolation mechanisms are available to realize this architecture of | ||
143 | +guest isolation and the principle of least privilege. With the exception of | ||
144 | +Linux seccomp, these mechanisms are all deployed by management tools that | ||
145 | +launch QEMU, such as libvirt. They are also platform-specific so they are only | ||
146 | +described briefly for Linux here. | ||
147 | + | ||
148 | +The fundamental isolation mechanism is that QEMU processes must run as | ||
149 | +unprivileged users. Sometimes it seems more convenient to launch QEMU as | ||
150 | +root to give it access to host devices (e.g. @code{/dev/net/tun}) but this poses a | ||
151 | +huge security risk. File descriptor passing can be used to give an otherwise | ||
152 | +unprivileged QEMU process access to host devices without running QEMU as root. | ||
153 | +It is also possible to launch QEMU as a non-root user and configure UNIX groups | ||
154 | +for access to @code{/dev/kvm}, @code{/dev/net/tun}, and other device nodes. | ||
155 | +Some Linux distros already ship with UNIX groups for these devices by default. | ||
156 | + | ||
157 | +@itemize | ||
158 | +@item SELinux and AppArmor make it possible to confine processes beyond the | ||
159 | +traditional UNIX process and file permissions model. They restrict the QEMU | ||
160 | +process from accessing processes and files on the host system that are not | ||
161 | +needed by QEMU. | ||
162 | + | ||
163 | +@item Resource limits and cgroup controllers provide throughput and utilization | ||
164 | +limits on key resources such as CPU time, memory, and I/O bandwidth. | ||
165 | + | ||
166 | +@item Linux namespaces can be used to make process, file system, and other system | ||
167 | +resources unavailable to QEMU. A namespaced QEMU process is restricted to only | ||
168 | +those resources that were granted to it. | ||
169 | + | ||
170 | +@item Linux seccomp is available via the QEMU @option{--sandbox} option. It disables | ||
171 | +system calls that are not needed by QEMU, thereby reducing the host kernel | ||
172 | +attack surface. | ||
173 | +@end itemize | ||
174 | diff --git a/qemu-doc.texi b/qemu-doc.texi | ||
175 | index XXXXXXX..XXXXXXX 100644 | 48 | index XXXXXXX..XXXXXXX 100644 |
176 | --- a/qemu-doc.texi | 49 | --- a/hw/remote/proxy.c |
177 | +++ b/qemu-doc.texi | 50 | +++ b/hw/remote/proxy.c |
178 | @@ -XXX,XX +XXX,XX @@ | 51 | @@ -XXX,XX +XXX,XX @@ static void probe_pci_info(PCIDevice *dev, Error **errp) |
179 | * QEMU Guest Agent:: | 52 | PCI_BASE_ADDRESS_SPACE_IO : PCI_BASE_ADDRESS_SPACE_MEMORY; |
180 | * QEMU User space emulator:: | 53 | |
181 | * System requirements:: | 54 | if (size) { |
182 | +* Security:: | 55 | - g_autofree char *name; |
183 | * Implementation notes:: | 56 | + g_autofree char *name = g_strdup_printf("bar-region-%d", i); |
184 | * Deprecated features:: | 57 | pdev->region[i].dev = pdev; |
185 | * Supported build platforms:: | 58 | pdev->region[i].present = true; |
186 | @@ -XXX,XX +XXX,XX @@ added with Linux 4.5 which is supported by the major distros. And even | 59 | if (type == PCI_BASE_ADDRESS_SPACE_MEMORY) { |
187 | if RHEL7 has kernel 3.10, KVM there has the required functionality there | 60 | pdev->region[i].memory = true; |
188 | to make it close to a 4.5 or newer kernel. | 61 | } |
189 | 62 | - name = g_strdup_printf("bar-region-%d", i); | |
190 | +@include docs/security.texi | 63 | memory_region_init_io(&pdev->region[i].mr, OBJECT(pdev), |
191 | + | 64 | &proxy_mr_ops, &pdev->region[i], |
192 | @include qemu-tech.texi | 65 | name, size); |
193 | |||
194 | @include qemu-deprecated.texi | ||
195 | -- | 66 | -- |
196 | 2.21.0 | 67 | 2.31.1 |
197 | 68 | ||
198 | diff view generated by jsdifflib |
1 | At KVM Forum 2018 I gave a presentation on security in QEMU: | 1 | From: Philippe Mathieu-Daudé <philmd@redhat.com> |
---|---|---|---|
2 | https://www.youtube.com/watch?v=YAdRf_hwxU8 (video) | ||
3 | https://vmsplice.net/~stefan/stefanha-kvm-forum-2018.pdf (slides) | ||
4 | 2 | ||
5 | This patch adds a guide to secure coding practices. This document | 3 | Document the following functions return the bitmap size |
6 | covers things that developers should know about security in QEMU. It is | 4 | if no matching bit is found: |
7 | just a starting point that we can expand on later. I hope it will be | ||
8 | useful as a resource for new contributors and will save code reviewers | ||
9 | from explaining the same concepts many times. | ||
10 | 5 | ||
11 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 6 | - find_first_bit |
12 | Acked-by: Stefano Garzarella <sgarzare@redhat.com> | 7 | - find_next_bit |
13 | Reviewed-by: Alex Bennée <alex.bennee@linaro.org> | 8 | - find_last_bit |
14 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | 9 | - find_first_zero_bit |
15 | Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> | 10 | - find_next_zero_bit |
16 | Reviewed-by: Li Qiang <liq3ea@gmail.com> | 11 | |
17 | Message-id: 20190509121820.16294-2-stefanha@redhat.com | 12 | Reviewed-by: Richard Henderson <richard.henderson@linaro.org> |
18 | Message-Id: <20190509121820.16294-2-stefanha@redhat.com> | 13 | Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com> |
14 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
15 | Message-id: 20210510200758.2623154-2-philmd@redhat.com | ||
19 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 16 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> |
20 | --- | 17 | --- |
21 | docs/devel/index.rst | 1 + | 18 | include/qemu/bitops.h | 15 ++++++++++++--- |
22 | docs/devel/secure-coding-practices.rst | 106 +++++++++++++++++++++++++ | 19 | 1 file changed, 12 insertions(+), 3 deletions(-) |
23 | 2 files changed, 107 insertions(+) | ||
24 | create mode 100644 docs/devel/secure-coding-practices.rst | ||
25 | 20 | ||
26 | diff --git a/docs/devel/index.rst b/docs/devel/index.rst | 21 | diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h |
27 | index XXXXXXX..XXXXXXX 100644 | 22 | index XXXXXXX..XXXXXXX 100644 |
28 | --- a/docs/devel/index.rst | 23 | --- a/include/qemu/bitops.h |
29 | +++ b/docs/devel/index.rst | 24 | +++ b/include/qemu/bitops.h |
30 | @@ -XXX,XX +XXX,XX @@ Contents: | 25 | @@ -XXX,XX +XXX,XX @@ static inline int test_bit(long nr, const unsigned long *addr) |
31 | stable-process | 26 | * @addr: The address to start the search at |
32 | testing | 27 | * @size: The maximum size to search |
33 | decodetree | 28 | * |
34 | + secure-coding-practices | 29 | - * Returns the bit number of the first set bit, or size. |
35 | diff --git a/docs/devel/secure-coding-practices.rst b/docs/devel/secure-coding-practices.rst | 30 | + * Returns the bit number of the last set bit, |
36 | new file mode 100644 | 31 | + * or @size if there is no set bit in the bitmap. |
37 | index XXXXXXX..XXXXXXX | 32 | */ |
38 | --- /dev/null | 33 | unsigned long find_last_bit(const unsigned long *addr, |
39 | +++ b/docs/devel/secure-coding-practices.rst | 34 | unsigned long size); |
40 | @@ -XXX,XX +XXX,XX @@ | 35 | @@ -XXX,XX +XXX,XX @@ unsigned long find_last_bit(const unsigned long *addr, |
41 | +======================= | 36 | * @addr: The address to base the search on |
42 | +Secure Coding Practices | 37 | * @offset: The bitnumber to start searching at |
43 | +======================= | 38 | * @size: The bitmap size in bits |
44 | +This document covers topics that both developers and security researchers must | 39 | + * |
45 | +be aware of so that they can develop safe code and audit existing code | 40 | + * Returns the bit number of the next set bit, |
46 | +properly. | 41 | + * or @size if there are no further set bits in the bitmap. |
47 | + | 42 | */ |
48 | +Reporting Security Bugs | 43 | unsigned long find_next_bit(const unsigned long *addr, |
49 | +----------------------- | 44 | unsigned long size, |
50 | +For details on how to report security bugs or ask questions about potential | 45 | @@ -XXX,XX +XXX,XX @@ unsigned long find_next_bit(const unsigned long *addr, |
51 | +security bugs, see the `Security Process wiki page | 46 | * @addr: The address to base the search on |
52 | +<https://wiki.qemu.org/SecurityProcess>`_. | 47 | * @offset: The bitnumber to start searching at |
53 | + | 48 | * @size: The bitmap size in bits |
54 | +General Secure C Coding Practices | 49 | + * |
55 | +--------------------------------- | 50 | + * Returns the bit number of the next cleared bit, |
56 | +Most CVEs (security bugs) reported against QEMU are not specific to | 51 | + * or @size if there are no further clear bits in the bitmap. |
57 | +virtualization or emulation. They are simply C programming bugs. Therefore | 52 | */ |
58 | +it's critical to be aware of common classes of security bugs. | 53 | |
59 | + | 54 | unsigned long find_next_zero_bit(const unsigned long *addr, |
60 | +There is a wide selection of resources available covering secure C coding. For | 55 | @@ -XXX,XX +XXX,XX @@ unsigned long find_next_zero_bit(const unsigned long *addr, |
61 | +example, the `CERT C Coding Standard | 56 | * @addr: The address to start the search at |
62 | +<https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard>`_ | 57 | * @size: The maximum size to search |
63 | +covers the most important classes of security bugs. | 58 | * |
64 | + | 59 | - * Returns the bit number of the first set bit. |
65 | +Instead of describing them in detail here, only the names of the most important | 60 | + * Returns the bit number of the first set bit, |
66 | +classes of security bugs are mentioned: | 61 | + * or @size if there is no set bit in the bitmap. |
67 | + | 62 | */ |
68 | +* Buffer overflows | 63 | static inline unsigned long find_first_bit(const unsigned long *addr, |
69 | +* Use-after-free and double-free | 64 | unsigned long size) |
70 | +* Integer overflows | 65 | @@ -XXX,XX +XXX,XX @@ static inline unsigned long find_first_bit(const unsigned long *addr, |
71 | +* Format string vulnerabilities | 66 | * @addr: The address to start the search at |
72 | + | 67 | * @size: The maximum size to search |
73 | +Some of these classes of bugs can be detected by analyzers. Static analysis is | 68 | * |
74 | +performed regularly by Coverity and the most obvious of these bugs are even | 69 | - * Returns the bit number of the first cleared bit. |
75 | +reported by compilers. Dynamic analysis is possible with valgrind, tsan, and | 70 | + * Returns the bit number of the first cleared bit, |
76 | +asan. | 71 | + * or @size if there is no clear bit in the bitmap. |
77 | + | 72 | */ |
78 | +Input Validation | 73 | static inline unsigned long find_first_zero_bit(const unsigned long *addr, |
79 | +---------------- | 74 | unsigned long size) |
80 | +Inputs from the guest or external sources (e.g. network, files) cannot be | ||
81 | +trusted and may be invalid. Inputs must be checked before using them in a way | ||
82 | +that could crash the program, expose host memory to the guest, or otherwise be | ||
83 | +exploitable by an attacker. | ||
84 | + | ||
85 | +The most sensitive attack surface is device emulation. All hardware register | ||
86 | +accesses and data read from guest memory must be validated. A typical example | ||
87 | +is a device that contains multiple units that are selectable by the guest via | ||
88 | +an index register:: | ||
89 | + | ||
90 | + typedef struct { | ||
91 | + ProcessingUnit unit[2]; | ||
92 | + ... | ||
93 | + } MyDeviceState; | ||
94 | + | ||
95 | + static void mydev_writel(void *opaque, uint32_t addr, uint32_t val) | ||
96 | + { | ||
97 | + MyDeviceState *mydev = opaque; | ||
98 | + ProcessingUnit *unit; | ||
99 | + | ||
100 | + switch (addr) { | ||
101 | + case MYDEV_SELECT_UNIT: | ||
102 | + unit = &mydev->unit[val]; <-- this input wasn't validated! | ||
103 | + ... | ||
104 | + } | ||
105 | + } | ||
106 | + | ||
107 | +If ``val`` is not in range [0, 1] then an out-of-bounds memory access will take | ||
108 | +place when ``unit`` is dereferenced. The code must check that ``val`` is 0 or | ||
109 | +1 and handle the case where it is invalid. | ||
110 | + | ||
111 | +Unexpected Device Accesses | ||
112 | +-------------------------- | ||
113 | +The guest may access device registers in unusual orders or at unexpected | ||
114 | +moments. Device emulation code must not assume that the guest follows the | ||
115 | +typical "theory of operation" presented in driver writer manuals. The guest | ||
116 | +may make nonsense accesses to device registers such as starting operations | ||
117 | +before the device has been fully initialized. | ||
118 | + | ||
119 | +A related issue is that device emulation code must be prepared for unexpected | ||
120 | +device register accesses while asynchronous operations are in progress. A | ||
121 | +well-behaved guest might wait for a completion interrupt before accessing | ||
122 | +certain device registers. Device emulation code must handle the case where the | ||
123 | +guest overwrites registers or submits further requests before an ongoing | ||
124 | +request completes. Unexpected accesses must not cause memory corruption or | ||
125 | +leaks in QEMU. | ||
126 | + | ||
127 | +Invalid device register accesses can be reported with | ||
128 | +``qemu_log_mask(LOG_GUEST_ERROR, ...)``. The ``-d guest_errors`` command-line | ||
129 | +option enables these log messages. | ||
130 | + | ||
131 | +Live Migration | ||
132 | +-------------- | ||
133 | +Device state can be saved to disk image files and shared with other users. | ||
134 | +Live migration code must validate inputs when loading device state so an | ||
135 | +attacker cannot gain control by crafting invalid device states. Device state | ||
136 | +is therefore considered untrusted even though it is typically generated by QEMU | ||
137 | +itself. | ||
138 | + | ||
139 | +Guest Memory Access Races | ||
140 | +------------------------- | ||
141 | +Guests with multiple vCPUs may modify guest RAM while device emulation code is | ||
142 | +running. Device emulation code must copy in descriptors and other guest RAM | ||
143 | +structures and only process the local copy. This prevents | ||
144 | +time-of-check-to-time-of-use (TOCTOU) race conditions that could cause QEMU to | ||
145 | +crash when a vCPU thread modifies guest RAM while device emulation is | ||
146 | +processing it. | ||
147 | -- | 75 | -- |
148 | 2.21.0 | 76 | 2.31.1 |
149 | 77 | ||
150 | diff view generated by jsdifflib |
1 | From: Paolo Bonzini <pbonzini@redhat.com> | 1 | From: Paolo Bonzini <pbonzini@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | With aio=thread, adaptive polling makes latency worse rather than | 3 | The lifetime of the timer is well-known (it cannot outlive |
4 | better, because it delays the execution of the ThreadPool's | 4 | qemu_co_sleep_ns_wakeable, because it's deleted by the time the |
5 | completion bottom half. | 5 | coroutine resumes), so it is not necessary to place it on the heap. |
6 | 6 | ||
7 | event_notifier_poll() does run while polling, detecting that | 7 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
8 | a bottom half was scheduled by a worker thread, but because | ||
9 | ctx->notifier is explicitly ignored in run_poll_handlers_once(), | ||
10 | scheduling the BH does not count as making progress and | ||
11 | run_poll_handlers() keeps running. Fix this by recomputing | ||
12 | the deadline after *timeout could have changed. | ||
13 | |||
14 | With this change, ThreadPool still cannot participate in polling | ||
15 | but at least it does not suffer from extra latency. | ||
16 | |||
17 | Reported-by: Sergio Lopez <slp@redhat.com> | ||
18 | Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | 8 | Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> |
19 | Message-id: 20190409122823.12416-1-pbonzini@redhat.com | 9 | Message-id: 20210517100548.28806-2-pbonzini@redhat.com |
20 | Cc: Stefan Hajnoczi <stefanha@gmail.com> | ||
21 | Cc: Kevin Wolf <kwolf@redhat.com> | ||
22 | Cc: qemu-block@nongnu.org | ||
23 | Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | ||
24 | Message-Id: <1553692145-86728-1-git-send-email-pbonzini@redhat.com> | ||
25 | Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | ||
26 | Message-Id: <20190409122823.12416-1-pbonzini@redhat.com> | ||
27 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 10 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> |
28 | --- | 11 | --- |
29 | util/aio-posix.c | 12 ++++++++---- | 12 | util/qemu-coroutine-sleep.c | 9 ++++----- |
30 | 1 file changed, 8 insertions(+), 4 deletions(-) | 13 | 1 file changed, 4 insertions(+), 5 deletions(-) |
31 | 14 | ||
32 | diff --git a/util/aio-posix.c b/util/aio-posix.c | 15 | diff --git a/util/qemu-coroutine-sleep.c b/util/qemu-coroutine-sleep.c |
33 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
34 | --- a/util/aio-posix.c | 17 | --- a/util/qemu-coroutine-sleep.c |
35 | +++ b/util/aio-posix.c | 18 | +++ b/util/qemu-coroutine-sleep.c |
36 | @@ -XXX,XX +XXX,XX @@ static bool run_poll_handlers_once(AioContext *ctx, int64_t *timeout) | 19 | @@ -XXX,XX +XXX,XX @@ static const char *qemu_co_sleep_ns__scheduled = "qemu_co_sleep_ns"; |
37 | if (!node->deleted && node->io_poll && | 20 | |
38 | aio_node_check(ctx, node->is_external) && | 21 | struct QemuCoSleepState { |
39 | node->io_poll(node->opaque)) { | 22 | Coroutine *co; |
40 | + /* | 23 | - QEMUTimer *ts; |
41 | + * Polling was successful, exit try_poll_mode immediately | 24 | + QEMUTimer ts; |
42 | + * to adjust the next polling time. | 25 | QemuCoSleepState **user_state_pointer; |
43 | + */ | 26 | }; |
44 | *timeout = 0; | 27 | |
45 | if (node->opaque != &ctx->notifier) { | 28 | @@ -XXX,XX +XXX,XX @@ void qemu_co_sleep_wake(QemuCoSleepState *sleep_state) |
46 | progress = true; | 29 | if (sleep_state->user_state_pointer) { |
47 | @@ -XXX,XX +XXX,XX @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout) | 30 | *sleep_state->user_state_pointer = NULL; |
48 | do { | 31 | } |
49 | progress = run_poll_handlers_once(ctx, timeout); | 32 | - timer_del(sleep_state->ts); |
50 | elapsed_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start_time; | 33 | + timer_del(&sleep_state->ts); |
51 | - } while (!progress && elapsed_time < max_ns | 34 | aio_co_wake(sleep_state->co); |
52 | - && !atomic_read(&ctx->poll_disable_cnt)); | 35 | } |
53 | + max_ns = qemu_soonest_timeout(*timeout, max_ns); | 36 | |
54 | + assert(!(max_ns && progress)); | 37 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_sleep_ns_wakeable(QEMUClockType type, int64_t ns, |
55 | + } while (elapsed_time < max_ns && !atomic_read(&ctx->poll_disable_cnt)); | 38 | AioContext *ctx = qemu_get_current_aio_context(); |
56 | 39 | QemuCoSleepState state = { | |
57 | /* If time has passed with no successful polling, adjust *timeout to | 40 | .co = qemu_coroutine_self(), |
58 | * keep the same ending time. | 41 | - .ts = aio_timer_new(ctx, type, SCALE_NS, co_sleep_cb, &state), |
59 | @@ -XXX,XX +XXX,XX @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout) | 42 | .user_state_pointer = sleep_state, |
60 | */ | 43 | }; |
61 | static bool try_poll_mode(AioContext *ctx, int64_t *timeout) | 44 | |
62 | { | 45 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_sleep_ns_wakeable(QEMUClockType type, int64_t ns, |
63 | - /* See qemu_soonest_timeout() uint64_t hack */ | 46 | abort(); |
64 | - int64_t max_ns = MIN((uint64_t)*timeout, (uint64_t)ctx->poll_ns); | 47 | } |
65 | + int64_t max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns); | 48 | |
66 | 49 | + aio_timer_init(ctx, &state.ts, type, SCALE_NS, co_sleep_cb, &state); | |
67 | if (max_ns && !atomic_read(&ctx->poll_disable_cnt)) { | 50 | if (sleep_state) { |
68 | poll_set_started(ctx, true); | 51 | *sleep_state = &state; |
52 | } | ||
53 | - timer_mod(state.ts, qemu_clock_get_ns(type) + ns); | ||
54 | + timer_mod(&state.ts, qemu_clock_get_ns(type) + ns); | ||
55 | qemu_coroutine_yield(); | ||
56 | if (sleep_state) { | ||
57 | /* | ||
58 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_sleep_ns_wakeable(QEMUClockType type, int64_t ns, | ||
59 | */ | ||
60 | assert(*sleep_state == NULL); | ||
61 | } | ||
62 | - timer_free(state.ts); | ||
63 | } | ||
69 | -- | 64 | -- |
70 | 2.21.0 | 65 | 2.31.1 |
71 | 66 | ||
72 | diff view generated by jsdifflib |
1 | From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> | 1 | From: Paolo Bonzini <pbonzini@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | On a file system used by the customer, fallocate() returns an error | 3 | Simplify the code by removing conditionals. qemu_co_sleep_ns |
4 | if the block is not properly aligned. So, bdrv_co_pwrite_zeroes() | 4 | can simply point the argument to an on-stack temporary. |
5 | fails. We can handle that case the same way as it is done for the | ||
6 | unsupported cases, namely, call to bdrv_driver_pwritev() that writes | ||
7 | zeroes to an image for the unaligned chunk of the block. | ||
8 | 5 | ||
9 | Suggested-by: Denis V. Lunev <den@openvz.org> | 6 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
10 | Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> | 7 | Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> |
11 | Reviewed-by: John Snow <jsnow@redhat.com> | 8 | Message-id: 20210517100548.28806-3-pbonzini@redhat.com |
12 | Message-id: 1554474244-553661-1-git-send-email-andrey.shinkevich@virtuozzo.com | ||
13 | Message-Id: <1554474244-553661-1-git-send-email-andrey.shinkevich@virtuozzo.com> | ||
14 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 9 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> |
15 | --- | 10 | --- |
16 | block/io.c | 2 +- | 11 | include/qemu/coroutine.h | 5 +++-- |
17 | 1 file changed, 1 insertion(+), 1 deletion(-) | 12 | util/qemu-coroutine-sleep.c | 18 +++++------------- |
13 | 2 files changed, 8 insertions(+), 15 deletions(-) | ||
18 | 14 | ||
19 | diff --git a/block/io.c b/block/io.c | 15 | diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h |
20 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
21 | --- a/block/io.c | 17 | --- a/include/qemu/coroutine.h |
22 | +++ b/block/io.c | 18 | +++ b/include/qemu/coroutine.h |
23 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, | 19 | @@ -XXX,XX +XXX,XX @@ typedef struct QemuCoSleepState QemuCoSleepState; |
24 | assert(!bs->supported_zero_flags); | 20 | |
25 | } | 21 | /** |
26 | 22 | * Yield the coroutine for a given duration. During this yield, @sleep_state | |
27 | - if (ret == -ENOTSUP && !(flags & BDRV_REQ_NO_FALLBACK)) { | 23 | - * (if not NULL) is set to an opaque pointer, which may be used for |
28 | + if (ret < 0 && !(flags & BDRV_REQ_NO_FALLBACK)) { | 24 | + * is set to an opaque pointer, which may be used for |
29 | /* Fall back to bounce buffer if write zeroes is unsupported */ | 25 | * qemu_co_sleep_wake(). Be careful, the pointer is set back to zero when the |
30 | BdrvRequestFlags write_flags = flags & ~BDRV_REQ_ZERO_WRITE; | 26 | * timer fires. Don't save the obtained value to other variables and don't call |
31 | 27 | * qemu_co_sleep_wake from another aio context. | |
28 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_sleep_ns_wakeable(QEMUClockType type, int64_t ns, | ||
29 | QemuCoSleepState **sleep_state); | ||
30 | static inline void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns) | ||
31 | { | ||
32 | - qemu_co_sleep_ns_wakeable(type, ns, NULL); | ||
33 | + QemuCoSleepState *unused = NULL; | ||
34 | + qemu_co_sleep_ns_wakeable(type, ns, &unused); | ||
35 | } | ||
36 | |||
37 | /** | ||
38 | diff --git a/util/qemu-coroutine-sleep.c b/util/qemu-coroutine-sleep.c | ||
39 | index XXXXXXX..XXXXXXX 100644 | ||
40 | --- a/util/qemu-coroutine-sleep.c | ||
41 | +++ b/util/qemu-coroutine-sleep.c | ||
42 | @@ -XXX,XX +XXX,XX @@ void qemu_co_sleep_wake(QemuCoSleepState *sleep_state) | ||
43 | qemu_co_sleep_ns__scheduled, NULL); | ||
44 | |||
45 | assert(scheduled == qemu_co_sleep_ns__scheduled); | ||
46 | - if (sleep_state->user_state_pointer) { | ||
47 | - *sleep_state->user_state_pointer = NULL; | ||
48 | - } | ||
49 | + *sleep_state->user_state_pointer = NULL; | ||
50 | timer_del(&sleep_state->ts); | ||
51 | aio_co_wake(sleep_state->co); | ||
52 | } | ||
53 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_sleep_ns_wakeable(QEMUClockType type, int64_t ns, | ||
54 | } | ||
55 | |||
56 | aio_timer_init(ctx, &state.ts, type, SCALE_NS, co_sleep_cb, &state); | ||
57 | - if (sleep_state) { | ||
58 | - *sleep_state = &state; | ||
59 | - } | ||
60 | + *sleep_state = &state; | ||
61 | timer_mod(&state.ts, qemu_clock_get_ns(type) + ns); | ||
62 | qemu_coroutine_yield(); | ||
63 | - if (sleep_state) { | ||
64 | - /* | ||
65 | - * Note that *sleep_state is cleared during qemu_co_sleep_wake | ||
66 | - * before resuming this coroutine. | ||
67 | - */ | ||
68 | - assert(*sleep_state == NULL); | ||
69 | - } | ||
70 | + | ||
71 | + /* qemu_co_sleep_wake clears *sleep_state before resuming this coroutine. */ | ||
72 | + assert(*sleep_state == NULL); | ||
73 | } | ||
32 | -- | 74 | -- |
33 | 2.21.0 | 75 | 2.31.1 |
34 | 76 | ||
35 | diff view generated by jsdifflib |
1 | From: Nikita Alekseev <n.alekseev2104@gmail.com> | 1 | From: Paolo Bonzini <pbonzini@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | bdrv_check_co_entry calls bdrv_co_check, which is a coroutine function. | 3 | All callers of qemu_co_sleep_wake are checking whether they are passing |
4 | Thus, it also needs to be marked as a coroutine. | 4 | a NULL argument inside the pointer-to-pointer: do the check in |
5 | qemu_co_sleep_wake itself. | ||
5 | 6 | ||
6 | Signed-off-by: Nikita Alekseev <n.alekseev2104@gmail.com> | 7 | As a side effect, qemu_co_sleep_wake can be called more than once and |
7 | Message-id: 20190401093051.16488-1-n.alekseev2104@gmail.com | 8 | it will only wake the coroutine once; after the first time, the argument |
8 | Message-Id: <20190401093051.16488-1-n.alekseev2104@gmail.com> | 9 | will be set to NULL via *sleep_state->user_state_pointer. However, this |
10 | would not be safe unless co_sleep_cb keeps using the QemuCoSleepState* | ||
11 | directly, so make it go through the pointer-to-pointer instead. | ||
12 | |||
13 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
14 | Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | ||
15 | Message-id: 20210517100548.28806-4-pbonzini@redhat.com | ||
9 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 16 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> |
10 | --- | 17 | --- |
11 | block.c | 2 +- | 18 | block/block-copy.c | 4 +--- |
12 | 1 file changed, 1 insertion(+), 1 deletion(-) | 19 | block/nbd.c | 8 ++------ |
20 | util/qemu-coroutine-sleep.c | 21 ++++++++++++--------- | ||
21 | 3 files changed, 15 insertions(+), 18 deletions(-) | ||
13 | 22 | ||
14 | diff --git a/block.c b/block.c | 23 | diff --git a/block/block-copy.c b/block/block-copy.c |
15 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
16 | --- a/block.c | 25 | --- a/block/block-copy.c |
17 | +++ b/block.c | 26 | +++ b/block/block-copy.c |
18 | @@ -XXX,XX +XXX,XX @@ typedef struct CheckCo { | 27 | @@ -XXX,XX +XXX,XX @@ out: |
19 | int ret; | 28 | |
20 | } CheckCo; | 29 | void block_copy_kick(BlockCopyCallState *call_state) |
21 | |||
22 | -static void bdrv_check_co_entry(void *opaque) | ||
23 | +static void coroutine_fn bdrv_check_co_entry(void *opaque) | ||
24 | { | 30 | { |
25 | CheckCo *cco = opaque; | 31 | - if (call_state->sleep_state) { |
26 | cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix); | 32 | - qemu_co_sleep_wake(call_state->sleep_state); |
33 | - } | ||
34 | + qemu_co_sleep_wake(call_state->sleep_state); | ||
35 | } | ||
36 | |||
37 | /* | ||
38 | diff --git a/block/nbd.c b/block/nbd.c | ||
39 | index XXXXXXX..XXXXXXX 100644 | ||
40 | --- a/block/nbd.c | ||
41 | +++ b/block/nbd.c | ||
42 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn nbd_client_co_drain_begin(BlockDriverState *bs) | ||
43 | BDRVNBDState *s = (BDRVNBDState *)bs->opaque; | ||
44 | |||
45 | s->drained = true; | ||
46 | - if (s->connection_co_sleep_ns_state) { | ||
47 | - qemu_co_sleep_wake(s->connection_co_sleep_ns_state); | ||
48 | - } | ||
49 | + qemu_co_sleep_wake(s->connection_co_sleep_ns_state); | ||
50 | |||
51 | nbd_co_establish_connection_cancel(bs, false); | ||
52 | |||
53 | @@ -XXX,XX +XXX,XX @@ static void nbd_teardown_connection(BlockDriverState *bs) | ||
54 | |||
55 | s->state = NBD_CLIENT_QUIT; | ||
56 | if (s->connection_co) { | ||
57 | - if (s->connection_co_sleep_ns_state) { | ||
58 | - qemu_co_sleep_wake(s->connection_co_sleep_ns_state); | ||
59 | - } | ||
60 | + qemu_co_sleep_wake(s->connection_co_sleep_ns_state); | ||
61 | nbd_co_establish_connection_cancel(bs, true); | ||
62 | } | ||
63 | if (qemu_in_coroutine()) { | ||
64 | diff --git a/util/qemu-coroutine-sleep.c b/util/qemu-coroutine-sleep.c | ||
65 | index XXXXXXX..XXXXXXX 100644 | ||
66 | --- a/util/qemu-coroutine-sleep.c | ||
67 | +++ b/util/qemu-coroutine-sleep.c | ||
68 | @@ -XXX,XX +XXX,XX @@ struct QemuCoSleepState { | ||
69 | |||
70 | void qemu_co_sleep_wake(QemuCoSleepState *sleep_state) | ||
71 | { | ||
72 | - /* Write of schedule protected by barrier write in aio_co_schedule */ | ||
73 | - const char *scheduled = qatomic_cmpxchg(&sleep_state->co->scheduled, | ||
74 | - qemu_co_sleep_ns__scheduled, NULL); | ||
75 | + if (sleep_state) { | ||
76 | + /* Write of schedule protected by barrier write in aio_co_schedule */ | ||
77 | + const char *scheduled = qatomic_cmpxchg(&sleep_state->co->scheduled, | ||
78 | + qemu_co_sleep_ns__scheduled, NULL); | ||
79 | |||
80 | - assert(scheduled == qemu_co_sleep_ns__scheduled); | ||
81 | - *sleep_state->user_state_pointer = NULL; | ||
82 | - timer_del(&sleep_state->ts); | ||
83 | - aio_co_wake(sleep_state->co); | ||
84 | + assert(scheduled == qemu_co_sleep_ns__scheduled); | ||
85 | + *sleep_state->user_state_pointer = NULL; | ||
86 | + timer_del(&sleep_state->ts); | ||
87 | + aio_co_wake(sleep_state->co); | ||
88 | + } | ||
89 | } | ||
90 | |||
91 | static void co_sleep_cb(void *opaque) | ||
92 | { | ||
93 | - qemu_co_sleep_wake(opaque); | ||
94 | + QemuCoSleepState **sleep_state = opaque; | ||
95 | + qemu_co_sleep_wake(*sleep_state); | ||
96 | } | ||
97 | |||
98 | void coroutine_fn qemu_co_sleep_ns_wakeable(QEMUClockType type, int64_t ns, | ||
99 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_sleep_ns_wakeable(QEMUClockType type, int64_t ns, | ||
100 | abort(); | ||
101 | } | ||
102 | |||
103 | - aio_timer_init(ctx, &state.ts, type, SCALE_NS, co_sleep_cb, &state); | ||
104 | + aio_timer_init(ctx, &state.ts, type, SCALE_NS, co_sleep_cb, sleep_state); | ||
105 | *sleep_state = &state; | ||
106 | timer_mod(&state.ts, qemu_clock_get_ns(type) + ns); | ||
107 | qemu_coroutine_yield(); | ||
27 | -- | 108 | -- |
28 | 2.21.0 | 109 | 2.31.1 |
29 | 110 | ||
30 | diff view generated by jsdifflib |
1 | From: Jules Irenge <jbi.octave@gmail.com> | 1 | From: Paolo Bonzini <pbonzini@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Add braces to fix errors issued by checkpatch.pl tool | 3 | This simplification is enabled by the previous patch. Now aio_co_wake |
4 | "ERROR: braces {} are necessary for all arms of this statement" | 4 | will only be called once, therefore we do not care about a spurious |
5 | Within "util/readline.c" file | 5 | firing of the timer after a qemu_co_sleep_wake. |
6 | Message-Id: <20190330112142.14082-1-jbi.octave@gmail.com> | ||
7 | 6 | ||
7 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
8 | Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | ||
9 | Message-id: 20210517100548.28806-5-pbonzini@redhat.com | ||
8 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 10 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> |
9 | --- | 11 | --- |
10 | util/readline.c | 50 ++++++++++++++++++++++++++++++++----------------- | 12 | util/qemu-coroutine-sleep.c | 8 ++++---- |
11 | 1 file changed, 33 insertions(+), 17 deletions(-) | 13 | 1 file changed, 4 insertions(+), 4 deletions(-) |
12 | 14 | ||
13 | diff --git a/util/readline.c b/util/readline.c | 15 | diff --git a/util/qemu-coroutine-sleep.c b/util/qemu-coroutine-sleep.c |
14 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
15 | --- a/util/readline.c | 17 | --- a/util/qemu-coroutine-sleep.c |
16 | +++ b/util/readline.c | 18 | +++ b/util/qemu-coroutine-sleep.c |
17 | @@ -XXX,XX +XXX,XX @@ static void readline_update(ReadLineState *rs) | 19 | @@ -XXX,XX +XXX,XX @@ static const char *qemu_co_sleep_ns__scheduled = "qemu_co_sleep_ns"; |
18 | rs->cmd_buf[rs->cmd_buf_size] = '\0'; | 20 | |
19 | if (rs->read_password) { | 21 | struct QemuCoSleepState { |
20 | len = strlen(rs->cmd_buf); | 22 | Coroutine *co; |
21 | - for (i = 0; i < len; i++) | 23 | - QEMUTimer ts; |
22 | + for (i = 0; i < len; i++) { | 24 | QemuCoSleepState **user_state_pointer; |
23 | rs->printf_func(rs->opaque, "*"); | 25 | }; |
24 | + } | 26 | |
25 | } else { | 27 | @@ -XXX,XX +XXX,XX @@ void qemu_co_sleep_wake(QemuCoSleepState *sleep_state) |
26 | rs->printf_func(rs->opaque, "%s", rs->cmd_buf); | 28 | |
27 | } | 29 | assert(scheduled == qemu_co_sleep_ns__scheduled); |
28 | @@ -XXX,XX +XXX,XX @@ static void readline_up_char(ReadLineState *rs) | 30 | *sleep_state->user_state_pointer = NULL; |
31 | - timer_del(&sleep_state->ts); | ||
32 | aio_co_wake(sleep_state->co); | ||
33 | } | ||
34 | } | ||
35 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_sleep_ns_wakeable(QEMUClockType type, int64_t ns, | ||
36 | QemuCoSleepState **sleep_state) | ||
29 | { | 37 | { |
30 | int idx; | 38 | AioContext *ctx = qemu_get_current_aio_context(); |
31 | 39 | + QEMUTimer ts; | |
32 | - if (rs->hist_entry == 0) | 40 | QemuCoSleepState state = { |
33 | + if (rs->hist_entry == 0) { | 41 | .co = qemu_coroutine_self(), |
34 | return; | 42 | .user_state_pointer = sleep_state, |
35 | + } | 43 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_sleep_ns_wakeable(QEMUClockType type, int64_t ns, |
36 | if (rs->hist_entry == -1) { | 44 | abort(); |
37 | /* Find latest entry */ | ||
38 | for (idx = 0; idx < READLINE_MAX_CMDS; idx++) { | ||
39 | - if (rs->history[idx] == NULL) | ||
40 | + if (rs->history[idx] == NULL) { | ||
41 | break; | ||
42 | + } | ||
43 | } | ||
44 | rs->hist_entry = idx; | ||
45 | } | 45 | } |
46 | @@ -XXX,XX +XXX,XX @@ static void readline_up_char(ReadLineState *rs) | 46 | |
47 | 47 | - aio_timer_init(ctx, &state.ts, type, SCALE_NS, co_sleep_cb, sleep_state); | |
48 | static void readline_down_char(ReadLineState *rs) | 48 | + aio_timer_init(ctx, &ts, type, SCALE_NS, co_sleep_cb, sleep_state); |
49 | { | 49 | *sleep_state = &state; |
50 | - if (rs->hist_entry == -1) | 50 | - timer_mod(&state.ts, qemu_clock_get_ns(type) + ns); |
51 | + if (rs->hist_entry == -1) { | 51 | + timer_mod(&ts, qemu_clock_get_ns(type) + ns); |
52 | return; | 52 | qemu_coroutine_yield(); |
53 | + } | 53 | + timer_del(&ts); |
54 | if (rs->hist_entry < READLINE_MAX_CMDS - 1 && | 54 | |
55 | rs->history[++rs->hist_entry] != NULL) { | 55 | /* qemu_co_sleep_wake clears *sleep_state before resuming this coroutine. */ |
56 | pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf), | 56 | assert(*sleep_state == NULL); |
57 | @@ -XXX,XX +XXX,XX @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline) | ||
58 | char *hist_entry, *new_entry; | ||
59 | int idx; | ||
60 | |||
61 | - if (cmdline[0] == '\0') | ||
62 | + if (cmdline[0] == '\0') { | ||
63 | return; | ||
64 | + } | ||
65 | new_entry = NULL; | ||
66 | if (rs->hist_entry != -1) { | ||
67 | /* We were editing an existing history entry: replace it */ | ||
68 | @@ -XXX,XX +XXX,XX @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline) | ||
69 | /* Search cmdline in history buffers */ | ||
70 | for (idx = 0; idx < READLINE_MAX_CMDS; idx++) { | ||
71 | hist_entry = rs->history[idx]; | ||
72 | - if (hist_entry == NULL) | ||
73 | + if (hist_entry == NULL) { | ||
74 | break; | ||
75 | + } | ||
76 | if (strcmp(hist_entry, cmdline) == 0) { | ||
77 | same_entry: | ||
78 | new_entry = hist_entry; | ||
79 | @@ -XXX,XX +XXX,XX @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline) | ||
80 | (READLINE_MAX_CMDS - (idx + 1)) * sizeof(char *)); | ||
81 | rs->history[READLINE_MAX_CMDS - 1] = NULL; | ||
82 | for (; idx < READLINE_MAX_CMDS; idx++) { | ||
83 | - if (rs->history[idx] == NULL) | ||
84 | + if (rs->history[idx] == NULL) { | ||
85 | break; | ||
86 | + } | ||
87 | } | ||
88 | break; | ||
89 | } | ||
90 | @@ -XXX,XX +XXX,XX @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline) | ||
91 | rs->history[READLINE_MAX_CMDS - 1] = NULL; | ||
92 | idx = READLINE_MAX_CMDS - 1; | ||
93 | } | ||
94 | - if (new_entry == NULL) | ||
95 | + if (new_entry == NULL) { | ||
96 | new_entry = g_strdup(cmdline); | ||
97 | + } | ||
98 | rs->history[idx] = new_entry; | ||
99 | rs->hist_entry = -1; | ||
100 | } | ||
101 | @@ -XXX,XX +XXX,XX @@ static void readline_completion(ReadLineState *rs) | ||
102 | g_free(cmdline); | ||
103 | |||
104 | /* no completion found */ | ||
105 | - if (rs->nb_completions <= 0) | ||
106 | + if (rs->nb_completions <= 0) { | ||
107 | return; | ||
108 | + } | ||
109 | if (rs->nb_completions == 1) { | ||
110 | len = strlen(rs->completions[0]); | ||
111 | for (i = rs->completion_index; i < len; i++) { | ||
112 | readline_insert_char(rs, rs->completions[0][i]); | ||
113 | } | ||
114 | /* extra space for next argument. XXX: make it more generic */ | ||
115 | - if (len > 0 && rs->completions[0][len - 1] != '/') | ||
116 | + if (len > 0 && rs->completions[0][len - 1] != '/') { | ||
117 | readline_insert_char(rs, ' '); | ||
118 | + } | ||
119 | } else { | ||
120 | qsort(rs->completions, rs->nb_completions, sizeof(char *), | ||
121 | completion_comp); | ||
122 | @@ -XXX,XX +XXX,XX @@ static void readline_completion(ReadLineState *rs) | ||
123 | if (i == 0) { | ||
124 | max_prefix = len; | ||
125 | } else { | ||
126 | - if (len < max_prefix) | ||
127 | + if (len < max_prefix) { | ||
128 | max_prefix = len; | ||
129 | + } | ||
130 | for (j = 0; j < max_prefix; j++) { | ||
131 | - if (rs->completions[i][j] != rs->completions[0][j]) | ||
132 | + if (rs->completions[i][j] != rs->completions[0][j]) { | ||
133 | max_prefix = j; | ||
134 | + } | ||
135 | } | ||
136 | } | ||
137 | - if (len > max_width) | ||
138 | + if (len > max_width) { | ||
139 | max_width = len; | ||
140 | + } | ||
141 | } | ||
142 | if (max_prefix > 0) | ||
143 | for (i = rs->completion_index; i < max_prefix; i++) { | ||
144 | readline_insert_char(rs, rs->completions[0][i]); | ||
145 | } | ||
146 | max_width += 2; | ||
147 | - if (max_width < 10) | ||
148 | + if (max_width < 10) { | ||
149 | max_width = 10; | ||
150 | - else if (max_width > 80) | ||
151 | + } else if (max_width > 80) { | ||
152 | max_width = 80; | ||
153 | + } | ||
154 | nb_cols = 80 / max_width; | ||
155 | j = 0; | ||
156 | for (i = 0; i < rs->nb_completions; i++) { | ||
157 | @@ -XXX,XX +XXX,XX @@ void readline_handle_byte(ReadLineState *rs, int ch) | ||
158 | case 10: | ||
159 | case 13: | ||
160 | rs->cmd_buf[rs->cmd_buf_size] = '\0'; | ||
161 | - if (!rs->read_password) | ||
162 | + if (!rs->read_password) { | ||
163 | readline_hist_add(rs, rs->cmd_buf); | ||
164 | + } | ||
165 | rs->printf_func(rs->opaque, "\n"); | ||
166 | rs->cmd_buf_index = 0; | ||
167 | rs->cmd_buf_size = 0; | ||
168 | @@ -XXX,XX +XXX,XX @@ void readline_restart(ReadLineState *rs) | ||
169 | |||
170 | const char *readline_get_history(ReadLineState *rs, unsigned int index) | ||
171 | { | ||
172 | - if (index >= READLINE_MAX_CMDS) | ||
173 | + if (index >= READLINE_MAX_CMDS) { | ||
174 | return NULL; | ||
175 | + } | ||
176 | return rs->history[index]; | ||
177 | } | ||
178 | |||
179 | -- | 57 | -- |
180 | 2.21.0 | 58 | 2.31.1 |
181 | 59 | ||
182 | diff view generated by jsdifflib |
1 | From: Jules Irenge <jbi.octave@gmail.com> | 1 | From: Paolo Bonzini <pbonzini@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Replace tab indent by four spaces to fix errors issued by checkpatch.pl tool | 3 | Right now, users of qemu_co_sleep_ns_wakeable are simply passing |
4 | "ERROR: code indent should never use tabs" within "util/readline.c" file. | 4 | a pointer to QemuCoSleepState by reference to the function. But |
5 | 5 | QemuCoSleepState really is just a Coroutine*; making the | |
6 | Signed-off-by: Jules Irenge <jbi.octave@gmail.com> | 6 | content of the struct public is just as efficient and lets us |
7 | Reviewed-by: Thomas Huth <thuth@redhat.com> | 7 | skip the user_state_pointer indirection. |
8 | Message-id: 20190401024406.10819-3-jbi.octave@gmail.com | 8 | |
9 | Message-Id: <20190401024406.10819-3-jbi.octave@gmail.com> | 9 | Since the usage is changed, take the occasion to rename the |
10 | struct to QemuCoSleep. | ||
11 | |||
12 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
13 | Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | ||
14 | Message-id: 20210517100548.28806-6-pbonzini@redhat.com | ||
10 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 15 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> |
11 | --- | 16 | --- |
12 | util/readline.c | 98 ++++++++++++++++++++++++------------------------- | 17 | include/qemu/coroutine.h | 23 +++++++++++---------- |
13 | 1 file changed, 49 insertions(+), 49 deletions(-) | 18 | block/block-copy.c | 8 ++++---- |
14 | 19 | block/nbd.c | 10 ++++----- | |
15 | diff --git a/util/readline.c b/util/readline.c | 20 | util/qemu-coroutine-sleep.c | 41 ++++++++++++++++--------------------- |
16 | index XXXXXXX..XXXXXXX 100644 | 21 | 4 files changed, 39 insertions(+), 43 deletions(-) |
17 | --- a/util/readline.c | 22 | |
18 | +++ b/util/readline.c | 23 | diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h |
19 | @@ -XXX,XX +XXX,XX @@ static void readline_up_char(ReadLineState *rs) | 24 | index XXXXXXX..XXXXXXX 100644 |
20 | int idx; | 25 | --- a/include/qemu/coroutine.h |
21 | 26 | +++ b/include/qemu/coroutine.h | |
22 | if (rs->hist_entry == 0) | 27 | @@ -XXX,XX +XXX,XX @@ void qemu_co_rwlock_wrlock(CoRwlock *lock); |
23 | - return; | 28 | */ |
24 | + return; | 29 | void qemu_co_rwlock_unlock(CoRwlock *lock); |
25 | if (rs->hist_entry == -1) { | 30 | |
26 | - /* Find latest entry */ | 31 | -typedef struct QemuCoSleepState QemuCoSleepState; |
27 | - for (idx = 0; idx < READLINE_MAX_CMDS; idx++) { | 32 | +typedef struct QemuCoSleep { |
28 | - if (rs->history[idx] == NULL) | 33 | + Coroutine *to_wake; |
29 | - break; | 34 | +} QemuCoSleep; |
30 | - } | 35 | |
31 | - rs->hist_entry = idx; | 36 | /** |
32 | + /* Find latest entry */ | 37 | - * Yield the coroutine for a given duration. During this yield, @sleep_state |
33 | + for (idx = 0; idx < READLINE_MAX_CMDS; idx++) { | 38 | - * is set to an opaque pointer, which may be used for |
34 | + if (rs->history[idx] == NULL) | 39 | - * qemu_co_sleep_wake(). Be careful, the pointer is set back to zero when the |
35 | + break; | 40 | - * timer fires. Don't save the obtained value to other variables and don't call |
36 | + } | 41 | - * qemu_co_sleep_wake from another aio context. |
37 | + rs->hist_entry = idx; | 42 | + * Yield the coroutine for a given duration. Initializes @w so that, |
43 | + * during this yield, it can be passed to qemu_co_sleep_wake() to | ||
44 | + * terminate the sleep. | ||
45 | */ | ||
46 | -void coroutine_fn qemu_co_sleep_ns_wakeable(QEMUClockType type, int64_t ns, | ||
47 | - QemuCoSleepState **sleep_state); | ||
48 | +void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w, | ||
49 | + QEMUClockType type, int64_t ns); | ||
50 | + | ||
51 | static inline void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns) | ||
52 | { | ||
53 | - QemuCoSleepState *unused = NULL; | ||
54 | - qemu_co_sleep_ns_wakeable(type, ns, &unused); | ||
55 | + QemuCoSleep w = { 0 }; | ||
56 | + qemu_co_sleep_ns_wakeable(&w, type, ns); | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | @@ -XXX,XX +XXX,XX @@ static inline void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns) | ||
61 | * qemu_co_sleep_ns() and should be checked to be non-NULL before calling | ||
62 | * qemu_co_sleep_wake(). | ||
63 | */ | ||
64 | -void qemu_co_sleep_wake(QemuCoSleepState *sleep_state); | ||
65 | +void qemu_co_sleep_wake(QemuCoSleep *w); | ||
66 | |||
67 | /** | ||
68 | * Yield until a file descriptor becomes readable | ||
69 | diff --git a/block/block-copy.c b/block/block-copy.c | ||
70 | index XXXXXXX..XXXXXXX 100644 | ||
71 | --- a/block/block-copy.c | ||
72 | +++ b/block/block-copy.c | ||
73 | @@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyCallState { | ||
74 | /* State */ | ||
75 | int ret; | ||
76 | bool finished; | ||
77 | - QemuCoSleepState *sleep_state; | ||
78 | + QemuCoSleep sleep; | ||
79 | bool cancelled; | ||
80 | |||
81 | /* OUT parameters */ | ||
82 | @@ -XXX,XX +XXX,XX @@ block_copy_dirty_clusters(BlockCopyCallState *call_state) | ||
83 | if (ns > 0) { | ||
84 | block_copy_task_end(task, -EAGAIN); | ||
85 | g_free(task); | ||
86 | - qemu_co_sleep_ns_wakeable(QEMU_CLOCK_REALTIME, ns, | ||
87 | - &call_state->sleep_state); | ||
88 | + qemu_co_sleep_ns_wakeable(&call_state->sleep, | ||
89 | + QEMU_CLOCK_REALTIME, ns); | ||
90 | continue; | ||
91 | } | ||
92 | } | ||
93 | @@ -XXX,XX +XXX,XX @@ out: | ||
94 | |||
95 | void block_copy_kick(BlockCopyCallState *call_state) | ||
96 | { | ||
97 | - qemu_co_sleep_wake(call_state->sleep_state); | ||
98 | + qemu_co_sleep_wake(&call_state->sleep); | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | diff --git a/block/nbd.c b/block/nbd.c | ||
103 | index XXXXXXX..XXXXXXX 100644 | ||
104 | --- a/block/nbd.c | ||
105 | +++ b/block/nbd.c | ||
106 | @@ -XXX,XX +XXX,XX @@ typedef struct BDRVNBDState { | ||
107 | CoQueue free_sema; | ||
108 | Coroutine *connection_co; | ||
109 | Coroutine *teardown_co; | ||
110 | - QemuCoSleepState *connection_co_sleep_ns_state; | ||
111 | + QemuCoSleep reconnect_sleep; | ||
112 | bool drained; | ||
113 | bool wait_drained_end; | ||
114 | int in_flight; | ||
115 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn nbd_client_co_drain_begin(BlockDriverState *bs) | ||
116 | BDRVNBDState *s = (BDRVNBDState *)bs->opaque; | ||
117 | |||
118 | s->drained = true; | ||
119 | - qemu_co_sleep_wake(s->connection_co_sleep_ns_state); | ||
120 | + qemu_co_sleep_wake(&s->reconnect_sleep); | ||
121 | |||
122 | nbd_co_establish_connection_cancel(bs, false); | ||
123 | |||
124 | @@ -XXX,XX +XXX,XX @@ static void nbd_teardown_connection(BlockDriverState *bs) | ||
125 | |||
126 | s->state = NBD_CLIENT_QUIT; | ||
127 | if (s->connection_co) { | ||
128 | - qemu_co_sleep_wake(s->connection_co_sleep_ns_state); | ||
129 | + qemu_co_sleep_wake(&s->reconnect_sleep); | ||
130 | nbd_co_establish_connection_cancel(bs, true); | ||
38 | } | 131 | } |
39 | rs->hist_entry--; | 132 | if (qemu_in_coroutine()) { |
40 | if (rs->hist_entry >= 0) { | 133 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn void nbd_co_reconnect_loop(BDRVNBDState *s) |
41 | - pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf), | 134 | } |
42 | + pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf), | 135 | bdrv_inc_in_flight(s->bs); |
43 | rs->history[rs->hist_entry]); | 136 | } else { |
44 | - rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf); | 137 | - qemu_co_sleep_ns_wakeable(QEMU_CLOCK_REALTIME, timeout, |
45 | + rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf); | 138 | - &s->connection_co_sleep_ns_state); |
139 | + qemu_co_sleep_ns_wakeable(&s->reconnect_sleep, | ||
140 | + QEMU_CLOCK_REALTIME, timeout); | ||
141 | if (s->drained) { | ||
142 | continue; | ||
143 | } | ||
144 | diff --git a/util/qemu-coroutine-sleep.c b/util/qemu-coroutine-sleep.c | ||
145 | index XXXXXXX..XXXXXXX 100644 | ||
146 | --- a/util/qemu-coroutine-sleep.c | ||
147 | +++ b/util/qemu-coroutine-sleep.c | ||
148 | @@ -XXX,XX +XXX,XX @@ | ||
149 | |||
150 | static const char *qemu_co_sleep_ns__scheduled = "qemu_co_sleep_ns"; | ||
151 | |||
152 | -struct QemuCoSleepState { | ||
153 | +void qemu_co_sleep_wake(QemuCoSleep *w) | ||
154 | +{ | ||
155 | Coroutine *co; | ||
156 | - QemuCoSleepState **user_state_pointer; | ||
157 | -}; | ||
158 | |||
159 | -void qemu_co_sleep_wake(QemuCoSleepState *sleep_state) | ||
160 | -{ | ||
161 | - if (sleep_state) { | ||
162 | + co = w->to_wake; | ||
163 | + w->to_wake = NULL; | ||
164 | + if (co) { | ||
165 | /* Write of schedule protected by barrier write in aio_co_schedule */ | ||
166 | - const char *scheduled = qatomic_cmpxchg(&sleep_state->co->scheduled, | ||
167 | + const char *scheduled = qatomic_cmpxchg(&co->scheduled, | ||
168 | qemu_co_sleep_ns__scheduled, NULL); | ||
169 | |||
170 | assert(scheduled == qemu_co_sleep_ns__scheduled); | ||
171 | - *sleep_state->user_state_pointer = NULL; | ||
172 | - aio_co_wake(sleep_state->co); | ||
173 | + aio_co_wake(co); | ||
46 | } | 174 | } |
47 | } | 175 | } |
48 | 176 | ||
49 | @@ -XXX,XX +XXX,XX @@ static void readline_down_char(ReadLineState *rs) | 177 | static void co_sleep_cb(void *opaque) |
50 | return; | 178 | { |
51 | if (rs->hist_entry < READLINE_MAX_CMDS - 1 && | 179 | - QemuCoSleepState **sleep_state = opaque; |
52 | rs->history[++rs->hist_entry] != NULL) { | 180 | - qemu_co_sleep_wake(*sleep_state); |
53 | - pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf), | 181 | + QemuCoSleep *w = opaque; |
54 | + pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf), | 182 | + qemu_co_sleep_wake(w); |
55 | rs->history[rs->hist_entry]); | 183 | } |
56 | } else { | 184 | |
57 | rs->cmd_buf[0] = 0; | 185 | -void coroutine_fn qemu_co_sleep_ns_wakeable(QEMUClockType type, int64_t ns, |
58 | - rs->hist_entry = -1; | 186 | - QemuCoSleepState **sleep_state) |
59 | + rs->hist_entry = -1; | 187 | +void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w, |
188 | + QEMUClockType type, int64_t ns) | ||
189 | { | ||
190 | + Coroutine *co = qemu_coroutine_self(); | ||
191 | AioContext *ctx = qemu_get_current_aio_context(); | ||
192 | QEMUTimer ts; | ||
193 | - QemuCoSleepState state = { | ||
194 | - .co = qemu_coroutine_self(), | ||
195 | - .user_state_pointer = sleep_state, | ||
196 | - }; | ||
197 | |||
198 | - const char *scheduled = qatomic_cmpxchg(&state.co->scheduled, NULL, | ||
199 | - qemu_co_sleep_ns__scheduled); | ||
200 | + const char *scheduled = qatomic_cmpxchg(&co->scheduled, NULL, | ||
201 | + qemu_co_sleep_ns__scheduled); | ||
202 | if (scheduled) { | ||
203 | fprintf(stderr, | ||
204 | "%s: Co-routine was already scheduled in '%s'\n", | ||
205 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_sleep_ns_wakeable(QEMUClockType type, int64_t ns, | ||
206 | abort(); | ||
60 | } | 207 | } |
61 | rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf); | 208 | |
62 | } | 209 | - aio_timer_init(ctx, &ts, type, SCALE_NS, co_sleep_cb, sleep_state); |
63 | @@ -XXX,XX +XXX,XX @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline) | 210 | - *sleep_state = &state; |
64 | int idx; | 211 | + w->to_wake = co; |
65 | 212 | + aio_timer_init(ctx, &ts, type, SCALE_NS, co_sleep_cb, w), | |
66 | if (cmdline[0] == '\0') | 213 | timer_mod(&ts, qemu_clock_get_ns(type) + ns); |
67 | - return; | 214 | qemu_coroutine_yield(); |
68 | + return; | 215 | timer_del(&ts); |
69 | new_entry = NULL; | 216 | |
70 | if (rs->hist_entry != -1) { | 217 | - /* qemu_co_sleep_wake clears *sleep_state before resuming this coroutine. */ |
71 | - /* We were editing an existing history entry: replace it */ | 218 | - assert(*sleep_state == NULL); |
72 | - hist_entry = rs->history[rs->hist_entry]; | 219 | + /* w->to_wake is cleared before resuming this coroutine. */ |
73 | - idx = rs->hist_entry; | 220 | + assert(w->to_wake == NULL); |
74 | - if (strcmp(hist_entry, cmdline) == 0) { | 221 | } |
75 | - goto same_entry; | ||
76 | - } | ||
77 | + /* We were editing an existing history entry: replace it */ | ||
78 | + hist_entry = rs->history[rs->hist_entry]; | ||
79 | + idx = rs->hist_entry; | ||
80 | + if (strcmp(hist_entry, cmdline) == 0) { | ||
81 | + goto same_entry; | ||
82 | + } | ||
83 | } | ||
84 | /* Search cmdline in history buffers */ | ||
85 | for (idx = 0; idx < READLINE_MAX_CMDS; idx++) { | ||
86 | - hist_entry = rs->history[idx]; | ||
87 | - if (hist_entry == NULL) | ||
88 | - break; | ||
89 | - if (strcmp(hist_entry, cmdline) == 0) { | ||
90 | - same_entry: | ||
91 | - new_entry = hist_entry; | ||
92 | - /* Put this entry at the end of history */ | ||
93 | - memmove(&rs->history[idx], &rs->history[idx + 1], | ||
94 | - (READLINE_MAX_CMDS - (idx + 1)) * sizeof(char *)); | ||
95 | - rs->history[READLINE_MAX_CMDS - 1] = NULL; | ||
96 | - for (; idx < READLINE_MAX_CMDS; idx++) { | ||
97 | - if (rs->history[idx] == NULL) | ||
98 | - break; | ||
99 | - } | ||
100 | - break; | ||
101 | - } | ||
102 | + hist_entry = rs->history[idx]; | ||
103 | + if (hist_entry == NULL) | ||
104 | + break; | ||
105 | + if (strcmp(hist_entry, cmdline) == 0) { | ||
106 | + same_entry: | ||
107 | + new_entry = hist_entry; | ||
108 | + /* Put this entry at the end of history */ | ||
109 | + memmove(&rs->history[idx], &rs->history[idx + 1], | ||
110 | + (READLINE_MAX_CMDS - (idx + 1)) * sizeof(char *)); | ||
111 | + rs->history[READLINE_MAX_CMDS - 1] = NULL; | ||
112 | + for (; idx < READLINE_MAX_CMDS; idx++) { | ||
113 | + if (rs->history[idx] == NULL) | ||
114 | + break; | ||
115 | + } | ||
116 | + break; | ||
117 | + } | ||
118 | } | ||
119 | if (idx == READLINE_MAX_CMDS) { | ||
120 | - /* Need to get one free slot */ | ||
121 | + /* Need to get one free slot */ | ||
122 | g_free(rs->history[0]); | ||
123 | - memmove(rs->history, &rs->history[1], | ||
124 | - (READLINE_MAX_CMDS - 1) * sizeof(char *)); | ||
125 | - rs->history[READLINE_MAX_CMDS - 1] = NULL; | ||
126 | - idx = READLINE_MAX_CMDS - 1; | ||
127 | + memmove(rs->history, &rs->history[1], | ||
128 | + (READLINE_MAX_CMDS - 1) * sizeof(char *)); | ||
129 | + rs->history[READLINE_MAX_CMDS - 1] = NULL; | ||
130 | + idx = READLINE_MAX_CMDS - 1; | ||
131 | } | ||
132 | if (new_entry == NULL) | ||
133 | new_entry = g_strdup(cmdline); | ||
134 | @@ -XXX,XX +XXX,XX @@ void readline_handle_byte(ReadLineState *rs, int ch) | ||
135 | case 8: | ||
136 | readline_backspace(rs); | ||
137 | break; | ||
138 | - case 155: | ||
139 | + case 155: | ||
140 | rs->esc_state = IS_CSI; | ||
141 | - break; | ||
142 | + break; | ||
143 | default: | ||
144 | if (ch >= 32) { | ||
145 | readline_insert_char(rs, ch); | ||
146 | @@ -XXX,XX +XXX,XX @@ void readline_handle_byte(ReadLineState *rs, int ch) | ||
147 | break; | ||
148 | case IS_CSI: | ||
149 | switch (ch) { | ||
150 | - case 'A': | ||
151 | - case 'F': | ||
152 | - readline_up_char(rs); | ||
153 | - break; | ||
154 | - case 'B': | ||
155 | - case 'E': | ||
156 | - readline_down_char(rs); | ||
157 | - break; | ||
158 | + case 'A': | ||
159 | + case 'F': | ||
160 | + readline_up_char(rs); | ||
161 | + break; | ||
162 | + case 'B': | ||
163 | + case 'E': | ||
164 | + readline_down_char(rs); | ||
165 | + break; | ||
166 | case 'D': | ||
167 | readline_backward_char(rs); | ||
168 | break; | ||
169 | -- | 222 | -- |
170 | 2.21.0 | 223 | 2.31.1 |
171 | 224 | ||
172 | diff view generated by jsdifflib |
1 | From: Jules Irenge <jbi.octave@gmail.com> | 1 | From: Paolo Bonzini <pbonzini@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | util/readline: add a space to fix errors reported by checkpatch.pl tool | 3 | Allow using QemuCoSleep to sleep forever until woken by qemu_co_sleep_wake. |
4 | "ERROR: space required before the open parenthesis" | 4 | This makes the logic of qemu_co_sleep_ns_wakeable easy to understand. |
5 | "ERROR: space required after that ..." | ||
6 | within "util/redline.c" file | ||
7 | 5 | ||
8 | Signed-off-by: Jules Irenge <jbi.octave@gmail.com> | 6 | In the future we will introduce an API that can work even if the |
9 | Reviewed-by: Thomas Huth <thuth@redhat.com> | 7 | sleep and wake happen from different threads. For now, initializing |
10 | Message-id: 20190401024406.10819-2-jbi.octave@gmail.com | 8 | w->to_wake after timer_mod is fine because the timer can only fire in |
11 | Message-Id: <20190401024406.10819-2-jbi.octave@gmail.com> | 9 | the same AioContext. |
10 | |||
11 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
12 | Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | ||
13 | Message-id: 20210517100548.28806-7-pbonzini@redhat.com | ||
12 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | 14 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> |
13 | --- | 15 | --- |
14 | util/readline.c | 34 +++++++++++++++++----------------- | 16 | include/qemu/coroutine.h | 5 +++++ |
15 | 1 file changed, 17 insertions(+), 17 deletions(-) | 17 | util/qemu-coroutine-sleep.c | 26 +++++++++++++++++++------- |
18 | 2 files changed, 24 insertions(+), 7 deletions(-) | ||
16 | 19 | ||
17 | diff --git a/util/readline.c b/util/readline.c | 20 | diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h |
18 | index XXXXXXX..XXXXXXX 100644 | 21 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/util/readline.c | 22 | --- a/include/qemu/coroutine.h |
20 | +++ b/util/readline.c | 23 | +++ b/include/qemu/coroutine.h |
21 | @@ -XXX,XX +XXX,XX @@ static void readline_update(ReadLineState *rs) | 24 | @@ -XXX,XX +XXX,XX @@ typedef struct QemuCoSleep { |
22 | 25 | void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w, | |
23 | if (rs->cmd_buf_size != rs->last_cmd_buf_size || | 26 | QEMUClockType type, int64_t ns); |
24 | memcmp(rs->cmd_buf, rs->last_cmd_buf, rs->cmd_buf_size) != 0) { | 27 | |
25 | - for(i = 0; i < rs->last_cmd_buf_index; i++) { | 28 | +/** |
26 | + for (i = 0; i < rs->last_cmd_buf_index; i++) { | 29 | + * Yield the coroutine until the next call to qemu_co_sleep_wake. |
27 | rs->printf_func(rs->opaque, "\033[D"); | 30 | + */ |
28 | } | 31 | +void coroutine_fn qemu_co_sleep(QemuCoSleep *w); |
29 | rs->cmd_buf[rs->cmd_buf_size] = '\0'; | 32 | + |
30 | if (rs->read_password) { | 33 | static inline void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns) |
31 | len = strlen(rs->cmd_buf); | ||
32 | - for(i = 0; i < len; i++) | ||
33 | + for (i = 0; i < len; i++) | ||
34 | rs->printf_func(rs->opaque, "*"); | ||
35 | } else { | ||
36 | rs->printf_func(rs->opaque, "%s", rs->cmd_buf); | ||
37 | @@ -XXX,XX +XXX,XX @@ static void readline_update(ReadLineState *rs) | ||
38 | if (rs->cmd_buf_index != rs->last_cmd_buf_index) { | ||
39 | delta = rs->cmd_buf_index - rs->last_cmd_buf_index; | ||
40 | if (delta > 0) { | ||
41 | - for(i = 0;i < delta; i++) { | ||
42 | + for (i = 0; i < delta; i++) { | ||
43 | rs->printf_func(rs->opaque, "\033[C"); | ||
44 | } | ||
45 | } else { | ||
46 | delta = -delta; | ||
47 | - for(i = 0;i < delta; i++) { | ||
48 | + for (i = 0; i < delta; i++) { | ||
49 | rs->printf_func(rs->opaque, "\033[D"); | ||
50 | } | ||
51 | } | ||
52 | @@ -XXX,XX +XXX,XX @@ static void readline_completion(ReadLineState *rs) | ||
53 | return; | ||
54 | if (rs->nb_completions == 1) { | ||
55 | len = strlen(rs->completions[0]); | ||
56 | - for(i = rs->completion_index; i < len; i++) { | ||
57 | + for (i = rs->completion_index; i < len; i++) { | ||
58 | readline_insert_char(rs, rs->completions[0][i]); | ||
59 | } | ||
60 | /* extra space for next argument. XXX: make it more generic */ | ||
61 | @@ -XXX,XX +XXX,XX @@ static void readline_completion(ReadLineState *rs) | ||
62 | completion_comp); | ||
63 | rs->printf_func(rs->opaque, "\n"); | ||
64 | max_width = 0; | ||
65 | - max_prefix = 0; | ||
66 | - for(i = 0; i < rs->nb_completions; i++) { | ||
67 | + max_prefix = 0; | ||
68 | + for (i = 0; i < rs->nb_completions; i++) { | ||
69 | len = strlen(rs->completions[i]); | ||
70 | - if (i==0) { | ||
71 | + if (i == 0) { | ||
72 | max_prefix = len; | ||
73 | } else { | ||
74 | if (len < max_prefix) | ||
75 | max_prefix = len; | ||
76 | - for(j=0; j<max_prefix; j++) { | ||
77 | + for (j = 0; j < max_prefix; j++) { | ||
78 | if (rs->completions[i][j] != rs->completions[0][j]) | ||
79 | max_prefix = j; | ||
80 | } | ||
81 | @@ -XXX,XX +XXX,XX @@ static void readline_completion(ReadLineState *rs) | ||
82 | if (len > max_width) | ||
83 | max_width = len; | ||
84 | } | ||
85 | - if (max_prefix > 0) | ||
86 | - for(i = rs->completion_index; i < max_prefix; i++) { | ||
87 | + if (max_prefix > 0) | ||
88 | + for (i = rs->completion_index; i < max_prefix; i++) { | ||
89 | readline_insert_char(rs, rs->completions[0][i]); | ||
90 | } | ||
91 | max_width += 2; | ||
92 | @@ -XXX,XX +XXX,XX @@ static void readline_completion(ReadLineState *rs) | ||
93 | max_width = 80; | ||
94 | nb_cols = 80 / max_width; | ||
95 | j = 0; | ||
96 | - for(i = 0; i < rs->nb_completions; i++) { | ||
97 | + for (i = 0; i < rs->nb_completions; i++) { | ||
98 | rs->printf_func(rs->opaque, "%-*s", max_width, rs->completions[i]); | ||
99 | if (++j == nb_cols || i == (rs->nb_completions - 1)) { | ||
100 | rs->printf_func(rs->opaque, "\n"); | ||
101 | @@ -XXX,XX +XXX,XX @@ static void readline_clear_screen(ReadLineState *rs) | ||
102 | /* return true if command handled */ | ||
103 | void readline_handle_byte(ReadLineState *rs, int ch) | ||
104 | { | 34 | { |
105 | - switch(rs->esc_state) { | 35 | QemuCoSleep w = { 0 }; |
106 | + switch (rs->esc_state) { | 36 | diff --git a/util/qemu-coroutine-sleep.c b/util/qemu-coroutine-sleep.c |
107 | case IS_NORM: | 37 | index XXXXXXX..XXXXXXX 100644 |
108 | - switch(ch) { | 38 | --- a/util/qemu-coroutine-sleep.c |
109 | + switch (ch) { | 39 | +++ b/util/qemu-coroutine-sleep.c |
110 | case 1: | 40 | @@ -XXX,XX +XXX,XX @@ static void co_sleep_cb(void *opaque) |
111 | readline_bol(rs); | 41 | qemu_co_sleep_wake(w); |
112 | break; | 42 | } |
113 | @@ -XXX,XX +XXX,XX @@ void readline_handle_byte(ReadLineState *rs, int ch) | 43 | |
114 | } | 44 | -void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w, |
115 | break; | 45 | - QEMUClockType type, int64_t ns) |
116 | case IS_CSI: | 46 | +void coroutine_fn qemu_co_sleep(QemuCoSleep *w) |
117 | - switch(ch) { | 47 | { |
118 | + switch (ch) { | 48 | Coroutine *co = qemu_coroutine_self(); |
119 | case 'A': | 49 | - AioContext *ctx = qemu_get_current_aio_context(); |
120 | case 'F': | 50 | - QEMUTimer ts; |
121 | readline_up_char(rs); | 51 | |
122 | @@ -XXX,XX +XXX,XX @@ void readline_handle_byte(ReadLineState *rs, int ch) | 52 | const char *scheduled = qatomic_cmpxchg(&co->scheduled, NULL, |
123 | rs->esc_param = rs->esc_param * 10 + (ch - '0'); | 53 | qemu_co_sleep_ns__scheduled); |
124 | goto the_end; | 54 | @@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w, |
125 | case '~': | 55 | } |
126 | - switch(rs->esc_param) { | 56 | |
127 | + switch (rs->esc_param) { | 57 | w->to_wake = co; |
128 | case 1: | 58 | - aio_timer_init(ctx, &ts, type, SCALE_NS, co_sleep_cb, w), |
129 | readline_bol(rs); | 59 | - timer_mod(&ts, qemu_clock_get_ns(type) + ns); |
130 | break; | 60 | qemu_coroutine_yield(); |
131 | @@ -XXX,XX +XXX,XX @@ void readline_handle_byte(ReadLineState *rs, int ch) | 61 | - timer_del(&ts); |
132 | the_end: | 62 | |
133 | break; | 63 | /* w->to_wake is cleared before resuming this coroutine. */ |
134 | case IS_SS3: | 64 | assert(w->to_wake == NULL); |
135 | - switch(ch) { | 65 | } |
136 | + switch (ch) { | 66 | + |
137 | case 'F': | 67 | +void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w, |
138 | readline_eol(rs); | 68 | + QEMUClockType type, int64_t ns) |
139 | break; | 69 | +{ |
70 | + AioContext *ctx = qemu_get_current_aio_context(); | ||
71 | + QEMUTimer ts; | ||
72 | + | ||
73 | + aio_timer_init(ctx, &ts, type, SCALE_NS, co_sleep_cb, w); | ||
74 | + timer_mod(&ts, qemu_clock_get_ns(type) + ns); | ||
75 | + | ||
76 | + /* | ||
77 | + * The timer will fire in the current AiOContext, so the callback | ||
78 | + * must happen after qemu_co_sleep yields and there is no race | ||
79 | + * between timer_mod and qemu_co_sleep. | ||
80 | + */ | ||
81 | + qemu_co_sleep(w); | ||
82 | + timer_del(&ts); | ||
83 | +} | ||
140 | -- | 84 | -- |
141 | 2.21.0 | 85 | 2.31.1 |
142 | 86 | ||
143 | diff view generated by jsdifflib |