Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Binder: add TF_UPDATE_TXN to replace outdated txn

When the target process is busy, incoming oneway transactions are
queued in the async_todo list. If the clients continue sending extra
oneway transactions while the target process is frozen, this queue can
become too large to accommodate new transactions. That's why binder
driver introduced ONEWAY_SPAM_DETECTION to detect this situation. It's
helpful to debug the async binder buffer exhausting issue, but the
issue itself isn't solved directly.

In real cases applications are designed to send oneway transactions
repeatedly, delivering updated inforamtion to the target process.
Typical examples are Wi-Fi signal strength and some real time sensor
data. Even if the apps might only care about the lastet information,
all outdated oneway transactions are still accumulated there until the
frozen process is thawed later. For this kind of situations, there's
no existing method to skip those outdated transactions and deliver the
latest one only.

This patch introduces a new transaction flag TF_UPDATE_TXN. To use it,
use apps can set this new flag along with TF_ONE_WAY. When such an
oneway transaction is to be queued into the async_todo list of a frozen
process, binder driver will check if any previous pending transactions
can be superseded by comparing their code, flags and target node. If
such an outdated pending transaction is found, the latest transaction
will supersede that outdated one. This effectively prevents the async
binder buffer running out and saves unnecessary binder read workloads.

Acked-by: Todd Kjos <tkjos@google.com>
Signed-off-by: Li Li <dualli@google.com>
Link: https://lore.kernel.org/r/20220526220018.3334775-2-dualli@chromium.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Li Li and committed by
Greg Kroah-Hartman
9864bb48 5afbfa8c

+87 -3
+82 -3
drivers/android/binder.c
··· 2627 2627 } 2628 2628 2629 2629 /** 2630 + * binder_can_update_transaction() - Can a txn be superseded by an updated one? 2631 + * @t1: the pending async txn in the frozen process 2632 + * @t2: the new async txn to supersede the outdated pending one 2633 + * 2634 + * Return: true if t2 can supersede t1 2635 + * false if t2 can not supersede t1 2636 + */ 2637 + static bool binder_can_update_transaction(struct binder_transaction *t1, 2638 + struct binder_transaction *t2) 2639 + { 2640 + if ((t1->flags & t2->flags & (TF_ONE_WAY | TF_UPDATE_TXN)) != 2641 + (TF_ONE_WAY | TF_UPDATE_TXN) || !t1->to_proc || !t2->to_proc) 2642 + return false; 2643 + if (t1->to_proc->tsk == t2->to_proc->tsk && t1->code == t2->code && 2644 + t1->flags == t2->flags && t1->buffer->pid == t2->buffer->pid && 2645 + t1->buffer->target_node->ptr == t2->buffer->target_node->ptr && 2646 + t1->buffer->target_node->cookie == t2->buffer->target_node->cookie) 2647 + return true; 2648 + return false; 2649 + } 2650 + 2651 + /** 2652 + * binder_find_outdated_transaction_ilocked() - Find the outdated transaction 2653 + * @t: new async transaction 2654 + * @target_list: list to find outdated transaction 2655 + * 2656 + * Return: the outdated transaction if found 2657 + * NULL if no outdated transacton can be found 2658 + * 2659 + * Requires the proc->inner_lock to be held. 2660 + */ 2661 + static struct binder_transaction * 2662 + binder_find_outdated_transaction_ilocked(struct binder_transaction *t, 2663 + struct list_head *target_list) 2664 + { 2665 + struct binder_work *w; 2666 + 2667 + list_for_each_entry(w, target_list, entry) { 2668 + struct binder_transaction *t_queued; 2669 + 2670 + if (w->type != BINDER_WORK_TRANSACTION) 2671 + continue; 2672 + t_queued = container_of(w, struct binder_transaction, work); 2673 + if (binder_can_update_transaction(t_queued, t)) 2674 + return t_queued; 2675 + } 2676 + return NULL; 2677 + } 2678 + 2679 + /** 2630 2680 * binder_proc_transaction() - sends a transaction to a process and wakes it up 2631 2681 * @t: transaction to send 2632 2682 * @proc: process to send the transaction to ··· 2701 2651 struct binder_node *node = t->buffer->target_node; 2702 2652 bool oneway = !!(t->flags & TF_ONE_WAY); 2703 2653 bool pending_async = false; 2654 + struct binder_transaction *t_outdated = NULL; 2704 2655 2705 2656 BUG_ON(!node); 2706 2657 binder_node_lock(node); ··· 2729 2678 if (!thread && !pending_async) 2730 2679 thread = binder_select_thread_ilocked(proc); 2731 2680 2732 - if (thread) 2681 + if (thread) { 2733 2682 binder_enqueue_thread_work_ilocked(thread, &t->work); 2734 - else if (!pending_async) 2683 + } else if (!pending_async) { 2735 2684 binder_enqueue_work_ilocked(&t->work, &proc->todo); 2736 - else 2685 + } else { 2686 + if ((t->flags & TF_UPDATE_TXN) && proc->is_frozen) { 2687 + t_outdated = binder_find_outdated_transaction_ilocked(t, 2688 + &node->async_todo); 2689 + if (t_outdated) { 2690 + binder_debug(BINDER_DEBUG_TRANSACTION, 2691 + "txn %d supersedes %d\n", 2692 + t->debug_id, t_outdated->debug_id); 2693 + list_del_init(&t_outdated->work.entry); 2694 + proc->outstanding_txns--; 2695 + } 2696 + } 2737 2697 binder_enqueue_work_ilocked(&t->work, &node->async_todo); 2698 + } 2738 2699 2739 2700 if (!pending_async) 2740 2701 binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */); ··· 2754 2691 proc->outstanding_txns++; 2755 2692 binder_inner_proc_unlock(proc); 2756 2693 binder_node_unlock(node); 2694 + 2695 + /* 2696 + * To reduce potential contention, free the outdated transaction and 2697 + * buffer after releasing the locks. 2698 + */ 2699 + if (t_outdated) { 2700 + struct binder_buffer *buffer = t_outdated->buffer; 2701 + 2702 + t_outdated->buffer = NULL; 2703 + buffer->transaction = NULL; 2704 + trace_binder_transaction_update_buffer_release(buffer); 2705 + binder_transaction_buffer_release(proc, NULL, buffer, 0, 0); 2706 + binder_alloc_free_buf(&proc->alloc, buffer); 2707 + kfree(t_outdated); 2708 + binder_stats_deleted(BINDER_STAT_TRANSACTION); 2709 + } 2757 2710 2758 2711 return 0; 2759 2712 }
+4
drivers/android/binder_trace.h
··· 311 311 TP_PROTO(struct binder_buffer *buffer), 312 312 TP_ARGS(buffer)); 313 313 314 + DEFINE_EVENT(binder_buffer_class, binder_transaction_update_buffer_release, 315 + TP_PROTO(struct binder_buffer *buffer), 316 + TP_ARGS(buffer)); 317 + 314 318 TRACE_EVENT(binder_update_page_range, 315 319 TP_PROTO(struct binder_alloc *alloc, bool allocate, 316 320 void __user *start, void __user *end),
+1
include/uapi/linux/android/binder.h
··· 287 287 TF_STATUS_CODE = 0x08, /* contents are a 32-bit status code */ 288 288 TF_ACCEPT_FDS = 0x10, /* allow replies with file descriptors */ 289 289 TF_CLEAR_BUF = 0x20, /* clear buffer on txn complete */ 290 + TF_UPDATE_TXN = 0x40, /* update the outdated pending async txn */ 290 291 }; 291 292 292 293 struct binder_transaction_data {