Skip to contentSkip to conversation

New: MAP_HUGETLB mmap fails for certain sizes

Bug 56881 Superseded Andrew Morton 6 participants 21 messages Apr 23, 2013 – Jun 18, 2013 1 message not in the archive

Subsystems (3)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]/

Andrew Morton

author maintainer
#1 lore raw

(switched to email. Please respond via emailed reply-to-all, not via the bugzilla web interface).

Quoted 6 lines · On Sat, 20 Apr 2013 03:00:30 +0000 (UTC) [email protected]…

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

Summary: MAP_HUGETLB mmap fails for certain sizes
Product: Memory Management
Version: 2.5
Kernel Version: 3.5.0-27

Thanks.

It's a post-3.4 regression, testcase included. Does someone want to take a look, please?

Quoted 39 lines

Platform: All
OS/Version: Linux
Tree: Mainline
Status: NEW
Severity: high
Priority: P1
Component: Other
AssignedTo: [email protected]
ReportedBy: [email protected]
Regression: No

This is on an Ubuntu 12.10 desktop, but the same issue has been found on 12.04 with 3.5.0 kernel.
See the sample program. An allocation with MAP_HUGETLB consistently fails with certain sizes, while it succeeds with others.
The allocation sizes are well below the number of free huge pages.

$ uname -a Linux davide-lnx2 3.5.0-27-generic #46-Ubuntu SMP Mon Mar 25
19:58:17 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
# echo 100 > /proc/sys/vm/nr_hugepages
# cat /proc/meminfo
...
AnonHugePages:         0 kB
HugePages_Total:     100
HugePages_Free:      100
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
$ ./mmappu $((5 * 2 * 1024 * 1024 - 4096))
size=10481664    0x9ff000
hugepage mmap: Invalid argument
Quoted 7 lines
$ ./mmappu $((5 * 2 * 1024 * 1024 - 4095))
size=10481665    0x9ff001
OK!

It seems the trigger point is a normal page size. The same binary works flawlessly in previous kernels.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#2 lore raw
Quoted 13 lines · Andrew Morton

(switched to email. Please respond via emailed reply-to-all, not via the bugzilla web interface).

Quoted 6 lines · On Sat, 20 Apr 2013 03:00:30 +0000 (UTC) [email protected]…

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

Summary: MAP_HUGETLB mmap fails for certain sizes
Product: Memory Management
Version: 2.5
Kernel Version: 3.5.0-27

Quoted 4 lines

Thanks.

It's a post-3.4 regression, testcase included. Does someone want to take a look, please?

Let me try it.

  static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
  {                                                                            
          struct inode *inode = file->f_path.dentry->d_inode;
          loff_t len, vma_len;                               
          int ret;                                           
          struct hstate *h = hstate_file(file);              
          ...                                                                               
          if (vma->vm_pgoff & (~huge_page_mask(h) >> PAGE_SHIFT))              
                  return -EINVAL;                                              

This code checks only whether a given hugetlb vma covers (1 << order) pages, not whether it's exactly hugepage aligned.
Before 2b37c35e6552 "fs/hugetlbfs/inode.c: fix pgoff alignment checking on 32-bit", it was

if (vma->vm_pgoff & ~(huge_page_mask(h) >> PAGE_SHIFT))

, but this made no sense because ~(huge_page_mask(h) >> PAGE_SHIFT) is 0xff for 2M hugepage.
I think the reported problem is not a bug because the behavior before this change was wrong or not as expected.

If we want to make sure that a given address range fits hugepage size, something like below can be useful.

fs/hugetlbfs/inode.c +3 -3
index 78bde32..a98304b 100644
@@ -113,11 +113,11 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
 	vma->vm_flags |= VM_HUGETLB | VM_DONTEXPAND | VM_DONTDUMP;
 	vma->vm_ops = &hugetlb_vm_ops;
 
-	if (vma->vm_pgoff & (~huge_page_mask(h) >> PAGE_SHIFT))
-		return -EINVAL;
-
 	vma_len = (loff_t)(vma->vm_end - vma->vm_start);
 
+	if (vma->len & ~huge_page_mask(h))
+		return -EINVAL;
+
 	mutex_lock(&inode->i_mutex);
 	file_accessed(file);
 

Thanks,
Naoya Horiguchi

Quoted 44 lines
Quoted 44 lines

Platform: All
OS/Version: Linux
Tree: Mainline
Status: NEW
Severity: high
Priority: P1
Component: Other
AssignedTo: [email protected]
ReportedBy: [email protected]
Regression: No

This is on an Ubuntu 12.10 desktop, but the same issue has been found on 12.04 with 3.5.0 kernel.
See the sample program. An allocation with MAP_HUGETLB consistently fails with certain sizes, while it succeeds with others.
The allocation sizes are well below the number of free huge pages.

$ uname -a Linux davide-lnx2 3.5.0-27-generic #46-Ubuntu SMP Mon Mar 25
19:58:17 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
# echo 100 > /proc/sys/vm/nr_hugepages
# cat /proc/meminfo
...
AnonHugePages:         0 kB
HugePages_Total:     100
HugePages_Free:      100
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
$ ./mmappu $((5 * 2 * 1024 * 1024 - 4096))
size=10481664    0x9ff000
hugepage mmap: Invalid argument
$ ./mmappu $((5 * 2 * 1024 * 1024 - 4095))
size=10481665    0x9ff001
OK!
Quoted 8 lines
Quoted 2 lines

It seems the trigger point is a normal page size. The same binary works flawlessly in previous kernels.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#3 lore raw
Quoted 66 lines · Naoya Horiguchi
Quoted 13 lines · Andrew Morton

(switched to email. Please respond via emailed reply-to-all, not via the bugzilla web interface).

Quoted 6 lines · On Sat, 20 Apr 2013 03:00:30 +0000 (UTC) [email protected]…

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

Summary: MAP_HUGETLB mmap fails for certain sizes
Product: Memory Management
Version: 2.5
Kernel Version: 3.5.0-27

Quoted 4 lines

Thanks.

It's a post-3.4 regression, testcase included. Does someone want to take a look, please?

Let me try it.

  static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
  {                                                                            
          struct inode *inode = file->f_path.dentry->d_inode;
          loff_t len, vma_len;                               
          int ret;                                           
          struct hstate *h = hstate_file(file);              
          ...                                                                               
          if (vma->vm_pgoff & (~huge_page_mask(h) >> PAGE_SHIFT))              
                  return -EINVAL;                                              

This code checks only whether a given hugetlb vma covers (1 << order) pages, not whether it's exactly hugepage aligned.
Before 2b37c35e6552 "fs/hugetlbfs/inode.c: fix pgoff alignment checking on 32-bit", it was

if (vma->vm_pgoff & ~(huge_page_mask(h) >> PAGE_SHIFT))

, but this made no sense because ~(huge_page_mask(h) >> PAGE_SHIFT) is 0xff for 2M hugepage.
I think the reported problem is not a bug because the behavior before this change was wrong or not as expected.

If we want to make sure that a given address range fits hugepage size, something like below can be useful.

fs/hugetlbfs/inode.c +3 -3
index 78bde32..a98304b 100644
@@ -113,11 +113,11 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
 	vma->vm_flags |= VM_HUGETLB | VM_DONTEXPAND | VM_DONTDUMP;
 	vma->vm_ops = &hugetlb_vm_ops;
 
-	if (vma->vm_pgoff & (~huge_page_mask(h) >> PAGE_SHIFT))
-		return -EINVAL;
-
 	vma_len = (loff_t)(vma->vm_end - vma->vm_start);
 
+	if (vma->len & ~huge_page_mask(h))
+		return -EINVAL;
+
 	mutex_lock(&inode->i_mutex);
 	file_accessed(file);
 
Quoted 3 lines

Thanks,
Naoya Horiguchi

Hi Naoya,

I think the -EINVAL is returned from hugetlb_get_unmapped_area(), for the two testcases:
1) $ ./mmappu $((5 * 2 * 1024 * 1024 - 4096)) //len1 = 0x9ff000
2) $ ./mmappu $((5 * 2 * 1024 * 1024 - 4095)) //len2 = 0x9ff001

