Skip to contentSkip to conversation

Memory Binding API v0.3 2.5.41

RFC PATCH In review Matthew Dobson 9 participants 29 messages Oct 10, 2002 – Oct 15, 2002

Subsystems (6)
lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 Memory Binding API v0.3 2.5.41

Greetings & Salutations,
Here's a wonderful patch that I know you're all dying for... Memory
Binding! It works just like CPU Affinity (binding) except that it binds a processes memory allocations (just buddy allocator for now) to specific memory blocks.
I've sent this out in the past, but haven't touched it in months. Since
the feature freeze is rapidly approaching, I want to get this out there again and see if anyone has any interest in it.
It's a fairly large patch, mostly because it includes a few odds and ends
that are topology related, and don't strictly belong in this patch, but are pre-requisites for it (ie: the [memblk|node]_online_map stuff, and some of the cleanups to page_alloc). I'll probably try and break it up into more discrete parts very soon.

[mcd@arrakis src]$ diffstat ~/patches/api_patches/mem_api/memory_binding_api-v0.3-2.5.41.patch

 arch/i386/kernel/entry.S   |    2 +
 arch/i386/kernel/numaq.c   |    4 ++
 arch/i386/kernel/smpboot.c |    4 ++
 include/asm-i386/smp.h     |   16 ++++++++
 include/asm-i386/unistd.h  |    2 +
 include/linux/init_task.h  |    4 ++
 include/linux/membind.h    |   50 +++++++++++++++++++++++++
 include/linux/mmzone.h     |   23 +++++++++++
 include/linux/sched.h      |    4 ++
 include/linux/smp.h        |    8 +++-
 kernel/sys.c               |   90 
+++++++++++++++++++++++++++++++++++++++++++++
 mm/numa.c                  |    8 ++++
 mm/page_alloc.c            |   67 ++++++++++++---------------------
 13 files changed, 237 insertions(+), 45 deletions(-)

Questions, comments, flames, and indifferent shrugs are all welcome.

btw, It applies (mostly) cleanly to mm1 as well. The mm/page_alloc.c changes fail, but if anyone is interested, they'll clean up easily, and I'll send you a patch.

Cheers!

-Matt

arch/i386/kernel/entry.S +2 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.41-vanilla/arch/i386/kernel/entry.S linux-2.5.41-memory_binding_api/arch/i386/kernel/entry.S
@@ -736,6 +736,8 @@
 	.long sys_alloc_hugepages /* 250 */
 	.long sys_free_hugepages
 	.long sys_exit_group
+	.long sys_mem_setbinding
+	.long sys_mem_getbinding
 
 	.rept NR_syscalls-(.-sys_call_table)/4
 		.long sys_ni_syscall
