// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2024 Google LLC. use kernel::{ list::{ AtomicListArcTracker, HasListLinks, List, ListArc, ListArcSafe, ListItem, ListLinks, TryNewListArc, }, prelude::*, seq_file::SeqFile, seq_print, sync::lock::{spinlock::SpinLockBackend, Guard}, sync::{Arc, LockedBy, SpinLock}, uaccess::UserSliceWriter, }; use crate::{ defs::*, error::BinderError, prio::{self, BinderPriority}, process::{NodeRefInfo, Process, ProcessInner}, thread::Thread, transaction::Transaction, DArc, DLArc, DTRWrap, DeliverToRead, }; mod wrapper; pub(crate) use self::wrapper::CritIncrWrapper; #[derive(Debug)] pub(crate) struct CouldNotDeliverCriticalIncrement; /// Keeps track of how this node is scheduled. /// /// There are two ways to schedule a node to a work list. Just schedule the node itself, or /// allocate a wrapper that references the node and schedule the wrapper. These wrappers exists to /// make it possible to "move" a node from one list to another - when `do_work` is called directly /// on the `Node`, then it's a no-op if there's also a pending wrapper. /// /// Wrappers are generally only needed for zero-to-one refcount increments, and there are two cases /// of this: weak increments and strong increments. We call such increments "critical" because it /// is critical that they are delivered to the thread doing the increment. Some examples: /// /// * One thread makes a zero-to-one strong increment, and another thread makes a zero-to-one weak /// increment. Delivering the node to the thread doing the weak increment is wrong, since the /// thread doing the strong increment may have ended a long time ago when the command is actually /// processed by userspace. /// /// * We have a weak reference and are about to drop it on one thread. But then another thread does /// a zero-to-one strong increment. If the strong increment gets sent to the thread that was /// about to drop the weak reference, then the strong increment could be processed after the /// other thread has already exited, which would be too late. /// /// Note that trying to create a `ListArc` to the node can succeed even if `has_normal_push` is /// set. This is because another thread might just have popped the node from a todo list, but not /// yet called `do_work`. However, if `has_normal_push` is false, then creating a `ListArc` should /// always succeed. /// /// Like the other fields in `NodeInner`, the delivery state is protected by the process lock. struct DeliveryState { /// Is the `Node` currently scheduled? has_pushed_node: bool, /// Is a wrapper currently scheduled? /// /// The wrapper is used only for strong zero2one increments. has_pushed_wrapper: bool, /// Is the currently scheduled `Node` scheduled due to a weak zero2one increment? /// /// Weak zero2one operations are always scheduled using the `Node`. has_weak_zero2one: bool, /// Is the currently scheduled wrapper/`Node` scheduled due to a strong zero2one increment? /// /// If `has_pushed_wrapper` is set, then the strong zero2one increment was scheduled using the /// wrapper. Otherwise, `has_pushed_node` must be set and it was scheduled using the `Node`. has_strong_zero2one: bool, } impl DeliveryState { fn should_normal_push(&self) -> bool { !self.has_pushed_node && !self.has_pushed_wrapper } fn did_normal_push(&mut self) { assert!(self.should_normal_push()); self.has_pushed_node = true; } fn should_push_weak_zero2one(&self) -> bool { !self.has_weak_zero2one && !self.has_strong_zero2one } fn can_push_weak_zero2one_normally(&self) -> bool { !self.has_pushed_node } fn did_push_weak_zero2one(&mut self) { assert!(self.should_push_weak_zero2one()); assert!(self.can_push_weak_zero2one_normally()); self.has_pushed_node = true; self.has_weak_zero2one = true; } fn should_push_strong_zero2one(&self) -> bool { !self.has_strong_zero2one } fn can_push_strong_zero2one_normally(&self) -> bool { !self.has_pushed_node } fn did_push_strong_zero2one(&mut self) { assert!(self.should_push_strong_zero2one()); assert!(self.can_push_strong_zero2one_normally()); self.has_pushed_node = true; self.has_strong_zero2one = true; } fn did_push_strong_zero2one_wrapper(&mut self) { assert!(self.should_push_strong_zero2one()); assert!(!self.can_push_strong_zero2one_normally()); self.has_pushed_wrapper = true; self.has_strong_zero2one = true; } } struct CountState { /// The reference count. count: usize, /// Whether the process that owns this node thinks that we hold a refcount on it. (Note that /// even if count is greater than one, we only increment it once in the owning process.) has_count: bool, } impl CountState { fn new() -> Self { Self { count: 0, has_count: false, } } } struct NodeInner { /// Strong refcounts held on this node by `NodeRef` objects. strong: CountState, /// Weak refcounts held on this node by `NodeRef` objects. weak: CountState, delivery_state: DeliveryState, /// The binder driver guarantees that oneway transactions sent to the same node are serialized, /// that is, userspace will not be given the next one until it has finished processing the /// previous oneway transaction. This is done to avoid the case where two oneway transactions /// arrive in opposite order from the order in which they were sent. (E.g., they could be /// delivered to two different threads, which could appear as-if they were sent in opposite /// order.) /// /// To fix that, we store pending oneway transactions in a separate list in the node, and don't /// deliver the next oneway transaction until userspace signals that it has finished processing /// the previous oneway transaction by calling the `BC_FREE_BUFFER` ioctl. oneway_todo: List>, /// Keeps track of whether this node has a pending oneway transaction. /// /// When this is true, incoming oneway transactions are stored in `oneway_todo`, instead of /// being delivered directly to the process. has_oneway_transaction: bool, /// List of processes to deliver a notification to when this node is destroyed (usually due to /// the process dying). death_list: List, 1>, /// The number of active BR_INCREFS or BR_ACQUIRE operations. (should be maximum two) /// /// If this is non-zero, then we postpone any BR_RELEASE or BR_DECREFS notifications until the /// active operations have ended. This avoids the situation an increment and decrement get /// reordered from userspace's perspective. active_inc_refs: u8, /// List of `NodeRefInfo` objects that reference this node. refs: List, } #[pin_data] pub(crate) struct Node { pub(crate) debug_id: usize, ptr: u64, cookie: u64, pub(crate) flags: u32, pub(crate) owner: Arc, inner: LockedBy, #[pin] links_track: AtomicListArcTracker, } kernel::list::impl_list_arc_safe! { impl ListArcSafe<0> for Node { tracked_by links_track: AtomicListArcTracker; } } // These make `oneway_todo` work. kernel::list::impl_has_list_links! { impl HasListLinks<0> for DTRWrap { self.links.inner } } kernel::list::impl_list_item! { impl ListItem<0> for DTRWrap { using ListLinks; } } impl Node { pub(crate) fn new( ptr: u64, cookie: u64, flags: u32, owner: Arc, ) -> impl PinInit { pin_init!(Self { inner: LockedBy::new( &owner.inner, NodeInner { strong: CountState::new(), weak: CountState::new(), delivery_state: DeliveryState { has_pushed_node: false, has_pushed_wrapper: false, has_weak_zero2one: false, has_strong_zero2one: false, }, death_list: List::new(), oneway_todo: List::new(), has_oneway_transaction: false, active_inc_refs: 0, refs: List::new(), }, ), debug_id: super::next_debug_id(), ptr, cookie, flags, owner, links_track <- AtomicListArcTracker::new(), }) } #[inline(never)] pub(crate) fn full_debug_print( &self, m: &mut SeqFile, owner_inner: &mut ProcessInner, ) -> Result<()> { let prio = self.node_prio(); let inner = self.inner.access_mut(owner_inner); seq_print!( m, " node {}: u{:016x} c{:016x} pri {}:{} hs {} hw {} cs {} cw {}", self.debug_id, self.ptr, self.cookie, prio.sched_policy, prio.prio, inner.strong.has_count, inner.weak.has_count, inner.strong.count, inner.weak.count, ); if !inner.refs.is_empty() { seq_print!(m, " proc"); for node_ref in &inner.refs { seq_print!(m, " {}", node_ref.process.task.pid()); } } seq_print!(m, "\n"); for t in &inner.oneway_todo { t.debug_print_inner(m, " pending async transaction "); } Ok(()) } /// Insert the `NodeRef` into this `refs` list. /// /// # Safety /// /// It must be the case that `info.node_ref.node` is this node. pub(crate) unsafe fn insert_node_info( &self, info: ListArc, ) { self.inner .access_mut(&mut self.owner.inner.lock()) .refs .push_front(info); } /// Insert the `NodeRef` into this `refs` list. /// /// # Safety /// /// It must be the case that `info.node_ref.node` is this node. pub(crate) unsafe fn remove_node_info( &self, info: &NodeRefInfo, ) -> Option> { // SAFETY: We always insert `NodeRefInfo` objects into the `refs` list of the node that it // references in `info.node_ref.node`. That is this node, so `info` cannot possibly be in // the `refs` list of another node. unsafe { self.inner .access_mut(&mut self.owner.inner.lock()) .refs .remove(info) } } pub(crate) fn node_prio(&self) -> prio::BinderPriority { let flags = self.flags; let priority = (flags & FLAT_BINDER_FLAG_PRIORITY_MASK) as prio::Nice; let sched_policy = (flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK) >> FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT; BinderPriority { sched_policy, prio: prio::to_kernel_prio(sched_policy, priority), } } pub(crate) fn inherit_rt(&self) -> bool { (self.flags & FLAT_BINDER_FLAG_INHERIT_RT) != 0 } /// An id that is unique across all binder nodes on the system. Used as the key in the /// `by_node` map. pub(crate) fn global_id(&self) -> usize { self as *const Node as usize } pub(crate) fn get_id(&self) -> (u64, u64) { (self.ptr, self.cookie) } pub(crate) fn next_death( &self, guard: &mut Guard<'_, ProcessInner, SpinLockBackend>, ) -> Option> { self.inner .access_mut(guard) .death_list .pop_front() .map(|larc| larc.into_arc()) } pub(crate) fn add_death( &self, death: ListArc, 1>, guard: &mut Guard<'_, ProcessInner, SpinLockBackend>, ) { self.inner.access_mut(guard).death_list.push_back(death); } pub(crate) fn inc_ref_done_locked( self: &DArc, _strong: bool, owner_inner: &mut ProcessInner, ) -> Option> { let inner = self.inner.access_mut(owner_inner); if inner.active_inc_refs == 0 { pr_err!("inc_ref_done called when no active inc_refs"); return None; } inner.active_inc_refs -= 1; if inner.active_inc_refs == 0 { // Having active inc_refs can inhibit dropping of ref-counts. Calculate whether we // would send a refcount decrement, and if so, tell the caller to schedule us. let strong = inner.strong.count > 0; let has_strong = inner.strong.has_count; let weak = strong || inner.weak.count > 0; let has_weak = inner.weak.has_count; let should_drop_weak = !weak && has_weak; let should_drop_strong = !strong && has_strong; // If we want to drop the ref-count again, tell the caller to schedule a work node for // that. let need_push = should_drop_weak || should_drop_strong; if need_push && inner.delivery_state.should_normal_push() { let list_arc = ListArc::try_from_arc(self.clone()).ok().unwrap(); inner.delivery_state.did_normal_push(); Some(list_arc) } else { None } } else { None } } pub(crate) fn update_refcount_locked( self: &DArc, inc: bool, strong: bool, count: usize, owner_inner: &mut ProcessInner, ) -> Option> { let is_dead = owner_inner.is_dead; let inner = self.inner.access_mut(owner_inner); // Get a reference to the state we'll update. let state = if strong { &mut inner.strong } else { &mut inner.weak }; // Update the count and determine whether we need to push work. let need_push = if inc { state.count += count; // TODO: This method shouldn't be used for zero-to-one increments. !is_dead && !state.has_count } else { if state.count < count { pr_err!("Failure: refcount underflow!"); return None; } state.count -= count; !is_dead && state.count == 0 && state.has_count }; if need_push && inner.delivery_state.should_normal_push() { let list_arc = ListArc::try_from_arc(self.clone()).ok().unwrap(); inner.delivery_state.did_normal_push(); Some(list_arc) } else { None } } pub(crate) fn incr_refcount_allow_zero2one( self: &DArc, strong: bool, owner_inner: &mut ProcessInner, ) -> Result>, CouldNotDeliverCriticalIncrement> { let is_dead = owner_inner.is_dead; let inner = self.inner.access_mut(owner_inner); // Get a reference to the state we'll update. let state = if strong { &mut inner.strong } else { &mut inner.weak }; // Update the count and determine whether we need to push work. state.count += 1; if is_dead || state.has_count { return Ok(None); } // Userspace needs to be notified of this. if !strong && inner.delivery_state.should_push_weak_zero2one() { assert!(inner.delivery_state.can_push_weak_zero2one_normally()); let list_arc = ListArc::try_from_arc(self.clone()).ok().unwrap(); inner.delivery_state.did_push_weak_zero2one(); Ok(Some(list_arc)) } else if strong && inner.delivery_state.should_push_strong_zero2one() { if inner.delivery_state.can_push_strong_zero2one_normally() { let list_arc = ListArc::try_from_arc(self.clone()).ok().unwrap(); inner.delivery_state.did_push_strong_zero2one(); Ok(Some(list_arc)) } else { state.count -= 1; Err(CouldNotDeliverCriticalIncrement) } } else { // Work is already pushed, and we don't need to push again. Ok(None) } } pub(crate) fn incr_refcount_allow_zero2one_with_wrapper( self: &DArc, strong: bool, wrapper: CritIncrWrapper, owner_inner: &mut ProcessInner, ) -> Option> { match self.incr_refcount_allow_zero2one(strong, owner_inner) { Ok(Some(node)) => Some(node as _), Ok(None) => None, Err(CouldNotDeliverCriticalIncrement) => { assert!(strong); let inner = self.inner.access_mut(owner_inner); inner.strong.count += 1; inner.delivery_state.did_push_strong_zero2one_wrapper(); Some(wrapper.init(self.clone())) } } } pub(crate) fn update_refcount(self: &DArc, inc: bool, count: usize, strong: bool) { self.owner .inner .lock() .update_node_refcount(self, inc, strong, count, None); } pub(crate) fn populate_counts( &self, out: &mut BinderNodeInfoForRef, guard: &Guard<'_, ProcessInner, SpinLockBackend>, ) { let inner = self.inner.access(guard); out.strong_count = inner.strong.count as _; out.weak_count = inner.weak.count as _; } pub(crate) fn populate_debug_info( &self, out: &mut BinderNodeDebugInfo, guard: &Guard<'_, ProcessInner, SpinLockBackend>, ) { out.ptr = self.ptr as _; out.cookie = self.cookie as _; let inner = self.inner.access(guard); if inner.strong.has_count { out.has_strong_ref = 1; } if inner.weak.has_count { out.has_weak_ref = 1; } } pub(crate) fn force_has_count(&self, guard: &mut Guard<'_, ProcessInner, SpinLockBackend>) { let inner = self.inner.access_mut(guard); inner.strong.has_count = true; inner.weak.has_count = true; } fn write(&self, writer: &mut UserSliceWriter, code: u32) -> Result { writer.write(&code)?; writer.write(&self.ptr)?; writer.write(&self.cookie)?; Ok(()) } pub(crate) fn submit_oneway( &self, transaction: DLArc, guard: &mut Guard<'_, ProcessInner, SpinLockBackend>, ) -> Result<(), (BinderError, DLArc)> { if guard.is_dead { return Err((BinderError::new_dead(), transaction)); } let inner = self.inner.access_mut(guard); if inner.has_oneway_transaction { inner.oneway_todo.push_back(transaction); } else { inner.has_oneway_transaction = true; guard.push_work(transaction)?; } Ok(()) } pub(crate) fn release(&self, guard: &mut Guard<'_, ProcessInner, SpinLockBackend>) { // Move every pending oneshot message to the process todolist. The process // will cancel it later. // // New items can't be pushed after this call, since `submit_oneway` fails when the process // is dead, which is set before `Node::release` is called. // // TODO: Give our linked list implementation the ability to move everything in one go. while let Some(work) = self.inner.access_mut(guard).oneway_todo.pop_front() { guard.push_work_for_release(work); } } pub(crate) fn pending_oneway_finished(&self) { let mut guard = self.owner.inner.lock(); if guard.is_dead { // Cleanup will happen in `Process::deferred_release`. return; } let inner = self.inner.access_mut(&mut guard); let transaction = inner.oneway_todo.pop_front(); inner.has_oneway_transaction = transaction.is_some(); if let Some(transaction) = transaction { match guard.push_work(transaction) { Ok(()) => {} Err((_err, work)) => { // Process is dead. // This shouldn't happen due to the `is_dead` check, but if it does, just drop // the transaction and return. drop(guard); drop(work); } } } } /// Finds an outdated transaction that the given transaction can replace. /// /// If one is found, it is removed from the list and returned. pub(crate) fn take_outdated_transaction( &self, new: &Transaction, guard: &mut Guard<'_, ProcessInner, SpinLockBackend>, ) -> Option> { let inner = self.inner.access_mut(guard); let mut cursor_opt = inner.oneway_todo.cursor_front(); while let Some(cursor) = cursor_opt { if new.can_replace(&cursor.current()) { return Some(cursor.remove()); } cursor_opt = cursor.next(); } None } /// This is split into a separate function since it's called by both `Node::do_work` and /// `NodeWrapper::do_work`. fn do_work_locked( &self, writer: &mut UserSliceWriter, mut guard: Guard<'_, ProcessInner, SpinLockBackend>, ) -> Result { let inner = self.inner.access_mut(&mut guard); let strong = inner.strong.count > 0; let has_strong = inner.strong.has_count; let weak = strong || inner.weak.count > 0; let has_weak = inner.weak.has_count; if weak && !has_weak { inner.weak.has_count = true; inner.active_inc_refs += 1; } if strong && !has_strong { inner.strong.has_count = true; inner.active_inc_refs += 1; } let no_active_inc_refs = inner.active_inc_refs == 0; let should_drop_weak = no_active_inc_refs && (!weak && has_weak); let should_drop_strong = no_active_inc_refs && (!strong && has_strong); if should_drop_weak { inner.weak.has_count = false; } if should_drop_strong { inner.strong.has_count = false; } if no_active_inc_refs && !weak { // Remove the node if there are no references to it. guard.remove_node(self.ptr); } drop(guard); if weak && !has_weak { self.write(writer, BR_INCREFS)?; } if strong && !has_strong { self.write(writer, BR_ACQUIRE)?; } if should_drop_strong { self.write(writer, BR_RELEASE)?; } if should_drop_weak { self.write(writer, BR_DECREFS)?; } Ok(true) } } impl DeliverToRead for Node { fn do_work(self: DArc, _thread: &Thread, writer: &mut UserSliceWriter) -> Result { let mut owner_inner = self.owner.inner.lock(); let inner = self.inner.access_mut(&mut owner_inner); assert!(inner.delivery_state.has_pushed_node); if inner.delivery_state.has_pushed_wrapper { // If the wrapper is scheduled, then we are either a normal push or weak zero2one // increment, and the wrapper is a strong zero2one increment, so the wrapper always // takes precedence over us. assert!(inner.delivery_state.has_strong_zero2one); inner.delivery_state.has_pushed_node = false; inner.delivery_state.has_weak_zero2one = false; return Ok(true); } inner.delivery_state.has_pushed_node = false; inner.delivery_state.has_weak_zero2one = false; inner.delivery_state.has_strong_zero2one = false; self.do_work_locked(writer, owner_inner) } fn cancel(self: DArc) {} fn on_thread_selected(&self, _thread: &Thread) {} fn should_sync_wakeup(&self) -> bool { false } #[inline(never)] fn debug_print(&self, m: &mut SeqFile, prefix: &str, _tprefix: &str) -> Result<()> { seq_print!( m, "{}node work {}: u{:016x} c{:016x}\n", prefix, self.debug_id, self.ptr, self.cookie, ); Ok(()) } } /// Represents something that holds one or more ref-counts to a `Node`. /// /// Whenever process A holds a refcount to a node owned by a different process B, then process A /// will store a `NodeRef` that refers to the `Node` in process B. When process A releases the /// refcount, we destroy the NodeRef, which decrements the ref-count in process A. /// /// This type is also used for some other cases. For example, a transaction allocation holds a /// refcount on the target node, and this is implemented by storing a `NodeRef` in the allocation /// so that the destructor of the allocation will drop a refcount of the `Node`. pub(crate) struct NodeRef { pub(crate) node: DArc, /// How many times does this NodeRef hold a refcount on the Node? strong_node_count: usize, weak_node_count: usize, /// How many times does userspace hold a refcount on this NodeRef? strong_count: usize, weak_count: usize, } impl NodeRef { pub(crate) fn new(node: DArc, strong_count: usize, weak_count: usize) -> Self { Self { node, strong_node_count: strong_count, weak_node_count: weak_count, strong_count, weak_count, } } pub(crate) fn absorb(&mut self, mut other: Self) { assert!( Arc::ptr_eq(&self.node, &other.node), "absorb called with differing nodes" ); self.strong_node_count += other.strong_node_count; self.weak_node_count += other.weak_node_count; self.strong_count += other.strong_count; self.weak_count += other.weak_count; other.strong_count = 0; other.weak_count = 0; other.strong_node_count = 0; other.weak_node_count = 0; } pub(crate) fn get_count(&self) -> (usize, usize) { (self.strong_count, self.weak_count) } pub(crate) fn clone(&self, strong: bool) -> Result { if strong && self.strong_count == 0 { return Err(EINVAL); } Ok(self .node .owner .inner .lock() .new_node_ref(self.node.clone(), strong, None)) } /// Updates (increments or decrements) the number of references held against the node. If the /// count being updated transitions from 0 to 1 or from 1 to 0, the node is notified by having /// its `update_refcount` function called. /// /// Returns whether `self` should be removed (when both counts are zero). pub(crate) fn update(&mut self, inc: bool, strong: bool) -> bool { if strong && self.strong_count == 0 { return false; } let (count, node_count, other_count) = if strong { ( &mut self.strong_count, &mut self.strong_node_count, self.weak_count, ) } else { ( &mut self.weak_count, &mut self.weak_node_count, self.strong_count, ) }; if inc { if *count == 0 { *node_count = 1; self.node.update_refcount(true, 1, strong); } *count += 1; } else { *count -= 1; if *count == 0 { self.node.update_refcount(false, *node_count, strong); *node_count = 0; return other_count == 0; } } false } } impl Drop for NodeRef { // This destructor is called conditionally from `Allocation::drop`. That branch is often // mispredicted. Inlining this method call reduces the cost of those branch mispredictions. #[inline(always)] fn drop(&mut self) { if self.strong_node_count > 0 { self.node .update_refcount(false, self.strong_node_count, true); } if self.weak_node_count > 0 { self.node .update_refcount(false, self.weak_node_count, false); } } } struct NodeDeathInner { dead: bool, cleared: bool, notification_done: bool, /// Indicates whether the normal flow was interrupted by removing the handle. In this case, we /// need behave as if the death notification didn't exist (i.e., we don't deliver anything to /// the user. aborted: bool, } /// Used to deliver notifications when a process dies. /// /// A process can request to be notified when a process dies using `BC_REQUEST_DEATH_NOTIFICATION`. /// This will make the driver send a `BR_DEAD_BINDER` to userspace when the process dies (or /// immediately if it is already dead). Userspace is supposed to respond with `BC_DEAD_BINDER_DONE` /// once it has processed the notification. /// /// Userspace can unregister from death notifications using the `BC_CLEAR_DEATH_NOTIFICATION` /// command. In this case, the kernel will respond with `BR_CLEAR_DEATH_NOTIFICATION_DONE` once the /// notification has been removed. Note that if the remote process dies before the kernel has /// responded with `BR_CLEAR_DEATH_NOTIFICATION_DONE`, then the kernel will still send a /// `BR_DEAD_BINDER`, which userspace must be able to process. In this case, the kernel will wait /// for the `BC_DEAD_BINDER_DONE` command before it sends `BR_CLEAR_DEATH_NOTIFICATION_DONE`. /// /// Note that even if the kernel sends a `BR_DEAD_BINDER`, this does not remove the death /// notification. Userspace must still remove it manually using `BC_CLEAR_DEATH_NOTIFICATION`. /// /// If a process uses `BC_RELEASE` to destroy its last refcount on a node that has an active death /// registration, then the death registration is immediately deleted (we implement this using the /// `aborted` field). However, userspace is not supposed to delete a `NodeRef` without first /// deregistering death notifications, so this codepath is not executed under normal circumstances. #[pin_data] pub(crate) struct NodeDeath { node: DArc, process: Arc, pub(crate) cookie: usize, #[pin] links_track: AtomicListArcTracker<0>, /// Used by the owner `Node` to store a list of registered death notifications. /// /// # Invariants /// /// Only ever used with the `death_list` list of `self.node`. #[pin] death_links: ListLinks<1>, /// Used by the process to keep track of the death notifications for which we have sent a /// `BR_DEAD_BINDER` but not yet received a `BC_DEAD_BINDER_DONE`. /// /// # Invariants /// /// Only ever used with the `delivered_deaths` list of `self.process`. #[pin] delivered_links: ListLinks<2>, #[pin] delivered_links_track: AtomicListArcTracker<2>, #[pin] inner: SpinLock, } impl NodeDeath { /// Constructs a new node death notification object. pub(crate) fn new( node: DArc, process: Arc, cookie: usize, ) -> impl PinInit> { DTRWrap::new(pin_init!( Self { node, process, cookie, links_track <- AtomicListArcTracker::new(), death_links <- ListLinks::new(), delivered_links <- ListLinks::new(), delivered_links_track <- AtomicListArcTracker::new(), inner <- kernel::new_spinlock!(NodeDeathInner { dead: false, cleared: false, notification_done: false, aborted: false, }, "NodeDeath::inner"), } )) } /// Sets the cleared flag to `true`. /// /// It removes `self` from the node's death notification list if needed. /// /// Returns whether it needs to be queued. pub(crate) fn set_cleared(self: &DArc, abort: bool) -> bool { let (needs_removal, needs_queueing) = { // Update state and determine if we need to queue a work item. We only need to do it // when the node is not dead or if the user already completed the death notification. let mut inner = self.inner.lock(); if abort { inner.aborted = true; } if inner.cleared { // Already cleared. return false; } inner.cleared = true; (!inner.dead, !inner.dead || inner.notification_done) }; // Remove death notification from node. if needs_removal { let mut owner_inner = self.node.owner.inner.lock(); let node_inner = self.node.inner.access_mut(&mut owner_inner); // SAFETY: A `NodeDeath` is never inserted into the death list of any node other than // its owner, so it is either in this death list or in no death list. unsafe { node_inner.death_list.remove(self) }; } needs_queueing } /// Sets the 'notification done' flag to `true`. pub(crate) fn set_notification_done(self: DArc, thread: &Thread) { let needs_queueing = { let mut inner = self.inner.lock(); inner.notification_done = true; inner.cleared }; if needs_queueing { if let Some(death) = ListArc::try_from_arc_or_drop(self) { let _ = thread.push_work_if_looper(death); } } } /// Sets the 'dead' flag to `true` and queues work item if needed. pub(crate) fn set_dead(self: DArc) { let needs_queueing = { let mut inner = self.inner.lock(); if inner.cleared { false } else { inner.dead = true; true } }; if needs_queueing { // Push the death notification to the target process. There is nothing else to do if // it's already dead. if let Some(death) = ListArc::try_from_arc_or_drop(self) { let process = death.process.clone(); let _ = process.push_work(death); } } } } kernel::list::impl_list_arc_safe! { impl ListArcSafe<0> for NodeDeath { tracked_by links_track: AtomicListArcTracker; } } kernel::list::impl_has_list_links! { impl HasListLinks<1> for DTRWrap { self.wrapped.death_links } } kernel::list::impl_list_arc_safe! { impl ListArcSafe<1> for DTRWrap { untracked; } } kernel::list::impl_list_item! { impl ListItem<1> for DTRWrap { using ListLinks; } } kernel::list::impl_has_list_links! { impl HasListLinks<2> for DTRWrap { self.wrapped.delivered_links } } kernel::list::impl_list_arc_safe! { impl ListArcSafe<2> for DTRWrap { tracked_by wrapped: NodeDeath; } } kernel::list::impl_list_arc_safe! { impl ListArcSafe<2> for NodeDeath { tracked_by delivered_links_track: AtomicListArcTracker<2>; } } kernel::list::impl_list_item! { impl ListItem<2> for DTRWrap { using ListLinks; } } impl DeliverToRead for NodeDeath { fn do_work(self: DArc, _thread: &Thread, writer: &mut UserSliceWriter) -> Result { let done = { let inner = self.inner.lock(); if inner.aborted { return Ok(true); } inner.cleared && (!inner.dead || inner.notification_done) }; let cookie = self.cookie; let cmd = if done { BR_CLEAR_DEATH_NOTIFICATION_DONE } else { let process = self.process.clone(); let mut process_inner = process.inner.lock(); let inner = self.inner.lock(); if inner.aborted { return Ok(true); } // We're still holding the inner lock, so it cannot be aborted while we insert it into // the delivered list. process_inner.death_delivered(self.clone()); BR_DEAD_BINDER }; writer.write(&cmd)?; writer.write(&cookie)?; // DEAD_BINDER notifications can cause transactions, so stop processing work items when we // get to a death notification. Ok(cmd != BR_DEAD_BINDER) } fn cancel(self: DArc) {} fn on_thread_selected(&self, _thread: &Thread) {} fn should_sync_wakeup(&self) -> bool { false } #[inline(never)] fn debug_print(&self, m: &mut SeqFile, prefix: &str, _tprefix: &str) -> Result<()> { let inner = self.inner.lock(); let dead_binder = inner.dead && !inner.notification_done; if dead_binder { if inner.cleared { seq_print!(m, "{}has cleared dead binder\n", prefix); } else { seq_print!(m, "{}has dead binder\n", prefix); } } else { seq_print!(m, "{}has cleared death notification\n", prefix); } Ok(()) } }