Skip to content

Commit

Permalink
tools/vm/page-types.c: catch sigbus if raced with truncate
Browse files Browse the repository at this point in the history
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: Konstantin Khlebnikov <[email protected]>
Cc: Naoya Horiguchi <[email protected]>
Cc: <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
koct9i authored and torvalds committed Jun 4, 2014
1 parent d270506 commit 1d46598
Showing 1 changed file with 32 additions and 3 deletions.
35 changes: 32 additions & 3 deletions tools/vm/page-types.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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;
Expand All @@ -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))
Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand Down

0 comments on commit 1d46598

Please sign in to comment.