Skip to contentSkip to conversation

[patch[ Simple Topology API

PATCH Matthew Dobson 4 participants 6 messages Jul 13, 2002 – Jul 15, 2002

lore
Share & tools
lore
#0 lore raw

Here is a very rudimentary topology API for NUMA systems. It uses prctl() for the userland calls, and exposes some useful things to userland. It would be nice to expose these simple structures to both users and the kernel itself. Any architecture wishing to use this API simply has to write a .h file that defines the 5 calls defined in core_ibmnumaq.h and include it in asm/mmzone.h.
Voila! Instant inclusion in the topology!

Enjoy!

-Matt

include/asm-i386/core_ibmnumaq.h +62 -0
diff -Nur linux-2.5.25-vanilla/include/asm-i386/core_ibmnumaq.h linux-2.5.25-api/include/asm-i386/core_ibmnumaq.h
@@ -0,0 +1,62 @@
+/*
+ * linux/include/asm-i386/core_ibmnumaq.h
+ *
+ * Written by: Matthew Dobson, IBM Corporation
+ *
+ * Copyright (C) 2002, 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 _ASM_CORE_IBMNUMAQ_H_
+#define _ASM_CORE_IBMNUMAQ_H_
+
+/*
+ * These functions need to be defined for every architecture.
+ * The first five are necessary for the Memory Binding API to function.
+ * The last is needed by several pieces of NUMA code.
+ */
+
+
+/* Returns the number of the node containing CPU 'cpu' */
+#define _cpu_to_node(cpu) (cpu_to_logical_apicid(cpu) >> 4)
+
+/* Returns the number of the node containing MemBlk 'memblk' */
+#define _memblk_to_node(memblk) (memblk)
+
+/* Returns the number of the node containing Node 'nid'.  This architecture is flat, 
+   so it is a pretty simple function! */
+#define _node_to_node(nid) (nid)
+
+/* Returns the number of the first CPU on Node 'node' */
+static inline int _node_to_cpu(int node)
+{
+	int i, cpu, logical_apicid = node << 4;
+
+	for(i = 1; i < 16; i <<= 1)
+		if ((cpu = logical_apicid_to_cpu(logical_apicid | i)) >= 0)
+			return cpu;
+
+	return 0;
+}
+
+/* Returns the number of the first MemBlk on Node 'node' */
+#define _node_to_memblk(node) (node)
+
+#endif /* _ASM_CORE_IBMNUMAQ_H_ */
include/asm-i386/mmzone.h +49 -0

49 lines not on this page · raw message

include/linux/membind.h +38 -0
diff -Nur linux-2.5.25-vanilla/include/linux/membind.h linux-2.5.25-api/include/linux/membind.h
@@ -0,0 +1,38 @@
+/*
+ * linux/include/linux/membind.h
+ *
+ * Written by: Matthew Dobson, IBM Corporation
+ *
+ * Copyright (C) 2002, 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_MEMBIND_H_
+#define _LINUX_MEMBIND_H_
+
+int cpu_to_node(int);
+int memblk_to_node(int);
+int node_to_node(int);
+int node_to_cpu(int);
+int node_to_memblk(int);
+int get_curr_cpu(void);
+int get_curr_node(void);
+
+#endif /* _LINUX_MEMBIND_H_ */
include/linux/prctl.h +13 -0
diff -Nur linux-2.5.25-vanilla/include/linux/prctl.h linux-2.5.25-api/include/linux/prctl.h
@@ -26,4 +26,17 @@
 # define PR_FPEMU_NOPRINT	1	/* silently emulate fp operations accesses */
 # define PR_FPEMU_SIGFPE	2	/* don't emulate fp operations, send SIGFPE instead */
 
+/* Get CPU/Node */
+#define PR_GET_CURR_CPU		13
+#define PR_GET_CURR_NODE    	14
+
+/* XX to Node conversion functions */
+#define PR_CPU_TO_NODE		15
+#define PR_MEMBLK_TO_NODE      	16
+#define PR_NODE_TO_NODE		17
+
+/* Node to XX conversion functions */
+#define PR_NODE_TO_CPU		18
+#define PR_NODE_TO_MEMBLK      	19
+
 #endif /* _LINUX_PRCTL_H */
kernel/membind.c +130 -0

130 lines not on this page · raw message

kernel/sys.c +22 -0
diff -Nur linux-2.5.25-vanilla/kernel/sys.c linux-2.5.25-api/kernel/sys.c
@@ -19,6 +19,7 @@
 #include <linux/tqueue.h>
 #include <linux/device.h>
 #include <linux/times.h>
+#include <linux/membind.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -1291,6 +1292,27 @@
 			}
 			current->keep_capabilities = arg2;
 			break;
+		case PR_GET_CURR_CPU:
+			error = (long) get_curr_cpu();
+			break;
+		case PR_GET_CURR_NODE:
+			error = (long) get_curr_node();
+			break;
+		case PR_CPU_TO_NODE:
+			error = (long) cpu_to_node((int)arg2);
+			break;
+		case PR_MEMBLK_TO_NODE:
+			error = (long) memblk_to_node((int)arg2);
+			break;
+		case PR_NODE_TO_NODE:
+			error = (long) node_to_node((int)arg2);
+			break;
+		case PR_NODE_TO_CPU:
+			error = (long) node_to_cpu((int)arg2);
+			break;
+		case PR_NODE_TO_MEMBLK:
+			error = (long) node_to_memblk((int)arg2);
+			break;
 		default:
 			error = -EINVAL;
 			break;
#1 lore raw
Quoted 6 lines · Matthew Dobson

Here is a very rudimentary topology API for NUMA systems. It uses prctl() for the userland calls, and exposes some useful things to userland. It would be nice to expose these simple structures to both users and the kernel itself. Any architecture wishing to use this API simply has to write a .h file that defines the 5 calls defined in core_ibmnumaq.h and include it in asm/mmzone.h.

Matt,

I suspect what happens when these patches come out is that most people simply don't have the knowledge/time/experience/context to judge them, and nothing ends up happening. No way would I pretend to be able to comment on the big picture, that's for sure.

If the code is clean, the interfaces make sense, the impact on other platforms is minimised and the stakeholders are OK with it then that should be sufficient, yes?

AFAIK, the interested parties with this and the memory binding API are ia32-NUMA, ia64, PPC, some MIPS and x86-64-soon. It would be helpful if the owners of those platforms could review this work and say "yes, this is something we can use and build upon". Have they done that?

I'd have a few micro-observations:

kernel/membind.c Fri Jul 12 16:13:17 2002

Quoted 5 lines

...

kernel/membind.c Fri Jul 12 16:13:17 2002 renamed from kernel/membind.c Wed Dec 31 16:00:00 1969 +1 -0
..
+inline int memblk_to_node(int memblk)

The inlines with global scope in this file seem strange?

kernel/membind.c Fri Jul 12 16:13:17 2002

Quoted 11 lines · Matthew Dobson

Here is a Memory Binding API
...

kernel/membind.c Fri Jul 12 16:13:17 2002 renamed from kernel/membind.c Wed Dec 31 16:00:00 1969 +6 -0
+    memblk_binding:    { MEMBLK_NO_BINDING, MPOL_STRICT },             \
 
...
+typedef struct memblk_list {
+       memblk_bitmask_t bitmask;
+       int behavior;
+       rwlock_t lock;
+} memblk_list_t;

Is is possible to reduce this type to something smaller for CONFIG_NUMA=n?

In the above task_struct initialiser you should initialise the rwlock to RWLOCK_LOCK_UNLOCKED.

It's nice to use the `name:value' initialiser format in there, too.

kernel/membind.c Fri Jul 12 16:13:17 2002

Quoted 5 lines

...

kernel/membind.c Fri Jul 12 16:13:17 2002 renamed from kernel/membind.c Wed Dec 31 16:00:00 1969 +3 -0
+int set_memblk_binding(memblk_bitmask_t memblks, int behavior)
+{
...
+       read_lock_irqsave(&current->memblk_binding.lock, flags);

Your code accesses current' a lot. You'll find that the code generation is fairly poor - evaluating current' chews 10-15 bytes of code. You can perform a manual CSE by copying current into a local, and save a few cycles.

kernel/membind.c Fri Jul 12 16:13:17 2002

Quoted 7 lines

...

kernel/membind.c Fri Jul 12 16:13:17 2002 renamed from kernel/membind.c Wed Dec 31 16:00:00 1969 +5 -0
+struct page * _alloc_pages(unsigned int gfp_mask, unsigned int order)
+{
...
+       spin_lock_irqsave(&node_lock, flags);
+       temp = pgdat_list;
+       spin_unlock_irqrestore(&node_lock, flags);

Not sure what you're trying to lock here, but you're not locking it ;) This is either racy code or unneeded locking.

Thanks.

#2 lore raw
Quoted 6 lines · Andrew Morton

Matt,

I suspect what happens when these patches come out is that most people simply don't have the knowledge/time/experience/context to judge them, and nothing ends up happening. No way would I pretend to be able to comment on the big picture, that's for sure.

Absolutely correct. I know that most people here on LKML don't have 8, 16, 32, or more CPU systems to test this code on, or for that matter, even care about code designed for said systems. I'm lucky enough to get to work on such machines, and I'm sure there are others out there (as evidenced by some of the replies I've gotten) that do care. Also, there are publicly available NUMA machines in the OSDL that people can use to "play" on large systems. I hope that by seeing code and using these systems, some more people might get interested in some of the interesting scalability issues that crop up with these machines.

Quoted 3 lines

If the code is clean, the interfaces make sense, the impact on other platforms is minimised and the stakeholders are OK with it then that should be sufficient, yes?

I would hope so. That's what I'm trying to establish! ;)

