Skip to content

Commit

Permalink
recovery: Fork a process for fuse when sideloading from SD card.
Browse files Browse the repository at this point in the history
For applying update from SD card, we used to use a thread to serve the
file with fuse. Since accessing through fuse involves going from kernel
to userspace to kernel, it may run into deadlock (e.g. for mmap_sem)
when a page fault occurs. Switch to using a process instead.

Bug: 23783099
Bug: 26313124
Change-Id: Iac0f55b1bdb078cadb520cfe1133e70fbb26eadd
  • Loading branch information
Tao Bao committed Jan 14, 2016
1 parent ab9db52 commit cdcf28f
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 69 deletions.
74 changes: 11 additions & 63 deletions fuse_sdcard_provider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <unistd.h>
Expand Down Expand Up @@ -60,81 +59,30 @@ static void close_file(void* cookie) {
close(fd->fd);
}

struct token {
pthread_t th;
const char* path;
int result;
};

static void* run_sdcard_fuse(void* cookie) {
token* t = reinterpret_cast<token*>(cookie);

bool start_sdcard_fuse(const char* path) {
struct stat sb;
if (stat(t->path, &sb) < 0) {
fprintf(stderr, "failed to stat %s: %s\n", t->path, strerror(errno));
t->result = -1;
return NULL;
if (stat(path, &sb) == -1) {
fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno));
return false;
}

struct file_data fd;
struct provider_vtab vtab;

fd.fd = open(t->path, O_RDONLY);
if (fd.fd < 0) {
fprintf(stderr, "failed to open %s: %s\n", t->path, strerror(errno));
t->result = -1;
return NULL;
file_data fd;
fd.fd = open(path, O_RDONLY);
if (fd.fd == -1) {
fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
return false;
}
fd.file_size = sb.st_size;
fd.block_size = 65536;

provider_vtab vtab;
vtab.read_block = read_block_file;
vtab.close = close_file;

t->result = run_fuse_sideload(&vtab, &fd, fd.file_size, fd.block_size);
return NULL;
}

// How long (in seconds) we wait for the fuse-provided package file to
// appear, before timing out.
#define SDCARD_INSTALL_TIMEOUT 10

void* start_sdcard_fuse(const char* path) {
token* t = new token;

t->path = path;
pthread_create(&(t->th), NULL, run_sdcard_fuse, t);

struct stat st;
int i;
for (i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) {
sleep(1);
continue;
} else {
return NULL;
}
}
}

// The installation process expects to find the sdcard unmounted.
// Unmount it with MNT_DETACH so that our open file continues to
// work but new references see it as unmounted.
umount2("/sdcard", MNT_DETACH);

return t;
}

void finish_sdcard_fuse(void* cookie) {
if (cookie == NULL) return;
token* t = reinterpret_cast<token*>(cookie);

// Calling stat() on this magic filename signals the fuse
// filesystem to shut down.
struct stat st;
stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);

pthread_join(t->th, NULL);
delete t;
return run_fuse_sideload(&vtab, &fd, fd.file_size, fd.block_size) == 0;
}
3 changes: 1 addition & 2 deletions fuse_sdcard_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#ifndef __FUSE_SDCARD_PROVIDER_H
#define __FUSE_SDCARD_PROVIDER_H

void* start_sdcard_fuse(const char* path);
void finish_sdcard_fuse(void* token);
bool start_sdcard_fuse(const char* path);

#endif
61 changes: 57 additions & 4 deletions recovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <sys/klog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

Expand Down Expand Up @@ -833,6 +834,10 @@ static void choose_recovery_file(Device* device) {
}
}

// How long (in seconds) we wait for the fuse-provided package file to
// appear, before timing out.
#define SDCARD_INSTALL_TIMEOUT 10

static int apply_from_sdcard(Device* device, bool* wipe_cache) {
modified_flash = true;

Expand All @@ -850,14 +855,62 @@ static int apply_from_sdcard(Device* device, bool* wipe_cache) {

ui->Print("\n-- Install %s ...\n", path);
set_sdcard_update_bootloader_message();
void* token = start_sdcard_fuse(path);

int status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache,
// We used to use fuse in a thread as opposed to a process. Since accessing
// through fuse involves going from kernel to userspace to kernel, it leads
// to deadlock when a page fault occurs. (Bug: 26313124)
pid_t child;
if ((child = fork()) == 0) {
bool status = start_sdcard_fuse(path);

_exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
}

// FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child
// process is ready.
int result = INSTALL_ERROR;
int status;
bool waited = false;
for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
if (waitpid(child, &status, WNOHANG) == -1) {
result = INSTALL_ERROR;
waited = true;
break;
}

struct stat sb;
if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &sb) == -1) {
if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) {
sleep(1);
continue;
} else {
LOGE("Timed out waiting for the fuse-provided package.\n");
result = INSTALL_ERROR;
kill(child, SIGKILL);
break;
}
}

result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache,
TEMPORARY_INSTALL_FILE, false);
break;
}

if (!waited) {
// Calling stat() on this magic filename signals the fuse
// filesystem to shut down.
struct stat sb;
stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &sb);

waitpid(child, &status, 0);
}

if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
LOGE("Error exit from the fuse process: %d\n", WEXITSTATUS(status));
}

finish_sdcard_fuse(token);
ensure_path_unmounted(SDCARD_ROOT);
return status;
return result;
}

// Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION
Expand Down

0 comments on commit cdcf28f

Please sign in to comment.