• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 
3 // Copyright (C) 2024 Google LLC.
4 
5 #![allow(unreachable_pub, dead_code)]
6 //! Shrinker for handling memory pressure.
7 //!
8 //! C header: [`include/linux/shrinker.h`](srctree/include/linux/shrinker.h)
9 
10 use kernel::{
11     alloc::AllocError,
12     bindings, c_str,
13     ffi::{c_int, c_long, c_ulong, c_void},
14     str::CStr,
15     types::ForeignOwnable,
16 };
17 
18 use core::{marker::PhantomData, ptr::NonNull};
19 
20 const SHRINK_STOP: c_ulong = bindings::SHRINK_STOP as c_ulong;
21 const SHRINK_EMPTY: c_ulong = bindings::SHRINK_EMPTY as c_ulong;
22 
23 /// The default value for the number of seeks needed to recreate an object.
24 pub const DEFAULT_SEEKS: u32 = bindings::DEFAULT_SEEKS;
25 
26 /// An unregistered shrinker.
27 ///
28 /// This type can be used to modify the settings of the shrinker before it is registered.
29 ///
30 /// # Invariants
31 ///
32 /// The `shrinker` pointer references an unregistered shrinker.
33 pub struct ShrinkerBuilder {
34     shrinker: NonNull<bindings::shrinker>,
35 }
36 
37 // SAFETY: Moving an unregistered shrinker between threads is okay.
38 unsafe impl Send for ShrinkerBuilder {}
39 // SAFETY: An unregistered shrinker is thread safe.
40 unsafe impl Sync for ShrinkerBuilder {}
41 
42 impl ShrinkerBuilder {
43     /// Create a new shrinker.
new(name: &CStr) -> Result<Self, AllocError>44     pub fn new(name: &CStr) -> Result<Self, AllocError> {
45         // TODO: Support numa/memcg aware shrinkers once list_lru is available.
46         let flags = 0;
47 
48         // SAFETY: Passing `0` as flags is okay. Using `%s` as the format string is okay when we
49         // pass a nul-terminated string as the string for `%s` to print.
50         let ptr = unsafe {
51             bindings::shrinker_alloc(flags, c_str!("%s").as_char_ptr(), name.as_char_ptr())
52         };
53 
54         let shrinker = NonNull::new(ptr).ok_or(AllocError)?;
55 
56         // INVARIANT: The allocated shrinker is unregistered.
57         Ok(Self { shrinker })
58     }
59 
60     /// Create a new shrinker using format arguments for the name.
new_fmt(name: core::fmt::Arguments<'_>) -> Result<Self, AllocError>61     pub fn new_fmt(name: core::fmt::Arguments<'_>) -> Result<Self, AllocError> {
62         // TODO: Support numa/memcg aware shrinkers once list_lru is available.
63         let flags = 0;
64 
65         // SAFETY: Passing `0` as flags is okay. Using `%pA` as the format string is okay when we
66         // pass a `fmt::Arguments` as the value to print.
67         let ptr = unsafe {
68             bindings::shrinker_alloc(
69                 flags,
70                 c_str!("%pA").as_char_ptr(),
71                 &name as *const _ as *const c_void,
72             )
73         };
74 
75         let shrinker = NonNull::new(ptr).ok_or(AllocError)?;
76 
77         // INVARIANT: The allocated shrinker is unregistered.
78         Ok(Self { shrinker })
79     }
80 
81     /// Set the number of seeks needed to recreate an object.
set_seeks(&mut self, seeks: u32)82     pub fn set_seeks(&mut self, seeks: u32) {
83         unsafe { (*self.shrinker.as_ptr()).seeks = seeks as c_int };
84     }
85 
86     /// Set the batch size for reclaiming on this shrinker.
set_batch(&mut self, batch: usize)87     pub fn set_batch(&mut self, batch: usize) {
88         unsafe { (*self.shrinker.as_ptr()).batch = batch as c_long };
89     }
90 
91     /// Register the shrinker.
92     ///
93     /// The provided pointer is used as the private data, and the type `T` determines the callbacks
94     /// that the shrinker will use.
register<T: Shrinker>(self, private_data: T::Ptr) -> ShrinkerRegistration<T>95     pub fn register<T: Shrinker>(self, private_data: T::Ptr) -> ShrinkerRegistration<T> {
96         let shrinker = self.shrinker;
97         let ptr = shrinker.as_ptr();
98 
99         // The destructor of `self` calls `shrinker_free`, so skip the destructor.
100         core::mem::forget(self);
101 
102         let private_data_ptr = <T::Ptr as ForeignOwnable>::into_foreign(private_data);
103 
104         // SAFETY: We own the private data, so we can assign to it.
105         unsafe { (*ptr).private_data = private_data_ptr.cast_mut() };
106         // SAFETY: The shrinker is not yet registered, so we can update this field.
107         unsafe { (*ptr).count_objects = Some(rust_count_objects::<T>) };
108         // SAFETY: The shrinker is not yet registered, so we can update this field.
109         unsafe { (*ptr).scan_objects = Some(rust_scan_objects::<T>) };
110 
111         // SAFETY: The shrinker is unregistered, so it's safe to register it.
112         unsafe { bindings::shrinker_register(ptr) };
113 
114         ShrinkerRegistration {
115             shrinker,
116             _phantom: PhantomData,
117         }
118     }
119 }
120 
121 impl Drop for ShrinkerBuilder {
drop(&mut self)122     fn drop(&mut self) {
123         // SAFETY: The shrinker is a valid but unregistered shrinker, and we will not use it
124         // anymore.
125         unsafe { bindings::shrinker_free(self.shrinker.as_ptr()) };
126     }
127 }
128 
129 /// A shrinker that is registered with the kernel.
130 ///
131 /// # Invariants
132 ///
133 /// The `shrinker` pointer refers to a registered shrinker using `T` as the private data.
134 pub struct ShrinkerRegistration<T: Shrinker> {
135     shrinker: NonNull<bindings::shrinker>,
136     _phantom: PhantomData<T::Ptr>,
137 }
138 
139 // SAFETY: This allows you to deregister the shrinker from a different thread, which means that
140 // private data could be dropped from any thread.
141 unsafe impl<T: Shrinker> Send for ShrinkerRegistration<T> where T::Ptr: Send {}
142 // SAFETY: The only thing you can do with an immutable reference is access the private data, which
143 // is okay to access in parallel as the `Shrinker` trait requires the private data to be `Sync`.
144 unsafe impl<T: Shrinker> Sync for ShrinkerRegistration<T> {}
145 
146 impl<T: Shrinker> ShrinkerRegistration<T> {
147     /// Access the private data in this shrinker.
private_data(&self) -> <T::Ptr as ForeignOwnable>::Borrowed<'_>148     pub fn private_data(&self) -> <T::Ptr as ForeignOwnable>::Borrowed<'_> {
149         // SAFETY: We own the private data, so we can access it.
150         let private = unsafe { (*self.shrinker.as_ptr()).private_data };
151         // SAFETY: By the type invariants, the private data is `T`. This access could happen in
152         // parallel with a shrinker callback, but that's okay as the `Shrinker` trait ensures that
153         // `T::Ptr` is `Sync`.
154         unsafe { <T::Ptr as ForeignOwnable>::borrow(private) }
155     }
156 }
157 
158 impl<T: Shrinker> Drop for ShrinkerRegistration<T> {
drop(&mut self)159     fn drop(&mut self) {
160         // SAFETY: We own the private data, so we can access it.
161         let private = unsafe { (*self.shrinker.as_ptr()).private_data };
162         // SAFETY: We will not access the shrinker after this call.
163         unsafe { bindings::shrinker_free(self.shrinker.as_ptr()) };
164         // SAFETY: The above call blocked until the completion of any shrinker callbacks, so there
165         // are no longer any users of the private data.
166         drop(unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) });
167     }
168 }
169 
170 /// Callbacks for a shrinker.
171 pub trait Shrinker {
172     /// The pointer type used to store the private data of the shrinker.
173     ///
174     /// Needs to be `Sync` because the shrinker callback could access this value immutably from
175     /// several thread in parallel.
176     type Ptr: ForeignOwnable + Sync;
177 
178     /// Count the number of freeable items in the cache.
count_objects( me: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, sc: ShrinkControl<'_>, ) -> CountObjects179     fn count_objects(
180         me: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
181         sc: ShrinkControl<'_>,
182     ) -> CountObjects;
183 
184     /// Free some objects in this cache.
scan_objects( me: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, sc: ShrinkControl<'_>, ) -> ScanObjects185     fn scan_objects(
186         me: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
187         sc: ShrinkControl<'_>,
188     ) -> ScanObjects;
189 }
190 
191 /// How many objects are there in the cache?
192 ///
193 /// This is used as the return value of [`Shrinker::count_objects`].
194 pub struct CountObjects {
195     inner: c_ulong,
196 }
197 
198 impl CountObjects {
199     /// Indicates that the number of objects is zero.
200     pub const EMPTY: Self = Self {
201         inner: SHRINK_EMPTY,
202     };
203 
204     /// The maximum possible number of freeable objects.
205     pub const MAX: Self = Self {
206         // The shrinker code assumes that it can multiply this value by two without overflow.
207         inner: c_ulong::MAX / 2,
208     };
209 
210     /// Creates a new `CountObjects` with the given value.
211     ///
212     /// This should be the number of objects that were actually freed. Objects that were scanned
213     /// but not freed should be counted in `nr_scanned` but not here.
214     ///
215     /// If `count` is zero, then this indicates that the real count is unknown. Use
216     /// `CountObjects::EMPTY` to indicate that the shrinker is empty.
new(count: usize) -> Self217     pub fn new(count: usize) -> Self {
218         if count > Self::MAX.inner as usize {
219             return Self::MAX;
220         }
221 
222         Self {
223             inner: count as c_ulong,
224         }
225     }
226 }
227 
228 /// How many objects were freed?
229 ///
230 /// This is used as the return value of [`Shrinker::scan_objects`].
231 pub struct ScanObjects {
232     inner: c_ulong,
233 }
234 
235 impl ScanObjects {
236     /// Indicates that the shrinker should stop trying to free objects from this cache due to
237     /// potential deadlocks.
238     pub const STOP: Self = Self { inner: SHRINK_STOP };
239 
240     /// The maximum possible number of freeable objects.
241     pub const MAX: Self = Self {
242         inner: SHRINK_STOP - 1,
243     };
244 
245     /// Creates a new `CountObjects` with the given value.
from_count(count: usize) -> Self246     pub fn from_count(count: usize) -> Self {
247         if count > Self::MAX.inner as usize {
248             return Self::MAX;
249         }
250 
251         Self {
252             inner: count as c_ulong,
253         }
254     }
255 }
256 
257 /// This struct is used to pass information from page reclaim to the shrinkers.
258 ///
259 /// # Invariants
260 ///
261 /// `ptr` has exclusive access to a valid `struct shrink_control`.
262 pub struct ShrinkControl<'a> {
263     ptr: NonNull<bindings::shrink_control>,
264     _phantom: PhantomData<&'a bindings::shrink_control>,
265 }
266 
267 impl<'a> ShrinkControl<'a> {
268     /// Create a `ShrinkControl` from a raw pointer.
269     ///
270     /// # Safety
271     ///
272     /// The pointer should point at a valid `shrink_control` for the duration of 'a.
from_raw(ptr: *mut bindings::shrink_control) -> Self273     pub unsafe fn from_raw(ptr: *mut bindings::shrink_control) -> Self {
274         Self {
275             // SAFETY: Caller promises that this pointer is valid.
276             ptr: unsafe { NonNull::new_unchecked(ptr) },
277             _phantom: PhantomData,
278         }
279     }
280 
281     /// Determines whether it is safe to call into filesystem code.
reclaim_fs_allowed(&self) -> bool282     pub fn reclaim_fs_allowed(&self) -> bool {
283         // SAFETY: Okay by type invariants.
284         let mask = unsafe { (*self.ptr.as_ptr()).gfp_mask };
285 
286         (mask & bindings::__GFP_FS) != 0
287     }
288 
289     /// Determines whether it is safe to call into IO code.
reclaim_io_allowed(&self) -> bool290     pub fn reclaim_io_allowed(&self) -> bool {
291         // SAFETY: Okay by type invariants.
292         let mask = unsafe { (*self.ptr.as_ptr()).gfp_mask };
293 
294         (mask & bindings::__GFP_IO) != 0
295     }
296 
297     /// Returns the number of objects that `scan_objects` should try to reclaim.
nr_to_scan(&self) -> usize298     pub fn nr_to_scan(&self) -> usize {
299         // SAFETY: Okay by type invariants.
300         unsafe { (*self.ptr.as_ptr()).nr_to_scan as usize }
301     }
302 
303     /// The callback should set this value to the number of objects inspected by the shrinker.
set_nr_scanned(&mut self, val: usize)304     pub fn set_nr_scanned(&mut self, val: usize) {
305         // SAFETY: Okay by type invariants.
306         unsafe { (*self.ptr.as_ptr()).nr_scanned = val as c_ulong };
307     }
308 }
309 
rust_count_objects<T: Shrinker>( shrink: *mut bindings::shrinker, sc: *mut bindings::shrink_control, ) -> c_ulong310 unsafe extern "C" fn rust_count_objects<T: Shrinker>(
311     shrink: *mut bindings::shrinker,
312     sc: *mut bindings::shrink_control,
313 ) -> c_ulong {
314     // SAFETY: We own the private data, so we can access it.
315     let private = unsafe { (*shrink).private_data };
316     // SAFETY: This function is only used with shrinkers where `T` is the type of the private data.
317     let private = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
318     // SAFETY: The caller passes a valid `sc` pointer.
319     let sc = unsafe { ShrinkControl::from_raw(sc) };
320 
321     let ret = T::count_objects(private, sc);
322     ret.inner
323 }
324 
rust_scan_objects<T: Shrinker>( shrink: *mut bindings::shrinker, sc: *mut bindings::shrink_control, ) -> c_ulong325 unsafe extern "C" fn rust_scan_objects<T: Shrinker>(
326     shrink: *mut bindings::shrinker,
327     sc: *mut bindings::shrink_control,
328 ) -> c_ulong {
329     // SAFETY: We own the private data, so we can access it.
330     let private = unsafe { (*shrink).private_data };
331     // SAFETY: This function is only used with shrinkers where `T` is the type of the private data.
332     let private = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
333     // SAFETY: The caller passes a valid `sc` pointer.
334     let sc = unsafe { ShrinkControl::from_raw(sc) };
335 
336     let ret = T::scan_objects(private, sc);
337     ret.inner
338 }
339