hugetlbfs: always use address space in inode for resv_map pointer
Continuing discussion about 58b6e5e8f1
("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.com
Signed-off-by: Mike Kravetz <mike.kravetz@oracle.com>
Reported-by: Yufen 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: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
1f862989b0
commit
f27a5136f7
|
@ -497,8 +497,15 @@ static void hugetlbfs_evict_inode(struct inode *inode)
|
|||
struct resv_map *resv_map;
|
||||
|
||||
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)
|
||||
resv_map_release(&resv_map->refs);
|
||||
clear_inode(inode);
|
||||
|
|
19
mm/hugetlb.c
19
mm/hugetlb.c
|
@ -740,7 +740,15 @@ void resv_map_release(struct kref *ref)
|
|||
|
||||
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)
|
||||
|
@ -4518,6 +4526,11 @@ int hugetlb_reserve_pages(struct inode *inode,
|
|||
* called to make the mapping read-write. Assume !vma is a shm mapping
|
||||
*/
|
||||
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);
|
||||
|
||||
chg = region_chg(resv_map, from, to);
|
||||
|
@ -4609,6 +4622,10 @@ long hugetlb_unreserve_pages(struct inode *inode, long start, long end,
|
|||
struct hugepage_subpool *spool = subpool_inode(inode);
|
||||
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) {
|
||||
chg = region_del(resv_map, start, end);
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue