ostd/mm/tlb.rs
1// SPDX-License-Identifier: MPL-2.0
2//! TLB flush operations.
3use vstd::prelude::*;
4
5use vstd_extra::ownership::*;
6
7use alloc::vec::Vec;
8use core::{
9 ops::Range,
10 sync::atomic::{AtomicBool, Ordering},
11};
12
13use super::{
14 PAGE_SIZE, Vaddr,
15 frame::{Frame, meta::AnyFrameMeta},
16};
17
18use crate::specs::mm::cpu::{AtomicCpuSet, CpuSet, PinCurrentCpu};
19
20use crate::specs::mm::tlb::TlbModel;
21
22/*use crate::{
23 arch::irq,
24 cpu::{AtomicCpuSet, CpuSet, PinCurrentCpu},
25 cpu_local,
26 sync::{LocalIrqDisabled, SpinLock},
27};*/
28
29#[verus_verify]
30verus! {
31
32/// A TLB flusher that is aware of which CPUs are needed to be flushed.
33///
34/// The flusher needs to stick to the current CPU.
35pub struct TlbFlusher<'a /*, G: PinCurrentCpu*/ > {
36 target_cpus: &'a AtomicCpuSet,
37 have_unsynced_flush: CpuSet,
38 ops_stack: OpsStack,
39 //_pin_current: G,
40}
41
42} // verus!
43#[verus_verify]
44impl<'a /*, G: PinCurrentCpu*/> TlbFlusher<'a /*, G*/> {
45 /// Creates a new TLB flusher with the specified CPUs to be flushed.
46 ///
47 /// The target CPUs should be a reference to an [`AtomicCpuSet`] that will
48 /// be loaded upon [`Self::dispatch_tlb_flush`].
49 ///
50 /// The flusher needs to stick to the current CPU. So please provide a
51 /// guard that implements [`PinCurrentCpu`].
52 pub fn new(target_cpus: &'a AtomicCpuSet /*, pin_current_guard: G*/) -> Self {
53 Self {
54 target_cpus,
55 have_unsynced_flush: CpuSet::new_empty(),
56 ops_stack: OpsStack::new(),
57 //_pin_current: pin_current_guard,
58 }
59 }
60
61 /// Issues a pending TLB flush request.
62 ///
63 /// This function does not guarantee to flush the TLB entries on either
64 /// this CPU or remote CPUs. The flush requests are only performed when
65 /// [`Self::dispatch_tlb_flush`] is called.
66 #[verus_spec(
67 with Tracked(model): Tracked<&mut TlbModel>
68 ensures
69 *final(model) == old(model).issue_tlb_flush(op),
70 final(model).inv(),
71 )]
72 #[verifier::external_body]
73 pub fn issue_tlb_flush(&mut self, op: TlbFlushOp) {
74 self.ops_stack.push(op, None);
75 }
76
77 /// Issues a TLB flush request that must happen before dropping the page.
78 ///
79 /// If we need to remove a mapped page from the page table, we can only
80 /// recycle the page after all the relevant TLB entries in all CPUs are
81 /// flushed. Otherwise if the page is recycled for other purposes, the user
82 /// space program can still access the page through the TLB entries. This
83 /// method is designed to be used in such cases.
84 #[verus_spec(r =>
85 with Tracked(model): Tracked<&mut TlbModel>
86 ensures
87 *final(model) == old(model).issue_tlb_flush(op),
88 final(model).inv(),
89 )]
90 #[verifier::external_body]
91 pub fn issue_tlb_flush_with(
92 &mut self,
93 op: TlbFlushOp,
94 drop_after_flush: Frame<dyn AnyFrameMeta>,
95 ) {
96 self.ops_stack.push(op, Some(drop_after_flush));
97 }
98
99 /// Dispatches all the pending TLB flush requests.
100 ///
101 /// All previous pending requests issued by [`Self::issue_tlb_flush`] or
102 /// [`Self::issue_tlb_flush_with`] starts to be processed after this
103 /// function. But it may not be synchronous. Upon the return of this
104 /// function, the TLB entries may not be coherent.
105 #[verifier::external_body]
106 #[verus_spec(r =>
107 with Tracked(model): Tracked<&mut TlbModel>
108 requires
109 old(model).inv(),
110 ensures
111 *final(model) == old(model).dispatch_tlb_flush_spec(),
112 final(model).inv(),
113 )]
114 pub fn dispatch_tlb_flush(&mut self) {
115 unimplemented!() /*let irq_guard = crate::trap::irq::disable_local();
116
117
118 if self.ops_stack.is_empty() {
119 return;
120 }
121
122 // `Release` to make sure our modification on the PT is visible to CPUs
123 // that are going to activate the PT.
124 let mut target_cpus = self.target_cpus.load(Ordering::Release);
125
126 let cur_cpu = irq_guard.current_cpu();
127 let mut need_flush_on_self = false;
128
129 if target_cpus.contains(cur_cpu) {
130 target_cpus.remove(cur_cpu);
131 need_flush_on_self = true;
132 }
133
134 for cpu in target_cpus.iter() {
135 {
136 let mut flush_ops = FLUSH_OPS.get_on_cpu(cpu).lock();
137 flush_ops.push_from(&self.ops_stack);
138
139 // Clear ACK before dropping the lock to avoid false ACKs.
140 ACK_REMOTE_FLUSH
141 .get_on_cpu(cpu)
142 .store(false, Ordering::Relaxed);
143 }
144 self.have_unsynced_flush.add(cpu);
145 }
146
147 crate::smp::inter_processor_call(&target_cpus, do_remote_flush);
148
149 // Flush ourselves after sending all IPIs to save some time.
150 if need_flush_on_self {
151 self.ops_stack.flush_all();
152 } else {
153 self.ops_stack.clear_without_flush();
154 }*/
155 }
156
157 /// Waits for all the previous TLB flush requests to be completed.
158 ///
159 /// After this function, all TLB entries corresponding to previous
160 /// dispatched TLB flush requests are guaranteed to be coherent.
161 ///
162 /// The TLB flush requests are issued with [`Self::issue_tlb_flush`] and
163 /// dispatched with [`Self::dispatch_tlb_flush`]. This method will not
164 /// dispatch any issued requests so it will not guarantee TLB coherence
165 /// of requests that are not dispatched.
166 ///
167 /// # Panics
168 ///
169 /// This method panics if the IRQs are disabled. Since the remote flush are
170 /// processed in IRQs, two CPUs may deadlock if they are waiting for each
171 /// other's TLB coherence.
172 #[verifier::external_body]
173 pub fn sync_tlb_flush(&mut self) {
174 unimplemented!() /*
175 assert!(
176 irq::is_local_enabled(),
177 "Waiting for remote flush with IRQs disabled"
178 );
179
180 for cpu in self.have_unsynced_flush.iter() {
181 while !ACK_REMOTE_FLUSH.get_on_cpu(cpu).load(Ordering::Relaxed) {
182 core::hint::spin_loop();
183 }
184 }
185
186 self.have_unsynced_flush = CpuSet::new_empty();
187 */
188 }
189}
190
191verus! {
192
193/// The operation to flush TLB entries.
194#[verifier::allow(autoderive_clone_without_spec)]
195#[derive(Debug, Clone, PartialEq, Eq)]
196pub enum TlbFlushOp {
197 /// Flush all TLB entries except for the global entries.
198 All,
199 /// Flush the TLB entry for the specified virtual address.
200 Address(Vaddr),
201 /// Flush the TLB entries for the specified virtual address range.
202 Range(Range<Vaddr>),
203}
204
205impl TlbFlushOp {
206 /// Performs the TLB flush operation on the current CPU.
207 #[verifier::external_body]
208 pub fn perform_on_current(&self) {
209 unimplemented!()/*use crate::arch::mm::{
210 tlb_flush_addr, tlb_flush_addr_range, tlb_flush_all_excluding_global,
211 };
212 match self {
213 TlbFlushOp::All => tlb_flush_all_excluding_global(),
214 TlbFlushOp::Address(addr) => tlb_flush_addr(*addr),
215 TlbFlushOp::Range(range) => tlb_flush_addr_range(range),
216 }*/
217
218 }
219
220 #[verifier::external_body]
221 fn optimize_for_large_range(self) -> Self {
222 match self {
223 TlbFlushOp::Range(range) => {
224 if range.len() > FLUSH_ALL_RANGE_THRESHOLD {
225 TlbFlushOp::All
226 } else {
227 TlbFlushOp::Range(range)
228 }
229 },
230 _ => self,
231 }
232 }
233}
234
235// The queues of pending requests on each CPU.
236/*cpu_local! {
237 static FLUSH_OPS: SpinLock<OpsStack, LocalIrqDisabled> = SpinLock::new(OpsStack::new());
238 /// Whether this CPU finishes the last remote flush request.
239 static ACK_REMOTE_FLUSH: AtomicBool = AtomicBool::new(true);
240}*/
241
242#[verifier::external_body]
243fn do_remote_flush() {
244 unimplemented!()/*
245 // No races because we are in IRQs or have disabled preemption.
246 let current_cpu = crate::cpu::CpuId::current_racy();
247
248 let mut new_op_queue = OpsStack::new();
249 {
250 let mut op_queue = FLUSH_OPS.get_on_cpu(current_cpu).lock();
251
252 core::mem::swap(&mut *op_queue, &mut new_op_queue);
253
254 // ACK before dropping the lock so that we won't miss flush requests.
255 ACK_REMOTE_FLUSH
256 .get_on_cpu(current_cpu)
257 .store(true, Ordering::Relaxed);
258 }
259 // Unlock the locks quickly to avoid contention. ACK before flushing is
260 // fine since we cannot switch back to userspace now.
261 new_op_queue.flush_all();
262 */
263
264}
265
266/// If a TLB flushing request exceeds this threshold, we flush all.
267const FLUSH_ALL_RANGE_THRESHOLD: usize = 32 * PAGE_SIZE;
268
269/// If the number of pending requests exceeds this threshold, we flush all the
270/// TLB entries instead of flushing them one by one.
271const FLUSH_ALL_OPS_THRESHOLD: usize = 32;
272
273struct OpsStack {
274 ops: [Option<TlbFlushOp>; FLUSH_ALL_OPS_THRESHOLD],
275 need_flush_all: bool,
276 size: usize,
277 // page_keeper: Vec<Frame<dyn AnyFrameMeta>>,
278}
279
280impl OpsStack {
281 #[verifier::external_body]
282 const fn new() -> Self {
283 Self {
284 ops: [const { None };FLUSH_ALL_OPS_THRESHOLD],
285 need_flush_all: false,
286 size: 0,
287 // page_keeper: Vec::new(),
288 }
289 }
290
291 #[verifier::external_body]
292 fn is_empty(&self) -> bool {
293 !self.need_flush_all && self.size == 0
294 }
295
296 #[verifier::external_body]
297 fn push(&mut self, op: TlbFlushOp, drop_after_flush: Option<Frame<dyn AnyFrameMeta>>) {
298 if let Some(frame) = drop_after_flush {
299 // self.page_keeper.push(frame);
300 }
301 if self.need_flush_all {
302 return;
303 }
304 let op = op.optimize_for_large_range();
305 if op == TlbFlushOp::All || self.size >= FLUSH_ALL_OPS_THRESHOLD {
306 self.need_flush_all = true;
307 self.size = 0;
308 return;
309 }
310 self.ops[self.size] = Some(op);
311 self.size += 1;
312 }
313
314 #[verifier::external_body]
315 fn push_from(&mut self, other: &OpsStack) {
316 // self.page_keeper.extend(other.page_keeper.iter().cloned());
317 if self.need_flush_all {
318 return;
319 }
320 if other.need_flush_all || self.size + other.size >= FLUSH_ALL_OPS_THRESHOLD {
321 self.need_flush_all = true;
322 self.size = 0;
323 return;
324 }
325 for i in 0..other.size {
326 self.ops[self.size] = other.ops[i].clone();
327 self.size += 1;
328 }
329 }
330
331 #[verifier::external_body]
332 fn flush_all(&mut self) {
333 if self.need_flush_all {
334 unimplemented!() /*crate::arch::mm::tlb_flush_all_excluding_global();*/
335
336 } else {
337 for i in 0..self.size {
338 if let Some(op) = &self.ops[i] {
339 op.perform_on_current();
340 }
341 }
342 }
343
344 self.clear_without_flush();
345 }
346
347 #[verifier::external_body]
348 fn clear_without_flush(&mut self) {
349 self.need_flush_all = false;
350 self.size = 0;
351 // self.page_keeper.clear();
352 }
353}
354
355} // verus!