From: Bo Zhang <zhangbo0325@gmail.com>
To: aliceryhl@google.com, gregkh@linuxfoundation.org, cmllamas@google.com
Cc: arve@android.com, tkjos@android.com, christian@brauner.io,
surenb@google.com, baohua@kernel.org, zhanghongru06@gmail.com,
linux-kernel@vger.kernel.org, Bo Zhang <zhangbo0325@gmail.com>,
Bo Zhang <zhangbo56@xiaomi.com>
Subject: [RFC PATCH v4 2/2] binder: add install_mutex to serialize page install and shrinker zap
Date: Mon, 7 Sep 2026 21:00:28 +0800 [thread overview]
Message-ID: <20260907130028.807366-3-zhangbo0325@gmail.com> (raw)
In-Reply-To: <20260907130028.807366-1-zhangbo0325@gmail.com>
The previous patch converted alloc->mutex to a spinlock for the hot
path (buffer alloc/free). However, page installation may sleep in
vm_insert_page(), and the shrinker may sleep in zap_vma_range(), so
these cannot be serialized by the spinlock. Without serialization, the
install side could observe and reuse a page that the shrinker is about
to zap and free.
Add a separate install_mutex to serialize page installation against the
shrinker's zap. The two locks have distinct roles:
- alloc->lock (spinlock) exclusively owns the non-sleeping metadata:
pages[], the LRU list, the rb-trees and free_async_space.
- install_mutex only serializes the sleeping PTE operations
(vm_insert_page vs zap_vma_range) for a given alloc.
pages[] and the LRU are always updated together under alloc->lock, so
binder_lru_freelist_del() always observes a consistent state.
The shrinker acquires install_mutex with mutex_trylock() and skips the
page (LRU_SKIP) on failure. This is required because the install side
may hold install_mutex while its vm_insert_page() recurses into direct
reclaim and re-enters this shrinker on the same thread; a blocking
acquire would self-deadlock. Using trylock also keeps the shrinker out
of any blocking lock cycle with mmap_lock, so the install side may take
mmap_lock while holding install_mutex without risking an ABBA deadlock.
Performance (binderThroughputTest, Qualcomm SM8850, 2 workers, 10 runs)
under concurrent drop_caches shows no regression from the install_mutex:
mutex (baseline) spinlock + install_mutex
throughput: 27k-59k iter/s 84k-89k iter/s
average: 0.031-0.068ms 0.021-0.022ms
P99: 0.088-0.148ms 0.046-0.056ms
Signed-off-by: Bo Zhang <zhangbo56@xiaomi.com>
---
drivers/android/binder_alloc.c | 59 +++++++++++++++++++++-------------
drivers/android/binder_alloc.h | 3 ++
2 files changed, 39 insertions(+), 23 deletions(-)
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index 9775df3616aa..61544e3cdae1 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -325,34 +325,34 @@ static int binder_install_single_page(struct binder_alloc *alloc,
goto out;
}
- ret = binder_page_insert(alloc, addr, page);
- switch (ret) {
- case -EBUSY:
- /*
- * EBUSY is ok. Someone installed the pte first but the
- * alloc->pages[index] has not been updated yet. Discard
- * our page and look up the one already installed.
- */
- ret = 0;
+ mutex_lock(&alloc->install_mutex);
+
+ /* Someone may have installed it already; check under alloc->lock */
+ spin_lock(&alloc->lock);
+ if (binder_get_installed_page(alloc, index)) {
+ spin_unlock(&alloc->lock);
+ mutex_unlock(&alloc->install_mutex);
binder_free_page(page);
- page = binder_page_lookup(alloc, addr);
- if (!page) {
- pr_err("%d: failed to find page at offset %lx\n",
- alloc->pid, addr - alloc->vm_start);
- ret = -ESRCH;
- break;
- }
- fallthrough;
- case 0:
- /* Mark page installation complete and safe to use */
- binder_set_installed_page(alloc, index, page);
- break;
- default:
+ ret = 0;
+ goto out;
+ }
+ spin_unlock(&alloc->lock);
+
+ ret = binder_page_insert(alloc, addr, page);
+ if (ret) {
binder_free_page(page);
pr_err("%d: %s failed to insert page at offset %lx with %d\n",
alloc->pid, __func__, addr - alloc->vm_start, ret);
- break;
+ mutex_unlock(&alloc->install_mutex);
+ goto out;
}
+
+ /* Mark page installation complete under alloc->lock */
+ spin_lock(&alloc->lock);
+ binder_set_installed_page(alloc, index, page);
+ spin_unlock(&alloc->lock);
+
+ mutex_unlock(&alloc->install_mutex);
out:
mmput_async(alloc->mm);
return ret;
@@ -1161,6 +1161,14 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
vma = vma_lookup(mm, page_addr);
}
+ /*
+ * Use trylock: the install side may hold install_mutex while its
+ * vm_insert_page() recurses into reclaim and re-enters this shrinker
+ * on the same thread, so blocking here would self-deadlock.
+ */
+ if (!mutex_trylock(&alloc->install_mutex))
+ goto err_get_install_mutex_failed;
+
if (!spin_trylock(&alloc->lock))
goto err_get_alloc_lock_failed;
@@ -1191,6 +1199,8 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
trace_binder_unmap_user_end(alloc, index);
}
+ mutex_unlock(&alloc->install_mutex);
+
if (mm_locked)
mmap_read_unlock(mm);
else
@@ -1203,6 +1213,8 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
err_invalid_vma:
spin_unlock(&alloc->lock);
err_get_alloc_lock_failed:
+ mutex_unlock(&alloc->install_mutex);
+err_get_install_mutex_failed:
if (mm_locked)
mmap_read_unlock(mm);
else
@@ -1236,6 +1248,7 @@ VISIBLE_IF_KUNIT void __binder_alloc_init(struct binder_alloc *alloc,
alloc->mm = current->mm;
mmgrab(alloc->mm);
spin_lock_init(&alloc->lock);
+ mutex_init(&alloc->install_mutex);
INIT_LIST_HEAD(&alloc->buffers);
alloc->freelist = freelist;
}
diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h
index bea5a77bb6da..85817efdbef6 100644
--- a/drivers/android/binder_alloc.h
+++ b/drivers/android/binder_alloc.h
@@ -9,6 +9,7 @@
#include <linux/rbtree.h>
#include <linux/list.h>
#include <linux/mm.h>
+#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
@@ -81,6 +82,7 @@ static inline struct list_head *page_to_lru(struct page *p)
/**
* struct binder_alloc - per-binder proc state for binder allocator
* @lock: protects binder_alloc fields
+ * @install_mutex: serializes page installation and shrinker zap
* @mm: copy of task->mm (invariant after open)
* @vm_start: base of per-proc address space mapped via mmap
* @buffers: list of all buffers for this proc
@@ -106,6 +108,7 @@ static inline struct list_head *page_to_lru(struct page *p)
*/
struct binder_alloc {
spinlock_t lock;
+ struct mutex install_mutex;
struct mm_struct *mm;
unsigned long vm_start;
struct list_head buffers;
--
2.34.1
prev parent reply other threads:[~2026-09-07 13:01 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-07 13:00 [RFC PATCH v4 0/2] binder: split alloc->mutex to improve performance Bo Zhang
2026-09-07 13:00 ` [RFC PATCH v4 1/2] binder: switch alloc->mutex to spinlock for buffer metadata Bo Zhang
2026-09-08 6:44 ` Bo Zhang
2026-09-07 13:00 ` Bo Zhang [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260907130028.807366-3-zhangbo0325@gmail.com \
--to=zhangbo0325@gmail.com \
--cc=aliceryhl@google.com \
--cc=arve@android.com \
--cc=baohua@kernel.org \
--cc=christian@brauner.io \
--cc=cmllamas@google.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=surenb@google.com \
--cc=tkjos@android.com \
--cc=zhangbo56@xiaomi.com \
--cc=zhanghongru06@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®