Merge "init: poll in first stage mount if required devices are not found"

am: 20e9312344

Change-Id: I3055e999ff976662da18818756fc6ed07b09f360
This commit is contained in:
Tom Cherry 2017-06-20 21:32:04 +00:00 committed by android-build-merger
commit 492a595091
4 changed files with 120 additions and 73 deletions

View file

@ -19,6 +19,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <chrono>
#include <memory> #include <memory>
#include <set> #include <set>
#include <string> #include <string>
@ -35,6 +36,8 @@
#include "uevent_listener.h" #include "uevent_listener.h"
#include "util.h" #include "util.h"
using namespace std::chrono_literals;
// Class Declarations // Class Declarations
// ------------------ // ------------------
class FirstStageMount { class FirstStageMount {
@ -49,11 +52,11 @@ class FirstStageMount {
bool InitDevices(); bool InitDevices();
protected: protected:
void InitRequiredDevices(); bool InitRequiredDevices();
void InitVerityDevice(const std::string& verity_device); bool InitVerityDevice(const std::string& verity_device);
bool MountPartitions(); bool MountPartitions();
virtual RegenerationAction UeventCallback(const Uevent& uevent); virtual ListenerAction UeventCallback(const Uevent& uevent);
// Pure virtual functions. // Pure virtual functions.
virtual bool GetRequiredDevices() = 0; virtual bool GetRequiredDevices() = 0;
@ -86,7 +89,7 @@ class FirstStageMountVBootV2 : public FirstStageMount {
~FirstStageMountVBootV2() override = default; ~FirstStageMountVBootV2() override = default;
protected: protected:
RegenerationAction UeventCallback(const Uevent& uevent) override; ListenerAction UeventCallback(const Uevent& uevent) override;
bool GetRequiredDevices() override; bool GetRequiredDevices() override;
bool SetUpDmVerity(fstab_rec* fstab_rec) override; bool SetUpDmVerity(fstab_rec* fstab_rec) override;
bool InitAvbHandle(); bool InitAvbHandle();
@ -141,49 +144,60 @@ bool FirstStageMount::DoFirstStageMount() {
} }
bool FirstStageMount::InitDevices() { bool FirstStageMount::InitDevices() {
if (!GetRequiredDevices()) return false; return GetRequiredDevices() && InitRequiredDevices();
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;
}
} }
// Creates devices with uevent->partition_name matching one in the member variable // Creates devices with uevent->partition_name matching one in the member variable
// required_devices_partition_names_. Found partitions will then be removed from it // required_devices_partition_names_. Found partitions will then be removed from it
// for the subsequent member function to check which devices are NOT created. // for the subsequent member function to check which devices are NOT created.
void FirstStageMount::InitRequiredDevices() { bool FirstStageMount::InitRequiredDevices() {
if (required_devices_partition_names_.empty()) { if (required_devices_partition_names_.empty()) {
return; return true;
} }
if (need_dm_verity_) { if (need_dm_verity_) {
const std::string dm_path = "/devices/virtual/misc/device-mapper"; const std::string dm_path = "/devices/virtual/misc/device-mapper";
uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, bool found = false;
[this, &dm_path](const Uevent& uevent) { auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
if (uevent.path == dm_path) { if (uevent.path == dm_path) {
device_handler_.HandleDeviceEvent(uevent); device_handler_.HandleDeviceEvent(uevent);
return RegenerationAction::kStop; found = true;
} return ListenerAction::kStop;
return RegenerationAction::kContinue; }
}); return ListenerAction::kContinue;
};
uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
if (!found) {
uevent_listener_.Poll(dm_callback, 10s);
}
if (!found) {
LOG(ERROR) << "device-mapper device not found";
return false;
}
} }
uevent_listener_.RegenerateUevents( auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
[this](const Uevent& uevent) { return UeventCallback(uevent); }); uevent_listener_.RegenerateUevents(uevent_callback);
// UeventCallback() 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()) {
uevent_listener_.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;
}
return true;
} }
RegenerationAction FirstStageMount::UeventCallback(const Uevent& uevent) { ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent) {
// Ignores everything that is not a block device. // Ignores everything that is not a block device.
if (uevent.subsystem != "block") { if (uevent.subsystem != "block") {
return RegenerationAction::kContinue; return ListenerAction::kContinue;
} }
if (!uevent.partition_name.empty()) { if (!uevent.partition_name.empty()) {
@ -192,34 +206,46 @@ RegenerationAction FirstStageMount::UeventCallback(const Uevent& uevent) {
// suffix when A/B is used. // suffix when A/B is used.
auto iter = required_devices_partition_names_.find(uevent.partition_name); auto iter = required_devices_partition_names_.find(uevent.partition_name);
if (iter != required_devices_partition_names_.end()) { if (iter != required_devices_partition_names_.end()) {
LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter; LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
required_devices_partition_names_.erase(iter); required_devices_partition_names_.erase(iter);
device_handler_.HandleDeviceEvent(uevent); device_handler_.HandleDeviceEvent(uevent);
if (required_devices_partition_names_.empty()) { if (required_devices_partition_names_.empty()) {
return RegenerationAction::kStop; return ListenerAction::kStop;
} else { } else {
return RegenerationAction::kContinue; return ListenerAction::kContinue;
} }
} }
} }
// Not found a partition or find an unneeded partition, continue to find others. // Not found a partition or find an unneeded partition, continue to find others.
return RegenerationAction::kContinue; return ListenerAction::kContinue;
} }
// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX. // 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 device_name(basename(verity_device.c_str()));
const std::string syspath = "/sys/block/" + device_name; const std::string syspath = "/sys/block/" + device_name;
bool found = false;
uevent_listener_.RegenerateUeventsForPath( auto verity_callback = [&device_name, &verity_device, this, &found](const Uevent& uevent) {
syspath, [&device_name, &verity_device, this](const Uevent& uevent) { if (uevent.device_name == device_name) {
if (uevent.device_name == device_name) { LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
LOG(VERBOSE) << "Creating dm-verity device : " << verity_device; device_handler_.HandleDeviceEvent(uevent);
device_handler_.HandleDeviceEvent(uevent); found = true;
return RegenerationAction::kStop; return ListenerAction::kStop;
} }
return RegenerationAction::kContinue; return ListenerAction::kContinue;
}); };
uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
if (!found) {
uevent_listener_.Poll(verity_callback, 10s);
}
if (!found) {
LOG(ERROR) << "dm-verity device not found";
return false;
}
return true;
} }
bool FirstStageMount::MountPartitions() { bool FirstStageMount::MountPartitions() {
@ -285,7 +311,7 @@ bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
} else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) { } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
// The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX". // 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. // 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 { } else {
return false; return false;
} }
@ -345,7 +371,7 @@ bool FirstStageMountVBootV2::GetRequiredDevices() {
return true; return true;
} }
RegenerationAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) { ListenerAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
// Check if this uevent corresponds to one of the required partitions and store its symlinks if // Check if this uevent corresponds to one of the required partitions and store its symlinks if
// so, in order to create FsManagerAvbHandle later. // so, in order to create FsManagerAvbHandle later.
// Note that the parent callback removes partitions from the list of required partitions // Note that the parent callback removes partitions from the list of required partitions

View file

@ -121,8 +121,8 @@ bool UeventListener::ReadUevent(Uevent* uevent) const {
// make sure we don't overrun the socket's buffer. // make sure we don't overrun the socket's buffer.
// //
RegenerationAction UeventListener::RegenerateUeventsForDir(DIR* d, ListenerAction UeventListener::RegenerateUeventsForDir(DIR* d,
RegenerateCallback callback) const { const ListenerCallback& callback) const {
int dfd = dirfd(d); int dfd = dirfd(d);
int fd = openat(dfd, "uevent", O_WRONLY); int fd = openat(dfd, "uevent", O_WRONLY);
@ -132,7 +132,7 @@ RegenerationAction UeventListener::RegenerateUeventsForDir(DIR* d,
Uevent uevent; Uevent uevent;
while (ReadUevent(&uevent)) { while (ReadUevent(&uevent)) {
if (callback(uevent) == RegenerationAction::kStop) return RegenerationAction::kStop; if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;
} }
} }
@ -147,49 +147,67 @@ RegenerationAction UeventListener::RegenerateUeventsForDir(DIR* d,
if (d2 == 0) { if (d2 == 0) {
close(fd); close(fd);
} else { } else {
if (RegenerateUeventsForDir(d2.get(), callback) == RegenerationAction::kStop) { if (RegenerateUeventsForDir(d2.get(), callback) == ListenerAction::kStop) {
return RegenerationAction::kStop; return ListenerAction::kStop;
} }
} }
} }
// default is always to continue looking for uevents // default is always to continue looking for uevents
return RegenerationAction::kContinue; return ListenerAction::kContinue;
} }
RegenerationAction UeventListener::RegenerateUeventsForPath(const std::string& path, ListenerAction UeventListener::RegenerateUeventsForPath(const std::string& path,
RegenerateCallback callback) const { const ListenerCallback& callback) const {
std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir); std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
if (!d) return RegenerationAction::kContinue; if (!d) return ListenerAction::kContinue;
return RegenerateUeventsForDir(d.get(), callback); return RegenerateUeventsForDir(d.get(), callback);
} }
static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"}; static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
void UeventListener::RegenerateUevents(RegenerateCallback callback) const { void UeventListener::RegenerateUevents(const ListenerCallback& callback) const {
for (const auto path : kRegenerationPaths) { for (const auto path : kRegenerationPaths) {
if (RegenerateUeventsForPath(path, callback) == RegenerationAction::kStop) return; if (RegenerateUeventsForPath(path, callback) == ListenerAction::kStop) return;
} }
} }
void UeventListener::DoPolling(PollCallback callback) const { void UeventListener::Poll(const ListenerCallback& callback,
const std::optional<std::chrono::milliseconds> relative_timeout) const {
using namespace std::chrono;
pollfd ufd; pollfd ufd;
ufd.events = POLLIN; ufd.events = POLLIN;
ufd.fd = device_fd_; ufd.fd = device_fd_;
auto start_time = steady_clock::now();
while (true) { while (true) {
ufd.revents = 0; ufd.revents = 0;
int nr = poll(&ufd, 1, -1);
if (nr <= 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) {
PLOG(ERROR) << "poll() of uevent socket failed, continuing";
continue; continue;
} }
if (ufd.revents & POLLIN) { if (ufd.revents & POLLIN) {
// We're non-blocking, so if we receive a poll event keep processing until there // We're non-blocking, so if we receive a poll event keep processing until
// we have exhausted all uevent messages. // we have exhausted all uevent messages.
Uevent uevent; Uevent uevent;
while (ReadUevent(&uevent)) { while (ReadUevent(&uevent)) {
callback(uevent); if (callback(uevent) == ListenerAction::kStop) return;
} }
} }
} }

View file

@ -19,7 +19,9 @@
#include <dirent.h> #include <dirent.h>
#include <chrono>
#include <functional> #include <functional>
#include <optional>
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>
@ -27,26 +29,26 @@
#define UEVENT_MSG_LEN 2048 #define UEVENT_MSG_LEN 2048
enum class RegenerationAction { enum class ListenerAction {
kStop = 0, // Stop regenerating uevents as we've handled the one(s) we're interested in. kStop = 0, // Stop regenerating uevents as we've handled the one(s) we're interested in.
kContinue, // Continue regenerating uevents as we haven't seen the one(s) we're interested in. kContinue, // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
}; };
using RegenerateCallback = std::function<RegenerationAction(const Uevent&)>; using ListenerCallback = std::function<ListenerAction(const Uevent&)>;
using PollCallback = std::function<void(const Uevent&)>;
class UeventListener { class UeventListener {
public: public:
UeventListener(); UeventListener();
void RegenerateUevents(RegenerateCallback callback) const; void RegenerateUevents(const ListenerCallback& callback) const;
RegenerationAction RegenerateUeventsForPath(const std::string& path, ListenerAction RegenerateUeventsForPath(const std::string& path,
RegenerateCallback callback) const; const ListenerCallback& callback) const;
void DoPolling(PollCallback callback) const; void Poll(const ListenerCallback& callback,
const std::optional<std::chrono::milliseconds> relative_timeout = {}) const;
private: private:
bool ReadUevent(Uevent* uevent) const; bool ReadUevent(Uevent* uevent) const;
RegenerationAction RegenerateUeventsForDir(DIR* d, RegenerateCallback callback) const; ListenerAction RegenerateUeventsForDir(DIR* d, const ListenerCallback& callback) const;
android::base::unique_fd device_fd_; android::base::unique_fd device_fd_;
}; };

View file

@ -138,7 +138,7 @@ void ColdBoot::RegenerateUevents() {
HandleFirmwareEvent(uevent); HandleFirmwareEvent(uevent);
uevent_queue_.emplace_back(std::move(uevent)); uevent_queue_.emplace_back(std::move(uevent));
return RegenerationAction::kContinue; return ListenerAction::kContinue;
}); });
} }
@ -266,9 +266,10 @@ int ueventd_main(int argc, char** argv) {
cold_boot.Run(); cold_boot.Run();
} }
uevent_listener.DoPolling([&device_handler](const Uevent& uevent) { uevent_listener.Poll([&device_handler](const Uevent& uevent) {
HandleFirmwareEvent(uevent); HandleFirmwareEvent(uevent);
device_handler.HandleDeviceEvent(uevent); device_handler.HandleDeviceEvent(uevent);
return ListenerAction::kContinue;
}); });
return 0; return 0;