diff options
Diffstat (limited to 'drivers/android')
28 files changed, 162 insertions, 2932 deletions
diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index 606a9d07f774..c920d52c0221 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -1,10 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 menu "Android" -config ANDROID_BINDER_IPC +config ANDROID_BINDER_IPC_RUST bool "Android Binder IPC Driver" depends on MMU depends on NET + depends on RUST default n help Binder is used in Android for both communication between processes, @@ -14,34 +15,9 @@ config ANDROID_BINDER_IPC Android process, using Binder to identify, invoke and pass arguments between said processes. -config ANDROID_BINDER_IPC_RUST - bool "Rust version of Android Binder IPC Driver" - depends on RUST && MMU && NET && !ANDROID_BINDER_IPC - help - This enables the Rust implementation of the Binder driver. - - Binder is used in Android for both communication between processes, - and remote method invocation. - - This means one Android process can call a method/routine in another - Android process, using Binder to identify, invoke and pass arguments - between said processes. - -config ANDROID_BINDERFS - bool "Android Binderfs filesystem" - depends on ANDROID_BINDER_IPC - default n - help - Binderfs is a pseudo-filesystem for the Android Binder IPC driver - which can be mounted per-ipc namespace allowing to run multiple - instances of Android. - Each binderfs mount initially only contains a binder-control device. - It can be used to dynamically allocate new binder IPC devices via - ioctls. - config ANDROID_BINDER_DEVICES string "Android Binder devices" - depends on ANDROID_BINDER_IPC || ANDROID_BINDER_IPC_RUST + depends on ANDROID_BINDER_IPC_RUST default "binder,hwbinder,vndbinder" help Default value for the binder.devices parameter. @@ -51,15 +27,4 @@ config ANDROID_BINDER_DEVICES created. Each binder device has its own context manager, and is therefore logically separated from the other devices. -config ANDROID_BINDER_ALLOC_KUNIT_TEST - tristate "KUnit Tests for Android Binder Alloc" if !KUNIT_ALL_TESTS - depends on ANDROID_BINDER_IPC && KUNIT - default KUNIT_ALL_TESTS - help - This feature builds the binder alloc KUnit tests. - - Each test case runs using a pared-down binder_alloc struct and - test-specific freelist, which allows this KUnit module to be loaded - for testing without interfering with a running system. - endmenu diff --git a/drivers/android/Makefile b/drivers/android/Makefile index e0c650d3898e..f83d39d16f6e 100644 --- a/drivers/android/Makefile +++ b/drivers/android/Makefile @@ -1,7 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only ccflags-y += -I$(src) # needed for trace events -obj-$(CONFIG_ANDROID_BINDERFS) += binderfs.o -obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o binder_netlink.o -obj-$(CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST) += tests/ obj-$(CONFIG_ANDROID_BINDER_IPC_RUST) += binder/ diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 8f2ef1bd539f..bc8bc9ee43a5 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2930,8 +2930,9 @@ static int binder_proc_transaction(struct binder_transaction *t, t_outdated->buffer = NULL; buffer->transaction = NULL; trace_binder_transaction_update_buffer_release(buffer); - binder_release_entire_buffer(proc, NULL, buffer, false); + binder_release_entire_buffer(proc, NULL, buffer, true); binder_alloc_free_buf(&proc->alloc, buffer); + binder_free_txn_fixups(t_outdated); kfree(t_outdated); binder_stats_deleted(BINDER_STAT_TRANSACTION); } diff --git a/drivers/android/binder/Makefile b/drivers/android/binder/Makefile index 7e0cd9782a8b..fe474628a94e 100644 --- a/drivers/android/binder/Makefile +++ b/drivers/android/binder/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only ccflags-y += -I$(src) # needed for trace events -obj-$(CONFIG_ANDROID_BINDER_IPC_RUST) += rust_binder.o -rust_binder-y := \ +obj-$(CONFIG_ANDROID_BINDER_IPC_RUST) += binder.o +binder-y := \ rust_binder_main.o \ rust_binderfs.o \ rust_binder_events.o diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index 165cb797eb1e..2f0763e6868d 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -208,7 +208,7 @@ impl Allocation { let num_close_on_free = files.iter().filter(|entry| entry.close_on_free).count(); let mut close_on_free = KVec::with_capacity(num_close_on_free, GFP_KERNEL)?; - let mut reservations = KVec::with_capacity(files.len(), GFP_KERNEL)?; + let mut reservations = KVVec::with_capacity(files.len(), GFP_KERNEL)?; for file_info in files { let res = FileDescriptorReservation::get_unused_fd_flags(bindings::O_CLOEXEC)?; let fd = res.reserved_fd(); @@ -503,7 +503,7 @@ impl BinderObject { /// The closure should write the bytes for the object into the provided slice. pub(crate) fn read_from_inner<R>(reader: R) -> Result<BinderObject> where - R: FnOnce(&mut [u8; size_of::<BinderObject>()]) -> Result<()>, + R: FnOnce(&mut [u8; size_of::<BinderObject>()]) -> Result, { let mut obj = MaybeUninit::<BinderObject>::zeroed(); @@ -567,7 +567,7 @@ impl BinderObject { #[derive(Default)] struct FileList { - files_to_translate: KVec<FileEntry>, + files_to_translate: KVVec<FileEntry>, close_on_free: KVec<u32>, } @@ -581,7 +581,7 @@ struct FileEntry { } pub(crate) struct TranslatedFds { - reservations: KVec<Reservation>, + reservations: KVVec<Reservation>, /// If commit is called, then these fds should be closed. (If commit is not called, then they /// shouldn't be closed.) close_on_free: FdsCloseOnFree, @@ -595,7 +595,7 @@ struct Reservation { impl TranslatedFds { pub(crate) fn new() -> Self { Self { - reservations: KVec::new(), + reservations: KVVec::new(), close_on_free: FdsCloseOnFree(KVec::new()), } } diff --git a/drivers/android/binder/defs.rs b/drivers/android/binder/defs.rs index 8ac9bdd7a499..cc4becd6e168 100644 --- a/drivers/android/binder/defs.rs +++ b/drivers/android/binder/defs.rs @@ -77,7 +77,8 @@ pub_no_prefix!( TF_ONE_WAY, TF_ACCEPT_FDS, TF_CLEAR_BUF, - TF_UPDATE_TXN + TF_UPDATE_TXN, + TF_DEFER_COMPLETE, ); pub(crate) use uapi::{ diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs index 66912b4cb527..ea2450f3f16f 100644 --- a/drivers/android/binder/freeze.rs +++ b/drivers/android/binder/freeze.rs @@ -155,7 +155,7 @@ impl DeliverToRead for FreezeMessage { } #[inline(never)] - fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> { + fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result { seq_print!(m, "{}has frozen binder\n", prefix); Ok(()) } @@ -177,10 +177,7 @@ impl FreezeListener { } impl Process { - pub(crate) fn request_freeze_notif( - self: &Arc<Self>, - reader: &mut UserSliceReader, - ) -> Result<()> { + pub(crate) fn request_freeze_notif(self: &Arc<Self>, reader: &mut UserSliceReader) -> Result { let hc = reader.read::<BinderHandleCookie>()?; let handle = hc.handle; let cookie = FreezeCookie(hc.cookie); @@ -272,7 +269,7 @@ impl Process { Ok(()) } - pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader) -> Result<()> { + pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader) -> Result { let cookie = FreezeCookie(reader.read()?); let alloc = FreezeMessage::new(GFP_KERNEL)?; let mut node_refs_guard = self.node_refs.lock(); @@ -313,7 +310,7 @@ impl Process { Ok(()) } - pub(crate) fn clear_freeze_notif(self: &Arc<Self>, reader: &mut UserSliceReader) -> Result<()> { + pub(crate) fn clear_freeze_notif(self: &Arc<Self>, reader: &mut UserSliceReader) -> Result { let hc = reader.read::<BinderHandleCookie>()?; let handle = hc.handle; let cookie = FreezeCookie(hc.cookie); diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index 0a82af14cda3..b16ea0ff9ed9 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -51,9 +51,9 @@ pub(crate) struct CouldNotDeliverCriticalIncrement; /// 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 +/// Note that trying to create a `ListArc` to the node can succeed even if `has_pushed_node` 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 +/// yet called `do_work`. However, if `has_pushed_node` is false, then creating a `ListArc` should /// always succeed. /// /// Like the other fields in `NodeInner`, the delivery state is protected by the process lock. @@ -255,11 +255,7 @@ impl Node { } #[inline(never)] - pub(crate) fn full_debug_print( - &self, - m: &SeqFile, - owner_inner: &mut ProcessInner, - ) -> Result<()> { + pub(crate) fn full_debug_print(&self, m: &SeqFile, owner_inner: &mut ProcessInner) -> Result { let inner = self.inner.access_mut(owner_inner); seq_print!( m, @@ -738,14 +734,28 @@ impl DeliverToRead for Node { self.do_work_locked(writer, owner_inner) } - fn cancel(self: DArc<Self>) {} + fn cancel(self: DArc<Self>) { + let _drop_outside_lock; + let mut owner_inner = self.owner.inner.lock(); + + // We only do something on BINDER_THREAD_EXIT, not process exit. + if owner_inner.is_dead { + return; + } + + // If BINDER_THREAD_EXIT is invoked on a thread with a pending node refcount update, we + // should move ourselves to ensure the refcount update is still delivered. + if let Some(node) = ListArc::try_from_arc_borrow(self.as_arc_borrow()) { + _drop_outside_lock = owner_inner.push_work(&self.owner, node); + } + } fn should_sync_wakeup(&self) -> bool { false } #[inline(never)] - fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> { + fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result { seq_print!( m, "{}node work {}: u{:016x} c{:016x}\n", @@ -1140,7 +1150,7 @@ impl DeliverToRead for NodeDeath { } #[inline(never)] - fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> { + fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result { let inner = self.inner.lock(); let dead_binder = inner.dead && !inner.notification_done; diff --git a/drivers/android/binder/node/wrapper.rs b/drivers/android/binder/node/wrapper.rs index 6e4ca01c941a..5a5e5e9883a4 100644 --- a/drivers/android/binder/node/wrapper.rs +++ b/drivers/android/binder/node/wrapper.rs @@ -57,14 +57,46 @@ impl DeliverToRead for NodeWrapper { node.do_work_locked(writer, owner_inner) } - fn cancel(self: DArc<Self>) {} + fn cancel(self: DArc<Self>) { + let _drop_outside_lock; + let node = &self.node; + let mut owner_inner = node.owner.inner.lock(); + + // We only do something on BINDER_THREAD_EXIT, not process exit. + if owner_inner.is_dead { + return; + } + + // We transfer the responsibility of the node refcount update to the scheduled Node because + // NodeWrapper has no way to re-create the ListArc. + let inner = node.inner.access_mut(&mut owner_inner); + + let ds = &mut inner.delivery_state; + assert!(ds.has_pushed_wrapper); + assert!(ds.has_strong_zero2one); + ds.has_pushed_wrapper = false; + + // We are changing the state to one where the Node is the strong zero2one update instead of + // the wrapper. + ds.has_weak_zero2one = false; + + if !ds.has_pushed_node { + if let Some(node2) = ListArc::try_from_arc_borrow(node.as_arc_borrow()) { + ds.has_pushed_node = true; + _drop_outside_lock = owner_inner.push_work(&node.owner, node2); + } else { + // This can't actually happen. + ds.has_strong_zero2one = false; + } + } + } fn should_sync_wakeup(&self) -> bool { false } #[inline(never)] - fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> { + fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result { seq_print!( m, "{}node work {}: u{:016x} c{:016x}\n", diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs index 71febd3d5b07..74c99432f867 100644 --- a/drivers/android/binder/page_range.rs +++ b/drivers/android/binder/page_range.rs @@ -66,7 +66,7 @@ impl Shrinker { } /// Register this shrinker with the kernel. - pub(crate) fn register(&'static self, name: &CStr) -> Result<()> { + pub(crate) fn register(&'static self, name: &CStr) -> Result { // SAFETY: These fields are not yet used, so it's okay to zero them. unsafe { self.inner.get().write(ptr::null_mut()); @@ -352,7 +352,7 @@ impl ShrinkablePageRange { /// Make sure that the given pages are allocated and mapped. /// /// Must not be called from an atomic context. - pub(crate) fn use_range(&self, start: usize, end: usize) -> Result<()> { + pub(crate) fn use_range(&self, start: usize, end: usize) -> Result { if start >= end { return Ok(()); } @@ -398,7 +398,7 @@ impl ShrinkablePageRange { /// /// Assumes that `i` is in bounds. #[cold] - unsafe fn use_page_slow(&self, i: usize) -> Result<()> { + unsafe fn use_page_slow(&self, i: usize) -> Result { let new_page = Page::alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO)?; let mm_mutex = self.mm_lock.lock(); diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 5372bfbd93b3..58c84be8ab77 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -548,7 +548,7 @@ impl Process { } #[inline(never)] - pub(crate) fn debug_print_stats(&self, m: &SeqFile, ctx: &Context) -> Result<()> { + pub(crate) fn debug_print_stats(&self, m: &SeqFile, ctx: &Context) -> Result { seq_print!(m, "proc {}\n", self.pid_in_current_ns()); seq_print!(m, "context {}\n", &*ctx.name); @@ -596,7 +596,7 @@ impl Process { } #[inline(never)] - pub(crate) fn debug_print(&self, m: &SeqFile, ctx: &Context, print_all: bool) -> Result<()> { + pub(crate) fn debug_print(&self, m: &SeqFile, ctx: &Context, print_all: bool) -> Result { seq_print!(m, "proc {}\n", self.pid_in_current_ns()); seq_print!(m, "context {}\n", &*ctx.name); @@ -686,8 +686,16 @@ impl Process { pub(crate) fn get_work_or_register<'a>( &'a self, thread: &'a Arc<Thread>, + thread_has_deferred_work: bool, ) -> GetWorkOrRegister<'a> { let mut inner = self.inner.lock(); + + if thread_has_deferred_work && !inner.work.is_empty() { + if let Some(work) = thread.pop_work_even_if_deferred() { + return GetWorkOrRegister::Work(work); + } + } + // Try to get work from the process queue. if let Some(work) = inner.work.pop_front() { return GetWorkOrRegister::Work(work); @@ -1208,11 +1216,10 @@ impl Process { { let inner = self.inner.lock(); - for (node_ptr, node) in &inner.nodes { - if *node_ptr > ptr { - node.populate_debug_info(&mut out, &inner); - break; - } + // cursor_lower_bound retrieves the "key" passed or the next existing larger key + if let Some(cursor) = inner.nodes.cursor_lower_bound(&(ptr + 1)) { + let (_, node) = cursor.current(); + node.populate_debug_info(&mut out, &inner); } } diff --git a/drivers/android/binder/range_alloc/array.rs b/drivers/android/binder/range_alloc/array.rs index 081d19b09d4b..71bf49f9db0d 100644 --- a/drivers/android/binder/range_alloc/array.rs +++ b/drivers/android/binder/range_alloc/array.rs @@ -61,7 +61,7 @@ impl<T> ArrayRangeAllocator<T> { self.ranges.len() == self.ranges.capacity() } - pub(crate) fn debug_print(&self, m: &SeqFile) -> Result<()> { + pub(crate) fn debug_print(&self, m: &SeqFile) -> Result { for range in &self.ranges { seq_print!( m, diff --git a/drivers/android/binder/range_alloc/mod.rs b/drivers/android/binder/range_alloc/mod.rs index 1f4734468ff1..bb6e0ec9650e 100644 --- a/drivers/android/binder/range_alloc/mod.rs +++ b/drivers/android/binder/range_alloc/mod.rs @@ -141,7 +141,7 @@ impl<T> RangeAllocator<T> { } } - pub(crate) fn debug_print(&self, m: &SeqFile) -> Result<()> { + pub(crate) fn debug_print(&self, m: &SeqFile) -> Result { match &self.inner { Impl::Empty(_size) => Ok(()), Impl::Array(array) => array.debug_print(m), diff --git a/drivers/android/binder/range_alloc/tree.rs b/drivers/android/binder/range_alloc/tree.rs index 48796fcdb362..058ff7498fa5 100644 --- a/drivers/android/binder/range_alloc/tree.rs +++ b/drivers/android/binder/range_alloc/tree.rs @@ -111,7 +111,7 @@ impl<T> TreeRangeAllocator<T> { .count() } - pub(crate) fn debug_print(&self, m: &SeqFile) -> Result<()> { + pub(crate) fn debug_print(&self, m: &SeqFile) -> Result { for desc in self.tree.values() { let state = match &desc.state { Some(state) => &state.0, diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index 955c4c348f73..92bf33f9ff1c 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -81,7 +81,7 @@ mod binderfs { module! { type: BinderModule, - name: "rust_binder", + name: "binder", authors: ["Wedson Almeida Filho", "Alice Ryhl"], description: "Android Binder", license: "GPL", @@ -158,7 +158,7 @@ trait DeliverToRead: ListArcSafe + Send + Sync { /// Generally only set to true for non-oneway transactions. fn should_sync_wakeup(&self) -> bool; - fn debug_print(&self, m: &SeqFile, prefix: &str, transaction_prefix: &str) -> Result<()>; + fn debug_print(&self, m: &SeqFile, prefix: &str, transaction_prefix: &str) -> Result; } // Wrapper around a `DeliverToRead` with linked list links. @@ -274,7 +274,7 @@ impl DeliverToRead for DeliverCode { false } - fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> { + fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result { seq_print!(m, "{}", prefix); if self.skip.load(Relaxed) { seq_print!(m, "(skipped) "); @@ -545,7 +545,7 @@ unsafe extern "C" fn rust_binder_transactions_show( 0 } -fn rust_binder_transactions_show_impl(m: &SeqFile) -> Result<()> { +fn rust_binder_transactions_show_impl(m: &SeqFile) -> Result { seq_print!(m, "binder transactions:\n"); let contexts = context::get_all_contexts()?; for ctx in contexts { @@ -558,7 +558,7 @@ fn rust_binder_transactions_show_impl(m: &SeqFile) -> Result<()> { Ok(()) } -fn rust_binder_stats_show_impl(m: &SeqFile) -> Result<()> { +fn rust_binder_stats_show_impl(m: &SeqFile) -> Result { seq_print!(m, "binder stats:\n"); stats::GLOBAL_STATS.debug_print("", m); let contexts = context::get_all_contexts()?; @@ -572,7 +572,7 @@ fn rust_binder_stats_show_impl(m: &SeqFile) -> Result<()> { Ok(()) } -fn rust_binder_state_show_impl(m: &SeqFile) -> Result<()> { +fn rust_binder_state_show_impl(m: &SeqFile) -> Result { seq_print!(m, "binder state:\n"); let contexts = context::get_all_contexts()?; for ctx in contexts { @@ -585,7 +585,7 @@ fn rust_binder_state_show_impl(m: &SeqFile) -> Result<()> { Ok(()) } -fn rust_binder_proc_show_impl(m: &SeqFile, pid: Pid) -> Result<()> { +fn rust_binder_proc_show_impl(m: &SeqFile, pid: Pid) -> Result { seq_print!(m, "binder proc state:\n"); let contexts = context::get_all_contexts()?; for ctx in contexts { diff --git a/drivers/android/binder/rust_binderfs.c b/drivers/android/binder/rust_binderfs.c index 300cc65562d1..acc83a179f38 100644 --- a/drivers/android/binder/rust_binderfs.c +++ b/drivers/android/binder/rust_binderfs.c @@ -49,7 +49,7 @@ DEFINE_SHOW_ATTRIBUTE(rust_binder_transactions); DEFINE_SHOW_ATTRIBUTE(rust_binder_proc); char *rust_binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES; -module_param_named(rust_devices, rust_binder_devices_param, charp, 0444); +module_param_named(devices, rust_binder_devices_param, charp, 0444); extern u32 rust_binder_debug_mask; module_param_named(debug_mask, rust_binder_debug_mask, uint, 0644); @@ -72,6 +72,7 @@ struct binder_features { bool oneway_spam_detection; bool extended_error; bool freeze_notification; + bool transaction_report; }; static const struct constant_table binderfs_param_stats[] = { @@ -89,6 +90,7 @@ static struct binder_features binder_features = { .oneway_spam_detection = true, .extended_error = true, .freeze_notification = true, + .transaction_report = true, }; static inline struct binderfs_info *BINDERFS_SB(const struct super_block *sb) @@ -566,6 +568,12 @@ static int init_binder_features(struct super_block *sb) if (IS_ERR(dentry)) return PTR_ERR(dentry); + dentry = rust_binderfs_create_file(dir, "transaction_report", + &binder_features_fops, + &binder_features.transaction_report); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + return 0; } diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 18a14aa8a835..edafce806d27 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -481,7 +481,7 @@ impl Thread { } #[inline(never)] - pub(crate) fn debug_print(self: &Arc<Self>, m: &SeqFile, print_all: bool) -> Result<()> { + 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() { @@ -584,9 +584,21 @@ impl Thread { // mangled symbol names. #[export_name = "rust_binder_wait"] fn get_work(self: &Arc<Self>, wait: bool) -> Result<Option<DLArc<dyn DeliverToRead>>> { + let thread_has_deferred_work; + // Try to get work from the thread's work queue, using only a local lock. { let mut inner = self.inner.lock(); + + // The process_work_list boolean is used to make us go to sleep even if there is work + // in the thread todo-list, but it doesn't apply to the process todo-list. Furthermore, + // work in the thread todo-list must still be delivered before the process list. + // + // Thus, in some scenarios we must return the thread work now even if we were requested + // to wait. Adjust `process_work_list` to `true` accordingly. + inner.process_work_list |= inner.looper_need_return; + inner.process_work_list |= !wait; + if let Some(work) = inner.pop_work() { return Ok(Some(work)); } @@ -594,18 +606,26 @@ impl Thread { drop(inner); return Ok(self.process.get_work()); } + + // Note that if the thread list is empty, then the call to `pop_work()` has changed + // `process_work_list` back to `false` even if we set it to `true` above. + thread_has_deferred_work = !inner.work_list.is_empty(); } // If the caller doesn't want to wait, try to grab work from the process queue. // // We know nothing will have been queued directly to the thread queue because it is not in - // a transaction and it is not in the process' ready list. + // a transaction and it is not in the process' ready list. We also know the thread list has + // no deferred work due to the `inner.process_work_list |= !wait` call above. if !wait { return self.process.get_work().ok_or(EAGAIN).map(Some); } // Get work from the process queue. If none is available, atomically register as ready. - let reg = match self.process.get_work_or_register(self) { + let reg = match self + .process + .get_work_or_register(self, thread_has_deferred_work) + { GetWorkOrRegister::Work(work) => return Ok(Some(work)), GetWorkOrRegister::Register(reg) => reg, }; @@ -621,14 +641,18 @@ impl Thread { inner.looper_flags &= !(LooperFlag::Waiting | LooperFlag::WaitingProc); if signal_pending || inner.looper_need_return { - // We need to return now. We need to pull the thread off the list of ready threads - // (by dropping `reg`), then check the state again after it's off the list to - // ensure that something was not queued in the meantime. If something has been - // queued, we just return it (instead of the error). + // We need to return now. + // + // We need to pull the thread off the list of ready threads (by dropping `reg`), + // then check the state again after it's off the list to ensure that something was + // not queued in the meantime. If something has been queued (or if there is + // deferred work), we just return it (instead of the error). drop(inner); drop(reg); - let res = match self.inner.lock().pop_work() { + inner = self.inner.lock(); + inner.process_work_list = true; + let res = match inner.pop_work() { Some(work) => Ok(Some(work)), None if signal_pending => Err(EINTR), None => Ok(None), @@ -686,6 +710,12 @@ impl Thread { self.inner.lock().push_return_work(reply); } + pub(crate) fn pop_work_even_if_deferred(&self) -> Option<DLArc<dyn DeliverToRead>> { + let mut thread_inner = self.inner.lock(); + thread_inner.process_work_list = true; + thread_inner.pop_work() + } + fn translate_object( &self, obj_index: usize, @@ -1250,7 +1280,7 @@ impl Thread { cmd: u32, reader: &mut UserSliceReader, info: &mut TransactionInfo, - ) -> Result<()> { + ) -> Result { let td = match cmd { BC_TRANSACTION | BC_REPLY => { reader.read::<BinderTransactionData>()?.with_buffers_size(0) @@ -1281,7 +1311,7 @@ impl Thread { } #[inline(never)] - fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Result<()> { + fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Result { let mut info = TransactionInfo::zeroed(); self.read_transaction_info(cmd, reader, &mut info)?; @@ -1410,8 +1440,16 @@ impl Thread { let process = orig.from.process.clone(); let allow_fds = orig.flags.contains(TransactionFlag::AcceptFds); let reply = Transaction::new_reply(self, process, info, allow_fds)?; - // Not notifying: Reply to current thread. - let _ = self.inner.lock().push_work(completion); + { + let mut inner = self.inner.lock(); + if info.flags.contains(TransactionFlag::DeferComplete) { + // The flag is set. Perform a deferred push so that `read` can wait for the + // next incoming transaction without a userspace roundtrip. + inner.push_work_deferred(completion); + } else { + let _ = inner.push_work(completion); + } + } orig.from.deliver_reply(Ok(reply), &orig, None); Ok(()) })() @@ -1678,7 +1716,7 @@ impl Thread { self.unwind_transaction_stack(); // Cancel all pending work items. - while let Ok(Some(work)) = self.get_work_local(false) { + while let Some(work) = self.pop_work_even_if_deferred() { work.into_arc().cancel(); } } @@ -1738,7 +1776,7 @@ impl DeliverToRead for ThreadError { false } - fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> { + fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result { seq_print!( m, "{}transaction error: {}\n", diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 245f1556b5db..888a9e0eba8f 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -39,6 +39,7 @@ kernel::impl_flags!( AcceptFds = TF_ACCEPT_FDS, ClearBuf = TF_CLEAR_BUF, UpdateTxn = TF_UPDATE_TXN, + DeferComplete = TF_DEFER_COMPLETE, } ); @@ -576,7 +577,7 @@ impl DeliverToRead for Transaction { !self.flags.is_oneway() } - fn debug_print(&self, m: &SeqFile, _prefix: &str, tprefix: &str) -> Result<()> { + fn debug_print(&self, m: &SeqFile, _prefix: &str, tprefix: &str) -> Result { self.debug_print_inner(m, tprefix); Ok(()) } diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h deleted file mode 100644 index d6f1f6f2d00e..000000000000 --- a/drivers/android/binder_alloc.h +++ /dev/null @@ -1,189 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2017 Google, Inc. - */ - -#ifndef _LINUX_BINDER_ALLOC_H -#define _LINUX_BINDER_ALLOC_H - -#include <linux/rbtree.h> -#include <linux/list.h> -#include <linux/mm.h> -#include <linux/rtmutex.h> -#include <linux/vmalloc.h> -#include <linux/slab.h> -#include <linux/list_lru.h> -#include <uapi/linux/android/binder.h> - -struct binder_transaction; - -/** - * struct binder_buffer - buffer used for binder transactions - * @entry: entry alloc->buffers - * @rb_node: node for allocated_buffers/free_buffers rb trees - * @free: %true if buffer is free - * @clear_on_free: %true if buffer must be zeroed after use - * @allow_user_free: %true if user is allowed to free buffer - * @async_transaction: %true if buffer is in use for an async txn - * @oneway_spam_suspect: %true if total async allocate size just exceed - * spamming detect threshold - * @debug_id: unique ID for debugging - * @transaction: pointer to associated struct binder_transaction - * @target_node: struct binder_node associated with this buffer - * @data_size: size of @transaction data - * @offsets_size: size of array of offsets - * @extra_buffers_size: size of space for other objects (like sg lists) - * @user_data: user pointer to base of buffer space - * @pid: pid to attribute the buffer to (caller) - * - * Bookkeeping structure for binder transaction buffers - */ -struct binder_buffer { - struct list_head entry; /* free and allocated entries by address */ - struct rb_node rb_node; /* free entry by size or allocated entry */ - /* by address */ - unsigned free:1; - unsigned clear_on_free:1; - unsigned allow_user_free:1; - unsigned async_transaction:1; - unsigned oneway_spam_suspect:1; - unsigned debug_id:27; - struct binder_transaction *transaction; - struct binder_node *target_node; - size_t data_size; - size_t offsets_size; - size_t extra_buffers_size; - unsigned long user_data; - int pid; -}; - -/** - * struct binder_shrinker_mdata - binder metadata used to reclaim pages - * @lru: LRU entry in binder_freelist - * @alloc: binder_alloc owning the page to reclaim - * @page_index: offset in @alloc->pages[] into the page to reclaim - */ -struct binder_shrinker_mdata { - struct list_head lru; - struct binder_alloc *alloc; - unsigned long page_index; -}; - -static inline struct list_head *page_to_lru(struct page *p) -{ - struct binder_shrinker_mdata *mdata; - - mdata = (struct binder_shrinker_mdata *)page_private(p); - - return &mdata->lru; -} - -/** - * struct binder_alloc - per-binder proc state for binder allocator - * @mutex: protects binder_alloc fields - * @mm: copy of task->mm (invariant after open) - * @vm_start: base of per-proc address space mapped via mmap - * @buffers: list of all buffers for this proc - * @free_buffers: rb tree of buffers available for allocation - * sorted by size - * @allocated_buffers: rb tree of allocated buffers sorted by address - * @free_async_space: VA space available for async buffers. This is - * initialized at mmap time to 1/2 the full VA space - * @pages: array of struct page * - * @freelist: lru list to use for free pages (invariant after init) - * @buffer_size: size of address space specified via mmap - * @pid: pid for associated binder_proc (invariant after init) - * @pages_high: high watermark of offset in @pages - * @mapped: whether the vm area is mapped, each binder instance is - * allowed a single mapping throughout its lifetime - * @oneway_spam_detected: %true if oneway spam detection fired, clear that - * flag once the async buffer has returned to a healthy state - * - * Bookkeeping structure for per-proc address space management for binder - * buffers. It is normally initialized during binder_init() and binder_mmap() - * calls. The address space is used for both user-visible buffers and for - * struct binder_buffer objects used to track the user buffers - */ -struct binder_alloc { - struct mutex mutex; - struct mm_struct *mm; - unsigned long vm_start; - struct list_head buffers; - struct rb_root free_buffers; - struct rb_root allocated_buffers; - size_t free_async_space; - struct page **pages; - struct list_lru *freelist; - size_t buffer_size; - int pid; - size_t pages_high; - bool mapped; - bool oneway_spam_detected; -}; - -enum lru_status binder_alloc_free_page(struct list_head *item, - struct list_lru_one *lru, - void *cb_arg); -struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, - size_t data_size, - size_t offsets_size, - size_t extra_buffers_size, - int is_async); -void binder_alloc_init(struct binder_alloc *alloc); -int binder_alloc_shrinker_init(void); -void binder_alloc_shrinker_exit(void); -void binder_alloc_vma_close(struct binder_alloc *alloc); -struct binder_buffer * -binder_alloc_prepare_to_free(struct binder_alloc *alloc, - unsigned long user_ptr); -void binder_alloc_free_buf(struct binder_alloc *alloc, - struct binder_buffer *buffer); -int binder_alloc_mmap_handler(struct binder_alloc *alloc, - struct vm_area_struct *vma); -void binder_alloc_deferred_release(struct binder_alloc *alloc); -int binder_alloc_get_allocated_count(struct binder_alloc *alloc); -void binder_alloc_print_allocated(struct seq_file *m, - struct binder_alloc *alloc); -void binder_alloc_print_pages(struct seq_file *m, - struct binder_alloc *alloc); - -/** - * binder_alloc_get_free_async_space() - get free space available for async - * @alloc: binder_alloc for this proc - * - * Return: the bytes remaining in the address-space for async transactions - */ -static inline size_t -binder_alloc_get_free_async_space(struct binder_alloc *alloc) -{ - guard(mutex)(&alloc->mutex); - return alloc->free_async_space; -} - -unsigned long -binder_alloc_copy_user_to_buffer(struct binder_alloc *alloc, - struct binder_buffer *buffer, - binder_size_t buffer_offset, - const void __user *from, - size_t bytes); - -int binder_alloc_copy_to_buffer(struct binder_alloc *alloc, - struct binder_buffer *buffer, - binder_size_t buffer_offset, - void *src, - size_t bytes); - -int binder_alloc_copy_from_buffer(struct binder_alloc *alloc, - void *dest, - struct binder_buffer *buffer, - binder_size_t buffer_offset, - size_t bytes); - -#if IS_ENABLED(CONFIG_KUNIT) -void __binder_alloc_init(struct binder_alloc *alloc, struct list_lru *freelist); -size_t binder_alloc_buffer_size(struct binder_alloc *alloc, - struct binder_buffer *buffer); -#endif - -#endif /* _LINUX_BINDER_ALLOC_H */ - diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h deleted file mode 100644 index 342574bfd28a..000000000000 --- a/drivers/android/binder_internal.h +++ /dev/null @@ -1,597 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef _LINUX_BINDER_INTERNAL_H -#define _LINUX_BINDER_INTERNAL_H - -#include <linux/fs.h> -#include <linux/list.h> -#include <linux/miscdevice.h> -#include <linux/mutex.h> -#include <linux/refcount.h> -#include <linux/stddef.h> -#include <linux/types.h> -#include <linux/uidgid.h> -#include <uapi/linux/android/binderfs.h> -#include "binder_alloc.h" -#include "dbitmap.h" - -struct binder_context { - struct binder_node *binder_context_mgr_node; - struct mutex context_mgr_node_lock; - kuid_t binder_context_mgr_uid; - const char *name; -}; - -/** - * struct binder_device - information about a binder device node - * @hlist: list of binder devices - * @miscdev: information about a binder character device node - * @context: binder context information - * @binderfs_inode: This is the inode of the root dentry of the super block - * belonging to a binderfs mount. - */ -struct binder_device { - struct hlist_node hlist; - struct miscdevice miscdev; - struct binder_context context; - struct inode *binderfs_inode; - refcount_t ref; -}; - -/** - * binderfs_mount_opts - mount options for binderfs - * @max: maximum number of allocatable binderfs binder devices - * @stats_mode: enable binder stats in binderfs. - */ -struct binderfs_mount_opts { - int max; - int stats_mode; -}; - -/** - * binderfs_info - information about a binderfs mount - * @ipc_ns: The ipc namespace the binderfs mount belongs to. - * @control_dentry: This records the dentry of this binderfs mount - * binder-control device. - * @root_uid: uid that needs to be used when a new binder device is - * created. - * @root_gid: gid that needs to be used when a new binder device is - * created. - * @mount_opts: The mount options in use. - * @device_count: The current number of allocated binder devices. - * @proc_log_dir: Pointer to the directory dentry containing process-specific - * logs. - */ -struct binderfs_info { - struct ipc_namespace *ipc_ns; - struct dentry *control_dentry; - kuid_t root_uid; - kgid_t root_gid; - struct binderfs_mount_opts mount_opts; - int device_count; - struct dentry *proc_log_dir; -}; - -extern const struct file_operations binder_fops; - -extern char *binder_devices_param; - -#ifdef CONFIG_ANDROID_BINDERFS -extern bool is_binderfs_device(const struct inode *inode); -extern struct dentry *binderfs_create_file(struct dentry *dir, const char *name, - const struct file_operations *fops, - void *data); -#else -static inline bool is_binderfs_device(const struct inode *inode) -{ - return false; -} -static inline struct dentry *binderfs_create_file(struct dentry *dir, - const char *name, - const struct file_operations *fops, - void *data) -{ - return NULL; -} -#endif - -#ifdef CONFIG_ANDROID_BINDERFS -extern int __init init_binderfs(void); -#else -static inline int __init init_binderfs(void) -{ - return 0; -} -#endif - -struct binder_debugfs_entry { - const char *name; - umode_t mode; - const struct file_operations *fops; - void *data; -}; - -extern const struct binder_debugfs_entry binder_debugfs_entries[]; - -#define binder_for_each_debugfs_entry(entry) \ - for ((entry) = binder_debugfs_entries; \ - (entry)->name; \ - (entry)++) - -enum binder_stat_types { - BINDER_STAT_PROC, - BINDER_STAT_THREAD, - BINDER_STAT_NODE, - BINDER_STAT_REF, - BINDER_STAT_DEATH, - BINDER_STAT_TRANSACTION, - BINDER_STAT_TRANSACTION_COMPLETE, - BINDER_STAT_FREEZE, - BINDER_STAT_COUNT -}; - -struct binder_stats { - atomic_t br[_IOC_NR(BR_CLEAR_FREEZE_NOTIFICATION_DONE) + 1]; - atomic_t bc[_IOC_NR(BC_FREEZE_NOTIFICATION_DONE) + 1]; - atomic_t obj_created[BINDER_STAT_COUNT]; - atomic_t obj_deleted[BINDER_STAT_COUNT]; -}; - -/** - * struct binder_work - work enqueued on a worklist - * @entry: node enqueued on list - * @type: type of work to be performed - * - * There are separate work lists for proc, thread, and node (async). - */ -struct binder_work { - struct list_head entry; - - enum binder_work_type { - BINDER_WORK_TRANSACTION = 1, - BINDER_WORK_TRANSACTION_COMPLETE, - BINDER_WORK_TRANSACTION_PENDING, - BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT, - BINDER_WORK_RETURN_ERROR, - BINDER_WORK_NODE, - BINDER_WORK_DEAD_BINDER, - BINDER_WORK_DEAD_BINDER_AND_CLEAR, - BINDER_WORK_CLEAR_DEATH_NOTIFICATION, - BINDER_WORK_FROZEN_BINDER, - BINDER_WORK_CLEAR_FREEZE_NOTIFICATION, - } type; -}; - -struct binder_error { - struct binder_work work; - uint32_t cmd; -}; - -/** - * struct binder_node - binder node bookkeeping - * @debug_id: unique ID for debugging - * (invariant after initialized) - * @lock: lock for node fields - * @work: worklist element for node work - * (protected by @proc->inner_lock) - * @rb_node: element for proc->nodes tree - * (protected by @proc->inner_lock) - * @dead_node: element for binder_dead_nodes list - * (protected by binder_dead_nodes_lock) - * @proc: binder_proc that owns this node - * (invariant after initialized) - * @refs: list of references on this node - * (protected by @lock) - * @internal_strong_refs: used to take strong references when - * initiating a transaction - * (protected by @proc->inner_lock if @proc - * and by @lock) - * @local_weak_refs: weak user refs from local process - * (protected by @proc->inner_lock if @proc - * and by @lock) - * @local_strong_refs: strong user refs from local process - * (protected by @proc->inner_lock if @proc - * and by @lock) - * @tmp_refs: temporary kernel refs - * (protected by @proc->inner_lock while @proc - * is valid, and by binder_dead_nodes_lock - * if @proc is NULL. During inc/dec and node release - * it is also protected by @lock to provide safety - * as the node dies and @proc becomes NULL) - * @ptr: userspace pointer for node - * (invariant, no lock needed) - * @cookie: userspace cookie for node - * (invariant, no lock needed) - * @has_strong_ref: userspace notified of strong ref - * (protected by @proc->inner_lock if @proc - * and by @lock) - * @pending_strong_ref: userspace has acked notification of strong ref - * (protected by @proc->inner_lock if @proc - * and by @lock) - * @has_weak_ref: userspace notified of weak ref - * (protected by @proc->inner_lock if @proc - * and by @lock) - * @pending_weak_ref: userspace has acked notification of weak ref - * (protected by @proc->inner_lock if @proc - * and by @lock) - * @has_async_transaction: async transaction to node in progress - * (protected by @lock) - * @accept_fds: file descriptor operations supported for node - * (invariant after initialized) - * @min_priority: minimum scheduling priority - * (invariant after initialized) - * @txn_security_ctx: require sender's security context - * (invariant after initialized) - * @async_todo: list of async work items - * (protected by @proc->inner_lock) - * - * Bookkeeping structure for binder nodes. - */ -struct binder_node { - int debug_id; - spinlock_t lock; - struct binder_work work; - union { - struct rb_node rb_node; - struct hlist_node dead_node; - }; - struct binder_proc *proc; - struct hlist_head refs; - int internal_strong_refs; - int local_weak_refs; - int local_strong_refs; - int tmp_refs; - binder_uintptr_t ptr; - binder_uintptr_t cookie; - struct { - /* - * bitfield elements protected by - * proc inner_lock - */ - u8 has_strong_ref:1; - u8 pending_strong_ref:1; - u8 has_weak_ref:1; - u8 pending_weak_ref:1; - }; - struct { - /* - * invariant after initialization - */ - u8 accept_fds:1; - u8 txn_security_ctx:1; - u8 min_priority; - }; - bool has_async_transaction; - struct list_head async_todo; -}; - -struct binder_ref_death { - /** - * @work: worklist element for death notifications - * (protected by inner_lock of the proc that - * this ref belongs to) - */ - struct binder_work work; - binder_uintptr_t cookie; -}; - -struct binder_ref_freeze { - struct binder_work work; - binder_uintptr_t cookie; - bool is_frozen:1; - bool sent:1; - bool resend:1; -}; - -/** - * struct binder_ref_data - binder_ref counts and id - * @debug_id: unique ID for the ref - * @desc: unique userspace handle for ref - * @strong: strong ref count (debugging only if not locked) - * @weak: weak ref count (debugging only if not locked) - * - * Structure to hold ref count and ref id information. Since - * the actual ref can only be accessed with a lock, this structure - * is used to return information about the ref to callers of - * ref inc/dec functions. - */ -struct binder_ref_data { - int debug_id; - uint32_t desc; - int strong; - int weak; -}; - -/** - * struct binder_ref - struct to track references on nodes - * @data: binder_ref_data containing id, handle, and current refcounts - * @rb_node_desc: node for lookup by @data.desc in proc's rb_tree - * @rb_node_node: node for lookup by @node in proc's rb_tree - * @node_entry: list entry for node->refs list in target node - * (protected by @node->lock) - * @proc: binder_proc containing ref - * @node: binder_node of target node. When cleaning up a - * ref for deletion in binder_cleanup_ref, a non-NULL - * @node indicates the node must be freed - * @death: pointer to death notification (ref_death) if requested - * (protected by @node->lock) - * @freeze: pointer to freeze notification (ref_freeze) if requested - * (protected by @node->lock) - * - * Structure to track references from procA to target node (on procB). This - * structure is unsafe to access without holding @proc->outer_lock. - */ -struct binder_ref { - /* Lookups needed: */ - /* node + proc => ref (transaction) */ - /* desc + proc => ref (transaction, inc/dec ref) */ - /* node => refs + procs (proc exit) */ - struct binder_ref_data data; - struct rb_node rb_node_desc; - struct rb_node rb_node_node; - struct hlist_node node_entry; - struct binder_proc *proc; - struct binder_node *node; - struct binder_ref_death *death; - struct binder_ref_freeze *freeze; -}; - -/** - * struct binder_proc - binder process bookkeeping - * @proc_node: element for binder_procs list - * @threads: rbtree of binder_threads in this proc - * (protected by @inner_lock) - * @nodes: rbtree of binder nodes associated with - * this proc ordered by node->ptr - * (protected by @inner_lock) - * @refs_by_desc: rbtree of refs ordered by ref->desc - * (protected by @outer_lock) - * @refs_by_node: rbtree of refs ordered by ref->node - * (protected by @outer_lock) - * @waiting_threads: threads currently waiting for proc work - * (protected by @inner_lock) - * @pid PID of group_leader of process - * (invariant after initialized) - * @tsk task_struct for group_leader of process - * (invariant after initialized) - * @cred struct cred associated with the `struct file` - * in binder_open() - * (invariant after initialized) - * @deferred_work_node: element for binder_deferred_list - * (protected by binder_deferred_lock) - * @deferred_work: bitmap of deferred work to perform - * (protected by binder_deferred_lock) - * @outstanding_txns: number of transactions to be transmitted before - * processes in freeze_wait are woken up - * (protected by @inner_lock) - * @is_dead: process is dead and awaiting free - * when outstanding transactions are cleaned up - * (protected by @inner_lock) - * @is_frozen: process is frozen and unable to service - * binder transactions - * (protected by @inner_lock) - * @sync_recv: process received sync transactions since last frozen - * bit 0: received sync transaction after being frozen - * bit 1: new pending sync transaction during freezing - * (protected by @inner_lock) - * @async_recv: process received async transactions since last frozen - * (protected by @inner_lock) - * @freeze_wait: waitqueue of processes waiting for all outstanding - * transactions to be processed - * (protected by @inner_lock) - * @dmap dbitmap to manage available reference descriptors - * (protected by @outer_lock) - * @todo: list of work for this process - * (protected by @inner_lock) - * @stats: per-process binder statistics - * (atomics, no lock needed) - * @delivered_death: list of delivered death notification - * (protected by @inner_lock) - * @delivered_freeze: list of delivered freeze notification - * (protected by @inner_lock) - * @max_threads: cap on number of binder threads - * (protected by @inner_lock) - * @requested_threads: number of binder threads requested but not - * yet started. In current implementation, can - * only be 0 or 1. - * (protected by @inner_lock) - * @requested_threads_started: number binder threads started - * (protected by @inner_lock) - * @tmp_ref: temporary reference to indicate proc is in use - * (protected by @inner_lock) - * @default_priority: default scheduler priority - * (invariant after initialized) - * @debugfs_entry: debugfs node - * @alloc: binder allocator bookkeeping - * @context: binder_context for this proc - * (invariant after initialized) - * @inner_lock: can nest under outer_lock and/or node lock - * @outer_lock: no nesting under innor or node lock - * Lock order: 1) outer, 2) node, 3) inner - * @binderfs_entry: process-specific binderfs log file - * @oneway_spam_detection_enabled: process enabled oneway spam detection - * or not - * - * Bookkeeping structure for binder processes - */ -struct binder_proc { - struct hlist_node proc_node; - struct rb_root threads; - struct rb_root nodes; - struct rb_root refs_by_desc; - struct rb_root refs_by_node; - struct list_head waiting_threads; - int pid; - struct task_struct *tsk; - const struct cred *cred; - struct hlist_node deferred_work_node; - int deferred_work; - int outstanding_txns; - bool is_dead; - bool is_frozen; - bool sync_recv; - bool async_recv; - wait_queue_head_t freeze_wait; - struct dbitmap dmap; - struct list_head todo; - struct binder_stats stats; - struct list_head delivered_death; - struct list_head delivered_freeze; - u32 max_threads; - int requested_threads; - int requested_threads_started; - int tmp_ref; - long default_priority; - struct dentry *debugfs_entry; - struct binder_alloc alloc; - struct binder_context *context; - spinlock_t inner_lock; - spinlock_t outer_lock; - struct dentry *binderfs_entry; - bool oneway_spam_detection_enabled; -}; - -/** - * struct binder_thread - binder thread bookkeeping - * @proc: binder process for this thread - * (invariant after initialization) - * @rb_node: element for proc->threads rbtree - * (protected by @proc->inner_lock) - * @waiting_thread_node: element for @proc->waiting_threads list - * (protected by @proc->inner_lock) - * @pid: PID for this thread - * (invariant after initialization) - * @looper: bitmap of looping state - * (only accessed by this thread) - * @looper_needs_return: looping thread needs to exit driver - * (no lock needed) - * @transaction_stack: stack of in-progress transactions for this thread - * (protected by @proc->inner_lock) - * @todo: list of work to do for this thread - * (protected by @proc->inner_lock) - * @process_todo: whether work in @todo should be processed - * (protected by @proc->inner_lock) - * @return_error: transaction errors reported by this thread - * (only accessed by this thread) - * @reply_error: transaction errors reported by target thread - * (protected by @proc->inner_lock) - * @ee: extended error information from this thread - * (protected by @proc->inner_lock) - * @wait: wait queue for thread work - * @stats: per-thread statistics - * (atomics, no lock needed) - * @tmp_ref: temporary reference to indicate thread is in use - * (atomic since @proc->inner_lock cannot - * always be acquired) - * @is_dead: thread is dead and awaiting free - * when outstanding transactions are cleaned up - * (protected by @proc->inner_lock) - * - * Bookkeeping structure for binder threads. - */ -struct binder_thread { - struct binder_proc *proc; - struct rb_node rb_node; - struct list_head waiting_thread_node; - int pid; - int looper; /* only modified by this thread */ - bool looper_need_return; /* can be written by other thread */ - struct binder_transaction *transaction_stack; - struct list_head todo; - bool process_todo; - struct binder_error return_error; - struct binder_error reply_error; - struct binder_extended_error ee; - wait_queue_head_t wait; - struct binder_stats stats; - atomic_t tmp_ref; - bool is_dead; -}; - -/** - * struct binder_txn_fd_fixup - transaction fd fixup list element - * @fixup_entry: list entry - * @file: struct file to be associated with new fd - * @offset: offset in buffer data to this fixup - * @target_fd: fd to use by the target to install @file - * - * List element for fd fixups in a transaction. Since file - * descriptors need to be allocated in the context of the - * target process, we pass each fd to be processed in this - * struct. - */ -struct binder_txn_fd_fixup { - struct list_head fixup_entry; - struct file *file; - size_t offset; - int target_fd; -}; - -struct binder_transaction { - int debug_id; - struct binder_work work; - struct binder_thread *from; - pid_t from_pid; - pid_t from_tid; - struct binder_transaction *from_parent; - struct binder_proc *to_proc; - struct binder_thread *to_thread; - struct binder_transaction *to_parent; - unsigned is_async:1; - unsigned is_reply:1; - - struct binder_buffer *buffer; - unsigned int code; - unsigned int flags; - long priority; - long saved_priority; - kuid_t sender_euid; - ktime_t start_time; - struct list_head fd_fixups; - binder_uintptr_t security_ctx; - /** - * @lock: protects @from, @to_proc, and @to_thread - * - * @from, @to_proc, and @to_thread can be set to NULL - * during thread teardown - */ - spinlock_t lock; -}; - -/** - * struct binder_object - union of flat binder object types - * @hdr: generic object header - * @fbo: binder object (nodes and refs) - * @fdo: file descriptor object - * @bbo: binder buffer pointer - * @fdao: file descriptor array - * - * Used for type-independent object copies - */ -struct binder_object { - union { - struct binder_object_header hdr; - struct flat_binder_object fbo; - struct binder_fd_object fdo; - struct binder_buffer_object bbo; - struct binder_fd_array_object fdao; - }; -}; - -/** - * Add a binder device to binder_devices - * @device: the new binder device to add to the global list - */ -void binder_add_device(struct binder_device *device); - -/** - * Remove a binder device to binder_devices - * @device: the binder device to remove from the global list - */ -void binder_remove_device(struct binder_device *device); - -#if IS_ENABLED(CONFIG_KUNIT) -vm_fault_t binder_vm_fault(struct vm_fault *vmf); -#endif - -#endif /* _LINUX_BINDER_INTERNAL_H */ diff --git a/drivers/android/binder_netlink.c b/drivers/android/binder_netlink.c deleted file mode 100644 index 81e8432b5904..000000000000 --- a/drivers/android/binder_netlink.c +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) -/* Do not edit directly, auto-generated from: */ -/* Documentation/netlink/specs/binder.yaml */ -/* YNL-GEN kernel source */ -/* To regenerate run: tools/net/ynl/ynl-regen.sh */ - -#include <net/netlink.h> -#include <net/genetlink.h> - -#include "binder_netlink.h" - -#include <uapi/linux/android/binder_netlink.h> - -/* Ops table for binder */ -static const struct genl_split_ops binder_nl_ops[] = { -}; - -static const struct genl_multicast_group binder_nl_mcgrps[] = { - [BINDER_NLGRP_REPORT] = { "report", }, -}; - -struct genl_family binder_nl_family __ro_after_init = { - .name = BINDER_FAMILY_NAME, - .version = BINDER_FAMILY_VERSION, - .netnsok = true, - .parallel_ops = true, - .module = THIS_MODULE, - .split_ops = binder_nl_ops, - .n_split_ops = ARRAY_SIZE(binder_nl_ops), - .mcgrps = binder_nl_mcgrps, - .n_mcgrps = ARRAY_SIZE(binder_nl_mcgrps), -}; diff --git a/drivers/android/binder_netlink.h b/drivers/android/binder_netlink.h deleted file mode 100644 index 57399942a5e3..000000000000 --- a/drivers/android/binder_netlink.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ -/* Do not edit directly, auto-generated from: */ -/* Documentation/netlink/specs/binder.yaml */ -/* YNL-GEN kernel header */ -/* To regenerate run: tools/net/ynl/ynl-regen.sh */ - -#ifndef _LINUX_BINDER_GEN_H -#define _LINUX_BINDER_GEN_H - -#include <net/netlink.h> -#include <net/genetlink.h> - -#include <uapi/linux/android/binder_netlink.h> - -enum { - BINDER_NLGRP_REPORT, -}; - -extern struct genl_family binder_nl_family; - -#endif /* _LINUX_BINDER_GEN_H */ diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h deleted file mode 100644 index fa5eb61cf580..000000000000 --- a/drivers/android/binder_trace.h +++ /dev/null @@ -1,448 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2012 Google, Inc. - */ - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM binder - -#if !defined(_BINDER_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) -#define _BINDER_TRACE_H - -#include <linux/tracepoint.h> - -struct binder_buffer; -struct binder_node; -struct binder_proc; -struct binder_alloc; -struct binder_ref_data; -struct binder_thread; -struct binder_transaction; - -TRACE_EVENT(binder_ioctl, - TP_PROTO(unsigned int cmd, unsigned long arg), - TP_ARGS(cmd, arg), - - TP_STRUCT__entry( - __field(unsigned int, cmd) - __field(unsigned long, arg) - ), - TP_fast_assign( - __entry->cmd = cmd; - __entry->arg = arg; - ), - TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg) -); - -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_BINDER_FUNCTION_RETURN_EVENT(name) \ -DEFINE_EVENT(binder_function_return_class, name, \ - TP_PROTO(int ret), \ - TP_ARGS(ret)) - -DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_ioctl_done); -DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_write_done); -DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_read_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_txn_latency_free, - TP_PROTO(struct binder_transaction *t, - int from_proc, int from_thread, - int to_proc, int to_thread), - TP_ARGS(t, from_proc, from_thread, to_proc, to_thread), - TP_STRUCT__entry( - __field(int, debug_id) - __field(int, from_proc) - __field(int, from_thread) - __field(int, to_proc) - __field(int, to_thread) - __field(unsigned int, code) - __field(unsigned int, flags) - ), - TP_fast_assign( - __entry->debug_id = t->debug_id; - __entry->from_proc = from_proc; - __entry->from_thread = from_thread; - __entry->to_proc = to_proc; - __entry->to_thread = to_thread; - __entry->code = t->code; - __entry->flags = t->flags; - ), - TP_printk("transaction=%d from %d:%d to %d:%d flags=0x%x code=0x%x", - __entry->debug_id, __entry->from_proc, __entry->from_thread, - __entry->to_proc, __entry->to_thread, __entry->code, - __entry->flags) -); - -TRACE_EVENT(binder_transaction, - TP_PROTO(bool reply, struct binder_transaction *t, - struct binder_node *target_node), - TP_ARGS(reply, t, target_node), - TP_STRUCT__entry( - __field(int, debug_id) - __field(int, target_node) - __field(int, to_proc) - __field(int, to_thread) - __field(int, reply) - __field(unsigned int, code) - __field(unsigned int, flags) - ), - TP_fast_assign( - __entry->debug_id = t->debug_id; - __entry->target_node = target_node ? target_node->debug_id : 0; - __entry->to_proc = t->to_proc->pid; - __entry->to_thread = t->to_thread ? t->to_thread->pid : 0; - __entry->reply = reply; - __entry->code = t->code; - __entry->flags = t->flags; - ), - TP_printk("transaction=%d dest_node=%d dest_proc=%d dest_thread=%d reply=%d flags=0x%x code=0x%x", - __entry->debug_id, __entry->target_node, - __entry->to_proc, __entry->to_thread, - __entry->reply, __entry->flags, __entry->code) -); - -TRACE_EVENT(binder_transaction_received, - TP_PROTO(struct binder_transaction *t), - TP_ARGS(t), - - TP_STRUCT__entry( - __field(int, debug_id) - ), - TP_fast_assign( - __entry->debug_id = t->debug_id; - ), - TP_printk("transaction=%d", __entry->debug_id) -); - -TRACE_EVENT(binder_transaction_node_to_ref, - TP_PROTO(struct binder_transaction *t, struct binder_node *node, - struct binder_ref_data *rdata), - TP_ARGS(t, node, rdata), - - TP_STRUCT__entry( - __field(int, debug_id) - __field(int, node_debug_id) - __field(binder_uintptr_t, node_ptr) - __field(int, ref_debug_id) - __field(uint32_t, ref_desc) - ), - TP_fast_assign( - __entry->debug_id = t->debug_id; - __entry->node_debug_id = node->debug_id; - __entry->node_ptr = node->ptr; - __entry->ref_debug_id = rdata->debug_id; - __entry->ref_desc = rdata->desc; - ), - TP_printk("transaction=%d node=%d src_ptr=0x%016llx ==> dest_ref=%d dest_desc=%d", - __entry->debug_id, __entry->node_debug_id, - (u64)__entry->node_ptr, - __entry->ref_debug_id, __entry->ref_desc) -); - -TRACE_EVENT(binder_transaction_ref_to_node, - TP_PROTO(struct binder_transaction *t, struct binder_node *node, - struct binder_ref_data *rdata), - TP_ARGS(t, node, rdata), - - TP_STRUCT__entry( - __field(int, debug_id) - __field(int, ref_debug_id) - __field(uint32_t, ref_desc) - __field(int, node_debug_id) - __field(binder_uintptr_t, node_ptr) - ), - TP_fast_assign( - __entry->debug_id = t->debug_id; - __entry->ref_debug_id = rdata->debug_id; - __entry->ref_desc = rdata->desc; - __entry->node_debug_id = node->debug_id; - __entry->node_ptr = node->ptr; - ), - TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ptr=0x%016llx", - __entry->debug_id, __entry->node_debug_id, - __entry->ref_debug_id, __entry->ref_desc, - (u64)__entry->node_ptr) -); - -TRACE_EVENT(binder_transaction_ref_to_ref, - TP_PROTO(struct binder_transaction *t, struct binder_node *node, - struct binder_ref_data *src_ref, - struct binder_ref_data *dest_ref), - TP_ARGS(t, node, src_ref, dest_ref), - - TP_STRUCT__entry( - __field(int, debug_id) - __field(int, node_debug_id) - __field(int, src_ref_debug_id) - __field(uint32_t, src_ref_desc) - __field(int, dest_ref_debug_id) - __field(uint32_t, dest_ref_desc) - ), - TP_fast_assign( - __entry->debug_id = t->debug_id; - __entry->node_debug_id = node->debug_id; - __entry->src_ref_debug_id = src_ref->debug_id; - __entry->src_ref_desc = src_ref->desc; - __entry->dest_ref_debug_id = dest_ref->debug_id; - __entry->dest_ref_desc = dest_ref->desc; - ), - TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ref=%d dest_desc=%d", - __entry->debug_id, __entry->node_debug_id, - __entry->src_ref_debug_id, __entry->src_ref_desc, - __entry->dest_ref_debug_id, __entry->dest_ref_desc) -); - -TRACE_EVENT(binder_transaction_fd_send, - TP_PROTO(struct binder_transaction *t, int fd, size_t offset), - TP_ARGS(t, 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(struct binder_transaction *t, int fd, size_t offset), - TP_ARGS(t, 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) -); - -DECLARE_EVENT_CLASS(binder_buffer_class, - TP_PROTO(struct binder_buffer *buf), - TP_ARGS(buf), - TP_STRUCT__entry( - __field(int, debug_id) - __field(size_t, data_size) - __field(size_t, offsets_size) - __field(size_t, extra_buffers_size) - ), - TP_fast_assign( - __entry->debug_id = buf->debug_id; - __entry->data_size = buf->data_size; - __entry->offsets_size = buf->offsets_size; - __entry->extra_buffers_size = buf->extra_buffers_size; - ), - TP_printk("transaction=%d data_size=%zd offsets_size=%zd extra_buffers_size=%zd", - __entry->debug_id, __entry->data_size, __entry->offsets_size, - __entry->extra_buffers_size) -); - -DEFINE_EVENT(binder_buffer_class, binder_transaction_alloc_buf, - TP_PROTO(struct binder_buffer *buffer), - TP_ARGS(buffer)); - -DEFINE_EVENT(binder_buffer_class, binder_transaction_buffer_release, - TP_PROTO(struct binder_buffer *buffer), - TP_ARGS(buffer)); - -DEFINE_EVENT(binder_buffer_class, binder_transaction_failed_buffer_release, - TP_PROTO(struct binder_buffer *buffer), - TP_ARGS(buffer)); - -DEFINE_EVENT(binder_buffer_class, binder_transaction_update_buffer_release, - TP_PROTO(struct binder_buffer *buffer), - TP_ARGS(buffer)); - -TRACE_EVENT(binder_update_page_range, - TP_PROTO(struct binder_alloc *alloc, bool allocate, - unsigned long start, unsigned long end), - TP_ARGS(alloc, allocate, start, end), - TP_STRUCT__entry( - __field(int, proc) - __field(bool, allocate) - __field(size_t, offset) - __field(size_t, size) - ), - TP_fast_assign( - __entry->proc = alloc->pid; - __entry->allocate = allocate; - __entry->offset = start - alloc->vm_start; - __entry->size = end - start; - ), - TP_printk("proc=%d allocate=%d offset=%zu size=%zu", - __entry->proc, __entry->allocate, - __entry->offset, __entry->size) -); - -DECLARE_EVENT_CLASS(binder_lru_page_class, - TP_PROTO(const struct binder_alloc *alloc, size_t page_index), - TP_ARGS(alloc, page_index), - TP_STRUCT__entry( - __field(int, proc) - __field(size_t, page_index) - ), - TP_fast_assign( - __entry->proc = alloc->pid; - __entry->page_index = page_index; - ), - TP_printk("proc=%d page_index=%zu", - __entry->proc, __entry->page_index) -); - -DEFINE_EVENT(binder_lru_page_class, binder_alloc_lru_start, - TP_PROTO(const struct binder_alloc *alloc, size_t page_index), - TP_ARGS(alloc, page_index)); - -DEFINE_EVENT(binder_lru_page_class, binder_alloc_lru_end, - TP_PROTO(const struct binder_alloc *alloc, size_t page_index), - TP_ARGS(alloc, page_index)); - -DEFINE_EVENT(binder_lru_page_class, binder_free_lru_start, - TP_PROTO(const struct binder_alloc *alloc, size_t page_index), - TP_ARGS(alloc, page_index)); - -DEFINE_EVENT(binder_lru_page_class, binder_free_lru_end, - TP_PROTO(const struct binder_alloc *alloc, size_t page_index), - TP_ARGS(alloc, page_index)); - -DEFINE_EVENT(binder_lru_page_class, binder_alloc_page_start, - TP_PROTO(const struct binder_alloc *alloc, size_t page_index), - TP_ARGS(alloc, page_index)); - -DEFINE_EVENT(binder_lru_page_class, binder_alloc_page_end, - TP_PROTO(const struct binder_alloc *alloc, size_t page_index), - TP_ARGS(alloc, page_index)); - -DEFINE_EVENT(binder_lru_page_class, binder_unmap_user_start, - TP_PROTO(const struct binder_alloc *alloc, size_t page_index), - TP_ARGS(alloc, page_index)); - -DEFINE_EVENT(binder_lru_page_class, binder_unmap_user_end, - TP_PROTO(const struct binder_alloc *alloc, size_t page_index), - TP_ARGS(alloc, page_index)); - -DEFINE_EVENT(binder_lru_page_class, binder_unmap_kernel_start, - TP_PROTO(const struct binder_alloc *alloc, size_t page_index), - TP_ARGS(alloc, page_index)); - -DEFINE_EVENT(binder_lru_page_class, binder_unmap_kernel_end, - TP_PROTO(const struct binder_alloc *alloc, size_t page_index), - TP_ARGS(alloc, page_index)); - -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") -); - -TRACE_EVENT(binder_netlink_report, - TP_PROTO(const char *context, - struct binder_transaction *t, - u32 data_size, - u32 error), - TP_ARGS(context, t, data_size, error), - TP_STRUCT__entry( - __field(const char *, context) - __field(u32, error) - __field(int, from_pid) - __field(int, from_tid) - __field(int, to_pid) - __field(int, to_tid) - __field(bool, is_reply) - __field(unsigned int, flags) - __field(unsigned int, code) - __field(size_t, data_size) - ), - TP_fast_assign( - __entry->context = context; - __entry->error = error; - __entry->from_pid = t->from_pid; - __entry->from_tid = t->from_tid; - __entry->to_pid = t->to_proc ? t->to_proc->pid : 0; - __entry->to_tid = t->to_thread ? t->to_thread->pid : 0; - __entry->is_reply = t->is_reply; - __entry->flags = t->flags; - __entry->code = t->code; - __entry->data_size = data_size; - ), - TP_printk("from %d:%d to %d:%d context=%s error=%d is_reply=%d flags=0x%x code=0x%x size=%zu", - __entry->from_pid, __entry->from_tid, - __entry->to_pid, __entry->to_tid, - __entry->context, __entry->error, __entry->is_reply, - __entry->flags, __entry->code, __entry->data_size) -); - -#endif /* _BINDER_TRACE_H */ - -#undef TRACE_INCLUDE_PATH -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_PATH . -#define TRACE_INCLUDE_FILE binder_trace -#include <trace/define_trace.h> diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c deleted file mode 100644 index 361d69f756f5..000000000000 --- a/drivers/android/binderfs.c +++ /dev/null @@ -1,786 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include <linux/compiler_types.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/fsnotify.h> -#include <linux/gfp.h> -#include <linux/idr.h> -#include <linux/init.h> -#include <linux/ipc_namespace.h> -#include <linux/kdev_t.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/namei.h> -#include <linux/magic.h> -#include <linux/major.h> -#include <linux/miscdevice.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/mount.h> -#include <linux/fs_parser.h> -#include <linux/sched.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/spinlock_types.h> -#include <linux/stddef.h> -#include <linux/string.h> -#include <linux/types.h> -#include <linux/uaccess.h> -#include <linux/user_namespace.h> -#include <linux/xarray.h> -#include <uapi/linux/android/binder.h> -#include <uapi/linux/android/binderfs.h> - -#include "binder_internal.h" - -#define FIRST_INODE 1 -#define SECOND_INODE 2 -#define INODE_OFFSET 3 -#define BINDERFS_MAX_MINOR (1U << MINORBITS) -/* Ensure that the initial ipc namespace always has devices available. */ -#define BINDERFS_MAX_MINOR_CAPPED (BINDERFS_MAX_MINOR - 4) - -static dev_t binderfs_dev; -static DEFINE_MUTEX(binderfs_minors_mutex); -static DEFINE_IDA(binderfs_minors); - -enum binderfs_param { - Opt_max, - Opt_stats_mode, -}; - -enum binderfs_stats_mode { - binderfs_stats_mode_unset, - binderfs_stats_mode_global, -}; - -struct binder_features { - bool oneway_spam_detection; - bool extended_error; - bool freeze_notification; - bool transaction_report; -}; - -static const struct constant_table binderfs_param_stats[] = { - { "global", binderfs_stats_mode_global }, - {} -}; - -static const struct fs_parameter_spec binderfs_fs_parameters[] = { - fsparam_u32("max", Opt_max), - fsparam_enum("stats", Opt_stats_mode, binderfs_param_stats), - {} -}; - -static struct binder_features binder_features = { - .oneway_spam_detection = true, - .extended_error = true, - .freeze_notification = true, - .transaction_report = true, -}; - -static inline struct binderfs_info *BINDERFS_SB(const struct super_block *sb) -{ - return sb->s_fs_info; -} - -bool is_binderfs_device(const struct inode *inode) -{ - if (inode->i_sb->s_magic == BINDERFS_SUPER_MAGIC) - return true; - - return false; -} - -/** - * binderfs_binder_device_create - allocate inode from super block of a - * binderfs mount - * @ref_inode: inode from which the super block will be taken - * @userp: buffer to copy information about new device for userspace to - * @req: struct binderfs_device as copied from userspace - * - * This function allocates a new binder_device and reserves a new minor - * number for it. - * Minor numbers are limited and tracked globally in binderfs_minors. The - * function will stash a struct binder_device for the specific binder - * device in i_private of the inode. - * It will go on to allocate a new inode from the super block of the - * filesystem mount, stash a struct binder_device in its i_private field - * and attach a dentry to that inode. - * - * Return: 0 on success, negative errno on failure - */ -static int binderfs_binder_device_create(struct inode *ref_inode, - struct binderfs_device __user *userp, - struct binderfs_device *req) -{ - int minor, ret; - struct dentry *dentry, *root; - struct binder_device *device; - char *name = NULL; - struct inode *inode = NULL; - struct super_block *sb = ref_inode->i_sb; - struct binderfs_info *info = sb->s_fs_info; -#if defined(CONFIG_IPC_NS) - bool use_reserve = (info->ipc_ns == &init_ipc_ns); -#else - bool use_reserve = true; -#endif - - /* Reserve new minor number for the new device. */ - mutex_lock(&binderfs_minors_mutex); - if (++info->device_count <= info->mount_opts.max) - minor = ida_alloc_max(&binderfs_minors, - use_reserve ? BINDERFS_MAX_MINOR - 1 : - BINDERFS_MAX_MINOR_CAPPED - 1, - GFP_KERNEL); - else - minor = -ENOSPC; - if (minor < 0) { - --info->device_count; - mutex_unlock(&binderfs_minors_mutex); - return minor; - } - mutex_unlock(&binderfs_minors_mutex); - - ret = -ENOMEM; - device = kzalloc_obj(*device); - if (!device) - goto err; - - inode = new_inode(sb); - if (!inode) - goto err; - - inode->i_ino = minor + INODE_OFFSET; - simple_inode_init_ts(inode); - init_special_inode(inode, S_IFCHR | 0600, - MKDEV(MAJOR(binderfs_dev), minor)); - inode->i_fop = &binder_fops; - inode->i_uid = info->root_uid; - inode->i_gid = info->root_gid; - - req->name[BINDERFS_MAX_NAME] = '\0'; /* NUL-terminate */ - name = kstrdup(req->name, GFP_KERNEL); - if (!name) - goto err; - - refcount_set(&device->ref, 1); - device->binderfs_inode = inode; - device->context.binder_context_mgr_uid = INVALID_UID; - device->context.name = name; - device->miscdev.name = name; - device->miscdev.minor = minor; - mutex_init(&device->context.context_mgr_node_lock); - - req->major = MAJOR(binderfs_dev); - req->minor = minor; - - if (userp && copy_to_user(userp, req, sizeof(*req))) { - ret = -EFAULT; - goto err; - } - - root = sb->s_root; - dentry = simple_start_creating(root, name); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto err; - } - inode->i_private = device; - d_make_persistent(dentry, inode); - fsnotify_create(root->d_inode, dentry); - simple_done_creating(dentry); - - binder_add_device(device); - - return 0; - -err: - kfree(name); - kfree(device); - mutex_lock(&binderfs_minors_mutex); - --info->device_count; - ida_free(&binderfs_minors, minor); - mutex_unlock(&binderfs_minors_mutex); - iput(inode); - - return ret; -} - -/** - * binder_ctl_ioctl - handle binder device node allocation requests - * @file: The file pointer for the binder-control device node. - * @cmd: The ioctl command. - * @arg: The ioctl argument. - * - * The request handler for the binder-control device. All requests operate on - * the binderfs mount the binder-control device resides in: - * - BINDER_CTL_ADD - * Allocate a new binder device. - * - * Return: %0 on success, negative errno on failure. - */ -static long binder_ctl_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ret = -EINVAL; - struct inode *inode = file_inode(file); - struct binderfs_device __user *device = (struct binderfs_device __user *)arg; - struct binderfs_device device_req; - - switch (cmd) { - case BINDER_CTL_ADD: - ret = copy_from_user(&device_req, device, sizeof(device_req)); - if (ret) { - ret = -EFAULT; - break; - } - - ret = binderfs_binder_device_create(inode, device, &device_req); - break; - default: - break; - } - - return ret; -} - -static void binderfs_evict_inode(struct inode *inode) -{ - struct binder_device *device = inode->i_private; - struct binderfs_info *info = BINDERFS_SB(inode->i_sb); - - clear_inode(inode); - - if (!S_ISCHR(inode->i_mode) || !device) - return; - - mutex_lock(&binderfs_minors_mutex); - --info->device_count; - ida_free(&binderfs_minors, device->miscdev.minor); - mutex_unlock(&binderfs_minors_mutex); - - if (refcount_dec_and_test(&device->ref)) { - binder_remove_device(device); - kfree(device->context.name); - kfree(device); - } -} - -static int binderfs_fs_context_parse_param(struct fs_context *fc, - struct fs_parameter *param) -{ - int opt; - struct binderfs_mount_opts *ctx = fc->fs_private; - struct fs_parse_result result; - - opt = fs_parse(fc, binderfs_fs_parameters, param, &result); - if (opt < 0) - return opt; - - switch (opt) { - case Opt_max: - if (result.uint_32 > BINDERFS_MAX_MINOR) - return invalfc(fc, "Bad value for '%s'", param->key); - - ctx->max = result.uint_32; - break; - case Opt_stats_mode: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - ctx->stats_mode = result.uint_32; - break; - default: - return invalfc(fc, "Unsupported parameter '%s'", param->key); - } - - return 0; -} - -static int binderfs_fs_context_reconfigure(struct fs_context *fc) -{ - struct binderfs_mount_opts *ctx = fc->fs_private; - struct binderfs_info *info = BINDERFS_SB(fc->root->d_sb); - - if (info->mount_opts.stats_mode != ctx->stats_mode) - return invalfc(fc, "Binderfs stats mode cannot be changed during a remount"); - - info->mount_opts.stats_mode = ctx->stats_mode; - info->mount_opts.max = ctx->max; - return 0; -} - -static int binderfs_show_options(struct seq_file *seq, struct dentry *root) -{ - struct binderfs_info *info = BINDERFS_SB(root->d_sb); - - if (info->mount_opts.max <= BINDERFS_MAX_MINOR) - seq_printf(seq, ",max=%d", info->mount_opts.max); - - switch (info->mount_opts.stats_mode) { - case binderfs_stats_mode_unset: - break; - case binderfs_stats_mode_global: - seq_printf(seq, ",stats=global"); - break; - } - - return 0; -} - -static const struct super_operations binderfs_super_ops = { - .evict_inode = binderfs_evict_inode, - .show_options = binderfs_show_options, - .statfs = simple_statfs, -}; - -static inline bool is_binderfs_control_device(const struct dentry *dentry) -{ - struct binderfs_info *info = dentry->d_sb->s_fs_info; - - return info->control_dentry == dentry; -} - -static int binderfs_rename(struct mnt_idmap *idmap, - struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry, - unsigned int flags) -{ - if (is_binderfs_control_device(old_dentry) || - is_binderfs_control_device(new_dentry)) - return -EPERM; - - return simple_rename(idmap, old_dir, old_dentry, new_dir, - new_dentry, flags); -} - -static int binderfs_unlink(struct inode *dir, struct dentry *dentry) -{ - if (is_binderfs_control_device(dentry)) - return -EPERM; - - return simple_unlink(dir, dentry); -} - -static const struct file_operations binder_ctl_fops = { - .owner = THIS_MODULE, - .open = nonseekable_open, - .unlocked_ioctl = binder_ctl_ioctl, - .compat_ioctl = binder_ctl_ioctl, - .llseek = noop_llseek, -}; - -/** - * binderfs_binder_ctl_create - create a new binder-control device - * @sb: super block of the binderfs mount - * - * This function creates a new binder-control device node in the binderfs mount - * referred to by @sb. - * - * Return: 0 on success, negative errno on failure - */ -static int binderfs_binder_ctl_create(struct super_block *sb) -{ - int minor, ret; - struct dentry *dentry; - struct binder_device *device; - struct inode *inode = NULL; - struct dentry *root = sb->s_root; - struct binderfs_info *info = sb->s_fs_info; -#if defined(CONFIG_IPC_NS) - bool use_reserve = (info->ipc_ns == &init_ipc_ns); -#else - bool use_reserve = true; -#endif - - device = kzalloc_obj(*device); - if (!device) - return -ENOMEM; - - ret = -ENOMEM; - inode = new_inode(sb); - if (!inode) - goto out; - - /* Reserve a new minor number for the new device. */ - mutex_lock(&binderfs_minors_mutex); - minor = ida_alloc_max(&binderfs_minors, - use_reserve ? BINDERFS_MAX_MINOR - 1 : - BINDERFS_MAX_MINOR_CAPPED - 1, - GFP_KERNEL); - mutex_unlock(&binderfs_minors_mutex); - if (minor < 0) { - ret = minor; - goto out; - } - - inode->i_ino = SECOND_INODE; - simple_inode_init_ts(inode); - init_special_inode(inode, S_IFCHR | 0600, - MKDEV(MAJOR(binderfs_dev), minor)); - inode->i_fop = &binder_ctl_fops; - inode->i_uid = info->root_uid; - inode->i_gid = info->root_gid; - - refcount_set(&device->ref, 1); - device->binderfs_inode = inode; - device->miscdev.minor = minor; - - dentry = d_alloc_name(root, "binder-control"); - if (!dentry) - goto out; - - inode->i_private = device; - info->control_dentry = dentry; - d_make_persistent(dentry, inode); - dput(dentry); - - return 0; - -out: - kfree(device); - iput(inode); - - return ret; -} - -static const struct inode_operations binderfs_dir_inode_operations = { - .lookup = simple_lookup, - .rename = binderfs_rename, - .unlink = binderfs_unlink, -}; - -static struct inode *binderfs_make_inode(struct super_block *sb, int mode) -{ - struct inode *ret; - - ret = new_inode(sb); - if (ret) { - ret->i_ino = iunique(sb, BINDERFS_MAX_MINOR + INODE_OFFSET); - ret->i_mode = mode; - simple_inode_init_ts(ret); - } - return ret; -} - -struct dentry *binderfs_create_file(struct dentry *parent, const char *name, - const struct file_operations *fops, - void *data) -{ - struct dentry *dentry; - struct inode *new_inode, *parent_inode; - struct super_block *sb; - - parent_inode = d_inode(parent); - - dentry = simple_start_creating(parent, name); - if (IS_ERR(dentry)) - return dentry; - - sb = parent_inode->i_sb; - new_inode = binderfs_make_inode(sb, S_IFREG | 0444); - if (!new_inode) { - simple_done_creating(dentry); - return ERR_PTR(-ENOMEM); - } - - new_inode->i_fop = fops; - new_inode->i_private = data; - d_make_persistent(dentry, new_inode); - fsnotify_create(parent_inode, dentry); - simple_done_creating(dentry); - return dentry; // borrowed -} - -static struct dentry *binderfs_create_dir(struct dentry *parent, - const char *name) -{ - struct dentry *dentry; - struct inode *new_inode, *parent_inode; - struct super_block *sb; - - parent_inode = d_inode(parent); - - dentry = simple_start_creating(parent, name); - if (IS_ERR(dentry)) - return dentry; - - sb = parent_inode->i_sb; - new_inode = binderfs_make_inode(sb, S_IFDIR | 0755); - if (!new_inode) { - simple_done_creating(dentry); - return ERR_PTR(-ENOMEM); - } - - new_inode->i_fop = &simple_dir_operations; - new_inode->i_op = &simple_dir_inode_operations; - - set_nlink(new_inode, 2); - d_make_persistent(dentry, new_inode); - inc_nlink(parent_inode); - fsnotify_mkdir(parent_inode, dentry); - simple_done_creating(dentry); - return dentry; -} - -static int binder_features_show(struct seq_file *m, void *unused) -{ - bool *feature = m->private; - - seq_printf(m, "%d\n", *feature); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(binder_features); - -static int init_binder_features(struct super_block *sb) -{ - struct dentry *dentry, *dir; - - dir = binderfs_create_dir(sb->s_root, "features"); - if (IS_ERR(dir)) - return PTR_ERR(dir); - - dentry = binderfs_create_file(dir, "oneway_spam_detection", - &binder_features_fops, - &binder_features.oneway_spam_detection); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - - dentry = binderfs_create_file(dir, "extended_error", - &binder_features_fops, - &binder_features.extended_error); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - - dentry = binderfs_create_file(dir, "freeze_notification", - &binder_features_fops, - &binder_features.freeze_notification); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - - dentry = binderfs_create_file(dir, "transaction_report", - &binder_features_fops, - &binder_features.transaction_report); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - - return 0; -} - -static int init_binder_logs(struct super_block *sb) -{ - struct dentry *binder_logs_root_dir, *dentry, *proc_log_dir; - const struct binder_debugfs_entry *db_entry; - struct binderfs_info *info; - int ret = 0; - - binder_logs_root_dir = binderfs_create_dir(sb->s_root, - "binder_logs"); - if (IS_ERR(binder_logs_root_dir)) { - ret = PTR_ERR(binder_logs_root_dir); - goto out; - } - - binder_for_each_debugfs_entry(db_entry) { - dentry = binderfs_create_file(binder_logs_root_dir, - db_entry->name, - db_entry->fops, - db_entry->data); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto out; - } - } - - proc_log_dir = binderfs_create_dir(binder_logs_root_dir, "proc"); - if (IS_ERR(proc_log_dir)) { - ret = PTR_ERR(proc_log_dir); - goto out; - } - info = sb->s_fs_info; - info->proc_log_dir = proc_log_dir; - -out: - return ret; -} - -static int binderfs_fill_super(struct super_block *sb, struct fs_context *fc) -{ - int ret; - struct binderfs_info *info; - struct binderfs_mount_opts *ctx = fc->fs_private; - struct inode *inode = NULL; - struct binderfs_device device_info = {}; - const char *name; - size_t len; - - sb->s_blocksize = PAGE_SIZE; - sb->s_blocksize_bits = PAGE_SHIFT; - - /* - * The binderfs filesystem can be mounted by userns root in a - * non-initial userns. By default such mounts have the SB_I_NODEV flag - * set in s_iflags to prevent security issues where userns root can - * just create random device nodes via mknod() since it owns the - * filesystem mount. But binderfs does not allow to create any files - * including devices nodes. The only way to create binder devices nodes - * is through the binder-control device which userns root is explicitly - * allowed to do. So removing the SB_I_NODEV flag from s_iflags is both - * necessary and safe. - */ - sb->s_iflags &= ~SB_I_NODEV; - sb->s_iflags |= SB_I_NOEXEC; - sb->s_magic = BINDERFS_SUPER_MAGIC; - sb->s_op = &binderfs_super_ops; - sb->s_time_gran = 1; - - sb->s_fs_info = kzalloc_obj(struct binderfs_info); - if (!sb->s_fs_info) - return -ENOMEM; - info = sb->s_fs_info; - - info->ipc_ns = get_ipc_ns(current->nsproxy->ipc_ns); - - info->root_gid = make_kgid(sb->s_user_ns, 0); - if (!gid_valid(info->root_gid)) - info->root_gid = GLOBAL_ROOT_GID; - info->root_uid = make_kuid(sb->s_user_ns, 0); - if (!uid_valid(info->root_uid)) - info->root_uid = GLOBAL_ROOT_UID; - info->mount_opts.max = ctx->max; - info->mount_opts.stats_mode = ctx->stats_mode; - - inode = new_inode(sb); - if (!inode) - return -ENOMEM; - - inode->i_ino = FIRST_INODE; - inode->i_fop = &simple_dir_operations; - inode->i_mode = S_IFDIR | 0755; - simple_inode_init_ts(inode); - inode->i_op = &binderfs_dir_inode_operations; - set_nlink(inode, 2); - - sb->s_root = d_make_root(inode); - if (!sb->s_root) - return -ENOMEM; - - ret = binderfs_binder_ctl_create(sb); - if (ret) - return ret; - - name = binder_devices_param; - for (len = strcspn(name, ","); len > 0; len = strcspn(name, ",")) { - strscpy(device_info.name, name, len + 1); - ret = binderfs_binder_device_create(inode, NULL, &device_info); - if (ret) - return ret; - name += len; - if (*name == ',') - name++; - } - - ret = init_binder_features(sb); - if (ret) - return ret; - - if (info->mount_opts.stats_mode == binderfs_stats_mode_global) - return init_binder_logs(sb); - - return 0; -} - -static int binderfs_fs_context_get_tree(struct fs_context *fc) -{ - return get_tree_nodev(fc, binderfs_fill_super); -} - -static void binderfs_fs_context_free(struct fs_context *fc) -{ - struct binderfs_mount_opts *ctx = fc->fs_private; - - kfree(ctx); -} - -static const struct fs_context_operations binderfs_fs_context_ops = { - .free = binderfs_fs_context_free, - .get_tree = binderfs_fs_context_get_tree, - .parse_param = binderfs_fs_context_parse_param, - .reconfigure = binderfs_fs_context_reconfigure, -}; - -static int binderfs_init_fs_context(struct fs_context *fc) -{ - struct binderfs_mount_opts *ctx; - - ctx = kzalloc_obj(struct binderfs_mount_opts); - if (!ctx) - return -ENOMEM; - - ctx->max = BINDERFS_MAX_MINOR; - ctx->stats_mode = binderfs_stats_mode_unset; - - fc->fs_private = ctx; - fc->ops = &binderfs_fs_context_ops; - - return 0; -} - -static void binderfs_kill_super(struct super_block *sb) -{ - struct binderfs_info *info = sb->s_fs_info; - - /* - * During inode eviction struct binderfs_info is needed. - * So first wipe the super_block then free struct binderfs_info. - */ - kill_anon_super(sb); - - if (info && info->ipc_ns) - put_ipc_ns(info->ipc_ns); - - kfree(info); -} - -static struct file_system_type binder_fs_type = { - .name = "binder", - .init_fs_context = binderfs_init_fs_context, - .parameters = binderfs_fs_parameters, - .kill_sb = binderfs_kill_super, - .fs_flags = FS_USERNS_MOUNT, -}; - -int __init init_binderfs(void) -{ - int ret; - const char *name; - size_t len; - - /* Verify that the default binderfs device names are valid. */ - name = binder_devices_param; - for (len = strcspn(name, ","); len > 0; len = strcspn(name, ",")) { - if (len > BINDERFS_MAX_NAME) - return -E2BIG; - name += len; - if (*name == ',') - name++; - } - - /* Allocate new major number for binderfs. */ - ret = alloc_chrdev_region(&binderfs_dev, 0, BINDERFS_MAX_MINOR, - "binder"); - if (ret) - return ret; - - ret = register_filesystem(&binder_fs_type); - if (ret) { - unregister_chrdev_region(binderfs_dev, BINDERFS_MAX_MINOR); - return ret; - } - - return ret; -} diff --git a/drivers/android/dbitmap.h b/drivers/android/dbitmap.h deleted file mode 100644 index c7299ce8b374..000000000000 --- a/drivers/android/dbitmap.h +++ /dev/null @@ -1,169 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright 2024 Google LLC - * - * dbitmap - dynamically sized bitmap library. - * - * Used by the binder driver to optimize the allocation of the smallest - * available descriptor ID. Each bit in the bitmap represents the state - * of an ID. - * - * A dbitmap can grow or shrink as needed. This part has been designed - * considering that users might need to briefly release their locks in - * order to allocate memory for the new bitmap. These operations then, - * are verified to determine if the grow or shrink is sill valid. - * - * This library does not provide protection against concurrent access - * by itself. Binder uses the proc->outer_lock for this purpose. - */ - -#ifndef _LINUX_DBITMAP_H -#define _LINUX_DBITMAP_H -#include <linux/bitmap.h> - -#define NBITS_MIN BITS_PER_TYPE(unsigned long) - -struct dbitmap { - unsigned int nbits; - unsigned long *map; -}; - -static inline int dbitmap_enabled(struct dbitmap *dmap) -{ - return !!dmap->nbits; -} - -static inline void dbitmap_free(struct dbitmap *dmap) -{ - dmap->nbits = 0; - kfree(dmap->map); - dmap->map = NULL; -} - -/* Returns the nbits that a dbitmap can shrink to, 0 if not possible. */ -static inline unsigned int dbitmap_shrink_nbits(struct dbitmap *dmap) -{ - unsigned int bit; - - if (dmap->nbits <= NBITS_MIN) - return 0; - - /* - * Determine if the bitmap can shrink based on the position of - * its last set bit. If the bit is within the first quarter of - * the bitmap then shrinking is possible. In this case, the - * bitmap should shrink to half its current size. - */ - bit = find_last_bit(dmap->map, dmap->nbits); - if (bit < (dmap->nbits >> 2)) - return dmap->nbits >> 1; - - /* find_last_bit() returns dmap->nbits when no bits are set. */ - if (bit == dmap->nbits) - return NBITS_MIN; - - return 0; -} - -/* Replace the internal bitmap with a new one of different size */ -static inline void -dbitmap_replace(struct dbitmap *dmap, unsigned long *new, unsigned int nbits) -{ - bitmap_copy(new, dmap->map, min(dmap->nbits, nbits)); - kfree(dmap->map); - dmap->map = new; - dmap->nbits = nbits; -} - -static inline void -dbitmap_shrink(struct dbitmap *dmap, unsigned long *new, unsigned int nbits) -{ - if (!new) - return; - - /* - * Verify that shrinking to @nbits is still possible. The @new - * bitmap might have been allocated without locks, so this call - * could now be outdated. In this case, free @new and move on. - */ - if (!dbitmap_enabled(dmap) || dbitmap_shrink_nbits(dmap) != nbits) { - kfree(new); - return; - } - - dbitmap_replace(dmap, new, nbits); -} - -/* Returns the nbits that a dbitmap can grow to. */ -static inline unsigned int dbitmap_grow_nbits(struct dbitmap *dmap) -{ - return dmap->nbits << 1; -} - -static inline void -dbitmap_grow(struct dbitmap *dmap, unsigned long *new, unsigned int nbits) -{ - /* - * Verify that growing to @nbits is still possible. The @new - * bitmap might have been allocated without locks, so this call - * could now be outdated. In this case, free @new and move on. - */ - if (!dbitmap_enabled(dmap) || nbits <= dmap->nbits) { - kfree(new); - return; - } - - /* - * Check for ENOMEM after confirming the grow operation is still - * required. This ensures we only disable the dbitmap when it's - * necessary. Once the dbitmap is disabled, binder will fallback - * to slow_desc_lookup_olocked(). - */ - if (!new) { - dbitmap_free(dmap); - return; - } - - dbitmap_replace(dmap, new, nbits); -} - -/* - * Finds and sets the next zero bit in the bitmap. Upon success @bit - * is populated with the index and 0 is returned. Otherwise, -ENOSPC - * is returned to indicate that a dbitmap_grow() is needed. - */ -static inline int -dbitmap_acquire_next_zero_bit(struct dbitmap *dmap, unsigned long offset, - unsigned long *bit) -{ - unsigned long n; - - n = find_next_zero_bit(dmap->map, dmap->nbits, offset); - if (n == dmap->nbits) - return -ENOSPC; - - *bit = n; - set_bit(n, dmap->map); - - return 0; -} - -static inline void -dbitmap_clear_bit(struct dbitmap *dmap, unsigned long bit) -{ - clear_bit(bit, dmap->map); -} - -static inline int dbitmap_init(struct dbitmap *dmap) -{ - dmap->map = bitmap_zalloc(NBITS_MIN, GFP_KERNEL); - if (!dmap->map) { - dmap->nbits = 0; - return -ENOMEM; - } - - dmap->nbits = NBITS_MIN; - - return 0; -} -#endif diff --git a/drivers/android/tests/.kunitconfig b/drivers/android/tests/.kunitconfig deleted file mode 100644 index 39b76bab9d9a..000000000000 --- a/drivers/android/tests/.kunitconfig +++ /dev/null @@ -1,7 +0,0 @@ -# -# Copyright 2025 Google LLC. -# - -CONFIG_KUNIT=y -CONFIG_ANDROID_BINDER_IPC=y -CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST=y diff --git a/drivers/android/tests/Makefile b/drivers/android/tests/Makefile deleted file mode 100644 index 27268418eb03..000000000000 --- a/drivers/android/tests/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Copyright 2025 Google LLC. -# - -obj-$(CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST) += binder_alloc_kunit.o diff --git a/drivers/android/tests/binder_alloc_kunit.c b/drivers/android/tests/binder_alloc_kunit.c deleted file mode 100644 index 7f9cc003bbe3..000000000000 --- a/drivers/android/tests/binder_alloc_kunit.c +++ /dev/null @@ -1,572 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Test cases for binder allocator code. - * - * Copyright 2025 Google LLC. - * Author: Tiffany Yang <ynaffit@google.com> - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <kunit/test.h> -#include <linux/anon_inodes.h> -#include <linux/err.h> -#include <linux/file.h> -#include <linux/fs.h> -#include <linux/mm.h> -#include <linux/mman.h> -#include <linux/seq_buf.h> -#include <linux/sizes.h> - -#include "../binder_alloc.h" -#include "../binder_internal.h" - -MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); - -#define BINDER_MMAP_SIZE SZ_128K - -#define BUFFER_NUM 5 -#define BUFFER_MIN_SIZE (PAGE_SIZE / 8) - -#define FREESEQ_BUFLEN ((3 * BUFFER_NUM) + 1) - -#define ALIGN_TYPE_STRLEN (12) - -#define ALIGNMENTS_BUFLEN (((ALIGN_TYPE_STRLEN + 6) * BUFFER_NUM) + 1) - -#define PRINT_ALL_CASES (0) - -/* 5^5 alignment combinations * 2 places to share pages * 5! free sequences */ -#define TOTAL_EXHAUSTIVE_CASES (3125 * 2 * 120) - -/** - * enum buf_end_align_type - Page alignment of a buffer - * end with regard to the end of the previous buffer. - * - * In the pictures below, buf2 refers to the buffer we - * are aligning. buf1 refers to previous buffer by addr. - * Symbol [ means the start of a buffer, ] means the end - * of a buffer, and | means page boundaries. - */ -enum buf_end_align_type { - /** - * @SAME_PAGE_UNALIGNED: The end of this buffer is on - * the same page as the end of the previous buffer and - * is not page aligned. Examples: - * buf1 ][ buf2 ][ ... - * buf1 ]|[ buf2 ][ ... - */ - SAME_PAGE_UNALIGNED = 0, - /** - * @SAME_PAGE_ALIGNED: When the end of the previous buffer - * is not page aligned, the end of this buffer is on the - * same page as the end of the previous buffer and is page - * aligned. When the previous buffer is page aligned, the - * end of this buffer is aligned to the next page boundary. - * Examples: - * buf1 ][ buf2 ]| ... - * buf1 ]|[ buf2 ]| ... - */ - SAME_PAGE_ALIGNED, - /** - * @NEXT_PAGE_UNALIGNED: The end of this buffer is on - * the page next to the end of the previous buffer and - * is not page aligned. Examples: - * buf1 ][ buf2 | buf2 ][ ... - * buf1 ]|[ buf2 | buf2 ][ ... - */ - NEXT_PAGE_UNALIGNED, - /** - * @NEXT_PAGE_ALIGNED: The end of this buffer is on - * the page next to the end of the previous buffer and - * is page aligned. Examples: - * buf1 ][ buf2 | buf2 ]| ... - * buf1 ]|[ buf2 | buf2 ]| ... - */ - NEXT_PAGE_ALIGNED, - /** - * @NEXT_NEXT_UNALIGNED: The end of this buffer is on - * the page that follows the page after the end of the - * previous buffer and is not page aligned. Examples: - * buf1 ][ buf2 | buf2 | buf2 ][ ... - * buf1 ]|[ buf2 | buf2 | buf2 ][ ... - */ - NEXT_NEXT_UNALIGNED, - /** - * @LOOP_END: The number of enum values in &buf_end_align_type. - * It is used for controlling loop termination. - */ - LOOP_END, -}; - -static const char *const buf_end_align_type_strs[LOOP_END] = { - [SAME_PAGE_UNALIGNED] = "SP_UNALIGNED", - [SAME_PAGE_ALIGNED] = " SP_ALIGNED ", - [NEXT_PAGE_UNALIGNED] = "NP_UNALIGNED", - [NEXT_PAGE_ALIGNED] = " NP_ALIGNED ", - [NEXT_NEXT_UNALIGNED] = "NN_UNALIGNED", -}; - -struct binder_alloc_test_case_info { - char alignments[ALIGNMENTS_BUFLEN]; - struct seq_buf alignments_sb; - size_t *buffer_sizes; - int *free_sequence; - bool front_pages; -}; - -static void stringify_free_seq(struct kunit *test, int *seq, struct seq_buf *sb) -{ - int i; - - for (i = 0; i < BUFFER_NUM; i++) - seq_buf_printf(sb, "[%d]", seq[i]); - - KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(sb)); -} - -static void stringify_alignments(struct kunit *test, int *alignments, - struct seq_buf *sb) -{ - int i; - - for (i = 0; i < BUFFER_NUM; i++) - seq_buf_printf(sb, "[ %d:%s ]", i, - buf_end_align_type_strs[alignments[i]]); - - KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(sb)); -} - -static bool check_buffer_pages_allocated(struct kunit *test, - struct binder_alloc *alloc, - struct binder_buffer *buffer, - size_t size) -{ - unsigned long page_addr; - unsigned long end; - int page_index; - - end = PAGE_ALIGN(buffer->user_data + size); - page_addr = buffer->user_data; - for (; page_addr < end; page_addr += PAGE_SIZE) { - page_index = (page_addr - alloc->vm_start) / PAGE_SIZE; - if (!alloc->pages[page_index] || - !list_empty(page_to_lru(alloc->pages[page_index]))) { - kunit_err(test, "expect alloc but is %s at page index %d\n", - alloc->pages[page_index] ? - "lru" : "free", page_index); - return false; - } - } - return true; -} - -static unsigned long binder_alloc_test_alloc_buf(struct kunit *test, - struct binder_alloc *alloc, - struct binder_buffer *buffers[], - size_t *sizes, int *seq) -{ - unsigned long failures = 0; - int i; - - for (i = 0; i < BUFFER_NUM; i++) { - buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0); - if (IS_ERR(buffers[i]) || - !check_buffer_pages_allocated(test, alloc, buffers[i], sizes[i])) - failures++; - } - - return failures; -} - -static unsigned long binder_alloc_test_free_buf(struct kunit *test, - struct binder_alloc *alloc, - struct binder_buffer *buffers[], - size_t *sizes, int *seq, size_t end) -{ - unsigned long failures = 0; - int i; - - for (i = 0; i < BUFFER_NUM; i++) - binder_alloc_free_buf(alloc, buffers[seq[i]]); - - for (i = 0; i <= (end - 1) / PAGE_SIZE; i++) { - if (list_empty(page_to_lru(alloc->pages[i]))) { - kunit_err(test, "expect lru but is %s at page index %d\n", - alloc->pages[i] ? "alloc" : "free", i); - failures++; - } - } - - return failures; -} - -static unsigned long binder_alloc_test_free_page(struct kunit *test, - struct binder_alloc *alloc) -{ - unsigned long failures = 0; - unsigned long count; - int i; - - while ((count = list_lru_count(alloc->freelist))) { - list_lru_walk(alloc->freelist, binder_alloc_free_page, - NULL, count); - } - - for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) { - if (alloc->pages[i]) { - kunit_err(test, "expect free but is %s at page index %d\n", - list_empty(page_to_lru(alloc->pages[i])) ? - "alloc" : "lru", i); - failures++; - } - } - - return failures; -} - -/* Executes one full test run for the given test case. */ -static bool binder_alloc_test_alloc_free(struct kunit *test, - struct binder_alloc *alloc, - struct binder_alloc_test_case_info *tc, - size_t end) -{ - unsigned long pages = PAGE_ALIGN(end) / PAGE_SIZE; - struct binder_buffer *buffers[BUFFER_NUM]; - unsigned long failures; - bool failed = false; - - failures = binder_alloc_test_alloc_buf(test, alloc, buffers, - tc->buffer_sizes, - tc->free_sequence); - failed = failed || failures; - KUNIT_EXPECT_EQ_MSG(test, failures, 0, - "Initial allocation failed: %lu/%u buffers with errors", - failures, BUFFER_NUM); - - failures = binder_alloc_test_free_buf(test, alloc, buffers, - tc->buffer_sizes, - tc->free_sequence, end); - failed = failed || failures; - KUNIT_EXPECT_EQ_MSG(test, failures, 0, - "Initial buffers not freed correctly: %lu/%lu pages not on lru list", - failures, pages); - - /* Allocate from lru. */ - failures = binder_alloc_test_alloc_buf(test, alloc, buffers, - tc->buffer_sizes, - tc->free_sequence); - failed = failed || failures; - KUNIT_EXPECT_EQ_MSG(test, failures, 0, - "Reallocation failed: %lu/%u buffers with errors", - failures, BUFFER_NUM); - - failures = list_lru_count(alloc->freelist); - failed = failed || failures; - KUNIT_EXPECT_EQ_MSG(test, failures, 0, - "lru list should be empty after reallocation but still has %lu pages", - failures); - - failures = binder_alloc_test_free_buf(test, alloc, buffers, - tc->buffer_sizes, - tc->free_sequence, end); - failed = failed || failures; - KUNIT_EXPECT_EQ_MSG(test, failures, 0, - "Reallocated buffers not freed correctly: %lu/%lu pages not on lru list", - failures, pages); - - failures = binder_alloc_test_free_page(test, alloc); - failed = failed || failures; - KUNIT_EXPECT_EQ_MSG(test, failures, 0, - "Failed to clean up allocated pages: %lu/%lu pages still installed", - failures, (alloc->buffer_size / PAGE_SIZE)); - - return failed; -} - -static bool is_dup(int *seq, int index, int val) -{ - int i; - - for (i = 0; i < index; i++) { - if (seq[i] == val) - return true; - } - return false; -} - -/* Generate BUFFER_NUM factorial free orders. */ -static void permute_frees(struct kunit *test, struct binder_alloc *alloc, - struct binder_alloc_test_case_info *tc, - unsigned long *runs, unsigned long *failures, - int index, size_t end) -{ - bool case_failed; - int i; - - if (index == BUFFER_NUM) { - DECLARE_SEQ_BUF(freeseq_sb, FREESEQ_BUFLEN); - - case_failed = binder_alloc_test_alloc_free(test, alloc, tc, end); - *runs += 1; - *failures += case_failed; - - if (case_failed || PRINT_ALL_CASES) { - stringify_free_seq(test, tc->free_sequence, - &freeseq_sb); - kunit_err(test, "case %lu: [%s] | %s - %s - %s", *runs, - case_failed ? "FAILED" : "PASSED", - tc->front_pages ? "front" : "back ", - seq_buf_str(&tc->alignments_sb), - seq_buf_str(&freeseq_sb)); - } - - return; - } - for (i = 0; i < BUFFER_NUM; i++) { - if (is_dup(tc->free_sequence, index, i)) - continue; - tc->free_sequence[index] = i; - permute_frees(test, alloc, tc, runs, failures, index + 1, end); - } -} - -static void gen_buf_sizes(struct kunit *test, - struct binder_alloc *alloc, - struct binder_alloc_test_case_info *tc, - size_t *end_offset, unsigned long *runs, - unsigned long *failures) -{ - size_t last_offset, offset = 0; - size_t front_sizes[BUFFER_NUM]; - size_t back_sizes[BUFFER_NUM]; - int seq[BUFFER_NUM] = {0}; - int i; - - tc->free_sequence = seq; - for (i = 0; i < BUFFER_NUM; i++) { - last_offset = offset; - offset = end_offset[i]; - front_sizes[i] = offset - last_offset; - back_sizes[BUFFER_NUM - i - 1] = front_sizes[i]; - } - back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1]; - - /* - * Buffers share the first or last few pages. - * Only BUFFER_NUM - 1 buffer sizes are adjustable since - * we need one giant buffer before getting to the last page. - */ - tc->front_pages = true; - tc->buffer_sizes = front_sizes; - permute_frees(test, alloc, tc, runs, failures, 0, - end_offset[BUFFER_NUM - 1]); - - tc->front_pages = false; - tc->buffer_sizes = back_sizes; - permute_frees(test, alloc, tc, runs, failures, 0, alloc->buffer_size); -} - -static void gen_buf_offsets(struct kunit *test, struct binder_alloc *alloc, - size_t *end_offset, int *alignments, - unsigned long *runs, unsigned long *failures, - int index) -{ - size_t end, prev; - int align; - - if (index == BUFFER_NUM) { - struct binder_alloc_test_case_info tc = {0}; - - seq_buf_init(&tc.alignments_sb, tc.alignments, - ALIGNMENTS_BUFLEN); - stringify_alignments(test, alignments, &tc.alignments_sb); - - gen_buf_sizes(test, alloc, &tc, end_offset, runs, failures); - return; - } - prev = index == 0 ? 0 : end_offset[index - 1]; - end = prev; - - BUILD_BUG_ON(BUFFER_MIN_SIZE * BUFFER_NUM >= PAGE_SIZE); - - for (align = SAME_PAGE_UNALIGNED; align < LOOP_END; align++) { - if (align % 2) - end = ALIGN(end, PAGE_SIZE); - else - end += BUFFER_MIN_SIZE; - end_offset[index] = end; - alignments[index] = align; - gen_buf_offsets(test, alloc, end_offset, alignments, runs, - failures, index + 1); - } -} - -struct binder_alloc_test { - struct binder_alloc alloc; - struct list_lru binder_test_freelist; - struct file *filp; - unsigned long mmap_uaddr; -}; - -static void binder_alloc_test_init_freelist(struct kunit *test) -{ - struct binder_alloc_test *priv = test->priv; - - KUNIT_EXPECT_PTR_EQ(test, priv->alloc.freelist, - &priv->binder_test_freelist); -} - -static void binder_alloc_test_mmap(struct kunit *test) -{ - struct binder_alloc_test *priv = test->priv; - struct binder_alloc *alloc = &priv->alloc; - struct binder_buffer *buf; - struct rb_node *n; - - KUNIT_EXPECT_EQ(test, alloc->mapped, true); - KUNIT_EXPECT_EQ(test, alloc->buffer_size, BINDER_MMAP_SIZE); - - n = rb_first(&alloc->allocated_buffers); - KUNIT_EXPECT_PTR_EQ(test, n, NULL); - - n = rb_first(&alloc->free_buffers); - buf = rb_entry(n, struct binder_buffer, rb_node); - KUNIT_EXPECT_EQ(test, binder_alloc_buffer_size(alloc, buf), - BINDER_MMAP_SIZE); - KUNIT_EXPECT_TRUE(test, list_is_last(&buf->entry, &alloc->buffers)); -} - -/** - * binder_alloc_exhaustive_test() - Exhaustively test alloc and free of buffer pages. - * @test: The test context object. - * - * Allocate BUFFER_NUM buffers to cover all page alignment cases, - * then free them in all orders possible. Check that pages are - * correctly allocated, put onto lru when buffers are freed, and - * are freed when binder_alloc_free_page() is called. - */ -static void binder_alloc_exhaustive_test(struct kunit *test) -{ - struct binder_alloc_test *priv = test->priv; - size_t end_offset[BUFFER_NUM]; - int alignments[BUFFER_NUM]; - unsigned long failures = 0; - unsigned long runs = 0; - - gen_buf_offsets(test, &priv->alloc, end_offset, alignments, &runs, - &failures, 0); - - KUNIT_EXPECT_EQ(test, runs, TOTAL_EXHAUSTIVE_CASES); - KUNIT_EXPECT_EQ(test, failures, 0); -} - -/* ===== End test cases ===== */ - -static void binder_alloc_test_vma_close(struct vm_area_struct *vma) -{ - struct binder_alloc *alloc = vma->vm_private_data; - - binder_alloc_vma_close(alloc); -} - -static const struct vm_operations_struct binder_alloc_test_vm_ops = { - .close = binder_alloc_test_vma_close, - .fault = binder_vm_fault, -}; - -static int binder_alloc_test_mmap_handler(struct file *filp, - struct vm_area_struct *vma) -{ - struct binder_alloc *alloc = filp->private_data; - - vm_flags_mod(vma, VM_DONTCOPY | VM_MIXEDMAP, VM_MAYWRITE); - - vma->vm_ops = &binder_alloc_test_vm_ops; - vma->vm_private_data = alloc; - - return binder_alloc_mmap_handler(alloc, vma); -} - -static const struct file_operations binder_alloc_test_fops = { - .mmap = binder_alloc_test_mmap_handler, -}; - -static int binder_alloc_test_init(struct kunit *test) -{ - struct binder_alloc_test *priv; - int ret; - - priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - test->priv = priv; - - ret = list_lru_init(&priv->binder_test_freelist); - if (ret) { - kunit_err(test, "Failed to initialize test freelist\n"); - return ret; - } - - /* __binder_alloc_init requires mm to be attached */ - ret = kunit_attach_mm(); - if (ret) { - kunit_err(test, "Failed to attach mm\n"); - return ret; - } - __binder_alloc_init(&priv->alloc, &priv->binder_test_freelist); - - priv->filp = anon_inode_getfile("binder_alloc_kunit", - &binder_alloc_test_fops, &priv->alloc, - O_RDWR | O_CLOEXEC); - if (IS_ERR_OR_NULL(priv->filp)) { - kunit_err(test, "Failed to open binder alloc test driver file\n"); - return priv->filp ? PTR_ERR(priv->filp) : -ENOMEM; - } - - priv->mmap_uaddr = kunit_vm_mmap(test, priv->filp, 0, BINDER_MMAP_SIZE, - PROT_READ, MAP_PRIVATE | MAP_NORESERVE, - 0); - if (!priv->mmap_uaddr) { - kunit_err(test, "Could not map the test's transaction memory\n"); - return -ENOMEM; - } - - return 0; -} - -static void binder_alloc_test_exit(struct kunit *test) -{ - struct binder_alloc_test *priv = test->priv; - - /* Close the backing file to make sure binder_alloc_vma_close runs */ - if (!IS_ERR_OR_NULL(priv->filp)) - fput(priv->filp); - - if (priv->alloc.mm) - binder_alloc_deferred_release(&priv->alloc); - - /* Make sure freelist is empty */ - KUNIT_EXPECT_EQ(test, list_lru_count(&priv->binder_test_freelist), 0); - list_lru_destroy(&priv->binder_test_freelist); -} - -static struct kunit_case binder_alloc_test_cases[] = { - KUNIT_CASE(binder_alloc_test_init_freelist), - KUNIT_CASE(binder_alloc_test_mmap), - KUNIT_CASE_SLOW(binder_alloc_exhaustive_test), - {} -}; - -static struct kunit_suite binder_alloc_test_suite = { - .name = "binder_alloc", - .test_cases = binder_alloc_test_cases, - .init = binder_alloc_test_init, - .exit = binder_alloc_test_exit, -}; - -kunit_test_suite(binder_alloc_test_suite); - -MODULE_AUTHOR("Tiffany Yang <ynaffit@google.com>"); -MODULE_DESCRIPTION("Binder Alloc KUnit tests"); -MODULE_LICENSE("GPL"); |
