hugetlb-madvise.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * hugepage-madvise:
  4. *
  5. * Basic functional testing of madvise MADV_DONTNEED and MADV_REMOVE
  6. * on hugetlb mappings.
  7. *
  8. * Before running this test, make sure the administrator has pre-allocated
  9. * at least MIN_FREE_PAGES hugetlb pages and they are free. In addition,
  10. * the test takes an argument that is the path to a file in a hugetlbfs
  11. * filesystem. Therefore, a hugetlbfs filesystem must be mounted on some
  12. * directory.
  13. */
  14. #include <stdlib.h>
  15. #include <stdio.h>
  16. #include <unistd.h>
  17. #include <sys/mman.h>
  18. #define __USE_GNU
  19. #include <fcntl.h>
  20. #define USAGE "USAGE: %s <hugepagefile_name>\n"
  21. #define MIN_FREE_PAGES 20
  22. #define NR_HUGE_PAGES 10 /* common number of pages to map/allocate */
  23. #define validate_free_pages(exp_free) \
  24. do { \
  25. int fhp = get_free_hugepages(); \
  26. if (fhp != (exp_free)) { \
  27. printf("Unexpected number of free huge " \
  28. "pages line %d\n", __LINE__); \
  29. exit(1); \
  30. } \
  31. } while (0)
  32. unsigned long huge_page_size;
  33. unsigned long base_page_size;
  34. /*
  35. * default_huge_page_size copied from mlock2-tests.c
  36. */
  37. unsigned long default_huge_page_size(void)
  38. {
  39. unsigned long hps = 0;
  40. char *line = NULL;
  41. size_t linelen = 0;
  42. FILE *f = fopen("/proc/meminfo", "r");
  43. if (!f)
  44. return 0;
  45. while (getline(&line, &linelen, f) > 0) {
  46. if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) {
  47. hps <<= 10;
  48. break;
  49. }
  50. }
  51. free(line);
  52. fclose(f);
  53. return hps;
  54. }
  55. unsigned long get_free_hugepages(void)
  56. {
  57. unsigned long fhp = 0;
  58. char *line = NULL;
  59. size_t linelen = 0;
  60. FILE *f = fopen("/proc/meminfo", "r");
  61. if (!f)
  62. return fhp;
  63. while (getline(&line, &linelen, f) > 0) {
  64. if (sscanf(line, "HugePages_Free: %lu", &fhp) == 1)
  65. break;
  66. }
  67. free(line);
  68. fclose(f);
  69. return fhp;
  70. }
  71. void write_fault_pages(void *addr, unsigned long nr_pages)
  72. {
  73. unsigned long i;
  74. for (i = 0; i < nr_pages; i++)
  75. *((unsigned long *)(addr + (i * huge_page_size))) = i;
  76. }
  77. void read_fault_pages(void *addr, unsigned long nr_pages)
  78. {
  79. unsigned long dummy = 0;
  80. unsigned long i;
  81. for (i = 0; i < nr_pages; i++)
  82. dummy += *((unsigned long *)(addr + (i * huge_page_size)));
  83. }
  84. int main(int argc, char **argv)
  85. {
  86. unsigned long free_hugepages;
  87. void *addr, *addr2;
  88. int fd;
  89. int ret;
  90. if (argc != 2) {
  91. printf(USAGE, argv[0]);
  92. exit(1);
  93. }
  94. huge_page_size = default_huge_page_size();
  95. if (!huge_page_size) {
  96. printf("Unable to determine huge page size, exiting!\n");
  97. exit(1);
  98. }
  99. base_page_size = sysconf(_SC_PAGE_SIZE);
  100. if (!huge_page_size) {
  101. printf("Unable to determine base page size, exiting!\n");
  102. exit(1);
  103. }
  104. free_hugepages = get_free_hugepages();
  105. if (free_hugepages < MIN_FREE_PAGES) {
  106. printf("Not enough free huge pages to test, exiting!\n");
  107. exit(1);
  108. }
  109. fd = open(argv[1], O_CREAT | O_RDWR, 0755);
  110. if (fd < 0) {
  111. perror("Open failed");
  112. exit(1);
  113. }
  114. /*
  115. * Test validity of MADV_DONTNEED addr and length arguments. mmap
  116. * size is NR_HUGE_PAGES + 2. One page at the beginning and end of
  117. * the mapping will be unmapped so we KNOW there is nothing mapped
  118. * there.
  119. */
  120. addr = mmap(NULL, (NR_HUGE_PAGES + 2) * huge_page_size,
  121. PROT_READ | PROT_WRITE,
  122. MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
  123. -1, 0);
  124. if (addr == MAP_FAILED) {
  125. perror("mmap");
  126. exit(1);
  127. }
  128. if (munmap(addr, huge_page_size) ||
  129. munmap(addr + (NR_HUGE_PAGES + 1) * huge_page_size,
  130. huge_page_size)) {
  131. perror("munmap");
  132. exit(1);
  133. }
  134. addr = addr + huge_page_size;
  135. write_fault_pages(addr, NR_HUGE_PAGES);
  136. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  137. /* addr before mapping should fail */
  138. ret = madvise(addr - base_page_size, NR_HUGE_PAGES * huge_page_size,
  139. MADV_DONTNEED);
  140. if (!ret) {
  141. printf("Unexpected success of madvise call with invalid addr line %d\n",
  142. __LINE__);
  143. exit(1);
  144. }
  145. /* addr + length after mapping should fail */
  146. ret = madvise(addr, (NR_HUGE_PAGES * huge_page_size) + base_page_size,
  147. MADV_DONTNEED);
  148. if (!ret) {
  149. printf("Unexpected success of madvise call with invalid length line %d\n",
  150. __LINE__);
  151. exit(1);
  152. }
  153. (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
  154. /*
  155. * Test alignment of MADV_DONTNEED addr and length arguments
  156. */
  157. addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
  158. PROT_READ | PROT_WRITE,
  159. MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
  160. -1, 0);
  161. if (addr == MAP_FAILED) {
  162. perror("mmap");
  163. exit(1);
  164. }
  165. write_fault_pages(addr, NR_HUGE_PAGES);
  166. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  167. /* addr is not huge page size aligned and should fail */
  168. ret = madvise(addr + base_page_size,
  169. NR_HUGE_PAGES * huge_page_size - base_page_size,
  170. MADV_DONTNEED);
  171. if (!ret) {
  172. printf("Unexpected success of madvise call with unaligned start address %d\n",
  173. __LINE__);
  174. exit(1);
  175. }
  176. /* addr + length should be aligned up to huge page size */
  177. if (madvise(addr,
  178. ((NR_HUGE_PAGES - 1) * huge_page_size) + base_page_size,
  179. MADV_DONTNEED)) {
  180. perror("madvise");
  181. exit(1);
  182. }
  183. /* should free all pages in mapping */
  184. validate_free_pages(free_hugepages);
  185. (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
  186. /*
  187. * Test MADV_DONTNEED on anonymous private mapping
  188. */
  189. addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
  190. PROT_READ | PROT_WRITE,
  191. MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
  192. -1, 0);
  193. if (addr == MAP_FAILED) {
  194. perror("mmap");
  195. exit(1);
  196. }
  197. write_fault_pages(addr, NR_HUGE_PAGES);
  198. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  199. if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
  200. perror("madvise");
  201. exit(1);
  202. }
  203. /* should free all pages in mapping */
  204. validate_free_pages(free_hugepages);
  205. (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
  206. /*
  207. * Test MADV_DONTNEED on private mapping of hugetlb file
  208. */
  209. if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) {
  210. perror("fallocate");
  211. exit(1);
  212. }
  213. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  214. addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
  215. PROT_READ | PROT_WRITE,
  216. MAP_PRIVATE, fd, 0);
  217. if (addr == MAP_FAILED) {
  218. perror("mmap");
  219. exit(1);
  220. }
  221. /* read should not consume any pages */
  222. read_fault_pages(addr, NR_HUGE_PAGES);
  223. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  224. /* madvise should not free any pages */
  225. if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
  226. perror("madvise");
  227. exit(1);
  228. }
  229. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  230. /* writes should allocate private pages */
  231. write_fault_pages(addr, NR_HUGE_PAGES);
  232. validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
  233. /* madvise should free private pages */
  234. if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
  235. perror("madvise");
  236. exit(1);
  237. }
  238. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  239. /* writes should allocate private pages */
  240. write_fault_pages(addr, NR_HUGE_PAGES);
  241. validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
  242. /*
  243. * The fallocate below certainly should free the pages associated
  244. * with the file. However, pages in the private mapping are also
  245. * freed. This is not the 'correct' behavior, but is expected
  246. * because this is how it has worked since the initial hugetlb
  247. * implementation.
  248. */
  249. if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
  250. 0, NR_HUGE_PAGES * huge_page_size)) {
  251. perror("fallocate");
  252. exit(1);
  253. }
  254. validate_free_pages(free_hugepages);
  255. (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
  256. /*
  257. * Test MADV_DONTNEED on shared mapping of hugetlb file
  258. */
  259. if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) {
  260. perror("fallocate");
  261. exit(1);
  262. }
  263. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  264. addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
  265. PROT_READ | PROT_WRITE,
  266. MAP_SHARED, fd, 0);
  267. if (addr == MAP_FAILED) {
  268. perror("mmap");
  269. exit(1);
  270. }
  271. /* write should not consume any pages */
  272. write_fault_pages(addr, NR_HUGE_PAGES);
  273. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  274. /* madvise should not free any pages */
  275. if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
  276. perror("madvise");
  277. exit(1);
  278. }
  279. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  280. /*
  281. * Test MADV_REMOVE on shared mapping of hugetlb file
  282. *
  283. * madvise is same as hole punch and should free all pages.
  284. */
  285. if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) {
  286. perror("madvise");
  287. exit(1);
  288. }
  289. validate_free_pages(free_hugepages);
  290. (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
  291. /*
  292. * Test MADV_REMOVE on shared and private mapping of hugetlb file
  293. */
  294. if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) {
  295. perror("fallocate");
  296. exit(1);
  297. }
  298. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  299. addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
  300. PROT_READ | PROT_WRITE,
  301. MAP_SHARED, fd, 0);
  302. if (addr == MAP_FAILED) {
  303. perror("mmap");
  304. exit(1);
  305. }
  306. /* shared write should not consume any additional pages */
  307. write_fault_pages(addr, NR_HUGE_PAGES);
  308. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  309. addr2 = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
  310. PROT_READ | PROT_WRITE,
  311. MAP_PRIVATE, fd, 0);
  312. if (addr2 == MAP_FAILED) {
  313. perror("mmap");
  314. exit(1);
  315. }
  316. /* private read should not consume any pages */
  317. read_fault_pages(addr2, NR_HUGE_PAGES);
  318. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  319. /* private write should consume additional pages */
  320. write_fault_pages(addr2, NR_HUGE_PAGES);
  321. validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
  322. /* madvise of shared mapping should not free any pages */
  323. if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
  324. perror("madvise");
  325. exit(1);
  326. }
  327. validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
  328. /* madvise of private mapping should free private pages */
  329. if (madvise(addr2, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
  330. perror("madvise");
  331. exit(1);
  332. }
  333. validate_free_pages(free_hugepages - NR_HUGE_PAGES);
  334. /* private write should consume additional pages again */
  335. write_fault_pages(addr2, NR_HUGE_PAGES);
  336. validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
  337. /*
  338. * madvise should free both file and private pages although this is
  339. * not correct. private pages should not be freed, but this is
  340. * expected. See comment associated with FALLOC_FL_PUNCH_HOLE call.
  341. */
  342. if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) {
  343. perror("madvise");
  344. exit(1);
  345. }
  346. validate_free_pages(free_hugepages);
  347. (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
  348. (void)munmap(addr2, NR_HUGE_PAGES * huge_page_size);
  349. close(fd);
  350. unlink(argv[1]);
  351. return 0;
  352. }