Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2
3// Copyright (C) 2025 Google LLC.
4
5use kernel::{
6 alloc::AllocError,
7 list::ListArc,
8 prelude::*,
9 rbtree::{self, RBTreeNodeReservation},
10 seq_file::SeqFile,
11 seq_print,
12 sync::{Arc, UniqueArc},
13 uaccess::UserSliceReader,
14};
15
16use crate::{
17 defs::*, node::Node, process::Process, thread::Thread, BinderReturnWriter, DArc, DLArc,
18 DTRWrap, DeliverToRead,
19};
20
21#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
22pub(crate) struct FreezeCookie(u64);
23
24/// Represents a listener for changes to the frozen state of a process.
25pub(crate) struct FreezeListener {
26 /// The node we are listening for.
27 pub(crate) node: DArc<Node>,
28 /// The cookie of this freeze listener.
29 cookie: FreezeCookie,
30 /// What value of `is_frozen` did we most recently tell userspace about?
31 last_is_frozen: Option<bool>,
32 /// We sent a `BR_FROZEN_BINDER` and we are waiting for `BC_FREEZE_NOTIFICATION_DONE` before
33 /// sending any other commands.
34 is_pending: bool,
35 /// Userspace sent `BC_CLEAR_FREEZE_NOTIFICATION` and we need to reply with
36 /// `BR_CLEAR_FREEZE_NOTIFICATION_DONE` as soon as possible. If `is_pending` is set, then we
37 /// must wait for it to be unset before we can reply.
38 is_clearing: bool,
39 /// Number of cleared duplicates that can't be deleted until userspace sends
40 /// `BC_FREEZE_NOTIFICATION_DONE`.
41 num_pending_duplicates: u64,
42 /// Number of cleared duplicates that can be deleted.
43 num_cleared_duplicates: u64,
44}
45
46impl FreezeListener {
47 /// Is it okay to create a new listener with the same cookie as this one for the provided node?
48 ///
49 /// Under some scenarios, userspace may delete a freeze listener and immediately recreate it
50 /// with the same cookie. This results in duplicate listeners. To avoid issues with ambiguity,
51 /// we allow this only if the new listener is for the same node, and we also require that the
52 /// old listener has already been cleared.
53 fn allow_duplicate(&self, node: &DArc<Node>) -> bool {
54 Arc::ptr_eq(&self.node, node) && self.is_clearing
55 }
56}
57
58type UninitFM = UniqueArc<core::mem::MaybeUninit<DTRWrap<FreezeMessage>>>;
59
60/// Represents a notification that the freeze state has changed.
61pub(crate) struct FreezeMessage {
62 cookie: FreezeCookie,
63}
64
65kernel::list::impl_list_arc_safe! {
66 impl ListArcSafe<0> for FreezeMessage {
67 untracked;
68 }
69}
70
71impl FreezeMessage {
72 fn new(flags: kernel::alloc::Flags) -> Result<UninitFM, AllocError> {
73 UniqueArc::new_uninit(flags)
74 }
75
76 fn init(ua: UninitFM, cookie: FreezeCookie) -> DLArc<FreezeMessage> {
77 match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie })) {
78 Ok(msg) => ListArc::from(msg),
79 Err(err) => match err {},
80 }
81 }
82}
83
84impl DeliverToRead for FreezeMessage {
85 fn do_work(
86 self: DArc<Self>,
87 thread: &Thread,
88 writer: &mut BinderReturnWriter<'_>,
89 ) -> Result<bool> {
90 let _removed_listener;
91 let mut node_refs = thread.process.node_refs.lock();
92 let Some(mut freeze_entry) = node_refs.freeze_listeners.find_mut(&self.cookie) else {
93 return Ok(true);
94 };
95 let freeze = freeze_entry.get_mut();
96
97 if freeze.num_cleared_duplicates > 0 {
98 freeze.num_cleared_duplicates -= 1;
99 drop(node_refs);
100 writer.write_code(BR_CLEAR_FREEZE_NOTIFICATION_DONE)?;
101 writer.write_payload(&self.cookie.0)?;
102 return Ok(true);
103 }
104
105 if freeze.is_pending {
106 return Ok(true);
107 }
108 if freeze.is_clearing {
109 kernel::warn_on!(freeze.num_cleared_duplicates != 0);
110 if freeze.num_pending_duplicates > 0 {
111 // The primary freeze listener was deleted, so convert a pending duplicate back
112 // into the primary one.
113 freeze.num_pending_duplicates -= 1;
114 freeze.is_pending = true;
115 freeze.is_clearing = true;
116 } else {
117 _removed_listener = freeze_entry.remove_node();
118 }
119 drop(node_refs);
120 writer.write_code(BR_CLEAR_FREEZE_NOTIFICATION_DONE)?;
121 writer.write_payload(&self.cookie.0)?;
122 Ok(true)
123 } else {
124 let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen();
125 if freeze.last_is_frozen == Some(is_frozen) {
126 return Ok(true);
127 }
128
129 let mut state_info = BinderFrozenStateInfo::default();
130 state_info.is_frozen = is_frozen as u32;
131 state_info.cookie = freeze.cookie.0;
132 freeze.is_pending = true;
133 freeze.last_is_frozen = Some(is_frozen);
134 drop(node_refs);
135
136 writer.write_code(BR_FROZEN_BINDER)?;
137 writer.write_payload(&state_info)?;
138 // BR_FROZEN_BINDER notifications can cause transactions
139 Ok(false)
140 }
141 }
142
143 fn cancel(self: DArc<Self>) {}
144
145 fn should_sync_wakeup(&self) -> bool {
146 false
147 }
148
149 #[inline(never)]
150 fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> {
151 seq_print!(m, "{}has frozen binder\n", prefix);
152 Ok(())
153 }
154}
155
156impl FreezeListener {
157 pub(crate) fn on_process_exit(&self, proc: &Arc<Process>) {
158 if !self.is_clearing {
159 self.node.remove_freeze_listener(proc);
160 }
161 }
162}
163
164impl Process {
165 pub(crate) fn request_freeze_notif(
166 self: &Arc<Self>,
167 reader: &mut UserSliceReader,
168 ) -> Result<()> {
169 let hc = reader.read::<BinderHandleCookie>()?;
170 let handle = hc.handle;
171 let cookie = FreezeCookie(hc.cookie);
172
173 let msg = FreezeMessage::new(GFP_KERNEL)?;
174 let alloc = RBTreeNodeReservation::new(GFP_KERNEL)?;
175
176 let mut node_refs_guard = self.node_refs.lock();
177 let node_refs = &mut *node_refs_guard;
178 let Some(info) = node_refs.by_handle.get_mut(&handle) else {
179 pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle);
180 return Err(EINVAL);
181 };
182 if info.freeze().is_some() {
183 pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n");
184 return Err(EINVAL);
185 }
186 let node_ref = info.node_ref();
187 let freeze_entry = node_refs.freeze_listeners.entry(cookie);
188
189 if let rbtree::Entry::Occupied(ref dupe) = freeze_entry {
190 if !dupe.get().allow_duplicate(&node_ref.node) {
191 pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n");
192 return Err(EINVAL);
193 }
194 }
195
196 // All failure paths must come before this call, and all modifications must come after this
197 // call.
198 node_ref.node.add_freeze_listener(self, GFP_KERNEL)?;
199
200 match freeze_entry {
201 rbtree::Entry::Vacant(entry) => {
202 entry.insert(
203 FreezeListener {
204 cookie,
205 node: node_ref.node.clone(),
206 last_is_frozen: None,
207 is_pending: false,
208 is_clearing: false,
209 num_pending_duplicates: 0,
210 num_cleared_duplicates: 0,
211 },
212 alloc,
213 );
214 }
215 rbtree::Entry::Occupied(mut dupe) => {
216 let dupe = dupe.get_mut();
217 if dupe.is_pending {
218 dupe.num_pending_duplicates += 1;
219 } else {
220 dupe.num_cleared_duplicates += 1;
221 }
222 dupe.last_is_frozen = None;
223 dupe.is_pending = false;
224 dupe.is_clearing = false;
225 }
226 }
227
228 *info.freeze() = Some(cookie);
229 let msg = FreezeMessage::init(msg, cookie);
230 drop(node_refs_guard);
231 let _ = self.push_work(msg);
232 Ok(())
233 }
234
235 pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader) -> Result<()> {
236 let cookie = FreezeCookie(reader.read()?);
237 let alloc = FreezeMessage::new(GFP_KERNEL)?;
238 let mut node_refs_guard = self.node_refs.lock();
239 let node_refs = &mut *node_refs_guard;
240 let Some(freeze) = node_refs.freeze_listeners.get_mut(&cookie) else {
241 pr_warn!("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n", cookie.0);
242 return Err(EINVAL);
243 };
244 let mut clear_msg = None;
245 if freeze.num_pending_duplicates > 0 {
246 clear_msg = Some(FreezeMessage::init(alloc, cookie));
247 freeze.num_pending_duplicates -= 1;
248 freeze.num_cleared_duplicates += 1;
249 } else {
250 if !freeze.is_pending {
251 pr_warn!(
252 "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending\n",
253 cookie.0
254 );
255 return Err(EINVAL);
256 }
257 let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen();
258 if freeze.is_clearing || freeze.last_is_frozen != Some(is_frozen) {
259 // Immediately send another FreezeMessage.
260 clear_msg = Some(FreezeMessage::init(alloc, cookie));
261 }
262 freeze.is_pending = false;
263 }
264 drop(node_refs_guard);
265 if let Some(clear_msg) = clear_msg {
266 let _ = self.push_work(clear_msg);
267 }
268 Ok(())
269 }
270
271 pub(crate) fn clear_freeze_notif(self: &Arc<Self>, reader: &mut UserSliceReader) -> Result<()> {
272 let hc = reader.read::<BinderHandleCookie>()?;
273 let handle = hc.handle;
274 let cookie = FreezeCookie(hc.cookie);
275
276 let alloc = FreezeMessage::new(GFP_KERNEL)?;
277 let mut node_refs_guard = self.node_refs.lock();
278 let node_refs = &mut *node_refs_guard;
279 let Some(info) = node_refs.by_handle.get_mut(&handle) else {
280 pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n", handle);
281 return Err(EINVAL);
282 };
283 let Some(info_cookie) = info.freeze() else {
284 pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n");
285 return Err(EINVAL);
286 };
287 if *info_cookie != cookie {
288 pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n");
289 return Err(EINVAL);
290 }
291 let Some(listener) = node_refs.freeze_listeners.get_mut(&cookie) else {
292 pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n", handle);
293 return Err(EINVAL);
294 };
295 listener.is_clearing = true;
296 listener.node.remove_freeze_listener(self);
297 *info.freeze() = None;
298 let mut msg = None;
299 if !listener.is_pending {
300 msg = Some(FreezeMessage::init(alloc, cookie));
301 }
302 drop(node_refs_guard);
303
304 if let Some(msg) = msg {
305 let _ = self.push_work(msg);
306 }
307 Ok(())
308 }
309
310 fn get_freeze_cookie(&self, node: &DArc<Node>) -> Option<FreezeCookie> {
311 let node_refs = &mut *self.node_refs.lock();
312 let handle = node_refs.by_node.get(&node.global_id())?;
313 let node_ref = node_refs.by_handle.get_mut(handle)?;
314 *node_ref.freeze()
315 }
316
317 /// Creates a vector of every freeze listener on this process.
318 ///
319 /// Returns pairs of the remote process listening for notifications and the local node it is
320 /// listening on.
321 #[expect(clippy::type_complexity)]
322 fn find_freeze_recipients(&self) -> Result<KVVec<(DArc<Node>, Arc<Process>)>, AllocError> {
323 // Defined before `inner` to drop after releasing spinlock if `push_within_capacity` fails.
324 let mut node_proc_pair;
325
326 // We pre-allocate space for up to 8 recipients before we take the spinlock. However, if
327 // the allocation fails, use a vector with a capacity of zero instead of failing. After
328 // all, there might not be any freeze listeners, in which case this operation could still
329 // succeed.
330 let mut recipients =
331 KVVec::with_capacity(8, GFP_KERNEL).unwrap_or_else(|_err| KVVec::new());
332
333 let mut inner = self.lock_with_nodes();
334 let mut curr = inner.nodes.cursor_front_mut();
335 while let Some(cursor) = curr {
336 let (key, node) = cursor.current();
337 let key = *key;
338 let list = node.freeze_list(&inner.inner);
339 let len = list.len();
340
341 if recipients.spare_capacity_mut().len() < len {
342 drop(inner);
343 recipients.reserve(len, GFP_KERNEL)?;
344 inner = self.lock_with_nodes();
345 // Find the node we were looking at and try again. If the set of nodes was changed,
346 // then just proceed to the next node. This is ok because we don't guarantee the
347 // inclusion of nodes that are added or removed in parallel with this operation.
348 curr = inner.nodes.cursor_lower_bound_mut(&key);
349 continue;
350 }
351
352 for proc in list {
353 node_proc_pair = (node.clone(), proc.clone());
354 recipients
355 .push_within_capacity(node_proc_pair)
356 .map_err(|_| {
357 pr_err!(
358 "push_within_capacity failed even though we checked the capacity\n"
359 );
360 AllocError
361 })?;
362 }
363
364 curr = cursor.move_next();
365 }
366 Ok(recipients)
367 }
368
369 /// Prepare allocations for sending freeze messages.
370 pub(crate) fn prepare_freeze_messages(&self) -> Result<FreezeMessages, AllocError> {
371 let recipients = self.find_freeze_recipients()?;
372 let mut batch = KVVec::with_capacity(recipients.len(), GFP_KERNEL)?;
373 for (node, proc) in recipients {
374 let Some(cookie) = proc.get_freeze_cookie(&node) else {
375 // If the freeze listener was removed in the meantime, just discard the
376 // notification.
377 continue;
378 };
379 let msg_alloc = FreezeMessage::new(GFP_KERNEL)?;
380 let msg = FreezeMessage::init(msg_alloc, cookie);
381 batch.push((proc, msg), GFP_KERNEL)?;
382 }
383
384 Ok(FreezeMessages { batch })
385 }
386}
387
388pub(crate) struct FreezeMessages {
389 batch: KVVec<(Arc<Process>, DLArc<FreezeMessage>)>,
390}
391
392impl FreezeMessages {
393 pub(crate) fn send_messages(self) {
394 for (proc, msg) in self.batch {
395 let _ = proc.push_work(msg);
396 }
397 }
398}