mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-06 17:35:20 +02:00
ANDROID: rust_binder: add process freezing
When you want to freeze a process, you should process all incoming transactions before you freeze it. This patch helps with that. The idea is that before you freeze the process, you mark it as frozen in the binder driver. When this happens, all new incoming transactions are rejected, which lets you empty the queue of incoming transactions that were sent before you decided to freeze the process. Once you have processed every transaction in that queue, you can perform the actual freeze operation. Link: https://lore.kernel.org/rust-for-linux/20231101-rust-binder-v1-15-08ba9197f637@google.com/ Change-Id: Iae79352b897618913a0c710596da98e9f274c58b Signed-off-by: Alice Ryhl <aliceryhl@google.com> Bug: 278052745
This commit is contained in:
parent
571343fd97
commit
50259804b1
|
@ -87,6 +87,18 @@ pub(crate) struct ContextList {
|
||||||
list: List<Context>,
|
list: List<Context>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_all_contexts() -> Result<Vec<Arc<Context>>> {
|
||||||
|
let lock = CONTEXTS.lock();
|
||||||
|
|
||||||
|
let count = lock.list.iter().count();
|
||||||
|
|
||||||
|
let mut ctxs = Vec::try_with_capacity(count)?;
|
||||||
|
for ctx in &lock.list {
|
||||||
|
ctxs.try_push(Arc::from(ctx))?;
|
||||||
|
}
|
||||||
|
Ok(ctxs)
|
||||||
|
}
|
||||||
|
|
||||||
/// This struct keeps track of the processes using this context, and which process is the context
|
/// This struct keeps track of the processes using this context, and which process is the context
|
||||||
/// manager.
|
/// manager.
|
||||||
struct Manager {
|
struct Manager {
|
||||||
|
@ -201,4 +213,31 @@ impl Context {
|
||||||
.clone(strong)
|
.clone(strong)
|
||||||
.map_err(BinderError::from)
|
.map_err(BinderError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn for_each_proc<F>(&self, mut func: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&Process),
|
||||||
|
{
|
||||||
|
let lock = self.manager.lock();
|
||||||
|
for proc in &lock.all_procs {
|
||||||
|
func(&proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_all_procs(&self) -> Result<Vec<Arc<Process>>> {
|
||||||
|
let lock = self.manager.lock();
|
||||||
|
let count = lock.all_procs.iter().count();
|
||||||
|
|
||||||
|
let mut procs = Vec::try_with_capacity(count)?;
|
||||||
|
for proc in &lock.all_procs {
|
||||||
|
procs.try_push(Arc::from(proc))?;
|
||||||
|
}
|
||||||
|
Ok(procs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_procs_with_pid(&self, pid: i32) -> Result<Vec<Arc<Process>>> {
|
||||||
|
let mut procs = self.get_all_procs()?;
|
||||||
|
procs.retain(|proc| proc.task.pid() == pid);
|
||||||
|
Ok(procs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ pub_no_prefix!(
|
||||||
BR_REPLY,
|
BR_REPLY,
|
||||||
BR_DEAD_REPLY,
|
BR_DEAD_REPLY,
|
||||||
BR_FAILED_REPLY,
|
BR_FAILED_REPLY,
|
||||||
|
BR_FROZEN_REPLY,
|
||||||
BR_NOOP,
|
BR_NOOP,
|
||||||
BR_SPAWN_LOOPER,
|
BR_SPAWN_LOOPER,
|
||||||
BR_TRANSACTION_COMPLETE,
|
BR_TRANSACTION_COMPLETE,
|
||||||
|
@ -125,6 +126,8 @@ decl_wrapper!(
|
||||||
);
|
);
|
||||||
decl_wrapper!(BinderWriteRead, bindings::binder_write_read);
|
decl_wrapper!(BinderWriteRead, bindings::binder_write_read);
|
||||||
decl_wrapper!(BinderVersion, bindings::binder_version);
|
decl_wrapper!(BinderVersion, bindings::binder_version);
|
||||||
|
decl_wrapper!(BinderFrozenStatusInfo, bindings::binder_frozen_status_info);
|
||||||
|
decl_wrapper!(BinderFreezeInfo, bindings::binder_freeze_info);
|
||||||
decl_wrapper!(ExtendedError, bindings::binder_extended_error);
|
decl_wrapper!(ExtendedError, bindings::binder_extended_error);
|
||||||
|
|
||||||
impl BinderVersion {
|
impl BinderVersion {
|
||||||
|
|
|
@ -23,6 +23,13 @@ impl BinderError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_frozen() -> Self {
|
||||||
|
Self {
|
||||||
|
reply: BR_FROZEN_REPLY,
|
||||||
|
source: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn is_dead(&self) -> bool {
|
pub(crate) fn is_dead(&self) -> bool {
|
||||||
self.reply == BR_DEAD_REPLY
|
self.reply == BR_DEAD_REPLY
|
||||||
}
|
}
|
||||||
|
@ -78,6 +85,7 @@ impl core::fmt::Debug for BinderError {
|
||||||
None => f.pad("BR_FAILED_REPLY"),
|
None => f.pad("BR_FAILED_REPLY"),
|
||||||
},
|
},
|
||||||
BR_DEAD_REPLY => f.pad("BR_DEAD_REPLY"),
|
BR_DEAD_REPLY => f.pad("BR_DEAD_REPLY"),
|
||||||
|
BR_FROZEN_REPLY => f.pad("BR_FROZEN_REPLY"),
|
||||||
BR_TRANSACTION_COMPLETE => f.pad("BR_TRANSACTION_COMPLETE"),
|
BR_TRANSACTION_COMPLETE => f.pad("BR_TRANSACTION_COMPLETE"),
|
||||||
_ => f
|
_ => f
|
||||||
.debug_struct("BinderError")
|
.debug_struct("BinderError")
|
||||||
|
|
|
@ -22,7 +22,9 @@ use kernel::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
rbtree::{self, RBTree},
|
rbtree::{self, RBTree},
|
||||||
sync::poll::PollTable,
|
sync::poll::PollTable,
|
||||||
sync::{lock::Guard, Arc, ArcBorrow, Mutex, SpinLock, UniqueArc},
|
sync::{
|
||||||
|
lock::Guard, Arc, ArcBorrow, CondVar, CondVarTimeoutResult, Mutex, SpinLock, UniqueArc,
|
||||||
|
},
|
||||||
task::Task,
|
task::Task,
|
||||||
types::{ARef, Either},
|
types::{ARef, Either},
|
||||||
uaccess::{UserSlice, UserSliceReader},
|
uaccess::{UserSlice, UserSliceReader},
|
||||||
|
@ -83,6 +85,16 @@ pub(crate) struct ProcessInner {
|
||||||
|
|
||||||
/// Bitmap of deferred work to do.
|
/// Bitmap of deferred work to do.
|
||||||
defer_work: u8,
|
defer_work: u8,
|
||||||
|
|
||||||
|
/// Number of transactions to be transmitted before processes in freeze_wait
|
||||||
|
/// are woken up.
|
||||||
|
outstanding_txns: u32,
|
||||||
|
/// Process is frozen and unable to service binder transactions.
|
||||||
|
pub(crate) is_frozen: bool,
|
||||||
|
/// Process received sync transactions since last frozen.
|
||||||
|
pub(crate) sync_recv: bool,
|
||||||
|
/// Process received async transactions since last frozen.
|
||||||
|
pub(crate) async_recv: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProcessInner {
|
impl ProcessInner {
|
||||||
|
@ -100,6 +112,10 @@ impl ProcessInner {
|
||||||
max_threads: 0,
|
max_threads: 0,
|
||||||
started_thread_count: 0,
|
started_thread_count: 0,
|
||||||
defer_work: 0,
|
defer_work: 0,
|
||||||
|
outstanding_txns: 0,
|
||||||
|
is_frozen: false,
|
||||||
|
sync_recv: false,
|
||||||
|
async_recv: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,6 +278,22 @@ impl ProcessInner {
|
||||||
pr_warn!("Notification added to `delivered_deaths` twice.");
|
pr_warn!("Notification added to `delivered_deaths` twice.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_outstanding_txn(&mut self) {
|
||||||
|
self.outstanding_txns += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn txns_pending_locked(&self) -> bool {
|
||||||
|
if self.outstanding_txns > 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for thread in self.threads.values() {
|
||||||
|
if thread.has_current_transaction() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to keep track of a node that this process has a handle to.
|
/// Used to keep track of a node that this process has a handle to.
|
||||||
|
@ -355,6 +387,11 @@ pub(crate) struct Process {
|
||||||
#[pin]
|
#[pin]
|
||||||
pub(crate) inner: SpinLock<ProcessInner>,
|
pub(crate) inner: SpinLock<ProcessInner>,
|
||||||
|
|
||||||
|
// Waitqueue of processes waiting for all outstanding transactions to be
|
||||||
|
// processed.
|
||||||
|
#[pin]
|
||||||
|
freeze_wait: CondVar,
|
||||||
|
|
||||||
// Node references are in a different lock to avoid recursive acquisition when
|
// Node references are in a different lock to avoid recursive acquisition when
|
||||||
// incrementing/decrementing a node in another process.
|
// incrementing/decrementing a node in another process.
|
||||||
#[pin]
|
#[pin]
|
||||||
|
@ -412,6 +449,7 @@ impl Process {
|
||||||
cred,
|
cred,
|
||||||
inner <- kernel::new_spinlock!(ProcessInner::new(), "Process::inner"),
|
inner <- kernel::new_spinlock!(ProcessInner::new(), "Process::inner"),
|
||||||
node_refs <- kernel::new_mutex!(ProcessNodeRefs::new(), "Process::node_refs"),
|
node_refs <- kernel::new_mutex!(ProcessNodeRefs::new(), "Process::node_refs"),
|
||||||
|
freeze_wait <- kernel::new_condvar!("Process::freeze_wait"),
|
||||||
task: kernel::current!().group_leader().into(),
|
task: kernel::current!().group_leader().into(),
|
||||||
defer_work <- kernel::new_work!("Process::defer_work"),
|
defer_work <- kernel::new_work!("Process::defer_work"),
|
||||||
links <- ListLinks::new(),
|
links <- ListLinks::new(),
|
||||||
|
@ -975,6 +1013,9 @@ impl Process {
|
||||||
let is_manager = {
|
let is_manager = {
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
inner.is_dead = true;
|
inner.is_dead = true;
|
||||||
|
inner.is_frozen = false;
|
||||||
|
inner.sync_recv = false;
|
||||||
|
inner.async_recv = false;
|
||||||
inner.is_manager
|
inner.is_manager
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1075,6 +1116,118 @@ impl Process {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn drop_outstanding_txn(&self) {
|
||||||
|
let wake = {
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
if inner.outstanding_txns == 0 {
|
||||||
|
pr_err!("outstanding_txns underflow");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
inner.outstanding_txns -= 1;
|
||||||
|
inner.is_frozen && inner.outstanding_txns == 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if wake {
|
||||||
|
self.freeze_wait.notify_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn ioctl_freeze(&self, info: &BinderFreezeInfo) -> Result {
|
||||||
|
if info.enable != 0 {
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
inner.sync_recv = false;
|
||||||
|
inner.async_recv = false;
|
||||||
|
inner.is_frozen = false;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
inner.sync_recv = false;
|
||||||
|
inner.async_recv = false;
|
||||||
|
inner.is_frozen = true;
|
||||||
|
|
||||||
|
if info.timeout_ms > 0 {
|
||||||
|
let mut jiffies = kernel::time::msecs_to_jiffies(info.timeout_ms);
|
||||||
|
while jiffies > 0 {
|
||||||
|
if inner.outstanding_txns == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self
|
||||||
|
.freeze_wait
|
||||||
|
.wait_interruptible_timeout(&mut inner, jiffies)
|
||||||
|
{
|
||||||
|
CondVarTimeoutResult::Signal { .. } => {
|
||||||
|
inner.is_frozen = false;
|
||||||
|
return Err(ERESTARTSYS);
|
||||||
|
}
|
||||||
|
CondVarTimeoutResult::Woken { jiffies: remaining } => {
|
||||||
|
jiffies = remaining;
|
||||||
|
}
|
||||||
|
CondVarTimeoutResult::Timeout => {
|
||||||
|
jiffies = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if inner.txns_pending_locked() {
|
||||||
|
inner.is_frozen = false;
|
||||||
|
Err(EAGAIN)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_frozen_status(data: UserSlice) -> Result {
|
||||||
|
let (mut reader, mut writer) = data.reader_writer();
|
||||||
|
|
||||||
|
let mut info = reader.read::<BinderFrozenStatusInfo>()?;
|
||||||
|
info.sync_recv = 0;
|
||||||
|
info.async_recv = 0;
|
||||||
|
let mut found = false;
|
||||||
|
|
||||||
|
for ctx in crate::context::get_all_contexts()? {
|
||||||
|
ctx.for_each_proc(|proc| {
|
||||||
|
if proc.task.pid() == info.pid as _ {
|
||||||
|
found = true;
|
||||||
|
let inner = proc.inner.lock();
|
||||||
|
let txns_pending = inner.txns_pending_locked();
|
||||||
|
info.async_recv |= inner.async_recv as u32;
|
||||||
|
info.sync_recv |= inner.sync_recv as u32;
|
||||||
|
info.sync_recv |= (txns_pending as u32) << 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if found {
|
||||||
|
writer.write(&info)?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(EINVAL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ioctl_freeze(reader: &mut UserSliceReader) -> Result {
|
||||||
|
let info = reader.read::<BinderFreezeInfo>()?;
|
||||||
|
|
||||||
|
// Very unlikely for there to be more than 3, since a process normally uses at most binder and
|
||||||
|
// hwbinder.
|
||||||
|
let mut procs = Vec::try_with_capacity(3)?;
|
||||||
|
|
||||||
|
let ctxs = crate::context::get_all_contexts()?;
|
||||||
|
for ctx in ctxs {
|
||||||
|
for proc in ctx.get_procs_with_pid(info.pid as i32)? {
|
||||||
|
procs.try_push(proc)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for proc in procs {
|
||||||
|
proc.ioctl_freeze(&info)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The ioctl handler.
|
/// The ioctl handler.
|
||||||
|
@ -1096,6 +1249,7 @@ impl Process {
|
||||||
bindings::BINDER_SET_CONTEXT_MGR_EXT => {
|
bindings::BINDER_SET_CONTEXT_MGR_EXT => {
|
||||||
this.set_as_manager(Some(reader.read()?), &thread)?
|
this.set_as_manager(Some(reader.read()?), &thread)?
|
||||||
}
|
}
|
||||||
|
bindings::BINDER_FREEZE => ioctl_freeze(reader)?,
|
||||||
_ => return Err(EINVAL),
|
_ => return Err(EINVAL),
|
||||||
}
|
}
|
||||||
Ok(0)
|
Ok(0)
|
||||||
|
@ -1117,6 +1271,7 @@ impl Process {
|
||||||
bindings::BINDER_GET_NODE_DEBUG_INFO => this.get_node_debug_info(data)?,
|
bindings::BINDER_GET_NODE_DEBUG_INFO => this.get_node_debug_info(data)?,
|
||||||
bindings::BINDER_GET_NODE_INFO_FOR_REF => this.get_node_info_from_ref(data)?,
|
bindings::BINDER_GET_NODE_INFO_FOR_REF => this.get_node_info_from_ref(data)?,
|
||||||
bindings::BINDER_VERSION => this.version(data)?,
|
bindings::BINDER_VERSION => this.version(data)?,
|
||||||
|
bindings::BINDER_GET_FROZEN_INFO => get_frozen_status(data)?,
|
||||||
bindings::BINDER_GET_EXTENDED_ERROR => thread.get_extended_error(data)?,
|
bindings::BINDER_GET_EXTENDED_ERROR => thread.get_extended_error(data)?,
|
||||||
_ => return Err(EINVAL),
|
_ => return Err(EINVAL),
|
||||||
}
|
}
|
||||||
|
|
|
@ -460,6 +460,10 @@ impl Thread {
|
||||||
self.inner.lock().current_transaction = Some(transaction);
|
self.inner.lock().current_transaction = Some(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn has_current_transaction(&self) -> bool {
|
||||||
|
self.inner.lock().current_transaction.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to fetch a work item from the thread-local queue. The behaviour if the queue is
|
/// Attempts to fetch a work item from the thread-local queue. The behaviour if the queue is
|
||||||
/// empty depends on `wait`: if it is true, the function waits for some work to be queued (or a
|
/// empty depends on `wait`: if it is true, the function waits for some work to be queued (or a
|
||||||
/// signal); otherwise it returns indicating that none is available.
|
/// signal); otherwise it returns indicating that none is available.
|
||||||
|
@ -484,7 +488,7 @@ impl Thread {
|
||||||
}
|
}
|
||||||
|
|
||||||
inner.looper_flags |= LOOPER_WAITING;
|
inner.looper_flags |= LOOPER_WAITING;
|
||||||
let signal_pending = self.work_condvar.wait_interruptible(&mut inner);
|
let signal_pending = self.work_condvar.wait_interruptible_freezable(&mut inner);
|
||||||
inner.looper_flags &= !LOOPER_WAITING;
|
inner.looper_flags &= !LOOPER_WAITING;
|
||||||
|
|
||||||
if signal_pending {
|
if signal_pending {
|
||||||
|
@ -535,7 +539,7 @@ impl Thread {
|
||||||
}
|
}
|
||||||
|
|
||||||
inner.looper_flags |= LOOPER_WAITING | LOOPER_WAITING_PROC;
|
inner.looper_flags |= LOOPER_WAITING | LOOPER_WAITING_PROC;
|
||||||
let signal_pending = self.work_condvar.wait_interruptible(&mut inner);
|
let signal_pending = self.work_condvar.wait_interruptible_freezable(&mut inner);
|
||||||
inner.looper_flags &= !(LOOPER_WAITING | LOOPER_WAITING_PROC);
|
inner.looper_flags &= !(LOOPER_WAITING | LOOPER_WAITING_PROC);
|
||||||
|
|
||||||
if signal_pending || inner.looper_need_return {
|
if signal_pending || inner.looper_need_return {
|
||||||
|
@ -1050,6 +1054,10 @@ impl Thread {
|
||||||
reply: Result<DLArc<Transaction>, u32>,
|
reply: Result<DLArc<Transaction>, u32>,
|
||||||
transaction: &DArc<Transaction>,
|
transaction: &DArc<Transaction>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
if let Ok(transaction) = &reply {
|
||||||
|
transaction.set_outstanding(&mut self.process.inner.lock());
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
if !inner.pop_transaction_replied(transaction) {
|
if !inner.pop_transaction_replied(transaction) {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
// Copyright (C) 2024 Google LLC.
|
// Copyright (C) 2024 Google LLC.
|
||||||
|
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
use kernel::{
|
use kernel::{
|
||||||
list::ListArcSafe,
|
list::ListArcSafe,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
@ -16,13 +17,13 @@ use crate::{
|
||||||
defs::*,
|
defs::*,
|
||||||
error::{BinderError, BinderResult},
|
error::{BinderError, BinderResult},
|
||||||
node::{Node, NodeRef},
|
node::{Node, NodeRef},
|
||||||
process::Process,
|
process::{Process, ProcessInner},
|
||||||
ptr_align,
|
ptr_align,
|
||||||
thread::{PushWorkRes, Thread},
|
thread::{PushWorkRes, Thread},
|
||||||
DArc, DLArc, DTRWrap, DeliverToRead,
|
DArc, DLArc, DTRWrap, DeliverToRead,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[pin_data]
|
#[pin_data(PinnedDrop)]
|
||||||
pub(crate) struct Transaction {
|
pub(crate) struct Transaction {
|
||||||
target_node: Option<DArc<Node>>,
|
target_node: Option<DArc<Node>>,
|
||||||
from_parent: Option<DArc<Transaction>>,
|
from_parent: Option<DArc<Transaction>>,
|
||||||
|
@ -30,6 +31,7 @@ pub(crate) struct Transaction {
|
||||||
to: Arc<Process>,
|
to: Arc<Process>,
|
||||||
#[pin]
|
#[pin]
|
||||||
allocation: SpinLock<Option<Allocation>>,
|
allocation: SpinLock<Option<Allocation>>,
|
||||||
|
is_outstanding: AtomicBool,
|
||||||
code: u32,
|
code: u32,
|
||||||
pub(crate) flags: u32,
|
pub(crate) flags: u32,
|
||||||
data_size: usize,
|
data_size: usize,
|
||||||
|
@ -95,6 +97,7 @@ impl Transaction {
|
||||||
offsets_size: trd.offsets_size as _,
|
offsets_size: trd.offsets_size as _,
|
||||||
data_address,
|
data_address,
|
||||||
allocation <- kernel::new_spinlock!(Some(alloc), "Transaction::new"),
|
allocation <- kernel::new_spinlock!(Some(alloc), "Transaction::new"),
|
||||||
|
is_outstanding: AtomicBool::new(false),
|
||||||
txn_security_ctx_off,
|
txn_security_ctx_off,
|
||||||
}))?)
|
}))?)
|
||||||
}
|
}
|
||||||
|
@ -128,6 +131,7 @@ impl Transaction {
|
||||||
offsets_size: trd.offsets_size as _,
|
offsets_size: trd.offsets_size as _,
|
||||||
data_address: alloc.ptr,
|
data_address: alloc.ptr,
|
||||||
allocation <- kernel::new_spinlock!(Some(alloc), "Transaction::new"),
|
allocation <- kernel::new_spinlock!(Some(alloc), "Transaction::new"),
|
||||||
|
is_outstanding: AtomicBool::new(false),
|
||||||
txn_security_ctx_off: None,
|
txn_security_ctx_off: None,
|
||||||
}))?)
|
}))?)
|
||||||
}
|
}
|
||||||
|
@ -173,6 +177,26 @@ impl Transaction {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_outstanding(&self, to_process: &mut ProcessInner) {
|
||||||
|
// No race because this method is only called once.
|
||||||
|
if !self.is_outstanding.load(Ordering::Relaxed) {
|
||||||
|
self.is_outstanding.store(true, Ordering::Relaxed);
|
||||||
|
to_process.add_outstanding_txn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrement `outstanding_txns` in `to` if it hasn't already been decremented.
|
||||||
|
fn drop_outstanding_txn(&self) {
|
||||||
|
// No race because this is called at most twice, and one of the calls are in the
|
||||||
|
// destructor, which is guaranteed to not race with any other operations on the
|
||||||
|
// transaction. It also cannot race with `set_outstanding`, since submission happens
|
||||||
|
// before delivery.
|
||||||
|
if self.is_outstanding.load(Ordering::Relaxed) {
|
||||||
|
self.is_outstanding.store(false, Ordering::Relaxed);
|
||||||
|
self.to.drop_outstanding_txn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Submits the transaction to a work queue. Uses a thread if there is one in the transaction
|
/// Submits the transaction to a work queue. Uses a thread if there is one in the transaction
|
||||||
/// stack, otherwise uses the destination process.
|
/// stack, otherwise uses the destination process.
|
||||||
///
|
///
|
||||||
|
@ -182,8 +206,13 @@ impl Transaction {
|
||||||
let process = self.to.clone();
|
let process = self.to.clone();
|
||||||
let mut process_inner = process.inner.lock();
|
let mut process_inner = process.inner.lock();
|
||||||
|
|
||||||
|
self.set_outstanding(&mut process_inner);
|
||||||
|
|
||||||
if oneway {
|
if oneway {
|
||||||
if let Some(target_node) = self.target_node.clone() {
|
if let Some(target_node) = self.target_node.clone() {
|
||||||
|
if process_inner.is_frozen {
|
||||||
|
process_inner.async_recv = true;
|
||||||
|
}
|
||||||
match target_node.submit_oneway(self, &mut process_inner) {
|
match target_node.submit_oneway(self, &mut process_inner) {
|
||||||
Ok(()) => return Ok(()),
|
Ok(()) => return Ok(()),
|
||||||
Err((err, work)) => {
|
Err((err, work)) => {
|
||||||
|
@ -198,6 +227,11 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if process_inner.is_frozen {
|
||||||
|
process_inner.sync_recv = true;
|
||||||
|
return Err(BinderError::new_frozen());
|
||||||
|
}
|
||||||
|
|
||||||
let res = if let Some(thread) = self.find_target_thread() {
|
let res = if let Some(thread) = self.find_target_thread() {
|
||||||
match thread.push_work(self) {
|
match thread.push_work(self) {
|
||||||
PushWorkRes::Ok => Ok(()),
|
PushWorkRes::Ok => Ok(()),
|
||||||
|
@ -242,6 +276,7 @@ impl DeliverToRead for Transaction {
|
||||||
let reply = Err(BR_FAILED_REPLY);
|
let reply = Err(BR_FAILED_REPLY);
|
||||||
self.from.deliver_reply(reply, &self);
|
self.from.deliver_reply(reply, &self);
|
||||||
}
|
}
|
||||||
|
self.drop_outstanding_txn();
|
||||||
});
|
});
|
||||||
let files = if let Ok(list) = self.prepare_file_list() {
|
let files = if let Ok(list) = self.prepare_file_list() {
|
||||||
list
|
list
|
||||||
|
@ -302,6 +337,8 @@ impl DeliverToRead for Transaction {
|
||||||
// It is now the user's responsibility to clear the allocation.
|
// It is now the user's responsibility to clear the allocation.
|
||||||
alloc.keep_alive();
|
alloc.keep_alive();
|
||||||
|
|
||||||
|
self.drop_outstanding_txn();
|
||||||
|
|
||||||
// When this is not a reply and not a oneway transaction, update `current_transaction`. If
|
// When this is not a reply and not a oneway transaction, update `current_transaction`. If
|
||||||
// it's a reply, `current_transaction` has already been updated appropriately.
|
// it's a reply, `current_transaction` has already been updated appropriately.
|
||||||
if self.target_node.is_some() && tr_sec.transaction_data.flags & TF_ONE_WAY == 0 {
|
if self.target_node.is_some() && tr_sec.transaction_data.flags & TF_ONE_WAY == 0 {
|
||||||
|
@ -319,9 +356,18 @@ impl DeliverToRead for Transaction {
|
||||||
let reply = Err(BR_DEAD_REPLY);
|
let reply = Err(BR_DEAD_REPLY);
|
||||||
self.from.deliver_reply(reply, &self);
|
self.from.deliver_reply(reply, &self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.drop_outstanding_txn();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_sync_wakeup(&self) -> bool {
|
fn should_sync_wakeup(&self) -> bool {
|
||||||
self.flags & TF_ONE_WAY == 0
|
self.flags & TF_ONE_WAY == 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pinned_drop]
|
||||||
|
impl PinnedDrop for Transaction {
|
||||||
|
fn drop(self: Pin<&mut Self>) {
|
||||||
|
self.drop_outstanding_txn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,9 @@ use crate::{
|
||||||
init::PinInit,
|
init::PinInit,
|
||||||
pin_init,
|
pin_init,
|
||||||
str::CStr,
|
str::CStr,
|
||||||
task::{MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE, TASK_NORMAL, TASK_UNINTERRUPTIBLE},
|
task::{
|
||||||
|
MAX_SCHEDULE_TIMEOUT, TASK_FREEZABLE, TASK_INTERRUPTIBLE, TASK_NORMAL, TASK_UNINTERRUPTIBLE,
|
||||||
|
},
|
||||||
time::Jiffies,
|
time::Jiffies,
|
||||||
types::Opaque,
|
types::Opaque,
|
||||||
};
|
};
|
||||||
|
@ -159,6 +161,20 @@ impl CondVar {
|
||||||
crate::current!().signal_pending()
|
crate::current!().signal_pending()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Releases the lock and waits for a notification in interruptible and freezable mode.
|
||||||
|
#[must_use = "wait returns if a signal is pending, so the caller must check the return value"]
|
||||||
|
pub fn wait_interruptible_freezable<T: ?Sized, B: Backend>(
|
||||||
|
&self,
|
||||||
|
guard: &mut Guard<'_, T, B>,
|
||||||
|
) -> bool {
|
||||||
|
self.wait_internal(
|
||||||
|
TASK_INTERRUPTIBLE | TASK_FREEZABLE,
|
||||||
|
guard,
|
||||||
|
MAX_SCHEDULE_TIMEOUT,
|
||||||
|
);
|
||||||
|
crate::current!().signal_pending()
|
||||||
|
}
|
||||||
|
|
||||||
/// Releases the lock and waits for a notification in interruptible mode.
|
/// Releases the lock and waits for a notification in interruptible mode.
|
||||||
///
|
///
|
||||||
/// Atomically releases the given lock (whose ownership is proven by the guard) and puts the
|
/// Atomically releases the given lock (whose ownership is proven by the guard) and puts the
|
||||||
|
|
|
@ -22,6 +22,8 @@ pub const MAX_SCHEDULE_TIMEOUT: c_long = c_long::MAX;
|
||||||
pub const TASK_INTERRUPTIBLE: c_int = bindings::TASK_INTERRUPTIBLE as c_int;
|
pub const TASK_INTERRUPTIBLE: c_int = bindings::TASK_INTERRUPTIBLE as c_int;
|
||||||
/// Bitmask for tasks that are sleeping in an uninterruptible state.
|
/// Bitmask for tasks that are sleeping in an uninterruptible state.
|
||||||
pub const TASK_UNINTERRUPTIBLE: c_int = bindings::TASK_UNINTERRUPTIBLE as c_int;
|
pub const TASK_UNINTERRUPTIBLE: c_int = bindings::TASK_UNINTERRUPTIBLE as c_int;
|
||||||
|
/// Bitmask for tasks that are sleeping in a freezable state.
|
||||||
|
pub const TASK_FREEZABLE: c_int = bindings::TASK_FREEZABLE as c_int;
|
||||||
/// Convenience constant for waking up tasks regardless of whether they are in interruptible or
|
/// Convenience constant for waking up tasks regardless of whether they are in interruptible or
|
||||||
/// uninterruptible sleep.
|
/// uninterruptible sleep.
|
||||||
pub const TASK_NORMAL: c_uint = bindings::TASK_NORMAL as c_uint;
|
pub const TASK_NORMAL: c_uint = bindings::TASK_NORMAL as c_uint;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user