Skip to content

Commit af73e4d

Browse files
Naoya Horiguchitorvalds
Naoya Horiguchi
authored andcommitted
hugetlbfs: fix mmap failure in unaligned size request
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]>
1 parent 1ab4ce7 commit af73e4d

File tree

4 files changed

+34
-22
lines changed

4 files changed

+34
-22
lines changed

fs/hugetlbfs/inode.c

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -909,11 +909,8 @@ static int can_do_hugetlb_shm(void)
909909

910910
static int get_hstate_idx(int page_size_log)
911911
{
912-
struct hstate *h;
912+
struct hstate *h = hstate_sizelog(page_size_log);
913913

914-
if (!page_size_log)
915-
return default_hstate_idx;
916-
h = size_to_hstate(1 << page_size_log);
917914
if (!h)
918915
return -1;
919916
return h - hstates;
@@ -929,18 +926,19 @@ static struct dentry_operations anon_ops = {
929926
.d_dname = hugetlb_dname
930927
};
931928

932-
struct file *hugetlb_file_setup(const char *name, unsigned long addr,
933-
size_t size, vm_flags_t acctflag,
934-
struct user_struct **user,
929+
/*
930+
* Note that size should be aligned to proper hugepage size in caller side,
931+
* otherwise hugetlb_reserve_pages reserves one less hugepages than intended.
932+
*/
933+
struct file *hugetlb_file_setup(const char *name, size_t size,
934+
vm_flags_t acctflag, struct user_struct **user,
935935
int creat_flags, int page_size_log)
936936
{
937937
struct file *file = ERR_PTR(-ENOMEM);
938938
struct inode *inode;
939939
struct path path;
940940
struct super_block *sb;
941941
struct qstr quick_string;
942-
struct hstate *hstate;
943-
unsigned long num_pages;
944942
int hstate_idx;
945943

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

983-
hstate = hstate_inode(inode);
984-
size += addr & ~huge_page_mask(hstate);
985-
num_pages = ALIGN(size, huge_page_size(hstate)) >>
986-
huge_page_shift(hstate);
987981
file = ERR_PTR(-ENOMEM);
988-
if (hugetlb_reserve_pages(inode, 0, num_pages, NULL, acctflag))
982+
if (hugetlb_reserve_pages(inode, 0,
983+
size >> huge_page_shift(hstate_inode(inode)), NULL,
984+
acctflag))
989985
goto out_inode;
990986

991987
d_instantiate(path.dentry, inode);

include/linux/hugetlb.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,7 @@ static inline struct hugetlbfs_sb_info *HUGETLBFS_SB(struct super_block *sb)
189189

190190
extern const struct file_operations hugetlbfs_file_operations;
191191
extern const struct vm_operations_struct hugetlb_vm_ops;
192-
struct file *hugetlb_file_setup(const char *name, unsigned long addr,
193-
size_t size, vm_flags_t acct,
192+
struct file *hugetlb_file_setup(const char *name, size_t size, vm_flags_t acct,
194193
struct user_struct **user, int creat_flags,
195194
int page_size_log);
196195

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

210209
#define is_file_hugepages(file) 0
211210
static inline struct file *
212-
hugetlb_file_setup(const char *name, unsigned long addr, size_t size,
213-
vm_flags_t acctflag, struct user_struct **user, int creat_flags,
211+
hugetlb_file_setup(const char *name, size_t size, vm_flags_t acctflag,
212+
struct user_struct **user, int creat_flags,
214213
int page_size_log)
215214
{
216215
return ERR_PTR(-ENOSYS);
@@ -288,6 +287,13 @@ static inline struct hstate *hstate_file(struct file *f)
288287
return hstate_inode(file_inode(f));
289288
}
290289

290+
static inline struct hstate *hstate_sizelog(int page_size_log)
291+
{
292+
if (!page_size_log)
293+
return &default_hstate;
294+
return size_to_hstate(1 << page_size_log);
295+
}
296+
291297
static inline struct hstate *hstate_vma(struct vm_area_struct *vma)
292298
{
293299
return hstate_file(vma->vm_file);
@@ -352,11 +358,12 @@ static inline int hstate_index(struct hstate *h)
352358
return h - hstates;
353359
}
354360

355-
#else
361+
#else /* CONFIG_HUGETLB_PAGE */
356362
struct hstate {};
357363
#define alloc_huge_page_node(h, nid) NULL
358364
#define alloc_bootmem_huge_page(h) NULL
359365
#define hstate_file(f) NULL
366+
#define hstate_sizelog(s) NULL
360367
#define hstate_vma(v) NULL
361368
#define hstate_inode(i) NULL
362369
#define huge_page_size(h) PAGE_SIZE
@@ -371,6 +378,6 @@ static inline unsigned int pages_per_huge_page(struct hstate *h)
371378
}
372379
#define hstate_index_to_shift(index) 0
373380
#define hstate_index(h) 0
374-
#endif
381+
#endif /* CONFIG_HUGETLB_PAGE */
375382

376383
#endif /* _LINUX_HUGETLB_H */

ipc/shm.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,10 +491,14 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
491491

492492
sprintf (name, "SYSV%08x", key);
493493
if (shmflg & SHM_HUGETLB) {
494+
struct hstate *hs = hstate_sizelog((shmflg >> SHM_HUGE_SHIFT)
495+
& SHM_HUGE_MASK);
496+
size_t hugesize = ALIGN(size, huge_page_size(hs));
497+
494498
/* hugetlb_file_setup applies strict accounting */
495499
if (shmflg & SHM_NORESERVE)
496500
acctflag = VM_NORESERVE;
497-
file = hugetlb_file_setup(name, 0, size, acctflag,
501+
file = hugetlb_file_setup(name, hugesize, acctflag,
498502
&shp->mlock_user, HUGETLB_SHMFS_INODE,
499503
(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
500504
} else {

mm/mmap.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1363,15 +1363,20 @@ SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
13631363
file = fget(fd);
13641364
if (!file)
13651365
goto out;
1366+
if (is_file_hugepages(file))
1367+
len = ALIGN(len, huge_page_size(hstate_file(file)));
13661368
} else if (flags & MAP_HUGETLB) {
13671369
struct user_struct *user = NULL;
1370+
1371+
len = ALIGN(len, huge_page_size(hstate_sizelog(
1372+
(flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK)));
13681373
/*
13691374
* VM_NORESERVE is used because the reservations will be
13701375
* taken when vm_ops->mmap() is called
13711376
* A dummy user value is used because we are not locking
13721377
* memory so no accounting is necessary
13731378
*/
1374-
file = hugetlb_file_setup(HUGETLB_ANON_FILE, addr, len,
1379+
file = hugetlb_file_setup(HUGETLB_ANON_FILE, len,
13751380
VM_NORESERVE,
13761381
&user, HUGETLB_ANONHUGE_INODE,
13771382
(flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK);

0 commit comments

Comments
 (0)