Commit f83a275dbc5ca1721143698e844243fcadfabf6a

Authored by Mel Gorman
Committed by Linus Torvalds
1 parent 32b154c0b0

mm: account for MAP_SHARED mappings using VM_MAYSHARE and not VM_SHARED in hugetlbfs

Addresses http://bugzilla.kernel.org/show_bug.cgi?id=13302

hugetlbfs reserves huge pages but does not fault them at mmap() time to
ensure that future faults succeed.  The reservation behaviour differs
depending on whether the mapping was mapped MAP_SHARED or MAP_PRIVATE.
For MAP_SHARED mappings, hugepages are reserved when mmap() is first
called and are tracked based on information associated with the inode.
Other processes mapping MAP_SHARED use the same reservation.  MAP_PRIVATE
track the reservations based on the VMA created as part of the mmap()
operation.  Each process mapping MAP_PRIVATE must make its own
reservation.

hugetlbfs currently checks if a VMA is MAP_SHARED with the VM_SHARED flag
and not VM_MAYSHARE.  For file-backed mappings, such as hugetlbfs,
VM_SHARED is set only if the mapping is MAP_SHARED and the file was opened
read-write.  If a shared memory mapping was mapped shared-read-write for
populating of data and mapped shared-read-only by other processes, then
hugetlbfs would account for the mapping as if it was MAP_PRIVATE.  This
causes processes to fail to map the file MAP_SHARED even though it should
succeed as the reservation is there.

This patch alters mm/hugetlb.c and replaces VM_SHARED with VM_MAYSHARE
when the intent of the code was to check whether the VMA was mapped
MAP_SHARED or MAP_PRIVATE.

Signed-off-by: Mel Gorman <mel@csn.ul.ie>
Cc: Hugh Dickins <hugh.dickins@tiscali.co.uk>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: <stable@kernel.org>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: <starlight@binnacle.cx>
Cc: Eric B Munson <ebmunson@us.ibm.com>
Cc: Adam Litke <agl@us.ibm.com>
Cc: Andy Whitcroft <apw@canonical.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 1 changed file with 13 additions and 13 deletions Side-by-side Diff