arch/i386/kernel/numaq.c +4 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.41-vanilla/arch/i386/kernel/numaq.c linux-2.5.41-memory_binding_api/arch/i386/kernel/numaq.c
@@ -52,6 +52,10 @@
 	numnodes = 0;
 	for(node = 0; node < MAX_NUMNODES; node++) {
 		if(scd->quads_present31_0 & (1 << node)) {
+			if (test_and_set_bit(numnodes, &node_online_map)){
+				printk("smp_dump_qct: node alread counted?!?!\n");
+				BUG();
+			}
 			numnodes++;
 			eq = &scd->eq[node];
 			/* Convert to pages */
arch/i386/kernel/smpboot.c +4 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.41-vanilla/arch/i386/kernel/smpboot.c linux-2.5.41-memory_binding_api/arch/i386/kernel/smpboot.c
@@ -61,6 +61,10 @@
 
 /* Bitmask of currently online CPUs */
 unsigned long cpu_online_map;
+/* Bitmask of currently online memory blocks */
+unsigned long memblk_online_map = 0UL;
+/* Bitmask of currently online nodes */
+unsigned long node_online_map = 0UL;
 
 static volatile unsigned long cpu_callin_map;
 volatile unsigned long cpu_callout_map;
include/asm-i386/smp.h +16 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.41-vanilla/include/asm-i386/smp.h linux-2.5.41-memory_binding_api/include/asm-i386/smp.h
@@ -54,6 +54,8 @@
 extern void smp_alloc_memory(void);
 extern unsigned long phys_cpu_present_map;
 extern unsigned long cpu_online_map;
+extern unsigned long memblk_online_map;
+extern unsigned long node_online_map;
 extern volatile unsigned long smp_invalidate_needed;
 extern int pic_mode;
 extern int smp_num_siblings;
@@ -102,6 +104,20 @@
 	return -1;
 }
 
+#define memblk_online(memblk) (memblk_online_map & (1<<(memblk)))
+ 
+extern inline unsigned int num_online_memblks(void)
+{
+	return hweight32(memblk_online_map);
+}
+
+#define node_online(node) (node_online_map & (1<<(node)))
+ 
+extern inline unsigned int num_online_nodes(void)
+{
+	return hweight32(node_online_map);
+}
+
 static __inline int hard_smp_processor_id(void)
 {
 	/* we don't want to mark this access volatile - bad code generation */
include/asm-i386/unistd.h +2 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.41-vanilla/include/asm-i386/unistd.h linux-2.5.41-memory_binding_api/include/asm-i386/unistd.h
@@ -257,6 +257,8 @@
 #define __NR_alloc_hugepages	250
 #define __NR_free_hugepages	251
 #define __NR_exit_group		252
+#define __NR_mem_setbinding	253
+#define __NR_mem_getbinding	254
 
 /* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
 
include/linux/init_task.h +4 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.41-vanilla/include/linux/init_task.h linux-2.5.41-memory_binding_api/include/linux/init_task.h
@@ -76,6 +76,10 @@
 	.children	= LIST_HEAD_INIT(tsk.children),			\
 	.sibling	= LIST_HEAD_INIT(tsk.sibling),			\
 	.group_leader	= &tsk,						\
+	.memblk_binding		= {					\
+		.bitmask	= MEMBLK_NO_BINDING,			\
+		.behavior	= MPOL_STRICT,				\
+	},								\
 	.wait_chldexit	= __WAIT_QUEUE_HEAD_INITIALIZER(tsk.wait_chldexit),\
 	.real_timer	= {						\
 		.function	= it_real_fn				\
include/linux/membind.h +50 -0

50 lines not on this page · raw message

include/linux/mmzone.h +22 -1
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.41-vanilla/include/linux/mmzone.h linux-2.5.41-memory_binding_api/include/linux/mmzone.h
@@ -167,8 +167,9 @@
 	unsigned long node_start_pfn;
 	unsigned long node_size;
 	int node_id;
+	int memblk_id; /* A unique ID for each memory block */
 	struct pglist_data *pgdat_next;
-	wait_queue_head_t       kswapd_wait;
+	wait_queue_head_t kswapd_wait;
 } pg_data_t;
 
 extern int numnodes;
@@ -249,6 +250,26 @@
 #define for_each_zone(zone) \
 	for (zone = pgdat_list->node_zones; zone; zone = next_zone(zone))
 
+/**
+ * for_each_valid_zone - helper macro to iterate over all memory zones 
+ * 	in a zonelist
+ * @zone - pointer to struct zone variable
+ * @zonelist - pointer to struct zonelist variable
+ *
+ * for_each_valid_zone() is basically an easier to read version of this 
+ * piece of code:
+ *
+ * for (i = 0; zonelist->zones[i] != NULL; i++) {
+ * 	struct zone *z = zonelist->zones[i];
+ * 	...
+ * 	}
+ *
+ * Useful for several loops in __alloc_pages.
+ */
+#define for_each_valid_zone(zone, zonelist) 		\
+	for (zone = *zonelist->zones; zone; zone++)	\
+		if (current->memblk_binding.bitmask & (1 << zone->zone_pgdat->memblk_id))
+
 #ifdef CONFIG_NUMA
 #define MAX_NR_MEMBLKS	BITS_PER_LONG /* Max number of Memory Blocks */
 #else /* !CONFIG_NUMA */
include/linux/sched.h +4 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.41-vanilla/include/linux/sched.h linux-2.5.41-memory_binding_api/include/linux/sched.h
@@ -29,6 +29,7 @@
 #include <linux/compiler.h>
 #include <linux/completion.h>
 #include <linux/pid.h>
+#include <linux/membind.h>
 
 struct exec_domain;
 
@@ -335,6 +336,9 @@
 	/* PID/PID hash table linkage. */
 	struct pid_link pids[PIDTYPE_MAX];
 
+	/* additional Memory Binding stuff */
+	memblk_list_t memblk_binding;
+	
 	wait_queue_head_t wait_chldexit;	/* for wait4() */
 	struct completion *vfork_done;		/* for vfork() */
 	int *user_tid;				/* for CLONE_CLEARTID */
include/linux/smp.h +7 -1
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.41-vanilla/include/linux/smp.h linux-2.5.41-memory_binding_api/include/linux/smp.h
@@ -94,7 +94,13 @@
 #define cpu_online(cpu)				({ BUG_ON((cpu) != 0); 1; })
 #define num_online_cpus()			1
 #define num_booting_cpus()			1
-#define cpu_possible(cpu)				({ BUG_ON((cpu) != 0); 1; })
+#define cpu_possible(cpu)			({ BUG_ON((cpu) != 0); 1; })
+#define memblk_online_map			1
+#define memblk_online(memblk)			({ BUG_ON((memblk) != 0); 1; })
+#define num_online_memblks()			1
+#define node_online_map				1
+#define node_online(node)			({ BUG_ON((node) != 0); 1; })
+#define num_online_nodes()			1
 
 struct notifier_block;
 
kernel/sys.c +90 -0

96 lines not on this page · raw message

mm/numa.c +8 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.41-vanilla/mm/numa.c linux-2.5.41-memory_binding_api/mm/numa.c
@@ -8,6 +8,7 @@
 #include <linux/bootmem.h>
 #include <linux/mmzone.h>
 #include <linux/spinlock.h>
+#include <linux/membind.h>
 
 int numnodes = 1;	/* Initialized for UMA platforms */
 
@@ -29,6 +30,7 @@
 
 	pgdat = &contig_page_data;
 	contig_page_data.node_id = 0;
+	contig_page_data.memblk_id = 0;
 	contig_page_data.node_start_pfn = node_start_pfn;
 	calculate_totalpages (&contig_page_data, zones_size, zholes_size);
 	if (pmap == (struct page *)0) {
@@ -37,6 +39,7 @@
 	}
 	contig_page_data.node_mem_map = pmap;
 	free_area_init_core(&contig_page_data, zones_size, zholes_size);
+	memblk_online_map = 1UL;
 	mem_map = contig_page_data.node_mem_map;
 }
 
