// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2025 Google LLC. //! This module defines the `Thread` type, which represents a userspace thread that is using//! binder.//!//! The `Process` object stores all of the threads in an rb tree. use kernel::{ bindings, fs::{File, LocalFile}, list::{AtomicTracker, List, ListArc, ListLinks, TryNewListArc}, prelude::*, security, seq_file::SeqFile, seq_print, sync::atomic::{ordering::Relaxed, Atomic}, sync::poll::{PollCondVar, PollTable}, sync::{aref::ARef, Arc, SpinLock}, task::Task, uaccess::UserSlice, uapi,}; use crate::{ allocation::{Allocation, AllocationView, BinderObject, BinderObjectRef, NewAllocation}, defs::*, error::BinderResult, process::{GetWorkOrRegister, Process}, ptr_align, stats::GLOBAL_STATS, transaction::Transaction, BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead,}; use core::mem::size_of; fn is_aligned(value: usize, to: usize) -> bool { value % to == 0} /// Stores the layout of the scatter-gather entries. This is used during the `translate_objects`/// call and is discarded when it returns.struct ScatterGatherState { /// A struct that tracks the amount of unused buffer space. unused_buffer_space: UnusedBufferSpace, /// Scatter-gather entries to copy. sg_entries: KVec<ScatterGatherEntry>, /// Indexes into `sg_entries` corresponding to the last binder_buffer_object that /// was processed and all of its ancestors. The array is in sorted order. ancestors: KVec<usize>,} /// This entry specifies an additional buffer that should be copied using the scatter-gather/// mechanism.struct ScatterGatherEntry { /// The index in the offset array of the BINDER_TYPE_PTR that this entry originates from. obj_index: usize, /// Offset in target buffer. offset: usize, /// User address in source buffer. sender_uaddr: usize, /// Number of bytes to copy. length: usize, /// The minimum offset of the next fixup in this buffer. fixup_min_offset: usize, /// The offsets within this buffer that contain pointers which should be translated. pointer_fixups: KVec<PointerFixupEntry>,} /// This entry specifies that a fixup should happen at `target_offset` of the/// buffer.enum PointerFixupEntry { /// A fixup for a `binder_buffer_object`. Fixup { /// The translated pointer to write. pointer_value: u64, /// The offset at which the value should be written. The offset is relative /// to the original buffer. target_offset: usize, }, /// A skip for a `binder_fd_array_object`. Skip { /// The number of bytes to skip. skip: usize, /// The offset at which the skip should happen. The offset is relative /// to the original buffer. target_offset: usize, },} /// Return type of `apply_and_validate_fixup_in_parent`.struct ParentFixupInfo { /// The index of the parent buffer in `sg_entries`. parent_sg_index: usize, /// The number of ancestors of the buffer. /// /// The buffer is considered an ancestor of itself, so this is always at /// least one. num_ancestors: usize, /// New value of `fixup_min_offset` if this fixup is applied. new_min_offset: usize, /// The offset of the fixup in the target buffer. target_offset: usize,} impl ScatterGatherState { /// Called when a `binder_buffer_object` or `binder_fd_array_object` tries /// to access a region in its parent buffer. These accesses have various /// restrictions, which this method verifies. /// /// The `parent_offset` and `length` arguments describe the offset and /// length of the access in the parent buffer. /// /// # Detailed restrictions /// /// Obviously the fixup must be in-bounds for the parent buffer. /// /// For safety reasons, we only allow fixups inside a buffer to happen /// at increasing offsets; additionally, we only allow fixup on the last /// buffer object that was verified, or one of its parents. /// /// Example of what is allowed: /// /// A /// B (parent = A, offset = 0) /// C (parent = A, offset = 16) /// D (parent = C, offset = 0) /// E (parent = A, offset = 32) // min_offset is 16 (C.parent_offset) /// /// Examples of what is not allowed: /// /// Decreasing offsets within the same parent: /// A /// C (parent = A, offset = 16) /// B (parent = A, offset = 0) // decreasing offset within A /// /// Arcerring to a parent that wasn't the last object or any of its parents: /// A /// B (parent = A, offset = 0) /// C (parent = A, offset = 0) /// C (parent = A, offset = 16) /// D (parent = B, offset = 0) // B is not A or any of A's parents fn validate_parent_fixup( &self, parent: usize, parent_offset: usize, length: usize, ) -> Result<ParentFixupInfo> { // Using `position` would also be correct, but `rposition` avoids // quadratic running times. let ancestors_i = self .ancestors .iter() .copied() .rposition(|sg_idx| self.sg_entries[sg_idx].obj_index == parent) .ok_or(EINVAL)?; let sg_idx = self.ancestors[ancestors_i]; let sg_entry = match self.sg_entries.get(sg_idx) { Some(sg_entry) => sg_entry, None => { pr_err!( "self.ancestors[{}] is {}, but self.sg_entries.len() is {}", ancestors_i, sg_idx, self.sg_entries.len() ); return Err(EINVAL); } }; if sg_entry.fixup_min_offset > parent_offset { pr_warn!( "validate_parent_fixup: fixup_min_offset={}, parent_offset={}", sg_entry.fixup_min_offset, parent_offset ); return Err(EINVAL); } let new_min_offset = parent_offset.checked_add(length).ok_or(EINVAL)?; if new_min_offset > sg_entry.length { pr_warn!( "validate_parent_fixup: new_min_offset={}, sg_entry.length={}", new_min_offset, sg_entry.length ); return Err(EINVAL); } let target_offset = sg_entry.offset.checked_add(parent_offset).ok_or(EINVAL)?; // The `ancestors_i + 1` operation can't overflow since the output of the addition is at // most `self.ancestors.len()`, which also fits in a usize. Ok(ParentFixupInfo { parent_sg_index: sg_idx, num_ancestors: ancestors_i + 1, new_min_offset, target_offset, }) }} /// Keeps track of how much unused buffer space is left. The initial amount is the number of bytes/// requested by the user using the `buffers_size` field of `binder_transaction_data_sg`. Each time/// we translate an object of type `BINDER_TYPE_PTR`, some of the unused buffer space is consumed.struct UnusedBufferSpace { /// The start of the remaining space. offset: usize, /// The end of the remaining space. limit: usize,}impl UnusedBufferSpace { /// Claim the next `size` bytes from the unused buffer space. The offset for the claimed chunk /// into the buffer is returned. fn claim_next(&mut self, size: usize) -> Result<usize> { // We require every chunk to be aligned. let size = ptr_align(size).ok_or(EINVAL)?; let new_offset = self.offset.checked_add(size).ok_or(EINVAL)?; if new_offset <= self.limit { let offset = self.offset; self.offset = new_offset; Ok(offset) } else { Err(EINVAL) } }} pub(crate) enum PushWorkRes { Ok, FailedDead(DLArc<dyn DeliverToRead>),} impl PushWorkRes { fn is_ok(&self) -> bool { match self { PushWorkRes::Ok => true, PushWorkRes::FailedDead(_) => false, } }} /// The fields of `Thread` protected by the spinlock.struct InnerThread { /// Determines the looper state of the thread. It is a bit-wise combination of the constants /// prefixed with `LOOPER_`. looper_flags: u32, /// Determines whether the looper should return. looper_need_return: bool, /// Determines if thread is dead. is_dead: bool, /// Work item used to deliver error codes to the thread that started a transaction. Stored here /// so that it can be reused. reply_work: DArc<ThreadError>, /// Work item used to deliver error codes to the current thread. Stored here so that it can be /// reused. return_work: DArc<ThreadError>, /// Determines whether the work list below should be processed. When set to false, `work_list` /// is treated as if it were empty. process_work_list: bool, /// List of work items to deliver to userspace. work_list: List<DTRWrap<dyn DeliverToRead>>, current_transaction: Option<DArc<Transaction>>, /// Extended error information for this thread. extended_error: ExtendedError,} const LOOPER_REGISTERED: u32 = 0x01;const LOOPER_ENTERED: u32 = 0x02;const LOOPER_EXITED: u32 = 0x04;const LOOPER_INVALID: u32 = 0x08;const LOOPER_WAITING: u32 = 0x10;const LOOPER_WAITING_PROC: u32 = 0x20;const LOOPER_POLL: u32 = 0x40; impl InnerThread { fn new() -> Result<Self> { fn next_err_id() -> u32 { static EE_ID: Atomic<u32> = Atomic::new(0); EE_ID.fetch_add(1, Relaxed) } Ok(Self { looper_flags: 0, looper_need_return: false, is_dead: false, process_work_list: false, reply_work: ThreadError::try_new()?, return_work: ThreadError::try_new()?, work_list: List::new(), current_transaction: None, extended_error: ExtendedError::new(next_err_id(), BR_OK, 0), }) } fn pop_work(&mut self) -> Option<DLArc<dyn DeliverToRead>> { if !self.process_work_list { return None; } let ret = self.work_list.pop_front(); self.process_work_list = !self.work_list.is_empty(); ret } fn push_work(&mut self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes { if self.is_dead { PushWorkRes::FailedDead(work) } else { self.work_list.push_back(work); self.process_work_list = true; PushWorkRes::Ok } } fn push_reply_work(&mut self, code: u32) { if let Ok(work) = ListArc::try_from_arc(self.reply_work.clone()) { work.set_error_code(code); self.push_work(work); } else { pr_warn!("Thread reply work is already in use."); } } fn push_return_work(&mut self, reply: u32) { if let Ok(work) = ListArc::try_from_arc(self.return_work.clone()) { work.set_error_code(reply); self.push_work(work); } else { pr_warn!("Thread return work is already in use."); } } /// Used to push work items that do not need to be processed immediately and can wait until the /// thread gets another work item. fn push_work_deferred(&mut self, work: DLArc<dyn DeliverToRead>) { self.work_list.push_back(work); } /// Fetches the transaction this thread can reply to. If the thread has a pending transaction /// (that it could respond to) but it has also issued a transaction, it must first wait for the /// previously-issued transaction to complete. /// /// The `thread` parameter should be the thread containing this `ThreadInner`. fn pop_transaction_to_reply(&mut self, thread: &Thread) -> Result<DArc<Transaction>> { let transaction = self.current_transaction.take().ok_or(EINVAL)?; if core::ptr::eq(thread, transaction.from.as_ref()) { self.current_transaction = Some(transaction); return Err(EINVAL); } // Find a new current transaction for this thread. self.current_transaction = transaction.find_from(thread).cloned(); Ok(transaction) } fn pop_transaction_replied(&mut self, transaction: &DArc<Transaction>) -> bool { match self.current_transaction.take() { None => false, Some(old) => { if !Arc::ptr_eq(transaction, &old) { self.current_transaction = Some(old); return false; } self.current_transaction = old.clone_next(); true } } } fn looper_enter(&mut self) { self.looper_flags |= LOOPER_ENTERED; if self.looper_flags & LOOPER_REGISTERED != 0 { self.looper_flags |= LOOPER_INVALID; } } fn looper_register(&mut self, valid: bool) { self.looper_flags |= LOOPER_REGISTERED; if !valid || self.looper_flags & LOOPER_ENTERED != 0 { self.looper_flags |= LOOPER_INVALID; } } fn looper_exit(&mut self) { self.looper_flags |= LOOPER_EXITED; } /// Determines whether the thread is part of a pool, i.e., if it is a looper. fn is_looper(&self) -> bool { self.looper_flags & (LOOPER_ENTERED | LOOPER_REGISTERED) != 0 } /// Determines whether the thread should attempt to fetch work items from the process queue. /// This is generally case when the thread is registered as a looper and not part of a /// transaction stack. But if there is local work, we want to return to userspace before we /// deliver any remote work. fn should_use_process_work_queue(&self) -> bool { self.current_transaction.is_none() && !self.process_work_list && self.is_looper() } fn poll(&mut self) -> u32 { self.looper_flags |= LOOPER_POLL; if self.process_work_list || self.looper_need_return { bindings::POLLIN } else { 0 } }} /// This represents a thread that's used with binder.#[pin_data]pub(crate) struct Thread { pub(crate) id: i32, pub(crate) process: Arc<Process>, pub(crate) task: ARef<Task>, #[pin] inner: SpinLock<InnerThread>, #[pin] work_condvar: PollCondVar, /// Used to insert this thread into the process' `ready_threads` list. /// /// INVARIANT: May never be used for any other list than the `self.process.ready_threads`. #[pin] links: ListLinks, #[pin] links_track: AtomicTracker,} kernel::list::impl_list_arc_safe! { impl ListArcSafe<0> for Thread { tracked_by links_track: AtomicTracker; }}kernel::list::impl_list_item! { impl ListItem<0> for Thread { using ListLinks { self.links }; }} impl Thread { pub(crate) fn new(id: i32, process: Arc<Process>) -> Result<Arc<Self>> { let inner = InnerThread::new()?; Arc::pin_init( try_pin_init!(Thread { id, process, task: ARef::from(&**kernel::current!()), inner <- kernel::new_spinlock!(inner, "Thread::inner"), work_condvar <- kernel::new_poll_condvar!("Thread::work_condvar"), links <- ListLinks::new(), links_track <- AtomicTracker::new(), }), GFP_KERNEL, ) } #[inline(never)] pub(crate) fn debug_print(self: &Arc<Self>, m: &SeqFile, print_all: bool) -> Result<()> { let inner = self.inner.lock(); if print_all || inner.current_transaction.is_some() || !inner.work_list.is_empty() { seq_print!( m, " thread {}: l {:02x} need_return {}\n", self.id, inner.looper_flags, inner.looper_need_return, ); } let mut t_opt = inner.current_transaction.as_ref(); while let Some(t) = t_opt { if Arc::ptr_eq(&t.from, self) { t.debug_print_inner(m, " outgoing transaction "); t_opt = t.from_parent.as_ref(); } else if Arc::ptr_eq(&t.to, &self.process) { t.debug_print_inner(m, " incoming transaction "); t_opt = t.find_from(self); } else { t.debug_print_inner(m, " bad transaction "); t_opt = None; } } for work in &inner.work_list { work.debug_print(m, " ", " pending transaction ")?; } Ok(()) } pub(crate) fn get_extended_error(&self, data: UserSlice) -> Result { let mut writer = data.writer(); let ee = self.inner.lock().extended_error;Lines 1–500 of 1620. Load more
// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2025 Google LLC. //! This module defines the `Thread` type, which represents a userspace thread that is using //! binder. //! //! The `Process` object stores all of the threads in an rb tree. use kernel::{ bindings, fs::{File, LocalFile}, list::{AtomicTracker, List, ListArc, ListLinks, TryNewListArc}, prelude::*, security, seq_file::SeqFile, seq_print, sync::atomic::{ordering::Relaxed, Atomic}, sync::poll::{PollCondVar, PollTable}, sync::{aref::ARef, Arc, SpinLock}, task::Task, uaccess::UserSlice, uapi, }; use crate::{ allocation::{Allocation, AllocationView, BinderObject, BinderObjectRef, NewAllocation}, defs::*, error::BinderResult, process::{GetWorkOrRegister, Process}, ptr_align, stats::GLOBAL_STATS, transaction::Transaction, BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead, }; use core::mem::size_of; fn is_aligned(value: usize, to: usize) -> bool { value % to == 0 } /// Stores the layout of the scatter-gather entries. This is used during the `translate_objects` /// call and is discarded when it returns. struct ScatterGatherState { /// A struct that tracks the amount of unused buffer space. unused_buffer_space: UnusedBufferSpace, /// Scatter-gather entries to copy. sg_entries: KVec<ScatterGatherEntry>, /// Indexes into `sg_entries` corresponding to the last binder_buffer_object that /// was processed and all of its ancestors. The array is in sorted order. ancestors: KVec<usize>, } /// This entry specifies an additional buffer that should be copied using the scatter-gather /// mechanism. struct ScatterGatherEntry { /// The index in the offset array of the BINDER_TYPE_PTR that this entry originates from. obj_index: usize, /// Offset in target buffer. offset: usize, /// User address in source buffer. sender_uaddr: usize, /// Number of bytes to copy. length: usize, /// The minimum offset of the next fixup in this buffer. fixup_min_offset: usize, /// The offsets within this buffer that contain pointers which should be translated. pointer_fixups: KVec<PointerFixupEntry>, } /// This entry specifies that a fixup should happen at `target_offset` of the /// buffer. enum PointerFixupEntry { /// A fixup for a `binder_buffer_object`. Fixup { /// The translated pointer to write. pointer_value: u64, /// The offset at which the value should be written. The offset is relative /// to the original buffer. target_offset: usize, }, /// A skip for a `binder_fd_array_object`. Skip { /// The number of bytes to skip. skip: usize, /// The offset at which the skip should happen. The offset is relative /// to the original buffer. target_offset: usize, }, } /// Return type of `apply_and_validate_fixup_in_parent`. struct ParentFixupInfo { /// The index of the parent buffer in `sg_entries`. parent_sg_index: usize, /// The number of ancestors of the buffer. /// /// The buffer is considered an ancestor of itself, so this is always at /// least one. num_ancestors: usize, /// New value of `fixup_min_offset` if this fixup is applied. new_min_offset: usize, /// The offset of the fixup in the target buffer. target_offset: usize, } impl ScatterGatherState { /// Called when a `binder_buffer_object` or `binder_fd_array_object` tries /// to access a region in its parent buffer. These accesses have various /// restrictions, which this method verifies. /// /// The `parent_offset` and `length` arguments describe the offset and /// length of the access in the parent buffer. /// /// # Detailed restrictions /// /// Obviously the fixup must be in-bounds for the parent buffer. /// /// For safety reasons, we only allow fixups inside a buffer to happen /// at increasing offsets; additionally, we only allow fixup on the last /// buffer object that was verified, or one of its parents. /// /// Example of what is allowed: /// /// A /// B (parent = A, offset = 0) /// C (parent = A, offset = 16) /// D (parent = C, offset = 0) /// E (parent = A, offset = 32) // min_offset is 16 (C.parent_offset) /// /// Examples of what is not allowed: /// /// Decreasing offsets within the same parent: /// A /// C (parent = A, offset = 16) /// B (parent = A, offset = 0) // decreasing offset within A /// /// Arcerring to a parent that wasn't the last object or any of its parents: /// A /// B (parent = A, offset = 0) /// C (parent = A, offset = 0) /// C (parent = A, offset = 16) /// D (parent = B, offset = 0) // B is not A or any of A's parents fn validate_parent_fixup( &self, parent: usize, parent_offset: usize, length: usize, ) -> Result<ParentFixupInfo> { // Using `position` would also be correct, but `rposition` avoids // quadratic running times. let ancestors_i = self .ancestors .iter() .copied() .rposition(|sg_idx| self.sg_entries[sg_idx].obj_index == parent) .ok_or(EINVAL)?; let sg_idx = self.ancestors[ancestors_i]; let sg_entry = match self.sg_entries.get(sg_idx) { Some(sg_entry) => sg_entry, None => { pr_err!( "self.ancestors[{}] is {}, but self.sg_entries.len() is {}", ancestors_i, sg_idx, self.sg_entries.len() ); return Err(EINVAL); } }; if sg_entry.fixup_min_offset > parent_offset { pr_warn!( "validate_parent_fixup: fixup_min_offset={}, parent_offset={}", sg_entry.fixup_min_offset, parent_offset ); return Err(EINVAL); } let new_min_offset = parent_offset.checked_add(length).ok_or(EINVAL)?; if new_min_offset > sg_entry.length { pr_warn!( "validate_parent_fixup: new_min_offset={}, sg_entry.length={}", new_min_offset, sg_entry.length ); return Err(EINVAL); } let target_offset = sg_entry.offset.checked_add(parent_offset).ok_or(EINVAL)?; // The `ancestors_i + 1` operation can't overflow since the output of the addition is at // most `self.ancestors.len()`, which also fits in a usize. Ok(ParentFixupInfo { parent_sg_index: sg_idx, num_ancestors: ancestors_i + 1, new_min_offset, target_offset, }) } } /// Keeps track of how much unused buffer space is left. The initial amount is the number of bytes /// requested by the user using the `buffers_size` field of `binder_transaction_data_sg`. Each time /// we translate an object of type `BINDER_TYPE_PTR`, some of the unused buffer space is consumed. struct UnusedBufferSpace { /// The start of the remaining space. offset: usize, /// The end of the remaining space. limit: usize, } impl UnusedBufferSpace { /// Claim the next `size` bytes from the unused buffer space. The offset for the claimed chunk /// into the buffer is returned. fn claim_next(&mut self, size: usize) -> Result<usize> { // We require every chunk to be aligned. let size = ptr_align(size).ok_or(EINVAL)?; let new_offset = self.offset.checked_add(size).ok_or(EINVAL)?; if new_offset <= self.limit { let offset = self.offset; self.offset = new_offset; Ok(offset) } else { Err(EINVAL) } } } pub(crate) enum PushWorkRes { Ok, FailedDead(DLArc<dyn DeliverToRead>), } impl PushWorkRes { fn is_ok(&self) -> bool { match self { PushWorkRes::Ok => true, PushWorkRes::FailedDead(_) => false, } } } /// The fields of `Thread` protected by the spinlock. struct InnerThread { /// Determines the looper state of the thread. It is a bit-wise combination of the constants /// prefixed with `LOOPER_`. looper_flags: u32, /// Determines whether the looper should return. looper_need_return: bool, /// Determines if thread is dead. is_dead: bool, /// Work item used to deliver error codes to the thread that started a transaction. Stored here /// so that it can be reused. reply_work: DArc<ThreadError>, /// Work item used to deliver error codes to the current thread. Stored here so that it can be /// reused. return_work: DArc<ThreadError>, /// Determines whether the work list below should be processed. When set to false, `work_list` /// is treated as if it were empty. process_work_list: bool, /// List of work items to deliver to userspace. work_list: List<DTRWrap<dyn DeliverToRead>>, current_transaction: Option<DArc<Transaction>>, /// Extended error information for this thread. extended_error: ExtendedError, } const LOOPER_REGISTERED: u32 = 0x01; const LOOPER_ENTERED: u32 = 0x02; const LOOPER_EXITED: u32 = 0x04; const LOOPER_INVALID: u32 = 0x08; const LOOPER_WAITING: u32 = 0x10; const LOOPER_WAITING_PROC: u32 = 0x20; const LOOPER_POLL: u32 = 0x40; impl InnerThread { fn new() -> Result<Self> { fn next_err_id() -> u32 { static EE_ID: Atomic<u32> = Atomic::new(0); EE_ID.fetch_add(1, Relaxed) } Ok(Self { looper_flags: 0, looper_need_return: false, is_dead: false, process_work_list: false, reply_work: ThreadError::try_new()?, return_work: ThreadError::try_new()?, work_list: List::new(), current_transaction: None, extended_error: ExtendedError::new(next_err_id(), BR_OK, 0), }) } fn pop_work(&mut self) -> Option<DLArc<dyn DeliverToRead>> { if !self.process_work_list { return None; } let ret = self.work_list.pop_front(); self.process_work_list = !self.work_list.is_empty(); ret } fn push_work(&mut self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes { if self.is_dead { PushWorkRes::FailedDead(work) } else { self.work_list.push_back(work); self.process_work_list = true; PushWorkRes::Ok } } fn push_reply_work(&mut self, code: u32) { if let Ok(work) = ListArc::try_from_arc(self.reply_work.clone()) { work.set_error_code(code); self.push_work(work); } else { pr_warn!("Thread reply work is already in use."); } } fn push_return_work(&mut self, reply: u32) { if let Ok(work) = ListArc::try_from_arc(self.return_work.clone()) { work.set_error_code(reply); self.push_work(work); } else { pr_warn!("Thread return work is already in use."); } } /// Used to push work items that do not need to be processed immediately and can wait until the /// thread gets another work item. fn push_work_deferred(&mut self, work: DLArc<dyn DeliverToRead>) { self.work_list.push_back(work); } /// Fetches the transaction this thread can reply to. If the thread has a pending transaction /// (that it could respond to) but it has also issued a transaction, it must first wait for the /// previously-issued transaction to complete. /// /// The `thread` parameter should be the thread containing this `ThreadInner`. fn pop_transaction_to_reply(&mut self, thread: &Thread) -> Result<DArc<Transaction>> { let transaction = self.current_transaction.take().ok_or(EINVAL)?; if core::ptr::eq(thread, transaction.from.as_ref()) { self.current_transaction = Some(transaction); return Err(EINVAL); } // Find a new current transaction for this thread. self.current_transaction = transaction.find_from(thread).cloned(); Ok(transaction) } fn pop_transaction_replied(&mut self, transaction: &DArc<Transaction>) -> bool { match self.current_transaction.take() { None => false, Some(old) => { if !Arc::ptr_eq(transaction, &old) { self.current_transaction = Some(old); return false; } self.current_transaction = old.clone_next(); true } } } fn looper_enter(&mut self) { self.looper_flags |= LOOPER_ENTERED; if self.looper_flags & LOOPER_REGISTERED != 0 { self.looper_flags |= LOOPER_INVALID; } } fn looper_register(&mut self, valid: bool) { self.looper_flags |= LOOPER_REGISTERED; if !valid || self.looper_flags & LOOPER_ENTERED != 0 { self.looper_flags |= LOOPER_INVALID; } } fn looper_exit(&mut self) { self.looper_flags |= LOOPER_EXITED; } /// Determines whether the thread is part of a pool, i.e., if it is a looper. fn is_looper(&self) -> bool { self.looper_flags & (LOOPER_ENTERED | LOOPER_REGISTERED) != 0 } /// Determines whether the thread should attempt to fetch work items from the process queue. /// This is generally case when the thread is registered as a looper and not part of a /// transaction stack. But if there is local work, we want to return to userspace before we /// deliver any remote work. fn should_use_process_work_queue(&self) -> bool { self.current_transaction.is_none() && !self.process_work_list && self.is_looper() } fn poll(&mut self) -> u32 { self.looper_flags |= LOOPER_POLL; if self.process_work_list || self.looper_need_return { bindings::POLLIN } else { 0 } } } /// This represents a thread that's used with binder. #[pin_data] pub(crate) struct Thread { pub(crate) id: i32, pub(crate) process: Arc<Process>, pub(crate) task: ARef<Task>, #[pin] inner: SpinLock<InnerThread>, #[pin] work_condvar: PollCondVar, /// Used to insert this thread into the process' `ready_threads` list. /// /// INVARIANT: May never be used for any other list than the `self.process.ready_threads`. #[pin] links: ListLinks, #[pin] links_track: AtomicTracker, } kernel::list::impl_list_arc_safe! { impl ListArcSafe<0> for Thread { tracked_by links_track: AtomicTracker; } } kernel::list::impl_list_item! { impl ListItem<0> for Thread { using ListLinks { self.links }; } } impl Thread { pub(crate) fn new(id: i32, process: Arc<Process>) -> Result<Arc<Self>> { let inner = InnerThread::new()?; Arc::pin_init( try_pin_init!(Thread { id, process, task: ARef::from(&**kernel::current!()), inner <- kernel::new_spinlock!(inner, "Thread::inner"), work_condvar <- kernel::new_poll_condvar!("Thread::work_condvar"), links <- ListLinks::new(), links_track <- AtomicTracker::new(), }), GFP_KERNEL, ) } #[inline(never)] pub(crate) fn debug_print(self: &Arc<Self>, m: &SeqFile, print_all: bool) -> Result<()> { let inner = self.inner.lock(); if print_all || inner.current_transaction.is_some() || !inner.work_list.is_empty() { seq_print!( m, " thread {}: l {:02x} need_return {}\n", self.id, inner.looper_flags, inner.looper_need_return, ); } let mut t_opt = inner.current_transaction.as_ref(); while let Some(t) = t_opt { if Arc::ptr_eq(&t.from, self) { t.debug_print_inner(m, " outgoing transaction "); t_opt = t.from_parent.as_ref(); } else if Arc::ptr_eq(&t.to, &self.process) { t.debug_print_inner(m, " incoming transaction "); t_opt = t.find_from(self); } else { t.debug_print_inner(m, " bad transaction "); t_opt = None; } } for work in &inner.work_list { work.debug_print(m, " ", " pending transaction ")?; } Ok(()) } pub(crate) fn get_extended_error(&self, data: UserSlice) -> Result { let mut writer = data.writer(); let ee = self.inner.lock().extended_error;