Home | History | Annotate | Download | only in memory_hotplug
      1 /*
      2  * memtoy:  segment.c - manage memory segments
      3  *
      4  * create/destroy/map/unmap - anonymous, file and SysV shmem segments
      5  * touch [read or write] - ranges of segments
      6  * mbind - ranges of segments
      7  * show mappings or locations of segment pages
      8  */
      9 /*
     10  *  Copyright (c) 2005 Hewlett-Packard, Inc
     11  *  All rights reserved.
     12  */
     13 
     14 /*
     15  *  This program is free software; you can redistribute it and/or modify
     16  *  it under the terms of the GNU General Public License as published by
     17  *  the Free Software Foundation; either version 2 of the License, or
     18  *  (at your option) any later version.
     19  *
     20  *  This program is distributed in the hope that it will be useful,
     21  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     22  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     23  *  GNU General Public License for more details.
     24  *
     25  *  You should have received a copy of the GNU General Public License
     26  *  along with this program; if not, write to the Free Software
     27  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     28  */
     29 
     30 #include "config.h"
     31 
     32 #ifdef HAVE_NUMA_V2
     33 
     34 #include <sys/types.h>
     35 #include <sys/ipc.h>
     36 #include <sys/mman.h>
     37 #include <sys/shm.h>
     38 #include <sys/stat.h>
     39 #include <sys/time.h>
     40 #include <errno.h>
     41 #include <fcntl.h>
     42 #include <libgen.h>
     43 #include <numa.h>
     44 #include <numaif.h>
     45 #include <stdarg.h>
     46 #include <stdlib.h>
     47 #include <stdio.h>
     48 #include <string.h>
     49 #include <unistd.h>
     50 
     51 #include "memtoy.h"
     52 #include "segment.h"
     53 
     54 struct segment {
     55 	char *seg_name;
     56 	void *seg_start;
     57 	size_t seg_length;
     58 
     59 	off_t seg_offset;	/* memory mapped files */
     60 	char *seg_path;		/*   "      "      "   */
     61 
     62 	seg_type_t seg_type;
     63 	int seg_slot;
     64 	int seg_flags;		/* shared|private */
     65 	int seg_prot;
     66 	int seg_fd;		/* saved file descriptor */
     67 	int seg_shmid;
     68 
     69 };
     70 
     71 #define MAX_SEGMENTS 63		/* arbitrary max */
     72 #define SEG_FD_NONE (-1)
     73 #define SHM_ID_NONE (-1)
     74 
     75 #define SEG_ERR (0)
     76 #define SEG_OK  (1)
     77 
     78 #define SEG_OFFSET(SEGP, ADDR) ((char *)(ADDR) - (char *)(SEGP->seg_start))
     79 
     80 /*
     81  * =========================================================================
     82  */
     83 void segment_init(struct global_context *gcp)
     84 {
     85 	/*
     86 	 * one extra slot to terminate the list
     87 	 */
     88 	gcp->seglist = calloc(MAX_SEGMENTS + 1, sizeof(segment_t *));
     89 	if (!gcp->seglist)
     90 		die(4, "%s: can't alloc segment table\n", gcp->program_name);
     91 	gcp->seg_avail = NULL;
     92 
     93 }
     94 
     95 static segment_t *new_segment(void)
     96 {
     97 	glctx_t *gcp = &glctx;
     98 	segment_t *segp = (segment_t *) calloc(1, sizeof(segment_t));
     99 
    100 	if (segp == NULL)
    101 		fprintf(stderr, "%s:  failed to allocate segment\n",
    102 			gcp->program_name);
    103 	return segp;
    104 }
    105 
    106 /*
    107  * get_seg_slot() -- allocate a segment table slot for a new segment
    108  */
    109 static segment_t *get_seg_slot(void)
    110 {
    111 	glctx_t *gcp = &glctx;
    112 	segment_t *segp, **segpp;
    113 
    114 	/*
    115 	 * consume saved slot, if any
    116 	 */
    117 	segp = gcp->seg_avail;
    118 	if (segp != NULL) {
    119 		gcp->seg_avail = NULL;
    120 		return segp;
    121 	}
    122 
    123 	/*
    124 	 * simple linear scan for first available slot
    125 	 */
    126 	for (segpp = gcp->seglist; (segp = *segpp); ++segpp) {
    127 		if (segp->seg_type == SEGT_NONE)
    128 			return segp;
    129 	}
    130 
    131 	if (segpp < &gcp->seglist[MAX_SEGMENTS]) {
    132 		/*
    133 		 * previously unused slot
    134 		 */
    135 		*segpp = segp = new_segment();
    136 		segp->seg_slot = segpp - gcp->seglist;
    137 		return segp;
    138 	}
    139 
    140 	fprintf(stderr, "%s:  segment table full\n", gcp->program_name);
    141 	return NULL;
    142 }
    143 
    144 static void unmap_segment(segment_t * segp)
    145 {
    146 
    147 	if (segp->seg_start == MAP_FAILED)
    148 		return;		/* already unmapped */
    149 
    150 	switch (segp->seg_type) {
    151 	case SEGT_ANON:
    152 	case SEGT_FILE:
    153 		munmap(segp->seg_start, segp->seg_length);
    154 		break;
    155 
    156 	case SEGT_SHM:
    157 		shmdt(segp->seg_start);
    158 		break;
    159 
    160 	default:
    161 		// shouldn't happen?
    162 		break;
    163 	}
    164 
    165 	segp->seg_start = MAP_FAILED;
    166 }
    167 
    168 /*
    169  * free up a segment table slot, freeing any string storage
    170  * and removing shm segment, if necessary
    171  * clear out the segment, but preserve slot #
    172  */
    173 static void free_seg_slot(segment_t * segp)
    174 {
    175 	glctx_t *gcp = &glctx;
    176 	int slot = segp->seg_slot;
    177 
    178 	if (segp->seg_name != NULL)
    179 		free(segp->seg_name);
    180 
    181 	if (segp->seg_path != NULL)
    182 		free(segp->seg_path);
    183 
    184 	if (segp->seg_type == SEGT_FILE && segp->seg_fd != SEG_FD_NONE)
    185 		close(segp->seg_fd);
    186 
    187 	if (segp->seg_type == SEGT_SHM && segp->seg_shmid != SHM_ID_NONE)
    188 		shmctl(segp->seg_shmid, IPC_RMID, NULL);
    189 
    190 	(void)memset(segp, 0, sizeof(*segp));
    191 
    192 	segp->seg_slot = slot;
    193 	if (gcp->seg_avail == NULL)
    194 		gcp->seg_avail = segp;
    195 
    196 }
    197 
    198 /*
    199  * called from memtoy "at exit" cleanup().
    200  * primarily to remove any shm segments created.
    201  */
    202 void segment_cleanup(struct global_context *gcp)
    203 {
    204 	segment_t *segp, **segpp;
    205 
    206 	segpp = gcp->seglist;
    207 	if (segpp == NULL)
    208 		return;
    209 
    210 	for (; (segp = *segpp); ++segpp) {
    211 		if (segp->seg_type != SEGT_SHM) {
    212 			continue;
    213 		}
    214 		free_seg_slot(segp);	/* to remove shared mem */
    215 	}
    216 }
    217 
    218 static size_t round_up_to_pagesize(size_t size)
    219 {
    220 	glctx_t *gcp = &glctx;
    221 	size_t pagemask = gcp->pagesize - 1;
    222 
    223 	return ((size + pagemask) & ~pagemask);
    224 
    225 }
    226 
    227 static size_t round_down_to_pagesize(size_t size)
    228 {
    229 	glctx_t *gcp = &glctx;
    230 	size_t pagemask = gcp->pagesize - 1;
    231 
    232 	return (size & ~pagemask);
    233 
    234 }
    235 
    236 /*
    237  * get_node() -- fetch numa node id of page at vaddr
    238  * [from Ray Bryant's [SGI] memory migration tests]
    239  */
    240 static int get_node(void *vaddr)
    241 {
    242 	int rc, node;
    243 
    244 	rc = get_mempolicy(&node, NULL, 0, vaddr, MPOL_F_NODE | MPOL_F_ADDR);
    245 	if (rc)
    246 		return -1;
    247 
    248 	return node;
    249 }
    250 
    251 /*
    252  * =========================================================================
    253  */
    254 static int map_anon_segment(segment_t * segp)
    255 {
    256 	glctx_t *gcp = &glctx;
    257 
    258 	char *memp;
    259 	int flags = segp->seg_flags;
    260 
    261 	if (!flags)
    262 		flags = MAP_PRIVATE;	/* default */
    263 
    264 	memp = (char *)mmap(0, segp->seg_length, segp->seg_prot, flags | MAP_ANONYMOUS, 0,	/* fd -- ignored */
    265 			    0);	/* offset -- ignored */
    266 
    267 	if (memp == MAP_FAILED) {
    268 		int err = errno;
    269 		fprintf(stderr, "%s:  anonymous mmap failed - %s\n",
    270 			__FUNCTION__, strerror(err));
    271 		return SEG_ERR;
    272 	}
    273 
    274 	vprint("%s:  mmap()ed anon seg %s at 0x%lx-0x%lx\n",
    275 	       gcp->program_name, segp->seg_name,
    276 	       memp, memp + segp->seg_length - 1);
    277 
    278 	segp->seg_start = memp;
    279 
    280 	return SEG_OK;
    281 }
    282 
    283 /*
    284  * open_file() -- open and validate file when registering a file segment.
    285  * remember fd in segment struct.
    286  */
    287 static int open_file(segment_t * segp)
    288 {
    289 	glctx_t *gcp = &glctx;
    290 
    291 	struct stat stbuf;
    292 	int fd, flags;
    293 
    294 	if (stat(segp->seg_path, &stbuf) < 0) {
    295 		int err = errno;
    296 		fprintf(stderr, "%s:  can't stat %s - %s\n",
    297 			gcp->program_name, segp->seg_path, strerror(err));
    298 		free_seg_slot(segp);
    299 		return SEG_ERR;
    300 	}
    301 
    302 	/*
    303 	 * TODO:  for now, just regular files.  later?
    304 	 */
    305 	if (!S_ISREG(stbuf.st_mode)) {
    306 		fprintf(stderr, "%s:  %s - is not a regular file\n",
    307 			gcp->program_name, segp->seg_path);
    308 		free_seg_slot(segp);
    309 		return SEG_ERR;
    310 	}
    311 
    312 	/*
    313 	 * Open file with maximal privileges;  adjust segment mapping
    314 	 * protections if permissions don't allow full R/W access.
    315 	 */
    316 	if (!access(segp->seg_path, R_OK | W_OK))
    317 		flags = O_RDWR;
    318 	else if (!access(segp->seg_path, R_OK)) {
    319 		flags = O_RDONLY;
    320 		segp->seg_prot &= ~PROT_WRITE;
    321 	} else if (!access(segp->seg_path, W_OK)) {
    322 		flags = O_WRONLY;
    323 		segp->seg_prot &= ~PROT_READ;
    324 	} else {
    325 		fprintf(stderr, "%s:  can't access %s\n",
    326 			gcp->program_name, segp->seg_path);
    327 		free_seg_slot(segp);
    328 		return SEG_ERR;
    329 	}
    330 
    331 	fd = open(segp->seg_path, flags);
    332 	if (fd < 0) {
    333 		int err = errno;
    334 		fprintf(stderr, "%s:  can't open %s - %s\n",
    335 			gcp->program_name, segp->seg_path, strerror(err));
    336 		free_seg_slot(segp);
    337 		return SEG_ERR;
    338 	}
    339 
    340 	segp->seg_fd = fd;
    341 	return SEG_OK;
    342 }
    343 
    344 /*
    345  * re-fetch file size at map time -- just in case it's changed
    346  */
    347 static size_t file_size(int fd)
    348 {
    349 	struct stat stbuf;
    350 
    351 	if (fstat(fd, &stbuf) != 0) {
    352 		return BOGUS_SIZE;
    353 	}
    354 
    355 	return stbuf.st_size;
    356 }
    357 
    358 /*
    359  * map_file_segment() -- map a [range of a] registered file segment.
    360  */
    361 static int map_file_segment(segment_t * segp)
    362 {
    363 	glctx_t *gcp = &glctx;
    364 
    365 	char *memp;
    366 	size_t size;
    367 	int fd;
    368 	int flags = segp->seg_flags;
    369 
    370 	if (!flags)
    371 		flags = MAP_PRIVATE;	/* default */
    372 
    373 	if ((fd = segp->seg_fd) == SEG_FD_NONE) {
    374 		fprintf(stderr, "%s:  file %s not open\n",
    375 			gcp->program_name, segp->seg_path);
    376 		return SEG_ERR;
    377 	}
    378 
    379 	size = file_size(fd);
    380 
    381 	/*
    382 	 * page align offset/length;  verify fit in file
    383 	 */
    384 	segp->seg_offset = round_down_to_pagesize(segp->seg_offset);
    385 	if (segp->seg_offset > size) {
    386 		fprintf(stderr, "%s: offset 0x%lx beyond end of file %s\n",
    387 			gcp->program_name, segp->seg_offset, segp->seg_path);
    388 		return SEG_ERR;
    389 	}
    390 
    391 	if (segp->seg_length == 0)
    392 		segp->seg_length = round_up_to_pagesize(size) -
    393 		    segp->seg_offset;
    394 	else
    395 		segp->seg_length = round_up_to_pagesize(segp->seg_length);
    396 
    397 	memp = (char *)mmap(0, segp->seg_length,
    398 			    segp->seg_prot, flags, fd, segp->seg_offset);
    399 
    400 	if (memp == MAP_FAILED) {
    401 		int err = errno;
    402 		fprintf(stderr, "%s:  mmap of %s failed - %s\n",
    403 			__FUNCTION__, segp->seg_path, strerror(err));
    404 		return SEG_ERR;
    405 	}
    406 
    407 	vprint("%s:  mmap()ed file seg %s at 0x%lx-0x%lx\n",
    408 	       gcp->program_name, segp->seg_name,
    409 	       memp, memp + segp->seg_length - 1);
    410 
    411 	segp->seg_start = memp;
    412 
    413 	return SEG_OK;
    414 }
    415 
    416 /*
    417  * get_shm_segment() -- create [shmget] a new shared memory segment
    418  */
    419 static int get_shm_segment(segment_t * segp)
    420 {
    421 	glctx_t *gcp = &glctx;
    422 
    423 	int shmid;
    424 
    425 	shmid = shmget(IPC_PRIVATE, segp->seg_length, SHM_R | SHM_W);
    426 	if (shmid == -1) {
    427 		int err = errno;
    428 		fprintf(stderr, "%s:  failed to get shm segment %s - %s\n",
    429 			gcp->program_name, segp->seg_name, strerror(err));
    430 		free_seg_slot(segp);
    431 		return SEG_ERR;
    432 	}
    433 
    434 	segp->seg_shmid = shmid;
    435 	vprint("%s:  shm seg %s id:  %d\n",
    436 	       gcp->program_name, segp->seg_name, segp->seg_shmid);
    437 	return SEG_OK;
    438 }
    439 
    440 /*
    441  * map_shm_segment() -- attach [shmat] a shared memory segment
    442  */
    443 static int map_shm_segment(segment_t * segp)
    444 {
    445 	glctx_t *gcp = &glctx;
    446 
    447 	segp->seg_start = shmat(segp->seg_shmid, NULL, 0);
    448 	if (segp->seg_start == MAP_FAILED) {
    449 		int err = errno;
    450 		fprintf(stderr, "%s:  failed to attach shm segment %s: %s\n",
    451 			gcp->program_name, segp->seg_name, strerror(err));
    452 		return SEG_ERR;
    453 	}
    454 
    455 	vprint("%s:  mmap()ed shm seg %s at 0x%lx-0x%lx\n",
    456 	       gcp->program_name, segp->seg_name,
    457 	       segp->seg_start, segp->seg_start + segp->seg_length - 1);
    458 
    459 	return SEG_OK;
    460 }
    461 
    462 /*
    463  * =========================================================================
    464  * segment API
    465  */
    466 /*
    467  * segment_get(name) - lookup named segment
    468 TODO:  move to segment private functions?
    469  */
    470 segment_t *segment_get(char *name)
    471 {
    472 	glctx_t *gcp = &glctx;
    473 	segment_t *segp, **segpp;
    474 
    475 	for (segpp = gcp->seglist; (segp = *segpp); ++segpp) {
    476 		if (segp->seg_type == SEGT_NONE) {
    477 			if (gcp->seg_avail == NULL)
    478 				gcp->seg_avail = *segpp;
    479 			continue;
    480 		}
    481 		if (!strcmp(name, segp->seg_name))
    482 			return segp;
    483 	}
    484 
    485 	if (gcp->seg_avail == NULL && segpp < &gcp->seglist[MAX_SEGMENTS]) {
    486 		/*
    487 		 * prealloc an available segment
    488 		 */
    489 		*segpp = segp = new_segment();
    490 		if (segp != NULL) {
    491 			segp->seg_slot = segpp - gcp->seglist;
    492 			gcp->seg_avail = segp;
    493 		}
    494 	}
    495 
    496 	return NULL;
    497 }
    498 
    499 /*
    500  * segment_register:  register an anon, file or shm segment based on args.
    501  *	for anon and shm, 'name' = segment name.
    502  *	for file, 'name' = path name; segment name = basename(path)
    503  *
    504  * returns: !0 on success; 0 on failure
    505  */
    506 int segment_register(seg_type_t type, char *name, range_t * range, int flags)
    507 {
    508 	glctx_t *gcp = &glctx;
    509 	segment_t *segp;
    510 	char *path;
    511 
    512 	segp = segment_get(basename(name));	/* ensure unique name */
    513 	if (segp != NULL) {
    514 		fprintf(stderr, "%s:  segment %s already exists\n",
    515 			gcp->program_name, segp->seg_name);
    516 		return SEG_ERR;
    517 	}
    518 
    519 	segp = get_seg_slot();
    520 	if (segp == NULL)
    521 		return SEG_ERR;
    522 
    523 	path = strdup(name);	/* save a copy */
    524 	segp->seg_name = strdup(basename(name));
    525 	segp->seg_start = MAP_FAILED;
    526 	segp->seg_length = round_up_to_pagesize(range->length);
    527 	segp->seg_offset = round_down_to_pagesize(range->offset);
    528 	segp->seg_type = type;
    529 	segp->seg_flags = flags;	/* possibly 0 */
    530 	segp->seg_prot = PROT_READ | PROT_WRITE;	/* default */
    531 	segp->seg_fd = SEG_FD_NONE;
    532 	segp->seg_shmid = SHM_ID_NONE;
    533 
    534 	switch (type) {
    535 	case SEGT_ANON:
    536 		free(path);
    537 		break;
    538 
    539 	case SEGT_FILE:
    540 		segp->seg_path = path;
    541 		return open_file(segp);
    542 		break;
    543 
    544 	case SEGT_SHM:
    545 		free(path);
    546 		return get_shm_segment(segp);
    547 		break;
    548 
    549 	default:
    550 		free(path);
    551 	}
    552 	return SEG_OK;
    553 }
    554 
    555 static char *segment_header =
    556     "  _____address______ ____length____ ____offset____ prot  share  name\n";
    557 
    558 static char seg_type[] = { '.', 'a', 'f', 's' };
    559 
    560 static int show_one_segment(segment_t * segp, bool header)
    561 {
    562 	char *protection, *share, *name;
    563 
    564 	switch (segp->seg_prot & (PROT_READ | PROT_WRITE)) {
    565 	case PROT_READ | PROT_WRITE:
    566 		protection = "rw";
    567 		break;
    568 
    569 	case PROT_READ:
    570 		protection = "r-";
    571 		break;
    572 
    573 	case PROT_WRITE:
    574 		protection = "-w";
    575 		break;
    576 
    577 	default:
    578 		protection = "--";
    579 		break;
    580 	}
    581 
    582 	if (segp->seg_flags)
    583 		share = (segp->seg_flags & MAP_SHARED) ? "shared " : "private";
    584 	else
    585 		share = "default";
    586 
    587 	name = (segp->seg_type == SEGT_FILE) ? segp->seg_path : segp->seg_name;
    588 
    589 	if (header)
    590 		puts(segment_header);
    591 
    592 	if (segp->seg_start != MAP_FAILED) {
    593 		printf("%c 0x%p 0x%012zx 0x%012lx  %s  %s %s\n",
    594 		       seg_type[segp->seg_type],
    595 		       segp->seg_start,
    596 		       segp->seg_length,
    597 		       segp->seg_offset, protection, share, name);
    598 	} else {
    599 		printf("%c *** not-mapped *** 0x%012zx 0x%012lx  %s  %s %s\n",
    600 		       seg_type[segp->seg_type],
    601 		       segp->seg_length,
    602 		       segp->seg_offset, protection, share, name);
    603 	}
    604 
    605 	return SEG_OK;
    606 }
    607 
    608 /*
    609  * segment_show() -- show specified segment, or all, if none specified.
    610  */
    611 int segment_show(char *name)
    612 {
    613 	glctx_t *gcp = &glctx;
    614 	segment_t *segp, **segpp;
    615 	bool header;
    616 
    617 	if (name != NULL) {
    618 		segp = segment_get(name);
    619 		if (segp == NULL) {
    620 			fprintf(stderr, "%s:  no such segment:  %s\n",
    621 				gcp->program_name, name);
    622 			return SEG_ERR;
    623 		}
    624 		show_one_segment(segp, false);
    625 		return SEG_OK;
    626 	}
    627 
    628 	/*
    629 	 * show all
    630 	 */
    631 	header = true;
    632 	for (segpp = gcp->seglist; (segp = *segpp); ++segpp) {
    633 		if (segp->seg_type != SEGT_NONE) {
    634 			show_one_segment(segp, header);
    635 			header = false;	/* first time only */
    636 		}
    637 	}
    638 
    639 	return SEG_OK;
    640 
    641 }
    642 
    643 /*
    644  * segment_remove() - remove the specified segment, if exists.
    645  */
    646 int segment_remove(char *name)
    647 {
    648 	glctx_t *gcp = &glctx;
    649 	segment_t *segp;
    650 
    651 	segp = segment_get(name);
    652 	if (segp == NULL) {
    653 		fprintf(stderr, "%s:  no such segment:  %s\n",
    654 			gcp->program_name, name);
    655 		return SEG_ERR;
    656 	}
    657 
    658 	unmap_segment(segp);
    659 
    660 	free_seg_slot(segp);
    661 
    662 	return SEG_OK;
    663 }
    664 
    665 /*
    666  * segment_touch() - "touch" [read or write] each page of specified range
    667  *                   -- from offset to offset+length -- to fault in or to
    668  *                   test protection.
    669  * NOTE:  offset is relative to start of mapping, not start of file!
    670  */
    671 int segment_touch(char *name, range_t * range, int rw)
    672 {
    673 	glctx_t *gcp = &glctx;
    674 	segment_t *segp;
    675 	off_t offset;
    676 	size_t length, maxlength;
    677 	unsigned long *memp;
    678 	struct timeval t_start, t_end;
    679 
    680 	segp = segment_get(name);
    681 	if (segp == NULL) {
    682 		fprintf(stderr, "%s:  no such segment:  %s\n",
    683 			gcp->program_name, name);
    684 		return SEG_ERR;
    685 	}
    686 
    687 	offset = round_down_to_pagesize(range->offset);
    688 	if (offset >= segp->seg_length) {
    689 		fprintf(stderr, "%s:  offset %ld is past end of segment %s\n",
    690 			gcp->program_name, offset, name);
    691 		return SEG_ERR;
    692 	}
    693 
    694 	memp = (unsigned long *)(segp->seg_start + offset);
    695 	maxlength = segp->seg_length - offset;
    696 
    697 	length = range->length;
    698 	if (length)
    699 		length = round_up_to_pagesize(length);
    700 
    701 	/*
    702 	 * note:  we silently truncate to max length [end of segment]
    703 	 */
    704 	if (length == 0 || length > maxlength)
    705 		length = maxlength;
    706 
    707 	gettimeofday(&t_start, NULL);
    708 	touch_memory(rw, memp, length);
    709 	gettimeofday(&t_end, NULL);
    710 	printf("%s:  touched %d pages in %6.3f secs\n",
    711 	       gcp->program_name, length / gcp->pagesize,
    712 	       (float)(tv_diff_usec(&t_start, &t_end)) / 1000000.0);
    713 
    714 	return SEG_OK;
    715 }
    716 
    717 /*
    718  * segment_unmap() -  unmap the specified segment, if any, from seg_start
    719  *                    to seg_start+seg_lenth.  Leave the segment in the
    720  *                    table;
    721  */
    722 int segment_unmap(char *name)
    723 {
    724 	glctx_t *gcp = &glctx;
    725 	segment_t *segp;
    726 
    727 	segp = segment_get(name);
    728 	if (segp == NULL) {
    729 		fprintf(stderr, "%s:  no such segment:  %s\n",
    730 			gcp->program_name, name);
    731 		return SEG_ERR;
    732 	}
    733 
    734 	if (segp->seg_start == MAP_FAILED)
    735 		return SEG_OK;	/* silent success */
    736 
    737 	switch (segp->seg_type) {
    738 	case SEGT_ANON:
    739 	case SEGT_FILE:
    740 		munmap(segp->seg_start, segp->seg_length);
    741 		break;
    742 
    743 	case SEGT_SHM:
    744 		//TODO:  shmdt()...
    745 		break;
    746 		/* Handle default to get rid of -Wswitch-enum */
    747 	default:
    748 		break;
    749 	}
    750 
    751 	segp->seg_start = MAP_FAILED;
    752 
    753 	return SEG_OK;
    754 }
    755 
    756 /*
    757  * segment_map() -- [re] map() a previously unmapped segment
    758  *                  no-op if already mapped.
    759  *                  range only applies to mapped file.
    760  */
    761 int segment_map(char *name, range_t * range, int flags)
    762 {
    763 	glctx_t *gcp = &glctx;
    764 	segment_t *segp;
    765 
    766 	segp = segment_get(name);
    767 	if (segp == NULL) {
    768 		fprintf(stderr, "%s:  no such segment:  %s\n",
    769 			gcp->program_name, name);
    770 		return SEG_ERR;
    771 	}
    772 
    773 	if (segp->seg_start != MAP_FAILED) {
    774 		fprintf(stderr, "%s:  segment %s already mapped\n",
    775 			gcp->program_name, name);
    776 		return SEG_OK;	/* treat as success */
    777 	}
    778 
    779 	if (flags != 0)
    780 		segp->seg_flags = flags;
    781 
    782 	switch (segp->seg_type) {
    783 	case SEGT_ANON:
    784 		return map_anon_segment(segp);
    785 		break;
    786 
    787 	case SEGT_FILE:
    788 		if (range != NULL) {
    789 			segp->seg_offset = range->offset;
    790 			segp->seg_length = range->length;
    791 		}
    792 		return map_file_segment(segp);
    793 		break;
    794 
    795 	case SEGT_SHM:
    796 		return map_shm_segment(segp);
    797 		break;
    798 		/* Handle default to get rid of -Wswitch-enum */
    799 	default:
    800 		break;
    801 	}
    802 
    803 	return SEG_ERR;		/* unrecognized segment type -- shouldn't happen */
    804 
    805 }
    806 
    807 /*
    808  * segment_mbind() - set memory policy for a range of specified segment
    809  *
    810  * NOTE:  offset is relative to start of mapping, not start of file
    811  */
    812 int
    813 segment_mbind(char *name, range_t * range, int policy,
    814 	      nodemask_t * nodemask, int flags)
    815 {
    816 	glctx_t *gcp = &glctx;
    817 	segment_t *segp;
    818 	char *start;
    819 	off_t offset;
    820 	size_t length, maxlength;
    821 	int ret;
    822 
    823 	segp = segment_get(name);
    824 	if (segp == NULL) {
    825 		fprintf(stderr, "%s:  no such segment:  %s\n",
    826 			gcp->program_name, name);
    827 		return SEG_ERR;
    828 	}
    829 
    830 	if (segp->seg_start == MAP_FAILED) {
    831 		fprintf(stderr, "%s:  segment %s not mapped\n",
    832 			gcp->program_name, name);
    833 		return SEG_ERR;
    834 	}
    835 
    836 	offset = round_down_to_pagesize(range->offset);
    837 	if (offset >= segp->seg_length) {
    838 		fprintf(stderr, "%s:  offset %ld is past end of segment %s\n",
    839 			gcp->program_name, offset, name);
    840 		return SEG_ERR;
    841 	}
    842 
    843 	start = segp->seg_start + offset;
    844 	maxlength = segp->seg_length - offset;
    845 
    846 	length = range->length;
    847 	if (length)
    848 		length = round_up_to_pagesize(length);
    849 
    850 	/*
    851 	 * note:  we silently truncate to max length [end of segment]
    852 	 */
    853 	if (length == 0 || length > maxlength)
    854 		length = maxlength;
    855 
    856 	ret = mbind(segp->seg_start + offset, length, policy, nodemask->n,
    857 		    NUMA_NUM_NODES, flags);
    858 
    859 	if (ret == -1) {
    860 		int err = errno;
    861 		fprintf(stderr, "%s:  mbind() of segment %s failed - %s\n",
    862 			gcp->program_name, name, strerror(err));
    863 		return SEG_ERR;
    864 	}
    865 
    866 	return SEG_OK;
    867 }
    868 
    869 /*
    870  * segment_location() - report node location of specified range of segment
    871  *
    872  * NOTE:  offset is relative to start of mapping, not start of file
    873  */
    874 #define PG_PER_LINE 8
    875 #define PPL_MASK (PG_PER_LINE - 1)
    876 int segment_location(char *name, range_t * range)
    877 {
    878 	glctx_t *gcp = &glctx;
    879 	segment_t *segp;
    880 	char *apage, *end;
    881 	off_t offset;
    882 	size_t length, maxlength;
    883 	int pgid, i;
    884 	bool need_nl;
    885 
    886 	segp = segment_get(name);
    887 	if (segp == NULL) {
    888 		fprintf(stderr, "%s:  no such segment:  %s\n",
    889 			gcp->program_name, name);
    890 		return SEG_ERR;
    891 	}
    892 
    893 	if (segp->seg_start == MAP_FAILED) {
    894 		fprintf(stderr, "%s:  segment %s not mapped\n",
    895 			gcp->program_name, name);
    896 		return SEG_ERR;
    897 	}
    898 
    899 	offset = round_down_to_pagesize(range->offset);
    900 	if (offset >= segp->seg_length) {
    901 		fprintf(stderr, "%s:  offset %ld is past end of segment %s\n",
    902 			gcp->program_name, offset, name);
    903 		return SEG_ERR;
    904 	}
    905 
    906 	apage = segp->seg_start + offset;
    907 	maxlength = segp->seg_length - offset;
    908 
    909 	length = range->length;
    910 	if (length)
    911 		length = round_up_to_pagesize(length);
    912 
    913 	/*
    914 	 * note:  we silently truncate to max length [end of segment]
    915 	 */
    916 	if (length == 0 || length > maxlength)
    917 		length = maxlength;
    918 
    919 	end = apage + length;
    920 	pgid = offset / gcp->pagesize;
    921 
    922 	show_one_segment(segp, false);	/* show mapping, no header */
    923 
    924 	printf("page offset   ");
    925 	for (i = 0; i < PG_PER_LINE; ++i)
    926 		printf(" +%02d", i);
    927 	printf("\n");
    928 	if (pgid & PPL_MASK) {
    929 		/*
    930 		 * start partial line
    931 		 */
    932 		int pgid2 = pgid & ~PPL_MASK;
    933 		printf("%12x: ", pgid2);
    934 		while (pgid2 < pgid) {
    935 			printf("    ");
    936 			++pgid2;
    937 		}
    938 		need_nl = true;
    939 	} else
    940 		need_nl = false;
    941 
    942 	for (; apage < end; apage += gcp->pagesize, ++pgid) {
    943 		int node;
    944 
    945 		node = get_node(apage);
    946 		if (node < 0) {
    947 			fprintf(stderr, "\n%s:  "
    948 				"failed to get node for segment %s, offset 0x%x\n",
    949 				gcp->program_name, name, SEG_OFFSET(segp,
    950 								    apage));
    951 			return SEG_ERR;
    952 		}
    953 
    954 		if ((pgid & PPL_MASK) == 0) {
    955 			if (need_nl)
    956 				printf("\n");
    957 			printf("%12x: ", pgid);	/* start a new line */
    958 			need_nl = true;
    959 		}
    960 		printf(" %3d", node);
    961 
    962 		if (signalled(gcp)) {
    963 			reset_signal();
    964 			break;
    965 		}
    966 	}
    967 	printf("\n");
    968 
    969 	return SEG_OK;
    970 }
    971 #endif
    972