In do_mmap_pgoff(), after "len = PAGE_ALIGN(len);", len1 = 0x9ff000, len2 = 0xa00000, so len2 will pass "if (len & ~huge_page_mask(h))" check in hugetlb_get_unmapped_area(), and len1 will return -EINVAL. As follow:

do_mmap_pgoff()

{
	...
	/* Careful about overflows.. */
	len = PAGE_ALIGN(len);
	...
	get_unmapped_area()
		-->hugetlb_get_unmapped_area()
		   {
			...
			if (len & ~huge_page_mask(h))
				return -EINVAL;
			...
		   }
}

do we need to align len to hugepage size if it's hugetlbfs mmap? something like below:

---
 mm/mmap.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
mm/mmap.c +4 -1
index 0db0de1..bd42be24 100644
@@ -1188,7 +1188,10 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
 		addr = round_hint_to_min(addr);
 
 	/* Careful about overflows.. */
-	len = PAGE_ALIGN(len);
+	if (file && is_file_hugepages(file))
+		len = ALIGN(len, huge_page_size(hstate_file(file)));
+	else
+		len = PAGE_ALIGN(len);
 	if (!len)
 		return -ENOMEM;
 
Signature · 2 lines
Thanks,
Jianguo Wu
Quoted 53 lines
Quoted 44 lines
Quoted 44 lines

Platform: All
OS/Version: Linux
Tree: Mainline
Status: NEW
Severity: high
Priority: P1
Component: Other
AssignedTo: [email protected]
ReportedBy: [email protected]
Regression: No

This is on an Ubuntu 12.10 desktop, but the same issue has been found on 12.04 with 3.5.0 kernel.
See the sample program. An allocation with MAP_HUGETLB consistently fails with certain sizes, while it succeeds with others.
The allocation sizes are well below the number of free huge pages.

$ uname -a Linux davide-lnx2 3.5.0-27-generic #46-Ubuntu SMP Mon Mar 25
19:58:17 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
# echo 100 > /proc/sys/vm/nr_hugepages
# cat /proc/meminfo
...
AnonHugePages:         0 kB
HugePages_Total:     100
HugePages_Free:      100
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
$ ./mmappu $((5 * 2 * 1024 * 1024 - 4096))
size=10481664    0x9ff000
hugepage mmap: Invalid argument
$ ./mmappu $((5 * 2 * 1024 * 1024 - 4095))
size=10481665    0x9ff001
OK!
Quoted 8 lines
Quoted 2 lines

It seems the trigger point is a normal page size. The same binary works flawlessly in previous kernels.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

Quoted 7 lines

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#4 lore raw

On Wed, Apr 24, 2013 at 04:20:50PM +0800, Jianguo Wu wrote:
...

Quoted 12 lines

Hi Naoya,

I think the -EINVAL is returned from hugetlb_get_unmapped_area(), for the two testcases:
1) $ ./mmappu $((5 * 2 * 1024 * 1024 - 4096)) //len1 = 0x9ff000
2) $ ./mmappu $((5 * 2 * 1024 * 1024 - 4095)) //len2 = 0x9ff001

In do_mmap_pgoff(), after "len = PAGE_ALIGN(len);", len1 = 0x9ff000, len2 = 0xa00000, so len2 will pass "if (len & ~huge_page_mask(h))" check in hugetlb_get_unmapped_area(), and len1 will return -EINVAL. As follow:

do_mmap_pgoff()

Quoted 14 lines
{
	...
	/* Careful about overflows.. */
	len = PAGE_ALIGN(len);
	...
	get_unmapped_area()
		-->hugetlb_get_unmapped_area()
		   {
			...
			if (len & ~huge_page_mask(h))
				return -EINVAL;
			...
		   }
}

You are right, Jianguo. Thanks you.
I totally missed the point.

Quoted 14 lines

do we need to align len to hugepage size if it's hugetlbfs mmap? something like below:

---
 mm/mmap.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
mm/mmap.c +0 -0
index 0db0de1..bd42be24 100644
@@ -1188,7 +1188,10 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
 		addr = round_hint_to_min(addr);

mm/mmap.c unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, · line 1197

Quoted 10 lines
mm/mmap.c +4 -1
 
 	/* Careful about overflows.. */
-	len = PAGE_ALIGN(len);
+	if (file && is_file_hugepages(file))
+		len = ALIGN(len, huge_page_size(hstate_file(file)));
+	else
+		len = PAGE_ALIGN(len);
 	if (!len)
 		return -ENOMEM;
 

I like putting this alignment code in if (flags & MAP_HUGETLB) branch in SYSCALL_DEFINE6(mmap_pgoff) as Johannes pointed out in another subthread, because it adds no impact on mmap calls with !MAP_HUGETLB.

Thanks,
Naoya Horiguchi

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#5 lore raw
In reply to Andrew Morton
Quoted 13 lines · Andrew Morton

(switched to email. Please respond via emailed reply-to-all, not via the bugzilla web interface).

Quoted 6 lines · On Sat, 20 Apr 2013 03:00:30 +0000 (UTC) [email protected]…

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

Summary: MAP_HUGETLB mmap fails for certain sizes
Product: Memory Management
Version: 2.5
Kernel Version: 3.5.0-27

Quoted 4 lines

Thanks.

It's a post-3.4 regression, testcase included. Does someone want to take a look, please?

commit 40716e29243de46720e5773797791466c28904ec

Author: Steven Truelove <[email protected]>
Date:   Wed Mar 21 16:34:14 2012 -0700

hugetlbfs: fix alignment of huge page requests

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.

This pushes down the length alignment from mmap_pgoff() into hugetlb_file_setup() and failed to observe that mmap_pgoff() continues to work with that now unaligned length parameter. I.e. this part:

mm/mmap.c +3 -3
index 9e0c0de..a19cc27 100644
@@ -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);
 	}

It would probably be best to revert this commit for the most part and fix up the alignment in the shmem code.

Quoted 12 lines
Quoted 12 lines
# echo 100 > /proc/sys/vm/nr_hugepages
# cat /proc/meminfo
...
AnonHugePages:         0 kB
HugePages_Total:     100
HugePages_Free:      100
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
Quoted 8 lines
Quoted 8 lines
$ ./mmappu $((5 * 2 * 1024 * 1024 - 4096))
size=10481664    0x9ff000
hugepage mmap: Invalid argument
$ ./mmappu $((5 * 2 * 1024 * 1024 - 4095))
size=10481665    0x9ff001
OK!

hugetlb_get_unmapped_area() expects a hugepage-aligned size argument. Before do_mmap_pgoff() calls it, it does len = PAGE_ALIGN(len), which is why the second case works and the first one does not.

How about this (untested) partial revert of the above patch that keeps the shm alignment fix?

fs/hugetlbfs/inode.c +5 -10
index 84e3d85..13a7d51 100644
@@ -929,9 +929,8 @@ 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,
+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);
@@ -939,8 +938,6 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
 	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);
@@ -980,12 +977,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);
include/linux/hugetlb.h +4 -5
index 16e4e9a..437875c 100644
@@ -185,8 +185,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);
 
@@ -205,9 +204,9 @@ 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,
-		int page_size_log)
+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);
 }
ipc/shm.c +4 -1
index cb858df..c1293d1 100644
@@ -491,10 +491,13 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 
 	sprintf (name, "SYSV%08x", key);
 	if (shmflg & SHM_HUGETLB) {
+		unsigned int hugesize;
+
 		/* hugetlb_file_setup applies strict accounting */
 		if (shmflg & SHM_NORESERVE)
 			acctflag = VM_NORESERVE;
-		file = hugetlb_file_setup(name, 0, size, acctflag,
+		hugesize = ALIGN(size, huge_page_size(&default_hstate));
+		file = hugetlb_file_setup(name, hugesize, acctflag,
 				  &shp->mlock_user, HUGETLB_SHMFS_INODE,
 				(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
 	} else {
mm/mmap.c +2 -1
index 6466699..36342dd 100644
@@ -1335,7 +1335,8 @@ 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
 		 */
-		file = hugetlb_file_setup(HUGETLB_ANON_FILE, addr, len,
+		len = ALIGN(len, huge_page_size(&default_hstate));
+		file = hugetlb_file_setup(HUGETLB_ANON_FILE, len,
 				VM_NORESERVE,
 				&user, HUGETLB_ANONHUGE_INODE,
 				(flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK);

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#6 lore raw
Quoted 1 line · Johannes Weiner

On Tue, Apr 23, 2013 at 01:25:22PM -0700, Andrew Morton wrote:

...

Quoted 6 lines

hugetlb_get_unmapped_area() expects a hugepage-aligned size argument. Before do_mmap_pgoff() calls it, it does len = PAGE_ALIGN(len), which is why the second case works and the first one does not.

How about this (untested) partial revert of the above patch that keeps the shm alignment fix?

I have one comment about the suggested patch.

Quoted 77 lines
fs/hugetlbfs/inode.c +5 -10
index 84e3d85..13a7d51 100644
@@ -929,9 +929,8 @@ 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,
+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);
@@ -939,8 +938,6 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
 	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);
@@ -980,12 +977,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);
include/linux/hugetlb.h +4 -5
index 16e4e9a..437875c 100644
@@ -185,8 +185,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);
 
@@ -205,9 +204,9 @@ 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,
-		int page_size_log)
+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);
 }
