* [PATCH 1/3] Fix hugetlb pool allocation with empty nodes
@ 2007-06-18 17:34 Nishanth Aravamudan
2007-06-18 17:37 ` [PATCH 2/3] hugetlb: numafy several functions Nishanth Aravamudan
2007-06-18 18:20 ` [PATCH 1/3] Fix hugetlb pool allocation with empty nodes Nishanth Aravamudan
0 siblings, 2 replies; 4+ messages in thread
From: Nishanth Aravamudan @ 2007-06-18 17:34 UTC (permalink / raw)
To: wli; +Cc: anton, lee.schermerhorn, clameter, linux-mm
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 and for exporting a
node_memory_map indicating which nodes have memory. Since mempolicy.c
already has a number of functions which support interleaving, create a
mempolicy when we invoke alloc_fresh_huge_page() that specifies
interleaving across all the nodes in node_memory_map, rather than custom
interleaving code in hugetlb.c. This requires adding some dummy
functions, and some declarations, in mempolicy.h to compile with NUMA or
!NUMA.
Compiled-test w/ NUMA and !NUMA. Run-tested with the follow-on patches
on 4-node ppc64 w/ 2 memoryless nodes and 4-node x86_64 w/ 0 memoryless
nodes.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Cc: Anton Blanchard <anton@samba.org>
Cc: Lee Schermerhorn <lee.schermerhon@hp.com>
Cc: Christoph Lameter <clameter@sgi.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h
index 22b668c..c8a68b8 100644
--- a/include/linux/mempolicy.h
+++ b/include/linux/mempolicy.h
@@ -76,6 +76,8 @@ struct mempolicy {
* The default fast path of a NULL MPOL_DEFAULT policy is always inlined.
*/
+extern struct mempolicy *mpol_new(int mode, nodemask_t *nodes);
+
extern void __mpol_free(struct mempolicy *pol);
static inline void mpol_free(struct mempolicy *pol)
{
@@ -164,6 +166,8 @@ static inline void check_highest_zone(enum zone_type k)
policy_zone = k;
}
+extern unsigned interleave_nodes(struct mempolicy *policy);
+
int do_migrate_pages(struct mm_struct *mm,
const nodemask_t *from_nodes, const nodemask_t *to_nodes, int flags);
@@ -179,6 +183,11 @@ static inline int mpol_equal(struct mempolicy *a, struct mempolicy *b)
#define mpol_set_vma_default(vma) do {} while(0)
+static inline struct mempolicy *mpol_new(int mode, nodemask_t *nodes)
+{
+ return NULL;
+}
+
static inline void mpol_free(struct mempolicy *p)
{
}
@@ -267,6 +276,11 @@ static inline int do_migrate_pages(struct mm_struct *mm,
static inline void check_highest_zone(int k)
{
}
+
+static inline unsigned interleave_nodes(struct mempolicy *policy)
+{
+ return 0;
+}
#endif /* CONFIG_NUMA */
#endif /* __KERNEL__ */
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 858c0b3..88e1a30 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -103,15 +103,22 @@ static void free_huge_page(struct page *page)
spin_unlock(&hugetlb_lock);
}
-static int alloc_fresh_huge_page(void)
+static int alloc_fresh_huge_page(struct mempolicy *policy)
{
- static int nid = 0;
+ int nid;
struct page *page;
- page = alloc_pages_node(nid, htlb_alloc_mask|__GFP_COMP|__GFP_NOWARN,
- HUGETLB_PAGE_ORDER);
- nid = next_node(nid, node_online_map);
- if (nid == MAX_NUMNODES)
- nid = first_node(node_online_map);
+ int start_nid = interleave_nodes(policy);
+
+ nid = start_nid;
+
+ do {
+ page = alloc_pages_node(nid,
+ htlb_alloc_mask|__GFP_COMP|GFP_THISNODE,
+ HUGETLB_PAGE_ORDER);
+ if (page)
+ break;
+ nid = interleave_nodes(policy);
+ } while (nid != start_nid);
if (page) {
set_compound_page_dtor(page, free_huge_page);
spin_lock(&hugetlb_lock);
@@ -153,6 +160,7 @@ fail:
static int __init hugetlb_init(void)
{
unsigned long i;
+ struct mempolicy *pol;
if (HPAGE_SHIFT == 0)
return 0;
@@ -160,11 +168,16 @@ static int __init hugetlb_init(void)
for (i = 0; i < MAX_NUMNODES; ++i)
INIT_LIST_HEAD(&hugepage_freelists[i]);
+ pol = mpol_new(MPOL_INTERLEAVE, &node_memory_map);
+ if (IS_ERR(pol))
+ goto quit;
for (i = 0; i < max_huge_pages; ++i) {
- if (!alloc_fresh_huge_page())
+ if (!alloc_fresh_huge_page(pol))
break;
}
+ mpol_free(pol);
max_huge_pages = free_huge_pages = nr_huge_pages = i;
+quit:
printk("Total HugeTLB memory allocated, %ld\n", free_huge_pages);
return 0;
}
@@ -232,10 +245,16 @@ static inline void try_to_free_low(unsigned long count)
static unsigned long set_max_huge_pages(unsigned long count)
{
+ struct mempolicy *pol;
+
+ pol = mpol_new(MPOL_INTERLEAVE, &node_memory_map);
+ if (IS_ERR(pol))
+ return nr_huge_pages;
while (count > nr_huge_pages) {
- if (!alloc_fresh_huge_page())
- return nr_huge_pages;
+ if (!alloc_fresh_huge_page(pol))
+ break;
}
+ mpol_free(pol);
if (count >= nr_huge_pages)
return nr_huge_pages;
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index d2289f3..00bf061 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -171,7 +171,7 @@ static struct zonelist *bind_zonelist(nodemask_t *nodes)
}
/* Create a new policy */
-static struct mempolicy *mpol_new(int mode, nodemask_t *nodes)
+struct mempolicy *mpol_new(int mode, nodemask_t *nodes)
{
struct mempolicy *policy;
@@ -1121,7 +1121,7 @@ static struct zonelist *zonelist_policy(gfp_t gfp, struct mempolicy *policy)
}
/* Do dynamic interleaving for a process */
-static unsigned interleave_nodes(struct mempolicy *policy)
+unsigned interleave_nodes(struct mempolicy *policy)
{
unsigned nid, next;
struct task_struct *me = current;
--
Nishanth Aravamudan <nacc@us.ibm.com>
IBM Linux Technology Center
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 2/3] hugetlb: numafy several functions
2007-06-18 17:34 [PATCH 1/3] Fix hugetlb pool allocation with empty nodes Nishanth Aravamudan
@ 2007-06-18 17:37 ` Nishanth Aravamudan
2007-06-18 17:53 ` [PATCH 3/3] hugetlb: add per-node nr_hugepages sysfs attribute Nishanth Aravamudan
2007-06-18 18:20 ` [PATCH 1/3] Fix hugetlb pool allocation with empty nodes Nishanth Aravamudan
1 sibling, 1 reply; 4+ messages in thread
From: Nishanth Aravamudan @ 2007-06-18 17:37 UTC (permalink / raw)
To: wli; +Cc: anton, lee.schermerhorn, clameter, linux-mm
Add node-parameterized helpers for dequeue_huge_page,
alloc_fresh_huge_page and try_to_free_low. Also have
update_and_free_page() take a nid parameter. This is necessary to add a
per-node sysfs attribute to specify the number of hugepages on that
node.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Acked-by: Christoph Lameter <clameter@sgi.com>
Cc: Anton Blanchard <anton@samba.org>
Cc: Lee Schermerhorn <lee.schermerhorn@hp.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 88e1a30..ca89057 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -66,11 +66,22 @@ static void enqueue_huge_page(struct page *page)
free_huge_pages_node[nid]++;
}
+static struct page *dequeue_huge_page_node(int nid)
+{
+ struct page *page;
+
+ page = list_entry(hugepage_freelists[nid].next,
+ struct page, lru);
+ list_del(&page->lru);
+ free_huge_pages--;
+ free_huge_pages_node[nid]--;
+ return page;
+}
+
static struct page *dequeue_huge_page(struct vm_area_struct *vma,
unsigned long address)
{
int nid;
- struct page *page = NULL;
struct zonelist *zonelist = huge_zonelist(vma, address,
htlb_alloc_mask);
struct zone **z;
@@ -82,14 +93,9 @@ static struct page *dequeue_huge_page(struct vm_area_struct *vma,
break;
}
- if (*z) {
- page = list_entry(hugepage_freelists[nid].next,
- struct page, lru);
- list_del(&page->lru);
- free_huge_pages--;
- free_huge_pages_node[nid]--;
- }
- return page;
+ if (*z)
+ return dequeue_huge_page_node(nid);
+ return NULL;
}
static void free_huge_page(struct page *page)
@@ -103,6 +109,25 @@ static void free_huge_page(struct page *page)
spin_unlock(&hugetlb_lock);
}
+static struct page *alloc_fresh_huge_page_node(int nid)
+{
+ struct page *page;
+
+ page = alloc_pages_node(nid,
+ GFP_HIGHUSER|__GFP_COMP|GFP_THISNODE,
+ HUGETLB_PAGE_ORDER);
+ if (page) {
+ set_compound_page_dtor(page, free_huge_page);
+ spin_lock(&hugetlb_lock);
+ nr_huge_pages++;
+ nr_huge_pages_node[nid]++;
+ spin_unlock(&hugetlb_lock);
+ put_page(page); /* free it into the hugepage allocator */
+ }
+
+ return page;
+}
+
static int alloc_fresh_huge_page(struct mempolicy *policy)
{
int nid;
@@ -112,22 +137,12 @@ static int alloc_fresh_huge_page(struct mempolicy *policy)
nid = start_nid;
do {
- page = alloc_pages_node(nid,
- htlb_alloc_mask|__GFP_COMP|GFP_THISNODE,
- HUGETLB_PAGE_ORDER);
+ page = alloc_fresh_huge_page_node(nid);
if (page)
- break;
+ return 1;
nid = interleave_nodes(policy);
} while (nid != start_nid);
- if (page) {
- set_compound_page_dtor(page, free_huge_page);
- spin_lock(&hugetlb_lock);
- nr_huge_pages++;
- nr_huge_pages_node[page_to_nid(page)]++;
- spin_unlock(&hugetlb_lock);
- put_page(page); /* free it into the hugepage allocator */
- return 1;
- }
+
return 0;
}
@@ -203,11 +218,11 @@ static unsigned int cpuset_mems_nr(unsigned int *array)
}
#ifdef CONFIG_SYSCTL
-static void update_and_free_page(struct page *page)
+static void update_and_free_page(int nid, struct page *page)
{
int i;
nr_huge_pages--;
- nr_huge_pages_node[page_to_nid(page)]--;
+ nr_huge_pages_node[nid]--;
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 |
@@ -219,25 +234,37 @@ static void update_and_free_page(struct page *page)
}
#ifdef CONFIG_HIGHMEM
+static void try_to_free_low_node(int nid, unsigned long count)
+{
+ struct page *page, *next;
+
+ list_for_each_entry_safe(page, next,
+ &hugepage_freelists[nid], lru) {
+ if (PageHighMem(page))
+ continue;
+ list_del(&page->lru);
+ update_and_free_page(nid, page);
+ free_huge_pages--;
+ free_huge_pages_node[nid]--;
+ if (count >= nr_huge_pages_node[nid])
+ return;
+ }
+}
+
static void try_to_free_low(unsigned long count)
{
int i;
for (i = 0; i < MAX_NUMNODES; ++i) {
- struct page *page, *next;
- list_for_each_entry_safe(page, next, &hugepage_freelists[i], lru) {
- if (PageHighMem(page))
- continue;
- list_del(&page->lru);
- update_and_free_page(page);
- free_huge_pages--;
- free_huge_pages_node[page_to_nid(page)]--;
- if (count >= nr_huge_pages)
- return;
- }
+ try_to_free_low_node(i, count);
+ if (count >= nr_huge_pages)
+ break;
}
}
#else
+static inline void try_to_free_low_node(int nid, unsigned long count)
+{
+}
static inline void try_to_free_low(unsigned long count)
{
}
@@ -265,7 +292,7 @@ static unsigned long set_max_huge_pages(unsigned long count)
struct page *page = dequeue_huge_page(NULL, 0);
if (!page)
break;
- update_and_free_page(page);
+ update_and_free_page(page_to_nid(page), page);
}
spin_unlock(&hugetlb_lock);
return nr_huge_pages;
--
Nishanth Aravamudan <nacc@us.ibm.com>
IBM Linux Technology Center
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 3/3] hugetlb: add per-node nr_hugepages sysfs attribute
2007-06-18 17:37 ` [PATCH 2/3] hugetlb: numafy several functions Nishanth Aravamudan
@ 2007-06-18 17:53 ` Nishanth Aravamudan
0 siblings, 0 replies; 4+ messages in thread
From: Nishanth Aravamudan @ 2007-06-18 17:53 UTC (permalink / raw)
To: wli; +Cc: anton, lee.schermerhorn, clameter, linux-mm
Allow specifying the number of hugepages to allocate on a particular
node. Our current global sysctl will try its best to put hugepages
equally on each node, but htat may not always be desired. This allows
the admin to control the layout of hugepage allocation at a finer level
(while not breaking the existing interface). Add callbacks in the sysfs
node registration and unregistration functions into hugetlb to add the
nr_hugepages attribute, which is a no-op if !NUMA or !HUGETLB.
Compile-tested with NUMA X HUGETLB (all 4 on/off combinations).
Run-tested on 4-node ppc64 w/ 2 memoryless nodes and 4-node x86_64 w/ 0
memoryless nodes.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Cc: Anton Blanchard <anton@samba.org>
Cc: Lee Schermerhorn <lee.schermerhorn@hp.com>
Cc: Christoph Lameter <clameter@sgi.com>
Cc: William Lee Irwin III <wli@holomorphy.com>
diff --git a/drivers/base/node.c b/drivers/base/node.c
index cae346e..c9d531f 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -151,6 +151,7 @@ int register_node(struct node *node, int num, struct node *parent)
sysdev_create_file(&node->sysdev, &attr_meminfo);
sysdev_create_file(&node->sysdev, &attr_numastat);
sysdev_create_file(&node->sysdev, &attr_distance);
+ hugetlb_register_node(node);
}
return error;
}
@@ -168,6 +169,7 @@ void unregister_node(struct node *node)
sysdev_remove_file(&node->sysdev, &attr_meminfo);
sysdev_remove_file(&node->sysdev, &attr_numastat);
sysdev_remove_file(&node->sysdev, &attr_distance);
+ hugetlb_unregister_node(node);
sysdev_unregister(&node->sysdev);
}
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index aa0dc9b..0d97cd4 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -4,7 +4,9 @@
#ifdef CONFIG_HUGETLB_PAGE
#include <linux/mempolicy.h>
+#include <linux/node.h>
#include <linux/shm.h>
+#include <linux/sysdev.h>
#include <asm/tlbflush.h>
struct ctl_table;
@@ -23,6 +25,13 @@ void __unmap_hugepage_range(struct vm_area_struct *, unsigned long, unsigned lon
int hugetlb_prefault(struct address_space *, struct vm_area_struct *);
int hugetlb_report_meminfo(char *);
int hugetlb_report_node_meminfo(int, char *);
+#ifdef CONFIG_NUMA
+int hugetlb_register_node(struct node *);
+void hugetlb_unregister_node(struct node *);
+#else
+#define hugetlb_register_node(node) 0
+#define hugetlb_unregister_node(node) ((void)0)
+#endif
unsigned long hugetlb_total_pages(void);
int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long address, int write_access);
@@ -114,6 +123,8 @@ static inline unsigned long hugetlb_total_pages(void)
#define unmap_hugepage_range(vma, start, end) BUG()
#define hugetlb_report_meminfo(buf) 0
#define hugetlb_report_node_meminfo(n, buf) 0
+#define hugetlb_register_node(node) 0
+#define hugetlb_unregister_node(node) ((void)0)
#define follow_huge_pmd(mm, addr, pmd, write) NULL
#define prepare_hugepage_range(addr,len,pgoff) (-EINVAL)
#define pmd_huge(x) 0
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index ca89057..7e737d1 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -217,7 +217,6 @@ static unsigned int cpuset_mems_nr(unsigned int *array)
return nr;
}
-#ifdef CONFIG_SYSCTL
static void update_and_free_page(int nid, struct page *page)
{
int i;
@@ -270,6 +269,7 @@ static inline void try_to_free_low(unsigned long count)
}
#endif
+#ifdef CONFIG_SYSCTL
static unsigned long set_max_huge_pages(unsigned long count)
{
struct mempolicy *pol;
@@ -343,6 +343,67 @@ int hugetlb_report_node_meminfo(int nid, char *buf)
nid, free_huge_pages_node[nid]);
}
+#ifdef CONFIG_NUMA
+static ssize_t hugetlb_read_nr_hugepages_node(struct sys_device *dev,
+ char *buf)
+{
+ return sprintf(buf, "%u\n", nr_huge_pages_node[dev->id]);
+}
+
+static ssize_t hugetlb_write_nr_hugepages_node(struct sys_device *dev,
+ const char *buf, size_t count)
+{
+ int nid = dev->id;
+ unsigned long target;
+ unsigned long free_on_other_nodes;
+ unsigned long nr_huge_pages_req = simple_strtoul(buf, NULL, 10);
+
+ while (nr_huge_pages_req > nr_huge_pages_node[nid]) {
+ if (!alloc_fresh_huge_page_node(nid))
+ return count;
+ }
+ if (nr_huge_pages_req >= nr_huge_pages_node[nid])
+ return count;
+
+ /* need to ensure that our counts are accurate */
+ spin_lock(&hugetlb_lock);
+ free_on_other_nodes = free_huge_pages - free_huge_pages_node[nid];
+ if (free_on_other_nodes >= resv_huge_pages) {
+ /* other nodes can satisfy reserve */
+ target = nr_huge_pages_req;
+ } else {
+ /* this node needs some free to satisfy reserve */
+ target = max((resv_huge_pages - free_on_other_nodes),
+ nr_huge_pages_req);
+ }
+ try_to_free_low_node(nid, target);
+ while (target < nr_huge_pages_node[nid]) {
+ struct page *page = dequeue_huge_page_node(nid);
+ if (!page)
+ break;
+ update_and_free_page(nid, page);
+ }
+ spin_unlock(&hugetlb_lock);
+
+ return count;
+}
+
+static SYSDEV_ATTR(nr_hugepages, S_IRUGO | S_IWUSR,
+ hugetlb_read_nr_hugepages_node,
+ hugetlb_write_nr_hugepages_node);
+
+int hugetlb_register_node(struct node *node)
+{
+ return sysdev_create_file(&node->sysdev, &attr_nr_hugepages);
+}
+
+void hugetlb_unregister_node(struct node *node)
+{
+ sysdev_remove_file(&node->sysdev, &attr_nr_hugepages);
+}
+
+#endif
+
/* Return the number pages of memory we physically have, in PAGE_SIZE units. */
unsigned long hugetlb_total_pages(void)
{
--
Nishanth Aravamudan <nacc@us.ibm.com>
IBM Linux Technology Center
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 1/3] Fix hugetlb pool allocation with empty nodes
2007-06-18 17:34 [PATCH 1/3] Fix hugetlb pool allocation with empty nodes Nishanth Aravamudan
2007-06-18 17:37 ` [PATCH 2/3] hugetlb: numafy several functions Nishanth Aravamudan
@ 2007-06-18 18:20 ` Nishanth Aravamudan
1 sibling, 0 replies; 4+ messages in thread
From: Nishanth Aravamudan @ 2007-06-18 18:20 UTC (permalink / raw)
To: wli; +Cc: anton, lee.schermerhorn, clameter, linux-mm
On 18.06.2007 [10:34:28 -0700], Nishanth Aravamudan wrote:
> 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 and for exporting a
> node_memory_map indicating which nodes have memory. Since mempolicy.c
> already has a number of functions which support interleaving, create a
> mempolicy when we invoke alloc_fresh_huge_page() that specifies
> interleaving across all the nodes in node_memory_map, rather than custom
> interleaving code in hugetlb.c. This requires adding some dummy
> functions, and some declarations, in mempolicy.h to compile with NUMA or
> !NUMA.
Sigh, in case it wasn't clear from the preceding dicussions, these
patches depend on Christoph's memoryless node fixes.
Thanks,
Nish
--
Nishanth Aravamudan <nacc@us.ibm.com>
IBM Linux Technology Center
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2007-06-18 18:16 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-06-18 17:34 [PATCH 1/3] Fix hugetlb pool allocation with empty nodes Nishanth Aravamudan
2007-06-18 17:37 ` [PATCH 2/3] hugetlb: numafy several functions Nishanth Aravamudan
2007-06-18 17:53 ` [PATCH 3/3] hugetlb: add per-node nr_hugepages sysfs attribute Nishanth Aravamudan
2007-06-18 18:20 ` [PATCH 1/3] Fix hugetlb pool allocation with empty nodes Nishanth Aravamudan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox