Skip to contentSkip to conversation

Memory Binding Take 2 (0/1)

RFC PATCH In review Matthew Dobson 5 participants 9 messages Apr 3, 2003 – Apr 4, 2003

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

RFC PATCH 1/1 Memory Binding Take 2 (0/1)

Alrighty, let's give this another go...

This patch hasn't changed much. Just added bitmap_t, typedef'd to unsigned long * for passing around bitmaps without breaking the abstraction. I think it's good if we can keep the underlying data type hidden to partially future-proof (protect? ;) the code.

Part 1/1 coming up...

Cheers!

-Matt

include/linux/gfp.h +1 -1
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-vanilla/include/linux/gfp.h linux-2.5.66-pre_membind/include/linux/gfp.h
@@ -52,7 +52,7 @@
 	if (unlikely(order >= MAX_ORDER))
 		return NULL;
 
-	return __alloc_pages(gfp_mask, order, NODE_DATA(nid)->node_zonelists + (gfp_mask & GFP_ZONEMASK));
+	return __alloc_pages(gfp_mask, order, get_node_zonelist(nid, gfp_mask));
 }
 
 #define alloc_pages(gfp_mask, order) \
include/linux/mmzone.h +8 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-vanilla/include/linux/mmzone.h linux-2.5.66-pre_membind/include/linux/mmzone.h
@@ -326,6 +326,14 @@
 #define num_online_memblks()		1
 
 #endif /* CONFIG_DISCONTIGMEM || CONFIG_NUMA */
+
+static inline struct zonelist *get_node_zonelist(int nid, int gfp_mask)
+{
+	return NODE_DATA(nid)->node_zonelists + (gfp_mask & GFP_ZONEMASK);
+}
+
+#define get_zonelist(gfp_mask) get_node_zonelist(numa_node_id(), gfp_mask)
+
 #endif /* !__ASSEMBLY__ */
 #endif /* __KERNEL__ */
 #endif /* _LINUX_MMZONE_H */
include/linux/types.h +1 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-vanilla/include/linux/types.h linux-2.5.66-pre_membind/include/linux/types.h
@@ -10,6 +10,7 @@
 	unsigned long name[BITS_TO_LONGS(bits)]
 #define CLEAR_BITMAP(name,bits) \
 	memset(name, 0, BITS_TO_LONGS(bits)*sizeof(unsigned long))
+typedef unsigned long * bitmap_t;
 #endif
 
 #include <linux/posix_types.h>
#1 lore raw

RFC PATCH 1/1 Memory Binding Take 2 (1/1)

Now for the good stuff! ;)

This one has had more changes... I've changed the syscall from sys_membind to sys_mbind. I liked Paolo's suggestion of aligning the naming. I've fixed up the way the bitmaps are passed. I pulled out all the ZONE_* code, and now just have it use all the zones on the node. I made sure that the binding pointers are not compiled in for non-NUMA kernels. All that is added for non-NUMA kernels is the cond_syscall and a small change in the page_cache_alloc callpath. Now page_cache_alloc calls __page_cache_alloc, which is just the old page_cache_alloc for non-NUMA. For NUMA, it's obviously a different function. I also cleaned up the bitmask size issue, by just making sure userspace doesn't pass in a bitmask that's way too large.

I guess that's it... As always, I'm looking forward to any comments!

Cheers!

-Matt

arch/i386/kernel/entry.S +1 -1
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-pre_membind/arch/i386/kernel/entry.S linux-2.5.66-membind/arch/i386/kernel/entry.S
@@ -807,7 +807,7 @@
 	.long sys_getdents64	/* 220 */
 	.long sys_fcntl64
 	.long sys_ni_syscall	/* reserved for TUX */
-	.long sys_ni_syscall
+ 	.long sys_mbind
 	.long sys_gettid
 	.long sys_readahead	/* 225 */
 	.long sys_setxattr
fs/inode.c +3 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-pre_membind/fs/inode.c linux-2.5.66-membind/fs/inode.c
@@ -144,6 +144,9 @@
 		mapping->dirtied_when = 0;
 		mapping->assoc_mapping = NULL;
 		mapping->backing_dev_info = &default_backing_dev_info;
+#ifdef CONFIG_NUMA
+		mapping->binding = NULL;
+#endif
 		if (sb->s_bdev)
 			mapping->backing_dev_info = sb->s_bdev->bd_inode->i_mapping->backing_dev_info;
 		memset(&inode->u, 0, sizeof(inode->u));
include/asm-i386/unistd.h +1 -1
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-pre_membind/include/asm-i386/unistd.h linux-2.5.66-membind/include/asm-i386/unistd.h
@@ -228,7 +228,7 @@
 #define __NR_madvise1		219	/* delete when C lib stub is removed */
 #define __NR_getdents64		220
 #define __NR_fcntl64		221
-/* 223 is unused */
+#define __NR_mbind		223
 #define __NR_gettid		224
 #define __NR_readahead		225
 #define __NR_setxattr		226
include/linux/fs.h +4 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-pre_membind/include/linux/fs.h linux-2.5.66-membind/include/linux/fs.h
@@ -19,6 +19,7 @@
 #include <linux/cache.h>
 #include <linux/radix-tree.h>
 #include <linux/kobject.h>
+#include <linux/mbind.h>
 #include <asm/atomic.h>
 
 struct iovec;
@@ -329,6 +330,9 @@
 	spinlock_t		private_lock;	/* for use by the address_space */
 	struct list_head	private_list;	/* ditto */
 	struct address_space	*assoc_mapping;	/* ditto */
+#ifdef CONFIG_NUMA
+	struct binding		*binding;	/* for memory bindings */
+#endif
 };
 
 struct block_device {
include/linux/mbind.h +40 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-pre_membind/include/linux/mbind.h linux-2.5.66-membind/include/linux/mbind.h
@@ -0,0 +1,40 @@
+/*
+ * include/linux/mbind.h
+ *
+ * Written by: Matthew Dobson, IBM Corporation
+ *
+ * Copyright (C) 2003, IBM Corp.
+ *
+ * All rights reserved.          
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <[email protected]>
+ */
+#ifndef _LINUX_MBIND_H
+#define _LINUX_MBIND_H
+
+#ifdef CONFIG_NUMA
+
+#include <linux/mmzone.h>
+
+/* Structure to keep track of memory segment (VMA) bindings */
+struct binding {
+	struct zonelist	zonelist;
+};
+
+#endif /* CONFIG_NUMA */
+#endif /* _LINUX_MBIND_H */
include/linux/pagemap.h +26 -2
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-pre_membind/include/linux/pagemap.h linux-2.5.66-membind/include/linux/pagemap.h
@@ -8,6 +8,7 @@
 #include <linux/fs.h>
 #include <linux/list.h>
 #include <linux/highmem.h>
+#include <linux/mbind.h>
 #include <asm/uaccess.h>
 
 /*
@@ -27,14 +28,37 @@
 #define page_cache_release(page)	put_page(page)
 void release_pages(struct page **pages, int nr, int cold);
 
+#ifndef CONFIG_NUMA
+
+static inline struct page *__page_cache_alloc(struct address_space *x, int gfp_mask)
+{
+	return alloc_pages(gfp_mask, 0);
+}
+
+#else /* CONFIG_NUMA */
+
+static inline struct page *__page_cache_alloc(struct address_space *x, int gfp_mask)
+{
+	struct zonelist *zonelist;
+
+	if (!x->binding)
+		zonelist = get_zonelist(gfp_mask);
+	else
+		zonelist = &x->binding->zonelist;
+
+	return __alloc_pages(gfp_mask, 0, zonelist);
+}
+
+#endif /* !CONFIG_NUMA */
+
 static inline struct page *page_cache_alloc(struct address_space *x)
 {
-	return alloc_pages(x->gfp_mask, 0);
+	return __page_cache_alloc(x, x->gfp_mask);
 }
 
 static inline struct page *page_cache_alloc_cold(struct address_space *x)
 {
-	return alloc_pages(x->gfp_mask|__GFP_COLD, 0);
+	return __page_cache_alloc(x, x->gfp_mask|__GFP_COLD);
 }
 
 typedef int filler_t(void *, struct page *);
kernel/sys.c +1 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-pre_membind/kernel/sys.c linux-2.5.66-membind/kernel/sys.c
@@ -226,6 +226,7 @@
 cond_syscall(sys_sendmsg)
 cond_syscall(sys_recvmsg)
 cond_syscall(sys_socketcall)
+cond_syscall(sys_mbind)
 
 static int set_one_prio(struct task_struct *p, int niceval, int error)
 {
mm/Makefile +3 -1
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-pre_membind/mm/Makefile linux-2.5.66-membind/mm/Makefile
@@ -7,8 +7,10 @@
 			   mlock.o mmap.o mprotect.o mremap.o msync.o rmap.o \
 			   shmem.o vmalloc.o
 
-obj-y			:= bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \
+obj-y			:= bootmem.o fadvise.o filemap.o mempool.o oom_kill.o \
 			   page_alloc.o page-writeback.o pdflush.o readahead.o \
 			   slab.o swap.o truncate.o vcache.o vmscan.o $(mmu-y)
 
 obj-$(CONFIG_SWAP)	+= page_io.o swap_state.o swapfile.o
+
+obj-$(CONFIG_NUMA)	+= mbind.o
mm/mbind.c +131 -0

131 lines not on this page · raw message

mm/swap_state.c +3 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-pre_membind/mm/swap_state.c linux-2.5.66-membind/mm/swap_state.c
@@ -47,6 +47,9 @@
 	.i_shared_sem	= __MUTEX_INITIALIZER(swapper_space.i_shared_sem),
 	.private_lock	= SPIN_LOCK_UNLOCKED,
 	.private_list	= LIST_HEAD_INIT(swapper_space.private_list),
+#ifdef CONFIG_NUMA
+	.binding	= NULL,
+#endif
 };
 
 #define INC_CACHE_INFO(x)	do { swap_cache_info.x++; } while (0)
#2 lore raw

Re: [rfc][patch] Memory Binding Take 2 (1/1)

Quoted 2 lines · Matthew Dobson

+#define __NR_mbind 223

What was wrong with "membind"?

mm/mbind.c line 40

Quoted 7 lines
mm/mbind.c +7 -0
+/* Translate a cpumask to a nodemask */
+static inline void cpumask_to_nodemask(bitmap_t cpumask, bitmap_t nodemask)
+{
+	int i;
+
+	for (i = 0; i < NR_CPUS; i++)
+		if (test_bit(i, cpumask))

That's a bit weird. test_bit is only permitted on longs, so why introduce bitmap_t?

Quoted 15 lines
mm/mbind.c +15 -0
+/* Top-level function for allocating a binding for a region of memory */
+static inline struct binding *alloc_binding(bitmap_t nodemask)
+{
+	struct binding *binding;
+	int node, zone_num;
+
+	binding = (struct binding *)kmalloc(sizeof(struct binding), GFP_KERNEL);
+	if (!binding)
+		return NULL;
+	memset(binding, 0, sizeof(struct binding));
+
+	/* Build binding zonelist */
+	for (node = 0, zone_num = 0; node < MAX_NUMNODES; node++)
+		if (test_bit(node, nodemask) && node_online(node))
+			zone_num = add_node(NODE_DATA(node), 

mm/mbind.c line 85

Quoted 10 lines
mm/mbind.c +10 -0
+				&binding->zonelist, zone_num);
+	binding->zonelist.zones[zone_num] = NULL;
+
+	if (zone_num == 0) {
+		/* No zones were added to the zonelist.  Let the caller know. */
+		kfree(binding);
+		binding = NULL;
+	}
+	return binding;
+} 

It looks like this function needs to be able to return a real errno (see below).

mm/mbind.c line 101

Quoted 5 lines
mm/mbind.c +5 -0
+asmlinkage unsigned long sys_mbind(unsigned long start, unsigned long len, 
+		unsigned long *mask_ptr, unsigned int mask_len, unsigned long policy)
+{
+	DECLARE_BITMAP(cpu_mask, NR_CPUS);
+	DECLARE_BITMAP(node_mask, MAX_NUMNODES);

Bah. Who cooked that up? It should be DEFINE_BITMAP. Oh well.

Quoted 16 lines
mm/mbind.c +16 -0
+	struct vm_area_struct *vma = NULL;
+	struct address_space *mapping;
+	int copy_len, error = 0;
+
+	/* Deal with getting cpu_mask from userspace & translating to node_mask */
+	copy_len = min(mask_len, (unsigned int)NR_CPUS);
+	CLEAR_BITMAP(cpu_mask, NR_CPUS);
+	CLEAR_BITMAP(node_mask, MAX_NUMNODES);
+	if (copy_from_user(cpu_mask, mask_ptr, (copy_len+7)/8)) {
+		error = -EFAULT;
+		goto out;
+	}
+	cpumask_to_nodemask(cpu_mask, node_mask);
+
+	vma = find_vma(current->mm, start);
+	if (!(vma && vma->vm_file && vma->vm_ops && 

mm/mbind.c line 127

Quoted 10 lines
mm/mbind.c +10 -0
+		vma->vm_ops->nopage == shmem_nopage)) {
+		/* This isn't a shm segment.  For now, we bail. */
+		error = -EINVAL;
+		goto out;
+	}
+
+	mapping = vma->vm_file->f_dentry->d_inode->i_mapping;
+	mapping->binding = alloc_binding(node_mask);
+	if (!mapping->binding)
+		error = -EFAULT;

It returns EFAULT on memory exhaustion?

btw, can you remind me again why this is only available to tmpfs pagecache?

#3 lore raw

Re: [rfc][patch] Memory Binding Take 2 (1/1)

Quoted 6 lines · Andrew Morton
Quoted 1 line · Matthew Dobson

+#define __NR_mbind 223

What was wrong with "membind"?

Well, there was nothing wrong with it, per se, I just liked Paolo's suggestion to align the naming with mmap, munmap, mremap, etc. The syscalls that manipulate a processes address space tend to be called m(something). Right now it isn't as generic as I'd like it to be, but all in good time.

Quoted 11 lines

mm/mbind.c line 40

Quoted 7 lines
mm/mbind.c +7 -0
+/* Translate a cpumask to a nodemask */
+static inline void cpumask_to_nodemask(bitmap_t cpumask, bitmap_t nodemask)
+{
+	int i;
+
+	for (i = 0; i < NR_CPUS; i++)
+		if (test_bit(i, cpumask))

That's a bit weird. test_bit is only permitted on longs, so why introduce bitmap_t?

Erm... Good point. I really wanted to try and maintain the abstraction of a bitmap type. I hoped that we could, via macros and typedefs, keep the underlying data type obscured, and have a good facsimile of variable length bitmaps. It's proving too difficult to hide the fact that they're just unsigned long[]'s, so I'll give up the ghost and pass them as unsigned long *'s.

Quoted 20 lines
Quoted 20 lines
mm/mbind.c +20 -0
+/* Top-level function for allocating a binding for a region of memory */
+static inline struct binding *alloc_binding(bitmap_t nodemask)
+{
+	struct binding *binding;
+	int node, zone_num;
+
+	binding = (struct binding *)kmalloc(sizeof(struct binding), GFP_KERNEL);
+	if (!binding)
+		return NULL;
+	memset(binding, 0, sizeof(struct binding));
+
+	/* Build binding zonelist */
+	for (node = 0, zone_num = 0; node < MAX_NUMNODES; node++)
+		if (test_bit(node, nodemask) && node_online(node))
+			zone_num = add_node(NODE_DATA(node), 
+				&binding->zonelist, zone_num);
+	binding->zonelist.zones[zone_num] = NULL;
+
+	if (zone_num == 0) {
+		/* No zones were added to the zonelist.  Let the caller know. */
Quoted 8 lines

mm/mbind.c line 85

Quoted 5 lines
mm/mbind.c +5 -0
+		kfree(binding);
+		binding = NULL;
+	}
+	return binding;
+} 

It looks like this function needs to be able to return a real errno (see below).

True. EFAULT is a sorta decent catchall, but not appropriate for something like no memory, etc.

Quoted 21 lines
Quoted 21 lines
mm/mbind.c +21 -0
+	struct vm_area_struct *vma = NULL;
+	struct address_space *mapping;
+	int copy_len, error = 0;
+
+	/* Deal with getting cpu_mask from userspace & translating to node_mask */
+	copy_len = min(mask_len, (unsigned int)NR_CPUS);
+	CLEAR_BITMAP(cpu_mask, NR_CPUS);
+	CLEAR_BITMAP(node_mask, MAX_NUMNODES);
+	if (copy_from_user(cpu_mask, mask_ptr, (copy_len+7)/8)) {
+		error = -EFAULT;
+		goto out;
+	}
+	cpumask_to_nodemask(cpu_mask, node_mask);
+
+	vma = find_vma(current->mm, start);
+	if (!(vma && vma->vm_file && vma->vm_ops && 
+		vma->vm_ops->nopage == shmem_nopage)) {
+		/* This isn't a shm segment.  For now, we bail. */
+		error = -EINVAL;
+		goto out;
+	}
Quoted 8 lines

mm/mbind.c line 127

Quoted 5 lines
mm/mbind.c +5 -0
+
+	mapping = vma->vm_file->f_dentry->d_inode->i_mapping;
+	mapping->binding = alloc_binding(node_mask);
+	if (!mapping->binding)
+		error = -EFAULT;

It returns EFAULT on memory exhaustion?

No longer... That'll be fixed in version 3.

Quoted 1 line

btw, can you remind me again why this is only available to tmpfs pagecache?

I can try! ;) I originally wanted to do just a shared memory binding call, but people (correctly) suggested a more generic memory binding would be more useful. So I've basically just set up a lot of the infrastructure for a more generic call, but haven't fully implemented it. This patch is intended to be a starting point, from which it will be easy to incrementally add more functionality and power to the binding call. The underlying code (syscalls, structures, .c files, allocator changes) won't have to change too much. So this patch works for any shared memory segment. It'd be straightforward to extend this to any file-backed vma (because it already has a struct address_space, with a struct binding in it), so I hope to grow this into something more.

Cheers!

-Matt

#4 lore raw

Re: [rfc][patch] Memory Binding Take 2 (1/1)

In reply to Matthew Dobson

On Wed, 2 Apr 2003, Matthew Dobson wrote:
+/*
+ * membind - Bind a range of a process' VM space to a set of memory blocks according to

membind or mbind? Me, I like mbind (modulo remarks below), but you may find Linus does not (he was rather caustic when I suggested that fremap should be mseek, and it ended up as remap_file_pages instead).

+ *            a predefined policy.
+ * @start:    beginning address of memory region to bind
+ * @len:      length of memory region to bind

Oh really? len is unused in the code below. If you were to use it, you'd need to loop over vmas, splitting where necessary.

+ * @mask_ptr: pointer to bitmask of cpus
+ * @mask_len: length of the bitmask
+ * @policy: flag specifying the policy to use for the segment

I think you already remarked that policy is currently unused, fair enough.

+ */
+asmlinkage unsigned long sys_mbind(unsigned long start, unsigned long len,
+ unsigned long *mask_ptr, unsigned int mask_len, unsigned long policy)
+{
+ DECLARE_BITMAP(cpu_mask, NR_CPUS);
+ DECLARE_BITMAP(node_mask, MAX_NUMNODES);
+ struct vm_area_struct *vma = NULL;
+ struct address_space *mapping;
+ int copy_len, error = 0;
+
+ /* Deal with getting cpu_mask from userspace & translating to node_mask */
+ copy_len = min(mask_len, (unsigned int)NR_CPUS);
+ CLEAR_BITMAP(cpu_mask, NR_CPUS);
+ CLEAR_BITMAP(node_mask, MAX_NUMNODES);
+ if (copy_from_user(cpu_mask, mask_ptr, (copy_len+7)/8)) {
+ error = -EFAULT;
+ goto out;
+ }

Shouldn't there be some capability restriction? Is it right that anyone who can mmap a file for reading can determine its binding (until the next does it differently)?

+ cpumask_to_nodemask(cpu_mask, node_mask);
+
+ vma = find_vma(current->mm, start);

You must not scan the vma list without at least
down_read(&current->mm->mmap_sem).

+ if (!(vma && vma->vm_file && vma->vm_ops &&
+ vma->vm_ops->nopage == shmem_nopage)) {
+ /* This isn't a shm segment. For now, we bail. */

So you're allowing this on any file on tmpfs,
but on no file on any other filesystem: curious.

+ error = -EINVAL;
+ goto out;
+ }
+
+ mapping = vma->vm_file->f_dentry->d_inode->i_mapping;
+ mapping->binding = alloc_binding(node_mask);

Your NUMA machines clearly have more memory than is good for you:
nowhere is there an equivalent free_binding: which in particular would need to be called first here if binding is already set (or else old structure reused), and when inode is freed.

So... mapping->binding conditions every page_cache_alloc for that inode. Hmm, what on earth does this have to do with mbind or membind? It looks to me like fbind, except that you've dressed up the interface to use an address in the caller's address space: presumably because you couldn't get a file handle on SysV shared memory, and that's what you were really wanting to bind, hence the shmem_nopage test?

I think this interface is confused (but it probably thinks I am).

+ if (!mapping->binding)
+ error = -EFAULT;
+
+out:
+ return error;
+}

mm/swap_state.c +3 -0
diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-pre_membind/mm/swap_state.c linux-2.5.66-membind/mm/swap_state.c
@@ -47,6 +47,9 @@
 	.i_shared_sem	= __MUTEX_INITIALIZER(swapper_space.i_shared_sem),
 	.private_lock	= SPIN_LOCK_UNLOCKED,
 	.private_list	= LIST_HEAD_INIT(swapper_space.private_list),
+#ifdef CONFIG_NUMA
+	.binding	= NULL,
+#endif
 };
 
 #define INC_CACHE_INFO(x)	do { swap_cache_info.x++; } while (0)

Please leave swap_state.c out of it: this patch does nothing but add an ugly #ifdef to initialize something to 0 which would be 0 anyway.

Hugh

#5 lore raw

Re: [rfc][patch] Memory Binding Take 2 (1/1)

I suggested mbind for consistency with mmap, munmap, mremap and msync, that is IFF the mbind operation is in some ways related with these other syscalls.

Quoted 107 lines · Hugh Dickins

On Wed, 2 Apr 2003, Matthew Dobson wrote:
+/*
+ * membind - Bind a range of a process' VM space to a set of memory blocks according to

membind or mbind? Me, I like mbind (modulo remarks below), but you may find Linus does not (he was rather caustic when I suggested that fremap should be mseek, and it ended up as remap_file_pages instead).

+ *            a predefined policy.
+ * @start:    beginning address of memory region to bind
+ * @len:      length of memory region to bind

Oh really? len is unused in the code below. If you were to use it, you'd need to loop over vmas, splitting where necessary.

+ * @mask_ptr: pointer to bitmask of cpus
+ * @mask_len: length of the bitmask
+ * @policy: flag specifying the policy to use for the segment

I think you already remarked that policy is currently unused, fair enough.

+ */

(quoted diff) +17 -0
+asmlinkage unsigned long sys_mbind(unsigned long start, unsigned long len, 
+		unsigned long *mask_ptr, unsigned int mask_len, unsigned long policy)
+{
+	DECLARE_BITMAP(cpu_mask, NR_CPUS);
+	DECLARE_BITMAP(node_mask, MAX_NUMNODES);
+	struct vm_area_struct *vma = NULL;
+	struct address_space *mapping;
+	int copy_len, error = 0;
+
+	/* Deal with getting cpu_mask from userspace & translating to node_mask */
+	copy_len = min(mask_len, (unsigned int)NR_CPUS);
+	CLEAR_BITMAP(cpu_mask, NR_CPUS);
+	CLEAR_BITMAP(node_mask, MAX_NUMNODES);
+	if (copy_from_user(cpu_mask, mask_ptr, (copy_len+7)/8)) {
+		error = -EFAULT;
+		goto out;
+	}

Shouldn't there be some capability restriction? Is it right that anyone who can mmap a file for reading can determine its binding (until the next does it differently)?

(quoted diff) +3 -0
+	cpumask_to_nodemask(cpu_mask, node_mask);
+
+	vma = find_vma(current->mm, start);

You must not scan the vma list without at least
down_read(&current->mm->mmap_sem).

(quoted diff) +3 -0
+	if (!(vma && vma->vm_file && vma->vm_ops && 
+		vma->vm_ops->nopage == shmem_nopage)) {
+		/* This isn't a shm segment.  For now, we bail. */

So you're allowing this on any file on tmpfs,
but on no file on any other filesystem: curious.

(quoted diff) +6 -0
+		error = -EINVAL;
+		goto out;
+	}
+
+	mapping = vma->vm_file->f_dentry->d_inode->i_mapping;
+	mapping->binding = alloc_binding(node_mask);

Your NUMA machines clearly have more memory than is good for you:
nowhere is there an equivalent free_binding: which in particular would need to be called first here if binding is already set (or else old structure reused), and when inode is freed.

So... mapping->binding conditions every page_cache_alloc for that inode. Hmm, what on earth does this have to do with mbind or membind? It looks to me like fbind, except that you've dressed up the interface to use an address in the caller's address space: presumably because you couldn't get a file handle on SysV shared memory, and that's what you were really wanting to bind, hence the shmem_nopage test?

I think this interface is confused (but it probably thinks I am).

(quoted diff) +6 -0
+	if (!mapping->binding)
+		error = -EFAULT;
+
+out:
+	return error;
+}

diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-pre_membind/mm/swap_state.c linux-2.5.66-membind/mm/swap_state.c

mm/swap_state.c +3 -0
@@ -47,6 +47,9 @@
 	.i_shared_sem	= __MUTEX_INITIALIZER(swapper_space.i_shared_sem),
 	.private_lock	= SPIN_LOCK_UNLOCKED,
 	.private_list	= LIST_HEAD_INIT(swapper_space.private_list),
+#ifdef CONFIG_NUMA
+	.binding	= NULL,
+#endif
 };
 
 #define INC_CACHE_INFO(x)	do { swap_cache_info.x++; } while (0)

Please leave swap_state.c out of it: this patch does nothing but add an ugly #ifdef to initialize something to 0 which would be 0 anyway.

Hugh

#6 lore raw

Re: [rfc][patch] Memory Binding Take 2 (1/1)

In reply to Hugh Dickins
Quoted 7 lines · Hugh Dickins

On Wed, 2 Apr 2003, Matthew Dobson wrote:
+/*
+ * membind - Bind a range of a process' VM space to a set of memory blocks according to

membind or mbind? Me, I like mbind (modulo remarks below), but you may find Linus does not (he was rather caustic when I suggested that fremap should be mseek, and it ended up as remap_file_pages instead).

I'm a fan of mbind. s/mbind/whatever_linus_wants/ should be easy enough if necessary. ;)

Quoted 6 lines
+ *            a predefined policy.
+ * @start:    beginning address of memory region to bind
+ * @len:      length of memory region to bind

Oh really? len is unused in the code below. If you were to use it, you'd need to loop over vmas, splitting where necessary.

No.. Not really! ;) As I mentioned in my reply to Andrew, one of the main thrusts of this patch is to set up the infrastructure in a way that allows it to be fairly easily expanded in the future. For now, as has been noted several time, it just works on shared memory segments and doesn't actually respect the length argument. Your point is well taken, though. I imagine the code for looping over vmas will looks similar to what is done in the mmap and mlock calls.

Quoted 19 lines

+ */

(quoted diff) +17 -0
+asmlinkage unsigned long sys_mbind(unsigned long start, unsigned long len, 
+		unsigned long *mask_ptr, unsigned int mask_len, unsigned long policy)
+{
+	DECLARE_BITMAP(cpu_mask, NR_CPUS);
+	DECLARE_BITMAP(node_mask, MAX_NUMNODES);
+	struct vm_area_struct *vma = NULL;
+	struct address_space *mapping;
+	int copy_len, error = 0;
+
+	/* Deal with getting cpu_mask from userspace & translating to node_mask */
+	copy_len = min(mask_len, (unsigned int)NR_CPUS);
+	CLEAR_BITMAP(cpu_mask, NR_CPUS);
+	CLEAR_BITMAP(node_mask, MAX_NUMNODES);
+	if (copy_from_user(cpu_mask, mask_ptr, (copy_len+7)/8)) {
+		error = -EFAULT;
+		goto out;
+	}
Quoted 3 lines

Shouldn't there be some capability restriction? Is it right that anyone who can mmap a file for reading can determine its binding (until the next does it differently)?

Probably. Once the binding call is expanded to encompass any mmap'd file, then yes, we'll want capabilities. For now, with just shared memory segments, it's probably not quite as imperative. Although I guess it also works for any shmem_fs segments, too. I'll look into finding an appropriate capability to check... Any suggestions?

Quoted 6 lines
(quoted diff) +3 -0
+	cpumask_to_nodemask(cpu_mask, node_mask);
+
+	vma = find_vma(current->mm, start);

You must not scan the vma list without at least
down_read(&current->mm->mmap_sem).

OK.. Thanks for the heads up! ;)

Quoted 6 lines
(quoted diff) +3 -0
+	if (!(vma && vma->vm_file && vma->vm_ops && 
+		vma->vm_ops->nopage == shmem_nopage)) {
+		/* This isn't a shm segment.  For now, we bail. */

So you're allowing this on any file on tmpfs,
but on no file on any other filesystem: curious.

See comments near the top of this reply, and the comments in my reply to Andrew.

Quoted 11 lines
(quoted diff) +6 -0
+		error = -EINVAL;
+		goto out;
+	}
+
+	mapping = vma->vm_file->f_dentry->d_inode->i_mapping;
+	mapping->binding = alloc_binding(node_mask);

Your NUMA machines clearly have more memory than is good for you:
nowhere is there an equivalent free_binding: which in particular would need to be called first here if binding is already set (or else old structure reused), and when inode is freed.

Heh... erm, maybe I should retake CS-101. Version 3 awaits! ;)

Quoted 8 lines

So... mapping->binding conditions every page_cache_alloc for that inode. Hmm, what on earth does this have to do with mbind or membind? It looks to me like fbind, except that you've dressed up the interface to use an address in the caller's address space: presumably because you couldn't get a file handle on SysV shared memory, and that's what you were really wanting to bind, hence the shmem_nopage test?

I think this interface is confused (but it probably thinks I am).

The original RFC I sent out some time ago was for shared memory segment binding only. This is to be expanded, as per comments above.

Quoted 20 lines
(quoted diff) +6 -0
+	if (!mapping->binding)
+		error = -EFAULT;
+
+out:
+	return error;
+}

diff -Nur --exclude-from=/usr/src/.dontdiff linux-2.5.66-pre_membind/mm/swap_state.c linux-2.5.66-membind/mm/swap_state.c

mm/swap_state.c +3 -0
@@ -47,6 +47,9 @@
 	.i_shared_sem	= __MUTEX_INITIALIZER(swapper_space.i_shared_sem),
 	.private_lock	= SPIN_LOCK_UNLOCKED,
 	.private_list	= LIST_HEAD_INIT(swapper_space.private_list),
+#ifdef CONFIG_NUMA
+	.binding	= NULL,
+#endif
 };
 
 #define INC_CACHE_INFO(x)	do { swap_cache_info.x++; } while (0)
Quoted 2 lines

Please leave swap_state.c out of it: this patch does nothing but add an ugly #ifdef to initialize something to 0 which would be 0 anyway.

Also in the anxiously awaited version 3. Or should I say *not* in it?

Cheers!

-Matt

#7 lore raw

Re: [rfc][patch] Memory Binding Take 2 (1/1)

In reply to Matthew Dobson
Quoted 4 lines
include/linux/mbind.h +4 -0
+#ifndef _LINUX_MBIND_H
+#define _LINUX_MBIND_H
+
+#ifdef CONFIG_NUMA

include/linux/mbind.h line 40

Quoted 10 lines
include/linux/mbind.h +10 -0
+
+#include <linux/mmzone.h>
+
+/* Structure to keep track of memory segment (VMA) bindings */
+struct binding {
+	struct zonelist	zonelist;
+};
+
+#endif /* CONFIG_NUMA */
+#endif /* _LINUX_MBIND_H */

Using CONFIG_ without explicitly including config.h is a bad idea. But the ifdef is unessecary anyway, no one is hurt by having this struct defintion in the non-numa case. Also I wonder how you can have a copyright on a single trivial struct defintion :) What about just moving it to mmzone.h, btw?

mm/mbind.c line 30

Quoted 4 lines
mm/mbind.c +4 -0
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/mbind.h>
+#include <asm/string.h>

Use <linux/string.h> instead.

mm/mbind.c line 67

Quoted 1 line
mm/mbind.c +1 -0
+	binding = (struct binding *)kmalloc(sizeof(struct binding), GFP_KERNEL);

The cast is superflous.

mm/mbind.c line 122

Quoted 6 lines
mm/mbind.c +6 -0
+	if (!(vma && vma->vm_file && vma->vm_ops && 
+		vma->vm_ops->nopage == shmem_nopage)) {
+		/* This isn't a shm segment.  For now, we bail. */
+		error = -EINVAL;
+		goto out;
+	}

This check is just horrible. Please describe what kind of mappings this doesn't work for and why and add a VM_ flag for those that support memory binding.

mm/swap_state.c line 52

Quoted 4 lines
mm/swap_state.c +3 -0
 	.private_list	= LIST_HEAD_INIT(swapper_space.private_list),
+#ifdef CONFIG_NUMA
+	.binding	= NULL,
+#endif

You don't need to initialize this at all.

#8 lore raw
In reply to Matthew Dobson
Quoted 6 lines · Matthew Dobson

Alrighty, let's give this another go...

This patch hasn't changed much. Just added bitmap_t, typedef'd to unsigned long * for passing around bitmaps without breaking the abstraction. I think it's good if we can keep the underlying data type hidden to partially future-proof (protect? ;) the code.

Not a good idea. Se_bit & co are defined to work on unsigned longs arrays.