... ... @@ -316,7 +316,7 @@
316 316 static struct resv_map *vma_resv_map(struct vm_area_struct *vma)
317 317 {
318 318 VM_BUG_ON(!is_vm_hugetlb_page(vma));
319   - if (!(vma->vm_flags & VM_SHARED))
  319 + if (!(vma->vm_flags & VM_MAYSHARE))
320 320 return (struct resv_map *)(get_vma_private_data(vma) &
321 321 ~HPAGE_RESV_MASK);
322 322 return NULL;
... ... @@ -325,7 +325,7 @@
325 325 static void set_vma_resv_map(struct vm_area_struct *vma, struct resv_map *map)
326 326 {
327 327 VM_BUG_ON(!is_vm_hugetlb_page(vma));
328   - VM_BUG_ON(vma->vm_flags & VM_SHARED);
  328 + VM_BUG_ON(vma->vm_flags & VM_MAYSHARE);
329 329  
330 330 set_vma_private_data(vma, (get_vma_private_data(vma) &
331 331 HPAGE_RESV_MASK) | (unsigned long)map);
... ... @@ -334,7 +334,7 @@
334 334 static void set_vma_resv_flags(struct vm_area_struct *vma, unsigned long flags)
335 335 {
336 336 VM_BUG_ON(!is_vm_hugetlb_page(vma));
337   - VM_BUG_ON(vma->vm_flags & VM_SHARED);
  337 + VM_BUG_ON(vma->vm_flags & VM_MAYSHARE);
338 338  
339 339 set_vma_private_data(vma, get_vma_private_data(vma) | flags);
340 340 }
... ... @@ -353,7 +353,7 @@
353 353 if (vma->vm_flags & VM_NORESERVE)
354 354 return;
355 355  
356   - if (vma->vm_flags & VM_SHARED) {
  356 + if (vma->vm_flags & VM_MAYSHARE) {
357 357 /* Shared mappings always use reserves */
358 358 h->resv_huge_pages--;
359 359 } else if (is_vma_resv_set(vma, HPAGE_RESV_OWNER)) {
360 360  
... ... @@ -369,14 +369,14 @@
369 369 void reset_vma_resv_huge_pages(struct vm_area_struct *vma)
370 370 {
371 371 VM_BUG_ON(!is_vm_hugetlb_page(vma));
372   - if (!(vma->vm_flags & VM_SHARED))
  372 + if (!(vma->vm_flags & VM_MAYSHARE))
373 373 vma->vm_private_data = (void *)0;
374 374 }
375 375  
376 376 /* Returns true if the VMA has associated reserve pages */
377 377 static int vma_has_reserves(struct vm_area_struct *vma)
378 378 {
379   - if (vma->vm_flags & VM_SHARED)
  379 + if (vma->vm_flags & VM_MAYSHARE)
380 380 return 1;
381 381 if (is_vma_resv_set(vma, HPAGE_RESV_OWNER))
382 382 return 1;
... ... @@ -924,7 +924,7 @@
924 924 struct address_space *mapping = vma->vm_file->f_mapping;
925 925 struct inode *inode = mapping->host;
926 926  
927   - if (vma->vm_flags & VM_SHARED) {
  927 + if (vma->vm_flags & VM_MAYSHARE) {
928 928 pgoff_t idx = vma_hugecache_offset(h, vma, addr);
929 929 return region_chg(&inode->i_mapping->private_list,
930 930 idx, idx + 1);
... ... @@ -949,7 +949,7 @@
949 949 struct address_space *mapping = vma->vm_file->f_mapping;
950 950 struct inode *inode = mapping->host;
951 951  
952   - if (vma->vm_flags & VM_SHARED) {
  952 + if (vma->vm_flags & VM_MAYSHARE) {
953 953 pgoff_t idx = vma_hugecache_offset(h, vma, addr);
954 954 region_add(&inode->i_mapping->private_list, idx, idx + 1);
955 955  
... ... @@ -1893,7 +1893,7 @@
1893 1893 * at the time of fork() could consume its reserves on COW instead
1894 1894 * of the full address range.
1895 1895 */
1896   - if (!(vma->vm_flags & VM_SHARED) &&
  1896 + if (!(vma->vm_flags & VM_MAYSHARE) &&
1897 1897 is_vma_resv_set(vma, HPAGE_RESV_OWNER) &&
1898 1898 old_page != pagecache_page)
1899 1899 outside_reserve = 1;
... ... @@ -2000,7 +2000,7 @@
2000 2000 clear_huge_page(page, address, huge_page_size(h));
2001 2001 __SetPageUptodate(page);
2002 2002  
2003   - if (vma->vm_flags & VM_SHARED) {
  2003 + if (vma->vm_flags & VM_MAYSHARE) {
2004 2004 int err;
2005 2005 struct inode *inode = mapping->host;
2006 2006  
... ... @@ -2104,7 +2104,7 @@
2104 2104 goto out_mutex;
2105 2105 }
2106 2106  
2107   - if (!(vma->vm_flags & VM_SHARED))
  2107 + if (!(vma->vm_flags & VM_MAYSHARE))
2108 2108 pagecache_page = hugetlbfs_pagecache_page(h,
2109 2109 vma, address);
2110 2110 }
... ... @@ -2289,7 +2289,7 @@
2289 2289 * to reserve the full area even if read-only as mprotect() may be
2290 2290 * called to make the mapping read-write. Assume !vma is a shm mapping
2291 2291 */
2292   - if (!vma || vma->vm_flags & VM_SHARED)
  2292 + if (!vma || vma->vm_flags & VM_MAYSHARE)
2293 2293 chg = region_chg(&inode->i_mapping->private_list, from, to);
2294 2294 else {
2295 2295 struct resv_map *resv_map = resv_map_alloc();
... ... @@ -2330,7 +2330,7 @@
2330 2330 * consumed reservations are stored in the map. Hence, nothing
2331 2331 * else has to be done for private mappings here
2332 2332 */
2333   - if (!vma || vma->vm_flags & VM_SHARED)
  2333 + if (!vma || vma->vm_flags & VM_MAYSHARE)
2334 2334 region_add(&inode->i_mapping->private_list, from, to);
2335 2335 return 0;
2336 2336 }