Skip to content

Commit d579b04

Browse files
Yu-Ting Tsenggregkh
Yu-Ting Tseng
authored andcommittedJul 31, 2024
binder: frozen notification
Frozen processes present a significant challenge in binder transactions. When a process is frozen, it cannot, by design, accept and/or respond to binder transactions. As a result, the sender needs to adjust its behavior, such as postponing transactions until the peer process unfreezes. However, there is currently no way to subscribe to these state change events, making it impossible to implement frozen-aware behaviors efficiently. Introduce a binder API for subscribing to frozen state change events. This allows programs to react to changes in peer process state, mitigating issues related to binder transactions sent to frozen processes. Implementation details: For a given binder_ref, the state of frozen notification can be one of the followings: 1. Userspace doesn't want a notification. binder_ref->freeze is null. 2. Userspace wants a notification but none is in flight. list_empty(&binder_ref->freeze->work.entry) = true 3. A notification is in flight and waiting to be read by userspace. binder_ref_freeze.sent is false. 4. A notification was read by userspace and kernel is waiting for an ack. binder_ref_freeze.sent is true. When a notification is in flight, new state change events are coalesced into the existing binder_ref_freeze struct. If userspace hasn't picked up the notification yet, the driver simply rewrites the state. Otherwise, the notification is flagged as requiring a resend, which will be performed once userspace acks the original notification that's inflight. See https://r.android.com/3070045 for how userspace is going to use this feature. Signed-off-by: Yu-Ting Tseng <yutingtseng@google.com> Acked-by: Carlos Llamas <cmllamas@google.com> Link: https://lore.kernel.org/r/20240709070047.4055369-4-yutingtseng@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

File tree

3 files changed

+337
-4
lines changed

3 files changed

+337
-4
lines changed
 

‎drivers/android/binder.c

+282-2
Original file line numberDiff line numberDiff line change
@@ -1355,6 +1355,7 @@ static void binder_free_ref(struct binder_ref *ref)
13551355
if (ref->node)
13561356
binder_free_node(ref->node);
13571357
kfree(ref->death);
1358+
kfree(ref->freeze);
13581359
kfree(ref);
13591360
}
13601361

@@ -3844,6 +3845,155 @@ static void binder_transaction(struct binder_proc *proc,
38443845
}
38453846
}
38463847