@@ -66,6 +69,7 @@
 	unsigned long size;
 
 	pgdat->node_id = nid;
+	pgdat->memblk_id = __node_to_memblk(nid);
 	pgdat->node_start_pfn = node_start_pfn;
 	calculate_totalpages (pgdat, zones_size, zholes_size);
 	if (pmap == (struct page *)0) {
@@ -74,6 +78,10 @@
 	}
 	pgdat->node_mem_map = pmap;
 	free_area_init_core(pgdat, zones_size, zholes_size);
+	if (test_and_set_bit(num_online_memblks(), &memblk_online_map)){
+		printk("free_area_init_core: memblk alread counted?!?!\n");
+		BUG();
+	}
 
 	/*
 	 * Get space for the valid bitmap.
mm/page_alloc.c +24 -43

109 lines not on this page · raw message

#1 lore raw
Quoted 4 lines · Matthew Dobson

Greetings & Salutations,
Here's a wonderful patch that I know you're all dying for... Memory
Binding!

Seems reasonable to me.

Could you tell us a bit about the operator's view of this?

I assume that a typical usage scenario would be to bind a process to a bunch of CPUs and to then bind that process to a bunch of memblks as well?

If so, then how does the operator know how to identify those memblks? To perform the (cpu list) <-> (memblk list) mapping?

Also, what advantage does this provide over the current node-local allocation policy? I'd have thought that once you'd bound a process to a CPU (or to a node's CPUs) that as long as the zone fallback list was right, that process would be getting local memory pretty much all the time anyway?

Last but not least: you got some benchmark numbers for this?

Thanks.

#2 lore raw
Quoted 5 lines · Andrew Morton
Quoted 3 lines · Matthew Dobson

Greetings & Salutations,
Here's a wonderful patch that I know you're all dying for... Memory
Binding!

Seems reasonable to me.

Good news!

Quoted 8 lines

Could you tell us a bit about the operator's view of this?

I assume that a typical usage scenario would be to bind a process to a bunch of CPUs and to then bind that process to a bunch of memblks as well?

If so, then how does the operator know how to identify those memblks? To perform the (cpu list) <-> (memblk list) mapping?

Well, that's what the super-duper In-Kernel topology API is for! ;) If the operator wanted to ensure that all the processes memory was *only* allocated from the memblks closest to her bound CPUs, she'd loop over her cpu binding, and for each set bit, she'd:

	bitmask &= 1 << __node_to_memblk(__cpu_to_node(cpu));

I suppose that I could include a macro to do this in the patch, but I was a bit afraid (and still am) that it already may be a bit large for people's tastes. I've got some suggestions on how to split it up/pare it down, so we'll see.

Quoted 4 lines

Also, what advantage does this provide over the current node-local
allocation policy? I'd have thought that once you'd bound a process to a CPU (or to a node's CPUs) that as long as the zone fallback list was right, that process would be getting local memory
pretty much all the time anyway?

Very true... This is to specifically allow for processes that want to do something *different* than the default policy. Again, akin to CPU affinity, this is not something that the average process is going to ever use, or even care about. The majority of processes don't specifically bind themselves to certain CPUs or groups of CPUs, because for them the default scheduler policies are fine. For the majority of processes, the default memory allocation policy works just dandy. This is for processes that want to do something different: Testing/debugging apps on a large (likely NUMA) system, high-end databases, and who knows what else? There is also a plan to add a function call to bind specific regions of a processes memory to certain memblks, and this would allow for efficient shared memory for process groups spread across a large system.

Quoted 1 line

Last but not least: you got some benchmark numbers for this?

Nope.. It is not something that is going to (on average) improve benchmark numbers for something like a kernel compile... As you mentioned above, the default policy is to allocate from the local memory block anyway. This API is more useful for something where you want to pin your memory close to a particular process that your process is working with, or pin you memory to a different node than the one you're executing on to purposely test/debug something. If you'd like, I can do some kernbench runs or something to come up with some numbers to show that it doesn't negatively affect performance, but I don't know of any benchmarking suites offhand that would show positive numbers.

Quoted 1 line

Thanks.

My pleasure! ;)

Cheers!

-Matt

#3 lore raw
In reply to Matthew Dobson

include/linux/mmzone.h line 271

Quoted 4 lines
include/linux/mmzone.h +3 -0
+#define for_each_valid_zone(zone, zonelist) 		\
+	for (zone = *zonelist->zones; zone; zone++)	\
+		if (current->memblk_binding.bitmask & (1 << zone->zone_pgdat->memblk_id))

Does the compiler optimise the last bit away on non-NUMA? Want to wrap it in #ifdef CONFIG_NUMA_MEMBIND or something? Not sure what the speed impact of this would be, but I'd rather it was optional, even on NUMA boxen.

Other than that, looks pretty good.

M.

#4 lore raw
Quoted 5 lines · Martin J. Bligh

include/linux/mmzone.h line 271

Quoted 3 lines
include/linux/mmzone.h +3 -0
+#define for_each_valid_zone(zone, zonelist) 		\
+	for (zone = *zonelist->zones; zone; zone++)	\
+		if (current->memblk_binding.bitmask & (1 << zone->zone_pgdat->memblk_id))

Does the compiler optimise the last bit away on non-NUMA?

Nope.

Quoted 1 line

Want to wrap it in #ifdef CONFIG_NUMA_MEMBIND or something?

Not a problem... I've got some free time this afternoon... Should only take me a few hours to retool the patch to include this change. ;)

Quoted 2 lines

Not sure what the speed impact of this would be, but I'd rather it was optional, even on NUMA boxen.

Sounds reasonable... It'll be in the next itteration.

Quoted 1 line

Other than that, looks pretty good.

Glad to hear!

Quoted 1 line

M.

#5 lore raw
In reply to Matthew Dobson
Quoted 5 lines · Matthew Dobson

Greetings & Salutations,
Here's a wonderful patch that I know you're all dying for... Memory
Binding! It works just like CPU Affinity (binding) except that it binds a processes memory allocations (just buddy allocator for now) to specific memory blocks.

If the VM works right just doing CPU binding ought to be enough, surely?

#6 lore raw
Quoted 7 lines · Arjan van de Ven
Quoted 3 lines · Matthew Dobson

Greetings & Salutations,
Here's a wonderful patch that I know you're all dying for... Memory
Binding! It works just like CPU Affinity (binding) except that it binds a processes memory allocations (just buddy allocator for now) to specific memory blocks.

If the VM works right just doing CPU binding ought to be enough, surely?

You'll have to look at the response I wrote to Andrew's question along the same lines... This patch is for processes who feel that the VM *isn't* doing quite what they want, and want different behavior.

Cheers!

-Matt

#7 lore raw
In reply to Matthew Dobson

kernel/sys.c line 1314

Quoted 10 lines
kernel/sys.c +9 -0
+/**
+ * sys_mem_setbinding - set the memory binding of a process
+ * @pid: pid of the process
+ * @memblks: new bitmask of memory blocks
+ * @behavior: new behavior
+ */
+asmlinkage long sys_mem_setbinding(pid_t pid, unsigned long memblks, 
+				    unsigned int behavior)
+{

Do you really think exposing low level internals as memory layout / zone split up to userspace is a good idea ? (and worth it given that the VM already has a cpu locality preference?)

I'd much rather see the VM have an arch-specified "cost" for getting memory from not-the-prefered zones than exposing all this stuff to userspace and depending on userspace to do the right thing.... it's the kernel's task to abstract the low level details of the hardware after all.

Greetings,
Arjan van de Ven

#8 lore raw
Quoted 14 lines · Arjan van de Ven

kernel/sys.c line 1314

Quoted 10 lines
kernel/sys.c +9 -0
+/**
+ * sys_mem_setbinding - set the memory binding of a process
+ * @pid: pid of the process
+ * @memblks: new bitmask of memory blocks
+ * @behavior: new behavior
+ */
+asmlinkage long sys_mem_setbinding(pid_t pid, unsigned long memblks, 
+				    unsigned int behavior)
+{

Do you really think exposing low level internals as memory layout / zone split up to userspace is a good idea ? (and worth it given that the VM already has a cpu locality preference?)

At least in the embedded world that level is a good idea. I'm not sure about the syscall interface. An "unsigned long" mask of blocks sounds like a good way to ensure a broken syscall in the future

#9 lore raw
Quoted 2 lines · Matthew Dobson
Quoted 2 lines
Quoted 2 lines
kernel/sys.c +2 -0
+asmlinkage long sys_mem_setbinding(pid_t pid, unsigned long memblks, 
+				    unsigned int behavior)
Quoted 3 lines · Arjan van de Ven
Quoted 3 lines

Do you really think exposing low level internals as memory layout / zone split up to userspace is a good idea ? (and worth it given that the VM already has a cpu locality preference?)

Quoted 3 lines · Alan Cox

At least in the embedded world that level is a good idea. I'm not sure about the syscall interface. An "unsigned long" mask of blocks sounds like a good way to ensure a broken syscall in the future

Seconded wrt. memblk bitmask interface.

IMHO this level of topology exposure is not inappropriate. These kinds of details are already exported (unidirectionally) by /proc/, if not dmesg(1) and in my mind there is neither an aesthetic nor practical barrier to referring to these machine features in userspace API's in an advisory way (and absolutely not any kind of reliance). It's simply another kind of request, and one which several high-performance applications would like to make. I would also be interested in hearing more of how embedded applications would make use of this, as my direct experience in embedded systems is somewhat lacking.

Also, I've already privately replied with some of my stylistic concerns, including things like the separability of the for_each_in_zonelist() cleanup bundled into the patch and a typedef or so.

Bill

#10 lore raw
Quoted 13 lines · William Lee Irwin III
Quoted 3 lines · Matthew Dobson
Quoted 3 lines
Quoted 2 lines
kernel/sys.c +2 -0
+asmlinkage long sys_mem_setbinding(pid_t pid, unsigned long memblks, 
+				    unsigned int behavior)
Quoted 4 lines · Arjan van de Ven
Quoted 3 lines

Do you really think exposing low level internals as memory layout / zone split up to userspace is a good idea ? (and worth it given that the VM already has a cpu locality preference?)

Quoted 6 lines
Quoted 3 lines · Alan Cox

At least in the embedded world that level is a good idea. I'm not sure about the syscall interface. An "unsigned long" mask of blocks sounds like a good way to ensure a broken syscall in the future

Seconded wrt. memblk bitmask interface.

Glad to have your support! :)

Quoted 3 lines

Also, I've already privately replied with some of my stylistic concerns, including things like the separability of the for_each_in_zonelist() cleanup bundled into the patch and a typedef or so.

Some very good points in your email. Most (if not all) will be incorporated in v0.4 (later today or tomorrow).

Cheers!

-Matt

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

#11 lore raw
In reply to Alan Cox
Quoted 16 lines · Alan Cox
Quoted 5 lines · Arjan van de Ven
Quoted 5 lines

+/**
+ * sys_mem_setbinding - set the memory binding of a process
+ * @pid: pid of the process
+ * @memblks: new bitmask of memory blocks
+ * @behavior: new behavior

Quoted 8 lines

kernel/sys.c line 1314

Quoted 4 lines

+ */

kernel/sys.c +3 -0
+asmlinkage long sys_mem_setbinding(pid_t pid, unsigned long memblks, 
+				    unsigned int behavior)
+{

Do you really think exposing low level internals as memory layout / zone split up to userspace is a good idea ? (and worth it given that the VM already has a cpu locality preference?)

Quoted 3 lines

At least in the embedded world that level is a good idea. I'm not sure about the syscall interface. An "unsigned long" mask of blocks sounds like a good way to ensure a broken syscall in the future

Agreed. This is a first pass (well 3rd, but the first two were long ago), and I'll probably immitate the sys_sched_(s|g)etaffinity calls (even more than I already have ;) and add a 'length' argument in the next itteration.

Cheers!

-Matt

#12 lore raw
In reply to Arjan van de Ven
Quoted 5 lines · Arjan van de Ven
Quoted 5 lines

+/**
+ * sys_mem_setbinding - set the memory binding of a process
+ * @pid: pid of the process
+ * @memblks: new bitmask of memory blocks
+ * @behavior: new behavior

Quoted 8 lines

kernel/sys.c line 1314

Quoted 4 lines

+ */

kernel/sys.c +3 -0
+asmlinkage long sys_mem_setbinding(pid_t pid, unsigned long memblks, 
+				    unsigned int behavior)
+{

Do you really think exposing low level internals as memory layout / zone split up to userspace is a good idea ? (and worth it given that the VM already has a cpu locality preference?)

Yes, I actually do. If userspace processes/users don't care about the memory/zone layout, they don't have to look. But if they *do* care, they should be able to find out, and not be left in the proverbial dark.
Embedded systems will care, as will really large NUMA/Discontig
systems. As with some other patches, this functionality will not affect the average user on the average computer... They are useful for really small systems, and really large systems, however.

Quoted 5 lines

I'd much rather see the VM have an arch-specified "cost" for getting memory from not-the-prefered zones than exposing all this stuff to userspace and depending on userspace to do the right thing.... it's the kernel's task to abstract the low level details of the hardware after all.

The arch-specified 'cost' is also a good idea. Some of the topology stuff I'm working on will allow that sort of interface as well. And this patch does not 'depend' on userspace to do the right thing. The patch does not alter the default VM behavior unless userspace *specifically* asks to alter it.

Cheers!

-Matt

#13 lore raw
In reply to Matthew Dobson
Quoted 7 lines · Matthew Dobson

Greetings & Salutations,
Here's a wonderful patch that I know you're all dying for... Memory
Binding! It works just like CPU Affinity (binding) except that it binds a processes memory allocations (just buddy allocator for now) to specific memory blocks.
I've sent this out in the past, but haven't touched it in months. Since

Quoted 8 lines

the feature freeze is rapidly approaching, I want to get this out there again and see if anyone has any interest in it.
It's a fairly large patch, mostly because it includes a few odds and
ends that are topology related, and don't strictly belong in this patch, but are

pre-requisites for it (ie: the [memblk|node]_online_map stuff, and some of the cleanups to page_alloc). I'll probably try and break it up into more discrete parts very soon.

Due we want this per numa area or simply per zone? My suspicion is that internally at least we want this per zone.

Quoted 5 lines

Questions, comments, flames, and indifferent shrugs are all welcome.

btw, It applies (mostly) cleanly to mm1 as well. The mm/page_alloc.c changes fail, but if anyone is interested, they'll clean up easily, and I'll send you a patch.

The API doesn't make much sense at the moment.

1) You are operating on tasks and not mm's, or preferably vmas.
2) sys_mem_setbinding does not move the mm to the new binding.
3) You specify a pid and then change current task instead of the specified one.

