aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNaoya Horiguchi <n-horiguchi@ah.jp.nec.com>2013-05-07 19:18:13 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-05-07 21:38:27 -0400
commitaf73e4d9506d3b797509f3c030e7dcd554f7d9c4 (patch)
treef9f1bf7483495b66b6cf2cfb3c676791133733b1
parent1ab4ce762370b82870834899e49c08129d7ae271 (diff)
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 40716e29243d ("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 [akpm@linux-foundation.org: fix warning when CONFIG_HUGETLB_PAGE=n] Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> Signed-off-by: Johannes Weiner <hannes@cmpxchg.org> Reported-by: <iceman_dvd@yahoo.com> Cc: Steven Truelove <steven.truelove@utoronto.ca> Cc: Jianguo Wu <wujianguo@huawei.com> Cc: Hugh Dickins <hughd@google.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/hugetlbfs/inode.c24
-rw-r--r--include/linux/hugetlb.h19
-rw-r--r--ipc/shm.c6
-rw-r--r--mm/mmap.c7
4 files changed, 34 insertions, 22 deletions
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 523464e62849..a3f868ae3fd4 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -909,11 +909,8 @@ static int can_do_hugetlb_shm(void)
909 909
910static int get_hstate_idx(int page_size_log) 910static int get_hstate_idx(int page_size_log)
911{ 911{
912 struct hstate *h; 912 struct hstate *h = hstate_sizelog(page_size_log);
913 913
914 if (!page_size_log)
915 return default_hstate_idx;
916 h = size_to_hstate(1 << page_size_log);
917 if (!h) 914 if (!h)
918 return -1; 915 return -1;
919 return h - hstates; 916 return h - hstates;
@@ -929,9 +926,12 @@ static struct dentry_operations anon_ops = {
929 .d_dname = hugetlb_dname 926 .d_dname = hugetlb_dname
930}; 927};
931 928
932struct file *hugetlb_file_setup(const char *name, unsigned long addr, 929/*
933 size_t size, vm_flags_t acctflag, 930 * Note that size should be aligned to proper hugepage size in caller side,
934 struct user_struct **user, 931 * otherwise hugetlb_reserve_pages reserves one less hugepages than intended.
932 */
933struct file *hugetlb_file_setup(const char *name, size_t size,
934 vm_flags_t acctflag, struct user_struct **user,
935 int creat_flags, int page_size_log) 935 int creat_flags, int page_size_log)
936{ 936{
937 struct file *file = ERR_PTR(-ENOMEM); 937 struct file *file = ERR_PTR(-ENOMEM);
@@ -939,8 +939,6 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
939 struct path path; 939 struct path path;
940 struct super_block *sb; 940 struct super_block *sb;
941 struct qstr quick_string; 941 struct qstr quick_string;
942 struct hstate *hstate;
943 unsigned long num_pages;
944 int hstate_idx; 942 int hstate_idx;
945 943
946 hstate_idx = get_hstate_idx(page_size_log); 944 hstate_idx = get_hstate_idx(page_size_log);
@@ -980,12 +978,10 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
980 if (!inode) 978 if (!inode)
981 goto out_dentry; 979 goto out_dentry;
982 980
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);
987 file = ERR_PTR(-ENOMEM); 981 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))
989 goto out_inode; 985 goto out_inode;
990 986
991 d_instantiate(path.dentry, inode); 987 d_instantiate(path.dentry, inode);
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 3a62df310f2e..6b4890fa57e7 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -189,8 +189,7 @@ static inline struct hugetlbfs_sb_info *HUGETLBFS_SB(struct super_block *sb)
189 189
190extern const struct file_operations hugetlbfs_file_operations; 190extern const struct file_operations hugetlbfs_file_operations;
191extern const struct vm_operations_struct hugetlb_vm_ops; 191extern const struct vm_operations_struct hugetlb_vm_ops;
192struct file *hugetlb_file_setup(const char *name, unsigned long addr, 192struct file *hugetlb_file_setup(const char *name, size_t size, vm_flags_t acct,
193 size_t size, vm_flags_t acct,
194 struct user_struct **user, int creat_flags, 193 struct user_struct **user, int creat_flags,
195 int page_size_log); 194 int page_size_log);
196 195
@@ -209,8 +208,8 @@ static inline int is_file_hugepages(struct file *file)
209 208
210#define is_file_hugepages(file) 0 209#define is_file_hugepages(file) 0
211static inline struct file * 210static inline struct file *
212hugetlb_file_setup(const char *name, unsigned long addr, size_t size, 211hugetlb_file_setup(const char *name, size_t size, vm_flags_t acctflag,
213 vm_flags_t acctflag, struct user_struct **user, int creat_flags, 212 struct user_struct **user, int creat_flags,
214 int page_size_log) 213 int page_size_log)
215{ 214{
216 return ERR_PTR(-ENOSYS); 215 return ERR_PTR(-ENOSYS);
@@ -288,6 +287,13 @@ static inline struct hstate *hstate_file(struct file *f)
288 return hstate_inode(file_inode(f)); 287 return hstate_inode(file_inode(f));
289} 288}
290 289
290static 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
291static inline struct hstate *hstate_vma(struct vm_area_struct *vma) 297static inline struct hstate *hstate_vma(struct vm_area_struct *vma)
292{ 298{
293 return hstate_file(vma->vm_file); 299 return hstate_file(vma->vm_file);
@@ -352,11 +358,12 @@ static inline int hstate_index(struct hstate *h)
352 return h - hstates; 358 return h - hstates;
353} 359}
354 360
355#else 361#else /* CONFIG_HUGETLB_PAGE */
356struct hstate {}; 362struct hstate {};
357#define alloc_huge_page_node(h, nid) NULL 363#define alloc_huge_page_node(h, nid) NULL
358#define alloc_bootmem_huge_page(h) NULL 364#define alloc_bootmem_huge_page(h) NULL
359#define hstate_file(f) NULL 365#define hstate_file(f) NULL
366#define hstate_sizelog(s) NULL
360#define hstate_vma(v) NULL 367#define hstate_vma(v) NULL
361#define hstate_inode(i) NULL 368#define hstate_inode(i) NULL
362#define huge_page_size(h) PAGE_SIZE 369#define huge_page_size(h) PAGE_SIZE
@@ -371,6 +378,6 @@ static inline unsigned int pages_per_huge_page(struct hstate *h)
371} 378}
372#define hstate_index_to_shift(index) 0 379#define hstate_index_to_shift(index) 0
373#define hstate_index(h) 0 380#define hstate_index(h) 0
374#endif 381#endif /* CONFIG_HUGETLB_PAGE */
375 382
376#endif /* _LINUX_HUGETLB_H */ 383#endif /* _LINUX_HUGETLB_H */
diff --git a/ipc/shm.c b/ipc/shm.c
index 8247c49ec073..34af1fe34701 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -491,10 +491,14 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
491 491
492 sprintf (name, "SYSV%08x", key); 492 sprintf (name, "SYSV%08x", key);
493 if (shmflg & SHM_HUGETLB) { 493 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
494 /* hugetlb_file_setup applies strict accounting */ 498 /* hugetlb_file_setup applies strict accounting */
495 if (shmflg & SHM_NORESERVE) 499 if (shmflg & SHM_NORESERVE)
496 acctflag = VM_NORESERVE; 500 acctflag = VM_NORESERVE;
497 file = hugetlb_file_setup(name, 0, size, acctflag, 501 file = hugetlb_file_setup(name, hugesize, acctflag,
498 &shp->mlock_user, HUGETLB_SHMFS_INODE, 502 &shp->mlock_user, HUGETLB_SHMFS_INODE,
499 (shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK); 503 (shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
500 } else { 504 } else {
diff --git a/mm/mmap.c b/mm/mmap.c
index da3e9c04bf37..1ae21d645c68 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1363,15 +1363,20 @@ SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
1363 file = fget(fd); 1363 file = fget(fd);
1364 if (!file) 1364 if (!file)
1365 goto out; 1365 goto out;
1366 if (is_file_hugepages(file))
1367 len = ALIGN(len, huge_page_size(hstate_file(file)));
1366 } else if (flags & MAP_HUGETLB) { 1368 } else if (flags & MAP_HUGETLB) {
1367 struct user_struct *user = NULL; 1369 struct user_struct *user = NULL;
1370
1371 len = ALIGN(len, huge_page_size(hstate_sizelog(
1372 (flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK)));
1368 /* 1373 /*
1369 * VM_NORESERVE is used because the reservations will be 1374 * VM_NORESERVE is used because the reservations will be
1370 * taken when vm_ops->mmap() is called 1375 * taken when vm_ops->mmap() is called
1371 * A dummy user value is used because we are not locking 1376 * A dummy user value is used because we are not locking
1372 * memory so no accounting is necessary 1377 * memory so no accounting is necessary
1373 */ 1378 */
1374 file = hugetlb_file_setup(HUGETLB_ANON_FILE, addr, len, 1379 file = hugetlb_file_setup(HUGETLB_ANON_FILE, len,
1375 VM_NORESERVE, 1380 VM_NORESERVE,
1376 &user, HUGETLB_ANONHUGE_INODE, 1381 &user, HUGETLB_ANONHUGE_INODE,
1377 (flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK); 1382 (flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK);