Skip to content

Commit

Permalink
hugetlbfs: fix mmap failure in unaligned size request
Browse files Browse the repository at this point in the history
The current kernel returns -EINVAL unless a given mmap length is
"almost" hugepage aligned.  This is because in sys_mmap_pgoff() the
given length is passed to vm_mmap_pgoff() as it is without being aligned
with hugepage boundary.

This is a regression introduced in commit 40716e2 ("hugetlbfs: fix
alignment of huge page requests"), where alignment code is pushed into
hugetlb_file_setup() and the variable len in caller side is not changed.

To fix this, this patch partially reverts that commit, and adds
alignment code in caller side.  And it also introduces hstate_sizelog()
in order to get proper hstate to specified hugepage size.

Addresses https://bugzilla.kernel.org/show_bug.cgi?id=56881

[[email protected]: fix warning when CONFIG_HUGETLB_PAGE=n]
Signed-off-by: Naoya Horiguchi <[email protected]>
Signed-off-by: Johannes Weiner <[email protected]>
Reported-by: <[email protected]>
Cc: Steven Truelove <[email protected]>
Cc: Jianguo Wu <[email protected]>
Cc: Hugh Dickins <[email protected]>
Cc: <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Naoya Horiguchi authored and torvalds committed May 8, 2013
1 parent 1ab4ce7 commit af73e4d
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 22 deletions.
24 changes: 10 additions & 14 deletions fs/hugetlbfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -909,11 +909,8 @@ static int can_do_hugetlb_shm(void)

static int get_hstate_idx(int page_size_log)
{
struct hstate *h;
struct hstate *h = hstate_sizelog(page_size_log);

if (!page_size_log)
return default_hstate_idx;
h = size_to_hstate(1 << page_size_log);
if (!h)
return -1;
return h - hstates;
Expand All @@ -929,18 +926,19 @@ static struct dentry_operations anon_ops = {
.d_dname = hugetlb_dname
};

struct file *hugetlb_file_setup(const char *name, unsigned long addr,
size_t size, vm_flags_t acctflag,
struct user_struct **user,
/*
* Note that size should be aligned to proper hugepage size in caller side,
* otherwise hugetlb_reserve_pages reserves one less hugepages than intended.
*/
struct file *hugetlb_file_setup(const char *name, size_t size,
vm_flags_t acctflag, struct user_struct **user,
int creat_flags, int page_size_log)
{
struct file *file = ERR_PTR(-ENOMEM);
struct inode *inode;
struct path path;
struct super_block *sb;
struct qstr quick_string;
struct hstate *hstate;
unsigned long num_pages;
int hstate_idx;

hstate_idx = get_hstate_idx(page_size_log);
Expand Down Expand Up @@ -980,12 +978,10 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
if (!inode)
goto out_dentry;

hstate = hstate_inode(inode);
size += addr & ~huge_page_mask(hstate);
num_pages = ALIGN(size, huge_page_size(hstate)) >>
huge_page_shift(hstate);
file = ERR_PTR(-ENOMEM);
if (hugetlb_reserve_pages(inode, 0, num_pages, NULL, acctflag))
if (hugetlb_reserve_pages(inode, 0,
size >> huge_page_shift(hstate_inode(inode)), NULL,
acctflag))
goto out_inode;

d_instantiate(path.dentry, inode);
Expand Down
19 changes: 13 additions & 6 deletions include/linux/hugetlb.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,7 @@ static inline struct hugetlbfs_sb_info *HUGETLBFS_SB(struct super_block *sb)

extern const struct file_operations hugetlbfs_file_operations;
extern const struct vm_operations_struct hugetlb_vm_ops;
struct file *hugetlb_file_setup(const char *name, unsigned long addr,
size_t size, vm_flags_t acct,
struct file *hugetlb_file_setup(const char *name, size_t size, vm_flags_t acct,
struct user_struct **user, int creat_flags,
int page_size_log);

Expand All @@ -209,8 +208,8 @@ static inline int is_file_hugepages(struct file *file)

#define is_file_hugepages(file) 0
static inline struct file *
hugetlb_file_setup(const char *name, unsigned long addr, size_t size,
vm_flags_t acctflag, struct user_struct **user, int creat_flags,
hugetlb_file_setup(const char *name, size_t size, vm_flags_t acctflag,
struct user_struct **user, int creat_flags,
int page_size_log)
{
return ERR_PTR(-ENOSYS);
Expand Down Expand Up @@ -288,6 +287,13 @@ static inline struct hstate *hstate_file(struct file *f)
return hstate_inode(file_inode(f));
}

static inline struct hstate *hstate_sizelog(int page_size_log)
{
if (!page_size_log)
return &default_hstate;
return size_to_hstate(1 << page_size_log);
}

static inline struct hstate *hstate_vma(struct vm_area_struct *vma)
{
return hstate_file(vma->vm_file);
Expand Down Expand Up @@ -352,11 +358,12 @@ static inline int hstate_index(struct hstate *h)
return h - hstates;
}

#else
#else /* CONFIG_HUGETLB_PAGE */
struct hstate {};
#define alloc_huge_page_node(h, nid) NULL
#define alloc_bootmem_huge_page(h) NULL
#define hstate_file(f) NULL
#define hstate_sizelog(s) NULL
#define hstate_vma(v) NULL
#define hstate_inode(i) NULL
#define huge_page_size(h) PAGE_SIZE
Expand All @@ -371,6 +378,6 @@ static inline unsigned int pages_per_huge_page(struct hstate *h)
}
#define hstate_index_to_shift(index) 0
#define hstate_index(h) 0
#endif
#endif /* CONFIG_HUGETLB_PAGE */

#endif /* _LINUX_HUGETLB_H */
6 changes: 5 additions & 1 deletion ipc/shm.c
Original file line number Diff line number Diff line change
Expand Up @@ -491,10 +491,14 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)

sprintf (name, "SYSV%08x", key);
if (shmflg & SHM_HUGETLB) {
struct hstate *hs = hstate_sizelog((shmflg >> SHM_HUGE_SHIFT)
& SHM_HUGE_MASK);
size_t hugesize = ALIGN(size, huge_page_size(hs));

/* hugetlb_file_setup applies strict accounting */
if (shmflg & SHM_NORESERVE)
acctflag = VM_NORESERVE;
file = hugetlb_file_setup(name, 0, size, acctflag,
file = hugetlb_file_setup(name, hugesize, acctflag,
&shp->mlock_user, HUGETLB_SHMFS_INODE,
(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
} else {
Expand Down
7 changes: 6 additions & 1 deletion mm/mmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -1363,15 +1363,20 @@ SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
file = fget(fd);
if (!file)
goto out;
if (is_file_hugepages(file))
len = ALIGN(len, huge_page_size(hstate_file(file)));
} else if (flags & MAP_HUGETLB) {
struct user_struct *user = NULL;

len = ALIGN(len, huge_page_size(hstate_sizelog(
(flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK)));
/*
* VM_NORESERVE is used because the reservations will be
* taken when vm_ops->mmap() is called
* A dummy user value is used because we are not locking
* memory so no accounting is necessary
*/
file = hugetlb_file_setup(HUGETLB_ANON_FILE, addr, len,
file = hugetlb_file_setup(HUGETLB_ANON_FILE, len,
VM_NORESERVE,
&user, HUGETLB_ANONHUGE_INODE,
(flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK);
Expand Down

0 comments on commit af73e4d

Please sign in to comment.