tools/vm/page-types.c: catch sigbus if raced with truncate
[firefly-linux-kernel-4.4.55.git] / tools / vm / page-types.c
index 05654f5e48d5f527939e2dfe62d98310f3831edb..c4d6d2e20e0debb8ea3bb8638fb6fb0a757014f8 100644 (file)
@@ -32,6 +32,8 @@
 #include <assert.h>
 #include <ftw.h>
 #include <time.h>
+#include <setjmp.h>
+#include <signal.h>
 #include <sys/types.h>
 #include <sys/errno.h>
 #include <sys/fcntl.h>
@@ -824,21 +826,38 @@ static void show_file(const char *name, const struct stat *st)
                        atime, now - st->st_atime);
 }
 
+static sigjmp_buf sigbus_jmp;
+
+static void * volatile sigbus_addr;
+
+static void sigbus_handler(int sig, siginfo_t *info, void *ucontex)
+{
+       (void)sig;
+       (void)ucontex;
+       sigbus_addr = info ? info->si_addr : NULL;
+       siglongjmp(sigbus_jmp, 1);
+}
+
+static struct sigaction sigbus_action = {
+       .sa_sigaction = sigbus_handler,
+       .sa_flags = SA_SIGINFO,
+};
+
 static void walk_file(const char *name, const struct stat *st)
 {
        uint8_t vec[PAGEMAP_BATCH];
        uint64_t buf[PAGEMAP_BATCH], flags;
        unsigned long nr_pages, pfn, i;
+       off_t off, end = st->st_size;
        int fd;
-       off_t off;
        ssize_t len;
        void *ptr;
        int first = 1;
 
        fd = checked_open(name, O_RDONLY|O_NOATIME|O_NOFOLLOW);
 
-       for (off = 0; off < st->st_size; off += len) {
-               nr_pages = (st->st_size - off + page_size - 1) / page_size;
+       for (off = 0; off < end; off += len) {
+               nr_pages = (end - off + page_size - 1) / page_size;
                if (nr_pages > PAGEMAP_BATCH)
                        nr_pages = PAGEMAP_BATCH;
                len = nr_pages * page_size;
@@ -855,11 +874,19 @@ static void walk_file(const char *name, const struct stat *st)
                if (madvise(ptr, len, MADV_RANDOM))
                        fatal("madvice failed: %s", name);
 
+               if (sigsetjmp(sigbus_jmp, 1)) {
+                       end = off + sigbus_addr ? sigbus_addr - ptr : 0;
+                       fprintf(stderr, "got sigbus at offset %lld: %s\n",
+                                       (long long)end, name);
+                       goto got_sigbus;
+               }
+
                /* populate ptes */
                for (i = 0; i < nr_pages ; i++) {
                        if (vec[i] & 1)
                                (void)*(volatile int *)(ptr + i * page_size);
                }
+got_sigbus:
 
                /* turn off harvesting reference bits */
                if (madvise(ptr, len, MADV_SEQUENTIAL))
@@ -910,6 +937,7 @@ static void walk_page_cache(void)
 
        kpageflags_fd = checked_open(PROC_KPAGEFLAGS, O_RDONLY);
        pagemap_fd = checked_open("/proc/self/pagemap", O_RDONLY);
+       sigaction(SIGBUS, &sigbus_action, NULL);
 
        if (stat(opt_file, &st))
                fatal("stat failed: %s\n", opt_file);
@@ -925,6 +953,7 @@ static void walk_page_cache(void)
 
        close(kpageflags_fd);
        close(pagemap_fd);
+       signal(SIGBUS, SIG_DFL);
 }
 
 static void parse_file(const char *name)