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