3848+
static int
3849+
binder_request_freeze_notification(struct binder_proc *proc,
3850+
struct binder_thread *thread,
3851+
struct binder_handle_cookie *handle_cookie)
3852+
{
3853+
struct binder_ref_freeze *freeze;
3854+
struct binder_ref *ref;
3855+
bool is_frozen;
3856+
3857+
freeze = kzalloc(sizeof(*freeze), GFP_KERNEL);
3858+
if (!freeze)
3859+
return -ENOMEM;
3860+
binder_proc_lock(proc);
3861+
ref = binder_get_ref_olocked(proc, handle_cookie->handle, false);
3862+
if (!ref) {
3863+
binder_user_error("%d:%d BC_REQUEST_FREEZE_NOTIFICATION invalid ref %d\n",
3864+
proc->pid, thread->pid, handle_cookie->handle);
3865+
binder_proc_unlock(proc);
3866+
kfree(freeze);
3867+
return -EINVAL;
3868+
}
3869+
3870+
binder_node_lock(ref->node);
3871+
3872+
if (ref->freeze || !ref->node->proc) {
3873+
binder_user_error("%d:%d invalid BC_REQUEST_FREEZE_NOTIFICATION %s\n",
3874+
proc->pid, thread->pid,
3875+
ref->freeze ? "already set" : "dead node");
3876+
binder_node_unlock(ref->node);
3877+
binder_proc_unlock(proc);
3878+
kfree(freeze);
3879+
return -EINVAL;
3880+
}
3881+
binder_inner_proc_lock(ref->node->proc);
3882+
is_frozen = ref->node->proc->is_frozen;
3883+
binder_inner_proc_unlock(ref->node->proc);
3884+
3885+
binder_stats_created(BINDER_STAT_FREEZE);
3886+
INIT_LIST_HEAD(&freeze->work.entry);
3887+
freeze->cookie = handle_cookie->cookie;
3888+
freeze->work.type = BINDER_WORK_FROZEN_BINDER;
3889+
freeze->is_frozen = is_frozen;
3890+
3891+
ref->freeze = freeze;
3892+
3893+
binder_inner_proc_lock(proc);
3894+
binder_enqueue_work_ilocked(&ref->freeze->work, &proc->todo);
3895+
binder_wakeup_proc_ilocked(proc);
3896+
binder_inner_proc_unlock(proc);
3897+
3898+
binder_node_unlock(ref->node);
3899+
binder_proc_unlock(proc);
3900+
return 0;
3901+
}
3902+
3903+
static int
3904+
binder_clear_freeze_notification(struct binder_proc *proc,
3905+
struct binder_thread *thread,
3906+
struct binder_handle_cookie *handle_cookie)
3907+
{
3908+
struct binder_ref_freeze *freeze;
3909+
struct binder_ref *ref;
3910+
3911+
binder_proc_lock(proc);
3912+
ref = binder_get_ref_olocked(proc, handle_cookie->handle, false);
3913+
if (!ref) {
3914+
binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION invalid ref %d\n",
3915+
proc->pid, thread->pid, handle_cookie->handle);
3916+
binder_proc_unlock(proc);
3917+
return -EINVAL;
3918+
}
3919+
3920+
binder_node_lock(ref->node);
3921+
3922+
if (!ref->freeze) {
3923+
binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n",
3924+
proc->pid, thread->pid);
3925+
binder_node_unlock(ref->node);
3926+
binder_proc_unlock(proc);
3927+
return -EINVAL;
3928+
}
3929+
freeze = ref->freeze;
3930+
binder_inner_proc_lock(proc);
3931+
if (freeze->cookie != handle_cookie->cookie) {
3932+
binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch %016llx != %016llx\n",
3933+
proc->pid, thread->pid, (u64)freeze->cookie,
3934+
(u64)handle_cookie->cookie);
3935+
binder_inner_proc_unlock(proc);
3936+
binder_node_unlock(ref->node);
3937+
binder_proc_unlock(proc);
3938+
return -EINVAL;
3939+
}
3940+
ref->freeze = NULL;
3941+
/*
3942+
* Take the existing freeze object and overwrite its work type. There are three cases here:
3943+
* 1. No pending notification. In this case just add the work to the queue.
3944+
* 2. A notification was sent and is pending an ack from userspace. Once an ack arrives, we
3945+
* should resend with the new work type.
3946+
* 3. A notification is pending to be sent. Since the work is already in the queue, nothing
3947+
* needs to be done here.
3948+
*/
3949+
freeze->work.type = BINDER_WORK_CLEAR_FREEZE_NOTIFICATION;
3950+
if (list_empty(&freeze->work.entry)) {
3951+
binder_enqueue_work_ilocked(&freeze->work, &proc->todo);
3952+
binder_wakeup_proc_ilocked(proc);
3953+
} else if (freeze->sent) {
3954+
freeze->resend = true;
3955+
}
3956+
binder_inner_proc_unlock(proc);
3957+
binder_node_unlock(ref->node);
3958+
binder_proc_unlock(proc);
3959+
return 0;
3960+
}
3961+
3962+
static int
3963+
binder_freeze_notification_done(struct binder_proc *proc,
3964+
struct binder_thread *thread,
3965+
binder_uintptr_t cookie)
3966+
{
3967+
struct binder_ref_freeze *freeze = NULL;
3968+
struct binder_work *w;
3969+
3970+
binder_inner_proc_lock(proc);
3971+
list_for_each_entry(w, &proc->delivered_freeze, entry) {
3972+
struct binder_ref_freeze *tmp_freeze =
3973+
container_of(w, struct binder_ref_freeze, work);
3974+
3975+
if (tmp_freeze->cookie == cookie) {
3976+
freeze = tmp_freeze;
3977+
break;
3978+
}
3979+
}
3980+
if (!freeze) {
3981+
binder_user_error("%d:%d BC_FREEZE_NOTIFICATION_DONE %016llx not found\n",
3982+
proc->pid, thread->pid, (u64)cookie);
3983+
binder_inner_proc_unlock(proc);
3984+
return -EINVAL;
3985+
}
3986+
binder_dequeue_work_ilocked(&freeze->work);
3987+
freeze->sent = false;
3988+
if (freeze->resend) {
3989+
freeze->resend = false;
3990+
binder_enqueue_work_ilocked(&freeze->work, &proc->todo);
3991+
binder_wakeup_proc_ilocked(proc);
3992+
}
3993+
binder_inner_proc_unlock(proc);
3994+
return 0;
3995+
}
3996+
38473997
/**
38483998
* binder_free_buf() - free the specified buffer
38493999
* @proc: binder proc that owns buffer
@@ -4327,6 +4477,44 @@ static int binder_thread_write(struct binder_proc *proc,
43274477
binder_inner_proc_unlock(proc);
43284478
} break;
43294479

4480+
case BC_REQUEST_FREEZE_NOTIFICATION: {
4481+
struct binder_handle_cookie handle_cookie;
4482+
int error;
4483+
4484+
if (copy_from_user(&handle_cookie, ptr, sizeof(handle_cookie)))
4485+
return -EFAULT;
4486+
ptr += sizeof(handle_cookie);
4487+
error = binder_request_freeze_notification(proc, thread,
4488+
&handle_cookie);
4489+
if (error)
4490+
return error;
4491+
} break;
4492+
4493+
case BC_CLEAR_FREEZE_NOTIFICATION: {
4494+
struct binder_handle_cookie handle_cookie;
4495+
int error;
4496+
4497+
if (copy_from_user(&handle_cookie, ptr, sizeof(handle_cookie)))
4498+
return -EFAULT;
4499+
ptr += sizeof(handle_cookie);
4500+
error = binder_clear_freeze_notification(proc, thread, &handle_cookie);
4501+
if (error)
4502+
return error;
4503+
} break;
4504+
4505+
case BC_FREEZE_NOTIFICATION_DONE: {
4506+
binder_uintptr_t cookie;
4507+
int error;
4508+
4509+
if (get_user(cookie, (binder_uintptr_t __user *)ptr))
4510+
return -EFAULT;
4511+
4512+
ptr += sizeof(cookie);
4513+
error = binder_freeze_notification_done(proc, thread, cookie);
4514+
if (error)
4515+
return error;
4516+
} break;
4517+
43304518
default:
43314519
pr_err("%d:%d unknown command %u\n",
43324520
proc->pid, thread->pid, cmd);
@@ -4716,6 +4904,46 @@ static int binder_thread_read(struct binder_proc *proc,
47164904
if (cmd == BR_DEAD_BINDER)
47174905
goto done; /* DEAD_BINDER notifications can cause transactions */
47184906
} break;
4907+
4908+
case BINDER_WORK_FROZEN_BINDER: {
4909+
struct binder_ref_freeze *freeze;
4910+
struct binder_frozen_state_info info;
4911+
4912+
memset(&info, 0, sizeof(info));
4913+
freeze = container_of(w, struct binder_ref_freeze, work);
4914+
info.is_frozen = freeze->is_frozen;
4915+
info.cookie = freeze->cookie;
4916+
freeze->sent = true;
4917+
binder_enqueue_work_ilocked(w, &proc->delivered_freeze);
4918+
binder_inner_proc_unlock(proc);
4919+
4920+
if (put_user(BR_FROZEN_BINDER, (uint32_t __user *)ptr))
4921+
return -EFAULT;
4922+
ptr += sizeof(uint32_t);
4923+
if (copy_to_user(ptr, &info, sizeof(info)))
4924+
return -EFAULT;
4925+
ptr += sizeof(info);
4926+
binder_stat_br(proc, thread, BR_FROZEN_BINDER);
4927+
goto done; /* BR_FROZEN_BINDER notifications can cause transactions */
4928+
} break;
4929+
4930+
case BINDER_WORK_CLEAR_FREEZE_NOTIFICATION: {
4931+
struct binder_ref_freeze *freeze =
4932+
container_of(w, struct binder_ref_freeze, work);
4933+
binder_uintptr_t cookie = freeze->cookie;
4934+
4935+
binder_inner_proc_unlock(proc);
4936+
kfree(freeze);
4937+
binder_stats_deleted(BINDER_STAT_FREEZE);
4938+
if (put_user(BR_CLEAR_FREEZE_NOTIFICATION_DONE, (uint32_t __user *)ptr))
4939+
return -EFAULT;
4940+
ptr += sizeof(uint32_t);
4941+
if (put_user(cookie, (binder_uintptr_t __user *)ptr))
4942+
return -EFAULT;
4943+
ptr += sizeof(binder_uintptr_t);
4944+
binder_stat_br(proc, thread, BR_CLEAR_FREEZE_NOTIFICATION_DONE);
4945+
} break;
4946+
47194947
default:
47204948
binder_inner_proc_unlock(proc);
47214949
pr_err("%d:%d: bad work type %d\n",
@@ -5324,6 +5552,48 @@ static bool binder_txns_pending_ilocked(struct binder_proc *proc)
53245552
return false;
53255553
}
53265554

