Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * kselftest suite for mincore().
0004  *
0005  * Copyright (C) 2020 Collabora, Ltd.
0006  */
0007 
0008 #define _GNU_SOURCE
0009 
0010 #include <stdio.h>
0011 #include <errno.h>
0012 #include <unistd.h>
0013 #include <stdlib.h>
0014 #include <sys/mman.h>
0015 #include <string.h>
0016 #include <fcntl.h>
0017 
0018 #include "../kselftest.h"
0019 #include "../kselftest_harness.h"
0020 
0021 /* Default test file size: 4MB */
0022 #define MB (1UL << 20)
0023 #define FILE_SIZE (4 * MB)
0024 
0025 
0026 /*
0027  * Tests the user interface. This test triggers most of the documented
0028  * error conditions in mincore().
0029  */
0030 TEST(basic_interface)
0031 {
0032     int retval;
0033     int page_size;
0034     unsigned char vec[1];
0035     char *addr;
0036 
0037     page_size = sysconf(_SC_PAGESIZE);
0038 
0039     /* Query a 0 byte sized range */
0040     retval = mincore(0, 0, vec);
0041     EXPECT_EQ(0, retval);
0042 
0043     /* Addresses in the specified range are invalid or unmapped */
0044     errno = 0;
0045     retval = mincore(NULL, page_size, vec);
0046     EXPECT_EQ(-1, retval);
0047     EXPECT_EQ(ENOMEM, errno);
0048 
0049     errno = 0;
0050     addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
0051         MAP_SHARED | MAP_ANONYMOUS, -1, 0);
0052     ASSERT_NE(MAP_FAILED, addr) {
0053         TH_LOG("mmap error: %s", strerror(errno));
0054     }
0055 
0056     /* <addr> argument is not page-aligned */
0057     errno = 0;
0058     retval = mincore(addr + 1, page_size, vec);
0059     EXPECT_EQ(-1, retval);
0060     EXPECT_EQ(EINVAL, errno);
0061 
0062     /* <length> argument is too large */
0063     errno = 0;
0064     retval = mincore(addr, -1, vec);
0065     EXPECT_EQ(-1, retval);
0066     EXPECT_EQ(ENOMEM, errno);
0067 
0068     /* <vec> argument points to an illegal address */
0069     errno = 0;
0070     retval = mincore(addr, page_size, NULL);
0071     EXPECT_EQ(-1, retval);
0072     EXPECT_EQ(EFAULT, errno);
0073     munmap(addr, page_size);
0074 }
0075 
0076 
0077 /*
0078  * Test mincore() behavior on a private anonymous page mapping.
0079  * Check that the page is not loaded into memory right after the mapping
0080  * but after accessing it (on-demand allocation).
0081  * Then free the page and check that it's not memory-resident.
0082  */
0083 TEST(check_anonymous_locked_pages)
0084 {
0085     unsigned char vec[1];
0086     char *addr;
0087     int retval;
0088     int page_size;
0089 
0090     page_size = sysconf(_SC_PAGESIZE);
0091 
0092     /* Map one page and check it's not memory-resident */
0093     errno = 0;
0094     addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
0095             MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
0096     ASSERT_NE(MAP_FAILED, addr) {
0097         TH_LOG("mmap error: %s", strerror(errno));
0098     }
0099     retval = mincore(addr, page_size, vec);
0100     ASSERT_EQ(0, retval);
0101     ASSERT_EQ(0, vec[0]) {
0102         TH_LOG("Page found in memory before use");
0103     }
0104 
0105     /* Touch the page and check again. It should now be in memory */
0106     addr[0] = 1;
0107     mlock(addr, page_size);
0108     retval = mincore(addr, page_size, vec);
0109     ASSERT_EQ(0, retval);
0110     ASSERT_EQ(1, vec[0]) {
0111         TH_LOG("Page not found in memory after use");
0112     }
0113 
0114     /*
0115      * It shouldn't be memory-resident after unlocking it and
0116      * marking it as unneeded.
0117      */
0118     munlock(addr, page_size);
0119     madvise(addr, page_size, MADV_DONTNEED);
0120     retval = mincore(addr, page_size, vec);
0121     ASSERT_EQ(0, retval);
0122     ASSERT_EQ(0, vec[0]) {
0123         TH_LOG("Page in memory after being zapped");
0124     }
0125     munmap(addr, page_size);
0126 }
0127 
0128 
0129 /*
0130  * Check mincore() behavior on huge pages.
0131  * This test will be skipped if the mapping fails (ie. if there are no
0132  * huge pages available).
0133  *
0134  * Make sure the system has at least one free huge page, check
0135  * "HugePages_Free" in /proc/meminfo.
0136  * Increment /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages if
0137  * needed.
0138  */
0139 TEST(check_huge_pages)
0140 {
0141     unsigned char vec[1];
0142     char *addr;
0143     int retval;
0144     int page_size;
0145 
0146     page_size = sysconf(_SC_PAGESIZE);
0147 
0148     errno = 0;
0149     addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
0150         MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
0151         -1, 0);
0152     if (addr == MAP_FAILED) {
0153         if (errno == ENOMEM)
0154             SKIP(return, "No huge pages available.");
0155         else
0156             TH_LOG("mmap error: %s", strerror(errno));
0157     }
0158     retval = mincore(addr, page_size, vec);
0159     ASSERT_EQ(0, retval);
0160     ASSERT_EQ(0, vec[0]) {
0161         TH_LOG("Page found in memory before use");
0162     }
0163 
0164     addr[0] = 1;
0165     mlock(addr, page_size);
0166     retval = mincore(addr, page_size, vec);
0167     ASSERT_EQ(0, retval);
0168     ASSERT_EQ(1, vec[0]) {
0169         TH_LOG("Page not found in memory after use");
0170     }
0171 
0172     munlock(addr, page_size);
0173     munmap(addr, page_size);
0174 }
0175 
0176 
0177 /*
0178  * Test mincore() behavior on a file-backed page.
0179  * No pages should be loaded into memory right after the mapping. Then,
0180  * accessing any address in the mapping range should load the page
0181  * containing the address and a number of subsequent pages (readahead).
0182  *
0183  * The actual readahead settings depend on the test environment, so we
0184  * can't make a lot of assumptions about that. This test covers the most
0185  * general cases.
0186  */
0187 TEST(check_file_mmap)
0188 {
0189     unsigned char *vec;
0190     int vec_size;
0191     char *addr;
0192     int retval;
0193     int page_size;
0194     int fd;
0195     int i;
0196     int ra_pages = 0;
0197 
0198     page_size = sysconf(_SC_PAGESIZE);
0199     vec_size = FILE_SIZE / page_size;
0200     if (FILE_SIZE % page_size)
0201         vec_size++;
0202 
0203     vec = calloc(vec_size, sizeof(unsigned char));
0204     ASSERT_NE(NULL, vec) {
0205         TH_LOG("Can't allocate array");
0206     }
0207 
0208     errno = 0;
0209     fd = open(".", O_TMPFILE | O_RDWR, 0600);
0210     if (fd < 0) {
0211         ASSERT_EQ(errno, EOPNOTSUPP) {
0212             TH_LOG("Can't create temporary file: %s",
0213                    strerror(errno));
0214         }
0215         SKIP(goto out_free, "O_TMPFILE not supported by filesystem.");
0216     }
0217     errno = 0;
0218     retval = fallocate(fd, 0, 0, FILE_SIZE);
0219     if (retval) {
0220         ASSERT_EQ(errno, EOPNOTSUPP) {
0221             TH_LOG("Error allocating space for the temporary file: %s",
0222                    strerror(errno));
0223         }
0224         SKIP(goto out_close, "fallocate not supported by filesystem.");
0225     }
0226 
0227     /*
0228      * Map the whole file, the pages shouldn't be fetched yet.
0229      */
0230     errno = 0;
0231     addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE,
0232             MAP_SHARED, fd, 0);
0233     ASSERT_NE(MAP_FAILED, addr) {
0234         TH_LOG("mmap error: %s", strerror(errno));
0235     }
0236     retval = mincore(addr, FILE_SIZE, vec);
0237     ASSERT_EQ(0, retval);
0238     for (i = 0; i < vec_size; i++) {
0239         ASSERT_EQ(0, vec[i]) {
0240             TH_LOG("Unexpected page in memory");
0241         }
0242     }
0243 
0244     /*
0245      * Touch a page in the middle of the mapping. We expect the next
0246      * few pages (the readahead window) to be populated too.
0247      */
0248     addr[FILE_SIZE / 2] = 1;
0249     retval = mincore(addr, FILE_SIZE, vec);
0250     ASSERT_EQ(0, retval);
0251     ASSERT_EQ(1, vec[FILE_SIZE / 2 / page_size]) {
0252         TH_LOG("Page not found in memory after use");
0253     }
0254 
0255     i = FILE_SIZE / 2 / page_size + 1;
0256     while (i < vec_size && vec[i]) {
0257         ra_pages++;
0258         i++;
0259     }
0260     EXPECT_GT(ra_pages, 0) {
0261         TH_LOG("No read-ahead pages found in memory");
0262     }
0263 
0264     EXPECT_LT(i, vec_size) {
0265         TH_LOG("Read-ahead pages reached the end of the file");
0266     }
0267     /*
0268      * End of the readahead window. The rest of the pages shouldn't
0269      * be in memory.
0270      */
0271     if (i < vec_size) {
0272         while (i < vec_size && !vec[i])
0273             i++;
0274         EXPECT_EQ(vec_size, i) {
0275             TH_LOG("Unexpected page in memory beyond readahead window");
0276         }
0277     }
0278 
0279     munmap(addr, FILE_SIZE);
0280 out_close:
0281     close(fd);
0282 out_free:
0283     free(vec);
0284 }
0285 
0286 
0287 /*
0288  * Test mincore() behavior on a page backed by a tmpfs file.  This test
0289  * performs the same steps as the previous one. However, we don't expect
0290  * any readahead in this case.
0291  */
0292 TEST(check_tmpfs_mmap)
0293 {
0294     unsigned char *vec;
0295     int vec_size;
0296     char *addr;
0297     int retval;
0298     int page_size;
0299     int fd;
0300     int i;
0301     int ra_pages = 0;
0302 
0303     page_size = sysconf(_SC_PAGESIZE);
0304     vec_size = FILE_SIZE / page_size;
0305     if (FILE_SIZE % page_size)
0306         vec_size++;
0307 
0308     vec = calloc(vec_size, sizeof(unsigned char));
0309     ASSERT_NE(NULL, vec) {
0310         TH_LOG("Can't allocate array");
0311     }
0312 
0313     errno = 0;
0314     fd = open("/dev/shm", O_TMPFILE | O_RDWR, 0600);
0315     ASSERT_NE(-1, fd) {
0316         TH_LOG("Can't create temporary file: %s",
0317             strerror(errno));
0318     }
0319     errno = 0;
0320     retval = fallocate(fd, 0, 0, FILE_SIZE);
0321     ASSERT_EQ(0, retval) {
0322         TH_LOG("Error allocating space for the temporary file: %s",
0323             strerror(errno));
0324     }
0325 
0326     /*
0327      * Map the whole file, the pages shouldn't be fetched yet.
0328      */
0329     errno = 0;
0330     addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE,
0331             MAP_SHARED, fd, 0);
0332     ASSERT_NE(MAP_FAILED, addr) {
0333         TH_LOG("mmap error: %s", strerror(errno));
0334     }
0335     retval = mincore(addr, FILE_SIZE, vec);
0336     ASSERT_EQ(0, retval);
0337     for (i = 0; i < vec_size; i++) {
0338         ASSERT_EQ(0, vec[i]) {
0339             TH_LOG("Unexpected page in memory");
0340         }
0341     }
0342 
0343     /*
0344      * Touch a page in the middle of the mapping. We expect only
0345      * that page to be fetched into memory.
0346      */
0347     addr[FILE_SIZE / 2] = 1;
0348     retval = mincore(addr, FILE_SIZE, vec);
0349     ASSERT_EQ(0, retval);
0350     ASSERT_EQ(1, vec[FILE_SIZE / 2 / page_size]) {
0351         TH_LOG("Page not found in memory after use");
0352     }
0353 
0354     i = FILE_SIZE / 2 / page_size + 1;
0355     while (i < vec_size && vec[i]) {
0356         ra_pages++;
0357         i++;
0358     }
0359     ASSERT_EQ(ra_pages, 0) {
0360         TH_LOG("Read-ahead pages found in memory");
0361     }
0362 
0363     munmap(addr, FILE_SIZE);
0364     close(fd);
0365     free(vec);
0366 }
0367 
0368 TEST_HARNESS_MAIN