| @@ -2626,6 +2626,56 @@ static int binder_fixup_parent(struct list_head *pf_head, · 2625 unmodified lines |
| 2626 | 2626 | return binder_add_fixup(pf_head, buffer_offset, bp->buffer, 0); |
| 2627 | 2627 | } |
| 2628 | 2628 | |
| 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 | + |
| 2629 | 2679 | /** |
| 2630 | 2680 | * binder_proc_transaction() - sends a transaction to a process and wakes it up |
| 2631 | 2681 | * @t: transaction to send |
| @@ -2651,6 +2701,7 @@ static int binder_proc_transaction(struct binder_transaction *t, · 19 unmodified lines |
| 2651 | 2701 | struct binder_node *node = t->buffer->target_node; |
| 2652 | 2702 | bool oneway = !!(t->flags & TF_ONE_WAY); |
| 2653 | 2703 | bool pending_async = false; |
| 2704 | + struct binder_transaction *t_outdated = NULL; |
| 2654 | 2705 | |
| 2655 | 2706 | BUG_ON(!node); |
| 2656 | 2707 | binder_node_lock(node); |
| @@ -2678,12 +2729,24 @@ static int binder_proc_transaction(struct binder_transaction *t, · 21 unmodified lines |
| 2678 | 2729 | if (!thread && !pending_async) |
| 2679 | 2730 | thread = binder_select_thread_ilocked(proc); |
| 2680 | 2731 | |
| 2681 | | − if (thread) |
| 2732 | + if (thread) { |
| 2682 | 2733 | binder_enqueue_thread_work_ilocked(thread, &t->work); |
| 2683 | | − else if (!pending_async) |
| 2734 | + } else if (!pending_async) { |
| 2684 | 2735 | 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 | + } |
| 2686 | 2748 | binder_enqueue_work_ilocked(&t->work, &node->async_todo); |
| 2749 | + } |
| 2687 | 2750 | |
| 2688 | 2751 | if (!pending_async) |
| 2689 | 2752 | binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */); |
| @@ -2692,6 +2755,22 @@ static int binder_proc_transaction(struct binder_transaction *t, · 2 unmodified lines |
| 2692 | 2755 | binder_inner_proc_unlock(proc); |
| 2693 | 2756 | binder_node_unlock(node); |
| 2694 | 2757 | |
| 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 | + |
| 2695 | 2774 | return 0; |
| 2696 | 2775 | } |
| 2697 | 2776 | |