← Documents Documentation/virt/kvm/vcpu-requests.rst GitHub 원문 ↗

Linux 6.18.37 · 가상화 / KVM / vCPU

KVM VCPU Requests

vCPU request 비트맵, kick, mode, memory barrier와 IPI 동기화 규칙을 설명합니다.

Source pathDocumentation/virt/kvm/vcpu-requests.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

vcpu-requests.rst:1-294

vCPU request 비트맵, kick, mode, memory barrier와 IPI 동기화 규칙을 설명합니다.

요청 생성부터 guest exit, acknowledgement와 sleep 처리까지 함수·상태·코드 순서를 보존했습니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 =================
4 KVM VCPU Requests
5 =================
6
7 Overview
8 ========
9
10 KVM supports an internal API enabling threads to request a VCPU thread to
11 perform some activity. For example, a thread may request a VCPU to flush
12 its TLB with a VCPU request. The API consists of the following functions::
13
14 /* Check if any requests are pending for VCPU @vcpu. */
15 bool kvm_request_pending(struct kvm_vcpu *vcpu);
16
17 /* Check if VCPU @vcpu has request @req pending. */
18 bool kvm_test_request(int req, struct kvm_vcpu *vcpu);
19
20 /* Clear request @req for VCPU @vcpu. */
21 void kvm_clear_request(int req, struct kvm_vcpu *vcpu);
22
23 /*
24 * Check if VCPU @vcpu has request @req pending. When the request is
25 * pending it will be cleared and a memory barrier, which pairs with
26 * another in kvm_make_request(), will be issued.
27 */
28 bool kvm_check_request(int req, struct kvm_vcpu *vcpu);
29
30 /*
31 * Make request @req of VCPU @vcpu. Issues a memory barrier, which pairs
32 * with another in kvm_check_request(), prior to setting the request.
33 */
34 void kvm_make_request(int req, struct kvm_vcpu *vcpu);
35
36 /* Make request @req of all VCPUs of the VM with struct kvm @kvm. */
37 bool kvm_make_all_cpus_request(struct kvm *kvm, unsigned int req);
38
39 Typically a requester wants the VCPU to perform the activity as soon
40 as possible after making the request. This means most requests
41 (kvm_make_request() calls) are followed by a call to kvm_vcpu_kick(),
42 and kvm_make_all_cpus_request() has the kicking of all VCPUs built
43 into it.
44
45 VCPU Kicks
46 ----------
47
48 The goal of a VCPU kick is to bring a VCPU thread out of guest mode in
49 order to perform some KVM maintenance. To do so, an IPI is sent, forcing
50 a guest mode exit. However, a VCPU thread may not be in guest mode at the
51 time of the kick. Therefore, depending on the mode and state of the VCPU
52 thread, there are two other actions a kick may take. All three actions
53 are listed below:
54
55 1) Send an IPI. This forces a guest mode exit.
56 2) Waking a sleeping VCPU. Sleeping VCPUs are VCPU threads outside guest
57 mode that wait on waitqueues. Waking them removes the threads from
58 the waitqueues, allowing the threads to run again. This behavior
59 may be suppressed, see KVM_REQUEST_NO_WAKEUP below.
60 3) Nothing. When the VCPU is not in guest mode and the VCPU thread is not
61 sleeping, then there is nothing to do.
62
63 VCPU Mode
64 ---------
65
66 VCPUs have a mode state, ``vcpu->mode``, that is used to track whether the
67 guest is running in guest mode or not, as well as some specific
68 outside guest mode states. The architecture may use ``vcpu->mode`` to
69 ensure VCPU requests are seen by VCPUs (see "Ensuring Requests Are Seen"),
70 as well as to avoid sending unnecessary IPIs (see "IPI Reduction"), and
71 even to ensure IPI acknowledgements are waited upon (see "Waiting for
72 Acknowledgements"). The following modes are defined:
73
74 OUTSIDE_GUEST_MODE
75
76 The VCPU thread is outside guest mode.
77
78 IN_GUEST_MODE
79
80 The VCPU thread is in guest mode.
81
82 EXITING_GUEST_MODE
83
84 The VCPU thread is transitioning from IN_GUEST_MODE to
85 OUTSIDE_GUEST_MODE.
86
87 READING_SHADOW_PAGE_TABLES
88
89 The VCPU thread is outside guest mode, but it wants the sender of
90 certain VCPU requests, namely KVM_REQ_TLB_FLUSH, to wait until the VCPU
91 thread is done reading the page tables.
92
93 VCPU Request Internals
94 ======================
95
96 VCPU requests are simply bit indices of the ``vcpu->requests`` bitmap.
97 This means general bitops, like those documented in [atomic-ops]_ could
98 also be used, e.g. ::
99
100 clear_bit(KVM_REQ_UNBLOCK & KVM_REQUEST_MASK, &vcpu->requests);
101
102 However, VCPU request users should refrain from doing so, as it would
103 break the abstraction. The first 8 bits are reserved for architecture
104 independent requests; all additional bits are available for architecture
105 dependent requests.
106
107 Architecture Independent Requests
108 ---------------------------------
109
110 KVM_REQ_TLB_FLUSH
111
112 KVM's common MMU notifier may need to flush all of a guest's TLB
113 entries, calling kvm_flush_remote_tlbs() to do so. Architectures that
114 choose to use the common kvm_flush_remote_tlbs() implementation will
115 need to handle this VCPU request.
116
117 KVM_REQ_VM_DEAD
118
119 This request informs all VCPUs that the VM is dead and unusable, e.g. due to
120 fatal error or because the VM's state has been intentionally destroyed.
121
122 KVM_REQ_UNBLOCK
123
124 This request informs the vCPU to exit kvm_vcpu_block. It is used for
125 example from timer handlers that run on the host on behalf of a vCPU,
126 or in order to update the interrupt routing and ensure that assigned
127 devices will wake up the vCPU.
128
129 KVM_REQ_OUTSIDE_GUEST_MODE
130
131 This "request" ensures the target vCPU has exited guest mode prior to the
132 sender of the request continuing on. No action needs be taken by the target,
133 and so no request is actually logged for the target. This request is similar
134 to a "kick", but unlike a kick it guarantees the vCPU has actually exited
135 guest mode. A kick only guarantees the vCPU will exit at some point in the
136 future, e.g. a previous kick may have started the process, but there's no
137 guarantee the to-be-kicked vCPU has fully exited guest mode.
138
139 KVM_REQUEST_MASK
140 ----------------
141
142 VCPU requests should be masked by KVM_REQUEST_MASK before using them with
143 bitops. This is because only the lower 8 bits are used to represent the
144 request's number. The upper bits are used as flags. Currently only two
145 flags are defined.
146
147 VCPU Request Flags
148 ------------------
149
150 KVM_REQUEST_NO_WAKEUP
151
152 This flag is applied to requests that only need immediate attention
153 from VCPUs running in guest mode. That is, sleeping VCPUs do not need
154 to be awakened for these requests. Sleeping VCPUs will handle the
155 requests when they are awakened later for some other reason.
156
157 KVM_REQUEST_WAIT
158
159 When requests with this flag are made with kvm_make_all_cpus_request(),
160 then the caller will wait for each VCPU to acknowledge its IPI before
161 proceeding. This flag only applies to VCPUs that would receive IPIs.
162 If, for example, the VCPU is sleeping, so no IPI is necessary, then
163 the requesting thread does not wait. This means that this flag may be
164 safely combined with KVM_REQUEST_NO_WAKEUP. See "Waiting for
165 Acknowledgements" for more information about requests with
166 KVM_REQUEST_WAIT.
167
168 VCPU Requests with Associated State
169 ===================================
170
171 Requesters that want the receiving VCPU to handle new state need to ensure
172 the newly written state is observable to the receiving VCPU thread's CPU
173 by the time it observes the request. This means a write memory barrier
174 must be inserted after writing the new state and before setting the VCPU
175 request bit. Additionally, on the receiving VCPU thread's side, a
176 corresponding read barrier must be inserted after reading the request bit
177 and before proceeding to read the new state associated with it. See
178 scenario 3, Message and Flag, of [lwn-mb]_ and the kernel documentation
179 [memory-barriers]_.
180
181 The pair of functions, kvm_check_request() and kvm_make_request(), provide
182 the memory barriers, allowing this requirement to be handled internally by
183 the API.
184
185 Ensuring Requests Are Seen
186 ==========================
187
188 When making requests to VCPUs, we want to avoid the receiving VCPU
189 executing in guest mode for an arbitrary long time without handling the
190 request. We can be sure this won't happen as long as we ensure the VCPU
191 thread checks kvm_request_pending() before entering guest mode and that a
192 kick will send an IPI to force an exit from guest mode when necessary.
193 Extra care must be taken to cover the period after the VCPU thread's last
194 kvm_request_pending() check and before it has entered guest mode, as kick
195 IPIs will only trigger guest mode exits for VCPU threads that are in guest
196 mode or at least have already disabled interrupts in order to prepare to
197 enter guest mode. This means that an optimized implementation (see "IPI
198 Reduction") must be certain when it's safe to not send the IPI. One
199 solution, which all architectures except s390 apply, is to:
200
201 - set ``vcpu->mode`` to IN_GUEST_MODE between disabling the interrupts and
202 the last kvm_request_pending() check;
203 - enable interrupts atomically when entering the guest.
204
205 This solution also requires memory barriers to be placed carefully in both
206 the requesting thread and the receiving VCPU. With the memory barriers we
207 can exclude the possibility of a VCPU thread observing
208 !kvm_request_pending() on its last check and then not receiving an IPI for
209 the next request made of it, even if the request is made immediately after
210 the check. This is done by way of the Dekker memory barrier pattern
211 (scenario 10 of [lwn-mb]_). As the Dekker pattern requires two variables,
212 this solution pairs ``vcpu->mode`` with ``vcpu->requests``. Substituting
213 them into the pattern gives::
214
215 CPU1 CPU2
216 ================= =================
217 local_irq_disable();
218 WRITE_ONCE(vcpu->mode, IN_GUEST_MODE); kvm_make_request(REQ, vcpu);
219 smp_mb(); smp_mb();
220 if (kvm_request_pending(vcpu)) { if (READ_ONCE(vcpu->mode) ==
221 IN_GUEST_MODE) {
222 ...abort guest entry... ...send IPI...
223 } }
224
225 As stated above, the IPI is only useful for VCPU threads in guest mode or
226 that have already disabled interrupts. This is why this specific case of
227 the Dekker pattern has been extended to disable interrupts before setting
228 ``vcpu->mode`` to IN_GUEST_MODE. WRITE_ONCE() and READ_ONCE() are used to
229 pedantically implement the memory barrier pattern, guaranteeing the
230 compiler doesn't interfere with ``vcpu->mode``'s carefully planned
231 accesses.
232
233 IPI Reduction
234 -------------
235
236 As only one IPI is needed to get a VCPU to check for any/all requests,
237 then they may be coalesced. This is easily done by having the first IPI
238 sending kick also change the VCPU mode to something !IN_GUEST_MODE. The
239 transitional state, EXITING_GUEST_MODE, is used for this purpose.
240
241 Waiting for Acknowledgements
242 ----------------------------
243
244 Some requests, those with the KVM_REQUEST_WAIT flag set, require IPIs to
245 be sent, and the acknowledgements to be waited upon, even when the target
246 VCPU threads are in modes other than IN_GUEST_MODE. For example, one case
247 is when a target VCPU thread is in READING_SHADOW_PAGE_TABLES mode, which
248 is set after disabling interrupts. To support these cases, the
249 KVM_REQUEST_WAIT flag changes the condition for sending an IPI from
250 checking that the VCPU is IN_GUEST_MODE to checking that it is not
251 OUTSIDE_GUEST_MODE.
252
253 Request-less VCPU Kicks
254 -----------------------
255
256 As the determination of whether or not to send an IPI depends on the
257 two-variable Dekker memory barrier pattern, then it's clear that
258 request-less VCPU kicks are almost never correct. Without the assurance
259 that a non-IPI generating kick will still result in an action by the
260 receiving VCPU, as the final kvm_request_pending() check does for
261 request-accompanying kicks, then the kick may not do anything useful at
262 all. If, for instance, a request-less kick was made to a VCPU that was
263 just about to set its mode to IN_GUEST_MODE, meaning no IPI is sent, then
264 the VCPU thread may continue its entry without actually having done
265 whatever it was the kick was meant to initiate.
266
267 One exception is x86's posted interrupt mechanism. In this case, however,
268 even the request-less VCPU kick is coupled with the same
269 local_irq_disable() + smp_mb() pattern described above; the ON bit
270 (Outstanding Notification) in the posted interrupt descriptor takes the
271 role of ``vcpu->requests``. When sending a posted interrupt, PIR.ON is
272 set before reading ``vcpu->mode``; dually, in the VCPU thread,
273 vmx_sync_pir_to_irr() reads PIR after setting ``vcpu->mode`` to
274 IN_GUEST_MODE.
275
276 Additional Considerations
277 =========================
278
279 Sleeping VCPUs
280 --------------
281
282 VCPU threads may need to consider requests before and/or after calling
283 functions that may put them to sleep, e.g. kvm_vcpu_block(). Whether they
284 do or not, and, if they do, which requests need consideration, is
285 architecture dependent. kvm_vcpu_block() calls kvm_arch_vcpu_runnable()
286 to check if it should awaken. One reason to do so is to provide
287 architectures a function where requests may be checked if necessary.
288
289 References
290 ==========
291
292 .. [atomic-ops] Documentation/atomic_bitops.txt and Documentation/atomic_t.txt
293 .. [memory-barriers] Documentation/memory-barriers.txt
294 .. [lwn-mb] https://lwn.net/Articles/573436/
295

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

내부 요청 API

1-44

KVM vCPU request는 한 스레드가 특정 vCPU 스레드에 TLB flush 같은 유지보수 작업을 수행하도록 요청하는 내부 API입니다. 요청자는 보통 가능한 빨리 처리되기를 원하므로 `kvm_make_request()` 뒤에 `kvm_vcpu_kick()`을 호출합니다. `kvm_make_all_cpus_request()`는 모든 vCPU kick을 자체적으로 수행합니다.

vCPU request API
함수동작
`kvm_request_pending(vcpu)`vCPU에 하나 이상의 pending request가 있는지 확인
`kvm_test_request(req, vcpu)`특정 request가 pending인지 확인
`kvm_clear_request(req, vcpu)`특정 request 삭제
`kvm_check_request(req, vcpu)`pending이면 삭제하고 `kvm_make_request()`와 짝인 memory barrier 실행
`kvm_make_request(req, vcpu)`request 설정 전에 짝을 이루는 memory barrier 실행
`kvm_make_all_cpus_request(kvm, req)`VM의 모든 vCPU에 request 생성하고 kick

요청 상태 조회, 삭제, 생성의 핵심 함수입니다.

`kvm_test_request()`는 상태를 바꾸지 않는 단순 검사이고 `kvm_check_request()`는 확인과 동시에 bit를 지우며 필요한 barrier까지 실행한다는 차이가 있습니다. 수신 경로가 연관 상태를 소비한다면 후자를 써야 합니다.

단일 vCPU 요청은 request bit 설정과 kick이 별도 단계이지만 all-CPU helper는 전체 vCPU에 대한 두 동작을 하나의 인터페이스로 묶습니다.

.. SPDX-License-Identifier: GPL-2.0

=================
KVM VCPU Requests
=================

Overview
========

KVM supports an internal API enabling threads to request a VCPU thread to
perform some activity.  For example, a thread may request a VCPU to flush
its TLB with a VCPU request.  The API consists of the following functions::

  /* Check if any requests are pending for VCPU @vcpu. */
  bool kvm_request_pending(struct kvm_vcpu *vcpu);

  /* Check if VCPU @vcpu has request @req pending. */
  bool kvm_test_request(int req, struct kvm_vcpu *vcpu);

  /* Clear request @req for VCPU @vcpu. */
  void kvm_clear_request(int req, struct kvm_vcpu *vcpu);

  /*
   * Check if VCPU @vcpu has request @req pending. When the request is
   * pending it will be cleared and a memory barrier, which pairs with
   * another in kvm_make_request(), will be issued.
   */
  bool kvm_check_request(int req, struct kvm_vcpu *vcpu);

  /*
   * Make request @req of VCPU @vcpu. Issues a memory barrier, which pairs
   * with another in kvm_check_request(), prior to setting the request.
   */
  void kvm_make_request(int req, struct kvm_vcpu *vcpu);

  /* Make request @req of all VCPUs of the VM with struct kvm @kvm. */
  bool kvm_make_all_cpus_request(struct kvm *kvm, unsigned int req);

Typically a requester wants the VCPU to perform the activity as soon
as possible after making the request.  This means most requests
(kvm_make_request() calls) are followed by a call to kvm_vcpu_kick(),
and kvm_make_all_cpus_request() has the kicking of all VCPUs built
into it.

vCPU kick

45-62

vCPU kick의 목적은 vCPU 스레드를 guest mode에서 꺼내 KVM 유지보수를 수행하게 하는 것입니다. vCPU가 guest mode이면 IPI를 보내 강제로 exit시킵니다.

kick의 세 동작
상태동작
guest modeIPI를 보내 guest mode exit 강제
guest mode 밖에서 waitqueue sleepwaitqueue에서 깨워 다시 실행; `KVM_REQUEST_NO_WAKEUP`이면 생략 가능
guest mode 밖이며 sleep 아님할 일 없음

kick 시점의 vCPU mode와 sleep 상태에 따라 결과가 달라집니다.

sleep vCPU를 깨우는 것은 guest exit가 아니라 waitqueue에서 runnable 상태로 되돌리는 작업입니다. 반면 이미 실행 중이면서 guest 밖에 있는 vCPU에는 별도의 강제 동작이 필요하지 않습니다.

VCPU Kicks
----------

The goal of a VCPU kick is to bring a VCPU thread out of guest mode in
order to perform some KVM maintenance.  To do so, an IPI is sent, forcing
a guest mode exit.  However, a VCPU thread may not be in guest mode at the
time of the kick.  Therefore, depending on the mode and state of the VCPU
thread, there are two other actions a kick may take.  All three actions
are listed below:

1) Send an IPI.  This forces a guest mode exit.
2) Waking a sleeping VCPU.  Sleeping VCPUs are VCPU threads outside guest
   mode that wait on waitqueues.  Waking them removes the threads from
   the waitqueues, allowing the threads to run again.  This behavior
   may be suppressed, see KVM_REQUEST_NO_WAKEUP below.
