2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Generic hugetlb support.
|
|
|
|
* (C) William Irwin, April 2004
|
|
|
|
*/
|
|
|
|
#include <linux/gfp.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/sysctl.h>
|
|
|
|
#include <linux/highmem.h>
|
|
|
|
#include <linux/nodemask.h>
|
2005-06-22 07:14:44 +07:00
|
|
|
#include <linux/pagemap.h>
|
2006-01-06 15:10:46 +07:00
|
|
|
#include <linux/mempolicy.h>
|
2006-01-08 16:00:57 +07:00
|
|
|
#include <linux/cpuset.h>
|
[PATCH] hugepage: serialize hugepage allocation and instantiation
Currently, no lock or mutex is held between allocating a hugepage and
inserting it into the pagetables / page cache. When we do go to insert the
page into pagetables or page cache, we recheck and may free the newly
allocated hugepage. However, since the number of hugepages in the system
is strictly limited, and it's usualy to want to use all of them, this can
still lead to spurious allocation failures.
For example, suppose two processes are both mapping (MAP_SHARED) the same
hugepage file, large enough to consume the entire available hugepage pool.
If they race instantiating the last page in the mapping, they will both
attempt to allocate the last available hugepage. One will fail, of course,
returning OOM from the fault and thus causing the process to be killed,
despite the fact that the entire mapping can, in fact, be instantiated.
The patch fixes this race by the simple method of adding a (sleeping) mutex
to serialize the hugepage fault path between allocation and insertion into
pagetables and/or page cache. It would be possible to avoid the
serialization by catching the allocation failures, waiting on some
condition, then rechecking to see if someone else has instantiated the page
for us. Given the likely frequency of hugepage instantiations, it seems
very doubtful it's worth the extra complexity.
This patch causes no regression on the libhugetlbfs testsuite, and one
test, which can trigger this race now passes where it previously failed.
Actually, the test still sometimes fails, though less often and only as a
shmat() failure, rather processes getting OOM killed by the VM. The dodgy
heuristic tests in fs/hugetlbfs/inode.c for whether there's enough hugepage
space aren't protected by the new mutex, and would be ugly to do so, so
there's still a race there. Another patch to replace those tests with
something saner for this reason as well as others coming...
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 15:08:53 +07:00
|
|
|
#include <linux/mutex.h>
|
2006-01-06 15:10:46 +07:00
|
|
|
|
2005-06-22 07:14:44 +07:00
|
|
|
#include <asm/page.h>
|
|
|
|
#include <asm/pgtable.h>
|
|
|
|
|
|
|
|
#include <linux/hugetlb.h>
|
2006-03-22 15:08:40 +07:00
|
|
|
#include "internal.h"
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL;
|
2006-06-23 16:03:15 +07:00
|
|
|
static unsigned long nr_huge_pages, free_huge_pages, resv_huge_pages;
|
2007-10-16 15:26:18 +07:00
|
|
|
static unsigned long surplus_huge_pages;
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned long max_huge_pages;
|
|
|
|
static struct list_head hugepage_freelists[MAX_NUMNODES];
|
|
|
|
static unsigned int nr_huge_pages_node[MAX_NUMNODES];
|
|
|
|
static unsigned int free_huge_pages_node[MAX_NUMNODES];
|
2007-10-16 15:26:18 +07:00
|
|
|
static unsigned int surplus_huge_pages_node[MAX_NUMNODES];
|
2007-07-17 18:03:13 +07:00
|
|
|
static gfp_t htlb_alloc_mask = GFP_HIGHUSER;
|
|
|
|
unsigned long hugepages_treat_as_movable;
|
hugetlb: introduce nr_overcommit_hugepages sysctl
hugetlb: introduce nr_overcommit_hugepages sysctl
While examining the code to support /proc/sys/vm/hugetlb_dynamic_pool, I
became convinced that having a boolean sysctl was insufficient:
1) To support per-node control of hugepages, I have previously submitted
patches to add a sysfs attribute related to nr_hugepages. However, with
a boolean global value and per-mount quota enforcement constraining the
dynamic pool, adding corresponding control of the dynamic pool on a
per-node basis seems inconsistent to me.
2) Administration of the hugetlb dynamic pool with multiple hugetlbfs
mount points is, arguably, more arduous than it needs to be. Each quota
would need to be set separately, and the sum would need to be monitored.
To ease the administration, and to help make the way for per-node
control of the static & dynamic hugepage pool, I added a separate
sysctl, nr_overcommit_hugepages. This value serves as a high watermark
for the overall hugepage pool, while nr_hugepages serves as a low
watermark. The boolean sysctl can then be removed, as the condition
nr_overcommit_hugepages > 0
indicates the same administrative setting as
hugetlb_dynamic_pool == 1
Quotas still serve as local enforcement of the size of the pool on a
per-mount basis.
A few caveats:
1) There is a race whereby the global surplus huge page counter is
incremented before a hugepage has allocated. Another process could then
try grow the pool, and fail to convert a surplus huge page to a normal
huge page and instead allocate a fresh huge page. I believe this is
benign, as no memory is leaked (the actual pages are still tracked
correctly) and the counters won't go out of sync.
2) Shrinking the static pool while a surplus is in effect will allow the
number of surplus huge pages to exceed the overcommit value. As long as
this condition holds, however, no more surplus huge pages will be
allowed on the system until one of the two sysctls are increased
sufficiently, or the surplus huge pages go out of use and are freed.
Successfully tested on x86_64 with the current libhugetlbfs snapshot,
modified to use the new sysctl.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Adam Litke <agl@us.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Dave Hansen <haveblue@us.ibm.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-12-18 07:20:12 +07:00
|
|
|
unsigned long nr_overcommit_huge_pages;
|
hugetlb: fix hugepage allocation with memoryless nodes
Anton found a problem with the hugetlb pool allocation when some nodes have
no memory (http://marc.info/?l=linux-mm&m=118133042025995&w=2). Lee worked
on versions that tried to fix it, but none were accepted. Christoph has
created a set of patches which allow for GFP_THISNODE allocations to fail
if the node has no memory.
Currently, alloc_fresh_huge_page() returns NULL when it is not able to
allocate a huge page on the current node, as specified by its custom
interleave variable. The callers of this function, though, assume that a
failure in alloc_fresh_huge_page() indicates no hugepages can be allocated
on the system period. This might not be the case, for instance, if we have
an uneven NUMA system, and we happen to try to allocate a hugepage on a
node with less memory and fail, while there is still plenty of free memory
on the other nodes.
To correct this, make alloc_fresh_huge_page() search through all online
nodes before deciding no hugepages can be allocated. Add a helper function
for actually allocating the hugepage. Use a new global nid iterator to
control which nid to allocate on.
Note: we expect particular semantics for __GFP_THISNODE, which are now
enforced even for memoryless nodes. That is, there is should be no
fallback to other nodes. Therefore, we rely on the nid passed into
alloc_pages_node() to be the nid the page comes from. If this is
incorrect, accounting will break.
Tested on x86 !NUMA, x86 NUMA, x86_64 NUMA and ppc64 NUMA (with 2
memoryless nodes).
Before on the ppc64 box:
Trying to clear the hugetlb pool
Done. 0 free
Trying to resize the pool to 100
Node 0 HugePages_Free: 25
Node 1 HugePages_Free: 75
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. Initially 100 free
Trying to resize the pool to 200
Node 0 HugePages_Free: 50
Node 1 HugePages_Free: 150
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. 200 free
After:
Trying to clear the hugetlb pool
Done. 0 free
Trying to resize the pool to 100
Node 0 HugePages_Free: 50
Node 1 HugePages_Free: 50
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. Initially 100 free
Trying to resize the pool to 200
Node 0 HugePages_Free: 100
Node 1 HugePages_Free: 100
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. 200 free
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Christoph Lameter <clameter@sgi.com>
Cc: Adam Litke <agl@us.ibm.com>
Cc: David Gibson <hermes@gibson.dropbear.id.au>
Cc: Badari Pulavarty <pbadari@us.ibm.com>
Cc: Ken Chen <kenchen@google.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Lee Schermerhorn <lee.schermerhorn@hp.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-10-16 15:26:24 +07:00
|
|
|
static int hugetlb_next_nid;
|
2007-07-17 18:03:13 +07:00
|
|
|
|
[PATCH] hugepage: serialize hugepage allocation and instantiation
Currently, no lock or mutex is held between allocating a hugepage and
inserting it into the pagetables / page cache. When we do go to insert the
page into pagetables or page cache, we recheck and may free the newly
allocated hugepage. However, since the number of hugepages in the system
is strictly limited, and it's usualy to want to use all of them, this can
still lead to spurious allocation failures.
For example, suppose two processes are both mapping (MAP_SHARED) the same
hugepage file, large enough to consume the entire available hugepage pool.
If they race instantiating the last page in the mapping, they will both
attempt to allocate the last available hugepage. One will fail, of course,
returning OOM from the fault and thus causing the process to be killed,
despite the fact that the entire mapping can, in fact, be instantiated.
The patch fixes this race by the simple method of adding a (sleeping) mutex
to serialize the hugepage fault path between allocation and insertion into
pagetables and/or page cache. It would be possible to avoid the
serialization by catching the allocation failures, waiting on some
condition, then rechecking to see if someone else has instantiated the page
for us. Given the likely frequency of hugepage instantiations, it seems
very doubtful it's worth the extra complexity.
This patch causes no regression on the libhugetlbfs testsuite, and one
test, which can trigger this race now passes where it previously failed.
Actually, the test still sometimes fails, though less often and only as a
shmat() failure, rather processes getting OOM killed by the VM. The dodgy
heuristic tests in fs/hugetlbfs/inode.c for whether there's enough hugepage
space aren't protected by the new mutex, and would be ugly to do so, so
there's still a race there. Another patch to replace those tests with
something saner for this reason as well as others coming...
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 15:08:53 +07:00
|
|
|
/*
|
|
|
|
* Protects updates to hugepage_freelists, nr_huge_pages, and free_huge_pages
|
|
|
|
*/
|
|
|
|
static DEFINE_SPINLOCK(hugetlb_lock);
|
2005-11-22 12:32:28 +07:00
|
|
|
|
2006-03-22 15:08:51 +07:00
|
|
|
static void clear_huge_page(struct page *page, unsigned long addr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
might_sleep();
|
|
|
|
for (i = 0; i < (HPAGE_SIZE/PAGE_SIZE); i++) {
|
|
|
|
cond_resched();
|
2007-10-01 15:20:10 +07:00
|
|
|
clear_user_highpage(page + i, addr + i * PAGE_SIZE);
|
2006-03-22 15:08:51 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void copy_huge_page(struct page *dst, struct page *src,
|
2006-12-13 00:14:55 +07:00
|
|
|
unsigned long addr, struct vm_area_struct *vma)
|
2006-03-22 15:08:51 +07:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
might_sleep();
|
|
|
|
for (i = 0; i < HPAGE_SIZE/PAGE_SIZE; i++) {
|
|
|
|
cond_resched();
|
2006-12-13 00:14:55 +07:00
|
|
|
copy_user_highpage(dst + i, src + i, addr + i*PAGE_SIZE, vma);
|
2006-03-22 15:08:51 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static void enqueue_huge_page(struct page *page)
|
|
|
|
{
|
|
|
|
int nid = page_to_nid(page);
|
|
|
|
list_add(&page->lru, &hugepage_freelists[nid]);
|
|
|
|
free_huge_pages++;
|
|
|
|
free_huge_pages_node[nid]++;
|
|
|
|
}
|
|
|
|
|
2006-01-06 15:10:46 +07:00
|
|
|
static struct page *dequeue_huge_page(struct vm_area_struct *vma,
|
|
|
|
unsigned long address)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2007-07-16 13:38:02 +07:00
|
|
|
int nid;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct page *page = NULL;
|
Fix NUMA Memory Policy Reference Counting
This patch proposes fixes to the reference counting of memory policy in the
page allocation paths and in show_numa_map(). Extracted from my "Memory
Policy Cleanups and Enhancements" series as stand-alone.
Shared policy lookup [shmem] has always added a reference to the policy,
but this was never unrefed after page allocation or after formatting the
numa map data.
Default system policy should not require additional ref counting, nor
should the current task's task policy. However, show_numa_map() calls
get_vma_policy() to examine what may be [likely is] another task's policy.
The latter case needs protection against freeing of the policy.
This patch adds a reference count to a mempolicy returned by
get_vma_policy() when the policy is a vma policy or another task's
mempolicy. Again, shared policy is already reference counted on lookup. A
matching "unref" [__mpol_free()] is performed in alloc_page_vma() for
shared and vma policies, and in show_numa_map() for shared and another
task's mempolicy. We can call __mpol_free() directly, saving an admittedly
inexpensive inline NULL test, because we know we have a non-NULL policy.
Handling policy ref counts for hugepages is a bit trickier.
huge_zonelist() returns a zone list that might come from a shared or vma
'BIND policy. In this case, we should hold the reference until after the
huge page allocation in dequeue_hugepage(). The patch modifies
huge_zonelist() to return a pointer to the mempolicy if it needs to be
unref'd after allocation.
Kernel Build [16cpu, 32GB, ia64] - average of 10 runs:
w/o patch w/ refcount patch
Avg Std Devn Avg Std Devn
Real: 100.59 0.38 100.63 0.43
User: 1209.60 0.37 1209.91 0.31
System: 81.52 0.42 81.64 0.34
Signed-off-by: Lee Schermerhorn <lee.schermerhorn@hp.com>
Acked-by: Andi Kleen <ak@suse.de>
Cc: Christoph Lameter <clameter@sgi.com>
Acked-by: Mel Gorman <mel@csn.ul.ie>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-09-19 12:46:47 +07:00
|
|
|
struct mempolicy *mpol;
|
2007-07-17 18:03:13 +07:00
|
|
|
struct zonelist *zonelist = huge_zonelist(vma, address,
|
Fix NUMA Memory Policy Reference Counting
This patch proposes fixes to the reference counting of memory policy in the
page allocation paths and in show_numa_map(). Extracted from my "Memory
Policy Cleanups and Enhancements" series as stand-alone.
Shared policy lookup [shmem] has always added a reference to the policy,
but this was never unrefed after page allocation or after formatting the
numa map data.
Default system policy should not require additional ref counting, nor
should the current task's task policy. However, show_numa_map() calls
get_vma_policy() to examine what may be [likely is] another task's policy.
The latter case needs protection against freeing of the policy.
This patch adds a reference count to a mempolicy returned by
get_vma_policy() when the policy is a vma policy or another task's
mempolicy. Again, shared policy is already reference counted on lookup. A
matching "unref" [__mpol_free()] is performed in alloc_page_vma() for
shared and vma policies, and in show_numa_map() for shared and another
task's mempolicy. We can call __mpol_free() directly, saving an admittedly
inexpensive inline NULL test, because we know we have a non-NULL policy.
Handling policy ref counts for hugepages is a bit trickier.
huge_zonelist() returns a zone list that might come from a shared or vma
'BIND policy. In this case, we should hold the reference until after the
huge page allocation in dequeue_hugepage(). The patch modifies
huge_zonelist() to return a pointer to the mempolicy if it needs to be
unref'd after allocation.
Kernel Build [16cpu, 32GB, ia64] - average of 10 runs:
w/o patch w/ refcount patch
Avg Std Devn Avg Std Devn
Real: 100.59 0.38 100.63 0.43
User: 1209.60 0.37 1209.91 0.31
System: 81.52 0.42 81.64 0.34
Signed-off-by: Lee Schermerhorn <lee.schermerhorn@hp.com>
Acked-by: Andi Kleen <ak@suse.de>
Cc: Christoph Lameter <clameter@sgi.com>
Acked-by: Mel Gorman <mel@csn.ul.ie>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-09-19 12:46:47 +07:00
|
|
|
htlb_alloc_mask, &mpol);
|
2006-01-06 15:10:45 +07:00
|
|
|
struct zone **z;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-01-06 15:10:45 +07:00
|
|
|
for (z = zonelist->zones; *z; z++) {
|
2006-09-26 13:31:55 +07:00
|
|
|
nid = zone_to_nid(*z);
|
2007-07-17 18:03:13 +07:00
|
|
|
if (cpuset_zone_allowed_softwall(*z, htlb_alloc_mask) &&
|
2007-07-19 15:49:08 +07:00
|
|
|
!list_empty(&hugepage_freelists[nid])) {
|
|
|
|
page = list_entry(hugepage_freelists[nid].next,
|
|
|
|
struct page, lru);
|
|
|
|
list_del(&page->lru);
|
|
|
|
free_huge_pages--;
|
|
|
|
free_huge_pages_node[nid]--;
|
2007-10-16 15:26:19 +07:00
|
|
|
if (vma && vma->vm_flags & VM_MAYSHARE)
|
|
|
|
resv_huge_pages--;
|
2007-07-24 08:44:00 +07:00
|
|
|
break;
|
2007-07-19 15:49:08 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
Fix NUMA Memory Policy Reference Counting
This patch proposes fixes to the reference counting of memory policy in the
page allocation paths and in show_numa_map(). Extracted from my "Memory
Policy Cleanups and Enhancements" series as stand-alone.
Shared policy lookup [shmem] has always added a reference to the policy,
but this was never unrefed after page allocation or after formatting the
numa map data.
Default system policy should not require additional ref counting, nor
should the current task's task policy. However, show_numa_map() calls
get_vma_policy() to examine what may be [likely is] another task's policy.
The latter case needs protection against freeing of the policy.
This patch adds a reference count to a mempolicy returned by
get_vma_policy() when the policy is a vma policy or another task's
mempolicy. Again, shared policy is already reference counted on lookup. A
matching "unref" [__mpol_free()] is performed in alloc_page_vma() for
shared and vma policies, and in show_numa_map() for shared and another
task's mempolicy. We can call __mpol_free() directly, saving an admittedly
inexpensive inline NULL test, because we know we have a non-NULL policy.
Handling policy ref counts for hugepages is a bit trickier.
huge_zonelist() returns a zone list that might come from a shared or vma
'BIND policy. In this case, we should hold the reference until after the
huge page allocation in dequeue_hugepage(). The patch modifies
huge_zonelist() to return a pointer to the mempolicy if it needs to be
unref'd after allocation.
Kernel Build [16cpu, 32GB, ia64] - average of 10 runs:
w/o patch w/ refcount patch
Avg Std Devn Avg Std Devn
Real: 100.59 0.38 100.63 0.43
User: 1209.60 0.37 1209.91 0.31
System: 81.52 0.42 81.64 0.34
Signed-off-by: Lee Schermerhorn <lee.schermerhorn@hp.com>
Acked-by: Andi Kleen <ak@suse.de>
Cc: Christoph Lameter <clameter@sgi.com>
Acked-by: Mel Gorman <mel@csn.ul.ie>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-09-19 12:46:47 +07:00
|
|
|
mpol_free(mpol); /* unref if mpol !NULL */
|
2005-04-17 05:20:36 +07:00
|
|
|
return page;
|
|
|
|
}
|
|
|
|
|
hugetlb: Move update_and_free_page
Dynamic huge page pool resizing.
In most real-world scenarios, configuring the size of the hugetlb pool
correctly is a difficult task. If too few pages are allocated to the pool,
applications using MAP_SHARED may fail to mmap() a hugepage region and
applications using MAP_PRIVATE may receive SIGBUS. Isolating too much memory
in the hugetlb pool means it is not available for other uses, especially those
programs not using huge pages.
The obvious answer is to let the hugetlb pool grow and shrink in response to
the runtime demand for huge pages. The work Mel Gorman has been doing to
establish a memory zone for movable memory allocations makes dynamically
resizing the hugetlb pool reliable within the limits of that zone. This patch
series implements dynamic pool resizing for private and shared mappings while
being careful to maintain existing semantics. Please reply with your comments
and feedback; even just to say whether it would be a useful feature to you.
Thanks.
How it works
============
Upon depletion of the hugetlb pool, rather than reporting an error immediately,
first try and allocate the needed huge pages directly from the buddy allocator.
Care must be taken to avoid unbounded growth of the hugetlb pool, so the
hugetlb filesystem quota is used to limit overall pool size.
The real work begins when we decide there is a shortage of huge pages. What
happens next depends on whether the pages are for a private or shared mapping.
Private mappings are straightforward. At fault time, if alloc_huge_page()
fails, we allocate a page from the buddy allocator and increment the source
node's surplus_huge_pages counter. When free_huge_page() is called for a page
on a node with a surplus, the page is freed directly to the buddy allocator
instead of the hugetlb pool.
Because shared mappings require all of the pages to be reserved up front, some
additional work must be done at mmap() to support them. We determine the
reservation shortage and allocate the required number of pages all at once.
These pages are then added to the hugetlb pool and marked reserved. Where that
is not possible the mmap() will fail. As with private mappings, the
appropriate surplus counters are updated. Since reserved huge pages won't
necessarily be used by the process, we can't be sure that free_huge_page() will
always be called to return surplus pages to the buddy allocator. To prevent
the huge page pool from bloating, we must free unused surplus pages when their
reservation has ended.
Controlling it
==============
With the entire patch series applied, pool resizing is off by default so unless
specific action is taken, the semantics are unchanged.
To take advantage of the flexibility afforded by this patch series one must
tolerate a change in semantics. To control hugetlb pool growth, the following
techniques can be employed:
* A sysctl tunable to enable/disable the feature entirely
* The size= mount option for hugetlbfs filesystems to limit pool size
Performance
===========
When contiguous memory is readily available, it is expected that the cost of
dynamicly resizing the pool will be small. This series has been performance
tested with 'stream' to measure this cost.
Stream (http://www.cs.virginia.edu/stream/) was linked with libhugetlbfs to
enable remapping of the text and data/bss segments into huge pages.
Stream with small array
-----------------------
Baseline: nr_hugepages = 0, No libhugetlbfs segment remapping
Preallocated: nr_hugepages = 5, Text and data/bss remapping
Dynamic: nr_hugepages = 0, Text and data/bss remapping
Rate (MB/s)
Function Baseline Preallocated Dynamic
Copy: 4695.6266 5942.8371 5982.2287
Scale: 4451.5776 5017.1419 5658.7843
Add: 5815.8849 7927.7827 8119.3552
Triad: 5949.4144 8527.6492 8110.6903
Stream with large array
-----------------------
Baseline: nr_hugepages = 0, No libhugetlbfs segment remapping
Preallocated: nr_hugepages = 67, Text and data/bss remapping
Dynamic: nr_hugepages = 0, Text and data/bss remapping
Rate (MB/s)
Function Baseline Preallocated Dynamic
Copy: 2227.8281 2544.2732 2546.4947
Scale: 2136.3208 2430.7294 2421.2074
Add: 2773.1449 4004.0021 3999.4331
Triad: 2748.4502 3777.0109 3773.4970
* All numbers are averages taken from 10 consecutive runs with a maximum
standard deviation of 1.3 percent noted.
This patch:
Simply move update_and_free_page() so that it can be reused later in this
patch series. The implementation is not changed.
Signed-off-by: Adam Litke <agl@us.ibm.com>
Acked-by: Andy Whitcroft <apw@shadowen.org>
Acked-by: Dave McCracken <dave.mccracken@oracle.com>
Acked-by: William Irwin <bill.irwin@oracle.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Cc: Ken Chen <kenchen@google.com>
Cc: Badari Pulavarty <pbadari@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-10-16 15:26:16 +07:00
|
|
|
static void update_and_free_page(struct page *page)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
nr_huge_pages--;
|
|
|
|
nr_huge_pages_node[page_to_nid(page)]--;
|
|
|
|
for (i = 0; i < (HPAGE_SIZE / PAGE_SIZE); i++) {
|
|
|
|
page[i].flags &= ~(1 << PG_locked | 1 << PG_error | 1 << PG_referenced |
|
|
|
|
1 << PG_dirty | 1 << PG_active | 1 << PG_reserved |
|
|
|
|
1 << PG_private | 1<< PG_writeback);
|
|
|
|
}
|
|
|
|
set_compound_page_dtor(page, NULL);
|
|
|
|
set_page_refcounted(page);
|
|
|
|
__free_pages(page, HUGETLB_PAGE_ORDER);
|
|
|
|
}
|
|
|
|
|
2006-03-22 15:08:56 +07:00
|
|
|
static void free_huge_page(struct page *page)
|
|
|
|
{
|
2007-10-16 15:26:18 +07:00
|
|
|
int nid = page_to_nid(page);
|
2007-11-15 07:59:38 +07:00
|
|
|
struct address_space *mapping;
|
2006-03-22 15:08:56 +07:00
|
|
|
|
2007-11-15 07:59:38 +07:00
|
|
|
mapping = (struct address_space *) page_private(page);
|
2007-10-16 15:26:18 +07:00
|
|
|
BUG_ON(page_count(page));
|
2006-03-22 15:08:56 +07:00
|
|
|
INIT_LIST_HEAD(&page->lru);
|
|
|
|
|
|
|
|
spin_lock(&hugetlb_lock);
|
2007-10-16 15:26:18 +07:00
|
|
|
if (surplus_huge_pages_node[nid]) {
|
|
|
|
update_and_free_page(page);
|
|
|
|
surplus_huge_pages--;
|
|
|
|
surplus_huge_pages_node[nid]--;
|
|
|
|
} else {
|
|
|
|
enqueue_huge_page(page);
|
|
|
|
}
|
2006-03-22 15:08:56 +07:00
|
|
|
spin_unlock(&hugetlb_lock);
|
2007-11-15 07:59:38 +07:00
|
|
|
if (mapping)
|
2007-11-15 07:59:41 +07:00
|
|
|
hugetlb_put_quota(mapping, 1);
|
2007-11-15 07:59:38 +07:00
|
|
|
set_page_private(page, 0);
|
2006-03-22 15:08:56 +07:00
|
|
|
}
|
|
|
|
|
2007-10-16 15:26:18 +07:00
|
|
|
/*
|
|
|
|
* Increment or decrement surplus_huge_pages. Keep node-specific counters
|
|
|
|
* balanced by operating on them in a round-robin fashion.
|
|
|
|
* Returns 1 if an adjustment was made.
|
|
|
|
*/
|
|
|
|
static int adjust_pool_surplus(int delta)
|
|
|
|
{
|
|
|
|
static int prev_nid;
|
|
|
|
int nid = prev_nid;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
VM_BUG_ON(delta != -1 && delta != 1);
|
|
|
|
do {
|
|
|
|
nid = next_node(nid, node_online_map);
|
|
|
|
if (nid == MAX_NUMNODES)
|
|
|
|
nid = first_node(node_online_map);
|
|
|
|
|
|
|
|
/* To shrink on this node, there must be a surplus page */
|
|
|
|
if (delta < 0 && !surplus_huge_pages_node[nid])
|
|
|
|
continue;
|
|
|
|
/* Surplus cannot exceed the total number of pages */
|
|
|
|
if (delta > 0 && surplus_huge_pages_node[nid] >=
|
|
|
|
nr_huge_pages_node[nid])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
surplus_huge_pages += delta;
|
|
|
|
surplus_huge_pages_node[nid] += delta;
|
|
|
|
ret = 1;
|
|
|
|
break;
|
|
|
|
} while (nid != prev_nid);
|
|
|
|
|
|
|
|
prev_nid = nid;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
hugetlb: fix hugepage allocation with memoryless nodes
Anton found a problem with the hugetlb pool allocation when some nodes have
no memory (http://marc.info/?l=linux-mm&m=118133042025995&w=2). Lee worked
on versions that tried to fix it, but none were accepted. Christoph has
created a set of patches which allow for GFP_THISNODE allocations to fail
if the node has no memory.
Currently, alloc_fresh_huge_page() returns NULL when it is not able to
allocate a huge page on the current node, as specified by its custom
interleave variable. The callers of this function, though, assume that a
failure in alloc_fresh_huge_page() indicates no hugepages can be allocated
on the system period. This might not be the case, for instance, if we have
an uneven NUMA system, and we happen to try to allocate a hugepage on a
node with less memory and fail, while there is still plenty of free memory
on the other nodes.
To correct this, make alloc_fresh_huge_page() search through all online
nodes before deciding no hugepages can be allocated. Add a helper function
for actually allocating the hugepage. Use a new global nid iterator to
control which nid to allocate on.
Note: we expect particular semantics for __GFP_THISNODE, which are now
enforced even for memoryless nodes. That is, there is should be no
fallback to other nodes. Therefore, we rely on the nid passed into
alloc_pages_node() to be the nid the page comes from. If this is
incorrect, accounting will break.
Tested on x86 !NUMA, x86 NUMA, x86_64 NUMA and ppc64 NUMA (with 2
memoryless nodes).
Before on the ppc64 box:
Trying to clear the hugetlb pool
Done. 0 free
Trying to resize the pool to 100
Node 0 HugePages_Free: 25
Node 1 HugePages_Free: 75
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. Initially 100 free
Trying to resize the pool to 200
Node 0 HugePages_Free: 50
Node 1 HugePages_Free: 150
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. 200 free
After:
Trying to clear the hugetlb pool
Done. 0 free
Trying to resize the pool to 100
Node 0 HugePages_Free: 50
Node 1 HugePages_Free: 50
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. Initially 100 free
Trying to resize the pool to 200
Node 0 HugePages_Free: 100
Node 1 HugePages_Free: 100
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. 200 free
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Christoph Lameter <clameter@sgi.com>
Cc: Adam Litke <agl@us.ibm.com>
Cc: David Gibson <hermes@gibson.dropbear.id.au>
Cc: Badari Pulavarty <pbadari@us.ibm.com>
Cc: Ken Chen <kenchen@google.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Lee Schermerhorn <lee.schermerhorn@hp.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-10-16 15:26:24 +07:00
|
|
|
static struct page *alloc_fresh_huge_page_node(int nid)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct page *page;
|
2007-07-16 13:38:12 +07:00
|
|
|
|
hugetlb: fix hugepage allocation with memoryless nodes
Anton found a problem with the hugetlb pool allocation when some nodes have
no memory (http://marc.info/?l=linux-mm&m=118133042025995&w=2). Lee worked
on versions that tried to fix it, but none were accepted. Christoph has
created a set of patches which allow for GFP_THISNODE allocations to fail
if the node has no memory.
Currently, alloc_fresh_huge_page() returns NULL when it is not able to
allocate a huge page on the current node, as specified by its custom
interleave variable. The callers of this function, though, assume that a
failure in alloc_fresh_huge_page() indicates no hugepages can be allocated
on the system period. This might not be the case, for instance, if we have
an uneven NUMA system, and we happen to try to allocate a hugepage on a
node with less memory and fail, while there is still plenty of free memory
on the other nodes.
To correct this, make alloc_fresh_huge_page() search through all online
nodes before deciding no hugepages can be allocated. Add a helper function
for actually allocating the hugepage. Use a new global nid iterator to
control which nid to allocate on.
Note: we expect particular semantics for __GFP_THISNODE, which are now
enforced even for memoryless nodes. That is, there is should be no
fallback to other nodes. Therefore, we rely on the nid passed into
alloc_pages_node() to be the nid the page comes from. If this is
incorrect, accounting will break.
Tested on x86 !NUMA, x86 NUMA, x86_64 NUMA and ppc64 NUMA (with 2
memoryless nodes).
Before on the ppc64 box:
Trying to clear the hugetlb pool
Done. 0 free
Trying to resize the pool to 100
Node 0 HugePages_Free: 25
Node 1 HugePages_Free: 75
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. Initially 100 free
Trying to resize the pool to 200
Node 0 HugePages_Free: 50
Node 1 HugePages_Free: 150
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. 200 free
After:
Trying to clear the hugetlb pool
Done. 0 free
Trying to resize the pool to 100
Node 0 HugePages_Free: 50
Node 1 HugePages_Free: 50
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. Initially 100 free
Trying to resize the pool to 200
Node 0 HugePages_Free: 100
Node 1 HugePages_Free: 100
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. 200 free
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Christoph Lameter <clameter@sgi.com>
Cc: Adam Litke <agl@us.ibm.com>
Cc: David Gibson <hermes@gibson.dropbear.id.au>
Cc: Badari Pulavarty <pbadari@us.ibm.com>
Cc: Ken Chen <kenchen@google.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Lee Schermerhorn <lee.schermerhorn@hp.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-10-16 15:26:24 +07:00
|
|
|
page = alloc_pages_node(nid,
|
|
|
|
htlb_alloc_mask|__GFP_COMP|__GFP_THISNODE|__GFP_NOWARN,
|
|
|
|
HUGETLB_PAGE_ORDER);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (page) {
|
2006-12-07 11:33:32 +07:00
|
|
|
set_compound_page_dtor(page, free_huge_page);
|
2005-11-22 12:32:28 +07:00
|
|
|
spin_lock(&hugetlb_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
nr_huge_pages++;
|
hugetlb: fix hugepage allocation with memoryless nodes
Anton found a problem with the hugetlb pool allocation when some nodes have
no memory (http://marc.info/?l=linux-mm&m=118133042025995&w=2). Lee worked
on versions that tried to fix it, but none were accepted. Christoph has
created a set of patches which allow for GFP_THISNODE allocations to fail
if the node has no memory.
Currently, alloc_fresh_huge_page() returns NULL when it is not able to
allocate a huge page on the current node, as specified by its custom
interleave variable. The callers of this function, though, assume that a
failure in alloc_fresh_huge_page() indicates no hugepages can be allocated
on the system period. This might not be the case, for instance, if we have
an uneven NUMA system, and we happen to try to allocate a hugepage on a
node with less memory and fail, while there is still plenty of free memory
on the other nodes.
To correct this, make alloc_fresh_huge_page() search through all online
nodes before deciding no hugepages can be allocated. Add a helper function
for actually allocating the hugepage. Use a new global nid iterator to
control which nid to allocate on.
Note: we expect particular semantics for __GFP_THISNODE, which are now
enforced even for memoryless nodes. That is, there is should be no
fallback to other nodes. Therefore, we rely on the nid passed into
alloc_pages_node() to be the nid the page comes from. If this is
incorrect, accounting will break.
Tested on x86 !NUMA, x86 NUMA, x86_64 NUMA and ppc64 NUMA (with 2
memoryless nodes).
Before on the ppc64 box:
Trying to clear the hugetlb pool
Done. 0 free
Trying to resize the pool to 100
Node 0 HugePages_Free: 25
Node 1 HugePages_Free: 75
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. Initially 100 free
Trying to resize the pool to 200
Node 0 HugePages_Free: 50
Node 1 HugePages_Free: 150
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. 200 free
After:
Trying to clear the hugetlb pool
Done. 0 free
Trying to resize the pool to 100
Node 0 HugePages_Free: 50
Node 1 HugePages_Free: 50
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. Initially 100 free
Trying to resize the pool to 200
Node 0 HugePages_Free: 100
Node 1 HugePages_Free: 100
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. 200 free
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Christoph Lameter <clameter@sgi.com>
Cc: Adam Litke <agl@us.ibm.com>
Cc: David Gibson <hermes@gibson.dropbear.id.au>
Cc: Badari Pulavarty <pbadari@us.ibm.com>
Cc: Ken Chen <kenchen@google.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Lee Schermerhorn <lee.schermerhorn@hp.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-10-16 15:26:24 +07:00
|
|
|
nr_huge_pages_node[nid]++;
|
2005-11-22 12:32:28 +07:00
|
|
|
spin_unlock(&hugetlb_lock);
|
2006-03-22 15:08:08 +07:00
|
|
|
put_page(page); /* free it into the hugepage allocator */
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
hugetlb: fix hugepage allocation with memoryless nodes
Anton found a problem with the hugetlb pool allocation when some nodes have
no memory (http://marc.info/?l=linux-mm&m=118133042025995&w=2). Lee worked
on versions that tried to fix it, but none were accepted. Christoph has
created a set of patches which allow for GFP_THISNODE allocations to fail
if the node has no memory.
Currently, alloc_fresh_huge_page() returns NULL when it is not able to
allocate a huge page on the current node, as specified by its custom
interleave variable. The callers of this function, though, assume that a
failure in alloc_fresh_huge_page() indicates no hugepages can be allocated
on the system period. This might not be the case, for instance, if we have
an uneven NUMA system, and we happen to try to allocate a hugepage on a
node with less memory and fail, while there is still plenty of free memory
on the other nodes.
To correct this, make alloc_fresh_huge_page() search through all online
nodes before deciding no hugepages can be allocated. Add a helper function
for actually allocating the hugepage. Use a new global nid iterator to
control which nid to allocate on.
Note: we expect particular semantics for __GFP_THISNODE, which are now
enforced even for memoryless nodes. That is, there is should be no
fallback to other nodes. Therefore, we rely on the nid passed into
alloc_pages_node() to be the nid the page comes from. If this is
incorrect, accounting will break.
Tested on x86 !NUMA, x86 NUMA, x86_64 NUMA and ppc64 NUMA (with 2
memoryless nodes).
Before on the ppc64 box:
Trying to clear the hugetlb pool
Done. 0 free
Trying to resize the pool to 100
Node 0 HugePages_Free: 25
Node 1 HugePages_Free: 75
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. Initially 100 free
Trying to resize the pool to 200
Node 0 HugePages_Free: 50
Node 1 HugePages_Free: 150
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. 200 free
After:
Trying to clear the hugetlb pool
Done. 0 free
Trying to resize the pool to 100
Node 0 HugePages_Free: 50
Node 1 HugePages_Free: 50
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. Initially 100 free
Trying to resize the pool to 200
Node 0 HugePages_Free: 100
Node 1 HugePages_Free: 100
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. 200 free
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Christoph Lameter <clameter@sgi.com>
Cc: Adam Litke <agl@us.ibm.com>
Cc: David Gibson <hermes@gibson.dropbear.id.au>
Cc: Badari Pulavarty <pbadari@us.ibm.com>
Cc: Ken Chen <kenchen@google.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Lee Schermerhorn <lee.schermerhorn@hp.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-10-16 15:26:24 +07:00
|
|
|
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int alloc_fresh_huge_page(void)
|
|
|
|
{
|
|
|
|
struct page *page;
|
|
|
|
int start_nid;
|
|
|
|
int next_nid;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
start_nid = hugetlb_next_nid;
|
|
|
|
|
|
|
|
do {
|
|
|
|
page = alloc_fresh_huge_page_node(hugetlb_next_nid);
|
|
|
|
if (page)
|
|
|
|
ret = 1;
|
|
|
|
/*
|
|
|
|
* Use a helper variable to find the next node and then
|
|
|
|
* copy it back to hugetlb_next_nid afterwards:
|
|
|
|
* otherwise there's a window in which a racer might
|
|
|
|
* pass invalid nid MAX_NUMNODES to alloc_pages_node.
|
|
|
|
* But we don't need to use a spin_lock here: it really
|
|
|
|
* doesn't matter if occasionally a racer chooses the
|
|
|
|
* same nid as we do. Move nid forward in the mask even
|
|
|
|
* if we just successfully allocated a hugepage so that
|
|
|
|
* the next caller gets hugepages on the next node.
|
|
|
|
*/
|
|
|
|
next_nid = next_node(hugetlb_next_nid, node_online_map);
|
|
|
|
if (next_nid == MAX_NUMNODES)
|
|
|
|
next_nid = first_node(node_online_map);
|
|
|
|
hugetlb_next_nid = next_nid;
|
|
|
|
} while (!page && hugetlb_next_nid != start_nid);
|
|
|
|
|
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2007-10-16 15:26:18 +07:00
|
|
|
static struct page *alloc_buddy_huge_page(struct vm_area_struct *vma,
|
|
|
|
unsigned long address)
|
|
|
|
{
|
|
|
|
struct page *page;
|
hugetlb: introduce nr_overcommit_hugepages sysctl
hugetlb: introduce nr_overcommit_hugepages sysctl
While examining the code to support /proc/sys/vm/hugetlb_dynamic_pool, I
became convinced that having a boolean sysctl was insufficient:
1) To support per-node control of hugepages, I have previously submitted
patches to add a sysfs attribute related to nr_hugepages. However, with
a boolean global value and per-mount quota enforcement constraining the
dynamic pool, adding corresponding control of the dynamic pool on a
per-node basis seems inconsistent to me.
2) Administration of the hugetlb dynamic pool with multiple hugetlbfs
mount points is, arguably, more arduous than it needs to be. Each quota
would need to be set separately, and the sum would need to be monitored.
To ease the administration, and to help make the way for per-node
control of the static & dynamic hugepage pool, I added a separate
sysctl, nr_overcommit_hugepages. This value serves as a high watermark
for the overall hugepage pool, while nr_hugepages serves as a low
watermark. The boolean sysctl can then be removed, as the condition
nr_overcommit_hugepages > 0
indicates the same administrative setting as
hugetlb_dynamic_pool == 1
Quotas still serve as local enforcement of the size of the pool on a
per-mount basis.
A few caveats:
1) There is a race whereby the global surplus huge page counter is
incremented before a hugepage has allocated. Another process could then
try grow the pool, and fail to convert a surplus huge page to a normal
huge page and instead allocate a fresh huge page. I believe this is
benign, as no memory is leaked (the actual pages are still tracked
correctly) and the counters won't go out of sync.
2) Shrinking the static pool while a surplus is in effect will allow the
number of surplus huge pages to exceed the overcommit value. As long as
this condition holds, however, no more surplus huge pages will be
allowed on the system until one of the two sysctls are increased
sufficiently, or the surplus huge pages go out of use and are freed.
Successfully tested on x86_64 with the current libhugetlbfs snapshot,
modified to use the new sysctl.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Adam Litke <agl@us.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Dave Hansen <haveblue@us.ibm.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-12-18 07:20:12 +07:00
|
|
|
unsigned int nid;
|
2007-10-16 15:26:18 +07:00
|
|
|
|
hugetlb: introduce nr_overcommit_hugepages sysctl
hugetlb: introduce nr_overcommit_hugepages sysctl
While examining the code to support /proc/sys/vm/hugetlb_dynamic_pool, I
became convinced that having a boolean sysctl was insufficient:
1) To support per-node control of hugepages, I have previously submitted
patches to add a sysfs attribute related to nr_hugepages. However, with
a boolean global value and per-mount quota enforcement constraining the
dynamic pool, adding corresponding control of the dynamic pool on a
per-node basis seems inconsistent to me.
2) Administration of the hugetlb dynamic pool with multiple hugetlbfs
mount points is, arguably, more arduous than it needs to be. Each quota
would need to be set separately, and the sum would need to be monitored.
To ease the administration, and to help make the way for per-node
control of the static & dynamic hugepage pool, I added a separate
sysctl, nr_overcommit_hugepages. This value serves as a high watermark
for the overall hugepage pool, while nr_hugepages serves as a low
watermark. The boolean sysctl can then be removed, as the condition
nr_overcommit_hugepages > 0
indicates the same administrative setting as
hugetlb_dynamic_pool == 1
Quotas still serve as local enforcement of the size of the pool on a
per-mount basis.
A few caveats:
1) There is a race whereby the global surplus huge page counter is
incremented before a hugepage has allocated. Another process could then
try grow the pool, and fail to convert a surplus huge page to a normal
huge page and instead allocate a fresh huge page. I believe this is
benign, as no memory is leaked (the actual pages are still tracked
correctly) and the counters won't go out of sync.
2) Shrinking the static pool while a surplus is in effect will allow the
number of surplus huge pages to exceed the overcommit value. As long as
this condition holds, however, no more surplus huge pages will be
allowed on the system until one of the two sysctls are increased
sufficiently, or the surplus huge pages go out of use and are freed.
Successfully tested on x86_64 with the current libhugetlbfs snapshot,
modified to use the new sysctl.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Adam Litke <agl@us.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Dave Hansen <haveblue@us.ibm.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-12-18 07:20:12 +07:00
|
|
|
/*
|
|
|
|
* Assume we will successfully allocate the surplus page to
|
|
|
|
* prevent racing processes from causing the surplus to exceed
|
|
|
|
* overcommit
|
|
|
|
*
|
|
|
|
* This however introduces a different race, where a process B
|
|
|
|
* tries to grow the static hugepage pool while alloc_pages() is
|
|
|
|
* called by process A. B will only examine the per-node
|
|
|
|
* counters in determining if surplus huge pages can be
|
|
|
|
* converted to normal huge pages in adjust_pool_surplus(). A
|
|
|
|
* won't be able to increment the per-node counter, until the
|
|
|
|
* lock is dropped by B, but B doesn't drop hugetlb_lock until
|
|
|
|
* no more huge pages can be converted from surplus to normal
|
|
|
|
* state (and doesn't try to convert again). Thus, we have a
|
|
|
|
* case where a surplus huge page exists, the pool is grown, and
|
|
|
|
* the surplus huge page still exists after, even though it
|
|
|
|
* should just have been converted to a normal huge page. This
|
|
|
|
* does not leak memory, though, as the hugepage will be freed
|
|
|
|
* once it is out of use. It also does not allow the counters to
|
|
|
|
* go out of whack in adjust_pool_surplus() as we don't modify
|
|
|
|
* the node values until we've gotten the hugepage and only the
|
|
|
|
* per-node value is checked there.
|
|
|
|
*/
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
|
|
if (surplus_huge_pages >= nr_overcommit_huge_pages) {
|
|
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
nr_huge_pages++;
|
|
|
|
surplus_huge_pages++;
|
|
|
|
}
|
|
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
|
2007-10-16 15:26:18 +07:00
|
|
|
page = alloc_pages(htlb_alloc_mask|__GFP_COMP|__GFP_NOWARN,
|
|
|
|
HUGETLB_PAGE_ORDER);
|
hugetlb: introduce nr_overcommit_hugepages sysctl
hugetlb: introduce nr_overcommit_hugepages sysctl
While examining the code to support /proc/sys/vm/hugetlb_dynamic_pool, I
became convinced that having a boolean sysctl was insufficient:
1) To support per-node control of hugepages, I have previously submitted
patches to add a sysfs attribute related to nr_hugepages. However, with
a boolean global value and per-mount quota enforcement constraining the
dynamic pool, adding corresponding control of the dynamic pool on a
per-node basis seems inconsistent to me.
2) Administration of the hugetlb dynamic pool with multiple hugetlbfs
mount points is, arguably, more arduous than it needs to be. Each quota
would need to be set separately, and the sum would need to be monitored.
To ease the administration, and to help make the way for per-node
control of the static & dynamic hugepage pool, I added a separate
sysctl, nr_overcommit_hugepages. This value serves as a high watermark
for the overall hugepage pool, while nr_hugepages serves as a low
watermark. The boolean sysctl can then be removed, as the condition
nr_overcommit_hugepages > 0
indicates the same administrative setting as
hugetlb_dynamic_pool == 1
Quotas still serve as local enforcement of the size of the pool on a
per-mount basis.
A few caveats:
1) There is a race whereby the global surplus huge page counter is
incremented before a hugepage has allocated. Another process could then
try grow the pool, and fail to convert a surplus huge page to a normal
huge page and instead allocate a fresh huge page. I believe this is
benign, as no memory is leaked (the actual pages are still tracked
correctly) and the counters won't go out of sync.
2) Shrinking the static pool while a surplus is in effect will allow the
number of surplus huge pages to exceed the overcommit value. As long as
this condition holds, however, no more surplus huge pages will be
allowed on the system until one of the two sysctls are increased
sufficiently, or the surplus huge pages go out of use and are freed.
Successfully tested on x86_64 with the current libhugetlbfs snapshot,
modified to use the new sysctl.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Adam Litke <agl@us.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Dave Hansen <haveblue@us.ibm.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-12-18 07:20:12 +07:00
|
|
|
|
|
|
|
spin_lock(&hugetlb_lock);
|
2007-10-16 15:26:18 +07:00
|
|
|
if (page) {
|
hugetlb: introduce nr_overcommit_hugepages sysctl
hugetlb: introduce nr_overcommit_hugepages sysctl
While examining the code to support /proc/sys/vm/hugetlb_dynamic_pool, I
became convinced that having a boolean sysctl was insufficient:
1) To support per-node control of hugepages, I have previously submitted
patches to add a sysfs attribute related to nr_hugepages. However, with
a boolean global value and per-mount quota enforcement constraining the
dynamic pool, adding corresponding control of the dynamic pool on a
per-node basis seems inconsistent to me.
2) Administration of the hugetlb dynamic pool with multiple hugetlbfs
mount points is, arguably, more arduous than it needs to be. Each quota
would need to be set separately, and the sum would need to be monitored.
To ease the administration, and to help make the way for per-node
control of the static & dynamic hugepage pool, I added a separate
sysctl, nr_overcommit_hugepages. This value serves as a high watermark
for the overall hugepage pool, while nr_hugepages serves as a low
watermark. The boolean sysctl can then be removed, as the condition
nr_overcommit_hugepages > 0
indicates the same administrative setting as
hugetlb_dynamic_pool == 1
Quotas still serve as local enforcement of the size of the pool on a
per-mount basis.
A few caveats:
1) There is a race whereby the global surplus huge page counter is
incremented before a hugepage has allocated. Another process could then
try grow the pool, and fail to convert a surplus huge page to a normal
huge page and instead allocate a fresh huge page. I believe this is
benign, as no memory is leaked (the actual pages are still tracked
correctly) and the counters won't go out of sync.
2) Shrinking the static pool while a surplus is in effect will allow the
number of surplus huge pages to exceed the overcommit value. As long as
this condition holds, however, no more surplus huge pages will be
allowed on the system until one of the two sysctls are increased
sufficiently, or the surplus huge pages go out of use and are freed.
Successfully tested on x86_64 with the current libhugetlbfs snapshot,
modified to use the new sysctl.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Adam Litke <agl@us.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Dave Hansen <haveblue@us.ibm.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-12-18 07:20:12 +07:00
|
|
|
nid = page_to_nid(page);
|
2007-10-16 15:26:18 +07:00
|
|
|
set_compound_page_dtor(page, free_huge_page);
|
hugetlb: introduce nr_overcommit_hugepages sysctl
hugetlb: introduce nr_overcommit_hugepages sysctl
While examining the code to support /proc/sys/vm/hugetlb_dynamic_pool, I
became convinced that having a boolean sysctl was insufficient:
1) To support per-node control of hugepages, I have previously submitted
patches to add a sysfs attribute related to nr_hugepages. However, with
a boolean global value and per-mount quota enforcement constraining the
dynamic pool, adding corresponding control of the dynamic pool on a
per-node basis seems inconsistent to me.
2) Administration of the hugetlb dynamic pool with multiple hugetlbfs
mount points is, arguably, more arduous than it needs to be. Each quota
would need to be set separately, and the sum would need to be monitored.
To ease the administration, and to help make the way for per-node
control of the static & dynamic hugepage pool, I added a separate
sysctl, nr_overcommit_hugepages. This value serves as a high watermark
for the overall hugepage pool, while nr_hugepages serves as a low
watermark. The boolean sysctl can then be removed, as the condition
nr_overcommit_hugepages > 0
indicates the same administrative setting as
hugetlb_dynamic_pool == 1
Quotas still serve as local enforcement of the size of the pool on a
per-mount basis.
A few caveats:
1) There is a race whereby the global surplus huge page counter is
incremented before a hugepage has allocated. Another process could then
try grow the pool, and fail to convert a surplus huge page to a normal
huge page and instead allocate a fresh huge page. I believe this is
benign, as no memory is leaked (the actual pages are still tracked
correctly) and the counters won't go out of sync.
2) Shrinking the static pool while a surplus is in effect will allow the
number of surplus huge pages to exceed the overcommit value. As long as
this condition holds, however, no more surplus huge pages will be
allowed on the system until one of the two sysctls are increased
sufficiently, or the surplus huge pages go out of use and are freed.
Successfully tested on x86_64 with the current libhugetlbfs snapshot,
modified to use the new sysctl.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Adam Litke <agl@us.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Dave Hansen <haveblue@us.ibm.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-12-18 07:20:12 +07:00
|
|
|
/*
|
|
|
|
* We incremented the global counters already
|
|
|
|
*/
|
|
|
|
nr_huge_pages_node[nid]++;
|
|
|
|
surplus_huge_pages_node[nid]++;
|
|
|
|
} else {
|
|
|
|
nr_huge_pages--;
|
|
|
|
surplus_huge_pages--;
|
2007-10-16 15:26:18 +07:00
|
|
|
}
|
hugetlb: introduce nr_overcommit_hugepages sysctl
hugetlb: introduce nr_overcommit_hugepages sysctl
While examining the code to support /proc/sys/vm/hugetlb_dynamic_pool, I
became convinced that having a boolean sysctl was insufficient:
1) To support per-node control of hugepages, I have previously submitted
patches to add a sysfs attribute related to nr_hugepages. However, with
a boolean global value and per-mount quota enforcement constraining the
dynamic pool, adding corresponding control of the dynamic pool on a
per-node basis seems inconsistent to me.
2) Administration of the hugetlb dynamic pool with multiple hugetlbfs
mount points is, arguably, more arduous than it needs to be. Each quota
would need to be set separately, and the sum would need to be monitored.
To ease the administration, and to help make the way for per-node
control of the static & dynamic hugepage pool, I added a separate
sysctl, nr_overcommit_hugepages. This value serves as a high watermark
for the overall hugepage pool, while nr_hugepages serves as a low
watermark. The boolean sysctl can then be removed, as the condition
nr_overcommit_hugepages > 0
indicates the same administrative setting as
hugetlb_dynamic_pool == 1
Quotas still serve as local enforcement of the size of the pool on a
per-mount basis.
A few caveats:
1) There is a race whereby the global surplus huge page counter is
incremented before a hugepage has allocated. Another process could then
try grow the pool, and fail to convert a surplus huge page to a normal
huge page and instead allocate a fresh huge page. I believe this is
benign, as no memory is leaked (the actual pages are still tracked
correctly) and the counters won't go out of sync.
2) Shrinking the static pool while a surplus is in effect will allow the
number of surplus huge pages to exceed the overcommit value. As long as
this condition holds, however, no more surplus huge pages will be
allowed on the system until one of the two sysctls are increased
sufficiently, or the surplus huge pages go out of use and are freed.
Successfully tested on x86_64 with the current libhugetlbfs snapshot,
modified to use the new sysctl.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Adam Litke <agl@us.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Dave Hansen <haveblue@us.ibm.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-12-18 07:20:12 +07:00
|
|
|
spin_unlock(&hugetlb_lock);
|
2007-10-16 15:26:18 +07:00
|
|
|
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
|
2007-10-16 15:26:19 +07:00
|
|
|
/*
|
|
|
|
* Increase the hugetlb pool such that it can accomodate a reservation
|
|
|
|
* of size 'delta'.
|
|
|
|
*/
|
|
|
|
static int gather_surplus_pages(int delta)
|
|
|
|
{
|
|
|
|
struct list_head surplus_list;
|
|
|
|
struct page *page, *tmp;
|
|
|
|
int ret, i;
|
|
|
|
int needed, allocated;
|
|
|
|
|
|
|
|
needed = (resv_huge_pages + delta) - free_huge_pages;
|
|
|
|
if (needed <= 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
allocated = 0;
|
|
|
|
INIT_LIST_HEAD(&surplus_list);
|
|
|
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
retry:
|
|
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
for (i = 0; i < needed; i++) {
|
|
|
|
page = alloc_buddy_huge_page(NULL, 0);
|
|
|
|
if (!page) {
|
|
|
|
/*
|
|
|
|
* We were not able to allocate enough pages to
|
|
|
|
* satisfy the entire reservation so we free what
|
|
|
|
* we've allocated so far.
|
|
|
|
*/
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
|
|
needed = 0;
|
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_add(&page->lru, &surplus_list);
|
|
|
|
}
|
|
|
|
allocated += needed;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* After retaking hugetlb_lock, we need to recalculate 'needed'
|
|
|
|
* because either resv_huge_pages or free_huge_pages may have changed.
|
|
|
|
*/
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
|
|
needed = (resv_huge_pages + delta) - (free_huge_pages + allocated);
|
|
|
|
if (needed > 0)
|
|
|
|
goto retry;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The surplus_list now contains _at_least_ the number of extra pages
|
|
|
|
* needed to accomodate the reservation. Add the appropriate number
|
|
|
|
* of pages to the hugetlb pool and free the extras back to the buddy
|
|
|
|
* allocator.
|
|
|
|
*/
|
|
|
|
needed += allocated;
|
|
|
|
ret = 0;
|
|
|
|
free:
|
|
|
|
list_for_each_entry_safe(page, tmp, &surplus_list, lru) {
|
|
|
|
list_del(&page->lru);
|
|
|
|
if ((--needed) >= 0)
|
|
|
|
enqueue_huge_page(page);
|
2007-10-16 15:26:25 +07:00
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* Decrement the refcount and free the page using its
|
|
|
|
* destructor. This must be done with hugetlb_lock
|
|
|
|
* unlocked which is safe because free_huge_page takes
|
|
|
|
* hugetlb_lock before deciding how to free the page.
|
|
|
|
*/
|
|
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
put_page(page);
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
|
|
}
|
2007-10-16 15:26:19 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When releasing a hugetlb pool reservation, any surplus pages that were
|
|
|
|
* allocated to satisfy the reservation must be explicitly freed if they were
|
|
|
|
* never used.
|
|
|
|
*/
|
2007-11-15 07:59:43 +07:00
|
|
|
static void return_unused_surplus_pages(unsigned long unused_resv_pages)
|
2007-10-16 15:26:19 +07:00
|
|
|
{
|
|
|
|
static int nid = -1;
|
|
|
|
struct page *page;
|
|
|
|
unsigned long nr_pages;
|
|
|
|
|
|
|
|
nr_pages = min(unused_resv_pages, surplus_huge_pages);
|
|
|
|
|
|
|
|
while (nr_pages) {
|
|
|
|
nid = next_node(nid, node_online_map);
|
|
|
|
if (nid == MAX_NUMNODES)
|
|
|
|
nid = first_node(node_online_map);
|
|
|
|
|
|
|
|
if (!surplus_huge_pages_node[nid])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!list_empty(&hugepage_freelists[nid])) {
|
|
|
|
page = list_entry(hugepage_freelists[nid].next,
|
|
|
|
struct page, lru);
|
|
|
|
list_del(&page->lru);
|
|
|
|
update_and_free_page(page);
|
|
|
|
free_huge_pages--;
|
|
|
|
free_huge_pages_node[nid]--;
|
|
|
|
surplus_huge_pages--;
|
|
|
|
surplus_huge_pages_node[nid]--;
|
|
|
|
nr_pages--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-15 07:59:37 +07:00
|
|
|
|
|
|
|
static struct page *alloc_huge_page_shared(struct vm_area_struct *vma,
|
|
|
|
unsigned long addr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2007-11-15 07:59:37 +07:00
|
|
|
struct page *page;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
spin_lock(&hugetlb_lock);
|
[PATCH] hugepage: Strict page reservation for hugepage inodes
These days, hugepages are demand-allocated at first fault time. There's a
somewhat dubious (and racy) heuristic when making a new mmap() to check if
there are enough available hugepages to fully satisfy that mapping.
A particularly obvious case where the heuristic breaks down is where a
process maps its hugepages not as a single chunk, but as a bunch of
individually mmap()ed (or shmat()ed) blocks without touching and
instantiating the pages in between allocations. In this case the size of
each block is compared against the total number of available hugepages.
It's thus easy for the process to become overcommitted, because each block
mapping will succeed, although the total number of hugepages required by
all blocks exceeds the number available. In particular, this defeats such
a program which will detect a mapping failure and adjust its hugepage usage
downward accordingly.
The patch below addresses this problem, by strictly reserving a number of
physical hugepages for hugepage inodes which have been mapped, but not
instatiated. MAP_SHARED mappings are thus "safe" - they will fail on
mmap(), not later with an OOM SIGKILL. MAP_PRIVATE mappings can still
trigger an OOM. (Actually SHARED mappings can technically still OOM, but
only if the sysadmin explicitly reduces the hugepage pool between mapping
and instantiation)
This patch appears to address the problem at hand - it allows DB2 to start
correctly, for instance, which previously suffered the failure described
above.
This patch causes no regressions on the libhugetblfs testsuite, and makes a
test (designed to catch this problem) pass which previously failed (ppc64,
POWER5).
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 15:08:55 +07:00
|
|
|
page = dequeue_huge_page(vma, addr);
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock(&hugetlb_lock);
|
2007-11-15 07:59:42 +07:00
|
|
|
return page ? page : ERR_PTR(-VM_FAULT_OOM);
|
2007-11-15 07:59:37 +07:00
|
|
|
}
|
[PATCH] hugepage: Strict page reservation for hugepage inodes
These days, hugepages are demand-allocated at first fault time. There's a
somewhat dubious (and racy) heuristic when making a new mmap() to check if
there are enough available hugepages to fully satisfy that mapping.
A particularly obvious case where the heuristic breaks down is where a
process maps its hugepages not as a single chunk, but as a bunch of
individually mmap()ed (or shmat()ed) blocks without touching and
instantiating the pages in between allocations. In this case the size of
each block is compared against the total number of available hugepages.
It's thus easy for the process to become overcommitted, because each block
mapping will succeed, although the total number of hugepages required by
all blocks exceeds the number available. In particular, this defeats such
a program which will detect a mapping failure and adjust its hugepage usage
downward accordingly.
The patch below addresses this problem, by strictly reserving a number of
physical hugepages for hugepage inodes which have been mapped, but not
instatiated. MAP_SHARED mappings are thus "safe" - they will fail on
mmap(), not later with an OOM SIGKILL. MAP_PRIVATE mappings can still
trigger an OOM. (Actually SHARED mappings can technically still OOM, but
only if the sysadmin explicitly reduces the hugepage pool between mapping
and instantiation)
This patch appears to address the problem at hand - it allows DB2 to start
correctly, for instance, which previously suffered the failure described
above.
This patch causes no regressions on the libhugetblfs testsuite, and makes a
test (designed to catch this problem) pass which previously failed (ppc64,
POWER5).
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 15:08:55 +07:00
|
|
|
|
2007-11-15 07:59:37 +07:00
|
|
|
static struct page *alloc_huge_page_private(struct vm_area_struct *vma,
|
|
|
|
unsigned long addr)
|
|
|
|
{
|
|
|
|
struct page *page = NULL;
|
2007-10-16 15:26:18 +07:00
|
|
|
|
2007-11-15 07:59:42 +07:00
|
|
|
if (hugetlb_get_quota(vma->vm_file->f_mapping, 1))
|
|
|
|
return ERR_PTR(-VM_FAULT_SIGBUS);
|
|
|
|
|
2007-11-15 07:59:37 +07:00
|
|
|
spin_lock(&hugetlb_lock);
|
|
|
|
if (free_huge_pages > resv_huge_pages)
|
|
|
|
page = dequeue_huge_page(vma, addr);
|
|
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
if (!page)
|
2007-10-16 15:26:18 +07:00
|
|
|
page = alloc_buddy_huge_page(vma, addr);
|
2007-11-15 07:59:42 +07:00
|
|
|
return page ? page : ERR_PTR(-VM_FAULT_OOM);
|
2007-11-15 07:59:37 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct page *alloc_huge_page(struct vm_area_struct *vma,
|
|
|
|
unsigned long addr)
|
|
|
|
{
|
|
|
|
struct page *page;
|
2007-11-15 07:59:39 +07:00
|
|
|
struct address_space *mapping = vma->vm_file->f_mapping;
|
|
|
|
|
2007-11-15 07:59:37 +07:00
|
|
|
if (vma->vm_flags & VM_MAYSHARE)
|
|
|
|
page = alloc_huge_page_shared(vma, addr);
|
|
|
|
else
|
|
|
|
page = alloc_huge_page_private(vma, addr);
|
2007-11-15 07:59:42 +07:00
|
|
|
|
|
|
|
if (!IS_ERR(page)) {
|
2007-11-15 07:59:37 +07:00
|
|
|
set_page_refcounted(page);
|
2007-11-15 07:59:39 +07:00
|
|
|
set_page_private(page, (unsigned long) mapping);
|
2007-11-15 07:59:42 +07:00
|
|
|
}
|
|
|
|
return page;
|
[PATCH] hugepage: Strict page reservation for hugepage inodes
These days, hugepages are demand-allocated at first fault time. There's a
somewhat dubious (and racy) heuristic when making a new mmap() to check if
there are enough available hugepages to fully satisfy that mapping.
A particularly obvious case where the heuristic breaks down is where a
process maps its hugepages not as a single chunk, but as a bunch of
individually mmap()ed (or shmat()ed) blocks without touching and
instantiating the pages in between allocations. In this case the size of
each block is compared against the total number of available hugepages.
It's thus easy for the process to become overcommitted, because each block
mapping will succeed, although the total number of hugepages required by
all blocks exceeds the number available. In particular, this defeats such
a program which will detect a mapping failure and adjust its hugepage usage
downward accordingly.
The patch below addresses this problem, by strictly reserving a number of
physical hugepages for hugepage inodes which have been mapped, but not
instatiated. MAP_SHARED mappings are thus "safe" - they will fail on
mmap(), not later with an OOM SIGKILL. MAP_PRIVATE mappings can still
trigger an OOM. (Actually SHARED mappings can technically still OOM, but
only if the sysadmin explicitly reduces the hugepage pool between mapping
and instantiation)
This patch appears to address the problem at hand - it allows DB2 to start
correctly, for instance, which previously suffered the failure described
above.
This patch causes no regressions on the libhugetblfs testsuite, and makes a
test (designed to catch this problem) pass which previously failed (ppc64,
POWER5).
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 15:08:55 +07:00
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int __init hugetlb_init(void)
|
|
|
|
{
|
|
|
|
unsigned long i;
|
|
|
|
|
2005-11-07 07:06:55 +07:00
|
|
|
if (HPAGE_SHIFT == 0)
|
|
|
|
return 0;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
for (i = 0; i < MAX_NUMNODES; ++i)
|
|
|
|
INIT_LIST_HEAD(&hugepage_freelists[i]);
|
|
|
|
|
hugetlb: fix hugepage allocation with memoryless nodes
Anton found a problem with the hugetlb pool allocation when some nodes have
no memory (http://marc.info/?l=linux-mm&m=118133042025995&w=2). Lee worked
on versions that tried to fix it, but none were accepted. Christoph has
created a set of patches which allow for GFP_THISNODE allocations to fail
if the node has no memory.
Currently, alloc_fresh_huge_page() returns NULL when it is not able to
allocate a huge page on the current node, as specified by its custom
interleave variable. The callers of this function, though, assume that a
failure in alloc_fresh_huge_page() indicates no hugepages can be allocated
on the system period. This might not be the case, for instance, if we have
an uneven NUMA system, and we happen to try to allocate a hugepage on a
node with less memory and fail, while there is still plenty of free memory
on the other nodes.
To correct this, make alloc_fresh_huge_page() search through all online
nodes before deciding no hugepages can be allocated. Add a helper function
for actually allocating the hugepage. Use a new global nid iterator to
control which nid to allocate on.
Note: we expect particular semantics for __GFP_THISNODE, which are now
enforced even for memoryless nodes. That is, there is should be no
fallback to other nodes. Therefore, we rely on the nid passed into
alloc_pages_node() to be the nid the page comes from. If this is
incorrect, accounting will break.
Tested on x86 !NUMA, x86 NUMA, x86_64 NUMA and ppc64 NUMA (with 2
memoryless nodes).
Before on the ppc64 box:
Trying to clear the hugetlb pool
Done. 0 free
Trying to resize the pool to 100
Node 0 HugePages_Free: 25
Node 1 HugePages_Free: 75
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. Initially 100 free
Trying to resize the pool to 200
Node 0 HugePages_Free: 50
Node 1 HugePages_Free: 150
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. 200 free
After:
Trying to clear the hugetlb pool
Done. 0 free
Trying to resize the pool to 100
Node 0 HugePages_Free: 50
Node 1 HugePages_Free: 50
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. Initially 100 free
Trying to resize the pool to 200
Node 0 HugePages_Free: 100
Node 1 HugePages_Free: 100
Node 2 HugePages_Free: 0
Node 3 HugePages_Free: 0
Done. 200 free
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Christoph Lameter <clameter@sgi.com>
Cc: Adam Litke <agl@us.ibm.com>
Cc: David Gibson <hermes@gibson.dropbear.id.au>
Cc: Badari Pulavarty <pbadari@us.ibm.com>
Cc: Ken Chen <kenchen@google.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Lee Schermerhorn <lee.schermerhorn@hp.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-10-16 15:26:24 +07:00
|
|
|
hugetlb_next_nid = first_node(node_online_map);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
for (i = 0; i < max_huge_pages; ++i) {
|
2006-03-22 15:08:08 +07:00
|
|
|
if (!alloc_fresh_huge_page())
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
max_huge_pages = free_huge_pages = nr_huge_pages = i;
|
|
|
|
printk("Total HugeTLB memory allocated, %ld\n", free_huge_pages);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
module_init(hugetlb_init);
|
|
|
|
|
|
|
|
static int __init hugetlb_setup(char *s)
|
|
|
|
{
|
|
|
|
if (sscanf(s, "%lu", &max_huge_pages) <= 0)
|
|
|
|
max_huge_pages = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
__setup("hugepages=", hugetlb_setup);
|
|
|
|
|
2007-05-09 16:33:34 +07:00
|
|
|
static unsigned int cpuset_mems_nr(unsigned int *array)
|
|
|
|
{
|
|
|
|
int node;
|
|
|
|
unsigned int nr = 0;
|
|
|
|
|
|
|
|
for_each_node_mask(node, cpuset_current_mems_allowed)
|
|
|
|
nr += array[node];
|
|
|
|
|
|
|
|
return nr;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
#ifdef CONFIG_SYSCTL
|
|
|
|
#ifdef CONFIG_HIGHMEM
|
|
|
|
static void try_to_free_low(unsigned long count)
|
|
|
|
{
|
2006-09-26 13:31:55 +07:00
|
|
|
int i;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
for (i = 0; i < MAX_NUMNODES; ++i) {
|
|
|
|
struct page *page, *next;
|
|
|
|
list_for_each_entry_safe(page, next, &hugepage_freelists[i], lru) {
|
2007-10-16 15:26:23 +07:00
|
|
|
if (count >= nr_huge_pages)
|
|
|
|
return;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (PageHighMem(page))
|
|
|
|
continue;
|
|
|
|
list_del(&page->lru);
|
|
|
|
update_and_free_page(page);
|
|
|
|
free_huge_pages--;
|
2006-09-26 13:31:55 +07:00
|
|
|
free_huge_pages_node[page_to_nid(page)]--;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static inline void try_to_free_low(unsigned long count)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-10-16 15:26:18 +07:00
|
|
|
#define persistent_huge_pages (nr_huge_pages - surplus_huge_pages)
|
2005-04-17 05:20:36 +07:00
|
|
|
static unsigned long set_max_huge_pages(unsigned long count)
|
|
|
|
{
|
2007-10-16 15:26:18 +07:00
|
|
|
unsigned long min_count, ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-10-16 15:26:18 +07:00
|
|
|
/*
|
|
|
|
* Increase the pool size
|
|
|
|
* First take pages out of surplus state. Then make up the
|
|
|
|
* remaining difference by allocating fresh huge pages.
|
hugetlb: introduce nr_overcommit_hugepages sysctl
hugetlb: introduce nr_overcommit_hugepages sysctl
While examining the code to support /proc/sys/vm/hugetlb_dynamic_pool, I
became convinced that having a boolean sysctl was insufficient:
1) To support per-node control of hugepages, I have previously submitted
patches to add a sysfs attribute related to nr_hugepages. However, with
a boolean global value and per-mount quota enforcement constraining the
dynamic pool, adding corresponding control of the dynamic pool on a
per-node basis seems inconsistent to me.
2) Administration of the hugetlb dynamic pool with multiple hugetlbfs
mount points is, arguably, more arduous than it needs to be. Each quota
would need to be set separately, and the sum would need to be monitored.
To ease the administration, and to help make the way for per-node
control of the static & dynamic hugepage pool, I added a separate
sysctl, nr_overcommit_hugepages. This value serves as a high watermark
for the overall hugepage pool, while nr_hugepages serves as a low
watermark. The boolean sysctl can then be removed, as the condition
nr_overcommit_hugepages > 0
indicates the same administrative setting as
hugetlb_dynamic_pool == 1
Quotas still serve as local enforcement of the size of the pool on a
per-mount basis.
A few caveats:
1) There is a race whereby the global surplus huge page counter is
incremented before a hugepage has allocated. Another process could then
try grow the pool, and fail to convert a surplus huge page to a normal
huge page and instead allocate a fresh huge page. I believe this is
benign, as no memory is leaked (the actual pages are still tracked
correctly) and the counters won't go out of sync.
2) Shrinking the static pool while a surplus is in effect will allow the
number of surplus huge pages to exceed the overcommit value. As long as
this condition holds, however, no more surplus huge pages will be
allowed on the system until one of the two sysctls are increased
sufficiently, or the surplus huge pages go out of use and are freed.
Successfully tested on x86_64 with the current libhugetlbfs snapshot,
modified to use the new sysctl.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Adam Litke <agl@us.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Dave Hansen <haveblue@us.ibm.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-12-18 07:20:12 +07:00
|
|
|
*
|
|
|
|
* We might race with alloc_buddy_huge_page() here and be unable
|
|
|
|
* to convert a surplus huge page to a normal huge page. That is
|
|
|
|
* not critical, though, it just means the overall size of the
|
|
|
|
* pool might be one hugepage larger than it needs to be, but
|
|
|
|
* within all the constraints specified by the sysctls.
|
2007-10-16 15:26:18 +07:00
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_lock(&hugetlb_lock);
|
2007-10-16 15:26:18 +07:00
|
|
|
while (surplus_huge_pages && count > persistent_huge_pages) {
|
|
|
|
if (!adjust_pool_surplus(-1))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (count > persistent_huge_pages) {
|
|
|
|
int ret;
|
|
|
|
/*
|
|
|
|
* If this allocation races such that we no longer need the
|
|
|
|
* page, free_huge_page will handle it by freeing the page
|
|
|
|
* and reducing the surplus.
|
|
|
|
*/
|
|
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
ret = alloc_fresh_huge_page();
|
|
|
|
spin_lock(&hugetlb_lock);
|
|
|
|
if (!ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decrease the pool size
|
|
|
|
* First return free pages to the buddy allocator (being careful
|
|
|
|
* to keep enough around to satisfy reservations). Then place
|
|
|
|
* pages into surplus state as needed so the pool will shrink
|
|
|
|
* to the desired size as pages become free.
|
hugetlb: introduce nr_overcommit_hugepages sysctl
hugetlb: introduce nr_overcommit_hugepages sysctl
While examining the code to support /proc/sys/vm/hugetlb_dynamic_pool, I
became convinced that having a boolean sysctl was insufficient:
1) To support per-node control of hugepages, I have previously submitted
patches to add a sysfs attribute related to nr_hugepages. However, with
a boolean global value and per-mount quota enforcement constraining the
dynamic pool, adding corresponding control of the dynamic pool on a
per-node basis seems inconsistent to me.
2) Administration of the hugetlb dynamic pool with multiple hugetlbfs
mount points is, arguably, more arduous than it needs to be. Each quota
would need to be set separately, and the sum would need to be monitored.
To ease the administration, and to help make the way for per-node
control of the static & dynamic hugepage pool, I added a separate
sysctl, nr_overcommit_hugepages. This value serves as a high watermark
for the overall hugepage pool, while nr_hugepages serves as a low
watermark. The boolean sysctl can then be removed, as the condition
nr_overcommit_hugepages > 0
indicates the same administrative setting as
hugetlb_dynamic_pool == 1
Quotas still serve as local enforcement of the size of the pool on a
per-mount basis.
A few caveats:
1) There is a race whereby the global surplus huge page counter is
incremented before a hugepage has allocated. Another process could then
try grow the pool, and fail to convert a surplus huge page to a normal
huge page and instead allocate a fresh huge page. I believe this is
benign, as no memory is leaked (the actual pages are still tracked
correctly) and the counters won't go out of sync.
2) Shrinking the static pool while a surplus is in effect will allow the
number of surplus huge pages to exceed the overcommit value. As long as
this condition holds, however, no more surplus huge pages will be
allowed on the system until one of the two sysctls are increased
sufficiently, or the surplus huge pages go out of use and are freed.
Successfully tested on x86_64 with the current libhugetlbfs snapshot,
modified to use the new sysctl.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Adam Litke <agl@us.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Cc: Dave Hansen <haveblue@us.ibm.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-12-18 07:20:12 +07:00
|
|
|
*
|
|
|
|
* By placing pages into the surplus state independent of the
|
|
|
|
* overcommit value, we are allowing the surplus pool size to
|
|
|
|
* exceed overcommit. There are few sane options here. Since
|
|
|
|
* alloc_buddy_huge_page() is checking the global counter,
|
|
|
|
* though, we'll note that we're not allowed to exceed surplus
|
|
|
|
* and won't grow the pool anywhere else. Not until one of the
|
|
|
|
* sysctls are changed, or the surplus pages go out of use.
|
2007-10-16 15:26:18 +07:00
|
|
|
*/
|
2007-10-16 15:26:23 +07:00
|
|
|
min_count = resv_huge_pages + nr_huge_pages - free_huge_pages;
|
|
|
|
min_count = max(count, min_count);
|
2007-10-16 15:26:18 +07:00
|
|
|
try_to_free_low(min_count);
|
|
|
|
while (min_count < persistent_huge_pages) {
|
2006-01-06 15:10:46 +07:00
|
|
|
struct page *page = dequeue_huge_page(NULL, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!page)
|
|
|
|
break;
|
|
|
|
update_and_free_page(page);
|
|
|
|
}
|
2007-10-16 15:26:18 +07:00
|
|
|
while (count < persistent_huge_pages) {
|
|
|
|
if (!adjust_pool_surplus(1))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
ret = persistent_huge_pages;
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock(&hugetlb_lock);
|
2007-10-16 15:26:18 +07:00
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
int hugetlb_sysctl_handler(struct ctl_table *table, int write,
|
|
|
|
struct file *file, void __user *buffer,
|
|
|
|
size_t *length, loff_t *ppos)
|
|
|
|
{
|
|
|
|
proc_doulongvec_minmax(table, write, file, buffer, length, ppos);
|
|
|
|
max_huge_pages = set_max_huge_pages(max_huge_pages);
|
|
|
|
return 0;
|
|
|
|
}
|
2007-07-17 18:03:13 +07:00
|
|
|
|
|
|
|
int hugetlb_treat_movable_handler(struct ctl_table *table, int write,
|
|
|
|
struct file *file, void __user *buffer,
|
|
|
|
size_t *length, loff_t *ppos)
|
|
|
|
{
|
|
|
|
proc_dointvec(table, write, file, buffer, length, ppos);
|
|
|
|
if (hugepages_treat_as_movable)
|
|
|
|
htlb_alloc_mask = GFP_HIGHUSER_MOVABLE;
|
|
|
|
else
|
|
|
|
htlb_alloc_mask = GFP_HIGHUSER;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif /* CONFIG_SYSCTL */
|
|
|
|
|
|
|
|
int hugetlb_report_meminfo(char *buf)
|
|
|
|
{
|
|
|
|
return sprintf(buf,
|
|
|
|
"HugePages_Total: %5lu\n"
|
|
|
|
"HugePages_Free: %5lu\n"
|
2006-06-23 16:03:15 +07:00
|
|
|
"HugePages_Rsvd: %5lu\n"
|
2007-10-16 15:26:18 +07:00
|
|
|
"HugePages_Surp: %5lu\n"
|
2005-04-17 05:20:36 +07:00
|
|
|
"Hugepagesize: %5lu kB\n",
|
|
|
|
nr_huge_pages,
|
|
|
|
free_huge_pages,
|
2006-06-23 16:03:15 +07:00
|
|
|
resv_huge_pages,
|
2007-10-16 15:26:18 +07:00
|
|
|
surplus_huge_pages,
|
2005-04-17 05:20:36 +07:00
|
|
|
HPAGE_SIZE/1024);
|
|
|
|
}
|
|
|
|
|
|
|
|
int hugetlb_report_node_meminfo(int nid, char *buf)
|
|
|
|
{
|
|
|
|
return sprintf(buf,
|
|
|
|
"Node %d HugePages_Total: %5u\n"
|
|
|
|
"Node %d HugePages_Free: %5u\n",
|
|
|
|
nid, nr_huge_pages_node[nid],
|
|
|
|
nid, free_huge_pages_node[nid]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the number pages of memory we physically have, in PAGE_SIZE units. */
|
|
|
|
unsigned long hugetlb_total_pages(void)
|
|
|
|
{
|
|
|
|
return nr_huge_pages * (HPAGE_SIZE / PAGE_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We cannot handle pagefaults against hugetlb pages at all. They cause
|
|
|
|
* handle_mm_fault() to try to instantiate regular-sized pages in the
|
|
|
|
* hugegpage VMA. do_page_fault() is supposed to trap this, so BUG is we get
|
|
|
|
* this far.
|
|
|
|
*/
|
2007-07-19 15:47:03 +07:00
|
|
|
static int hugetlb_vm_op_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
BUG();
|
2007-07-19 15:47:03 +07:00
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
struct vm_operations_struct hugetlb_vm_ops = {
|
2007-07-19 15:47:03 +07:00
|
|
|
.fault = hugetlb_vm_op_fault,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2006-01-06 15:10:44 +07:00
|
|
|
static pte_t make_huge_pte(struct vm_area_struct *vma, struct page *page,
|
|
|
|
int writable)
|
2005-06-22 07:14:44 +07:00
|
|
|
{
|
|
|
|
pte_t entry;
|
|
|
|
|
2006-01-06 15:10:44 +07:00
|
|
|
if (writable) {
|
2005-06-22 07:14:44 +07:00
|
|
|
entry =
|
|
|
|
pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
|
|
|
|
} else {
|
|
|
|
entry = pte_wrprotect(mk_pte(page, vma->vm_page_prot));
|
|
|
|
}
|
|
|
|
entry = pte_mkyoung(entry);
|
|
|
|
entry = pte_mkhuge(entry);
|
|
|
|
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
2006-01-06 15:10:44 +07:00
|
|
|
static void set_huge_ptep_writable(struct vm_area_struct *vma,
|
|
|
|
unsigned long address, pte_t *ptep)
|
|
|
|
{
|
|
|
|
pte_t entry;
|
|
|
|
|
|
|
|
entry = pte_mkwrite(pte_mkdirty(*ptep));
|
2007-06-17 00:16:12 +07:00
|
|
|
if (ptep_set_access_flags(vma, address, ptep, entry, 1)) {
|
|
|
|
update_mmu_cache(vma, address, entry);
|
|
|
|
}
|
2006-01-06 15:10:44 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-06-22 07:14:44 +07:00
|
|
|
int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
|
|
|
|
struct vm_area_struct *vma)
|
|
|
|
{
|
|
|
|
pte_t *src_pte, *dst_pte, entry;
|
|
|
|
struct page *ptepage;
|
2005-10-20 11:23:43 +07:00
|
|
|
unsigned long addr;
|
2006-01-06 15:10:44 +07:00
|
|
|
int cow;
|
|
|
|
|
|
|
|
cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
|
2005-06-22 07:14:44 +07:00
|
|
|
|
2005-10-20 11:23:43 +07:00
|
|
|
for (addr = vma->vm_start; addr < vma->vm_end; addr += HPAGE_SIZE) {
|
2005-10-30 08:16:23 +07:00
|
|
|
src_pte = huge_pte_offset(src, addr);
|
|
|
|
if (!src_pte)
|
|
|
|
continue;
|
2005-06-22 07:14:44 +07:00
|
|
|
dst_pte = huge_pte_alloc(dst, addr);
|
|
|
|
if (!dst_pte)
|
|
|
|
goto nomem;
|
2005-10-30 08:16:23 +07:00
|
|
|
spin_lock(&dst->page_table_lock);
|
2005-10-20 11:23:43 +07:00
|
|
|
spin_lock(&src->page_table_lock);
|
2005-10-30 08:16:23 +07:00
|
|
|
if (!pte_none(*src_pte)) {
|
2006-01-06 15:10:44 +07:00
|
|
|
if (cow)
|
|
|
|
ptep_set_wrprotect(src, addr, src_pte);
|
2005-10-20 11:23:43 +07:00
|
|
|
entry = *src_pte;
|
|
|
|
ptepage = pte_page(entry);
|
|
|
|
get_page(ptepage);
|
|
|
|
set_huge_pte_at(dst, addr, dst_pte, entry);
|
|
|
|
}
|
|
|
|
spin_unlock(&src->page_table_lock);
|
2005-10-30 08:16:23 +07:00
|
|
|
spin_unlock(&dst->page_table_lock);
|
2005-06-22 07:14:44 +07:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nomem:
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2006-10-11 15:20:46 +07:00
|
|
|
void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
|
|
|
|
unsigned long end)
|
2005-06-22 07:14:44 +07:00
|
|
|
{
|
|
|
|
struct mm_struct *mm = vma->vm_mm;
|
|
|
|
unsigned long address;
|
2005-08-06 01:59:35 +07:00
|
|
|
pte_t *ptep;
|
2005-06-22 07:14:44 +07:00
|
|
|
pte_t pte;
|
|
|
|
struct page *page;
|
2006-10-04 16:15:24 +07:00
|
|
|
struct page *tmp;
|
2006-12-07 11:31:39 +07:00
|
|
|
/*
|
|
|
|
* A page gathering list, protected by per file i_mmap_lock. The
|
|
|
|
* lock is used to avoid list corruption from multiple unmapping
|
|
|
|
* of the same page since we are using page->lru.
|
|
|
|
*/
|
2006-10-04 16:15:24 +07:00
|
|
|
LIST_HEAD(page_list);
|
2005-06-22 07:14:44 +07:00
|
|
|
|
|
|
|
WARN_ON(!is_vm_hugetlb_page(vma));
|
|
|
|
BUG_ON(start & ~HPAGE_MASK);
|
|
|
|
BUG_ON(end & ~HPAGE_MASK);
|
|
|
|
|
2005-10-30 08:16:30 +07:00
|
|
|
spin_lock(&mm->page_table_lock);
|
2005-06-22 07:14:44 +07:00
|
|
|
for (address = start; address < end; address += HPAGE_SIZE) {
|
2005-08-06 01:59:35 +07:00
|
|
|
ptep = huge_pte_offset(mm, address);
|
2005-10-30 08:16:46 +07:00
|
|
|
if (!ptep)
|
2005-08-06 01:59:35 +07:00
|
|
|
continue;
|
|
|
|
|
2006-12-07 11:32:03 +07:00
|
|
|
if (huge_pmd_unshare(mm, &address, ptep))
|
|
|
|
continue;
|
|
|
|
|
2005-08-06 01:59:35 +07:00
|
|
|
pte = huge_ptep_get_and_clear(mm, address, ptep);
|
2005-06-22 07:14:44 +07:00
|
|
|
if (pte_none(pte))
|
|
|
|
continue;
|
2005-08-06 01:59:35 +07:00
|
|
|
|
2005-06-22 07:14:44 +07:00
|
|
|
page = pte_page(pte);
|
2007-02-09 05:20:27 +07:00
|
|
|
if (pte_dirty(pte))
|
|
|
|
set_page_dirty(page);
|
2006-10-04 16:15:24 +07:00
|
|
|
list_add(&page->lru, &page_list);
|
2005-06-22 07:14:44 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock(&mm->page_table_lock);
|
2005-10-30 08:16:30 +07:00
|
|
|
flush_tlb_range(vma, start, end);
|
2006-10-04 16:15:24 +07:00
|
|
|
list_for_each_entry_safe(page, tmp, &page_list, lru) {
|
|
|
|
list_del(&page->lru);
|
|
|
|
put_page(page);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-06-22 07:14:44 +07:00
|
|
|
|
2006-10-11 15:20:46 +07:00
|
|
|
void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
|
|
|
|
unsigned long end)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* It is undesirable to test vma->vm_file as it should be non-null
|
|
|
|
* for valid hugetlb area. However, vm_file will be NULL in the error
|
|
|
|
* cleanup path of do_mmap_pgoff. When hugetlbfs ->mmap method fails,
|
|
|
|
* do_mmap_pgoff() nullifies vma->vm_file before calling this function
|
|
|
|
* to clean up. Since no pte has actually been setup, it is safe to
|
|
|
|
* do nothing in this case.
|
|
|
|
*/
|
|
|
|
if (vma->vm_file) {
|
|
|
|
spin_lock(&vma->vm_file->f_mapping->i_mmap_lock);
|
|
|
|
__unmap_hugepage_range(vma, start, end);
|
|
|
|
spin_unlock(&vma->vm_file->f_mapping->i_mmap_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-01-06 15:10:44 +07:00
|
|
|
static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
|
|
unsigned long address, pte_t *ptep, pte_t pte)
|
|
|
|
{
|
|
|
|
struct page *old_page, *new_page;
|
2006-03-22 15:08:51 +07:00
|
|
|
int avoidcopy;
|
2006-01-06 15:10:44 +07:00
|
|
|
|
|
|
|
old_page = pte_page(pte);
|
|
|
|
|
|
|
|
/* If no-one else is actually using this page, avoid the copy
|
|
|
|
* and just make the page writable */
|
|
|
|
avoidcopy = (page_count(old_page) == 1);
|
|
|
|
if (avoidcopy) {
|
|
|
|
set_huge_ptep_writable(vma, address, ptep);
|
2007-07-19 15:47:05 +07:00
|
|
|
return 0;
|
2006-01-06 15:10:44 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
page_cache_get(old_page);
|
2006-01-06 15:10:46 +07:00
|
|
|
new_page = alloc_huge_page(vma, address);
|
2006-01-06 15:10:44 +07:00
|
|
|
|
2007-11-15 07:59:39 +07:00
|
|
|
if (IS_ERR(new_page)) {
|
2006-01-06 15:10:44 +07:00
|
|
|
page_cache_release(old_page);
|
2007-11-15 07:59:39 +07:00
|
|
|
return -PTR_ERR(new_page);
|
2006-01-06 15:10:44 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock(&mm->page_table_lock);
|
2006-12-13 00:14:55 +07:00
|
|
|
copy_huge_page(new_page, old_page, address, vma);
|
2006-01-06 15:10:44 +07:00
|
|
|
spin_lock(&mm->page_table_lock);
|
|
|
|
|
|
|
|
ptep = huge_pte_offset(mm, address & HPAGE_MASK);
|
|
|
|
if (likely(pte_same(*ptep, pte))) {
|
|
|
|
/* Break COW */
|
|
|
|
set_huge_pte_at(mm, address, ptep,
|
|
|
|
make_huge_pte(vma, new_page, 1));
|
|
|
|
/* Make the old page be freed below */
|
|
|
|
new_page = old_page;
|
|
|
|
}
|
|
|
|
page_cache_release(new_page);
|
|
|
|
page_cache_release(old_page);
|
2007-07-19 15:47:05 +07:00
|
|
|
return 0;
|
2006-01-06 15:10:44 +07:00
|
|
|
}
|
|
|
|
|
2007-07-17 18:03:33 +07:00
|
|
|
static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
2006-01-06 15:10:44 +07:00
|
|
|
unsigned long address, pte_t *ptep, int write_access)
|
2005-10-20 22:24:28 +07:00
|
|
|
{
|
|
|
|
int ret = VM_FAULT_SIGBUS;
|
2005-10-30 08:16:46 +07:00
|
|
|
unsigned long idx;
|
|
|
|
unsigned long size;
|
|
|
|
struct page *page;
|
|
|
|
struct address_space *mapping;
|
2006-01-06 15:10:44 +07:00
|
|
|
pte_t new_pte;
|
2005-10-30 08:16:46 +07:00
|
|
|
|
|
|
|
mapping = vma->vm_file->f_mapping;
|
|
|
|
idx = ((address - vma->vm_start) >> HPAGE_SHIFT)
|
|
|
|
+ (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use page lock to guard against racing truncation
|
|
|
|
* before we get page_table_lock.
|
|
|
|
*/
|
2006-01-06 15:10:49 +07:00
|
|
|
retry:
|
|
|
|
page = find_lock_page(mapping, idx);
|
|
|
|
if (!page) {
|
2006-10-29 00:38:43 +07:00
|
|
|
size = i_size_read(mapping->host) >> HPAGE_SHIFT;
|
|
|
|
if (idx >= size)
|
|
|
|
goto out;
|
2006-01-06 15:10:49 +07:00
|
|
|
page = alloc_huge_page(vma, address);
|
2007-11-15 07:59:39 +07:00
|
|
|
if (IS_ERR(page)) {
|
|
|
|
ret = -PTR_ERR(page);
|
2006-01-06 15:10:49 +07:00
|
|
|
goto out;
|
|
|
|
}
|
2006-03-22 15:08:51 +07:00
|
|
|
clear_huge_page(page, address);
|
2005-10-20 22:24:28 +07:00
|
|
|
|
2006-01-06 15:10:49 +07:00
|
|
|
if (vma->vm_flags & VM_SHARED) {
|
|
|
|
int err;
|
2007-11-15 07:59:44 +07:00
|
|
|
struct inode *inode = mapping->host;
|
2006-01-06 15:10:49 +07:00
|
|
|
|
|
|
|
err = add_to_page_cache(page, mapping, idx, GFP_KERNEL);
|
|
|
|
if (err) {
|
|
|
|
put_page(page);
|
|
|
|
if (err == -EEXIST)
|
|
|
|
goto retry;
|
|
|
|
goto out;
|
|
|
|
}
|
2007-11-15 07:59:44 +07:00
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
inode->i_blocks += BLOCKS_PER_HUGEPAGE;
|
|
|
|
spin_unlock(&inode->i_lock);
|
2006-01-06 15:10:49 +07:00
|
|
|
} else
|
|
|
|
lock_page(page);
|
|
|
|
}
|
2006-01-06 15:10:44 +07:00
|
|
|
|
2005-10-20 22:24:28 +07:00
|
|
|
spin_lock(&mm->page_table_lock);
|
2005-10-30 08:16:46 +07:00
|
|
|
size = i_size_read(mapping->host) >> HPAGE_SHIFT;
|
|
|
|
if (idx >= size)
|
|
|
|
goto backout;
|
|
|
|
|
2007-07-19 15:47:05 +07:00
|
|
|
ret = 0;
|
2006-01-06 15:10:43 +07:00
|
|
|
if (!pte_none(*ptep))
|
2005-10-30 08:16:46 +07:00
|
|
|
goto backout;
|
|
|
|
|
2006-01-06 15:10:44 +07:00
|
|
|
new_pte = make_huge_pte(vma, page, ((vma->vm_flags & VM_WRITE)
|
|
|
|
&& (vma->vm_flags & VM_SHARED)));
|
|
|
|
set_huge_pte_at(mm, address, ptep, new_pte);
|
|
|
|
|
|
|
|
if (write_access && !(vma->vm_flags & VM_SHARED)) {
|
|
|
|
/* Optimization, do the COW without a second fault */
|
|
|
|
ret = hugetlb_cow(mm, vma, address, ptep, new_pte);
|
|
|
|
}
|
|
|
|
|
2005-10-20 22:24:28 +07:00
|
|
|
spin_unlock(&mm->page_table_lock);
|
2005-10-30 08:16:46 +07:00
|
|
|
unlock_page(page);
|
|
|
|
out:
|
2005-10-20 22:24:28 +07:00
|
|
|
return ret;
|
2005-10-30 08:16:46 +07:00
|
|
|
|
|
|
|
backout:
|
|
|
|
spin_unlock(&mm->page_table_lock);
|
|
|
|
unlock_page(page);
|
|
|
|
put_page(page);
|
|
|
|
goto out;
|
2005-10-20 22:24:28 +07:00
|
|
|
}
|
|
|
|
|
2006-01-06 15:10:43 +07:00
|
|
|
int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
|
|
unsigned long address, int write_access)
|
|
|
|
{
|
|
|
|
pte_t *ptep;
|
|
|
|
pte_t entry;
|
2006-01-06 15:10:44 +07:00
|
|
|
int ret;
|
[PATCH] hugepage: serialize hugepage allocation and instantiation
Currently, no lock or mutex is held between allocating a hugepage and
inserting it into the pagetables / page cache. When we do go to insert the
page into pagetables or page cache, we recheck and may free the newly
allocated hugepage. However, since the number of hugepages in the system
is strictly limited, and it's usualy to want to use all of them, this can
still lead to spurious allocation failures.
For example, suppose two processes are both mapping (MAP_SHARED) the same
hugepage file, large enough to consume the entire available hugepage pool.
If they race instantiating the last page in the mapping, they will both
attempt to allocate the last available hugepage. One will fail, of course,
returning OOM from the fault and thus causing the process to be killed,
despite the fact that the entire mapping can, in fact, be instantiated.
The patch fixes this race by the simple method of adding a (sleeping) mutex
to serialize the hugepage fault path between allocation and insertion into
pagetables and/or page cache. It would be possible to avoid the
serialization by catching the allocation failures, waiting on some
condition, then rechecking to see if someone else has instantiated the page
for us. Given the likely frequency of hugepage instantiations, it seems
very doubtful it's worth the extra complexity.
This patch causes no regression on the libhugetlbfs testsuite, and one
test, which can trigger this race now passes where it previously failed.
Actually, the test still sometimes fails, though less often and only as a
shmat() failure, rather processes getting OOM killed by the VM. The dodgy
heuristic tests in fs/hugetlbfs/inode.c for whether there's enough hugepage
space aren't protected by the new mutex, and would be ugly to do so, so
there's still a race there. Another patch to replace those tests with
something saner for this reason as well as others coming...
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 15:08:53 +07:00
|
|
|
static DEFINE_MUTEX(hugetlb_instantiation_mutex);
|
2006-01-06 15:10:43 +07:00
|
|
|
|
|
|
|
ptep = huge_pte_alloc(mm, address);
|
|
|
|
if (!ptep)
|
|
|
|
return VM_FAULT_OOM;
|
|
|
|
|
[PATCH] hugepage: serialize hugepage allocation and instantiation
Currently, no lock or mutex is held between allocating a hugepage and
inserting it into the pagetables / page cache. When we do go to insert the
page into pagetables or page cache, we recheck and may free the newly
allocated hugepage. However, since the number of hugepages in the system
is strictly limited, and it's usualy to want to use all of them, this can
still lead to spurious allocation failures.
For example, suppose two processes are both mapping (MAP_SHARED) the same
hugepage file, large enough to consume the entire available hugepage pool.
If they race instantiating the last page in the mapping, they will both
attempt to allocate the last available hugepage. One will fail, of course,
returning OOM from the fault and thus causing the process to be killed,
despite the fact that the entire mapping can, in fact, be instantiated.
The patch fixes this race by the simple method of adding a (sleeping) mutex
to serialize the hugepage fault path between allocation and insertion into
pagetables and/or page cache. It would be possible to avoid the
serialization by catching the allocation failures, waiting on some
condition, then rechecking to see if someone else has instantiated the page
for us. Given the likely frequency of hugepage instantiations, it seems
very doubtful it's worth the extra complexity.
This patch causes no regression on the libhugetlbfs testsuite, and one
test, which can trigger this race now passes where it previously failed.
Actually, the test still sometimes fails, though less often and only as a
shmat() failure, rather processes getting OOM killed by the VM. The dodgy
heuristic tests in fs/hugetlbfs/inode.c for whether there's enough hugepage
space aren't protected by the new mutex, and would be ugly to do so, so
there's still a race there. Another patch to replace those tests with
something saner for this reason as well as others coming...
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 15:08:53 +07:00
|
|
|
/*
|
|
|
|
* Serialize hugepage allocation and instantiation, so that we don't
|
|
|
|
* get spurious allocation failures if two CPUs race to instantiate
|
|
|
|
* the same page in the page cache.
|
|
|
|
*/
|
|
|
|
mutex_lock(&hugetlb_instantiation_mutex);
|
2006-01-06 15:10:43 +07:00
|
|
|
entry = *ptep;
|
[PATCH] hugepage: serialize hugepage allocation and instantiation
Currently, no lock or mutex is held between allocating a hugepage and
inserting it into the pagetables / page cache. When we do go to insert the
page into pagetables or page cache, we recheck and may free the newly
allocated hugepage. However, since the number of hugepages in the system
is strictly limited, and it's usualy to want to use all of them, this can
still lead to spurious allocation failures.
For example, suppose two processes are both mapping (MAP_SHARED) the same
hugepage file, large enough to consume the entire available hugepage pool.
If they race instantiating the last page in the mapping, they will both
attempt to allocate the last available hugepage. One will fail, of course,
returning OOM from the fault and thus causing the process to be killed,
despite the fact that the entire mapping can, in fact, be instantiated.
The patch fixes this race by the simple method of adding a (sleeping) mutex
to serialize the hugepage fault path between allocation and insertion into
pagetables and/or page cache. It would be possible to avoid the
serialization by catching the allocation failures, waiting on some
condition, then rechecking to see if someone else has instantiated the page
for us. Given the likely frequency of hugepage instantiations, it seems
very doubtful it's worth the extra complexity.
This patch causes no regression on the libhugetlbfs testsuite, and one
test, which can trigger this race now passes where it previously failed.
Actually, the test still sometimes fails, though less often and only as a
shmat() failure, rather processes getting OOM killed by the VM. The dodgy
heuristic tests in fs/hugetlbfs/inode.c for whether there's enough hugepage
space aren't protected by the new mutex, and would be ugly to do so, so
there's still a race there. Another patch to replace those tests with
something saner for this reason as well as others coming...
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 15:08:53 +07:00
|
|
|
if (pte_none(entry)) {
|
|
|
|
ret = hugetlb_no_page(mm, vma, address, ptep, write_access);
|
|
|
|
mutex_unlock(&hugetlb_instantiation_mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
2006-01-06 15:10:43 +07:00
|
|
|
|
2007-07-19 15:47:05 +07:00
|
|
|
ret = 0;
|
2006-01-06 15:10:44 +07:00
|
|
|
|
|
|
|
spin_lock(&mm->page_table_lock);
|
|
|
|
/* Check for a racing update before calling hugetlb_cow */
|
|
|
|
if (likely(pte_same(entry, *ptep)))
|
|
|
|
if (write_access && !pte_write(entry))
|
|
|
|
ret = hugetlb_cow(mm, vma, address, ptep, entry);
|
|
|
|
spin_unlock(&mm->page_table_lock);
|
[PATCH] hugepage: serialize hugepage allocation and instantiation
Currently, no lock or mutex is held between allocating a hugepage and
inserting it into the pagetables / page cache. When we do go to insert the
page into pagetables or page cache, we recheck and may free the newly
allocated hugepage. However, since the number of hugepages in the system
is strictly limited, and it's usualy to want to use all of them, this can
still lead to spurious allocation failures.
For example, suppose two processes are both mapping (MAP_SHARED) the same
hugepage file, large enough to consume the entire available hugepage pool.
If they race instantiating the last page in the mapping, they will both
attempt to allocate the last available hugepage. One will fail, of course,
returning OOM from the fault and thus causing the process to be killed,
despite the fact that the entire mapping can, in fact, be instantiated.
The patch fixes this race by the simple method of adding a (sleeping) mutex
to serialize the hugepage fault path between allocation and insertion into
pagetables and/or page cache. It would be possible to avoid the
serialization by catching the allocation failures, waiting on some
condition, then rechecking to see if someone else has instantiated the page
for us. Given the likely frequency of hugepage instantiations, it seems
very doubtful it's worth the extra complexity.
This patch causes no regression on the libhugetlbfs testsuite, and one
test, which can trigger this race now passes where it previously failed.
Actually, the test still sometimes fails, though less often and only as a
shmat() failure, rather processes getting OOM killed by the VM. The dodgy
heuristic tests in fs/hugetlbfs/inode.c for whether there's enough hugepage
space aren't protected by the new mutex, and would be ugly to do so, so
there's still a race there. Another patch to replace those tests with
something saner for this reason as well as others coming...
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-03-22 15:08:53 +07:00
|
|
|
mutex_unlock(&hugetlb_instantiation_mutex);
|
2006-01-06 15:10:44 +07:00
|
|
|
|
|
|
|
return ret;
|
2006-01-06 15:10:43 +07:00
|
|
|
}
|
|
|
|
|
2005-06-22 07:14:44 +07:00
|
|
|
int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
|
|
|
struct page **pages, struct vm_area_struct **vmas,
|
2007-11-15 07:59:33 +07:00
|
|
|
unsigned long *position, int *length, int i,
|
|
|
|
int write)
|
2005-06-22 07:14:44 +07:00
|
|
|
{
|
2006-03-22 15:09:03 +07:00
|
|
|
unsigned long pfn_offset;
|
|
|
|
unsigned long vaddr = *position;
|
2005-06-22 07:14:44 +07:00
|
|
|
int remainder = *length;
|
|
|
|
|
2005-10-20 11:23:43 +07:00
|
|
|
spin_lock(&mm->page_table_lock);
|
2005-06-22 07:14:44 +07:00
|
|
|
while (vaddr < vma->vm_end && remainder) {
|
2005-10-30 08:16:46 +07:00
|
|
|
pte_t *pte;
|
|
|
|
struct page *page;
|
2005-06-22 07:14:44 +07:00
|
|
|
|
2005-10-30 08:16:46 +07:00
|
|
|
/*
|
|
|
|
* Some archs (sparc64, sh*) have multiple pte_ts to
|
|
|
|
* each hugepage. We have to make * sure we get the
|
|
|
|
* first, for the page indexing below to work.
|
|
|
|
*/
|
|
|
|
pte = huge_pte_offset(mm, vaddr & HPAGE_MASK);
|
2005-06-22 07:14:44 +07:00
|
|
|
|
2007-12-11 06:49:28 +07:00
|
|
|
if (!pte || pte_none(*pte) || (write && !pte_write(*pte))) {
|
2005-10-30 08:16:46 +07:00
|
|
|
int ret;
|
2005-06-22 07:14:44 +07:00
|
|
|
|
2005-10-30 08:16:46 +07:00
|
|
|
spin_unlock(&mm->page_table_lock);
|
2007-11-15 07:59:33 +07:00
|
|
|
ret = hugetlb_fault(mm, vma, vaddr, write);
|
2005-10-30 08:16:46 +07:00
|
|
|
spin_lock(&mm->page_table_lock);
|
2007-08-23 04:01:51 +07:00
|
|
|
if (!(ret & VM_FAULT_ERROR))
|
2005-10-30 08:16:46 +07:00
|
|
|
continue;
|
2005-06-22 07:14:44 +07:00
|
|
|
|
2005-10-30 08:16:46 +07:00
|
|
|
remainder = 0;
|
|
|
|
if (!i)
|
|
|
|
i = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2006-03-22 15:09:03 +07:00
|
|
|
pfn_offset = (vaddr & ~HPAGE_MASK) >> PAGE_SHIFT;
|
|
|
|
page = pte_page(*pte);
|
|
|
|
same_page:
|
2006-03-31 17:29:57 +07:00
|
|
|
if (pages) {
|
|
|
|
get_page(page);
|
2006-03-22 15:09:03 +07:00
|
|
|
pages[i] = page + pfn_offset;
|
2006-03-31 17:29:57 +07:00
|
|
|
}
|
2005-06-22 07:14:44 +07:00
|
|
|
|
|
|
|
if (vmas)
|
|
|
|
vmas[i] = vma;
|
|
|
|
|
|
|
|
vaddr += PAGE_SIZE;
|
2006-03-22 15:09:03 +07:00
|
|
|
++pfn_offset;
|
2005-06-22 07:14:44 +07:00
|
|
|
--remainder;
|
|
|
|
++i;
|
2006-03-22 15:09:03 +07:00
|
|
|
if (vaddr < vma->vm_end && remainder &&
|
|
|
|
pfn_offset < HPAGE_SIZE/PAGE_SIZE) {
|
|
|
|
/*
|
|
|
|
* We use pfn_offset to avoid touching the pageframes
|
|
|
|
* of this compound page.
|
|
|
|
*/
|
|
|
|
goto same_page;
|
|
|
|
}
|
2005-06-22 07:14:44 +07:00
|
|
|
}
|
2005-10-20 11:23:43 +07:00
|
|
|
spin_unlock(&mm->page_table_lock);
|
2005-06-22 07:14:44 +07:00
|
|
|
*length = remainder;
|
|
|
|
*position = vaddr;
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
2006-03-22 15:08:50 +07:00
|
|
|
|
|
|
|
void hugetlb_change_protection(struct vm_area_struct *vma,
|
|
|
|
unsigned long address, unsigned long end, pgprot_t newprot)
|
|
|
|
{
|
|
|
|
struct mm_struct *mm = vma->vm_mm;
|
|
|
|
unsigned long start = address;
|
|
|
|
pte_t *ptep;
|
|
|
|
pte_t pte;
|
|
|
|
|
|
|
|
BUG_ON(address >= end);
|
|
|
|
flush_cache_range(vma, address, end);
|
|
|
|
|
2006-12-07 11:32:03 +07:00
|
|
|
spin_lock(&vma->vm_file->f_mapping->i_mmap_lock);
|
2006-03-22 15:08:50 +07:00
|
|
|
spin_lock(&mm->page_table_lock);
|
|
|
|
for (; address < end; address += HPAGE_SIZE) {
|
|
|
|
ptep = huge_pte_offset(mm, address);
|
|
|
|
if (!ptep)
|
|
|
|
continue;
|
2006-12-07 11:32:03 +07:00
|
|
|
if (huge_pmd_unshare(mm, &address, ptep))
|
|
|
|
continue;
|
2006-03-22 15:08:50 +07:00
|
|
|
if (!pte_none(*ptep)) {
|
|
|
|
pte = huge_ptep_get_and_clear(mm, address, ptep);
|
|
|
|
pte = pte_mkhuge(pte_modify(pte, newprot));
|
|
|
|
set_huge_pte_at(mm, address, ptep, pte);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock(&mm->page_table_lock);
|
2006-12-07 11:32:03 +07:00
|
|
|
spin_unlock(&vma->vm_file->f_mapping->i_mmap_lock);
|
2006-03-22 15:08:50 +07:00
|
|
|
|
|
|
|
flush_tlb_range(vma, start, end);
|
|
|
|
}
|
|
|
|
|
2006-06-23 16:03:15 +07:00
|
|
|
struct file_region {
|
|
|
|
struct list_head link;
|
|
|
|
long from;
|
|
|
|
long to;
|
|
|
|
};
|
|
|
|
|
|
|
|
static long region_add(struct list_head *head, long f, long t)
|
|
|
|
{
|
|
|
|
struct file_region *rg, *nrg, *trg;
|
|
|
|
|
|
|
|
/* Locate the region we are either in or before. */
|
|
|
|
list_for_each_entry(rg, head, link)
|
|
|
|
if (f <= rg->to)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Round our left edge to the current segment if it encloses us. */
|
|
|
|
if (f > rg->from)
|
|
|
|
f = rg->from;
|
|
|
|
|
|
|
|
/* Check for and consume any regions we now overlap with. */
|
|
|
|
nrg = rg;
|
|
|
|
list_for_each_entry_safe(rg, trg, rg->link.prev, link) {
|
|
|
|
if (&rg->link == head)
|
|
|
|
break;
|
|
|
|
if (rg->from > t)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* If this area reaches higher then extend our area to
|
|
|
|
* include it completely. If this is not the first area
|
|
|
|
* which we intend to reuse, free it. */
|
|
|
|
if (rg->to > t)
|
|
|
|
t = rg->to;
|
|
|
|
if (rg != nrg) {
|
|
|
|
list_del(&rg->link);
|
|
|
|
kfree(rg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nrg->from = f;
|
|
|
|
nrg->to = t;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long region_chg(struct list_head *head, long f, long t)
|
|
|
|
{
|
|
|
|
struct file_region *rg, *nrg;
|
|
|
|
long chg = 0;
|
|
|
|
|
|
|
|
/* Locate the region we are before or in. */
|
|
|
|
list_for_each_entry(rg, head, link)
|
|
|
|
if (f <= rg->to)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* If we are below the current region then a new region is required.
|
|
|
|
* Subtle, allocate a new region at the position but make it zero
|
2007-10-20 06:27:18 +07:00
|
|
|
* size such that we can guarantee to record the reservation. */
|
2006-06-23 16:03:15 +07:00
|
|
|
if (&rg->link == head || t < rg->from) {
|
|
|
|
nrg = kmalloc(sizeof(*nrg), GFP_KERNEL);
|
2007-10-18 17:07:05 +07:00
|
|
|
if (!nrg)
|
2006-06-23 16:03:15 +07:00
|
|
|
return -ENOMEM;
|
|
|
|
nrg->from = f;
|
|
|
|
nrg->to = f;
|
|
|
|
INIT_LIST_HEAD(&nrg->link);
|
|
|
|
list_add(&nrg->link, rg->link.prev);
|
|
|
|
|
|
|
|
return t - f;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Round our left edge to the current segment if it encloses us. */
|
|
|
|
if (f > rg->from)
|
|
|
|
f = rg->from;
|
|
|
|
chg = t - f;
|
|
|
|
|
|
|
|
/* Check for and consume any regions we now overlap with. */
|
|
|
|
list_for_each_entry(rg, rg->link.prev, link) {
|
|
|
|
if (&rg->link == head)
|
|
|
|
break;
|
|
|
|
if (rg->from > t)
|
|
|
|
return chg;
|
|
|
|
|
|
|
|
/* We overlap with this area, if it extends futher than
|
|
|
|
* us then we must extend ourselves. Account for its
|
|
|
|
* existing reservation. */
|
|
|
|
if (rg->to > t) {
|
|
|
|
chg += rg->to - t;
|
|
|
|
t = rg->to;
|
|
|
|
}
|
|
|
|
chg -= rg->to - rg->from;
|
|
|
|
}
|
|
|
|
return chg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long region_truncate(struct list_head *head, long end)
|
|
|
|
{
|
|
|
|
struct file_region *rg, *trg;
|
|
|
|
long chg = 0;
|
|
|
|
|
|
|
|
/* Locate the region we are either in or before. */
|
|
|
|
list_for_each_entry(rg, head, link)
|
|
|
|
if (end <= rg->to)
|
|
|
|
break;
|
|
|
|
if (&rg->link == head)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* If we are in the middle of a region then adjust it. */
|
|
|
|
if (end > rg->from) {
|
|
|
|
chg = rg->to - end;
|
|
|
|
rg->to = end;
|
|
|
|
rg = list_entry(rg->link.next, typeof(*rg), link);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Drop any remaining regions. */
|
|
|
|
list_for_each_entry_safe(rg, trg, rg->link.prev, link) {
|
|
|
|
if (&rg->link == head)
|
|
|
|
break;
|
|
|
|
chg += rg->to - rg->from;
|
|
|
|
list_del(&rg->link);
|
|
|
|
kfree(rg);
|
|
|
|
}
|
|
|
|
return chg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hugetlb_acct_memory(long delta)
|
|
|
|
{
|
|
|
|
int ret = -ENOMEM;
|
|
|
|
|
|
|
|
spin_lock(&hugetlb_lock);
|
2007-05-09 16:33:34 +07:00
|
|
|
/*
|
|
|
|
* When cpuset is configured, it breaks the strict hugetlb page
|
|
|
|
* reservation as the accounting is done on a global variable. Such
|
|
|
|
* reservation is completely rubbish in the presence of cpuset because
|
|
|
|
* the reservation is not checked against page availability for the
|
|
|
|
* current cpuset. Application can still potentially OOM'ed by kernel
|
|
|
|
* with lack of free htlb page in cpuset that the task is in.
|
|
|
|
* Attempt to enforce strict accounting with cpuset is almost
|
|
|
|
* impossible (or too ugly) because cpuset is too fluid that
|
|
|
|
* task or memory node can be dynamically moved between cpusets.
|
|
|
|
*
|
|
|
|
* The change of semantics for shared hugetlb mapping with cpuset is
|
|
|
|
* undesirable. However, in order to preserve some of the semantics,
|
|
|
|
* we fall back to check against current free page availability as
|
|
|
|
* a best attempt and hopefully to minimize the impact of changing
|
|
|
|
* semantics that cpuset has.
|
|
|
|
*/
|
2007-10-16 15:26:19 +07:00
|
|
|
if (delta > 0) {
|
|
|
|
if (gather_surplus_pages(delta) < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (delta > cpuset_mems_nr(free_huge_pages_node))
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
resv_huge_pages += delta;
|
|
|
|
if (delta < 0)
|
|
|
|
return_unused_surplus_pages((unsigned long) -delta);
|
|
|
|
|
|
|
|
out:
|
|
|
|
spin_unlock(&hugetlb_lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int hugetlb_reserve_pages(struct inode *inode, long from, long to)
|
|
|
|
{
|
|
|
|
long ret, chg;
|
|
|
|
|
|
|
|
chg = region_chg(&inode->i_mapping->private_list, from, to);
|
|
|
|
if (chg < 0)
|
|
|
|
return chg;
|
2007-05-09 16:33:34 +07:00
|
|
|
|
2007-11-15 07:59:42 +07:00
|
|
|
if (hugetlb_get_quota(inode->i_mapping, chg))
|
|
|
|
return -ENOSPC;
|
2006-06-23 16:03:15 +07:00
|
|
|
ret = hugetlb_acct_memory(chg);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
region_add(&inode->i_mapping->private_list, from, to);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed)
|
|
|
|
{
|
|
|
|
long chg = region_truncate(&inode->i_mapping->private_list, offset);
|
2007-11-15 07:59:44 +07:00
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
inode->i_blocks -= BLOCKS_PER_HUGEPAGE * freed;
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
|
2007-11-15 07:59:42 +07:00
|
|
|
hugetlb_put_quota(inode->i_mapping, (chg - freed));
|
|
|
|
hugetlb_acct_memory(-(chg - freed));
|
2006-06-23 16:03:15 +07:00
|
|
|
}
|