Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * hugepage-madvise:
0004  *
0005  * Basic functional testing of madvise MADV_DONTNEED and MADV_REMOVE
0006  * on hugetlb mappings.
0007  *
0008  * Before running this test, make sure the administrator has pre-allocated
0009  * at least MIN_FREE_PAGES hugetlb pages and they are free.  In addition,
0010  * the test takes an argument that is the path to a file in a hugetlbfs
0011  * filesystem.  Therefore, a hugetlbfs filesystem must be mounted on some
0012  * directory.
0013  */
0014 
0015 #include <stdlib.h>
0016 #include <stdio.h>
0017 #include <unistd.h>
0018 #include <sys/mman.h>
0019 #define __USE_GNU
0020 #include <fcntl.h>
0021 
0022 #define USAGE   "USAGE: %s <hugepagefile_name>\n"
0023 #define MIN_FREE_PAGES  20
0024 #define NR_HUGE_PAGES   10  /* common number of pages to map/allocate */
0025 
0026 #define validate_free_pages(exp_free)                   \
0027     do {                                \
0028         int fhp = get_free_hugepages();             \
0029         if (fhp != (exp_free)) {                \
0030             printf("Unexpected number of free huge "    \
0031                 "pages line %d\n", __LINE__);       \
0032             exit(1);                    \
0033         }                           \
0034     } while (0)
0035 
0036 unsigned long huge_page_size;
0037 unsigned long base_page_size;
0038 
0039 /*
0040  * default_huge_page_size copied from mlock2-tests.c
0041  */
0042 unsigned long default_huge_page_size(void)
0043 {
0044     unsigned long hps = 0;
0045     char *line = NULL;
0046     size_t linelen = 0;
0047     FILE *f = fopen("/proc/meminfo", "r");
0048 
0049     if (!f)
0050         return 0;
0051     while (getline(&line, &linelen, f) > 0) {
0052         if (sscanf(line, "Hugepagesize:       %lu kB", &hps) == 1) {
0053             hps <<= 10;
0054             break;
0055         }
0056     }
0057 
0058     free(line);
0059     fclose(f);
0060     return hps;
0061 }
0062 
0063 unsigned long get_free_hugepages(void)
0064 {
0065     unsigned long fhp = 0;
0066     char *line = NULL;
0067     size_t linelen = 0;
0068     FILE *f = fopen("/proc/meminfo", "r");
0069 
0070     if (!f)
0071         return fhp;
0072     while (getline(&line, &linelen, f) > 0) {
0073         if (sscanf(line, "HugePages_Free:      %lu", &fhp) == 1)
0074             break;
0075     }
0076 
0077     free(line);
0078     fclose(f);
0079     return fhp;
0080 }
0081 
0082 void write_fault_pages(void *addr, unsigned long nr_pages)
0083 {
0084     unsigned long i;
0085 
0086     for (i = 0; i < nr_pages; i++)
0087         *((unsigned long *)(addr + (i * huge_page_size))) = i;
0088 }
0089 
0090 void read_fault_pages(void *addr, unsigned long nr_pages)
0091 {
0092     unsigned long dummy = 0;
0093     unsigned long i;
0094 
0095     for (i = 0; i < nr_pages; i++)
0096         dummy += *((unsigned long *)(addr + (i * huge_page_size)));
0097 }
0098 
0099 int main(int argc, char **argv)
0100 {
0101     unsigned long free_hugepages;
0102     void *addr, *addr2;
0103     int fd;
0104     int ret;
0105 
0106     if (argc != 2) {
0107         printf(USAGE, argv[0]);
0108         exit(1);
0109     }
0110 
0111     huge_page_size = default_huge_page_size();
0112     if (!huge_page_size) {
0113         printf("Unable to determine huge page size, exiting!\n");
0114         exit(1);
0115     }
0116     base_page_size = sysconf(_SC_PAGE_SIZE);
0117     if (!huge_page_size) {
0118         printf("Unable to determine base page size, exiting!\n");
0119         exit(1);
0120     }
0121 
0122     free_hugepages = get_free_hugepages();
0123     if (free_hugepages < MIN_FREE_PAGES) {
0124         printf("Not enough free huge pages to test, exiting!\n");
0125         exit(1);
0126     }
0127 
0128     fd = open(argv[1], O_CREAT | O_RDWR, 0755);
0129     if (fd < 0) {
0130         perror("Open failed");
0131         exit(1);
0132     }
0133 
0134     /*
0135      * Test validity of MADV_DONTNEED addr and length arguments.  mmap
0136      * size is NR_HUGE_PAGES + 2.  One page at the beginning and end of
0137      * the mapping will be unmapped so we KNOW there is nothing mapped
0138      * there.
0139      */
0140     addr = mmap(NULL, (NR_HUGE_PAGES + 2) * huge_page_size,
0141             PROT_READ | PROT_WRITE,
0142             MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
0143             -1, 0);
0144     if (addr == MAP_FAILED) {
0145         perror("mmap");
0146         exit(1);
0147     }
0148     if (munmap(addr, huge_page_size) ||
0149             munmap(addr + (NR_HUGE_PAGES + 1) * huge_page_size,
0150                 huge_page_size)) {
0151         perror("munmap");
0152         exit(1);
0153     }
0154     addr = addr + huge_page_size;
0155 
0156     write_fault_pages(addr, NR_HUGE_PAGES);
0157     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0158 
0159     /* addr before mapping should fail */
0160     ret = madvise(addr - base_page_size, NR_HUGE_PAGES * huge_page_size,
0161         MADV_DONTNEED);
0162     if (!ret) {
0163         printf("Unexpected success of madvise call with invalid addr line %d\n",
0164                 __LINE__);
0165             exit(1);
0166     }
0167 
0168     /* addr + length after mapping should fail */
0169     ret = madvise(addr, (NR_HUGE_PAGES * huge_page_size) + base_page_size,
0170         MADV_DONTNEED);
0171     if (!ret) {
0172         printf("Unexpected success of madvise call with invalid length line %d\n",
0173                 __LINE__);
0174             exit(1);
0175     }
0176 
0177     (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
0178 
0179     /*
0180      * Test alignment of MADV_DONTNEED addr and length arguments
0181      */
0182     addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
0183             PROT_READ | PROT_WRITE,
0184             MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
0185             -1, 0);
0186     if (addr == MAP_FAILED) {
0187         perror("mmap");
0188         exit(1);
0189     }
0190     write_fault_pages(addr, NR_HUGE_PAGES);
0191     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0192 
0193     /* addr is not huge page size aligned and should fail */
0194     ret = madvise(addr + base_page_size,
0195             NR_HUGE_PAGES * huge_page_size - base_page_size,
0196             MADV_DONTNEED);
0197     if (!ret) {
0198         printf("Unexpected success of madvise call with unaligned start address %d\n",
0199                 __LINE__);
0200             exit(1);
0201     }
0202 
0203     /* addr + length should be aligned up to huge page size */
0204     if (madvise(addr,
0205             ((NR_HUGE_PAGES - 1) * huge_page_size) + base_page_size,
0206             MADV_DONTNEED)) {
0207         perror("madvise");
0208         exit(1);
0209     }
0210 
0211     /* should free all pages in mapping */
0212     validate_free_pages(free_hugepages);
0213 
0214     (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
0215 
0216     /*
0217      * Test MADV_DONTNEED on anonymous private mapping
0218      */
0219     addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
0220             PROT_READ | PROT_WRITE,
0221             MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
0222             -1, 0);
0223     if (addr == MAP_FAILED) {
0224         perror("mmap");
0225         exit(1);
0226     }
0227     write_fault_pages(addr, NR_HUGE_PAGES);
0228     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0229 
0230     if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
0231         perror("madvise");
0232         exit(1);
0233     }
0234 
0235     /* should free all pages in mapping */
0236     validate_free_pages(free_hugepages);
0237 
0238     (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
0239 
0240     /*
0241      * Test MADV_DONTNEED on private mapping of hugetlb file
0242      */
0243     if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) {
0244         perror("fallocate");
0245         exit(1);
0246     }
0247     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0248 
0249     addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
0250             PROT_READ | PROT_WRITE,
0251             MAP_PRIVATE, fd, 0);
0252     if (addr == MAP_FAILED) {
0253         perror("mmap");
0254         exit(1);
0255     }
0256 
0257     /* read should not consume any pages */
0258     read_fault_pages(addr, NR_HUGE_PAGES);
0259     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0260 
0261     /* madvise should not free any pages */
0262     if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
0263         perror("madvise");
0264         exit(1);
0265     }
0266     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0267 
0268     /* writes should allocate private pages */
0269     write_fault_pages(addr, NR_HUGE_PAGES);
0270     validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
0271 
0272     /* madvise should free private pages */
0273     if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
0274         perror("madvise");
0275         exit(1);
0276     }
0277     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0278 
0279     /* writes should allocate private pages */
0280     write_fault_pages(addr, NR_HUGE_PAGES);
0281     validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
0282 
0283     /*
0284      * The fallocate below certainly should free the pages associated
0285      * with the file.  However, pages in the private mapping are also
0286      * freed.  This is not the 'correct' behavior, but is expected
0287      * because this is how it has worked since the initial hugetlb
0288      * implementation.
0289      */
0290     if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
0291                     0, NR_HUGE_PAGES * huge_page_size)) {
0292         perror("fallocate");
0293         exit(1);
0294     }
0295     validate_free_pages(free_hugepages);
0296 
0297     (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
0298 
0299     /*
0300      * Test MADV_DONTNEED on shared mapping of hugetlb file
0301      */
0302     if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) {
0303         perror("fallocate");
0304         exit(1);
0305     }
0306     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0307 
0308     addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
0309             PROT_READ | PROT_WRITE,
0310             MAP_SHARED, fd, 0);
0311     if (addr == MAP_FAILED) {
0312         perror("mmap");
0313         exit(1);
0314     }
0315 
0316     /* write should not consume any pages */
0317     write_fault_pages(addr, NR_HUGE_PAGES);
0318     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0319 
0320     /* madvise should not free any pages */
0321     if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
0322         perror("madvise");
0323         exit(1);
0324     }
0325     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0326 
0327     /*
0328      * Test MADV_REMOVE on shared mapping of hugetlb file
0329      *
0330      * madvise is same as hole punch and should free all pages.
0331      */
0332     if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) {
0333         perror("madvise");
0334         exit(1);
0335     }
0336     validate_free_pages(free_hugepages);
0337     (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
0338 
0339     /*
0340      * Test MADV_REMOVE on shared and private mapping of hugetlb file
0341      */
0342     if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) {
0343         perror("fallocate");
0344         exit(1);
0345     }
0346     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0347 
0348     addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
0349             PROT_READ | PROT_WRITE,
0350             MAP_SHARED, fd, 0);
0351     if (addr == MAP_FAILED) {
0352         perror("mmap");
0353         exit(1);
0354     }
0355 
0356     /* shared write should not consume any additional pages */
0357     write_fault_pages(addr, NR_HUGE_PAGES);
0358     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0359 
0360     addr2 = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
0361             PROT_READ | PROT_WRITE,
0362             MAP_PRIVATE, fd, 0);
0363     if (addr2 == MAP_FAILED) {
0364         perror("mmap");
0365         exit(1);
0366     }
0367 
0368     /* private read should not consume any pages */
0369     read_fault_pages(addr2, NR_HUGE_PAGES);
0370     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0371 
0372     /* private write should consume additional pages */
0373     write_fault_pages(addr2, NR_HUGE_PAGES);
0374     validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
0375 
0376     /* madvise of shared mapping should not free any pages */
0377     if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
0378         perror("madvise");
0379         exit(1);
0380     }
0381     validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
0382 
0383     /* madvise of private mapping should free private pages */
0384     if (madvise(addr2, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
0385         perror("madvise");
0386         exit(1);
0387     }
0388     validate_free_pages(free_hugepages - NR_HUGE_PAGES);
0389 
0390     /* private write should consume additional pages again */
0391     write_fault_pages(addr2, NR_HUGE_PAGES);
0392     validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
0393 
0394     /*
0395      * madvise should free both file and private pages although this is
0396      * not correct.  private pages should not be freed, but this is
0397      * expected.  See comment associated with FALLOC_FL_PUNCH_HOLE call.
0398      */
0399     if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) {
0400         perror("madvise");
0401         exit(1);
0402     }
0403     validate_free_pages(free_hugepages);
0404 
0405     (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
0406     (void)munmap(addr2, NR_HUGE_PAGES * huge_page_size);
0407 
0408     close(fd);
0409     unlink(argv[1]);
0410     return 0;
0411 }