| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- // SPDX-License-Identifier: GPL-2.0
- // Copyright (C) 2025 Google LLC.
- 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);
- /// Represents a listener for changes to the frozen state of a process.
- pub(crate) struct FreezeListener {
- /// The node we are listening for.
- pub(crate) node: DArc<Node>,
- /// The cookie of this freeze listener.
- cookie: FreezeCookie,
- /// What value of `is_frozen` did we most recently tell userspace about?
- last_is_frozen: Option<bool>,
- /// We sent a `BR_FROZEN_BINDER` and we are waiting for `BC_FREEZE_NOTIFICATION_DONE` before
- /// sending any other commands.
- is_pending: bool,
- /// Userspace sent `BC_CLEAR_FREEZE_NOTIFICATION` and we need to reply with
- /// `BR_CLEAR_FREEZE_NOTIFICATION_DONE` as soon as possible. If `is_pending` is set, then we
- /// must wait for it to be unset before we can reply.
- is_clearing: bool,
- /// Number of cleared duplicates that can't be deleted until userspace sends
- /// `BC_FREEZE_NOTIFICATION_DONE`.
- num_pending_duplicates: u64,
- /// Number of cleared duplicates that can be deleted.
- num_cleared_duplicates: u64,
- }
- impl FreezeListener {
- /// Is it okay to create a new listener with the same cookie as this one for the provided node?
- ///
- /// Under some scenarios, userspace may delete a freeze listener and immediately recreate it
- /// with the same cookie. This results in duplicate listeners. To avoid issues with ambiguity,
- /// we allow this only if the new listener is for the same node, and we also require that the
- /// old listener has already been cleared.
- 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>>>;
- /// Represents a notification that the freeze state has changed.
- 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 {
- kernel::warn_on!(freeze.num_cleared_duplicates != 0);
- if freeze.num_pending_duplicates > 0 {
- // The primary freeze listener was deleted, so convert a pending duplicate back
- // into the primary one.
- freeze.num_pending_duplicates -= 1;
- freeze.is_pending = true;
- freeze.is_clearing = true;
- } else {
- _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.is_fully_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)?;
- // BR_FROZEN_BINDER notifications can cause transactions
- 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);
- }
- }
- // All failure paths must come before this call, and all modifications must come after this
- // call.
- 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);
- }
- let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen();
- if freeze.is_clearing || freeze.last_is_frozen != Some(is_frozen) {
- // Immediately send another FreezeMessage.
- 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()
- }
- /// Creates a vector of every freeze listener on this process.
- ///
- /// Returns pairs of the remote process listening for notifications and the local node it is
- /// listening on.
- #[expect(clippy::type_complexity)]
- fn find_freeze_recipients(&self) -> Result<KVVec<(DArc<Node>, Arc<Process>)>, AllocError> {
- // Defined before `inner` to drop after releasing spinlock if `push_within_capacity` fails.
- let mut node_proc_pair;
- // We pre-allocate space for up to 8 recipients before we take the spinlock. However, if
- // the allocation fails, use a vector with a capacity of zero instead of failing. After
- // all, there might not be any freeze listeners, in which case this operation could still
- // succeed.
- 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_mut();
- 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();
- // Find the node we were looking at and try again. If the set of nodes was changed,
- // then just proceed to the next node. This is ok because we don't guarantee the
- // inclusion of nodes that are added or removed in parallel with this operation.
- curr = inner.nodes.cursor_lower_bound_mut(&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)
- }
- /// Prepare allocations for sending freeze messages.
- 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 {
- // If the freeze listener was removed in the meantime, just discard the
- // notification.
- 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);
- }
- }
- }
|