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