libsnapshot: Add a helper for waiting for device paths.
Normally, DeviceMapper::CreateDevice() handles this for us. However, it does not work in first-stage init, because ueventd is not running. Therefore this patch adds a way for first-stage init to set a callback to manually regenerate and process uevents. Additionally, even with ueventd, dm-user misc device creation needs a WaitForFile() call, since ueventd is asynchronous. The WaitForDevice() helper in this patch accounts for both of these scenarios. Bug: 173476209 Test: device boots into first-stage init after full VABC ota Change-Id: Ib7a9bfc2a5a5095aa00b358072f9cb1743c19ab2
This commit is contained in:
parent
46d1844377
commit
189e8e3a25
2 changed files with 70 additions and 11 deletions
|
@ -345,6 +345,14 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
|
||||
bool UnmapAllSnapshots() override;
|
||||
|
||||
// We can't use WaitForFile during first-stage init, because ueventd is not
|
||||
// running and therefore will not automatically create symlinks. Instead,
|
||||
// we let init provide us with the correct function to use to ensure
|
||||
// uevents have been processed and symlink/mknod calls completed.
|
||||
void SetUeventRegenCallback(std::function<bool(const std::string&)> callback) {
|
||||
uevent_regen_callback_ = callback;
|
||||
}
|
||||
|
||||
private:
|
||||
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
|
||||
FRIEND_TEST(SnapshotTest, CreateSnapshot);
|
||||
|
@ -676,6 +684,12 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
// Same as above, but for paths only (no major:minor device strings).
|
||||
bool GetMappedImageDevicePath(const std::string& device_name, std::string* device_path);
|
||||
|
||||
// Wait for a device to be created by ueventd (eg, its symlink or node to be populated).
|
||||
// This is needed for any code that uses device-mapper path in first-stage init. If
|
||||
// |timeout_ms| is empty or the given device is not a path, WaitForDevice immediately
|
||||
// returns true.
|
||||
bool WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms);
|
||||
|
||||
std::string gsid_dir_;
|
||||
std::string metadata_dir_;
|
||||
std::unique_ptr<IDeviceInfo> device_;
|
||||
|
@ -683,6 +697,7 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
bool has_local_image_manager_ = false;
|
||||
bool use_first_stage_snapuserd_ = false;
|
||||
bool in_factory_data_reset_ = false;
|
||||
std::function<bool(const std::string&)> uevent_regen_callback_;
|
||||
std::unique_ptr<SnapuserdClient> snapuserd_client_;
|
||||
};
|
||||
|
||||
|
|
|
@ -408,10 +408,12 @@ bool SnapshotManager::MapDmUserCow(LockedFile* lock, const std::string& name,
|
|||
if (!dm.CreateDevice(name, table, path, timeout_ms)) {
|
||||
return false;
|
||||
}
|
||||
if (!WaitForDevice(*path, timeout_ms)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto control_device = "/dev/dm-user/" + misc_name;
|
||||
if (!android::fs_mgr::WaitForFile(control_device, timeout_ms)) {
|
||||
LOG(ERROR) << "Timed out waiting for dm-user misc device: " << control_device;
|
||||
if (!WaitForDevice(control_device, timeout_ms)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1339,10 +1341,12 @@ bool SnapshotManager::PerformSecondStageTransition() {
|
|||
continue;
|
||||
}
|
||||
|
||||
auto misc_name = user_cow_name;
|
||||
|
||||
DmTable table;
|
||||
table.Emplace<DmTargetUser>(0, target.spec.length, user_cow_name);
|
||||
table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
|
||||
if (!dm.LoadTableAndActivate(user_cow_name, table)) {
|
||||
LOG(ERROR) << "Unable to swap tables for " << user_cow_name;
|
||||
LOG(ERROR) << "Unable to swap tables for " << misc_name;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1368,14 +1372,13 @@ bool SnapshotManager::PerformSecondStageTransition() {
|
|||
}
|
||||
|
||||
// Wait for ueventd to acknowledge and create the control device node.
|
||||
std::string control_device = "/dev/dm-user/" + user_cow_name;
|
||||
if (!android::fs_mgr::WaitForFile(control_device, 10s)) {
|
||||
LOG(ERROR) << "Could not find control device: " << control_device;
|
||||
std::string control_device = "/dev/dm-user/" + misc_name;
|
||||
if (!WaitForDevice(control_device, 10s)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t base_sectors =
|
||||
snapuserd_client_->InitDmUserCow(user_cow_name, cow_image_device, backing_device);
|
||||
snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, backing_device);
|
||||
if (base_sectors == 0) {
|
||||
// Unrecoverable as metadata reads from cow device failed
|
||||
LOG(FATAL) << "Failed to retrieve base_sectors from Snapuserd";
|
||||
|
@ -1384,7 +1387,7 @@ bool SnapshotManager::PerformSecondStageTransition() {
|
|||
|
||||
CHECK(base_sectors == target.spec.length);
|
||||
|
||||
if (!snapuserd_client_->AttachDmUser(user_cow_name)) {
|
||||
if (!snapuserd_client_->AttachDmUser(misc_name)) {
|
||||
// This error is unrecoverable. We cannot proceed because reads to
|
||||
// the underlying device will fail.
|
||||
LOG(FATAL) << "Could not initialize snapuserd for " << user_cow_name;
|
||||
|
@ -1923,13 +1926,20 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
|
|||
if (IsCompressionEnabled()) {
|
||||
auto name = GetDmUserCowName(params.GetPartitionName());
|
||||
|
||||
// :TODO: need to force init to process uevents for these in first-stage.
|
||||
std::string cow_path;
|
||||
if (!GetMappedImageDevicePath(cow_name, &cow_path)) {
|
||||
LOG(ERROR) << "Could not determine path for: " << cow_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure both |base_path| and |cow_path| are created, for snapuserd.
|
||||
if (!WaitForDevice(base_path, remaining_time)) {
|
||||
return false;
|
||||
}
|
||||
if (!WaitForDevice(cow_path, remaining_time)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string new_cow_device;
|
||||
if (!MapDmUserCow(lock, name, cow_path, base_path, remaining_time, &new_cow_device)) {
|
||||
LOG(ERROR) << "Could not map dm-user device for partition "
|
||||
|
@ -2069,7 +2079,7 @@ bool SnapshotManager::UnmapCowDevices(LockedFile* lock, const std::string& name)
|
|||
if (!EnsureSnapuserdConnected()) {
|
||||
return false;
|
||||
}
|
||||
if (!dm.DeleteDevice(dm_user_name)) {
|
||||
if (!dm.DeleteDeviceIfExists(dm_user_name)) {
|
||||
LOG(ERROR) << "Cannot unmap " << dm_user_name;
|
||||
return false;
|
||||
}
|
||||
|
@ -3293,5 +3303,39 @@ bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SnapshotManager::WaitForDevice(const std::string& device,
|
||||
std::chrono::milliseconds timeout_ms) {
|
||||
if (!android::base::StartsWith(device, "/")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// In first-stage init, we rely on init setting a callback which can
|
||||
// regenerate uevents and populate /dev for us.
|
||||
if (uevent_regen_callback_) {
|
||||
if (!uevent_regen_callback_(device)) {
|
||||
LOG(ERROR) << "Failed to find device after regenerating uevents: " << device;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, the only kind of device we need to wait for is a dm-user
|
||||
// misc device. Normal calls to DeviceMapper::CreateDevice() guarantee
|
||||
// the path has been created.
|
||||
if (!android::base::StartsWith(device, "/dev/dm-user/")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (timeout_ms.count() == 0) {
|
||||
LOG(ERROR) << "No timeout was specified to wait for device: " << device;
|
||||
return false;
|
||||
}
|
||||
if (!android::fs_mgr::WaitForFile(device, timeout_ms)) {
|
||||
LOG(ERROR) << "Timed out waiting for device to appear: " << device;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
Loading…
Reference in a new issue