summaryrefslogtreecommitdiff
path: root/drivers/android/binder_alloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/android/binder_alloc.c')
-rw-r--r--drivers/android/binder_alloc.c63
1 files changed, 37 insertions, 26 deletions
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index d6eae0aa7085..e4488ad86a65 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -259,14 +259,21 @@ static int binder_page_insert(struct binder_alloc *alloc,
struct vm_area_struct *vma;
int ret = -ESRCH;
- vma = vma_start_read_unlocked(mm, addr);
- if (!vma)
+ /* 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;
+ }
- if (binder_alloc_is_mapped(alloc))
+ /* 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);
-
- vma_end_read(vma);
+ mmap_read_unlock(mm);
return ret;
}
@@ -1135,6 +1142,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
struct vm_area_struct *vma;
struct page *page_to_free;
unsigned long page_addr;
+ int mm_locked = 0;
size_t index;
if (!mmget_not_zero(mm))
@@ -1143,25 +1151,27 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
index = mdata->page_index;
page_addr = alloc->vm_start + index * PAGE_SIZE;
- /*
- * Attempt per-vma lock. This is essentially a
- * "trylock". It can fail even if the VMA exists
- * for 'page_addr'.
- */
+ /* attempt per-vma lock first */
vma = lock_vma_under_rcu(mm, page_addr);
if (!vma) {
- /*
- * If the vma exists, we can't continue because we cannot
- * remove the page from the vma. However, if the vma was
- * unmapped, it's okay to continue.
- */
- if (binder_alloc_is_mapped(alloc))
- goto err_vma_lock_failed;
+ /* fall back to mmap_lock */
+ if (!mmap_read_trylock(mm))
+ goto err_mmap_read_lock_failed;
+ mm_locked = 1;
+ vma = vma_lookup(mm, page_addr);
}
if (!mutex_trylock(&alloc->mutex))
goto err_get_alloc_mutex_failed;
+ /*
+ * Since a binder_alloc can only be mapped once, we ensure
+ * the vma corresponds to this mapping by checking whether
+ * the binder_alloc is still mapped.
+ */
+ if (vma && !binder_alloc_is_mapped(alloc))
+ goto err_invalid_vma;
+
trace_binder_unmap_kernel_start(alloc, index);
page_to_free = alloc->pages[index];
@@ -1172,12 +1182,7 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
list_lru_isolate(lru, item);
spin_unlock(&lru->lock);
- /*
- * Since a binder_alloc can only be mapped once, we ensure
- * the vma corresponds to this mapping by checking whether
- * the binder_alloc is still mapped.
- */
- if (vma && binder_alloc_is_mapped(alloc)) {
+ if (vma) {
trace_binder_unmap_user_start(alloc, index);
zap_vma_range(vma, page_addr, PAGE_SIZE);
@@ -1186,17 +1191,23 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
}
mutex_unlock(&alloc->mutex);
- if (vma)
+ if (mm_locked)
+ mmap_read_unlock(mm);
+ else
vma_end_read(vma);
mmput_async(mm);
binder_free_page(page_to_free);
return LRU_REMOVED_RETRY;
+err_invalid_vma:
+ mutex_unlock(&alloc->mutex);
err_get_alloc_mutex_failed:
- if (vma)
+ if (mm_locked)
+ mmap_read_unlock(mm);
+ else
vma_end_read(vma);
-err_vma_lock_failed:
+err_mmap_read_lock_failed:
mmput_async(mm);
err_mmget:
return LRU_SKIP;