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