5555+
static void binder_add_freeze_work(struct binder_proc *proc, bool is_frozen)
5556+
{
5557+
struct rb_node *n;
5558+
struct binder_ref *ref;
5559+
5560+
binder_inner_proc_lock(proc);
5561+
for (n = rb_first(&proc->nodes); n; n = rb_next(n)) {
5562+
struct binder_node *node;
5563+
5564+
node = rb_entry(n, struct binder_node, rb_node);
5565+
binder_inner_proc_unlock(proc);
5566+
binder_node_lock(node);
5567+
hlist_for_each_entry(ref, &node->refs, node_entry) {
5568+
/*
5569+
* Need the node lock to synchronize
5570+
* with new notification requests and the
5571+
* inner lock to synchronize with queued
5572+
* freeze notifications.
5573+
*/
5574+
binder_inner_proc_lock(ref->proc);
5575+
if (!ref->freeze) {
5576+
binder_inner_proc_unlock(ref->proc);
5577+
continue;
5578+
}
5579+
ref->freeze->work.type = BINDER_WORK_FROZEN_BINDER;
5580+
if (list_empty(&ref->freeze->work.entry)) {
5581+
ref->freeze->is_frozen = is_frozen;
5582+
binder_enqueue_work_ilocked(&ref->freeze->work, &ref->proc->todo);
5583+
binder_wakeup_proc_ilocked(ref->proc);
5584+
} else {
5585+
if (ref->freeze->sent && ref->freeze->is_frozen != is_frozen)
5586+
ref->freeze->resend = true;
5587+
ref->freeze->is_frozen = is_frozen;
5588+
}
5589+
binder_inner_proc_unlock(ref->proc);
5590+
}
5591+
binder_node_unlock(node);
5592+
binder_inner_proc_lock(proc);
5593+
}
5594+
binder_inner_proc_unlock(proc);
5595+
}
5596+
53275597
static int binder_ioctl_freeze(struct binder_freeze_info *info,
53285598
struct binder_proc *target_proc)
53295599
{
@@ -5335,6 +5605,7 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info,
53355605
target_proc->async_recv = false;
53365606
target_proc->is_frozen = false;
53375607
binder_inner_proc_unlock(target_proc);
5608+
binder_add_freeze_work(target_proc, false);
53385609
return 0;
53395610
}
53405611

@@ -5367,6 +5638,8 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info,
53675638
binder_inner_proc_lock(target_proc);
53685639
target_proc->is_frozen = false;
53695640
binder_inner_proc_unlock(target_proc);
5641+
} else {
5642+
binder_add_freeze_work(target_proc, true);
53705643
}
53715644

53725645
return ret;
@@ -5742,6 +6015,7 @@ static int binder_open(struct inode *nodp, struct file *filp)
57426015
binder_stats_created(BINDER_STAT_PROC);
57436016
proc->pid = current->group_leader->pid;
57446017
INIT_LIST_HEAD(&proc->delivered_death);
6018+
INIT_LIST_HEAD(&proc->delivered_freeze);
57456019
INIT_LIST_HEAD(&proc->waiting_threads);
57466020
filp->private_data = proc;
57476021

@@ -6293,7 +6567,9 @@ static const char * const binder_return_strings[] = {
62936567
"BR_FAILED_REPLY",
62946568
"BR_FROZEN_REPLY",
62956569
"BR_ONEWAY_SPAM_SUSPECT",
6296-
"BR_TRANSACTION_PENDING_FROZEN"
6570+
"BR_TRANSACTION_PENDING_FROZEN",
6571+
"BR_FROZEN_BINDER",
6572+
"BR_CLEAR_FREEZE_NOTIFICATION_DONE",
62976573
};
62986574

62996575
static const char * const binder_command_strings[] = {
@@ -6316,6 +6592,9 @@ static const char * const binder_command_strings[] = {
63166592
"BC_DEAD_BINDER_DONE",
63176593
"BC_TRANSACTION_SG",
63186594
"BC_REPLY_SG",
6595+
"BC_REQUEST_FREEZE_NOTIFICATION",
6596+
"BC_CLEAR_FREEZE_NOTIFICATION",
6597+
"BC_FREEZE_NOTIFICATION_DONE",
63196598
};
63206599

63216600
static const char * const binder_objstat_strings[] = {
@@ -6325,7 +6604,8 @@ static const char * const binder_objstat_strings[] = {
63256604
"ref",
63266605
"death",
63276606
"transaction",
6328-
"transaction_complete"
6607+
"transaction_complete",
6608+
"freeze",
63296609
};
63306610

63316611
static void print_binder_stats(struct seq_file *m, const char *prefix,

‎drivers/android/binder_internal.h

+19-2
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,13 @@ enum binder_stat_types {
130130
BINDER_STAT_DEATH,
131131
BINDER_STAT_TRANSACTION,
132132
BINDER_STAT_TRANSACTION_COMPLETE,
133+
BINDER_STAT_FREEZE,
133134
BINDER_STAT_COUNT
134135
};
135136

136137
struct binder_stats {
137-
atomic_t br[_IOC_NR(BR_TRANSACTION_PENDING_FROZEN) + 1];
138-
atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1];
138+
atomic_t br[_IOC_NR(BR_CLEAR_FREEZE_NOTIFICATION_DONE) + 1];
139+
atomic_t bc[_IOC_NR(BC_FREEZE_NOTIFICATION_DONE) + 1];
139140
atomic_t obj_created[BINDER_STAT_COUNT];
140141
atomic_t obj_deleted[BINDER_STAT_COUNT];
141142
};
@@ -160,6 +161,8 @@ struct binder_work {
160161
BINDER_WORK_DEAD_BINDER,
161162
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
162163
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
164+
BINDER_WORK_FROZEN_BINDER,
165+
BINDER_WORK_CLEAR_FREEZE_NOTIFICATION,
163166
} type;
164167
};
165168

@@ -276,6 +279,14 @@ struct binder_ref_death {
276279
binder_uintptr_t cookie;
277280
};
278281

282+
struct binder_ref_freeze {
283+
struct binder_work work;
284+
binder_uintptr_t cookie;
285+
bool is_frozen:1;
286+
bool sent:1;
287+
bool resend:1;
288+
};
289+
279290
/**
280291
* struct binder_ref_data - binder_ref counts and id
281292
* @debug_id: unique ID for the ref
@@ -308,6 +319,8 @@ struct binder_ref_data {
308319
* @node indicates the node must be freed
309320
* @death: pointer to death notification (ref_death) if requested
310321
* (protected by @node->lock)
322+
* @freeze: pointer to freeze notification (ref_freeze) if requested
323+
* (protected by @node->lock)
311324
*
312325
* Structure to track references from procA to target node (on procB). This
313326
* structure is unsafe to access without holding @proc->outer_lock.
@@ -324,6 +337,7 @@ struct binder_ref {
324337
struct binder_proc *proc;
325338
struct binder_node *node;
326339
struct binder_ref_death *death;
340+
struct binder_ref_freeze *freeze;
327341
};
328342

329343
/**
@@ -377,6 +391,8 @@ struct binder_ref {
377391
* (atomics, no lock needed)
378392
* @delivered_death: list of delivered death notification
379393
* (protected by @inner_lock)
394+
* @delivered_freeze: list of delivered freeze notification
395+
* (protected by @inner_lock)
380396
* @max_threads: cap on number of binder threads
381397
* (protected by @inner_lock)
382398
* @requested_threads: number of binder threads requested but not
@@ -424,6 +440,7 @@ struct binder_proc {
424440
struct list_head todo;
425441
struct binder_stats stats;
426442
struct list_head delivered_death;
443+
struct list_head delivered_freeze;
427444
u32 max_threads;
428445
int requested_threads;
429446
int requested_threads_started;

‎include/uapi/linux/android/binder.h

+36
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,12 @@ struct binder_frozen_status_info {
236236
__u32 async_recv;
237237
};
238238

239+
struct binder_frozen_state_info {
240+
binder_uintptr_t cookie;
241+
__u32 is_frozen;
242+
__u32 reserved;
243+
};
244+
239245
/* struct binder_extened_error - extended error information
240246
* @id: identifier for the failed operation
241247
* @command: command as defined by binder_driver_return_protocol
@@ -467,6 +473,17 @@ enum binder_driver_return_protocol {
467473
/*
468474
* The target of the last async transaction is frozen. No parameters.
469475
*/
476+
477+
BR_FROZEN_BINDER = _IOR('r', 21, struct binder_frozen_state_info),
478+
/*
479+
* The cookie and a boolean (is_frozen) that indicates whether the process
480+
* transitioned into a frozen or an unfrozen state.
481+
*/
482+
483+
BR_CLEAR_FREEZE_NOTIFICATION_DONE = _IOR('r', 22, binder_uintptr_t),
484+
/*
485+
* void *: cookie
486+
*/
470487
};
471488

472489
enum binder_driver_command_protocol {
@@ -550,6 +567,25 @@ enum binder_driver_command_protocol {
550567
/*
551568
* binder_transaction_data_sg: the sent command.
552569
*/
570+
571+
BC_REQUEST_FREEZE_NOTIFICATION =
572+
_IOW('c', 19, struct binder_handle_cookie),
573+
/*
574+
* int: handle
575+
* void *: cookie
576+
*/
577+
578+
BC_CLEAR_FREEZE_NOTIFICATION = _IOW('c', 20,
579+
struct binder_handle_cookie),
580+
/*
581+
* int: handle
582+
* void *: cookie
583+
*/
584+
585+
BC_FREEZE_NOTIFICATION_DONE = _IOW('c', 21, binder_uintptr_t),
586+
/*
587+
* void *: cookie
588+
*/
553589
};
554590

555591
#endif /* _UAPI_LINUX_BINDER_H */

0 commit comments

Comments
 (0)
Please sign in to comment.