1776 lines · 60.4 KB · Rust
RawDownload
// 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 1776.