ipc/shm.c +1 -0
index cb858df..c1293d1 100644
@@ -491,10 +491,13 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 
 	sprintf (name, "SYSV%08x", key);
 	if (shmflg & SHM_HUGETLB) {
+		unsigned int hugesize;

ipc/shm.c static int newseg(struct ipc_namespace *ns, struct ipc_params *params) · line 503

Quoted 10 lines
ipc/shm.c +3 -1
+
 		/* hugetlb_file_setup applies strict accounting */
 		if (shmflg & SHM_NORESERVE)
 			acctflag = VM_NORESERVE;
-		file = hugetlb_file_setup(name, 0, size, acctflag,
+		hugesize = ALIGN(size, huge_page_size(&default_hstate));
+		file = hugetlb_file_setup(name, hugesize, acctflag,
 				  &shp->mlock_user, HUGETLB_SHMFS_INODE,
 				(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
 	} else {

Would it be better to find proper hstate instead of using default_hstate?

Quoted 6 lines
mm/mmap.c +0 -0
index 6466699..36342dd 100644

mm/mmap.c SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len, · line 1342

Quoted 9 lines
mm/mmap.c +2 -1
@@ -1335,7 +1335,8 @@ 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
 		 */
-		file = hugetlb_file_setup(HUGETLB_ANON_FILE, addr, len,
+		len = ALIGN(len, huge_page_size(&default_hstate));
+		file = hugetlb_file_setup(HUGETLB_ANON_FILE, len,
 				VM_NORESERVE,
 				&user, HUGETLB_ANONHUGE_INODE,
 				(flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK);

ditto.

Thanks,
Naoya Horiguchi

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#7 lore raw
Quoted 18 lines · Naoya Horiguchi
Quoted 5 lines · Johannes Weiner
ipc/shm.c +1 -0
@@ -491,10 +491,13 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 
 	sprintf (name, "SYSV%08x", key);
 	if (shmflg & SHM_HUGETLB) {
+		unsigned int hugesize;

ipc/shm.c static int newseg(struct ipc_namespace *ns, struct ipc_params *params) · line 503

Quoted 10 lines
ipc/shm.c +3 -1
+
 		/* hugetlb_file_setup applies strict accounting */
 		if (shmflg & SHM_NORESERVE)
 			acctflag = VM_NORESERVE;
-		file = hugetlb_file_setup(name, 0, size, acctflag,
+		hugesize = ALIGN(size, huge_page_size(&default_hstate));
+		file = hugetlb_file_setup(name, hugesize, acctflag,
 				  &shp->mlock_user, HUGETLB_SHMFS_INODE,
 				(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
 	} else {

Would it be better to find proper hstate instead of using default_hstate?

You are probably right, I guess we can't assume default_hstate anymore after page_size_log can be passed in.

Can we have hugetlb_file_setup() return an adjusted length, or an alignment requirement?

Or pull the hstate lookup into the callsites (since they pass in page_size_log to begin with)?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#8 lore raw
Quoted 20 lines · Johannes Weiner
Quoted 18 lines · Naoya Horiguchi
Quoted 5 lines · Johannes Weiner
(quoted diff) +1 -0
@@ -491,10 +491,13 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 
 	sprintf (name, "SYSV%08x", key);
 	if (shmflg & SHM_HUGETLB) {
+		unsigned int hugesize;

Review static int newseg(struct ipc_namespace *ns, struct ipc_params *params) · line 503

Quoted 10 lines
(quoted diff) +3 -1
+
 		/* hugetlb_file_setup applies strict accounting */
 		if (shmflg & SHM_NORESERVE)
 			acctflag = VM_NORESERVE;
-		file = hugetlb_file_setup(name, 0, size, acctflag,
+		hugesize = ALIGN(size, huge_page_size(&default_hstate));
+		file = hugetlb_file_setup(name, hugesize, acctflag,
 				  &shp->mlock_user, HUGETLB_SHMFS_INODE,
 				(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
 	} else {

Would it be better to find proper hstate instead of using default_hstate?

Quoted 5 lines

You are probably right, I guess we can't assume default_hstate anymore after page_size_log can be passed in.

Can we have hugetlb_file_setup() return an adjusted length, or an alignment requirement?

Yes, it's possible if callers pass the pointer of size (length) to hugetlb_file_setup() and make it adjusted inside the function. And as for alignment, I think it's not a hugetlb_file_setup's job, so we don't have to do it in this function.

Quoted 2 lines

Or pull the hstate lookup into the callsites (since they pass in page_size_log to begin with)?

This is also a possible solution, where we might need to define and export a function converting hugepage order to hstate.

I like the former one, so wrote a patch like below.
# I added your Signed-off-by: because this's based on your draft patch.

# if you don't like it, please let me know.

Thanks,
Naoya Horiguchi
---
From: Naoya Horiguchi <[email protected]>
Date: Wed, 24 Apr 2013 16:44:19 -0400
Subject: [PATCH] hugetlbfs: fix mmap failure in unaligned size request

As reported in https://bugzilla.kernel.org/show_bug.cgi?id=56881, 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 changes the type of parameter size from size_t to (size_t *) in order to align the size in caller side.

---
 fs/hugetlbfs/inode.c    | 20 ++++++++++----------
 include/linux/hugetlb.h |  7 +++----
 ipc/shm.c               |  2 +-
 mm/mmap.c               |  2 +-
 4 files changed, 15 insertions(+), 16 deletions(-)
fs/hugetlbfs/inode.c +10 -10
index 523464e..7adbd7b 100644
@@ -929,9 +929,8 @@ 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,
+struct file *hugetlb_file_setup(const char *name, size_t *sizeptr,
+				vm_flags_t acctflag, struct user_struct **user,
 				int creat_flags, int page_size_log)
 {
 	struct file *file = ERR_PTR(-ENOMEM);
@@ -939,9 +938,8 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
 	struct path path;
 	struct super_block *sb;
 	struct qstr quick_string;
-	struct hstate *hstate;
-	unsigned long num_pages;
 	int hstate_idx;
+	size_t size;
 
 	hstate_idx = get_hstate_idx(page_size_log);
 	if (hstate_idx < 0)
@@ -951,6 +949,10 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
 	if (!hugetlbfs_vfsmount[hstate_idx])
 		return ERR_PTR(-ENOENT);
 
+	size = 1 << hstate_index_to_shift(hstate_idx);
+	if (sizeptr)
+		*sizeptr = ALIGN(*sizeptr, size);
+
 	if (creat_flags == HUGETLB_SHMFS_INODE && !can_do_hugetlb_shm()) {
 		*user = current_user();
 		if (user_shm_lock(size, *user)) {
@@ -980,12 +982,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);
include/linux/hugetlb.h +3 -4
index 8220a8a..ca67d42 100644
@@ -193,8 +193,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);
 
@@ -213,8 +212,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);
ipc/shm.c +1 -1
index cb858df..e2cb809 100644
@@ -494,7 +494,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, 0, size, acctflag,
+		file = hugetlb_file_setup(name, NULL, acctflag,
 				  &shp->mlock_user, HUGETLB_SHMFS_INODE,
 				(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
 	} else {
mm/mmap.c +1 -1
index 2664a47..d03a541 100644
@@ -1333,7 +1333,7 @@ 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
 		 */
-		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);
Signature · 7 lines
1.7.11.7

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#9 lore raw

On Wed, Apr 24, 2013 at 07:05:35PM -0400, Naoya Horiguchi wrote:
...

ipc/shm.c static int newseg(struct ipc_namespace *ns, struct ipc_params *params) · line 500

Quoted 13 lines
ipc/shm.c +1 -1
index cb858df..e2cb809 100644
@@ -494,7 +494,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, 0, size, acctflag,
+		file = hugetlb_file_setup(name, NULL, acctflag,
 				  &shp->mlock_user, HUGETLB_SHMFS_INODE,
 				(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
 	} else {

Ugh, NULL is not correct, it should be &size.
But anyway, I want your comment, and after that I'll repost with fixing this.

Naoya

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#10 lore raw
Quoted 17 lines · Naoya Horiguchi

On Wed, Apr 24, 2013 at 07:05:35PM -0400, Naoya Horiguchi wrote:
...

ipc/shm.c static int newseg(struct ipc_namespace *ns, struct ipc_params *params) · line 500

Quoted 13 lines
ipc/shm.c +1 -1
index cb858df..e2cb809 100644
@@ -494,7 +494,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, 0, size, acctflag,
+		file = hugetlb_file_setup(name, NULL, acctflag,
 				  &shp->mlock_user, HUGETLB_SHMFS_INODE,
 				(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
 	} else {

Ugh, NULL is not correct, it should be &size.

No, shm does not want its size variable aligned, it wants the segment to be the originally requested size... only mmap uses the aligned size later on.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#11 lore raw
In reply to Naoya Horiguchi
Quoted 32 lines · Naoya Horiguchi
Quoted 20 lines · Johannes Weiner
Quoted 18 lines · Naoya Horiguchi
Quoted 5 lines · Johannes Weiner
ipc/shm.c +1 -0
@@ -491,10 +491,13 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 
 	sprintf (name, "SYSV%08x", key);
 	if (shmflg & SHM_HUGETLB) {
+		unsigned int hugesize;

ipc/shm.c static int newseg(struct ipc_namespace *ns, struct ipc_params *params) · line 503

Quoted 10 lines
ipc/shm.c +3 -1
+
 		/* hugetlb_file_setup applies strict accounting */
 		if (shmflg & SHM_NORESERVE)
 			acctflag = VM_NORESERVE;
-		file = hugetlb_file_setup(name, 0, size, acctflag,
+		hugesize = ALIGN(size, huge_page_size(&default_hstate));
+		file = hugetlb_file_setup(name, hugesize, acctflag,
 				  &shp->mlock_user, HUGETLB_SHMFS_INODE,
 				(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
 	} else {

Would it be better to find proper hstate instead of using default_hstate?

Quoted 5 lines

You are probably right, I guess we can't assume default_hstate anymore after page_size_log can be passed in.

Can we have hugetlb_file_setup() return an adjusted length, or an alignment requirement?

Yes, it's possible if callers pass the pointer of size (length) to hugetlb_file_setup() and make it adjusted inside the function. And as for alignment, I think it's not a hugetlb_file_setup's job, so we don't have to do it in this function.

Quoted 5 lines
Quoted 2 lines

Or pull the hstate lookup into the callsites (since they pass in page_size_log to begin with)?

This is also a possible solution, where we might need to define and export a function converting hugepage order to hstate.

After thinking about it some more, I would actually prefer this. The callsites have all the information and the file setup code should not really care about the alignment requirements of the callers.

I.e. export something like get_hstate_idx() but which returns hstate, then make the callers look it up, do the alignment, pass in the aligned size and hstate instead of page_size_log. Then they are free to use the aligned size (mmap) or use the original size (shm).

Quoted 3 lines

I like the former one, so wrote a patch like below.
# I added your Signed-off-by: because this's based on your draft patch.

# if you don't like it, please let me know.

Thanks, I appreciate it. But usually if you take and modify a patch add the original From: line to the changelog to give credit, then add your own signoff and only add other people's signoff after they agree.

Quoted 24 lines
fs/hugetlbfs/inode.c +3 -5
@@ -929,9 +929,8 @@ 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,
+struct file *hugetlb_file_setup(const char *name, size_t *sizeptr,
+				vm_flags_t acctflag, struct user_struct **user,
 				int creat_flags, int page_size_log)
 {
 	struct file *file = ERR_PTR(-ENOMEM);
@@ -939,9 +938,8 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
 	struct path path;
 	struct super_block *sb;
 	struct qstr quick_string;
-	struct hstate *hstate;
-	unsigned long num_pages;
 	int hstate_idx;
+	size_t size;
 
 	hstate_idx = get_hstate_idx(page_size_log);
 	if (hstate_idx < 0)

fs/hugetlbfs/inode.c struct file *hugetlb_file_setup(const char *name, unsigned long addr, · line 954

Quoted 6 lines
fs/hugetlbfs/inode.c +3 -0
@@ -951,6 +949,10 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
 	if (!hugetlbfs_vfsmount[hstate_idx])
 		return ERR_PTR(-ENOENT);
 
+	size = 1 << hstate_index_to_shift(hstate_idx);
+	if (sizeptr)
+		*sizeptr = ALIGN(*sizeptr, size);

You always assume the file will just be one hugepage in size?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#12 lore raw
Quoted 39 lines · Johannes Weiner
Quoted 32 lines · Naoya Horiguchi
Quoted 20 lines · Johannes Weiner
Quoted 18 lines · Naoya Horiguchi
Quoted 5 lines · Johannes Weiner
ipc/shm.c +1 -0
@@ -491,10 +491,13 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 
 	sprintf (name, "SYSV%08x", key);
 	if (shmflg & SHM_HUGETLB) {
+		unsigned int hugesize;

ipc/shm.c static int newseg(struct ipc_namespace *ns, struct ipc_params *params) · line 503

Quoted 10 lines
ipc/shm.c +3 -1
+
 		/* hugetlb_file_setup applies strict accounting */
 		if (shmflg & SHM_NORESERVE)
 			acctflag = VM_NORESERVE;
-		file = hugetlb_file_setup(name, 0, size, acctflag,
+		hugesize = ALIGN(size, huge_page_size(&default_hstate));
+		file = hugetlb_file_setup(name, hugesize, acctflag,
 				  &shp->mlock_user, HUGETLB_SHMFS_INODE,
 				(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
 	} else {

Would it be better to find proper hstate instead of using default_hstate?

Quoted 5 lines

You are probably right, I guess we can't assume default_hstate anymore after page_size_log can be passed in.

Can we have hugetlb_file_setup() return an adjusted length, or an alignment requirement?

Yes, it's possible if callers pass the pointer of size (length) to hugetlb_file_setup() and make it adjusted inside the function. And as for alignment, I think it's not a hugetlb_file_setup's job, so we don't have to do it in this function.

Quoted 5 lines
Quoted 2 lines

Or pull the hstate lookup into the callsites (since they pass in page_size_log to begin with)?

This is also a possible solution, where we might need to define and export a function converting hugepage order to hstate.

Quoted 8 lines

After thinking about it some more, I would actually prefer this. The callsites have all the information and the file setup code should not really care about the alignment requirements of the callers.

I.e. export something like get_hstate_idx() but which returns hstate, then make the callers look it up, do the alignment, pass in the aligned size and hstate instead of page_size_log. Then they are free to use the aligned size (mmap) or use the original size (shm).

OK. I'll do this.

Quoted 7 lines
Quoted 3 lines

I like the former one, so wrote a patch like below.
# I added your Signed-off-by: because this's based on your draft patch.

# if you don't like it, please let me know.

Thanks, I appreciate it. But usually if you take and modify a patch add the original From: line to the changelog to give credit, then add your own signoff and only add other people's signoff after they agree.

OK, got it.

Quoted 27 lines
Quoted 27 lines
fs/hugetlbfs/inode.c +3 -5
@@ -929,9 +929,8 @@ 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,
+struct file *hugetlb_file_setup(const char *name, size_t *sizeptr,
+				vm_flags_t acctflag, struct user_struct **user,
 				int creat_flags, int page_size_log)
 {
 	struct file *file = ERR_PTR(-ENOMEM);
@@ -939,9 +938,8 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
 	struct path path;
 	struct super_block *sb;
 	struct qstr quick_string;
-	struct hstate *hstate;
-	unsigned long num_pages;
 	int hstate_idx;
+	size_t size;
 
 	hstate_idx = get_hstate_idx(page_size_log);
 	if (hstate_idx < 0)
@@ -951,6 +949,10 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
 	if (!hugetlbfs_vfsmount[hstate_idx])
 		return ERR_PTR(-ENOENT);
 
Quoted 5 lines

fs/hugetlbfs/inode.c struct file *hugetlb_file_setup(const char *name, unsigned long addr, · line 954

Quoted 3 lines
fs/hugetlbfs/inode.c +3 -0
+	size = 1 << hstate_index_to_shift(hstate_idx);
+	if (sizeptr)
+		*sizeptr = ALIGN(*sizeptr, size);

You always assume the file will just be one hugepage in size?

No, this line means that *sizeptr (given by the caller) is round up to the multiple of hugepage's size. So assuming size is 2MB, for example, if 5MB is given it's round up to 6MB in return (3 hugepages.)

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#13 lore raw

PATCH v2 1/1 hugetlbfs: fix mmap failure in unaligned size request

Here is a revised patch.
Thank you for the nice feedback, Johannes, Jianguo.
---
From: Naoya Horiguchi <[email protected]>
Date: Fri, 26 Apr 2013 00:31:18 -0400
Subject: [PATCH v2] hugetlbfs: fix mmap failure in unaligned size request

As reported in https://bugzilla.kernel.org/show_bug.cgi?id=56881, 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.

ChangeLog v2:
- introduce hstate_sizelog to calculate alignment in caller side
- add ALIGN also in file mmap path.

---
 fs/hugetlbfs/inode.c    | 24 ++++++++++--------------
 include/linux/hugetlb.h | 19 +++++++++++++------
 ipc/shm.c               |  6 +++++-
 mm/mmap.c               |  7 ++++++-
 4 files changed, 34 insertions(+), 22 deletions(-)
fs/hugetlbfs/inode.c +10 -14
index 523464e..a3f868a 100644
@@ -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;
@@ -929,9 +926,12 @@ 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);
@@ -939,8 +939,6 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
 	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);
@@ -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);
include/linux/hugetlb.h +13 -6
index 8220a8a..c78f5a2 100644
@@ -193,8 +193,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);
 
@@ -213,8 +212,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);
@@ -294,6 +293,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);
@@ -361,12 +367,13 @@ static inline int hstate_index(struct hstate *h)
 extern void dissolve_free_huge_pages(unsigned long start_pfn,
 				     unsigned long end_pfn);
 
-#else
+#else /* !CONFIG_HUGETLB_PAGE */
 struct hstate {};
 #define alloc_huge_page(v, a, r) NULL
 #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
@@ -382,6 +389,6 @@ static inline unsigned int pages_per_huge_page(struct hstate *h)
 #define hstate_index_to_shift(index) 0
 #define hstate_index(h) 0
 #define dissolve_free_huge_pages(s, e) 0
-#endif
+#endif /* !CONFIG_HUGETLB_PAGE */
 
 #endif /* _LINUX_HUGETLB_H */
ipc/shm.c +5 -1
index cb858df..e316cb9 100644
@@ -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 {
mm/mmap.c +6 -1
index 2664a47..212721f 100644
@@ -1325,15 +1325,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;
+		struct hstate *hs = hstate_sizelog((flags >> MAP_HUGE_SHIFT)
+						& MAP_HUGE_MASK);
+		len = ALIGN(len, huge_page_size(hs));
 		/*
 		 * 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);
Signature · 7 lines
1.7.11.7

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#14 lore raw

Re: [PATCH v2] hugetlbfs: fix mmap failure in unaligned size request

Quoted 2 lines · Naoya Horiguchi

Here is a revised patch.
Thank you for the nice feedback, Johannes, Jianguo.

FWIW, this looks good to me. Could you include

Reported-by [email protected]

and resend it to Andrew? Unless Andrew sees and picks it directly from this thread. Hi, Andrew!

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#15 lore raw

PATCH v3 1/1 hugetlbfs: fix mmap failure in unaligned size request

Quoted 7 lines · Johannes Weiner
Quoted 2 lines · Naoya Horiguchi

Here is a revised patch.
Thank you for the nice feedback, Johannes, Jianguo.

FWIW, this looks good to me. Could you include

Reported-by [email protected]

OK, added.
And thank you for the report, iceman_dvd :)

Quoted 2 lines

and resend it to Andrew? Unless Andrew sees and picks it directly from this thread. Hi, Andrew!

Andrew, could you review and merge this into your tree?

Thanks,
Naoya Horiguchi
---
From: Naoya Horiguchi <[email protected]>
Date: Fri, 26 Apr 2013 00:31:18 -0400
Subject: [PATCH v3] hugetlbfs: fix mmap failure in unaligned size request

As reported in https://bugzilla.kernel.org/show_bug.cgi?id=56881, 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.

ChangeLog v3:
- add Reported-by

ChangeLog v2:
- introduce hstate_sizelog to calculate alignment in caller side
- add ALIGN also in file mmap path.

---
 fs/hugetlbfs/inode.c    | 24 ++++++++++--------------
 include/linux/hugetlb.h | 19 +++++++++++++------
 ipc/shm.c               |  6 +++++-
 mm/mmap.c               |  7 ++++++-
 4 files changed, 34 insertions(+), 22 deletions(-)
fs/hugetlbfs/inode.c +10 -14
index 523464e..a3f868a 100644
@@ -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;
@@ -929,9 +926,12 @@ 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);
@@ -939,8 +939,6 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
 	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);
@@ -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);
include/linux/hugetlb.h +13 -6
index 8220a8a..c78f5a2 100644
@@ -193,8 +193,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);
 
@@ -213,8 +212,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);
@@ -294,6 +293,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);
@@ -361,12 +367,13 @@ static inline int hstate_index(struct hstate *h)
 extern void dissolve_free_huge_pages(unsigned long start_pfn,
 				     unsigned long end_pfn);
 
-#else
+#else /* !CONFIG_HUGETLB_PAGE */
 struct hstate {};
 #define alloc_huge_page(v, a, r) NULL
 #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
@@ -382,6 +389,6 @@ static inline unsigned int pages_per_huge_page(struct hstate *h)
 #define hstate_index_to_shift(index) 0
 #define hstate_index(h) 0
 #define dissolve_free_huge_pages(s, e) 0
-#endif
+#endif /* !CONFIG_HUGETLB_PAGE */
 
 #endif /* _LINUX_HUGETLB_H */
ipc/shm.c +5 -1
index cb858df..e316cb9 100644
@@ -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 {
mm/mmap.c +6 -1
index 2664a47..212721f 100644
@@ -1325,15 +1325,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;
+		struct hstate *hs = hstate_sizelog((flags >> MAP_HUGE_SHIFT)
+						& MAP_HUGE_MASK);
+		len = ALIGN(len, huge_page_size(hs));
 		/*
 		 * 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);
Signature · 7 lines
1.7.11.7

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#16 lore raw

Re: [PATCH v3] hugetlbfs: fix mmap failure in unaligned size request

Quoted 21 lines · Naoya Horiguchi
Quoted 6 lines · Johannes Weiner
Quoted 2 lines · Naoya Horiguchi

Here is a revised patch.
Thank you for the nice feedback, Johannes, Jianguo.

FWIW, this looks good to me. Could you include

Reported-by [email protected]

OK, added.
And thank you for the report, iceman_dvd :)

Quoted 2 lines

and resend it to Andrew? Unless Andrew sees and picks it directly from this thread. Hi, Andrew!

Andrew, could you review and merge this into your tree?

Thanks,
Naoya Horiguchi
---
From: Naoya Horiguchi <[email protected]>
Date: Fri, 26 Apr 2013 00:31:18 -0400
Subject: [PATCH v3] hugetlbfs: fix mmap failure in unaligned size request

Quoted 3 lines

As reported in https://bugzilla.kernel.org/show_bug.cgi?id=56881, 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

Hi all,

When I use mmap in userspcae, when will call old_mmap and when will call sys_mmap_pgoff()?

Quoted 183 lines

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.

ChangeLog v3:
- add Reported-by

ChangeLog v2:
- introduce hstate_sizelog to calculate alignment in caller side
- add ALIGN also in file mmap path.

---
 fs/hugetlbfs/inode.c    | 24 ++++++++++--------------
 include/linux/hugetlb.h | 19 +++++++++++++------
 ipc/shm.c               |  6 +++++-
 mm/mmap.c               |  7 ++++++-
 4 files changed, 34 insertions(+), 22 deletions(-)
fs/hugetlbfs/inode.c +10 -14
index 523464e..a3f868a 100644
@@ -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;
@@ -929,9 +926,12 @@ 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);
@@ -939,8 +939,6 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
 	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);
@@ -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);
include/linux/hugetlb.h +13 -6
index 8220a8a..c78f5a2 100644
@@ -193,8 +193,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);
 
@@ -213,8 +212,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);
@@ -294,6 +293,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);
@@ -361,12 +367,13 @@ static inline int hstate_index(struct hstate *h)
 extern void dissolve_free_huge_pages(unsigned long start_pfn,
 				     unsigned long end_pfn);
 
-#else
+#else /* !CONFIG_HUGETLB_PAGE */
 struct hstate {};
 #define alloc_huge_page(v, a, r) NULL
 #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
@@ -382,6 +389,6 @@ static inline unsigned int pages_per_huge_page(struct hstate *h)
 #define hstate_index_to_shift(index) 0
 #define hstate_index(h) 0
 #define dissolve_free_huge_pages(s, e) 0
-#endif
+#endif /* !CONFIG_HUGETLB_PAGE */
 
 #endif /* _LINUX_HUGETLB_H */
ipc/shm.c +5 -1
index cb858df..e316cb9 100644
@@ -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 {
mm/mmap.c +5 -0
index 2664a47..212721f 100644
@@ -1325,15 +1325,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;
+		struct hstate *hs = hstate_sizelog((flags >> MAP_HUGE_SHIFT)
+						& MAP_HUGE_MASK);
+		len = ALIGN(len, huge_page_size(hs));
 		/*

mm/mmap.c SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len, · line 1344

Quoted 10 lines
mm/mmap.c +1 -1
 		 * 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);

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#17 lore raw
In reply to Naoya Horiguchi
Quoted 54 lines · Naoya Horiguchi
Quoted 20 lines · Johannes Weiner
Quoted 18 lines · Naoya Horiguchi
Quoted 5 lines · Johannes Weiner
(quoted diff) +1 -0
@@ -491,10 +491,13 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 
 	sprintf (name, "SYSV%08x", key);
 	if (shmflg & SHM_HUGETLB) {
+		unsigned int hugesize;

Review static int newseg(struct ipc_namespace *ns, struct ipc_params *params) · line 503

Quoted 10 lines
(quoted diff) +3 -1
+
 		/* hugetlb_file_setup applies strict accounting */
 		if (shmflg & SHM_NORESERVE)
 			acctflag = VM_NORESERVE;
-		file = hugetlb_file_setup(name, 0, size, acctflag,
+		hugesize = ALIGN(size, huge_page_size(&default_hstate));
+		file = hugetlb_file_setup(name, hugesize, acctflag,
 				  &shp->mlock_user, HUGETLB_SHMFS_INODE,
 				(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
 	} else {

Would it be better to find proper hstate instead of using default_hstate?

Quoted 5 lines

You are probably right, I guess we can't assume default_hstate anymore after page_size_log can be passed in.

Can we have hugetlb_file_setup() return an adjusted length, or an alignment requirement?

Yes, it's possible if callers pass the pointer of size (length) to hugetlb_file_setup() and make it adjusted inside the function. And as for alignment, I think it's not a hugetlb_file_setup's job, so we don't have to do it in this function.

Quoted 2 lines

Or pull the hstate lookup into the callsites (since they pass in page_size_log to begin with)?

This is also a possible solution, where we might need to define and export a function converting hugepage order to hstate.

I like the former one, so wrote a patch like below.
# I added your Signed-off-by: because this's based on your draft patch.

# if you don't like it, please let me know.

Thanks,
Naoya Horiguchi
---
From: Naoya Horiguchi <[email protected]>
Date: Wed, 24 Apr 2013 16:44:19 -0400
Subject: [PATCH] hugetlbfs: fix mmap failure in unaligned size request

As reported in https://bugzilla.kernel.org/show_bug.cgi?id=56881, 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.

Quoted 8 lines

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 changes the type of parameter size from size_t to (size_t *) in order to align the size in caller side.

Hi Naoya,

This patch only fix anonymous hugetlb mmap case, should also fix hugetlbfs file mmap case?

mm/mmap.c +2 -0
index 0db0de1..5ed9561 100644
@@ -1327,6 +1327,8 @@ SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
 		file = fget(fd);
 		if (!file)
 			goto out;
+		else if (is_file_hugepages(file))
+			len = ALIGN(len, huge_page_size(hstate_file(file)));
 	} else if (flags & MAP_HUGETLB) {
 		struct user_struct *user = NULL;
 		/*

Thanks,
Jianguo Wu

Quoted 106 lines
---
 fs/hugetlbfs/inode.c    | 20 ++++++++++----------
 include/linux/hugetlb.h |  7 +++----
 ipc/shm.c               |  2 +-
 mm/mmap.c               |  2 +-
 4 files changed, 15 insertions(+), 16 deletions(-)
fs/hugetlbfs/inode.c +10 -10
index 523464e..7adbd7b 100644
@@ -929,9 +929,8 @@ 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,
+struct file *hugetlb_file_setup(const char *name, size_t *sizeptr,
+				vm_flags_t acctflag, struct user_struct **user,
 				int creat_flags, int page_size_log)
 {
 	struct file *file = ERR_PTR(-ENOMEM);
@@ -939,9 +938,8 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
 	struct path path;
 	struct super_block *sb;
 	struct qstr quick_string;
-	struct hstate *hstate;
-	unsigned long num_pages;
 	int hstate_idx;
+	size_t size;
 
 	hstate_idx = get_hstate_idx(page_size_log);
 	if (hstate_idx < 0)
@@ -951,6 +949,10 @@ struct file *hugetlb_file_setup(const char *name, unsigned long addr,
 	if (!hugetlbfs_vfsmount[hstate_idx])
 		return ERR_PTR(-ENOENT);
 
+	size = 1 << hstate_index_to_shift(hstate_idx);
+	if (sizeptr)
+		*sizeptr = ALIGN(*sizeptr, size);
+
 	if (creat_flags == HUGETLB_SHMFS_INODE && !can_do_hugetlb_shm()) {
 		*user = current_user();
 		if (user_shm_lock(size, *user)) {
@@ -980,12 +982,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);
include/linux/hugetlb.h +3 -4
index 8220a8a..ca67d42 100644
@@ -193,8 +193,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);
 
@@ -213,8 +212,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);
ipc/shm.c +1 -1
index cb858df..e2cb809 100644
@@ -494,7 +494,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, 0, size, acctflag,
+		file = hugetlb_file_setup(name, NULL, acctflag,
 				  &shp->mlock_user, HUGETLB_SHMFS_INODE,
 				(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
 	} else {
mm/mmap.c +0 -0
index 2664a47..d03a541 100644

mm/mmap.c SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len, · line 1339

Quoted 8 lines
mm/mmap.c +1 -1
@@ -1333,7 +1333,7 @@ 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
 		 */
-		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);

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#18 lore raw
Quoted 60 lines · Jianguo Wu
Quoted 58 lines · Naoya Horiguchi
Quoted 20 lines · Johannes Weiner
Quoted 18 lines · Naoya Horiguchi
Quoted 5 lines · Johannes Weiner
(quoted diff) +1 -0
@@ -491,10 +491,13 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 
 	sprintf (name, "SYSV%08x", key);
 	if (shmflg & SHM_HUGETLB) {
+		unsigned int hugesize;

Review static int newseg(struct ipc_namespace *ns, struct ipc_params *params) · line 503

Quoted 10 lines
(quoted diff) +3 -1
+
 		/* hugetlb_file_setup applies strict accounting */
 		if (shmflg & SHM_NORESERVE)
 			acctflag = VM_NORESERVE;
-		file = hugetlb_file_setup(name, 0, size, acctflag,
+		hugesize = ALIGN(size, huge_page_size(&default_hstate));
+		file = hugetlb_file_setup(name, hugesize, acctflag,
 				  &shp->mlock_user, HUGETLB_SHMFS_INODE,
 				(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
 	} else {

Would it be better to find proper hstate instead of using default_hstate?

Quoted 5 lines

You are probably right, I guess we can't assume default_hstate anymore after page_size_log can be passed in.

Can we have hugetlb_file_setup() return an adjusted length, or an alignment requirement?

Yes, it's possible if callers pass the pointer of size (length) to hugetlb_file_setup() and make it adjusted inside the function. And as for alignment, I think it's not a hugetlb_file_setup's job, so we don't have to do it in this function.

Quoted 2 lines

Or pull the hstate lookup into the callsites (since they pass in page_size_log to begin with)?

This is also a possible solution, where we might need to define and export a function converting hugepage order to hstate.

I like the former one, so wrote a patch like below.
# I added your Signed-off-by: because this's based on your draft patch.

# if you don't like it, please let me know.

Thanks,
Naoya Horiguchi
---
From: Naoya Horiguchi <[email protected]>
Date: Wed, 24 Apr 2013 16:44:19 -0400
Subject: [PATCH] hugetlbfs: fix mmap failure in unaligned size request

As reported in https://bugzilla.kernel.org/show_bug.cgi?id=56881, 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.

Quoted 8 lines
Quoted 4 lines

To fix this, this patch partially reverts that commit, and changes the type of parameter size from size_t to (size_t *) in order to align the size in caller side.

Hi Naoya,

This patch only fix anonymous hugetlb mmap case, should also fix hugetlbfs file mmap case?

Right, thank you, Jianguo.

mm/mmap.c SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len, · line 1334

Quoted 13 lines
mm/mmap.c +2 -0
index 0db0de1..5ed9561 100644
@@ -1327,6 +1327,8 @@ SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
 		file = fget(fd);
 		if (!file)
 			goto out;
+		else if (is_file_hugepages(file))
+			len = ALIGN(len, huge_page_size(hstate_file(file)));
 	} else if (flags & MAP_HUGETLB) {
 		struct user_struct *user = NULL;
 		/*

I'll added this in next post.

Thanks,
Naoya Horiguchi

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#19 lore raw
In reply to Naoya Horiguchi
Quoted 54 lines · Naoya Horiguchi
Quoted 20 lines · Johannes Weiner
Quoted 18 lines · Naoya Horiguchi
Quoted 5 lines · Johannes Weiner
ipc/shm.c +1 -0
@@ -491,10 +491,13 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 
 	sprintf (name, "SYSV%08x", key);
 	if (shmflg & SHM_HUGETLB) {
+		unsigned int hugesize;

ipc/shm.c static int newseg(struct ipc_namespace *ns, struct ipc_params *params) · line 503

Quoted 10 lines
ipc/shm.c +3 -1
+
 		/* hugetlb_file_setup applies strict accounting */
 		if (shmflg & SHM_NORESERVE)
 			acctflag = VM_NORESERVE;
-		file = hugetlb_file_setup(name, 0, size, acctflag,
+		hugesize = ALIGN(size, huge_page_size(&default_hstate));
+		file = hugetlb_file_setup(name, hugesize, acctflag,
 				  &shp->mlock_user, HUGETLB_SHMFS_INODE,
 				(shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
 	} else {

Would it be better to find proper hstate instead of using default_hstate?

Quoted 5 lines

You are probably right, I guess we can't assume default_hstate anymore after page_size_log can be passed in.

Can we have hugetlb_file_setup() return an adjusted length, or an alignment requirement?

Yes, it's possible if callers pass the pointer of size (length) to hugetlb_file_setup() and make it adjusted inside the function. And as for alignment, I think it's not a hugetlb_file_setup's job, so we don't have to do it in this function.

Quoted 2 lines

Or pull the hstate lookup into the callsites (since they pass in page_size_log to begin with)?

This is also a possible solution, where we might need to define and export a function converting hugepage order to hstate.

I like the former one, so wrote a patch like below.
# I added your Signed-off-by: because this's based on your draft patch.

# if you don't like it, please let me know.

Thanks,
Naoya Horiguchi
---
From: Naoya Horiguchi <[email protected]>
Date: Wed, 24 Apr 2013 16:44:19 -0400
Subject: [PATCH] hugetlbfs: fix mmap failure in unaligned size request

As reported in https://bugzilla.kernel.org/show_bug.cgi?id=56881, 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.

Quoted 7 lines

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 changes the type of parameter size from size_t to (size_t *) in order to align the size in caller side.

After the change af73e4d9506d3b797509f3c030e7dcd554f7d9c4 we have alignment related failures in libhugetlbfs test suite. misalign test fails with 3.10-rc5, while it works with 3.9.

-aneesh

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

author maintainer
#20 lore raw
Quoted 13 lines · On Wed, 12 Jun 2013 17:46:16 +0530 "Aneesh Kumar K.V"…
Quoted 13 lines

From: Naoya Horiguchi <[email protected]>
Date: Wed, 24 Apr 2013 16:44:19 -0400
Subject: [PATCH] hugetlbfs: fix mmap failure in unaligned size request

As reported in https://bugzilla.kernel.org/show_bug.cgi?id=56881, 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.

Quoted 7 lines
Quoted 3 lines

To fix this, this patch partially reverts that commit, and changes the type of parameter size from size_t to (size_t *) in order to align the size in caller side.

After the change af73e4d9506d3b797509f3c030e7dcd554f7d9c4 we have alignment related failures in libhugetlbfs test suite. misalign test fails with 3.10-rc5, while it works with 3.9.

What does this mean. Is 3.10-rc5 more strict, or less strict?

If "less strict" then that's expected and old userspace should be OK with the change and the test should be updated (sorry).

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#21 lore raw
Quoted 19 lines · Andrew Morton
Quoted 17 lines · On Wed, 12 Jun 2013 17:46:16 +0530 "Aneesh Kumar K.V"…
Quoted 16 lines

From: Naoya Horiguchi <[email protected]>
Date: Wed, 24 Apr 2013 16:44:19 -0400
Subject: [PATCH] hugetlbfs: fix mmap failure in unaligned size request

As reported in https://bugzilla.kernel.org/show_bug.cgi?id=56881, 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 changes the type of parameter size from size_t to (size_t *) in order to align the size in caller side.

Quoted 8 lines
Quoted 3 lines

After the change af73e4d9506d3b797509f3c030e7dcd554f7d9c4 we have alignment related failures in libhugetlbfs test suite. misalign test fails with 3.10-rc5, while it works with 3.9.

What does this mean. Is 3.10-rc5 more strict, or less strict?

If "less strict" then that's expected and old userspace should be OK with the change and the test should be updated (sorry).

3.10_rc5 is less strict. Also Naoya Horiguchi updated that relevant changes to libhugetlbfs test is also posted at

http://www.mail-archive.com/[email protected]/msg13317.html

-aneesh

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>