./numactl-2.0.9~rc5/0000755000175000017500000000000012213661424012611 5ustar ianwianw./numactl-2.0.9~rc5/rtnetlink.c0000644000175000017500000000407212213661423014771 0ustar ianwianw/* Simple LPGLed rtnetlink library */ #include #include #include #include #include #include #define hidden __attribute__((visibility("hidden"))) #include "rtnetlink.h" hidden void *rta_put(struct nlmsghdr *m, int type, int len) { struct rtattr *rta = (void *)m + NLMSG_ALIGN(m->nlmsg_len); int rtalen = RTA_LENGTH(len); rta->rta_type = type; rta->rta_len = rtalen; m->nlmsg_len = NLMSG_ALIGN(m->nlmsg_len) + RTA_ALIGN(rtalen); return RTA_DATA(rta); } hidden struct rtattr *rta_get(struct nlmsghdr *m, struct rtattr *p, int offset) { struct rtattr *rta; if (p) { rta = RTA_NEXT(p, m->nlmsg_len); if (!RTA_OK(rta, m->nlmsg_len)) return NULL; } else { rta = (void *)m + NLMSG_ALIGN(offset); } return rta; } hidden int rta_put_address(struct nlmsghdr *msg, int type, struct sockaddr *adr) { switch (adr->sa_family) { case AF_INET: { struct in_addr *i = rta_put(msg, type, 4); *i = ((struct sockaddr_in *)adr)->sin_addr; break; } case AF_INET6: { struct in6_addr *i6 = rta_put(msg, type, 16); *i6 = ((struct sockaddr_in6 *)adr)->sin6_addr; break; } default: return -1; } return 0; } /* Assumes no truncation. Make the buffer large enough. */ hidden int rtnetlink_request(struct nlmsghdr *msg, int buflen, struct sockaddr_nl *adr) { int rsk; int n; int e; /* Use a private socket to avoid having to keep state for a sequence number. */ rsk = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (rsk < 0) return -1; n = sendto(rsk, msg, msg->nlmsg_len, 0, (struct sockaddr *)adr, sizeof(struct sockaddr_nl)); if (n >= 0) { socklen_t adrlen = sizeof(struct sockaddr_nl); n = recvfrom(rsk, msg, buflen, 0, (struct sockaddr *)adr, &adrlen); } e = errno; close(rsk); errno = e; if (n < 0) return -1; /* Assume we only get a single reply back. This is (hopefully?) safe because it's a single use socket. */ if (msg->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = NLMSG_DATA(msg); errno = -err->error; return -1; } return 0; } ./numactl-2.0.9~rc5/numastat0000755000175000017500000000000012213661422014357 0ustar ianwianw./numactl-2.0.9~rc5/numademo.c0000755000175000017500000002755412213661422014600 0ustar ianwianw/* Copyright (C) 2003,2004 Andi Kleen, SuSE Labs. Test/demo program for libnuma. This is also a more or less useful benchmark of the NUMA characteristics of your machine. It benchmarks most possible NUMA policy memory configurations with various benchmarks. Compile standalone with cc -O2 numademo.c -o numademo -lnuma -lm numactl 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; version 2. numactl 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. See the GNU General Public License for more details. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define _GNU_SOURCE 1 #include #include #include #include #include #include "numa.h" #ifdef HAVE_STREAM_LIB #include "stream_lib.h" #endif #ifdef HAVE_MT #include "mt.h" #endif #ifdef HAVE_CLEAR_CACHE #include "clearcache.h" #else static inline void clearcache(void *a, unsigned size) {} #endif #define FRACT_NODES 8 #define FRACT_MASKS 32 int fract_nodes; unsigned long msize; /* Should get this from cpuinfo, but on !x86 it's not there */ enum { CACHELINESIZE = 64, }; enum test { MEMSET = 0, MEMCPY, FORWARD, BACKWARD, STREAM, RANDOM2, PTRCHASE, } thistest; char *delim = " "; int force; int regression_testing=0; char *testname[] = { "memset", "memcpy", "forward", "backward", #ifdef HAVE_STREAM_LIB "stream", #endif #ifdef HAVE_MT "random2", #endif "ptrchase", NULL, }; void output(char *title, char *result) { if (!isspace(delim[0])) printf("%s%s%s\n", title,delim, result); else printf("%-42s%s\n", title, result); } #ifdef HAVE_STREAM_LIB void do_stream(char *name, unsigned char *mem) { int i; char title[100], buf[100]; double res[STREAM_NRESULTS]; stream_verbose = 0; clearcache(mem, msize); stream_init(mem); stream_test(res); sprintf(title, "%s%s%s", name, delim, "STREAM"); buf[0] = '\0'; for (i = 0; i < STREAM_NRESULTS; i++) { if (buf[0]) strcat(buf,delim); sprintf(buf+strlen(buf), "%s%s%.2f%sMB/s", stream_names[i], delim, res[i], delim); } output(title, buf); clearcache(mem, msize); } #endif /* Set up a randomly distributed list to fool prefetchers */ union node { union node *next; struct { unsigned nexti; unsigned val; }; }; static int cmp_node(const void *ap, const void *bp) { union node *a = (union node *)ap; union node *b = (union node *)bp; return a->val - b->val; } void **ptrchase_init(unsigned char *mem) { long i; union node *nodes = (union node *)mem; long nmemb = msize / sizeof(union node); srand(1234); for (i = 0; i < nmemb; i++) { nodes[i].val = rand(); nodes[i].nexti = i + 1; } qsort(nodes, nmemb, sizeof(union node), cmp_node); for (i = 0; i < nmemb; i++) { union node *n = &nodes[i]; n->next = n->nexti >= nmemb ? NULL : &nodes[n->nexti]; } return (void **)nodes; } static inline unsigned long long timerfold(struct timeval *tv) { return tv->tv_sec * 1000000ULL + tv->tv_usec; } #define LOOPS 10 void memtest(char *name, unsigned char *mem) { long k; struct timeval start, end, res; unsigned long long max, min, sum, r; int i; char title[128], result[128]; if (!mem) { fprintf(stderr, "Failed to allocate %lu bytes of memory. Test \"%s\" exits.\n", msize, name); return; } #ifdef HAVE_STREAM_LIB if (thistest == STREAM) { do_stream(name, mem); goto out; } #endif max = 0; min = ~0UL; sum = 0; /* * Note: 0th pass allocates the pages, don't measure */ for (i = 0; i < LOOPS+1; i++) { clearcache(mem, msize); switch (thistest) { case PTRCHASE: { void **ptr; ptr = ptrchase_init(mem); gettimeofday(&start,NULL); while (*ptr) ptr = (void **)*ptr; gettimeofday(&end,NULL); /* Side effect to trick the optimizer */ *ptr = "bla"; break; } case MEMSET: gettimeofday(&start,NULL); memset(mem, 0xff, msize); gettimeofday(&end,NULL); break; case MEMCPY: gettimeofday(&start,NULL); memcpy(mem, mem + msize/2, msize/2); gettimeofday(&end,NULL); break; case FORWARD: /* simple kernel to just fetch cachelines and write them back. will trigger hardware prefetch */ gettimeofday(&start,NULL); for (k = 0; k < msize; k+=CACHELINESIZE) mem[k]++; gettimeofday(&end,NULL); break; case BACKWARD: gettimeofday(&start,NULL); for (k = msize-5; k > 0; k-=CACHELINESIZE) mem[k]--; gettimeofday(&end,NULL); break; #ifdef HAVE_MT case RANDOM2: { unsigned * __restrict m = (unsigned *)mem; unsigned max = msize / sizeof(unsigned); unsigned mask; mt_init(); mask = 1; while (mask < max) mask = (mask << 1) | 1; /* * There's no guarantee all memory is touched, but * we assume (hope) that the distribution of the MT * is good enough to touch most. */ gettimeofday(&start,NULL); for (k = 0; k < max; k++) { unsigned idx = mt_random() & mask; if (idx >= max) idx -= max; m[idx]++; } gettimeofday(&end,NULL); } #endif default: break; } if (!i) continue; /* don't count allocation pass */ timersub(&end, &start, &res); r = timerfold(&res); if (r > max) max = r; if (r < min) min = r; sum += r; } sprintf(title, "%s%s%s", name, delim, testname[thistest]); #define H(t) (((double)msize) / ((double)t)) #define D3 delim,delim,delim sprintf(result, "Avg%s%.2f%sMB/s%sMax%s%.2f%sMB/s%sMin%s%.2f%sMB/s", delim, H(sum/LOOPS), D3, H(min), D3, H(max), delim); #undef H #undef D3 output(title,result); #ifdef HAVE_STREAM_LIB out: #endif /* Just to make sure that when we switch CPUs that the old guy doesn't still keep it around. */ clearcache(mem, msize); numa_free(mem, msize); } int popcnt(unsigned long val) { int i = 0, cnt = 0; while (val >> i) { if ((1UL << i) & val) cnt++; i++; } return cnt; } int max_node; void test(enum test type) { unsigned long mask; int i, k; char buf[512]; struct bitmask *nodes; nodes = numa_allocate_nodemask(); thistest = type; if (regression_testing) { printf("\nTest %s doing 1 of %d nodes and 1 of %d masks.\n", testname[thistest], fract_nodes, FRACT_MASKS); } memtest("memory with no policy", numa_alloc(msize)); memtest("local memory", numa_alloc_local(msize)); memtest("memory interleaved on all nodes", numa_alloc_interleaved(msize)); for (i = 0; i <= max_node; i++) { if (regression_testing && (i % fract_nodes)) { /* for regression testing (-t) do only every eighth node */ continue; } sprintf(buf, "memory on node %d", i); memtest(buf, numa_alloc_onnode(msize, i)); } for (mask = 1, i = 0; mask < (1UL<<(max_node+1)); mask++, i++) { int w; char buf2[10]; if (popcnt(mask) == 1) continue; if (regression_testing && (i > 50)) { break; } if (regression_testing && (i % FRACT_MASKS)) { /* for regression testing (-t) do only every 32nd mask permutation */ continue; } numa_bitmask_clearall(nodes); for (w = 0; mask >> w; w++) { if ((mask >> w) & 1) numa_bitmask_setbit(nodes, w); } sprintf(buf, "memory interleaved on"); for (k = 0; k <= max_node; k++) if ((1UL< 0) { numa_bitmask_clearall(nodes); numa_bitmask_setbit(nodes, 0); numa_bitmask_setbit(nodes, 1); numa_set_interleave_mask(nodes); memtest("manual interleaving on node 0/1", numa_alloc(msize)); printf("current interleave node %d\n", numa_get_interleave_node()); } numa_set_interleave_mask(numa_no_nodes_ptr); nodes = numa_allocate_nodemask(); for (i = 0; i <= max_node; i++) { int oldhn = numa_preferred(); if (regression_testing && (i % fract_nodes)) { /* for regression testing (-t) do only every eighth node */ continue; } numa_run_on_node(i); printf("running on node %d, preferred node %d\n",i, oldhn); memtest("local memory", numa_alloc_local(msize)); memtest("memory interleaved on all nodes", numa_alloc_interleaved(msize)); if (max_node >= 1) { numa_bitmask_clearall(nodes); numa_bitmask_setbit(nodes, 0); numa_bitmask_setbit(nodes, 1); memtest("memory interleaved on node 0/1", numa_alloc_interleaved_subset(msize, nodes)); } for (k = 0; k <= max_node; k++) { if (k == i) continue; if (regression_testing && (k % fract_nodes)) { /* for regression testing (-t) do only every eighth node */ continue; } sprintf(buf, "alloc on node %d", k); numa_bitmask_clearall(nodes); numa_bitmask_setbit(nodes, k); numa_set_membind(nodes); memtest(buf, numa_alloc(msize)); numa_set_membind(numa_all_nodes_ptr); } numa_set_localalloc(); memtest("local allocation", numa_alloc(msize)); numa_set_preferred((i+1) % (1+max_node)); memtest("setting wrong preferred node", numa_alloc(msize)); numa_set_preferred(i); memtest("setting correct preferred node", numa_alloc(msize)); numa_set_preferred(-1); if (!delim[0]) printf("\n\n\n"); } /* numa_run_on_node_mask is not tested */ } void usage(void) { int i; printf("usage: numademo [-S] [-f] [-c] [-e] [-t] msize[kmg] {tests}\nNo tests means run all.\n"); printf("-c output CSV data. -f run even without NUMA API. -S run stupid tests. -e exit on error\n"); printf("-t regression test; do not run all node combinations\n"); printf("valid tests:"); for (i = 0; testname[i]; i++) printf(" %s", testname[i]); putchar('\n'); exit(1); } /* duplicated to make numademo standalone */ long memsize(char *s) { char *end; long length = strtoul(s,&end,0); switch (toupper(*end)) { case 'G': length *= 1024; /*FALL THROUGH*/ case 'M': length *= 1024; /*FALL THROUGH*/ case 'K': length *= 1024; break; } return length; } int main(int ac, char **av) { int simple_tests = 0; while (av[1] && av[1][0] == '-') { ac--; switch (av[1][1]) { case 'c': delim = ","; break; case 'f': force = 1; break; case 'S': simple_tests = 1; break; case 'e': numa_exit_on_error = 1; numa_exit_on_warn = 1; break; case 't': regression_testing = 1; break; default: usage(); break; } ++av; } if (!av[1]) usage(); if (numa_available() < 0) { printf("your system does not support the numa API.\n"); if (!force) exit(1); } max_node = numa_max_node(); printf("%d nodes available\n", max_node+1); fract_nodes = ((max_node/8)*2) + FRACT_NODES; if (max_node <= 2) regression_testing = 0; /* set -t auto-off for small systems */ msize = memsize(av[1]); if (!msize) usage(); #ifdef HAVE_STREAM_LIB stream_setmem(msize); #endif if (av[2] == NULL) { test(MEMSET); test(MEMCPY); if (simple_tests) { test(FORWARD); test(BACKWARD); } #ifdef HAVE_MT test(RANDOM2); #endif #ifdef HAVE_STREAM_LIB test(STREAM); #endif if (msize >= sizeof(union node)) { test(PTRCHASE); } else { fprintf(stderr, "You must set msize at least %lu bytes for ptrchase test.\n", sizeof(union node)); exit(1); } } else { int k; for (k = 2; k < ac; k++) { int i; int found = 0; for (i = 0; testname[i]; i++) { if (!strcmp(testname[i],av[k])) { test(i); found = 1; break; } } if (!found) { fprintf(stderr,"unknown test `%s'\n", av[k]); usage(); } } } return 0; } ./numactl-2.0.9~rc5/getlibdir0000755000175000017500000000013612213661422014502 0ustar ianwianw#!/bin/sh case `uname -m` in s390x|x86_64|ppc64) echo -n lib64 ;; *) echo -n lib ;; esac ./numactl-2.0.9~rc5/manlinks0000644000175000017500000000021012213661422014337 0ustar ianwianw#!/bin/sh # print names of all functions listed in numa.3 # no globals grep '^\.BI.*numa.*(' numa.3 | sed -e 's/.*\(numa_.*\)(.*/\1/' ./numactl-2.0.9~rc5/TODO0000644000175000017500000000174312213661422013304 0ustar ianwianwlast update Aug 16 2007: need to fix hugetlbfs to allow holey files (for numactl) numademo numbers seem to be unstable. investigate. need more test programs Replace unreliable counters in numamon with supported ones. According to Alex Tomas: seems ht bus utilization can be found by (cmd+data+bufrelease / cmd+data+bufrelease+nop) according to chm in codeanalyst. quote from .chm: Buses 0,1, 2 ?The number of Dwords transmitted (or unused, in the case +of Nops) on the outgoing side of the HyperTransport links. The sum of all +four sub-events (all four unit mask bits set) directly reflects the +transmission rate of the link. Calculate link utilization by dividing the +combined Command, Data, and Buffer Release count (unit mask 07h) by that +value plus the Nop count (unit mask 08h). Bandwi dth in terms of bytes per unit-time for any one component, or +combination of components, is calculated by multiplying the count by four and +dividing by elapsed time. and 0xE9 is also documented ./numactl-2.0.9~rc5/mkolddemo0000755000175000017500000000031712213661422014511 0ustar ianwianw# test the numacompat1.h stuff by compiling an old version of numademo.c cc -L. -lnuma -I. -DNUMA_VERSION1_COMPATIBILITY -o olddemo olddemo.c export LD_LIBRARY_PATH=. echo "executing olddemo:" ./olddemo ./numactl-2.0.9~rc5/test/0000755000175000017500000000000012213661423013567 5ustar ianwianw./numactl-2.0.9~rc5/test/shmtest0000755000175000017500000000305512213661423015207 0ustar ianwianw#!/bin/sh # basic shared memory policy test # hugetlbfs and tmpfs must be mounted on these mount points TMPFS=/dev/shm HUGE=/huge #valgrind 3.0.1 doesn't implement mbind() yet on x86-64 #VALGRIND="valgrind --tool=memcheck" VALGRIND= set -e export PATH=`pwd`/..:$PATH export LD_LIBRARY_PATH=`pwd`/.. numactl() { $VALGRIND ../numactl "$@" } failure() { numastat > after set +e diff -u before after echo echo TEST FAILED exit 1 } success() { echo test succeeded } checkpoint() { numastat > before } trap failure EXIT basictest() { echo initial checkpoint numactl --length=20m $1 --dump echo interleave checkpoint numactl --offset=2m --length=2m $1 --strict --interleave=0,1 --verify --dump echo interleave verify checkpoint numactl $1 --dump echo membind setup checkpoint numactl --offset 4m --length=2m $1 --strict --membind=1 --verify --dump echo membind verify checkpoint numactl $1 --dump echo preferred setup checkpoint numactl --offset 6m --length 2m $1 --strict --preferred=1 --verify --dump echo preferred verify checkpoint numactl $1 --dump # check overlaps here } cleanupshm() { if [ -f $1 ] ; then ipcrm -M `./ftok $1` || true rm $1 fi } banner() { echo echo ++++++++++++ $1 +++++++++++++++ echo } banner shm cleanupshm A basictest --shm=A cleanupshm A banner hugeshm cleanupshm B basictest "--huge --shm=B" cleanupshm B banner tmpfs basictest "--file $TMPFS/B" rm $TMPFS/B # first need a way to create holey hugetlbfs files. #banner hugetlbfs #basictest "--file $HUGE/B" #rm /hugetlbfs/B rm before trap success EXIT ./numactl-2.0.9~rc5/test/move_pages.c0000755000175000017500000000423612213661423016070 0ustar ianwianw/* * Test program to test the moving of individual pages in a process. * * (C) 2006 Silicon Graphics, Inc. * Christoph Lameter */ #include #include #include "numa.h" #include #include unsigned int pagesize; unsigned int page_count = 32; char *page_base; char *pages; void **addr; int *status; int *nodes; int errors; int nr_nodes; int main(int argc, char **argv) { int i, rc; pagesize = getpagesize(); nr_nodes = numa_max_node(); if (nr_nodes < 2) { printf("A minimum of 2 nodes is required for this test.\n"); exit(1); } setbuf(stdout, NULL); printf("move_pages() test ......\n"); if (argc > 1) sscanf(argv[1], "%d", &page_count); printf("pages=%d (%s)\n", page_count, argv[1]); page_base = malloc((pagesize + 1) * page_count); addr = malloc(sizeof(char *) * page_count); status = malloc(sizeof(int *) * page_count); nodes = malloc(sizeof(int *) * page_count); if (!page_base || !addr || !status || !nodes) { printf("Unable to allocate memory\n"); exit(1); } pages = (void *) ((((long)page_base) & ~((long)(pagesize - 1))) + pagesize); for (i = 0; i < page_count; i++) { if (i != 2) /* We leave page 2 unallocated */ pages[ i * pagesize ] = (char) i; addr[i] = pages + i * pagesize; nodes[i] = (i % nr_nodes); status[i] = -123; } printf("\nMoving pages to start node ...\n"); rc = numa_move_pages(0, page_count, addr, NULL, status, 0); if (rc < 0) perror("move_pages"); for (i = 0; i < page_count; i++) printf("Page %d vaddr=%p node=%d\n", i, pages + i * pagesize, status[i]); printf("\nMoving pages to target nodes ...\n"); rc = numa_move_pages(0, page_count, addr, nodes, status, 0); if (rc < 0) { perror("move_pages"); errors++; } for (i = 0; i < page_count; i++) { if (i != 2) { if (pages[ i* pagesize ] != (char) i) errors++; else if (nodes[i] != (i % nr_nodes)) errors++; } } for (i = 0; i < page_count; i++) { printf("Page %d vaddr=%lx node=%d\n", i, (unsigned long)(pages + i * pagesize), status[i]); } if (!errors) printf("Test successful.\n"); else printf("%d errors.\n", errors); return errors > 0 ? 1 : 0; } ./numactl-2.0.9~rc5/test/node-parse.c0000644000175000017500000000066112213661423015773 0ustar ianwianw/* Test wrapper for the nodemask parser */ #include #include "numa.h" #include "util.h" /* For util.c. Fixme. */ void usage(void) { exit(1); } int main(int ac, char **av) { int err = 0; while (*++av) { struct bitmask *mask = numa_parse_nodestring(*av); if (!mask) { printf("Failed to convert `%s'\n", *av); err |= 1; continue; } printmask("result", mask); numa_bitmask_free(mask); } return err; } ./numactl-2.0.9~rc5/test/mynode.c0000644000175000017500000000042112213661423015223 0ustar ianwianw#include #include #include int main(void) { int nd; char *man = numa_alloc(1000); *man = 1; if (get_mempolicy(&nd, NULL, 0, man, MPOL_F_NODE|MPOL_F_ADDR) < 0) perror("get_mempolicy"); else printf("my node %d\n", nd); return 0; } ./numactl-2.0.9~rc5/test/tshared.c0000644000175000017500000000167612213661423015377 0ustar ianwianw#include #include #include #include #include #include #define err(x) perror(x),exit(1) enum SZ { MEMSZ = 100<<20, NTHR = 10, }; /* test if shared interleaving state works. */ int main(void) { int i, k; char *mem; int pagesz = getpagesize(); int max_node; if (numa_available() < 0) { printf("no NUMA API available\n"); exit(1); } max_node = numa_max_node(); mem = numa_alloc_interleaved(MEMSZ); for (i = 0; i < NTHR; i++) { if (fork() == 0) { for (k = i*pagesz; k < MEMSZ; k += pagesz * NTHR) { mem[k] = 1; } _exit(0); } } for (i = 0; i < NTHR; i++) wait(NULL); k = 0; for (i = 0; i < MEMSZ; i += pagesz) { int nd; if (get_mempolicy(&nd, NULL, 0, mem + i, MPOL_F_NODE|MPOL_F_ADDR) < 0) err("get_mempolicy"); if (nd != k) printf("offset %d node %d expected %d\n", i, nd, k); k = (k+1)%(max_node+1); } return 0; } ./numactl-2.0.9~rc5/test/bind_range0000600000175000017500000000416712213661423015602 0ustar ianwianw#!/bin/bash # This simple script checks --all/-a option which is used for # supressing of default cpuset awareness of options --cpunodebind, # --physcpubind, --interleave, --preferred and --membind. # NOTE: Test needs two nodes and two cpus at least export old_mask eval_test() { # echo "Running $1.." $1 if [ $? == 1 ] ; then echo -e "$1 FAILED!" reset_mask exit 1 fi echo -e "$1 PASSED" } function check_arg_order { ../numactl --all --physcpubind=$HIGHESTCPU ls > /dev/null 2>&1 if [ $? == 1 ] ; then return 1; fi ../numactl --physcpubind=$HIGHESTCPU --all ls > /dev/null 2>&1 if [ $? == 0 ] ; then return 1; fi return 0 } function check_physcpubind { reset_mask set_cpu_affinity 0 ../numactl --physcpubind=$HIGHESTCPU ls > /dev/null 2>&1 if [ $? == 0 ] ; then # shouldn't pass so easy return 1; fi ../numactl --all --physcpubind=$HIGHESTCPU ls > /dev/null 2>&1 if [ $? == 1 ] ; then # shouldn't fail return 1; fi return 0 } function check_cpunodebind { local low_cpu_range local high_cpu reset_mask low_cpu_range=$(cat /sys/devices/system/node/node$LOWESTNODE/cpulist) set_cpu_affinity $low_cpu_range ../numactl --cpunodebind=$HIGHESTNODE ls > /dev/null 2>&1 if [ $? == 1 ] ; then # should pass return 1; fi ../numactl --all --cpunodebind=$HIGHESTNODE ls > /dev/null 2>&1 if [ $? == 1 ] ; then # should pass for sure return 1; fi return 0 } function set_cpu_affinity { taskset -p -c $1 $$ > /dev/null #echo -e "\taffinity of shell was set to" $1 } function get_mask { old_mask=$(taskset -p $$ | cut -f2 -d: | sed -e 's/^[ \t]*//') } function reset_mask { taskset -p $old_mask $$ > /dev/null #echo -e "\taffinity of shell was reset to" $old_mask } HIGHESTCPU=$(grep 'processor' /proc/cpuinfo | tail -n1 | cut -f2 -d':') HIGHESTCPU=$(echo $HIGHESTCPU | cut -f2 -d' ') HIGHESTNODE=$(numactl -H | grep -e 'node [0-9]* cpus' | tail -n1 | cut -f2 -d' ') LOWESTNODE=$(numactl -H | grep -e 'node [0-9]* cpus' | head -n1 | cut -f2 -d' ') get_mask eval_test check_arg_order eval_test check_physcpubind eval_test check_cpunodebind reset_mask exit 0 ./numactl-2.0.9~rc5/test/regress0000755000175000017500000001166112213661423015174 0ustar ianwianw#!/bin/bash # simple regression test for numactl/numaapi # must be run from 'test' directory of numactl source package, # after build [just use 'make test'] # note the statistics checks may fail when the system is under # memory pressure # Copyright 2003,2004 Andi Kleen, SuSE Labs. BASE=`(cd ..; pwd)` export LD_LIBRARY_PATH=$BASE export PATH=$BASE:$PATH : ${NUMACTL:=$BASE/numactl} VALGRIND=${VALGRIND:-} MB=$[1024*1024] SIZE=$[15 * $MB] DEMOSIZE=$[10 * $MB] STAT_INTERVAL=5 PAGESIZE=$(./pagesize) PAGES=$[ $SIZE / $PAGESIZE ] HALFPAGES=$[ $PAGES / 2 ] HALFPAGES=$[ $HALFPAGES - 100 ] DOUBLEPAGES=$[ $PAGES * 2 ] DOUBLEPAGES=$[ $DOUBLEPAGES - 200 ] NEEDPAGES=$[ $DOUBLEPAGES + $DOUBLEPAGES / 5 ] # 20% spare EXIT=0 declare -i maxnode declare -a node # ===================================================================== numactl() { $VALGRIND $NUMACTL "$@" } failed() { echo '=======FAILED' echo "Check if machine doesn't have background jobs and try again" EXIT=1 } # nstat statname node nstat() { sleep $STAT_INTERVAL declare -a fields numastat | grep $1 | while read -a fields ; do echo ${fields[$[1 + $2]]} done } probe_hardware() { declare -i n=0 numnodes=$(numactl --hardware | awk '/^available/ { print $2 }') maxnode=$(expr $numnodes - 1) # find nodes with at least NEEDPAGES of free memory for i in $(seq 0 $maxnode) ; do free=$(numactl --hardware | fgrep " $i free" | awk '{print $4}') free=$(( free * MB )) if [[ $((free / PAGESIZE)) -ge $NEEDPAGES ]]; then node[$n]=$i n=$((n + 1 )) fi done numnodes=$n maxnode=$(expr $numnodes - 1) if [ $numnodes -lt 2 ] ; then echo "need at least two nodes with at least $NEEDPAGES each of" echo "free memory for mempolicy regression tests" exit 1 fi } # ========================================================================= _test_process_state() { echo '=>testing numactl' "$@" "memhog -H $SIZE" numactl "$@" memhog -H $SIZE || failed } test_process_state() { declare -i n0=${node[0]} n1=${node[1]} _test_process_state --interleave=$n1 a0=`nstat interleave_hit $n0` a1=`nstat interleave_hit $n1` _test_process_state --interleave=$n0,$n1 b0=`nstat interleave_hit $n0` b1=`nstat interleave_hit $n1` if [ $(expr $b1 - $a1) -lt $HALFPAGES ]; then echo "interleaving test failed $n1 $b1 $a1" failed fi if [ $(expr $b0 - $a0) -lt $HALFPAGES ]; then echo "interleaving test failed $n0 $b0 $a0" failed fi _test_process_state --interleave=all _test_process_state --membind=all a=$(expr $(nstat numa_hit $n0) + $(nstat numa_hit $n1)) _test_process_state --membind=$n0,$n1 b=$(expr $(nstat numa_hit $n0) + $(nstat numa_hit $n1)) if [ $(expr $b - $a) -lt $PAGES ]; then echo "membind test failed $n1 $b $a ($PAGES)" failed fi for i in $(seq 0 $maxnode) ; do declare -i ni=${node[$i]} a=`nstat numa_hit $ni` _test_process_state --membind=$ni _test_process_state --preferred=$ni b=`nstat numa_hit $ni` if [ $(expr $b - $a) -lt $DOUBLEPAGES ]; then echo "membind/preferred on node $ni failed $b $a" failed fi done _test_process_state --localalloc } # ========================================================================= # test mbind _test_mbind() { echo '=>testing memhog -H' "$@" memhog -H $SIZE "$@" || failed } test_mbind() { declare -i n0=${node[0]} n1=${node[1]} a0=`nstat interleave_hit $n0` a1=`nstat interleave_hit $n1` _test_mbind interleave $n0,$n1 b0=`nstat interleave_hit $n0` b1=`nstat interleave_hit $n1` if [ $(expr $b1 - $a1) -lt $HALFPAGES ]; then echo "interleaving test 2 failed $n1 $b1 $a1 expected $HALFPAGES" failed fi if [ $(expr $b0 - $a0) -lt $HALFPAGES ]; then echo "interleaving test 2 failed $n0 $b0 $a0" failed fi _test_mbind interleave all a=$(expr $(nstat numa_hit 0) + $(nstat numa_hit 1)) _test_mbind membind $n0,1 b=$(expr $(nstat numa_hit 0) + $(nstat numa_hit 1)) if [ $(expr $b - $a) -lt $PAGES ]; then echo "membind test 2 failed $b $a ($PAGES)" failed fi for i in $(seq 0 $maxnode) ; do declare -i ni=${node[$i]} a=`nstat numa_hit $ni` _test_mbind membind $ni _test_mbind preferred $ni b=`nstat numa_hit $ni` if [ $(expr $b - $a) -lt $DOUBLEPAGES ]; then echo "membind/preferred test 2 on node $ni failed $b $a" failed fi done } # ========================================================================= main() { # Get the interval vm statistics refresh at if [ -e /proc/sys/vm/stat_interval ]; then STAT_INTERVAL=`cat /proc/sys/vm/stat_interval` STAT_INTERVAL=`expr $STAT_INTERVAL \* 2` fi probe_hardware numactl --cpubind=${node[0]} /bin/true numactl --cpubind=${node[1]} /bin/true numactl -s numactl --hardware numastat > A test_process_state test_mbind numastat > B diff -u A B rm A B if [ "$EXIT" = 0 ] ; then echo '========SUCCESS' else echo '========FAILURE' exit 1 fi } # ========================================================================= main ./numactl-2.0.9~rc5/test/move_pages0000755000175000017500000004760212213661423015653 0ustar ianwianwELF2 @@(7@8@.+@@@@@@@@@HHHH`H`0pp`p`@@\\pXX@X@/lib/ld-linux-ia64.so.2GNUSuSESuSE GNUSJl^-;   l  g[1sA`libnuma.so.1__gmon_start___Jv_RegisterClassesnuma_move_pagesnuma_max_nodelibc.so.6.1exitperrorputsprintfgetpagesizestdoutmallocsscanfsetbuf__libc_start_mainlibnuma_1.1libnuma_1.2GLIBC_2.20AE BE Oii `G`G`'P```p````` ` ` ` ` ` 0B `?#`@!U  !H A80(``x$Hx$Hx$Hx $Hx$Hx$Hx$pHx$`Hx $PHx$$@Hx($0Hx,$ H x$A $ > $ > 4: ; I?  &IU%% : ; I$ > $ > $ >   I : ;  : ;I8 : ; : ; I8 I !I/ .? : ; ' I@: ; I4: ; I4: ; I4: ; I? < 4: ; I?  %% $ > : ; I$ > .? : ; ' @ .? : ; ' @ : ; I 4: ; I U 4: ; I  I &I I!' I4: ; I? < U%# init.cK /usr/src/packages/BUILD/glibc-2.9/cc-nptl/csucrti.S  @!!"!!!!!!!!!!! @:"" `@"" test/usr/lib/gcc/ia64-suse-linux/4.3/include/usr/include/bits/usr/includemove_pages.cstddef.htypes.hlibio.hstdio.h  @%#,@ &m,!!!!/y )"#!/$//&z z  /!0-0!/".$0"-P 0-0!###p.0 %#M.!v/^0vG ../../../libgcc/../gcc/config/ia64lib1funcs.asm @!!!"!!!!/V /usr/lib/gcc/ia64-suse-linux/4.3/includeelf-init.cstddef.h @^+? }K /usr/src/packages/BUILD/glibc-2.9/cc-nptl/csucrtn.S @!! @!!!_IO_stdin_usedshort unsigned intshort intunsigned charlong int/usr/src/packages/BUILD/glibc-2.9/csuGNU C 4.3.2 [gcc-4_3-branch revision 141291]_IO_FILE_IO_save_endsize_tpagesize_IO_write_ptr_flags_IO_buf_baseerrors_markers_IO_read_end_locknr_nodesaddr_cur_column_posargv_sbuf_old_offsetargcpage_countlong long unsigned int_IO_marker_shortbuf_IO_write_base_unused2_IO_read_ptr_IO_buf_endstatusmain_next__pad1__pad2__pad3__pad4__pad5page_basetest/move_pages.c/home/estes02/cpw/numactl-dev_IO_write_end__off64_t_fileno_chain__off_t_IO_backup_basestdin_flags2_mode_IO_read_base_vtable_offset_IO_save_basepagesstdout_IO_lock_tenvp__libc_csu_initelf-init.c__init_array_start__init_array_end__libc_csu_fini\ |2 P  ! !2 "2 2_ "_0 0"  " P   @ @@@`@@ "A@@@@OspWG.symtab.strtab.shstrtab.interp.note.ABI-tag.note.SuSE.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rela.dyn.rela.IA_64.pltoff.init.plt.text.fini.rodata.opd.IA_64.unwind_info.IA_64.unwind.init_array.fini_array.jcr.dynamic.data.ctors.dtors.got.sdata.sbss.bss.comment.debug_aranges.debug_pubnames.debug_info.debug_abbrev.debug_line.debug_str.debug_loc.debug_ranges.comment.SUSE.OPTs@#@ 1@<@$S@@@POo@Y @ha@io@vo@PX@XH@ @@@  @ `@`@2@H@p`@`pX@XH`H```h`hp`p p`px`x`!`P`P&`-(`$D3p`$8$A P0!`"l)Wz+0/240`5f5B -@ PL2@@@@@@@@@ @ @ X@ @ @@ @`@@@`@X@H```h`p`p`x```P``(` p`!"#$%&'()* @p$ @ *@ 0x`>`Lh`Y`b @px @``h````H` p` p`/@? @F U i*`@ {0`8``@`p`H`@@%P`+B p`O@_zX```(`$` `x` h`$`- @ $ @ init.cinitfini.cgmon_initializer_init_fini__CTOR_LIST____DTOR_LIST____JCR_LIST__dtor_ptr__do_global_dtors_aux__do_jv_register_classesmove_pages.celf-init.c__CTOR_END____DTOR_END____JCR_END___GLOBAL_OFFSET_TABLE___init_array_end__init_array_start_DYNAMICdata_startprintf@@GLIBC_2.2__libc_csu_fini_start__gmon_start___Jv_RegisterClassesmalloc@@GLIBC_2.2nr_nodesputs@@GLIBC_2.2stdout@@GLIBC_2.2page_base_IO_stdin_usednodes__data_starterrors__modsi3exit@@GLIBC_2.2__do_global_ctors_auxnuma_move_pages@@libnuma_1.2pagesgetpagesize@@GLIBC_2.2__dso_handle__libc_csu_initnuma_max_node@@libnuma_1.1statussetbuf@@GLIBC_2.2addr__libc_ia64_register_backing_store_base__bss_start__libc_start_main@@GLIBC_2.2perror@@GLIBC_2.2page_count_endpagesize_edatasscanf@@GLIBC_2.2main./numactl-2.0.9~rc5/test/prefered.c0000755000175000017500000000250212213661423015531 0ustar ianwianw/* Test prefer policy */ #include "numa.h" #include "numaif.h" #include #include #include #include #include #include #define err(x) perror(x),exit(1) int main(void) { int max = numa_max_node(); int maxmask = numa_num_possible_nodes(); struct bitmask *nodes, *mask; int pagesize = getpagesize(); int i; int pol; int node; int err = 0; nodes = numa_bitmask_alloc(maxmask); mask = numa_bitmask_alloc(maxmask); for (i = max; i >= 0; --i) { char *mem = mmap(NULL, pagesize*(max+1), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); char *adr = mem; if (mem == (char *)-1) err("mmap"); printf("%d offset %lx\n", i, (long)(adr - mem)); numa_bitmask_clearall(nodes); numa_bitmask_clearall(mask); numa_bitmask_setbit(nodes, i); if (mbind(adr, pagesize, MPOL_PREFERRED, nodes->maskp, nodes->size, 0) < 0) err("mbind"); ++*adr; if (get_mempolicy(&pol, mask->maskp, mask->size, adr, MPOL_F_ADDR) < 0) err("get_mempolicy"); assert(pol == MPOL_PREFERRED); assert(numa_bitmask_isbitset(mask, i)); node = 0x123; if (get_mempolicy(&node, NULL, 0, adr, MPOL_F_ADDR|MPOL_F_NODE) < 0) err("get_mempolicy2"); printf("got node %d expected %d\n", node, i); if (node != i) err = 1; } return err; } ./numactl-2.0.9~rc5/test/migrate_pages.c0000755000175000017500000000563012213661423016551 0ustar ianwianw/* * Test program to test the moving of a processes pages. * * (C) 2006 Silicon Graphics, Inc. * Christoph Lameter */ #include #include #include #include #include unsigned int pagesize; unsigned int page_count = 32; char *page_base; char *pages; void **addr; int *status; int *nodes; int errors; int nr_nodes; struct bitmask *old_nodes; struct bitmask *new_nodes; int main(int argc, char **argv) { int i, rc; pagesize = getpagesize(); nr_nodes = numa_max_node()+1; old_nodes = numa_bitmask_alloc(nr_nodes); new_nodes = numa_bitmask_alloc(nr_nodes); numa_bitmask_setbit(old_nodes, 1); numa_bitmask_setbit(new_nodes, 0); if (nr_nodes < 2) { printf("A minimum of 2 nodes is required for this test.\n"); exit(1); } setbuf(stdout, NULL); printf("migrate_pages() test ......\n"); if (argc > 1) sscanf(argv[1], "%d", &page_count); page_base = malloc((pagesize + 1) * page_count); addr = malloc(sizeof(char *) * page_count); status = malloc(sizeof(int *) * page_count); nodes = malloc(sizeof(int *) * page_count); if (!page_base || !addr || !status || !nodes) { printf("Unable to allocate memory\n"); exit(1); } pages = (void *) ((((long)page_base) & ~((long)(pagesize - 1))) + pagesize); for (i = 0; i < page_count; i++) { if (i != 2) /* We leave page 2 unallocated */ pages[ i * pagesize ] = (char) i; addr[i] = pages + i * pagesize; nodes[i] = 1; status[i] = -123; } /* Move to starting node */ rc = numa_move_pages(0, page_count, addr, nodes, status, 0); if (rc < 0 && errno != ENOENT) { perror("move_pages"); exit(1); } /* Verify correct startup locations */ printf("Page location at the beginning of the test\n"); printf("------------------------------------------\n"); numa_move_pages(0, page_count, addr, NULL, status, 0); for (i = 0; i < page_count; i++) { printf("Page %d vaddr=%p node=%d\n", i, pages + i * pagesize, status[i]); if (i != 2 && status[i] != 1) { printf("Bad page state before migrate_pages. Page %d status %d\n",i, status[i]); exit(1); } } /* Move to node zero */ numa_move_pages(0, page_count, addr, nodes, status, 0); printf("\nMigrating the current processes pages ...\n"); rc = numa_migrate_pages(0, old_nodes, new_nodes); if (rc < 0) { perror("numa_migrate_pages failed"); errors++; } /* Get page state after migration */ numa_move_pages(0, page_count, addr, NULL, status, 0); for (i = 0; i < page_count; i++) { printf("Page %d vaddr=%lx node=%d\n", i, (unsigned long)(pages + i * pagesize), status[i]); if (i != 2) { if (pages[ i* pagesize ] != (char) i) { printf("*** Page contents corrupted.\n"); errors++; } else if (status[i]) { printf("*** Page on the wrong node\n"); errors++; } } } if (!errors) printf("Test successful.\n"); else printf("%d errors.\n", errors); return errors > 0 ? 1 : 0; } ./numactl-2.0.9~rc5/test/README0000644000175000017500000000143212213661423014447 0ustar ianwianw Various simple test scripts to verify some parts of the NUMA API. To do a full regression test run make test You should have at least two nodes on a NUMA system for the test suite. The tests in regress assume that there is enough memory free on nodes 0/1. They consider PREFERRED/INTERLEAVE not hitting the first choice node an error. They also require a relatively idle machine to avoid too much noise from memory allocation from other processes. Without that regress1 might fail. You can run the tests under valgrind with VALGRIND=valgrind make test Older valgrind versions incorrectly report a uninitialized byte error on set_mempolicy. That is a false positive. TBD: more detailed unit tests for mbind / shm / {get,set}_mempolicy Currently everything is tested using numactl only. ./numactl-2.0.9~rc5/test/checkaffinity0000755000175000017500000000145112213661423016325 0ustar ianwianw#!/bin/bash # check if affinity works BASE=`pwd`/.. export LD_LIBRARY_PATH=$BASE export PATH=$BASE:$PATH S=`numactl --show | grep nodebind:` NODES=`echo $S | sed -e "s/nodebind://"` S=`numactl --show | grep physcpubind:` CPUS=`echo $S | sed -e "s/physcpubind://"` for i in $CPUS ; do if [ "$(numactl --physcpubind=$i ./printcpu)" != "$i" ] ; then echo "--physcpubind for $i doesn't work" exit 1 fi if [ "$(numactl --physcpubind=$i numactl --show | awk '/^physcpubind/ { print $2 }' )" != "$i" ] ; then echo "--show doesn't agree with physcpubind for cpu $i" exit 1 fi done for i in $NODES ; do if [ $(numactl --cpunodebind=$i numactl --show | awk '/nodebind/ { print $2 }' ) != $i ] ; then echo "--show doesn't agree with cpunodebind for node $i" exit 1 fi done ./numactl-2.0.9~rc5/test/runltp0000755000175000017500000000137112213661423015043 0ustar ianwianw#!/bin/sh # run the Linux Test Project with various numactl settings. will run for a few hours. # must run as root # You can download LTP from http://ltp.sourceforge.net # Change LTP below to the source directory of a compiled LTP distribution LTP=/src/ltp LEN=2h LTPOPT="-q -p -t $LEN" export LD_LIBRARY_PATH=`pwd`/.. export PATH=`pwd`/..:$PATH cd $LTP for i in 1 2 3 ; do numactl --interleave=all ./runltp $LTPOPT -l n.interleave.all.$i numactl --interleave=0,1 ./runltp $LTPOPT -l n.interleave.01.$i numactl --preferred=0 --cpubind=1 ./runltp $LTPOPT -l n.preferred.$i # the VM test that allocates all memory may fail numactl --membind=1 --cpubind=0 ./runltp $LTPOPT -l n.membind1.$i numactl --membind=0,1 ./runltp $LTPOPT -l n.membind01.$i done ./numactl-2.0.9~rc5/test/distance.c0000644000175000017500000000130112213661423015520 0ustar ianwianw/* Test numa_distance */ #include #include #include int main(void) { int numnodes, a, b; if (numa_available() < 0) { printf("no numa support in kernel\n"); exit(1); } numnodes = numa_num_configured_nodes(); for (a = 0; a < numnodes; a++) { printf("%03d: ", a); if (numa_distance(a, a) != 10) { printf("%d: self distance is not 10 (%d)\n", a, numa_distance(a,a)); exit(1); } for (b = 0; b < numnodes; b++) { int d1 = numa_distance(a, b); int d2 = numa_distance(b, a); printf("%03d ", d1); if (d1 != d2) { printf("\n(%d,%d)->(%d,%d) wrong!\n",a,b,d1,d2); exit(1); } } printf("\n"); } return 0; } ./numactl-2.0.9~rc5/test/regress20000755000175000017500000000061512213661423015253 0ustar ianwianw#!/bin/sh # More regression tests for libnuma/numa api VALGRIND=${VALGRIND:-} export LD_LIBRARY_PATH=`pwd`/.. T() { echo "$@" if ! $VALGRIND "$@" ; then echo $1 FAILED!!!! exit 1 fi echo } # various tests chmod 755 bind_range # still broken #T ./prefered T ./distance T ./nodemap T ./checkaffinity T ./checktopology T ./tbitmap T ./bind_range #T ./randmap ./numactl-2.0.9~rc5/test/tbitmap.c0000644000175000017500000000542512213661423015401 0ustar ianwianw/* Unit test bitmap parser */ #define _GNU_SOURCE 1 //#include #include #include #include #include #include #include "numa.h" #define ALIGN(x,a) (((x)+(a)-1)&~((a)-1)) #define test_bit(i,p) ((p)[(i) / BITS_PER_LONG] & (1UL << ((i)%BITS_PER_LONG))) #define set_bit(i,p) ((p)[(i) / BITS_PER_LONG] |= (1UL << ((i)%BITS_PER_LONG))) #define clear_bit(i,p) ((p)[(i) / BITS_PER_LONG] &= ~(1UL << ((i)%BITS_PER_LONG))) typedef unsigned u32; #define BITS_PER_LONG (sizeof(long)*8) #define round_up(x,y) (((x) + (y) - 1) & ~((y)-1)) #define CPU_BYTES(x) (round_up(x, BITS_PER_LONG)/8) #define CPU_LONGS(x) (CPU_BYTES(x) / sizeof(long)) /* Following routine extracted from Linux 2.6.16 */ #define CHUNKSZ 32 #define nbits_to_hold_value(val) fls(val) #define unhex(c) (isdigit(c) ? (c - '0') : (toupper(c) - 'A' + 10)) #define BASEDEC 10 /* fancier cpuset lists input in decimal */ /** * bitmap_scnprintf - convert bitmap to an ASCII hex string. * @buf: byte buffer into which string is placed * @buflen: reserved size of @buf, in bytes * @mask: pointer to struct bitmask to convert * * Hex digits are grouped into comma-separated sets of eight digits per set. */ int bitmap_scnprintf(char *buf, unsigned int buflen, struct bitmask *mask) { int i, word, bit, len = 0; unsigned long val; const char *sep = ""; int chunksz; u32 chunkmask; chunksz = mask->size & (CHUNKSZ - 1); if (chunksz == 0) chunksz = CHUNKSZ; i = ALIGN(mask->size, CHUNKSZ) - CHUNKSZ; for (; i >= 0; i -= CHUNKSZ) { chunkmask = ((1ULL << chunksz) - 1); word = i / BITS_PER_LONG; bit = i % BITS_PER_LONG; val = (mask->maskp[word] >> bit) & chunkmask; len += snprintf(buf+len, buflen-len, "%s%0*lx", sep, (chunksz+3)/4, val); chunksz = CHUNKSZ; sep = ","; } return len; } extern int numa_parse_bitmap(char *buf, struct bitmask *mask); #define MASKSIZE 300 int main(void) { char buf[1024]; struct bitmask *mask, *mask2; int i; mask = numa_bitmask_alloc(MASKSIZE); mask2 = numa_bitmask_alloc(MASKSIZE); printf("Testing bitmap functions\n"); for (i = 0; i < MASKSIZE; i++) { numa_bitmask_clearall(mask); numa_bitmask_clearall(mask2); numa_bitmask_setbit(mask, i); bitmap_scnprintf(buf, sizeof(buf), mask); strcat(buf,"\n"); if (numa_parse_bitmap(buf, mask2) < 0) assert(0); if (memcmp(mask, mask2, sizeof(mask))) { bitmap_scnprintf(buf, sizeof(buf), mask2); printf("mask2 differs: %s\n", buf); assert(0); } } printf("Passed\n"); return 0; } ./numactl-2.0.9~rc5/test/regress-io0000755000175000017500000000135412213661423015577 0ustar ianwianw#!/bin/bash # test IO affinity parsing # tests may fail depending on machine setup E=0 check() { echo testing $@ if "$@" ; then true else echo failed E=1 fi } fail() { echo testing failure of $@ if "$@" ; then echo failed E=1 else true fi } BASE=`(cd ..; pwd)` export LD_LIBRARY_PATH=$BASE export PATH=$BASE:$PATH check ./node-parse file:. check ./node-parse ip:8.8.8.8 fail ./node-parse ip:127.0.0.1 IF=$(ip link ls | grep eth | cut -d: -f2 | head -1) check ./node-parse "netdev:$IF" fail ./node-parse netdev:lo DEV=$(df | awk '/\/$/ { print $1 }') check ./node-parse file:$DEV check ./node-parse block:$(basename $DEV) check ./node-parse pci:0:0.0 if [ "$E" = 0 ] ; then echo SUCCESS ; else echo FAILURE ; fi exit $E ./numactl-2.0.9~rc5/test/getnodemask.c0000644000175000017500000000110712213661423016233 0ustar ianwianw#include #include #include #include #include #include int main(int argc, char *argv[]) { nodemask_t nodemask; int rc, i; rc = numa_available(); printf("numa_available returns %d\n", rc); if (rc < 0) exit(1); nodemask_zero(&nodemask); nodemask = numa_get_run_node_mask(); for (i = 0; i < 4; i++) { printf("numa_get_run_node_mask nodemask_isset returns=0x%lx\n", nodemask_isset(&nodemask, i)); } rc = numa_run_on_node_mask(&nodemask); printf("rc=%d from numa_run_on_node_mask\n", rc); return (0); } ./numactl-2.0.9~rc5/test/pagesize.c0000644000175000017500000000014612213661423015543 0ustar ianwianw#include #include int main(void) { printf("%d\n", getpagesize()); return 0; } ./numactl-2.0.9~rc5/test/tshm.c0000644000175000017500000000413712213661423014713 0ustar ianwianw#include #include #include #include #include #define err(x) perror(x),exit(1) enum { MEMSZ = 10*1024*1024, }; struct req { enum cmd { SET = 1, CHECK, REPLY, EXIT, } cmd; long offset; long len; int policy; nodemask_t nodes; }; void worker(void) { struct req req; while (read(0, &req, sizeof(struct req) > 0)) { switch (req.cmd) { case SET: if (mbind(map + req.offset, req.len, req.policy, &req.nodes, NUMA_MAX_NODES+1, 0) < 0) err("mbind"); break; case TEST: req.cmd = REPLY; if (get_mempolicy(&req.policy, &req.nodes, NUMA_MAX_NODES+1, map + req.offset, MPOL_F_ADDR) < 0) err("get_mempolicy"); write(1, &req, sizeof(struct req)); break; case EXIT: return; default: abort(); } } } void sendreq(int fd, enum cmd cmd, int policy, long offset, long len, nodemask_t nodes) { struct req req = { .cmd = cmd, .offset = offset, .len = len, .policy = policy, .nodes = nodes }; if (write(fd, &req, sizeof(struct req)) != sizeof(struct req)) panic("bad req write"); } void readreq(int fd, int *policy, nodemask_t *nodes, long offset, long len) { struct req req; if (read(fd, &req, sizeof(struct req)) != sizeof(struct req)) panic("bad req read"); if (req.cmd != REPLY) abort(); *policy = req.policy; *nodes = req.nodes; } int main(void) { int fd = open("tshm", O_CREAT, 0600); close(fd); key_t key = ftok("tshm", 1); int shm = shmget(key, MEMSZ, IPC_CREAT|0600); if (shm < 0) err("shmget"); char *map = shmat(shm, NULL, 0); printf("map = %p\n", map); unsigned long nmask = 0x3; if (mbind(map, MEMSZ, MPOL_INTERLEAVE, &nmask, 4, 0) < 0) err("mbind1"); int fd[2]; if (pipe(fd) < 0) err("pipe"); if (fork() == 0) { close(0); close(1); dup2(fd[0], 0); dup2(fd[1], 1); worker(); _exit(0); } int pagesz = getpagesize(); int i; srand(1); for (;;) { /* chose random offset */ /* either in child or here */ /* change policy */ /* ask other guy to check */ } shmdt(map); shmctl(shm, IPC_RMID, 0); } ./numactl-2.0.9~rc5/test/mbind_mig_pages.c0000755000175000017500000000577112213661423017054 0ustar ianwianw/* * Test program to test the moving of pages using mbind. * * (C) 2006 Silicon Graphics, Inc. * Christoph Lameter */ #include #include #include #include #include #include unsigned int pagesize; unsigned int page_count = 32; char *page_base; char *pages; void **addr; int *status; int *nodes; int errors; int nr_nodes; struct bitmask *old_nodes; struct bitmask *new_nodes; int main(int argc, char **argv) { int i, rc; pagesize = getpagesize(); nr_nodes = numa_max_node()+1; old_nodes = numa_bitmask_alloc(nr_nodes); new_nodes = numa_bitmask_alloc(nr_nodes); numa_bitmask_setbit(old_nodes, 0); numa_bitmask_setbit(new_nodes, 1); if (nr_nodes < 2) { printf("A minimum of 2 nodes is required for this test.\n"); exit(1); } setbuf(stdout, NULL); printf("mbind migration test ......\n"); if (argc > 1) sscanf(argv[1], "%d", &page_count); page_base = malloc((pagesize + 1) * page_count); addr = malloc(sizeof(char *) * page_count); status = malloc(sizeof(int *) * page_count); nodes = malloc(sizeof(int *) * page_count); if (!page_base || !addr || !status || !nodes) { printf("Unable to allocate memory\n"); exit(1); } pages = (void *) ((((long)page_base) & ~((long)(pagesize - 1))) + pagesize); for (i = 0; i < page_count; i++) { if (i != 2) /* We leave page 2 unallocated */ pages[ i * pagesize ] = (char) i; addr[i] = pages + i * pagesize; nodes[i] = 0; status[i] = -123; } /* Move pages toi node zero */ numa_move_pages(0, page_count, addr, nodes, status, 0); printf("\nPage status before page migration\n"); printf("---------------------------------\n"); rc = numa_move_pages(0, page_count, addr, NULL, status, 0); if (rc < 0) { perror("move_pages"); exit(1); } for (i = 0; i < page_count; i++) { printf("Page %d vaddr=%p node=%d\n", i, pages + i * pagesize, status[i]); if (i != 2 && status[i]) { printf("Bad page state. Page %d status %d\n",i, status[i]); exit(1); } } /* Move to node zero */ printf("\nMoving pages via mbind to node 0 ...\n"); rc = mbind(pages, page_count * pagesize, MPOL_BIND, old_nodes->maskp, old_nodes->size + 1, MPOL_MF_MOVE | MPOL_MF_STRICT); if (rc < 0) { perror("mbind"); errors++; } printf("\nMoving pages via mbind from node 0 to 1 ...\n"); rc = mbind(pages, page_count * pagesize, MPOL_BIND, new_nodes->maskp, new_nodes->size + 1, MPOL_MF_MOVE | MPOL_MF_STRICT); if (rc < 0) { perror("mbind"); errors++; } numa_move_pages(0, page_count, addr, NULL, status, 0); for (i = 0; i < page_count; i++) { printf("Page %d vaddr=%lx node=%d\n", i, (unsigned long)(pages + i * pagesize), status[i]); if (i != 2) { if (pages[ i* pagesize ] != (char) i) { printf("*** Page content corrupted.\n"); errors++; } else if (status[i] != 1) { printf("*** Page on wrong node.\n"); errors++; } } } if (!errors) printf("Test successful.\n"); else printf("%d errors.\n", errors); return errors > 0 ? 1 : 0; } ./numactl-2.0.9~rc5/test/printcpu0000755000175000017500000000014212213661423015356 0ustar ianwianw#!/bin/sh #print cpu it is running on declare -a arr arr=( $(< /proc/self/stat) ) echo ${arr[38]} ./numactl-2.0.9~rc5/test/nodemap.c0000644000175000017500000000113612213661423015357 0ustar ianwianw#include "numa.h" #include "bitops.h" #include #include int main(void) { int i, k, w, ncpus; struct bitmask *cpus; int maxnode = numa_num_configured_nodes()-1; if (numa_available() < 0) { printf("no numa\n"); exit(1); } cpus = numa_allocate_cpumask(); ncpus = cpus->size; for (i = 0; i <= maxnode ; i++) { if (numa_node_to_cpus(i, cpus) < 0) { printf("node %d failed to convert\n",i); } printf("%d: ", i); w = 0; for (k = 0; k < ncpus; k++) if (numa_bitmask_isbitset(cpus, k)) printf(" %s%d", w>0?",":"", k); putchar('\n'); } return 0; } ./numactl-2.0.9~rc5/test/ftok.c0000644000175000017500000000020612213661423014674 0ustar ianwianw#include #include int main(int ac, char **av) { while (*++av) printf("0x%x\n", ftok(*av, 0)); return 0; } ./numactl-2.0.9~rc5/test/randmap.c0000644000175000017500000000731012213661423015356 0ustar ianwianw/* Randomly change policy */ #include #include "numa.h" #include "numaif.h" #include #include #include #include #include #include #include #include #define SIZE (100*1024*1024) #define PAGES (SIZE/pagesize) #define perror(x) printf("%s: %s\n", x, strerror(errno)) #define err(x) perror(x),exit(1) struct page { unsigned long mask; int policy; }; struct page *pages; char *map; int pagesize; void setpol(unsigned long offset, unsigned long length, int policy, unsigned long nodes) { long i, end; printf("off:%lx length:%lx policy:%d nodes:%lx\n", offset, length, policy, nodes); if (mbind(map + offset*pagesize, length*pagesize, policy, &nodes, 8, 0) < 0) { printf("mbind: %s offset %lx length %lx policy %d nodes %lx\n", strerror(errno), offset*pagesize, length*pagesize, policy, nodes); return; } for (i = offset; i < offset+length; i++) { pages[i].mask = nodes; pages[i].policy = policy; } i = offset - 20; if (i < 0) i = 0; end = offset+length+20; if (end > PAGES) end = PAGES; for (; i < end; i++) { int pol2; unsigned long nodes2; if (get_mempolicy(&pol2, &nodes2, sizeof(long)*8, map+i*pagesize, MPOL_F_ADDR) < 0) err("get_mempolicy"); if (pol2 != pages[i].policy) { printf("%lx: got policy %d expected %d, nodes got %lx expected %lx\n", i, pol2, pages[i].policy, nodes2, pages[i].mask); } if (policy != MPOL_DEFAULT && nodes2 != pages[i].mask) { printf("%lx: nodes %lx, expected %lx, policy %d\n", i, nodes2, pages[i].mask, policy); } } } static unsigned char pop4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; int popcnt(unsigned long val) { int count = 0; while (val) { count += pop4[val & 0xf]; val >>= 4; } return count; } void testmap(void) { pages = calloc(1, PAGES * sizeof(struct page)); if (!pages) exit(100); printf("simple tests\n"); #define MB ((1024*1024)/pagesize) setpol(0, PAGES, MPOL_INTERLEAVE, 3); setpol(0, MB, MPOL_BIND, 1); setpol(MB, MB, MPOL_BIND, 1); setpol(MB, MB, MPOL_DEFAULT, 0); setpol(MB, MB, MPOL_PREFERRED, 2); setpol(MB/2, MB, MPOL_DEFAULT, 0); setpol(MB+MB/2, MB, MPOL_BIND, 2); setpol(MB/2+100, 100, MPOL_PREFERRED, 1); setpol(100, 200, MPOL_PREFERRED, 1); printf("done\n"); for (;;) { unsigned long offset = random() % PAGES; int policy = random() % (MPOL_MAX+1); unsigned long nodes = random() % 4; long length = random() % (PAGES - offset); /* validate */ switch (policy) { case MPOL_DEFAULT: nodes = 0; break; case MPOL_INTERLEAVE: case MPOL_BIND: if (nodes == 0) continue; break; case MPOL_PREFERRED: if (popcnt(nodes) != 1) continue; break; } setpol(offset, length, policy, nodes); } } int main(int ac, char **av) { unsigned long seed; pagesize = getpagesize(); #if 0 map = mmap(NULL, SIZE, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, 0, 0); if (map == (char*)-1) err("mmap"); #else int shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT|0666); if (shmid < 0) err("shmget"); map = shmat(shmid, NULL, SHM_RDONLY); shmctl(shmid, IPC_RMID, NULL); if (map == (char *)-1) err("shmat"); printf("map %p\n", map); #endif if (av[1]) { char *end; unsigned long timeout = strtoul(av[1], &end, 0); switch (*end) { case 'h': timeout *= 3600; break; case 'm': timeout *= 60; break; } printf("running for %lu seconds\n", timeout); alarm(timeout); } else printf("running forever\n"); if (av[1] && av[2]) seed = strtoul(av[2], 0, 0); else seed = time(0); printf("random seed %lu\n", seed); srandom(seed); testmap(); /* test shm etc. */ return 0; } ./numactl-2.0.9~rc5/test/realloc_test.c0000644000175000017500000000462312213661423016420 0ustar ianwianw#include #include #include #include #include #include #include #include "numa.h" #include "numaif.h" #define DEFAULT_NR_PAGES 1024 static int parse_int(const char *str) { char *endptr; long ret = strtol(str, &endptr, 0); if (*endptr != '\0') { fprintf(stderr, "[error] strtol() failed: parse error: %s\n", endptr); exit(1); } if (errno == ERANGE) fprintf(stderr, "[warning] strtol() out of range\n"); if (ret > INT_MAX || ret < INT_MIN) { fprintf(stderr, "[warning] parse_int() out of range\n"); ret = (ret > 0) ? INT_MAX : INT_MIN; } return (int) ret; } int main(int argc, char **argv) { char *mem; int page_size = numa_pagesize(); int node = 0; int nr_pages = DEFAULT_NR_PAGES; if (numa_available() < 0) { fprintf(stderr, "numa is not available"); exit(1); } if (argc > 1) node = parse_int(argv[1]); if (argc > 2) nr_pages = parse_int(argv[2]); mem = numa_alloc_onnode(page_size, node); /* Store the policy of the newly allocated area */ unsigned long nodemask; int mode; int nr_nodes = numa_num_possible_nodes(); if (get_mempolicy(&mode, &nodemask, nr_nodes, mem, MPOL_F_NODE | MPOL_F_ADDR) < 0) { perror("get_mempolicy() failed"); exit(1); } /* Print some info */ printf("Page size: %d\n", page_size); printf("Pages realloc'ed: %d\n", nr_pages); printf("Allocate data in node: %d\n", node); int i; int nr_inplace = 0; int nr_moved = 0; for (i = 0; i < nr_pages; i++) { /* Enlarge mem with one more page */ char *new_mem = numa_realloc(mem, (i+1)*page_size, (i+2)*page_size); if (!new_mem) { perror("numa_realloc() failed"); exit(1); } if (new_mem == mem) ++nr_inplace; else ++nr_moved; mem = new_mem; /* Check the policy of the realloc'ed area */ unsigned long realloc_nodemask; int realloc_mode; if (get_mempolicy(&realloc_mode, &realloc_nodemask, nr_nodes, mem, MPOL_F_NODE | MPOL_F_ADDR) < 0) { perror("get_mempolicy() failed"); exit(1); } assert(realloc_nodemask == nodemask && realloc_mode == mode && "policy changed"); } /* Shrink to the original size */ mem = numa_realloc(mem, (nr_pages + 1)*page_size, page_size); if (!mem) { perror("numa_realloc() failed"); exit(1); } numa_free(mem, page_size); printf("In-place reallocs: %d\n", nr_inplace); printf("Moved reallocs: %d\n", nr_moved); return 0; } ./numactl-2.0.9~rc5/test/checktopology0000755000175000017500000000200212213661423016361 0ustar ianwianw#!/bin/bash # check numactl --hardware output # this checks most of the topology discovery in libnuma BASE=`pwd`/.. export LD_LIBRARY_PATH=$BASE export PATH=$BASE:$PATH numcpus=$(grep -c processor /proc/cpuinfo) numnodes=$(ls -1d /sys/devices/system/node/node[0-9]* | wc -l) nccpus=$(numactl --hardware | grep cpus | sed 's/node.*cpus://' | wc -w ) ncnodes=$(numactl --hardware | grep -c 'node.*size' ) if [ $numnodes != $ncnodes ] ; then echo "numactl --hardware doesnt report all nodes" exit 1 fi if [ $numcpus != $nccpus -a \( $[$nccpus / $numnodes] != $numcpus \) ] ; then echo "numactl --hardware cpus look bogus" exit 1 fi numactl --hardware | grep cpus | while read n ; do node=${n/ cpus*/} node=${node/ /} cpus=${n/*: /} k=0 for i in $cpus ; do if [ ! -h "/sys/devices/system/node/$node/cpu$i" ] ; then echo "$node doesn't have cpu $i" exit 1 fi k=$[$k+1] done if [ $k != $(echo $cpus | wc -w) ] ; then echo "$node missing cpu" exit 1 fi done ./numactl-2.0.9~rc5/CHANGES0000644000175000017500000003713212213661422013610 0ustar ianwianw[Note: newer entries are at the bottom] The actual patches and their series are in the tarball: patches.tar.gz Early prehistory: - Fix some warnings - Add const to global variables and prototypes - Document numa_warn - Add numa_distance() support to read topology - remove internal alias from numa_{warn,error} to allow overwriting again - Various bug fixes - add -f option to memhog to map file - more bugfixes - replace forward backward with STREAM test in numademo 0.6.1 - make headers C++ clean 0.6.2 - use more accurate buffer length for cpumask - add --cpubind to test suite 0.6.3 - fix cpumask parser for large number of cpus. Note that you need a kernel patch (as of 2.6.7-pre) for that too if the cpumask is longer than 99 characters. 0.6.4 - Add Copyright headers 0.6.5 - Reduce unneeded DSO relocations (Arjan van de Ven) - Add -r option to memhog (repeat walk) - some manpage fixes - Use syscall numbers from asm/unistd.h if possible - Add numa_node_size64 to handle large nodes on 32bit architectures - Fix numactl to use it (report from Rajan Ravindran) - Use ln -sf in make install (Rajan Ravindran) - Add syscall numbers for ppc/ppc64 - Add private syscall6 for i386 since the glibc version is broken - Remove STUB - Change numactl --show to use cpubind instead of nodebind for CPU affinity. - Fix make install into examples directory - Work around broken sched_set_affinity API. This adds a 32768 CPUs limit. - Fix segfault with /sys not mounted. - Clean up Makefile - Make numactl --show more clever 0.7-pre1 - add test/regress2 and some fixes to test programs - Fix DSO relocation patch for global variables - Change nodeset sizes back to be binary compatible with SLES9 - Cosmetic changes to manpages (pointed out by Eric S. Raymond) - Make numa_run_on_node etc. act on current thread only even on NPTL systems (Dinakar Guniguntala) - Make numa_no_nodes / numa_all_nodes const (Werner Almesberger) - Fix up the warnings caused by above change - Add numa_distance() on systems with ACPI - remove some obsolete code - add rdtsc for ppc64 - fix unsigned/unsigned long confusion in cpumasks (Matt Dobson) - fix CPU_BYTES and rename CPU_WORDS to CPU_LONGS (Matt Dobson) - Print node distances in numactl [0.7 skipped] 0.8 - hardend numactl command line parsing against bad arguments in some cases - remove cpumask/nodemask confusion which has become a FAQ: --cpubind deprecated, added --cpunodebind and --physcpubind= options print both in --show, old cpumask kept for compatibility - Fix --show problems - various fixes for bugs noted by Mike Stroyan (thanks!) - install set_mempolicy manpage - various smaller fixes 0.9 - Get rid of bogus distance.o that broke compilation on !x86-64 (sorry) - Handle CFLAGS overriding without OPT_CFLAGS (Ian Wienand) - Fix up section of get/set_mempolicy (Ian Wienand) - When no NUMA available fall back to one global node instead of one node per CPU (Samuel Thibault) - Don't rely on architecture symbols for dependency generation - Use __powerpc__ to detect PPC/PPC64 - numastat: * wrap display properly with many nodes * display nodes in forward order * install manpage in `make install'. - remove bogus numamemcpy.c - numademo: * allow standalone compile, make streamlib optional * clean up output * change output unit to standard MB/s * compile with more optimization * add random pass to fool any prefetching (slow) - make numademo compileable outside source tree - use gettimeofday instead of time stamp counters in benchmarks - support valgrind in testsuite - other minor changes 0.9.1 - Make automatic selection of lib64 vs lib more robust. Now should work even on ppc32 with a lib64 directory. Architecture lists are hardcoded now unfortunately. 0.9.2 - Fix compilation on architectures with gcc 3.3+ but without TLS (MIPS, Alpha, Sparc) - Add warning against using of MPOL_F_NODE - numa.3 improvements from Michael Kerrisk - Support page migration (migratepages, manpages) from Christoph Lameter. Requires 2.6.16+ kernels 0.9.3 - Some more manpage fixes - install migratepages manpage in make install - Build fix for Debian make from Ian Wienand 0.9.4: - Remove syscall manpages. They're in main man-pages now. - More migrate fixes from C.Lameter 0.9.5: - Fix parsing of cpumap in sysfs from Doug Chapman 0.9.6: - Fix make install again 0.9.7 - Fix cpumap parsing fix to not corrupt memory (Doug Chapman) - Small optimization to cpumap parsing - Create target directories for Debian (Ian Wienand) 0.9.8 - Fix cpumap parsing again (Doug Chapman) 0.9.9 (aka "Will 1.0 ever happen?") - Fix sizing of cpu buffers for affinity syscalls - Don't corrupt errno in numa_run_on_node_mask. This fixes numactl cpubind issues on some systems. - Print cpus belonging to nodes in numactl --hardware - Rewrite cpumap parser to be simpler and hopefully finally work in all cases - add testcases for cpu affinity and topology discovery - Add make test target to run regression test easier and fix up test/README Lots of fixes thanks to thorough testing by Noriyuki Taniuchi: - Better command line parsing in numactl and fix various documentation bugs - Wrong arguments to --prefered don't crash numactl anymore - Fix --cpunodebind=all - Auto collect short option list in numactl - a couple were missing. - Fix documentation of numa_set_localalloc. It doesn't have a flag. - Fix numa_run_on_node(-1) - numa_get_run_node_mask(): Fix documentation, don't warn 0.9.10: - Fix cpumap parsing bug when NR_CPUS < 32 (dean gaudet) 0.9.11 - Fix usage output for --shmid (Noriyuki Taniuchi) - Use correct syscall number for migrate_pages() on PPC 1.0 - Add sleep to regression test to work with delayed statistic updating in newer kernels (Mel Gorman) - Default to -O2 1.0.1 - Fix build on powerpc 1.0.2 - Fix parallel Makefile build (Andreas Herrmann) - Fix target command argument parsing for numactl (no -- needed again anymore) - Clarify numa_node_to_cpus() manpage 1.0.3 - Add the migspeed test program to test the speed of migrating pages from one node to another - Support for move_pages(2) to numactl, and numa_move_pages() to libnuma - Add the move_pages test command to exercise the move_pages(2) system call - Add the mbind_mig_pages test command to verify the moving of a task's pages with move_pages(2) - Add the migrate_pages test command to test that a task's pages can be moved with move_pages(2) - Support numactl +nn syntax for cpuset_relative cpu and node numbers - Modify libnuma to use variable-length bit masks (the bitmask structure) - Modify numactl, migspeed, memhog, migratepages, numademo and stream_main to use variable-length bit masks - Modify the test/ programs to use the libnuma that uses variable size bit masks - Version 2 (symbol versioning) - Man page changes with the change to variable-length bit masks, move_pages migrate_pages and others 2.0.0 - Added API version 2 and symbol versioning. This provides binary compatibility with old codes that use libnuma. - Brought the man page in line with the version 2 changes. - Provide numacompat1.h and additions to numa.h, which provide source code compatibility to libnuma version 1. (The application progamming interface changes, but the ABI is preserved through the use of symbol versioning. So the library stays libnuma.so.1) - Added variable-length bit masks to libnuma. These are struct bitmask. This allows libnuma to be independent of ever increasing cpu counts. o Modified the test/ programs to use variable size bit masks. o Modified numactl, migspeed, memhog, migratepages, numademo and stream_main to use variable-length bit masks. - Added support for move_pages(2) (sys_move_pages()) to numactl. Adds numa_move_pages() to libnuma. o Added the mbind_mig_pages test command to verify the moving of a task's pages with move_pages(2). o Added the move_pages test command to exercise the move_pages(2) system call. o Added the migrate_pages test command to test that a task's pages can be moved with move_pages(2). o Added the migspeed test program. It tests the speed of migrating pages from one node to another. - Allow a numactl +nn syntax for cpuset_relative cpu and node numbers. - General cleanup of man page. - Return nodes allowed by the application's current cpuset context via new API numa_get_mems_allowed(). - Change numa_alloc_local() to use MPOL_PREFERRED with NULL nodemask to effect local allocation. - Man page for numactl: numa_maps man page clarifications and cleanup - Minor cleanups of numademo.c - Fix numastat sysfs scanning in numactl - Reorganize the regress test script. - Fix mempolicy regression test for assymetric platforms and memoryless nodes. - Fix checkaffinity and checktopology regression tests. - Fix the __NR_migrate_pages system call number. - Fix the way numactl handles the building of the mask when executing the --physbind option, and the way Cpus_allowed mask is created. 2.0.1 - Fix bug in dombind (when passed a null) - Make 4 fixes from Debian: MIPS support, MIPS hppa fix for syscalls, make sure -lm for numademo, build a static library - Fix parsing of /proc/self/status for 2.6.25 additions to it 2.0.2 - Various numademo improvements: * Fix random benchmark to use all specified memory * Rename to random2 to signify it's different * Optimize random benchmark by inlining random number generator fast path. * Clear caches between runs for more stable results * Add new random pointer chaser benchmark * Compile benchmarks with gcc vectorization if available * run numademo in regression test - Add numa_exit_on_warn - Fix no cpuset fallback in libnuma.c - Install symlinks for the manpages of all new functions - Make internal libnuma functions static - Add copy_bitmask_to_bitmask() to numa.h - Some cleanups - Fix line reading in proc - Add versions.ldscript to libnuma.so dependencies - Remove the non-"numa_" functions from numacompat1.h and numa.h - Add ia64 clearcache() to numademo - Add -t to numademo for regression testing - Remove "numa_all_cpus" from numa.h - Changed VERSION1_COMPATIBILITY to NUMA_VERSION1_COMPATIBILITY - Defined LIBNUMA_API_VERSION 2 in numa.h - Fix numaif.h and numaint.h (migrate_pages; from Masatake Yamato) - Fixes to numademo (min/max, and array index; from Kent Liu) - Fixes to Makefile and permissions; from Berhard Walle 2.0.3-rc1 - rc3 - Fixes to libnuma.c numa.h numacompat1.h by Daniel Gollub to fix v1 compatiblity - Restore nodemask_zero() and nodemask_equal() - Drops a warning message about this not being a NUMA system - Remove the numa_maps.5 man page (it's in Linux now) (by Bernhard Walle) - Fix makefiles in tests (Andi) - Fix off-by-ones in test mbind_mig_pages (Andi) - Fix test/prefered.c (Andi) - Fix to print_node_cpus() (Arnd/Bill Buros) - Fixes to read_mask() (Arnd's on top of cpw's) - Fix to makefile (LDFLAGS/LDLIBS/AR/RANLIB) (Mike Frysinger) - Fix numactl for noncontiguous nodes (Amit Arora) - Fix bitmask memory leaks, numa_alloc_onnode/numa_preferred (Kornilios Kourtis) - Add numa_node_of_cpu() to retrieve local node of a cpu (Kornilios Kourtis) - Fix parsing of /proc/self/status (Brice Goglin/Lee Shermerhorn) - Small reorganization of numa_alloc_local() (L.S.) - Fixes of bitmask memory leaks in about eight functions (L.S.) - Make library always return allocated masks that user can free (L.S.) - Fix to numademo memtest (allocation overhead) (L.S.) - Fix to checkaffinity test (possible shell errors) (L.S.) - Fix a printf in migspeed.c (Frederik Himpe) - Fix test/regress grep of node number (Cliff W.) - Change numademo to finish in a timely manner on large machines (Cliff W.) - tested on 96p 48nodes 2.0.3-rc1 - rc4 - Add --dump-nodes option to numactl (Andi) 2.0.3 released in June, 2009 2.0.4-rc3 - Fix numactl for a machine with sparse cpu ids (Anton Blanchard) - Fix makefile to remove move_pages on make clean (Andi) - Fix numa_node_to_cpus() (Sharyathi Nagesh) - Rename 'thread' to 'task' (L.S.) - Remove other trailing spaces (Cliff W.) - Man page correction/clarification for numa_node_to_cpus() (Ian Wienand) - Man page clarification for numactl (Mike MacCana) - Fix numactl --hardware for cpu-less nodes (Thomas Renninger) - Fix set_configured_cpus() (Jan Beulich) - Fix memory corrupting use of strlen (Jan Beulich) - Add a DSO destructor for memory deallocation (Neil Horman) 2.0.4 released in July, 2010 2.0.5 released in July, 2010 (about 2 days after 2.0.4) - Fix numactl calls to set_mempolicy, get_mempolicy and mbind newer: - include stat.h in shm.c (Mike Frysinger) 2.0.6-rc1 - Correct numa_max_node() use of broken numa_num_configured_nodes() (Tim Pepper) - Use numa_max_node() not numa_num_configured_nodes() (Tim Pepper) - Fix numa_num_configured_nodes() to match man page description (Tim Pepper) - Clarify comment for numa_all_nodes_ptr extern (Tim Pepper) - numactl --hardware should handle sparse node numbering (Tim Pepper) - Maintain compatibility with 2.0.3 numa_num_thread...()'s (Cliff W.) 2.0.6-rc2 - numa_num_task_cpus()/..nodes() to return actual counts (Cliff W.) 2.0.6-rc3 - Fix numa_get_run_node_mask() to return a cpuset-aware node mask (Cliff W.) (replaced 110112) - Add a better warning to numa_node_to_cpus() 2.0.6-rc4 - Fix numa_get_mems_allowed() to use MPOL_F_MEMS_ALLOWED (Michael Spiegel) 2.0.6 released Dec, 2010 2.0.7-rc1 - 110111 Add numa_realloc() (and realloc_test) (Vasileios Karakasis) - 110112 Re-fix numa_get_run_node_mask() and fix numa_get_run_node_mask (Cliff) - 110112 Fix the numa_get_run_node_mask() man page (cpus vs nodes) (Cliff W.) 2.0.7-rc2 - 110112 Fix the cpu and node parsing to be cpuset aware (Cliff W.) - 110112 Fix test/checkaffinity to be cpuset aware (Cliff W.) - 110302 Fix two typos in numactl.8 (John Bradshaw) 2.0.7 released Apr, 2011 2.0.8-rc1 - 110818 Checking of sucessful allocations in numademo (Petr Holasek) 2.0.8-rc2 - 110823 Fix of numactl (--touch) warnings and man page (Cliff W.) 2.0.8-rc3 - 111214 Add "same" nodemask alias to numactl (Andi Kleen) - 111214 Add constructors for numa_init/exit (Andi Kleen) - 111214 Add use of glibc syscall stub where possible (Andi Kleen) - 111214 Fix regress1 to show all the problems before exiting (Andi Kleen) - 111214 Add IO affinity support (Andi Kleen) - 111214 Clean regression test temp files (Andi Kleen) - 111214 Add an option to memhog to disable transparent huge pages (Andi Kleen) - 111214 Fix the test suite on systems that force THP, disable them (Andi Kleen) 2.0.8-rc4 - 120106 Install man pages migspeed, migratepages and numastat (Petr Holasek) - 120106 Warnings in numa_node_to_cpus_v1 to be more verbose (Petr Holasek) - 120216 Fix for numademo: msize check for ptrchase test (Petr Holasek) 2.0.8-rc5 - 120823 Fix calculation of maxconfiguredcpu (Petr Holasek) - 120823 Fix: do not recalculate maxconfiguredcpu (Petr Holasek) - 120823 v.1.3 numa_num_possible_cpus symbol is exported (Petr Holasek) - 120823 Add all versions of numa_parse_{cpu,node}string() (Petr Holasek) - 120823 Add numa_parse_cpustring take a const char* parameter (Petr Holasek) - 120823 Fix unused bufferlen variable (Petr Holasek) - 120823 Fix warnings when there are holes in numbering of nodes (Petr Holasek) 2.0.8-rc6 - 120911 Show distances on machines without a node 0 (Petr Holasek) - 121007 Replace perl numastat with a C command (Bill Gray) - 121011 Allow an install location PREFIX in the Makefile (Frank Tampe) 2.0.8 released Oct, 2012 2.0.9-rc1 - 130207 Add a prototype for numa_bitmask_weight (Cliff W.) 2.0.9-rc2 - 130725 Fix hubstats huge pages bug, version number, man page (Bill Gray) - 130726 Disable the regress-io test (Cliff W.) - 130730 Fix typos in numactl man page; add short opts to --help (Petr Holasek) 2.0.9-rc3 - 130906 numactl: option --all/-a added for policy settings (Petr Holasek) - 130906 libnuma: new function numa_run_on_node_mask_all (Petr Holasek) ./numactl-2.0.9~rc5/affinity.c0000644000175000017500000002021212213661422014561 0ustar ianwianw/* Support for specifying IO affinity by various means. Copyright 2010 Intel Corporation Author: Andi Kleen libnuma is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; version 2.1. libnuma 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. See the GNU Lesser General Public License for more details. You should find a copy of v2.1 of the GNU Lesser General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Notebook: - Separate real errors from no NUMA with fallback - Infiniband - FCoE? - Support for other special IO devices - Specifying cpu subsets inside the IO node? - Handle multiple IO nodes (needs kernel changes) - Better support for multi-path IO? */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "numa.h" #include "numaint.h" #include "sysfs.h" #include "affinity.h" #include "rtnetlink.h" static int badchar(const char *s) { if (strpbrk(s, "/.")) return 1; return 0; } static int node_parse_failure(int ret, char *cls, const char *dev) { if (!cls) cls = ""; if (ret == -2) numa_warn(W_node_parse1, "Kernel does not know node mask for%s%s device `%s'", *cls ? " " : "", cls, dev); else numa_warn(W_node_parse2, "Cannot read node mask for %s device `%s'", cls, dev); return -1; } /* Generic sysfs class lookup */ static int affinity_class(struct bitmask *mask, char *cls, const const char *dev) { int ret; while (isspace(*dev)) dev++; if (badchar(dev)) { numa_warn(W_badchar, "Illegal characters in `%s' specification", dev); return -1; } /* Somewhat hackish: extract device from symlink path. Better would be a direct backlink. This knows slightly too much about the actual sysfs layout. */ char path[1024]; char *fn = NULL; if (asprintf(&fn, "/sys/class/%s/%s", cls, dev) > 0 && readlink(fn, path, sizeof path) > 0) { regex_t re; regmatch_t match[2]; char *p; regcomp(&re, "(/devices/pci[0-9a-fA-F:/]+\\.[0-9]+)/", REG_EXTENDED); ret = regexec(&re, path, 2, match, 0); regfree(&re); if (ret == 0) { free(fn); assert(match[0].rm_so > 0); assert(match[0].rm_eo > 0); path[match[1].rm_eo + 1] = 0; p = path + match[0].rm_so; ret = sysfs_node_read(mask, "/sys/%s/numa_node", p); if (ret < 0) return node_parse_failure(ret, NULL, p); return ret; } } free(fn); ret = sysfs_node_read(mask, "/sys/class/%s/%s/device/numa_node", cls, dev); if (ret < 0) return node_parse_failure(ret, cls, dev); return 0; } /* Turn file (or device node) into class name */ static int affinity_file(struct bitmask *mask, char *cls, const char *file) { struct stat st; DIR *dir; int n; unsigned maj = 0, min = 0; dev_t d; struct dirent de, *dep; cls = "block"; char fn[sizeof("/sys/class/") + strlen(cls)]; if (stat(file, &st) < 0) { numa_warn(W_blockdev1, "Cannot stat file %s", file); return -1; } d = st.st_dev; if (S_ISCHR(st.st_mode)) { /* Better choice than misc? Most likely misc will not work anyways unless the kernel is fixed. */ cls = "misc"; d = st.st_rdev; } else if (S_ISBLK(st.st_mode)) d = st.st_rdev; sprintf(fn, "/sys/class/%s", cls); dir = opendir(fn); if (!dir) { numa_warn(W_blockdev2, "Cannot enumerate %s devices in sysfs", cls); return -1; } while (readdir_r(dir, &de, &dep) == 0 && dep) { char *name = dep->d_name; if (*name == '.') continue; char *dev; char fn2[sizeof("/sys/class/block//dev") + strlen(name)]; n = -1; if (sprintf(fn2, "/sys/class/block/%s/dev", name) < 0) break; dev = sysfs_read(fn2); if (dev) { n = sscanf(dev, "%u:%u", &maj, &min); free(dev); } if (n != 2) { numa_warn(W_blockdev3, "Cannot parse sysfs device %s", name); continue; } if (major(d) != maj || minor(d) != min) continue; closedir(dir); return affinity_class(mask, "block", name); } closedir(dir); numa_warn(W_blockdev5, "Cannot find block device %x:%x in sysfs for `%s'", maj, min, file); return -1; } /* Look up interface of route using rtnetlink. */ static int find_route(struct sockaddr *dst, int *iifp) { struct rtattr *rta; const int hdrlen = NLMSG_LENGTH(sizeof(struct rtmsg)); struct { struct nlmsghdr msg; struct rtmsg rt; char buf[256]; } req = { .msg = { .nlmsg_len = hdrlen, .nlmsg_type = RTM_GETROUTE, .nlmsg_flags = NLM_F_REQUEST, }, .rt = { .rtm_family = dst->sa_family, }, }; struct sockaddr_nl adr = { .nl_family = AF_NETLINK, }; if (rta_put_address(&req.msg, RTA_DST, dst) < 0) { numa_warn(W_netlink1, "Cannot handle network family %x", dst->sa_family); return -1; } if (rtnetlink_request(&req.msg, sizeof req, &adr) < 0) { numa_warn(W_netlink2, "Cannot request rtnetlink route: %s", strerror(errno)); return -1; } /* Fish the interface out of the netlink soup. */ rta = NULL; while ((rta = rta_get(&req.msg, rta, hdrlen)) != NULL) { if (rta->rta_type == RTA_OIF) { memcpy(iifp, RTA_DATA(rta), sizeof(int)); return 0; } } numa_warn(W_netlink3, "rtnetlink query did not return interface"); return -1; } static int iif_to_name(int iif, struct ifreq *ifr) { int n; int sk = socket(PF_INET, SOCK_DGRAM, 0); if (sk < 0) return -1; ifr->ifr_ifindex = iif; n = ioctl(sk, SIOCGIFNAME, ifr); close(sk); return n; } /* Resolve an IP address to the nodes of a network device. This generally only attempts to handle simple cases: no multi-path, no bounding etc. In these cases only the first interface or none is chosen. */ static int affinity_ip(struct bitmask *mask, char *cls, const char *id) { struct addrinfo *ai; int n; int iif; struct ifreq ifr; if ((n = getaddrinfo(id, NULL, NULL, &ai)) != 0) { numa_warn(W_net1, "Cannot resolve %s: %s", id, gai_strerror(n)); return -1; } if (find_route(&ai->ai_addr[0], &iif) < 0) goto out_ai; if (iif_to_name(iif, &ifr) < 0) { numa_warn(W_net2, "Cannot resolve network interface %d", iif); goto out_ai; } freeaddrinfo(ai); return affinity_class(mask, "net", ifr.ifr_name); out_ai: freeaddrinfo(ai); return -1; } /* Look up affinity for a PCI device */ static int affinity_pci(struct bitmask *mask, char *cls, const char *id) { unsigned seg, bus, dev, func; int n, ret; /* Func is optional. */ if ((n = sscanf(id, "%x:%x:%x.%x",&seg,&bus,&dev,&func)) == 4 || n == 3) { if (n == 3) func = 0; } /* Segment is optional too */ else if ((n = sscanf(id, "%x:%x.%x",&bus,&dev,&func)) == 3 || n == 2) { seg = 0; if (n == 2) func = 0; } else { numa_warn(W_pci1, "Cannot parse PCI device `%s'", id); return -1; } ret = sysfs_node_read(mask, "/sys/devices/pci%04x:%02x/%04x:%02x:%02x.%x/numa_node", seg, bus, seg, bus, dev, func); if (ret < 0) return node_parse_failure(ret, cls, id); return 0; } static struct handler { char first; char *name; char *cls; int (*handler)(struct bitmask *mask, char *cls, const char *desc); } handlers[] = { { 'n', "netdev:", "net", affinity_class }, { 'i', "ip:", NULL, affinity_ip }, { 'f', "file:", NULL, affinity_file }, { 'b', "block:", "block", affinity_class }, { 'p', "pci:", NULL, affinity_pci }, {} }; hidden int resolve_affinity(const char *id, struct bitmask *mask) { struct handler *h; for (h = &handlers[0]; h->first; h++) { int len; if (id[0] != h->first) continue; len = strlen(h->name); if (!strncmp(id, h->name, len)) { int ret = h->handler(mask, h->cls, id + len); if (ret == -2) { numa_warn(W_nonode, "Kernel does not know node for %s\n", id + len); } return ret; } } return NO_IO_AFFINITY; } ./numactl-2.0.9~rc5/rtnetlink.h0000644000175000017500000000047112213661423014775 0ustar ianwianwhidden int rta_put_address(struct nlmsghdr *msg, int type, struct sockaddr *adr); hidden struct rtattr *rta_get(struct nlmsghdr *m, struct rtattr *p, int offset); hidden void *rta_put(struct nlmsghdr *m, int type, int len); hidden int rtnetlink_request(struct nlmsghdr *msg, int buflen, struct sockaddr_nl *adr); ./numactl-2.0.9~rc5/memhog.c0000755000175000017500000000616412213661422014241 0ustar ianwianw/* Copyright (C) 2003,2004 Andi Kleen, SuSE Labs. Allocate memory with policy for testing. numactl 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; version 2. numactl 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. See the GNU General Public License for more details. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include "numa.h" #include "numaif.h" #include "util.h" #define terr(x) perror(x) enum { UNIT = 10*1024*1024, }; #ifndef MADV_NOHUGEPAGE #define MADV_NOHUGEPAGE 15 #endif int repeat = 1; void usage(void) { printf("memhog [-rNUM] size[kmg] [policy [nodeset]]\n"); printf("-rNUM repeat memset NUM times\n"); printf("-H disable transparent hugepages\n"); print_policies(); exit(1); } long length; void hog(void *map) { long i; for (i = 0; i < length; i += UNIT) { long left = length - i; if (left > UNIT) left = UNIT; putchar('.'); fflush(stdout); memset(map + i, 0xff, left); } putchar('\n'); } int main(int ac, char **av) { char *map; struct bitmask *nodes, *gnodes; int policy, gpolicy; int ret = 0; int loose = 0; int i; int fd = -1; bool disable_hugepage = false; nodes = numa_allocate_nodemask(); gnodes = numa_allocate_nodemask(); while (av[1] && av[1][0] == '-') { switch (av[1][1]) { case 'f': fd = open(av[1]+2, O_RDWR); if (fd < 0) perror(av[1]+2); break; case 'r': repeat = atoi(av[1] + 2); break; case 'H': disable_hugepage = true; break; default: usage(); } av++; } if (!av[1]) usage(); length = memsize(av[1]); if (av[2] && numa_available() < 0) { printf("Kernel doesn't support NUMA policy\n"); exit(1); } else loose = 1; policy = parse_policy(av[2], av[3]); if (policy != MPOL_DEFAULT) nodes = numa_parse_nodestring(av[3]); if (!nodes) { printf ("<%s> is invalid\n", av[3]); exit(1); } if (fd >= 0) map = mmap(NULL,length,PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); else map = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); if (map == (char*)-1) err("mmap"); if (mbind(map, length, policy, nodes->maskp, nodes->size, 0) < 0) terr("mbind"); if (disable_hugepage) madvise(map, length, MADV_NOHUGEPAGE); gpolicy = -1; if (get_mempolicy(&gpolicy, gnodes->maskp, gnodes->size, map, MPOL_F_ADDR) < 0) terr("get_mempolicy"); if (!loose && policy != gpolicy) { ret = 1; printf("policy %d gpolicy %d\n", policy, gpolicy); } if (!loose && !numa_bitmask_equal(gnodes, nodes)) { printf("nodes differ %lx, %lx!\n", gnodes->maskp[0], nodes->maskp[0]); ret = 1; } for (i = 0; i < repeat; i++) hog(map); exit(ret); } ./numactl-2.0.9~rc5/clearcache.c0000755000175000017500000000362712213661422015040 0ustar ianwianw/* Clear the CPU cache for benchmark purposes. Pretty simple minded. * Might not work in some complex cache topologies. * When you switch CPUs it's a good idea to clear the cache after testing * too. */ #include #include #include #include "clearcache.h" unsigned cache_size(void) { unsigned cs = 0; #ifdef _SC_LEVEL1_DCACHE_SIZE cs += sysconf(_SC_LEVEL1_DCACHE_SIZE); #endif #ifdef _SC_LEVEL2_DCACHE_SIZE cs += sysconf(_SC_LEVEL2_DCACHE_SIZE); #endif #ifdef _SC_LEVEL3_DCACHE_SIZE cs += sysconf(_SC_LEVEL3_DCACHE_SIZE); #endif #ifdef _SC_LEVEL4_DCACHE_SIZE cs += sysconf(_SC_LEVEL4_DCACHE_SIZE); #endif if (cs == 0) { static int warned; if (!warned) { printf("Cannot determine CPU cache size\n"); warned = 1; } cs = 64*1024*1024; } cs *= 2; /* safety factor */ return cs; } void fallback_clearcache(void) { static unsigned char *clearmem; unsigned cs = cache_size(); unsigned i; if (!clearmem) clearmem = malloc(cs); if (!clearmem) { printf("Warning: cannot allocate %u bytes of clear cache buffer\n", cs); return; } for (i = 0; i < cs; i += 32) clearmem[i] = 1; } void clearcache(unsigned char *mem, unsigned size) { #if defined(__i386__) || defined(__x86_64__) unsigned i, cl, eax, feat; /* get clflush unit and feature */ asm("cpuid" : "=a" (eax), "=b" (cl), "=d" (feat) : "0" (1) : "cx"); if (!(feat & (1 << 19))) fallback_clearcache(); cl = ((cl >> 8) & 0xff) * 8; for (i = 0; i < size; i += cl) asm("clflush %0" :: "m" (mem[i])); #elif defined(__ia64__) unsigned long cl, endcl; // flush probable 128 byte cache lines (but possibly 64 bytes) cl = (unsigned long)mem; endcl = (unsigned long)(mem + (size-1)); for (; cl <= endcl; cl += 64) asm ("fc %0" :: "r"(cl) : "memory" ); #else #warning "Consider adding a clearcache implementation for your architecture" fallback_clearcache(); #endif } ./numactl-2.0.9~rc5/libnuma.c0000755000175000017500000013335712213661422014421 0ustar ianwianw/* Simple NUMA library. Copyright (C) 2003,2004,2005,2008 Andi Kleen,SuSE Labs and Cliff Wickman,SGI. libnuma is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; version 2.1. libnuma 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. See the GNU Lesser General Public License for more details. You should find a copy of v2.1 of the GNU Lesser General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA All calls are undefined when numa_available returns an error. */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include #include "numa.h" #include "numaif.h" #include "numaint.h" #include "util.h" #include "affinity.h" #define WEAK __attribute__((weak)) #define CPU_BUFFER_SIZE 4096 /* This limits you to 32768 CPUs */ /* these are the old (version 1) masks */ nodemask_t numa_no_nodes; nodemask_t numa_all_nodes; /* these are now the default bitmask (pointers to) (version 2) */ struct bitmask *numa_no_nodes_ptr = NULL; struct bitmask *numa_all_nodes_ptr = NULL; struct bitmask *numa_possible_nodes_ptr = NULL; struct bitmask *numa_all_cpus_ptr = NULL; struct bitmask *numa_possible_cpus_ptr = NULL; /* I would prefer to use symbol versioning to create v1 and v2 versions of numa_no_nodes and numa_all_nodes, but the loader does not correctly handle versioning of BSS versus small data items */ struct bitmask *numa_nodes_ptr = NULL; static struct bitmask *numa_memnode_ptr = NULL; static unsigned long *node_cpu_mask_v1[NUMA_NUM_NODES]; struct bitmask **node_cpu_mask_v2; WEAK void numa_error(char *where); #ifdef __thread #warning "not threadsafe" #endif static __thread int bind_policy = MPOL_BIND; static __thread unsigned int mbind_flags = 0; static int sizes_set=0; static int maxconfigurednode = -1; static int maxconfiguredcpu = -1; static int numprocnode = -1; static int numproccpu = -1; static int nodemask_sz = 0; static int cpumask_sz = 0; int numa_exit_on_error = 0; int numa_exit_on_warn = 0; static void set_sizes(void); /* * There are two special functions, _init(void) and _fini(void), which * are called automatically by the dynamic loader whenever a library is loaded. * * The v1 library depends upon nodemask_t's of all nodes and no nodes. */ void __attribute__((constructor)) numa_init(void) { int max,i; if (sizes_set) return; set_sizes(); /* numa_all_nodes should represent existing nodes on this system */ max = numa_num_configured_nodes(); for (i = 0; i < max; i++) nodemask_set_compat((nodemask_t *)&numa_all_nodes, i); memset(&numa_no_nodes, 0, sizeof(numa_no_nodes)); } #define FREE_AND_ZERO(x) if (x) { \ numa_bitmask_free(x); \ x = NULL; \ } void __attribute__((destructor)) numa_fini(void) { FREE_AND_ZERO(numa_all_cpus_ptr); FREE_AND_ZERO(numa_possible_cpus_ptr); FREE_AND_ZERO(numa_all_nodes_ptr); FREE_AND_ZERO(numa_possible_nodes_ptr); FREE_AND_ZERO(numa_no_nodes_ptr); FREE_AND_ZERO(numa_memnode_ptr); FREE_AND_ZERO(numa_nodes_ptr); } /* * The following bitmask declarations, bitmask_*() routines, and associated * _setbit() and _getbit() routines are: * Copyright (c) 2004_2007 Silicon Graphics, Inc. (SGI) All rights reserved. * SGI publishes it under the terms of the GNU General Public License, v2, * as published by the Free Software Foundation. */ static unsigned int _getbit(const struct bitmask *bmp, unsigned int n) { if (n < bmp->size) return (bmp->maskp[n/bitsperlong] >> (n % bitsperlong)) & 1; else return 0; } static void _setbit(struct bitmask *bmp, unsigned int n, unsigned int v) { if (n < bmp->size) { if (v) bmp->maskp[n/bitsperlong] |= 1UL << (n % bitsperlong); else bmp->maskp[n/bitsperlong] &= ~(1UL << (n % bitsperlong)); } } int numa_bitmask_isbitset(const struct bitmask *bmp, unsigned int i) { return _getbit(bmp, i); } struct bitmask * numa_bitmask_setall(struct bitmask *bmp) { unsigned int i; for (i = 0; i < bmp->size; i++) _setbit(bmp, i, 1); return bmp; } struct bitmask * numa_bitmask_clearall(struct bitmask *bmp) { unsigned int i; for (i = 0; i < bmp->size; i++) _setbit(bmp, i, 0); return bmp; } struct bitmask * numa_bitmask_setbit(struct bitmask *bmp, unsigned int i) { _setbit(bmp, i, 1); return bmp; } struct bitmask * numa_bitmask_clearbit(struct bitmask *bmp, unsigned int i) { _setbit(bmp, i, 0); return bmp; } unsigned int numa_bitmask_nbytes(struct bitmask *bmp) { return longsperbits(bmp->size) * sizeof(unsigned long); } /* where n is the number of bits in the map */ /* This function should not exit on failure, but right now we cannot really recover from this. */ struct bitmask * numa_bitmask_alloc(unsigned int n) { struct bitmask *bmp; if (n < 1) { numa_error("request to allocate mask for invalid number; abort\n"); exit(1); } bmp = malloc(sizeof(*bmp)); if (bmp == 0) goto oom; bmp->size = n; bmp->maskp = calloc(longsperbits(n), sizeof(unsigned long)); if (bmp->maskp == 0) { free(bmp); goto oom; } return bmp; oom: numa_error("Out of memory allocating bitmask"); exit(1); } void numa_bitmask_free(struct bitmask *bmp) { if (bmp == 0) return; free(bmp->maskp); bmp->maskp = (unsigned long *)0xdeadcdef; /* double free tripwire */ free(bmp); return; } /* True if two bitmasks are equal */ int numa_bitmask_equal(const struct bitmask *bmp1, const struct bitmask *bmp2) { unsigned int i; for (i = 0; i < bmp1->size || i < bmp2->size; i++) if (_getbit(bmp1, i) != _getbit(bmp2, i)) return 0; return 1; } /* Hamming Weight: number of set bits */ unsigned int numa_bitmask_weight(const struct bitmask *bmp) { unsigned int i; unsigned int w = 0; for (i = 0; i < bmp->size; i++) if (_getbit(bmp, i)) w++; return w; } /* *****end of bitmask_ routines ************ */ /* Next two can be overwritten by the application for different error handling */ WEAK void numa_error(char *where) { int olde = errno; perror(where); if (numa_exit_on_error) exit(1); errno = olde; } WEAK void numa_warn(int num, char *fmt, ...) { static unsigned warned; va_list ap; int olde = errno; /* Give each warning only once */ if ((1<maskp, bmp->size + 1) < 0) numa_error("set_mempolicy"); } static void getpol(int *oldpolicy, struct bitmask *bmp) { if (get_mempolicy(oldpolicy, bmp->maskp, bmp->size + 1, 0, 0) < 0) numa_error("get_mempolicy"); } static void dombind(void *mem, size_t size, int pol, struct bitmask *bmp) { if (mbind(mem, size, pol, bmp ? bmp->maskp : NULL, bmp ? bmp->size + 1 : 0, mbind_flags) < 0) numa_error("mbind"); } /* (undocumented) */ /* gives the wrong answer for hugetlbfs mappings. */ int numa_pagesize(void) { static int pagesize; if (pagesize > 0) return pagesize; pagesize = getpagesize(); return pagesize; } make_internal_alias(numa_pagesize); /* * Find nodes (numa_nodes_ptr), nodes with memory (numa_memnode_ptr) * and the highest numbered existing node (maxconfigurednode). */ static void set_configured_nodes(void) { DIR *d; struct dirent *de; long long freep; numa_memnode_ptr = numa_allocate_nodemask(); numa_nodes_ptr = numa_allocate_nodemask(); d = opendir("/sys/devices/system/node"); if (!d) { maxconfigurednode = 0; } else { while ((de = readdir(d)) != NULL) { int nd; if (strncmp(de->d_name, "node", 4)) continue; nd = strtoul(de->d_name+4, NULL, 0); numa_bitmask_setbit(numa_nodes_ptr, nd); if (numa_node_size64(nd, &freep) > 0) numa_bitmask_setbit(numa_memnode_ptr, nd); if (maxconfigurednode < nd) maxconfigurednode = nd; } closedir(d); } } /* * Convert the string length of an ascii hex mask to the number * of bits represented by that mask. */ static int s2nbits(const char *s) { return strlen(s) * 32 / 9; } /* Is string 'pre' a prefix of string 's'? */ static int strprefix(const char *s, const char *pre) { return strncmp(s, pre, strlen(pre)) == 0; } static const char *mask_size_file = "/proc/self/status"; static const char *nodemask_prefix = "Mems_allowed:\t"; /* * (do this the way Paul Jackson's libcpuset does it) * The nodemask values in /proc/self/status are in an * ascii format that uses 9 characters for each 32 bits of mask. * (this could also be used to find the cpumask size) */ static void set_nodemask_size(void) { FILE *fp; char *buf = NULL; size_t bufsize = 0; if ((fp = fopen(mask_size_file, "r")) == NULL) goto done; while (getline(&buf, &bufsize, fp) > 0) { if (strprefix(buf, nodemask_prefix)) { nodemask_sz = s2nbits(buf + strlen(nodemask_prefix)); break; } } free(buf); fclose(fp); done: if (nodemask_sz == 0) {/* fall back on error */ int pol; unsigned long *mask = NULL; nodemask_sz = 16; do { nodemask_sz <<= 1; mask = realloc(mask, nodemask_sz / 8); if (!mask) return; } while (get_mempolicy(&pol, mask, nodemask_sz + 1, 0, 0) < 0 && errno == EINVAL && nodemask_sz < 4096*8); free(mask); } } /* * Read a mask consisting of a sequence of hexadecimal longs separated by * commas. Order them correctly and return the number of bits set. */ static int read_mask(char *s, struct bitmask *bmp) { char *end = s; int tmplen = (bmp->size + bitsperint - 1) / bitsperint; unsigned int tmp[tmplen]; unsigned int *start = tmp; unsigned int i, n = 0, m = 0; if (!s) return 0; /* shouldn't happen */ i = strtoul(s, &end, 16); /* Skip leading zeros */ while (!i && *end++ == ',') { i = strtoul(end, &end, 16); } if (!i) /* End of string. No mask */ return -1; start[n++] = i; /* Read sequence of ints */ while (*end++ == ',') { i = strtoul(end, &end, 16); start[n++] = i; /* buffer overflow */ if (n > tmplen) return -1; } /* * Invert sequence of ints if necessary since the first int * is the highest and we put it first because we read it first. */ while (n) { int w; unsigned long x = 0; /* read into long values in an endian-safe way */ for (w = 0; n && w < bitsperlong; w += bitsperint) x |= ((unsigned long)start[n-- - 1] << w); bmp->maskp[m++] = x; } /* * Return the number of bits set */ return numa_bitmask_weight(bmp); } /* * Read a processes constraints in terms of nodes and cpus from * /proc/self/status. */ static void set_task_constraints(void) { int hicpu = maxconfiguredcpu; int i; char *buffer = NULL; size_t buflen = 0; FILE *f; numa_all_cpus_ptr = numa_allocate_cpumask(); numa_possible_cpus_ptr = numa_allocate_cpumask(); numa_all_nodes_ptr = numa_allocate_nodemask(); numa_possible_nodes_ptr = numa_allocate_cpumask(); numa_no_nodes_ptr = numa_allocate_nodemask(); f = fopen(mask_size_file, "r"); if (!f) { //numa_warn(W_cpumap, "Cannot parse %s", mask_size_file); return; } while (getline(&buffer, &buflen, f) > 0) { /* mask starts after [last] tab */ char *mask = strrchr(buffer,'\t') + 1; if (strncmp(buffer,"Cpus_allowed:",13) == 0) numproccpu = read_mask(mask, numa_all_cpus_ptr); if (strncmp(buffer,"Mems_allowed:",13) == 0) { numprocnode = read_mask(mask, numa_all_nodes_ptr); } } fclose(f); free(buffer); for (i = 0; i <= hicpu; i++) numa_bitmask_setbit(numa_possible_cpus_ptr, i); for (i = 0; i <= maxconfigurednode; i++) numa_bitmask_setbit(numa_possible_nodes_ptr, i); /* * Cpus_allowed in the kernel can be defined to all f's * i.e. it may be a superset of the actual available processors. * As such let's reduce numproccpu to the number of actual * available cpus. */ if (numproccpu <= 0) { for (i = 0; i <= hicpu; i++) numa_bitmask_setbit(numa_all_cpus_ptr, i); numproccpu = hicpu+1; } if (numproccpu > hicpu+1) { numproccpu = hicpu+1; for (i=hicpu+1; isize; i++) { numa_bitmask_clearbit(numa_all_cpus_ptr, i); } } if (numprocnode <= 0) { for (i = 0; i <= maxconfigurednode; i++) numa_bitmask_setbit(numa_all_nodes_ptr, i); numprocnode = maxconfigurednode + 1; } return; } /* * Find the highest cpu number possible (in other words the size * of a kernel cpumask_t (in bits) - 1) */ static void set_numa_max_cpu(void) { int len = 4096; int n; int olde = errno; struct bitmask *buffer; do { buffer = numa_bitmask_alloc(len); n = numa_sched_getaffinity_v2_int(0, buffer); /* on success, returns size of kernel cpumask_t, in bytes */ if (n < 0 && errno == EINVAL) { if (len >= 1024*1024) break; len *= 2; numa_bitmask_free(buffer); continue; } } while (n < 0); numa_bitmask_free(buffer); errno = olde; cpumask_sz = n*8; } /* * get the total (configured) number of cpus - both online and offline */ static void set_configured_cpus(void) { maxconfiguredcpu = sysconf(_SC_NPROCESSORS_CONF) - 1; if (maxconfiguredcpu == -1) numa_error("sysconf(NPROCESSORS_CONF) failed.\n"); } /* * Initialize all the sizes. */ static void set_sizes(void) { sizes_set++; set_nodemask_size(); /* size of kernel nodemask_t */ set_configured_nodes(); /* configured nodes listed in /sys */ set_numa_max_cpu(); /* size of kernel cpumask_t */ set_configured_cpus(); /* cpus listed in /sys/devices/system/cpu */ set_task_constraints(); /* cpus and nodes for current task */ } int numa_num_configured_nodes(void) { /* * NOTE: this function's behavior matches the documentation (ie: it * returns a count of nodes with memory) despite the poor function * naming. We also cannot use the similarly poorly named * numa_all_nodes_ptr as it only tracks nodes with memory from which * the calling process can allocate. Think sparse nodes, memory-less * nodes, cpusets... */ int memnodecount=0, i; for (i=0; i <= maxconfigurednode; i++) { if (numa_bitmask_isbitset(numa_memnode_ptr, i)) memnodecount++; } return memnodecount; } int numa_num_configured_cpus(void) { return maxconfiguredcpu+1; } int numa_num_possible_nodes(void) { return nodemask_sz; } int numa_num_possible_cpus(void) { return cpumask_sz; } int numa_num_task_nodes(void) { return numprocnode; } /* * for backward compatibility */ int numa_num_thread_nodes(void) { return numa_num_task_nodes(); } int numa_num_task_cpus(void) { return numproccpu; } /* * for backward compatibility */ int numa_num_thread_cpus(void) { return numa_num_task_cpus(); } /* * Return the number of the highest node in this running system, */ int numa_max_node(void) { return maxconfigurednode; } make_internal_alias(numa_max_node); /* * Return the number of the highest possible node in a system, * which for v1 is the size of a numa.h nodemask_t(in bits)-1. * but for v2 is the size of a kernel nodemask_t(in bits)-1. */ int numa_max_possible_node_v1(void) { return ((sizeof(nodemask_t)*8)-1); } __asm__(".symver numa_max_possible_node_v1,numa_max_possible_node@libnuma_1.1"); int numa_max_possible_node_v2(void) { return numa_num_possible_nodes()-1; } __asm__(".symver numa_max_possible_node_v2,numa_max_possible_node@@libnuma_1.2"); make_internal_alias(numa_max_possible_node_v1); make_internal_alias(numa_max_possible_node_v2); /* * Allocate a bitmask for cpus, of a size large enough to * match the kernel's cpumask_t. */ struct bitmask * numa_allocate_cpumask() { int ncpus = numa_num_possible_cpus(); return numa_bitmask_alloc(ncpus); } /* * Allocate a bitmask the size of a libnuma nodemask_t */ static struct bitmask * allocate_nodemask_v1(void) { int nnodes = numa_max_possible_node_v1_int()+1; return numa_bitmask_alloc(nnodes); } /* * Allocate a bitmask for nodes, of a size large enough to * match the kernel's nodemask_t. */ struct bitmask * numa_allocate_nodemask(void) { struct bitmask *bmp; int nnodes = numa_max_possible_node_v2_int() + 1; bmp = numa_bitmask_alloc(nnodes); return bmp; } /* (cache the result?) */ long long numa_node_size64(int node, long long *freep) { size_t len = 0; char *line = NULL; long long size = -1; FILE *f; char fn[64]; int ok = 0; int required = freep ? 2 : 1; if (freep) *freep = -1; sprintf(fn,"/sys/devices/system/node/node%d/meminfo", node); f = fopen(fn, "r"); if (!f) return -1; while (getdelim(&line, &len, '\n', f) > 0) { char *end; char *s = strcasestr(line, "kB"); if (!s) continue; --s; while (s > line && isspace(*s)) --s; while (s > line && isdigit(*s)) --s; if (strstr(line, "MemTotal")) { size = strtoull(s,&end,0) << 10; if (end == s) size = -1; else ok++; } if (freep && strstr(line, "MemFree")) { *freep = strtoull(s,&end,0) << 10; if (end == s) *freep = -1; else ok++; } } fclose(f); free(line); if (ok != required) numa_warn(W_badmeminfo, "Cannot parse sysfs meminfo (%d)", ok); return size; } make_internal_alias(numa_node_size64); long numa_node_size(int node, long *freep) { long long f2; long sz = numa_node_size64_int(node, &f2); if (freep) *freep = f2; return sz; } int numa_available(void) { if (get_mempolicy(NULL, NULL, 0, 0, 0) < 0 && errno == ENOSYS) return -1; return 0; } void numa_interleave_memory_v1(void *mem, size_t size, const nodemask_t *mask) { struct bitmask bitmask; bitmask.size = sizeof(nodemask_t) * 8; bitmask.maskp = (unsigned long *)mask; dombind(mem, size, MPOL_INTERLEAVE, &bitmask); } __asm__(".symver numa_interleave_memory_v1,numa_interleave_memory@libnuma_1.1"); void numa_interleave_memory_v2(void *mem, size_t size, struct bitmask *bmp) { dombind(mem, size, MPOL_INTERLEAVE, bmp); } __asm__(".symver numa_interleave_memory_v2,numa_interleave_memory@@libnuma_1.2"); void numa_tonode_memory(void *mem, size_t size, int node) { struct bitmask *nodes; nodes = numa_allocate_nodemask(); numa_bitmask_setbit(nodes, node); dombind(mem, size, bind_policy, nodes); numa_bitmask_free(nodes); } void numa_tonodemask_memory_v1(void *mem, size_t size, const nodemask_t *mask) { struct bitmask bitmask; bitmask.maskp = (unsigned long *)mask; bitmask.size = sizeof(nodemask_t); dombind(mem, size, bind_policy, &bitmask); } __asm__(".symver numa_tonodemask_memory_v1,numa_tonodemask_memory@libnuma_1.1"); void numa_tonodemask_memory_v2(void *mem, size_t size, struct bitmask *bmp) { dombind(mem, size, bind_policy, bmp); } __asm__(".symver numa_tonodemask_memory_v2,numa_tonodemask_memory@@libnuma_1.2"); void numa_setlocal_memory(void *mem, size_t size) { dombind(mem, size, MPOL_PREFERRED, NULL); } void numa_police_memory(void *mem, size_t size) { int pagesize = numa_pagesize_int(); unsigned long i; for (i = 0; i < size; i += pagesize) asm volatile("" :: "r" (((volatile unsigned char *)mem)[i])); } make_internal_alias(numa_police_memory); void *numa_alloc(size_t size) { char *mem; mem = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); if (mem == (char *)-1) return NULL; numa_police_memory_int(mem, size); return mem; } void *numa_realloc(void *old_addr, size_t old_size, size_t new_size) { char *mem; mem = mremap(old_addr, old_size, new_size, MREMAP_MAYMOVE); if (mem == (char *)-1) return NULL; /* * The memory policy of the allocated pages is preserved by mremap(), so * there is no need to (re)set it here. If the policy of the original * allocation is not set, the new pages will be allocated according to the * process' mempolicy. Trying to allocate explicitly the new pages on the * same node as the original ones would require changing the policy of the * newly allocated pages, which violates the numa_realloc() semantics. */ return mem; } void *numa_alloc_interleaved_subset_v1(size_t size, const nodemask_t *mask) { char *mem; struct bitmask bitmask; mem = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); if (mem == (char *)-1) return NULL; bitmask.maskp = (unsigned long *)mask; bitmask.size = sizeof(nodemask_t); dombind(mem, size, MPOL_INTERLEAVE, &bitmask); return mem; } __asm__(".symver numa_alloc_interleaved_subset_v1,numa_alloc_interleaved_subset@libnuma_1.1"); void *numa_alloc_interleaved_subset_v2(size_t size, struct bitmask *bmp) { char *mem; mem = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); if (mem == (char *)-1) return NULL; dombind(mem, size, MPOL_INTERLEAVE, bmp); return mem; } __asm__(".symver numa_alloc_interleaved_subset_v2,numa_alloc_interleaved_subset@@libnuma_1.2"); make_internal_alias(numa_alloc_interleaved_subset_v1); make_internal_alias(numa_alloc_interleaved_subset_v2); void * numa_alloc_interleaved(size_t size) { return numa_alloc_interleaved_subset_v2_int(size, numa_all_nodes_ptr); } /* * given a user node mask, set memory policy to use those nodes */ void numa_set_interleave_mask_v1(nodemask_t *mask) { struct bitmask *bmp; int nnodes = numa_max_possible_node_v1_int()+1; bmp = numa_bitmask_alloc(nnodes); copy_nodemask_to_bitmask(mask, bmp); if (numa_bitmask_equal(bmp, numa_no_nodes_ptr)) setpol(MPOL_DEFAULT, bmp); else setpol(MPOL_INTERLEAVE, bmp); numa_bitmask_free(bmp); } __asm__(".symver numa_set_interleave_mask_v1,numa_set_interleave_mask@libnuma_1.1"); void numa_set_interleave_mask_v2(struct bitmask *bmp) { if (numa_bitmask_equal(bmp, numa_no_nodes_ptr)) setpol(MPOL_DEFAULT, bmp); else setpol(MPOL_INTERLEAVE, bmp); } __asm__(".symver numa_set_interleave_mask_v2,numa_set_interleave_mask@@libnuma_1.2"); nodemask_t numa_get_interleave_mask_v1(void) { int oldpolicy; struct bitmask *bmp; nodemask_t mask; bmp = allocate_nodemask_v1(); getpol(&oldpolicy, bmp); if (oldpolicy == MPOL_INTERLEAVE) copy_bitmask_to_nodemask(bmp, &mask); else copy_bitmask_to_nodemask(numa_no_nodes_ptr, &mask); numa_bitmask_free(bmp); return mask; } __asm__(".symver numa_get_interleave_mask_v1,numa_get_interleave_mask@libnuma_1.1"); struct bitmask * numa_get_interleave_mask_v2(void) { int oldpolicy; struct bitmask *bmp; bmp = numa_allocate_nodemask(); getpol(&oldpolicy, bmp); if (oldpolicy != MPOL_INTERLEAVE) copy_bitmask_to_bitmask(numa_no_nodes_ptr, bmp); return bmp; } __asm__(".symver numa_get_interleave_mask_v2,numa_get_interleave_mask@@libnuma_1.2"); /* (undocumented) */ int numa_get_interleave_node(void) { int nd; if (get_mempolicy(&nd, NULL, 0, 0, MPOL_F_NODE) == 0) return nd; return 0; } void *numa_alloc_onnode(size_t size, int node) { char *mem; struct bitmask *bmp; bmp = numa_allocate_nodemask(); numa_bitmask_setbit(bmp, node); mem = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); if (mem == (char *)-1) mem = NULL; else dombind(mem, size, bind_policy, bmp); numa_bitmask_free(bmp); return mem; } void *numa_alloc_local(size_t size) { char *mem; mem = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); if (mem == (char *)-1) mem = NULL; else dombind(mem, size, MPOL_PREFERRED, NULL); return mem; } void numa_set_bind_policy(int strict) { if (strict) bind_policy = MPOL_BIND; else bind_policy = MPOL_PREFERRED; } void numa_set_membind_v1(const nodemask_t *mask) { struct bitmask bitmask; bitmask.maskp = (unsigned long *)mask; bitmask.size = sizeof(nodemask_t); setpol(MPOL_BIND, &bitmask); } __asm__(".symver numa_set_membind_v1,numa_set_membind@libnuma_1.1"); void numa_set_membind_v2(struct bitmask *bmp) { setpol(MPOL_BIND, bmp); } __asm__(".symver numa_set_membind_v2,numa_set_membind@@libnuma_1.2"); make_internal_alias(numa_set_membind_v2); /* * copy a bitmask map body to a numa.h nodemask_t structure */ void copy_bitmask_to_nodemask(struct bitmask *bmp, nodemask_t *nmp) { int max, i; memset(nmp, 0, sizeof(nodemask_t)); max = (sizeof(nodemask_t)*8); for (i=0; isize; i++) { if (i >= max) break; if (numa_bitmask_isbitset(bmp, i)) nodemask_set_compat((nodemask_t *)nmp, i); } } /* * copy a bitmask map body to another bitmask body * fill a larger destination with zeroes */ void copy_bitmask_to_bitmask(struct bitmask *bmpfrom, struct bitmask *bmpto) { int bytes; if (bmpfrom->size >= bmpto->size) { memcpy(bmpto->maskp, bmpfrom->maskp, CPU_BYTES(bmpto->size)); } else if (bmpfrom->size < bmpto->size) { bytes = CPU_BYTES(bmpfrom->size); memcpy(bmpto->maskp, bmpfrom->maskp, bytes); memset(((char *)bmpto->maskp)+bytes, 0, CPU_BYTES(bmpto->size)-bytes); } } /* * copy a numa.h nodemask_t structure to a bitmask map body */ void copy_nodemask_to_bitmask(nodemask_t *nmp, struct bitmask *bmp) { int max, i; numa_bitmask_clearall(bmp); max = (sizeof(nodemask_t)*8); if (max > bmp->size) max = bmp->size; for (i=0; imaskp, bmp->size + 1, 0, MPOL_F_MEMS_ALLOWED) < 0) numa_error("get_mempolicy"); return bmp; } make_internal_alias(numa_get_mems_allowed); void numa_free(void *mem, size_t size) { munmap(mem, size); } int numa_parse_bitmap_v1(char *line, unsigned long *mask, int ncpus) { int i; char *p = strchr(line, '\n'); if (!p) return -1; for (i = 0; p > line;i++) { char *oldp, *endp; oldp = p; if (*p == ',') --p; while (p > line && *p != ',') --p; /* Eat two 32bit fields at a time to get longs */ if (p > line && sizeof(unsigned long) == 8) { oldp--; memmove(p, p+1, oldp-p+1); while (p > line && *p != ',') --p; } if (*p == ',') p++; if (i >= CPU_LONGS(ncpus)) return -1; mask[i] = strtoul(p, &endp, 16); if (endp != oldp) return -1; p--; } return 0; } __asm__(".symver numa_parse_bitmap_v1,numa_parse_bitmap@libnuma_1.1"); int numa_parse_bitmap_v2(char *line, struct bitmask *mask) { int i, ncpus; char *p = strchr(line, '\n'); if (!p) return -1; ncpus = mask->size; for (i = 0; p > line;i++) { char *oldp, *endp; oldp = p; if (*p == ',') --p; while (p > line && *p != ',') --p; /* Eat two 32bit fields at a time to get longs */ if (p > line && sizeof(unsigned long) == 8) { oldp--; memmove(p, p+1, oldp-p+1); while (p > line && *p != ',') --p; } if (*p == ',') p++; if (i >= CPU_LONGS(ncpus)) return -1; mask->maskp[i] = strtoul(p, &endp, 16); if (endp != oldp) return -1; p--; } return 0; } __asm__(".symver numa_parse_bitmap_v2,numa_parse_bitmap@@libnuma_1.2"); void init_node_cpu_mask_v2(void) { int nnodes = numa_max_possible_node_v2_int() + 1; node_cpu_mask_v2 = calloc (nnodes, sizeof(struct bitmask *)); } /* This would be better with some locking, but I don't want to make libnuma dependent on pthreads right now. The races are relatively harmless. */ int numa_node_to_cpus_v1(int node, unsigned long *buffer, int bufferlen) { int err = 0; char fn[64]; FILE *f; char *line = NULL; size_t len = 0; struct bitmask bitmask; int buflen_needed; unsigned long *mask; int ncpus = numa_num_possible_cpus(); int maxnode = numa_max_node_int(); buflen_needed = CPU_BYTES(ncpus); if ((unsigned)node > maxnode || bufferlen < buflen_needed) { errno = ERANGE; return -1; } if (bufferlen > buflen_needed) memset(buffer, 0, bufferlen); if (node_cpu_mask_v1[node]) { memcpy(buffer, node_cpu_mask_v1[node], buflen_needed); return 0; } mask = malloc(buflen_needed); if (!mask) mask = (unsigned long *)buffer; memset(mask, 0, buflen_needed); sprintf(fn, "/sys/devices/system/node/node%d/cpumap", node); f = fopen(fn, "r"); if (!f || getdelim(&line, &len, '\n', f) < 1) { numa_warn(W_nosysfs2, "/sys not mounted or invalid. Assuming one node: %s", strerror(errno)); numa_warn(W_nosysfs2, "(cannot open or correctly parse %s)", fn); bitmask.maskp = (unsigned long *)mask; bitmask.size = buflen_needed * 8; numa_bitmask_setall(&bitmask); err = -1; } if (f) fclose(f); if (line && (numa_parse_bitmap_v1(line, mask, ncpus) < 0)) { numa_warn(W_cpumap, "Cannot parse cpumap. Assuming one node"); bitmask.maskp = (unsigned long *)mask; bitmask.size = buflen_needed * 8; numa_bitmask_setall(&bitmask); err = -1; } free(line); memcpy(buffer, mask, buflen_needed); /* slightly racy, see above */ if (node_cpu_mask_v1[node]) { if (mask != buffer) free(mask); } else { node_cpu_mask_v1[node] = mask; } return err; } __asm__(".symver numa_node_to_cpus_v1,numa_node_to_cpus@libnuma_1.1"); /* * test whether a node has cpus */ /* This would be better with some locking, but I don't want to make libnuma dependent on pthreads right now. The races are relatively harmless. */ /* * deliver a bitmask of cpus representing the cpus on a given node */ int numa_node_to_cpus_v2(int node, struct bitmask *buffer) { int err = 0; int nnodes = numa_max_node(); char fn[64], *line = NULL; FILE *f; size_t len = 0; struct bitmask *mask; if (!node_cpu_mask_v2) init_node_cpu_mask_v2(); if (node > nnodes) { errno = ERANGE; return -1; } numa_bitmask_clearall(buffer); if (node_cpu_mask_v2[node]) { /* have already constructed a mask for this node */ if (buffer->size < node_cpu_mask_v2[node]->size) { numa_error("map size mismatch; abort\n"); return -1; } copy_bitmask_to_bitmask(node_cpu_mask_v2[node], buffer); return 0; } /* need a new mask for this node */ mask = numa_allocate_cpumask(); /* this is a kernel cpumask_t (see node_read_cpumap()) */ sprintf(fn, "/sys/devices/system/node/node%d/cpumap", node); f = fopen(fn, "r"); if (!f || getdelim(&line, &len, '\n', f) < 1) { numa_warn(W_nosysfs2, "/sys not mounted or invalid. Assuming one node: %s", strerror(errno)); numa_warn(W_nosysfs2, "(cannot open or correctly parse %s)", fn); numa_bitmask_setall(mask); err = -1; } if (f) fclose(f); if (line && (numa_parse_bitmap_v2(line, mask) < 0)) { numa_warn(W_cpumap, "Cannot parse cpumap. Assuming one node"); numa_bitmask_setall(mask); err = -1; } free(line); copy_bitmask_to_bitmask(mask, buffer); /* slightly racy, see above */ /* save the mask we created */ if (node_cpu_mask_v2[node]) { /* how could this be? */ if (mask != buffer) numa_bitmask_free(mask); } else { node_cpu_mask_v2[node] = mask; } return err; } __asm__(".symver numa_node_to_cpus_v2,numa_node_to_cpus@@libnuma_1.2"); make_internal_alias(numa_node_to_cpus_v1); make_internal_alias(numa_node_to_cpus_v2); /* report the node of the specified cpu */ int numa_node_of_cpu(int cpu) { struct bitmask *bmp; int ncpus, nnodes, node, ret; ncpus = numa_num_possible_cpus(); if (cpu > ncpus){ errno = EINVAL; return -1; } bmp = numa_bitmask_alloc(ncpus); nnodes = numa_max_node(); for (node = 0; node <= nnodes; node++){ numa_node_to_cpus_v2_int(node, bmp); if (numa_bitmask_isbitset(bmp, cpu)){ ret = node; goto end; } } ret = -1; errno = EINVAL; end: numa_bitmask_free(bmp); return ret; } int numa_run_on_node_mask_v1(const nodemask_t *mask) { int ncpus = numa_num_possible_cpus(); int i, k, err; unsigned long cpus[CPU_LONGS(ncpus)], nodecpus[CPU_LONGS(ncpus)]; memset(cpus, 0, CPU_BYTES(ncpus)); for (i = 0; i < NUMA_NUM_NODES; i++) { if (mask->n[i / BITS_PER_LONG] == 0) continue; if (nodemask_isset_compat(mask, i)) { if (numa_node_to_cpus_v1_int(i, nodecpus, CPU_BYTES(ncpus)) < 0) { numa_warn(W_noderunmask, "Cannot read node cpumask from sysfs"); continue; } for (k = 0; k < CPU_LONGS(ncpus); k++) cpus[k] |= nodecpus[k]; } } err = numa_sched_setaffinity_v1(0, CPU_BYTES(ncpus), cpus); /* The sched_setaffinity API is broken because it expects the user to guess the kernel cpuset size. Do this in a brute force way. */ if (err < 0 && errno == EINVAL) { int savederrno = errno; char *bigbuf; static int size = -1; if (size == -1) size = CPU_BYTES(ncpus) * 2; bigbuf = malloc(CPU_BUFFER_SIZE); if (!bigbuf) { errno = ENOMEM; return -1; } errno = savederrno; while (size <= CPU_BUFFER_SIZE) { memcpy(bigbuf, cpus, CPU_BYTES(ncpus)); memset(bigbuf + CPU_BYTES(ncpus), 0, CPU_BUFFER_SIZE - CPU_BYTES(ncpus)); err = numa_sched_setaffinity_v1_int(0, size, (unsigned long *)bigbuf); if (err == 0 || errno != EINVAL) break; size *= 2; } savederrno = errno; free(bigbuf); errno = savederrno; } return err; } __asm__(".symver numa_run_on_node_mask_v1,numa_run_on_node_mask@libnuma_1.1"); /* * Given a node mask (size of a kernel nodemask_t) (probably populated by * a user argument list) set up a map of cpus (map "cpus") on those nodes. * Then set affinity to those cpus. */ int numa_run_on_node_mask_v2(struct bitmask *bmp) { int ncpus, i, k, err; struct bitmask *cpus, *nodecpus; cpus = numa_allocate_cpumask(); ncpus = cpus->size; nodecpus = numa_allocate_cpumask(); for (i = 0; i < bmp->size; i++) { if (bmp->maskp[i / BITS_PER_LONG] == 0) continue; if (numa_bitmask_isbitset(bmp, i)) { /* * numa_all_nodes_ptr is cpuset aware; use only * these nodes */ if (!numa_bitmask_isbitset(numa_all_nodes_ptr, i)) { numa_warn(W_noderunmask, "node %d not allowed", i); continue; } if (numa_node_to_cpus_v2_int(i, nodecpus) < 0) { numa_warn(W_noderunmask, "Cannot read node cpumask from sysfs"); continue; } for (k = 0; k < CPU_LONGS(ncpus); k++) cpus->maskp[k] |= nodecpus->maskp[k]; } } err = numa_sched_setaffinity_v2_int(0, cpus); numa_bitmask_free(cpus); numa_bitmask_free(nodecpus); /* used to have to consider that this could fail - it shouldn't now */ if (err < 0) { numa_error("numa_sched_setaffinity_v2_int() failed; abort\n"); } return err; } __asm__(".symver numa_run_on_node_mask_v2,numa_run_on_node_mask@@libnuma_1.2"); make_internal_alias(numa_run_on_node_mask_v2); /* * Given a node mask (size of a kernel nodemask_t) (probably populated by * a user argument list) set up a map of cpus (map "cpus") on those nodes * without any cpuset awareness. Then set affinity to those cpus. */ int numa_run_on_node_mask_all(struct bitmask *bmp) { int ncpus, i, k, err; struct bitmask *cpus, *nodecpus; cpus = numa_allocate_cpumask(); ncpus = cpus->size; nodecpus = numa_allocate_cpumask(); for (i = 0; i < bmp->size; i++) { if (bmp->maskp[i / BITS_PER_LONG] == 0) continue; if (numa_bitmask_isbitset(bmp, i)) { if (!numa_bitmask_isbitset(numa_possible_nodes_ptr, i)) { numa_warn(W_noderunmask, "node %d not allowed", i); continue; } if (numa_node_to_cpus_v2_int(i, nodecpus) < 0) { numa_warn(W_noderunmask, "Cannot read node cpumask from sysfs"); continue; } for (k = 0; k < CPU_LONGS(ncpus); k++) cpus->maskp[k] |= nodecpus->maskp[k]; } } err = numa_sched_setaffinity_v2_int(0, cpus); numa_bitmask_free(cpus); numa_bitmask_free(nodecpus); /* With possible nodes freedom it can happen easily now */ if (err < 0) { numa_error("numa_sched_setaffinity_v2_int() failed; abort\n"); } return err; } nodemask_t numa_get_run_node_mask_v1(void) { int ncpus = numa_num_configured_cpus(); int i, k; int max = numa_max_node_int(); struct bitmask *bmp, *cpus, *nodecpus; nodemask_t nmp; cpus = numa_allocate_cpumask(); if (numa_sched_getaffinity_v2_int(0, cpus) < 0){ nmp = numa_no_nodes; goto free_cpus; } nodecpus = numa_allocate_cpumask(); bmp = allocate_nodemask_v1(); /* the size of a nodemask_t */ for (i = 0; i <= max; i++) { if (numa_node_to_cpus_v2_int(i, nodecpus) < 0) { /* It's possible for the node to not exist */ continue; } for (k = 0; k < CPU_LONGS(ncpus); k++) { if (nodecpus->maskp[k] & cpus->maskp[k]) numa_bitmask_setbit(bmp, i); } } copy_bitmask_to_nodemask(bmp, &nmp); numa_bitmask_free(bmp); numa_bitmask_free(nodecpus); free_cpus: numa_bitmask_free(cpus); return nmp; } __asm__(".symver numa_get_run_node_mask_v1,numa_get_run_node_mask@libnuma_1.1"); struct bitmask * numa_get_run_node_mask_v2(void) { int i, k; int ncpus = numa_num_configured_cpus(); int max = numa_max_node_int(); struct bitmask *bmp, *cpus, *nodecpus; bmp = numa_allocate_cpumask(); cpus = numa_allocate_cpumask(); if (numa_sched_getaffinity_v2_int(0, cpus) < 0){ copy_bitmask_to_bitmask(numa_no_nodes_ptr, bmp); goto free_cpus; } nodecpus = numa_allocate_cpumask(); for (i = 0; i <= max; i++) { /* * numa_all_nodes_ptr is cpuset aware; show only * these nodes */ if (!numa_bitmask_isbitset(numa_all_nodes_ptr, i)) { continue; } if (numa_node_to_cpus_v2_int(i, nodecpus) < 0) { /* It's possible for the node to not exist */ continue; } for (k = 0; k < CPU_LONGS(ncpus); k++) { if (nodecpus->maskp[k] & cpus->maskp[k]) numa_bitmask_setbit(bmp, i); } } numa_bitmask_free(nodecpus); free_cpus: numa_bitmask_free(cpus); return bmp; } __asm__(".symver numa_get_run_node_mask_v2,numa_get_run_node_mask@@libnuma_1.2"); int numa_migrate_pages(int pid, struct bitmask *fromnodes, struct bitmask *tonodes) { int numa_num_nodes = numa_num_possible_nodes(); return migrate_pages(pid, numa_num_nodes + 1, fromnodes->maskp, tonodes->maskp); } int numa_move_pages(int pid, unsigned long count, void **pages, const int *nodes, int *status, int flags) { return move_pages(pid, count, pages, nodes, status, flags); } int numa_run_on_node(int node) { int numa_num_nodes = numa_num_possible_nodes(); int ret = -1; struct bitmask *cpus; if (node >= numa_num_nodes){ errno = EINVAL; goto out; } cpus = numa_allocate_cpumask(); if (node == -1) numa_bitmask_setall(cpus); else if (numa_node_to_cpus_v2_int(node, cpus) < 0){ numa_warn(W_noderunmask, "Cannot read node cpumask from sysfs"); goto free; } ret = numa_sched_setaffinity_v2_int(0, cpus); free: numa_bitmask_free(cpus); out: return ret; } int numa_preferred(void) { int policy; int ret; struct bitmask *bmp; bmp = numa_allocate_nodemask(); getpol(&policy, bmp); if (policy == MPOL_PREFERRED || policy == MPOL_BIND) { int i; int max = numa_num_possible_nodes(); for (i = 0; i < max ; i++) if (numa_bitmask_isbitset(bmp, i)){ ret = i; goto end; } } /* could read the current CPU from /proc/self/status. Probably not worth it. */ ret = 0; /* or random one? */ end: numa_bitmask_free(bmp); return ret; } void numa_set_preferred(int node) { struct bitmask *bmp; bmp = numa_allocate_nodemask(); if (node >= 0) { numa_bitmask_setbit(bmp, node); setpol(MPOL_PREFERRED, bmp); } else setpol(MPOL_DEFAULT, bmp); numa_bitmask_free(bmp); } void numa_set_localalloc(void) { setpol(MPOL_DEFAULT, numa_no_nodes_ptr); } void numa_bind_v1(const nodemask_t *nodemask) { struct bitmask bitmask; bitmask.maskp = (unsigned long *)nodemask; bitmask.size = sizeof(nodemask_t); numa_run_on_node_mask_v2_int(&bitmask); numa_set_membind_v2_int(&bitmask); } __asm__(".symver numa_bind_v1,numa_bind@libnuma_1.1"); void numa_bind_v2(struct bitmask *bmp) { numa_run_on_node_mask_v2_int(bmp); numa_set_membind_v2_int(bmp); } __asm__(".symver numa_bind_v2,numa_bind@@libnuma_1.2"); void numa_set_strict(int flag) { if (flag) mbind_flags |= MPOL_MF_STRICT; else mbind_flags &= ~MPOL_MF_STRICT; } /* * Extract a node or processor number from the given string. * Allow a relative node / processor specification within the allowed * set if "relative" is nonzero */ static unsigned long get_nr(const char *s, char **end, struct bitmask *bmp, int relative) { long i, nr; if (!relative) return strtoul(s, end, 0); nr = strtoul(s, end, 0); if (s == *end) return nr; /* Find the nth set bit */ for (i = 0; nr >= 0 && i <= bmp->size; i++) if (numa_bitmask_isbitset(bmp, i)) nr--; return i-1; } /* * __numa_parse_nodestring() is called to create a node mask, given * an ascii string such as 25 or 12-15 or 1,3,5-7 or +6-10. * (the + indicates that the numbers are nodeset-relative) * * The nodes may be specified as absolute, or relative to the current nodeset. * The list of available nodes is in a map pointed to by "allowed_nodes_ptr", * which may represent all nodes or the nodes in the current nodeset. * * The caller must free the returned bitmask. */ static struct bitmask * __numa_parse_nodestring(const char *s, struct bitmask *allowed_nodes_ptr) { int invert = 0, relative = 0; int conf_nodes = numa_num_configured_nodes(); char *end; struct bitmask *mask; mask = numa_allocate_nodemask(); if (s[0] == 0){ copy_bitmask_to_bitmask(numa_no_nodes_ptr, mask); return mask; /* return freeable mask */ } if (*s == '!') { invert = 1; s++; } if (*s == '+') { relative++; s++; } do { unsigned long arg; int i; if (isalpha(*s)) { int n; if (!strcmp(s,"all")) { copy_bitmask_to_bitmask(allowed_nodes_ptr, mask); s+=4; break; } n = resolve_affinity(s, mask); if (n != NO_IO_AFFINITY) { if (n < 0) goto err; s += strlen(s) + 1; break; } } arg = get_nr(s, &end, allowed_nodes_ptr, relative); if (end == s) { numa_warn(W_nodeparse, "unparseable node description `%s'\n", s); goto err; } if (!numa_bitmask_isbitset(allowed_nodes_ptr, arg)) { numa_warn(W_nodeparse, "node argument %d is out of range\n", arg); goto err; } i = arg; numa_bitmask_setbit(mask, i); s = end; if (*s == '-') { char *end2; unsigned long arg2; arg2 = get_nr(++s, &end2, allowed_nodes_ptr, relative); if (end2 == s) { numa_warn(W_nodeparse, "missing node argument %s\n", s); goto err; } if (!numa_bitmask_isbitset(allowed_nodes_ptr, arg2)) { numa_warn(W_nodeparse, "node argument %d out of range\n", arg2); goto err; } while (arg <= arg2) { i = arg; if (numa_bitmask_isbitset(allowed_nodes_ptr,i)) numa_bitmask_setbit(mask, i); arg++; } s = end2; } } while (*s++ == ','); if (s[-1] != '\0') goto err; if (invert) { int i; for (i = 0; i < conf_nodes; i++) { if (numa_bitmask_isbitset(mask, i)) numa_bitmask_clearbit(mask, i); else numa_bitmask_setbit(mask, i); } } return mask; err: numa_bitmask_free(mask); return NULL; } /* * numa_parse_nodestring() is called to create a bitmask from nodes available * for this task. */ struct bitmask * numa_parse_nodestring(const char *s) { return __numa_parse_nodestring(s, numa_all_nodes_ptr); } /* * numa_parse_nodestring_all() is called to create a bitmask from all nodes * available. */ struct bitmask * numa_parse_nodestring_all(const char *s) { return __numa_parse_nodestring(s, numa_possible_nodes_ptr); } /* * __numa_parse_cpustring() is called to create a bitmask, given * an ascii string such as 25 or 12-15 or 1,3,5-7 or +6-10. * (the + indicates that the numbers are cpuset-relative) * * The cpus may be specified as absolute, or relative to the current cpuset. * The list of available cpus for this task is in the map pointed to by * "allowed_cpus_ptr", which may represent all cpus or the cpus in the * current cpuset. * * The caller must free the returned bitmask. */ static struct bitmask * __numa_parse_cpustring(const char *s, struct bitmask *allowed_cpus_ptr) { int invert = 0, relative=0; int conf_cpus = numa_num_configured_cpus(); char *end; struct bitmask *mask; mask = numa_allocate_cpumask(); if (s[0] == 0) return mask; if (*s == '!') { invert = 1; s++; } if (*s == '+') { relative++; s++; } do { unsigned long arg; int i; if (!strcmp(s,"all")) { copy_bitmask_to_bitmask(allowed_cpus_ptr, mask); s+=4; break; } arg = get_nr(s, &end, allowed_cpus_ptr, relative); if (end == s) { numa_warn(W_cpuparse, "unparseable cpu description `%s'\n", s); goto err; } if (!numa_bitmask_isbitset(allowed_cpus_ptr, arg)) { numa_warn(W_cpuparse, "cpu argument %s is out of range\n", s); goto err; } i = arg; numa_bitmask_setbit(mask, i); s = end; if (*s == '-') { char *end2; unsigned long arg2; int i; arg2 = get_nr(++s, &end2, allowed_cpus_ptr, relative); if (end2 == s) { numa_warn(W_cpuparse, "missing cpu argument %s\n", s); goto err; } if (!numa_bitmask_isbitset(allowed_cpus_ptr, arg2)) { numa_warn(W_cpuparse, "cpu argument %s out of range\n", s); goto err; } while (arg <= arg2) { i = arg; if (numa_bitmask_isbitset(allowed_cpus_ptr, i)) numa_bitmask_setbit(mask, i); arg++; } s = end2; } } while (*s++ == ','); if (s[-1] != '\0') goto err; if (invert) { int i; for (i = 0; i < conf_cpus; i++) { if (numa_bitmask_isbitset(mask, i)) numa_bitmask_clearbit(mask, i); else numa_bitmask_setbit(mask, i); } } return mask; err: numa_bitmask_free(mask); return NULL; } /* * numa_parse_cpustring() is called to create a bitmask from cpus available * for this task. */ struct bitmask * numa_parse_cpustring(const char *s) { return __numa_parse_cpustring(s, numa_all_cpus_ptr); } /* * numa_parse_cpustring_all() is called to create a bitmask from all cpus * available. */ struct bitmask * numa_parse_cpustring_all(const char *s) { return __numa_parse_cpustring(s, numa_possible_cpus_ptr); } ./numactl-2.0.9~rc5/numamon.c0000755000175000017500000001563512213661422014442 0ustar ianwianw/* Copyright (C) 2003,2004 Andi Kleen, SuSE Labs. numamon 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; version 2. numamon 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. See the GNU General Public License for more details. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Display some numa statistics collected by the CPU. Opteron specific. Also not reliable because the counters are not quite correct in hardware. */ #define _LARGE_FILE_SOURCE 1 #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include enum { LOCALLOCAL = 0, LOCALREMOTE = 1, REMOTELOCAL = 2 }; static int mem[] = { [LOCALLOCAL] = 0xa8, [LOCALREMOTE] = 0x98, [REMOTELOCAL] = 0x68 }; static int io[] = { [LOCALLOCAL] = 0xa4, [LOCALREMOTE] = 0x94, [REMOTELOCAL] = 0x64 }; static int *masks = mem; #define err(x) perror(x),exit(1) #define PERFEVTSEL0 0xc0010000 #define PERFEVTSEL1 0xc0010001 #define PERFEVTSEL2 0xc0010002 #define PERFEVTSEL3 0xc0010003 #define PERFCTR0 0xc0010004 #define PERFCTR1 0xc0010005 #define PERFCTR2 0xc0010006 #define PERFCTR3 0xc0010007 #define EVENT 0xe9 #define PERFEVTSEL_EN (1 << 22) #define PERFEVTSEL_OS (1 << 17) #define PERFEVTSEL_USR (1 << 16) #define BASE (EVENT | PERFEVTSEL_EN | PERFEVTSEL_OS | PERFEVTSEL_USR) #define MAXCPU 8 int force = 0; int msrfd[MAXCPU]; int delay; int absolute; char *cfilter; int verbose; void usage(void); void Vprintf(char *fmt, ...) { va_list ap; va_start(ap,fmt); if (verbose) vfprintf(stderr,fmt,ap); va_end(ap); } unsigned long long rdmsr(int cpu, unsigned long msr) { unsigned long long val; if (pread(msrfd[cpu], &val, 8, msr) != 8) { fprintf(stderr, "rdmsr of %lx failed: %s\n", msr, strerror(errno)); exit(1); } return val; } void wrmsr(int cpu, unsigned long msr, unsigned long long value) { if (pwrite(msrfd[cpu], &value, 8, msr) != 8) { fprintf(stderr, "wdmsr of %lx failed: %s\n", msr, strerror(errno)); exit(1); } } int cpufilter(int cpu) { long num; char *end; char *s; if (!cfilter) return 1; for (s = cfilter;;) { num = strtoul(s, &end, 0); if (end == s) usage(); if (cpu == num) return 1; if (*end == ',') s = end+1; else if (*end == 0) break; else usage(); } return 0; } void checkcounter(int cpu, int clear) { int i; for (i = 1; i < 4; i++) { int clear_this = clear; unsigned long long evtsel = rdmsr(cpu, PERFEVTSEL0 + i); Vprintf("%d: %x %Lx\n", cpu, PERFEVTSEL0 + i, evtsel); if (!(evtsel & PERFEVTSEL_EN)) { Vprintf("reinit %d\n", cpu); wrmsr(cpu, PERFEVTSEL0 + i, BASE | masks[i - 1]); clear_this = 1; } else if (evtsel == (BASE | (masks[i-1] << 8))) { /* everything fine */ } else if (force) { Vprintf("reinit force %d\n", cpu); wrmsr(cpu, PERFEVTSEL0 + i, BASE | (masks[i - 1] << 8)); clear_this = 1; } else { fprintf(stderr, "perfctr %d cpu %d already used with %Lx\n", i, cpu, evtsel); fprintf(stderr, "Consider using -f if you know what you're doing.\n"); exit(1); } if (clear_this) { Vprintf("clearing %d\n", cpu); wrmsr(cpu, PERFCTR0 + i, 0); } } } void setup(int clear) { DIR *dir; struct dirent *d; int numcpus = 0; memset(msrfd, -1, sizeof(msrfd)); dir = opendir("/dev/cpu"); if (!dir) err("cannot open /dev/cpu"); while ((d = readdir(dir)) != NULL) { char buf[64]; char *end; long cpunum = strtoul(d->d_name, &end, 0); if (*end != 0) continue; if (cpunum > MAXCPU) { fprintf(stderr, "too many cpus %ld %s\n", cpunum, d->d_name); continue; } if (!cpufilter(cpunum)) continue; snprintf(buf, 63, "/dev/cpu/%ld/msr", cpunum); msrfd[cpunum] = open64(buf, O_RDWR); if (msrfd[cpunum] < 0) continue; numcpus++; checkcounter(cpunum, clear); } closedir(dir); if (numcpus == 0) { fprintf(stderr, "No CPU found using MSR driver.\n"); exit(1); } } void printf_padded(int pad, char *fmt, ...) { char buf[pad + 1]; va_list ap; va_start(ap, fmt); vsnprintf(buf, pad, fmt, ap); printf("%-*s", pad, buf); va_end(ap); } void print_header(void) { printf_padded(4, "CPU "); printf_padded(16, "LOCAL"); printf_padded(16, "LOCAL->REMOTE"); printf_padded(16, "REMOTE->LOCAL"); putchar('\n'); } void print_cpu(int cpu) { int i; static unsigned long long lastval[4]; printf_padded(4, "%d", cpu); for (i = 1; i < 4; i++) { unsigned long long val = rdmsr(cpu, PERFCTR0 + i); if (absolute) printf_padded(16, "%Lu", val); else printf_padded(16, "%Lu", val - lastval[i]); lastval[i] = val; } putchar('\n'); } void dumpall(void) { int cnt = 0; int cpu; print_header(); for (;;) { for (cpu = 0; cpu < MAXCPU; ++cpu) { if (msrfd[cpu] < 0) continue; print_cpu(cpu); } if (!delay) break; sleep(delay); if (++cnt > 40) { cnt = 0; print_header(); } } } void checkk8(void) { char *line = NULL; size_t size = 0; int bad = 0; FILE *f = fopen("/proc/cpuinfo", "r"); if (!f) return; while (getline(&line, &size, f) > 0) { if (!strncmp("vendor_id", line, 9)) { if (!strstr(line, "AMD")) bad++; } if (!strncmp("cpu family", line, 10)) { char *s = line + strcspn(line,":"); int family; if (*s == ':') ++s; family = strtoul(s, NULL, 0); if (family != 15) bad++; } } if (bad) { printf("not a opteron cpu\n"); exit(1); } free(line); fclose(f); } void usage(void) { fprintf(stderr, "usage: numamon [args] [delay]\n"); fprintf(stderr, " -f forcibly overwrite counters\n"); fprintf(stderr, " -i count IO (default memory)\n"); fprintf(stderr, " -a print absolute counter values (with delay)\n"); fprintf(stderr, " -s setup counters and exit\n"); fprintf(stderr, " -c clear counters and exit\n"); fprintf(stderr, " -m Print memory traffic (default)\n"); fprintf(stderr, " -C cpu{,cpu} only print for cpus\n"); fprintf(stderr, " -v Be verbose\n"); exit(1); } int main(int ac, char **av) { int opt; checkk8(); while ((opt = getopt(ac,av,"ifscmaC:v")) != -1) { switch (opt) { case 'f': force = 1; break; case 'c': setup(1); exit(0); case 's': setup(0); exit(0); case 'm': masks = mem; break; case 'i': masks = io; break; case 'a': absolute = 1; break; case 'C': cfilter = optarg; break; case 'v': verbose = 1; break; default: usage(); } } if (av[optind]) { char *end; delay = strtoul(av[optind], &end, 10); if (*end) usage(); if (av[optind+1]) usage(); } setup(0); dumpall(); return 0; } ./numactl-2.0.9~rc5/numactl.c0000755000175000017500000003477112213661422014435 0ustar ianwianw/* Copyright (C) 2003,2004,2005 Andi Kleen, SuSE Labs. Command line NUMA policy control. numactl 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; version 2. numactl 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. See the GNU General Public License for more details. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "numa.h" #include "numaif.h" #include "numaint.h" #include "util.h" #include "shm.h" #define CPUSET 0 #define ALL 1 int exitcode; struct option opts[] = { {"all", 0, 0, 'a'}, {"interleave", 1, 0, 'i' }, {"preferred", 1, 0, 'p' }, {"cpubind", 1, 0, 'c' }, {"cpunodebind", 1, 0, 'N' }, {"physcpubind", 1, 0, 'C' }, {"membind", 1, 0, 'm'}, {"show", 0, 0, 's' }, {"localalloc", 0,0, 'l'}, {"hardware", 0,0,'H' }, {"shm", 1, 0, 'S'}, {"file", 1, 0, 'f'}, {"offset", 1, 0, 'o'}, {"length", 1, 0, 'L'}, {"strict", 0, 0, 't'}, {"shmmode", 1, 0, 'M'}, {"dump", 0, 0, 'd'}, {"dump-nodes", 0, 0, 'D'}, {"shmid", 1, 0, 'I'}, {"huge", 0, 0, 'u'}, {"touch", 0, 0, 'T'}, {"verify", 0, 0, 'V'}, /* undocumented - for debugging */ { 0 } }; void usage(void) { fprintf(stderr, "usage: numactl [--all | -a] [--interleave= | -i ] [--preferred= | -p ]\n" " [--physcpubind= | -C ] [--cpunodebind= | -N ]\n" " [--membind= | -m ] [--localalloc | -l] command args ...\n" " numactl [--show | -s]\n" " numactl [--hardware | -H]\n" " numactl [--length | -l ] [--offset | -o ] [--shmmode | -M ]\n" " [--strict | -t]\n" " [--shmid | -I ] --shm | -S \n" " [--shmid | -I ] --file | -f \n" " [--huge | -u] [--touch | -T] \n" " memory policy | --dump | -d | --dump-nodes | -D\n" "\n" "memory policy is --interleave | -i, --preferred | -p, --membind | -m, --localalloc | -l\n" " is a comma delimited list of node numbers or A-B ranges or all.\n" "Instead of a number a node can also be:\n" " netdev:DEV the node connected to network device DEV\n" " file:PATH the node the block device of path is connected to\n" " ip:HOST the node of the network device host routes through\n" " block:PATH the node of block device path\n" " pci:[seg:]bus:dev[:func] The node of a PCI device\n" " is a comma delimited list of cpu numbers or A-B ranges or all\n" "all ranges can be inverted with !\n" "all numbers and ranges can be made cpuset-relative with +\n" "the old --cpubind argument is deprecated.\n" "use --cpunodebind or --physcpubind instead\n" " can have g (GB), m (MB) or k (KB) suffixes\n"); exit(1); } void usage_msg(char *msg, ...) { va_list ap; va_start(ap,msg); fprintf(stderr, "numactl: "); vfprintf(stderr, msg, ap); putchar('\n'); usage(); } void show_physcpubind(void) { int ncpus = numa_num_configured_cpus(); for (;;) { struct bitmask *cpubuf; cpubuf = numa_bitmask_alloc(ncpus); if (numa_sched_getaffinity(0, cpubuf) < 0) { if (errno == EINVAL && ncpus < 1024*1024) { ncpus *= 2; continue; } err("sched_get_affinity"); } printcpumask("physcpubind", cpubuf); break; } } void show(void) { unsigned long prefnode; struct bitmask *membind, *interleave, *cpubind; unsigned long cur; int policy; int numa_num_nodes = numa_num_possible_nodes(); if (numa_available() < 0) { show_physcpubind(); printf("No NUMA support available on this system.\n"); exit(1); } cpubind = numa_get_run_node_mask(); prefnode = numa_preferred(); interleave = numa_get_interleave_mask(); membind = numa_get_membind(); cur = numa_get_interleave_node(); policy = 0; if (get_mempolicy(&policy, NULL, 0, 0, 0) < 0) perror("get_mempolicy"); printf("policy: %s\n", policy_name(policy)); printf("preferred node: "); switch (policy) { case MPOL_PREFERRED: if (prefnode != -1) { printf("%ld\n", prefnode); break; } /*FALL THROUGH*/ case MPOL_DEFAULT: printf("current\n"); break; case MPOL_INTERLEAVE: printf("%ld (interleave next)\n",cur); break; case MPOL_BIND: printf("%d\n", find_first_bit(&membind, numa_num_nodes)); break; } if (policy == MPOL_INTERLEAVE) { printmask("interleavemask", interleave); printf("interleavenode: %ld\n", cur); } show_physcpubind(); printmask("cpubind", cpubind); // for compatibility printmask("nodebind", cpubind); printmask("membind", membind); } char *fmt_mem(unsigned long long mem, char *buf) { if (mem == -1L) sprintf(buf, ""); else sprintf(buf, "%Lu MB", mem >> 20); return buf; } static void print_distances(int maxnode) { int i,k; int fst = 0; for (i = 0; i <= maxnode; i++) if (numa_bitmask_isbitset(numa_nodes_ptr, i)) { fst = i; break; } if (numa_distance(maxnode,fst) == 0) { printf("No distance information available.\n"); return; } printf("node distances:\n"); printf("node "); for (i = 0; i <= maxnode; i++) if (numa_bitmask_isbitset(numa_nodes_ptr, i)) printf("% 3d ", i); printf("\n"); for (i = 0; i <= maxnode; i++) { if (!numa_bitmask_isbitset(numa_nodes_ptr, i)) continue; printf("% 3d: ", i); for (k = 0; k <= maxnode; k++) if (numa_bitmask_isbitset(numa_nodes_ptr, i) && numa_bitmask_isbitset(numa_nodes_ptr, k)) printf("% 3d ", numa_distance(i,k)); printf("\n"); } } void print_node_cpus(int node) { int i, err; struct bitmask *cpus; cpus = numa_allocate_cpumask(); err = numa_node_to_cpus(node, cpus); if (err >= 0) { for (i = 0; i < cpus->size; i++) if (numa_bitmask_isbitset(cpus, i)) printf(" %d", i); } putchar('\n'); } void hardware(void) { int i; int numnodes=0; int prevnode=-1; int skip=0; int maxnode = numa_max_node(); for (i=0; i<=maxnode; i++) if (numa_bitmask_isbitset(numa_nodes_ptr, i)) numnodes++; printf("available: %d nodes (", numnodes); for (i=0; i<=maxnode; i++) { if (numa_bitmask_isbitset(numa_nodes_ptr, i)) { if (prevnode == -1) { printf("%d", i); prevnode=i; continue; } if (i > prevnode + 1) { if (skip) { printf("%d", prevnode); skip=0; } printf(",%d", i); prevnode=i; continue; } if (i == prevnode + 1) { if (!skip) { printf("-"); skip=1; } prevnode=i; } if ((i == maxnode) && skip) printf("%d", prevnode); } } printf(")\n"); for (i = 0; i <= maxnode; i++) { char buf[64]; long long fr; unsigned long long sz = numa_node_size64(i, &fr); if (!numa_bitmask_isbitset(numa_nodes_ptr, i)) continue; printf("node %d cpus:", i); print_node_cpus(i); printf("node %d size: %s\n", i, fmt_mem(sz, buf)); printf("node %d free: %s\n", i, fmt_mem(fr, buf)); } print_distances(maxnode); } void checkerror(char *s) { if (errno) { perror(s); exit(1); } } void checknuma(void) { static int numa = -1; if (numa < 0) { if (numa_available() < 0) complain("This system does not support NUMA policy"); } numa = 0; } int set_policy = -1; void setpolicy(int pol) { if (set_policy != -1) usage_msg("Conflicting policies"); set_policy = pol; } void nopolicy(void) { if (set_policy >= 0) usage_msg("specify policy after --shm/--file"); } int did_cpubind = 0; int did_strict = 0; int do_shm = 0; int do_dump = 0; int shmattached = 0; int did_node_cpu_parse = 0; int parse_all = 0; char *shmoption; void check_cpubind(int flag) { if (flag) usage_msg("cannot do --cpubind on shared memory\n"); } void noshm(char *opt) { if (shmattached) usage_msg("%s must be before shared memory specification", opt); shmoption = opt; } void dontshm(char *opt) { if (shmoption) usage_msg("%s shm option is not allowed before %s", shmoption, opt); } void needshm(char *opt) { if (!shmattached) usage_msg("%s must be after shared memory specification", opt); } void check_all_parse(int flag) { if (did_node_cpu_parse) usage_msg("--all/-a option must be before all cpu/node specifications"); } void get_short_opts(struct option *o, char *s) { *s++ = '+'; while (o->name) { if (isprint(o->val)) { *s++ = o->val; if (o->has_arg) *s++ = ':'; } o++; } *s = '\0'; } void check_shmbeyond(char *msg) { if (shmoffset >= shmlen) { fprintf(stderr, "numactl: region offset %#llx beyond its length %#llx at %s\n", shmoffset, shmlen, msg); exit(1); } } static struct bitmask *numactl_parse_nodestring(char *s, int flag) { static char *last; if (s[0] == 's' && !strcmp(s, "same")) { if (!last) usage_msg("same needs previous node specification"); s = last; } else { last = s; } if (flag == ALL) return numa_parse_nodestring_all(s); else return numa_parse_nodestring(s); } int main(int ac, char **av) { int c, i, nnodes=0; long node=-1; char *end; char shortopts[array_len(opts)*2 + 1]; struct bitmask *mask = NULL; get_short_opts(opts,shortopts); while ((c = getopt_long(ac, av, shortopts, opts, NULL)) != -1) { switch (c) { case 's': /* --show */ show(); exit(0); case 'H': /* --hardware */ nopolicy(); hardware(); exit(0); case 'i': /* --interleave */ checknuma(); if (parse_all) mask = numactl_parse_nodestring(optarg, ALL); else mask = numactl_parse_nodestring(optarg, CPUSET); if (!mask) { printf ("<%s> is invalid\n", optarg); usage(); } errno = 0; did_node_cpu_parse = 1; setpolicy(MPOL_INTERLEAVE); if (shmfd >= 0) numa_interleave_memory(shmptr, shmlen, mask); else numa_set_interleave_mask(mask); checkerror("setting interleave mask"); break; case 'N': /* --cpunodebind */ case 'c': /* --cpubind */ dontshm("-c/--cpubind/--cpunodebind"); checknuma(); if (parse_all) mask = numactl_parse_nodestring(optarg, ALL); else mask = numactl_parse_nodestring(optarg, CPUSET); if (!mask) { printf ("<%s> is invalid\n", optarg); usage(); } errno = 0; check_cpubind(do_shm); did_cpubind = 1; did_node_cpu_parse = 1; numa_run_on_node_mask_all(mask); checkerror("sched_setaffinity"); break; case 'C': /* --physcpubind */ { struct bitmask *cpubuf; dontshm("-C/--physcpubind"); if (parse_all) cpubuf = numa_parse_cpustring_all(optarg); else cpubuf = numa_parse_cpustring(optarg); if (!cpubuf) { printf ("<%s> is invalid\n", optarg); usage(); } errno = 0; check_cpubind(do_shm); did_cpubind = 1; did_node_cpu_parse = 1; numa_sched_setaffinity(0, cpubuf); checkerror("sched_setaffinity"); free(cpubuf); break; } case 'm': /* --membind */ checknuma(); setpolicy(MPOL_BIND); if (parse_all) mask = numactl_parse_nodestring(optarg, ALL); else mask = numactl_parse_nodestring(optarg, CPUSET); if (!mask) { printf ("<%s> is invalid\n", optarg); usage(); } errno = 0; did_node_cpu_parse = 1; numa_set_bind_policy(1); if (shmfd >= 0) { numa_tonodemask_memory(shmptr, shmlen, mask); } else { numa_set_membind(mask); } numa_set_bind_policy(0); checkerror("setting membind"); break; case 'p': /* --preferred */ checknuma(); setpolicy(MPOL_PREFERRED); if (parse_all) mask = numactl_parse_nodestring(optarg, ALL); else mask = numactl_parse_nodestring(optarg, CPUSET); if (!mask) { printf ("<%s> is invalid\n", optarg); usage(); } for (i=0; isize; i++) { if (numa_bitmask_isbitset(mask, i)) { node = i; nnodes++; } } if (nnodes != 1) usage(); numa_bitmask_free(mask); errno = 0; did_node_cpu_parse = 1; numa_set_bind_policy(0); if (shmfd >= 0) numa_tonode_memory(shmptr, shmlen, node); else numa_set_preferred(node); checkerror("setting preferred node"); break; case 'l': /* --local */ checknuma(); setpolicy(MPOL_DEFAULT); errno = 0; if (shmfd >= 0) numa_setlocal_memory(shmptr, shmlen); else numa_set_localalloc(); checkerror("local allocation"); break; case 'S': /* --shm */ check_cpubind(did_cpubind); nopolicy(); attach_sysvshm(optarg, "--shm"); shmattached = 1; break; case 'f': /* --file */ check_cpubind(did_cpubind); nopolicy(); attach_shared(optarg, "--file"); shmattached = 1; break; case 'L': /* --length */ noshm("--length"); shmlen = memsize(optarg); break; case 'M': /* --shmmode */ noshm("--shmmode"); shmmode = strtoul(optarg, &end, 8); if (end == optarg || *end) usage(); break; case 'd': /* --dump */ if (shmfd < 0) complain( "Cannot do --dump without shared memory.\n"); dump_shm(); do_dump = 1; break; case 'D': /* --dump-nodes */ if (shmfd < 0) complain( "Cannot do --dump-nodes without shared memory.\n"); dump_shm_nodes(); do_dump = 1; break; case 't': /* --strict */ did_strict = 1; numa_set_strict(1); break; case 'I': /* --shmid */ shmid = strtoul(optarg, &end, 0); if (end == optarg || *end) usage(); break; case 'u': /* --huge */ noshm("--huge"); shmflags |= SHM_HUGETLB; break; case 'o': /* --offset */ noshm("--offset"); shmoffset = memsize(optarg); break; case 'T': /* --touch */ needshm("--touch"); check_shmbeyond("--touch"); numa_police_memory(shmptr, shmlen); break; case 'V': /* --verify */ needshm("--verify"); if (set_policy < 0) complain("Need a policy first to verify"); check_shmbeyond("--verify"); numa_police_memory(shmptr, shmlen); if (!mask) complain("Need a mask to verify"); else verify_shm(set_policy, mask); break; case 'a': /* --all */ check_all_parse(did_node_cpu_parse); parse_all = 1; break; default: usage(); } } av += optind; ac -= optind; if (shmfd >= 0) { if (*av) usage(); exit(exitcode); } if (did_strict) fprintf(stderr, "numactl: warning. Strict flag for process ignored.\n"); if (do_dump) usage_msg("cannot do --dump|--dump-shm for process"); if (shmoption) usage_msg("shm related option %s for process", shmoption); if (*av == NULL) usage(); execvp(*av, av); complain("execution of `%s': %s\n", av[0], strerror(errno)); return 0; /* not reached */ } ./numactl-2.0.9~rc5/shm.h0000644000175000017500000000064312213661423013553 0ustar ianwianw extern int shmfd; extern long shmid; extern char *shmptr; extern unsigned long long shmlen; extern mode_t shmmode; extern unsigned long long shmoffset; extern int shmflags; extern void dump_shm(void); extern void dump_shm_nodes(void); extern void attach_shared(char *, char *); extern void attach_sysvshm(char *, char *); extern void verify_shm(int policy, struct bitmask *); /* in numactl.c */ extern int exitcode; ./numactl-2.0.9~rc5/patches.tar.gz0000644000175000017500000071654412213661423015407 0ustar ianwianwb/Rpatches.tar\{w۶Ͽ@M"YzٱnGi|-XvѡHH-IV~g_l6i,f03paD朇ۏ>ՂkÿmߡNzziG-=bɔad=27w W%B꿵jMg|0f{(OA68 z‘v̹͸Ugn }/ԪuewoT5س^V1/rس`#~]Y tkOmxx1y4v\vg7|{<ܵgZy2;sǞ2QǪǶ<_-ΘBPcz֛kgIo63w;^Mp7 <چ(^w嚓Nn#ݎS$W)Hn_Gv{fWC vH\^vތʏ?<[F{rp>_\ nx+U<$3snG܌EpQw2 HN]o? 2㫀ga \ qjp )uE ׸|4]a}RD/h LEuVmmmIJ.l!1(߰GKy3uYXj@BcڒqA"٭Qi57h`u0{Id܇uL(9qmWxmSa$(߁XwI6%xP,pƆca%3s*5*Nl0mXxՊϹj%DW9W#b SQ's1[* Nxxr0b5Sdύ_[AI㉙peeĘ`}og%7ː` t N8Mk*g݈sn1]rW7FrWrcp k6Zeq21b#z|Ka;՗l*o)xWX>ͅ ogzsN)֞xF XJ „2ҥETԼ{u$^a^VHk}P\DGOǯ~\̧mZސ\nla2Η3ΰd8%;::r0Bxً|p1>9PޭQ:dmk0) A4VS ZM+6FcF;fv~6LrWɛDʮaX_UY곑 vY&3Ek-N>135BhWtfxm`O^` A*;=KWm#49֓6E ](ࢣE!.!ąZ!\#<0c ρ}lR7$(զ`>=ma8?x|&?"6KCo5::+򖵛[Vs]< )fӁVKAo&l2 (!reszxw/`LgiqMpYȑѕ"~ҰXJ·1tn-eGgC" TL^ZM<"}p*'˹2af\OHa$GMK O[툋!XʩDtlAsj ҂$c#)vb.$4f% iyw r`ℙ.,)4x$ܦp@X:e4 ap#4țbǃ&;i҄4ARJ1IIArŞEyA]F+a*xRV=+6a{SޤIq%KJi %$OBdjBEzd+eJ;rd%'`HX1!o31Gݬht$Nf j9S ;BNNlOToFRod X$Md$YJ<1 #7Bv8Ozo0Y2-]((G#X:,\zy=P8gE/g-Yɦ/E4jСDók0 ]#DbZ,Ə 2AFC魔voh1\-'ZNNƧƸZ~πFZJ$h-!: &'kC=r$Ƈ''2M $bhb:y !Fu"$*VU+~' }gw"[:$ju2OYdI,rc gdt Շ!LfOqfY9 I =LHB5h$nC(=^~;p۶Z\zjڈ Spz>qpZ!Ú1&Xp^vx2:+LҲ^.s5gbR\t6@J*9t309Uw%S$f`@hT* Qh;VT"ݗ<{v~1xvkxW?k a^IhܽKo靝^K\k;T#V4KQS 뮖[dpCo>Þ+rujhmpebM~j+6Fv:y_N5Ҡզ[ZmKTB]}?B\n&z}*T t^CZK0lLcOfS!-.ݳrN=jjAi%a%&^ғb$rv_*ˋGܡn`|H+DRz7 :>,0II ؅KU^V0!{ue0ۑD "O/Vʇ Sw颕NAlU5xeT@.сbV@ YPv(Zh.#a1)]Vg'VΞ&T'< k=:TAj.ÕJ,0s!KOjcEZ+-|}Jz ?cH:ͩ{# Y_UV ñjB=e jĩJѢ*6g6d2:$8H=>Dm?qpCb`KO "oԯVḶ̦!~XOUHvp$BFBqCowR’r~ 3>0y̏Vt]1 3oᖔL~'=yDf|.=x a^є2SHrr]DohOQGČFO'ќx[_{5wfOރ!ڗWK\_v& ÷BЎYx!"|h&}Va$6i?6X vzyXPaPTyTy'14q|RX+D| DٺN=Αo W%H_A+stUMAդqae}!iڪӧqT^.m2E[_zO1&* /dMc{m"P\z^{4RKize=Yᑹ%\!]^]ޣideQ5N״/=؇iwA?ucĬ\uaw,w DͽC\p%Bi7_nokw@aG'rT'nq+ĽS:e|$.vZBkG<ރZ0w!Snw㧤q7z/vWK\)-S7F`qU &(oZJ^d,"քt%ur2lj#)s;rdo]kj181m'ֶLR3$") SkAE7bbYN0)|VG݅$T ( -~٭Y}_;=d-dqP7|Jc7Uy_anv=jY#]. (焯^`owx+xSRX6\: t7z(FRkLz:.Je+x{L 6 n(C~|,~ɓsⴘL0RZOyJlO|#όIETkBhk8RpoJ>{ra 5C{qrٵc!{gCg ŵbNR(SZDD$2,A;8`[C+[<9~S^ ӧ"zCCˊ˃ P 2dOr1LV^Ã"#WWc1iC/t%h2juUw YْzlDW0}4kXP7(H|oZ3)PϺ31^Q"Od oigKʇ1Gw۪dw7a8|qx%^E%.9e։nQNNp ōu^SNJ}NKuuuuuշ֝" ̓18 iޛ]^0J\fFT7yc}fcO{<%? 1lJb? 7m ,DBT:AJrASDŽ?MZYMBxYᑨ$gH7z+MCXZi;SQ Av6"_@.mUzE6HaXA3}ܘp?ccר!\v#U~lKgd+<` q`R_mõIh2{; '48SָnP0҃-FmtB$/VglCNUZ5dݡƸ_v۳B2*$}?JE22 / ֽc_HYo#}'"7۷4QՀ5]JWQEn ƊI_J$2It&5TLE2w`Lf( 2jeR@r}jy?C`O&AZ-'쳳\ΪUi4<_ 4'6`*>ق(ǷN{?JDZ#*Ы3x~UWaժS-0#pd%ɗ^[=q6  b)vCR'!t8.SG69UCփ-ԜKጅ-ztP8vϭX3VY6<\nX_6XJ慎4Y>7ya_W gސp "AU䒚o3kG&je@z9ϵB&hcMw1pjR;-ԔcшڧL/ek%+gFrhQw@8XyB)'5-R^24wTű32G[" (321aʩd܀P.;cgK1$hwx(xn݃I6 %ѫ7Tkq1cڜY7o.\$YPOnI?kr4 ݸ?^=y3iC7kŷ(-J[ybUu@M(SD ۳qNzH^ݐ@jD7ތ1l?>y39ۡE{= %' ߯&s`Kʺ Y nb' ]`0 0lmݲn:9m݋^6Dj j&[FFl[\|g|D'+Xvݚ>AKZkxAl -<oE#X;Bp ȹN,$3)axgPTET weV$^a0\\Vd~yң Mu0<4ٚD 3:\'SKS/G=P :v;2~. 9(xek(uPiLg;ecUw0%kER'V:bxT'*cqmH9ґoT閶}n;r;&w}}80VїqNq*k |g;~Oc12t Gp"%VG_/)S}Eog2LW]%MQ[Vd=\Edە8RvH>d9 2jcT=u41KX(4l#7R4w_L{C=d6Q$N{czu;ݹު7- 颲xkB퀄>%*{#\~;rDsOA=dF!s /_K݇9zdh:8;BKcMSᑓCBq4dIDCpo]!X5LvXNJ 'a) s4'E=Y NqUng S_5!%8멲 ݫ`&ͅ+&^-ⲭB7ӺfhC40k@u_Z28 tO% oա7iP8/(C²tMʃBwzz! 6{O]WP+-+ۚRc=RT.YSQe %R⻒JB(i|!#|d4!ԏ|ݻ)k9+) ¥ŵ"DtTQyRL8Ųݴ {6@V`p$|j9!T -.["SӟUINۘPrH?GI((vQQ rr\L3Q_ui ?<{ڶ+F79h@G7f_-9LkF_7I]zEyGyA4xo.wӃk{t3iY-ʊSϜAByFeWHvni o|%`4L_m kn@E+^ph(L] ySGby{%RlA(U`qbUwtҤrb&8Moe)9KP +* IvC xYV;Ū5! <37X.v*#E0!7%P6|Ѿ?|' 7\Y* ϼA1ioKhD٭.Ujj;U3FO>9.ͣ> E Xs6?vϫr!ek"j1oěQI(FEzJ cb>Q?Gء7| F]?k[mBO|\=`]: rԶXV vld`& Fƣo*7ufN0 1k+bK1Zc,}Oղa+i@ZI8Ew.+Aa.zGI.ȯ $]tTSr+56}?D|ˎ#k[}ಚ)!'ٱT1L*dF8z4cxp&dm %MЭ)5U).%|y`xB9-_`Uy~kw,ک׶3LV k: uejfUغ@NNS&_MJjzU^tSMlvOWHy"O9plw?o+ tGR|J]vg/SSףGPe^>{W.*gu}D4ÁQG]9fo7P@*E'"̑m3GrQe/'OpJ<@_fů?<}[TEiS5ɓ|x_s鯔+>W3߭{ tMK[^+l;WK\Ef(GܡAaz1Ko*RXy[OᷯtX}[Wh!>/'3|*(P6:ʽ86h0yxݳOELUPltAAwۭu"oͤGfz:9xVUߦSv'܃--G3=ؼ}F1t46cYDX:QR yX/-^ke\U+Fb3q@B_Q g{3rD0?{)N z^8+:C: 聁s8 Diŝ>,[Ǘx6x@oUƊA[PZ+OQ쿋y續!#p*vym=[A3Oӳ-PgXFc BcRO2$%_#vkhQtdZUO~R&֌}1Amdtd9b"?ru,MطЉYs?:U/grN!{RZP8q}=&5F(tPi<u>ߴ:H4ލz_|2O~Y_kƦQ{sitD ʈ7? ?v 6CN2W$ H8Ӯ z/b- RX xpNGG#aޓ1 6[L]]@$d..]i& z5nq՘ im@0UL36fҬ˦[>w= g#֒\׶z8Լjd ,L@|%i;]#fk8iunv%4 70MKAsQ u,"Cɋ.NN!l|`5r^fAcUbǀB{vطޑ"V_&;5:0ʑ1Il5M@ڄ|&PU^sa u>Aߘ ߗYY188`(+ǿzr]A!ŸPfMc6s5īax]~HOG@՛]"t+OEh?7góBms3@|ewzk'@/1zdoJdP=zt8zV8;ZL,0`c^>!U뢺6Ōe;E1~JeK~C*_KH>.[icuURYet׉tXocsؓ'hҨ)n! qFz!l _+Kcߣ X*8&߳/U`*R"Ny" JKb/Cyk\^%׸R"-!-/*=L4̟ȥ&w)*|g(nph@趒 v< &t(U)UHݪV}ċ}"E/f:h]^=>7WOroůxᆷJUe#p~+i#a'iw̞'?:\nJy,OTK6Wbl+f3B41Z%ɸLU/j Pe.P%߅lM~NUz6괕z#-8U99Q(KxCI. WWQJW]שF xuNjV}|ߞ&< !9AaS9 4ryB>iSԍ9oA춒ï4Mcd7Na;gY'h,M1\F46nxb h\{B&Rn2: :>Os4" l> ._2 Žۊh` ś[ST[ڿfYHl4$mg í]3XڏI.Pl '"hjqƢc)BMK7'yN8f3V^6#8Kq-Z^Ư*ݼWx+bϕcﰳqҁj%vY3qSL7 zbwG\bj| f" Rc$}og}}DIiDkǯN8|%/wA?qMIE.r+ ۚWA鍩=R2 ݀7ĵ~#0IYq7%<5X FYNVAv C0h!3aMAPT-(^T#0$q'V1V* qrWje/I|;o79fsvBIc L\hM6~gAyp 3?GwW,+̷c q&BE41&($Ӭ_O:4B&v4LR .‡Z6E%zQ; ϲrJ5>Ps2^.8\KOOnP%H7 I"ÿ!*鈜 hk 98Pn'9xJ jֆӫ Cd?z%j)RL'Űǁ{3jg1Mhj*AY6%u$ }_9@u«K$wg6RdLP/,*]u޲; /Y/YNj̬abMw Ǜ,xkM r-=Vx[|ZYe%utJGX`IeEENcdD+NP\CڤvAHu,EsVUuK+cAmêdLxdz27<߼#x@޽0teSw.->_IqYs]"{LZ5/H/L__<@2v@6(K:SJ!xKzO cx/N^>A-|6ySiP04sDLA} Gw }J;n08 hhyvIiTТjq'H H9Eۤ"^pId_<{LR8MA0xS< Ҋ5Qul \#˥ɡT >>⛢⡋vlD130m1P g pXt/*\BpDlhi0CL}뽫^OR^f4b[ߖnp s(џەݎJGK6L?:XڔwkCZ`pk|r@\{}bכTt;z6wj l*1!]V + U\ce3ҘK-l̄=ixM v@q^ɱyЍtßCRJT|`WH^ "zr9AJ}IMz<:Jie)(}ؗL nsdۣ{yu_PZcoy4W?ALkqpTZ曧~d6RXs:H X'> b8#ձ榉Gk#Ol;?Bj<22 9|rI0y<{Q&J7ՅD'D|/!&!h?;6~Mp;<%sȗ+M#fĮgš{#a\ }; ߯ l%P:_Yk=\iPsE2?s5ڴΓrd⎽[ͤi Z`=Ҍ']aqAټO6 ]B~ zUn]d"P:T7kA(CdـO~`~ocjoXq8рjTIgōֽ]#,+rX_!LӎhڥRK7' ȃŧi2vo^R*.HmYu$"7k4#'tj=&L92nz;.J9UꦭXU/Z9Vu/x/szE]pC5"HR1ټ۵$|mx1jOtHR|lcvaj|7U^l5m#}DzV(z蛎 (ܡyݖxT@fd1ۡ@iub !ȯ3ILi(8|]6 {9eNxR' a`M$ i K"J 뇵 :d.g"D#c!D!j ;dUyy7C8{?Gӑ,Uc)Z ! pٺ k'?KSEOԼ#u? FAoi壝8YpY:L,4wzc`H|v_v'?;Zֿ8YTR.#doGm)%de*Q7@š{DH'R_Kg֌wyVO(S=Cti F4D+dd4?/TRWiPQʈYFZ_zKxHBȉTǃ}ͅB %Z;LZFqmKox遺 .=E4Z}^jG^Ξ׫oILntºb l}ƭ6ay S-F*|oL%}. #ֲ\g׫ȑaM5LX/1Zb}aHZ(?O'\ڌQ{XcBkƖ^jC%nz%abG RIC@cnᐭ} *@)w+fk[Nv#n\-{Fvn5s疎gU@1>R.m-jNBǰb]eNڪ9\uى2`sK$s 2b'x58GWs Bȥ Jv:r8d:+1ƨ اd}44 a QEt?!HM⦢ 1ްJ_Ԣ9ٌ8˖).XgjF k"rf5KsVGh.㵠̅ahгx+e|G98M0F`Ǟ.C߱CbAD +\*XC ge !#ܤNV%k8 r:{U-!,)&\ĮlJGv~!y칥 _ѡMB LqBviup#2,KhrN*E ..V_N_{V$KqQ|D#|ZdHhX- FDay>. M#@dkڄQ j4\Ié cI(jЛ4LU \_e@A zI|wTTa)@3x~SLUrlGS'U65skJi0YyCYsx&>ZLd`ӁEFcbr3<gN r!h,U[gbE*?-?M0lM8#DiJ je;|szlpOV,)~~_ z$;EΠ>h1IvzK㉗9033EifLֳbff4PTȸ~jJ7W^>?Vbsȥ C3(N_xʱSäS>,R@ϲ)Em~ɹ= kg6tDE}M<&$=wuw ւpA C.|0-ʛ}m72o3Ewع׉aR2/$LE1 }ZQqZZ{58.߃VGhll904`c.N=iy)^NA-n+nY0!SG']Qu31N%Hk#Pim_ NWfvb9*zvjV,J``-=w ٓWS1h^|R,:9GB(?.y{:!53Ғ ]W%$l8䢧!!OqH`P'T']q##1'rOu}*2K$ :©=5zq,e۝:]Zɱ#ڕvs?iޗ ;lEGHXӇsgCY+N+IǕ+Ui-rD㣛l6,'c{{Ĕ;tU&nL(V&azݖ3=5,f.?K1염7mo+ZޖL,>>y̧l{7~fö][߄=?h3.~=OOQuEf݁o9r]k~" Bci[N_F2}Cc_Gjr h2o@ C?G1EaAmtĿ7b 5K%4ح~سK2Pd3yALNv1 ~B`^$r$]هY =P#nF}$e6]@Auv֟9Nt&S}0HkWD;Uqնi(>H& ) n}GO F^#zT?$75UV? ̩Ϛ-mLUV=P[*:&iju-ەvC}s?NiQ/jEn |$[g^ߡm̷aY 6IN7 PHx#>b+)7zh+j%bB#@}ʒm7PH )Ro!z_y*Xcߖ `jYMTSFVyR lA;WemKrL rYǽ@x}vB ІcaVHA4t Ps$"`b3m&] N..~KkzNr[0d8Y[#Nt!FdqyE}(v#S0LaN,! "(/'!e\.?6NGۺ#UR^bhW=9E!+m28eEα-7ӸG'aA3V [do y-Ƭh &ݵ3fSp S7 `@/&!xec|i 2zb$dGEF9ɚay;uS^XFZVd ͺ@x8-oN\lX#deUh"9E'ta2:ݪWu0m:2mΣ 'c":h0(50 [3Lw<}j[ed 1W"P[|_x˜Pʓl+\jmFn҉ZRe>5o QC'! f2)Ku.V&w ƧlXcتW rۣ|)B\;A ih34Gq̶a~WF?'ؖr@3O#Cf)6eѰוja{Wth+FuOtczym"M>ʹLb HwbDkb4˰39Zk!2q1KG?0dVҞq~aVl UuhpgHP+6o+8t+RV?0;R__ v _<^)R;Y=؃%OgGy^KÔ4jZ%FQ1(qk,$^k}ݛԋd!7ЁIgCy?<ۤoԞst 7$|JY*HpFp@Iϸ̭JtIvzߴ""!N[o9ͽřx[zX? [V76t#l4;5DCqট{U5TPMejjR'jn\^X]왘}YYkTlp鍀F2/B̛2ojf'ֱ} tY!X5 oˆKY^!,s #po7Ԣ6e ZuI4n.M1Ю[ߚ[\7 3qQe/75J-Oا|))qoAV.߿y[;) dRp><9O 8#50>|!}땹7zbΖ^ƬoY)9uyƖ~|d;ͬsW ʼ(^|/)1NW$| tf@Bpq=g,WlD>p0YzNƔ/h W)& ,f*’,[) Z#.:p<oAMskScH K jt֛"3x/坮V@|'y[dYBlpCf-Xv,Xh2pGl.0&+ND b֦2*QP I$QC"eht |:݀V,\ ]"J꣧/㓧<{ Fo |¤+R>{ٳy\{6 C@եDnɹbK_3ǗvFntQ1P;}G}D{4Nn: OgRɯ&gӫ/MD⥹`fN?[kӟ"2pq 4{>! J{T)-}xA m&-+Xh&N *hS{|G'0P"mU5G%ZC?yx yZvw c"򛭇CZ% %E{Ӈ3݉M W7(}0ϦK.gZ 7`>#~l25/-#M%s5s2<:S~`|6+,-\ʬ=G(:n,A}; =~PAǢR:E swrp` V@I7bomzg@;om]K6ya1dCrrCl$^=vW^Y٬|{ufâx"UEd]9U-D<ÁNynTv7!Qf,5;\,MŃC8Qdbӛ$v@jՍJ`I6g[ެ:0-0l7tVJ{k@h\$Yc.@'Qz/¿2a<.A~f ⟵6Լ=D|D lFץV\s}b/*=ׂ{]mDk2։7͎6XaaF MbMVjUK6Ź+-F.7(wC|^L#^[rN919HI<1=[wv[I /lo9 ԯ3 &D)d@o#]rK"t/##MG &[}AS_Ć*epCƣQ(ɽcE8>zd(=߭7-}&E̸GI!O݊Q\Z"aY?騆OK֭|%BrZ|ؒ xRlV@|ǯ^=Q^p.kn4zV\ڹf坨wX][͝ͻ'7!2ܳ_htq ѡiLҙmDds*WuӚ^2 .0T-pX{"1nc?[1IϮtʈciLXpj~uB,+ZoЁn>V}hBneoDX5ߵ]9%^a4Wh9%dk})y5OsUVP%?7 v+Nh0-k!-/I1ƺ>4EnFR(ŅA#u5+r=16]OW1*)ż˄|;ɻGމ%1KY]' 9E :bgvOw""W|lzgp Gmo7Z6P+nzuhP$`{Ow!'O\u|s^g;}3]vт5g'AGuRMȺ˔HLb($MYB[kٽ4fw-wMt$y4| 8P[PE+g"/_x>/?FՓ׏%s/ً_O z$aX@O'Ź{jwx&8J Sߣ*1% 7M'ka'R"?w^3vbёksJ0<;sɈzN/>leB 9*X]"HM%!m?| 3,&)6r_x lG`'u>ƃH+M|YfrcNذgIRhBn8skxD!{bRέ[o}U&~; ;h!j,YLȊ 0EOLĢŝ4߽ZGdk.{Aglk)}6qC<-zú[R[ FҒk޾P FQd5bTZj{֒4Mxe,^1߸$x $܎;"-ȯV%ٴE"zͰO*Z0**E~Y]v |ɲ}Íc0%J hҥP/0;jw9c|}T-ƸW#XVJ<}ꇤgTx_ !3s1 Wt %rؑKrNGRW_!9Bopo68.QGh3.d1huF*H3hOEWlR;iX%` Z3ԋ_L0jS,dUL]!J]"%RGI,1Vg)VlG+KP 2sh 43S 0u4 q;%ӋK4[f5NSj[ʉecJi9Pm3? PZux\]%C @gAx6yO芆 !g=Nc>i"NVn Bw^0gρ[ %ljp!\-'^ r6EȯX/ F|ܕ0#07ϝ_wZL8д,|//cA1]?~k.izc,ZK9Lmm[[mmtCEWt37a|{8l:?uyxye i$ΧuH.EH+t׷~m/drlaAz{7~Oq&+C(~-Ke @Xp`ڏ p} ʍ(L[k.* Zv7/``E"M#H.+WN 9;͍izvՙ Z:@TX_s9M)s'KncX)콎?;9l6.ޕ>lӧO%HL||.lZ=x1]֧Y/BcuR?;壳v~3[޹${uZ-^oqej>ejnLn|9MQËC+R6fI.#C&G uVre)]ކn}902ߢsQJoߚT, C(T'NB#d^ɶl{maBG2Ÿ7< ^^5եYum![*GL%#HQ9EWԴs|Yzޑu`t-i!3kbd2gp;OWg了ᆤbLZ؆}No& <w{ P #i7GښUca6 ƓkjE9⌍mM˿hbv?gp;kŹ b8A^|2ñm-޷l ~T'F9}'u@N^<3nPzHO@jb o# i\fԻM?{)gAvu@[z0*-Ɉ~KQ+(ˠ9-)'ӛ3:_$ʭUC! Yaзܯę ^ś ]Sjoo ~PߖnvHBIVv0uK5wG'.k-6rM=-x}D^O&zC~(ƅ!"p{*p Ճ5p*r['#$,yR7h-*,7!JX/P;It:dnĚ`p&`$DMTE wڈ aju-ΰYp5VPQ'͑!;jԺ[Xw$O*w^kbO]"~ YD@ ,@aU0w %p wf4Jr֝"B@ A+}wCs1Ox3AAJr7+JvSa2g3}}Rq&_ُ4*M|YK zfupwOm}*dF6p6QoU߸7JڡȺQiHgǚ@3̝!Nu wOL*ED3Pwg8gΞ|NxDV)E(ʰ9u(.kC(- ź.s}-6hc͆y_{!{DLX2nʻR/nV pUF W&(1!P3Hgwa׌9= RKɜzD9CҊ. ~-"j0*-_j\cXFJXTXW|j%H«ۑĹ^նHHZݪe+\K&,X9@*eV3 ꊶQb}%| (u]^rP>[ Ws"|7 :KVajӽoaJW]3D+txf`-z:̪ Bl&uuA_-$--ҰZ"^2XIMY_%{.ewv)焣_a;Ԝ9fY7M>7LU:,.@OG9~nwWm)^52#.Wee;2F4I(W4lih/HԁjCc9,%atP;+BKJKg,|b01qbCH4?' $rv8TFe鼭la[i-Tũ,6" B 2[554,wv [uWԨj׿bY/??92o8nU;%N->Dݓږ#1$7ud9%۷e\gdXbQFUplf;^864 O,jѲ DidP nRj܅W.[%Z|­bCOo/HE^rqnG-.J\soAk4_2t*' krjj%챸JtkR*U8KWX]K4o:˗ےH(Y|+^g'yC[5]?4O1R5+wIJJZ'~n./QIyaFv=A%/r}V r.UuCZ6Q/C1k{ 5sDƵ1^fS4.ϕxĒ^^MT"jí}Ρi4Gɿ*‘;a zo>;U=ޔD4G'&){\V.'T-%bU$]"S#('GľXʘ*qwi-g8z4qxܜ]2-|6M-K0A{fvVf\иJ581wETոc.1p_'\=.덲9Y5y;g¬UxMLSEhK[bBAMG[plu aB|'`%`I1-B֡LAixwX\A=$S:V529A5rsީ0_ My)\ ØkY$deUZ{س1uywԸm8Ot>zb1vYTYV)hb VYbRsqXkvoM! ))y3})aB9%3C:=y[2C<-STf R(Ϻ n;WYW<%53L v{ $LFIQґU"VF1}"J>vY=N9ܶJnN ͮo+Tx+T e^/;}4MK܍2j咍ɳgA܈{h1F5˼4VYܸsv6âXl2n5RZHBg҅'!5BCcc9tpW*׆!kf9~3um q L՘)tM.%şOA@bVq9Aodj-g1h+Qu5&RG]vGuAYOͳm?-Ԕȩ;;P (IGYM16Jun: Svwy8Sqb΄M9a>Y jLݎ#X{7e0pa1[.6hd`9Z̓wX/6dlGX"ϐ{t**|dZŊ͗POéo S]V9lStRcs9ݾW*㬏0 zD &`{CB]_XE?1 mάY:}}:Q aہ+t 4_GI;Ǝj4^򿚸O92rՙDKN8MFv}$N i#RAfM2u% XqD_ tC.g^4:џuyUbv_;LHQh9Q@^N~8V@y]craf)3JB;-ޅۻ̻D%Pv}u*8^@q]8*ZG Pw*`PXE ,_R(`‡܇5հvD ͽi-u Z]eh#ӗ:Koosz5騡 oY9O5,4l vWqlB7TKJ/DOM5.jdvAX A:[@jC ip7JL"]b@zzmSMԼClv;S5ߊ2>E-Q:uKGVQ'/f|>CŴC i÷<#VXyARRV?Nd {>[0{Ouf۞q;4I*<YG&VyB'FJW[֗'֠ Kj4?;|tgvl u0knCpJօp!a6п0QIAͪh$cOB O,i0 ;: 7D_ ~p>$zKk:M{Ct.=κ&{Kr#`9/tTA_7k oη5>5a>RvhR\vl,˜gu.%,r!G)zO/vVXzZ<κ@" |A 7mݽug]ZZq+~}&[Ar++X#&Ywsb?Y*E2y$m吡1jP@ 8AP[L0Rs݁vމ ,a K:_垩[sm\yE"\<5TK^9_en3 V"osBԺ$K{op(9 RB1Déícg YufAJZKp*c'*xR3a?Ŷ:B$0C:zGWĞ5Vi?}ƥބHonY}Z/ tZ "Q:XZQʍx::kg2Rs:^Xȋ+QI q,j|h|>v5vR#CPD`)$wcfY2q '^w 1.>TT+QT xSb$)=~m`[S]ezo*:stP&iQ‚u/Ϙ>RD:[{ *zDs4ly⥾TnFvhVM{A2|q+!D-3۞\D-eL e8K{n%G Wt>-/]<߻Jia#wB/[.;v®^B,r![0hpNvol˒8e`Rod5%A]X8e3a5' c FbR[۔_=_ |1LKctuc( h W8؜˜|HDva&}N 44#vWBK( q5oCOEd_~fȰqeݰ2# t,X; T[X^౴+DzlpLvtvDp. Y TnnF$SK'ƧP_Z~:;}ɣLn/U~0LNm^q,gӜEa7!,ra =&6&,V&UVI$>0 c1WP5xizSWekl"Txl0#΢P98evj)>u{[#K{&7? mc RIlrhw0ewp۲8KU^6"&Z h' 0aDcIkcR+̋Ej-%$}sTQ Ϥ@l g||(n0H|2\AF -UymSWj]i@<r:a:n`5ܦ=wL&jWg4!35Z@) =fE S-#`,fcs6T"|Q_M~' uWol{)^oPAKoھ}!t`?9?뺴{:)o<]ovi&l^bRKF+@گvs.f}}EyaO0)+&^ss1&4 NAY6}?SVփ}0`AeTq2vwe=/S Km/ W9TН憞fˣQUyY"B:tԄl;e9 $lNrHXY1:Rq©y_hG7˶f"YeLu1ɶ` Pl}iRߛ]%x;^Ug{ud0:MNrVp ZLg" MR~A-7t+R{JUd2C^PP\5|N{Q){|'kSde?gwom?'CW5;?tT8[t3ake$]oj,g1te;㟌#%vFk*?_hoOf٥n6vGSģٸ?\`> ҋduM39=7 `.2Xճ{g{ݳ^YT$ txPVR!P\.PRW 9x6m⍣)[cai|2~㷺SbmPGPJhKO~,li zWW!Ȕ/;{GPL[hRӏ)2z^08lT oh, |jC}sPBEƷ\Wx'P!%zV׫; MV)˜Xnӌ]FsXfLb4kNsA卹%ze<ŵfy׫rYxoSO E<ԟCc@($YnWHHJsۇX?)LOWZ)5MC"-9 \\hkB{uW- qE7YkS&H\W̏ع%]c ֋\D9G|K;Gm@tScVO.$!1U0%[; q.~'{(i5^)#>nH]l8SIiG"5EGqj)1LHݪѿq͚ڧ0^”37xx(A.&K </*J]ٵ= [Q5-!K{A7D+JPz %b$T3ѦXS'³);mrid_,mј. 8jϻe8H[S(TqDKw<QX;z@sރ D^xW~/5`:vݣ+[ຸ%*V ?wgK[!szp?|{m<\ "t+ ^x'k2v& yk(~}6R^Z uU / f@G,ft>Bh>eM4( ~ISQ`IP5^׺-uN+=Z{dCh_(\d.X ū%Y2Z#v@:C̩V)ڿ r2o GZr?#CunuעF$X d!)h[|(Ʌe.7wI0^i: ] Jl%t0W@4߾D+VUe<|Y%O$ܲ"av G_/>׷gp޷j?ZY=;:VVgwkcruzDbOsw0$ f[s6SdWx&'F װ e-yFeC ӳ<ϣTLR2hhF 'wMY]z/ǚ6[y;!*I_I{#yD2E[mxGMU6|:2M8N6vmO@w?=9hF>C"՘x?N-hgqF3 C[YoGk`sdsE+w;=(Va"W4&]]NoFa=Ui֛݈:Lpx'1Y{XIR^\~ʩl1'vrr8mo's5>2Šc v+y0Td ON{#u]Cm.dۆڰBwTS <| rD@ ;s諾Pϵ|ߥ3UQ c7%@Uol$2,v6 `3 ٹtl!;%g!1N.S 4;p7U:')\kXtxr=ɲMN"Z{ .]}A *=0Mg!?ƐB9ԝwv6aKo:ggc{|_x"U/9tX[|Y}v]rox>ds/@ 5aTc}FwK=s<nq\5ϵfPxϑb#1Cq\E2/y_釯̺_Lv`g)U!c| M%ZloV:U**Vڊ}kmWhSCHG{?jm%%҂.; w$lu;EfſXpwn-q= 9Z#?؋PJ;ްtɛk.cZ[Oa1yR>bS{.q剾(|rMҿ/[QK/ח8JZmUj+r12Ulzu{mG񱾚R>%HDImfIAp SR8V =H`:Y+HԖ]W.k%p#+Aj,COjr seYY!hADm9{C}QTʹ`X;<6=lE,á+rB2/iNGeVb"6jtUߪsEakN 0ӯ̶yɿ!U/?Sn=Ch W9KG_֢J;Ə 9krxh EIuQ spxi 2#눐ė}1iPsR77!=U6vsmN5Qmd5N}ϪCT `A¼)q Ndo.ܵU$[癑|b*xYSQ]^}_KS-h`jҸ+Yo4◛9Wa|"ޥzo)ڋe-|qJ K&x =!z,ox?Eo1'y'Br"/HR6M:h%sFCԪBX(_m9&Ѿ+V~ s>lUݸ*)vEݐ|Wvc)6~PѶvcvVh۫<`D~YiKX9Ol(k6nI ̈́%f/Ռ\$F10zЛ=ǴzQQs/5emZ )3Gnˢ+@r64DZ 2D[丸{Q $"G}}hp=>"ExY$4<!( A*`r6|LN4ar6 G&ش*i)5 3gn ZlX OYq!p "yjh N{!qR$N;ҚH!1CΆѭ5NK(YFN>T8$.ٻ]l%jƆ0)G.͆w7ׁ\fAРR$%|l9סK ח 3vm taW i\!!&=(B|T7S~ 퇿^'?'?d]C2apȍֳ$9IZĶG1*Ptը%J9˙FPT?~'jg*I|ab[H.RP|Sh -q/ꡈAA3^j'Oi Z4)L6g5~ո|74& 6sd"LC74v,&}j,%0F8P{g4?+ŗm-4o溡\K-HwThl nێ !>4@ ++߅_z׏Oxg/~=mh֔9rsVL6Ud-&wgaXH)zz%|9%e=ou'pƼi[{aص|d^ VldފX3c'Sg'T{فGcO^ .d!KyG4WiJ+'1r5(0@ꭊfM|@Zl)DM1-VұzUi:81ҵ^n6kƬI}[ϧG;; *R!W´p bk1=67Ī6o[UKd^/fX\ |un|kw#Hئ~L{ (kH^A]`JS/6\^'!ъR0+"-tu:cQ6-Up<[!A :cVtxoPNx̓vxP$k?H- Vl/YZU͍݈W1z18RT#m ĽpYp¯igU`^tFDTYXWιl(oʎՎUR{=շ.j5&'gzSr>uU\:u~m $J D:k6AaX+YPUZ{+d`b_,[DM!wh_6s<,}䖈s SFvbuWUEbfbֆgw}.eY;BJA/}ћ[L./K[GK\C2[LD\K HqaSU B /Cnbw4eArJ(d(laE^m F ɈkvB ?4.3N%:n%kRq$*NH̵¥)ظ?n5t_Re'ERO;/pTn}`%D(FS1R86lkPJɓפ]ERO5gx4{zcF~btc`0C9𮣳 ]Q :l`?[-V'%1,-*Z\.-_@L]]8Lf7/z>ѧ87~,=O؇E; lOOO~>N瘁0b7+[ע.M6B} sRbjEUغL;#- i6=< /GuDHmL./Qvt p.S5g:kĝ=tc]9pT7uwE&m_wPإD> Řɀ2{*wi3o%PD Г%hW]H qx Z*߿xB6;g?Ӫ[(ġTDgzD59UaN*x|( [ :AZFBywМHHW} qQW{P1$Aj\zꢇE' 3aE(9SDw\V@mȇ}5ztHA8PJG]Б} H9e++b|a/N܋S!@9.I{{u+qͥh6Rr|f=xg3=7cuv[ԛ/UWjrj,A!^M^K+5XZS!g%މ7QIg߄,u"P+}OIGML߸'.̃Mu1]f)$IA }mpJ{ktEQkUF'䂎颕:m'z>L=LjK`(jfgKPR~QE;wwwrJrZP3,ݽ&i&Zd=1sUT_E=-_H% C^ n0y)I1ar!'@cN LvqK{+uݛqD yιG:rSkaZAQo#&HYp\9(/ZsPj,N+D'jR9 ҆.M삾іЏS Y6㒆$Gӆ%j˴c`&2|!"f,0eۑ}mRq/NO~z q/0N~ NpI|NPrD:¬&]8ԁG/^Փy]i|Y/Sӫ+{>G:GրQ +FŸGl`Tj8V/nNlÄWXX=V'l#CKgTǘ~>` <|  ',t (*_kn&GvYc23[|l ,锻hCi 7PC{ANẢgCŜx6`G<цg{Sj5$+? @؄B+<)%h%\ay#2Pv3Ri9hR} O)ëA'G0@o/\}\ $ָgd:N.1%<#b' {l\s7D '^{I j`4o)N!, 1Ѝ7Z@-39፭a9e0xJ"04PFquhwE5{7Ez^f.P1p}‡;C G^3 -c7e,df{]*Qk?B2Yy T*'ӀhnFXn⸎rqYW-[9) YqŅXkK][$ͺ=]녷Y6KdX?>y G 7Kh*2F r~9?uO?'9g#SvE٦~/N_z|gQսZ_Ls(oNVNkkwkCeR\B9*~/AJGO}^!zR!{xizlV*Ƅ%Ͷzwɵ:OӱNr^g \LuzH&|6yj'4-+mS@;jj: vaeR:P)ƽH*WoN1q`wO RnQ;|NC@ uo2a*ԯ~;4?&Y9v@k\]]nR>ʶ,ɥ(c;`+p|۝:X}u~6 ep0f1Ne %܃ڤ$?od+GQVx2c">R]b╎jVM8OPp{3b+1B@tsu1M*ku2؃dFMYf_:ԋB]p+fR{ tnDZ }N+Dv;rĒrڠ,27 17$:鰠N%" cp5MCrST?MѪv৓֥G,GleH{~guƉ۴!|vj_gΊ_wE}<k4UQ:z0Ly:= lMrm{{}χmJEor]Rark>dAJ .۰魄7A@7V<)6PZimIg+^혯bhiBMw9tHB}lW*91xϛ_UyKh>^; o?5(2o=O0a.G[PATZKu:CK!di/BJ!(zStw/_x}0Vo}D=;yyՓ~~={iBЬ+Ep?AwXc= z6"fvb(X3}LI߬_ CTrs_)QU;QeެFwVpƺڒ\L*ePa+pL tXTa:Pv~l*۹,bK8#c=5;v>?@wZOA$hƊh:(~C;IZM+&:3: cDkyGlh!ہCGeRa\m<`J0s2G GtPt"82KYf11?Zf|YA##K(r q"lsW!04Q{^_$,̀cJw*Jf#]u`PvDDM3I}+SN<}cCsU%#aI8<'6MRcE%W<{)ǯ^4bQ]oeo%u6՘ͳtiN s:wU6\Z!38GD Kp @aQlwP(wKJ.B UH].TmK齦ɫu= Dt3!@Yn"0A[b8`vVkH87\é25ȌJ@A f~zۮ4_˜wCPjӀO `rspN GeZ}~1sU9?;/O0R{/9:B)W`{Ws1cAG c86yST,ܺp =F{%|1otL?-^8) p +lB&ֹg e\ OP?S#oE Ī'|r1W]AObAbDkpK \Bv^ژ]E%% \ n\؛ MC\Yts3t 6VoGՒ(ش Frf@ۺM ºmY%5xK  4\RNXɔKI"{e){nئK-E]e>rZ忶L. s9<:ϩ4c2?5" j_!]`]j贸.y#Z֡nS/6K*u<ǒqBy )YI=i1L ?)d5S#Vb2.f<Ɔkf]+}RUp'*6{4jft!$Zi˞E۱xw 3׉Kڏz3aEmbL.F?3[ޢO߈Y_1&8P5_󮗫mDlY"kU!Aߪsa~;?نV7'9~||Փx^A I7ٗt6.yЕlkl@闿s*B'C ˦HseuӀbJ6}Os盹rRS] 4MwB)Qc%@Xg)%6HxMZ3eĬyVJIpüѭU@11R <չJ6#V ?p2&2 +`Sz1'@\c47 (9c69z钦*-c6Z%V[ DQzpOx{GhX2]Ofr ?`j"Pt܉5glĎBlABc i @At>{/@&( ze".ć&lz(p)AwVd^<6=d 3y"&ޒVꜿ @?eOtRɄ+ہ";=q.N; vO0-OӀKE&-)[ 9H5-`= HITY~#iaRb7Rb~r躤VS sHy[U}OO~>;Oዙ2\@olB7\¬3]2@yWhAuztlEZ<8QNs!@EcOCI3$\]2s5vfhP;7m ` y. Cr$ja-pKZjpe0{5̸T̓!}JymJw?-+MWo"db}@Gސ!Xhez)Qfn1ה[5:^tkwoyk`0RHfocR) OebYs7OaG8- ҍ:2IBkI vsӺrqpiWhnwIΓ 621Vp2\=f#A{n 0`;hz ~N#}Wpл fJ5ZSRHk( g x~5'`귫E YWMd#z>eVwы=Ʌ#p53t:LRFbʊMq6 yCלZDunr>z*]Bm!;6͎#جN\) B`7!~0XCG ƍڋ8O0R|hŶ bmS[r> {Gcy2Ř5N3KѢr5urqM3eXR -D*kR6d _&+VXsN Q"B4 5m$kDX2H)j&֥7.Fx4)' خ4,7GTg6ԯ5Á6օuMZkY~•8. L , !͂jJM90ܭz|5!idtZXC! dܫZ@}F9kb] t)cdfbG]Q*qQ hi'ȕ%jC Dfl<4r '_`Yt'cŋi10}՜I5&[Z8ۨeJ]yb=ݓ(dOl(/;x} eVSZԨ_{"\+ְ:!ɞcpX8ٸza.(fatRXp k~z"MPa;N|m=Q:ä^xUkj=<Wkq_q4& {536Z 3cQgfUXzi|L Rx$!.cJSbcOwq~8ұ#IkOջ=qJ:>}5]mqml[[o[ПOݷD߾Wcl5:'^(65mf!1 :@]Aä}IhVI׏M9d4IʩߛaU0RAiKiDˉv6kIrXtPHB>jW%98+SvdэKa$u,(*v#h6H,e;N];J)_L疦t'fG}PI[Kt RٻtÔ$ƻ>#O$ZvvoU͡jVʾWV[ 2CN s[@d ^1F<6Pr6]r}ptZ%ou1eQ/NA} kƪ hc>?LZ}d. TrsNՅUSFWEt{LXBp 36d21h94y !9p4&3~8wvhu(؛>r̡r4_ѩ!FɩqƧ:ӋUKΔ"f=ϲsDEmw録۔Lm@4jUӭ"2!'I\N;ζb`1`2O}k#1];C a +?2W=گuJ|^i"W'~̊,I xGMS˜Ql,Nfr3/ {s\"0FIc,_\"tj5ai5^XYo$؁iH™~$C^!wiJX6ck|n5њ)2xDO)M!ݩm?: o2ƕPKZ1,_,qxkEw|779f5oiHrx+gJSy.4 ‹-h՝k9'H->)5 *@?aţi]RuԂj9;t֜xREe3P %>GaO%%/`rkxo s{f1L]0g#dY>!WppJjۉI &=6 [ R2P/r_kGVc)Qaqp<&<4%?Wi䦛cCB&g\AQ,ģ|@eT#w h!Ak^8^#fF4BF@@Bltzrzf[u_X\3gMӽl0,83mxTyS$e:&7.ֽH Zs~ HqMh[iq[D߫/ܒF۫^j{[i{KR. NjE8PR Tj`J3){u \tBh\dZq{ʐKF4nh4jрB|Bh[Ш5.n`9LŀsUx q`J4:%NAIqaU;l>]7M#2fu-=Q t( ke/_eߟ2II.I`:{f6$Н+LE@%ٵ\EӴ$O dz:mOQ-:]Ot㮗8|ڻ! 7 }0"g$=p9rSOID{V|X 3 ҌPN`ԃԊD ;E9 č_[~R8bn n4%5);&heKrq1.YE,5-@]pF*J-36r85nwvHik Z)H. -74pޘte9=v"ۼd{j/|VG}P<;w9Fk̭0@>U\ 5:gG7#Z8 Nohأl)Y̳#= tM_ۥ|5k*d}.DV, a.#P] ~dCx64;Ũ]yM3ApHAf4|qjcfZMs8`7ʗpN]O`ɜ];WGs8WtDcK}^ed9R % A^K* e[ǁD'j#1Nތf2Rd`Ioaw ,F\]'%+5taĀ0Px1fgjF$7اt|'sE!h&Tdme/ lFK VTVdm{SIa{R?PË2-_vjqtAzv$ݠTg(kkAփ&Vodt]4#ao?]~%/RN>Òqk0hG&"U; {,Qa-{DnJq<Ξ>GXFOJyJV lu };Sx_ݶA[Za55}G VWt/ZPzR8GjLiŕF1s/3_c ʽ"4;5X0^:YRN 2MṯJ_P1#i{VỈ1Nߎ%$ (۝wcͺk}jgm6*~z5%`#r߲ߺʥDGw)˯MiI?&ֲnԆFn, tIe拟G >x޷̗$l62y()Nְ>Tbtq@S6!lOM OM5ʃ)U7pV>$iR4u@xrL0dJ5WOUvy'VKz [;_T{E)C^mD!em{ /&=Ki"pN =@@l2vhCg?=yXm]L+N]=_\g|Uݦf刺NY[mb䳼)4m9AVDdd$b$'焼GSˏ*?nWA^Q P(7+XXA! ~+Pp)>,qp=w[W֒Mt a\MG5;k!yN!ÑE KbP}/R-A){Z-$?Yܳ'mW؜Zv2mFpy_&Thy4؃u}.0EնV8Ұ2-^$Aq'=^wm*R%x ?;r51dGlv:;壳v~3KاIۛHD5; uƄns؋γ7x_%XLbS.:x BNque9J>MIFq"e42sJSw_MfGʉ,J׋GRQhˣTy]?˪3ҒRI~MR0Rj]R,LX\u'Ih/ $h2vip6LH`K`cufTvl>ZV,T3Pw x}=|{ ol{=4i?Z{:wlDsk{Vo֌T^`C?,䴲%:g|'+F5l@L99s |sCcA45JIpzJOC r\ z0e:b(svxMA襭7ƃ$"m$d'm mQA, .Tգ0YRW/M 5>lA0iuT}FL$:!e릊wcj"0.SG՘6IJV ն#f국,@Aj~?8Srڈpq!ȹ tdjԼlfPGN;Nfc>&Ѵ:HA r٤խq 9(w.gMTS@/ao8L?S10+y -⊂J^`ǔLÞU%:/pзd9JhXv='@퉟8ضlQ픣xq:TPcP/ގǝ-Q_F4wyMPoCTMR4# `<;vV5ى ?ݸ4xH*ivi/m<{F$CQe f8R`<\yS?Dm]' lJx)ZF#AQh!坄J Oz͔bBy)M. | c q)GaQDmp̐^xګ_=u“0>/" +>t?֯cvWey_^{:#+ȴ#Q/4L\Vkj{kk(@%g'$/޾vg+ip'w:*j`4 q%ڏFP^<&VT- m8c^-wl2Ieő>ǜ}XN1EW'ZX!ѫڢ C _8+ពfyL#!U%;T?zzV31Y Ctg:{ \a[ԗN'llu:eC:[mx" PU?e}y(t,„S;1q~<, {E~Q ;X?'5nsL.0S< k?6%pC qA4..b``<8R"{`M/&ϲעt<|ݰ>dI1;#]-. nAr} .K~`0 m[$Z} bs(&Hx9~ns S`yD+2A0F΍rrmНW Ne!lp4~3/h˶%=S{v=21˩٠Ȥ40Wѥi!uN E TL,Sq^/dm""lA /lp;ok*) ܢ+ F.Z=Ym:;rAV^;AtRE)nȫ@'ÏxQ_Pgo>}-vͫ>]H]3+UN.R]nbMfCuyɻ.)c2\Ð7f4Žp}_?q %I9%%xٙQE`/^>Re&Dת401Dxvs.< ~ ݆/ٍzn;xB9Z-7J7ZH;LA<7 (]؝gΡHV b6No5v:NGh' },izmnv7C},2ǞA&^M"% |U^ 3~׍jE|, 1Ȩ{͝pDp'{m;A! 8_\P@(%Bv" e}ykj~E,WШ%4I/-%IƘ\^ t ][LLMFvVNIN&cOd@ct&RT4) ll[,oP*RDtj,oޟ\:}>-[[&ng@[{Mo zdg5|™xVYw^,Qq6Nh,T#_h!WxEB-i!f[feס:C !db!BƔv59;N'~*Wa3S*W$dkUG NS,FlάLwP97%g*&PICR{/ t bςB ć.ReGc3yJ[hZua }F}UkAZKE>lFJ@#sb]Lgtk9&8$1:QWG%۲AKfmm1Yĺt92bwطƟd,w;!#.:,4&(W9KrM9p2sV5asx4u\W:10ffuX(@j"`rIh_q:DYDP- ,36eiʿ©&7g,t!&ג"UiJiXSfYPBu-i#e$lNEf0z(>yzt1u $keڥ k<8nT7ɋVU9nx{gHhK+7hO_7>9zm]+YnPp9CB]ߣ_k&MEr#KyMh'bM^5vk _]Ia26U:@J# 4 FY=IL~BRQKl: n__x`'ˣu͉urUrᚍ)WPb :>sU_K!ҟqcʓj f ^jiX"gz&Af}$spklDeAU7;B_Gs^̅{DžY? }Sf H]2+4㈺0IAs7 ; A8osz!cM^ksk ?@|gdJû>ەI&`9G34ُbv+`hx(>_8y &OHQ]MإKi& #P'Eчt,tedqIDYc M"ܹR$lbc4}u(Jkn$ fw0lc', J*a_vwۭ5&h)L{~Lߪ,䕥k7 s`:=Pok/175aI*@Aנ4n4cZiO^% 1Scu&pZٖr@`gZ Ͳ8k0I@+^-UYҘ͙rtp^z))lkXbͨ~n z,wo;G/~oUVsyU|꟦*Ṣh69w7B考}/Q&Uef)ˣӣ!Ǒ+ >¡,=u4\f.9dDJg"*{h6 ?{U^D%C^a?n <ܓێpP)-M$0Փ- g};9ˎzE} Hpk5ݣIyeQ6X:@cQאޢ!&o q:?/IA;n;NüF0FKBo! 1(SOz/$mU=lfJDU͑g%۾z<a?:(Q;+ؔ3`a&<X>wYdvNT6JJ:MoKsZeNyy[mzxTLvx믍Q_Kij:pUkxCZsp[Ia!i;tVNIܧ1yD9@w @5P/-GzLEH[xRTxouOYkaŗ^Hш (8 . nި?NtntkW[]uPwxl-ZzS,yrjl9(< %cxEݤѤW7K\W&'xZAQVQTŊz-CNOͲ,=oV*{s.PB؎fUD;MdUJhd\AmpͭtPa |  w?qS& LwJ{3zP3yu@|&T&a'A$)rwR2hUB9Ea Sd"PJ@xL.0-ƨ@Y-Y S}cd~RN"]]:J T)^睍Fg~ "1Yauᜇhn'@7&A6+Xl PM%7wnZj4QW  4A"<,"N%PdzТ} D}_:}'f( V4Ed>lPep26Lxhnױ}v2+BN .+n1E83"$jEhop%Qe;RVN&inufij @sEz!8fYnJ&&rPiww݃Fb;:g*G#30o>1R3tSu~FhWE/JØۻ<53k#@4'z]iO3т',(0dr"Y f#A]wRXy Fu1ݢGO1 v .g%ỖѐҹC'ikAs)q5I>P;.'p1 bֳ4p̙ƓrE"%#~ J8#: ,ecY#'bp1U:4iѸbĔu) uSVN~|i:@< Ʀ,uF4Ar  9 csE_/#FJ a:9BSVsJ6 "mY^s;Wn0? -Ɗ>}o,gW[C+Φ0*ͻӷϏޞu}\3WR qƷX.`f /́fOwH":Ge¦* 7kiݒT)'oX4 !ն;lӜ=[&mkY]ԓ cНLڮ"4m۷N+@vHTjCz/8:H#_PYXrjB R~me`|TjIM@O^̦A! U:gIRݫpЛP <2=SMP"JPbRc•sFTY2"P?ƿU'D 'L7;zc`eGۧscZm%sH  DOI Ѹs\t&6DrxioMwjRxjr/`(9rڻ{b .QUP'㈥^p"xq y/GGVА~F3F*TVolL#S H?G@3 +Lx U(QM d2Pj$H0ן{2 dr謴.aAHL]M1 E Ժȵ1Q"!WC8fֈ47={ Sg"t`@ςi$Y(h`Me $NOCɡph=N D۲Uk3[ E73wMC,׬zmXGllJU2[:$n4!J7* fU@TWPZ9.j^3!Rq%#N@@45GYL)l4  7 ;L̅P?qts [Aƪ~q\@"ܩ sB?5{w\Nگi3u1w". 8 Q g8qp:چ -&Vp c eюen*sPK Y-ٵXHcy Aܯ94Zgx/ص&w8A8 Ct"evI9OT|ͅgoϯ="/Cm,0{m+}O̽A`S7XoK}fϸTuF]B.Y)|/ [fœ k'/%UfI e r`DJ1Qc<ע:R6S`kT±UXנxho b.q(b@,nT4E3F1F 3KZ'x qfӫde*DG^r Ṽs{'jcv╱>CLuCY m^o|#!WR:eHH>5uMO2F[jzQ) n@RtXWb* N_WǎlYbl#~pgRʦ/ bM'#@VXU@ =Hh&W]_#gF)ad )0R1<SK,_#+R r֨.%]noB2?hV'y `WgK7_!ɮ? j<- Ү#}PvEzp̞G/q[)ĐF z$^X+ ك+vs$ebuq(mw?vInTz%;>m X@~<טbB|&Bqu hjd 6?9}~jl(Q-d]Bs5cJhL#KpƘ7SM _6^~T& -0cs궫jvt*ԼJv8Μڕ?Q /N1>XnU҃@r}' _},MM[=&9׿au/__|gMg](r֤䭹*O5n ʂ[z!?~|N,&W"{^㫻J"c |ڨ~͆_uXk7֖tn}şb%jv6[yYr: *qp۟&AnfI@)ĿӞlddLdB&uUś5max EMP 4ɡ$!ఫY$te2$&^%/neX2=S+Gkb 6Mek̵jlonR g~ G6.¼`x]f @R97`y/vhQ9`"3Z; Tu7CT7=ז"TLJptc`jqcXoыQ䄻|iO/?ߢ_{{vFs&?<~{d줽w7M,:pIMd \k9*='JpMn#Cb8\#kkǃPLPmQT8`*9uKι ʑF$I A 9Bv,?_ƾ|l62I˳<~]B[p8%yu@RP :7{<t(lxt&rt%1_+[<((~PW쯾D:aD6)D=y+0]a,8 9#S$'H}IO`AZf Aom<WN>p|eCbOsHz4pvE|eK;Uâj$cqU,-P5 ;3f9wKfCzsYq+MjkWWݘ g_vLk lEs[Aݭ0=&I;Z%rIP8Lz`GܬneBgkS\os !.'ppJCswyxϿ(|ɇ% }^}rbC&U{)Ex6^K뿎AOY'MMQ>N':EA։#kac}H%OhHAS#NVǂð6wxh:AFJG}J1v"&~ 0!\6䎍[*5az ijs=uTc y=oĨ$ROWf%򰂐|IJG`}6p^TO qJALyWZ[ ɑjJEQs׬l%K.j,V2Òྕ.ZvFiy?9:Dx9:6^dy-I,V f\W裒$a LsBOj3¨C&\5 xs.qjy<,ne+yX<_AAy N5R0="/;狂h`nM=Yffliь}Cnvh"Vަ~C/?ID!藽5 svJweU*>+]YUx\},{Ͷ[ o"6I܌~8eZ x\pX;P}|&siYb3'5>/yuX/_LUԏ=;zU|\%n,˞V4ip;~`i#E9Wm}fWV ` mPNkbTўRDޤ{|gZ="ߛ{JGNs|=!R%&?n26Fseq.fknB*8zVb|/N_8r29&4f"`wc=NDRRfhN~ƀbD;GK-v3QWoE Rߤ;@k P'>@=#:'u6dP)'^YY*&`dF5F+1釒7=J$]A*kYm6WU̦5aִhAjQ4vD{yFIG1J<&Q C 9Kgk.4}8NL/; &%7ưط,DHErP֋ofk)5%m~ cnoMxOoǧ-y9H* wuE\~| #l3 ıTpUKu)ʇ;&D $ڎfӂ2}-uumU-=ǖç ~ژSGgoxoO:~voi^ S œRN_×GPGHL _Y9G)LƝ FdE~ee@y}#gigÁQ} OוdC6 npXjJ`ѠIvڦr@7ѹ4wY06?o{o),{*K}tɥ& EVfFZ%zkWE5^(Pf8U :z0j2 LZp8^^$WQcšS4bɈiw*rq8tѡ8C?Ba ][y͗fI#=J؉RڂWOc@ ɫRO,#}#r >@ ;~NTW\ijź~{ w9d,iɲ. @2 aTyaq v#<,#1 :DCVɎ >OP[O7<.Ud؞ߝCKs"Heweڜׇ" ䷪j&$VBUoauTvAJS#fdjbG]nmxh):5Y&HZ[M6,=U7$k^{w6g(ն(ǪsQ+'YjX 튭 ~M,(6EXɋ~C1@aH:P Ĝ$,Ph6bȣŸ@>&)Q;7?\`PWr܆!8{,Ts X ۤ[{d 3}c&4N4H;iw 3H[0cCa<,bQyy%R´p*[7KVM!p\[AwնZ9f#?Ȫβ +UZNw<S%1܋ {, 2&ڍ-嵽]0Qk6T?c]䆌8j7 SG}tBXa}PCT!"4ãhT#EJ%vIБO|sTKiΎ`l/g@"hUAbrR* z(V U|`f8/&@541뵂buǞz ʲC6?ډpA$aoPK'+}^p6OaXk7l46;TZTAm6+z ̯_NV k&ԟK`U_ GЯ i0V LG]2[,V/`w'3aE 2=5YL/ :q d{>Ml6[[ +Láz(5(ËKQSfSFF{hZq Sm$97?cO>ާMh.l nT8MY~},.ςOM ֠E-*FMj i*RGj/n,999"WQ'B861):F}juKݎ4%~[6b̀fhd9?Z.Ѥ\BlSG|[[voM"}"拧*EkAtDp%%)f1$`*^5z-{e7v=U<[YU׼NLt)s~NCSt]cl_=_ncm6Z [ÿJIF0[ I7`5Sx4$7c>2푋lq"[Yʄ>9x x _7DCRQ(Ko{U%F2"+ 4N( %_؎#G.n`LIPwd\6R^iiIݎ-RIs&(zgӽ}5yν_RBߣ{هzqtV?mg7G=|}ѻ?Q74wy|#P?뗉2 5^р"/QN >&r;&6S1ħzeysώ]}(TyUjXk_f/RIP{1CzAwfPg)T3ND?cKZFFm{`hO/HʲB輣Qv#r?:h yu=@1ZNP)A~lO#x&q2?EUF)a$iepoh2a ]`ay#é!5[sMF&JcuLHD/6岪]G;ŏ8 ?>>r?Z 8&`[_qݫH1Ӧrr5ϝI>pTQhYCĦj<\0Pha04Ƽ~T-a:6.b=7轹Ӫ7TԘ Xq/P_?(TF!"._r?UV( :(cJ1 5x@r`$٩yŬ[x۞*[^,^}zjan=fztPNjزۮs'oFm?<5k&WR3S/,"fwJ0ʁ7-I!{g!ѹd1TAyA-sx/Ԗjf98lā#z.NI9L;1ͮ6sc:=}%sATKTbIEcuỮ#߇MDDXpml'Ia!kSW/E&F4b(rTYbJrGȾw(l> )i]-?9D&5?FӺ.j:ŠTU,p Rh`b8)8dm|ivSX3OB`8J"E;pc|vmf_OԵ+2B" 9 涷T;O{g $x࿟ ws} lp-8gYA/ kgpe F}A, yl[1Bj1ln:]Ab{6%-:jQ(S=wUN :ZUj'H;l?>j5z-W^Bϧo^>Az_̮a=WhXQ7$AQpfo} ] A[Fs-Pfe;eR(0_ CvxvtmuyLf;?TeRba>j~9+D~ ៟[9gK(Y=wA%+[ݿQ-'@3nV=LS6xwZb>ыu~~l1Pyc9kTsYbB&(O;&刈`N֪b I1[5/'u^#pSLĈD\QڑI"rCv-?!s$TR!1 q Aو5`'EȒG$ @2$Ycݯ*AAq_ÇOib@) /%hO!#= 5g7dnl.gPM k l!c@+PWPUm$ u$T${jЯbQ\)j>[Phjt(YrTSzsD>?G<*J呫>V88=Y‹zV h&PV1TZ1\/\fUA!uq]KVIF}ӽR}^U{] Yhe"{E-Y̳UIs1^- I؝T!_?Y#oGR MA)?RrVJ7aZa o)a,բ4IZodUsI(̣XRF_DdR&yv >QJ 6ISHYuj!~jrE[\Gտq?+G̷.ʧFr-ȳQsנ*2@@߆Xv$+{P@bBwjRJf=[cockRQw#OLc5M,-' .cDp KB&=m\55k\B눫F*g /˵z>jonyg=J% kw0 /1y xpL:Z4)1 V0`^q јr× *1B4fk~Y蓿VmV'ꛚuD0'7+O.\a~Ɯȧ^oݬoսu\q\ST;b&,H-@)p0mCpn" P8w0^0rGo$@wvRxJ?* *P -FML,Ho{'\j_ja=8|A:bdcUA*|.NYy| '^1/PaRpqh)4@aQer,.L nHLq%15w 1x>"v2bm>.8\ItA#uF-"II33ȉ(3Q36J* Öoe5j6ҶT|K7bR]=iڦisx[k`xf -C+f U7F3QuS[d6P|UUYr="xRy~8  Z_,ʏC^YuAӔR*yCSe(8UR6 aceá_,Q \RM@jWuWL)Ƀa8e@U% А6(&a* :?qcC2dꅦW҂ހfeGu7]d1M_*>bU]qZdcp+ɗz^m=?-A[ ,ʎ]+U#e ROaΊ )/.0~vSӍ$~FpY|UKEXp(XDWD)(BN}CW9qRR:MN8w{yU:cGUfU1pغZݖ,Êo/ZHͦٴDOe6R*99`2)3(?@ӿ/Zz},Ø&G_o7VIί"i@?nz/xZUi4RmNÃ?ws'uL:D;tM7\pඍ)[~f|[{-昰*nG4wZ_ `KE$ⳏ-!GI%&&x/6thBf/YG!FIX;.>qD\0 _O,r$a) rRPS(E\OS.GnKS[:}Oa^/EEB8¼\F#VP;3g:k -YaFxh-M =DC0W^Lu7C~ PsPO#Ebu*jv萢f~cљζ, sj_Nl̃gG/b`b*8b H8*/gR6Fb+ŷ-Dt8!%!! xXw\zؒ6܃ӊ1?oedMCVVϑS+,`7rQB.s_ZTv#ZÁЅC24}׼כn*SB?WwBZ\méY-O~lOqSyM d!٫\5o?\ܧEg@vBؗhM U#JZ8sU ~,&Mt*ƾ1rb68'Rs:Y7d驓.PRIN!ٕ pX)܈%1"fS@XSzfH#O\|?4p ,j&r×%\?Pޣ' ӛ:6|63)p:}iRjXnpe|HHq̓A4 | 1)^mZpT@0L4=ǯX'[%@޼=zsz ۈHRC4OԓxW i;InNY!i#Po6ަlo#tL H5g)y;Ά\✽pM/^l1QRY;Zz<;&]G/8.~>đ&&naTpo<إKrK`9lYV^R\.5ʳl52sɵT}%cKt e;Z[Ck '1,_[mݭzn6{xnr&^nz(4B 0 T}JqD>(o!wBL, ճ3zW8p6X#BR(t*߁:@]4M X7$K!Qha .qfק'VD'FsnNߟ8SoMJ3@1ؘy`~\J,K^B NTaSH:(B+rF#$qĂFADoVW2o$BvPLwo4(1t՚UTP}X0u )Φ$!_[GBB,0[3xb l:uR/=H(RU.#^**&].fNdkc^R­v )8gCmYT˙,a䌒^|$VLθIicwkˆgseqQ_N?LS6_ݠw-MvKjoGÌR7̸\7qapi9NGKLjTc?cc\< "lrɞ }}_'`d&AiH굦`]V8,s-Zm<Δ5?1)J'Wl-5M&GkY&`j#1IK5 /;7hT:44˺@R A9{ꑹG2̯a,_)VL1Dbwʃ9vo.Oe5By`Ii7kʹ]Qea ̓d_rM/7] ;X+&GG;4.?D8}$Dv8VV{ZLs1j%AJ_FP,!,,XNS$0VI$$Z48+ed$ڬ"L߽A4wot{DyL M, iD3<},AهIQR֫j r[2g?n->wmo;_E\hc#G\СJeN|λӣGG/Cܰ[! }קv‹?>FgTR={TXOʆ-8}L.)+ĠaGJ:$?fbzѭPa"[w6R  N-hwc;(H꼏3ZM`*C8 X:; 0:w˙O =lL}Qںjxq+~(LlXm:J%uvڍ紱{[{Qw[-zfgΜhIJd@A.%84Ueekkbc!d+aEa: [MWkd+N0@9ZZs1'|Y(1URΓA,3f!RzPi%)]@= D)9t(:lg9Χ@+3ՁsNqJe8< /#6`Sq r|RݢL/Yvkؿ 8? l(cM{xܼFCQWxoc#U|Ôat8wNto;rm9^=PDsjlm577oxo~pH̄mP^^QHѮ .t7 .*0|_5Z>h;o 546!OYKcWp`4c~M=N* 7!]JI'sep7fjE)h á?^g xw֙I͌Nl: y^!-~48@64CZB""gR}sLac/-:K5m"̐%s]f6@Ġ,壸ȕ=n+n6 n5VWj;jʇ殜^Ր. l%~4p /ɴ$. ˛E= -uzP]P{SXa|)QDIq5]/JHi8*>*ʈd+G)F衳kkvhbpbE!x/9ʐhT=dۓgȑB n 2V?W'W?c ^fJHgZ^8' I|l|FjÇ-7ns$v.ע_k@EsT0-W4`9a%O3&|k5/_85T|mPBT̤"MFdw]3:25fy/] _"yk=L$.kHye :ۛH&mOTm%ko8ob,evą#'گ%=ɴALJdδ! O=mNr.;t8)J'},@t /j nu ʻW^&u }P@MW08Nwlj45UimeL 4Eᬉh"›}k/>/݋ڛ^mŝX)Jf`4ď@wqH -@gN0 lWP !;tu8dy2c gBUH  )A N,YM/anu+Zeuh_xil?aã"@]ub?>ypC ٮ!kY6`Q`X'{}:rSB.|KQZdvƼ.x=`X9)N @ɇqĸFX;`i\*UU1& tA]0F{&Ʃ(dq1 fF I!$cc՟sJKˮS zE\ƚM ,FZ*L) 4鞀Rs,28j^9Im_x2*Lؑ[6B'K*/W< PQk}@ygxF.Bt@.@ v@[oD)lF([>}+eWc b&}N~ 4/|t Z"yGɐ -Dνj:|IpP+7O1 \Uğ%oƑftXe wG ]wpNJix~Jj⋠3EU<E yMˉ?aQU;dW6ƍ'h+o csj( I=ABҥGk\F9Mxȥ7?B\Uu7aW\|Xz\IsI|ǟtN1 &; =eR2Qn X۰7 ppEh6 ϣ-:0n<|4~%I"6LTэVDp#%_f{B :1i)xhdŒ4Glc!]s`JAle P_VCR sQ끈h> ZGh7k6Vn> ,e`hXES【dFTBx_V*_bl٩ɉ+ 03ޚ5 metCȂ 922àPۈ `]E337ᄂ&zrfir[m?88 lBHP]㔆P1*Z&xYC~ӹBsZke͟j[B$5h"!ɣ2 *)):(sC_rjMNRUȾfq"c#wD\`dU N &V/V^[(&.Ho03Lj%riWЕCBNq2*+B;gO@`k]gPF'B4 BUBɼ" h2uQ9{C2yiXb1 Ο]# Gݐ̕9J9? us||-AИh>:CzIJ}Ag-3h$E.G U&4tnnDhH d?h-ŬD6 _ 5j*vƅ|V8(47X?26[C 3tʰ_kUcy7N Ygî/T2Sm9L^9) 5F 8hWf^\yR]N #E6Frg$[J B283=BYȽؑgY/'ʓl cMޮ6neBb} P׌XoL+I}UVҦ.LH$ sOxY0D %e(󘺂*{=U@ xXusAR̟l6"DD$T7HШ(9׻2=M鈸hX25oޞqƎ~j\b7ZJ!v#@g$v6gUp_U"Y-wKZ)f6}a-BMAрQ%+~GO@Lq}6షpMOr֛^t ]r¤jKi!6lj rFwHۑOlH)( Ԉ%{Xp&uSLjo܀ؿίdOCd,52:Zp܃pLJ? =K{~A3}.I%\TvYL]SՒְ2q7cc\Y`_D7_.v4(jBeբ9Th^N DRHHgih&+O&,wTDb2șd|mb}ϺL6m`+TTl)݁ _g:oVz5X"iZ1|s Z<+[YƟ}kVB0~VΛ*Fqh(ڤ9[56|N+O.zza!er'N# Ƴb^!wX9]MQ|IZ 4S,ꊢPUf#"YݣefÂBĠ-dEiS6h洘4侠Ϙ%jKN49ƨt1deT[$+7@]؛H'!2 q5WPi-?k-rN®MN`"gr.ż ]К yY4J\LAQ4"w:eX'N^m#֝ڱ9Ոc%=:8-, M`jr#9jjy:S-THZ2RD9o= Y#ͳ:44SҲ!8k*;ccZA~]2~ gr@/ kۤň4w!tbIIÊMVMh a4QR~`.)A8l9UeC[t匱S(rtvׂR^ wpvHƁ'1+r0[llV Vۍj~, cth_yZ,7RکOVG>i+ykŽY`DGݟFg@[R0\%4<,>kHke sP+f/>MZCJϫ {)f8:$RjPPg,Q9T!pa؍? ˓"wQNN_×GTF)p>%CR";F ĚS\CTtYz6vu}$DF*;PT*ٜ2bn\Q8%H3fPkc$Y#YsD6ɓƬwohWDЪ8|h-2%aw;=zn4fmӿUu &@=\A&ş^#'&ѻz l7ͺguvt_K߷3_!~_?\ 4[h6F 7`q6߈0K(p;6A뼼rZ ~̓z*d"*YsBA ()N-iL?$cFQo)aUah%)Swl"Xcn3_h-KTE-'v^gQ^U}~<Ϊ$ bIH-Hk ƯP@{^adʡ lD:b7LidP=8E(+ubkб eJ*7݊]Az,@ it=(W{U@.&1Lb^&Tc2r˜ $`U%]0v΃IL0= S=͌qj jI0Q"OKܨQp\F!REPctCTki ߲r|yDR \89m<%:med9I'|-EЏKujiן֝DВ|a@j>"dڐ(,&w)y} Xr_cCrSl kk/_A=wD~knF& rRl† ,DObAQ&fY:9`sBb ]U󽺴%,$ q8fpt2LTئ8.sڶe{%AT'S9: W,mM Jq'T/'OsKҦP **胓9S{Zt]wg' Ɍ 90g {+vC#\˼'CwQ|1ћ}S x,AA/ '&"$&>.zW] ajV=SgK4F̦/%to0I_ xK m-@މatkp*˧>[Fu2(2hՌX{und0Tf+.}NǏNXeNwӜ;Ŕ&Rx7GzD4C:f5͟$Nǫ7̗X^&avۑЙ,&S&uFDŽNG"# iץ!@% UL/0ZƀQ,=0~j9dI/?9 ;(?5[RW8\f7̻ӷrz|~bww=iTO,FKN:\Ld.F74vg3G|s 0"жZ"9ɼ#Rua|yZ6f5,|QR[Y.TsB`¹ fo ՚0/1P+= Z: Fg<$'0L?qVg1!" CY圸om[F_/^<D! Gu޻Mb'm)/}%+QXb<兹!5XʋosǺ8C 0v=WSŽz= 9wF0R"DN7or:]rK jGh%^'A^v޼}qTFZq+]sK^)/%"J{(I5r,Ls6ZCAڅ}7K|(^1u.^-Q~pYA}R}K (ӜGʧcRfP2٠Mz"YbnujZi`1|m|gXŰS&XǙ.u2[.}${ nkZH$DnRtَ =O.>("s (i^zNbo0fυ2DYp B]v+w"&ѦG2Ot99c"d/W@EjDʕ#ʟLG^PN뒝PbXMC>Cgirb31Hoyjs g_0:u1tς>\FZyJce_(W˟(BZGL|^M¸x|7HѴ~7d&Ds1<#5hEkD|b€cĠyg誻Z):L8|A 0 N&p2xyQZr(Ȟlik+)7'rTތ<&)?oM,ΓX|3Bt1DFaQhe|s;9P9CҾP"џN,$\CJφĺc0JiMUhWU qh<ȝm:UZ7" 8&C/ռEbD sӳ:k<=;:~ϊQ;j:TJ1G"DX*6~4Y~~%((񾌭6Yl\Yh#+鰈wŤbMɃ#ѱer.:1Bҍ}H='Mް{Dlb{njy8! )V֗A -ڇ#6[ #r DʢD:`5-ǓJie̶؋(W 3 H:L½ϐF^ pT,&㞎Q&꽮)\Cr|du(R@ }̡IQe0SdsLVTs@t>jn/u)XB <7'5`jh e>4B*8הcF&o 򦲤+F[yHlS,3@}8EYpUŇE\$Qe_,?e @ %L/r+H$NF嗵ZVa9xkQiQ$d+TRy#9}7=y!<6ۚ|@fe%4kX5?tY5%ߗ0bOi<=*U  ҁh:0lh!qxRL_&ԭMA9rɤ$r AmBpoz^٦϶\Lҍ۞axǴD("O6y"_/? E6~UW*G, (<ݳ1qa [F^` Bh+}ΛrzOqO#h?rќ7 VK;cB^s"m!̅Tj`WO QRpl\Ji.C?W%M+ȭ7>UlY̕ƍDڜak5AbD%XWL3b*g0}]7iKmLOXhs%ޏ/ eeUu&h++;PI&.!CP?o{#/įm 4P\35=eC!,h٦-%G%旊UUsSaȌ̀ ⚿s1G.j;cèA)ПFLLs_']`9 ^o*66VAP&s!m: Zh ԱxMI> lY,lL,O{ȭji[eм\>=P:6ē{Hgə߳_H $xsG^0h!?6 C4EG;J^|DƁwL(%Al)x_K<(zs]ԛ21 T_8M#JU8K@:61PMm<)PkH3Q5 W9ng${NAIEmDTg(Z/%ްIk+5q& 3ySw!nr6.R&Λ&]wtd/7zZ1ttO1Iѳlg+,%3{$o"Ѹg g̸ Y@lcLן u֙\ ۨK'(~8wdE Gh;+{ETbw@T6ljgH2'a4Ra}?;T mS٥QR1P߃e|;! cc >'O $؂_U3睓óV}ܩ_bTP{yLd D#'hQ>lpL\RЖ3gWG# Ha6V=9:9;:ᷗoO9<}[d%|-PWTE:{ G$ֺ1|1{ (r߻;}v֙?ӭǘ !j牰$t<dNabp;8>.LB*GCŪJIvr p:s_N?6I-]E( |-BV wIgG'7GgL[uĠpN )8bp]ISesC0.xi"6Rf*E% ]Zq2 { mhTjR tj"b: SrSRҨpth0-~-ԛGɳ QJ!߇ <&O#n vujb3ashgҏ,}35E(ٛ#veˑL۪ر S BrB '|~{Ey5w>E?Ń^#{3! 5tܗs3@ҧ(!InQ+j TgC9+a7L/R\TF sWgGģoǎWN^2ʜ ~Ͽ+QR"8P(,z6nr$5T nvx0EhVgKK~c ̽E.:ҡӎAcW0 {]W T2&Q3,?˺j{_eaR&$HB׬Z&|?_d*G }Jî-j v#a.sfijCҩS8ր]Y3hB\wnQq X㉈L4rF?I(el(:+i%< YQ#:vhMS֟N^q(5ir}T:Z`Dm1BʀSW]gt@1+Hxxæ҈MA|:;1N_1|am4Lɪpl'U=aC$31iH'8n`afS\h5"$(CUg%mL5ҋ4Q5NRsN6TӻNẜcI[.@GOۋ-JJW*q P>uAW >Tҁ@DYydfXub6$X#U%Bɓ'% wY8巬3|av 'HT|{6SxU WlC k8|.9EUks| 4 }ukFY2k{C$XmJW9w &ON$gqLs,i/]yŤ N1rf8kjT7Ѱ?Bo^ @Xέ#/$ĐE$$1@Jc4FfӅ7m >2Z+(۬GC֞&h;G3`M5@#rS&2NJS#t$ i,-9%r!i©닪 i7>k|NwS}x\ gŽJNbEHcл>YqA=‹p+WM#<^OTz֟Gu߇Uhl8Mkz6E+DYBd6㬀r`/s~{e2&QO ؋=U[87Lcx C6ZRR9c{jR#A>$zϘ"οiY Wh RђWTNXN(CA-V!tw_xKCm4,W$>gsy1H:]*!\3q.:8LL$&j3?^ =ݳkkg>g%E7MћEbPO1׺b,U_W>xdm2;|w om</P"E-4t'Ιб%2@:+fPF~θ5]oXL;vX4R9-rBXE17BпR;t=$@;:v#@l\RİG8Ȓ=$ !-|@apH+ku~w㟰z6vHkb}iB=RZYI$Nղ߬E\9uќjq?a/Zskz\<|:h8O :ީ6w 9KN/FYsL : Sbo߸صWlDU Z"c9y*; u4*d;qy2\ȂEKL@+N@њ =Bk%ڨh^k\1,[jU؆)?nUEu߼}qtf5f@"7iι003l00bxW CY% ʇDSeyI[>1!sQAjMl2Nͤ6-vJvKM$ݻp6ΒI{:%TVCٖ)iDzƩ򶹘i5=O?p2R4FPFP3tO5/b*`q6ELGpeAiucfBC{ #DJAezDMtǃY7qϋ t0bBIF>&ݡ)x:/Ln6p"p6}n2u.'BM[HG%F7n6j;иl8E?*3Q6=k#9. ǹ3p.(9hg)#W 8H2ss"175zpswBn;{ۊn#$i ˶NO$kC]Jޓ'@pYhݪPLh? \jXmD>CGT@.WX .b̩^,0ya:'i^(V2q#%L%`r %BIpD!;mf9fCľ}r4QfCF#crdiAA'l|ПN'lJ\ˀX"Wa^ٕiHQ#/@ڏc<'3A8vm\sO@7 8!r]c ?+w,octQqiG=N s0}mg]"oɇCv}־[F+_6h `|1*q7 b;w?c1bLX ٟ.]]ÃcE2]&1YD>Zx>8z~.6Xw]@~~e')|T!Ow(?kNa$4mpZNr'.P%w:AΆ{r>ȳ^{Uq4b_{vؾW)X԰ݮ6Q&ZWaL Z Oq9(q OYgg҃ E'&7zN]^UΠ>MDm5PU?۪ک~8P;l=ݞ@&%&/x3_PAtAdI]d12i&f,0VjdruYMP w1r0 8ilْ<5S.Qe~_,JZmo߄*E^yhZ} Ad2$P,:]ьiVKLIșaт{c^]CCgG"7#yhNJ"nO}AÕFc'v)U'R q?ő nq0@$B4wG/z~v*J >2V\z#dϩQftSs#MY-mqaó#;{SP>_-_=mES{fg"zbc*I%i?yxmn0pH]jU;@ʤGµNN|mv:lrW(;U)w.˥R#37}t~< qrA}>D~/|aMPK[^S &:YKFI< {N@=g=?? K8rkƥ8>U{!Cq_hm}. Fz@pT1薖@k]Q_OJ @Wl†Do_J#r +k"PB %l+cx*  ,87Ǽ+4v20 V6՞:S%=Bw<~hC4 ~Ё>S N9Z hFw$t#n ˾9~L*+r8ܒ_Ղ߀lF5{MbÓs\m/RxzɲNsgrqt-AUi81w,2]w/r*̆%r$ ƈG0ky CS : 1'01olRB$RM$9GʮOl}Z1 j%΁lS[WK 4\{M0,Vǀ9fVR+z9yVlQ*`ؒqYW+ AʤznϨ`]8Ώȼ]N,`B2Gs"Ip ((ZTʤ7z7e̽T=~\-x乧*O k岷N:#w7'%x֮ >ڞ֌ؔFk@L1*2_!M@v_Fh~E1[n{5'zDw#cn *n0wu>\mp 0w9ws>T7I,)sԒ`ݼϠ]T)I55 Ņ,ћGo3 kcR?T_qĉj5qF/~ycs)}S{iC}ڠڏGv='؝_7bF\fc/Zw;O6O,[rA>T#9f$fT~sISh {\G CK6=n?ս|I7n{*~~y. 3I|j?]>Iv[m`SLGаRLNf6;$لn /o5&Qr[}Fw3. K4G<h,(z oz"}XjĞ3=uKfw8w𥠳 Ѓ@C 10.x {KÏ'$P6h_tͽ& ZW(LE`&,v,Qޙa##A7@:賌\U|k5/X1g|.B_dy du^ueU$GQ͹VP%Ӎ*+[s]lhPYɪ;s&C9u"s5@qqTdtyhxDNyM J?@D83@(chR^[m lA_Jt$~%Bog2{U=O^c.В5|۟ҭ.05?LEѯf M\4ϒ\)暙ja70 *PZ 9k])el~ֶ[̹9i+4Jj^rd0݉Y+1ڡf=B^/N H"~ʻv3 EpX|J#zjU-Up8fknT|޾rQݙTaD?KRK?4ńpZ, >=9Tql&-XkFfnښN,7]N࣓ͣ;̻zoz~Q>\fi0J:dDh0Fo뀃Wq,Z_qE7r ٰcpv&22`B "AhUnq{֏X˄!E֤+)bv-xğp08KU5aq_#_=V "݉A/xF q3J1$^'E2Rj=L^KM}/ͅ򍔼x 81> G$%}dKqN"t.dKĽސ]\\M?GӪ8f9ޱ:~tq7;Bjx;a)F&XR`-Ǚlf99g hYI_̂}H*ΰD ϔ7F ƶש*`twˢpc0֨&5D7'lqVv.U;bHֱ-<˾pHh 7{-&Ӈk0{kZla,a&+yE NhIVHdks"I? 5 ^2v KG)ASb$sKN]+TinўjgGU;.jݤoa4(`9NFE]tq89G$C]&A>tA u,EᴞACGqlJy Aɒ"h$h]:E_sc j*M,(3ZgJ@$?V`Ut.!O"e۞I8|!v8F Xqߜ훳=Z(gB"QjxGu#S-#Hf].`ܖ%WpH P]k똖6e}]uzQt(2L"`q#t5^8 Vx'6DlmC>h8`\52i4q,S#j{|ٲۗ/1n(ZaRT\ YbCMMjz\J3.HGeeRTڇIݨ%.39Q8_q+H#CBw1Ft9Zܦ5c}{~*~y;45נKM`mH(8}3f{K=92Vmp0`.f$1W.GyEZ2nԁ&HAiPpetf*0Z|^Tv>{g]AH ix"2 (c.G{M#W$';Ag7jLNT/bOaary]=|')Q1sA%<{/G_~j(Nu#bjFւ+HHizQV{ՠicdHav|kY_q__ &^j39&4q>}ݩA N"b&V)|#܀;$9֡Xdb往9kf%Y~d.G=řٕy\XjtdzυĪVܬ6U?FoAYtj[H]IZN-1-J E`Oؒ&U5Js%iT[sEVlA "\\`Ͳr—%C\ W^:Mi74[oXQ}\;gryQ%'|Qξ HDhJ~"rjj<~s.մuGU}J1IzƊ=;~)`S*)b iGl ʣR^a\js%Çv;k*ƀlJ/fbE;Dj^-Ǿ8p^1cAKe?>=Pdu`[qis X|%r/,a=!p..}\\Ջ{w`nJ{Z|<֞&VѴ󎞙Lpk&7yP'$$q\)/ONcٮ3(kOy4rlh-☬ CZzǹ<'JU LhԑGu4Ӂ ;)K0B)ӹʮ1nL<65ވ [6~6}6Z=W^j_g8ɿRJGw|0޷7kl73 /q IY3G"r_-uqFs?` }, eIYC;vg<Ĺ?ɟvfVvNnj 3j:WSxь0xrx&AM,5?b`5w[ӋhQw/a8N1y|59ƀ -So5Jr^´q8ˌ.Ll%?7???w?wSh~F楆m8'l2PIf5ϑ<4;;߯Nl=t?2fQoIxKyDNxMQAfCn]8ÜE)dc3gA{;BѶȿ舠ipYM3k/7v?tsqiv6QcJ* $^M$^MMmD1%ŋ(?nh,D6&~}8i;;fv.4wEDzC$(x XxDk6G4),0hM`QJ4;mR3bl`I` գ~x99$]tGV6zFMӊ&X|gwpWCLǝFLlL (mQOi7 Gk4~<'-}f^1RFWBi5px}4:^XF&z35j(ɫIo;7[{̰ %p`SӯUg8Ƨ-錯졠2$#h;ף}ٽs} VppNB?ҷ̜muhOIaXo{5ڭvkj$hG`2 &9ԓAcɟu^3~ 0XS'YyGyjvQ(;<J57zR!St]zߥrpx釣x !QtBE9:?j㓣x)& [LVȫfNB% ҉͉Za(A`fKS 7A19nD=utMUIh9XRTL2oѾ#SN$,q}Ћp9H yԊO;?v:b=*|5On;^R*o_ c%_o5AIiz1n MK }UP2n gzy ci ?u9?fP>Y@7om3[lWϖ#xG|-~P@-Z]\trW.]%!-u_#5s_jwп_},x x?djnn~|w6tњF:Qw>"\UK$Br] NHqRU1ќTcK)P]A/I `1] oI^rỳ/$EOaMbtVzeJ"A6WKy9ݻ3H>{[TSÑ2FE..XUORX; ћWQE ,E)pn-Fg G8i8&Iy`!$:\_*3#oɃ}XY寖WPg002VRfUU]L|! < R#EuñTv'+_e99|w6UN Ppngͺ`=c4txrT kMaʮɐaV %e}͆=n(A‹0FZrJ"8B 9 ╜qHS<] yr-jxz#pX>p;DJ$\q!r,9c*r`lBD0 w.f #W^Nzp +BsnY?AcT* #A% (@QP;& #Y&q .> lr[5.BȢ*EQ`(<(^+5wFچEFǑ_,:Uw*4?3pB%93xtE\DiM2]+1`rya7(PAcuaJ 1A&~mQx,bRܧ(=3(5u=c!6#kϏc c΀S j?OmsG;vg{{jA4&I3!448+<}py/(*cdL6exeD Ѥ#.z⊊q笁pw 0%*.2`UquC(VZ R6@X Utq +N'~N%`cĐ;@9}(,8p$Mp<t7ulc"O_%cGùr /koG_0EYA䲄sZF(χ;,6s`R ȩXh!o`:l7feu.@]nD0 qvDh:b` Fe-ֽ^4YֶJ -Nk9Z`E"P(.::fE1KH.HIܩ6Ue-TV^PoLBJl y$cuwU㒂71N@oǃq\67Q+a  Oh(0c8!BJm=mǀ .A?՛M(m-X|/ C%Bз*v 9OI5V`'\Y:`!zh;gЅJbԹh>]7띈[FʹCaYLÆv &dJbPEEV)E?7B"$N.;D \ -H?MRsLJᕝ({ )$X/(윓\,@ t^qɼS3WYqէKѹ+iBVHFDLJw1]^gXO bW}Iϥٶ)5PfYOG>F jT[RO(`RrQ(]{/qȊSS1=A԰$ ~>YWN!*-KC}rԤnZKh%"(>KP\ fiНDu[߻qk9 _D(A[°dWd}zLk&[SLݠ{?{W$u_-ݦKSwZ& /~E&fqg|4aNW5wJA}< 'i8KpABJф|OHE?@]TmVuq!ꅑf %f27f֘a?봊, D FRIz\EHWhmC݃T8߀̭ƛ4ޔH'&aPDZop2@q+OI8nDYDGSpr Fh(&16,gmTG꾏P Û6ψqACbp܆!?F_1&^L ND-ݸkKӄ6,WRﮆa2qq}6@2CUˀh@_ꁶhc[)dfBT4jaOzIsbK?oI^!?  ;X`jP("xa<aRNJ EFn&.9ld!3[c3[+KPn˫G!3 Vkh{U>:s$("^γ Tx`H& :aK98g ?uN/~DlL7 @9 PUPY/]Dt"t"@+fLwL1P42^5WZ’eU |)g[$R@Dbƣ'0:v1Э-XRC֡I39}]x %''g9|m dr}AQ|3RxqEsaTe$ȖD:,@נap tџ֠ĴHH5k<8ҬS( ǃbsś%~nfY:GAx{w\/2H9Mpj))"%vV&YlQxYo%_ޮ9՚[*鶝Ly<QTQ4&6bw烡?c Nl 46t-JLdzؠne\ 'm\2ޗƥmH;d2G\'U0Ӣ!2|@/Ki``ͮSq5g_plylJ,+3m; ]_|  ~$N;"ؑH8u42EG3 Ai F?uitzM$/<:/9RA[+ܡ&Q8LTOE=w%#o>&;/;.kp@)Pј+ i8NENQU>LΝC z߃Zh~5SmyFk:b#aLflk-x8QdNLoɥ 4Mn}KgXAo3Al+ޔ؆, &c"a_*>YeYыp} @|@ yў``JPFdѩĎeŖO@6zP wpr*rHW\JfI3-'sR ]#?dծ =BACqp 1Bs`y6A1񢤢Q1鐉ľ c!<14 )AdKS&Pm_UD"ʐL[H.PRa)LU,! ڳZsTrHXzpLyJ/t U_uQM"9rU8͟xFaig_CI K4 4z5M'-C: k$^W#-̏UpYܐcĞ#ެ/D)ײQXׇBD ,rux z5R9-ONGE~WnDe͜0dOtPj0Ӗd;+`E^TE7I" YKRMa)2);rTs×/*'Y#~~YWSVbk\e Bt1`k2Ct*=|Tj>rƸd·H;@炝{scAN;~ Z(] I+ DJy$1! ڃ1gy{:VNSs[SGx ekUiK+,iqo{+VպH\")+iLL$L_fDeR.yLet6k{]$TR8fMoāpk] '8eR>V_a߇wt:)z \}?d+[V?U'~<ǰJT5 0fZ'3z)PAl %BbOI$kTˎ tghuEںԍ-50$U Ց.JF-]7n&>gb,Xl~k0qAe;VKHc^X/1?I ٹt= {lF@/_ӓ.p8C ?h8?Ag T[@e0Sigo^\j^'b%j_8 K+aq=yo(kz31Tz.ͼ1gk@`SP%J^δϣLMR'[ǂ{#cyK3 - 1X70`[`IF |ePpUEj!Ǵk;z'?ʉy@Πx]} O.d{w8d+bɅ\-TEpzrvQJU36:]~va5R1#<0 ~f/b8-Y Ϳ3)ZS)ߜUGϲDb=|ra(|_x7?BIwOh%yu|QS00|Q8_e=`? oIޤV(ijghJY?'OO \݆#K{4Кv|cu"*:J.իXwٻY;!?[N4խu{$v#hBZfZyf3{NRnQV}+cLiTFf@g2{8i˵Ookb)ZgpX8hS[5a=#MTUgj'ī]V^Y ^|vqb7zڕ$Uie[X:垤䴒/ ^WS'\OdFwwbIcYY4ay ^j3ZvoT1lDQsdTƊ2_)۟2053POYJz.0k6va?7/ #-Sbx?洤GPiB lx6(6^?񏿌  XU SlQQ8hNbSgPTN)y@7". |;연Isg"?(U5kn̊2-k.> S-111 Bv F_,T S7Ceb^rD̪j j5 TИJRwk @u!Ieut8hbqʝn&iF,ёTG~egQy8X󛊩5 ?!6tоFHۂe_ۡ\mH Ul 07àT(s<9q~3'SU1&njL؊KW8`QLYhFe H9t}f|_"y; v\İq7ʗb",IxX> ߚqa*qUJwBM.~sc1, }L{,q+e;s/ǥg9Bl:$Eƫ:Љb:@GHJ}:V;5jZaF&GwGuNMvzmruQk[ɫpm$1ʮe:U{3Qkݽ=vYURMb \y&UxKq3dmg)zG#ߛU>=Ց"{ 5oE57?bwd %4aa`&B }²(`+zH"52 3Gaَ@=M %!B 0BVj&r~)H*RVԉ^D;EG5aZ"lt)l[AftGR8} &}(BC;V;MK[eDsL?߉.T$=M(%JR ?rUܥp_[I{#_n ɷ .& IujC2U2D9+H xΌԓ6v#M^Q0(T`[bv}=~>c4;D?l5}ʇ)~ߟ7U0~7[ɛ<&VC2;}$+.g:8 |>jC&VTxk}\/Dviblc.z=q @Mwˬ&Y݌%gJ"Vwͧ ;S|aPpTB$jN]oji%ay{PT^YkIM2o Q)+ "`s ԉ@ D:AeouE[NJm8\!/| Oj 7,|XKg_ ẏGɓIB,`p=$^r䠣T=)ZT#Z˻Aإ[v A4җNSŸO 7`\ #RR uS{ʩ(W sd0fa8wv(DCHcBVY`n8;Y\b x ,AUS~r4Z(rJ V!7&4;n /|"PapaY**YfGDd@9#{$-5,U ђ6#h<28`+,Z5QeF)ğɀ'Ѡ`xl"O/XH YuQ5L,Zb$7g?Nz_wP9VQ?R 2?9Uu'j֛MX($=*?{| Vu-pNm___ђ_נLBJ'[/at B2#sN i͈ܮz*z0ZbTXּwF̯+B]$K"\@feS`wDw磻K&BjI.y6'&Ĵ `VA?eUT~A2T{I30jN莸& 1-UzЇ7 `HCƘXcSBeqd(Kv4BiPO5+VB=F@;IwT8NeU78ןD#Œ (C00/钀'QB8xozCq}[#gq]T*3[yg/7?DxT`Ez`{K ,[G`X(D r[ sJYѐM)9, 5[Eu$9e'Z֑7P5{ERnN/$\۱g;ݎO p>2> %AIE= A;_DߛyUa(=A,?|{o/m4Od".U߿Fjx( }\Ր"@r?]~_r۵4iR`t $鬫]udGЋ6i3# wxUL$<9َ=.ܯկb!ysQu4x +$/A8_r6_JdZp`K3Y2R?o#cd&01MD/7% Ĕ%%rbxF!=DcXwf?_Y6}mW-z;-pib*,)IX= rD@nQi#as-T-T!:0r\T:Y#ݵ^0C0 CǽTO;BVS2f_NԒ>/nQ,J[daQ?%T>%%vSQ ~+@DxѨX yt-D- @U;|uJOQt ۂx8bł1gJMy .-5j?,8CR9hgƨ0V1Q,P5-]àE>2Q8p)rgL 2%M8:-z7sݭ toOo@ZrIW*ED-$-5IRg5.zx-/x}3ų--0nRZQe,NۓN<"6-}xQlĒU=Hﶒ0ɉf:h{"M7[uJߍ4w t{-iػM?XX]n4$wC(չ7楽^yi/`^ 1/"=hetK9s9< .L 딜iY5b(z>J<)V!Б7Q C!c3Z:E4{e42'5Z. ?K+P'*ֈ=K㧱\,K2f&ѫǜI6&Ca8gAw2B3?R<޽qn1"=rx;<}x͈QV Z?Nu,9nݭa?H_=UP,X4@0uHw@=_.x#0 i@y#8/ /'oΜћSLN^>kU|OM%TnQf[+aj;m @.@ퟆdlLQxA8|chʼnUég4c(̽!:esSZJ= Qqٲأ,HTkCp8w[l;{H[k!/TbDCs;"0+Jo([kvM)q-@V01(@ѢH@U&Pus?i*p _q]+4o=p[ A/iYгR~-2wx<ˬV$vVMQѹdy>y hƦS(pD&UFQJ nb_&;nx4)xSIhyGˁZ1XZ^0V]Ƈ; -ٿ^IǢgϨo&.ie _Oڙ!V6]ue%j45#j SZ *':uYiU.fjKz9.ؖ?]?;46L]\Xw_YŽ{*A1dy`*)G޴X(T\|%2lo06noӨpkmZd5orL}cى^}s3h"0Շ 5u6އ]4rM_}Eo 4d6'P":JnwwM` hQgpK]rߍ0P",[52jg^Xm4bj]scWX)=Z*fs~W}FGq ]#*WQӦU萗 ed<^_zl?,)S [<(G d)q^tܓċʾ~ vvxנjP-}RY`"VvGlwn,1g#3FSWisĺY"@, ig-.gz1\BkIEc5HlS2"MwS>=sqes $O=/ԁi'{x41\6L(6Uh@p̊A*}yJ*?SQ0XTpx(Wd`ai0p "h^jrq5yn.*u yKML,+ˢ2JB,x懓(-ZŸҭt%ufVG*򨴱x[^?S GAD2KLֲ QW>C1\&ԃZ͘-NaxY[5 1M%A<: 3+biA$ "tRM^Z!0"t˗edǔ6;O^v&p!'a~zjϙ9a # CQdWaH.z6(GpBZUpk|caˢ$|Ac,SoDuե6TwKd`̼Q' ҃`-.pu&Si|9eFZ~N!=NZ6$c>E97ֻYEjΚBUh{fqEx\Nq Sv HUӉzr.8R1fvi BPZ/X=}HPǥ:hLda)PTe{QjxA3sH )OObt CͰG‡>|{='b 1D~w _ ؖy_je @'%8dU/h xsMG$W9X3ҟ#YPhdFg@0V<dW'"ò,HoϘ$W*⋀BQ88A׾7SsZm0I:(M,W;pFS0ޘ-y|l,p=Î<߷AQ|W:cڸ3]P[+ݨ;c֨ܕt(##}ţi yFǃg4lP۩1H#9p!o nUzZH{lWu #ձi{=.7!Ӂ6_t G[gCnPh|(NK|z <-y,i]=IQ|:ӨPe ă:#\NC3k8i:,[qxoN&oup :H[PfQ]!/pnb(~sieQɾ2Xxϋs'n)(t)JH?A ?HBW.? ÆcCQۨZ߾·wO hV46ʜnnpej<",ȟ^wL= 9 ğǓIU|߅;J Ch3cFޘ+ӏc Ch#& +:Vnl`C1I3E2N'2p@@֯ 0UܺZr̐|`>h 8.1 й? oB0( )q*8UoL.Fa:[ *;SZȢ@~$`$m# px'LdWXJQX'т诿Z6"ڷ6E%R(, 2o~H9k#X>3"4i &b{,n9ظFM >>h2YDDTQ'kr죽G[0(Axܗa,ŀ-Pfd-ax7.1vv[.sP-W7? dضy X0'X!(Z1>ٮk%JoadҮdء^T'YCQ㊈ |}&/a c6 p1-,g -44oCi?bY54y9]E7D-m [SG+((WA6?时vw\aڠI8ߋUֈq^oxX#Ag)G_:q_zT\¿hNoȘAM=d?wg4[MzU>h#FśbP ̥FGdR59Z w^#s% A*(sQ,??- IJZ2#*g,lRj)~y >5n?4 wR+^E>AljNn1de[`{3^8:t14~DdD%򒛢̝ɧK is}~Hx}}ۗ#D搪Wx_1]1;+Ce1B+A}}R\c҆"Kp| WrU 9%QaH+Oc]Ueq/|v,ߚiZ/a 2g=+),E'aOci`ɰKvI|sjﵧ$lO7j[YSb`x@+ǕZDMKT 1jNrE^vR.Kˆc 33lBS؅!=k*7jcŃuÃuxc7˒.<:~?T:[lc-x{Q U㣖-խ_==e7^z[rQbܾu`󘙁)& g䷉"+M> fg|uн`Ws݁55ol? !"fP9_bi$QsDSEIvMɴB9˒RB Et(v.:go_\99T*v2~?9ad& H`VEΒ`LؓyNf(L2k̪EqfSgCkI]~8;lǽlW!ňdl5ԉy& iqw` Hr2kKACgDh#ikhZ󷩁ʿ `ہP,J T^7>JGDWg֬!Q7T܊e[c"My[T۲ֶ|/gGhnKrkū!Bg"cOÌICRbj?VI9FQ5e)ݝ\I> lIrW.[ޠs۟r[Ef}mO<_weZW (MH$ AD@eYx/%\с~hӐ)aa6h[ #{7Ѣ0I9Aˤ" ɡ  FcNa,a\F8Š-_'@t߃+VXmE<:E fS1.̝Vgci! ̊)=If%Nȇ uhcBgTjN; Uyy3O]зSGSV԰O&Vc0Iƴqn֘$BQF2890}ʕ6NȚڋ"y#?İͰpۣң;9ez6";Fm3٢ԋQy:D-F uaTJD\?wfELdBj\<^cq`}_lU@MM5 ˾e+^um]u{j[DVS DNndEmsl3zy'%F霏qKk9b~er.}D6v|$~$BAqonsU'X?*BȀP#jyZֺb3 51aV, ;.]PM`rזFi1߯[vJLq]"C2Lvd`( ֩xep3Cs*r_0B#\\f\s\6n WZTG;9PPFE78|?C߻&0J_mp:<_92Yp)v5,o< >۸ 8Ҙ\gQl8pl a&OQgYP~#\g?(*NGa0!o>Fi&vQmfXaoh>^00]#r/S`$Cf [ZUU}^C_s,%=\WB`|:4aQH1K qiEijws!\^sгМz;1Tb~AN _ƊGjY' CRmK0EŊIk=#q#E+[АI‚S<]m_|䜋a\Ѭ}Ѩ8{ xTʊ\:4 e$ɩ7Цqo4),Td0ue/3vq9hlt#X 5zb2(zE5v`g {kex8QA6ҏB6$&JTܧ=#2ݨC }~%RH‹a |i8GA~ְcǵU:1[=cK⸍Rt,p>1i6aD:hD(fX,Kt¸1QvDh@`ppE5@s_Pc= ] p$2%*4TSoQnl*$v*:yE$*"-W QMI23BU=*3)nIix\Zz-0dX f2so\vD@?:L'`4>@6`L(QApٛPQfj/3i/N.'T6y*li rUuSrx[Jx`l$( j?5$H5&Lu\3M܍gGVIPh$*Z*itw$K pR[+}l|A9AO*e3phW0 ^]v~Cv ٮm^(â%eɏ2)wKZJ+X7ۙ BsxBlR):3ŚJHq0") 6m)22^ F2 5(P2((AE)Bt?I^h5Mq?gD ?ʽPRȎbS!懶 eĹ2q5`9(#\eEhX/Pu8xO:"gl|PUb \jˉʟ:}2~<7jv/9h+t-nߣaϛ0q*J#ˆ8L |bl?Cqf0(7+NE>$4]>h}#ךwbu;/ষO={+>2> #| 4vCh _ݡ*GU`"D(l>A\lv3vo~ p&څ,< Єc79#MI⨾hč+˰ÑYG|y_'/\$CzEjWA\~m[D%MV0aAY-u‘p $А!Y? q9l6iooo6^FC?]?p!+u,\BϦS5w;Gk|IWSNv#v}nN$c./^CFGoW$uonEC[{~u XjV,t pMYnn{^TX֫Bh-# L`A9 '//Ib7CLD[aUW8vA#oRT*(DNdŰ1CJ_S0H 'ARRH`pUP 5qSqZBgMQ_k5NMvc "q]Cr2+g?  |b{SG:Qr$ &.*.CebKc!;Ql\f*4U3`0 Bq;sqA78fl_YZ\qNPBEoDeq3^CL#…(#yz'x`&Cp7)ܨ=]_˃⯻zbD>`ڻ$ШفZ`bXX!)U#5r؎ S$_Yx#KQB4f~?ԨZ==YHuא^V^2(IXI/,Yժ і)e9ό%H3Mlg[]7gm ph-\Qgo=@뺯, ]~%g?\ttͦ[L0k\y7\qfZj; ԋ_ -r5 +,e/5/݅2" ͓K|(2sQ!$ܣq~UzH}/}Ǧ& ZiɣbIh%bpV[c%啔v=ѬUSqjR\i:^?7>O=#!mzO42tҍ>=1`@᱌ß^_QNm`DHBBx65^|IdJ<6IuENm`-.N =Xos5t{pON45SQ*Br^\öQ,^Y5\}ⅷκ1iJU'c,522Ni楦Z2bB]JJ|:5Ef/"|3 ?Z\nZDjA# +sq?xc0YmLR0?9nT4¿}uv07qpW&p ;)ó'g?^H%_r䉡c r~4?]/bĦ3_=\+ deqGu^ 8F软zZjo'h_ kH -/YC-ۺ8[Osu'Y.O"Le.IFBI`$b8 4f~(+Q=/8Y tLT(O: L2}-S 1~rhA*`Iуt0]qΞ5rVBA)FCP;UBDW4[))&Wv_ &8_Q}ž@8Od  ]bwы:QL\T@F~aaFQ ߋ}Ұ fd]!Jn mز=*!9Xs(- :w î7=_/N?n7P5h KKX7# ? w+cVHZ9]RQg Te} ز]ӓ=Iq/gZu-Lu_ydp!0?x:=ϠWV ~>~GRmQJ1e)wtՐ 6S}nyK= j;<.!bgE[.6!AYy[{mh[I&K(;QӘ9F&f}P_)k!7ҿsۄWS 1EHM "Tl1p~=|uIqr/FVFS;k˜_&~ʛ8/{gxܥg\2' ھ??<$K ~|zxㅨ8ɋVJ&5}[Mw ~ !@ZgYb=DkT Jqs0)Z$\2E"UH}A6`K7UK_ƗHC>;H|Wᲀ&PO1bUBRUbOŜm/6uA{Ϯԅ BgQ:JL-]<9f!!#p?tqi@<;G(ד쨣k5cVa ]?dky M&0#9D·S Kz(em$@T 8 i$P%lLHAD>(ruR/6hΩ'8p2#g$^kMCrX\b).| bʅJFsq+WHP~NIJZ^[qj /IǪ'ԒJ ''Ȓ#ʅcw~1w`yw90>*9 *_ jA+5 oMjw] dMs6lT3k>y=x5I7 T߫Vnފcbӿ}Ξ}F_EjpH=K2Fx2<㨞~F.FU *u {y0 + AٜmR,hax]cH&}i֨/u=B:.&#Բrb d \ +Q_s: z-!锉FbdqDUKy 1q49Jcv: uj(k[?x*^̍/zxzxFy#o~ҵ7Ckg`4eӁ qM>/ =e8o#α&'ߪzmL#_+sR?t2a+13~+dr &sTC RA"h-/IFQKKCPRJ1ɤ$Rǔ}]f%$貢\*;OׇDu*]4XT KE'ZIċܮbl9$,UT#&xFJ|- հ*)g͹qe7U pvDym,6|pbZ*#GuZ56gu9e%Ufm)%/N~l-)'?s>sܖ 6=O$7Ht>@ZXu6>T  "6xuyǗ_VD_av@-+_`BI{+_Z3vrlp `\lwGk|eO'v2S*ͦj'@`棷? YBZ ^--)/OpE0RM8n۩1,GY):D!«c>Aarّ%L O( P%XfوkG.ƚKFUŊaSN)3op@G|lpeЭA8F%S(2/?(#X]FIF)\7Cd8#4 `b@V`ăpU٦TN4KBA|c4oSF[aq9y=VJ#Ƙ3⪌C\*MK6ˆF9nrYčc6j7&)-BX* ( &(1bsmYZ,Jy]XYw[n]POр f*ͿG{,іOOܚ?7c: C,*իKv:R+ԚZ8Fm@{<K쨗͚LMv1~vC; 4Ut1NYwx/9)g2N0=y", I\H Q\w C-]6 :r-ԚAc(mq^'؇(lG4xQ S 5'+gBIPh \+ HW/D i19G&1֥H 2{YQPey$ԙkl֢8Ap5}ڷ@⣩:q&lEm>;yeGe^7ku:vblJy8GVJdd_v%¦&[Ueo%aL%EɶePʭ()kV4gvߴ]yj s`Ί0~ב+2#iį 9 qr@0-('{}pޡ2G(]E:KK\zeI`4|د3"#tp`9S#FsW۟ufn1-ҁ7P y eI vl,\\G(vMҳpꏒ]k*.Y:љFscF/X] )q,XV\,)*%َ,+wm$1D'nb#Ao @q ]3xJnЯPqR1 :z6w#5>27#R]V `ˢ"WWdHHr^{4[ouW萫8[."xpAPtbJB3꿼;Op8 Ud' ѽ5"#hySm\`p{7 f6N sB׍EIoX/.HH=(\qC!~:24%Ag|Qx?/&UmS5=Cz );'w-H'g/92]ʃ㡑FYV(Sy~u>v?,~p68h ҽ@3 (yHap:!3õ5 }<q7:Èwwl5wu  Xӑ?peV']O.;>İG2 |: "}׋=:ͽyx1{V^)ѯ*.O:dl1d5a*P.c9'Lr$ .ċtHW[her,9uZrH[gEHytHvN ÷_u0lOn |PhQ N{`o{ cxbn'u3P6[]pp< Du[A0h5k^נ5k ߨ-3'g޶/$ẁRB'lepI4v}ɻ'+Ǎi<}OO u;5?Wj/CԽ w9i?"8x5_a5>6 f6Wp>Ȓs! ^JT@z&C RN=GQ8"b(PoP? i+b~q,^{ݨJe*Ȭ8g7+N(⅏"Jqq!zXL}4K9Ƿ"Q8B8#! H>~} /~Cm NqE噡Yb]%"eG/8  V*fd%P6r b6Ίjaߦ,KkYUhVWuש_ŽvtFgUyNI[!1Mʋ24f~Cٟc*{tI|ʇ"/v[0ګYۿsB !')R#.tCZ|! ӝuAbSMH!'ąM?B'jQ6 V~ӄR1m&?`VtJphlSBa%D63: mj_*0Wjv?9$<#I00BM4f46+|?#@^=@}٫ܞ+A׭!p'y g'<ʆ&(畀F !Tv)Z3X@'o_òv(΀1Sl'M(Z1Eeɲ~l>L;S1:tXwdXҙ#-5Y*0`IIr0A;@@qE{q;񳇲xN[!DU=eK$_3}AztIp?+v"Ye6V,Bw~vhx7~j0A3j#|Cʓ$Nds8?>2Mu˃1&?:Z܁FH0:j ?]5M3>EJx ؀ ˋ7g?є<&Ki7ӒNEx?xa9)1[mkY  fgA !VDK%m[%@VXDsVs}5kSe KMF;.iֵJ>zqG F'i4%E@..GИ~O;^̧aD'VCH?qj[&K}b~At (oIz[oin5ty)Ln ۫ 44G@&(i% =n "o@Ŕ4&BWt>l3?LP󌺑 eCk L,v▏q|~xñvq:1tvְ׶|F2^alyޥa7͠nb \߂akhp{!}ާ?$Ù} xf}@|]op_\` 82A"bbZ?%aUKzI$(dv} Xd矘b0/p$9$8;Y\bO2 2=d.wA "XYqhWEXC.(lSV Dr~dYfG6K:fG2[}r(Y[Npu!'\A)1LYTV`dCۨ 1_?&a(D6∹{y\U.ʶSxYHoAW&Zr[%m[;?_U{w%# w-.W9i e=NuokMw+%N'YɨW=VB?n(2Z;*k ~Ƹ)N?/q>)xaWYPhIȟC,s' Țx+J(hMڀ>+*9%>$ ƽE>*; 8Vg&6%QC}#|;͈&tSאT\dmحb[zOOoQ2AKF7?q1?)Suzxy~ߝCH6UC>1dN%/HoeFfjj"e>JpP43xlEutU'!j-oޓZx"5?(V/JVW*υ./6&vߩkJND1¨N "'1F,_f[[6+u (Gc#RXǣ¥ bb/9qdy}WUiwz*#k~,MWE[q ތo$74rb],.ea?kv4Ӫ3u>_%_fc|q֗?]3| #FKPM0A݋7U-Bm$$y$'Ӹ$dM-e)o<(!6E;|z-nyÛ=tt'QÁ~Gn4.p^Hƞ(2BK>Y^i>D @/c9_4l%=[{m`VK᳴,DiDl & '> Dٽ(f6薐ѡ45*|Ps.Y[a0+tMY-Y8S׿ Rضlc$LJdU%P"b&d`y5jX~|"$Xbs27$>bԋhܙ7rԩ؈R;C٨ԁ7j5A6©oGyt4bF$5x:CY?"=@dxU H!uD";TԕwxMb a"Ud9T,7GcfQv(+6k!N7=iО.УS =QލPt w. #;>gN2&p$.e ywRr)xg\=45%bO3nuVxɐJH̖u8 "}-ȶd`Co͂t fM6@iy ~Sv#^WqWmIyXo6;c$};ZS~m6{h94ع8ꜽ=śћ) n*3#d'dAܚjw<|Ĕm>lʹӽ<ۼq4 pq9hcR]YC/0 AVCA]r}̟I#2-O.cld<ޗ:gҫXT2\.$婶Ϟ9EdPʧq왱zMbY'Qs1˷qr*h 4+JE)f[+Q2،qj&pAMުF2lߋ\U;],5 ?9@&<(@ṃP7]W:UUg*(Zx~Q]wPWr||7 >T-KYZJY ݎUcPS10N/nmsvB#F~:Ε[ Hy;d0GlL! = 0<-u1U !]a-hdl AR݃^6G@7jXE!.ThX$on^u8Ym ؀*.2ƿKB+7Ln]Zz:_v^8? Th *ʘ (jdbJ\OaXe:vjqv9 G1!ne#8; O-9=Ym=ƽ78A&^PV>*'H.Q3TVTE uCWaHUi, H/$xq|vs.=;jR T67l 'Tl:nQYQMbOve;! &1DTN#Q! z= Vʆ㈄mJҊP< Z>¾0.vdH@d% $t5p:H8G0X~`!:!N.h D F )tqy~|xJᢳ `q=ȃji8ڴr6}}YzbRW H)~!, o^ >Y)IGqR|R||ٹ7痥8DiY^A/U13%:I 0+_D @ `v*q\Tjp޻lP+PQ N3ء_91e9ߙ  Oaa:A<-^LW53:P^:}/ 8`C*L ~c7/0| 37I`~$z(=`Xq,2$ G˽C&^LNւZqg}0 odeS lMUFeR YB?jdH3IW мvI #xw1cBo>h`iԜi=}^^ρ쪉ܩzI38o_ a8 uMX lK_"qFqEs5qWތɗw:XA?$F1!%"mS^RdDE2J6%h?tTh2"jM0 l3%Z:vo}r!*8`7bY@v, ycAB۸~ aRh@j5dyՇ 0 \xh2CCMɘg`%^Bij$ڷ(N^kFP{H)vɿk;vGUj8u`b9?j{EBP7(Z|L_ Gyr2Z> Oߜ01uGY*˭lu>q9z˒xU&;8vY#~ `evl, (B;Wy(ѓZR|z'l CkxQ]lk J›^W%}q_DQ/LA`#CǶ$$!4>,,ȅ^o ,F gSJ@suqq_NiԑqXӢ]tG;JkCDk5`!al'mmglsH?coTe hև۩ e͞[a/o_ v $F^[a1FX0/̡V^(%@JEZ?찔ݸ';uw?Ni՜?_cX\q #nl7oYW_p ƏUq8 ^rH؟ŀ=7/^ɰ}"0-wB"hg@s!:(bD٪xEL.cf"rmE|81X^T@K;):X?i&:+,jbn,UWpi @u)z붚 輻2m!>wm4B4yp>F<'?xCdROK(ぴYhiw\+d c\徬ԗS_VLC{ĩ:+X5dI{^<MY2usa~ UBFrYFcgO?ض?U Ģ h l(Jqgi_[oK6Ro wFrqn FS6韪ܷuTϺN>(M2v)+m+xʽ"P,Ӿ65,UZ4;a*T .ͦͩ(Jq8,K?Sw{$) K{9"G X'jr @5:q DZəfBH#ڶ&}봱NRM|( [n(p_=f6JrR$v+toXtoΖO.>l违ܺ?YH5?\G 1YS3 x\G8utԆ('nk-IY*8_ \RXGȊ9J"w;$ۡ}c#ﳹ7$nZEƋyʁ#A5,~/)ó<(mD_hϓ^y0" GzoB2%tB\sht+8Q6AjbJDK}!u-V3'BiBE(< Q8bC%:Ĵc/IcxF"[G|A pڇk+#E?E04=(oooo6^FCm1V_QU> fݨ=˻ Ȃr;Ah6UIn:L QVƒV=&b{&c͟"Ȋg%Ŗ']Ӯ9D[c ͠> {R`ށ|;wHwy'/YE:KEw8A,LxQ39.g$m K2 fn@w|@_14Sx9}%Y!Q$|]&6]1"jk#Y r)7%Is D `Z` D ~=P6oX X\0dmh i9( ,_%:)K!qf90*tPX|ۏ5KNYYѡ&g5s)jů.vZugiW#r2(U(C$;^vRC!\܁H d+iٮ+--e-B $BX!t/_ %7( G'.7vOyZRE_9b+| W aaГ!\j9˯fWWY]FK|[)De]~7/f[jhQi6e償r7%$]&# 3?$9=jI#< ~W, ?k"m5G:ΨPF}[caoWfcN˹_8(ۮ}Es{"bLk uZo2g0+6Qkn MoR+d'DQ#` =" zt'اW޴ofin?[N&q䉩Mg5 y+]٣"U5V22=^Ђ!CijM Ml;Dg"JXXrOڣy߸m mP` u-dJEVA%VĵELi ?zW9F"Qlb6Ra.G)[>E vM*iixs@HCOC>x;-75 ޸ZK}pc@J$\wӢdK8TE)?Tiz7mP*U*}yq^/yĢ&j*))/'3ڗѩ< }/_rq`"N jW0N⦂}(AMkj|Dg0$XZ-k#a? U\+"1W(XUd7~!re2j Ann*,+@&bF30_ecox]B]Ѷ 9,hRQQP?fh(e)˰lLl%Fq4=pkH$xH ɛUR`,bR\ET!*bL4ErvO  $y `Vbߜ78q>>.Ǽ/҂~\։z~=s`)wPwВRQ[$ʸZzln+X=> 6?DVȿSҀ-K]A=Njil9c#CMD^Ji0 )&LFͥ3Ꟊ}&3a6׶q;˶!5XWa#I@"5dCF}h@% XY˻rI[}UXh%ZuoյV L*TLVS1(@w j`6D%`LƂ^"zi㥍=r!rx8&Gx -b#o~rЖu^yA-RCA2UVziv6ҝi Le!Z;-LL?5ߑOtWi /*| շ|-|!&Z8` 5i'_$FFJk}?p.O7JSg] 2JYrFIeT+i'ea?oD)g#nr &j)c;\:u$؀T 6|wcwG̉yv\ZV^֜6ÓKf:ʲ'(/[ep/q(5##1$r{Pj`W.g w{ Ĭ얡rc5I3;- /%m{lF܃KQԲ|' [&4Oو](J!|dnl}}QUBTBlF;.cQ~c[g--zTsCͅUZ?%EDVUD$# ewu}i5צO8|X=91m½f^ûP4w)?pB_V~ks 9]fHs_lqdA-}X܈%3яp2>)OVGl㋣󓷗'o$O(  C1 X;+,GDfxF?* ȡ 9m^K˔hqE|&Uta|cKpw팼1]Y jN#g(_zkלԛ(=P_3_,|q| &BQSX Er\4&4>;6m q+~~t#W[jT7?S^՚]{iKN"_~t8- X?<4ԒbE82O\6Qa,}|r>Y %yХ$ 2s *.|"9spk_̌pm5CbG%rܞ&zySդ.% UR9 ʟn%ڂtBIowD)GݖDߋ?%_uq;MsZrN B>=PǬԀbM(Ðb;?CY'3!ɘҞz[KٛC6CNL[dsyq %4 &@P@fq3o1l0\\hB\C_syral G50: 2+-=q|4QV.m%a7hj^NF&5<߯d87LrWg-w0j|F6wǣj7mw/SQxuF(SĢNNjFNPF(ɬ2[ޡE7@E^TIuVro3 ML ʓkL&eJjpU Y׍mye1eXOrn QJ[O/㵠d\j^od'P԰1y5KƱ44SZ7$?ɾr"{flEȲ2b31)"QRZc#/ª/œ7ty7L$ѪUԫT8u/NDA# ~W}2g;M_i`!mH[Ũh*ss 7u>Y5Ul"$tgb p5Rupw"9XALݜm2Hç@yvDTAy&΄#c/~C^3E:SˠthPTYplM蠀DDsRCF" VSE+hߪ5`I 4 VdeC*Zoʪ*I2s$,hPa c i!Y#Mt $q2",1EKfuG@*{*[Oĩ@{*;Dc<ƌ$Q8c&LH8im^0+{D1B)nf}J#Y8i>xPݑ6[-7q5Kjت50-/7%eт5w5rA1t?q\> r05HK^+xx>cGqcs_Z2?/ŀyGvT|jF]@2H1P#SUٵB/,, CM%Zr8a8v|ۋKT`ܛOKe [0`)1BW|$@A5)(Sh^Fh.J/5ҮZGBS^mCȖÂH7eG_rG؊lpVaːev"&EJaEۋ }aIB#dpG|nXO 9oq[0އNҬ }b/oXـ{1@$6W[fjZSz8#B8kTkV-|Og4%b:UF1:ʵ%P D 5-Ed$4t>49o; j);r ؜=dVSP=*឵Q~">qOB$mxPLZ?.C?Uύzcth㶜k| '.WT0I}' ,Ѱki30n<( 0`ؗ(K;qi5$#Dzo[Ԁ6 aB‡Tp{mW# r Q !a,H9G-: UcӷȜ:eхUwdD@d43-wM}l#Iy &Sr<pr*5u-|W$O=}|{Ovb=4}:V뭆U*S@ҝv(݄:ru>o|]$+]2cp dp>Áݤ鋝IC"t ))?^ߎލ?=]:nUuwjWvJ5|97yMB+z[࿟9- W v?<֗izVL敪օ72B*|PDmcd.n?`6P'q؍^Ҵt)+ Kͧ.%qvu4ĜIDfgj\xM!'K.$W%FM (hZ0dy̸ NР~ Zm(*|_>cz)0do 0(TA}2ͪ,- \/YsU$G\JyazdAKȻCVmYZ*%Yle%T6>F[[*ZY%C`=Ѳb4pA B5m Lä ;#Dn6Z$M~+tf l` EwHXmCbDLpN=e20Mc% Y]5=̨5YvQ'4PݴሓgSg4l8&Z8vq=ᄏ/:[#cV73: \ ֬9J_?wOw?H? /8G]F(o6=i}N-Hyh6꣔[sTT'`uN"xIh\@~ZNe_*Sn)D#GKpW^ \ Ө~̎fhqƵ+0Ou]cKSE%%l?_H2Z 'wڹ`Ʈt,= > V!000C1i=T@$U:N8bk@-7w rk8ddõ3\ǩQ! ҍ pE r=qYH՟=\J vEl#f5|e1jxYY*B>F9 Siv;OwD6>^oQֱY)cU*ֱMY`hKT`q?% R`Se+=\}mޑ((!{šn"ZkZ$UUmq 5'@*x;߯KGfљ/0M3iQnLysDPކx= K~-f*0\A{ "V/u7m< Lh0hs_zX[V[;#t>V-\_aXӨo߾?YhH_Fs P3$@'/f+VƂ† -+ɝ8|{ױϽ<]HZ#K7dE (*{M(Hu`jЬM̍4i&4.ٝnK4G(6zPkctX*2<(orO81R(\` $*iVIhTt }SDuRsXF_E;VƹnFB7)lD0a V^#IM=kX9Z-bJB `/6+uji쿖dv^= hKyÿ){$ٱ,zKHB|+@CP.{.iը}ư#.c 7$S6™s`xKF[j>Zq(O1ȏpN=hٯ FKU71.lJX֫~csK23I7 7DR9Q/,] :EO#edppǷD zJ+Ͷ㤑p-oxb!FW0^Veiy1ę̳ٯb*VC 1>P20ijpeܴ։Y3SwB&⨕&鏪h2 r^Œr(\8ȩT\`KyّzpSePCۃ{Dx( :Z PHpBU-U^߸tQ@j8kdY,8qd((U:Ij\"{MGK Jm,p$]1|k 1T d.܏en@-%VG(gl\{fePvx.CMZ1EXpGBDHjI[EBbvI!y* ?Ćʭ%ŁF,-8_{2\R|'vۭc/%sjNeE ҪddG7Cm,9$|A-}7S2a*T˘6񅪀0vlU^[E =-9>h7t JF_꠾QvāC 7 tpс#X3+8LCaYꎰkUaxz9 ɚ`LN 4?A'@ =Fa4ωkG_RK4FdSPАg#|hGQO<%G HwW&Q.g`O2\ r ZQȳe1 haCdg=I4H1:i"Q>K|fŖ,Ecoܩ?f?%Y*t(#K,+؟U-q(`U)#%ED:"ueelT 0I.Ӥ .+:&7ؽU\gJGw\VifѝčK9ze|^]UHvUjwQw a HH"?-/ NU,ZL[Hޖ{$ B,upܗd(*_YEad|`_[@*&,O7]ab+7(@#F!4_cـ/Hn1L*?5_^Evq`8]@E9l}GFVC.,KLu7nmO%bӋ*ԷfzNָҞ:eBFaT2`q(}Q,OD^AQkp(#S# .Tx˹X{%Þ/E TFc#5R#%Ͼ܅"qzw_e`u=f&м=?_b|~oz\ߝcNC7Z7jkW [@! j";!ޕɗ".4aa>aт6EpC@zZGih+ѸưxJQB  (9gJd!4R($H!GcHM]RO KXSY_eQN;o'GHvn CIѦQbQ[7ШNzkE֓+*¹ƈ$ٛc)U iiʁF{pyWS_IHb#3EC~MZK`N[уTD"%k] `GEdך pmNEڍLHPt]Uׇ?V.{ 4-!Ӕē_߼yYÈ8}Bu􇹆]eě$ 2hu$B%._\Klbt%xtT\ ;4!26s5F w kd%OB#b ڈu{8YK5r ƈ͡.MȚ1P3 :40=)SXZ8(t@M s#a EimīV_!M YWs4dv2wFVF4* #SDF@:öSJBq;E6Z91-y)2k,xEzz'aV(; -+J6 %C$6oE=)¡56"*OVttE/Kz\ɿɲGb:ډaŃ?5Q5tXM,,c =x{rլt9<ڷsZgo8m<dnԞ2.=_=}e,yЖ@Ÿś9 n1Oo7 {R޳ bctžc>&*>U-Oj^ YJ,J`$#!KRWL@\^P⽚8C\l:ffJ̖{shtzFI}:Czs4|A?_\G ^@6 *eCV|,WDV]n/it-6_pyQXrZ\m0fZN=w덥K 80q=;-l%^"j)_B }_tt?25nbL{/ hj1\9y&k?G 1ȩV m%c"\:ɘ=, uaGwV/]!S&sl%5hIq_Nխk(4RDHXUO})- (ZʨK+B)HШ@֨7J@ cu[l$Qg tV2uDgjm$Px$^/kLGŎa%ۋ+O/:o;ߜ #1%%vKyl6%).lE)Ù*vuS؋E3HM/aL K*3gMQGzWҦu宅k?B7oa ^ߟkJ81_ù !y@|pw~Z^eCYrK{Yޅ)?J>;"nLk}`v\TO<{ՊhK-[5Tk"fPi8vOFQ1&906vJdEUS0d2ZQWI|`sX䉮 O@rXB-~Mڏ-e1+UlL O "Z;@RrO?%#VL84+cWd!= 6FS, ͛1" ;PPu)qKcf[Jj}ܵn夝Z-n-A ޜOy6=ûGI*;*Y95ۥ#,b^Sre'nVʗSbM e-&i"Q'ۨ!HNE4 %(j<қO&}oƎWð D q!G ZLH 'cY4 1YkeݛN;z~ *yR"r0҇?#Y}pF8, ILb5F\g&FCD-( Ȼ6j#& @˪ bPfn2siǒe|M0𧳓K7m95ATK1$&,ft ;by AӳNft^ gʇXe,F"Gx6'PY{xC߉z$BmFgᠺ]c͟: QrGo9ox bcU=B+NDV;D†24IL,DYM3RM; yt]gtG2r뵏Aj.,Z&of+l5fAD%Xb~c9B* ""=ɾ$LQQڗ֕(g`T1hm_20|SOb9U)o\"t5A]RD}xG(6{Siz99k:l"F(UIDQwde"t_|.=\u ǟL;#sddV!vB3_, dBufxM5^,2LƆ\ޗqJP%gs7prT_. RJY4$G;:{#XHD|ּU:}ÿ+с}T؀?EUx*o\vjcv.^"q"u^n5H_Z^˳0hl^9雟.$5nkT́ V wHN,p T=ya s'1Aj3.7]])au%Ț~>z^%*×/HZ[^bbYdSwM%~Wi+9nԾZ?&?ްx$E$&MV' (Y 2?/B* 'O L8MÒdk0_Pj'Nf)FA#EYH'#sٔdz80og_tXgy9 h @%Na-ƒXap Y+՗!=TQZ^2Aн~~(p!8V“ʩ6DTBU!L%BphK"~(&G4iU@ I@]z&Yցpx|(tTbCrQ*kGR'b&nz׈Iÿ8V.W>udDIbC% լDP9MtRaCЛOU/S4;.kJה I+6E|tI!4+K5NI) i-FeQ]S~?F 59 L4UPL">7Jqo샛4)"hC.t 0!8fY_gR[V FIiVHHVT.n7c~L<3 f7"iloW՞flbTW U tO2d?)ZdM-)~G? nf *A% O{)15YZˡQCȰUM:,0e}"e^ZjL~{S/jt>Ӆ#,mKzլUܙ|HY~A(QL=Kn POd H*9YQJS{cf!eOԠuB.3JʴHͰO+1Bj+H`N6hg宐E:ECLIyGa)*2`aǗsVv|dj+YgFoowڑCG'.&JeMdy&m`a GJQ !aĽBScFlT v2 ވ$ z-ja9zMV$#9S0lMPڝz}=!1ant>GnoXz y1szxㅨ,J(N9:T*=HR9;8 i%}8Ahl ^_R:zW_r[? F RXk+G+ 2 In !+MC+ DNߡ.X_}iMSy?$7 @2@ȧH}գgL Afl :W!&mHcRG?JVxEbC-̳ӹ4GIaa.Z<휝_wA2q \TWp2v )8G:LOy^J=J$jI@ߣ?N-,J44aHmJbЦ PzRuF/G)N1]^r/ȡrLcxoQ^t'7o^7L*M…&pc${# p9i[E;6%Lj;}vgtX:qG'/$q@\K}I<ޓP 3~ȫ3Y᜴Aa8, msE2æ鈥e>Betvind,FLhS[hdϨ0:2/co#XPy jSxeg8}++:JKF/^l㘻6\_A$V#o֫{z^B`{ٍ%SzfRKi]'i-'Z=.l#gѽhLcʃ'De6ET7aYLq*{*s zIKEi{ط(ҙJ58ax|=T_pq}l|N[\K1vرզF""PJΓ }Jb X #U%Aɓ'%MwJl k5 da&ɁX 9$^:OUa ,b\ieYU%Ch j4Ȩ6 zW]Ű<,Wȵ|BWJt ]e>eݳ\D DfD5PIG/޾iڴ5bU,16lڈJ 4Gʲ|"uΑraEN=Ӓ #|.imSdh>u~81Š5Z~6GY3&$fGݒ;g J_7vaؘ?AjrюQ>|i"L"X?-΁P>cpvh ^U: t@g4ts"̍)'}U3Ǭ@"")jKd\6J 뎹QK^-8ane;q*b4b3H?[̽Rs| ,TrrqD#G.L_@ԥPU1}~`ޓ $NepRR]㔦Ep=v'7O2@TJ7!RMҖZO ώGv*9P0z' FYBo͇|YK/|WݍhAn5߽gSX \TW-ptٛg|bvXCsd|7oOĊME v jgQQݪC.nSP~‚~ f,Yp'] dcwUQd?Y lΆo<x0W{#)vl`YӋn%6&S_!MSV '%pvzf `YpffEjrX2E20%&4~UU}l=nO S'5 TPI>2.YzRi!f׬0WNjᥨ2u}/rpիמIW#1r2 kf28-5.y"-((,fQUh;^z&DD.C BԙhNZgJE ǷPL,cqqdk?l4xV|r Qču>( Th4\I:hvrmgR%_ju"#=#]CoI,a_u#ɮH]zCgJTwp#M3Z&WL,.g' WZ!k4 N \7ك{$E p>p&6( Q}Uj',z*]KviL޻GѦ(O&sҝYu29\*q@3Av G(e!ڞ~(kjC;_NGѵ-F}m{J z z~z,$/8O(F}2p MaEz [_uǖ Iu:&1, *–Gm(a`KsۦY;$0'dcۡIm^_:]xKJ[^מ'?=J$.֐%ovdJ6#.0IA3}(VI`s8lhM*lpCP"}RѢDt*XL]^@ѣ=:#\5o^&sT$S :1EO71ol`kFQ$9]K*Hs@?霎VlR!Ue OZqƘ#QZB4y6w#;qG7-+3=)@ݰS޷"h)[geGuT[ e.TA͕4)^f=5gFi" -SKEQ5 Wx8ݘnn :Wu04Pt­,f;˰B4q%s4R$pIrӑ$D>GQ?H Q$+/ E$,O&$ nxVY!#0:?@ki=a$|yl?5a*ޭ}0:|Jb,~enڲ.oGPH<֦||6m'g^yLnхn>C !Cƀ" |fN& Eo>R>\gN[yU8X,[5F")7.^%(aw|?] 3R\I5ƽ*qiI8MT([)K+i,MGoLx:0o~ NSzx2}%EGh)hEvKo9r@װv(֭8IMƕf<.Y2[}dU܌GhTUkZh bgqI"~ܴڹ'?q'@D-m:꬚ \x0爄 I|eO*68Vߜ1!AiHEL"Dw>&skpBBϬk6뵿!Iyը:[gcʊUIqYݘ(FPh C.b՗w:٧.lLa2S-4`#Z-&j  ud@,IT:F-~m{؛w=^sU`C gv1 嚁@[tN~{i*@Un2-*vL观+N8W&.O;q,2v v]zU>|ڡ2 GO-QhGgd@x'̯J,3EFetyRaH7q2u.9mF@>[yr:²iI:ռٺ/9IR4=#ͦIu-_;yw_iO/`5?zIʞ-pܬ6oSNcgjm6Brϸ;*5BòL5- 84k9t[|y0֤O?7'z^oC&%?b| ]>祱g U$ z8FwQA<ZSQפ-a>QdA5/yl.8! ]wiOrqUc ^tCfk7/mGs$p\aݭdMB^lj:l8c]z YGI*g@=Rd`JJÍ.2mzxwKN^CxqeeU)&Gs,-o U3>l)$bKF8|ZOV1o Zձxmp? 34H^Ql+2yz%~+LglFchB^G-09_`L\{!;VGB&l!4&&xثj}:ƧPx@CBtm71_Y09?LF_M giBj}0*u,&VL7丯fuؔŷښAbE mX {jg4YWuB<{hp!3Xhu 3X9kfUu"Ip8fjn)V|޾QݙdaF?u`PpW%b̂ "`8rx֏FJ_yvF5UlۡI ZͶM#37TmMPS| dE/fyC3Z8R .m18~ۢQG>٩TWƔח I$<(U #sקT#Fΐ"kRds7oKjJVS/Ic-X>A>m,^k?,$|ߜhV\iؔSR0dlR7ClH+H񭨉[ 0'gStdDRG x.jLkD g ,W1Ѳm,S;S'g7s#WG؝Sht4L* >Oߢ=NHmӠ$lAyk%~b̫E|Vq8KkTzcLnlN5B!_ h<C5CZ b~a#L &wF{[Mk;|r6H ,w0$ ZRUp l-L`>4PkI)/i%`cKLM聀?(TC`F~Uσ3or;-Ɵ#Er)tb^՛sw4'P-:;Br:ΰM ih:O1Z#&6G!sejTmGx [z%=Vk]&+vts1աASrh@KGlLTʨ}$FNsĒ|I 1Ǯ|G2v?A]ק7gvσ!rZP1$yC07#ɉ3`".b"j[%c--n|4Ā3a.g4-'@C뗗C] j6[Lp>CLlW- rg R{22Vm+<4ޛ9iLς:((K ݀:8^P=( /z V+gUs;Fs ] l2n8] c.GDGOCz E\19Q=B?Ň x) |w)CzxC1,8sb8n?"49fWhY mSѲhǫ.sDxqs)L(z~k-.@O_\AP> şc jzG֡rFlLV0/j @«Cnl OximD(3ɝ@dA'HVKhzVWU:4@;R%_@qK#8/ƗKψSE_*[x{i9UUan@ hX(89l}& =wDB;gJZk%dT5b)xPEᅹ'xtyive%`9fϢd?^R~R8[NmFx'cE1EF>nGN6i"# Z%B|y3/|lr//cY>(̮,q6Lџ.KU٩n7U? nݽزg u͟j[bZʀ"0'lpH[w}9lmTIĜdP϶["tx(o.%W&K,,M. ,Qz1Hb\={!W4X64[oh!E39[ķX=eۘxa,-ZIι^mS- EUI&AXQqgG/&.1%^I&V9bkPUĴ@D =TS|jS=b Maʌ5(͵ #P%KeٗVrl5?jǧ{ʝܛL.m`Z.'6@eۦ}bo& Bh54ק3_JnJ wtif.Y0cqttyI~vAqZ{ʝfCk1~Gd&; C\P+e-"Z*HYGLwh7h[3d59weTLW=|qco7%p!EzQ/numl:}6Z=o4قs:M?T˫oRџ^/M'pk+Nԅ^P< %c8 Qo*jǡ}w6SRRrPx|pn4:NQo 'ѵb`z^I_~ :) {Ux ވN*w+&y:;[ z T)W!yV.$ug卬=.mcF1ْQШ_IOx8ڠ34>y%)] ґC0nuՉK0Q?ӬF[e w\B*Yᢇz}C+*Y:)!R/ ~c=jbF\͙J֓RE+%C93c2ӮEPU0wm-kD\"ѣf}ʲ;΀[ޛfg4.j~4r+_lӶ')Zl.7Nj1Nަ66HRNŲB"be=ҷkNP郱PO CBX c4C&*hUvj]3ᄋRE SL \jY6]@Z_b /轟@9xlO دQ7~TOFtXP1 @ԯFc'}th5/b¥jDwb Q|uSTHbW[%8C4[}ao\'e!u &f}G{:NTY\g8w6QŲV3 e5f3[fKd$K|j9:!;3ǽ;'پL=tW*dVQN=+,pє('.I~5sR N?>=q`xxb2oRҭSڃ*ډIHs,ࡼ܋UPcqɟc1G EYٚ1>;RHфOtYkQOvcgonS|ą^@>Fӎ78^ʚ%sюtYa~Bִp}}g!b"*rk *qrW_n|~Ӗ)6UI*4\L;gu"SNtM +_vHg@ox7u!Ր| >?BLg0tt!Q>g؅477;"%7V7#jAM3Yc9!KS1;KsˠǼ2髰Ȧ[+bMG=8ug5FcN=9`@Z"(?AXӳt+S9|to@b=&"eeܠ=b 5S!+1 c(8h>o)govMo4D1Na#k[CͨXizf:{،p1[q.F ! ,i,FU D;w4ZԚ|Ts%ݍųU ۣ_wP3ws u1ilڛׇG7}5ihlӞ;?Lm4rfiwNӁtN{/gxGOz硡BQ=M>?ݟFh{Vx6f X;kڳT'T5NC9NZc(;rp>tu+ 'sێn+E3깜$)f7 v@ ÂT./lGEZ?xm;F9БN{{"6Rd~0s(g^hә0  Jw ӯ֦bҧD_Bz~bpqzXq YF/>z1`&'̎1`DQsfLڵf^C"nQI5"Ľ3./"n,tc. NzaV;nDeE >jtn^ fW9t7Gdu;7;(<0tk 4g.RH )/0AQ 1zd52qG=wa)V)_ro~:M3K#2t: 3Z`2NIDyA6f!`Y!5ڄ&gm~ #΃st-c }ma#,yTG0Nl.U?]<Ԯh"m1](Gsi7vmV3άۮכ;hVVj%ziviN{:1lg#y` ;g"6*Ayj²x#(C $zd• JhgMl_mt"BIv~.҉l+2DA_+cdUb^gЛY6xB==Dԉ6io3hlsXz8mBJT]b\q-ݽȟsN֣uk D)Gx*סS/q~>MjkpRW[n| H`ߝ.z1&ϗ~q LσA?+2:+مKAozV6F.LcsHAxNшEe:Ѿr[8ۋR~3MAnDkiZYXl˰(>GcѲu\ܗrG2ݽ8!}Ls襹K4zk>E?v6Yo9M8tM*w?jloey;Z:/D=,H|!i9k\FלB~s tNz#xrRxi \KWvy\t]a^ESFu 1"0ae~^ hgtnLIpȋ)SV43$G6q/rRR:xwN'HÄ6 :]Ҷ8g0İ QQQʬd:ݣyY)-f)_ۦ.bg _ ^*N ֨𨭬bJ}$-ܫMʘlMtUɗN!g@SezS դ _o 3_h[hr^jZٌoϱ9HJ`q!QF(FqGxg/GcN@FlK6Fs3o_;.V %~8ֆd5B]Ʈ5'E7YLP ,RC |Sq=K._X-wluffw5Xߟ |ב^^oF,)ISGafc>ml4ζ {O<<+ j>!Z+VTc~Ot`O}EVv{ >RI<fJ#\ KuY{kYfw{y7.fCѼ*oZ #ϣ5 8ĝ˘wmS8$iV8qVpuǩ B E:14ַ߯ߗ|Ўg`uFԘ` S|Q*߲ t6DUc2Tg2Q!|k^^c]>Ʉmi( |u@YM;wgeE:qNPioZ˭dn鑋s9AA'CM&5ݹd+߲*Sreaichwo6~btڼC٩HCuM쓋s›Y`,>s8;d|A|$ B !DWž[ g Bp; 5{^DڇGtAI+i] |͙@QbڃI qYS9VoIV%x Usuc̆)po/CE; sf-8-O@qęhom=E#PawUxec=p)EBt+R),'ZZUyZ*_YE eOsW83_.6 b^\(&df]ufk_2BFZP-~o~#Ȱ$T&{#ui5*.jfdUȗ%?CRZy7A5oI?)tVemQJ>+Kysa4oB Zbi#FNBiFeHΈ𧋣ueq4 mgb$owf+F㥑02r)G0[YhažT(m%Nv3^}u+KV[7Qoi8k:gY<* iS?3_7̗7:A u2.(=ufF؁W;q`4>"KCwBR)QN?Q8q(l(ld QʩuU]GQkɊ6eZfLTI/HƖzZ:ٜ.4H:ڬ=`O6{bDfp6c!¼9"7Ozl8<#9kJ=].,/wX5&Wm; M䤜c'+vY{PKjH%7%0@ `x 0Xa$!07!edc߿_ًJz:nw'm_7Ro٥گgdLܢz2>?FQ<iƬ 3Gň%-S ,B)X1,|2 @U-2NnU~g?!:x Ry''oOz,Q*.2 hI(8sG-ϡg2_8/n[^.w2eK#j | KM~CS{7>ɓuھA#PP TGRdmyo&V7wbQ'qL(Uɻx[ӔyFh&4; o,:0ه.j`(@i@p&+FW!+,oSޥPrzF^}#mmCflQEһeThtFjm@;[ x& G}k+f؎ k2p.ivzSs1vx4oKs9ȯ\rq)׷DoX%zd~ S2E< EGc`d3@5;/?zt̃􋜁ot:;G@Fk62+UVK~qwe2`' -XT,;ծn7[Ҙ jeMN:Cr8ф'i" (y[wf4+J~&8tμ-Yn l"#i3S4ryoM 4t30cnv|~ M92Л$$ I'afB.6h;;C}vU}C?Á|o9!#M U 7Ip=!-2]JB|CWٺK{JuX}r۟E0[NlufZ ovxFv'b &lףe5un[#beFAC9ҁ ǎELd+> DYkU4PD5q*iڱBde.½J\Džhw ;Pig0u,ȉ?mu1O*aY$l SOn~A>-RwOu8/^i9/HC@]^L0h k خ ]hwl.Ԩ׏kһё3N R ¬DwFFusn 5ѩ1ЉEX d*D&j=Jut+ЊtY fٲSs-Ƅ85@8>RUBq=ͣҕb}GsBP zOBa;SLR'aVFY+Kh"k(0#/C^hމh`Ѷa{~9#jXNg^c =*S~2*=z֧f'ӑZP( /pf d޺Ŝ<>|h̟ρݰt:BLlrr=ŸH^muYk{wpYF\z* |i#2B ԝi³b qc+( 2 ݐ1ȇ+&FcٰzaeCt2bGPQbo])NW*az}bڸV_`d9c7>I2fdW5)Oc)~UE)61lkm-X^]eފmM\WcDֲEFhY SJ{R/e }>e|6_S2ñc c$Iц)VXbl8sFbЪ84);)moqf&0FEnu1tM1b81C 7 !T-mi Bc10M{.3'`AydjMR+Rf}԰ZĘފ4B-Ǒ6: d-l-(Q%Sx"f?2yKLt.4R#f'ӏ$՗t`y$”$Ŝ"JlDEge gm@d $_&㋢:Ňq(ࢶBB.z*Q\EEVX[ݦ`%K$_Q :A#.rHhQAu 0ԀI =|oD+͂Чk1sE#B݂= ܫeX:* aN`C tfuǩpђZmT8uTcAVnVyZ-싅)4c3~7 Ac݌73 |ݵn SΣngt6=A^2^?@Yj;wBzkм5/p .jXy7!E~Xł v;bIp}qSЪ.V!Xxpi7[}I[nć3/t +mdWZ=p` $1Կ58{&/Vm +H ?w!75SܙƢhl)@4q4"̛wo_^9~$qAoE tW| \\T| sͺ@ …ۿ.,)UҐ"hG o|HL}>A} %zG%f$N)3eZstX,+W+,Qɤ x,Y>y.Ũd)-Xh4UXΚ' FY쩪>fL/P2Hrޟg4{v_L[mn6;MoksܸuѣÒcSn["8A 'm|"Ig$tW{8X5!nu[v DKkkj-hrܬ@mޏr!YSAuŸ{ +BÀ)EmRU9 o\gN)cpG|]qxSCI66) P}3}c Ga2%(>gAHZdb?L<\aۅGtOl9yž4֝FMmG!}[?^Zr:΃50D7ǿXmv2:t?;_y6;z#)?\? ȶ?jq 6_MMWˋHp69RZCThfb6lOMP,:R02h5=, 0BocҮߞTʢQ橲 k}xxGvdyܫGtLs\(Ed D$/%hѠYDҢsJˑxF\hT5(<$4bxCo@`>@4vW~ūK"q}M i?떞Ǜzk+U&r_BkpäEc_Zv!_&}(p1H[ *K{m-Z`7 DY(iF(˔8-ml~o7+DX-I1.D9ڻ]Vxkb'{?|P?787B[`4EvJꨏ{HOxS9#!zNtftDV$_P7^N.Bgn|,$50 5*fr:) t=A| -m6߸ϲBg:Bm-֏1TPxWw\HTHtkm<i_Ⱦ/oiUl6_a-_,BH[FCWK>db7 ٠W (pC2;E3 nTCr7†'J_Q-#r&U6Š/8ؖc(:kLG|b_ASQi'@mI JVv.WNb[kV䢼Ix-&LNj=8&ٜ8nw,S-dHZ"NJ O9&o:Mt6}?ڇ(U/3olt;8NNwXa<6@6? 1Hzz@WwN*obԠ 1Oi0ksـ8jЎ* iu ̍zU[z6)b[ynk(0՜fVNgSp@ftAiwZzoS6QQNV%/`^wrz|kyXn]jW'ek 4QF W_Kͪkxh!n/aUnDt|wyɨInq ^o;⍼0[㿴Lfۍo翯,[VTn,C DD#@鮮ܙOWtHì2 fImx < رEu[:C ) yae`Dd>cF5@EIalb 'U>Beq>UB `ߜ` bFeP+yj*:{Rr%S龸c4d?8? `K^qnE_|*VlϞ `'C4 Hx/ :Z0xCNWu% HCQ+N5nElb7Ic.c`vYm{kNη_,#=HC۰&6-[~3AURBQPvwy=*Y0IH([6N(~!I/q[-+ߵ7%4:\=@ffK#\?Xf0LBby~i/ݬR ijZPvw<x+hJbs&iAL@lQj#TJAu:I8`2oL #zhхe9wd6q *"iJNN[2˻,W.X$D v^#鵠6N4vnQbfֱ|w6-N?v5^,pR#:-kq1VfD88<3DnD{>s}t$t= >̛7YbAbZ13T8 VWܺ؝_ xN6:xY<@"V)Nu4a;[cmȆ?c[hjf[9ZkM's6SqAIȘ ˙CG 7Y^wYg>v[>VrX䏘gk1Ot6xs]<J~pkr vܼ4<]`Nfuօc 4j@-&w0%K: ? ׁwo٪S{vYٌ߯\RM@Hs)ՇsbJPV%&p% q ;d&G%?$ZDbtQO@;)D {W~K%R:2E]%{ժ1m|ݵ\R 1ԟk9zAC.䷹3j>đ~SvW ;"o( }CVÁkHUЋ7Qoއ`䡂)g )b UBЛsƟ:0腞9,+a qg\OZw67yPdP`N_B:!zPԼO|2npPCm J+U^u@qbS*I5'@XǙV튕Ci]W aM!=ިY<c`+|+St(%>ʞ$0UlujJ? C X8 FW^O^6 Bei+^+زl–Yt V5qZ<")H,jV!cCd|yxtx+75C]w?"l1-<[^F©:4.#;Q1;yR)"F@Cs82#LfQX 9p!3FU&+WBmE<0Lc8I.2}~=Op[d8fԑrDdمZVkEץLdX`Jn!qPؐL>x ϣ[*xr, f"%n7ȅ1[g '"9Q1 !)[[ڮpw%+8I2lc[]ȟ"ͭ+ąI1ՖXT__vXl%S|x+E[fU-L(GGfvW>2}3h+T]FHInx-KZh^cV:Nrˎq*Nqktd***U.BZQaȝ9(%5"vwm5%ٔ{sN-^_Ј$c&ma;9I|ǡΊKLMWV2ͭvΚjA\w)~g,S<(ziz;WZ.Ra<x q' obkH9F-[k4ϻ}uݫi:ƹ".ĽrohXsr^=b+8poi[RGEIZ Fʋn0(F% eSv?c a#$D]no0;O<:4IaQOW/$A}qvȍ._ ]V2`Y77. r'zdӫ8f{lln޺ݝvn gsZuFiae?AT"%2+"G|M)|Q (a‹ -i0s R=y呩+@=08ֶh2Q(&+ΘVՒt2 xjO1x'il7ZFp~,:HtzE;'P=13vI  Ѽş M@ Fq JE):6 GXQ)tۓSCE_@, _/bRn]L-ؐ(Ǵ|gp|AGһݣ`"PռlKC(] k]&~g'"gBArc5cBTtH`G: "0Fɘs(bX x4-]pj /6QsUYƪY__[!QY sx|aFx~:nDo߃jvn[!XX{Ѵ}TŤL6a1" lHbvd,eӀTa89ZARHD̂2`zm8ݪ;ͳF^d{loG OfIÓ$R6ENTAwˋHKbQ3b ~#R;-7и `l*blq\Vi۹EjA~xlOdYJ#F t:s-@̷/XJbpn4zLYfu"8RL`B՟j`'xVO-BN8 鴐 Zz" t$ V0拜o(L97T2W|\Z8|S_o9go:Zk&\j{xOZPPp霊a0)0\?0#Z\A#x %ag J5UJvO|ܼͤ݀"rdӌ(Sٕ3Tbk"DZ½B1 /U^(qx'+sʱ) LF h Lʸ5\hQ[۔DԬF dzE@5\Pb%&YUBfn7 c zT6## :-v}&&:T@0pX)>dPpsX\@#D2\q{ ,ec<<.Dp%Q; \Iz3;]!濒$i! YlW>W^{$ӂ {\!飧V؍ 3b@&Emt%}_[ +,o Tvg:OW|9 *菧Ų99/~MX;K԰{NU ZL|z7aT_N c\4JXf9JaS=+%pc:E%MnSy%P fZemo4V\n{~0v1ś4DkbhZww']nueQhih Ŗ$)sa-NLmޕmuR!G?/#tX6U֖;\$u_\$wJmT3YHNKN¶[-)}$taKhKTk?~-n9;m?v|gLiV<f3([ ¼zzκCl`zsd( |[L΄ O!TpeQ-`l nq- -T6Qcc@dUc?bh,}q9Ej9#֕&,_Xmݖ&6|Fw2ZaG8y5x=L+.G-7R7wLj4~˝l&Q`CQ!b|ycF!~bj1$Dkdߠ `JZbY>AЂuֺi3,1Ŋ@|]g:p튱ŚZ̼l#X'\pE7dܤii 'l;?qGBo*0-/FL{ۭx 94(O'w'fϕkZ+Q܍9litZ^R-f#樢Gx]Ppze|=wYDW"i&Vr[VpLv@&?c[?сɣSnX͍dOeYrp:mC${XBc[a(im2TPB\k/s,em@@&9ؽQ#dbl\ gEF$Y<Өa3),7mpRA濕mV cWA2LgEzqRcƒ1C8 _^=pE, b sa^Tn9hD* ۋWked|pj- md-J { MߐHsx;L,ȗmU-۝Z{c~ WJ 岎:t~ P<>8㷥Oe ߐ,i{O]y.k3EJXV1rʰMM`ۊʰmK+F[ %ZRQݕPfQ)7ͭ%:&ޗbb zOZ=4ρ967qfUFkir(C֡>H~--[(,)*)p=HUgvK,kiXTӢ-r)ϓӢ*rZN,R .@^Vmz _sx~Q_weA0b708zVjKdr[L#6SI(X$Hrw_QGwvg;c_KNg{:(sgPŌb/V"Hj/1`r Efplq~~8C,<  E]L3 dX_؀U>/sX]6;=?*1FKַ{A3m5IV6NO(ryWHLaZ¯܈9 6A)镻cǽx^sysIO~ދj jja*D8PųGÚAA} g`k=WEp_"n8H [ /u@nU[0w,}N#81t</5-{pFx? 9feQfAOe% xE:{Fh† }Ѡ'oO;mk8.9BXi3p7,rԴHOS Jy5˭;à qϧ- /5s\0;;GA=C3A]~%v񄤺kMVK|Ӝa'I?:jZ%Cv2W3/!wC 5QHyUoI1%7E?Y^vUOtEsv ks<~ ~A(גdcHM@[;C1d[@%{ZCI&XZRڴ*CbSO?&D7U)AUAPMix!͐%6D^PfWo*YwDh<*Zm̓s|ZWoߟ_/ O5XޕҢ4*ˡeXvHE98~ ?;|}x+<<=:89Q/}nװ={A]bdː goPpb1$XwcL4u8tT*IR"̂,>ffs`^U3U_b1¤׫N=Tکw,P9%ZzsLf_50Dz/Bw;T| Y,χld9 vT8C\\Mgx^>a@7HFk] UDl$,uٯJA! uJ B).:`xv̛2F%(4[,)Sz0=>c)o&=OxüAB1jY=J<|ʜTQQпU ?=*Մ x'$o,T$m;{~3]0\\Iɪa>şi(&-T3g(\)6CC3w{wms5Mfb2R|(Kȱ^ (l&J(_aX~o*T{Xܕ-kͲ1vL6m*5Rt8e`(|j-0>ZMFbKreVe֪ik-4¾ 9(5ҢoP-)Q ՝MЗ 6%? K_҇FO"zf6}5̣;wq@a&'* 1;0m vPю%k'?IX o* H%(V uR8BiAc$e"-mLN'$O$rG{"~Qh8ǰ%̇UiBAePppcd%|~hR]2°g'Qڐmc?4jnm_{ou|Rސ;>wӃ/0B^7܆*4V7 ?xNsylX{pzKziVѷ(|ʹkf 84 Ci`DR'0HĉKbY8UIր<68>[(FǮr:[ ?4;ݏf?(0cJV{*n^~Rxs|Ñ{rz/{)L PF–> 絧+,kr&kfz0E><έEۛ.=,v}Bo{/؆O愵]jE<9b==}pۤ|`7Xxj Zۂh;Pt͇JieH/\$^yf0ߤ*`1(nMd)Ss'<ʔ+7Œ =l<hsӛ.?}U6Qr\mrn)IGKNR!H1X<KA!.u-11UWw/{0˫^|FwhL ?>/܁QcX c3l)B嵧[Zh t(2@G=\ ?|gqtF0@ݻz(*=H H6 L \lIݴyޙ3f蝣3m&`UY#x;vn&H8S$uuuuFQsZK, cr8u`Tqḱ,IܞLS 6ӬfF0,NRJFTE.Y%j`,m٣Fh~0s%Ypo(G}/YdOH['?{Ir3hϭ<_b?x$h^JKTS7:g!\`A /@ wE)0x/X7tfiHNÇ!]tXƶ۪bAuPTP8<*'5[l\ eA|%r4Vg[LX˗GʒZfGr5H.l;.Y(o9jdz"gf_Ev[-PPk!^&ݫ^ыS9kds/^Axf44}fr{ZL  [f|-&U&Yp)fG_b%Tbfom/<~]X\= 7 Qd5F?C@C~&j&P6mY  ߟ xUda뛭jx#(t{(ȿdC#<)y&B$srQ%[GRdd79*>vi(Z%MѪ,G!-KBCl% B]ܪ[ɺL ) FE QpoRO,dQ ίojty'4Lpj';Vo6Ӱr<{O 5NA5BM,}NJؙVsX'88\dU3"_h)] ԙg kV׫ BߑZCc9,C{AxɲB1( ɢj ?k}54cPT-2Wa,;WjPhI>s!N@)Z1٫U(! u()AM_(cR ?Hg BZޅC{&:* ",WwbX:8OO {VH9V8CV5Rrq+"bڃI 9F^=9EAxaφcl%;4dj7k5V#Z omPE(8F(QQ-#>Ty@>TI!RB/Ik9[Jt̕Q%<֣z@Θ4ۈ0Ziv;U@?rBmԭ<[Sr\PQ0+h3 yGwԠհ#\fW"4bDjT-S']'?Q0ܡ Jj31Zp4P"IST6*NԒ,7VY i=VW l> 64L/7 Y+dž#xk vFdwv;S&+>MYKsmbUJv 7K<6J\^y {]}Qi\{j "刍g#!۬ڰk?\0sCӰB~ʘjLJYdK$21sU2YMG?-A1)RZ"cX}m#'-钊9~qS4aР+Q)kI4cܿP%xbu1i'fOz\/d8Rץ&[@l(%QrPftu9-M 煝<ص;0\bRA> #ep0{t eEn1x@M0gZcLL L;y=Ogx qԥMT %  >LiP;QX#3<9&yiXː8bʦ 0C11/_JP_cTȏ\și,grEuL4r]yt`1/yCwA 2Od앬fc˚Ao:&DZ|=%\hu.]%1RsHwHٍ< ́&?XuMQƶITBD2ᅇrG@U}3\At')L<}؍n{͝=ɯ'/Oz^Wv^!Ld0j8㧁xkmۣ׿ =kPUtA 5jAY6z QWcݨ)2IQQgn݇qBׄF\f7ϫ^'֯@vGՏ;`Y7.uJ c&/dByɅPrYS*L yZM^]1r=v1 ɰ(̓Qi-->$sM 0fu35x1YERBmLn֎nȇa>~An.JJyhLN=}\?1V&?@Ӵh.+;bm񙿻/cAI"R #gtG%dyc$G 7iKӘA+ bF=G'z5]?2+*.,FOwJYaٝKxYRъT(u?WҹS #"0m cgY*e$WrV wgd /cٰe80ߛ5ԑ/("a3` ă/*~*]Gä#u,CZy d*tJ7UYVZQ5Oo?׃-T@gǿ~hLlFv~` tU|Ow?nܬ3<}r?U  ~w𵿘`GՆMuwy5 FQ6e1^y= :C(ҽT7~aCh1qv {[r) "}'4T`{GrE}sDteu={м4Q)v_+YR /e81X;ƛzrւNiAc JFrGg #0L!"ӽ.N s"{&pI1^yXpaWGI<ٯ6h᮪`VʾQR@Mk=[N-"q]Ow[K `$^jDB?]q[nzkK^ou7[ݭn;ud5C^&Q:r>~ѶCvO ^⪇ dA]`bpzXWhNh`xC'qn\؈ǂCѡ}qq>!5PA`zyiMscvsW/2"ݐYsc;# w! @Efvj 2;QO_ՠVzs_k4kNNckӨ7`14cٵH18Lǐf} [m9MNCѿuƫsXLT9лB^TӔL|§ 蔴gm V{P=؃#UVjݮdgR͵$2btstbU\'r_13ni|qh)Il4s'SJm1>WNd8vӀo֛Mӄmi`Ml2GR߳ 'b2a#~xTsAJ🽒Dmz!B+j5Q(tD\BX!(Kqvzt[-lO:;zKF<YF4 ޻`Ib 3pl_5FW )itfـ9kQV{n%V7V`w,Rv̰Xx"'*l!dNx۶a:tb]uYUy4D )1Hۓ" kh~J]b]|:݊~f}Ϸ0 -t㲌&d"W=7,8[jOFAV<*Ա.sM[tHPw7چ jk;Wo+a;vz2H,ufUYzIn,LuєuF93 |(\ZNsi9RTA,-rfX9$Ԏf%o'456aD&TxX|iw~D'wYQ.v]bTNR#hV!VTce1X$"}9q> So#JZ wc%tp7:c;{z( Λa׀aԄVfJe"C:]^a@owF%jQHBpgE"e՛^8hmde8<@?mڟwڛFf57W Kdz~p`"GxX_݉z$)<ʷ#W-8z?p324'`g4DnBufB)7Rϳ`2&nQ=Fɨ~\Y->۪Q+zvLQ1rXrq" fK/ekq F.=Q Y~]zQ K $'R‰QMb"^=2y7ei+ց]YW|M!ƥX}Kt* =t~T ܘ n*Va"]B/0B6~`AQSFmtHrӑ:x6s e/AECD2Zuww-z̼=>={%zCCƠ&w7~7# D Ĥ0GnآAly^ABYh 3~:ŋc.#1 pW#lFiBVfH=@1(e3Iha!Q:@bSaW ]2YTUZ&TŔ}6t BRy.Jإո,l ڶ~e|(MlT`nۍfs{yNkk;t ۅ6;08.d (ںC me4l7=(V'Bx޽S |^jQ?Zʌ'` [qY(, =aMW{ʕӇTREVimɩoH?O1,a芍q2~9j|hŠ wxw:hN%`q ÿu<(}!C9\C9XYDƦۉhK , 9w *σ͌v2P@ :i jz^:S?sunqAv^}ož]S|^\/ʉ4d(Fli֝zo%{h-!@.F5Woߟ_ _N%'R܎4s' Z(q^=Vʻ_w߽=9[4E7G|0z6t0$6 h2 gg9 ' ^,.JuF10U T֯WUg[zZ{j5Y1r89Ni}BTF_P"# x PO#tl]8"U-i*yqzYws |yep?O,IcŌn ztY 0[B&J.G">s\ ?k2rqSҒ=@G?Rò }32S2BP >4h\Q y=6iؤu[U 烽b{[~AV-그kƥޟ< Vp<٪kW45ٔYLJG?}zW|as[ JNٶB9y?z֕R<{w%WUׯ{2V\(JFs/ 8)ӑ,>޼Gct;R]4)4);tffQ'5G\; n7;w'h>o b'p4c]bRyCRe,/Pׂv \* @]x'{fan m2He`_qNA~ 5I('EI#Zϑ UכW ZOw!jޫ?^twLJ?iZX)]'O> 401H+VU~Z2}g"i3xKӞ8HX[%;˷㷿X}%:lBJ\K jK 7G|}qt&4uӄIR~-xY8*'vmxE_0:.)M+J'1'4N"ri*ʠ17dnfOQt*Z_&?|zռGʾT0iXWb")|7)Tpʊ7vF5wb*`R.&iٰGiOLj'*% \mR6RL~{li! |5/hwH_P>f0i/i̹a 17Dy )!a9&x|1%fOU2G/OzсImR"eOiy۱u:MJ cEi mONҖo)|~2w/JSa6dˢǽhu'NY'&u3d1Oɯ'oYh Z 45i@p63ʹFLw:h+*,ɱ H". wϪx0x,t.p yBQLX8 K44[5N&#*&,ˌᜊ;#hs҇6Q;G#?3]"˥Ћ T;6/ơ]bB8[`?y;@3ܳZ.EJhuA vbgNJHd S(M5s\dfEOzjIC.k1 IIpiåK5$+kZ-U@pA.ԓ'1.2SI%i4LiyDOPn<&H,*> _gFRQiACscHVEzHCU)?/OjEORҩ"5մ7̍B BlR%'Է/gF0f7y yOys/Q6!yYbvEbh h | az0IHnn piRH!SJN,VY㬉ZJ\p0 }%PO(JU]QĜp/N^ŔWY*O)#K٭oTV[34@X~V77M̓='} ;a3ɼۆL]a#m$!`7/4J a͂p7yL)\U߯CbQZh8~ <%z ,N P'|oĕe,-lbEVAP i]ŽJ?J$|R. ~up{8}KCgMSCG~gy{f>b%clU=ʫ^Tbky% }Ѫɭ3zERNsTwYd֙裁,V[V='Y jwVjz@ ()Q"A(iDɗfs#;43jg%`Nv]me"߿m2g0_sf`SM3ixE|6h-?Ifۖ WJK60vȾ+${R8Rr8XZ J%O4eUۨ%j%th!:۬>#d>PgQ[~<Ծ?~44wl )1F9PֽPh ۀh:Fy@%EԴ;U݂]E_FY@WSFՓT9L1rmbbOZcdʰְwvϠ<홹΃i/=3`T=;Ttф>?Q;~@L 5Ze-=3-%=X 3쳔zfHWѱj5^Qnl89آdnRYJ F tek=v)?cա2ZW\i62_efj%"܉?uDsj/=_Yz?CeV7| 4poo ]]KM_=@I:2,ߺ #Vb%9z'@$ΏWrUg{HBQ 1:%2Iݍ$XJ*Y%לs;N6Vc$YhOt, ꉶ}Mη lPx!}o},uN*A-(p厀A卧s2uMxҥ)@t!Fy[LrIn 3kO #3-kf>~q 4Sg_oZV՘ɢ%CMvwg A7Tc=;1yt5nOS3N#|Y`|t)~h{N.P!H]{R`Z(}7KyfW;e$wF^+(?ΩK\ݭ?Yd%P2vD%:@z!W$,`(c`t94fXAB خZ?wܞuCڷ:m04pn0<)_b(f \L~%!N;|mlX,9Xlz;M̠=cl]`z8Qvath*p= FT¦9UJs^Dr7^6nn|{{{S> U╽UG ~6vC'0[Ȩ YvfOۖf, .x87Eÿk/@Drq|Er3D2tXU+[S%XhVr~~~ )/s~-:/_0 ''JXqb[}8yb^,Du[&Q_$u=Drn.v~-HK\99Yz zS&1*U,0xE{Xye(DkG7:4f7_"ёu6 t=FbLQ(? =+rN؃C.m~)=UDV)5[^UUyވc1}!#,dKD9P* 'Oa ӏ O'17A >!] GT: Em0:)5IO)5 I)hKȽB.+T@7B+3-FG!{\\iI%胂$ruV7T)}0K,T4PYtR4F}];|\8,G⪕w9[HF!mm%?pCK,Fxy@L1fM8ͧr!mfŨ]䅼"3?l6L7B%y|rU tmJXJ7V#ɘEO߿9:+m)޶E  2rgZ%Պ`#qX:xh@7#봕RyfbZ:ܧ&xI\Z#{VIv2cDB|Hv+!GRP*1~rh} d⟮b@6ߦ!S2L"@kkiK zzp}Bpc.z2PQmTSYWKzE<.:te~NA4.O:eH%LsGoVT5L,LMn|A$ZsUHGَp| g`תor/#yp C8ڎE)_Gyx8S+D6MEZ.LKx:Nc0 UM nlrr❍gԍ(>1`EzH@G|ԟ0[/i%"H/@ڰ!p {*/ð{4OrC~F: ~z]5Kq,t!trINir=` lϣAA4:B %m׼{ ŏ%8+ RtIݖgoC}pB- UAaMH^ sj_3DH4 )<xcNY, VI[N\zB-9\d]]@Ǎ߉vEe"/`\HfL4!J) T1Zq/.בrk/e8H԰9 ? 79T8zY?T[QLp"2gM&C-V 2Q8(f*k$ QvYlk,Tq3Fmb[Bn[ mbߝ!Ra ]6hifNҡ74kBa"8/~wJBk^r,)+f+&2Wtqjrb͕ Ah5yZb&$&)<%艀aMb~N-sJ8/Cuݭ|V44h6$ OFhQs<f'!d,޹\s;c ݦT]ٜƋMc8{i-> :bJJR:D`m^l;m Q.nCܢu(S|2,^mD#6ďIq x*VZ bi^[ҏ R3<\LYEz0H0s\GsU◵߱-m`l_ģjJ FoOP_U*(y+@BOl.RZ5XCq8fJo%m6e+T)j J%~TY,rjt.#L +LHRgוf@>=ik uM+ؒBJA3?Cwu_QeSC Cs﬘WBeͅwݔaq, ֕?XȺÈKsptŝ$ ^Eܖm0u^z0D>gTMb>pf// Tq/ګOX-j}!#Ç\=C بŝf>hԻX8y6g<(+ޖ5D)4O{3z&7F}'Q'^1'R2=!9ruZQ6 ޹%slHiMi#X0I5UyֵwՍsLx뤖מVf>8 ;>,,xb`Jq1vt3 ƒi/$V"g:*G}B.z/;Aw3)%0_`-h3[uwSt1IH&+@M4]T6X+M%2e VS?d n>{r:q\A"n=7l7CZGJ8dC0&sr&UkiaCKatEEdJ }#1 ւJPcCS;XX+ૄr/ӫߠ\AE< ڴ+Ȟz|dYa*UbM3zTqXU?17%}%Y%z ]:=/r9j Rr+5tsqbCGΩp ~K$C )EON|c' {ƒ˄^R2r'w33∭]'0C &iBĶծnyl/; 9n %{akƣO# .F) pE}FL)KbJOgmvO"Yְ6Cdҿr(j%iAW|NT ц5D`7 u-D^9[/$9 C-I,䳌$fpͲј9r=!NsJIә7Ee<61p!onX*pWQk9:S ]1C;TgL&*F hf:;àhԤ"B@t.Ht@#bCRS8h<-j(y)e%d MbU5Ic<#=_AYrA{L@AF9GAnH&tֻϵό]~ڒj6/ .'e>1{ɗ\ zwU hXȻ 5q!eQM3}tJXL$`KˋlpެO+є\Le/A zF+}kВ,ipd5Ri5dl}oek*l5JO|6 #`U;- @xg%ц4X8P uL⃓7#rBa="c33 H0voΈ`VhgQNY#^#9>XETOc;HMЧZ۩v+Y,L#1RRz*53#CQLı.JLuki9}BLt/5xwͺ)xB).Y!&xK0g_v9;7=!˄q bČ-{ m\ehciz4n-V\8ACSBcz [1U YYKc jnq֍- hk]+SR%YɵNdVEL.Vh$ [@HT C^蹳cqǗdŻ al61sd0)=|R]h=\9=a<"Oƒ@2?K{m6;Hqӄˈ|rT|sTV\xɱ=y%ODYV04 g{9:g-{ڝs 3fvigJG#;({DWgЩ v Ep-v ?k2( H&{3#W{Y9zKĕ"&6ǪĠ,rHw`4PJ}lL>Ao_&*;|$R;6*\jH}tI4IUl8'`kifVe-SDK3sMD҇(}I/&B̮?8܎(f8w&Euuqu"7| kLs5}|4Gy4EޘXyK"Nj`ahFx/H%#-2 ZA< I0تm&4f3mjnBeMf>r"4cɠjr57!"RyCB1`6eՐ8kc!g@%vR=6R hbG:RSOTQ?J&LFoZ]: A}n*E88'z7kwYluz@,=/fѪ?m_um@v=fCPa0A d 6`GŜ\{ IOZ`ccW{7fBkkMR|^5xm86(: ƑY9VpxĨ?sl5mpU1;ZrxSexLJ%0We_<<=F >zX{jgO̰R侨ZͫFթ6z3JYƦGգq*`B} es!DUwQG $f^:  )PST∐24dTak)ؐٴp.6 08$Q?|g7G`ɣf#_&$_Yuo~bA`5(Q(F-^;hˀ[D!2t^_u vW8ƵBs80n(PO$&jC @3eU*|>QʃQpfads+TQχF.UX{墪?ߚ^ǔPFC4}ʉ!OQ=Ca>!ң/+j5zBeTCRɮTլn ?>?B]ә{??+( -u6 zc?G)~399?pT~XuV&.HV r6)xڽjvvd*5G'|O.!f夑LN޾?~δ:4M$ޢF\j/sIp悯O9&+I@ C-:,bx.GiuRk!I_ԷzէYTGokΈQ? i )6_yp)Y1D>@ )3& 7+l QWp6}v\JdtóLGVT؝DVY䂼ܟyg"]y3q. +OQꂢ0P/ai|f9[g30e\ 6Fs(NΩ?1DJg3<Zk7P_ 6sQ@)T&Q>iJ鎉DcLmv]Td5RH.X]A)./)lz"Hs1;I5! 3T ۴#op%f:!Uq䇇积_*-Yp1`¸`8To8O7Q!VEcsP DGRhl(*ףNO9Ϗߝ=*T*nNQo3TKiYL,ٕ-D*Y,RgRPPf=0L ];ÄT4}Bo ɥ_Rq|sI 3P-HeݔB UP!^/CS[Aa _zU3X8& Z*}XqqB 9QWD"JJJ[R S3eUc Y&RG!fTBqBPEuaƽ78N3½1dqHat;[?[d`QÝj@Dyֽ!z#4H%7$m01/>T _E=i&# /W'Cug P9Ik$(­&$;1IeUQO'@4!|!&_,\a.] a Q !W%I0LngE%ۤۚJմ¶yN֣D*5yoVNp@4@`K6`q#NKyQG82̏bBV%U%jByM*i#[pZsM5j3 ][Q3V yp_M9)a9YOޕ?m,ijhe-Eώ!V߳x`({ŷ&}¸ ;W,Xv&xX8zE_Yp4@d)N !^؎Z'M@[0y&[LbD{ L͌׽Ɯau GnC24M_d kf90Je^NYCT !\.' v lI["( DtLU'|ҹG21H9j zAΒmLc̃gsIZ"(f눉jGw,>̙dGV=sY{ iNj|4>1J=$ӟ%=|vRd[ pXXR#}]}jإ)!!@z|)T|zX\\z^Su5pL iQ+-pi쮄NeHIljjG)ei(Ӊ/*M+BWͳ)hfR)PIMAPS';I!YFzEzf0qZU%DCFYmǕ.[}!:o\wASaMpv gat0<086 IABB-ǕGY$Ҟ IE%17d,3\Oj]5$쁻n]42}-%7T&$K6 ,ehZPԺ7Jmӥ S 9 [#b6mvQH 0R6yӑ^E |DX͵1֌l?Lxwz) RC3fގN؞T- #C4IYY{r,GѨVO=)1t'VP >ZEĥAknc4r9 ddR&1g~cۍȋEjȬ sLPFXPCT1G:A"d8aށr5ψd{1"%~Gh5EǑeAczE̗\h<[Vai+ЅR%ܙƏR@?Ҕ{ŴD bMƓAj ~#~F8gV9L[$8pLixE mqdF` xrf"qk$Y-qɴL\/ 0=,DV\ZfTӐM`8썢s֨:@@* BiRVӛԜ6צɏ8m"k&HkDh#rϯ߿{P|5Mtk Is^( +HxqWܝcwRg;8{y> #Ӥk R ޿@;$3P>fSVs_y|=Qw~_rġX8Ҽjk3:uO3' QEǔ[)2% yVߐ7X_^<()Ȟq)5|OW-}.S]w'lUvonC+>H<>Շ&~+K j%ǻRV=>B&5k96.?b{y훽+|-2 a&UԐH} Z(ɕ􎢪쁲^X=ٔoʳbTa Oփ_΃ƿ/ރƿW=h[S4*h}NC9e{E}}\4 O9?g0%GxNz::*^.v[bU V-4: %QhuZ[I(mxJ8+B<%aڧ Hb ~nRg!fyTw'jO=ɮetW/2ONmU~ߨW*zZiTkFoQ}%.kmf`T8O6U4+\g3^Y6MCfU maןN!ѾLo .jltjX Y pS NT{McqPDkKGF{n=m>fV8V I X9Rj3F Knoq_բW"b=_JHj*?̄,nupqTyL3B؁ikep,\Cʆ.6]$T \RJ%oYaWmv.HHPFʗQ&"o! 8:4,8qct|̎g&+9Љ6 ANvz=Y9'T:})cύyMWuq쿝ᙉMbu`l_Po5Z>ԃ|x^E$sxx&h0U/dV>'Q=!{`PyƊXE#OKM0,i8*qnK`r?$w_Rf X~6J o-V%8Ō S$ ;}RMu[6$JxFrSeX k9/_ {ʒn4NƒH=c3iI'Y.oh͵+*MΛ,ݷ!5Rr6dR#x8W8$KbY GA#rjGް#蠲 kB:N g&E?iŮrEJ?Ѭ-ڪ=O>PW.XۆanjzxSrLx" ('xCp.i߻  #;3ϦCmMWaiԷ坭FU]m5jgJԽNifHzÉKM@"ǃCD56Wyԫ6 $#I{#IBj EWA=T$@VoMVZ,}_7'YZh"d5+6!ܶ7͔+Cf`I5:ԚJPw-?O?yfϚ5hrs8 KRr jd޻??f8bihtnfU)SB6(l-ϴpeP9 ~t@csI+>4j&ߩvg~^:lW΋̈ܳg)C3ȥRj#f3l5]df@SMʱu/F:`v_"u02zY {w-LɩRՑ^K6H`)Ua-P?>f? -!QƺDit{W^tdp/cmre_{_IR}O+}tv74ԼH#Te臾T=e"̊1PrAE ~V6M6t[Y2| #include #include #include #include #include #include "numa.h" #include "numaint.h" #define SYSFS_BLOCK 4096 hidden char *sysfs_read(char *name) { char *buf; int n; int fd; fd = open(name, O_RDONLY); buf = malloc(SYSFS_BLOCK); if (!buf) return NULL; n = read(fd, buf, SYSFS_BLOCK - 1); close(fd); if (n <= 0) { free(buf); return NULL; } buf[n] = 0; return buf; } hidden int sysfs_node_read(struct bitmask *mask, char *fmt, ...) { int n; va_list ap; char *p, *fn, *m, *end; int num; va_start(ap, fmt); n = vasprintf(&fn, fmt, ap); va_end(ap); if (n < 0) return -1; p = sysfs_read(fn); free(fn); if (!p) return -1; m = p; do { num = strtol(m, &end, 0); if (m == end) return -1; if (num < 0) return -2; if (num >= numa_num_task_nodes()) return -1; numa_bitmask_setbit(mask, num); /* Continuation not supported by kernel yet. */ m = end; while (isspace(*m) || *m == ',') m++; } while (isdigit(*m)); free(p); return 0; } ./numactl-2.0.9~rc5/util.h0000644000175000017500000000126612213661423013743 0ustar ianwianwextern void printmask(char *name, struct bitmask *mask); extern void printcpumask(char *name, struct bitmask *mask); extern struct bitmask *nodemask(char *s); extern struct bitmask *cpumask(char *s, int *ncpus); extern int read_sysctl(char *name); extern void complain(char *fmt, ...); extern void nerror(char *fmt, ...); /* defined in main module, but called by util.c */ extern void usage(void); extern long memsize(char *s); extern int parse_policy(char *name, char *arg); extern void print_policies(void); extern char *policy_name(int policy); #define err(x) perror("numactl: " x),exit(1) #define array_len(x) (sizeof(x)/sizeof(*(x))) #define round_up(x,y) (((x) + (y) - 1) & ~((y)-1)) ./numactl-2.0.9~rc5/Makefile0000755000175000017500000001474212213661422014262 0ustar ianwianw# these can (and should) be overridden on the make command line for production CFLAGS := -g -Wall -O2 # these are used for the benchmarks in addition to the normal CFLAGS. # Normally no need to overwrite unless you find a new magic flag to make # STREAM run faster. BENCH_CFLAGS := -O3 -ffast-math -funroll-loops # for compatibility with old releases CFLAGS += ${OPT_CFLAGS} override CFLAGS += -I. # find out if compiler supports __thread THREAD_SUPPORT := $(shell if $(CC) $(CFLAGS) threadtest.c -o threadtest \ >/dev/null 2>/dev/null ; then echo "yes" ; else echo "no"; fi) ifeq ($(THREAD_SUPPORT),no) override CFLAGS += -D__thread="" endif # find out if compiler supports -ftree-vectorize THREAD_SUPPORT := $(shell touch empty.c ; if $(CC) $(CFLAGS) -c -ftree-vectorize empty.c -o empty.o \ >/dev/null 2>/dev/null ; then echo "yes" ; else echo "no"; fi) ifeq ($(THREAD_SUPPORT),yes) BENCH_CFLAGS += -ftree-vectorize endif CLEANFILES := numactl.o libnuma.o numactl numademo numademo.o distance.o \ memhog libnuma.so libnuma.so.1 numamon numamon.o syscall.o bitops.o \ memhog.o util.o stream_main.o stream_lib.o shm.o stream clearcache.o \ test/pagesize test/tshared test/mynode.o test/tshared.o mt.o empty.o empty.c \ test/mynode test/ftok test/prefered test/randmap \ .depend .depend.X test/nodemap test/distance test/tbitmap \ test/after test/before threadtest test_move_pages \ test/mbind_mig_pages test/migrate_pages \ migratepages migspeed migspeed.o libnuma.a \ test/move_pages test/realloc_test sysfs.o affinity.o \ test/node-parse rtnetlink.o test/A numastat SOURCES := bitops.c libnuma.c distance.c memhog.c numactl.c numademo.c \ numamon.c shm.c stream_lib.c stream_main.c syscall.c util.c mt.c \ clearcache.c test/*.c affinity.c sysfs.c rtnetlink.c numastat.c ifeq ($(strip $(PREFIX)),) prefix := /usr else prefix := $(PREFIX) endif libdir := ${prefix}/$(shell ./getlibdir) docdir := ${prefix}/share/doc all: numactl migratepages migspeed libnuma.so numademo numamon memhog \ test/tshared stream test/mynode test/pagesize test/ftok test/prefered \ test/randmap test/nodemap test/distance test/tbitmap test/move_pages \ test/mbind_mig_pages test/migrate_pages test/realloc_test libnuma.a \ test/node-parse numastat numactl: numactl.o util.o shm.o bitops.o libnuma.so numastat: CFLAGS += -std=gnu99 migratepages: migratepages.c util.o bitops.o libnuma.so migspeed: LDLIBS += -lrt migspeed: migspeed.o util.o libnuma.so util.o: util.c memhog: util.o memhog.o libnuma.so numactl.o: numactl.c numademo: LDLIBS += -lm # GNU make 3.80 appends BENCH_CFLAGS twice. Bug? It's harmless though. numademo: CFLAGS += -DHAVE_STREAM_LIB -DHAVE_MT -DHAVE_CLEAR_CACHE ${BENCH_CFLAGS} stream_lib.o: CFLAGS += ${BENCH_CFLAGS} mt.o: CFLAGS += ${BENCH_CFLAGS} mt.o: mt.c numademo: numademo.o stream_lib.o mt.o libnuma.so clearcache.o test_numademo: numademo LD_LIBRARY_PATH=$$(pwd) ./numademo -t -e 10M numademo.o: numademo.c libnuma.so numamon: numamon.o stream: LDLIBS += -lm stream: stream_lib.o stream_main.o libnuma.so util.o ${CC} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDLIBS} stream_main.o: stream_main.c libnuma.so.1: versions.ldscript libnuma.so.1: libnuma.o syscall.o distance.o affinity.o sysfs.o rtnetlink.o ${CC} ${LDFLAGS} -shared -Wl,-soname=libnuma.so.1 -Wl,--version-script,versions.ldscript -Wl,-init,numa_init -Wl,-fini,numa_fini -o libnuma.so.1 $(filter-out versions.ldscript,$^) libnuma.so: libnuma.so.1 ln -sf libnuma.so.1 libnuma.so libnuma.o : CFLAGS += -fPIC AR ?= ar RANLIB ?= ranlib libnuma.a: libnuma.o syscall.o distance.o sysfs.o affinity.o rtnetlink.o $(AR) rc $@ $^ $(RANLIB) $@ distance.o : CFLAGS += -fPIC syscall.o : CFLAGS += -fPIC affinity.o : CFLAGS += -fPIC sysfs.o : CFLAGS += -fPIC rtnetlink.o : CFLAGS += -fPIC test/tshared: test/tshared.o libnuma.so test/mynode: test/mynode.o libnuma.so test/pagesize: test/pagesize.c libnuma.so test/prefered: test/prefered.c libnuma.so test/ftok: test/ftok.c libnuma.so test/randmap: test/randmap.c libnuma.so test/nodemap: test/nodemap.c libnuma.so test/distance: test/distance.c libnuma.so test/tbitmap: test/tbitmap.c libnuma.so test/move_pages: test/move_pages.c libnuma.so test/mbind_mig_pages: test/mbind_mig_pages.c libnuma.so test/migrate_pages: test/migrate_pages.c libnuma.so test/realloc_test: test/realloc_test.c libnuma.so test/node-parse: test/node-parse.c libnuma.so util.o .PHONY: install all clean html depend MANPAGES := numa.3 numactl.8 numastat.8 migratepages.8 migspeed.8 install: numactl migratepages migspeed numademo.c numamon memhog libnuma.so.1 numa.h numaif.h numacompat1.h numastat ${MANPAGES} mkdir -p ${prefix}/bin install -m 0755 numactl ${prefix}/bin install -m 0755 migratepages ${prefix}/bin install -m 0755 migspeed ${prefix}/bin install -m 0755 numademo ${prefix}/bin install -m 0755 memhog ${prefix}/bin mkdir -p ${prefix}/share/man/man2 ${prefix}/share/man/man8 ${prefix}/share/man/man3 install -m 0644 migspeed.8 ${prefix}/share/man/man8 install -m 0644 migratepages.8 ${prefix}/share/man/man8 install -m 0644 numactl.8 ${prefix}/share/man/man8 install -m 0644 numastat.8 ${prefix}/share/man/man8 install -m 0644 numa.3 ${prefix}/share/man/man3 ( cd ${prefix}/share/man/man3 ; for i in $$(./manlinks) ; do ln -sf numa.3 $$i.3 ; done ) mkdir -p ${libdir} install -m 0755 libnuma.so.1 ${libdir} cd ${libdir} ; ln -sf libnuma.so.1 libnuma.so install -m 0644 libnuma.a ${libdir} mkdir -p ${prefix}/include install -m 0644 numa.h numaif.h numacompat1.h ${prefix}/include install -m 0755 numastat ${prefix}/bin if [ -d ${docdir} ] ; then \ mkdir -p ${docdir}/numactl/examples ; \ install -m 0644 numademo.c ${docdir}/numactl/examples ; \ fi HTML := html/numactl.html html/numa.html clean: rm -f ${CLEANFILES} @rm -rf html distclean: clean rm -f .[^.]* */.[^.]* rm -f *~ */*~ *.orig */*.orig */*.rej *.rej html: ${HTML} htmldir: if [ ! -d html ] ; then mkdir html ; fi html/numactl.html: numactl.8 htmldir groff -Thtml -man numactl.8 > html/numactl.html html/numa.html: numa.3 htmldir groff -Thtml -man numa.3 > html/numa.html depend: .depend .depend: ${CC} -MM -DDEPS_RUN -I. ${SOURCES} > .depend.X && mv .depend.X .depend include .depend Makefile: .depend .PHONY: test regress1 regress2 regress1: cd test ; ./regress regress2: cd test ; ./regress2 # regress-io is known not to work # regress3: # cd test ; ./regress-io # test: all regress1 regress2 test_numademo regress3 test: all regress1 regress2 test_numademo ./numactl-2.0.9~rc5/clearcache.h0000644000175000017500000000006412213661422015032 0ustar ianwianwvoid clearcache(unsigned char *mem, unsigned size); ./numactl-2.0.9~rc5/threadtest.c0000755000175000017500000000011712213661423015125 0ustar ianwianw/* Test if gcc supports thread */ int __thread x; int main(void) { return x; } ./numactl-2.0.9~rc5/sysfs.h0000644000175000017500000000017012213661423014126 0ustar ianwianwstruct bitmask; hidden char *sysfs_read(char *name); hidden int sysfs_node_read(struct bitmask *mask, char *fmt, ...); ./numactl-2.0.9~rc5/releases/0000755000175000017500000000000012213661423014413 5ustar ianwianw./numactl-2.0.9~rc5/releases/2.0.50000644000175000017500000000025612213661423015003 0ustar ianwianwpatches in this release: 0007_ak_numactl Andi Kleen - Fix numactl calls to set_mempolicy, get_mempolicy and mbind (this was found about 1 day after the 2.0.4 release) ./numactl-2.0.9~rc5/releases/2.0.40000644000175000017500000000161512213661423015002 0ustar ianwianwpatches in this release: 0908_ab_numactl Anton Blanchard libnuma: Fix issue with numactl --hardware on sparse cpumaps 0912_ak_patch Andi Kleen Fix make clean for tests in numactl-2.0.4-rc1 1001_sn_nodetocpus Sharyathi Nagesh Fix to numa_node_to_cpus_v2 1004_ls_tasknaming Lee Schermerhorn Fix libnuma numa_num_{task vs thread}_{cpu|node}s() naming disc repancy 1004_cw_spaces Cliff Wickman removed trailing spaces 1002_iw_nodetocpus Ian Wienand correct and clarify the numa_node_to_cpus() man page 1002_mm_cputerms Mike MacCana Correct terminology. ('physical cpus') 1006_jb_configured_cpus Jan Beulich libnuma: enumerate CPUs correctly 1006_jb_strlen Jan Beulich libnuma: fix memory corruption 1005_tr_numactl_hardware Thomas Renninger Fix CPUless nodes numactl --hardware output 0908_nh_dsodestructor Neil Horman libnuma: introduce DSO destructor to free allocated memory ./numactl-2.0.9~rc5/README0000644000175000017500000000273012213661422013471 0ustar ianwianw Simple NUMA policy support. It consists of a numactl program to run other programs with a specific NUMA policy and a libnuma shared library ("NUMA API") to set NUMA policy in applications. The libnuma binary interface is supposed to stay binary compatible. Incompatible changes will use new symbol version numbers. In addition there are various test and utility programs, like numastat to display NUMA allocation statistics and memhog. In test there is a small regression test suite. Note that regress assumes a unloaded machine with memory free on each node. Otherwise you will get spurious failures in the non strict policies (prefered, interleave) See the manpages numactl.8 and numa.3 for details. Copyright: numactl and the demo programs are under the GNU General Public License, v.2 libnuma is under the GNU Lesser General Public License, v2.1. The manpages are under the same license as the Linux manpages (see the files) numademo links with a library derived from the C version of STREAM by John D. McCalpin and Joe R. Zagar for one sub benchmark. See stream_lib.c for the license. In particular when you publish numademo output you might need to pay attention there or filter out the STREAM results. It also uses a public domain Mersenne Twister implementation from Michael Brundage. Version 2.0.9-rc4: (C)2013 SGI Author: Andi Kleen, SUSE Labs Version 2.0.0 by Cliff Wickman, Christoph Lameter and Lee Schermerhorn cpw@sgi.com clameter@sgi.com lee.schermerhorn@hp.com ./numactl-2.0.9~rc5/stream_main.c0000755000175000017500000000147412213661423015264 0ustar ianwianw#include #include #include #include "numa.h" #include "numaif.h" #include "util.h" #include "stream_lib.h" void usage(void) { exit(1); } char *policy = "default"; /* Run STREAM with a numa policy */ int main(int ac, char **av) { struct bitmask *nodes; char *map; long size; int policy; policy = parse_policy(av[1], av[2]); nodes = numa_allocate_nodemask(); if (av[1] && av[2]) nodes = numa_parse_nodestring(av[2]); if (!nodes) { printf ("<%s> is invalid\n", av[2]); exit(1); } size = stream_memsize(); map = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); if (map == (char*)-1) exit(1); if (mbind(map, size, policy, nodes->maskp, nodes->size, 0) < 0) perror("mbind"), exit(1); stream_init(map); stream_test(NULL); return 0; } ./numactl-2.0.9~rc5/DESIGN0000644000175000017500000000007412213661422013504 0ustar ianwianw [ old description removed because it was too out of date ] ./numactl-2.0.9~rc5/shm.c0000755000175000017500000001731612213661423013556 0ustar ianwianw/* Copyright (C) 2003,2004 Andi Kleen, SuSE Labs. Manage shared memory policy for numactl. The actual policy is set in numactl itself, this just sets up and maps the shared memory segments and dumps them. numactl 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; version 2. numactl 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. See the GNU General Public License for more details. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include #include "numa.h" #include "numaif.h" #include "numaint.h" #include "util.h" #include "shm.h" int shmfd = -1; long shmid = 0; char *shmptr; unsigned long long shmlen; mode_t shmmode = 0600; unsigned long long shmoffset; int shmflags; static int shm_pagesize; long huge_page_size(void) { size_t len = 0; char *line = NULL; FILE *f = fopen("/proc/meminfo", "r"); if (f != NULL) { while (getdelim(&line, &len, '\n', f) > 0) { int ps; if (sscanf(line, "Hugepagesize: %d kB", &ps) == 1) return ps * 1024; } free(line); fclose(f); } return getpagesize(); } static void check_region(char *opt) { if (((unsigned long)shmptr % shm_pagesize) || (shmlen % shm_pagesize)) { fprintf(stderr, "numactl: policy region not page aligned\n"); exit(1); } if (!shmlen) { fprintf(stderr, "numactl: policy region length not specified before %s\n", opt); exit(1); } } static key_t sysvkey(char *name) { int fd; key_t key = ftok(name, shmid); if (key >= 0) return key; fprintf(stderr, "numactl: Creating shm key file %s mode %04o\n", name, shmmode); fd = creat(name, shmmode); if (fd < 0) nerror("cannot create key for shm %s\n", name); key = ftok(name, shmid); if (key < 0) nerror("cannot get key for newly created shm key file %s", name); return key; } /* Attach a sysv style shared memory segment. */ void attach_sysvshm(char *name, char *opt) { struct shmid_ds s; key_t key = sysvkey(name); shmfd = shmget(key, shmlen, shmflags); if (shmfd < 0 && errno == ENOENT) { if (shmlen == 0) complain( "need a --length to create a sysv shared memory segment"); fprintf(stderr, "numactl: Creating shared memory segment %s id %ld mode %04o length %.fMB\n", name, shmid, shmmode, ((double)shmlen) / (1024*1024) ); shmfd = shmget(key, shmlen, IPC_CREAT|shmmode|shmflags); if (shmfd < 0) nerror("cannot create shared memory segment"); } if (shmlen == 0) { if (shmctl(shmfd, IPC_STAT, &s) < 0) err("shmctl IPC_STAT"); shmlen = s.shm_segsz; } shmptr = shmat(shmfd, NULL, SHM_RDONLY); if (shmptr == (void*)-1) err("shmat"); shmptr += shmoffset; shm_pagesize = (shmflags & SHM_HUGETLB) ? huge_page_size() : getpagesize(); check_region(opt); } /* Attach a shared memory file. */ void attach_shared(char *name, char *opt) { struct stat64 st; shmfd = open(name, O_RDONLY); if (shmfd < 0) { errno = 0; if (shmlen == 0) complain("need a --length to create a shared file"); shmfd = open(name, O_RDWR|O_CREAT, shmmode); if (shmfd < 0) nerror("cannot create file %s", name); } if (fstat64(shmfd, &st) < 0) err("shm stat"); if (shmlen > st.st_size) { if (ftruncate64(shmfd, shmlen) < 0) { /* XXX: we could do it by hand, but it would it would be impossible to apply policy then. need to fix that in the kernel. */ perror("ftruncate"); } } shm_pagesize = st.st_blksize; check_region(opt); /* RED-PEN For shmlen > address space may need to map in pieces. Left for some poor 32bit soul. */ shmptr = mmap64(NULL, shmlen, PROT_READ, MAP_SHARED, shmfd, shmoffset); if (shmptr == (char*)-1) err("shm mmap"); } static void dumppol(unsigned long long start, unsigned long long end, int pol, struct bitmask *mask) { if (pol == MPOL_DEFAULT) return; printf("%016Lx-%016Lx: %s ", shmoffset+start, shmoffset+end, policy_name(pol)); printmask("", mask); } /* Dump policies in a shared memory segment. */ void dump_shm(void) { struct bitmask *nodes, *prevnodes; int prevpol = -1, pol; unsigned long long c, start; start = 0; if (shmlen == 0) { printf("nothing to dump\n"); return; } nodes = numa_allocate_nodemask(); prevnodes = numa_allocate_nodemask(); for (c = 0; c < shmlen; c += shm_pagesize) { if (get_mempolicy(&pol, nodes->maskp, nodes->size, c+shmptr, MPOL_F_ADDR) < 0) err("get_mempolicy on shm"); if (pol == prevpol) continue; if (prevpol != -1) dumppol(start, c, prevpol, prevnodes); prevnodes = nodes; prevpol = pol; start = c; } dumppol(start, c, prevpol, prevnodes); } static void dumpnode(unsigned long long start, unsigned long long end, int node) { printf("%016Lx-%016Lx: %d\n", shmoffset+start, shmoffset+end, node); } /* Dump nodes in a shared memory segment. */ void dump_shm_nodes(void) { int prevnode = -1, node; unsigned long long c, start; start = 0; if (shmlen == 0) { printf("nothing to dump\n"); return; } for (c = 0; c < shmlen; c += shm_pagesize) { if (get_mempolicy(&node, NULL, 0, c+shmptr, MPOL_F_ADDR|MPOL_F_NODE) < 0) err("get_mempolicy on shm"); if (node == prevnode) continue; if (prevnode != -1) dumpnode(start, c, prevnode); prevnode = node; start = c; } dumpnode(start, c, prevnode); } static void vwarn(char *ptr, char *fmt, ...) { va_list ap; unsigned long off = (unsigned long)ptr - (unsigned long)shmptr; va_start(ap,fmt); printf("numactl verify %lx(%lx): ", (unsigned long)ptr, off); vprintf(fmt, ap); va_end(ap); exitcode = 1; } static unsigned interleave_next(unsigned cur, struct bitmask *mask) { int numa_num_nodes = numa_num_possible_nodes(); ++cur; while (!numa_bitmask_isbitset(mask, cur)) { cur = (cur+1) % numa_num_nodes; } return cur; } /* Verify policy in a shared memory segment */ void verify_shm(int policy, struct bitmask *nodes) { char *p; int ilnode, node; int pol2; struct bitmask *nodes2; nodes2 = numa_allocate_nodemask(); if (policy == MPOL_INTERLEAVE) { if (get_mempolicy(&ilnode, NULL, 0, shmptr, MPOL_F_ADDR|MPOL_F_NODE) < 0) err("get_mempolicy"); } for (p = shmptr; p - (char *)shmptr < shmlen; p += shm_pagesize) { if (get_mempolicy(&pol2, nodes2->maskp, nodes2->size, p, MPOL_F_ADDR) < 0) err("get_mempolicy"); if (pol2 != policy) { vwarn(p, "wrong policy %s, expected %s\n", policy_name(pol2), policy_name(policy)); return; } if (memcmp(nodes2, nodes, numa_bitmask_nbytes(nodes))) { vwarn(p, "mismatched node mask\n"); printmask("expected", nodes); printmask("real", nodes2); } if (get_mempolicy(&node, NULL, 0, p, MPOL_F_ADDR|MPOL_F_NODE) < 0) err("get_mempolicy"); switch (policy) { case MPOL_INTERLEAVE: if (node < 0 || !numa_bitmask_isbitset(nodes2, node)) vwarn(p, "interleave node out of range %d\n", node); if (node != ilnode) { vwarn(p, "expected interleave node %d, got %d\n", ilnode,node); return; } ilnode = interleave_next(ilnode, nodes2); break; case MPOL_PREFERRED: case MPOL_BIND: if (!numa_bitmask_isbitset(nodes2, node)) { vwarn(p, "unexpected node %d\n", node); printmask("expected", nodes2); } break; case MPOL_DEFAULT: break; } } } ./numactl-2.0.9~rc5/move_pages.20000644000175000017500000001042312213661422015017 0ustar ianwianw.\" Hey Emacs! This file is -*- nroff -*- source. .\" .\" This manpage is Copyright (C) 2006 Silicon Graphics, Inc. .\" Christoph Lameter .\" .\" Permission is granted to make and distribute verbatim copies of this .\" manual provided the copyright notice and this permission notice are .\" preserved on all copies. .\" .\" Permission is granted to copy and distribute modified versions of this .\" manual under the conditions for verbatim copying, provided that the .\" entire resulting derived work is distributed under the terms of a .\" permission notice identical to this one. .\" .TH MOVE_PAGES 2 2006-10-31 "Linux 2.6.18" "Linux Programmer's Manual" .SH NAME move_pages \- Move individual pages of a process to another node .SH SYNOPSIS .B #include .sp .BI "long move_pages(int " pid ", unsigned long count, void ** " pages ", const int * " nodes ", int * " status ", int " flags ); .SH DESCRIPTION .BR move_pages () moves .I count pages to the .I nodes. The result of the move is reflected in .I status. The .I flags indicate constraints on the pages to be moved. .I pid is the process id in which pages are to be moved. Sufficient rights must exist to move pages of another process. This means the moving process either has root priviledges, has SYS_NICE administrative rights or the same owner. If pid is 0 then we move pages of the current process. .I count is the number of pages to move. It defines the size of the three arrays .I pages, .I nodes and .I status. .I pages is an array of pointers to the pages that should be moved. These are pointers that should be aligned to page boundaries. Addresses are specified as seen by the process specified by .I pid. .I nodes is either an array of integers that specify the desired location for each page or it is NULL. Each integer is a node number. If NULL is specified then move_pages will not move any pages but return the node of each page in the .I status array. Having the status of each page may be necessary to determine pages that need to be moved. .I status is an array of integers that return the status of each page. The array only contains valid values if .I move_pages did not return an error code. .I flags specify what types of pages to move. .B MPOL_MF_MOVE means that only pages that are in exclusive use by the process are to be moved. .B MPOL_MF_MOVE_ALL means that pages shared between multiple processes can also be moved. The process must have root priviledges or SYS_NICE priviledges. .SH Page states in the status array .TP .B 0..MAX_NUMNODES Indicates that the location of the page is on this node. .TP .B -ENOENT The page is not present. .TP .B -EACCES The page is mapped by multiple processes and can only be moved if .I MPOL_MF_MOVE_ALL is specified. .TP .B -EBUSY The page is currently busy and cannot be moved. Try again later. This occurs if a page is undergoing I/O or another kernel subsystem is holding a reference to the page. .TP .B -EFAULT This is a zero page or the memory area is not mapped by the process. .TP .B -ENOMEM Unable to allocate memory on target node. .TP .B -EIO Unable to write back a page. The page has to be written back in order to move ti since the page is dirty and the filesystem has not provide a migration function that would allow the move of dirty pages. .TP .B -EINVAL A dirty page cannot be moved. The filesystem does not provide a migration function and has no ability to write back pages. .SH "RETURN VALUE" On success .B move_pages returns zero. .SH ERRORS .TP .B -ENOENT No pages were found that require moving. All pages are either already on the target node, not present, had an invalid address or could not be moved because they were mapped by multiple processes. .TP .B -EINVAL Flags other than .I MPOL_MF_MOVE and .I MPOL_MF_MOVE_ALL was specified or an attempt was made to migrate pages of a kernel thread. .TP .B -EPERM .I MPOL_MF_MOVE_ALL specified without sufficient privileges or an attempt to move a process belonging to another user. .TP .B -EACCESS On of the target nodes is not allowed by the current cpuset. .TP .B -ENODEV On of the target nodes is not online. .TP .B -ESRCH Process does not exist. .TP .B -E2BIG Too many pages to move. .TP .B -EFAULT Parameter array could not be accessed. .SH "SEE ALSO" .BR numa_maps (5), .BR migratepages (8), .BR numa_stat (8), .BR numa (3) ./numactl-2.0.9~rc5/numaif.h0000755000175000017500000000274112213661422014246 0ustar ianwianw#ifndef NUMAIF_H #define NUMAIF_H 1 #ifdef __cplusplus extern "C" { #endif /* Kernel interface for NUMA API */ /* System calls */ extern long get_mempolicy(int *policy, const unsigned long *nmask, unsigned long maxnode, void *addr, int flags); extern long mbind(void *start, unsigned long len, int mode, const unsigned long *nmask, unsigned long maxnode, unsigned flags); extern long set_mempolicy(int mode, const unsigned long *nmask, unsigned long maxnode); extern long migrate_pages(int pid, unsigned long maxnode, const unsigned long *frommask, const unsigned long *tomask); extern long move_pages(int pid, unsigned long count, void **pages, const int *nodes, int *status, int flags); /* Policies */ #define MPOL_DEFAULT 0 #define MPOL_PREFERRED 1 #define MPOL_BIND 2 #define MPOL_INTERLEAVE 3 #define MPOL_MAX MPOL_INTERLEAVE /* Flags for get_mem_policy */ #define MPOL_F_NODE (1<<0) /* return next il node or node of address */ /* Warning: MPOL_F_NODE is unsupported and subject to change. Don't use. */ #define MPOL_F_ADDR (1<<1) /* look up vma using address */ #define MPOL_F_MEMS_ALLOWED (1<<2) /* query nodes allowed in cpuset */ /* Flags for mbind */ #define MPOL_MF_STRICT (1<<0) /* Verify existing pages in the mapping */ #define MPOL_MF_MOVE (1<<1) /* Move pages owned by this process to conform to mapping */ #define MPOL_MF_MOVE_ALL (1<<2) /* Move every page to conform to mapping */ #ifdef __cplusplus } #endif #endif ./numactl-2.0.9~rc5/distance.c0000755000175000017500000000534612213661422014560 0ustar ianwianw/* Discover distances Copyright (C) 2005 Andi Kleen, SuSE Labs. libnuma is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; version 2.1. libnuma 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. See the GNU Lesser General Public License for more details. You should find a copy of v2.1 of the GNU Lesser General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA All calls are undefined when numa_available returns an error. */ #define _GNU_SOURCE 1 #include #include #include #include "numa.h" #include "numaint.h" static int distance_numnodes; static int *distance_table; static void parse_numbers(char *s, int *iptr) { int i, d, j; char *end; int maxnode = numa_max_node(); int numnodes = 0; for (i = 0; i <= maxnode; i++) if (numa_bitmask_isbitset(numa_nodes_ptr, i)) numnodes++; for (i = 0, j = 0; i <= maxnode; i++, j++) { d = strtoul(s, &end, 0); /* Skip unavailable nodes */ while (j<=maxnode && !numa_bitmask_isbitset(numa_nodes_ptr, j)) j++; *(iptr+j) = d; if (s == end) break; s = end; } } static int read_distance_table(void) { int nd, len; char *line = NULL; size_t linelen = 0; int maxnode = numa_max_node() + 1; int *table = NULL; int err = -1; for (nd = 0;; nd++) { char fn[100]; FILE *dfh; sprintf(fn, "/sys/devices/system/node/node%d/distance", nd); dfh = fopen(fn, "r"); if (!dfh) { if (errno == ENOENT) err = 0; if (!err && nd/numastat statistics. In 2012, numastat was rewritten as a C program by Red Hat to display per-node memory data for applications and the system in general, while also remaining strictly compatible by default with the original numastat. A copy of the original numastat perl script is included for reference at the end of this file. */ // Compile with: gcc -O -std=gnu99 -Wall -o numastat numastat.c #define __USE_MISC #include #include #include #include #include #include #include #include #include #include #define STRINGIZE(s) #s #define STRINGIFY(s) STRINGIZE(s) #define KILOBYTE (1024) #define MEGABYTE (1024 * 1024) #define BUF_SIZE 2048 #define SMALL_BUF_SIZE 128 // Don't assume nodes are sequential or contiguous. // Need to discover and map node numbers. int *node_ix_map = NULL; char **node_header; // Structure to organize memory info from /proc//numa_maps for a specific // process, or from /sys/devices/system/node/node?/meminfo for system-wide // data. Tables are defined below for each process and for system-wide data. typedef struct meminfo { int index; char *token; char *label; } meminfo_t, *meminfo_p; #define PROCESS_HUGE_INDEX 0 #define PROCESS_PRIVATE_INDEX 3 meminfo_t process_meminfo[] = { { PROCESS_HUGE_INDEX, "huge", "Huge" }, { 1, "heap", "Heap" }, { 2, "stack", "Stack" }, { PROCESS_PRIVATE_INDEX, "N", "Private" } }; #define PROCESS_MEMINFO_ROWS (sizeof(process_meminfo) / sizeof(process_meminfo[0])) meminfo_t numastat_meminfo[] = { { 0, "numa_hit", "Numa_Hit" }, { 1, "numa_miss", "Numa_Miss" }, { 2, "numa_foreign", "Numa_Foreign" }, { 3, "interleave_hit", "Interleave_Hit" }, { 4, "local_node", "Local_Node" }, { 5, "other_node", "Other_Node" }, }; #define NUMASTAT_MEMINFO_ROWS (sizeof(numastat_meminfo) / sizeof(numastat_meminfo[0])) meminfo_t system_meminfo[] = { { 0, "MemTotal", "MemTotal" }, { 1, "MemFree", "MemFree" }, { 2, "MemUsed", "MemUsed" }, { 3, "HighTotal", "HighTotal" }, { 4, "HighFree", "HighFree" }, { 5, "LowTotal", "LowTotal" }, { 6, "LowFree", "LowFree" }, { 7, "Active", "Active" }, { 8, "Inactive", "Inactive" }, { 9, "Active(anon)", "Active(anon)" }, { 10, "Inactive(anon)", "Inactive(anon)" }, { 11, "Active(file)", "Active(file)" }, { 12, "Inactive(file)", "Inactive(file)" }, { 13, "Unevictable", "Unevictable" }, { 14, "Mlocked", "Mlocked" }, { 15, "Dirty", "Dirty" }, { 16, "Writeback", "Writeback" }, { 17, "FilePages", "FilePages" }, { 18, "Mapped", "Mapped" }, { 19, "AnonPages", "AnonPages" }, { 20, "Shmem", "Shmem" }, { 21, "KernelStack", "KernelStack" }, { 22, "PageTables", "PageTables" }, { 23, "NFS_Unstable", "NFS_Unstable" }, { 24, "Bounce", "Bounce" }, { 25, "WritebackTmp", "WritebackTmp" }, { 26, "Slab", "Slab" }, { 27, "SReclaimable", "SReclaimable" }, { 28, "SUnreclaim", "SUnreclaim" }, { 29, "AnonHugePages", "AnonHugePages" }, { 30, "HugePages_Total", "HugePages_Total" }, { 31, "HugePages_Free", "HugePages_Free" }, { 32, "HugePages_Surp", "HugePages_Surp" } }; #define SYSTEM_MEMINFO_ROWS (sizeof(system_meminfo) / sizeof(system_meminfo[0])) // To allow re-ordering the meminfo memory categories in system_meminfo and // numastat_meminfo relative to order in /proc, etc., a simple hash index is // used to look up the meminfo categories. The allocated hash table size must // be bigger than necessary to reduce collisions (and because these specific // hash algorithms depend on having some unused buckets. #define HASH_TABLE_SIZE 151 int hash_collisions = 0; struct hash_entry { char *name; int index; } hash_table[HASH_TABLE_SIZE]; void init_hash_table() { memset(hash_table, 0, sizeof(hash_table)); } int hash_ix(char *s) { unsigned int h = 17; while (*s) { // h * 33 + *s++ h = ((h << 5) + h) + *s++; } return (h % HASH_TABLE_SIZE); } int hash_lookup(char *s) { int ix = hash_ix(s); while (hash_table[ix].name) { // Assumes big table with blank entries if (!strcmp(s, hash_table[ix].name)) { return hash_table[ix].index; // found it } ix += 1; if (ix >= HASH_TABLE_SIZE) { ix = 0; } } return -1; } int hash_insert(char *s, int i) { int ix = hash_ix(s); while (hash_table[ix].name) { // assumes no duplicate entries hash_collisions += 1; ix += 1; if (ix >= HASH_TABLE_SIZE) { ix = 0; } } hash_table[ix].name = s; hash_table[ix].index = i; return ix; } // To decouple details of table display (e.g. column width, line folding for // display screen width, et cetera) from acquiring the data and populating the // tables, this semi-general table handling code is used. There are various // routines to set table attributes, assign and test some cell contents, // initialize and actually display the table. #define CELL_TYPE_NULL 0 #define CELL_TYPE_LONG 1 #define CELL_TYPE_DOUBLE 2 #define CELL_TYPE_STRING 3 #define CELL_TYPE_CHAR8 4 #define CELL_TYPE_REPCHAR 5 #define CELL_FLAG_FREEABLE (1 << 0) #define CELL_FLAG_ROWSPAN (1 << 1) #define CELL_FLAG_COLSPAN (1 << 2) #define COL_JUSTIFY_LEFT (1 << 0) #define COL_JUSTIFY_RIGHT (1 << 1) #define COL_JUSTIFY_CENTER 3 #define COL_JUSTIFY_MASK 0x3 #define COL_FLAG_SEEN_DATA (1 << 2) #define COL_FLAG_NON_ZERO_DATA (1 << 3) #define COL_FLAG_ALWAYS_SHOW (1 << 4) #define ROW_FLAG_SEEN_DATA COL_FLAG_SEEN_DATA #define ROW_FLAG_NON_ZERO_DATA COL_FLAG_NON_ZERO_DATA #define ROW_FLAG_ALWAYS_SHOW COL_FLAG_ALWAYS_SHOW typedef struct cell { uint32_t type; uint32_t flags; union { char *s; double d; int64_t l; char c[8]; }; } cell_t, *cell_p; typedef struct vtab { int header_rows; int header_cols; int data_rows; int data_cols; cell_p cell; int *row_ix_map; uint8_t *row_flags; uint8_t *col_flags; uint8_t *col_width; uint8_t *col_decimal_places; } vtab_t, *vtab_p; #define ALL_TABLE_ROWS (table->header_rows + table->data_rows) #define ALL_TABLE_COLS (table->header_cols + table->data_cols) #define GET_CELL_PTR(row, col) (&table->cell[(row * ALL_TABLE_COLS) + col]) #define USUAL_GUTTER_WIDTH 1 void set_row_flag(vtab_p table, int row, int flag) { table->row_flags[row] |= (uint8_t)flag; } void set_col_flag(vtab_p table, int col, int flag) { table->col_flags[col] |= (uint8_t)flag; } void clear_row_flag(vtab_p table, int row, int flag) { table->row_flags[row] &= (uint8_t)~flag; } void clear_col_flag(vtab_p table, int col, int flag) { table->col_flags[col] &= (uint8_t)~flag; } int test_row_flag(vtab_p table, int row, int flag) { return ((table->row_flags[row] & (uint8_t)flag) != 0); } int test_col_flag(vtab_p table, int col, int flag) { return ((table->col_flags[col] & (uint8_t)flag) != 0); } void set_col_justification(vtab_p table, int col, int justify) { table->col_flags[col] &= (uint8_t)~COL_JUSTIFY_MASK; table->col_flags[col] |= (uint8_t)(justify & COL_JUSTIFY_MASK); } void set_col_width(vtab_p table, int col, uint8_t width) { if (width >= SMALL_BUF_SIZE) { width = SMALL_BUF_SIZE - 1; } table->col_width[col] = width; } void set_col_decimal_places(vtab_p table, int col, uint8_t places) { table->col_decimal_places[col] = places; } void set_cell_flag(vtab_p table, int row, int col, int flag) { cell_p c_ptr = GET_CELL_PTR(row, col); c_ptr->flags |= (uint32_t)flag; } void clear_cell_flag(vtab_p table, int row, int col, int flag) { cell_p c_ptr = GET_CELL_PTR(row, col); c_ptr->flags &= (uint32_t)~flag; } int test_cell_flag(vtab_p table, int row, int col, int flag) { cell_p c_ptr = GET_CELL_PTR(row, col); return ((c_ptr->flags & (uint32_t)flag) != 0); } void string_assign(vtab_p table, int row, int col, char *s) { cell_p c_ptr = GET_CELL_PTR(row, col); c_ptr->type = CELL_TYPE_STRING; c_ptr->s = s; } void repchar_assign(vtab_p table, int row, int col, char c) { cell_p c_ptr = GET_CELL_PTR(row, col); c_ptr->type = CELL_TYPE_REPCHAR; c_ptr->c[0] = c; } void double_assign(vtab_p table, int row, int col, double d) { cell_p c_ptr = GET_CELL_PTR(row, col); c_ptr->type = CELL_TYPE_DOUBLE; c_ptr->d = d; } void long_assign(vtab_p table, int row, int col, int64_t l) { cell_p c_ptr = GET_CELL_PTR(row, col); c_ptr->type = CELL_TYPE_LONG; c_ptr->l = l; } void double_addto(vtab_p table, int row, int col, double d) { cell_p c_ptr = GET_CELL_PTR(row, col); c_ptr->type = CELL_TYPE_DOUBLE; c_ptr->d += d; } void long_addto(vtab_p table, int row, int col, int64_t l) { cell_p c_ptr = GET_CELL_PTR(row, col); c_ptr->type = CELL_TYPE_LONG; c_ptr->l += l; } void clear_assign(vtab_p table, int row, int col) { cell_p c_ptr = GET_CELL_PTR(row, col); memset(c_ptr, 0, sizeof(cell_t)); } void zero_table_data(vtab_p table, int type) { // Sets data area of table to zeros of specified type for (int row = table->header_rows; (row < ALL_TABLE_ROWS); row++) { for (int col = table->header_cols; (col < ALL_TABLE_COLS); col++) { cell_p c_ptr = GET_CELL_PTR(row, col); memset(c_ptr, 0, sizeof(cell_t)); c_ptr->type = type; } } } void sort_rows_descending_by_col(vtab_p table, int start_row, int stop_row, int col) { // Rearrange row_ix_map[] indices so the rows will be in // descending order by the value in the specified column for (int ix = start_row; (ix <= stop_row); ix++) { int biggest_ix = ix; cell_p biggest_ix_c_ptr = GET_CELL_PTR(table->row_ix_map[ix], col); for (int iy = ix + 1; (iy <= stop_row); iy++) { cell_p iy_c_ptr = GET_CELL_PTR(table->row_ix_map[iy], col); if (biggest_ix_c_ptr->d < iy_c_ptr->d) { biggest_ix_c_ptr = iy_c_ptr; biggest_ix = iy; } } if (biggest_ix != ix) { int tmp = table->row_ix_map[ix]; table->row_ix_map[ix] = table->row_ix_map[biggest_ix]; table->row_ix_map[biggest_ix] = tmp; } } } void span(vtab_p table, int first_row, int first_col, int last_row, int last_col) { // FIXME: implement row / col spannnig someday? } void init_table(vtab_p table, int header_rows, int header_cols, int data_rows, int data_cols) { // init table sizes table->header_rows = header_rows; table->header_cols = header_cols; table->data_rows = data_rows; table->data_cols = data_cols; // allocate memory for all the cells int alloc_size = ALL_TABLE_ROWS * ALL_TABLE_COLS * sizeof(cell_t); table->cell = malloc(alloc_size); if (table->cell == NULL) { perror("malloc failed line: " STRINGIFY(__LINE__)); exit(EXIT_FAILURE); } memset(table->cell, 0, alloc_size); // allocate memory for the row map vector alloc_size = ALL_TABLE_ROWS * sizeof(int); table->row_ix_map = malloc(alloc_size); if (table->row_ix_map == NULL) { perror("malloc failed line: " STRINGIFY(__LINE__)); exit(EXIT_FAILURE); } for (int row = 0; (row < ALL_TABLE_ROWS); row++) { table->row_ix_map[row] = row; } // allocate memory for the row flags vector alloc_size = ALL_TABLE_ROWS * sizeof(uint8_t); table->row_flags = malloc(alloc_size); if (table->row_flags == NULL) { perror("malloc failed line: " STRINGIFY(__LINE__)); exit(EXIT_FAILURE); } memset(table->row_flags, 0, alloc_size); // allocate memory for the column flags vector alloc_size = ALL_TABLE_COLS * sizeof(uint8_t); table->col_flags = malloc(alloc_size); if (table->col_flags == NULL) { perror("malloc failed line: " STRINGIFY(__LINE__)); exit(EXIT_FAILURE); } memset(table->col_flags, 0, alloc_size); // allocate memory for the column width vector alloc_size = ALL_TABLE_COLS * sizeof(uint8_t); table->col_width = malloc(alloc_size); if (table->col_width == NULL) { perror("malloc failed line: " STRINGIFY(__LINE__)); exit(EXIT_FAILURE); } memset(table->col_width, 0, alloc_size); // allocate memory for the column precision vector alloc_size = ALL_TABLE_COLS * sizeof(uint8_t); table->col_decimal_places = malloc(alloc_size); if (table->col_decimal_places == NULL) { perror("malloc failed line: " STRINGIFY(__LINE__)); exit(EXIT_FAILURE); } memset(table->col_decimal_places, 0, alloc_size); } void free_cell(vtab_p table, int row, int col) { cell_p c_ptr = GET_CELL_PTR(row, col); if ((c_ptr->type == CELL_TYPE_STRING) && (c_ptr->flags & CELL_FLAG_FREEABLE) && (c_ptr->s != NULL)) { free(c_ptr->s); } memset(c_ptr, 0, sizeof(cell_t)); } void free_table(vtab_p table) { if (table->cell != NULL) { for (int row = 0; (row < ALL_TABLE_ROWS); row++) { for (int col = 0; (col < ALL_TABLE_COLS); col++) { free_cell(table, row, col); } } free(table->cell); } if (table->row_ix_map != NULL) { free(table->row_ix_map); } if (table->row_flags != NULL) { free(table->row_flags); } if (table->col_flags != NULL) { free(table->col_flags); } if (table->col_width != NULL) { free(table->col_width); } if (table->col_decimal_places != NULL) { free(table->col_decimal_places); } } char *fmt_cell_data(cell_p c_ptr, int max_width, int decimal_places) { // Returns pointer to a static buffer, expecting caller to // immediately use or copy the contents before calling again. int rep_width = max_width - USUAL_GUTTER_WIDTH; static char buf[SMALL_BUF_SIZE]; switch (c_ptr->type) { case CELL_TYPE_NULL: buf[0] = '\0'; break; case CELL_TYPE_LONG: snprintf(buf, SMALL_BUF_SIZE, "%ld", c_ptr->l); break; case CELL_TYPE_DOUBLE: snprintf(buf, SMALL_BUF_SIZE, "%.*f", decimal_places, c_ptr->d); break; case CELL_TYPE_STRING: snprintf(buf, SMALL_BUF_SIZE, "%s", c_ptr->s); break; case CELL_TYPE_CHAR8: strncpy(buf, c_ptr->c, 8); buf[8] = '\0'; break; case CELL_TYPE_REPCHAR: memset(buf, c_ptr->c[0], rep_width); buf[rep_width] = '\0'; break; default: strcpy(buf, "Unknown"); break; } buf[max_width] = '\0'; return buf; } void auto_set_col_width(vtab_p table, int col, int min_width, int max_width) { int width = min_width; for (int row = 0; (row < ALL_TABLE_ROWS); row++) { cell_p c_ptr = GET_CELL_PTR(row, col); if (c_ptr->type == CELL_TYPE_REPCHAR) { continue; } char *p = fmt_cell_data(c_ptr, max_width, (int)(table->col_decimal_places[col])); int l = strlen(p); if (width < l) { width = l; } } width += USUAL_GUTTER_WIDTH; if (width > max_width) { width = max_width; } table->col_width[col] = (uint8_t)width; } void display_justified_cell(cell_p c_ptr, int row_flags, int col_flags, int width, int decimal_places) { char *p = fmt_cell_data(c_ptr, width, decimal_places); int l = strlen(p); char buf[SMALL_BUF_SIZE]; switch (col_flags & COL_JUSTIFY_MASK) { case COL_JUSTIFY_LEFT: memcpy(buf, p, l); if (l < width) { memset(&buf[l], ' ', width - l); } break; case COL_JUSTIFY_RIGHT: if (l < width) { memset(buf, ' ', width - l); } memcpy(&buf[width - l], p, l); break; case COL_JUSTIFY_CENTER: default: memset(buf, ' ', width); memcpy(&buf[(width - l + 1) / 2], p, l); break; } buf[width] = '\0'; printf("%s", buf); } void display_table(vtab_p table, int screen_width, int show_unseen_rows, int show_unseen_cols, int show_zero_rows, int show_zero_cols) { // Set row and column flags according to whether data in rows and cols // has been assigned, and is currently non-zero. int some_seen_data = 0; int some_non_zero_data = 0; for (int row = table->header_rows; (row < ALL_TABLE_ROWS); row++) { for (int col = table->header_cols; (col < ALL_TABLE_COLS); col++) { cell_p c_ptr = GET_CELL_PTR(row, col); // Currently, "seen data" includes not only numeric data, but also // any strings, etc -- anything non-NULL (other than rephcars). if ((c_ptr->type != CELL_TYPE_NULL) && (c_ptr->type != CELL_TYPE_REPCHAR)) { some_seen_data = 1; set_row_flag(table, row, ROW_FLAG_SEEN_DATA); set_col_flag(table, col, COL_FLAG_SEEN_DATA); // Currently, "non-zero data" includes not only numeric data, // but also any strings, etc -- anything non-zero (other than // repchars, which are already excluded above). So, note a // valid non-NULL pointer to an empty string would still be // counted as non-zero data. if (c_ptr->l != (int64_t)0) { some_non_zero_data = 1; set_row_flag(table, row, ROW_FLAG_NON_ZERO_DATA); set_col_flag(table, col, COL_FLAG_NON_ZERO_DATA); } } } } if (!some_seen_data) { printf("Table has no data.\n"); return; } if (!some_non_zero_data && !show_zero_rows && !show_zero_cols) { printf("Table has no non-zero data.\n"); return; } // Start with first data column and try to display table, // folding lines as necessary per screen_width int col = -1; int data_col = table->header_cols; while (data_col < ALL_TABLE_COLS) { // Skip data columns until we have one to display if ((!test_col_flag(table, data_col, COL_FLAG_ALWAYS_SHOW)) && (((!show_unseen_cols) && (!test_col_flag(table, data_col, COL_FLAG_SEEN_DATA))) || ((!show_zero_cols) && (!test_col_flag(table, data_col, COL_FLAG_NON_ZERO_DATA))))) { data_col += 1; continue; } // Display blank line between table sections if (col > 0) { printf("\n"); } // For each row, display as many columns as possible for (int row_ix = 0; (row_ix < ALL_TABLE_ROWS); row_ix++) { int row = table->row_ix_map[row_ix]; // If past the header rows, conditionally skip rows if ((row >= table->header_rows) && (!test_row_flag(table, row, ROW_FLAG_ALWAYS_SHOW))) { // Optionally skip row if no data seen or if all zeros if (((!show_unseen_rows) && (!test_row_flag(table, row, ROW_FLAG_SEEN_DATA))) || ((!show_zero_rows) && (!test_row_flag(table, row, ROW_FLAG_NON_ZERO_DATA)))) { continue; } } // Begin a new row... int cur_line_width = 0; // All lines start with the left header columns for (col = 0; (col < table->header_cols); col++) { display_justified_cell(GET_CELL_PTR(row, col), (int)(table->row_flags[row]), (int)(table->col_flags[col]), (int)(table->col_width[col]), (int)(table->col_decimal_places[col])); cur_line_width += (int)(table->col_width[col]); } // Reset column index to starting data column for each new row col = data_col; // Try to display as many data columns as possible in every section for (;;) { // See if we should print this column if (test_col_flag(table, col, COL_FLAG_ALWAYS_SHOW) || (((show_unseen_cols) || (test_col_flag(table, col, COL_FLAG_SEEN_DATA))) && ((show_zero_cols) || (test_col_flag(table, col, COL_FLAG_NON_ZERO_DATA))))) { display_justified_cell(GET_CELL_PTR(row, col), (int)(table->row_flags[row]), (int)(table->col_flags[col]), (int)(table->col_width[col]), (int)(table->col_decimal_places[col])); cur_line_width += (int)(table->col_width[col]); } col += 1; // End the line if no more columns or next column would exceed screen width if ((col >= ALL_TABLE_COLS) || ((cur_line_width + (int)(table->col_width[col])) > screen_width)) { break; } } printf("\n"); } // Remember next starting data column for next section data_col = col; } } int verbose = 0; int num_pids = 0; int num_nodes = 0; int screen_width = 0; int show_zero_data = 1; int compress_display = 0; int sort_table = 0; int sort_table_node = -1; int compatibility_mode = 0; int pid_array_max_pids = 0; int *pid_array = NULL; char *prog_name = NULL; double page_size_in_bytes = 0; double huge_page_size_in_bytes = 0; void display_version_and_exit() { char *version_string = "20130723"; printf("%s version: %s: %s\n", prog_name, version_string, __DATE__); exit(EXIT_SUCCESS); } void display_usage_and_exit() { fprintf(stderr, "Usage: %s [-c] [-m] [-n] [-p |] [-s[]] [-v] [-V] [-z] [ |... ]\n", prog_name); fprintf(stderr, "-c to minimize column widths\n"); fprintf(stderr, "-m to show meminfo-like system-wide memory usage\n"); fprintf(stderr, "-n to show the numastat statistics info\n"); fprintf(stderr, "-p | to show process info\n"); fprintf(stderr, "-s[] to sort data by total column or \n"); fprintf(stderr, "-v to make some reports more verbose\n"); fprintf(stderr, "-V to show the %s code version\n", prog_name); fprintf(stderr, "-z to skip rows and columns of zeros\n"); exit(EXIT_FAILURE); } int get_screen_width() { int width = 80; char *p = getenv("NUMASTAT_WIDTH"); if (p != NULL) { width = atoi(p); if ((width < 1) || (width > 10000000)) { width = 80; } } else if (isatty(fileno(stdout))) { FILE *fs = popen("resize 2>/dev/null", "r"); if (fs != NULL) { char columns[72]; fgets(columns, sizeof(columns), fs); pclose(fs); if (strncmp(columns, "COLUMNS=", 8) == 0) { width = atoi(&columns[8]); if ((width < 1) || (width > 10000000)) { width = 80; } } } } else { // Not a tty, so allow a really long line width = 10000000; } if (width < 32) { width = 32; } return width; } char *command_name_for_pid(int pid) { // Get the PID command name field from /proc/PID/status file. Return // pointer to a static buffer, expecting caller to immediately copy result. static char buf[SMALL_BUF_SIZE]; char fname[64]; snprintf(fname, sizeof(fname), "/proc/%d/status", pid); FILE *fs = fopen(fname, "r"); if (!fs) { return NULL; } else { while (fgets(buf, SMALL_BUF_SIZE, fs)) { if (strstr(buf, "Name:") == buf) { char *p = &buf[5]; while (isspace(*p)) { p++; } if (p[strlen(p) - 1] == '\n') { p[strlen(p) - 1] = '\0'; } fclose(fs); return p; } } fclose(fs); } return NULL; } void show_info_from_system_file(char *file, meminfo_p meminfo, int meminfo_rows, int tok_offset) { // Setup and init table vtab_t table; int header_rows = 2 - compatibility_mode; int header_cols = 1; // Add an extra data column for a total column init_table(&table, header_rows, header_cols, meminfo_rows, num_nodes + 1); int total_col_ix = header_cols + num_nodes; // Insert token mapping in hash table and assign left header column label for each row in table init_hash_table(); for (int row = 0; (row < meminfo_rows); row++) { hash_insert(meminfo[row].token, meminfo[row].index); if (compatibility_mode) { string_assign(&table, (header_rows + row), 0, meminfo[row].token); } else { string_assign(&table, (header_rows + row), 0, meminfo[row].label); } } // printf("There are %d table hash collisions.\n", hash_collisions); // Set left header column width and left justify it set_col_width(&table, 0, 16); set_col_justification(&table, 0, COL_JUSTIFY_LEFT); // Open /sys/devices/system/node/node?/ for each node and store data // in table. If not compatibility_mode, do approximately first third of // this loop also for (node_ix == num_nodes) to get "Total" column header. for (int node_ix = 0; (node_ix < (num_nodes + (1 - compatibility_mode))); node_ix++) { int col = header_cols + node_ix; // Assign header row label and horizontal line for this column... string_assign(&table, 0, col, node_header[node_ix]); if (!compatibility_mode) { repchar_assign(&table, 1, col, '-'); int decimal_places = 2; if (compress_display) { decimal_places = 0; } set_col_decimal_places(&table, col, decimal_places); } // Set column width and right justify data set_col_width(&table, col, 16); set_col_justification(&table, col, COL_JUSTIFY_RIGHT); if (node_ix == num_nodes) { break; } // Open /sys/.../node/numstast file for this node... char buf[SMALL_BUF_SIZE]; char fname[64]; snprintf(fname, sizeof(fname), "/sys/devices/system/node/node%d/%s", node_ix_map[node_ix], file); FILE *fs = fopen(fname, "r"); if (!fs) { sprintf(buf, "cannot open %s", fname); perror(buf); exit(EXIT_FAILURE); } // Get table values for this node... while (fgets(buf, SMALL_BUF_SIZE, fs)) { char *tok[64]; int tokens = 0; const char *delimiters = " \t\r\n:"; char *p = strtok(buf, delimiters); if (p == NULL) { continue; // Skip blank lines; } while (p) { tok[tokens++] = p; p = strtok(NULL, delimiters); } // example line from numastat file: "numa_miss 16463" // example line from meminfo file: "Node 3 Inactive: 210680 kB" int index = hash_lookup(tok[0 + tok_offset]); if (index < 0) { printf("Token %s not in hash table.\n", tok[0]); } else { double value = (double)atol(tok[1 + tok_offset]); if (!compatibility_mode) { double multiplier = 1.0; if (tokens < 4) { multiplier = page_size_in_bytes; } else if (!strncmp("HugePages", tok[2], 9)) { multiplier = huge_page_size_in_bytes; } else if (!strncmp("kB", tok[4], 2)) { multiplier = KILOBYTE; } value *= multiplier; value /= (double)MEGABYTE; } double_assign(&table, header_rows + index, col, value); double_addto(&table, header_rows + index, total_col_ix, value); } } fclose(fs); } // Crompress display column widths, if requested if (compress_display) { for (int col = 0; (col < header_cols + num_nodes + 1); col++) { auto_set_col_width(&table, col, 4, 16); } } // Optionally sort the table data if (sort_table) { int sort_col; if ((sort_table_node < 0) || (sort_table_node >= num_nodes)) { sort_col = total_col_ix; } else { sort_col = header_cols + node_ix_map[sort_table_node]; } sort_rows_descending_by_col(&table, header_rows, header_rows + meminfo_rows - 1, sort_col); } // Actually display the table now, doing line-folding as necessary display_table(&table, screen_width, 0, 0, show_zero_data, show_zero_data); free_table(&table); } void show_numastat_info() { if (!compatibility_mode) { printf("\nPer-node numastat info (in MBs):\n"); } show_info_from_system_file("numastat", numastat_meminfo, NUMASTAT_MEMINFO_ROWS, 0); } void show_system_info() { printf("\nPer-node system memory usage (in MBs):\n"); show_info_from_system_file("meminfo", system_meminfo, SYSTEM_MEMINFO_ROWS, 2); } void show_process_info() { vtab_t table; int header_rows = 2; int header_cols = 1; int data_rows; int show_sub_categories = (verbose || (num_pids == 1)); if (show_sub_categories) { data_rows = PROCESS_MEMINFO_ROWS; } else { data_rows = num_pids; } // Add two extra rows for a horizontal rule followed by a total row // Add one extra data column for a total column init_table(&table, header_rows, header_cols, data_rows + 2, num_nodes + 1); int total_col_ix = header_cols + num_nodes; int total_row_ix = header_rows + data_rows + 1; string_assign(&table, total_row_ix, 0, "Total"); if (show_sub_categories) { // Assign left header column label for each row in table for (int row = 0; (row < PROCESS_MEMINFO_ROWS); row++) { string_assign(&table, (header_rows + row), 0, process_meminfo[row].label); } } else { string_assign(&table, 0, 0, "PID"); repchar_assign(&table, 1, 0, '-'); printf("\nPer-node process memory usage (in MBs)\n"); } // Set left header column width and left justify it set_col_width(&table, 0, 16); set_col_justification(&table, 0, COL_JUSTIFY_LEFT); // Set up "Node " column headers over data columns, plus "Total" column for (int node_ix = 0; (node_ix <= num_nodes); node_ix++) { int col = header_cols + node_ix; // Assign header row label and horizontal line for this column... string_assign(&table, 0, col, node_header[node_ix]); repchar_assign(&table, 1, col, '-'); // Set column width, decimal places, and right justify data set_col_width(&table, col, 16); int decimal_places = 2; if (compress_display) { decimal_places = 0; } set_col_decimal_places(&table, col, decimal_places); set_col_justification(&table, col, COL_JUSTIFY_RIGHT); } // Initialize data in table to all zeros zero_table_data(&table, CELL_TYPE_DOUBLE); // If (show_sub_categories), show individual process tables for each PID, // Otherwise show one big table of process total lines from all the PIDs. for (int pid_ix = 0; (pid_ix < num_pids); pid_ix++) { int pid = pid_array[pid_ix]; if (show_sub_categories) { printf("\nPer-node process memory usage (in MBs) for PID %d (%s)\n", pid, command_name_for_pid(pid)); if (pid_ix > 0) { // Re-initialize show_sub_categories table, because we re-use it for each PID. zero_table_data(&table, CELL_TYPE_DOUBLE); } } else { // Put this row's "PID (cmd)" label in left header column for this PID total row char tmp_buf[64]; snprintf(tmp_buf, sizeof(tmp_buf), "%d (%s)", pid, command_name_for_pid(pid)); char *p = strdup(tmp_buf); if (p == NULL) { perror("malloc failed line: " STRINGIFY(__LINE__)); exit(EXIT_FAILURE); } string_assign(&table, header_rows + pid_ix, 0, p); set_cell_flag(&table, header_rows + pid_ix, 0, CELL_FLAG_FREEABLE); } // Open numa_map for this PID to get per-node data char fname[64]; snprintf(fname, sizeof(fname), "/proc/%d/numa_maps", pid); char buf[BUF_SIZE]; FILE *fs = fopen(fname, "r"); if (!fs) { sprintf(buf, "Can't read /proc/%d/numa_maps", pid); perror(buf); continue; } // Add up sub-category memory used from each node. Must go line by line // through the numa_map figuring out which category memory, node, and the // amount. while (fgets(buf, BUF_SIZE, fs)) { int category = PROCESS_PRIVATE_INDEX; // init category to the catch-all... const char *delimiters = " \t\r\n"; char *p = strtok(buf, delimiters); while (p) { // If the memory category for this line is still the catch-all // (i.e. private), then see if the current token is a special // keyword for a specific memory sub-category. if (category == PROCESS_PRIVATE_INDEX) { for (int ix = 0; (ix < PROCESS_PRIVATE_INDEX); ix++) { if (!strncmp(p, process_meminfo[ix].token, strlen(process_meminfo[ix].token))) { category = ix; break; } } } // If the current token is a per-node pages quantity, parse the // node number and accumulate the number of pages in the specific // category (and also add to the total). if (p[0] == 'N') { int node_num = (int)strtol(&p[1], &p, 10); if (p[0] != '=') { perror("node value parse error"); exit(EXIT_FAILURE); } double value = (double)strtol(&p[1], &p, 10); double multiplier = page_size_in_bytes; if (category == PROCESS_HUGE_INDEX) { multiplier = huge_page_size_in_bytes; } value *= multiplier; value /= (double)MEGABYTE; // Add value to data cell, total_col, and total_row int tmp_row; if (show_sub_categories) { tmp_row = header_rows + category; } else { tmp_row = header_rows + pid_ix; } int tmp_col = header_cols + node_num; double_addto(&table, tmp_row, tmp_col, value); double_addto(&table, tmp_row, total_col_ix, value); double_addto(&table, total_row_ix, tmp_col, value); double_addto(&table, total_row_ix, total_col_ix, value); } // Get next token on the line p = strtok(NULL, delimiters); } } // Currently, a non-root user can open some numa_map files successfully // without error, but can't actually read the contents -- despite the // 444 file permissions. So, use ferror() to check here to see if we // actually got a read error, and if so, alert the user so they know // not to trust the zero in the table. if (ferror(fs)) { sprintf(buf, "Can't read /proc/%d/numa_maps", pid); perror(buf); } fclose(fs); // If showing individual tables, or we just added the last total line, // prepare the table for display and display it... if ((show_sub_categories) || (pid_ix + 1 == num_pids)) { // Crompress display column widths, if requested if (compress_display) { for (int col = 0; (col < header_cols + num_nodes + 1); col++) { auto_set_col_width(&table, col, 4, 16); } } else { // Since not compressing the display, allow the left header // column to be wider. Otherwise, sometimes process command // name instance numbers can be truncated in an annoying way. auto_set_col_width(&table, 0, 16, 24); } // Put dashes above Total line... set_row_flag(&table, total_row_ix - 1, COL_FLAG_ALWAYS_SHOW); for (int col = 0; (col < header_cols + num_nodes + 1); col++) { repchar_assign(&table, total_row_ix - 1, col, '-'); } // Optionally sort the table data if (sort_table) { int sort_col; if ((sort_table_node < 0) || (sort_table_node >= num_nodes)) { sort_col = total_col_ix; } else { sort_col = header_cols + node_ix_map[sort_table_node]; } sort_rows_descending_by_col(&table, header_rows, header_rows + data_rows - 1, sort_col); } // Actually show the table display_table(&table, screen_width, 0, 0, show_zero_data, show_zero_data); } } // END OF FOR_EACH-PID loop free_table(&table); } // show_process_info() int node_and_digits(const struct dirent *dptr) { char *p = (char *)(dptr->d_name); if (*p++ != 'n') return 0; if (*p++ != 'o') return 0; if (*p++ != 'd') return 0; if (*p++ != 'e') return 0; do { if (!isdigit(*p++)) return 0; } while (*p != '\0'); return 1; } void init_node_ix_map_and_header(int compatibility_mode) { // Count directory names of the form: /sys/devices/system/node/node struct dirent **namelist; num_nodes = scandir("/sys/devices/system/node", &namelist, node_and_digits, NULL); if (num_nodes < 1) { if (compatibility_mode) { perror("sysfs not mounted or system not NUMA aware"); } else { perror("Couldn't open /sys/devices/system/node"); } exit(EXIT_FAILURE); } else { node_ix_map = malloc(num_nodes * sizeof(int)); if (node_ix_map == NULL) { perror("malloc failed line: " STRINGIFY(__LINE__)); exit(EXIT_FAILURE); } // For each "node" filename present, save in node_ix_map for (int ix = 0; (ix < num_nodes); ix++) { node_ix_map[ix] = atoi(&namelist[ix]->d_name[4]); free(namelist[ix]); } free(namelist); // Now, sort the node map in increasing order. Use a simplistic sort // since we expect a relatively short (and maybe pre-ordered) list. for (int ix = 0; (ix < num_nodes); ix++) { int smallest_ix = ix; for (int iy = ix + 1; (iy < num_nodes); iy++) { if (node_ix_map[smallest_ix] > node_ix_map[iy]) { smallest_ix = iy; } } if (smallest_ix != ix) { int tmp = node_ix_map[ix]; node_ix_map[ix] = node_ix_map[smallest_ix]; node_ix_map[smallest_ix] = tmp; } } // Construct vector of "Node " and "Total" column headers. Allocate // one for each NUMA node, plus one on the end for the "Total" column node_header = malloc((num_nodes + 1) * sizeof(char *)); if (node_header == NULL) { perror("malloc failed line: " STRINGIFY(__LINE__)); exit(EXIT_FAILURE); } for (int node_ix = 0; (node_ix <= num_nodes); node_ix++) { char node_label[64]; if (node_ix == num_nodes) { strcpy(node_label, "Total"); } else if (compatibility_mode) { snprintf(node_label, sizeof(node_label), "node%d", node_ix_map[node_ix]); } else { snprintf(node_label, sizeof(node_label), "Node %d", node_ix_map[node_ix]); } char *s = strdup(node_label); if (s == NULL) { perror("malloc failed line: " STRINGIFY(__LINE__)); exit(EXIT_FAILURE); } node_header[node_ix] = s; } } } void free_node_ix_map_and_header() { if (node_ix_map != NULL) { free(node_ix_map); node_ix_map = NULL; } if (node_header != NULL) { for (int ix = 0; (ix <= num_nodes); ix++) { free(node_header[ix]); } free(node_header); node_header = NULL; } } double get_huge_page_size_in_bytes() { double huge_page_size = 0;; FILE *fs = fopen("/proc/meminfo", "r"); if (!fs) { perror("Can't open /proc/meminfo"); exit(EXIT_FAILURE); } char buf[SMALL_BUF_SIZE]; while (fgets(buf, SMALL_BUF_SIZE, fs)) { if (!strncmp("Hugepagesize", buf, 12)) { char *p = &buf[12]; while ((!isdigit(*p)) && (p < buf + SMALL_BUF_SIZE)) { p++; } huge_page_size = strtod(p, NULL); break; } } fclose(fs); return huge_page_size * KILOBYTE; } int all_digits(char *p) { if (p == NULL) { return 0; } while (*p != '\0') { if (!isdigit(*p++)) return 0; } return 1; } int starts_with_digit(const struct dirent *dptr) { return (isdigit(dptr->d_name[0])); } void add_pid_to_list(int pid) { if (num_pids < pid_array_max_pids) { pid_array[num_pids++] = pid; } else { if (pid_array_max_pids == 0) { pid_array_max_pids = 32; } int *tmp_int_ptr = realloc(pid_array, 2 * pid_array_max_pids * sizeof(int)); if (tmp_int_ptr == NULL) { char buf[SMALL_BUF_SIZE]; sprintf(buf, "Too many PIDs, skipping %d", pid); perror(buf); } else { pid_array = tmp_int_ptr; pid_array_max_pids *= 2; pid_array[num_pids++] = pid; } } } int ascending(const void *p1, const void *p2) { return *(int *)p1 - *(int *) p2; } void sort_pids_and_remove_duplicates() { if (num_pids > 1) { qsort(pid_array, num_pids, sizeof(int), ascending); int ix1 = 0; for (int ix2 = 1; (ix2 < num_pids); ix2++) { if (pid_array[ix2] == pid_array[ix1]) { continue; } ix1 += 1; if (ix2 > ix1) { pid_array[ix1] = pid_array[ix2]; } } num_pids = ix1 + 1; } } void add_pids_from_pattern_search(char *pattern) { // Search all /proc//cmdline files and /proc//status:Name fields // for matching patterns. Show the memory details for matching PIDs. int num_matches_found = 0; struct dirent **namelist; int files = scandir("/proc", &namelist, starts_with_digit, NULL); if (files < 0) { perror("Couldn't open /proc"); } for (int ix = 0; (ix < files); ix++) { char buf[BUF_SIZE]; // First get Name field from status file int pid = atoi(namelist[ix]->d_name); char *p = command_name_for_pid(pid); if (p) { strcpy(buf, p); } else { buf[0] = '\0'; } // Next copy cmdline file contents onto end of buffer. Do it a // character at a time to convert nulls to spaces. char fname[64]; snprintf(fname, sizeof(fname), "/proc/%s/cmdline", namelist[ix]->d_name); FILE *fs = fopen(fname, "r"); if (fs) { p = buf; while (*p != '\0') { p++; } *p++ = ' '; int c; while (((c = fgetc(fs)) != EOF) && (p < buf + BUF_SIZE - 1)) { if (c == '\0') { c = ' '; } *p++ = c; } *p++ = '\0'; fclose(fs); } if (strstr(buf, pattern)) { if (pid != getpid()) { add_pid_to_list(pid); num_matches_found += 1; } } free(namelist[ix]); } free(namelist); if (num_matches_found == 0) { printf("Found no processes containing pattern: \"%s\"\n", pattern); } } int main(int argc, char **argv) { prog_name = argv[0]; int show_the_system_info = 0; int show_the_numastat_info = 0; static struct option long_options[] = { {"help", 0, 0, '?'}, {0, 0, 0, 0} }; int long_option_index = 0; int opt; while ((opt = getopt_long(argc, argv, "cmnp:s::vVz?", long_options, &long_option_index)) != -1) { switch (opt) { case 0: printf("Unexpected long option %s", long_options[long_option_index].name); if (optarg) { printf(" with arg %s", optarg); } printf("\n"); display_usage_and_exit(); break; case 'c': compress_display = 1; break; case 'm': show_the_system_info = 1; break; case 'n': show_the_numastat_info = 1; break; case 'p': if ((optarg) && (all_digits(optarg))) { add_pid_to_list(atoi(optarg)); } else { add_pids_from_pattern_search(optarg); } break; case 's': sort_table = 1; if ((optarg) && (all_digits(optarg))) { sort_table_node = atoi(optarg); } break; case 'v': verbose = 1; break; case 'V': display_version_and_exit(); break; case 'z': show_zero_data = 0; break; default: case '?': display_usage_and_exit(); break; } } // Figure out the display width, which is used to format the tables // and limit the output columns per row screen_width = get_screen_width(); // Any remaining arguments are assumed to be additional process specifiers while (optind < argc) { if (all_digits(argv[optind])) { add_pid_to_list(atoi(argv[optind])); } else { add_pids_from_pattern_search(argv[optind]); } optind += 1; } // If there are no program options or arguments, be extremely compatible // with the old numastat perl script (which is included at the end of this // file for reference) compatibility_mode = (argc == 1); init_node_ix_map_and_header(compatibility_mode); // enumarate the NUMA nodes if (compatibility_mode) { show_numastat_info(); free_node_ix_map_and_header(); exit(EXIT_SUCCESS); } // Figure out page sizes page_size_in_bytes = (double)sysconf(_SC_PAGESIZE); huge_page_size_in_bytes = get_huge_page_size_in_bytes(); // Display the info for the process specifiers if (num_pids > 0) { sort_pids_and_remove_duplicates(); show_process_info(); } if (pid_array != NULL) { free(pid_array); } // Display the system-wide memory usage info if (show_the_system_info) { show_system_info(); } // Display the numastat statistics info if ((show_the_numastat_info) || ((num_pids == 0) && (!show_the_system_info))) { show_numastat_info(); } free_node_ix_map_and_header(); exit(EXIT_SUCCESS); } #if 0 /* #!/usr/bin/perl # Print numa statistics for all nodes # Copyright (C) 2003,2004 Andi Kleen, SuSE Labs. # # numastat 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; version # 2. # # numastat 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. See the GNU # General Public License for more details. # You should find a copy of v2 of the GNU General Public License somewhere # on your Linux system; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Example: NUMASTAT_WIDTH=80 watch -n1 numastat # # output width $WIDTH=80; if (defined($ENV{'NUMASTAT_WIDTH'})) { $WIDTH=$ENV{'NUMASTAT_WIDTH'}; } else { use POSIX; if (POSIX::isatty(fileno(STDOUT))) { if (open(R, "resize |")) { while () { $WIDTH=$1 if /COLUMNS=(\d+)/; } close R; } } else { # don't split it up for easier parsing $WIDTH=10000000; } } $WIDTH = 32 if $WIDTH < 32; if (! -d "/sys/devices/system/node" ) { print STDERR "sysfs not mounted or system not NUMA aware\n"; exit 1; } %stat = (); $title = ""; $mode = 0; opendir(NODES, "/sys/devices/system/node") || exit 1; foreach $nd (readdir(NODES)) { next unless $nd =~ /node(\d+)/; # On newer kernels, readdir may enumerate the 'node(\d+) subdirs # in opposite order from older kernels--e.g., node{0,1,2,...} # as opposed to node{N,N-1,N-2,...}. Accomodate this by # switching to new mode so that the stats get emitted in # the same order. #print "readdir(NODES) returns $nd\n"; if (!$title && $nd =~ /node0/) { $mode = 1; } open(STAT, "/sys/devices/system/node/$nd/numastat") || die "cannot open $nd: $!\n"; if (! $mode) { $title = sprintf("%16s",$nd) . $title; } else { $title = $title . sprintf("%16s",$nd); } @fields = (); while () { ($name, $val) = split; if (! $mode) { $stat{$name} = sprintf("%16u", $val) . $stat{$name}; } else { $stat{$name} = $stat{$name} . sprintf("%16u", $val); } push(@fields, $name); } close STAT; } closedir NODES; $numfields = int(($WIDTH - 16) / 16); $l = 16 * $numfields; for ($i = 0; $i < length($title); $i += $l) { print "\n" if $i > 0; printf "%16s%s\n","",substr($title,$i,$l); foreach (@fields) { printf "%-16s%s\n",$_,substr($stat{$_},$i,$l); } } */ #endif ./numactl-2.0.9~rc5/numaint.h0000755000175000017500000000316512213661422014443 0ustar ianwianw/* Internal interfaces of libnuma */ #include "bitops.h" extern int numa_sched_setaffinity_v1(pid_t pid, unsigned len, const unsigned long *mask); extern int numa_sched_getaffinity_v1(pid_t pid, unsigned len, const unsigned long *mask); extern int numa_sched_setaffinity_v1_int(pid_t pid, unsigned len,const unsigned long *mask); extern int numa_sched_getaffinity_v1_int(pid_t pid, unsigned len,const unsigned long *mask); extern int numa_sched_setaffinity_v2(pid_t pid, struct bitmask *mask); extern int numa_sched_getaffinity_v2(pid_t pid, struct bitmask *mask); extern int numa_sched_setaffinity_v2_int(pid_t pid, struct bitmask *mask); extern int numa_sched_getaffinity_v2_int(pid_t pid, struct bitmask *mask); #define SHM_HUGETLB 04000 /* segment will use huge TLB pages */ #define CPU_BYTES(x) (round_up(x, BITS_PER_LONG)/8) #define CPU_LONGS(x) (CPU_BYTES(x) / sizeof(long)) #define make_internal_alias(x) extern __typeof (x) x##_int __attribute((alias(#x), visibility("hidden"))) #define hidden __attribute__((visibility("hidden"))) enum numa_warn { W_nosysfs, W_noproc, W_badmeminfo, W_nosysfs2, W_cpumap, W_numcpus, W_noderunmask, W_distance, W_memory, W_cpuparse, W_nodeparse, W_blockdev1, W_blockdev2, W_blockdev3, W_blockdev4, W_blockdev5, W_netlink1, W_netlink2, W_netlink3, W_net1, W_net2, W_class1, W_class2, W_pci1, W_pci2, W_node_parse1, W_node_parse2, W_nonode, W_badchar, }; #define howmany(x,y) (((x)+((y)-1))/(y)) #define bitsperlong (8 * sizeof(unsigned long)) #define bitsperint (8 * sizeof(unsigned int)) #define longsperbits(n) howmany(n, bitsperlong) #define bytesperbits(x) ((x+7)/8) ./numactl-2.0.9~rc5/numactl.80000600000175000017500000002211312213661422014332 0ustar ianwianw.\" t .\" Copyright 2003,2004 Andi Kleen, SuSE Labs. .\" .\" Permission is granted to make and distribute verbatim copies of this .\" manual provided the copyright notice and this permission notice are .\" preserved on all copies. .\" .\" Permission is granted to copy and distribute modified versions of this .\" manual under the conditions for verbatim copying, provided that the .\" entire resulting derived work is distributed under the terms of a .\" permission notice identical to this one. .\" .\" Since the Linux kernel and libraries are constantly changing, this .\" manual page may be incorrect or out-of-date. The author(s) assume no .\" responsibility for errors or omissions, or for damages resulting from .\" the use of the information contained herein. .\" .\" Formatted or processed versions of this manual, if unaccompanied by .\" the source, must acknowledge the copyright and authors of this work. .TH NUMACTL 8 "Mar 2004" "SuSE Labs" "Linux Administrator's Manual" .SH NAME numactl \- Control NUMA policy for processes or shared memory .SH SYNOPSIS .B numactl [ .B \-\-all ] [ .B \-\-interleave nodes ] [ .B \-\-preferred node ] [ .B \-\-membind nodes ] [ .B \-\-cpunodebind nodes ] [ .B \-\-physcpubind cpus ] [ .B \-\-localalloc ] [\-\-] command {arguments ...} .br .B numactl \-\-show .br .B numactl \-\-hardware .br .B numactl [ .B \-\-huge ] [ .B \-\-offset offset ] [ .B \-\-shmmode shmmode ] [ .B \-\-length length ] [ .B \-\-strict ] .br [ .B \-\-shmid id ] .B \-\-shm shmkeyfile | .B \-\-file tmpfsfile .br [ .B \-\-touch ] [ .B \-\-dump ] [ .B \-\-dump-nodes ] memory policy .SH DESCRIPTION .B numactl runs processes with a specific NUMA scheduling or memory placement policy. The policy is set for command and inherited by all of its children. In addition it can set persistent policy for shared memory segments or files. .PP Use -- before command if using command options that could be confused with numactl options. .PP .I nodes may be specified as N,N,N or N-N or N,N-N or N-N,N-N and so forth. Relative .I nodes may be specifed as +N,N,N or +N-N or +N,N-N and so forth. The + indicates that the node numbers are relative to the process' set of allowed nodes in its current cpuset. A !N-N notation indicates the inverse of N-N, in other words all nodes except N-N. If used with + notation, specify !+N-N. When .I same is specified the previous nodemask specified on the command line is used. all means all nodes in the current cpuset. .PP Instead of a number a node can also be: .TS tab(|); l l. netdev:DEV|The node connected to network device DEV. file:PATH |The node the block device of PATH. ip:HOST |The node of the network device of HOST block:PATH|The node of block device PATH pci:[seg:]bus:dev[:func]|The node of a PCI device. .TE Note that block resolves the kernel block device names only for udev names in /dev use .I file: .TP Policy settings are: .TP .B \-\-all, \-a Unset default cpuset awareness, so user can use all possible CPUs/nodes for following policy settings. .TP .B \-\-interleave=nodes, \-i nodes Set a memory interleave policy. Memory will be allocated using round robin on .I nodes. When memory cannot be allocated on the current interleave target fall back to other nodes. Multiple nodes may be specified on --interleave, --membind and --cpunodebind. .TP .B \-\-membind=nodes, \-m nodes Only allocate memory from nodes. Allocation will fail when there is not enough memory available on these nodes. .I nodes may be specified as noted above. .TP .B \-\-cpunodebind=nodes, \-N nodes Only execute .I command on the CPUs of .I nodes. Note that nodes may consist of multiple CPUs. .I nodes may be specified as noted above. .TP .B \-\-physcpubind=cpus, \-C cpus Only execute .I process on .I cpus. This accepts cpu numbers as shown in the .I processor fields of .I /proc/cpuinfo, or relative cpus as in relative to the current cpuset. You may specify "all", which means all cpus in the current cpuset. Physical .I cpus may be specified as N,N,N or N-N or N,N-N or N-N,N-N and so forth. Relative .I cpus may be specifed as +N,N,N or +N-N or +N,N-N and so forth. The + indicates that the cpu numbers are relative to the process' set of allowed cpus in its current cpuset. A !N-N notation indicates the inverse of N-N, in other words all cpus except N-N. If used with + notation, specify !+N-N. .TP .B \-\-localalloc, \-l Always allocate on the current node. .TP .B \-\-preferred=node Preferably allocate memory on .I node, but if memory cannot be allocated there fall back to other nodes. This option takes only a single node number. Relative notation may be used. .TP .B \-\-show, \-s Show NUMA policy settings of the current process. .TP .B \-\-hardware, \-H Show inventory of available nodes on the system. .TP 0 Numactl can set up policy for a SYSV shared memory segment or a file in shmfs/hugetlbfs. This policy is persistent and will be used by all mappings from that shared memory. The order of options matters here. The specification must at least include either of .I \-\-shm, .I \-\-shmid, .I \-\-file to specify the shared memory segment or file and a memory policy like described above ( .I \-\-interleave, .I \-\-localalloc, .I \-\-preferred, .I \-\-membind ). .TP .B \-\-huge When creating a SYSV shared memory segment use huge pages. Only valid before \-\-shmid or \-\-shm .TP .B \-\-offset Specify offset into the shared memory segment. Default 0. Valid units are .I m (for MB), .I g (for GB), .I k (for KB), otherwise it specifies bytes. .TP .B \-\-strict Give an error when a page in the policied area in the shared memory segment already was faulted in with a conflicting policy. Default is to silently ignore this. .TP .B \-\-shmmode shmmode Only valid before \-\-shmid or \-\-shm When creating a shared memory segment set it to numeric mode .I shmmode. .TP .B \-\-length length Apply policy to .I length range in the shared memory segment or make the segment length long Default is to use the remaining length Required when a shared memory segment is created and specifies the length of the new segment then. Valid units are .I m (for MB), .I g (for GB), .I k (for KB), otherwise it specifies bytes. .TP .B \-\-shmid id Create or use an shared memory segment with numeric ID .I id .TP .B \-\-shm shmkeyfile Create or use an shared memory segment, with the ID generated using .I ftok(3) from shmkeyfile .TP .B \-\-file tmpfsfile Set policy for a file in tmpfs or hugetlbfs .TP .B \-\-touch Touch pages to enforce policy early. Default is to not touch them, the policy is applied when an applications maps and accesses a page. .TP .B \-\-dump Dump policy in the specified range. .TP .B \-\-dump-nodes Dump all nodes of the specific range (very verbose!) .TP Valid node specifiers .TS tab(:); l l. all:All nodes number:Node number number1{,number2}:Node number1 and Node number2 number1-number2:Nodes from number1 to number2 ! nodes:Invert selection of the following specification. .TE .SH EXAMPLES numactl \-\-physcpubind=+0-4,8-12 myapplic arguments Run myapplic on cpus 0-4 and 8-12 of the current cpuset. numactl \-\-interleave=all bigdatabase arguments Run big database with its memory interleaved on all CPUs. numactl \-\-cpunodebind=0 \-\-membind=0,1 process Run process on node 0 with memory allocated on node 0 and 1. numactl \-\-cpunodebind=0 \-\-membind=0,1 -- process -l Run process as above, but with an option (-l) that would be confused with a numactl option. numactl \-\-cpunodebind=netdev:eth0 \-\-membind=netdev:eth0 network-server Run network-server on the node of network device eth0 with its memory also in the same node. numactl \-\-preferred=1 numactl \-\-show Set preferred node 1 and show the resulting state. numactl --interleave=all --shm /tmp/shmkey Interleave all of the sysv shared memory region specified by /tmp/shmkey over all nodes. Place a tmpfs file on 2 nodes: numactl --membind=2 dd if=/dev/zero of=/dev/shm/A bs=1M count=1024 numactl --membind=3 dd if=/dev/zero of=/dev/shm/A seek=1024 bs=1M count=1024 numactl --localalloc /dev/shm/file Reset the policy for the shared memory file .I file to the default localalloc policy. .SH NOTES Requires an NUMA policy aware kernel. Command is not executed using a shell. If you want to use shell metacharacters in the child use sh -c as wrapper. Setting policy for a hugetlbfs file does currently not work because it cannot be extended by truncate. Shared memory segments larger than numactl's address space cannot be completely policied. This could be a problem on 32bit architectures. Changing it piece by piece may work. The old .I --cpubind which accepts node numbers, not cpu numbers, is deprecated and replaced with the new .I --cpunodebind and .I --physcpubind options. .SH FILES .I /proc/cpuinfo for the listing of active CPUs. See .I proc(5) for details. .I /sys/devices/system/node/node*/numastat for NUMA memory hit statistics. .SH COPYRIGHT Copyright 2002,2004 Andi Kleen, SuSE Labs. numactl and the demo programs are under the GNU General Public License, v.2 .SH SEE ALSO .I set_mempolicy(2) , .I get_mempolicy(2) , .I mbind(2) , .I sched_setaffinity(2) , .I sched_getaffinity(2) , .I proc(5) , .I ftok(3) , .I shmat(2) , .I migratepages(8) ./numactl-2.0.9~rc5/numa.h0000755000175000017500000003220412213661422013724 0ustar ianwianw/* Copyright (C) 2003,2004 Andi Kleen, SuSE Labs. libnuma is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; version 2.1. libnuma 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. See the GNU Lesser General Public License for more details. You should find a copy of v2.1 of the GNU Lesser General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _NUMA_H #define _NUMA_H 1 /* allow an application to test for the current programming interface: */ #define LIBNUMA_API_VERSION 2 /* Simple NUMA policy library */ #include #include #include #include #if defined(__x86_64__) || defined(__i386__) #define NUMA_NUM_NODES 128 #else #define NUMA_NUM_NODES 2048 #endif #ifdef __cplusplus extern "C" { #endif typedef struct { unsigned long n[NUMA_NUM_NODES/(sizeof(unsigned long)*8)]; } nodemask_t; struct bitmask { unsigned long size; /* number of bits in the map */ unsigned long *maskp; }; /* operations on struct bitmask */ int numa_bitmask_isbitset(const struct bitmask *, unsigned int); struct bitmask *numa_bitmask_setall(struct bitmask *); struct bitmask *numa_bitmask_clearall(struct bitmask *); struct bitmask *numa_bitmask_setbit(struct bitmask *, unsigned int); struct bitmask *numa_bitmask_clearbit(struct bitmask *, unsigned int); unsigned int numa_bitmask_nbytes(struct bitmask *); unsigned int numa_bitmask_weight(const struct bitmask *); struct bitmask *numa_bitmask_alloc(unsigned int); void numa_bitmask_free(struct bitmask *); int numa_bitmask_equal(const struct bitmask *, const struct bitmask *); void copy_nodemask_to_bitmask(nodemask_t *, struct bitmask *); void copy_bitmask_to_nodemask(struct bitmask *, nodemask_t *); void copy_bitmask_to_bitmask(struct bitmask *, struct bitmask *); /* compatibility for codes that used them: */ static inline void nodemask_zero(nodemask_t *mask) { struct bitmask tmp; tmp.maskp = (unsigned long *)mask; tmp.size = sizeof(nodemask_t) * 8; numa_bitmask_clearall(&tmp); } static inline void nodemask_zero_compat(nodemask_t *mask) { struct bitmask tmp; tmp.maskp = (unsigned long *)mask; tmp.size = sizeof(nodemask_t) * 8; numa_bitmask_clearall(&tmp); } static inline void nodemask_set_compat(nodemask_t *mask, int node) { mask->n[node / (8*sizeof(unsigned long))] |= (1UL<<(node%(8*sizeof(unsigned long)))); } static inline void nodemask_clr_compat(nodemask_t *mask, int node) { mask->n[node / (8*sizeof(unsigned long))] &= ~(1UL<<(node%(8*sizeof(unsigned long)))); } static inline int nodemask_isset_compat(const nodemask_t *mask, int node) { if ((unsigned)node >= NUMA_NUM_NODES) return 0; if (mask->n[node / (8*sizeof(unsigned long))] & (1UL<<(node%(8*sizeof(unsigned long))))) return 1; return 0; } static inline int nodemask_equal(const nodemask_t *a, const nodemask_t *b) { struct bitmask tmp_a, tmp_b; tmp_a.maskp = (unsigned long *)a; tmp_a.size = sizeof(nodemask_t) * 8; tmp_b.maskp = (unsigned long *)b; tmp_b.size = sizeof(nodemask_t) * 8; return numa_bitmask_equal(&tmp_a, &tmp_b); } static inline int nodemask_equal_compat(const nodemask_t *a, const nodemask_t *b) { struct bitmask tmp_a, tmp_b; tmp_a.maskp = (unsigned long *)a; tmp_a.size = sizeof(nodemask_t) * 8; tmp_b.maskp = (unsigned long *)b; tmp_b.size = sizeof(nodemask_t) * 8; return numa_bitmask_equal(&tmp_a, &tmp_b); } /* NUMA support available. If this returns a negative value all other function in this library are undefined. */ int numa_available(void); /* Basic NUMA state */ /* Get max available node */ int numa_max_node(void); int numa_max_possible_node(void); /* Return preferred node */ int numa_preferred(void); /* Return node size and free memory */ long long numa_node_size64(int node, long long *freep); long numa_node_size(int node, long *freep); int numa_pagesize(void); /* Set with all nodes from which the calling process may allocate memory. Only valid after numa_available. */ extern struct bitmask *numa_all_nodes_ptr; /* Set with all nodes the kernel has exposed to userspace */ extern struct bitmask *numa_nodes_ptr; /* For source compatibility */ extern nodemask_t numa_all_nodes; /* Set with all cpus. */ extern struct bitmask *numa_all_cpus_ptr; /* Set with no nodes */ extern struct bitmask *numa_no_nodes_ptr; /* Source compatibility */ extern nodemask_t numa_no_nodes; /* Only run and allocate memory from a specific set of nodes. */ void numa_bind(struct bitmask *nodes); /* Set the NUMA node interleaving mask. 0 to turn off interleaving */ void numa_set_interleave_mask(struct bitmask *nodemask); /* Return the current interleaving mask */ struct bitmask *numa_get_interleave_mask(void); /* allocate a bitmask big enough for all nodes */ struct bitmask *numa_allocate_nodemask(void); static inline void numa_free_nodemask(struct bitmask *b) { numa_bitmask_free(b); } /* Some node to preferably allocate memory from for task. */ void numa_set_preferred(int node); /* Set local memory allocation policy for task */ void numa_set_localalloc(void); /* Only allocate memory from the nodes set in mask. 0 to turn off */ void numa_set_membind(struct bitmask *nodemask); /* Return current membind */ struct bitmask *numa_get_membind(void); /* Return allowed memories [nodes] */ struct bitmask *numa_get_mems_allowed(void); int numa_get_interleave_node(void); /* NUMA memory allocation. These functions always round to page size and are relatively slow. */ /* Alloc memory page interleaved on nodes in mask */ void *numa_alloc_interleaved_subset(size_t size, struct bitmask *nodemask); /* Alloc memory page interleaved on all nodes. */ void *numa_alloc_interleaved(size_t size); /* Alloc memory located on node */ void *numa_alloc_onnode(size_t size, int node); /* Alloc memory on local node */ void *numa_alloc_local(size_t size); /* Allocation with current policy */ void *numa_alloc(size_t size); /* Change the size of a memory area preserving the memory policy */ void *numa_realloc(void *old_addr, size_t old_size, size_t new_size); /* Free memory allocated by the functions above */ void numa_free(void *mem, size_t size); /* Low level functions, primarily for shared memory. All memory processed by these must not be touched yet */ /* Interleave an memory area. */ void numa_interleave_memory(void *mem, size_t size, struct bitmask *mask); /* Allocate a memory area on a specific node. */ void numa_tonode_memory(void *start, size_t size, int node); /* Allocate memory on a mask of nodes. */ void numa_tonodemask_memory(void *mem, size_t size, struct bitmask *mask); /* Allocate a memory area on the current node. */ void numa_setlocal_memory(void *start, size_t size); /* Allocate memory area with current memory policy */ void numa_police_memory(void *start, size_t size); /* Run current task only on nodes in mask */ int numa_run_on_node_mask(struct bitmask *mask); /* Run current task on nodes in mask without any cpuset awareness */ int numa_run_on_node_mask_all(struct bitmask *mask); /* Run current task only on node */ int numa_run_on_node(int node); /* Return current mask of nodes the task can run on */ struct bitmask * numa_get_run_node_mask(void); /* When strict fail allocation when memory cannot be allocated in target node(s). */ void numa_set_bind_policy(int strict); /* Fail when existing memory has incompatible policy */ void numa_set_strict(int flag); /* maximum nodes (size of kernel nodemask_t) */ int numa_num_possible_nodes(); /* maximum cpus (size of kernel cpumask_t) */ int numa_num_possible_cpus(); /* nodes in the system */ int numa_num_configured_nodes(); /* maximum cpus */ int numa_num_configured_cpus(); /* maximum cpus allowed to current task */ int numa_num_task_cpus(); int numa_num_thread_cpus(); /* backward compatibility */ /* maximum nodes allowed to current task */ int numa_num_task_nodes(); int numa_num_thread_nodes(); /* backward compatibility */ /* allocate a bitmask the size of the kernel cpumask_t */ struct bitmask *numa_allocate_cpumask(); static inline void numa_free_cpumask(struct bitmask *b) { numa_bitmask_free(b); } /* Convert node to CPU mask. -1/errno on failure, otherwise 0. */ int numa_node_to_cpus(int, struct bitmask *); /* report the node of the specified cpu. -1/errno on invalid cpu. */ int numa_node_of_cpu(int cpu); /* Report distance of node1 from node2. 0 on error.*/ int numa_distance(int node1, int node2); /* Error handling. */ /* This is an internal function in libnuma that can be overwritten by an user program. Default is to print an error to stderr and exit if numa_exit_on_error is true. */ void numa_error(char *where); /* When true exit the program when a NUMA system call (except numa_available) fails */ extern int numa_exit_on_error; /* Warning function. Can also be overwritten. Default is to print on stderr once. */ void numa_warn(int num, char *fmt, ...); /* When true exit the program on a numa_warn() call */ extern int numa_exit_on_warn; int numa_migrate_pages(int pid, struct bitmask *from, struct bitmask *to); int numa_move_pages(int pid, unsigned long count, void **pages, const int *nodes, int *status, int flags); int numa_sched_getaffinity(pid_t, struct bitmask *); int numa_sched_setaffinity(pid_t, struct bitmask *); /* Convert an ascii list of nodes to a bitmask */ struct bitmask *numa_parse_nodestring(const char *); /* Convert an ascii list of nodes to a bitmask without current nodeset * dependency */ struct bitmask *numa_parse_nodestring_all(const char *); /* Convert an ascii list of cpu to a bitmask */ struct bitmask *numa_parse_cpustring(const char *); /* Convert an ascii list of cpu to a bitmask without current taskset * dependency */ struct bitmask *numa_parse_cpustring_all(const char *); /* * The following functions are for source code compatibility * with releases prior to version 2. * Such codes should be compiled with NUMA_VERSION1_COMPATIBILITY defined. */ static inline void numa_set_interleave_mask_compat(nodemask_t *nodemask) { struct bitmask tmp; tmp.maskp = (unsigned long *)nodemask; tmp.size = sizeof(nodemask_t) * 8; numa_set_interleave_mask(&tmp); } static inline nodemask_t numa_get_interleave_mask_compat() { struct bitmask *tp; nodemask_t mask; tp = numa_get_interleave_mask(); copy_bitmask_to_nodemask(tp, &mask); numa_bitmask_free(tp); return mask; } static inline void numa_bind_compat(nodemask_t *mask) { struct bitmask *tp; tp = numa_allocate_nodemask(); copy_nodemask_to_bitmask(mask, tp); numa_bind(tp); numa_bitmask_free(tp); } static inline void numa_set_membind_compat(nodemask_t *mask) { struct bitmask tmp; tmp.maskp = (unsigned long *)mask; tmp.size = sizeof(nodemask_t) * 8; numa_set_membind(&tmp); } static inline nodemask_t numa_get_membind_compat() { struct bitmask *tp; nodemask_t mask; tp = numa_get_membind(); copy_bitmask_to_nodemask(tp, &mask); numa_bitmask_free(tp); return mask; } static inline void *numa_alloc_interleaved_subset_compat(size_t size, const nodemask_t *mask) { struct bitmask tmp; tmp.maskp = (unsigned long *)mask; tmp.size = sizeof(nodemask_t) * 8; return numa_alloc_interleaved_subset(size, &tmp); } static inline int numa_run_on_node_mask_compat(const nodemask_t *mask) { struct bitmask tmp; tmp.maskp = (unsigned long *)mask; tmp.size = sizeof(nodemask_t) * 8; return numa_run_on_node_mask(&tmp); } static inline nodemask_t numa_get_run_node_mask_compat() { struct bitmask *tp; nodemask_t mask; tp = numa_get_run_node_mask(); copy_bitmask_to_nodemask(tp, &mask); numa_bitmask_free(tp); return mask; } static inline void numa_interleave_memory_compat(void *mem, size_t size, const nodemask_t *mask) { struct bitmask tmp; tmp.maskp = (unsigned long *)mask; tmp.size = sizeof(nodemask_t) * 8; numa_interleave_memory(mem, size, &tmp); } static inline void numa_tonodemask_memory_compat(void *mem, size_t size, const nodemask_t *mask) { struct bitmask tmp; tmp.maskp = (unsigned long *)mask; tmp.size = sizeof(nodemask_t) * 8; numa_tonodemask_memory(mem, size, &tmp); } static inline int numa_sched_getaffinity_compat(pid_t pid, unsigned len, unsigned long *mask) { struct bitmask tmp; tmp.maskp = (unsigned long *)mask; tmp.size = len * 8; return numa_sched_getaffinity(pid, &tmp); } static inline int numa_sched_setaffinity_compat(pid_t pid, unsigned len, unsigned long *mask) { struct bitmask tmp; tmp.maskp = (unsigned long *)mask; tmp.size = len * 8; return numa_sched_setaffinity(pid, &tmp); } static inline int numa_node_to_cpus_compat(int node, unsigned long *buffer, int buffer_len) { struct bitmask tmp; tmp.maskp = (unsigned long *)buffer; tmp.size = buffer_len * 8; return numa_node_to_cpus(node, &tmp); } /* end of version 1 compatibility functions */ /* * To compile an application that uses libnuma version 1: * add -DNUMA_VERSION1_COMPATIBILITY to your Makefile's CFLAGS */ #ifdef NUMA_VERSION1_COMPATIBILITY #include #endif #ifdef __cplusplus } #endif #endif ./numactl-2.0.9~rc5/util.c0000755000175000017500000000551312213661423013740 0ustar ianwianw/* Copyright (C) 2003,2004 Andi Kleen, SuSE Labs. numactl 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; version 2. numactl 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. See the GNU General Public License for more details. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "numa.h" #include "numaif.h" #include "util.h" #include "bitops.h" #include #include #include #include #include #include #include void printmask(char *name, struct bitmask *mask) { int i; printf("%s: ", name); for (i = 0; i <= mask->size; i++) if (numa_bitmask_isbitset(mask, i)) printf("%d ", i); putchar('\n'); } void printcpumask(char *name, struct bitmask *mask) { int i; printf("%s: ", name); for (i = 0; i < mask->size; i++) { if (numa_bitmask_isbitset(mask, i)) printf("%d ", i); } putchar('\n'); } void complain(char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "numactl: "); vfprintf(stderr,fmt,ap); putchar('\n'); va_end(ap); exit(1); } void nerror(char *fmt, ...) { int err = errno; va_list ap; va_start(ap,fmt); fprintf(stderr, "numactl: "); vfprintf(stderr, fmt, ap); va_end(ap); if (err) fprintf(stderr,": %s\n", strerror(err)); else fputc('\n', stderr); exit(1); } long memsize(char *s) { char *end; long length = strtoul(s,&end,0); switch (toupper(*end)) { case 'G': length *= 1024; /*FALL THROUGH*/ case 'M': length *= 1024; /*FALL THROUGH*/ case 'K': length *= 1024; break; } return length; } static struct policy { char *name; int policy; int noarg; } policies[] = { { "interleave", MPOL_INTERLEAVE, }, { "membind", MPOL_BIND, }, { "preferred", MPOL_PREFERRED, }, { "default", MPOL_DEFAULT, 1 }, { NULL }, }; static char *policy_names[] = { "default", "preferred", "bind", "interleave" }; char *policy_name(int policy) { static char buf[32]; if (policy >= array_len(policy_names)) { sprintf(buf, "[%d]", policy); return buf; } return policy_names[policy]; } int parse_policy(char *name, char *arg) { int k; struct policy *p = NULL; if (!name) return MPOL_DEFAULT; for (k = 0; policies[k].name; k++) { p = &policies[k]; if (!strcmp(p->name, name)) break; } if (!p || !p->name || (!arg && !p->noarg)) usage(); return p->policy; } void print_policies(void) { int i; printf("Policies:"); for (i = 0; policies[i].name; i++) printf(" %s", policies[i].name); printf("\n"); } ./numactl-2.0.9~rc5/syscall.c0000600000175000017500000001561712213661423014430 0ustar ianwianw/* Copyright (C) 2003,2004 Andi Kleen, SuSE Labs. libnuma is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; version 2.1. libnuma 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. See the GNU Lesser General Public License for more details. You should find a copy of v2.1 of the GNU Lesser General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "numa.h" #include "numaif.h" #include "numaint.h" #define WEAK __attribute__((weak)) #if !defined(__NR_mbind) || !defined(__NR_set_mempolicy) || \ !defined(__NR_get_mempolicy) || !defined(__NR_migrate_pages) || \ !defined(__NR_move_pages) #if defined(__x86_64__) #define __NR_sched_setaffinity 203 #define __NR_sched_getaffinity 204 /* Official allocation */ #define __NR_mbind 237 #define __NR_set_mempolicy 238 #define __NR_get_mempolicy 239 #define __NR_migrate_pages 256 #define __NR_move_pages 279 #elif defined(__ia64__) #define __NR_sched_setaffinity 1231 #define __NR_sched_getaffinity 1232 #define __NR_migrate_pages 1280 #define __NR_move_pages 1276 /* Official allocation */ #define __NR_mbind 1259 #define __NR_get_mempolicy 1260 #define __NR_set_mempolicy 1261 #elif defined(__i386__) #define __NR_mbind 274 #define __NR_get_mempolicy 275 #define __NR_set_mempolicy 276 #define __NR_migrate_pages 294 #define __NR_move_pages 317 #elif defined(__powerpc__) #define __NR_mbind 259 #define __NR_get_mempolicy 260 #define __NR_set_mempolicy 261 #define __NR_migrate_pages 258 /* FIXME: powerpc is missing move pages!!! #define __NR_move_pages xxx */ #elif defined(__mips__) #if _MIPS_SIM == _ABIO32 /* * Linux o32 style syscalls are in the range from 4000 to 4999. */ #define __NR_Linux 4000 #define __NR_mbind (__NR_Linux + 268) #define __NR_get_mempolicy (__NR_Linux + 269) #define __NR_set_mempolicy (__NR_Linux + 270) #define __NR_migrate_pages (__NR_Linux + 287) #endif #if _MIPS_SIM == _ABI64 /* * Linux 64-bit syscalls are in the range from 5000 to 5999. */ #define __NR_Linux 5000 #define __NR_mbind (__NR_Linux + 227) #define __NR_get_mempolicy (__NR_Linux + 228) #define __NR_set_mempolicy (__NR_Linux + 229) #define __NR_migrate_pages (__NR_Linux + 246) #endif #if _MIPS_SIM == _ABIN32 /* * Linux N32 syscalls are in the range from 6000 to 6999. */ #define __NR_Linux 6000 #define __NR_mbind (__NR_Linux + 231) #define __NR_get_mempolicy (__NR_Linux + 232) #define __NR_set_mempolicy (__NR_Linux + 233) #define __NR_migrate_pages (__NR_Linux + 250) #endif #elif defined(__hppa__) #define __NR_migrate_pages 272 #elif !defined(DEPS_RUN) #error "Add syscalls for your architecture or update kernel headers" #endif #endif #if defined(__GLIBC__) && __GLIBC_PREREQ(2, 11) /* glibc 2.11 seems to have working 6 argument sycall. Use the glibc supplied syscall in this case. The version cut-off is rather arbitary and could be probably earlier. */ #define syscall6 syscall #elif defined(__x86_64__) /* 6 argument calls on x86-64 are often buggy in both glibc and asm/unistd.h. Add a working version here. */ long syscall6(long call, long a, long b, long c, long d, long e, long f) { long res; asm volatile ("movq %[d],%%r10 ; movq %[e],%%r8 ; movq %[f],%%r9 ; syscall" : "=a" (res) : "0" (call),"D" (a),"S" (b), "d" (c), [d] "g" (d), [e] "g" (e), [f] "g" (f) : "r11","rcx","r8","r10","r9","memory" ); if (res < 0) { errno = -res; res = -1; } return res; } #elif defined(__i386__) /* i386 has buggy syscall6 in glibc too. This is tricky to do in inline assembly because it clobbers so many registers. Do it out of line. */ asm( "__syscall6:\n" " pushl %ebp\n" " pushl %edi\n" " pushl %esi\n" " pushl %ebx\n" " movl (0+5)*4(%esp),%eax\n" " movl (1+5)*4(%esp),%ebx\n" " movl (2+5)*4(%esp),%ecx\n" " movl (3+5)*4(%esp),%edx\n" " movl (4+5)*4(%esp),%esi\n" " movl (5+5)*4(%esp),%edi\n" " movl (6+5)*4(%esp),%ebp\n" " int $0x80\n" " popl %ebx\n" " popl %esi\n" " popl %edi\n" " popl %ebp\n" " ret" ); extern long __syscall6(long n, long a, long b, long c, long d, long e, long f); long syscall6(long call, long a, long b, long c, long d, long e, long f) { long res = __syscall6(call,a,b,c,d,e,f); if (res < 0) { errno = -res; res = -1; } return res; } #else #define syscall6 syscall #endif long WEAK get_mempolicy(int *policy, const unsigned long *nmask, unsigned long maxnode, void *addr, int flags) { return syscall(__NR_get_mempolicy, policy, nmask, maxnode, addr, flags); } long WEAK mbind(void *start, unsigned long len, int mode, const unsigned long *nmask, unsigned long maxnode, unsigned flags) { return syscall6(__NR_mbind, (long)start, len, mode, (long)nmask, maxnode, flags); } long WEAK set_mempolicy(int mode, const unsigned long *nmask, unsigned long maxnode) { long i; i = syscall(__NR_set_mempolicy,mode,nmask,maxnode); return i; } long WEAK migrate_pages(int pid, unsigned long maxnode, const unsigned long *frommask, const unsigned long *tomask) { return syscall(__NR_migrate_pages, pid, maxnode, frommask, tomask); } long WEAK move_pages(int pid, unsigned long count, void **pages, const int *nodes, int *status, int flags) { return syscall(__NR_move_pages, pid, count, pages, nodes, status, flags); } /* SLES8 glibc doesn't define those */ int numa_sched_setaffinity_v1(pid_t pid, unsigned len, const unsigned long *mask) { return syscall(__NR_sched_setaffinity,pid,len,mask); } __asm__(".symver numa_sched_setaffinity_v1,numa_sched_setaffinity@libnuma_1.1"); int numa_sched_setaffinity_v2(pid_t pid, struct bitmask *mask) { return syscall(__NR_sched_setaffinity, pid, numa_bitmask_nbytes(mask), mask->maskp); } __asm__(".symver numa_sched_setaffinity_v2,numa_sched_setaffinity@@libnuma_1.2"); int numa_sched_getaffinity_v1(pid_t pid, unsigned len, const unsigned long *mask) { return syscall(__NR_sched_getaffinity,pid,len,mask); } __asm__(".symver numa_sched_getaffinity_v1,numa_sched_getaffinity@libnuma_1.1"); int numa_sched_getaffinity_v2(pid_t pid, struct bitmask *mask) { /* len is length in bytes */ return syscall(__NR_sched_getaffinity, pid, numa_bitmask_nbytes(mask), mask->maskp); /* sched_getaffinity returns sizeof(cpumask_t) */ } __asm__(".symver numa_sched_getaffinity_v2,numa_sched_getaffinity@@libnuma_1.2"); make_internal_alias(numa_sched_getaffinity_v1); make_internal_alias(numa_sched_getaffinity_v2); make_internal_alias(numa_sched_setaffinity_v1); make_internal_alias(numa_sched_setaffinity_v2); ./numactl-2.0.9~rc5/bitops.c0000755000175000017500000000030512213661422014254 0ustar ianwianw#include "bitops.h" /* extremly dumb */ int find_first_bit(void *m, int max) { unsigned long *mask = m; int i; for (i = 0; i < max; i++) { if (test_bit(i, mask)) break; } return i; } ./numactl-2.0.9~rc5/migspeed.80000644000175000017500000000144112213661422014475 0ustar ianwianw.\" t .\" Copyright 2005-2007 Christoph Lameter, Silicon Graphics, Inc. .\" .\" based on Andi Kleen's numactl manpage .\" .TH MIGSPEED 8 "April 2005" "SGI" "Linux Administrator's Manual" .SH NAME migspeed \- Test the speed of page migration .SH SYNOPSIS .B migspeed -p pages from-nodes to-nodes .SH DESCRIPTION .B migspeed attempts to move a sample of pages from the indicated node to the target node and measures the time it takes to perform the move. .B -p pages The default sample is 1000 pages. Override that with another number. .SH NOTES Requires an NUMA policy aware kernel with support for page migration (Linux 2.6.16 and later). .SH COPYRIGHT Copyright 2007 Christoph Lameter, Silicon Graphics, Inc. migratepages is under the GNU General Public License, v.2 .SH SEE ALSO .I numactl(8) ./numactl-2.0.9~rc5/versions.ldscript0000755000175000017500000001030212213661423016225 0ustar ianwianw# Symbols defined in the library which aren't specifically bound to a # version node are effectively bound to an unspecified base version of # the library. It is possible to bind all otherwise unspecified symbols # to a given version node using `global: *' somewhere in the version script. # # The interfaces at the "v1" level. # At this level we present these functions to the linker (and thus to an # application). # Any functions not defined in the global list (i.e. "local") will be internal # to the library (i.e. not exported but used within the library). # Thus the real function names, "numa_bind_v1" etc, are local and won't # be known to the linker. # the first 16 have v1 aliases # 3 of the 5 system calls that libnuma provides are common to all versions: libnuma_1.1 { global: set_mempolicy; get_mempolicy; mbind; numa_all_nodes; numa_alloc; numa_alloc_interleaved; numa_alloc_interleaved_subset; numa_alloc_local; numa_alloc_onnode; numa_available; numa_bind; numa_distance; numa_error; numa_exit_on_error; numa_free; numa_get_interleave_mask; numa_get_interleave_node; numa_get_membind; numa_get_run_node_mask; numa_interleave_memory; numa_max_node; numa_migrate_pages; numa_no_nodes; numa_node_size64; numa_node_size; numa_node_to_cpus; numa_pagesize; numa_parse_bitmap; numa_police_memory; numa_preferred; numa_run_on_node; numa_run_on_node_mask; numa_sched_getaffinity; numa_sched_setaffinity; numa_set_bind_policy; numa_set_interleave_mask; numa_set_localalloc; numa_set_membind; numa_set_preferred; numa_set_strict; numa_setlocal_memory; numa_tonode_memory; numa_tonodemask_memory; numa_warn; numa_exit_on_warn; local: *; }; # The interfaces at the "v2" level. # The first 17 have v2 aliases # We add the bitmask_ functions # and the move_pages and migrate_pages system calls # 1.2 depends on 1.1 libnuma_1.2 { global: copy_bitmask_to_nodemask; copy_nodemask_to_bitmask; copy_bitmask_to_bitmask; set_mempolicy; get_mempolicy; mbind; move_pages; migrate_pages; numa_all_cpus_ptr; numa_all_nodes_ptr; numa_alloc; numa_alloc_interleaved; numa_alloc_interleaved_subset; numa_alloc_local; numa_alloc_onnode; numa_realloc; numa_allocate_cpumask; numa_allocate_nodemask; numa_available; numa_bind; numa_bitmask_alloc; numa_bitmask_clearall; numa_bitmask_clearbit; numa_bitmask_equal; numa_bitmask_free; numa_bitmask_isbitset; numa_bitmask_nbytes; numa_bitmask_setall; numa_bitmask_setbit; numa_bitmask_weight; numa_distance; numa_error; numa_exit_on_error; numa_free; numa_get_interleave_mask; numa_get_interleave_node; numa_get_membind; numa_get_mems_allowed; numa_get_run_node_mask; numa_interleave_memory; numa_max_node; numa_max_possible_node; numa_migrate_pages; numa_move_pages; numa_no_nodes_ptr; numa_node_size64; numa_node_size; numa_node_to_cpus; numa_node_of_cpu; numa_nodes_ptr; numa_num_configured_cpus; numa_num_configured_nodes; numa_num_possible_nodes; numa_num_task_cpus; numa_num_task_nodes; numa_num_thread_cpus; numa_num_thread_nodes; numa_pagesize; numa_parse_bitmap; numa_parse_cpustring; numa_parse_nodestring; numa_police_memory; numa_preferred; numa_run_on_node; numa_run_on_node_mask; numa_sched_getaffinity; numa_sched_setaffinity; numa_set_bind_policy; numa_set_interleave_mask; numa_set_localalloc; numa_set_membind; numa_set_preferred; numa_set_strict; numa_setlocal_memory; numa_tonode_memory; numa_tonodemask_memory; numa_warn; local: *; } libnuma_1.1; # New parsing interface for cpu/numastrings # was added into version 1.3 libnuma_1.3 { global: numa_parse_cpustring_all; numa_parse_nodestring_all; numa_num_possible_cpus; local: *; } libnuma_1.2; # New interface with customizable cpuset awareness # was added into version 1.4 libnuma_1.4 { global: numa_run_on_node_mask_all; local: *; } libnuma_1.3; ./numactl-2.0.9~rc5/stream_lib.h0000755000175000017500000000035512213661423015110 0ustar ianwianwlong stream_memsize(void); long stream_init(void *mem); #define STREAM_NRESULTS 4 void stream_test(double *res); void stream_check(void); void stream_setmem(unsigned long size); extern int stream_verbose; extern char *stream_names[]; ./numactl-2.0.9~rc5/migspeed.c0000644000175000017500000000652312213661422014556 0ustar ianwianw/* * Migration test program * * (C) 2007 Silicon Graphics, Inc. Christoph Lameter * */ #include #include #include "numa.h" #include "numaif.h" #include #include #include #include #include "util.h" char *memory; unsigned long pages = 1000; unsigned long pagesize; const char *optstr = "hvp:"; char *cmd; int verbose; struct timespec start,end; void usage(void) { printf("usage %s [-p pages] [-h] [-v] from-nodes to-nodes\n", cmd); printf(" from and to nodes may specified in form N or N-N\n"); printf(" -p pages number of pages to try (defaults to %ld)\n", pages); printf(" -v verbose\n"); printf(" -h usage\n"); exit(1); } void displaymap(void) { FILE *f = fopen("/proc/self/numa_maps","r"); if (!f) { printf("/proc/self/numa_maps not accessible.\n"); exit(1); } while (!feof(f)) { char buffer[2000]; if (!fgets(buffer, sizeof(buffer), f)) break; if (!strstr(buffer, "bind")) continue ; printf("%s", buffer); } fclose(f); } int main(int argc, char *argv[]) { char *p; int option; int error = 0; struct timespec result; unsigned long bytes; double duration, mbytes; struct bitmask *from; struct bitmask *to; pagesize = getpagesize(); /* Command line processing */ opterr = 1; cmd = argv[0]; while ((option = getopt(argc, argv, optstr)) != EOF) switch (option) { case 'h' : case '?' : error = 1; break; case 'v' : verbose++; break; case 'p' : pages = strtoul(optarg, &p, 0); if (p == optarg || *p) usage(); break; } if (!argv[optind]) usage(); if (verbose > 1) printf("numa_max_node = %d\n", numa_max_node()); numa_exit_on_error = 1; from = numa_parse_nodestring(argv[optind]); if (!from) { printf ("<%s> is invalid\n", argv[optind]); exit(1); } if (errno) { perror("from mask"); exit(1); } if (verbose) printmask("From", from); if (!argv[optind+1]) usage(); to = numa_parse_nodestring(argv[optind+1]); if (!to) { printf ("<%s> is invalid\n", argv[optind+1]); exit(1); } if (errno) { perror("to mask"); exit(1); } if (verbose) printmask("To", to); bytes = pages * pagesize; if (verbose) printf("Allocating %lu pages of %lu bytes of memory\n", pages, pagesize); memory = memalign(pagesize, bytes); if (!memory) { printf("Out of Memory\n"); exit(2); } if (mbind(memory, bytes, MPOL_BIND, from->maskp, from->size, 0) < 0) numa_error("mbind"); if (verbose) printf("Dirtying memory....\n"); for (p = memory; p <= memory + bytes; p += pagesize) *p = 1; if (verbose) printf("Starting test\n"); displaymap(); clock_gettime(CLOCK_REALTIME, &start); if (mbind(memory, bytes, MPOL_BIND, to->maskp, to->size, MPOL_MF_MOVE) <0) numa_error("memory move"); clock_gettime(CLOCK_REALTIME, &end); displaymap(); result.tv_sec = end.tv_sec - start.tv_sec; result.tv_nsec = end.tv_nsec - start.tv_nsec; if (result.tv_nsec < 0) { result.tv_sec--; result.tv_nsec += 1000000000; } if (result.tv_nsec >= 1000000000) { result.tv_sec++; result.tv_nsec -= 1000000000; } duration = result.tv_sec + result.tv_nsec / 1000000000.0; mbytes = bytes / (1024*1024.0); printf("%1.1f Mbyte migrated in %1.2f secs. %3.1f Mbytes/second\n", mbytes, duration, mbytes / duration); return 0; } ./numactl-2.0.9~rc5/numastat.80000755000175000017500000001441012213661422014537 0ustar ianwianw.TH "numastat" "8" "1.0.0" "Bill Gray" "Administration" .SH "numastat" .LP \fBnumastat\fP \- Show per-NUMA-node memory statistics for processes and the operating system .SH "SYNTAX" .LP \fBnumastat\fP .br .LP \fBnumastat\fP [\fI\-V\fP] .br .LP \fBnumastat\fP [\fI\|...\fP] .br .LP \fBnumastat\fP [\fI\-c\fP] [\fI\-m\fP] [\fI\-n\fP] [\fI\-p |\fP] [\fI\-s[]\fP] [\fI\-v\fP] [\fI\-z\fP] [\fI\|...\fP] .br .SH "DESCRIPTION" .LP .B numastat with no command options or arguments at all, displays per-node NUMA hit and miss system statistics from the kernel memory allocator. This default \fBnumastat\fP behavior is strictly compatible with the previous long-standing \fBnumastat\fP perl script, written by Andi Kleen. The default \fBnumastat\fP statistics shows per-node numbers (in units of pages of memory) in these categories: .LP .B numa_hit is memory successfully allocated on this node as intended. .LP .B numa_miss is memory allocated on this node despite the process preferring some different node. Each .I numa_miss has a .I numa_foreign on another node. .LP .B numa_foreign is memory intended for this node, but actually allocated on some different node. Each .I numa_foreign has a .I numa_miss on another node. .LP .B interleave_hit is interleaved memory successfully allocated on this node as intended. .LP .B local_node is memory allocated on this node while a process was running on it. .LP .B other_node is memory allocated on this node while a process was running on some other node. .LP Any supplied options or arguments with the \fBnumastat\fP command will significantly change both the content and the format of the display. Specified options will cause display units to change to megabytes of memory, and will change other specific behaviors of \fBnumastat\fP as described below. .SH "OPTIONS" .LP .TP \fB\-c\fR Minimize table display width by dynamically shrinking column widths based on data contents. With this option, amounts of memory will be rounded to the nearest megabyte (rather than the usual display with two decimal places). Column width and inter-column spacing will be somewhat unpredictable with this option, but the more dense display will be very useful on systems with many NUMA nodes. .TP \fB\-m\fR Show the meminfo-like system-wide memory usage information. This option produces a per-node breakdown of memory usage information similar to that found in /proc/meminfo. .TP \fB\-n\fR Show the original \fBnumastat\fP statistics info. This will show the same information as the default \fBnumastat\fP behavior but the units will be megabytes of memory, and there will be other formatting and layout changes versus the original \fBnumastat\fP behavior. .TP \fB\-p\fR <\fBPID\fP> or <\fBpattern\fP> Show per-node memory allocation information for the specified PID or pattern. If the \-p argument is only digits, it is assumed to be a numerical PID. If the argument characters are not only digits, it is assumed to be a text fragment pattern to search for in process command lines. For example, \fBnumastat -p qemu\fP will attempt to find and show information for processes with "qemu" in the command line. Any command line arguments remaining after \fBnumastat\fP option flag processing is completed, are assumed to be additional <\fBPID\fP> or <\fBpattern\fP> process specifiers. In this sense, the \fB\-p\fP option flag is optional: \fBnumastat qemu\fP is equivalent to \fBnumastat -p qemu\fP .TP \fB\-s[]\fR Sort the table data in descending order before displaying it, so the biggest memory consumers are listed first. With no specified , the table will be sorted by the total column. If the optional argument is supplied, the data will be sorted by the column. Note that must follow the \fB\-s\fP immediately with no intermediate white space (e.g., \fBnumastat \-s2\fP). Because \fB\-s\fP can allow an optional argument, it must always be the last option character in a compound option character string. For example, instead of \fBnumastat \-msc\fP (which probably will not work as you expect), use \fBnumastat \-mcs\fP .TP \fB\-v\fR Make some reports more verbose. In particular, process information for multiple processes will display detailed information for each process. Normally when per-node information for multiple processes is displayed, only the total lines are shown. .TP \fB\-V\fR Display \fBnumastat\fP version information and exit. .TP \fB\-z\fR Skip display of table rows and columns of only zero valuess. This can be used to greatly reduce the amount of uninteresting zero data on systems with many NUMA nodes. Note that when rows or columns of zeros are still displayed with this option, that probably means there is at least one value in the row or column that is actually non-zero, but rounded to zero for display. .SH NOTES \fBnumastat\fP attempts to fold each table display so it will be conveniently readable on the output terminal. Normally a terminal width of 80 characters is assumed. When the \fBresize\fP command is available, \fBnumastat\fP attempts to dynamically determine and fine tune the output tty width from \fBresize\fP output. If \fBnumastat\fP output is not to a tty, very long output lines can be produced, depending on how many NUMA nodes are present. In all cases, output width can be explicitly specified via the \fBNUMASTAT_WIDTH\fP environment variable. For example, \fBNUMASTAT_WIDTH=100 numastat\fP. On systems with many NUMA nodes, \fBnumastat \-c \-z ....\fP can be very helpful to selectively reduce the amount of displayed information. .SH "ENVIRONMENT VARIABLES" .LP .TP NUMASTAT_WIDTH .SH "FILES" .LP \fI/proc/*/numa_maps\fP .br \fI/sys/devices/system/node/node*/meminfo\fP .br \fI/sys/devices/system/node/node*/numastat\fP .SH "EXAMPLES" .I numastat \-c \-z \-m \-n .br .I numastat \-czs libvirt kvm qemu .br .I watch \-n1 numastat .br .I watch \-n1 \-\-differences=cumulative numastat .SH "AUTHORS" .LP The original \fBnumastat\fP perl script was written circa 2003 by Andi Kleen . The current \fBnumastat\fP program was written in 2012 by Bill Gray to be compatible by default with the original, and to add options to display per-node system memory usage and per-node process memory allocation. .SH "SEE ALSO" .LP .BR numactl (8), .BR set_mempolicy( 2), .BR numa (3) ./numactl-2.0.9~rc5/numacompat1.h0000755000175000017500000000231712213661422015213 0ustar ianwianw#define numa_set_interleave_mask(m) numa_set_interleave_mask_compat(m) #define numa_get_interleave_mask() numa_get_interleave_mask_compat() #define numa_bind(m) numa_bind_compat(m) #define numa_get_membind(m) numa_get_membind_compat(m) #define numa_set_membind(m) numa_set_membind_compat(m) #define numa_alloc_interleaved_subset(s,m) numa_alloc_interleaved_subset_compat(s,m) #define numa_run_on_node_mask(m) numa_run_on_node_mask_compat(m) #define numa_get_run_node_mask() numa_get_run_node_mask_compat() #define numa_interleave_memory(st,si,m) numa_interleave_memory_compat(st,si,m) #define numa_tonodemask_memory(st,si,m) numa_tonodemask_memory_compat(st,si,m) #define numa_sched_getaffinity(p,l,m) numa_sched_getaffinity_compat(p,l,m) #define numa_sched_setaffinity(p,l,m) numa_sched_setaffinity_compat(p,l,m) #define numa_node_to_cpus(n,b,bl) numa_node_to_cpus_compat(n,b,bl) #define nodemask_zero(m) nodemask_zero_compat(m) #define nodemask_set(m, n) nodemask_set_compat(m, n) #define nodemask_clr(m, n) nodemask_clr_compat(m, n) #define nodemask_isset(m, n) nodemask_isset_compat(m, n) #define nodemask_equal(a, b) nodemask_equal_compat(a, b) ./numactl-2.0.9~rc5/stream_lib.c0000755000175000017500000001426512213661423015110 0ustar ianwianw#include #include #include #include #include #include #include "stream_lib.h" static inline double mysecond() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + tv.tv_usec * 1.e-6; } /* * Program: Stream * Programmer: Joe R. Zagar * Revision: 4.0-BETA, October 24, 1995 * Original code developed by John D. McCalpin * * This program measures memory transfer rates in MB/s for simple * computational kernels coded in C. These numbers reveal the quality * of code generation for simple uncacheable kernels as well as showing * the cost of floating-point operations relative to memory accesses. * * INSTRUCTIONS: * * 1) Stream requires a good bit of memory to run. Adjust the * value of 'N' (below) to give a 'timing calibration' of * at least 20 clock-ticks. This will provide rate estimates * that should be good to about 5% precision. * * Hacked by AK to be a library */ long N = 8000000; #define NTIMES 10 #define OFFSET 0 /* * 3) Compile the code with full optimization. Many compilers * generate unreasonably bad code before the optimizer tightens * things up. If the results are unreasonably good, on the * other hand, the optimizer might be too smart for me! * * Try compiling with: * cc -O stream_d.c second_wall.c -o stream_d -lm * * This is known to work on Cray, SGI, IBM, and Sun machines. * * * 4) Mail the results to mccalpin@cs.virginia.edu * Be sure to include: * a) computer hardware model number and software revision * b) the compiler flags * c) all of the output from the test case. * Thanks! * */ int checktick(); # define HLINE "-------------------------------------------------------------\n" # ifndef MIN # define MIN(x,y) ((x)<(y)?(x):(y)) # endif # ifndef MAX # define MAX(x,y) ((x)>(y)?(x):(y)) # endif static double *a, *b, *c; static double rmstime[4] = { 0 }, maxtime[4] = { 0}, mintime[4] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX}; static char *label[4] = { "Copy: ", "Scale: ", "Add: ", "Triad: " }; char *stream_names[] = { "Copy","Scale","Add","Triad" }; static double bytes[4]; int stream_verbose = 1; #define Vprintf(x...) do { if (stream_verbose) printf(x); } while(0) void stream_check(void) { int quantum; int BytesPerWord; register int j; double t; /* --- SETUP --- determine precision and check timing --- */ Vprintf(HLINE); BytesPerWord = sizeof(double); Vprintf("This system uses %d bytes per DOUBLE PRECISION word.\n", BytesPerWord); Vprintf(HLINE); Vprintf("Array size = %lu, Offset = %d\n", N, OFFSET); Vprintf("Total memory required = %.1f MB.\n", (3 * N * BytesPerWord) / 1048576.0); Vprintf("Each test is run %d times, but only\n", NTIMES); Vprintf("the *best* time for each is used.\n"); /* Get initial value for system clock. */ for (j = 0; j < N; j++) { a[j] = 1.0; b[j] = 2.0; c[j] = 0.0; } Vprintf(HLINE); if ((quantum = checktick()) >= 1) Vprintf("Your clock granularity/precision appears to be " "%d microseconds.\n", quantum); else Vprintf("Your clock granularity appears to be " "less than one microsecond.\n"); t = mysecond(); for (j = 0; j < N; j++) a[j] = 2.0E0 * a[j]; t = 1.0E6 * (mysecond() - t); Vprintf("Each test below will take on the order" " of %d microseconds.\n", (int) t); Vprintf(" (= %d clock ticks)\n", (int) (t / quantum)); Vprintf("Increase the size of the arrays if this shows that\n"); Vprintf("you are not getting at least 20 clock ticks per test.\n"); Vprintf(HLINE); Vprintf("WARNING -- The above is only a rough guideline.\n"); Vprintf("For best results, please be sure you know the\n"); Vprintf("precision of your system timer.\n"); Vprintf(HLINE); } void stream_test(double *res) { register int j, k; double scalar, times[4][NTIMES]; /* --- MAIN LOOP --- repeat test cases NTIMES times --- */ scalar = 3.0; for (k = 0; k < NTIMES; k++) { times[0][k] = mysecond(); for (j = 0; j < N; j++) c[j] = a[j]; times[0][k] = mysecond() - times[0][k]; times[1][k] = mysecond(); for (j = 0; j < N; j++) b[j] = scalar * c[j]; times[1][k] = mysecond() - times[1][k]; times[2][k] = mysecond(); for (j = 0; j < N; j++) c[j] = a[j] + b[j]; times[2][k] = mysecond() - times[2][k]; times[3][k] = mysecond(); for (j = 0; j < N; j++) a[j] = b[j] + scalar * c[j]; times[3][k] = mysecond() - times[3][k]; } /* --- SUMMARY --- */ for (k = 0; k < NTIMES; k++) { for (j = 0; j < 4; j++) { rmstime[j] = rmstime[j] + (times[j][k] * times[j][k]); mintime[j] = MIN(mintime[j], times[j][k]); maxtime[j] = MAX(maxtime[j], times[j][k]); } } Vprintf ("Function Rate (MB/s) RMS time Min time Max time\n"); for (j = 0; j < 4; j++) { double speed = 1.0E-06 * bytes[j] / mintime[j]; rmstime[j] = sqrt(rmstime[j] / (double) NTIMES); Vprintf("%s%11.4f %11.4f %11.4f %11.4f\n", label[j], speed, rmstime[j], mintime[j], maxtime[j]); if (res) res[j] = speed; } } # define M 20 int checktick() { int i, minDelta, Delta; double t1, t2, timesfound[M]; /* Collect a sequence of M unique time values from the system. */ for (i = 0; i < M; i++) { t1 = mysecond(); while (((t2 = mysecond()) - t1) < 1.0E-6); timesfound[i] = t1 = t2; } /* * Determine the minimum difference between these M values. * This result will be our estimate (in microseconds) for the * clock granularity. */ minDelta = 1000000; for (i = 1; i < M; i++) { Delta = (int) (1.0E6 * (timesfound[i] - timesfound[i - 1])); minDelta = MIN(minDelta, MAX(Delta, 0)); } return (minDelta); } void stream_setmem(unsigned long size) { N = (size - OFFSET) / (3*sizeof(double)); } long stream_memsize(void) { return 3*(sizeof(double) * (N+OFFSET)) ; } long stream_init(void *mem) { int i; for (i = 0; i < 4; i++) { rmstime[i] = 0; maxtime[i] = 0; mintime[i] = FLT_MAX; } bytes[0] = 2 * sizeof(double) * N; bytes[1] = 2 * sizeof(double) * N; bytes[2] = 3 * sizeof(double) * N; bytes[3] = 3 * sizeof(double) * N; a = mem; b = (double *)mem + (N+OFFSET); c = (double *)mem + 2*(N+OFFSET); stream_check(); return 0; } ./numactl-2.0.9~rc5/affinity.h0000644000175000017500000000013512213661422014570 0ustar ianwianwenum { NO_IO_AFFINITY = -2 }; int resolve_affinity(const char *id, struct bitmask *mask); ./numactl-2.0.9~rc5/mt.c0000755000175000017500000000220612213661422013376 0ustar ianwianw/* Mersenne twister implementation from Michael Brundage. Public Domain. MT is a very fast pseudo random number generator. This version works on 32bit words. Changes by AK. */ #include #include "mt.h" int mt_index; unsigned int mt_buffer[MT_LEN]; void mt_init(void) { int i; srand(1); for (i = 0; i < MT_LEN; i++) mt_buffer[i] = rand(); mt_index = 0; } #define MT_IA 397 #define MT_IB (MT_LEN - MT_IA) #define UPPER_MASK 0x80000000 #define LOWER_MASK 0x7FFFFFFF #define MATRIX_A 0x9908B0DF #define TWIST(b,i,j) ((b)[i] & UPPER_MASK) | ((b)[j] & LOWER_MASK) #define MAGIC(s) (((s)&1)*MATRIX_A) void mt_refill(void) { int i; unsigned int s; unsigned int * b = mt_buffer; mt_index = 0; i = 0; for (; i < MT_IB; i++) { s = TWIST(b, i, i+1); b[i] = b[i + MT_IA] ^ (s >> 1) ^ MAGIC(s); } for (; i < MT_LEN-1; i++) { s = TWIST(b, i, i+1); b[i] = b[i - MT_IB] ^ (s >> 1) ^ MAGIC(s); } s = TWIST(b, MT_LEN-1, 0); b[MT_LEN-1] = b[MT_IA-1] ^ (s >> 1) ^ MAGIC(s); } ./numactl-2.0.9~rc5/bitops.h0000755000175000017500000000066012213661422014265 0ustar ianwianw#ifndef BITOPS_H #define BITOPS_H 1 #define BITS_PER_LONG (sizeof(unsigned long) * 8) #define BYTES_PER_LONG (sizeof(long)) #define test_bit(i,p) ((p)[(i) / BITS_PER_LONG] & (1UL << ((i)%BITS_PER_LONG))) #define set_bit(i,p) ((p)[(i) / BITS_PER_LONG] |= (1UL << ((i)%BITS_PER_LONG))) #define clear_bit(i,p) ((p)[(i) / BITS_PER_LONG] &= ~(1UL << ((i)%BITS_PER_LONG))) extern int find_first_bit(void *mask, int max); #endif ./numactl-2.0.9~rc5/head0000644000175000017500000000043712213661422013437 0ustar ianwianwDate: Wed, 2 Mar 2011 13:32:07 +0100 From: Anton Arapov To: linux-numa@vger.kernel.org Subject: [PATCH] manpages Hello, please, commit the following patches to the numactl tree. Author: John Bradshaw p.s. keep me in Cc. thanks, Anton ./numactl-2.0.9~rc5/numa.30000755000175000017500000010116112213661422013636 0ustar ianwianw.\" Copyright 2003,2004 Andi Kleen, SuSE Labs. .\" .\" Permission is granted to make and distribute verbatim copies of this .\" manual provided the copyright notice and this permission notice are .\" preserved on all copies. .\" .\" Permission is granted to copy and distribute modified versions of this .\" manual under the conditions for verbatim copying, provided that the .\" entire resulting derived work is distributed under the terms of a .\" permission notice identical to this one. .\" .\" Since the Linux kernel and libraries are constantly changing, this .\" manual page may be incorrect or out-of-date. The author(s) assume no .\" responsibility for errors or omissions, or for damages resulting from .\" the use of the information contained herein. .\" .\" Formatted or processed versions of this manual, if unaccompanied by .\" the source, must acknowledge the copyright and authors of this work. .TH NUMA 3 "December 2007" "SuSE Labs" "Linux Programmer's Manual" .SH NAME numa \- NUMA policy library .SH SYNOPSIS .B #include .sp .B cc ... \-lnuma .sp .B int numa_available(void); .sp .BI "int numa_max_possible_node(void);" .br .BI "int numa_num_possible_nodes();" .sp .B int numa_max_node(void); .br .BI "int numa_num_configured_nodes();" .br .B struct bitmask *numa_get_mems_allowed(void); .sp .BI "int numa_num_configured_cpus(void);" .br .BI "struct bitmask *numa_all_nodes_ptr;" .br .BI "struct bitmask *numa_no_nodes_ptr;" .br .BI "struct bitmask *numa_all_cpus_ptr;" .sp .BI "int numa_num_task_cpus();" .br .BI "int numa_num_task_nodes();" .sp .BI "int numa_parse_bitmap(char *" line " , struct bitmask *" mask "); .br .BI "struct bitmask *numa_parse_nodestring(const char *" string ); .br .BI "struct bitmask *numa_parse_nodestring_all(const char *" string ); .br .BI "struct bitmask *numa_parse_cpustring(const char *" string ); .br .BI "struct bitmask *numa_parse_cpustring_all(const char *" string ); .sp .BI "long numa_node_size(int " node ", long *" freep ); .br .BI "long long numa_node_size64(int " node ", long long *" freep ); .sp .B int numa_preferred(void); .br .BI "void numa_set_preferred(int " node ); .br .BI "int numa_get_interleave_node(void); .br .B struct bitmask *numa_get_interleave_mask(void); .br .BI "void numa_set_interleave_mask(struct bitmask *" nodemask ); .br .BI "void numa_interleave_memory(void *" start ", size_t " size ", struct bitmask *" nodemask ); .br .BI "void numa_bind(struct bitmask *" nodemask ); .br .BI "void numa_set_localalloc(void); .br .BI "void numa_set_membind(struct bitmask *" nodemask ); .br .B struct bitmask *numa_get_membind(void); .sp .BI "void *numa_alloc_onnode(size_t " size ", int " node ); .br .BI "void *numa_alloc_local(size_t " size ); .br .BI "void *numa_alloc_interleaved(size_t " size ); .br .BI "void *numa_alloc_interleaved_subset(size_t " size ", struct bitmask *" nodemask ); .BI "void *numa_alloc(size_t " size ); .br .BI "void *numa_realloc(void *"old_addr ", size_t " old_size ", size_t " new_size ); .br .BI "void numa_free(void *" start ", size_t " size ); .sp .BI "int numa_run_on_node(int " node ); .br .BI "int numa_run_on_node_mask(struct bitmask *" nodemask ); .br .BI "int numa_run_on_node_mask_all(struct bitmask *" nodemask ); .br .B struct bitmask *numa_get_run_node_mask(void); .sp .BI "void numa_tonode_memory(void *" start ", size_t " size ", int " node ); .br .BI "void numa_tonodemask_memory(void *" start ", size_t " size ", struct bitmask *" nodemask ); .br .BI "void numa_setlocal_memory(void *" start ", size_t " size ); .br .BI "void numa_police_memory(void *" start ", size_t " size ); .br .BI "void numa_set_bind_policy(int " strict ); .br .BI "void numa_set_strict(int " strict ); .sp .\" should be undocumented ?? .BI "int numa_distance(int " node1 ", int " node2 ); .sp .BI "int numa_sched_getaffinity(pid_t " pid ", struct bitmask *" mask ); .br .BI "int numa_sched_setaffinity(pid_t " pid ", struct bitmask *" mask ); .br .BI "int numa_node_to_cpus(int " node ", struct bitmask *" mask "); .br .BI "int numa_node_of_cpu(int " cpu "); .sp .BI "struct bitmask *numa_allocate_cpumask();" .sp .BI "void numa_free_cpumask();" .br .BI "struct bitmask *numa_allocate_nodemask();" .sp .BI "void numa_free_nodemask();" .br .BI "struct bitmask *numa_bitmask_alloc(unsigned int " n "); .br .BI "struct bitmask *numa_bitmask_clearall(struct bitmask *" bmp ); .br .BI "struct bitmask *numa_bitmask_clearbit(struct bitmask *" bmp ", unsigned int " n ); .br .BI "int numa_bitmask_equal(const struct bitmask *" bmp1 ", const struct bitmask *" bmp2 ); .br .BI "void numa_bitmask_free(struct bitmask *" bmp ); .br .BI "int numa_bitmask_isbitset(const struct bitmask *" bmp ", unsigned int " n ");" .br .BI "unsigned int numa_bitmask_nbytes(struct bitmask *" bmp ); .br .BI "struct bitmask *numa_bitmask_setall(struct bitmask *" bmp ); .br .BI "struct bitmask *numa_bitmask_setbit(struct bitmask *" bmp ", unsigned int " n ); .br .BI "void copy_bitmask_to_nodemask(struct bitmask *" bmp ", nodemask_t *" nodemask ) .br .BI "void copy_nodemask_to_bitmask(nodemask_t *" nodemask ", struct bitmask *" bmp ) .br .BI "void copy_bitmask_to_bitmask(struct bitmask *" bmpfrom ", struct bitmask *" bmpto ) .br .BI "unsigned int numa_bitmask_weight(const struct bitmask *bmp ) .sp .BI "int numa_move_pages(int " pid ", unsigned long " count ", void **" pages ", const int *" nodes ", int *" status ", int " flags ); .br .BI "int numa_migrate_pages(int " pid ", struct bitmask *" fromnodes ", struct bitmask *" tonodes ); .sp .BI "void numa_error(char *" where ); .sp .BI "extern int " numa_exit_on_error ; .br .BI "extern int " numa_exit_on_warn ; .br .BI "void numa_warn(int " number ", char *" where ", ...);" .br .SH DESCRIPTION The .I libnuma library offers a simple programming interface to the NUMA (Non Uniform Memory Access) policy supported by the Linux kernel. On a NUMA architecture some memory areas have different latency or bandwidth than others. Available policies are page interleaving (i.e., allocate in a round-robin fashion from all, or a subset, of the nodes on the system), preferred node allocation (i.e., preferably allocate on a particular node), local allocation (i.e., allocate on the node on which the task is currently executing), or allocation only on specific nodes (i.e., allocate on some subset of the available nodes). It is also possible to bind tasks to specific nodes. Numa memory allocation policy may be specified as a per-task attribute, that is inherited by children tasks and processes, or as an attribute of a range of process virtual address space. Numa memory policies specified for a range of virtual address space are shared by all tasks in the process. Further more, memory policies specified for a range of a shared memory attached using .I shmat(2) or .I mmap(2) from shmfs/hugetlbfs are shared by all processes that attach to that region. Memory policies for shared disk backed file mappings are currently ignored. The default memory allocation policy for tasks and all memory range is local allocation. This assumes that no ancestor has installed a non-default policy. For setting a specific policy globally for all memory allocations in a process and its children it is easiest to start it with the .BR numactl (8) utility. For more finegrained policy inside an application this library can be used. All numa memory allocation policy only takes effect when a page is actually faulted into the address space of a process by accessing it. The .B numa_alloc_* functions take care of this automatically. A .I node is defined as an area where all memory has the same speed as seen from a particular CPU. A node can contain multiple CPUs. Caches are ignored for this definition. Most functions in this library are only concerned about numa nodes and their memory. The exceptions to this are: .IR numa_node_to_cpus (), .IR numa_node_of_cpu (), .IR numa_bind (), .IR numa_run_on_node (), .IR numa_run_on_node_mask (), .IR numa_run_on_node_mask_all (), and .IR numa_get_run_node_mask (). These functions deal with the CPUs associated with numa nodes. See the descriptions below for more information. Some of these functions accept or return a pointer to struct bitmask. A struct bitmask controls a bit map of arbitrary length containing a bit representation of nodes. The predefined variable .I numa_all_nodes_ptr points to a bit mask that has all available nodes set; .I numa_no_nodes_ptr points to the empty set. Before any other calls in this library can be used .BR numa_available () must be called. If it returns \-1, all other functions in this library are undefined. .BR numa_max_possible_node() returns the number of the highest possible node in a system. In other words, the size of a kernel type nodemask_t (in bits) minus 1. This number can be gotten by calling .BR numa_num_possible_nodes() and subtracting 1. .BR numa_num_possible_nodes() returns the size of kernel's node mask (kernel type nodemask_t). In other words, large enough to represent the maximum number of nodes that the kernel can handle. This will match the kernel's MAX_NUMNODES value. This count is derived from /proc/self/status, field Mems_allowed. .BR numa_max_node () returns the highest node number available on the current system. (See the node numbers in /sys/devices/system/node/ ). Also see .BR numa_num_configured_nodes(). .BR numa_num_configured_nodes() returns the number of memory nodes in the system. This count includes any nodes that are currently disabled. This count is derived from the node numbers in /sys/devices/system/node. (Depends on the kernel being configured with /sys (CONFIG_SYSFS)). .BR numa_get_mems_allowed() returns the mask of nodes from which the process is allowed to allocate memory in it's current cpuset context. Any nodes that are not included in the returned bitmask will be ignored in any of the following libnuma memory policy calls. .BR numa_num_configured_cpus() returns the number of cpus in the system. This count includes any cpus that are currently disabled. This count is derived from the cpu numbers in /sys/devices/system/cpu. If the kernel is configured without /sys (CONFIG_SYSFS=n) then it falls back to using the number of online cpus. .BR numa_all_nodes_ptr points to a bitmask that is allocated by the library with bits representing all nodes on which the calling task may allocate memory. This set may be up to all nodes on the system, or up to the nodes in the current cpuset. The bitmask is allocated by a call to .BR numa_allocate_nodemask() using size .BR numa_max_possible_node(). The set of nodes to record is derived from /proc/self/status, field "Mems_allowed". The user should not alter this bitmask. .BR numa_no_nodes_ptr points to a bitmask that is allocated by the library and left all zeroes. The bitmask is allocated by a call to .BR numa_allocate_nodemask() using size .BR numa_max_possible_node(). The user should not alter this bitmask. .BR numa_all_cpus_ptr points to a bitmask that is allocated by the library with bits representing all cpus on which the calling task may execute. This set may be up to all cpus on the system, or up to the cpus in the current cpuset. The bitmask is allocated by a call to .BR numa_allocate_cpumask() using size .BR numa_num_possible_cpus(). The set of cpus to record is derived from /proc/self/status, field "Cpus_allowed". The user should not alter this bitmask. .BR numa_num_task_cpus() returns the number of cpus that the calling task is allowed to use. This count is derived from the map /proc/self/status, field "Cpus_allowed". Also see the bitmask .BR numa_all_cpus_ptr. .BR numa_num_task_nodes() returns the number of nodes on which the calling task is allowed to allocate memory. This count is derived from the map /proc/self/status, field "Mems_allowed". Also see the bitmask .BR numa_all_nodes_ptr. .BR numa_parse_bitmap() parses .I line , which is a character string such as found in /sys/devices/system/node/nodeN/cpumap into a bitmask structure. The string contains the hexadecimal representation of a bit map. The bitmask may be allocated with .BR numa_allocate_cpumask(). Returns 0 on success. Returns -1 on failure. This function is probably of little use to a user application, but it is used by .I libnuma internally. .BR numa_parse_nodestring() parses a character string list of nodes into a bit mask. The bit mask is allocated by .BR numa_allocate_nodemask(). The string is a comma-separated list of node numbers or node ranges. A leading ! can be used to indicate "not" this list (in other words, all nodes except this list), and a leading + can be used to indicate that the node numbers in the list are relative to the task's cpuset. The string can be "all" to specify all ( .BR numa_num_task_nodes() ) nodes. Node numbers are limited by the number in the system. See .BR numa_max_node() and .BR numa_num_configured_nodes(). .br Examples: 1-5,7,10 !4-5 +0-3 .br If the string is of 0 length, bitmask .BR numa_no_nodes_ptr is returned. Returns 0 if the string is invalid. .BR numa_parse_nodestring_all() is similar to .BR numa_parse_nodestring , but can parse all possible nodes, not only current nodeset. .BR numa_parse_cpustring() parses a character string list of cpus into a bit mask. The bit mask is allocated by .BR numa_allocate_cpumask(). The string is a comma-separated list of cpu numbers or cpu ranges. A leading ! can be used to indicate "not" this list (in other words, all cpus except this list), and a leading + can be used to indicate that the cpu numbers in the list are relative to the task's cpuset. The string can be "all" to specify all ( .BR numa_num_task_cpus() ) cpus. Cpu numbers are limited by the number in the system. See .BR numa_num_task_cpus() and .BR numa_num_configured_cpus(). .br Examples: 1-5,7,10 !4-5 +0-3 .br Returns 0 if the string is invalid. .BR numa_parse_cpustring_all() is similar to .BR numa_parse_cpustring , but can parse all possible cpus, not only current cpuset. .BR numa_node_size () returns the memory size of a node. If the argument .I freep is not NULL, it used to return the amount of free memory on the node. On error it returns \-1. .BR numa_node_size64 () works the same as .BR numa_node_size () except that it returns values as .I long long instead of .IR long . This is useful on 32-bit architectures with large nodes. .BR numa_preferred () returns the preferred node of the current task. This is the node on which the kernel preferably allocates memory, unless some other policy overrides this. .\" TODO: results are misleading for MPOL_PREFERRED and may .\" be incorrect for MPOL_BIND when Mel Gorman's twozonelist .\" patches go in. In the latter case, we'd need to know the .\" order of the current node's zonelist to return the correct .\" node. Need to tighten this up with the syscall results. .BR numa_set_preferred () sets the preferred node for the current task to .IR node . The system will attempt to allocate memory from the preferred node, but will fall back to other nodes if no memory is available on the the preferred node. Passing a .I node of \-1 argument specifies local allocation and is equivalent to calling .BR numa_set_localalloc (). .BR numa_get_interleave_mask () returns the current interleave mask if the task's memory allocation policy is page interleaved. Otherwise, this function returns an empty mask. .BR numa_set_interleave_mask () sets the memory interleave mask for the current task to .IR nodemask . All new memory allocations are page interleaved over all nodes in the interleave mask. Interleaving can be turned off again by passing an empty mask .RI ( numa_no_nodes ). The page interleaving only occurs on the actual page fault that puts a new page into the current address space. It is also only a hint: the kernel will fall back to other nodes if no memory is available on the interleave target. .\" NOTE: the following is not really the case. this function sets the .\" task policy for all future allocations, including stack, bss, ... .\" The functions specified in this sentence actually allocate a new memory .\" range [via mmap()]. This is quite a different thing. Suggest we drop .\" this. .\" This is a low level .\" function, it may be more convenient to use the higher level functions like .\" .BR numa_alloc_interleaved () .\" or .\" .BR numa_alloc_interleaved_subset (). .BR numa_interleave_memory () interleaves .I size bytes of memory page by page from .I start on nodes specified in .IR nodemask . The .I size argument will be rounded up to a multiple of the system page size. If .I nodemask contains nodes that are externally denied to this process, this call will fail. This is a lower level function to interleave allocated but not yet faulted in memory. Not yet faulted in means the memory is allocated using .BR mmap (2) or .BR shmat (2), but has not been accessed by the current process yet. The memory is page interleaved to all nodes specified in .IR nodemask . Normally .BR numa_alloc_interleaved () should be used for private memory instead, but this function is useful to handle shared memory areas. To be useful the memory area should be several megabytes at least (or tens of megabytes of hugetlbfs mappings) If the .BR numa_set_strict () flag is true then the operation will cause a numa_error if there were already pages in the mapping that do not follow the policy. .BR numa_bind () binds the current task and its children to the nodes specified in .IR nodemask . They will only run on the CPUs of the specified nodes and only be able to allocate memory from them. This function is equivalent to calling .\" FIXME checkme .\" This is the case. --lts .I numa_run_on_node_mask(nodemask) followed by .IR numa_set_membind(nodemask) . If tasks should be bound to individual CPUs inside nodes consider using .I numa_node_to_cpus and the .I sched_setaffinity(2) syscall. .BR numa_set_localalloc () sets the memory allocation policy for the calling task to local allocation. In this mode, the preferred node for memory allocation is effectively the node where the task is executing at the time of a page allocation. .BR numa_set_membind () sets the memory allocation mask. The task will only allocate memory from the nodes set in .IR nodemask . Passing an empty .I nodemask or a .I nodemask that contains nodes other than those in the mask returned by .IR numa_get_mems_allowed () will result in an error. .BR numa_get_membind () returns the mask of nodes from which memory can currently be allocated. If the returned mask is equal to .IR numa_all_nodes , then memory allocation is allowed from all nodes. .BR numa_alloc_onnode () allocates memory on a specific node. The .I size argument will be rounded up to a multiple of the system page size. if the specified .I node is externally denied to this process, this call will fail. This function is relatively slow compared to the .IR malloc (3), family of functions. The memory must be freed with .BR numa_free (). On errors NULL is returned. .BR numa_alloc_local () allocates .I size bytes of memory on the local node. The .I size argument will be rounded up to a multiple of the system page size. This function is relatively slow compared to the .IR malloc (3) family of functions. The memory must be freed with .BR numa_free (). On errors NULL is returned. .BR numa_alloc_interleaved () allocates .I size bytes of memory page interleaved on all nodes. This function is relatively slow and should only be used for large areas consisting of multiple pages. The interleaving works at page level and will only show an effect when the area is large. The allocated memory must be freed with .BR numa_free (). On error, NULL is returned. .BR numa_alloc_interleaved_subset () attempts to allocate .I size bytes of memory page interleaved on all nodes. The .I size argument will be rounded up to a multiple of the system page size. The nodes on which a process is allowed to allocate memory may be constrained externally. If this is the case, this function may fail. This function is relatively slow compare to .IR malloc (3), family of functions and should only be used for large areas consisting of multiple pages. The interleaving works at page level and will only show an effect when the area is large. The allocated memory must be freed with .BR numa_free (). On error, NULL is returned. .BR numa_alloc () allocates .I size bytes of memory with the current NUMA policy. The .I size argument will be rounded up to a multiple of the system page size. This function is relatively slow compare to the .IR malloc (3) family of functions. The memory must be freed with .BR numa_free (). On errors NULL is returned. .BR numa_realloc () changes the size of the memory area pointed to by .I old_addr from .I old_size to .I new_size. The memory area pointed to by .I old_addr must have been allocated with one of the .BR numa_alloc* functions. The .I new_size will be rounded up to a multiple of the system page size. The contents of the memory area will be unchanged to the minimum of the old and new sizes; newly allocated memory will be uninitialized. The memory policy (and node bindings) associated with the original memory area will be preserved in the resized area. For example, if the initial area was allocated with a call to .BR numa_alloc_onnode(), then the new pages (if the area is enlarged) will be allocated on the same node. However, if no memory policy was set for the original area, then .BR numa_realloc () cannot guarantee that the new pages will be allocated on the same node. On success, the address of the resized area is returned (which might be different from that of the initial area), otherwise NULL is returned and .I errno is set to indicate the error. The pointer returned by .BR numa_realloc () is suitable for passing to .BR numa_free (). .BR numa_free () frees .I size bytes of memory starting at .IR start , allocated by the .B numa_alloc_* functions above. The .I size argument will be rounded up to a multiple of the system page size. .BR numa_run_on_node () runs the current task and its children on a specific node. They will not migrate to CPUs of other nodes until the node affinity is reset with a new call to .BR numa_run_on_node_mask (). Passing \-1 permits the kernel to schedule on all nodes again. On success, 0 is returned; on error \-1 is returned, and .I errno is set to indicate the error. .BR numa_run_on_node_mask () runs the current task and its children only on nodes specified in .IR nodemask . They will not migrate to CPUs of other nodes until the node affinity is reset with a new call to .BR numa_run_on_node_mask () or .BR numa_run_on_node (). Passing .I numa_all_nodes permits the kernel to schedule on all nodes again. On success, 0 is returned; on error \-1 is returned, and .I errno is set to indicate the error. .BR numa_run_on_node_mask_all () runs the current task and its children only on nodes specified in .IR nodemask like .I numa_run_on_node_mask but without any cpuset awareness. .BR numa_get_run_node_mask () returns a mask of CPUs on which the current task is allowed to run. .BR numa_tonode_memory () put memory on a specific node. The constraints described for .BR numa_interleave_memory () apply here too. .BR numa_tonodemask_memory () put memory on a specific set of nodes. The constraints described for .BR numa_interleave_memory () apply here too. .BR numa_setlocal_memory () locates memory on the current node. The constraints described for .BR numa_interleave_memory () apply here too. .BR numa_police_memory () locates memory with the current NUMA policy. The constraints described for .BR numa_interleave_memory () apply here too. .BR numa_distance () reports the distance in the machine topology between two nodes. The factors are a multiple of 10. It returns 0 when the distance cannot be determined. A node has distance 10 to itself. Reporting the distance requires a Linux kernel version of .I 2.6.10 or newer. .BR numa_set_bind_policy () specifies whether calls that bind memory to a specific node should use the preferred policy or a strict policy. The preferred policy allows the kernel to allocate memory on other nodes when there isn't enough free on the target node. strict will fail the allocation in that case. Setting the argument to specifies strict, 0 preferred. Note that specifying more than one node non strict may only use the first node in some kernel versions. .BR numa_set_strict () sets a flag that says whether the functions allocating on specific nodes should use use a strict policy. Strict means the allocation will fail if the memory cannot be allocated on the target node. Default operation is to fall back to other nodes. This doesn't apply to interleave and default. .BR numa_get_interleave_node() is used by .I libnuma internally. It is probably not useful for user applications. It uses the MPOL_F_NODE flag of the get_mempolicy system call, which is not intended for application use (its operation may change or be removed altogether in future kernel versions). See get_mempolicy(2). .BR numa_pagesize() returns the number of bytes in page. This function is simply a fast alternative to repeated calls to the getpagesize system call. See getpagesize(2). .BR numa_sched_getaffinity() retrieves a bitmask of the cpus on which a task may run. The task is specified by .I pid. Returns the return value of the sched_getaffinity system call. See sched_getaffinity(2). The bitmask must be at least the size of the kernel's cpu mask structure. Use .BR numa_allocate_cpumask() to allocate it. Test the bits in the mask by calling .BR numa_bitmask_isbitset(). .BR numa_sched_setaffinity() sets a task's allowed cpu's to those cpu's specified in .I mask. The task is specified by .I pid. Returns the return value of the sched_setaffinity system call. See sched_setaffinity(2). You may allocate the bitmask with .BR numa_allocate_cpumask(). Or the bitmask may be smaller than the kernel's cpu mask structure. For example, call .BR numa_bitmask_alloc() using a maximum number of cpus from .BR numa_num_configured_cpus(). Set the bits in the mask by calling .BR numa_bitmask_setbit(). .BR numa_node_to_cpus () converts a node number to a bitmask of CPUs. The user must pass a bitmask structure with a mask buffer long enough to represent all possible cpu's. Use numa_allocate_cpumask() to create it. If the bitmask is not long enough .I errno will be set to .I ERANGE and \-1 returned. On success 0 is returned. .BR numa_node_of_cpu () returns the node that a cpu belongs to. If the user supplies an invalid cpu .I errno will be set to .I EINVAL and \-1 will be returned. .BR numa_allocate_cpumask () returns a bitmask of a size equal to the kernel's cpu mask (kernel type cpumask_t). In other words, large enough to represent NR_CPUS cpus. This number of cpus can be gotten by calling .BR numa_num_possible_cpus(). The bitmask is zero-filled. .BR numa_free_cpumask frees a cpumask previously allocate by .I numa_allocate_cpumask. .BR numa_allocate_nodemask() returns a bitmask of a size equal to the kernel's node mask (kernel type nodemask_t). In other words, large enough to represent MAX_NUMNODES nodes. This number of nodes can be gotten by calling .BR numa_num_possible_nodes(). The bitmask is zero-filled. .BR numa_free_nodemask() frees a nodemask previous allocated by .I numa_allocate_nodemask(). .BR numa_bitmask_alloc() allocates a bitmask structure and its associated bit mask. The memory allocated for the bit mask contains enough words (type unsigned long) to contain .I n bits. The bit mask is zero-filled. The bitmask structure points to the bit mask and contains the .I n value. .BR numa_bitmask_clearall() sets all bits in the bit mask to 0. The bitmask structure points to the bit mask and contains its size ( .I bmp ->size). The value of .I bmp is always returned. Note that .BR numa_bitmask_alloc() creates a zero-filled bit mask. .BR numa_bitmask_clearbit() sets a specified bit in a bit mask to 0. Nothing is done if the .I n value is greater than the size of the bitmask (and no error is returned). The value of .I bmp is always returned. .BR numa_bitmask_equal() returns 1 if two bitmasks are equal. It returns 0 if they are not equal. If the bitmask structures control bit masks of different sizes, the "missing" trailing bits of the smaller bit mask are considered to be 0. .BR numa_bitmask_free() deallocates the memory of both the bitmask structure pointed to by .I bmp and the bit mask. It is an error to attempt to free this bitmask twice. .BR numa_bitmask_isbitset() returns the value of a specified bit in a bit mask. If the .I n value is greater than the size of the bit map, 0 is returned. .BR numa_bitmask_nbytes() returns the size (in bytes) of the bit mask controlled by .I bmp. The bit masks are always full words (type unsigned long), and the returned size is the actual size of all those words. .BR numa_bitmask_setall() sets all bits in the bit mask to 1. The bitmask structure points to the bit mask and contains its size ( .I bmp ->size). The value of .I bmp is always returned. .BR numa_bitmask_setbit() sets a specified bit in a bit mask to 1. Nothing is done if .I n is greater than the size of the bitmask (and no error is returned). The value of .I bmp is always returned. .BR copy_bitmask_to_nodemask() copies the body (the bit map itself) of the bitmask structure pointed to by .I bmp to the nodemask_t structure pointed to by the .I nodemask pointer. If the two areas differ in size, the copy is truncated to the size of the receiving field or zero-filled. .BR copy_nodemask_to_bitmask() copies the nodemask_t structure pointed to by the .I nodemask pointer to the body (the bit map itself) of the bitmask structure pointed to by the .I bmp pointer. If the two areas differ in size, the copy is truncated to the size of the receiving field or zero-filled. .BR copy_bitmask_to_bitmask() copies the body (the bit map itself) of the bitmask structure pointed to by the .I bmpfrom pointer to the body of the bitmask structure pointed to by the .I bmpto pointer. If the two areas differ in size, the copy is truncated to the size of the receiving field or zero-filled. .BR numa_bitmask_weight() returns a count of the bits that are set in the body of the bitmask pointed to by the .I bmp argument. .br .BR numa_move_pages() moves a list of pages in the address space of the currently executing or current process. It simply uses the move_pages system call. .br .I pid - ID of task. If not valid, use the current task. .br .I count - Number of pages. .br .I pages - List of pages to move. .br .I nodes - List of nodes to which pages can be moved. .br .I status - Field to which status is to be returned. .br .I flags - MPOL_MF_MOVE or MPOL_MF_MOVE_ALL .br See move_pages(2). .BR numa_migrate_pages() simply uses the migrate_pages system call to cause the pages of the calling task, or a specified task, to be migated from one set of nodes to another. See migrate_pages(2). The bit masks representing the nodes should be allocated with .BR numa_allocate_nodemask() , or with .BR numa_bitmask_alloc() using an .I n value returned from .BR numa_num_possible_nodes(). A task's current node set can be gotten by calling .BR numa_get_membind(). Bits in the .I tonodes mask can be set by calls to .BR numa_bitmask_setbit(). .BR numa_error () is a .I libnuma internal function that can be overridden by the user program. This function is called with a .I char * argument when a .I libnuma function fails. Overriding the library internal definition makes it possible to specify a different error handling strategy when a .I libnuma function fails. It does not affect .BR numa_available (). The .BR numa_error () function defined in .I libnuma prints an error on .I stderr and terminates the program if .I numa_exit_on_error is set to a non-zero value. The default value of .I numa_exit_on_error is zero. .BR numa_warn () is a .I libnuma internal function that can be also overridden by the user program. It is called to warn the user when a .I libnuma function encounters a non-fatal error. The default implementation prints a warning to .IR stderr . The first argument is a unique number identifying each warning. After that there is a .BR printf (3)-style format string and a variable number of arguments. .I numa_warn exits the program when .I numa_exit_on_warn is set to a non-zero value. The default value of .I numa_exit_on_warn is zero. .SH Compatibility with libnuma version 1 Binaries that were compiled for libnuma version 1 need not be re-compiled to run with libnuma version 2. .br Source codes written for libnuma version 1 may be re-compiled without change with version 2 installed. To do so, in the code's Makefile add this option to CFLAGS: -DNUMA_VERSION1_COMPATIBILITY .SH THREAD SAFETY .I numa_set_bind_policy and .I numa_exit_on_error are process global. The other calls are thread safe. .SH COPYRIGHT Copyright 2002, 2004, 2007, 2008 Andi Kleen, SuSE Labs. .I libnuma is under the GNU Lesser General Public License, v2.1. .SH SEE ALSO .BR get_mempolicy (2), .BR set_mempolicy (2), .BR getpagesize (2), .BR mbind (2), .BR mmap (2), .BR shmat (2), .BR numactl (8), .BR sched_getaffinity (2) .BR sched_setaffinity (2) .BR move_pages (2) .BR migrate_pages (2) ./numactl-2.0.9~rc5/migratepages.80000644000175000017500000000374312213661422015357 0ustar ianwianw.\" t .\" Copyright 2005-2006 Christoph Lameter, Silicon Graphics, Inc. .\" .\" based on Andi Kleen's numactl manpage .\" .TH MIGRATEPAGES 8 "Jan 2005" "SGI" "Linux Administrator's Manual" .SH NAME migratepages \- Migrate the physical location a processes pages .SH SYNOPSIS .B migratepages pid from-nodes to-nodes .SH DESCRIPTION .B migratepages moves the physical location of a processes pages without any changes of the virtual address space of the process. Moving the pages allows one to change the distances of a process to its memory. Performance may be optimized by moving a processes pages to the node where it is executing. If multiple nodes are specified for from-nodes or to-nodes then an attempt is made to preserve the relative location of each page in each nodeset. For example if we move from nodes 2-5 to 7,9,12-13 then the preferred mode of operation is to move pages from 2->7, 3->9, 4->12 and 5->13. However, this is only posssible if enough memory is available. .TP Valid node specifiers .TS tab(:); l l. all:All nodes number:Node number number1{,number2}:Node number1 and Node number2 number1-number2:Nodes from number1 to number2 ! nodes:Invert selection of the following specification. .TE .SH NOTES Requires an NUMA policy aware kernel with support for page migration (linux 2.6.16 and later). migratepages will only move pages that are not shared with other processes if called by a user without administrative priviledges (but with the right to modify the process). migratepages will move all pages if invoked from root (or a user with administrative priviledges). .SH FILES .I /proc//numa_maps for information about the NUMA memory use of a process. .SH COPYRIGHT Copyright 2005-2006 Christoph Lameter, Silicon Graphics, Inc. migratepages is under the GNU General Public License, v.2 .SH SEE ALSO .I numactl(8) , .I set_mempolicy(2) , .I get_mempolicy(2) , .I mbind(2) , .I sched_setaffinity(2) , .I sched_getaffinity(2) , .I proc(5) , .I ftok(3) , .I shmat(2) , .I taskset(1) ./numactl-2.0.9~rc5/mt.h0000755000175000017500000000064712213661422013412 0ustar ianwianw#define MT_LEN 624 extern void mt_init(void); extern void mt_refill(); extern int mt_index; extern unsigned int mt_buffer[MT_LEN]; static inline unsigned int mt_random(void) { unsigned int * b = mt_buffer; int idx = mt_index; if (idx == MT_LEN*sizeof(unsigned int)) { mt_refill(); idx = 0; } mt_index += sizeof(unsigned int); return *(unsigned int *)((unsigned char *)b + idx); } ./numactl-2.0.9~rc5/migratepages.c0000755000175000017500000000423112213661422015426 0ustar ianwianw/* * Copyright (C) 2005 Christoph Lameter, Silicon Graphics, Incorporated. * based on Andi Kleen's numactl.c. * * Manual process migration * * migratepages 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; version 2. * * migratepages 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. See the GNU * General Public License for more details. * * You should find a copy of v2 of the GNU General Public License somewhere * on your Linux system; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "numa.h" #include "numaif.h" #include "numaint.h" #include "util.h" struct option opts[] = { {"help", 0, 0, 'h' }, { 0 } }; void usage(void) { fprintf(stderr, "usage: migratepages pid from-nodes to-nodes\n" "\n" "nodes is a comma delimited list of node numbers or A-B ranges or all.\n" ); exit(1); } void checknuma(void) { static int numa = -1; if (numa < 0) { if (numa_available() < 0) complain("This system does not support NUMA functionality"); } numa = 0; } int main(int argc, char *argv[]) { int c; char *end; int rc; int pid; struct bitmask *fromnodes; struct bitmask *tonodes; while ((c = getopt_long(argc,argv,"h", opts, NULL)) != -1) { switch (c) { default: usage(); } } argv += optind; argc -= optind; if (argc != 3) usage(); checknuma(); pid = strtoul(argv[0], &end, 0); if (*end || end == argv[0]) usage(); fromnodes = numa_parse_nodestring(argv[1]); if (!fromnodes) { printf ("<%s> is invalid\n", argv[1]); exit(1); } tonodes = numa_parse_nodestring(argv[2]); if (!tonodes) { printf ("<%s> is invalid\n", argv[2]); exit(1); } rc = numa_migrate_pages(pid, fromnodes, tonodes); if (rc < 0) { perror("migrate_pages"); return 1; } return 0; }