Commit f27a5136 authored by Mike Kravetz's avatar Mike Kravetz Committed by Linus Torvalds

hugetlbfs: always use address space in inode for resv_map pointer

Continuing discussion about 58b6e5e8 ("hugetlbfs: fix memory leak for
resv_map") brought up the issue that inode->i_mapping may not point to the
address space embedded within the inode at inode eviction time.  The
hugetlbfs truncate routine handles this by explicitly using inode->i_data.
However, code cleaning up the resv_map will still use the address space
pointed to by inode->i_mapping.  Luckily, private_data is NULL for address
spaces in all such cases today but, there is no guarantee this will
continue.

Change all hugetlbfs code getting a resv_map pointer to explicitly get it
from the address space embedded within the inode.  In addition, add more
comments in the code to indicate why this is being done.

Link: http://lkml.kernel.org/r/20190419204435.16984-1-mike.kravetz@oracle.comSigned-off-by: default avatarMike Kravetz <mike.kravetz@oracle.com>
Reported-by: default avatarYufen Yu <yuyufen@huawei.com>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: "Kirill A . Shutemov" <kirill.shutemov@linux.intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 1f862989
...@@ -497,8 +497,15 @@ static void hugetlbfs_evict_inode(struct inode *inode) ...@@ -497,8 +497,15 @@ static void hugetlbfs_evict_inode(struct inode *inode)
struct resv_map *resv_map; struct resv_map *resv_map;
remove_inode_hugepages(inode, 0, LLONG_MAX); remove_inode_hugepages(inode, 0, LLONG_MAX);
resv_map = (struct resv_map *)inode->i_mapping->private_data;
/* root inode doesn't have the resv_map, so we should check it */ /*
* Get the resv_map from the address space embedded in the inode.
* This is the address space which points to any resv_map allocated
* at inode creation time. If this is a device special inode,
* i_mapping may not point to the original address space.
*/
resv_map = (struct resv_map *)(&inode->i_data)->private_data;
/* Only regular and link inodes have associated reserve maps */
if (resv_map) if (resv_map)
resv_map_release(&resv_map->refs); resv_map_release(&resv_map->refs);
clear_inode(inode); clear_inode(inode);
......
...@@ -740,7 +740,15 @@ void resv_map_release(struct kref *ref) ...@@ -740,7 +740,15 @@ void resv_map_release(struct kref *ref)
static inline struct resv_map *inode_resv_map(struct inode *inode) static inline struct resv_map *inode_resv_map(struct inode *inode)
{ {
return inode->i_mapping->private_data; /*
* At inode evict time, i_mapping may not point to the original
* address space within the inode. This original address space
* contains the pointer to the resv_map. So, always use the
* address space embedded within the inode.
* The VERY common case is inode->mapping == &inode->i_data but,
* this may not be true for device special inodes.
*/
return (struct resv_map *)(&inode->i_data)->private_data;
} }
static struct resv_map *vma_resv_map(struct vm_area_struct *vma) static struct resv_map *vma_resv_map(struct vm_area_struct *vma)
...@@ -4518,6 +4526,11 @@ int hugetlb_reserve_pages(struct inode *inode, ...@@ -4518,6 +4526,11 @@ int hugetlb_reserve_pages(struct inode *inode,
* called to make the mapping read-write. Assume !vma is a shm mapping * called to make the mapping read-write. Assume !vma is a shm mapping
*/ */
if (!vma || vma->vm_flags & VM_MAYSHARE) { if (!vma || vma->vm_flags & VM_MAYSHARE) {
/*
* resv_map can not be NULL as hugetlb_reserve_pages is only
* called for inodes for which resv_maps were created (see
* hugetlbfs_get_inode).
*/
resv_map = inode_resv_map(inode); resv_map = inode_resv_map(inode);
chg = region_chg(resv_map, from, to); chg = region_chg(resv_map, from, to);
...@@ -4609,6 +4622,10 @@ long hugetlb_unreserve_pages(struct inode *inode, long start, long end, ...@@ -4609,6 +4622,10 @@ long hugetlb_unreserve_pages(struct inode *inode, long start, long end,
struct hugepage_subpool *spool = subpool_inode(inode); struct hugepage_subpool *spool = subpool_inode(inode);
long gbl_reserve; long gbl_reserve;
/*
* Since this routine can be called in the evict inode path for all
* hugetlbfs inodes, resv_map could be NULL.
*/
if (resv_map) { if (resv_map) {
chg = region_del(resv_map, start, end); chg = region_del(resv_map, start, end);
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment