[patch[ Simple Topology API
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
@@ -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
@@ -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
@@ -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
@@ -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 ;
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 incore_ibmnumaq.hand include it inasm/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(¤t->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.
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_structinitialiser you should initialise the rwlock toRWLOCK_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(¤t->memblk_binding.lock, flags);Your code accesses
current' a lot. You'll find that the code generation is fairly poor - evaluatingcurrent' 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/
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 incore_ibmnumaq.hand include it inasm/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.
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;
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 incore_ibmnumaq.hand include it inasm/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.