diff options
| author | Alice Ryhl <aliceryhl@google.com> | 2026-06-15 12:36:41 +0000 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-07-10 16:57:56 +0200 |
| commit | b9d17aa74ddd79e2d081db5aacccc2992efceb4c (patch) | |
| tree | e023be93c350590e54d5278a6929998a36237125 /drivers/android | |
| parent | 9e32d2a9784736b3fc262f51ddda1141de753314 (diff) | |
| download | linux-b9d17aa74ddd79e2d081db5aacccc2992efceb4c.tar.gz linux-b9d17aa74ddd79e2d081db5aacccc2992efceb4c.zip | |
rust_binder: avoid allocating under node_refs for freeze listeners
The node_refs mutex needs to be changed to a spinlock, so in preparation
for that, update freeze.rs to avoid allocating under the node_refs lock.
This is done by adding a retry loop so that if add_freeze_listener()
requires reallocating the KVVec<_> of freeze listeners, the caller will
allocate a larger vector and retry.
Analogously, the remove_freeze_listener() function is updated to return
the empty KVVec<_> when it is no longer needed, to avoid calling
kvfree() under the node_refs lock.
Reviewed-by: Matthew Maurer <mmaurer@google.com>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Link: https://patch.msgid.link/20260615-binder-noderefs-spin-v3-1-3235f5a3e0a0@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/android')
| -rw-r--r-- | drivers/android/binder/freeze.rs | 65 | ||||
| -rw-r--r-- | drivers/android/binder/node.rs | 47 |
2 files changed, 67 insertions, 45 deletions
diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs index 2178258772e5..918c4e98b66f 100644 --- a/drivers/android/binder/freeze.rs +++ b/drivers/android/binder/freeze.rs @@ -173,36 +173,58 @@ impl Process { let msg = FreezeMessage::new(GFP_KERNEL)?; let alloc = RBTreeNodeReservation::new(GFP_KERNEL)?; + let mut afl_vec_alloc = KVVec::new(); + let mut info; + let mut freeze_entry; let mut node_refs_guard = self.node_refs.lock(); - let node_refs = &mut *node_refs_guard; - let Some(info) = node_refs.by_handle.get_mut(&handle) else { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle); - return Err(EINVAL); - }; - if info.freeze().is_some() { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n"); - return Err(EINVAL); - } - let node_ref = info.node_ref(); - let freeze_entry = node_refs.freeze_listeners.entry(cookie); - - if let rbtree::Entry::Occupied(ref dupe) = freeze_entry { - if !dupe.get().allow_duplicate(&node_ref.node) { - pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n"); + loop { + let node_refs = &mut *node_refs_guard; + info = match node_refs.by_handle.get_mut(&handle) { + Some(info) => info, + None => { + pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle); + return Err(EINVAL); + } + }; + if info.freeze().is_some() { + pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n"); return Err(EINVAL); } - } + let node_ref = info.node_ref(); + freeze_entry = node_refs.freeze_listeners.entry(cookie); + + if let rbtree::Entry::Occupied(ref dupe) = freeze_entry { + if !dupe.get().allow_duplicate(&node_ref.node) { + pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n"); + return Err(EINVAL); + } + } - // All failure paths must come before this call, and all modifications must come after this - // call. - node_ref.node.add_freeze_listener(self, GFP_KERNEL)?; + // Now we add to the node's freeze listener list, with retry and re-allocate if the + // vector is full. + // + // To ensure that the node is added atomically, this is the first time we modify any + // state. When this call succeeds, all other modifications must occur without the + // possibility for any failure paths. + match node_ref + .node + .add_freeze_listener(self, &mut afl_vec_alloc)? + { + Ok(()) => break, + Err(resize_target) => { + drop(node_refs_guard); + afl_vec_alloc = KVVec::with_capacity(resize_target, GFP_KERNEL)?; + node_refs_guard = self.node_refs.lock(); + } + } + } match freeze_entry { rbtree::Entry::Vacant(entry) => { entry.insert( FreezeListener { cookie, - node: node_ref.node.clone(), + node: info.node_ref().node.clone(), last_is_frozen: None, is_pending: false, is_clearing: false, @@ -273,6 +295,7 @@ impl Process { let handle = hc.handle; let cookie = FreezeCookie(hc.cookie); + let _to_free_fl; let alloc = FreezeMessage::new(GFP_KERNEL)?; let mut node_refs_guard = self.node_refs.lock(); let node_refs = &mut *node_refs_guard; @@ -293,7 +316,7 @@ impl Process { return Err(EINVAL); }; listener.is_clearing = true; - listener.node.remove_freeze_listener(self); + _to_free_fl = listener.node.remove_freeze_listener(self); *info.freeze() = None; let mut msg = None; if !listener.is_pending { diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs index 4e75f58bf0db..fb57c0b20888 100644 --- a/drivers/android/binder/node.rs +++ b/drivers/android/binder/node.rs @@ -659,33 +659,29 @@ impl Node { pub(crate) fn add_freeze_listener( &self, process: &Arc<Process>, - flags: kernel::alloc::Flags, - ) -> Result { - let mut vec_alloc = KVVec::<Arc<Process>>::new(); - loop { - let mut guard = self.owner.inner.lock(); - // Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the - // listener, no the target. - let inner = self.inner.access_mut(&mut guard); - let len = inner.freeze_list.len(); - if len >= inner.freeze_list.capacity() { - if len >= vec_alloc.capacity() { - drop(guard); - vec_alloc = KVVec::with_capacity((1 + len).next_power_of_two(), flags)?; - continue; - } - mem::swap(&mut inner.freeze_list, &mut vec_alloc); - for elem in vec_alloc.drain_all() { - inner.freeze_list.push_within_capacity(elem)?; - } + // If the vector needs to be resized, it's done via this argument. + vec_alloc: &mut KVVec<Arc<Process>>, + ) -> Result<Result<(), usize>> { + let mut guard = self.owner.inner.lock(); + // Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the + // listener, not the target. + let inner = self.inner.access_mut(&mut guard); + let len = inner.freeze_list.len(); + if len == inner.freeze_list.capacity() { + if len >= vec_alloc.capacity() { + // Request the caller to reallocate. + return Ok(Err((1 + len).next_power_of_two())); + } + mem::swap(&mut inner.freeze_list, vec_alloc); + for elem in vec_alloc.drain_all() { + inner.freeze_list.push_within_capacity(elem)?; } - inner.freeze_list.push_within_capacity(process.clone())?; - return Ok(()); } + inner.freeze_list.push_within_capacity(process.clone())?; + Ok(Ok(())) } - pub(crate) fn remove_freeze_listener(&self, p: &Arc<Process>) { - let _unused_capacity; + pub(crate) fn remove_freeze_listener(&self, p: &Arc<Process>) -> KVVec<Arc<Process>> { let mut guard = self.owner.inner.lock(); let inner = self.inner.access_mut(&mut guard); let len = inner.freeze_list.len(); @@ -696,9 +692,12 @@ impl Node { p.pid_in_current_ns() ); } + // If the vector is empty it needs to be freed. However, we can't free it here because that + // might sleep, so return it to the caller. if inner.freeze_list.is_empty() { - _unused_capacity = mem::take(&mut inner.freeze_list); + return mem::take(&mut inner.freeze_list); } + KVVec::new() } pub(crate) fn freeze_list<'a>(&'a self, guard: &'a ProcessInner) -> &'a [Arc<Process>] { |