Quoted 4 lines

AFAIK, the interested parties with this and the memory binding API are ia32-NUMA, ia64, PPC, some MIPS and x86-64-soon. It would be helpful if the owners of those platforms could review this work and say "yes, this is something we can use and build upon". Have they done that?

I've gotten some feedback from large systems people. I hope to get feedback from anyone with large systems that could potentially use this kind of API, and get a "this is great" or a "this sucks". I believe that bigger systems need new ways to improve efficiency and scalability than what the kernel offers now.
I know I do...

Quoted 19 lines

I'd have a few micro-observations:

kernel/membind.c Fri Jul 12 16:13:17 2002

Quoted 5 lines

...

kernel/membind.c Fri Jul 12 16:13:17 2002 renamed from kernel/membind.c Wed Dec 31 16:00:00 1969 +1 -0
..
+inline int memblk_to_node(int memblk)

The inlines with global scope in this file seem strange?

Quoted 3 lines · Matthew Dobson

Here is a Memory Binding API
...
+ memblk_binding: { MEMBLK_NO_BINDING, MPOL_STRICT }, \

Quoted 10 lines

kernel/membind.c Fri Jul 12 16:13:17 2002

Quoted 6 lines

...

kernel/membind.c Fri Jul 12 16:13:17 2002 renamed from kernel/membind.c Wed Dec 31 16:00:00 1969 +5 -0
+typedef struct memblk_list {
+       memblk_bitmask_t bitmask;
+       int behavior;
+       rwlock_t lock;
+} memblk_list_t;

