UPSTREAM: binder: use per-vma lock in page installation
Use per-vma locking for concurrent page installations, this minimizes contention with unrelated vmas improving performance. The mmap_lock is still acquired when needed though, e.g. before get_user_pages_remote(). Many thanks to Barry Song who posted a similar approach [1]. Link: https://lore.kernel.org/all/20240902225009.34576-1-21cnbao@gmail.com/ [1] Cc: Nhat Pham <nphamcs@gmail.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Barry Song <v-songbaohua@oppo.com> Cc: Suren Baghdasaryan <surenb@google.com> Cc: Hillf Danton <hdanton@sina.com> Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com> Reviewed-by: Suren Baghdasaryan <surenb@google.com> Signed-off-by: Carlos Llamas <cmllamas@google.com> Link: https://lore.kernel.org/r/20241210143114.661252-8-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Bug: 451083029 Bug: 410746221 (cherry picked from commit 9e2aa76549b9fd2b8f7b81260417a4ec853910e6) Change-Id: Ifebfbf7ddcb1877303941ea37662d314b5d9ed38 Signed-off-by: Carlos Llamas <cmllamas@google.com>
This commit is contained in:
@@ -234,6 +234,53 @@ static inline bool binder_alloc_is_mapped(struct binder_alloc *alloc)
|
||||
return smp_load_acquire(&alloc->mapped);
|
||||
}
|
||||
|
||||
static struct page *binder_page_lookup(struct binder_alloc *alloc,
|
||||
unsigned long addr)
|
||||
{
|
||||
struct mm_struct *mm = alloc->mm;
|
||||
struct page *page;
|
||||
long npages = 0;
|
||||
|
||||
/*
|
||||
* Find an existing page in the remote mm. If missing,
|
||||
* don't attempt to fault-in just propagate an error.
|
||||
*/
|
||||
mmap_read_lock(mm);
|
||||
if (binder_alloc_is_mapped(alloc))
|
||||
npages = get_user_pages_remote(mm, addr, 1, FOLL_NOFAULT,
|
||||
&page, NULL);
|
||||
mmap_read_unlock(mm);
|
||||
|
||||
return npages > 0 ? page : NULL;
|
||||
}
|
||||
|
||||
static int binder_page_insert(struct binder_alloc *alloc,
|
||||
unsigned long addr,
|
||||
struct page *page)
|
||||
{
|
||||
struct mm_struct *mm = alloc->mm;
|
||||
struct vm_area_struct *vma;
|
||||
int ret = -ESRCH;
|
||||
|
||||
/* attempt per-vma lock first */
|
||||
vma = lock_vma_under_rcu(mm, addr);
|
||||
if (vma) {
|
||||
if (binder_alloc_is_mapped(alloc))
|
||||
ret = vm_insert_page(vma, addr, page);
|
||||
vma_end_read(vma);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* fall back to mmap_lock */
|
||||
mmap_read_lock(mm);
|
||||
vma = vma_lookup(mm, addr);
|
||||
if (vma && binder_alloc_is_mapped(alloc))
|
||||
ret = vm_insert_page(vma, addr, page);
|
||||
mmap_read_unlock(mm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct page *binder_page_alloc(struct binder_alloc *alloc,
|
||||
unsigned long index)
|
||||
{
|
||||
@@ -269,9 +316,7 @@ static int binder_install_single_page(struct binder_alloc *alloc,
|
||||
unsigned long index,
|
||||
unsigned long addr)
|
||||
{
|
||||
struct vm_area_struct *vma;
|
||||
struct page *page;
|
||||
long npages;
|
||||
int ret;
|
||||
|
||||
if (!mmget_not_zero(alloc->mm))
|
||||
@@ -283,16 +328,7 @@ static int binder_install_single_page(struct binder_alloc *alloc,
|
||||
goto out;
|
||||
}
|
||||
|
||||
mmap_read_lock(alloc->mm);
|
||||
vma = vma_lookup(alloc->mm, addr);
|
||||
if (!vma || !binder_alloc_is_mapped(alloc)) {
|
||||
binder_free_page(page);
|
||||
pr_err("%d: %s failed, no vma\n", alloc->pid, __func__);
|
||||
ret = -ESRCH;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = vm_insert_page(vma, addr, page);
|
||||
ret = binder_page_insert(alloc, addr, page);
|
||||
switch (ret) {
|
||||
case -EBUSY:
|
||||
/*
|
||||
@@ -302,9 +338,8 @@ static int binder_install_single_page(struct binder_alloc *alloc,
|
||||
*/
|
||||
ret = 0;
|
||||
binder_free_page(page);
|
||||
npages = get_user_pages_remote(alloc->mm, addr, 1,
|
||||
FOLL_NOFAULT, &page, NULL);
|
||||
if (npages <= 0) {
|
||||
page = binder_page_lookup(alloc, addr);
|
||||
if (!page) {
|
||||
pr_err("%d: failed to find page at offset %lx\n",
|
||||
alloc->pid, addr - alloc->buffer);
|
||||
ret = -ESRCH;
|
||||
@@ -322,8 +357,6 @@ static int binder_install_single_page(struct binder_alloc *alloc,
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
unlock:
|
||||
mmap_read_unlock(alloc->mm);
|
||||
out:
|
||||
mmput_async(alloc->mm);
|
||||
return ret;
|
||||
|
||||
Reference in New Issue
Block a user