From: Carlos Llamas <cmllamas@google.com>
To: "Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"Arve Hjønnevåg" <arve@android.com>,
"Todd Kjos" <tkjos@android.com>,
"Martijn Coenen" <maco@android.com>,
"Joel Fernandes" <joel@joelfernandes.org>,
"Christian Brauner" <brauner@kernel.org>,
"Carlos Llamas" <cmllamas@google.com>,
"Suren Baghdasaryan" <surenb@google.com>
Cc: linux-kernel@vger.kernel.org, kernel-team@android.com
Subject: [PATCH 21/21] binder: switch alloc->mutex to spinlock_t
Date: Thu, 2 Nov 2023 18:59:22 +0000 [thread overview]
Message-ID: <20231102185934.773885-22-cmllamas@google.com> (raw)
In-Reply-To: <20231102185934.773885-1-cmllamas@google.com>
The alloc->mutex is a highly contended lock that causes performance
issues on Android devices. When a low-priority task is given this lock
and it sleeps, it becomes difficult for the task to wakeup and complete
its work. This delays other tasks that are also waiting on the mutex.
The problem gets worse when there is memory pressure in the system,
because this increases the contention on the alloc->mutex while the
shrinker reclaims binder pages.
Switching to a spinlock helps to keep the waiters running and avoids the
overhead of waking up tasks. This significantly improves the transaction
latency when the problematic scenario occurs.
The performance impact of this patchset was measured by stress-testing
the binder alloc contention. In this test, several clients of different
priorities send thousands of transactions of different sizes to a single
server. In parallel, pages get reclaimed using the shinker's debugfs.
The test was run on a Pixel 8, Pixel 6 and qemu machine. The results
were similar on all three devices:
after:
| sched | prio | average | max | min |
|--------+------+---------+-----------+---------|
| fifo | 99 | 0.135ms | 1.197ms | 0.022ms |
| fifo | 01 | 0.136ms | 5.232ms | 0.018ms |
| other | -20 | 0.180ms | 7.403ms | 0.019ms |
| other | 19 | 0.241ms | 58.094ms | 0.018ms |
before:
| sched | prio | average | max | min |
|--------+------+---------+-----------+---------|
| fifo | 99 | 0.350ms | 248.730ms | 0.020ms |
| fifo | 01 | 0.357ms | 248.817ms | 0.024ms |
| other | -20 | 0.399ms | 249.906ms | 0.020ms |
| other | 19 | 0.477ms | 297.756ms | 0.022ms |
The key metrics above are the average and max latencies (wall time).
These improvements should roughly translate to p95-p99 latencies on real
workloads. The response time is up to 200x faster in these scenarios and
there is no penalty in the regular path.
Note that it is only possible to convert this lock after a series of
changes made by previous patches. These mainly include refactoring the
sections that might_sleep() and changing the locking order with the
mmap_lock amongst others.
Signed-off-by: Carlos Llamas <cmllamas@google.com>
---
drivers/android/binder_alloc.c | 44 +++++++++++++++++-----------------
drivers/android/binder_alloc.h | 10 ++++----
2 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index 0ba9f524e0ff..a8a3e91ba308 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -169,9 +169,9 @@ struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc,
{
struct binder_buffer *buffer;
- mutex_lock(&alloc->mutex);
+ spin_lock(&alloc->lock);
buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr);
- mutex_unlock(&alloc->mutex);
+ spin_unlock(&alloc->lock);
return buffer;
}
@@ -572,9 +572,9 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
/* Pad 0-size buffers so they get assigned unique addresses */
size = max(size, sizeof(void *));
- mutex_lock(&alloc->mutex);
+ spin_lock(&alloc->lock);
buffer = binder_alloc_new_buf_locked(alloc, next, size, is_async);
- mutex_unlock(&alloc->mutex);
+ spin_unlock(&alloc->lock);
if (IS_ERR(buffer))
goto out;
@@ -786,17 +786,17 @@ void binder_alloc_free_buf(struct binder_alloc *alloc,
* We could eliminate the call to binder_alloc_clear_buf()
* from binder_alloc_deferred_release() by moving this to
* binder_free_buf_locked(). However, that could
- * increase contention for the alloc mutex if clear_on_free
- * is used frequently for large buffers. The mutex is not
+ * increase contention for the alloc->lock if clear_on_free
+ * is used frequently for large buffers. This lock is not
* needed for correctness here.
*/
if (buffer->clear_on_free) {
binder_alloc_clear_buf(alloc, buffer);
buffer->clear_on_free = false;
}
- mutex_lock(&alloc->mutex);
+ spin_lock(&alloc->lock);
binder_free_buf_locked(alloc, buffer, true);
- mutex_unlock(&alloc->mutex);
+ spin_unlock(&alloc->lock);
}
/**
@@ -894,7 +894,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
struct binder_buffer *buffer;
buffers = 0;
- mutex_lock(&alloc->mutex);
+ spin_lock(&alloc->lock);
BUG_ON(alloc->vma);
while ((n = rb_first(&alloc->allocated_buffers))) {
@@ -944,7 +944,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
}
kfree(alloc->pages);
}
- mutex_unlock(&alloc->mutex);
+ spin_unlock(&alloc->lock);
if (alloc->mm)
mmdrop(alloc->mm);
@@ -976,11 +976,11 @@ void binder_alloc_print_allocated(struct seq_file *m,
{
struct rb_node *n;
- mutex_lock(&alloc->mutex);
+ spin_lock(&alloc->lock);
for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
print_binder_buffer(m, " buffer",
rb_entry(n, struct binder_buffer, rb_node));
- mutex_unlock(&alloc->mutex);
+ spin_unlock(&alloc->lock);
}
/**
@@ -997,7 +997,7 @@ void binder_alloc_print_pages(struct seq_file *m,
int lru = 0;
int free = 0;
- mutex_lock(&alloc->mutex);
+ spin_lock(&alloc->lock);
/*
* Make sure the binder_alloc is fully initialized, otherwise we might
* read inconsistent state.
@@ -1013,7 +1013,7 @@ void binder_alloc_print_pages(struct seq_file *m,
lru++;
}
}
- mutex_unlock(&alloc->mutex);
+ spin_unlock(&alloc->lock);
seq_printf(m, " pages: %d:%d:%d\n", active, lru, free);
seq_printf(m, " pages high watermark: %zu\n", alloc->pages_high);
}
@@ -1029,10 +1029,10 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc)
struct rb_node *n;
int count = 0;
- mutex_lock(&alloc->mutex);
+ spin_lock(&alloc->lock);
for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
count++;
- mutex_unlock(&alloc->mutex);
+ spin_unlock(&alloc->lock);
return count;
}
@@ -1076,8 +1076,8 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
goto err_mmget;
if (!mmap_read_trylock(mm))
goto err_mmap_read_lock_failed;
- if (!mutex_trylock(&alloc->mutex))
- goto err_get_alloc_mutex_failed;
+ if (!spin_trylock(&alloc->lock))
+ goto err_get_alloc_lock_failed;
if (!page->page_ptr)
goto err_page_already_freed;
@@ -1096,7 +1096,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
trace_binder_unmap_kernel_end(alloc, index);
list_lru_isolate(lru, item);
- mutex_unlock(&alloc->mutex);
+ spin_unlock(&alloc->lock);
spin_unlock(lock);
if (vma) {
@@ -1115,8 +1115,8 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
err_invalid_vma:
err_page_already_freed:
- mutex_unlock(&alloc->mutex);
-err_get_alloc_mutex_failed:
+ spin_unlock(&alloc->lock);
+err_get_alloc_lock_failed:
mmap_read_unlock(mm);
err_mmap_read_lock_failed:
mmput_async(mm);
@@ -1155,7 +1155,7 @@ void binder_alloc_init(struct binder_alloc *alloc)
alloc->pid = current->group_leader->pid;
alloc->mm = current->mm;
mmgrab(alloc->mm);
- mutex_init(&alloc->mutex);
+ spin_lock_init(&alloc->lock);
INIT_LIST_HEAD(&alloc->buffers);
}
diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h
index bbc16bc6d5ac..3602a60d9609 100644
--- a/drivers/android/binder_alloc.h
+++ b/drivers/android/binder_alloc.h
@@ -9,7 +9,7 @@
#include <linux/rbtree.h>
#include <linux/list.h>
#include <linux/mm.h>
-#include <linux/rtmutex.h>
+#include <linux/spinlock.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/list_lru.h>
@@ -72,7 +72,7 @@ struct binder_lru_page {
/**
* struct binder_alloc - per-binder proc state for binder allocator
- * @mutex: protects binder_alloc fields
+ * @lock: protects binder_alloc fields
* @vma: vm_area_struct passed to mmap_handler
* (invariant after mmap)
* @mm: copy of task->mm (invariant after open)
@@ -96,7 +96,7 @@ struct binder_lru_page {
* struct binder_buffer objects used to track the user buffers
*/
struct binder_alloc {
- struct mutex mutex;
+ spinlock_t lock;
struct vm_area_struct *vma;
struct mm_struct *mm;
unsigned long buffer;
@@ -153,9 +153,9 @@ binder_alloc_get_free_async_space(struct binder_alloc *alloc)
{
size_t free_async_space;
- mutex_lock(&alloc->mutex);
+ spin_lock(&alloc->lock);
free_async_space = alloc->free_async_space;
- mutex_unlock(&alloc->mutex);
+ spin_unlock(&alloc->lock);
return free_async_space;
}
--
2.42.0.869.gea05f2083d-goog
next prev parent reply other threads:[~2023-11-02 19:02 UTC|newest]
Thread overview: 57+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-11-02 18:59 [PATCH 00/21] binder: convert alloc->mutex to spinlock Carlos Llamas
2023-11-02 18:59 ` [PATCH 01/21] binder: use EPOLLERR from eventpoll.h Carlos Llamas
2023-11-07 9:07 ` Alice Ryhl
2023-11-02 18:59 ` [PATCH 02/21] binder: fix use-after-free in shinker's callback Carlos Llamas
2023-11-02 19:20 ` Liam R. Howlett
2023-11-02 20:09 ` Carlos Llamas
2023-11-02 20:27 ` Liam R. Howlett
2023-11-07 9:07 ` Alice Ryhl
2023-11-02 18:59 ` [PATCH 03/21] binder: fix race between mmput() and do_exit() Carlos Llamas
2023-11-07 9:07 ` Alice Ryhl
2023-11-02 18:59 ` [PATCH 04/21] binder: fix async space check for 0-sized buffers Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-11-02 18:59 ` [PATCH 05/21] binder: fix trivial typo of binder_free_buf_locked() Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-12-01 6:52 ` Carlos Llamas
2023-11-02 18:59 ` [PATCH 06/21] binder: fix comment on binder_alloc_new_buf() return value Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-11-02 18:59 ` [PATCH 07/21] binder: remove extern from function prototypes Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-11-02 18:59 ` [PATCH 08/21] binder: keep vma addresses type as unsigned long Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-12-01 7:01 ` Carlos Llamas
2023-11-02 18:59 ` [PATCH 09/21] binder: split up binder_update_page_range() Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-12-01 7:03 ` Carlos Llamas
2023-11-02 18:59 ` [PATCH 10/21] binder: do unlocked work in binder_alloc_new_buf() Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-12-01 7:10 ` Carlos Llamas
2023-11-02 18:59 ` [PATCH 11/21] binder: remove pid param " Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-11-02 18:59 ` [PATCH 12/21] binder: separate the no-space debugging logic Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-11-02 18:59 ` [PATCH 13/21] binder: relocate low space calculation Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-12-01 7:12 ` Carlos Llamas
2023-11-02 18:59 ` [PATCH 14/21] binder: do not add pages to LRU in release path Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-12-01 7:15 ` Carlos Llamas
2023-11-02 18:59 ` [PATCH 15/21] binder: relocate binder_alloc_clear_buf() Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-11-02 18:59 ` [PATCH 16/21] binder: refactor page range allocation Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-12-01 7:19 ` Carlos Llamas
2023-11-02 18:59 ` [PATCH 17/21] binder: malloc new_buffer outside of locks Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-12-01 7:20 ` Carlos Llamas
2023-11-02 18:59 ` [PATCH 18/21] binder: initialize lru pages in mmap callback Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-11-02 18:59 ` [PATCH 19/21] binder: perform page allocation outside of locks Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-12-01 7:39 ` Carlos Llamas
2023-11-02 18:59 ` [PATCH 20/21] binder: reverse locking order in shrinker callback Carlos Llamas
2023-11-07 9:08 ` Alice Ryhl
2023-12-01 7:42 ` Carlos Llamas
2023-11-02 18:59 ` Carlos Llamas [this message]
2023-11-07 9:08 ` [PATCH 21/21] binder: switch alloc->mutex to spinlock_t Alice Ryhl
2023-12-01 7:46 ` Carlos Llamas
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=20231102185934.773885-22-cmllamas@google.com \
--to=cmllamas@google.com \
--cc=arve@android.com \
--cc=brauner@kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=joel@joelfernandes.org \
--cc=kernel-team@android.com \
--cc=linux-kernel@vger.kernel.org \
--cc=maco@android.com \
--cc=surenb@google.com \
--cc=tkjos@android.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®