use kernel::{
alloc::AllocError,
list::ListArc,
prelude::*,
rbtree::{self, RBTreeNodeReservation},
seq_file::SeqFile,
seq_print,
sync::{Arc, UniqueArc},
uaccess::UserSliceReader,
};
use crate::{
defs::*, node::Node, process::Process, thread::Thread, BinderReturnWriter, DArc, DLArc,
DTRWrap, DeliverToRead,
};
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub(crate) struct FreezeCookie(u64);
pub(crate) struct FreezeListener {
pub(crate) node: DArc<Node>,
cookie: FreezeCookie,
last_is_frozen: Option<bool>,
is_pending: bool,
is_clearing: bool,
num_pending_duplicates: u64,
num_cleared_duplicates: u64,
}
impl FreezeListener {
fn allow_duplicate(&self, node: &DArc<Node>) -> bool {
Arc::ptr_eq(&self.node, node) && self.is_clearing
}
}
type UninitFM = UniqueArc<core::mem::MaybeUninit<DTRWrap<FreezeMessage>>>;
pub(crate) struct FreezeMessage {
cookie: FreezeCookie,
}
kernel::list::impl_list_arc_safe! {
impl ListArcSafe<0> for FreezeMessage {
untracked;
}
}
impl FreezeMessage {
fn new(flags: kernel::alloc::Flags) -> Result<UninitFM, AllocError> {
UniqueArc::new_uninit(flags)
}
fn init(ua: UninitFM, cookie: FreezeCookie) -> DLArc<FreezeMessage> {
match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie })) {
Ok(msg) => ListArc::from(msg),
Err(err) => match err {},
}
}
}
impl DeliverToRead for FreezeMessage {
fn do_work(
self: DArc<Self>,
thread: &Thread,
writer: &mut BinderReturnWriter<'_>,
) -> Result<bool> {
let _removed_listener;
let mut node_refs = thread.process.node_refs.lock();
let Some(mut freeze_entry) = node_refs.freeze_listeners.find_mut(&self.cookie) else {
return Ok(true);
};
let freeze = freeze_entry.get_mut();
if freeze.num_cleared_duplicates > 0 {
freeze.num_cleared_duplicates -= 1;
drop(node_refs);
writer.write_code(BR_CLEAR_FREEZE_NOTIFICATION_DONE)?;
writer.write_payload(&self.cookie.0)?;
return Ok(true);
}
if freeze.is_pending {
return Ok(true);
}
if freeze.is_clearing {
_removed_listener = freeze_entry.remove_node();
drop(node_refs);
writer.write_code(BR_CLEAR_FREEZE_NOTIFICATION_DONE)?;
writer.write_payload(&self.cookie.0)?;
Ok(true)
} else {
let is_frozen = freeze.node.owner.inner.lock().is_frozen;
if freeze.last_is_frozen == Some(is_frozen) {
return Ok(true);
}
let mut state_info = BinderFrozenStateInfo::default();
state_info.is_frozen = is_frozen as u32;
state_info.cookie = freeze.cookie.0;
freeze.is_pending = true;
freeze.last_is_frozen = Some(is_frozen);
drop(node_refs);
writer.write_code(BR_FROZEN_BINDER)?;
writer.write_payload(&state_info)?;
Ok(false)
}
}
fn cancel(self: DArc<Self>) {}
fn should_sync_wakeup(&self) -> bool {
false
}
#[inline(never)]
fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> {
seq_print!(m, "{}has frozen binder\n", prefix);
Ok(())
}
}
impl FreezeListener {
pub(crate) fn on_process_exit(&self, proc: &Arc<Process>) {
if !self.is_clearing {
self.node.remove_freeze_listener(proc);
}
}
}
impl Process {
pub(crate) fn request_freeze_notif(
self: &Arc<Self>,
reader: &mut UserSliceReader,
) -> Result<()> {
let hc = reader.read::<BinderHandleCookie>()?;
let handle = hc.handle;
let cookie = FreezeCookie(hc.cookie);
let msg = FreezeMessage::new(GFP_KERNEL)?;
let alloc = RBTreeNodeReservation::new(GFP_KERNEL)?;
let mut node_refs_guard = self.node_refs.lock();
let node_refs = &mut *node_refs_guard;
let Some(info) = node_refs.by_handle.get_mut(&handle) else {
pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle);
return Err(EINVAL);
};
if info.freeze().is_some() {
pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n");
return Err(EINVAL);
}
let node_ref = info.node_ref();
let freeze_entry = node_refs.freeze_listeners.entry(cookie);
if let rbtree::Entry::Occupied(ref dupe) = freeze_entry {
if !dupe.get().allow_duplicate(&node_ref.node) {
pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n");
return Err(EINVAL);
}
}
node_ref.node.add_freeze_listener(self, GFP_KERNEL)?;
match freeze_entry {
rbtree::Entry::Vacant(entry) => {
entry.insert(
FreezeListener {
cookie,
node: node_ref.node.clone(),
last_is_frozen: None,
is_pending: false,
is_clearing: false,
num_pending_duplicates: 0,
num_cleared_duplicates: 0,
},
alloc,
);
}
rbtree::Entry::Occupied(mut dupe) => {
let dupe = dupe.get_mut();
if dupe.is_pending {
dupe.num_pending_duplicates += 1;
} else {
dupe.num_cleared_duplicates += 1;
}
dupe.last_is_frozen = None;
dupe.is_pending = false;
dupe.is_clearing = false;
}
}
*info.freeze() = Some(cookie);
let msg = FreezeMessage::init(msg, cookie);
drop(node_refs_guard);
let _ = self.push_work(msg);
Ok(())
}
pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader) -> Result<()> {
let cookie = FreezeCookie(reader.read()?);
let alloc = FreezeMessage::new(GFP_KERNEL)?;
let mut node_refs_guard = self.node_refs.lock();
let node_refs = &mut *node_refs_guard;
let Some(freeze) = node_refs.freeze_listeners.get_mut(&cookie) else {
pr_warn!("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n", cookie.0);
return Err(EINVAL);
};
let mut clear_msg = None;
if freeze.num_pending_duplicates > 0 {
clear_msg = Some(FreezeMessage::init(alloc, cookie));
freeze.num_pending_duplicates -= 1;
freeze.num_cleared_duplicates += 1;
} else {
if !freeze.is_pending {
pr_warn!(
"BC_FREEZE_NOTIFICATION_DONE {:016x} not pending\n",
cookie.0
);
return Err(EINVAL);
}
if freeze.is_clearing {
clear_msg = Some(FreezeMessage::init(alloc, cookie));
}
freeze.is_pending = false;
}
drop(node_refs_guard);
if let Some(clear_msg) = clear_msg {
let _ = self.push_work(clear_msg);
}
Ok(())
}
pub(crate) fn clear_freeze_notif(self: &Arc<Self>, reader: &mut UserSliceReader) -> Result<()> {
let hc = reader.read::<BinderHandleCookie>()?;
let handle = hc.handle;
let cookie = FreezeCookie(hc.cookie);
let alloc = FreezeMessage::new(GFP_KERNEL)?;
let mut node_refs_guard = self.node_refs.lock();
let node_refs = &mut *node_refs_guard;
let Some(info) = node_refs.by_handle.get_mut(&handle) else {
pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n", handle);
return Err(EINVAL);
};
let Some(info_cookie) = info.freeze() else {
pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n");
return Err(EINVAL);
};
if *info_cookie != cookie {
pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n");
return Err(EINVAL);
}
let Some(listener) = node_refs.freeze_listeners.get_mut(&cookie) else {
pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n", handle);
return Err(EINVAL);
};
listener.is_clearing = true;
listener.node.remove_freeze_listener(self);
*info.freeze() = None;
let mut msg = None;
if !listener.is_pending {
msg = Some(FreezeMessage::init(alloc, cookie));
}
drop(node_refs_guard);
if let Some(msg) = msg {
let _ = self.push_work(msg);
}
Ok(())
}
fn get_freeze_cookie(&self, node: &DArc<Node>) -> Option<FreezeCookie> {
let node_refs = &mut *self.node_refs.lock();
let handle = node_refs.by_node.get(&node.global_id())?;
let node_ref = node_refs.by_handle.get_mut(handle)?;
*node_ref.freeze()
}
#[expect(clippy::type_complexity)]
fn find_freeze_recipients(&self) -> Result<KVVec<(DArc<Node>, Arc<Process>)>, AllocError> {
let mut node_proc_pair;
let mut recipients =
KVVec::with_capacity(8, GFP_KERNEL).unwrap_or_else(|_err| KVVec::new());
let mut inner = self.lock_with_nodes();
let mut curr = inner.nodes.cursor_front();
while let Some(cursor) = curr {
let (key, node) = cursor.current();
let key = *key;
let list = node.freeze_list(&inner.inner);
let len = list.len();
if recipients.spare_capacity_mut().len() < len {
drop(inner);
recipients.reserve(len, GFP_KERNEL)?;
inner = self.lock_with_nodes();
curr = inner.nodes.cursor_lower_bound(&key);
continue;
}
for proc in list {
node_proc_pair = (node.clone(), proc.clone());
recipients
.push_within_capacity(node_proc_pair)
.map_err(|_| {
pr_err!(
"push_within_capacity failed even though we checked the capacity\n"
);
AllocError
})?;
}
curr = cursor.move_next();
}
Ok(recipients)
}
pub(crate) fn prepare_freeze_messages(&self) -> Result<FreezeMessages, AllocError> {
let recipients = self.find_freeze_recipients()?;
let mut batch = KVVec::with_capacity(recipients.len(), GFP_KERNEL)?;
for (node, proc) in recipients {
let Some(cookie) = proc.get_freeze_cookie(&node) else {
continue;
};
let msg_alloc = FreezeMessage::new(GFP_KERNEL)?;
let msg = FreezeMessage::init(msg_alloc, cookie);
batch.push((proc, msg), GFP_KERNEL)?;
}
Ok(FreezeMessages { batch })
}
}
pub(crate) struct FreezeMessages {
batch: KVVec<(Arc<Process>, DLArc<FreezeMessage>)>,
}
impl FreezeMessages {
pub(crate) fn send_messages(self) {
for (proc, msg) in self.batch {
let _ = proc.push_work(msg);
}
}
}