Commit 1d46598b authored by Konstantin Khlebnikov's avatar Konstantin Khlebnikov Committed by Linus Torvalds

tools/vm/page-types.c: catch sigbus if raced with truncate

Recently added page-cache dumping is known to be a little bit racy.
But after race with truncate it just dies due to unhandled SIGBUS
when it tries to poke pages beyond the new end of file.
This patch adds handler for SIGBUS which skips the rest of the file.
Signed-off-by: default avatarKonstantin Khlebnikov <koct9i@gmail.com>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d2705064
...@@ -32,6 +32,8 @@ ...@@ -32,6 +32,8 @@
#include <assert.h> #include <assert.h>
#include <ftw.h> #include <ftw.h>
#include <time.h> #include <time.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/errno.h> #include <sys/errno.h>
#include <sys/fcntl.h> #include <sys/fcntl.h>
...@@ -824,21 +826,38 @@ static void show_file(const char *name, const struct stat *st) ...@@ -824,21 +826,38 @@ static void show_file(const char *name, const struct stat *st)
atime, now - st->st_atime); 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) static void walk_file(const char *name, const struct stat *st)
{ {
uint8_t vec[PAGEMAP_BATCH]; uint8_t vec[PAGEMAP_BATCH];
uint64_t buf[PAGEMAP_BATCH], flags; uint64_t buf[PAGEMAP_BATCH], flags;
unsigned long nr_pages, pfn, i; unsigned long nr_pages, pfn, i;
off_t off, end = st->st_size;
int fd; int fd;
off_t off;
ssize_t len; ssize_t len;
void *ptr; void *ptr;
int first = 1; int first = 1;
fd = checked_open(name, O_RDONLY|O_NOATIME|O_NOFOLLOW); fd = checked_open(name, O_RDONLY|O_NOATIME|O_NOFOLLOW);
for (off = 0; off < st->st_size; off += len) { for (off = 0; off < end; off += len) {
nr_pages = (st->st_size - off + page_size - 1) / page_size; nr_pages = (end - off + page_size - 1) / page_size;
if (nr_pages > PAGEMAP_BATCH) if (nr_pages > PAGEMAP_BATCH)
nr_pages = PAGEMAP_BATCH; nr_pages = PAGEMAP_BATCH;
len = nr_pages * page_size; len = nr_pages * page_size;
...@@ -855,11 +874,19 @@ static void walk_file(const char *name, const struct stat *st) ...@@ -855,11 +874,19 @@ static void walk_file(const char *name, const struct stat *st)
if (madvise(ptr, len, MADV_RANDOM)) if (madvise(ptr, len, MADV_RANDOM))
fatal("madvice failed: %s", name); 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 */ /* populate ptes */
for (i = 0; i < nr_pages ; i++) { for (i = 0; i < nr_pages ; i++) {
if (vec[i] & 1) if (vec[i] & 1)
(void)*(volatile int *)(ptr + i * page_size); (void)*(volatile int *)(ptr + i * page_size);
} }
got_sigbus:
/* turn off harvesting reference bits */ /* turn off harvesting reference bits */
if (madvise(ptr, len, MADV_SEQUENTIAL)) if (madvise(ptr, len, MADV_SEQUENTIAL))
...@@ -910,6 +937,7 @@ static void walk_page_cache(void) ...@@ -910,6 +937,7 @@ static void walk_page_cache(void)
kpageflags_fd = checked_open(PROC_KPAGEFLAGS, O_RDONLY); kpageflags_fd = checked_open(PROC_KPAGEFLAGS, O_RDONLY);
pagemap_fd = checked_open("/proc/self/pagemap", O_RDONLY); pagemap_fd = checked_open("/proc/self/pagemap", O_RDONLY);
sigaction(SIGBUS, &sigbus_action, NULL);
if (stat(opt_file, &st)) if (stat(opt_file, &st))
fatal("stat failed: %s\n", opt_file); fatal("stat failed: %s\n", opt_file);
...@@ -925,6 +953,7 @@ static void walk_page_cache(void) ...@@ -925,6 +953,7 @@ static void walk_page_cache(void)
close(kpageflags_fd); close(kpageflags_fd);
close(pagemap_fd); close(pagemap_fd);
signal(SIGBUS, SIG_DFL);
} }
static void parse_file(const char *name) static void parse_file(const char *name)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment