History

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.

Li Li committed 4 years ago

Commit 9864bb41 parent 5afbfa83 files · +87 −3Browse files

Unified · Split

3 files
  1. drivers/android/binder.c +82 −3
  2. drivers/android/binder_trace.h +4 −0
  3. include/uapi/linux/android/binder.h +1 −0
drivers/android/binder.c+82−3
@@ -2626,6 +2626,56 @@ static int binder_fixup_parent(struct list_head *pf_head, · 2625 unmodified lines
26262626 return binder_add_fixup(pf_head, buffer_offset, bp->buffer, 0);
26272627 }
26282628
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+
26292679 /**
26302680 * binder_proc_transaction() - sends a transaction to a process and wakes it up
26312681 * @t: transaction to send
@@ -2651,6 +2701,7 @@ static int binder_proc_transaction(struct binder_transaction *t, · 19 unmodified lines
26512701 struct binder_node *node = t->buffer->target_node;
26522702 bool oneway = !!(t->flags & TF_ONE_WAY);
26532703 bool pending_async = false;
2704+ struct binder_transaction *t_outdated = NULL;
26542705
26552706 BUG_ON(!node);
26562707 binder_node_lock(node);
@@ -2678,12 +2729,24 @@ static int binder_proc_transaction(struct binder_transaction *t, · 21 unmodified lines
26782729 if (!thread && !pending_async)
26792730 thread = binder_select_thread_ilocked(proc);
26802731
2681− if (thread)
2732+ if (thread) {
26822733 binder_enqueue_thread_work_ilocked(thread, &t->work);
2683− else if (!pending_async)
2734+ } else if (!pending_async) {
26842735 binder_enqueue_work_ilocked(&t->work, &proc->todo);
2685− else
2736+ } else {
2737+ if ((t->flags & TF_UPDATE_TXN) && proc->is_frozen) {
2738+ t_outdated = binder_find_outdated_transaction_ilocked(t,
2739+ &node->async_todo);
2740+ if (t_outdated) {
2741+ binder_debug(BINDER_DEBUG_TRANSACTION,
2742+ "txn %d supersedes %d\n",
2743+ t->debug_id, t_outdated->debug_id);
2744+ list_del_init(&t_outdated->work.entry);
2745+ proc->outstanding_txns--;
2746+ }
2747+ }
26862748 binder_enqueue_work_ilocked(&t->work, &node->async_todo);
2749+ }
26872750
26882751 if (!pending_async)
26892752 binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */);
@@ -2692,6 +2755,22 @@ static int binder_proc_transaction(struct binder_transaction *t, · 2 unmodified lines
26922755 binder_inner_proc_unlock(proc);
26932756 binder_node_unlock(node);
26942757
2758+ /*
2759+ * To reduce potential contention, free the outdated transaction and
2760+ * buffer after releasing the locks.
2761+ */
2762+ if (t_outdated) {
2763+ struct binder_buffer *buffer = t_outdated->buffer;
2764+
2765+ t_outdated->buffer = NULL;
2766+ buffer->transaction = NULL;
2767+ trace_binder_transaction_update_buffer_release(buffer);
2768+ binder_transaction_buffer_release(proc, NULL, buffer, 0, 0);
2769+ binder_alloc_free_buf(&proc->alloc, buffer);
2770+ kfree(t_outdated);
2771+ binder_stats_deleted(BINDER_STAT_TRANSACTION);
2772+ }
2773+
26952774 return 0;
26962775 }
26972776
drivers/android/binder_trace.h+4−0
@@ -311,6 +311,10 @@ DEFINE_EVENT(binder_buffer_class, binder_transaction_failed_buffer_release, · 310 unmodified lines
311311 TP_PROTO(struct binder_buffer *buffer),
312312 TP_ARGS(buffer));
313313
314+DEFINE_EVENT(binder_buffer_class, binder_transaction_update_buffer_release,
315+ TP_PROTO(struct binder_buffer *buffer),
316+ TP_ARGS(buffer));
317+
314318 TRACE_EVENT(binder_update_page_range,
315319 TP_PROTO(struct binder_alloc *alloc, bool allocate,
316320 void __user *start, void __user *end),
include/uapi/linux/android/binder.h+1−0
@@ -287,6 +287,7 @@ enum transaction_flags { · 286 unmodified lines
287287 TF_STATUS_CODE = 0x08, /* contents are a 32-bit status code */
288288 TF_ACCEPT_FDS = 0x10, /* allow replies with file descriptors */
289289 TF_CLEAR_BUF = 0x20, /* clear buffer on txn complete */
290+ TF_UPDATE_TXN = 0x40, /* update the outdated pending async txn */
290291 };
291292
292293 struct binder_transaction_data {