summaryrefslogtreecommitdiff
path: root/drivers/android
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-04-24 13:23:50 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-04-24 13:23:50 -0700
commitcb4eb6771c0f8fd1c52a8f6fdec7762fb087380a (patch)
treec50311c87c0d464bfbba34e5c3b8699897fb1b65 /drivers/android
parentb2680ba4a2ad259c7bbd856ed830b459e11d88ba (diff)
parent1c0220a61508d67a09a6e71eb09593a8aea61822 (diff)
downloadlwn-cb4eb6771c0f8fd1c52a8f6fdec7762fb087380a.tar.gz
lwn-cb4eb6771c0f8fd1c52a8f6fdec7762fb087380a.zip
Merge tag 'char-misc-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char / misc / IIO / and others driver updates from Greg KH: "Here is the char/misc/iio and other smaller driver subsystem updates for 7.1-rc1. Lots of stuff in here, all tiny, but relevant for the different drivers they touch. Major points in here is: - the usual large set of new IIO drivers and updates for that subsystem (the large majority of this diffstat) - lots of comedi driver updates and bugfixes - coresight driver updates - interconnect driver updates and additions - mei driver updates - binder (both rust and C versions) updates and fixes - lots of other smaller driver subsystem updates and additions All of these have been in linux-next for a while with no reported issues" * tag 'char-misc-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (405 commits) coresight: tpdm: fix invalid MMIO access issue mei: me: add nova lake point H DID mei: lb: add late binding version 2 mei: bus: add mei_cldev_uuid w1: ds2490: drop redundant device reference bus: mhi: host: pci_generic: Add Telit FE912C04 modem support mei: csc: wake device while reading firmware status mei: csc: support controller with separate PCI device mei: convert PCI error to common errno mei: trace: print return value of pci_cfg_read mei: me: move trace into firmware status read mei: fix idle print specifiers mei: me: use PCI_DEVICE_DATA macro sonypi: Convert ACPI driver to a platform one misc: apds990x: fix all kernel-doc warnings most: usb: Use kzalloc_objs for endpoint address array hpet: Convert ACPI driver to a platform one misc: vmw_vmci: Fix spelling mistakes in comments parport: Remove completed item from to-do list char: remove unnecessary module_init/exit functions ...
Diffstat (limited to 'drivers/android')
-rw-r--r--drivers/android/binder.c2
-rw-r--r--drivers/android/binder/allocation.rs33
-rw-r--r--drivers/android/binder/context.rs11
-rw-r--r--drivers/android/binder/error.rs10
-rw-r--r--drivers/android/binder/process.rs29
-rw-r--r--drivers/android/binder/rust_binder.h5
-rw-r--r--drivers/android/binder/rust_binder_events.h121
-rw-r--r--drivers/android/binder/rust_binder_main.rs3
-rw-r--r--drivers/android/binder/thread.rs211
-rw-r--r--drivers/android/binder/trace.rs78
-rw-r--r--drivers/android/binder/transaction.rs89
11 files changed, 427 insertions, 165 deletions
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 21f91d9f2fbc..9e6194224593 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -3117,7 +3117,7 @@ static void binder_transaction(struct binder_proc *proc,
t->start_time = t_start_time;
t->from_pid = proc->pid;
t->from_tid = thread->pid;
- t->sender_euid = task_euid(proc->tsk);
+ t->sender_euid = current_euid();
t->code = tr->code;
t->flags = tr->flags;
t->priority = task_nice(current);
diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs
index 7f65a9c3a0e5..0cab959e4b7e 100644
--- a/drivers/android/binder/allocation.rs
+++ b/drivers/android/binder/allocation.rs
@@ -56,7 +56,6 @@ pub(crate) struct Allocation {
pub(crate) process: Arc<Process>,
allocation_info: Option<AllocationInfo>,
free_on_drop: bool,
- pub(crate) oneway_spam_detected: bool,
#[allow(dead_code)]
pub(crate) debug_id: usize,
}
@@ -68,7 +67,6 @@ impl Allocation {
offset: usize,
size: usize,
ptr: usize,
- oneway_spam_detected: bool,
) -> Self {
Self {
process,
@@ -76,7 +74,6 @@ impl Allocation {
size,
ptr,
debug_id,
- oneway_spam_detected,
allocation_info: None,
free_on_drop: true,
}
@@ -208,6 +205,7 @@ impl Allocation {
let res = FileDescriptorReservation::get_unused_fd_flags(bindings::O_CLOEXEC)?;
let fd = res.reserved_fd();
self.write::<u32>(file_info.buffer_offset, &fd)?;
+ crate::trace::trace_transaction_fd_recv(self.debug_id, fd, file_info.buffer_offset);
reservations.push(
Reservation {
@@ -260,19 +258,22 @@ impl Drop for Allocation {
}
}
- for &fd in &info.file_list.close_on_free {
- let closer = match DeferredFdCloser::new(GFP_KERNEL) {
- Ok(closer) => closer,
- Err(kernel::alloc::AllocError) => {
- // Ignore allocation failures.
- break;
- }
- };
-
- // Here, we ignore errors. The operation can fail if the fd is not valid, or if the
- // method is called from a kthread. However, this is always called from a syscall,
- // so the latter case cannot happen, and we don't care about the first case.
- let _ = closer.close_fd(fd);
+ if self.process.task == kernel::current!().group_leader() {
+ for &fd in &info.file_list.close_on_free {
+ let closer = match DeferredFdCloser::new(GFP_KERNEL) {
+ Ok(closer) => closer,
+ Err(kernel::alloc::AllocError) => {
+ // Ignore allocation failures.
+ break;
+ }
+ };
+
+ // Here, we ignore errors. The operation can fail if the fd is not valid, or if
+ // the method is called from a kthread. However, this is always called from a
+ // syscall, so the latter case cannot happen, and we don't care about the first
+ // case.
+ let _ = closer.close_fd(fd);
+ }
}
if info.clear_on_free {
diff --git a/drivers/android/binder/context.rs b/drivers/android/binder/context.rs
index 9cf437c025a2..ddddb66b3557 100644
--- a/drivers/android/binder/context.rs
+++ b/drivers/android/binder/context.rs
@@ -94,6 +94,17 @@ impl Context {
}
let mut manager = self.manager.lock();
manager.all_procs.retain(|p| !Arc::ptr_eq(p, proc));
+
+ // Shrink the vector if it has significant unused capacity to avoid memory waste,
+ // but use a conservative strategy to prevent shrink-then-regrow oscillation.
+ // Only shrink when length drops below 1/4 of capacity, and shrink to twice the length.
+ let len = manager.all_procs.len();
+ let cap = manager.all_procs.capacity();
+ if len < cap / 4 {
+ // Shrink to twice the current length. Ignore allocation failures since this
+ // is just an optimization; the vector remains valid even if shrinking fails.
+ let _ = manager.all_procs.shrink_to(len * 2, GFP_KERNEL);
+ }
}
pub(crate) fn set_manager_node(&self, node_ref: NodeRef) -> Result {
diff --git a/drivers/android/binder/error.rs b/drivers/android/binder/error.rs
index b24497cfa292..45d85d4c2815 100644
--- a/drivers/android/binder/error.rs
+++ b/drivers/android/binder/error.rs
@@ -13,7 +13,7 @@ pub(crate) type BinderResult<T = ()> = core::result::Result<T, BinderError>;
/// errno.
pub(crate) struct BinderError {
pub(crate) reply: u32,
- source: Option<Error>,
+ pub(crate) source: Option<Error>,
}
impl BinderError {
@@ -41,14 +41,6 @@ impl BinderError {
pub(crate) fn is_dead(&self) -> bool {
self.reply == BR_DEAD_REPLY
}
-
- pub(crate) fn as_errno(&self) -> kernel::ffi::c_int {
- self.source.unwrap_or(EINVAL).to_errno()
- }
-
- pub(crate) fn should_pr_warn(&self) -> bool {
- self.source.is_some()
- }
}
/// Convert an errno into a `BinderError` and store the errno used to construct it. The errno
diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs
index f06498129aa9..820cbd541435 100644
--- a/drivers/android/binder/process.rs
+++ b/drivers/android/binder/process.rs
@@ -48,6 +48,7 @@ use crate::{
range_alloc::{RangeAllocator, ReserveNew, ReserveNewArgs},
stats::BinderStats,
thread::{PushWorkRes, Thread},
+ transaction::TransactionInfo,
BinderfsProcFile, DArc, DLArc, DTRWrap, DeliverToRead,
};
@@ -681,7 +682,7 @@ impl Process {
fn get_current_thread(self: ArcBorrow<'_, Self>) -> Result<Arc<Thread>> {
let id = {
let current = kernel::current!();
- if !core::ptr::eq(current.group_leader(), &*self.task) {
+ if self.task != current.group_leader() {
pr_err!("get_current_thread was called from the wrong process.");
return Err(EINVAL);
}
@@ -1003,16 +1004,15 @@ impl Process {
self: &Arc<Self>,
debug_id: usize,
size: usize,
- is_oneway: bool,
- from_pid: i32,
+ info: &mut TransactionInfo,
) -> BinderResult<NewAllocation> {
use kernel::page::PAGE_SIZE;
let mut reserve_new_args = ReserveNewArgs {
debug_id,
size,
- is_oneway,
- pid: from_pid,
+ is_oneway: info.is_oneway(),
+ pid: info.from_pid,
..ReserveNewArgs::default()
};
@@ -1028,13 +1028,13 @@ impl Process {
reserve_new_args = alloc_request.make_alloc()?;
};
+ info.oneway_spam_suspect = new_alloc.oneway_spam_detected;
let res = Allocation::new(
self.clone(),
debug_id,
new_alloc.offset,
size,
addr + new_alloc.offset,
- new_alloc.oneway_spam_detected,
);
// This allocation will be marked as in use until the `Allocation` is used to free it.
@@ -1066,7 +1066,7 @@ impl Process {
let mapping = inner.mapping.as_mut()?;
let offset = ptr.checked_sub(mapping.address)?;
let (size, debug_id, odata) = mapping.alloc.reserve_existing(offset).ok()?;
- let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr, false);
+ let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr);
if let Some(data) = odata {
alloc.set_info(data);
}
@@ -1414,8 +1414,7 @@ impl Process {
.alloc
.take_for_each(|offset, size, debug_id, odata| {
let ptr = offset + address;
- let mut alloc =
- Allocation::new(self.clone(), debug_id, offset, size, ptr, false);
+ let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr);
if let Some(data) = odata {
alloc.set_info(data);
}
@@ -1443,6 +1442,9 @@ impl Process {
}
}
+ // #[export_name] is a temporary workaround so that ps output does not become unreadable from
+ // mangled symbol names.
+ #[export_name = "rust_binder_freeze"]
pub(crate) fn ioctl_freeze(&self, info: &BinderFreezeInfo) -> Result {
if info.enable == 0 {
let msgs = self.prepare_freeze_messages()?;
@@ -1657,11 +1659,14 @@ impl Process {
const _IOC_READ_WRITE: u32 = _IOC_READ | _IOC_WRITE;
- match _IOC_DIR(cmd) {
+ let res = match _IOC_DIR(cmd) {
_IOC_WRITE => Self::ioctl_write_only(this, file, cmd, &mut user_slice.reader()),
_IOC_READ_WRITE => Self::ioctl_write_read(this, file, cmd, user_slice),
_ => Err(EINVAL),
- }
+ };
+
+ crate::trace::trace_ioctl_done(res);
+ res
}
pub(crate) fn mmap(
@@ -1670,7 +1675,7 @@ impl Process {
vma: &mm::virt::VmaNew,
) -> Result {
// We don't allow mmap to be used in a different process.
- if !core::ptr::eq(kernel::current!().group_leader(), &*this.task) {
+ if this.task != kernel::current!().group_leader() {
return Err(EINVAL);
}
if vma.start() == 0 {
diff --git a/drivers/android/binder/rust_binder.h b/drivers/android/binder/rust_binder.h
index d2284726c025..3936b9d0b8cd 100644
--- a/drivers/android/binder/rust_binder.h
+++ b/drivers/android/binder/rust_binder.h
@@ -99,4 +99,9 @@ static inline size_t rust_binder_node_debug_id(rust_binder_node t)
return *(size_t *) (t + RUST_BINDER_LAYOUT.n.debug_id);
}
+static inline binder_uintptr_t rust_binder_node_ptr(rust_binder_node t)
+{
+ return *(binder_uintptr_t *) (t + RUST_BINDER_LAYOUT.n.ptr);
+}
+
#endif
diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h
index 8ad785c6bd0f..1a446787c8b3 100644
--- a/drivers/android/binder/rust_binder_events.h
+++ b/drivers/android/binder/rust_binder_events.h
@@ -15,7 +15,7 @@
#include <linux/tracepoint.h>
-TRACE_EVENT(rust_binder_ioctl,
+TRACE_EVENT(binder_ioctl,
TP_PROTO(unsigned int cmd, unsigned long arg),
TP_ARGS(cmd, arg),
@@ -30,7 +30,46 @@ TRACE_EVENT(rust_binder_ioctl,
TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg)
);
-TRACE_EVENT(rust_binder_transaction,
+DECLARE_EVENT_CLASS(binder_function_return_class,
+ TP_PROTO(int ret),
+ TP_ARGS(ret),
+ TP_STRUCT__entry(
+ __field(int, ret)
+ ),
+ TP_fast_assign(
+ __entry->ret = ret;
+ ),
+ TP_printk("ret=%d", __entry->ret)
+);
+
+#define DEFINE_RBINDER_FUNCTION_RETURN_EVENT(name) \
+DEFINE_EVENT(binder_function_return_class, name, \
+ TP_PROTO(int ret), \
+ TP_ARGS(ret))
+
+DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_ioctl_done);
+DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_read_done);
+DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_write_done);
+
+TRACE_EVENT(binder_wait_for_work,
+ TP_PROTO(bool proc_work, bool transaction_stack, bool thread_todo),
+ TP_ARGS(proc_work, transaction_stack, thread_todo),
+ TP_STRUCT__entry(
+ __field(bool, proc_work)
+ __field(bool, transaction_stack)
+ __field(bool, thread_todo)
+ ),
+ TP_fast_assign(
+ __entry->proc_work = proc_work;
+ __entry->transaction_stack = transaction_stack;
+ __entry->thread_todo = thread_todo;
+ ),
+ TP_printk("proc_work=%d transaction_stack=%d thread_todo=%d",
+ __entry->proc_work, __entry->transaction_stack,
+ __entry->thread_todo)
+);
+
+TRACE_EVENT(binder_transaction,
TP_PROTO(bool reply, rust_binder_transaction t, struct task_struct *thread),
TP_ARGS(reply, t, thread),
TP_STRUCT__entry(
@@ -60,6 +99,84 @@ TRACE_EVENT(rust_binder_transaction,
__entry->reply, __entry->flags, __entry->code)
);
+TRACE_EVENT(binder_transaction_received,
+ TP_PROTO(rust_binder_transaction t),
+ TP_ARGS(t),
+ TP_STRUCT__entry(
+ __field(int, debug_id)
+ ),
+ TP_fast_assign(
+ __entry->debug_id = rust_binder_transaction_debug_id(t);
+ ),
+ TP_printk("transaction=%d", __entry->debug_id)
+);
+
+TRACE_EVENT(binder_transaction_fd_send,
+ TP_PROTO(int t_debug_id, int fd, size_t offset),
+ TP_ARGS(t_debug_id, fd, offset),
+ TP_STRUCT__entry(
+ __field(int, debug_id)
+ __field(int, fd)
+ __field(size_t, offset)
+ ),
+ TP_fast_assign(
+ __entry->debug_id = t_debug_id;
+ __entry->fd = fd;
+ __entry->offset = offset;
+ ),
+ TP_printk("transaction=%d src_fd=%d offset=%zu",
+ __entry->debug_id, __entry->fd, __entry->offset)
+);
+
+TRACE_EVENT(binder_transaction_fd_recv,
+ TP_PROTO(int t_debug_id, int fd, size_t offset),
+ TP_ARGS(t_debug_id, fd, offset),
+ TP_STRUCT__entry(
+ __field(int, debug_id)
+ __field(int, fd)
+ __field(size_t, offset)
+ ),
+ TP_fast_assign(
+ __entry->debug_id = t_debug_id;
+ __entry->fd = fd;
+ __entry->offset = offset;
+ ),
+ TP_printk("transaction=%d dest_fd=%d offset=%zu",
+ __entry->debug_id, __entry->fd, __entry->offset)
+);
+
+TRACE_EVENT(binder_command,
+ TP_PROTO(uint32_t cmd),
+ TP_ARGS(cmd),
+ TP_STRUCT__entry(
+ __field(uint32_t, cmd)
+ ),
+ TP_fast_assign(
+ __entry->cmd = cmd;
+ ),
+ TP_printk("cmd=0x%x %s",
+ __entry->cmd,
+ _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_command_strings) ?
+ binder_command_strings[_IOC_NR(__entry->cmd)] :
+ "unknown")
+);
+
+TRACE_EVENT(binder_return,
+ TP_PROTO(uint32_t cmd),
+ TP_ARGS(cmd),
+ TP_STRUCT__entry(
+ __field(uint32_t, cmd)
+ ),
+ TP_fast_assign(
+ __entry->cmd = cmd;
+ ),
+ TP_printk("cmd=0x%x %s",
+ __entry->cmd,
+ _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_return_strings) ?
+ binder_return_strings[_IOC_NR(__entry->cmd)] :
+ "unknown")
+);
+
#endif /* _RUST_BINDER_TRACE_H */
/* This part must be outside protection */
diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs
index 632b54714e44..dc1941cd2407 100644
--- a/drivers/android/binder/rust_binder_main.rs
+++ b/drivers/android/binder/rust_binder_main.rs
@@ -118,6 +118,7 @@ impl<'a> BinderReturnWriter<'a> {
/// Write a return code back to user space.
/// Should be a `BR_` constant from [`defs`] e.g. [`defs::BR_TRANSACTION_COMPLETE`].
fn write_code(&mut self, code: u32) -> Result {
+ crate::trace::trace_return(code);
stats::GLOBAL_STATS.inc_br(code);
self.thread.process.stats.inc_br(code);
self.writer.write(&code)
@@ -294,8 +295,6 @@ impl kernel::Module for BinderModule {
// SAFETY: The module initializer never runs twice, so we only call this once.
unsafe { crate::context::CONTEXTS.init() };
- pr_warn!("Loaded Rust Binder.");
-
BINDER_SHRINKER.register(c"android-binder")?;
// SAFETY: The module is being loaded, so we can initialize binderfs.
diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs
index c004214b1662..97d5f31e8fe3 100644
--- a/drivers/android/binder/thread.rs
+++ b/drivers/android/binder/thread.rs
@@ -19,7 +19,7 @@ use kernel::{
sync::poll::{PollCondVar, PollTable},
sync::{aref::ARef, Arc, SpinLock},
task::Task,
- uaccess::UserSlice,
+ uaccess::{UserPtr, UserSlice, UserSliceReader},
uapi,
};
@@ -30,7 +30,7 @@ use crate::{
process::{GetWorkOrRegister, Process},
ptr_align,
stats::GLOBAL_STATS,
- transaction::Transaction,
+ transaction::{Transaction, TransactionInfo},
BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead,
};
@@ -513,6 +513,9 @@ impl Thread {
/// Attempts to fetch a work item from the thread-local queue. The behaviour if the queue is
/// empty depends on `wait`: if it is true, the function waits for some work to be queued (or a
/// signal); otherwise it returns indicating that none is available.
+ // #[export_name] is a temporary workaround so that ps output does not become unreadable from
+ // mangled symbol names.
+ #[export_name = "rust_binder_waitlcl"]
fn get_work_local(self: &Arc<Self>, wait: bool) -> Result<Option<DLArc<dyn DeliverToRead>>> {
{
let mut inner = self.inner.lock();
@@ -551,6 +554,9 @@ impl Thread {
///
/// This must only be called when the thread is not participating in a transaction chain. If it
/// is, the local version (`get_work_local`) should be used instead.
+ // #[export_name] is a temporary workaround so that ps output does not become unreadable from
+ // mangled symbol names.
+ #[export_name = "rust_binder_wait"]
fn get_work(self: &Arc<Self>, wait: bool) -> Result<Option<DLArc<dyn DeliverToRead>>> {
// Try to get work from the thread's work queue, using only a local lock.
{
@@ -706,6 +712,7 @@ impl Thread {
core::mem::offset_of!(uapi::binder_fd_object, __bindgen_anon_1.fd);
let field_offset = offset + FD_FIELD_OFFSET;
+ crate::trace::trace_transaction_fd_send(view.alloc.debug_id, fd, field_offset);
view.alloc.info_add_fd(file, field_offset, false)?;
}
@@ -945,13 +952,11 @@ impl Thread {
pub(crate) fn copy_transaction_data(
&self,
to_process: Arc<Process>,
- tr: &BinderTransactionDataSg,
+ info: &mut TransactionInfo,
debug_id: usize,
allow_fds: bool,
txn_security_ctx_offset: Option<&mut usize>,
) -> BinderResult<NewAllocation> {
- let trd = &tr.transaction_data;
- let is_oneway = trd.flags & TF_ONE_WAY != 0;
let mut secctx = if let Some(offset) = txn_security_ctx_offset {
let secid = self.process.cred.get_secid();
let ctx = match security::SecurityCtx::from_secid(secid) {
@@ -966,10 +971,10 @@ impl Thread {
None
};
- let data_size = trd.data_size.try_into().map_err(|_| EINVAL)?;
+ let data_size = info.data_size;
let aligned_data_size = ptr_align(data_size).ok_or(EINVAL)?;
- let offsets_size: usize = trd.offsets_size.try_into().map_err(|_| EINVAL)?;
- let buffers_size: usize = tr.buffers_size.try_into().map_err(|_| EINVAL)?;
+ let offsets_size = info.offsets_size;
+ let buffers_size = info.buffers_size;
let aligned_secctx_size = match secctx.as_ref() {
Some((_offset, ctx)) => ptr_align(ctx.len()).ok_or(EINVAL)?,
None => 0,
@@ -992,32 +997,25 @@ impl Thread {
size_of::<u64>(),
);
let secctx_off = aligned_data_size + offsets_size + buffers_size;
- let mut alloc =
- match to_process.buffer_alloc(debug_id, len, is_oneway, self.process.task.pid()) {
- Ok(alloc) => alloc,
- Err(err) => {
- pr_warn!(
- "Failed to allocate buffer. len:{}, is_oneway:{}",
- len,
- is_oneway
- );
- return Err(err);
- }
- };
+ let mut alloc = match to_process.buffer_alloc(debug_id, len, info) {
+ Ok(alloc) => alloc,
+ Err(err) => {
+ pr_warn!(
+ "Failed to allocate buffer. len:{}, is_oneway:{}",
+ len,
+ info.is_oneway(),
+ );
+ return Err(err);
+ }
+ };
- // SAFETY: This accesses a union field, but it's okay because the field's type is valid for
- // all bit-patterns.
- let trd_data_ptr = unsafe { &trd.data.ptr };
- let mut buffer_reader =
- UserSlice::new(UserPtr::from_addr(trd_data_ptr.buffer as _), data_size).reader();
+ let mut buffer_reader = UserSlice::new(info.data_ptr, data_size).reader();
let mut end_of_previous_object = 0;
let mut sg_state = None;
// Copy offsets if there are any.
if offsets_size > 0 {
- let mut offsets_reader =
- UserSlice::new(UserPtr::from_addr(trd_data_ptr.offsets as _), offsets_size)
- .reader();
+ let mut offsets_reader = UserSlice::new(info.offsets_ptr, offsets_size).reader();
let offsets_start = aligned_data_size;
let offsets_end = aligned_data_size + offsets_size;
@@ -1192,37 +1190,92 @@ impl Thread {
}
}
- fn transaction<T>(self: &Arc<Self>, tr: &BinderTransactionDataSg, inner: T)
- where
- T: FnOnce(&Arc<Self>, &BinderTransactionDataSg) -> BinderResult,
- {
- if let Err(err) = inner(self, tr) {
- if err.should_pr_warn() {
- let mut ee = self.inner.lock().extended_error;
- ee.command = err.reply;
- ee.param = err.as_errno();
- pr_warn!(
- "Transaction failed: {:?} my_pid:{}",
- err,
- self.process.pid_in_current_ns()
- );
+ // No inlining avoids allocating stack space for `BinderTransactionData` for the entire
+ // duration of `transaction()`.
+ #[inline(never)]
+ fn read_transaction_info(
+ &self,
+ cmd: u32,
+ reader: &mut UserSliceReader,
+ info: &mut TransactionInfo,
+ ) -> Result<()> {
+ let td = match cmd {
+ BC_TRANSACTION | BC_REPLY => {
+ reader.read::<BinderTransactionData>()?.with_buffers_size(0)
+ }
+ BC_TRANSACTION_SG | BC_REPLY_SG => reader.read::<BinderTransactionDataSg>()?,
+ _ => return Err(EINVAL),
+ };
+
+ // SAFETY: Above `read` call initializes all bytes, so this union read is ok.
+ let trd_data_ptr = unsafe { &td.transaction_data.data.ptr };
+
+ info.is_reply = matches!(cmd, BC_REPLY | BC_REPLY_SG);
+ info.from_pid = self.process.task.pid();
+ info.from_tid = self.id;
+ info.code = td.transaction_data.code;
+ info.flags = td.transaction_data.flags;
+ info.data_ptr = UserPtr::from_addr(trd_data_ptr.buffer as usize);
+ info.data_size = td.transaction_data.data_size as usize;
+ info.offsets_ptr = UserPtr::from_addr(trd_data_ptr.offsets as usize);
+ info.offsets_size = td.transaction_data.offsets_size as usize;
+ info.buffers_size = td.buffers_size as usize;
+ // SAFETY: Above `read` call initializes all bytes, so this union read is ok.
+ info.target_handle = unsafe { td.transaction_data.target.handle };
+ Ok(())
+ }
+
+ #[inline(never)]
+ fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Result<()> {
+ let mut info = TransactionInfo::zeroed();
+ self.read_transaction_info(cmd, reader, &mut info)?;
+
+ let ret = if info.is_reply {
+ self.reply_inner(&mut info)
+ } else if info.is_oneway() {
+ self.oneway_transaction_inner(&mut info)
+ } else {
+ self.transaction_inner(&mut info)
+ };
+
+ if let Err(err) = ret {
+ if err.reply != BR_TRANSACTION_COMPLETE {
+ info.reply = err.reply;
}
self.push_return_work(err.reply);
+ if let Some(source) = &err.source {
+ info.errno = source.to_errno();
+ info.reply = err.reply;
+
+ {
+ let mut ee = self.inner.lock().extended_error;
+ ee.command = err.reply;
+ ee.param = source.to_errno();
+ }
+
+ pr_warn!(
+ "{}:{} transaction to {} failed: {source:?}",
+ info.from_pid,
+ info.from_tid,
+ info.to_pid
+ );
+ }
}
+
+ Ok(())
}
- fn transaction_inner(self: &Arc<Self>, tr: &BinderTransactionDataSg) -> BinderResult {
- // SAFETY: Handle's type has no invalid bit patterns.
- let handle = unsafe { tr.transaction_data.target.handle };
- let node_ref = self.process.get_transaction_node(handle)?;
+ fn transaction_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
+ let node_ref = self.process.get_transaction_node(info.target_handle)?;
+ info.to_pid = node_ref.node.owner.task.pid();
security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?;
// TODO: We need to ensure that there isn't a pending transaction in the work queue. How
// could this happen?
let top = self.top_of_transaction_stack()?;
let list_completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
let completion = list_completion.clone_arc();
- let transaction = Transaction::new(node_ref, top, self, tr)?;
+ let transaction = Transaction::new(node_ref, top, self, info)?;
// Check that the transaction stack hasn't changed while the lock was released, then update
// it with the new transaction.
@@ -1238,7 +1291,7 @@ impl Thread {
inner.push_work_deferred(list_completion);
}
- if let Err(e) = transaction.submit() {
+ if let Err(e) = transaction.submit(info) {
completion.skip();
// Define `transaction` first to drop it after `inner`.
let transaction;
@@ -1251,18 +1304,21 @@ impl Thread {
}
}
- fn reply_inner(self: &Arc<Self>, tr: &BinderTransactionDataSg) -> BinderResult {
+ fn reply_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
let orig = self.inner.lock().pop_transaction_to_reply(self)?;
if !orig.from.is_current_transaction(&orig) {
return Err(EINVAL.into());
}
+ info.to_tid = orig.from.id;
+ info.to_pid = orig.from.process.task.pid();
+
// We need to complete the transaction even if we cannot complete building the reply.
let out = (|| -> BinderResult<_> {
let completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
let process = orig.from.process.clone();
let allow_fds = orig.flags & TF_ACCEPT_FDS != 0;
- let reply = Transaction::new_reply(self, process, tr, allow_fds)?;
+ let reply = Transaction::new_reply(self, process, info, allow_fds)?;
self.inner.lock().push_work(completion);
orig.from.deliver_reply(Ok(reply), &orig);
Ok(())
@@ -1283,16 +1339,12 @@ impl Thread {
out
}
- fn oneway_transaction_inner(self: &Arc<Self>, tr: &BinderTransactionDataSg) -> BinderResult {
- // SAFETY: The `handle` field is valid for all possible byte values, so reading from the
- // union is okay.
- let handle = unsafe { tr.transaction_data.target.handle };
- let node_ref = self.process.get_transaction_node(handle)?;
+ fn oneway_transaction_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
+ let node_ref = self.process.get_transaction_node(info.target_handle)?;
+ info.to_pid = node_ref.node.owner.task.pid();
security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?;
- let transaction = Transaction::new(node_ref, None, self, tr)?;
- let code = if self.process.is_oneway_spam_detection_enabled()
- && transaction.oneway_spam_detected
- {
+ let transaction = Transaction::new(node_ref, None, self, info)?;
+ let code = if self.process.is_oneway_spam_detection_enabled() && info.oneway_spam_suspect {
BR_ONEWAY_SPAM_SUSPECT
} else {
BR_TRANSACTION_COMPLETE
@@ -1300,7 +1352,7 @@ impl Thread {
let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?;
let completion = list_completion.clone_arc();
self.inner.lock().push_work(list_completion);
- match transaction.submit() {
+ match transaction.submit(info) {
Ok(()) => Ok(()),
Err(err) => {
completion.skip();
@@ -1318,32 +1370,12 @@ impl Thread {
while reader.len() >= size_of::<u32>() && self.inner.lock().return_work.is_unused() {
let before = reader.len();
let cmd = reader.read::<u32>()?;
+ crate::trace::trace_command(cmd);
GLOBAL_STATS.inc_bc(cmd);
self.process.stats.inc_bc(cmd);
match cmd {
- BC_TRANSACTION => {
- let tr = reader.read::<BinderTransactionData>()?.with_buffers_size(0);
- if tr.transaction_data.flags & TF_ONE_WAY != 0 {
- self.transaction(&tr, Self::oneway_transaction_inner);
- } else {
- self.transaction(&tr, Self::transaction_inner);
- }
- }
- BC_TRANSACTION_SG => {
- let tr = reader.read::<BinderTransactionDataSg>()?;
- if tr.transaction_data.flags & TF_ONE_WAY != 0 {
- self.transaction(&tr, Self::oneway_transaction_inner);
- } else {
- self.transaction(&tr, Self::transaction_inner);
- }
- }
- BC_REPLY => {
- let tr = reader.read::<BinderTransactionData>()?.with_buffers_size(0);
- self.transaction(&tr, Self::reply_inner)
- }
- BC_REPLY_SG => {
- let tr = reader.read::<BinderTransactionDataSg>()?;
- self.transaction(&tr, Self::reply_inner)
+ BC_TRANSACTION | BC_TRANSACTION_SG | BC_REPLY | BC_REPLY_SG => {
+ self.transaction(cmd, &mut reader)?;
}
BC_FREE_BUFFER => {
let buffer = self.process.buffer_get(reader.read()?);
@@ -1407,11 +1439,18 @@ impl Thread {
UserSlice::new(UserPtr::from_addr(read_start as _), read_len as _).writer(),
self,
);
- let (in_pool, use_proc_queue) = {
+ let (in_pool, has_transaction, thread_todo, use_proc_queue) = {
let inner = self.inner.lock();
- (inner.is_looper(), inner.should_use_process_work_queue())
+ (
+ inner.is_looper(),
+ inner.current_transaction.is_some(),
+ !inner.work_list.is_empty(),
+ inner.should_use_process_work_queue(),
+ )
};
+ crate::trace::trace_wait_for_work(use_proc_queue, has_transaction, thread_todo);
+
let getter = if use_proc_queue {
Self::get_work
} else {
@@ -1477,6 +1516,7 @@ impl Thread {
let mut ret = Ok(());
if req.write_size > 0 {
ret = self.write(&mut req);
+ crate::trace::trace_write_done(ret);
if let Err(err) = ret {
pr_warn!(
"Write failure {:?} in pid:{}",
@@ -1493,6 +1533,7 @@ impl Thread {
// Go through the work queue.
if req.read_size > 0 {
ret = self.read(&mut req, wait);
+ crate::trace::trace_read_done(ret);
if ret.is_err() && ret != Err(EINTR) {
pr_warn!(
"Read failure {:?} in pid:{}",
diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs
index 9839901c7151..5539672d7285 100644
--- a/drivers/android/binder/trace.rs
+++ b/drivers/android/binder/trace.rs
@@ -5,13 +5,23 @@
use crate::transaction::Transaction;
use kernel::bindings::{rust_binder_transaction, task_struct};
-use kernel::ffi::{c_uint, c_ulong};
+use kernel::error::Result;
+use kernel::ffi::{c_int, c_uint, c_ulong};
use kernel::task::Task;
use kernel::tracepoint::declare_trace;
declare_trace! {
- unsafe fn rust_binder_ioctl(cmd: c_uint, arg: c_ulong);
- unsafe fn rust_binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct);
+ unsafe fn binder_ioctl(cmd: c_uint, arg: c_ulong);
+ unsafe fn binder_ioctl_done(ret: c_int);
+ unsafe fn binder_read_done(ret: c_int);
+ unsafe fn binder_write_done(ret: c_int);
+ unsafe fn binder_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool);
+ unsafe fn binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct);
+ unsafe fn binder_transaction_received(t: rust_binder_transaction);
+ unsafe fn binder_transaction_fd_send(t_debug_id: c_int, fd: c_int, offset: usize);
+ unsafe fn binder_transaction_fd_recv(t_debug_id: c_int, fd: c_int, offset: usize);
+ unsafe fn binder_command(cmd: u32);
+ unsafe fn binder_return(ret: u32);
}
#[inline]
@@ -20,9 +30,39 @@ fn raw_transaction(t: &Transaction) -> rust_binder_transaction {
}
#[inline]
+fn to_errno(ret: Result) -> i32 {
+ match ret {
+ Ok(()) => 0,
+ Err(err) => err.to_errno(),
+ }
+}
+
+#[inline]
pub(crate) fn trace_ioctl(cmd: u32, arg: usize) {
// SAFETY: Always safe to call.
- unsafe { rust_binder_ioctl(cmd, arg as c_ulong) }
+ unsafe { binder_ioctl(cmd, arg as c_ulong) }
+}
+
+#[inline]
+pub(crate) fn trace_ioctl_done(ret: Result) {
+ // SAFETY: Always safe to call.
+ unsafe { binder_ioctl_done(to_errno(ret)) }
+}
+#[inline]
+pub(crate) fn trace_read_done(ret: Result) {
+ // SAFETY: Always safe to call.
+ unsafe { binder_read_done(to_errno(ret)) }
+}
+#[inline]
+pub(crate) fn trace_write_done(ret: Result) {
+ // SAFETY: Always safe to call.
+ unsafe { binder_write_done(to_errno(ret)) }
+}
+
+#[inline]
+pub(crate) fn trace_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool) {
+ // SAFETY: Always safe to call.
+ unsafe { binder_wait_for_work(proc_work, transaction_stack, thread_todo) }
}
#[inline]
@@ -33,5 +73,33 @@ pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Ta
};
// SAFETY: The raw transaction is valid for the duration of this call. The thread pointer is
// valid or null.
- unsafe { rust_binder_transaction(reply, raw_transaction(t), thread) }
+ unsafe { binder_transaction(reply, raw_transaction(t), thread) }
+}
+
+#[inline]
+pub(crate) fn trace_transaction_received(t: &Transaction) {
+ // SAFETY: The raw transaction is valid for the duration of this call.
+ unsafe { binder_transaction_received(raw_transaction(t)) }
+}
+
+#[inline]
+pub(crate) fn trace_transaction_fd_send(t_debug_id: usize, fd: u32, offset: usize) {
+ // SAFETY: This function is always safe to call.
+ unsafe { binder_transaction_fd_send(t_debug_id as c_int, fd as c_int, offset) }
+}
+#[inline]
+pub(crate) fn trace_transaction_fd_recv(t_debug_id: usize, fd: u32, offset: usize) {
+ // SAFETY: This function is always safe to call.
+ unsafe { binder_transaction_fd_recv(t_debug_id as c_int, fd as c_int, offset) }
+}
+
+#[inline]
+pub(crate) fn trace_command(cmd: u32) {
+ // SAFETY: This function is always safe to call.
+ unsafe { binder_command(cmd) }
+}
+#[inline]
+pub(crate) fn trace_return(ret: u32) {
+ // SAFETY: This function is always safe to call.
+ unsafe { binder_return(ret) }
}
diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs
index 75e6f5fbaaae..47d5e4d88b07 100644
--- a/drivers/android/binder/transaction.rs
+++ b/drivers/android/binder/transaction.rs
@@ -8,7 +8,7 @@ use kernel::{
seq_print,
sync::atomic::{ordering::Relaxed, Atomic},
sync::{Arc, SpinLock},
- task::Kuid,
+ task::{Kuid, Pid},
time::{Instant, Monotonic},
types::ScopeGuard,
};
@@ -24,6 +24,33 @@ use crate::{
BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead,
};
+#[derive(Zeroable)]
+pub(crate) struct TransactionInfo {
+ pub(crate) from_pid: Pid,
+ pub(crate) from_tid: Pid,
+ pub(crate) to_pid: Pid,
+ pub(crate) to_tid: Pid,
+ pub(crate) code: u32,
+ pub(crate) flags: u32,
+ pub(crate) data_ptr: UserPtr,
+ pub(crate) data_size: usize,
+ pub(crate) offsets_ptr: UserPtr,
+ pub(crate) offsets_size: usize,
+ pub(crate) buffers_size: usize,
+ pub(crate) target_handle: u32,
+ pub(crate) errno: i32,
+ pub(crate) reply: u32,
+ pub(crate) oneway_spam_suspect: bool,
+ pub(crate) is_reply: bool,
+}
+
+impl TransactionInfo {
+ #[inline]
+ pub(crate) fn is_oneway(&self) -> bool {
+ self.flags & TF_ONE_WAY != 0
+ }
+}
+
use core::mem::offset_of;
use kernel::bindings::rb_transaction_layout;
pub(crate) const TRANSACTION_LAYOUT: rb_transaction_layout = rb_transaction_layout {
@@ -52,7 +79,6 @@ pub(crate) struct Transaction {
data_address: usize,
sender_euid: Kuid,
txn_security_ctx_off: Option<usize>,
- pub(crate) oneway_spam_detected: bool,
start_time: Instant<Monotonic>,
}
@@ -65,17 +91,16 @@ impl Transaction {
node_ref: NodeRef,
from_parent: Option<DArc<Transaction>>,
from: &Arc<Thread>,
- tr: &BinderTransactionDataSg,
+ info: &mut TransactionInfo,
) -> BinderResult<DLArc<Self>> {
let debug_id = super::next_debug_id();
- let trd = &tr.transaction_data;
let allow_fds = node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0;
let txn_security_ctx = node_ref.node.flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX != 0;
let mut txn_security_ctx_off = if txn_security_ctx { Some(0) } else { None };
let to = node_ref.node.owner.clone();
let mut alloc = match from.copy_transaction_data(
to.clone(),
- tr,
+ info,
debug_id,
allow_fds,
txn_security_ctx_off.as_mut(),
@@ -88,15 +113,14 @@ impl Transaction {
return Err(err);
}
};
- let oneway_spam_detected = alloc.oneway_spam_detected;
- if trd.flags & TF_ONE_WAY != 0 {
+ if info.is_oneway() {
if from_parent.is_some() {
pr_warn!("Oneway transaction should not be in a transaction stack.");
return Err(EINVAL.into());
}
alloc.set_info_oneway_node(node_ref.node.clone());
}
- if trd.flags & TF_CLEAR_BUF != 0 {
+ if info.flags & TF_CLEAR_BUF != 0 {
alloc.set_info_clear_on_drop();
}
let target_node = node_ref.node.clone();
@@ -107,18 +131,17 @@ impl Transaction {
debug_id,
target_node: Some(target_node),
from_parent,
- sender_euid: from.process.task.euid(),
+ sender_euid: Kuid::current_euid(),
from: from.clone(),
to,
- code: trd.code,
- flags: trd.flags,
- data_size: trd.data_size as _,
- offsets_size: trd.offsets_size as _,
+ code: info.code,
+ flags: info.flags,
+ data_size: info.data_size,
+ offsets_size: info.offsets_size,
data_address,
allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"),
is_outstanding: Atomic::new(false),
txn_security_ctx_off,
- oneway_spam_detected,
start_time: Instant::now(),
}))?)
}
@@ -126,39 +149,36 @@ impl Transaction {
pub(crate) fn new_reply(
from: &Arc<Thread>,
to: Arc<Process>,
- tr: &BinderTransactionDataSg,
+ info: &mut TransactionInfo,
allow_fds: bool,
) -> BinderResult<DLArc<Self>> {
let debug_id = super::next_debug_id();
- let trd = &tr.transaction_data;
- let mut alloc = match from.copy_transaction_data(to.clone(), tr, debug_id, allow_fds, None)
- {
- Ok(alloc) => alloc,
- Err(err) => {
- pr_warn!("Failure in copy_transaction_data: {:?}", err);
- return Err(err);
- }
- };
- let oneway_spam_detected = alloc.oneway_spam_detected;
- if trd.flags & TF_CLEAR_BUF != 0 {
+ let mut alloc =
+ match from.copy_transaction_data(to.clone(), info, debug_id, allow_fds, None) {
+ Ok(alloc) => alloc,
+ Err(err) => {
+ pr_warn!("Failure in copy_transaction_data: {:?}", err);
+ return Err(err);
+ }
+ };
+ if info.flags & TF_CLEAR_BUF != 0 {
alloc.set_info_clear_on_drop();
}
Ok(DTRWrap::arc_pin_init(pin_init!(Transaction {
debug_id,
target_node: None,
from_parent: None,
- sender_euid: from.process.task.euid(),
+ sender_euid: Kuid::current_euid(),
from: from.clone(),
to,
- code: trd.code,
- flags: trd.flags,
- data_size: trd.data_size as _,
- offsets_size: trd.offsets_size as _,
+ code: info.code,
+ flags: info.flags,
+ data_size: info.data_size,
+ offsets_size: info.offsets_size,
data_address: alloc.ptr,
allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"),
is_outstanding: Atomic::new(false),
txn_security_ctx_off: None,
- oneway_spam_detected,
start_time: Instant::now(),
}))?)
}
@@ -248,7 +268,7 @@ impl Transaction {
/// stack, otherwise uses the destination process.
///
/// Not used for replies.
- pub(crate) fn submit(self: DLArc<Self>) -> BinderResult {
+ pub(crate) fn submit(self: DLArc<Self>, info: &mut TransactionInfo) -> BinderResult {
// Defined before `process_inner` so that the destructor runs after releasing the lock.
let mut _t_outdated;
@@ -298,6 +318,7 @@ impl Transaction {
}
let res = if let Some(thread) = self.find_target_thread() {
+ info.to_tid = thread.id;
crate::trace::trace_transaction(false, &self, Some(&thread.task));
match thread.push_work(self) {
PushWorkRes::Ok => Ok(()),
@@ -430,6 +451,8 @@ impl DeliverToRead for Transaction {
self.drop_outstanding_txn();
+ crate::trace::trace_transaction_received(&self);
+
// When this is not a reply and not a oneway transaction, update `current_transaction`. If
// it's a reply, `current_transaction` has already been updated appropriately.
if self.target_node.is_some() && tr_sec.transaction_data.flags & TF_ONE_WAY == 0 {