Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/android/binder/freeze.rs
49509 views
1
// SPDX-License-Identifier: GPL-2.0
2
3
// Copyright (C) 2025 Google LLC.
4
5
use 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
16
use 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)]
22
pub(crate) struct FreezeCookie(u64);
23
24
/// Represents a listener for changes to the frozen state of a process.
25
pub(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
46
impl 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
58
type UninitFM = UniqueArc<core::mem::MaybeUninit<DTRWrap<FreezeMessage>>>;
59
60
/// Represents a notification that the freeze state has changed.
61
pub(crate) struct FreezeMessage {
62
cookie: FreezeCookie,
63
}
64
65
kernel::list::impl_list_arc_safe! {
66
impl ListArcSafe<0> for FreezeMessage {
67
untracked;
68
}
69
}
70
71
impl 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
84
impl 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
156
impl 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
164
impl 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
388
pub(crate) struct FreezeMessages {
389
batch: KVVec<(Arc<Process>, DLArc<FreezeMessage>)>,
390
}
391
392
impl FreezeMessages {
393
pub(crate) fn send_messages(self) {
394
for (proc, msg) in self.batch {
395
let _ = proc.push_work(msg);
396
}
397
}
398
}
399
400