#define _GNU_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#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;
}
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);
}
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) {
perror("ftruncate");
}
}
shm_pagesize = st.st_blksize;
check_region(opt);
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("%016llx-%016llx: %s ",
shmoffset+start,
shmoffset+end,
policy_name(pol));
printmask("", mask);
}
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("%016llx-%016llx: %d\n", shmoffset+start, shmoffset+end, node);
}
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;
}
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;
}
}
}