4) An ordered zone list is probably the more natural mapping.
5) mprotect is the more natural model rather than set_cpu_affinity.
6) The code belongs in mm/* not kernel/*

Eric

#14 lore raw
Quoted 8 lines · Eric W. Biederman
Quoted 5 lines · Matthew Dobson

Greetings & Salutations,
Here's a wonderful patch that I know you're all dying for... Memory
Binding! It works just like CPU Affinity (binding) except that it binds a processes memory allocations (just buddy allocator for now) to specific memory blocks.

Due we want this per numa area or simply per zone? My suspicion is that internally at least we want this per zone.

I think that per memory block is better. We already have a method for allocating from specific zones (GFP_* flags). Also, using per zone binding would involve setting up some way of enumerating the zones, which would not be immediately obvious to the users of the API. The memory block already has a straight-forward definition and an easy way for users to get the appropriate number for the appropriate block (in-kernel topology). I'm not fanatically opposed to per zone binding, though, and if there is a general agreement that it would be better that way, I don't think it would be unreasonably difficult to change it.

Quoted 1 line

The API doesn't make much sense at the moment.

Hmm.. That is unfortunate, I'd aimed to make it as simple as possible.

Quoted 1 line

1) You are operating on tasks and not mm's, or preferably vmas.

Correct. There are plans (somewhere inside my cranium) to allow binding at that granularity. For now, per task seemed an appropriate level.

Quoted 1 line

2) sys_mem_setbinding does not move the mm to the new binding.

Also correct. A task may wish to allocate several large data structures from one memory area, rebind, do more allocations, rebind, ad nauseum. There are plans to have a flag that, if set, would force relocation of all currently allocated memory.

Quoted 2 lines

3) You specify a pid and then change current task instead of the specified one.

Yep... That was definitely a typo... fixed.

Quoted 1 line

4) An ordered zone list is probably the more natural mapping.

See my comments above about per zone/memblk. And you reemphasize my point, how do we order the zone lists in such a way that a user of the API can easily know/find out what zone #5 is?

Quoted 1 line

5) mprotect is the more natural model rather than set_cpu_affinity.

Well, I think that may be true for the API you are imagining (per zone, per mm/vma, etc), not the one that I've written.

Quoted 1 line

6) The code belongs in mm/* not kernel/*

Possibly... I just stuck it in with the vast majority of other syscalls in kernel/sys.c. As those changes are just code additions, they can easily be moved if it is deemed appropriate.

Cheers!

-Matt

#15 lore raw
Quoted 3 lines
Quoted 2 lines

4) An ordered zone list is probably the more natural mapping.

See my comments above about per zone/memblk. And you reemphasize my point, how do we order the zone lists in such a way that a user of the API can easily know/find out what zone #5 is?

Could you explain how that problem is different from finding out what memblk #5 is ... I don't see the difference?

M.

#16 lore raw
Quoted 5 lines · Martin J. Bligh
Quoted 3 lines
Quoted 1 line

4) An ordered zone list is probably the more natural mapping.

See my comments above about per zone/memblk. And you reemphasize my point, how do we order the zone lists in such a way that a user of the API can easily know/find out what zone #5 is?

Could you explain how that problem is different from finding out what memblk #5 is ... I don't see the difference?

Errm... __memblk_to_node(5)

I"m not saying that we couldn't add a similar interface for zones... something along the lines of:

	__memblk_and_zone_to_flat_zone_number(5, DMA)

or some such. It just isn't there now...

Also, right now, memblks map to nodes in a straightforward manner (1-1 on NUMA-Q, the only architecture that has defined them). It will likely look the same on most architectures, too.

Cheers!

-Matt

#17 lore raw
Quoted 6 lines
Quoted 5 lines
Quoted 3 lines
Quoted 1 line

4) An ordered zone list is probably the more natural mapping.

See my comments above about per zone/memblk. And you reemphasize my point, how do we order the zone lists in such a way that a user of the API can easily know/find out what zone #5 is?

Could you explain how that problem is different from finding out what memblk #5 is ... I don't see the difference?

Errm... __memblk_to_node(5)

As opposed to creating __zone_to_node(5) ?

Quoted 3 lines

I"m not saying that we couldn't add a similar interface for zones... something along the lines of:

	__memblk_and_zone_to_flat_zone_number(5, DMA)

or some such. It just isn't there now...

Surely this would dispose of the need for memblks? If not, then I'd agree it's probably just adding more complication.

M.

#18 lore raw
Quoted 9 lines · Martin J. Bligh
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
Quoted 1 line

4) An ordered zone list is probably the more natural mapping.

See my comments above about per zone/memblk. And you reemphasize my point, how do we order the zone lists in such a way that a user of the API can easily know/find out what zone #5 is?

Could you explain how that problem is different from finding out what memblk #5 is ... I don't see the difference?

Errm... __memblk_to_node(5)

Quoted 8 lines

As opposed to creating __zone_to_node(5) ?

Quoted 3 lines

I"m not saying that we couldn't add a similar interface for zones... something along the lines of:

	__memblk_and_zone_to_flat_zone_number(5, DMA)

or some such. It just isn't there now...

Surely this would dispose of the need for memblks? If not, then I'd agree it's probably just adding more complication.

Well, since each node's memory (or memblk in the parlance of my head ;) has several 'zones' in it (DMA, HIGHMEM, etc), this conversion function will need 2 parameters. It may well be called __node_and_zone_type_to_flat_zone_number(node, DMA|NORMAL|HIGHMEM).

Or, we could have:
__zone_to_node(5) = node #
and
__zone_to_zone_type(5) = DMA|NORMAL|HIGHMEM.

But either way, we would need to specify both pieces.

Cheers!

-Matt

#19 lore raw
Quoted 11 lines · Matthew Dobson

Well, since each node's memory (or memblk in the parlance of my head ;) has several 'zones' in it (DMA, HIGHMEM, etc), this conversion function will need 2 parameters. It may well be called __node_and_zone_type_to_flat_zone_number(node, DMA|NORMAL|HIGHMEM). Or, we could have:
__zone_to_node(5) = node #
and
__zone_to_zone_type(5) = DMA|NORMAL|HIGHMEM.
But either way, we would need to specify both pieces. Cheers!
-Matt

Zone "type" can be found in (page->flags >> ZONE_SHIFT) & 0x3UL and similarly node ID can be found in page_zone(page)->zone_pgdat->node_id and these are from the page.

zone->zone_pgdat->node_id does the zone to node conversion zone - zone_pgdat->node_zones does the zone to zone type conversion.

Node and zone type to flat zone number would be

	NODE_DATA(nid)->node_zones[type]

Basically there's a number written in page->flags that should be easy to decode if you can go on arithmetic alone, and if you need details, there's a zone_table[] you can get at the zones (and hence pgdats) with.

Bill
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/

#20 lore raw
In reply to Matthew Dobson
Quoted 3 lines · Matthew Dobson

Also, right now, memblks map to nodes in a straightforward manner (1-1 on NUMA-Q, the only architecture that has defined them). It will likely look the same on most architectures, too.

Just an FYI: I believe the x440 breaks this assumption.

There are 2 chunks on the first CEC. The current discontig patch for it has to drop the second chunk (anything over 3.5G on the first CEC) in order to work w/ the existing code. However, that will probably need to be addressed at some point, so be aware that this might affect you as well.

thanks
-john

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

#21 lore raw
Quoted 3 lines · Matthew Dobson
Quoted 3 lines

Also, right now, memblks map to nodes in a straightforward manner (1-1 on NUMA-Q, the only architecture that has defined them). It will likely look the same on most architectures, too.

Quoted 6 lines · john stultz

Just an FYI: I believe the x440 breaks this assumption. There are 2 chunks on the first CEC. The current discontig patch for it has to drop the second chunk (anything over 3.5G on the first CEC) in order to work w/ the existing code. However, that will probably need to be addressed at some point, so be aware that this might affect you as well.

MAP_NR_DENSE()-based zone-relative pfn to zone->zone_mem_map index remapping is designed to handle this (and actually more severe situations). The only constraint is that pfn's must be monotonically increasing with ->zone_mem_map index. Some non-i386 architectures virtually remap physical memory to provide the illusion of contiguity of kernel virtual memory, but in a mature port (e.g. i386) there's high risk of breaking numerous preexisting drivers.

Bill

#22 lore raw
Quoted 7 lines

MAP_NR_DENSE()-based zone-relative pfn to zone->zone_mem_map index remapping is designed to handle this (and actually more severe situations). The only constraint is that pfn's must be monotonically increasing with ->zone_mem_map index. Some non-i386 architectures virtually remap physical memory to provide the illusion of contiguity of kernel virtual memory, but in a mature port (e.g. i386) there's high risk of breaking numerous preexisting drivers.

As long as you don't need a hole between 0 and 896Mb (s/896/ appropriate defines/) I don't see that would be a problem. I purged direct usage of mem_map already, and made people use the macro wrappers.

Basically a "mini config_nonlinear".

M.

#23 lore raw
In reply to john stultz
Quoted 11 lines
Quoted 3 lines

Also, right now, memblks map to nodes in a straightforward manner (1-1 on NUMA-Q, the only architecture that has defined them). It will likely look the same on most architectures, too.

Just an FYI: I believe the x440 breaks this assumption.

There are 2 chunks on the first CEC. The current discontig patch for it has to drop the second chunk (anything over 3.5G on the first CEC) in order to work w/ the existing code. However, that will probably need to be addressed at some point, so be aware that this might affect you as well.

No, the NUMA code in the kernel doesn't support that anyway. You have to use zholes_size, and waste some struct pages, or config_nonlinear. Either way you get 1 memblk.

M.

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

#24 lore raw
Quoted 6 lines · jstultz
Quoted 6 lines

Just an FYI: I believe the x440 breaks this assumption. There are 2 chunks on the first CEC. The current discontig patch for it has to drop the second chunk (anything over 3.5G on the first CEC) in order to work w/ the existing code. However, that will probably need to be addressed at some point, so be aware that this might affect you as well.

Quoted 3 lines · Martin J. Bligh

No, the NUMA code in the kernel doesn't support that anyway. You have to use zholes_size, and waste some struct pages, or config_nonlinear. Either way you get 1 memblk.

I thought zholes stuff freed the struct pages. Maybe that was done by hand.

Bill

#25 lore raw
Quoted 6 lines
Quoted 3 lines

No, the NUMA code in the kernel doesn't support that anyway. You have to use zholes_size, and waste some struct pages, or config_nonlinear. Either way you get 1 memblk.

I thought zholes stuff freed the struct pages. Maybe that was done by hand.

The only place I see that used in generic code is calculate_zone_totalpages, free_area_init_core, free_area_init_node, none of which seem to do that. But cscope might be borked again, I guess. Must be done in each arch if at all ... which arch did you think did it?

M.

#26 lore raw
Quoted 2 lines · At some point in the past, I wrote:
Quoted 2 lines

I thought zholes stuff freed the struct pages. Maybe that was done by hand.

Quoted 5 lines · Martin J. Bligh

The only place I see that used in generic code is calculate_zone_totalpages, free_area_init_core, free_area_init_node, none of which seem to do that. But cscope might be borked again, I guess. Must be done in each arch if at all ... which arch did you think did it?

Not sure, ISTR something about this going on but I don't see any extant examples. At any rate, it should be easy to do it by hand, just make sure there are struct pages tracking the holes in mem_map and free the space in mem_map that would track the holes.

Bill

#27 lore raw
Quoted 5 lines · Martin J. Bligh
Quoted 5 lines

The only place I see that used in generic code is calculate_zone_totalpages, free_area_init_core, free_area_init_node, none of which seem to do that. But cscope might be borked again, I guess. Must be done in each arch if at all ... which arch did you think did it?

Quoted 4 lines · William Lee Irwin III

Not sure, ISTR something about this going on but I don't see any extant examples. At any rate, it should be easy to do it by hand, just make sure there are struct pages tracking the holes in mem_map and free the space in mem_map that would track the holes.

And buddy bitmaps and ->node_valid_addr too. Which stands a good chance of explaining why it broke, if it ever worked.

Bill

#28 lore raw
In reply to Matthew Dobson
Quoted 11 lines · Matthew Dobson
Quoted 9 lines · Eric W. Biederman
Quoted 6 lines · Matthew Dobson

Greetings & Salutations,
Here's a wonderful patch that I know you're all dying for... Memory
Binding! It works just like CPU Affinity (binding) except that it binds a processes memory allocations (just buddy allocator for now) to specific memory

blocks.

Due we want this per numa area or simply per zone? My suspicion is that internally at least we want this per zone.

I think that per memory block is better.

[snip]

Quoted 4 lines

I'm not fanatically
opposed to per zone binding, though, and if there is a general agreement that it would be better that way, I don't think it would be unreasonably difficult to change it.

My only feeling with zones is that it could be useful in the non numa cases, if it was per zone.

But unless this API becomes is a pure hint we need at least one specifier that says writing to swap is o.k.

Quoted 2 lines
Quoted 1 line

The API doesn't make much sense at the moment.

Hmm.. That is unfortunate, I'd aimed to make it as simple as possible.

Simple is good only if the proper pieces are connected.

Quoted 4 lines
Quoted 1 line

1) You are operating on tasks and not mm's, or preferably vmas.

Correct. There are plans (somewhere inside my cranium) to allow binding at that

granularity. For now, per task seemed an appropriate level.

It makes it terribly unpredictable. If you have two threads each bound to a different location there are race conditions which area the memory is allocated from.

Quoted 5 lines
Quoted 1 line

2) sys_mem_setbinding does not move the mm to the new binding.

Also correct. A task may wish to allocate several large data structures from one memory area, rebind, do more allocations, rebind, ad nauseum. There are plans to have a flag that, if set, would force relocation of all currently allocated memory.

Actually the bindings need to stick to the vma or to the struct address_space. Otherwise you are talking about an allocation hint, as swapping can trivially undue it and nothing happens when the actual call is made. A hint is a very different thing from a binding.

And if we stick this to struct address_space for the non anonymous cases having a fmem_setbinding(struct fd) that works on files would be a useful thing as well.

Quoted 3 lines
Quoted 1 line

5) mprotect is the more natural model rather than set_cpu_affinity.

Well, I think that may be true for the API you are imagining (per zone, per mm/vma, etc), not the one that I've written.

For a binding with respect to memory I imagine things like mlock(). For anything else you are talking a future hint to the memory allocators, which feels less much useful.

Eric