Is is possible to reduce this type to something smaller for CONFIG_NUMA=n?

Probably... I'll look at that today...

Quoted 2 lines

In the above task_struct initialiser you should initialise the rwlock to RWLOCK_LOCK_UNLOCKED.

Yep.. Totally forgot about that! :(

Quoted 1 line

It's nice to use the `name:value' initialiser format in there, too.

Sure, enhanced readability is always a good thing!

Quoted 11 lines

kernel/membind.c Fri Jul 12 16:13:17 2002

Quoted 5 lines

...

kernel/membind.c Fri Jul 12 16:13:17 2002 renamed from kernel/membind.c Wed Dec 31 16:00:00 1969 +3 -0
+int set_memblk_binding(memblk_bitmask_t memblks, int behavior)
+{
...
+       read_lock_irqsave(&current->memblk_binding.lock, flags);

Your code accesses current' a lot. You'll find that the code generation is fairly poor - evaluating current' chews 10-15 bytes of code. You can perform a manual CSE by copying current into a local, and save a few cycles.

Sure.. I've actually gotten a couple different ideas about improving the efficiency of that function, and will also be rewriting that today..

Quoted 11 lines

kernel/membind.c Fri Jul 12 16:13:17 2002

Quoted 7 lines

...

kernel/membind.c Fri Jul 12 16:13:17 2002 renamed from kernel/membind.c Wed Dec 31 16:00:00 1969 +5 -0
+struct page * _alloc_pages(unsigned int gfp_mask, unsigned int order)
+{
...
+       spin_lock_irqsave(&node_lock, flags);
+       temp = pgdat_list;
+       spin_unlock_irqrestore(&node_lock, flags);

Not sure what you're trying to lock here, but you're not locking it ;) This is either racy code or unneeded locking.

To be honest, I'm not entirely sure what that's locking either. That is the non-NUMA path of that function, and the locking was in the original code, so I just moved it along. After doing a bit of searching, that lock seems COMPLETELY useless there. Especially since in the original function, a few lines further down pgdat_list is read again, without the lock! I guess, unless someone here says otherwise, I'll pull that locking out of the next rev.

Thanks for all the feedback. I'll incorporate most of it into the next rev of the patch!

Cheers!

-Matt

Quoted 9 lines

Thanks.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

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

Here is a very rudimentary topology API for NUMA systems. It uses prctl() for the userland calls, and exposes some useful things to userland. It would be nice to expose these simple structures to both users and the kernel itself. Any architecture wishing to use this API simply has to write a .h file that defines the 5 calls defined in core_ibmnumaq.h and include it in asm/mmzone.h.
Voila! Instant inclusion in the topology!

Enjoy!

It's hard to enjoy the use of prctl(). Especially for things like "give me the number of the first CPU in node <n>" - it ain't no process controll, no matter how you stretch it.

<soapbox> That's yet another demonstration of the evil of multiplexing syscalls. They hide the broken APIs and make them easy to introduce. And broken APIs get introduced - through each of these. prctl(), fcntl(), ioctl() - you name it. Please, don't do that. </soapbox>

Please, replace that API with something sane. "Current processor" and _maybe_ "current node" are reasonable per-process things (even though the latter is obviously redundant). They are inherently racy, however - if you get scheduled on the return from syscall the value may have nothing to reality by the time you return to userland. The rest is obviously system-wide _and_ not process-related (it's "tell me about the configuration of machine"). Implementing them as prctls makes absolutely no sense. If anything, that's sysctl material.

#4 lore raw
Quoted 3 lines · Alexander Viro

It's hard to enjoy the use of prctl(). Especially for things like "give me the number of the first CPU in node <n>" - it ain't no process controll, no matter how you stretch it.

Yeah... eeew.

Quoted 4 lines

<soapbox> That's yet another demonstration of the evil of multiplexing syscalls. They hide the broken APIs and make them easy to introduce. And broken APIs get introduced - through each of these. prctl(), fcntl(), ioctl() - you name it. Please, don't do that. </soapbox>

This wouldn't happen if it wasn't so damn hard to add a syscall. If you make people go though all the arch maintainers just to add a simple arch-independent syscall, they'll just bolt their code into some dark hidden corner of the kernel. That's life. Make syscalls easy to write, and this won't happen.

Can you guess what would happen if you got rid of prctl(), fcntl(), and ioctl()? We'd get apps with code like this:

// write address of one of these to /proc/orifice
typedef struct evil {

  int version;        // struct version
  struct evil *next;  // next in list
  struct evil *prev;  // prev in list
  char opcode;        // indicates what we will do
  int (*fn)(void *);  // callback function (if not NULL)
  void *addr;         // an address in kernel memory
  short flags;        // 0x0001 call fn w/ ints off, 0x0002 w/ BKL
  double timeout;     // in microfortnights (uses APIC's NMI)

} evil;

#5 lore raw
In reply to Alexander Viro

Al,

If I can get 1-2 syscalls for the Topo API, and 1-2 for the Membind API, I'll gladly make the changes. For now, though, prctl() works fine. If it needs to be changed at some point, it can be done in about 5 minutes...

As far as the raciness of the get_curr_cpu & get_curr_node calls, that is noted in the comments. Until we get a better way of exposing the current working processor to userspace, they'll have to do. I believe that having *some* idea of where you're running is better than having *no* idea of where you're running.

-Matt

Quoted 29 lines · Alexander Viro
Quoted 4 lines · Matthew Dobson

Here is a very rudimentary topology API for NUMA systems. It uses prctl() for the userland calls, and exposes some useful things to userland. It would be nice to expose these simple structures to both users and the kernel itself. Any architecture wishing to use this API simply has to write a .h file that defines the 5 calls defined in core_ibmnumaq.h and include it in asm/mmzone.h.
Voila! Instant inclusion in the topology!

Enjoy!

It's hard to enjoy the use of prctl(). Especially for things like "give me the number of the first CPU in node <n>" - it ain't no process controll, no matter how you stretch it.

<soapbox> That's yet another demonstration of the evil of multiplexing syscalls. They hide the broken APIs and make them easy to introduce. And broken APIs get introduced - through each of these. prctl(), fcntl(), ioctl() - you name it. Please, don't do that. </soapbox>

Please, replace that API with something sane. "Current processor" and _maybe_ "current node" are reasonable per-process things (even though the latter is obviously redundant). They are inherently racy, however - if you get scheduled on the return from syscall the value may have nothing to reality by the time you return to userland. The rest is obviously system-wide _and_ not process-related (it's "tell me about the configuration of machine"). Implementing them as prctls makes absolutely no sense. If anything, that's sysctl material.