ANDROID: rust_binder: freeze notifications
The binder driver implements freeze notifications so that it can wait for processes to thaw. A quick overview of the protocol: Everything starts when a process requests freeze notifications by using BC_REQUEST_FREEZE_NOTIFICATION with a node that it has a handle to. When making this call, the process must pass a cookie that uniquely identifies the freeze notification in the local process (usually this is a pointer into its own address space). Upon creation of the freeze listener, and upon each change to the freeze state, a BR_FROZEN_BINDER message containing the current state is delivered. Whenever a BR_FROZEN_BINDER message is delivered, the driver guarantees that no further messages are sent in relation to that freeze listener until the driver responds with BC_FREEZE_NOTIFICATION_DONE. The BC_CLEAR_FREEZE_NOTIFICATION command can be used to remove an existing freeze listener. Once it's guaranteed that the kernel will no longer send anything for the listener, BR_CLEAR_FREEZE_NOTIFICATION_DONE is sent. Note that BR_CLEAR_FREEZE_NOTIFICATION_DONE is never sent between BR_FROZEN_BINDER and BC_FREEZE_NOTIFICATION_DONE messages, so if the kernel just delivered BR_FROZEN_BINDER, it will not reply with BR_CLEAR_FREEZE_NOTIFICATION_DONE until BC_FREEZE_NOTIFICATION_DONE is received. The implementation uses a FreezeMessage type which will deliver either BR_FROZEN_BINDER or BR_CLEAR_FREEZE_NOTIFICATION_DONE depending on the state, or it may do nothing if no action is needed. One assumption of this implementation is that it's always okay to have too many pending FreezeMessages in flight. This allows us to send a FreezeMessage at any time without checking whether one is already in flight. Bug: 423900220 Change-Id: Iabb589d2b311d9d9a5907ad9187824a10edb7415 Signed-off-by: Alice Ryhl <aliceryhl@google.com>
This commit is contained in:
@@ -36,6 +36,8 @@ pub_no_prefix!(
|
||||
BR_DECREFS,
|
||||
BR_DEAD_BINDER,
|
||||
BR_CLEAR_DEATH_NOTIFICATION_DONE,
|
||||
BR_FROZEN_BINDER,
|
||||
BR_CLEAR_FREEZE_NOTIFICATION_DONE,
|
||||
);
|
||||
|
||||
pub_no_prefix!(
|
||||
@@ -57,6 +59,9 @@ pub_no_prefix!(
|
||||
BC_REQUEST_DEATH_NOTIFICATION,
|
||||
BC_CLEAR_DEATH_NOTIFICATION,
|
||||
BC_DEAD_BINDER_DONE,
|
||||
BC_REQUEST_FREEZE_NOTIFICATION,
|
||||
BC_CLEAR_FREEZE_NOTIFICATION,
|
||||
BC_FREEZE_NOTIFICATION_DONE,
|
||||
);
|
||||
|
||||
pub_no_prefix!(
|
||||
@@ -141,6 +146,8 @@ decl_wrapper!(BinderWriteRead, uapi::binder_write_read);
|
||||
decl_wrapper!(BinderVersion, uapi::binder_version);
|
||||
decl_wrapper!(BinderFrozenStatusInfo, uapi::binder_frozen_status_info);
|
||||
decl_wrapper!(BinderFreezeInfo, uapi::binder_freeze_info);
|
||||
decl_wrapper!(BinderFrozenStateInfo, uapi::binder_frozen_state_info);
|
||||
decl_wrapper!(BinderHandleCookie, uapi::binder_handle_cookie);
|
||||
decl_wrapper!(ExtendedError, uapi::binder_extended_error);
|
||||
|
||||
impl BinderVersion {
|
||||
|
||||
342
drivers/android/binder/freeze.rs
Normal file
342
drivers/android/binder/freeze.rs
Normal file
@@ -0,0 +1,342 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
// Copyright (C) 2024 Google LLC.
|
||||
|
||||
use kernel::{
|
||||
alloc::AllocError,
|
||||
list::ListArc,
|
||||
prelude::*,
|
||||
rbtree::{self, RBTreeNodeReservation},
|
||||
seq_file::SeqFile,
|
||||
seq_print,
|
||||
sync::{Arc, UniqueArc},
|
||||
uaccess::UserSliceReader,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
defs::*, node::Node, process::Process, thread::Thread, BinderReturnWriter, DArc, DLArc,
|
||||
DTRWrap, DeliverToRead,
|
||||
};
|
||||
|
||||
use core::mem;
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub(crate) struct FreezeCookie(u64);
|
||||
|
||||
/// Represents a listener for changes to the frozen state of a process.
|
||||
pub(crate) struct FreezeListener {
|
||||
/// The node we are listening for.
|
||||
pub(crate) node: DArc<Node>,
|
||||
/// The cookie of this freeze listener.
|
||||
cookie: FreezeCookie,
|
||||
/// What value of `is_frozen` did we most recently tell userspace about?
|
||||
last_is_frozen: Option<bool>,
|
||||
/// We sent a `BR_FROZEN_BINDER` and we are waiting for `BC_FREEZE_NOTIFICATION_DONE` before
|
||||
/// sending any other commands.
|
||||
is_pending: bool,
|
||||
/// Userspace sent `BC_CLEAR_FREEZE_NOTIFICATION` and we need to reply with
|
||||
/// `BR_CLEAR_FREEZE_NOTIFICATION_DONE` as soon as possible. If `is_pending` is set, then we
|
||||
/// must wait for it to be unset before we can reply.
|
||||
is_clearing: bool,
|
||||
}
|
||||
|
||||
type UninitFM = UniqueArc<core::mem::MaybeUninit<DTRWrap<FreezeMessage>>>;
|
||||
|
||||
/// Represents a notification that the freeze state has changed.
|
||||
pub(crate) struct FreezeMessage {
|
||||
cookie: FreezeCookie,
|
||||
}
|
||||
|
||||
kernel::list::impl_list_arc_safe! {
|
||||
impl ListArcSafe<0> for FreezeMessage {
|
||||
untracked;
|
||||
}
|
||||
}
|
||||
|
||||
impl FreezeMessage {
|
||||
fn new(flags: kernel::alloc::Flags) -> Result<UninitFM, AllocError> {
|
||||
UniqueArc::new_uninit(flags)
|
||||
}
|
||||
|
||||
fn init(ua: UninitFM, cookie: FreezeCookie) -> DLArc<FreezeMessage> {
|
||||
match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie })) {
|
||||
Ok(msg) => ListArc::from(msg),
|
||||
Err(err) => match err {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DeliverToRead for FreezeMessage {
|
||||
fn do_work(
|
||||
self: DArc<Self>,
|
||||
thread: &Thread,
|
||||
writer: &mut BinderReturnWriter<'_>,
|
||||
) -> Result<bool> {
|
||||
let _removed_listener;
|
||||
let mut node_refs = thread.process.node_refs.lock();
|
||||
let Some(mut freeze_entry) = node_refs.freeze_listeners.find_mut(&self.cookie) else {
|
||||
return Ok(true);
|
||||
};
|
||||
let freeze = freeze_entry.get_mut();
|
||||
|
||||
if freeze.is_pending {
|
||||
return Ok(true);
|
||||
}
|
||||
if freeze.is_clearing {
|
||||
_removed_listener = freeze_entry.remove_node();
|
||||
drop(node_refs);
|
||||
writer.write_code(BR_CLEAR_FREEZE_NOTIFICATION_DONE)?;
|
||||
writer.write_payload(&self.cookie.0)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
let is_frozen = freeze.node.owner.inner.lock().is_frozen;
|
||||
if freeze.last_is_frozen == Some(is_frozen) {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let mut state_info = BinderFrozenStateInfo::default();
|
||||
state_info.is_frozen = is_frozen as u32;
|
||||
state_info.cookie = freeze.cookie.0;
|
||||
freeze.is_pending = true;
|
||||
freeze.last_is_frozen = Some(is_frozen);
|
||||
drop(node_refs);
|
||||
|
||||
writer.write_code(BR_FROZEN_BINDER)?;
|
||||
writer.write_payload(&state_info)?;
|
||||
// BR_FROZEN_BINDER notifications can cause transactions
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel(self: DArc<Self>) {}
|
||||
fn on_thread_selected(&self, _thread: &Thread) {}
|
||||
|
||||
fn should_sync_wakeup(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> {
|
||||
seq_print!(m, "{}has frozen binder\n", prefix);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FreezeListener {
|
||||
pub(crate) fn on_process_exit(&self, proc: &Arc<Process>) {
|
||||
if !self.is_clearing {
|
||||
self.node.remove_freeze_listener(proc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Process {
|
||||
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);
|
||||
|
||||
let msg = FreezeMessage::new(GFP_KERNEL)?;
|
||||
let alloc = RBTreeNodeReservation::new(GFP_KERNEL)?;
|
||||
|
||||
let mut node_refs_guard = self.node_refs.lock();
|
||||
let node_refs = &mut *node_refs_guard;
|
||||
let listener_entry = match node_refs.freeze_listeners.entry(cookie) {
|
||||
rbtree::Entry::Vacant(entry) => entry,
|
||||
rbtree::Entry::Occupied(_) => {
|
||||
pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n");
|
||||
return Err(EINVAL);
|
||||
}
|
||||
};
|
||||
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();
|
||||
node_ref.node.add_freeze_listener(self, GFP_KERNEL)?;
|
||||
|
||||
// From now on we added it to the node's list, so we can't fail.
|
||||
let msg = FreezeMessage::init(msg, cookie);
|
||||
listener_entry.insert(
|
||||
FreezeListener {
|
||||
cookie,
|
||||
node: node_ref.node.clone(),
|
||||
last_is_frozen: None,
|
||||
is_pending: false,
|
||||
is_clearing: false,
|
||||
},
|
||||
alloc,
|
||||
);
|
||||
*info.freeze() = Some(cookie);
|
||||
drop(node_refs_guard);
|
||||
let _ = self.push_work(msg);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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();
|
||||
let node_refs = &mut *node_refs_guard;
|
||||
let Some(freeze) = node_refs.freeze_listeners.get_mut(&cookie) else {
|
||||
pr_warn!("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n", cookie.0);
|
||||
return Err(EINVAL);
|
||||
};
|
||||
if !freeze.is_pending {
|
||||
pr_warn!(
|
||||
"BC_FREEZE_NOTIFICATION_DONE {:016x} not pending\n",
|
||||
cookie.0
|
||||
);
|
||||
return Err(EINVAL);
|
||||
}
|
||||
let mut clear_msg = None;
|
||||
if freeze.is_clearing {
|
||||
// Immediately send another FreezeMessage for BR_CLEAR_FREEZE_NOTIFICATION_DONE.
|
||||
clear_msg = Some(FreezeMessage::init(alloc, cookie));
|
||||
}
|
||||
freeze.is_pending = false;
|
||||
drop(node_refs_guard);
|
||||
if let Some(clear_msg) = clear_msg {
|
||||
let _ = self.push_work(clear_msg);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
let alloc = FreezeMessage::new(GFP_KERNEL)?;
|
||||
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_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n", handle);
|
||||
return Err(EINVAL);
|
||||
};
|
||||
let Some(info_cookie) = info.freeze() else {
|
||||
pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n");
|
||||
return Err(EINVAL);
|
||||
};
|
||||
if *info_cookie != cookie {
|
||||
pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n");
|
||||
return Err(EINVAL);
|
||||
}
|
||||
let Some(listener) = node_refs.freeze_listeners.get_mut(&cookie) else {
|
||||
pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n", handle);
|
||||
return Err(EINVAL);
|
||||
};
|
||||
listener.is_clearing = true;
|
||||
listener.node.remove_freeze_listener(self);
|
||||
*info.freeze() = None;
|
||||
let mut msg = None;
|
||||
if !listener.is_pending {
|
||||
msg = Some(FreezeMessage::init(alloc, cookie));
|
||||
}
|
||||
drop(node_refs_guard);
|
||||
|
||||
if let Some(msg) = msg {
|
||||
let _ = self.push_work(msg);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_freeze_cookie(&self, node: &DArc<Node>) -> Option<FreezeCookie> {
|
||||
let node_refs = &mut *self.node_refs.lock();
|
||||
let handle = node_refs.by_node.get(&node.global_id())?;
|
||||
let node_ref = node_refs.by_handle.get_mut(handle)?;
|
||||
*node_ref.freeze()
|
||||
}
|
||||
|
||||
/// Creates a vector of every freeze listener on this process.
|
||||
///
|
||||
/// Returns pairs of the remote process listening for notifications and the local node it is
|
||||
/// listening on.
|
||||
fn find_freeze_recipients(&self) -> Result<KVVec<(DArc<Node>, Arc<Process>)>, AllocError> {
|
||||
// Defined before `inner` to drop after releasing spinlock if `push_within_capacity` fails.
|
||||
let mut node_proc_pair;
|
||||
|
||||
// We pre-allocate space for up to 8 recipients before we take the spinlock. However, if
|
||||
// the allocation fails, use a vector with a capacity of zero instead of failing. After
|
||||
// all, there might not be any freeze listeners, in which case this operation could still
|
||||
// succeed.
|
||||
let mut recipients =
|
||||
KVVec::with_capacity(8, GFP_KERNEL).unwrap_or_else(|_err| KVVec::new());
|
||||
|
||||
let mut inner = self.inner.lock();
|
||||
let mut nodes = mem::take(&mut inner.nodes);
|
||||
let mut curr = nodes.cursor_front();
|
||||
while let Some(cursor) = curr {
|
||||
let (key, node) = cursor.current();
|
||||
let key = *key;
|
||||
let list = node.freeze_list(&inner);
|
||||
let len = list.len();
|
||||
|
||||
if recipients.spare_capacity_mut().len() < len {
|
||||
inner.nodes = nodes;
|
||||
drop(inner);
|
||||
recipients.reserve(len, GFP_KERNEL)?;
|
||||
inner = self.inner.lock();
|
||||
nodes = mem::take(&mut inner.nodes);
|
||||
// Find the node we were looking at and try again. If the set of nodes was changed,
|
||||
// then just proceed to the next node. This is ok because we don't guarantee the
|
||||
// inclusion of nodes that are added or removed in parallel with this operation.
|
||||
curr = nodes.cursor_lower_bound(&key);
|
||||
continue;
|
||||
}
|
||||
|
||||
for proc in list {
|
||||
node_proc_pair = (node.clone(), proc.clone());
|
||||
recipients
|
||||
.push_within_capacity(node_proc_pair)
|
||||
.map_err(|_| {
|
||||
pr_err!(
|
||||
"push_within_capacity failed even though we checked the capacity\n"
|
||||
);
|
||||
AllocError
|
||||
})?;
|
||||
}
|
||||
|
||||
curr = cursor.move_next();
|
||||
}
|
||||
inner.nodes = nodes;
|
||||
Ok(recipients)
|
||||
}
|
||||
|
||||
/// Prepare allocations for sending freeze messages.
|
||||
pub(crate) fn prepare_freeze_messages(&self) -> Result<FreezeMessages, AllocError> {
|
||||
let recipients = self.find_freeze_recipients()?;
|
||||
let mut batch = KVVec::with_capacity(recipients.len(), GFP_KERNEL)?;
|
||||
for (node, proc) in recipients {
|
||||
let Some(cookie) = proc.get_freeze_cookie(&node) else {
|
||||
// If the freeze listener was removed in the meantime, just discard the
|
||||
// notification.
|
||||
continue;
|
||||
};
|
||||
let msg_alloc = FreezeMessage::new(GFP_KERNEL)?;
|
||||
let msg = FreezeMessage::init(msg_alloc, cookie);
|
||||
batch.push((proc, msg), GFP_KERNEL)?;
|
||||
}
|
||||
|
||||
Ok(FreezeMessages { batch })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct FreezeMessages {
|
||||
batch: KVVec<(Arc<Process>, DLArc<FreezeMessage>)>,
|
||||
}
|
||||
|
||||
impl FreezeMessages {
|
||||
pub(crate) fn send_messages(self) {
|
||||
for (proc, msg) in self.batch {
|
||||
let _ = proc.push_work(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ use crate::{
|
||||
BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead,
|
||||
};
|
||||
|
||||
use core::mem;
|
||||
|
||||
mod wrapper;
|
||||
pub(crate) use self::wrapper::CritIncrWrapper;
|
||||
|
||||
@@ -165,6 +167,8 @@ struct NodeInner {
|
||||
/// List of processes to deliver a notification to when this node is destroyed (usually due to
|
||||
/// the process dying).
|
||||
death_list: List<DTRWrap<NodeDeath>, 1>,
|
||||
/// List of processes to deliver freeze notifications to.
|
||||
freeze_list: KVVec<Arc<Process>>,
|
||||
/// The number of active BR_INCREFS or BR_ACQUIRE operations. (should be maximum two)
|
||||
///
|
||||
/// If this is non-zero, then we postpone any BR_RELEASE or BR_DECREFS notifications until the
|
||||
@@ -175,8 +179,8 @@ struct NodeInner {
|
||||
refs: List<NodeRefInfo, { NodeRefInfo::LIST_NODE }>,
|
||||
}
|
||||
|
||||
use core::mem::offset_of;
|
||||
use kernel::bindings::rb_node_layout;
|
||||
use mem::offset_of;
|
||||
pub(crate) const NODE_LAYOUT: rb_node_layout = rb_node_layout {
|
||||
arc_offset: Arc::<Node>::DATA_OFFSET + offset_of!(DTRWrap<Node>, wrapped),
|
||||
debug_id: offset_of!(Node, debug_id),
|
||||
@@ -187,7 +191,7 @@ pub(crate) const NODE_LAYOUT: rb_node_layout = rb_node_layout {
|
||||
pub(crate) struct Node {
|
||||
pub(crate) debug_id: usize,
|
||||
ptr: u64,
|
||||
cookie: u64,
|
||||
pub(crate) cookie: u64,
|
||||
pub(crate) flags: u32,
|
||||
pub(crate) owner: Arc<Process>,
|
||||
inner: LockedBy<NodeInner, ProcessInner>,
|
||||
@@ -232,6 +236,7 @@ impl Node {
|
||||
},
|
||||
death_list: List::new(),
|
||||
oneway_todo: List::new(),
|
||||
freeze_list: KVVec::new(),
|
||||
has_oneway_transaction: false,
|
||||
active_inc_refs: 0,
|
||||
refs: List::new(),
|
||||
@@ -680,6 +685,55 @@ impl Node {
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
inner.freeze_list.push_within_capacity(process.clone())?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn remove_freeze_listener(&self, p: &Arc<Process>) {
|
||||
let _unused_capacity;
|
||||
let mut guard = self.owner.inner.lock();
|
||||
let inner = self.inner.access_mut(&mut guard);
|
||||
let len = inner.freeze_list.len();
|
||||
inner.freeze_list.retain(|proc| !Arc::ptr_eq(proc, p));
|
||||
if len == inner.freeze_list.len() {
|
||||
pr_warn!(
|
||||
"Could not remove freeze listener for {}\n",
|
||||
p.task.pid_in_current_ns()
|
||||
);
|
||||
}
|
||||
if inner.freeze_list.is_empty() {
|
||||
_unused_capacity = mem::replace(&mut inner.freeze_list, KVVec::new());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn freeze_list<'a>(&'a self, guard: &'a ProcessInner) -> &'a [Arc<Process>] {
|
||||
&self.inner.access(guard).freeze_list
|
||||
}
|
||||
}
|
||||
|
||||
impl DeliverToRead for Node {
|
||||
|
||||
@@ -50,6 +50,10 @@ use crate::{
|
||||
BinderfsProcFile, DArc, DLArc, DTRWrap, DeliverToRead,
|
||||
};
|
||||
|
||||
#[path = "freeze.rs"]
|
||||
mod freeze;
|
||||
use self::freeze::{FreezeCookie, FreezeListener};
|
||||
|
||||
struct Mapping {
|
||||
address: usize,
|
||||
alloc: RangeAllocator<AllocationInfo>,
|
||||
@@ -315,6 +319,8 @@ pub(crate) struct NodeRefInfo {
|
||||
/// The refcount that this process owns to the node.
|
||||
node_ref: ListArcField<NodeRef, { Self::LIST_PROC }>,
|
||||
death: ListArcField<Option<DArc<NodeDeath>>, { Self::LIST_PROC }>,
|
||||
/// Cookie of the active freeze listener for this node.
|
||||
freeze: ListArcField<Option<FreezeCookie>, { Self::LIST_PROC }>,
|
||||
/// Used to store this `NodeRefInfo` in the node's `refs` list.
|
||||
#[pin]
|
||||
links: ListLinks<{ Self::LIST_NODE }>,
|
||||
@@ -335,6 +341,7 @@ impl NodeRefInfo {
|
||||
debug_id: super::next_debug_id(),
|
||||
node_ref: ListArcField::new(node_ref),
|
||||
death: ListArcField::new(None),
|
||||
freeze: ListArcField::new(None),
|
||||
links <- ListLinks::new(),
|
||||
handle,
|
||||
process,
|
||||
@@ -343,6 +350,7 @@ impl NodeRefInfo {
|
||||
|
||||
kernel::list::define_list_arc_field_getter! {
|
||||
pub(crate) fn death(&mut self<{Self::LIST_PROC}>) -> &mut Option<DArc<NodeDeath>> { death }
|
||||
pub(crate) fn freeze(&mut self<{Self::LIST_PROC}>) -> &mut Option<FreezeCookie> { freeze }
|
||||
pub(crate) fn node_ref(&mut self<{Self::LIST_PROC}>) -> &mut NodeRef { node_ref }
|
||||
pub(crate) fn node_ref2(&self<{Self::LIST_PROC}>) -> &NodeRef { node_ref }
|
||||
}
|
||||
@@ -372,6 +380,11 @@ struct ProcessNodeRefs {
|
||||
/// Used to look up nodes without knowing their local 32-bit id. The usize is the address of
|
||||
/// the underlying `Node` struct as returned by `Node::global_id`.
|
||||
by_node: RBTree<usize, u32>,
|
||||
/// Used to look up a `FreezeListener` by cookie.
|
||||
///
|
||||
/// There might be multiple freeze listeners for the same node, but at most one of them is
|
||||
/// active.
|
||||
freeze_listeners: RBTree<FreezeCookie, FreezeListener>,
|
||||
}
|
||||
|
||||
impl ProcessNodeRefs {
|
||||
@@ -379,6 +392,7 @@ impl ProcessNodeRefs {
|
||||
Self {
|
||||
by_handle: RBTree::new(),
|
||||
by_node: RBTree::new(),
|
||||
freeze_listeners: RBTree::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1294,6 +1308,7 @@ impl Process {
|
||||
// while holding the lock.
|
||||
let mut refs = self.node_refs.lock();
|
||||
let mut node_refs = take(&mut refs.by_handle);
|
||||
let freeze_listeners = take(&mut refs.freeze_listeners);
|
||||
drop(refs);
|
||||
for info in node_refs.values_mut() {
|
||||
// SAFETY: We are removing the `NodeRefInfo` from the right node.
|
||||
@@ -1308,6 +1323,10 @@ impl Process {
|
||||
death.set_cleared(false);
|
||||
}
|
||||
drop(node_refs);
|
||||
for listener in freeze_listeners.values() {
|
||||
listener.on_process_exit(&self);
|
||||
}
|
||||
drop(freeze_listeners);
|
||||
|
||||
// Do similar dance for the state lock.
|
||||
let mut inner = self.inner.lock();
|
||||
@@ -1354,10 +1373,13 @@ impl Process {
|
||||
|
||||
pub(crate) fn ioctl_freeze(&self, info: &BinderFreezeInfo) -> Result {
|
||||
if info.enable == 0 {
|
||||
let msgs = self.prepare_freeze_messages()?;
|
||||
let mut inner = self.inner.lock();
|
||||
inner.sync_recv = false;
|
||||
inner.async_recv = false;
|
||||
inner.is_frozen = false;
|
||||
drop(inner);
|
||||
msgs.send_messages();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -1395,7 +1417,17 @@ impl Process {
|
||||
inner.is_frozen = false;
|
||||
Err(EAGAIN)
|
||||
} else {
|
||||
Ok(())
|
||||
drop(inner);
|
||||
match self.prepare_freeze_messages() {
|
||||
Ok(batch) => {
|
||||
batch.send_messages();
|
||||
Ok(())
|
||||
}
|
||||
Err(kernel::alloc::AllocError) => {
|
||||
self.inner.lock().is_frozen = false;
|
||||
Err(ENOMEM)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,10 @@ impl<T> ArrayRangeAllocator<T> {
|
||||
state: DescriptorState::new(is_oneway, debug_id, pid),
|
||||
};
|
||||
// Insert the value at the given index to keep the array sorted.
|
||||
self.ranges.insert_within_capacity(insert_at_idx, new_range).ok().unwrap();
|
||||
self.ranges
|
||||
.insert_within_capacity(insert_at_idx, new_range)
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
Ok(insert_at_offset)
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ enum binderfs_stats_mode {
|
||||
struct binder_features {
|
||||
bool oneway_spam_detection;
|
||||
bool extended_error;
|
||||
bool freeze_notification;
|
||||
};
|
||||
|
||||
static const struct constant_table binderfs_param_stats[] = {
|
||||
@@ -83,6 +84,7 @@ static const struct fs_parameter_spec binderfs_fs_parameters[] = {
|
||||
static struct binder_features binder_features = {
|
||||
.oneway_spam_detection = true,
|
||||
.extended_error = true,
|
||||
.freeze_notification = true,
|
||||
};
|
||||
|
||||
static inline struct binderfs_info *BINDERFS_SB(const struct super_block *sb)
|
||||
@@ -622,6 +624,12 @@ static int init_binder_features(struct super_block *sb)
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
|
||||
dentry = rust_binderfs_create_file(dir, "freeze_notification",
|
||||
&binder_features_fops,
|
||||
&binder_features.freeze_notification);
|
||||
if (IS_ERR(dentry))
|
||||
return PTR_ERR(dentry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1482,6 +1482,9 @@ impl Thread {
|
||||
}
|
||||
BC_ENTER_LOOPER => self.inner.lock().looper_enter(),
|
||||
BC_EXIT_LOOPER => self.inner.lock().looper_exit(),
|
||||
BC_REQUEST_FREEZE_NOTIFICATION => self.process.request_freeze_notif(&mut reader)?,
|
||||
BC_CLEAR_FREEZE_NOTIFICATION => self.process.clear_freeze_notif(&mut reader)?,
|
||||
BC_FREEZE_NOTIFICATION_DONE => self.process.freeze_notif_done(&mut reader)?,
|
||||
|
||||
// Fail if given an unknown error code.
|
||||
// BC_ATTEMPT_ACQUIRE and BC_ACQUIRE_RESULT are no longer supported.
|
||||
|
||||
Reference in New Issue
Block a user