init: poll in first stage mount if required devices are not found
First stage mount in init currently attempts to regenerate uevents for specific devices to create the corresponding dev nodes. However, this is racy as first stage mount happens early in the boot process and it's possible that some of these devices have not yet been created by the kernel. To fix this issue, init will poll on the uevent socket for up to 10 seconds waiting for the kernel to create the required device. It will return false and panic if this 10 second timeout passes. Note that the same uevent socket is used in the uevent regeneration and the polling code, so there is no race if the device is created after the uevent regeneration and before polling starts; the first poll will pick up the device. Bug: 62681642 Bug: 62682821 Test: Boot bullhead Test: Boot sailfish Test: Boot hikey + hotplug/unplug sdcard Merged-In: I4a6ff043eb7115b729ca4954ebc6c9e000132993 Change-Id: I4a6ff043eb7115b729ca4954ebc6c9e000132993
This commit is contained in:
parent
d43b615216
commit
5f4e8eac8a
4 changed files with 96 additions and 47 deletions
|
|
@ -19,6 +19,7 @@
|
|||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <libgen.h>
|
||||
#include <poll.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -1028,6 +1029,37 @@ void device_close() {
|
|||
selinux_status_close();
|
||||
}
|
||||
|
||||
int get_device_fd() {
|
||||
return device_fd;
|
||||
void device_poll(const coldboot_callback& callback,
|
||||
const std::optional<std::chrono::milliseconds> relative_timeout) {
|
||||
using namespace std::chrono;
|
||||
|
||||
pollfd ufd;
|
||||
ufd.events = POLLIN;
|
||||
ufd.fd = device_fd;
|
||||
|
||||
auto start_time = steady_clock::now();
|
||||
|
||||
while (true) {
|
||||
ufd.revents = 0;
|
||||
|
||||
int timeout_ms = -1;
|
||||
if (relative_timeout) {
|
||||
auto now = steady_clock::now();
|
||||
auto time_elapsed = duration_cast<milliseconds>(now - start_time);
|
||||
if (time_elapsed > *relative_timeout) return;
|
||||
|
||||
auto remaining_timeout = *relative_timeout - time_elapsed;
|
||||
timeout_ms = remaining_timeout.count();
|
||||
}
|
||||
|
||||
int nr = poll(&ufd, 1, timeout_ms);
|
||||
if (nr == 0) return;
|
||||
if (nr < 0) {
|
||||
continue;
|
||||
}
|
||||
if (ufd.revents & POLLIN) {
|
||||
auto ret = handle_device_fd(callback);
|
||||
if (should_stop_coldboot(ret)) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,12 @@
|
|||
#ifndef _INIT_DEVICES_H
|
||||
#define _INIT_DEVICES_H
|
||||
|
||||
#include <functional>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
enum coldboot_action_t {
|
||||
// coldboot continues without creating the device for the uevent
|
||||
COLDBOOT_CONTINUE = 0,
|
||||
|
|
@ -53,8 +56,10 @@ extern int add_dev_perms(const char *name, const char *attr,
|
|||
mode_t perm, unsigned int uid,
|
||||
unsigned int gid, unsigned short prefix,
|
||||
unsigned short wildcard);
|
||||
int get_device_fd();
|
||||
|
||||
char** get_block_device_symlinks(struct uevent* uevent);
|
||||
|
||||
void device_poll(const coldboot_callback& callback = nullptr,
|
||||
const std::optional<std::chrono::milliseconds> relative_timeout = {});
|
||||
|
||||
#endif /* _INIT_DEVICES_H */
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
|
@ -33,6 +34,8 @@
|
|||
#include "fs_mgr_avb.h"
|
||||
#include "util.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
// Class Declarations
|
||||
// ------------------
|
||||
class FirstStageMount {
|
||||
|
|
@ -47,8 +50,8 @@ class FirstStageMount {
|
|||
bool InitDevices();
|
||||
|
||||
protected:
|
||||
void InitRequiredDevices();
|
||||
void InitVerityDevice(const std::string& verity_device);
|
||||
bool InitRequiredDevices();
|
||||
bool InitVerityDevice(const std::string& verity_device);
|
||||
bool MountPartitions();
|
||||
|
||||
virtual coldboot_action_t ColdbootCallback(uevent* uevent);
|
||||
|
|
@ -139,42 +142,53 @@ bool FirstStageMount::DoFirstStageMount() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool FirstStageMount::InitDevices() {
|
||||
if (!GetRequiredDevices()) return false;
|
||||
|
||||
InitRequiredDevices();
|
||||
|
||||
// InitRequiredDevices() will remove found partitions from required_devices_partition_names_.
|
||||
// So if it isn't empty here, it means some partitions are not found.
|
||||
if (!required_devices_partition_names_.empty()) {
|
||||
LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
|
||||
<< android::base::Join(required_devices_partition_names_, ", ");
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
bool FirstStageMount::InitDevices() { return GetRequiredDevices() && InitRequiredDevices(); }
|
||||
|
||||
// Creates devices with uevent->partition_name matching one in the member variable
|
||||
// required_devices_partition_names_. Found partitions will then be removed from it
|
||||
// for the subsequent member function to check which devices are NOT created.
|
||||
void FirstStageMount::InitRequiredDevices() {
|
||||
bool FirstStageMount::InitRequiredDevices() {
|
||||
if (required_devices_partition_names_.empty()) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (need_dm_verity_) {
|
||||
const std::string dm_path = "/devices/virtual/misc/device-mapper";
|
||||
device_init(("/sys" + dm_path).c_str(), [&dm_path](uevent* uevent) -> coldboot_action_t {
|
||||
if (uevent->path && uevent->path == dm_path) return COLDBOOT_STOP;
|
||||
bool found = false;
|
||||
auto dm_callback = [&dm_path, &found](uevent* uevent) -> coldboot_action_t {
|
||||
if (uevent->path && uevent->path == dm_path) {
|
||||
found = true;
|
||||
return COLDBOOT_STOP;
|
||||
}
|
||||
return COLDBOOT_CONTINUE; // dm_path not found, continue to find it.
|
||||
});
|
||||
};
|
||||
device_init(("/sys" + dm_path).c_str(), dm_callback);
|
||||
if (!found) {
|
||||
device_poll(dm_callback, 10s);
|
||||
}
|
||||
if (!found) {
|
||||
LOG(ERROR) << "device-mapper device not found";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
device_init(nullptr,
|
||||
[this](uevent* uevent) -> coldboot_action_t { return ColdbootCallback(uevent); });
|
||||
auto uevent_callback = [this](uevent* uevent) -> coldboot_action_t {
|
||||
return ColdbootCallback(uevent);
|
||||
};
|
||||
|
||||
device_init(nullptr, uevent_callback);
|
||||
if (!required_devices_partition_names_.empty()) {
|
||||
device_poll(uevent_callback, 10s);
|
||||
}
|
||||
|
||||
if (!required_devices_partition_names_.empty()) {
|
||||
LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found: "
|
||||
<< android::base::Join(required_devices_partition_names_, ", ");
|
||||
return false;
|
||||
}
|
||||
|
||||
device_close();
|
||||
return true;
|
||||
}
|
||||
|
||||
coldboot_action_t FirstStageMount::ColdbootCallback(uevent* uevent) {
|
||||
|
|
@ -203,18 +217,30 @@ coldboot_action_t FirstStageMount::ColdbootCallback(uevent* uevent) {
|
|||
}
|
||||
|
||||
// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
|
||||
void FirstStageMount::InitVerityDevice(const std::string& verity_device) {
|
||||
bool FirstStageMount::InitVerityDevice(const std::string& verity_device) {
|
||||
const std::string device_name(basename(verity_device.c_str()));
|
||||
const std::string syspath = "/sys/block/" + device_name;
|
||||
bool found = false;
|
||||
|
||||
device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
|
||||
auto verity_callback = [&](uevent* uevent) -> coldboot_action_t {
|
||||
if (uevent->device_name && uevent->device_name == device_name) {
|
||||
LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
|
||||
found = true;
|
||||
return COLDBOOT_STOP;
|
||||
}
|
||||
return COLDBOOT_CONTINUE;
|
||||
});
|
||||
};
|
||||
|
||||
device_init(syspath.c_str(), verity_callback);
|
||||
if (!found) {
|
||||
device_poll(verity_callback, 10s);
|
||||
}
|
||||
if (!found) {
|
||||
LOG(ERROR) << "dm-verity device not found";
|
||||
return false;
|
||||
}
|
||||
device_close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FirstStageMount::MountPartitions() {
|
||||
|
|
@ -280,7 +306,7 @@ bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
|
|||
} else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
|
||||
// The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
|
||||
// Needs to create it because ueventd isn't started in init first stage.
|
||||
InitVerityDevice(fstab_rec->blk_device);
|
||||
return InitVerityDevice(fstab_rec->blk_device);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <poll.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -76,20 +75,7 @@ int ueventd_main(int argc, char **argv)
|
|||
|
||||
device_init();
|
||||
|
||||
pollfd ufd;
|
||||
ufd.events = POLLIN;
|
||||
ufd.fd = get_device_fd();
|
||||
|
||||
while (true) {
|
||||
ufd.revents = 0;
|
||||
int nr = poll(&ufd, 1, -1);
|
||||
if (nr <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (ufd.revents & POLLIN) {
|
||||
handle_device_fd();
|
||||
}
|
||||
}
|
||||
device_poll();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue