Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* Test selecting other page sizes for mmap/shmget.
0003 
0004    Before running this huge pages for each huge page size must have been
0005    reserved.
0006    For large pages beyond MAX_ORDER (like 1GB on x86) boot options must be used.
0007    Also shmmax must be increased.
0008    And you need to run as root to work around some weird permissions in shm.
0009    And nothing using huge pages should run in parallel.
0010    When the program aborts you may need to clean up the shm segments with
0011    ipcrm -m by hand, like this
0012    sudo ipcs | awk '$1 == "0x00000000" {print $2}' | xargs -n1 sudo ipcrm -m
0013    (warning this will remove all if someone else uses them) */
0014 
0015 #define _GNU_SOURCE 1
0016 #include <sys/mman.h>
0017 #include <stdlib.h>
0018 #include <stdio.h>
0019 #include <sys/ipc.h>
0020 #include <sys/shm.h>
0021 #include <sys/stat.h>
0022 #include <glob.h>
0023 #include <assert.h>
0024 #include <unistd.h>
0025 #include <stdarg.h>
0026 #include <string.h>
0027 
0028 #define err(x) perror(x), exit(1)
0029 
0030 #define MAP_HUGE_2MB    (21 << MAP_HUGE_SHIFT)
0031 #define MAP_HUGE_1GB    (30 << MAP_HUGE_SHIFT)
0032 #define MAP_HUGE_SHIFT  26
0033 #define MAP_HUGE_MASK   0x3f
0034 #if !defined(MAP_HUGETLB)
0035 #define MAP_HUGETLB 0x40000
0036 #endif
0037 
0038 #define SHM_HUGETLB     04000   /* segment will use huge TLB pages */
0039 #define SHM_HUGE_SHIFT  26
0040 #define SHM_HUGE_MASK   0x3f
0041 #define SHM_HUGE_2MB    (21 << SHM_HUGE_SHIFT)
0042 #define SHM_HUGE_1GB    (30 << SHM_HUGE_SHIFT)
0043 
0044 #define NUM_PAGESIZES   5
0045 
0046 #define NUM_PAGES 4
0047 
0048 #define Dprintf(fmt...) // printf(fmt)
0049 
0050 unsigned long page_sizes[NUM_PAGESIZES];
0051 int num_page_sizes;
0052 
0053 int ilog2(unsigned long v)
0054 {
0055     int l = 0;
0056     while ((1UL << l) < v)
0057         l++;
0058     return l;
0059 }
0060 
0061 void find_pagesizes(void)
0062 {
0063     glob_t g;
0064     int i;
0065     glob("/sys/kernel/mm/hugepages/hugepages-*kB", 0, NULL, &g);
0066     assert(g.gl_pathc <= NUM_PAGESIZES);
0067     for (i = 0; i < g.gl_pathc; i++) {
0068         sscanf(g.gl_pathv[i], "/sys/kernel/mm/hugepages/hugepages-%lukB",
0069                 &page_sizes[i]);
0070         page_sizes[i] <<= 10;
0071         printf("Found %luMB\n", page_sizes[i] >> 20);
0072     }
0073     num_page_sizes = g.gl_pathc;
0074     globfree(&g);
0075 }
0076 
0077 unsigned long default_huge_page_size(void)
0078 {
0079     unsigned long hps = 0;
0080     char *line = NULL;
0081     size_t linelen = 0;
0082     FILE *f = fopen("/proc/meminfo", "r");
0083     if (!f)
0084         return 0;
0085     while (getline(&line, &linelen, f) > 0) {
0086         if (sscanf(line, "Hugepagesize:       %lu kB", &hps) == 1) {
0087             hps <<= 10;
0088             break;
0089         }
0090     }
0091     free(line);
0092     return hps;
0093 }
0094 
0095 void show(unsigned long ps)
0096 {
0097     char buf[100];
0098     if (ps == getpagesize())
0099         return;
0100     printf("%luMB: ", ps >> 20);
0101     fflush(stdout);
0102     snprintf(buf, sizeof buf,
0103         "cat /sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages",
0104         ps >> 10);
0105     system(buf);
0106 }
0107 
0108 unsigned long read_sysfs(int warn, char *fmt, ...)
0109 {
0110     char *line = NULL;
0111     size_t linelen = 0;
0112     char buf[100];
0113     FILE *f;
0114     va_list ap;
0115     unsigned long val = 0;
0116 
0117     va_start(ap, fmt);
0118     vsnprintf(buf, sizeof buf, fmt, ap);
0119     va_end(ap);
0120 
0121     f = fopen(buf, "r");
0122     if (!f) {
0123         if (warn)
0124             printf("missing %s\n", buf);
0125         return 0;
0126     }
0127     if (getline(&line, &linelen, f) > 0) {
0128         sscanf(line, "%lu", &val);
0129     }
0130     fclose(f);
0131     free(line);
0132     return val;
0133 }
0134 
0135 unsigned long read_free(unsigned long ps)
0136 {
0137     return read_sysfs(ps != getpagesize(),
0138             "/sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages",
0139             ps >> 10);
0140 }
0141 
0142 void test_mmap(unsigned long size, unsigned flags)
0143 {
0144     char *map;
0145     unsigned long before, after;
0146     int err;
0147 
0148     before = read_free(size);
0149     map = mmap(NULL, size*NUM_PAGES, PROT_READ|PROT_WRITE,
0150             MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|flags, -1, 0);
0151 
0152     if (map == (char *)-1) err("mmap");
0153     memset(map, 0xff, size*NUM_PAGES);
0154     after = read_free(size);
0155     Dprintf("before %lu after %lu diff %ld size %lu\n",
0156         before, after, before - after, size);
0157     assert(size == getpagesize() || (before - after) == NUM_PAGES);
0158     show(size);
0159     err = munmap(map, size);
0160     assert(!err);
0161 }
0162 
0163 void test_shmget(unsigned long size, unsigned flags)
0164 {
0165     int id;
0166     unsigned long before, after;
0167     int err;
0168 
0169     before = read_free(size);
0170     id = shmget(IPC_PRIVATE, size * NUM_PAGES, IPC_CREAT|0600|flags);
0171     if (id < 0) err("shmget");
0172 
0173     struct shm_info i;
0174     if (shmctl(id, SHM_INFO, (void *)&i) < 0) err("shmctl");
0175     Dprintf("alloc %lu res %lu\n", i.shm_tot, i.shm_rss);
0176 
0177 
0178     Dprintf("id %d\n", id);
0179     char *map = shmat(id, NULL, 0600);
0180     if (map == (char*)-1) err("shmat");
0181 
0182     shmctl(id, IPC_RMID, NULL);
0183 
0184     memset(map, 0xff, size*NUM_PAGES);
0185     after = read_free(size);
0186 
0187     Dprintf("before %lu after %lu diff %ld size %lu\n",
0188         before, after, before - after, size);
0189     assert(size == getpagesize() || (before - after) == NUM_PAGES);
0190     show(size);
0191     err = shmdt(map);
0192     assert(!err);
0193 }
0194 
0195 void sanity_checks(void)
0196 {
0197     int i;
0198     unsigned long largest = getpagesize();
0199 
0200     for (i = 0; i < num_page_sizes; i++) {
0201         if (page_sizes[i] > largest)
0202             largest = page_sizes[i];
0203 
0204         if (read_free(page_sizes[i]) < NUM_PAGES) {
0205             printf("Not enough huge pages for page size %lu MB, need %u\n",
0206                 page_sizes[i] >> 20,
0207                 NUM_PAGES);
0208             exit(0);
0209         }
0210     }
0211 
0212     if (read_sysfs(0, "/proc/sys/kernel/shmmax") < NUM_PAGES * largest) {
0213         printf("Please do echo %lu > /proc/sys/kernel/shmmax", largest * NUM_PAGES);
0214         exit(0);
0215     }
0216 
0217 #if defined(__x86_64__)
0218     if (largest != 1U<<30) {
0219         printf("No GB pages available on x86-64\n"
0220                "Please boot with hugepagesz=1G hugepages=%d\n", NUM_PAGES);
0221         exit(0);
0222     }
0223 #endif
0224 }
0225 
0226 int main(void)
0227 {
0228     int i;
0229     unsigned default_hps = default_huge_page_size();
0230 
0231     find_pagesizes();
0232 
0233     sanity_checks();
0234 
0235     for (i = 0; i < num_page_sizes; i++) {
0236         unsigned long ps = page_sizes[i];
0237         int arg = ilog2(ps) << MAP_HUGE_SHIFT;
0238         printf("Testing %luMB mmap with shift %x\n", ps >> 20, arg);
0239         test_mmap(ps, MAP_HUGETLB | arg);
0240     }
0241     printf("Testing default huge mmap\n");
0242     test_mmap(default_hps, SHM_HUGETLB);
0243 
0244     puts("Testing non-huge shmget");
0245     test_shmget(getpagesize(), 0);
0246 
0247     for (i = 0; i < num_page_sizes; i++) {
0248         unsigned long ps = page_sizes[i];
0249         int arg = ilog2(ps) << SHM_HUGE_SHIFT;
0250         printf("Testing %luMB shmget with shift %x\n", ps >> 20, arg);
0251         test_shmget(ps, SHM_HUGETLB | arg);
0252     }
0253     puts("default huge shmget");
0254     test_shmget(default_hps, SHM_HUGETLB);
0255 
0256     return 0;
0257 }