vm_util.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <string.h>
  3. #include <fcntl.h>
  4. #include <dirent.h>
  5. #include <sys/ioctl.h>
  6. #include <linux/userfaultfd.h>
  7. #include <linux/fs.h>
  8. #include <sys/syscall.h>
  9. #include <unistd.h>
  10. #include "../kselftest.h"
  11. #include "vm_util.h"
  12. #define PMD_SIZE_FILE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size"
  13. #define SMAP_FILE_PATH "/proc/self/smaps"
  14. #define STATUS_FILE_PATH "/proc/self/status"
  15. #define MAX_LINE_LENGTH 500
  16. unsigned int __page_size;
  17. unsigned int __page_shift;
  18. uint64_t pagemap_get_entry(int fd, char *start)
  19. {
  20. const unsigned long pfn = (unsigned long)start / getpagesize();
  21. uint64_t entry;
  22. int ret;
  23. ret = pread(fd, &entry, sizeof(entry), pfn * sizeof(entry));
  24. if (ret != sizeof(entry))
  25. ksft_exit_fail_msg("reading pagemap failed\n");
  26. return entry;
  27. }
  28. static uint64_t __pagemap_scan_get_categories(int fd, char *start, struct page_region *r)
  29. {
  30. struct pm_scan_arg arg;
  31. arg.start = (uintptr_t)start;
  32. arg.end = (uintptr_t)(start + psize());
  33. arg.vec = (uintptr_t)r;
  34. arg.vec_len = 1;
  35. arg.flags = 0;
  36. arg.size = sizeof(struct pm_scan_arg);
  37. arg.max_pages = 0;
  38. arg.category_inverted = 0;
  39. arg.category_mask = 0;
  40. arg.category_anyof_mask = PAGE_IS_WPALLOWED | PAGE_IS_WRITTEN | PAGE_IS_FILE |
  41. PAGE_IS_PRESENT | PAGE_IS_SWAPPED | PAGE_IS_PFNZERO |
  42. PAGE_IS_HUGE | PAGE_IS_SOFT_DIRTY;
  43. arg.return_mask = arg.category_anyof_mask;
  44. return ioctl(fd, PAGEMAP_SCAN, &arg);
  45. }
  46. static uint64_t pagemap_scan_get_categories(int fd, char *start)
  47. {
  48. struct page_region r;
  49. long ret;
  50. ret = __pagemap_scan_get_categories(fd, start, &r);
  51. if (ret < 0)
  52. ksft_exit_fail_msg("PAGEMAP_SCAN failed: %s\n", strerror(errno));
  53. if (ret == 0)
  54. return 0;
  55. return r.categories;
  56. }
  57. /* `start` is any valid address. */
  58. static bool pagemap_scan_supported(int fd, char *start)
  59. {
  60. static int supported = -1;
  61. int ret;
  62. if (supported != -1)
  63. return supported;
  64. /* Provide an invalid address in order to trigger EFAULT. */
  65. ret = __pagemap_scan_get_categories(fd, start, (struct page_region *) ~0UL);
  66. if (ret == 0)
  67. ksft_exit_fail_msg("PAGEMAP_SCAN succeeded unexpectedly\n");
  68. supported = errno == EFAULT;
  69. return supported;
  70. }
  71. static bool page_entry_is(int fd, char *start, char *desc,
  72. uint64_t pagemap_flags, uint64_t pagescan_flags)
  73. {
  74. bool m = pagemap_get_entry(fd, start) & pagemap_flags;
  75. if (pagemap_scan_supported(fd, start)) {
  76. bool s = pagemap_scan_get_categories(fd, start) & pagescan_flags;
  77. if (m == s)
  78. return m;
  79. ksft_exit_fail_msg(
  80. "read and ioctl return unmatched results for %s: %d %d", desc, m, s);
  81. }
  82. return m;
  83. }
  84. bool pagemap_is_softdirty(int fd, char *start)
  85. {
  86. return page_entry_is(fd, start, "soft-dirty",
  87. PM_SOFT_DIRTY, PAGE_IS_SOFT_DIRTY);
  88. }
  89. bool pagemap_is_swapped(int fd, char *start)
  90. {
  91. return page_entry_is(fd, start, "swap", PM_SWAP, PAGE_IS_SWAPPED);
  92. }
  93. bool pagemap_is_populated(int fd, char *start)
  94. {
  95. return page_entry_is(fd, start, "populated",
  96. PM_PRESENT | PM_SWAP,
  97. PAGE_IS_PRESENT | PAGE_IS_SWAPPED);
  98. }
  99. unsigned long pagemap_get_pfn(int fd, char *start)
  100. {
  101. uint64_t entry = pagemap_get_entry(fd, start);
  102. /* If present (63th bit), PFN is at bit 0 -- 54. */
  103. if (entry & PM_PRESENT)
  104. return entry & 0x007fffffffffffffull;
  105. return -1ul;
  106. }
  107. void clear_softdirty(void)
  108. {
  109. int ret;
  110. const char *ctrl = "4";
  111. int fd = open("/proc/self/clear_refs", O_WRONLY);
  112. if (fd < 0)
  113. ksft_exit_fail_msg("opening clear_refs failed\n");
  114. ret = write(fd, ctrl, strlen(ctrl));
  115. close(fd);
  116. if (ret != strlen(ctrl))
  117. ksft_exit_fail_msg("writing clear_refs failed\n");
  118. }
  119. bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len)
  120. {
  121. while (fgets(buf, len, fp)) {
  122. if (!strncmp(buf, pattern, strlen(pattern)))
  123. return true;
  124. }
  125. return false;
  126. }
  127. uint64_t read_pmd_pagesize(void)
  128. {
  129. int fd;
  130. char buf[20];
  131. ssize_t num_read;
  132. fd = open(PMD_SIZE_FILE_PATH, O_RDONLY);
  133. if (fd == -1)
  134. return 0;
  135. num_read = read(fd, buf, 19);
  136. if (num_read < 1) {
  137. close(fd);
  138. return 0;
  139. }
  140. buf[num_read] = '\0';
  141. close(fd);
  142. return strtoul(buf, NULL, 10);
  143. }
  144. unsigned long rss_anon(void)
  145. {
  146. unsigned long rss_anon = 0;
  147. FILE *fp;
  148. char buffer[MAX_LINE_LENGTH];
  149. fp = fopen(STATUS_FILE_PATH, "r");
  150. if (!fp)
  151. ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, STATUS_FILE_PATH);
  152. if (!check_for_pattern(fp, "RssAnon:", buffer, sizeof(buffer)))
  153. goto err_out;
  154. if (sscanf(buffer, "RssAnon:%10lu kB", &rss_anon) != 1)
  155. ksft_exit_fail_msg("Reading status error\n");
  156. err_out:
  157. fclose(fp);
  158. return rss_anon;
  159. }
  160. char *__get_smap_entry(void *addr, const char *pattern, char *buf, size_t len)
  161. {
  162. int ret;
  163. FILE *fp;
  164. char *entry = NULL;
  165. char addr_pattern[MAX_LINE_LENGTH];
  166. ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-",
  167. (unsigned long)addr);
  168. if (ret >= MAX_LINE_LENGTH)
  169. ksft_exit_fail_msg("%s: Pattern is too long\n", __func__);
  170. fp = fopen(SMAP_FILE_PATH, "r");
  171. if (!fp)
  172. ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__,
  173. SMAP_FILE_PATH);
  174. if (!check_for_pattern(fp, addr_pattern, buf, len))
  175. goto err_out;
  176. /* Fetch the pattern in the same block */
  177. if (!check_for_pattern(fp, pattern, buf, len))
  178. goto err_out;
  179. /* Trim trailing newline */
  180. entry = strchr(buf, '\n');
  181. if (entry)
  182. *entry = '\0';
  183. entry = buf + strlen(pattern);
  184. err_out:
  185. fclose(fp);
  186. return entry;
  187. }
  188. bool __check_huge(void *addr, char *pattern, int nr_hpages,
  189. uint64_t hpage_size)
  190. {
  191. uint64_t thp = -1;
  192. int ret;
  193. FILE *fp;
  194. char buffer[MAX_LINE_LENGTH];
  195. char addr_pattern[MAX_LINE_LENGTH];
  196. ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-",
  197. (unsigned long) addr);
  198. if (ret >= MAX_LINE_LENGTH)
  199. ksft_exit_fail_msg("%s: Pattern is too long\n", __func__);
  200. fp = fopen(SMAP_FILE_PATH, "r");
  201. if (!fp)
  202. ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, SMAP_FILE_PATH);
  203. if (!check_for_pattern(fp, addr_pattern, buffer, sizeof(buffer)))
  204. goto err_out;
  205. /*
  206. * Fetch the pattern in the same block and check the number of
  207. * hugepages.
  208. */
  209. if (!check_for_pattern(fp, pattern, buffer, sizeof(buffer)))
  210. goto err_out;
  211. snprintf(addr_pattern, MAX_LINE_LENGTH, "%s%%9ld kB", pattern);
  212. if (sscanf(buffer, addr_pattern, &thp) != 1)
  213. ksft_exit_fail_msg("Reading smap error\n");
  214. err_out:
  215. fclose(fp);
  216. return thp == (nr_hpages * (hpage_size >> 10));
  217. }
  218. bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size)
  219. {
  220. return __check_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size);
  221. }
  222. bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size)
  223. {
  224. return __check_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size);
  225. }
  226. bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size)
  227. {
  228. return __check_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size);
  229. }
  230. int64_t allocate_transhuge(void *ptr, int pagemap_fd)
  231. {
  232. uint64_t ent[2];
  233. /* drop pmd */
  234. if (mmap(ptr, HPAGE_SIZE, PROT_READ | PROT_WRITE,
  235. MAP_FIXED | MAP_ANONYMOUS |
  236. MAP_NORESERVE | MAP_PRIVATE, -1, 0) != ptr)
  237. ksft_exit_fail_msg("mmap transhuge\n");
  238. if (madvise(ptr, HPAGE_SIZE, MADV_HUGEPAGE))
  239. ksft_exit_fail_msg("MADV_HUGEPAGE\n");
  240. /* allocate transparent huge page */
  241. *(volatile void **)ptr = ptr;
  242. if (pread(pagemap_fd, ent, sizeof(ent),
  243. (uintptr_t)ptr >> (pshift() - 3)) != sizeof(ent))
  244. ksft_exit_fail_msg("read pagemap\n");
  245. if (PAGEMAP_PRESENT(ent[0]) && PAGEMAP_PRESENT(ent[1]) &&
  246. PAGEMAP_PFN(ent[0]) + 1 == PAGEMAP_PFN(ent[1]) &&
  247. !(PAGEMAP_PFN(ent[0]) & ((1 << (HPAGE_SHIFT - pshift())) - 1)))
  248. return PAGEMAP_PFN(ent[0]);
  249. return -1;
  250. }
  251. unsigned long default_huge_page_size(void)
  252. {
  253. unsigned long hps = 0;
  254. char *line = NULL;
  255. size_t linelen = 0;
  256. FILE *f = fopen("/proc/meminfo", "r");
  257. if (!f)
  258. return 0;
  259. while (getline(&line, &linelen, f) > 0) {
  260. if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) {
  261. hps <<= 10;
  262. break;
  263. }
  264. }
  265. free(line);
  266. fclose(f);
  267. return hps;
  268. }
  269. int detect_hugetlb_page_sizes(size_t sizes[], int max)
  270. {
  271. DIR *dir = opendir("/sys/kernel/mm/hugepages/");
  272. int count = 0;
  273. if (!dir)
  274. return 0;
  275. while (count < max) {
  276. struct dirent *entry = readdir(dir);
  277. size_t kb;
  278. if (!entry)
  279. break;
  280. if (entry->d_type != DT_DIR)
  281. continue;
  282. if (sscanf(entry->d_name, "hugepages-%zukB", &kb) != 1)
  283. continue;
  284. sizes[count++] = kb * 1024;
  285. ksft_print_msg("[INFO] detected hugetlb page size: %zu KiB\n",
  286. kb);
  287. }
  288. closedir(dir);
  289. return count;
  290. }
  291. /* If `ioctls' non-NULL, the allowed ioctls will be returned into the var */
  292. int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len,
  293. bool miss, bool wp, bool minor, uint64_t *ioctls)
  294. {
  295. struct uffdio_register uffdio_register = { 0 };
  296. uint64_t mode = 0;
  297. int ret = 0;
  298. if (miss)
  299. mode |= UFFDIO_REGISTER_MODE_MISSING;
  300. if (wp)
  301. mode |= UFFDIO_REGISTER_MODE_WP;
  302. if (minor)
  303. mode |= UFFDIO_REGISTER_MODE_MINOR;
  304. uffdio_register.range.start = (unsigned long)addr;
  305. uffdio_register.range.len = len;
  306. uffdio_register.mode = mode;
  307. if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
  308. ret = -errno;
  309. else if (ioctls)
  310. *ioctls = uffdio_register.ioctls;
  311. return ret;
  312. }
  313. int uffd_register(int uffd, void *addr, uint64_t len,
  314. bool miss, bool wp, bool minor)
  315. {
  316. return uffd_register_with_ioctls(uffd, addr, len,
  317. miss, wp, minor, NULL);
  318. }
  319. int uffd_unregister(int uffd, void *addr, uint64_t len)
  320. {
  321. struct uffdio_range range = { .start = (uintptr_t)addr, .len = len };
  322. int ret = 0;
  323. if (ioctl(uffd, UFFDIO_UNREGISTER, &range) == -1)
  324. ret = -errno;
  325. return ret;
  326. }
  327. unsigned long get_free_hugepages(void)
  328. {
  329. unsigned long fhp = 0;
  330. char *line = NULL;
  331. size_t linelen = 0;
  332. FILE *f = fopen("/proc/meminfo", "r");
  333. if (!f)
  334. return fhp;
  335. while (getline(&line, &linelen, f) > 0) {
  336. if (sscanf(line, "HugePages_Free: %lu", &fhp) == 1)
  337. break;
  338. }
  339. free(line);
  340. fclose(f);
  341. return fhp;
  342. }
  343. static bool check_vmflag(void *addr, const char *flag)
  344. {
  345. char buffer[MAX_LINE_LENGTH];
  346. const char *flags;
  347. size_t flaglen;
  348. flags = __get_smap_entry(addr, "VmFlags:", buffer, sizeof(buffer));
  349. if (!flags)
  350. ksft_exit_fail_msg("%s: No VmFlags for %p\n", __func__, addr);
  351. while (true) {
  352. flags += strspn(flags, " ");
  353. flaglen = strcspn(flags, " ");
  354. if (!flaglen)
  355. return false;
  356. if (flaglen == strlen(flag) && !memcmp(flags, flag, flaglen))
  357. return true;
  358. flags += flaglen;
  359. }
  360. }
  361. bool softdirty_supported(void)
  362. {
  363. char *addr;
  364. bool supported = false;
  365. const size_t pagesize = getpagesize();
  366. /* New mappings are expected to be marked with VM_SOFTDIRTY (sd). */
  367. addr = mmap(0, pagesize, PROT_READ | PROT_WRITE,
  368. MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  369. if (!addr)
  370. ksft_exit_fail_msg("mmap failed\n");
  371. supported = check_vmflag(addr, "sd");
  372. munmap(addr, pagesize);
  373. return supported;
  374. }