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