hugetlbfs: fix alignment of huge page requests
Steven Truelove [Wed, 21 Mar 2012 23:34:14 +0000 (16:34 -0700)]
When calling shmget() with SHM_HUGETLB, shmget aligns the request size to
PAGE_SIZE, but this is not sufficient.

Modify hugetlb_file_setup() to align requests to the huge page size, and
to accept an address argument so that all alignment checks can be
performed in hugetlb_file_setup(), rather than in its callers.  Change
newseg() and mmap_pgoff() to match the new prototype and eliminate a now
redundant alignment check.

[akpm@linux-foundation.org: fix build]
Signed-off-by: Steven Truelove <steven.truelove@utoronto.ca>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

fs/hugetlbfs/inode.c
include/linux/hugetlb.h
ipc/shm.c
mm/mmap.c

index 7940815..631329f 100644 (file)
@@ -935,8 +935,8 @@ static int can_do_hugetlb_shm(void)
        return capable(CAP_IPC_LOCK) || in_group_p(sysctl_hugetlb_shm_group);
 }
 
-struct file *hugetlb_file_setup(const char *name, size_t size,
-                               vm_flags_t acctflag,
+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)
 {
        int error = -ENOMEM;
@@ -945,6 +945,8 @@ struct file *hugetlb_file_setup(const char *name, size_t size,
        struct path path;
        struct dentry *root;
        struct qstr quick_string;
+       struct hstate *hstate;
+       unsigned long num_pages;
 
        *user = NULL;
        if (!hugetlbfs_vfsmount)
@@ -978,10 +980,12 @@ struct file *hugetlb_file_setup(const char *name, size_t size,
        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);
        error = -ENOMEM;
-       if (hugetlb_reserve_pages(inode, 0,
-                       size >> huge_page_shift(hstate_inode(inode)), NULL,
-                       acctflag))
+       if (hugetlb_reserve_pages(inode, 0, num_pages, NULL, acctflag))
                goto out_inode;
 
        d_instantiate(path.dentry, inode);
index cf01817..000837e 100644 (file)
@@ -152,7 +152,8 @@ 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, size_t size, vm_flags_t acct,
+struct file *hugetlb_file_setup(const char *name, unsigned long addr,
+                               size_t size, vm_flags_t acct,
                                struct user_struct **user, int creat_flags);
 
 static inline int is_file_hugepages(struct file *file)
@@ -168,7 +169,8 @@ static inline int is_file_hugepages(struct file *file)
 #else /* !CONFIG_HUGETLBFS */
 
 #define is_file_hugepages(file)                        0
-static inline struct file *hugetlb_file_setup(const char *name, size_t size,
+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)
 {
        return ERR_PTR(-ENOSYS);
index b76be5b..406c5b2 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -482,7 +482,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
                /* hugetlb_file_setup applies strict accounting */
                if (shmflg & SHM_NORESERVE)
                        acctflag = VM_NORESERVE;
-               file = hugetlb_file_setup(name, size, acctflag,
+               file = hugetlb_file_setup(name, 0, size, acctflag,
                                        &shp->mlock_user, HUGETLB_SHMFS_INODE);
        } else {
                /*
index 9e0c0de..a19cc27 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1099,9 +1099,9 @@ SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
                 * A dummy user value is used because we are not locking
                 * memory so no accounting is necessary
                 */
-               len = ALIGN(len, huge_page_size(&default_hstate));
-               file = hugetlb_file_setup(HUGETLB_ANON_FILE, len, VM_NORESERVE,
-                                               &user, HUGETLB_ANONHUGE_INODE);
+               file = hugetlb_file_setup(HUGETLB_ANON_FILE, addr, len,
+                                               VM_NORESERVE, &user,
+                                               HUGETLB_ANONHUGE_INODE);
                if (IS_ERR(file))
                        return PTR_ERR(file);
        }