3) Nothing.  When the VCPU is not in guest mode and the VCPU thread is not
   sleeping, then there is nothing to do.

vcpu->mode 상태

63-92

`vcpu->mode`는 guest mode 실행 여부와 guest 밖의 특수 상태를 추적합니다. 아키텍처는 이 상태로 request 관측을 보장하고 불필요한 IPI를 줄이며 필요한 경우 IPI acknowledgement를 기다립니다.

vCPU mode
Mode의미
`OUTSIDE_GUEST_MODE`vCPU 스레드가 guest mode 밖
`IN_GUEST_MODE`vCPU 스레드가 guest mode 안
`EXITING_GUEST_MODE`guest mode에서 밖으로 전환 중
`READING_SHADOW_PAGE_TABLES`guest 밖에서 page table을 읽는 중이며 `KVM_REQ_TLB_FLUSH` 요청자가 완료를 기다려야 함

요청자와 대상 vCPU가 동기화할 때 사용하는 네 상태입니다.

`EXITING_GUEST_MODE`는 단순한 상태 설명뿐 아니라 첫 kick 뒤 추가 IPI를 합치기 위한 전이 표지입니다. `READING_SHADOW_PAGE_TABLES`는 guest 밖이라는 사실만으로 안전하지 않은 TLB flush의 완료 경계를 나타냅니다.

VCPU Mode
---------

VCPUs have a mode state, ``vcpu->mode``, that is used to track whether the
guest is running in guest mode or not, as well as some specific
outside guest mode states.  The architecture may use ``vcpu->mode`` to
ensure VCPU requests are seen by VCPUs (see "Ensuring Requests Are Seen"),
as well as to avoid sending unnecessary IPIs (see "IPI Reduction"), and
even to ensure IPI acknowledgements are waited upon (see "Waiting for
Acknowledgements").  The following modes are defined:

OUTSIDE_GUEST_MODE

  The VCPU thread is outside guest mode.

IN_GUEST_MODE

  The VCPU thread is in guest mode.

EXITING_GUEST_MODE

  The VCPU thread is transitioning from IN_GUEST_MODE to
  OUTSIDE_GUEST_MODE.

READING_SHADOW_PAGE_TABLES

  The VCPU thread is outside guest mode, but it wants the sender of
  certain VCPU requests, namely KVM_REQ_TLB_FLUSH, to wait until the VCPU
  thread is done reading the page tables.

request 비트맵 내부 표현

93-106

vCPU request는 `vcpu->requests` 비트맵의 bit index입니다. `clear_bit()` 같은 일반 bit operation으로 직접 다룰 수도 있지만 abstraction을 깨므로 request API를 사용해야 합니다.

처음 8개 비트는 architecture-independent request에 예약되고 나머지 비트는 아키텍처별 request에 사용할 수 있습니다.

원문의 `clear_bit(KVM_REQ_UNBLOCK & KVM_REQUEST_MASK, &vcpu->requests)` 예시는 직접 bit operation이 기술적으로 가능함을 보여줄 뿐 권장 사용법이 아닙니다. 상위 flag bit를 제거하는 마스크 규칙과 API barrier 의미를 우회하기 때문입니다.

VCPU Request Internals
======================

VCPU requests are simply bit indices of the ``vcpu->requests`` bitmap.
This means general bitops, like those documented in [atomic-ops]_ could
also be used, e.g. ::

  clear_bit(KVM_REQ_UNBLOCK & KVM_REQUEST_MASK, &vcpu->requests);

However, VCPU request users should refrain from doing so, as it would
break the abstraction.  The first 8 bits are reserved for architecture
independent requests; all additional bits are available for architecture
dependent requests.

아키텍처 독립 request

107-138
공통 vCPU request
Request의미
`KVM_REQ_TLB_FLUSH`공통 MMU notifier가 `kvm_flush_remote_tlbs()`로 guest TLB 전체를 flush하도록 요청
`KVM_REQ_VM_DEAD`치명적 오류 또는 의도적 상태 폐기로 VM이 죽어 사용할 수 없음을 모든 vCPU에 통지
`KVM_REQ_UNBLOCK`vCPU가 `kvm_vcpu_block`을 빠져나오도록 요청; timer, interrupt routing, assigned device wakeup에 사용
`KVM_REQ_OUTSIDE_GUEST_MODE`요청자가 계속하기 전에 대상이 실제로 guest mode를 완전히 빠져나왔음을 보장

모든 아키텍처가 공유할 수 있는 요청입니다.

`KVM_REQ_OUTSIDE_GUEST_MODE`는 대상이 수행할 작업을 기록하지 않는 특별한 request입니다. kick은 vCPU가 미래 어느 시점에 exit할 것만 보장하지만 이 request는 이미 완전히 exit했음을 보장합니다.

`KVM_REQ_UNBLOCK`은 호스트 timer가 vCPU 대신 실행된 뒤 차단을 풀거나 interrupt routing 변경 후 assigned device가 vCPU를 깨울 수 있게 할 때 사용됩니다. `KVM_REQ_VM_DEAD`는 복구 가능한 유지보수가 아니라 전체 VM을 사용할 수 없게 된 상태를 알립니다.

Architecture Independent Requests
---------------------------------

KVM_REQ_TLB_FLUSH

  KVM's common MMU notifier may need to flush all of a guest's TLB
  entries, calling kvm_flush_remote_tlbs() to do so.  Architectures that
  choose to use the common kvm_flush_remote_tlbs() implementation will
  need to handle this VCPU request.

KVM_REQ_VM_DEAD

  This request informs all VCPUs that the VM is dead and unusable, e.g. due to
  fatal error or because the VM's state has been intentionally destroyed.

KVM_REQ_UNBLOCK

  This request informs the vCPU to exit kvm_vcpu_block.  It is used for
  example from timer handlers that run on the host on behalf of a vCPU,
  or in order to update the interrupt routing and ensure that assigned
  devices will wake up the vCPU.

KVM_REQ_OUTSIDE_GUEST_MODE

  This "request" ensures the target vCPU has exited guest mode prior to the
  sender of the request continuing on.  No action needs be taken by the target,
  and so no request is actually logged for the target.  This request is similar
  to a "kick", but unlike a kick it guarantees the vCPU has actually exited
  guest mode.  A kick only guarantees the vCPU will exit at some point in the
  future, e.g. a previous kick may have started the process, but there's no
  guarantee the to-be-kicked vCPU has fully exited guest mode.

KVM_REQUEST_MASK와 플래그

139-167

request 번호는 하위 8비트에 있고 상위 비트는 flag이므로 bit operation 전에 `KVM_REQUEST_MASK`로 마스킹해야 합니다.

vCPU request flag
Flag동작
`KVM_REQUEST_NO_WAKEUP`guest mode에서 실행 중인 vCPU만 즉시 처리; sleep vCPU는 다른 이유로 깨어날 때 처리
`KVM_REQUEST_WAIT``kvm_make_all_cpus_request()`가 IPI를 받은 각 vCPU의 acknowledgement를 기다림

즉시 wakeup과 IPI 확인 대기를 제어합니다.

sleep 중이라 IPI가 필요 없는 vCPU에 대해서는 `KVM_REQUEST_WAIT` 요청자도 기다리지 않습니다. 따라서 `KVM_REQUEST_WAIT`와 `KVM_REQUEST_NO_WAKEUP`을 안전하게 함께 사용할 수 있습니다.

`KVM_REQUEST_WAIT`의 대기 대상은 request를 받은 모든 vCPU가 아니라 실제 IPI를 받아 acknowledgement 의무가 생긴 vCPU입니다. 이 구분 덕분에 sleep vCPU를 깨우지 않는 요청과 조합해도 불필요한 대기가 발생하지 않습니다.

KVM_REQUEST_MASK
----------------

VCPU requests should be masked by KVM_REQUEST_MASK before using them with
bitops.  This is because only the lower 8 bits are used to represent the
request's number.  The upper bits are used as flags.  Currently only two
flags are defined.

VCPU Request Flags
------------------

KVM_REQUEST_NO_WAKEUP

  This flag is applied to requests that only need immediate attention
  from VCPUs running in guest mode.  That is, sleeping VCPUs do not need
  to be awakened for these requests.  Sleeping VCPUs will handle the
  requests when they are awakened later for some other reason.

KVM_REQUEST_WAIT

  When requests with this flag are made with kvm_make_all_cpus_request(),
  then the caller will wait for each VCPU to acknowledge its IPI before
  proceeding.  This flag only applies to VCPUs that would receive IPIs.
  If, for example, the VCPU is sleeping, so no IPI is necessary, then
  the requesting thread does not wait.  This means that this flag may be
  safely combined with KVM_REQUEST_NO_WAKEUP.  See "Waiting for
  Acknowledgements" for more information about requests with
  KVM_REQUEST_WAIT.

연관 상태의 memory ordering

168-184

request와 함께 새 상태를 전달하는 요청자는 대상 CPU가 request를 관측할 때 새 상태도 볼 수 있게 해야 합니다. 새 상태를 쓴 뒤 request bit를 세우기 전에 write memory barrier가 필요합니다. 수신 vCPU는 request bit를 읽은 뒤 새 상태를 읽기 전에 대응하는 read barrier를 실행해야 합니다.

상태 동반 request
요청자: 새 상태 기록요청자: write memory barrier요청자: request bit 설정수신자: request bit 확인수신자: read memory barrier수신자: 새 상태 읽기

message-and-flag 패턴의 쓰기와 읽기 순서입니다.

`kvm_make_request()`와 `kvm_check_request()` 함수 쌍이 이 memory barrier를 내부에서 제공하므로 사용자가 직접 순서를 구현하지 않아도 됩니다.

이 규칙은 새 상태가 message이고 request bit가 flag인 message-and-flag 패턴입니다. bit만 먼저 보이거나 이전 상태를 읽는 일을 막으려면 송신 write barrier와 수신 read barrier가 반드시 쌍을 이뤄야 합니다.

VCPU Requests with Associated State
===================================

Requesters that want the receiving VCPU to handle new state need to ensure
the newly written state is observable to the receiving VCPU thread's CPU
by the time it observes the request.  This means a write memory barrier
must be inserted after writing the new state and before setting the VCPU
request bit.  Additionally, on the receiving VCPU thread's side, a
corresponding read barrier must be inserted after reading the request bit
and before proceeding to read the new state associated with it.  See
scenario 3, Message and Flag, of [lwn-mb]_ and the kernel documentation
[memory-barriers]_.

The pair of functions, kvm_check_request() and kvm_make_request(), provide
the memory barriers, allowing this requirement to be handled internally by
the API.

request 관측 보장

185-232

vCPU가 request를 처리하지 않은 채 guest mode에서 임의로 오래 실행되지 않게 하려면 guest 진입 전 `kvm_request_pending()`을 확인하고, 필요할 때 kick IPI가 guest mode exit를 강제해야 합니다.

특히 마지막 pending 확인 뒤 실제 guest 진입 전의 틈을 덮어야 합니다. kick IPI는 이미 guest mode에 있거나 guest 진입을 위해 interrupt를 끈 vCPU에서만 exit를 일으킬 수 있으므로 최적화된 구현은 IPI를 생략해도 되는 시점을 정확히 판단해야 합니다.

s390을 제외한 모든 아키텍처는 interrupt를 끈 뒤 마지막 request 확인 전에 `vcpu->mode`를 `IN_GUEST_MODE`로 설정하고 guest 진입 시 interrupt를 원자적으로 켭니다.

Dekker memory barrier 변수
CPU1: vCPU 진입CPU2: 요청자
`local_irq_disable()``kvm_make_request(REQ, vcpu)`
`WRITE_ONCE(vcpu->mode, IN_GUEST_MODE)`request bit 설정
`smp_mb()``smp_mb()`
pending이면 guest 진입 중단mode가 `IN_GUEST_MODE`이면 IPI 전송

두 CPU가 서로의 상태를 하나 이상 관측하도록 `vcpu->mode`와 `vcpu->requests`를 짝짓습니다.

memory barrier 때문에 vCPU가 마지막 검사에서 request 없음만 보고 곧바로 만들어진 다음 request의 IPI까지 놓치는 상황을 배제할 수 있습니다. `WRITE_ONCE()`와 `READ_ONCE()`는 컴파일러가 세심하게 설계한 `vcpu->mode` 접근을 바꾸지 못하게 합니다.

이 KVM 적용은 `vcpu->mode`를 `IN_GUEST_MODE`로 쓰기 전에 interrupt를 끈다는 점에서 일반 Dekker 패턴을 확장합니다. 그래야 상태를 읽은 요청자가 보낸 IPI가 guest 진입 준비 구간에서도 유효합니다.

Dekker 패턴의 보장은 CPU1이 새 request를 보거나 CPU2가 `IN_GUEST_MODE`를 보는 둘 중 적어도 하나입니다. 전자면 vCPU가 guest 진입을 중단하고, 후자면 요청자가 IPI를 보내므로 request가 장시간 미처리되는 경로가 사라집니다.

interrupt를 먼저 끄지 않으면 요청자가 mode를 보고 IPI를 보내더라도 vCPU가 guest 진입 준비 전에 이를 처리하고 다시 진입해 request를 놓칠 수 있습니다. 따라서 interrupt 상태 변경도 barrier 패턴의 일부입니다.

Ensuring Requests Are Seen
==========================

When making requests to VCPUs, we want to avoid the receiving VCPU
executing in guest mode for an arbitrary long time without handling the
request.  We can be sure this won't happen as long as we ensure the VCPU
thread checks kvm_request_pending() before entering guest mode and that a
kick will send an IPI to force an exit from guest mode when necessary.
Extra care must be taken to cover the period after the VCPU thread's last
kvm_request_pending() check and before it has entered guest mode, as kick
IPIs will only trigger guest mode exits for VCPU threads that are in guest
mode or at least have already disabled interrupts in order to prepare to
enter guest mode.  This means that an optimized implementation (see "IPI
Reduction") must be certain when it's safe to not send the IPI.  One
solution, which all architectures except s390 apply, is to:

- set ``vcpu->mode`` to IN_GUEST_MODE between disabling the interrupts and
  the last kvm_request_pending() check;
- enable interrupts atomically when entering the guest.

This solution also requires memory barriers to be placed carefully in both
the requesting thread and the receiving VCPU.  With the memory barriers we
can exclude the possibility of a VCPU thread observing
!kvm_request_pending() on its last check and then not receiving an IPI for
the next request made of it, even if the request is made immediately after
the check.  This is done by way of the Dekker memory barrier pattern
(scenario 10 of [lwn-mb]_).  As the Dekker pattern requires two variables,
this solution pairs ``vcpu->mode`` with ``vcpu->requests``.  Substituting
them into the pattern gives::

  CPU1                                    CPU2
  =================                       =================
  local_irq_disable();
  WRITE_ONCE(vcpu->mode, IN_GUEST_MODE);  kvm_make_request(REQ, vcpu);
  smp_mb();                               smp_mb();
  if (kvm_request_pending(vcpu)) {        if (READ_ONCE(vcpu->mode) ==
                                              IN_GUEST_MODE) {
      ...abort guest entry...                 ...send IPI...
  }                                       }

As stated above, the IPI is only useful for VCPU threads in guest mode or
that have already disabled interrupts.  This is why this specific case of
the Dekker pattern has been extended to disable interrupts before setting
``vcpu->mode`` to IN_GUEST_MODE.  WRITE_ONCE() and READ_ONCE() are used to
pedantically implement the memory barrier pattern, guaranteeing the
compiler doesn't interfere with ``vcpu->mode``'s carefully planned
accesses.

IPI 축약

233-240

하나의 IPI만으로 vCPU가 모든 pending request를 검사하게 할 수 있으므로 여러 request의 IPI를 합칠 수 있습니다. 첫 IPI를 보내는 kick이 mode를 `IN_GUEST_MODE`가 아닌 `EXITING_GUEST_MODE`로 바꾸면 이후 요청자는 추가 IPI를 생략할 수 있습니다.

mode 전환은 첫 IPI가 이미 guest exit를 시작했다는 신호입니다. 뒤따르는 요청은 같은 exit 뒤 한 번의 pending bitmap 검사에서 함께 처리되므로 별도의 IPI가 필요하지 않습니다.

IPI Reduction
-------------

As only one IPI is needed to get a VCPU to check for any/all requests,
then they may be coalesced.  This is easily done by having the first IPI
sending kick also change the VCPU mode to something !IN_GUEST_MODE.  The
transitional state, EXITING_GUEST_MODE, is used for this purpose.

acknowledgement 대기

241-252

`KVM_REQUEST_WAIT` request는 대상 mode가 `IN_GUEST_MODE`가 아니더라도 IPI를 보내고 acknowledgement를 기다려야 할 수 있습니다. 예를 들어 `READING_SHADOW_PAGE_TABLES`는 interrupt를 끈 뒤 설정되는 상태이므로 TLB 관련 요청자가 완료를 기다려야 합니다.

따라서 이 flag는 IPI 전송 조건을 `mode == IN_GUEST_MODE`에서 `mode != OUTSIDE_GUEST_MODE`로 넓힙니다.

`READING_SHADOW_PAGE_TABLES`처럼 interrupt가 꺼진 guest 밖 상태에도 IPI acknowledgement가 동기화 지점 역할을 합니다. 완전히 `OUTSIDE_GUEST_MODE`인 경우에만 별도 IPI와 대기가 필요 없습니다.

Waiting for Acknowledgements
----------------------------

Some requests, those with the KVM_REQUEST_WAIT flag set, require IPIs to
be sent, and the acknowledgements to be waited upon, even when the target
VCPU threads are in modes other than IN_GUEST_MODE.  For example, one case
is when a target VCPU thread is in READING_SHADOW_PAGE_TABLES mode, which
is set after disabling interrupts.  To support these cases, the
KVM_REQUEST_WAIT flag changes the condition for sending an IPI from
checking that the VCPU is IN_GUEST_MODE to checking that it is not
OUTSIDE_GUEST_MODE.

request 없는 kick의 위험

253-275

IPI 전송 여부가 `vcpu->mode`와 request bit의 Dekker 패턴에 의존하므로 request 없는 vCPU kick은 거의 항상 잘못된 설계입니다. IPI가 생략된 kick 뒤 대상이 어떤 작업을 수행하리라는 보장이 없기 때문입니다.

예를 들어 vCPU가 막 `IN_GUEST_MODE`로 mode를 바꾸려는 순간 request 없는 kick이 오면 IPI가 전송되지 않고, vCPU는 kick이 의도한 작업을 하지 않은 채 guest 진입을 계속할 수 있습니다.

예외는 x86 posted interrupt입니다. 이 경우에도 request 없는 kick이 같은 `local_irq_disable()`과 `smp_mb()` 패턴을 사용합니다. posted interrupt descriptor의 ON(Outstanding Notification) bit가 `vcpu->requests` 역할을 합니다.

송신자는 `vcpu->mode`를 읽기 전에 `PIR.ON`을 세우고, vCPU는 mode를 `IN_GUEST_MODE`로 설정한 뒤 `vmx_sync_pir_to_irr()`에서 PIR을 읽어 쌍을 이룹니다.

Request-less VCPU Kicks
-----------------------

As the determination of whether or not to send an IPI depends on the
two-variable Dekker memory barrier pattern, then it's clear that
request-less VCPU kicks are almost never correct.  Without the assurance
that a non-IPI generating kick will still result in an action by the
receiving VCPU, as the final kvm_request_pending() check does for
request-accompanying kicks, then the kick may not do anything useful at
all.  If, for instance, a request-less kick was made to a VCPU that was
just about to set its mode to IN_GUEST_MODE, meaning no IPI is sent, then
the VCPU thread may continue its entry without actually having done
whatever it was the kick was meant to initiate.

One exception is x86's posted interrupt mechanism.  In this case, however,
even the request-less VCPU kick is coupled with the same
local_irq_disable() + smp_mb() pattern described above; the ON bit
(Outstanding Notification) in the posted interrupt descriptor takes the
role of ``vcpu->requests``.  When sending a posted interrupt, PIR.ON is
set before reading ``vcpu->mode``; dually, in the VCPU thread,
vmx_sync_pir_to_irr() reads PIR after setting ``vcpu->mode`` to
IN_GUEST_MODE.

sleep vCPU 고려사항

276-288

vCPU 스레드는 `kvm_vcpu_block()`처럼 sleep할 수 있는 함수를 호출하기 전이나 후에 request를 확인해야 할 수 있습니다. 확인 시점과 대상 request는 아키텍처별입니다.

`kvm_vcpu_block()`은 `kvm_arch_vcpu_runnable()`로 깨워야 하는지 검사합니다. 이 hook은 필요한 아키텍처가 request도 함께 확인할 수 있는 지점을 제공합니다.

따라서 공통 차단 함수가 모든 request의 처리 시점을 고정하지는 않습니다. 각 아키텍처는 sleep 진입 전 처리해야 할 요청과 wakeup 판정 중 확인할 요청을 자신의 실행 모델에 맞게 선택합니다.

Additional Considerations
=========================

Sleeping VCPUs
--------------

VCPU threads may need to consider requests before and/or after calling
functions that may put them to sleep, e.g. kvm_vcpu_block().  Whether they
do or not, and, if they do, which requests need consideration, is
architecture dependent.  kvm_vcpu_block() calls kvm_arch_vcpu_runnable()
to check if it should awaken.  One reason to do so is to provide
architectures a function where requests may be checked if necessary.

참고 문서

289-294
참고 자료
표기문서
`atomic-ops``Documentation/atomic_bitops.txt`, `Documentation/atomic_t.txt`
`memory-barriers``Documentation/memory-barriers.txt`
`lwn-mb`LWN memory barrier 기사

비트 연산과 memory ordering의 배경 문서입니다.

References
==========

.. [atomic-ops] Documentation/atomic_bitops.txt and Documentation/atomic_t.txt
.. [memory-barriers] Documentation/memory-barriers.txt
.. [lwn-mb] https://lwn.net/Articles/573436/