162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Test selecting other page sizes for mmap/shmget. 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci Before running this huge pages for each huge page size must have been 562306a36Sopenharmony_ci reserved. 662306a36Sopenharmony_ci For large pages beyond MAX_ORDER (like 1GB on x86) boot options must be used. 762306a36Sopenharmony_ci Also shmmax must be increased. 862306a36Sopenharmony_ci And you need to run as root to work around some weird permissions in shm. 962306a36Sopenharmony_ci And nothing using huge pages should run in parallel. 1062306a36Sopenharmony_ci When the program aborts you may need to clean up the shm segments with 1162306a36Sopenharmony_ci ipcrm -m by hand, like this 1262306a36Sopenharmony_ci sudo ipcs | awk '$1 == "0x00000000" {print $2}' | xargs -n1 sudo ipcrm -m 1362306a36Sopenharmony_ci (warning this will remove all if someone else uses them) */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define _GNU_SOURCE 1 1662306a36Sopenharmony_ci#include <sys/mman.h> 1762306a36Sopenharmony_ci#include <stdlib.h> 1862306a36Sopenharmony_ci#include <stdio.h> 1962306a36Sopenharmony_ci#include <sys/ipc.h> 2062306a36Sopenharmony_ci#include <sys/shm.h> 2162306a36Sopenharmony_ci#include <sys/stat.h> 2262306a36Sopenharmony_ci#include <glob.h> 2362306a36Sopenharmony_ci#include <assert.h> 2462306a36Sopenharmony_ci#include <unistd.h> 2562306a36Sopenharmony_ci#include <stdarg.h> 2662306a36Sopenharmony_ci#include <string.h> 2762306a36Sopenharmony_ci#include "vm_util.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define err(x) perror(x), exit(1) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT) 3262306a36Sopenharmony_ci#define MAP_HUGE_1GB (30 << MAP_HUGE_SHIFT) 3362306a36Sopenharmony_ci#define MAP_HUGE_SHIFT 26 3462306a36Sopenharmony_ci#define MAP_HUGE_MASK 0x3f 3562306a36Sopenharmony_ci#if !defined(MAP_HUGETLB) 3662306a36Sopenharmony_ci#define MAP_HUGETLB 0x40000 3762306a36Sopenharmony_ci#endif 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define SHM_HUGETLB 04000 /* segment will use huge TLB pages */ 4062306a36Sopenharmony_ci#define SHM_HUGE_SHIFT 26 4162306a36Sopenharmony_ci#define SHM_HUGE_MASK 0x3f 4262306a36Sopenharmony_ci#define SHM_HUGE_2MB (21 << SHM_HUGE_SHIFT) 4362306a36Sopenharmony_ci#define SHM_HUGE_1GB (30 << SHM_HUGE_SHIFT) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define NUM_PAGESIZES 5 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define NUM_PAGES 4 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define Dprintf(fmt...) // printf(fmt) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ciunsigned long page_sizes[NUM_PAGESIZES]; 5262306a36Sopenharmony_ciint num_page_sizes; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ciint ilog2(unsigned long v) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci int l = 0; 5762306a36Sopenharmony_ci while ((1UL << l) < v) 5862306a36Sopenharmony_ci l++; 5962306a36Sopenharmony_ci return l; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_civoid find_pagesizes(void) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci glob_t g; 6562306a36Sopenharmony_ci int i; 6662306a36Sopenharmony_ci glob("/sys/kernel/mm/hugepages/hugepages-*kB", 0, NULL, &g); 6762306a36Sopenharmony_ci assert(g.gl_pathc <= NUM_PAGESIZES); 6862306a36Sopenharmony_ci for (i = 0; i < g.gl_pathc; i++) { 6962306a36Sopenharmony_ci sscanf(g.gl_pathv[i], "/sys/kernel/mm/hugepages/hugepages-%lukB", 7062306a36Sopenharmony_ci &page_sizes[i]); 7162306a36Sopenharmony_ci page_sizes[i] <<= 10; 7262306a36Sopenharmony_ci printf("Found %luMB\n", page_sizes[i] >> 20); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci num_page_sizes = g.gl_pathc; 7562306a36Sopenharmony_ci globfree(&g); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_civoid show(unsigned long ps) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci char buf[100]; 8162306a36Sopenharmony_ci if (ps == getpagesize()) 8262306a36Sopenharmony_ci return; 8362306a36Sopenharmony_ci printf("%luMB: ", ps >> 20); 8462306a36Sopenharmony_ci fflush(stdout); 8562306a36Sopenharmony_ci snprintf(buf, sizeof buf, 8662306a36Sopenharmony_ci "cat /sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", 8762306a36Sopenharmony_ci ps >> 10); 8862306a36Sopenharmony_ci system(buf); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ciunsigned long read_sysfs(int warn, char *fmt, ...) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci char *line = NULL; 9462306a36Sopenharmony_ci size_t linelen = 0; 9562306a36Sopenharmony_ci char buf[100]; 9662306a36Sopenharmony_ci FILE *f; 9762306a36Sopenharmony_ci va_list ap; 9862306a36Sopenharmony_ci unsigned long val = 0; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci va_start(ap, fmt); 10162306a36Sopenharmony_ci vsnprintf(buf, sizeof buf, fmt, ap); 10262306a36Sopenharmony_ci va_end(ap); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci f = fopen(buf, "r"); 10562306a36Sopenharmony_ci if (!f) { 10662306a36Sopenharmony_ci if (warn) 10762306a36Sopenharmony_ci printf("missing %s\n", buf); 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci if (getline(&line, &linelen, f) > 0) { 11162306a36Sopenharmony_ci sscanf(line, "%lu", &val); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci fclose(f); 11462306a36Sopenharmony_ci free(line); 11562306a36Sopenharmony_ci return val; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciunsigned long read_free(unsigned long ps) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci return read_sysfs(ps != getpagesize(), 12162306a36Sopenharmony_ci "/sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", 12262306a36Sopenharmony_ci ps >> 10); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_civoid test_mmap(unsigned long size, unsigned flags) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci char *map; 12862306a36Sopenharmony_ci unsigned long before, after; 12962306a36Sopenharmony_ci int err; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci before = read_free(size); 13262306a36Sopenharmony_ci map = mmap(NULL, size*NUM_PAGES, PROT_READ|PROT_WRITE, 13362306a36Sopenharmony_ci MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|flags, -1, 0); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (map == (char *)-1) err("mmap"); 13662306a36Sopenharmony_ci memset(map, 0xff, size*NUM_PAGES); 13762306a36Sopenharmony_ci after = read_free(size); 13862306a36Sopenharmony_ci Dprintf("before %lu after %lu diff %ld size %lu\n", 13962306a36Sopenharmony_ci before, after, before - after, size); 14062306a36Sopenharmony_ci assert(size == getpagesize() || (before - after) == NUM_PAGES); 14162306a36Sopenharmony_ci show(size); 14262306a36Sopenharmony_ci err = munmap(map, size * NUM_PAGES); 14362306a36Sopenharmony_ci assert(!err); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_civoid test_shmget(unsigned long size, unsigned flags) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci int id; 14962306a36Sopenharmony_ci unsigned long before, after; 15062306a36Sopenharmony_ci int err; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci before = read_free(size); 15362306a36Sopenharmony_ci id = shmget(IPC_PRIVATE, size * NUM_PAGES, IPC_CREAT|0600|flags); 15462306a36Sopenharmony_ci if (id < 0) err("shmget"); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci struct shm_info i; 15762306a36Sopenharmony_ci if (shmctl(id, SHM_INFO, (void *)&i) < 0) err("shmctl"); 15862306a36Sopenharmony_ci Dprintf("alloc %lu res %lu\n", i.shm_tot, i.shm_rss); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci Dprintf("id %d\n", id); 16262306a36Sopenharmony_ci char *map = shmat(id, NULL, 0600); 16362306a36Sopenharmony_ci if (map == (char*)-1) err("shmat"); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci shmctl(id, IPC_RMID, NULL); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci memset(map, 0xff, size*NUM_PAGES); 16862306a36Sopenharmony_ci after = read_free(size); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci Dprintf("before %lu after %lu diff %ld size %lu\n", 17162306a36Sopenharmony_ci before, after, before - after, size); 17262306a36Sopenharmony_ci assert(size == getpagesize() || (before - after) == NUM_PAGES); 17362306a36Sopenharmony_ci show(size); 17462306a36Sopenharmony_ci err = shmdt(map); 17562306a36Sopenharmony_ci assert(!err); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_civoid sanity_checks(void) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci int i; 18162306a36Sopenharmony_ci unsigned long largest = getpagesize(); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci for (i = 0; i < num_page_sizes; i++) { 18462306a36Sopenharmony_ci if (page_sizes[i] > largest) 18562306a36Sopenharmony_ci largest = page_sizes[i]; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (read_free(page_sizes[i]) < NUM_PAGES) { 18862306a36Sopenharmony_ci printf("Not enough huge pages for page size %lu MB, need %u\n", 18962306a36Sopenharmony_ci page_sizes[i] >> 20, 19062306a36Sopenharmony_ci NUM_PAGES); 19162306a36Sopenharmony_ci exit(0); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (read_sysfs(0, "/proc/sys/kernel/shmmax") < NUM_PAGES * largest) { 19662306a36Sopenharmony_ci printf("Please do echo %lu > /proc/sys/kernel/shmmax", largest * NUM_PAGES); 19762306a36Sopenharmony_ci exit(0); 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci#if defined(__x86_64__) 20162306a36Sopenharmony_ci if (largest != 1U<<30) { 20262306a36Sopenharmony_ci printf("No GB pages available on x86-64\n" 20362306a36Sopenharmony_ci "Please boot with hugepagesz=1G hugepages=%d\n", NUM_PAGES); 20462306a36Sopenharmony_ci exit(0); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci#endif 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ciint main(void) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci int i; 21262306a36Sopenharmony_ci unsigned default_hps = default_huge_page_size(); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci find_pagesizes(); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci sanity_checks(); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci for (i = 0; i < num_page_sizes; i++) { 21962306a36Sopenharmony_ci unsigned long ps = page_sizes[i]; 22062306a36Sopenharmony_ci int arg = ilog2(ps) << MAP_HUGE_SHIFT; 22162306a36Sopenharmony_ci printf("Testing %luMB mmap with shift %x\n", ps >> 20, arg); 22262306a36Sopenharmony_ci test_mmap(ps, MAP_HUGETLB | arg); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci printf("Testing default huge mmap\n"); 22562306a36Sopenharmony_ci test_mmap(default_hps, MAP_HUGETLB); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci puts("Testing non-huge shmget"); 22862306a36Sopenharmony_ci test_shmget(getpagesize(), 0); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci for (i = 0; i < num_page_sizes; i++) { 23162306a36Sopenharmony_ci unsigned long ps = page_sizes[i]; 23262306a36Sopenharmony_ci int arg = ilog2(ps) << SHM_HUGE_SHIFT; 23362306a36Sopenharmony_ci printf("Testing %luMB shmget with shift %x\n", ps >> 20, arg); 23462306a36Sopenharmony_ci test_shmget(ps, SHM_HUGETLB | arg); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci puts("default huge shmget"); 23762306a36Sopenharmony_ci test_shmget(default_hps, SHM_HUGETLB); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci} 241