diff options
Diffstat (limited to 'drivers/android/binder_alloc.c')
| -rw-r--r-- | drivers/android/binder_alloc.c | 63 |
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; |
