| /* |
| * memtoy: segment.c - manage memory segments |
| * |
| * create/destroy/map/unmap - anonymous, file and SysV shmem segments |
| * touch [read or write] - ranges of segments |
| * mbind - ranges of segments |
| * show mappings or locations of segment pages |
| */ |
| /* |
| * Copyright (c) 2005 Hewlett-Packard, Inc |
| * All rights reserved. |
| */ |
| |
| /* |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include "config.h" |
| #if HAVE_NUMA_H && HAVE_NUMAIF_H && LINUX_MEMPOLICY_H |
| #include <sys/types.h> |
| #include <sys/ipc.h> |
| #include <sys/mman.h> |
| #include <sys/shm.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <libgen.h> |
| #include <numa.h> |
| #include <numaif.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include "memtoy.h" |
| #include "segment.h" |
| |
| |
| struct segment { |
| char *seg_name; |
| void *seg_start; |
| size_t seg_length; |
| |
| off_t seg_offset; /* memory mapped files */ |
| char *seg_path; /* " " " */ |
| |
| seg_type_t seg_type; |
| int seg_slot; |
| int seg_flags; /* shared|private */ |
| int seg_prot; |
| int seg_fd; /* saved file descriptor */ |
| int seg_shmid; |
| |
| }; |
| |
| #define MAX_SEGMENTS 63 /* arbitrary max */ |
| #define SEG_FD_NONE (-1) |
| #define SHM_ID_NONE (-1) |
| |
| #define SEG_ERR (0) |
| #define SEG_OK (1) |
| |
| #define SEG_OFFSET(SEGP, ADDR) ((char *)(ADDR) - (char *)(SEGP->seg_start)) |
| |
| |
| /* |
| * ========================================================================= |
| */ |
| void |
| segment_init(struct global_context *gcp) |
| { |
| /* |
| * one extra slot to terminate the list |
| */ |
| gcp->seglist = calloc(MAX_SEGMENTS+1, sizeof(segment_t *)); |
| if (!gcp->seglist) |
| die(4,"%s: can't alloc segment table\n", |
| gcp->program_name); |
| gcp->seg_avail = NULL; |
| |
| } |
| |
| static segment_t * |
| new_segment(void) |
| { |
| glctx_t *gcp = &glctx; |
| segment_t *segp = (segment_t *)calloc(1, sizeof(segment_t)); |
| |
| if (segp == NULL) |
| fprintf(stderr, "%s: failed to allocate segment\n", |
| gcp->program_name); |
| return segp; |
| } |
| |
| /* |
| * get_seg_slot() -- allocate a segment table slot for a new segment |
| */ |
| static segment_t * |
| get_seg_slot(void) |
| { |
| glctx_t *gcp = &glctx; |
| segment_t *segp, **segpp; |
| |
| /* |
| * consume saved slot, if any |
| */ |
| segp = gcp->seg_avail; |
| if (segp != NULL) { |
| gcp->seg_avail = NULL; |
| return segp; |
| } |
| |
| /* |
| * simple linear scan for first available slot |
| */ |
| for (segpp = gcp->seglist; (segp = *segpp); ++segpp) { |
| if (segp->seg_type == SEGT_NONE) |
| return segp; |
| } |
| |
| if (segpp < &gcp->seglist[MAX_SEGMENTS]) { |
| /* |
| * previously unused slot |
| */ |
| *segpp = segp = new_segment(); |
| segp->seg_slot = segpp - gcp->seglist; |
| return segp; |
| } |
| |
| fprintf(stderr, "%s: segment table full\n", gcp->program_name); |
| return (segment_t *)NULL; |
| } |
| |
| static void |
| unmap_segment(segment_t *segp) |
| { |
| |
| if(segp->seg_start == MAP_FAILED) |
| return; /* already unmapped */ |
| |
| switch (segp->seg_type) { |
| case SEGT_ANON: |
| case SEGT_FILE: |
| munmap(segp->seg_start, segp->seg_length); |
| break; |
| |
| case SEGT_SHM: |
| shmdt(segp->seg_start); |
| break; |
| |
| default: |
| // shouldn't happen? |
| break; |
| } |
| |
| segp->seg_start = MAP_FAILED; |
| } |
| |
| /* |
| * free up a segment table slot, freeing any string storage |
| * and removing shm segment, if necessary |
| * clear out the segment, but preserve slot # |
| */ |
| static void |
| free_seg_slot(segment_t *segp) |
| { |
| glctx_t *gcp = &glctx; |
| int slot = segp->seg_slot; |
| |
| if (segp->seg_name != NULL) |
| free(segp->seg_name); |
| |
| if (segp->seg_path != NULL) |
| free(segp->seg_path); |
| |
| if (segp->seg_type == SEGT_FILE && |
| segp->seg_fd != SEG_FD_NONE) |
| close(segp->seg_fd); |
| |
| if (segp->seg_type == SEGT_SHM && |
| segp->seg_shmid != SHM_ID_NONE) |
| shmctl(segp->seg_shmid, IPC_RMID, NULL); |
| |
| (void)memset(segp, 0, sizeof(*segp)); |
| |
| segp->seg_slot = slot; |
| if (gcp->seg_avail == NULL) |
| gcp->seg_avail = segp; |
| |
| } |
| |
| /* |
| * called from memtoy "at exit" cleanup(). |
| * primarily to remove any shm segments created. |
| */ |
| void |
| segment_cleanup(struct global_context *gcp) |
| { |
| segment_t *segp, **segpp; |
| |
| segpp = gcp->seglist; |
| if (segpp == NULL) |
| return; |
| |
| for (; (segp = *segpp); ++segpp) { |
| if (segp->seg_type != SEGT_SHM) { |
| continue; |
| } |
| free_seg_slot(segp); /* to remove shared mem */ |
| } |
| } |
| |
| static size_t |
| round_up_to_pagesize(size_t size) |
| { |
| glctx_t *gcp = &glctx; |
| size_t pagemask = gcp->pagesize - 1; |
| |
| return ((size + pagemask ) & ~pagemask); |
| |
| } |
| |
| static size_t |
| round_down_to_pagesize(size_t size) |
| { |
| glctx_t *gcp = &glctx; |
| size_t pagemask = gcp->pagesize - 1; |
| |
| return (size & ~pagemask); |
| |
| } |
| |
| /* |
| * get_node() -- fetch numa node id of page at vaddr |
| * [from Ray Bryant's [SGI] memory migration tests] |
| */ |
| static int |
| get_node(void *vaddr) |
| { |
| int rc, node; |
| |
| rc = get_mempolicy(&node, NULL, 0, vaddr, MPOL_F_NODE|MPOL_F_ADDR); |
| if (rc) |
| return -1; |
| |
| return node; |
| } |
| |
| /* |
| * ========================================================================= |
| */ |
| static int |
| map_anon_segment(segment_t *segp) |
| { |
| glctx_t *gcp = &glctx; |
| |
| char *memp; |
| int flags = segp->seg_flags; |
| |
| if(!flags) |
| flags = MAP_PRIVATE; /* default */ |
| |
| memp = (char *)mmap(0, segp->seg_length, |
| segp->seg_prot, |
| flags|MAP_ANONYMOUS, |
| 0, /* fd -- ignored */ |
| 0); /* offset -- ignored */ |
| |
| if (memp == MAP_FAILED) { |
| int err = errno; |
| fprintf(stderr, "%s: anonymous mmap failed - %s\n", |
| __FUNCTION__, strerror(err)); |
| return SEG_ERR; |
| } |
| |
| vprint("%s: mmap()ed anon seg %s at 0x%lx-0x%lx\n", |
| gcp->program_name, segp->seg_name, |
| memp, memp+segp->seg_length-1); |
| |
| segp->seg_start = memp; |
| |
| return SEG_OK; |
| } |
| |
| /* |
| * open_file() -- open and validate file when registering a file segment. |
| * remember fd in segment struct. |
| */ |
| static int |
| open_file(segment_t *segp) |
| { |
| glctx_t *gcp = &glctx; |
| |
| struct stat stbuf; |
| int fd, flags; |
| |
| if(stat(segp->seg_path, &stbuf) < 0) { |
| int err = errno; |
| fprintf(stderr, "%s: can't stat %s - %s\n", |
| gcp->program_name, segp->seg_path, |
| strerror(err)); |
| free_seg_slot(segp); |
| return SEG_ERR; |
| } |
| |
| /* |
| * TODO: for now, just regular files. later? |
| */ |
| if(!S_ISREG(stbuf.st_mode)) { |
| fprintf(stderr, "%s: %s - is not a regular file\n", |
| gcp->program_name, segp->seg_path); |
| free_seg_slot(segp); |
| return SEG_ERR; |
| } |
| |
| /* |
| * Open file with maximal privileges; adjust segment mapping |
| * protections if permissions don't allow full R/W access. |
| */ |
| if (!access(segp->seg_path, R_OK|W_OK)) |
| flags = O_RDWR; |
| else if (!access(segp->seg_path, R_OK)) { |
| flags = O_RDONLY; |
| segp->seg_prot &= ~PROT_WRITE; |
| } else if (!access(segp->seg_path, W_OK)) { |
| flags = O_WRONLY; |
| segp->seg_prot &= ~PROT_READ; |
| } else { |
| fprintf(stderr, "%s: can't access %s\n", |
| gcp->program_name, segp->seg_path); |
| free_seg_slot(segp); |
| return SEG_ERR; |
| } |
| |
| fd = open(segp->seg_path, flags); |
| if (fd < 0) { |
| int err = errno; |
| fprintf(stderr, "%s: can't open %s - %s\n", |
| gcp->program_name, segp->seg_path, |
| strerror(err)); |
| free_seg_slot(segp); |
| return SEG_ERR; |
| } |
| |
| segp->seg_fd = fd; |
| return SEG_OK; |
| } |
| |
| /* |
| * re-fetch file size at map time -- just in case it's changed |
| */ |
| static size_t |
| file_size(int fd) |
| { |
| struct stat stbuf; |
| |
| if (fstat(fd, &stbuf) != 0) { |
| return BOGUS_SIZE; |
| } |
| |
| return stbuf.st_size; |
| } |
| |
| /* |
| * map_file_segment() -- map a [range of a] registered file segment. |
| */ |
| static int |
| map_file_segment(segment_t *segp) |
| { |
| glctx_t *gcp = &glctx; |
| |
| char *memp; |
| size_t size; |
| int fd; |
| int flags = segp->seg_flags; |
| |
| if(!flags) |
| flags = MAP_PRIVATE; /* default */ |
| |
| if ((fd = segp->seg_fd) == SEG_FD_NONE) { |
| fprintf(stderr, "%s: file %s not open\n", |
| gcp->program_name, segp->seg_path); |
| return SEG_ERR; |
| } |
| |
| size = file_size(fd); |
| |
| /* |
| * page align offset/length; verify fit in file |
| */ |
| segp->seg_offset = round_down_to_pagesize(segp->seg_offset); |
| if (segp->seg_offset > size) { |
| fprintf(stderr, "%s: offset 0x%lx beyond end of file %s\n", |
| gcp->program_name, segp->seg_offset, |
| segp->seg_path); |
| return SEG_ERR; |
| } |
| |
| if (segp->seg_length == 0) |
| segp->seg_length = round_up_to_pagesize(size) - |
| segp->seg_offset; |
| else |
| segp->seg_length = round_up_to_pagesize(segp->seg_length); |
| |
| memp = (char *)mmap(0, segp->seg_length, |
| segp->seg_prot, |
| flags, |
| fd, |
| segp->seg_offset); |
| |
| if (memp == MAP_FAILED) { |
| int err = errno; |
| fprintf(stderr, "%s: mmap of %s failed - %s\n", |
| __FUNCTION__, segp->seg_path, strerror(err)); |
| return SEG_ERR; |
| } |
| |
| vprint("%s: mmap()ed file seg %s at 0x%lx-0x%lx\n", |
| gcp->program_name, segp->seg_name, |
| memp, memp+segp->seg_length-1); |
| |
| segp->seg_start = memp; |
| |
| return SEG_OK; |
| } |
| |
| /* |
| * get_shm_segment() -- create [shmget] a new shared memory segment |
| */ |
| static int |
| get_shm_segment(segment_t *segp) |
| { |
| glctx_t *gcp = &glctx; |
| |
| int shmid; |
| |
| shmid = shmget(IPC_PRIVATE, segp->seg_length, SHM_R|SHM_W); |
| if (shmid == -1) { |
| int err = errno; |
| fprintf(stderr, "%s: failed to get shm segment %s - %s\n", |
| gcp->program_name, segp->seg_name, |
| strerror(err)); |
| free_seg_slot(segp); |
| return SEG_ERR; |
| } |
| |
| segp->seg_shmid = shmid; |
| vprint("%s: shm seg %s id: %d\n", |
| gcp->program_name, segp->seg_name, segp->seg_shmid); |
| return SEG_OK; |
| } |
| |
| /* |
| * map_shm_segment() -- attach [shmat] a shared memory segment |
| */ |
| static int |
| map_shm_segment(segment_t *segp) |
| { |
| glctx_t *gcp = &glctx; |
| |
| segp->seg_start = shmat(segp->seg_shmid, NULL, 0); |
| if (segp->seg_start == MAP_FAILED) { |
| int err = errno; |
| fprintf(stderr, "%s: failed to attach shm segment %s: %s\n", |
| gcp->program_name, segp->seg_name, |
| strerror(err)); |
| return SEG_ERR; |
| } |
| |
| vprint("%s: mmap()ed shm seg %s at 0x%lx-0x%lx\n", |
| gcp->program_name, segp->seg_name, |
| segp->seg_start, segp->seg_start+segp->seg_length-1); |
| |
| return SEG_OK; |
| } |
| |
| /* |
| * ========================================================================= |
| * segment API |
| */ |
| /* |
| * segment_get(name) - lookup named segment |
| TODO: move to segment private functions? |
| */ |
| segment_t * |
| segment_get(char *name) |
| { |
| glctx_t *gcp = &glctx; |
| segment_t *segp, **segpp; |
| |
| for (segpp = gcp->seglist; (segp = *segpp); ++segpp) { |
| if (segp->seg_type == SEGT_NONE) { |
| if (gcp->seg_avail == NULL) |
| gcp->seg_avail = *segpp; |
| continue; |
| } |
| if (!strcmp(name, segp->seg_name)) |
| return segp; |
| } |
| |
| if (gcp->seg_avail == NULL && |
| segpp < &gcp->seglist[MAX_SEGMENTS]) { |
| /* |
| * prealloc an available segment |
| */ |
| *segpp = segp = new_segment(); |
| if (segp != NULL) { |
| segp->seg_slot = segpp - gcp->seglist; |
| gcp->seg_avail = segp; |
| } |
| } |
| |
| return (segment_t *)NULL; |
| } |
| |
| /* |
| * segment_register: register an anon, file or shm segment based on args. |
| * for anon and shm, 'name' = segment name. |
| * for file, 'name' = path name; segment name = basename(path) |
| * |
| * returns: !0 on success; 0 on failure |
| */ |
| int |
| segment_register(seg_type_t type, char *name, range_t *range, int flags) |
| { |
| glctx_t *gcp = &glctx; |
| segment_t *segp; |
| char *path; |
| |
| segp = segment_get(basename(name)); /* ensure unique name */ |
| if (segp != NULL) { |
| fprintf(stderr, "%s: segment %s already exists\n", |
| gcp->program_name, segp->seg_name); |
| return SEG_ERR; |
| } |
| |
| segp = get_seg_slot(); |
| if (segp == NULL) |
| return SEG_ERR; |
| |
| path = strdup(name); /* save a copy */ |
| segp->seg_name = strdup(basename(name)); |
| segp->seg_start = MAP_FAILED; |
| segp->seg_length = round_up_to_pagesize(range->length); |
| segp->seg_offset = round_down_to_pagesize(range->offset); |
| segp->seg_type = type; |
| segp->seg_flags = flags; /* possibly 0 */ |
| segp->seg_prot = PROT_READ|PROT_WRITE; /* default */ |
| segp->seg_fd = SEG_FD_NONE; |
| segp->seg_shmid = SHM_ID_NONE; |
| |
| switch (type) { |
| case SEGT_ANON: |
| free(path); |
| break; |
| |
| case SEGT_FILE: |
| segp->seg_path = path; |
| return open_file(segp); |
| break; |
| |
| case SEGT_SHM: |
| free(path); |
| return get_shm_segment(segp); |
| break; |
| |
| default: |
| free(path); |
| } |
| return SEG_OK; |
| } |
| |
| static char *segment_header = |
| " _____address______ ____length____ ____offset____ prot share name\n"; |
| |
| static char seg_type[] = { '.', 'a', 'f', 's' }; |
| |
| static int |
| show_one_segment(segment_t *segp, bool header) |
| { |
| char *protection, *share, *name; |
| |
| switch (segp->seg_prot & (PROT_READ|PROT_WRITE)) { |
| case PROT_READ|PROT_WRITE: |
| protection = "rw"; |
| break; |
| |
| case PROT_READ: |
| protection = "r-"; |
| break; |
| |
| case PROT_WRITE: |
| protection = "-w"; |
| break; |
| |
| default: |
| protection = "--"; |
| break; |
| } |
| |
| if (segp->seg_flags) |
| share = (segp->seg_flags & MAP_SHARED) ? "shared " : "private"; |
| else |
| share = "default"; |
| |
| name = (segp->seg_type == SEGT_FILE) ? segp->seg_path : segp->seg_name; |
| |
| if (header) |
| puts(segment_header); |
| |
| if (segp->seg_start != MAP_FAILED) { |
| printf("%c 0x%p 0x%012lx 0x%012lx %s %s %s\n", |
| seg_type[segp->seg_type], |
| segp->seg_start, |
| segp->seg_length, |
| segp->seg_offset, |
| protection, share, name ); |
| } else { |
| printf("%c *** not-mapped *** 0x%012lx 0x%012lx %s %s %s\n", |
| seg_type[segp->seg_type], |
| segp->seg_length, |
| segp->seg_offset, |
| protection, share, name ); |
| } |
| |
| return SEG_OK; |
| } |
| |
| /* |
| * segment_show() -- show specifed segment, or all, if none specified. |
| */ |
| int |
| segment_show(char *name) |
| { |
| glctx_t *gcp = &glctx; |
| segment_t *segp, **segpp; |
| bool header; |
| |
| if(name != NULL) { |
| segp = segment_get(name); |
| if (segp == NULL) { |
| fprintf(stderr, "%s: no such segment: %s\n", |
| gcp->program_name, name); |
| return SEG_ERR; |
| } |
| show_one_segment(segp, false); |
| return SEG_OK; |
| } |
| |
| /* |
| * show all |
| */ |
| header = true; |
| for(segpp = gcp->seglist; (segp = *segpp); ++segpp) { |
| if (segp->seg_type != SEGT_NONE) { |
| show_one_segment(segp, header); |
| header = false; /* first time only */ |
| } |
| } |
| |
| return SEG_OK; |
| |
| } |
| |
| /* |
| * segment_remove() - remove the specified segment, if exists. |
| */ |
| int |
| segment_remove(char *name) |
| { |
| glctx_t *gcp = &glctx; |
| segment_t *segp; |
| |
| segp = segment_get(name); |
| if (segp == NULL) { |
| fprintf(stderr, "%s: no such segment: %s\n", |
| gcp->program_name, name); |
| return SEG_ERR; |
| } |
| |
| unmap_segment(segp); |
| |
| free_seg_slot(segp); |
| |
| return SEG_OK; |
| } |
| |
| /* |
| * segment_touch() - "touch" [read or write] each page of specified range |
| * -- from offset to offset+length -- to fault in or to |
| * test protection. |
| * NOTE: offset is relative to start of mapping, not start of file! |
| */ |
| int |
| segment_touch(char *name, range_t *range, int rw) |
| { |
| glctx_t *gcp = &glctx; |
| segment_t *segp; |
| off_t offset; |
| size_t length, maxlength; |
| unsigned long *memp; |
| struct timeval t_start, t_end; |
| |
| segp = segment_get(name); |
| if (segp == NULL) { |
| fprintf(stderr, "%s: no such segment: %s\n", |
| gcp->program_name, name); |
| return SEG_ERR; |
| } |
| |
| offset = round_down_to_pagesize(range->offset); |
| if (offset >= segp->seg_length) { |
| fprintf(stderr, "%s: offset %ld is past end of segment %s\n", |
| gcp->program_name, offset, name); |
| return SEG_ERR; |
| } |
| |
| memp = (unsigned long*)(segp->seg_start + offset); |
| maxlength = segp->seg_length - offset; |
| |
| length = range->length; |
| if (length) |
| length = round_up_to_pagesize(length); |
| |
| /* |
| * note: we silently truncate to max length [end of segment] |
| */ |
| if(length == 0 || length > maxlength) |
| length = maxlength; |
| |
| gettimeofday(&t_start, NULL); |
| touch_memory(rw, memp, length); |
| gettimeofday(&t_end, NULL); |
| printf("%s: touched %d pages in %6.3f secs\n", |
| gcp->program_name, length/gcp->pagesize, |
| (float)(tv_diff_usec(&t_start, &t_end))/1000000.0); |
| |
| return SEG_OK; |
| } |
| |
| /* |
| * segment_unmap() - unmap the specified segment, if any, from seg_start |
| * to seg_start+seg_lenth. Leave the segment in the |
| * table; |
| */ |
| int |
| segment_unmap(char *name) |
| { |
| glctx_t *gcp = &glctx; |
| segment_t *segp; |
| |
| segp = segment_get(name); |
| if (segp == NULL) { |
| fprintf(stderr, "%s: no such segment: %s\n", |
| gcp->program_name, name); |
| return SEG_ERR; |
| } |
| |
| if (segp->seg_start == MAP_FAILED) |
| return SEG_OK; /* silent success */ |
| |
| switch (segp->seg_type) { |
| case SEGT_ANON: |
| case SEGT_FILE: |
| munmap(segp->seg_start, segp->seg_length); |
| break; |
| |
| case SEGT_SHM: |
| //TODO: shmdt()... |
| break; |
| /* Handle default to get rid of -Wswitch-enum */ |
| default: |
| break; |
| } |
| |
| segp->seg_start = MAP_FAILED; |
| |
| return SEG_OK; |
| } |
| |
| /* |
| * segment_map() -- [re] map() a previously unmapped segment |
| * no-op if already mapped. |
| * range only applies to mapped file. |
| */ |
| int |
| segment_map(char *name, range_t *range, int flags) |
| { |
| glctx_t *gcp = &glctx; |
| segment_t *segp; |
| |
| segp = segment_get(name); |
| if (segp == NULL) { |
| fprintf(stderr, "%s: no such segment: %s\n", |
| gcp->program_name, name); |
| return SEG_ERR; |
| } |
| |
| if (segp->seg_start != MAP_FAILED) { |
| fprintf(stderr, "%s: segment %s already mapped\n", |
| gcp->program_name, name); |
| return SEG_OK; /* treat as success */ |
| } |
| |
| if (flags != 0) |
| segp->seg_flags = flags; |
| |
| switch (segp->seg_type) { |
| case SEGT_ANON: |
| return map_anon_segment(segp); |
| break; |
| |
| case SEGT_FILE: |
| if (range != NULL) { |
| segp->seg_offset = range->offset; |
| segp->seg_length = range->length; |
| } |
| return map_file_segment(segp); |
| break; |
| |
| case SEGT_SHM: |
| return map_shm_segment(segp); |
| break; |
| /* Handle default to get rid of -Wswitch-enum */ |
| default: |
| break; |
| } |
| |
| return SEG_ERR; /* unrecognized segment type -- shouldn't happen */ |
| |
| } |
| |
| /* |
| * segment_mbind() - set memory policy for a range of specified segment |
| * |
| * NOTE: offset is relative to start of mapping, not start of file |
| */ |
| int |
| segment_mbind(char *name, range_t *range, int policy, |
| nodemask_t *nodemask, int flags) |
| { |
| glctx_t *gcp = &glctx; |
| segment_t *segp; |
| char *start; |
| off_t offset; |
| size_t length, maxlength; |
| int ret; |
| |
| segp = segment_get(name); |
| if (segp == NULL) { |
| fprintf(stderr, "%s: no such segment: %s\n", |
| gcp->program_name, name); |
| return SEG_ERR; |
| } |
| |
| if (segp->seg_start == MAP_FAILED) { |
| fprintf(stderr, "%s: segment %s not mapped\n", |
| gcp->program_name, name); |
| return SEG_ERR; |
| } |
| |
| offset = round_down_to_pagesize(range->offset); |
| if (offset >= segp->seg_length) { |
| fprintf(stderr, "%s: offset %ld is past end of segment %s\n", |
| gcp->program_name, offset, name); |
| return SEG_ERR; |
| } |
| |
| start = segp->seg_start + offset; |
| maxlength = segp->seg_length - offset; |
| |
| length = range->length; |
| if (length) |
| length = round_up_to_pagesize(length); |
| |
| /* |
| * note: we silently truncate to max length [end of segment] |
| */ |
| if(length == 0 || length > maxlength) |
| length = maxlength; |
| |
| ret = mbind(segp->seg_start+offset, length, policy, nodemask->n, |
| NUMA_NUM_NODES, flags); |
| |
| if (ret == -1) { |
| int err = errno; |
| fprintf(stderr, "%s: mbind() of segment %s failed - %s\n", |
| gcp->program_name, name, strerror(err)); |
| return SEG_ERR; |
| } |
| |
| return SEG_OK; |
| } |
| |
| /* |
| * segment_location() - report node location of specified range of segment |
| * |
| * NOTE: offset is relative to start of mapping, not start of file |
| */ |
| #define PG_PER_LINE 8 |
| #define PPL_MASK (PG_PER_LINE - 1) |
| int |
| segment_location(char *name, range_t *range) |
| { |
| glctx_t *gcp = &glctx; |
| segment_t *segp; |
| char *apage, *end; |
| off_t offset; |
| size_t length, maxlength; |
| int pgid, i; |
| bool need_nl; |
| |
| segp = segment_get(name); |
| if (segp == NULL) { |
| fprintf(stderr, "%s: no such segment: %s\n", |
| gcp->program_name, name); |
| return SEG_ERR; |
| } |
| |
| if (segp->seg_start == MAP_FAILED) { |
| fprintf(stderr, "%s: segment %s not mapped\n", |
| gcp->program_name, name); |
| return SEG_ERR; |
| } |
| |
| offset = round_down_to_pagesize(range->offset); |
| if (offset >= segp->seg_length) { |
| fprintf(stderr, "%s: offset %ld is past end of segment %s\n", |
| gcp->program_name, offset, name); |
| return SEG_ERR; |
| } |
| |
| apage = segp->seg_start + offset; |
| maxlength = segp->seg_length - offset; |
| |
| length = range->length; |
| if (length) |
| length = round_up_to_pagesize(length); |
| |
| /* |
| * note: we silently truncate to max length [end of segment] |
| */ |
| if(length == 0 || length > maxlength) |
| length = maxlength; |
| |
| end = apage + length; |
| pgid = offset/gcp->pagesize; |
| |
| show_one_segment(segp, false); /* show mapping, no header */ |
| |
| printf("page offset "); |
| for (i=0; i<PG_PER_LINE; ++i) |
| printf(" +%02d", i); |
| printf("\n"); |
| if (pgid & PPL_MASK) { |
| /* |
| * start partial line |
| */ |
| int pgid2 = pgid & ~PPL_MASK; |
| printf("%12x: ", pgid2); |
| while (pgid2 < pgid) { |
| printf(" "); |
| ++pgid2; |
| } |
| need_nl = true; |
| } else |
| need_nl = false; |
| |
| for(; apage < end; apage += gcp->pagesize, ++pgid) { |
| int node; |
| |
| node = get_node(apage); |
| if (node < 0) { |
| fprintf(stderr, "\n%s: " |
| "failed to get node for segment %s, offset 0x%lx\n", |
| gcp->program_name, name, SEG_OFFSET(segp, apage)); |
| return SEG_ERR; |
| } |
| |
| if ((pgid & PPL_MASK) == 0) { |
| if (need_nl) |
| printf("\n"); |
| printf("%12x: ", pgid); /* start a new line */ |
| need_nl = true; |
| } |
| printf(" %3d", node); |
| |
| if (signalled(gcp)) { |
| reset_signal(); |
| break; |
| } |
| } |
| printf("\n"); |
| |
| return SEG_OK; |
| } |
| #endif |