vm_util.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <string.h>
  3. #include <errno.h>
  4. #include <fcntl.h>
  5. #include <dirent.h>
  6. #include <inttypes.h>
  7. #include <sys/ioctl.h>
  8. #include <linux/userfaultfd.h>
  9. #include <linux/fs.h>
  10. #include <sys/syscall.h>
  11. #include <unistd.h>
  12. #include "kselftest.h"
  13. #include "vm_util.h"
  14. #define PMD_SIZE_FILE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size"
  15. #define SMAP_FILE_PATH "/proc/self/smaps"
  16. #define STATUS_FILE_PATH "/proc/self/status"
  17. #define MAX_LINE_LENGTH 500
  18. unsigned int __page_size;
  19. unsigned int __page_shift;
  20. uint64_t pagemap_get_entry(int fd, char *start)
  21. {
  22. const unsigned long pfn = (unsigned long)start / getpagesize();
  23. uint64_t entry;
  24. int ret;
  25. ret = pread(fd, &entry, sizeof(entry), pfn * sizeof(entry));
  26. if (ret != sizeof(entry))
  27. ksft_exit_fail_msg("reading pagemap failed\n");
  28. return entry;
  29. }
  30. static uint64_t __pagemap_scan_get_categories(int fd, char *start, struct page_region *r)
  31. {
  32. struct pm_scan_arg arg;
  33. arg.start = (uintptr_t)start;
  34. arg.end = (uintptr_t)(start + psize());
  35. arg.vec = (uintptr_t)r;
  36. arg.vec_len = 1;
  37. arg.flags = 0;
  38. arg.size = sizeof(struct pm_scan_arg);
  39. arg.max_pages = 0;
  40. arg.category_inverted = 0;
  41. arg.category_mask = 0;
  42. arg.category_anyof_mask = PAGE_IS_WPALLOWED | PAGE_IS_WRITTEN | PAGE_IS_FILE |
  43. PAGE_IS_PRESENT | PAGE_IS_SWAPPED | PAGE_IS_PFNZERO |
  44. PAGE_IS_HUGE | PAGE_IS_SOFT_DIRTY;
  45. arg.return_mask = arg.category_anyof_mask;
  46. return ioctl(fd, PAGEMAP_SCAN, &arg);
  47. }
  48. static uint64_t pagemap_scan_get_categories(int fd, char *start)
  49. {
  50. struct page_region r;
  51. long ret;
  52. ret = __pagemap_scan_get_categories(fd, start, &r);
  53. if (ret < 0)
  54. ksft_exit_fail_msg("PAGEMAP_SCAN failed: %s\n", strerror(errno));
  55. if (ret == 0)
  56. return 0;
  57. return r.categories;
  58. }
  59. /* `start` is any valid address. */
  60. static bool pagemap_scan_supported(int fd, char *start)
  61. {
  62. static int supported = -1;
  63. int ret;
  64. if (supported != -1)
  65. return supported;
  66. /* Provide an invalid address in order to trigger EFAULT. */
  67. ret = __pagemap_scan_get_categories(fd, start, (struct page_region *) ~0UL);
  68. if (ret == 0)
  69. ksft_exit_fail_msg("PAGEMAP_SCAN succeeded unexpectedly\n");
  70. supported = errno == EFAULT;
  71. return supported;
  72. }
  73. static bool page_entry_is(int fd, char *start, char *desc,
  74. uint64_t pagemap_flags, uint64_t pagescan_flags)
  75. {
  76. bool m = pagemap_get_entry(fd, start) & pagemap_flags;
  77. if (pagemap_scan_supported(fd, start)) {
  78. bool s = pagemap_scan_get_categories(fd, start) & pagescan_flags;
  79. if (m == s)
  80. return m;
  81. ksft_exit_fail_msg(
  82. "read and ioctl return unmatched results for %s: %d %d", desc, m, s);
  83. }
  84. return m;
  85. }
  86. bool pagemap_is_softdirty(int fd, char *start)
  87. {
  88. return page_entry_is(fd, start, "soft-dirty",
  89. PM_SOFT_DIRTY, PAGE_IS_SOFT_DIRTY);
  90. }
  91. bool pagemap_is_swapped(int fd, char *start)
  92. {
  93. return page_entry_is(fd, start, "swap", PM_SWAP, PAGE_IS_SWAPPED);
  94. }
  95. bool pagemap_is_populated(int fd, char *start)
  96. {
  97. return page_entry_is(fd, start, "populated",
  98. PM_PRESENT | PM_SWAP,
  99. PAGE_IS_PRESENT | PAGE_IS_SWAPPED);
  100. }
  101. unsigned long pagemap_get_pfn(int fd, char *start)
  102. {
  103. uint64_t entry = pagemap_get_entry(fd, start);
  104. /* If present (63th bit), PFN is at bit 0 -- 54. */
  105. if (entry & PM_PRESENT)
  106. return entry & 0x007fffffffffffffull;
  107. return -1ul;
  108. }
  109. void clear_softdirty(void)
  110. {
  111. int ret;
  112. const char *ctrl = "4";
  113. int fd = open("/proc/self/clear_refs", O_WRONLY);
  114. if (fd < 0)
  115. ksft_exit_fail_msg("opening clear_refs failed\n");
  116. ret = write(fd, ctrl, strlen(ctrl));
  117. close(fd);
  118. if (ret != (signed int)strlen(ctrl))
  119. ksft_exit_fail_msg("writing clear_refs failed\n");
  120. }
  121. bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len)
  122. {
  123. while (fgets(buf, len, fp)) {
  124. if (!strncmp(buf, pattern, strlen(pattern)))
  125. return true;
  126. }
  127. return false;
  128. }
  129. uint64_t read_pmd_pagesize(void)
  130. {
  131. int fd;
  132. char buf[20];
  133. ssize_t num_read;
  134. fd = open(PMD_SIZE_FILE_PATH, O_RDONLY);
  135. if (fd == -1)
  136. return 0;
  137. num_read = read(fd, buf, 19);
  138. if (num_read < 1) {
  139. close(fd);
  140. return 0;
  141. }
  142. buf[num_read] = '\0';
  143. close(fd);
  144. return strtoul(buf, NULL, 10);
  145. }
  146. unsigned long rss_anon(void)
  147. {
  148. unsigned long rss_anon = 0;
  149. FILE *fp;
  150. char buffer[MAX_LINE_LENGTH];
  151. fp = fopen(STATUS_FILE_PATH, "r");
  152. if (!fp)
  153. ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, STATUS_FILE_PATH);
  154. if (!check_for_pattern(fp, "RssAnon:", buffer, sizeof(buffer)))
  155. goto err_out;
  156. if (sscanf(buffer, "RssAnon:%10lu kB", &rss_anon) != 1)
  157. ksft_exit_fail_msg("Reading status error\n");
  158. err_out:
  159. fclose(fp);
  160. return rss_anon;
  161. }
  162. char *__get_smap_entry(void *addr, const char *pattern, char *buf, size_t len)
  163. {
  164. int ret;
  165. FILE *fp;
  166. char *entry = NULL;
  167. char addr_pattern[MAX_LINE_LENGTH];
  168. ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-",
  169. (unsigned long) addr);
  170. if (ret >= MAX_LINE_LENGTH)
  171. ksft_exit_fail_msg("%s: Pattern is too long\n", __func__);
  172. fp = fopen(SMAP_FILE_PATH, "r");
  173. if (!fp)
  174. ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, SMAP_FILE_PATH);
  175. if (!check_for_pattern(fp, addr_pattern, buf, len))
  176. goto err_out;
  177. /* Fetch the pattern in the same block */
  178. if (!check_for_pattern(fp, pattern, buf, len))
  179. goto err_out;
  180. /* Trim trailing newline */
  181. entry = strchr(buf, '\n');
  182. if (entry)
  183. *entry = '\0';
  184. entry = buf + strlen(pattern);
  185. err_out:
  186. fclose(fp);
  187. return entry;
  188. }
  189. bool __check_huge(void *addr, char *pattern, int nr_hpages,
  190. uint64_t hpage_size)
  191. {
  192. char buffer[MAX_LINE_LENGTH];
  193. uint64_t thp = -1;
  194. char *entry;
  195. entry = __get_smap_entry(addr, pattern, buffer, sizeof(buffer));
  196. if (!entry)
  197. goto err_out;
  198. if (sscanf(entry, "%9" SCNu64 " kB", &thp) != 1)
  199. ksft_exit_fail_msg("Reading smap error\n");
  200. err_out:
  201. return thp == (nr_hpages * (hpage_size >> 10));
  202. }
  203. bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size)
  204. {
  205. return __check_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size);
  206. }
  207. bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size)
  208. {
  209. return __check_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size);
  210. }
  211. bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size)
  212. {
  213. return __check_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size);
  214. }
  215. int64_t allocate_transhuge(void *ptr, int pagemap_fd)
  216. {
  217. uint64_t ent[2];
  218. /* drop pmd */
  219. if (mmap(ptr, HPAGE_SIZE, PROT_READ | PROT_WRITE,
  220. MAP_FIXED | MAP_ANONYMOUS |
  221. MAP_NORESERVE | MAP_PRIVATE, -1, 0) != ptr)
  222. ksft_exit_fail_msg("mmap transhuge\n");
  223. if (madvise(ptr, HPAGE_SIZE, MADV_HUGEPAGE))
  224. ksft_exit_fail_msg("MADV_HUGEPAGE\n");
  225. /* allocate transparent huge page */
  226. *(volatile void **)ptr = ptr;
  227. if (pread(pagemap_fd, ent, sizeof(ent),
  228. (uintptr_t)ptr >> (pshift() - 3)) != sizeof(ent))
  229. ksft_exit_fail_msg("read pagemap\n");
  230. if (PAGEMAP_PRESENT(ent[0]) && PAGEMAP_PRESENT(ent[1]) &&
  231. PAGEMAP_PFN(ent[0]) + 1 == PAGEMAP_PFN(ent[1]) &&
  232. !(PAGEMAP_PFN(ent[0]) & ((1 << (HPAGE_SHIFT - pshift())) - 1)))
  233. return PAGEMAP_PFN(ent[0]);
  234. return -1;
  235. }
  236. unsigned long default_huge_page_size(void)
  237. {
  238. unsigned long hps = 0;
  239. char *line = NULL;
  240. size_t linelen = 0;
  241. FILE *f = fopen("/proc/meminfo", "r");
  242. if (!f)
  243. return 0;
  244. while (getline(&line, &linelen, f) > 0) {
  245. if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) {
  246. hps <<= 10;
  247. break;
  248. }
  249. }
  250. free(line);
  251. fclose(f);
  252. return hps;
  253. }
  254. int detect_hugetlb_page_sizes(size_t sizes[], int max)
  255. {
  256. DIR *dir = opendir("/sys/kernel/mm/hugepages/");
  257. int count = 0;
  258. if (!dir)
  259. return 0;
  260. while (count < max) {
  261. struct dirent *entry = readdir(dir);
  262. size_t kb;
  263. if (!entry)
  264. break;
  265. if (entry->d_type != DT_DIR)
  266. continue;
  267. if (sscanf(entry->d_name, "hugepages-%zukB", &kb) != 1)
  268. continue;
  269. sizes[count++] = kb * 1024;
  270. ksft_print_msg("[INFO] detected hugetlb page size: %zu KiB\n",
  271. kb);
  272. }
  273. closedir(dir);
  274. return count;
  275. }
  276. int pageflags_get(unsigned long pfn, int kpageflags_fd, uint64_t *flags)
  277. {
  278. size_t count;
  279. count = pread(kpageflags_fd, flags, sizeof(*flags),
  280. pfn * sizeof(*flags));
  281. if (count != sizeof(*flags))
  282. return -1;
  283. return 0;
  284. }
  285. /* If `ioctls' non-NULL, the allowed ioctls will be returned into the var */
  286. int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len,
  287. bool miss, bool wp, bool minor, uint64_t *ioctls)
  288. {
  289. struct uffdio_register uffdio_register = { 0 };
  290. uint64_t mode = 0;
  291. int ret = 0;
  292. if (miss)
  293. mode |= UFFDIO_REGISTER_MODE_MISSING;
  294. if (wp)
  295. mode |= UFFDIO_REGISTER_MODE_WP;
  296. if (minor)
  297. mode |= UFFDIO_REGISTER_MODE_MINOR;
  298. uffdio_register.range.start = (unsigned long)addr;
  299. uffdio_register.range.len = len;
  300. uffdio_register.mode = mode;
  301. if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
  302. ret = -errno;
  303. else if (ioctls)
  304. *ioctls = uffdio_register.ioctls;
  305. return ret;
  306. }
  307. int uffd_register(int uffd, void *addr, uint64_t len,
  308. bool miss, bool wp, bool minor)
  309. {
  310. return uffd_register_with_ioctls(uffd, addr, len,
  311. miss, wp, minor, NULL);
  312. }
  313. int uffd_unregister(int uffd, void *addr, uint64_t len)
  314. {
  315. struct uffdio_range range = { .start = (uintptr_t)addr, .len = len };
  316. int ret = 0;
  317. if (ioctl(uffd, UFFDIO_UNREGISTER, &range) == -1)
  318. ret = -errno;
  319. return ret;
  320. }
  321. unsigned long get_free_hugepages(void)
  322. {
  323. unsigned long fhp = 0;
  324. char *line = NULL;
  325. size_t linelen = 0;
  326. FILE *f = fopen("/proc/meminfo", "r");
  327. if (!f)
  328. return fhp;
  329. while (getline(&line, &linelen, f) > 0) {
  330. if (sscanf(line, "HugePages_Free: %lu", &fhp) == 1)
  331. break;
  332. }
  333. free(line);
  334. fclose(f);
  335. return fhp;
  336. }
  337. static bool check_vmflag(void *addr, const char *flag)
  338. {
  339. char buffer[MAX_LINE_LENGTH];
  340. const char *flags;
  341. size_t flaglen;
  342. flags = __get_smap_entry(addr, "VmFlags:", buffer, sizeof(buffer));
  343. if (!flags)
  344. ksft_exit_fail_msg("%s: No VmFlags for %p\n", __func__, addr);
  345. while (true) {
  346. flags += strspn(flags, " ");
  347. flaglen = strcspn(flags, " ");
  348. if (!flaglen)
  349. return false;
  350. if (flaglen == strlen(flag) && !memcmp(flags, flag, flaglen))
  351. return true;
  352. flags += flaglen;
  353. }
  354. }
  355. bool check_vmflag_io(void *addr)
  356. {
  357. return check_vmflag(addr, "io");
  358. }
  359. bool check_vmflag_pfnmap(void *addr)
  360. {
  361. return check_vmflag(addr, "pf");
  362. }
  363. bool check_vmflag_guard(void *addr)
  364. {
  365. return check_vmflag(addr, "gu");
  366. }
  367. bool softdirty_supported(void)
  368. {
  369. char *addr;
  370. bool supported = false;
  371. const size_t pagesize = getpagesize();
  372. /* New mappings are expected to be marked with VM_SOFTDIRTY (sd). */
  373. addr = mmap(0, pagesize, PROT_READ | PROT_WRITE,
  374. MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  375. if (!addr)
  376. ksft_exit_fail_msg("mmap failed\n");
  377. supported = check_vmflag(addr, "sd");
  378. munmap(addr, pagesize);
  379. return supported;
  380. }
  381. /*
  382. * Open an fd at /proc/$pid/maps and configure procmap_out ready for
  383. * PROCMAP_QUERY query. Returns 0 on success, or an error code otherwise.
  384. */
  385. int open_procmap(pid_t pid, struct procmap_fd *procmap_out)
  386. {
  387. char path[256];
  388. int ret = 0;
  389. memset(procmap_out, '\0', sizeof(*procmap_out));
  390. sprintf(path, "/proc/%d/maps", pid);
  391. procmap_out->query.size = sizeof(procmap_out->query);
  392. procmap_out->fd = open(path, O_RDONLY);
  393. if (procmap_out->fd < 0)
  394. ret = -errno;
  395. return ret;
  396. }
  397. /* Perform PROCMAP_QUERY. Returns 0 on success, or an error code otherwise. */
  398. int query_procmap(struct procmap_fd *procmap)
  399. {
  400. int ret = 0;
  401. if (ioctl(procmap->fd, PROCMAP_QUERY, &procmap->query) == -1)
  402. ret = -errno;
  403. return ret;
  404. }
  405. /*
  406. * Try to find the VMA at specified address, returns true if found, false if not
  407. * found, and the test is failed if any other error occurs.
  408. *
  409. * On success, procmap->query is populated with the results.
  410. */
  411. bool find_vma_procmap(struct procmap_fd *procmap, void *address)
  412. {
  413. int err;
  414. procmap->query.query_flags = 0;
  415. procmap->query.query_addr = (unsigned long)address;
  416. err = query_procmap(procmap);
  417. if (!err)
  418. return true;
  419. if (err != -ENOENT)
  420. ksft_exit_fail_msg("%s: Error %d on ioctl(PROCMAP_QUERY)\n",
  421. __func__, err);
  422. return false;
  423. }
  424. /*
  425. * Close fd used by PROCMAP_QUERY mechanism. Returns 0 on success, or an error
  426. * code otherwise.
  427. */
  428. int close_procmap(struct procmap_fd *procmap)
  429. {
  430. return close(procmap->fd);
  431. }
  432. int write_sysfs(const char *file_path, unsigned long val)
  433. {
  434. FILE *f = fopen(file_path, "w");
  435. if (!f) {
  436. fprintf(stderr, "f %s\n", file_path);
  437. perror("fopen");
  438. return 1;
  439. }
  440. if (fprintf(f, "%lu", val) < 0) {
  441. perror("fprintf");
  442. fclose(f);
  443. return 1;
  444. }
  445. fclose(f);
  446. return 0;
  447. }
  448. int read_sysfs(const char *file_path, unsigned long *val)
  449. {
  450. FILE *f = fopen(file_path, "r");
  451. if (!f) {
  452. fprintf(stderr, "f %s\n", file_path);
  453. perror("fopen");
  454. return 1;
  455. }
  456. if (fscanf(f, "%lu", val) != 1) {
  457. perror("fscanf");
  458. fclose(f);
  459. return 1;
  460. }
  461. fclose(f);
  462. return 0;
  463. }
  464. void *sys_mremap(void *old_address, unsigned long old_size,
  465. unsigned long new_size, int flags, void *new_address)
  466. {
  467. return (void *)syscall(__NR_mremap, (unsigned long)old_address,
  468. old_size, new_size, flags,
  469. (unsigned long)new_address);
  470. }
  471. bool detect_huge_zeropage(void)
  472. {
  473. int fd = open("/sys/kernel/mm/transparent_hugepage/use_zero_page",
  474. O_RDONLY);
  475. bool enabled = 0;
  476. char buf[15];
  477. int ret;
  478. if (fd < 0)
  479. return 0;
  480. ret = pread(fd, buf, sizeof(buf), 0);
  481. if (ret > 0 && ret < sizeof(buf)) {
  482. buf[ret] = 0;
  483. if (strtoul(buf, NULL, 10) == 1)
  484. enabled = 1;
  485. }
  486. close(fd);
  487. return enabled;
  488. }
  489. long ksm_get_self_zero_pages(void)
  490. {
  491. int proc_self_ksm_stat_fd;
  492. char buf[200];
  493. char *substr_ksm_zero;
  494. size_t value_pos;
  495. ssize_t read_size;
  496. proc_self_ksm_stat_fd = open("/proc/self/ksm_stat", O_RDONLY);
  497. if (proc_self_ksm_stat_fd < 0)
  498. return -errno;
  499. read_size = pread(proc_self_ksm_stat_fd, buf, sizeof(buf) - 1, 0);
  500. close(proc_self_ksm_stat_fd);
  501. if (read_size < 0)
  502. return -errno;
  503. buf[read_size] = 0;
  504. substr_ksm_zero = strstr(buf, "ksm_zero_pages");
  505. if (!substr_ksm_zero)
  506. return 0;
  507. value_pos = strcspn(substr_ksm_zero, "0123456789");
  508. return strtol(substr_ksm_zero + value_pos, NULL, 10);
  509. }
  510. long ksm_get_self_merging_pages(void)
  511. {
  512. int proc_self_ksm_merging_pages_fd;
  513. char buf[10];
  514. ssize_t ret;
  515. proc_self_ksm_merging_pages_fd = open("/proc/self/ksm_merging_pages",
  516. O_RDONLY);
  517. if (proc_self_ksm_merging_pages_fd < 0)
  518. return -errno;
  519. ret = pread(proc_self_ksm_merging_pages_fd, buf, sizeof(buf) - 1, 0);
  520. close(proc_self_ksm_merging_pages_fd);
  521. if (ret <= 0)
  522. return -errno;
  523. buf[ret] = 0;
  524. return strtol(buf, NULL, 10);
  525. }
  526. long ksm_get_full_scans(void)
  527. {
  528. int ksm_full_scans_fd;
  529. char buf[10];
  530. ssize_t ret;
  531. ksm_full_scans_fd = open("/sys/kernel/mm/ksm/full_scans", O_RDONLY);
  532. if (ksm_full_scans_fd < 0)
  533. return -errno;
  534. ret = pread(ksm_full_scans_fd, buf, sizeof(buf) - 1, 0);
  535. close(ksm_full_scans_fd);
  536. if (ret <= 0)
  537. return -errno;
  538. buf[ret] = 0;
  539. return strtol(buf, NULL, 10);
  540. }
  541. int ksm_use_zero_pages(void)
  542. {
  543. int ksm_use_zero_pages_fd;
  544. ssize_t ret;
  545. ksm_use_zero_pages_fd = open("/sys/kernel/mm/ksm/use_zero_pages", O_RDWR);
  546. if (ksm_use_zero_pages_fd < 0)
  547. return -errno;
  548. ret = write(ksm_use_zero_pages_fd, "1", 1);
  549. close(ksm_use_zero_pages_fd);
  550. return ret == 1 ? 0 : -errno;
  551. }
  552. int ksm_start(void)
  553. {
  554. int ksm_fd;
  555. ssize_t ret;
  556. long start_scans, end_scans;
  557. ksm_fd = open("/sys/kernel/mm/ksm/run", O_RDWR);
  558. if (ksm_fd < 0)
  559. return -errno;
  560. /* Wait for two full scans such that any possible merging happened. */
  561. start_scans = ksm_get_full_scans();
  562. if (start_scans < 0) {
  563. close(ksm_fd);
  564. return start_scans;
  565. }
  566. ret = write(ksm_fd, "1", 1);
  567. close(ksm_fd);
  568. if (ret != 1)
  569. return -errno;
  570. do {
  571. end_scans = ksm_get_full_scans();
  572. if (end_scans < 0)
  573. return end_scans;
  574. } while (end_scans < start_scans + 2);
  575. return 0;
  576. }
  577. int ksm_stop(void)
  578. {
  579. int ksm_fd;
  580. ssize_t ret;
  581. ksm_fd = open("/sys/kernel/mm/ksm/run", O_RDWR);
  582. if (ksm_fd < 0)
  583. return -errno;
  584. ret = write(ksm_fd, "2", 1);
  585. close(ksm_fd);
  586. return ret == 1 ? 0 : -errno;
  587. }
  588. int get_hardware_corrupted_size(unsigned long *val)
  589. {
  590. unsigned long size;
  591. char *line = NULL;
  592. size_t linelen = 0;
  593. FILE *f = fopen("/proc/meminfo", "r");
  594. int ret = -1;
  595. if (!f)
  596. return ret;
  597. while (getline(&line, &linelen, f) > 0) {
  598. if (sscanf(line, "HardwareCorrupted: %12lu kB", &size) == 1) {
  599. *val = size;
  600. ret = 0;
  601. break;
  602. }
  603. }
  604. free(line);
  605. fclose(f);
  606. return ret;
  607. }
  608. int unpoison_memory(unsigned long pfn)
  609. {
  610. int unpoison_fd, len;
  611. char buf[32];
  612. ssize_t ret;
  613. unpoison_fd = open("/sys/kernel/debug/hwpoison/unpoison-pfn", O_WRONLY);
  614. if (unpoison_fd < 0)
  615. return -errno;
  616. len = sprintf(buf, "0x%lx\n", pfn);
  617. ret = write(unpoison_fd, buf, len);
  618. close(unpoison_fd);
  619. return ret > 0 ? 0 : -errno;
  620. }