/* * bash-shared-mapping.c - Andrew Morton * * Create a huge and holey shared mapping, then conduct multithreaded * write() I/O on some of it, while truncating and expanding it. General * idea is to try to force pageout activity into the file while the kernel * is writing to and truncating the file. We also perform pageout of areas * which are subject to write() and vice versa. All sorts of stuff. * * It is good to run a concurrent task which uses heaps of memory, to force * pageouts. * * A good combination on a 1gigabyte machine is: * * bash-shared-mapping -t5 foo 1000000000 & * while true * do * usemem 1000 * done */ #include #include #include #include #include #include #include #include #include #include #include //ssize_t pwrite(unsigned int fd, const char * buf, size_t count, loff_t pos); long ftruncate64(unsigned int fd, loff_t length); #ifndef O_LARGEFILE #define O_LARGEFILE 0100000 #endif int verbose; char *progname; loff_t size; int fd; char *filename; void *mapped_mem; int got_sigbus; loff_t sigbus_offset; int ntasks = 1; int niters = -1; void open_file() { fd = open(filename, O_RDWR|O_LARGEFILE|O_TRUNC|O_CREAT, 0666); if (fd < 0) { fprintf(stderr, "%s: Cannot open `%s': %s\n", progname, filename, strerror(errno)); exit(1); } } ssize_t my_pwrite(unsigned int fd, const char * buf, size_t count, loff_t pos) { if (pos > 2000000000) printf("DRAT\n"); return pwrite(fd, buf, count, pos); } void mmap_file(void) { mapped_mem = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (mapped_mem == MAP_FAILED) { perror("mmap"); exit(1); } } void stretch_file(loff_t size) { long c = 1; int ret; if (verbose) printf("stretch file to %Ld\n", size); if ((ret = my_pwrite(fd, (const char *)&c, sizeof(c), size - sizeof(c))) != sizeof(c)) { fprintf(stderr, "%s: my_pwrite returned %d\n", __FUNCTION__, ret); perror("my_pwrite"); exit(1); } } /* * If another thread truncates the file, we get SIGBUS. * Who cares :) */ void sigbus(int sig) { long c = 1; struct stat statbuf; int ret; loff_t new_len = sigbus_offset + sizeof(c); if (verbose) printf("sigbus - stretch to %Ld\n", new_len); got_sigbus = 1; /* Instantiate the file up to the sigbus address */ if ((ret = my_pwrite(fd, (const char *)&c, sizeof(c), sigbus_offset)) != sizeof(c)) { fprintf(stderr, "%s: my_pwrite returned %d\n",__FUNCTION__, ret); perror("sigbus my_pwrite"); } if (fstat(fd, &statbuf)) { perror("fstat"); } if (verbose) printf("length is now %ld\n", statbuf.st_size); } void set_sigbus_offset(loff_t offset) { sigbus_offset = offset; } void install_signal_handler() { signal(SIGBUS, sigbus); } void dirty_pages(loff_t offset, loff_t amount) { long *p, val; loff_t idx; if (offset + amount > size) amount = size - offset; if (verbose) printf("dirty %Ld bytes at %Ld\n", amount, offset); val = 0; p = mapped_mem; amount /= sizeof(*p); offset /= sizeof(*p); got_sigbus = 0; for (idx = 0; idx < amount; idx++) { set_sigbus_offset((idx + offset) * sizeof(*p)); p[idx + offset] = val++; if (got_sigbus) { if (verbose) printf("dirty_pages: sigbus\n"); break; } } } void write_stuff(loff_t from, loff_t to, loff_t amount) { int ret; if (from + amount > size) amount = size - from; if (to + amount > size) amount = size - to; if (verbose) printf("my_pwrite %Ld bytes from %Ld to %Ld\n", amount, from, to); if ((ret = my_pwrite(fd, (char *)mapped_mem + from, amount, to)) != amount) { if (verbose) printf("%s: my_pwrite returned %d, not %Ld\n",__FUNCTION__, ret, amount); if (errno == EFAULT) { /* * It was unmapped under us */ if (verbose) printf("my_pwrite: EFAULT\n"); } else if (ret < 0) { perror("my_pwrite"); exit(1); } } } #if 1 loff_t rand_of(loff_t arg) { double dret = arg; loff_t ret; dret *= drand48(); ret = dret; #if 0 if (ret < 0 || ret > 0x40000000) printf("I goofed: %Ld\n", ret); #endif return ret; } #else loff_t rand_of(loff_t arg) { return rand() % arg; } #endif void usage(void) { fprintf(stderr, "Usage: %s [-v] [-nN] [-tN] filename size-in-bytes\n", progname); fprintf(stderr, " -v: Verbose\n"); fprintf(stderr, " -nN: Run N iterations\n"); fprintf(stderr, " -tN: Run N tasks\n"); exit(1); } int main(int argc, char *argv[]) { int c; int i; int niters = -1; progname = argv[0]; while ((c = getopt(argc, argv, "vn:t:")) != -1) { switch (c) { case 'n': niters = strtol(optarg, NULL, 10); break; case 't': ntasks = strtol(optarg, NULL, 10); break; case 'v': verbose++; break; } } if (optind == argc) usage(); filename = argv[optind++]; if (optind == argc) usage(); sscanf(argv[optind++], "%Ld", &size); if (optind != argc) usage(); if (size < 10) size = 10; open_file(); for (i = 1; i < ntasks; i++) { if (fork() == 0) break; } stretch_file(size); mmap_file(); install_signal_handler(); srand48(time(0) * getpid()); srand(10 * getpid()); while (niters--) { dirty_pages(rand_of(size), rand_of(size)); write_stuff(rand_of(size), rand_of(size), rand_of(size)); ftruncate64(fd, rand_of(size)); stretch_file(rand_of(size) + 10); } exit(0); }