// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2025 Google LLC. //! This module defines the `Process` type, which represents a process using a particular binder//! context.//!//! The `Process` object keeps track of all of the resources that this process owns in the binder//! context.//!//! There is one `Process` object for each binder fd that a process has opened, so processes using//! several binder contexts have several `Process` objects. This ensures that the contexts are//! fully separated. use core::mem::take; use kernel::{ bindings, cred::Credential, error::Error, fs::file::{self, File}, id_pool::IdPool, list::{List, ListArc, ListArcField, ListLinks}, mm, prelude::*, rbtree::{self, RBTree, RBTreeNode, RBTreeNodeReservation}, seq_file::SeqFile, seq_print, sync::poll::PollTable, sync::{ aref::ARef, lock::{spinlock::SpinLockBackend, Guard}, Arc, ArcBorrow, CondVar, CondVarTimeoutResult, Mutex, SpinLock, UniqueArc, }, task::Task, uaccess::{UserSlice, UserSliceReader}, uapi, workqueue::{self, Work},}; use crate::{ allocation::{Allocation, AllocationInfo, NewAllocation}, context::Context, defs::*, error::{BinderError, BinderResult}, node::{CouldNotDeliverCriticalIncrement, CritIncrWrapper, Node, NodeDeath, NodeRef}, page_range::ShrinkablePageRange, range_alloc::{RangeAllocator, ReserveNew, ReserveNewArgs}, stats::BinderStats, thread::{PushWorkRes, Thread}, transaction::TransactionInfo, BinderfsProcFile, DArc, DLArc, DTRWrap, DeliverToRead,}; #[path = "freeze.rs"]mod freeze;use self::freeze::{FreezeCookie, FreezeListener}; struct Mapping { address: usize, alloc: RangeAllocator<AllocationInfo>,} impl Mapping { fn new(address: usize, size: usize) -> Self { Self { address, alloc: RangeAllocator::new(size), } }} // bitflags for defer_work.const PROC_DEFER_FLUSH: u8 = 1;const PROC_DEFER_RELEASE: u8 = 2; #[derive(Copy, Clone)]pub(crate) enum IsFrozen { Yes, No, InProgress,} impl IsFrozen { /// Whether incoming transactions should be rejected due to freeze. pub(crate) fn is_frozen(self) -> bool { match self { IsFrozen::Yes => true, IsFrozen::No => false, IsFrozen::InProgress => true, } } /// Whether freeze notifications consider this process frozen. pub(crate) fn is_fully_frozen(self) -> bool { match self { IsFrozen::Yes => true, IsFrozen::No => false, IsFrozen::InProgress => false, } }} /// The fields of `Process` protected by the spinlock.pub(crate) struct ProcessInner { is_manager: bool, pub(crate) is_dead: bool, threads: RBTree<i32, Arc<Thread>>, /// INVARIANT: Threads pushed to this list must be owned by this process. ready_threads: List<Thread>, nodes: RBTree<u64, DArc<Node>>, mapping: Option<Mapping>, work: List<DTRWrap<dyn DeliverToRead>>, delivered_deaths: List<DTRWrap<NodeDeath>, 2>, /// The number of requested threads that haven't registered yet. requested_thread_count: u32, /// The maximum number of threads used by the process thread pool. max_threads: u32, /// The number of threads the started and registered with the thread pool. started_thread_count: u32, /// Bitmap of deferred work to do. defer_work: u8, /// Number of transactions to be transmitted before processes in freeze_wait /// are woken up. outstanding_txns: u32, /// Process is frozen and unable to service binder transactions. pub(crate) is_frozen: IsFrozen, /// Process received sync transactions since last frozen. pub(crate) sync_recv: bool, /// Process received async transactions since last frozen. pub(crate) async_recv: bool, pub(crate) binderfs_file: Option<BinderfsProcFile>, /// Check for oneway spam oneway_spam_detection_enabled: bool,} impl ProcessInner { fn new() -> Self { Self { is_manager: false, is_dead: false, threads: RBTree::new(), ready_threads: List::new(), mapping: None, nodes: RBTree::new(), work: List::new(), delivered_deaths: List::new(), requested_thread_count: 0, max_threads: 0, started_thread_count: 0, defer_work: 0, outstanding_txns: 0, is_frozen: IsFrozen::No, sync_recv: false, async_recv: false, binderfs_file: None, oneway_spam_detection_enabled: false, } } /// Schedule the work item for execution on this process. /// /// If any threads are ready for work, then the work item is given directly to that thread and /// it is woken up. Otherwise, it is pushed to the process work list. /// /// This call can fail only if the process is dead. In this case, the work item is returned to /// the caller so that the caller can drop it after releasing the inner process lock. This is /// necessary since the destructor of `Transaction` will take locks that can't necessarily be /// taken while holding the inner process lock. pub(crate) fn push_work( &mut self, work: DLArc<dyn DeliverToRead>, ) -> Result<(), (BinderError, DLArc<dyn DeliverToRead>)> { // Try to find a ready thread to which to push the work. if let Some(thread) = self.ready_threads.pop_front() { // Push to thread while holding state lock. This prevents the thread from giving up // (for example, because of a signal) when we're about to deliver work. match thread.push_work(work) { PushWorkRes::Ok => Ok(()), PushWorkRes::FailedDead(work) => Err((BinderError::new_dead(), work)), } } else if self.is_dead { Err((BinderError::new_dead(), work)) } else { let sync = work.should_sync_wakeup(); // Didn't find a thread waiting for proc work; this can happen // in two scenarios: // 1. All threads are busy handling transactions // In that case, one of those threads should call back into // the kernel driver soon and pick up this work. // 2. Threads are using the (e)poll interface, in which case // they may be blocked on the waitqueue without having been // added to waiting_threads. For this case, we just iterate // over all threads not handling transaction work, and // wake them all up. We wake all because we don't know whether // a thread that called into (e)poll is handling non-binder // work currently. self.work.push_back(work); // Wake up polling threads, if any. for thread in self.threads.values() { thread.notify_if_poll_ready(sync); } Ok(()) } } pub(crate) fn remove_node(&mut self, ptr: u64) { self.nodes.remove(&ptr); } /// Updates the reference count on the given node. pub(crate) fn update_node_refcount( &mut self, node: &DArc<Node>, inc: bool, strong: bool, count: usize, othread: Option<&Thread>, ) { let push = node.update_refcount_locked(inc, strong, count, self); // If we decided that we need to push work, push either to the process or to a thread if // one is specified. if let Some(node) = push { if let Some(thread) = othread { thread.push_work_deferred(node); } else { let _ = self.push_work(node); // Nothing to do: `push_work` may fail if the process is dead, but that's ok as in // that case, it doesn't care about the notification. } } } pub(crate) fn new_node_ref( &mut self, node: DArc<Node>, strong: bool, thread: Option<&Thread>, ) -> NodeRef { self.update_node_refcount(&node, true, strong, 1, thread); let strong_count = if strong { 1 } else { 0 }; NodeRef::new(node, strong_count, 1 - strong_count) } pub(crate) fn new_node_ref_with_thread( &mut self, node: DArc<Node>, strong: bool, thread: &Thread, wrapper: Option<CritIncrWrapper>, ) -> Result<NodeRef, CouldNotDeliverCriticalIncrement> { let push = match wrapper { None => node .incr_refcount_allow_zero2one(strong, self)? .map(|node| node as _), Some(wrapper) => node.incr_refcount_allow_zero2one_with_wrapper(strong, wrapper, self), }; if let Some(node) = push { thread.push_work_deferred(node); } let strong_count = if strong { 1 } else { 0 }; Ok(NodeRef::new(node, strong_count, 1 - strong_count)) } /// Returns an existing node with the given pointer and cookie, if one exists. /// /// Returns an error if a node with the given pointer but a different cookie exists. fn get_existing_node(&self, ptr: u64, cookie: u64) -> Result<Option<DArc<Node>>> { match self.nodes.get(&ptr) { None => Ok(None), Some(node) => { let (_, node_cookie) = node.get_id(); if node_cookie == cookie { Ok(Some(node.clone())) } else { Err(EINVAL) } } } } fn register_thread(&mut self) -> bool { if self.requested_thread_count == 0 { return false; } self.requested_thread_count -= 1; self.started_thread_count += 1; true } /// Finds a delivered death notification with the given cookie, removes it from the thread's /// delivered list, and returns it. fn pull_delivered_death(&mut self, cookie: u64) -> Option<DArc<NodeDeath>> { let mut cursor = self.delivered_deaths.cursor_front(); while let Some(next) = cursor.peek_next() { if next.cookie == cookie { return Some(next.remove().into_arc()); } cursor.move_next(); } None } pub(crate) fn death_delivered(&mut self, death: DArc<NodeDeath>) { if let Some(death) = ListArc::try_from_arc_or_drop(death) { self.delivered_deaths.push_back(death); } else { pr_warn!("Notification added to `delivered_deaths` twice."); } } pub(crate) fn add_outstanding_txn(&mut self) { self.outstanding_txns += 1; } fn txns_pending_locked(&self) -> bool { if self.outstanding_txns > 0 { return true; } for thread in self.threads.values() { if thread.has_current_transaction() { return true; } } false }} /// Used to keep track of a node that this process has a handle to.#[pin_data]pub(crate) struct NodeRefInfo { debug_id: usize, /// The refcount that this process owns to the node. node_ref: ListArcField<NodeRef, { Self::LIST_PROC }>, death: ListArcField<Option<DArc<NodeDeath>>, { Self::LIST_PROC }>, /// Cookie of the active freeze listener for this node. freeze: ListArcField<Option<FreezeCookie>, { Self::LIST_PROC }>, /// Used to store this `NodeRefInfo` in the node's `refs` list. #[pin] links: ListLinks<{ Self::LIST_NODE }>, /// The handle for this `NodeRefInfo`. handle: u32, /// The process that has a handle to the node. pub(crate) process: Arc<Process>,} impl NodeRefInfo { /// The id used for the `Node::refs` list. pub(crate) const LIST_NODE: u64 = 0x2da16350fb724a10; /// The id used for the `ListArc` in `ProcessNodeRefs`. const LIST_PROC: u64 = 0xd703a5263dcc8650; fn new(node_ref: NodeRef, handle: u32, process: Arc<Process>) -> impl PinInit<Self> { pin_init!(Self { debug_id: super::next_debug_id(), node_ref: ListArcField::new(node_ref), death: ListArcField::new(None), freeze: ListArcField::new(None), links <- ListLinks::new(), handle, process, }) } kernel::list::define_list_arc_field_getter! { pub(crate) fn death(&mut self<{Self::LIST_PROC}>) -> &mut Option<DArc<NodeDeath>> { death } pub(crate) fn freeze(&mut self<{Self::LIST_PROC}>) -> &mut Option<FreezeCookie> { freeze } pub(crate) fn node_ref(&mut self<{Self::LIST_PROC}>) -> &mut NodeRef { node_ref } pub(crate) fn node_ref2(&self<{Self::LIST_PROC}>) -> &NodeRef { node_ref } }} kernel::list::impl_list_arc_safe! { impl ListArcSafe<{Self::LIST_NODE}> for NodeRefInfo { untracked; } impl ListArcSafe<{Self::LIST_PROC}> for NodeRefInfo { untracked; }}kernel::list::impl_list_item! { impl ListItem<{Self::LIST_NODE}> for NodeRefInfo { using ListLinks { self.links }; }} /// Keeps track of references this process has to nodes owned by other processes.////// TODO: Currently, the rbtree requires two allocations per node reference, and two tree/// traversals to look up a node by `Node::global_id`. Once the rbtree is more powerful, these/// extra costs should be eliminated.struct ProcessNodeRefs { /// Used to look up nodes using the 32-bit id that this process knows it by. by_handle: RBTree<u32, ListArc<NodeRefInfo, { NodeRefInfo::LIST_PROC }>>, /// Used to quickly find unused ids in `by_handle`. handle_is_present: IdPool, /// Used to look up nodes without knowing their local 32-bit id. The usize is the address of /// the underlying `Node` struct as returned by `Node::global_id`. by_node: RBTree<usize, u32>, /// Used to look up a `FreezeListener` by cookie. /// /// There might be multiple freeze listeners for the same node, but at most one of them is /// active. freeze_listeners: RBTree<FreezeCookie, FreezeListener>,} impl ProcessNodeRefs { fn new() -> Self { Self { by_handle: RBTree::new(), handle_is_present: IdPool::new(), by_node: RBTree::new(), freeze_listeners: RBTree::new(), } }} use core::mem::offset_of;use kernel::bindings::rb_process_layout;pub(crate) const PROCESS_LAYOUT: rb_process_layout = rb_process_layout { arc_offset: Arc::<Process>::DATA_OFFSET, task: offset_of!(Process, task),}; /// A process using binder.////// Strictly speaking, there can be multiple of these per process. There is one for each binder fd/// that a process has opened, so processes using several binder contexts have several `Process`/// objects. This ensures that the contexts are fully separated.#[pin_data]pub(crate) struct Process { pub(crate) ctx: Arc<Context>, // The task leader (process). pub(crate) task: ARef<Task>, // Credential associated with file when `Process` is created. pub(crate) cred: ARef<Credential>, #[pin] pub(crate) inner: SpinLock<ProcessInner>, #[pin] pub(crate) pages: ShrinkablePageRange, // Waitqueue of processes waiting for all outstanding transactions to be // processed. #[pin] freeze_wait: CondVar, // Node references are in a different lock to avoid recursive acquisition when // incrementing/decrementing a node in another process. #[pin] node_refs: Mutex<ProcessNodeRefs>, // Work node for deferred work item. #[pin] defer_work: Work<Process>, // Links for process list in Context. #[pin] links: ListLinks, pub(crate) stats: BinderStats,} kernel::impl_has_work! { impl HasWork<Process> for Process { self.defer_work }} kernel::list::impl_list_arc_safe! { impl ListArcSafe<0> for Process { untracked; }}kernel::list::impl_list_item! { impl ListItem<0> for Process { using ListLinks { self.links }; }} impl workqueue::WorkItem for Process { type Pointer = Arc<Process>; fn run(me: Arc<Self>) { let defer; { let mut inner = me.inner.lock(); defer = inner.defer_work; inner.defer_work = 0; } if defer & PROC_DEFER_FLUSH != 0 { me.deferred_flush(); } if defer & PROC_DEFER_RELEASE != 0 { me.deferred_release(); }Lines 1–500 of 1780. Load more
// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2025 Google LLC. //! This module defines the `Process` type, which represents a process using a particular binder //! context. //! //! The `Process` object keeps track of all of the resources that this process owns in the binder //! context. //! //! There is one `Process` object for each binder fd that a process has opened, so processes using //! several binder contexts have several `Process` objects. This ensures that the contexts are //! fully separated. use core::mem::take; use kernel::{ bindings, cred::Credential, error::Error, fs::file::{self, File}, id_pool::IdPool, list::{List, ListArc, ListArcField, ListLinks}, mm, prelude::*, rbtree::{self, RBTree, RBTreeNode, RBTreeNodeReservation}, seq_file::SeqFile, seq_print, sync::poll::PollTable, sync::{ aref::ARef, lock::{spinlock::SpinLockBackend, Guard}, Arc, ArcBorrow, CondVar, CondVarTimeoutResult, Mutex, SpinLock, UniqueArc, }, task::Task, uaccess::{UserSlice, UserSliceReader}, uapi, workqueue::{self, Work}, }; use crate::{ allocation::{Allocation, AllocationInfo, NewAllocation}, context::Context, defs::*, error::{BinderError, BinderResult}, node::{CouldNotDeliverCriticalIncrement, CritIncrWrapper, Node, NodeDeath, NodeRef}, page_range::ShrinkablePageRange, range_alloc::{RangeAllocator, ReserveNew, ReserveNewArgs}, stats::BinderStats, thread::{PushWorkRes, Thread}, transaction::TransactionInfo, BinderfsProcFile, DArc, DLArc, DTRWrap, DeliverToRead, }; #[path = "freeze.rs"] mod freeze; use self::freeze::{FreezeCookie, FreezeListener}; struct Mapping { address: usize, alloc: RangeAllocator<AllocationInfo>, } impl Mapping { fn new(address: usize, size: usize) -> Self { Self { address, alloc: RangeAllocator::new(size), } } } // bitflags for defer_work. const PROC_DEFER_FLUSH: u8 = 1; const PROC_DEFER_RELEASE: u8 = 2; #[derive(Copy, Clone)] pub(crate) enum IsFrozen { Yes, No, InProgress, } impl IsFrozen { /// Whether incoming transactions should be rejected due to freeze. pub(crate) fn is_frozen(self) -> bool { match self { IsFrozen::Yes => true, IsFrozen::No => false, IsFrozen::InProgress => true, } } /// Whether freeze notifications consider this process frozen. pub(crate) fn is_fully_frozen(self) -> bool { match self { IsFrozen::Yes => true, IsFrozen::No => false, IsFrozen::InProgress => false, } } } /// The fields of `Process` protected by the spinlock. pub(crate) struct ProcessInner { is_manager: bool, pub(crate) is_dead: bool, threads: RBTree<i32, Arc<Thread>>, /// INVARIANT: Threads pushed to this list must be owned by this process. ready_threads: List<Thread>, nodes: RBTree<u64, DArc<Node>>, mapping: Option<Mapping>, work: List<DTRWrap<dyn DeliverToRead>>, delivered_deaths: List<DTRWrap<NodeDeath>, 2>, /// The number of requested threads that haven't registered yet. requested_thread_count: u32, /// The maximum number of threads used by the process thread pool. max_threads: u32, /// The number of threads the started and registered with the thread pool. started_thread_count: u32, /// Bitmap of deferred work to do. defer_work: u8, /// Number of transactions to be transmitted before processes in freeze_wait /// are woken up. outstanding_txns: u32, /// Process is frozen and unable to service binder transactions. pub(crate) is_frozen: IsFrozen, /// Process received sync transactions since last frozen. pub(crate) sync_recv: bool, /// Process received async transactions since last frozen. pub(crate) async_recv: bool, pub(crate) binderfs_file: Option<BinderfsProcFile>, /// Check for oneway spam oneway_spam_detection_enabled: bool, } impl ProcessInner { fn new() -> Self { Self { is_manager: false, is_dead: false, threads: RBTree::new(), ready_threads: List::new(), mapping: None, nodes: RBTree::new(), work: List::new(), delivered_deaths: List::new(), requested_thread_count: 0, max_threads: 0, started_thread_count: 0, defer_work: 0, outstanding_txns: 0, is_frozen: IsFrozen::No, sync_recv: false, async_recv: false, binderfs_file: None, oneway_spam_detection_enabled: false, } } /// Schedule the work item for execution on this process. /// /// If any threads are ready for work, then the work item is given directly to that thread and /// it is woken up. Otherwise, it is pushed to the process work list. /// /// This call can fail only if the process is dead. In this case, the work item is returned to /// the caller so that the caller can drop it after releasing the inner process lock. This is /// necessary since the destructor of `Transaction` will take locks that can't necessarily be /// taken while holding the inner process lock. pub(crate) fn push_work( &mut self, work: DLArc<dyn DeliverToRead>, ) -> Result<(), (BinderError, DLArc<dyn DeliverToRead>)> { // Try to find a ready thread to which to push the work. if let Some(thread) = self.ready_threads.pop_front() { // Push to thread while holding state lock. This prevents the thread from giving up // (for example, because of a signal) when we're about to deliver work. match thread.push_work(work) { PushWorkRes::Ok => Ok(()), PushWorkRes::FailedDead(work) => Err((BinderError::new_dead(), work)), } } else if self.is_dead { Err((BinderError::new_dead(), work)) } else { let sync = work.should_sync_wakeup(); // Didn't find a thread waiting for proc work; this can happen // in two scenarios: // 1. All threads are busy handling transactions // In that case, one of those threads should call back into // the kernel driver soon and pick up this work. // 2. Threads are using the (e)poll interface, in which case // they may be blocked on the waitqueue without having been // added to waiting_threads. For this case, we just iterate // over all threads not handling transaction work, and // wake them all up. We wake all because we don't know whether // a thread that called into (e)poll is handling non-binder // work currently. self.work.push_back(work); // Wake up polling threads, if any. for thread in self.threads.values() { thread.notify_if_poll_ready(sync); } Ok(()) } } pub(crate) fn remove_node(&mut self, ptr: u64) { self.nodes.remove(&ptr); } /// Updates the reference count on the given node. pub(crate) fn update_node_refcount( &mut self, node: &DArc<Node>, inc: bool, strong: bool, count: usize, othread: Option<&Thread>, ) { let push = node.update_refcount_locked(inc, strong, count, self); // If we decided that we need to push work, push either to the process or to a thread if // one is specified. if let Some(node) = push { if let Some(thread) = othread { thread.push_work_deferred(node); } else { let _ = self.push_work(node); // Nothing to do: `push_work` may fail if the process is dead, but that's ok as in // that case, it doesn't care about the notification. } } } pub(crate) fn new_node_ref( &mut self, node: DArc<Node>, strong: bool, thread: Option<&Thread>, ) -> NodeRef { self.update_node_refcount(&node, true, strong, 1, thread); let strong_count = if strong { 1 } else { 0 }; NodeRef::new(node, strong_count, 1 - strong_count) } pub(crate) fn new_node_ref_with_thread( &mut self, node: DArc<Node>, strong: bool, thread: &Thread, wrapper: Option<CritIncrWrapper>, ) -> Result<NodeRef, CouldNotDeliverCriticalIncrement> { let push = match wrapper { None => node .incr_refcount_allow_zero2one(strong, self)? .map(|node| node as _), Some(wrapper) => node.incr_refcount_allow_zero2one_with_wrapper(strong, wrapper, self), }; if let Some(node) = push { thread.push_work_deferred(node); } let strong_count = if strong { 1 } else { 0 }; Ok(NodeRef::new(node, strong_count, 1 - strong_count)) } /// Returns an existing node with the given pointer and cookie, if one exists. /// /// Returns an error if a node with the given pointer but a different cookie exists. fn get_existing_node(&self, ptr: u64, cookie: u64) -> Result<Option<DArc<Node>>> { match self.nodes.get(&ptr) { None => Ok(None), Some(node) => { let (_, node_cookie) = node.get_id(); if node_cookie == cookie { Ok(Some(node.clone())) } else { Err(EINVAL) } } } } fn register_thread(&mut self) -> bool { if self.requested_thread_count == 0 { return false; } self.requested_thread_count -= 1; self.started_thread_count += 1; true } /// Finds a delivered death notification with the given cookie, removes it from the thread's /// delivered list, and returns it. fn pull_delivered_death(&mut self, cookie: u64) -> Option<DArc<NodeDeath>> { let mut cursor = self.delivered_deaths.cursor_front(); while let Some(next) = cursor.peek_next() { if next.cookie == cookie { return Some(next.remove().into_arc()); } cursor.move_next(); } None } pub(crate) fn death_delivered(&mut self, death: DArc<NodeDeath>) { if let Some(death) = ListArc::try_from_arc_or_drop(death) { self.delivered_deaths.push_back(death); } else { pr_warn!("Notification added to `delivered_deaths` twice."); } } pub(crate) fn add_outstanding_txn(&mut self) { self.outstanding_txns += 1; } fn txns_pending_locked(&self) -> bool { if self.outstanding_txns > 0 { return true; } for thread in self.threads.values() { if thread.has_current_transaction() { return true; } } false } } /// Used to keep track of a node that this process has a handle to. #[pin_data] pub(crate) struct NodeRefInfo { debug_id: usize, /// The refcount that this process owns to the node. node_ref: ListArcField<NodeRef, { Self::LIST_PROC }>, death: ListArcField<Option<DArc<NodeDeath>>, { Self::LIST_PROC }>, /// Cookie of the active freeze listener for this node. freeze: ListArcField<Option<FreezeCookie>, { Self::LIST_PROC }>, /// Used to store this `NodeRefInfo` in the node's `refs` list. #[pin] links: ListLinks<{ Self::LIST_NODE }>, /// The handle for this `NodeRefInfo`. handle: u32, /// The process that has a handle to the node. pub(crate) process: Arc<Process>, } impl NodeRefInfo { /// The id used for the `Node::refs` list. pub(crate) const LIST_NODE: u64 = 0x2da16350fb724a10; /// The id used for the `ListArc` in `ProcessNodeRefs`. const LIST_PROC: u64 = 0xd703a5263dcc8650; fn new(node_ref: NodeRef, handle: u32, process: Arc<Process>) -> impl PinInit<Self> { pin_init!(Self { debug_id: super::next_debug_id(), node_ref: ListArcField::new(node_ref), death: ListArcField::new(None), freeze: ListArcField::new(None), links <- ListLinks::new(), handle, process, }) } kernel::list::define_list_arc_field_getter! { pub(crate) fn death(&mut self<{Self::LIST_PROC}>) -> &mut Option<DArc<NodeDeath>> { death } pub(crate) fn freeze(&mut self<{Self::LIST_PROC}>) -> &mut Option<FreezeCookie> { freeze } pub(crate) fn node_ref(&mut self<{Self::LIST_PROC}>) -> &mut NodeRef { node_ref } pub(crate) fn node_ref2(&self<{Self::LIST_PROC}>) -> &NodeRef { node_ref } } } kernel::list::impl_list_arc_safe! { impl ListArcSafe<{Self::LIST_NODE}> for NodeRefInfo { untracked; } impl ListArcSafe<{Self::LIST_PROC}> for NodeRefInfo { untracked; } } kernel::list::impl_list_item! { impl ListItem<{Self::LIST_NODE}> for NodeRefInfo { using ListLinks { self.links }; } } /// Keeps track of references this process has to nodes owned by other processes. /// /// TODO: Currently, the rbtree requires two allocations per node reference, and two tree /// traversals to look up a node by `Node::global_id`. Once the rbtree is more powerful, these /// extra costs should be eliminated. struct ProcessNodeRefs { /// Used to look up nodes using the 32-bit id that this process knows it by. by_handle: RBTree<u32, ListArc<NodeRefInfo, { NodeRefInfo::LIST_PROC }>>, /// Used to quickly find unused ids in `by_handle`. handle_is_present: IdPool, /// Used to look up nodes without knowing their local 32-bit id. The usize is the address of /// the underlying `Node` struct as returned by `Node::global_id`. by_node: RBTree<usize, u32>, /// Used to look up a `FreezeListener` by cookie. /// /// There might be multiple freeze listeners for the same node, but at most one of them is /// active. freeze_listeners: RBTree<FreezeCookie, FreezeListener>, } impl ProcessNodeRefs { fn new() -> Self { Self { by_handle: RBTree::new(), handle_is_present: IdPool::new(), by_node: RBTree::new(), freeze_listeners: RBTree::new(), } } } use core::mem::offset_of; use kernel::bindings::rb_process_layout; pub(crate) const PROCESS_LAYOUT: rb_process_layout = rb_process_layout { arc_offset: Arc::<Process>::DATA_OFFSET, task: offset_of!(Process, task), }; /// A process using binder. /// /// Strictly speaking, there can be multiple of these per process. There is one for each binder fd /// that a process has opened, so processes using several binder contexts have several `Process` /// objects. This ensures that the contexts are fully separated. #[pin_data] pub(crate) struct Process { pub(crate) ctx: Arc<Context>, // The task leader (process). pub(crate) task: ARef<Task>, // Credential associated with file when `Process` is created. pub(crate) cred: ARef<Credential>, #[pin] pub(crate) inner: SpinLock<ProcessInner>, #[pin] pub(crate) pages: ShrinkablePageRange, // Waitqueue of processes waiting for all outstanding transactions to be // processed. #[pin] freeze_wait: CondVar, // Node references are in a different lock to avoid recursive acquisition when // incrementing/decrementing a node in another process. #[pin] node_refs: Mutex<ProcessNodeRefs>, // Work node for deferred work item. #[pin] defer_work: Work<Process>, // Links for process list in Context. #[pin] links: ListLinks, pub(crate) stats: BinderStats, } kernel::impl_has_work! { impl HasWork<Process> for Process { self.defer_work } } kernel::list::impl_list_arc_safe! { impl ListArcSafe<0> for Process { untracked; } } kernel::list::impl_list_item! { impl ListItem<0> for Process { using ListLinks { self.links }; } } impl workqueue::WorkItem for Process { type Pointer = Arc<Process>; fn run(me: Arc<Self>) { let defer; { let mut inner = me.inner.lock(); defer = inner.defer_work; inner.defer_work = 0; } if defer & PROC_DEFER_FLUSH != 0 { me.deferred_flush(); } if defer & PROC_DEFER_RELEASE != 0 { me.deferred_release(); }