diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 6656807ce437..166183b7ee36 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -382,6 +382,7 @@ static void hugetlb_unmap_file_folio(struct hstate *h, struct folio *folio, pgoff_t index) { struct hugetlb_vma_lock *vma_lock; + struct resv_map *resv_map; unsigned long pfn = folio_pfn(folio); struct vm_area_struct *vma; unsigned long v_start; @@ -394,6 +395,7 @@ static void hugetlb_unmap_file_folio(struct hstate *h, i_mmap_lock_write(mapping); retry: vma_lock = NULL; + resv_map = NULL; mapping_rmap_tree_foreach(vma, mapping, start, end - 1) { v_start = vma_offset_start(vma, start); v_end = vma_offset_end(vma, end); @@ -402,15 +404,21 @@ static void hugetlb_unmap_file_folio(struct hstate *h, continue; if (!hugetlb_vma_trylock_write(vma)) { - vma_lock = vma->vm_private_data; - /* - * If we can not get vma lock, we need to drop - * immap_sema and take locks in order. First, - * take a ref on the vma_lock structure so that - * we can be guaranteed it will not go away when - * dropping immap_sema. - */ - kref_get(&vma_lock->refs); + if (vma->vm_flags & VM_MAYSHARE) { + vma_lock = vma->vm_private_data; + /* + * If we can not get vma lock, we need to drop + * immap_sema and take locks in order. First, + * take a ref on the vma_lock structure so that + * we can be guaranteed it will not go away when + * dropping immap_sema. + */ + kref_get(&vma_lock->refs); + } else { + resv_map = vma_resv_map(vma); + kref_get(&resv_map->refs); + } + break; } @@ -421,25 +429,28 @@ static void hugetlb_unmap_file_folio(struct hstate *h, i_mmap_unlock_write(mapping); - if (vma_lock) { + if (vma_lock || resv_map) { /* * Wait on vma_lock. We know it is still valid as we have * a reference. We must 'open code' vma locking as we do * not know if vma_lock is still attached to vma. */ - down_write(&vma_lock->rw_sema); + hugetlb_vma_lock_write(vma); i_mmap_lock_write(mapping); - vma = vma_lock->vma; - if (!vma) { - /* - * If lock is no longer attached to vma, then just - * unlock, drop our reference and retry looking for - * other vmas. - */ - up_write(&vma_lock->rw_sema); - kref_put(&vma_lock->refs, hugetlb_vma_lock_release); - goto retry; + if (vma_lock) { + vma = vma_lock->vma; + if (!vma) { + /* + * If lock is no longer attached to vma, then + * just unlock, drop our reference and retry + * looking for other vmas. + */ + up_write(&vma_lock->rw_sema); + kref_put(&vma_lock->refs, + hugetlb_vma_lock_release); + goto retry; + } } /* @@ -452,7 +463,10 @@ static void hugetlb_unmap_file_folio(struct hstate *h, unmap_hugepage_range(vma, v_start, v_end, NULL, ZAP_FLAG_DROP_MARKER); - kref_put(&vma_lock->refs, hugetlb_vma_lock_release); + if (vma_lock) + kref_put(&vma_lock->refs, hugetlb_vma_lock_release); + else + kref_put(&resv_map->refs, resv_map_release); hugetlb_vma_unlock_write(vma); goto retry; diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 80a5a03e9cee..f980191c651b 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -268,6 +268,7 @@ void hugetlb_vma_unlock_read(struct vm_area_struct *vma); void hugetlb_vma_lock_write(struct vm_area_struct *vma); void hugetlb_vma_unlock_write(struct vm_area_struct *vma); int hugetlb_vma_trylock_write(struct vm_area_struct *vma); +struct resv_map *vma_resv_map(struct vm_area_struct *vma); void hugetlb_vma_assert_locked(struct vm_area_struct *vma); void hugetlb_vma_lock_release(struct kref *kref); long hugetlb_change_protection(struct vm_area_struct *vma, @@ -392,6 +393,11 @@ static inline int is_hugepage_only_range(struct mm_struct *mm, return 0; } +static inline struct resv_map *vma_resv_map(struct vm_area_struct *vma) +{ + return NULL; +} + #ifdef CONFIG_USERFAULTFD static inline int hugetlb_mfill_atomic_pte(pte_t *dst_pte, struct vm_area_struct *dst_vma,