Skip to content

Commit 189e8e3

Browse files
committed
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
1 parent 46d1844 commit 189e8e3

File tree

2 files changed

+70
-11
lines changed

2 files changed

+70
-11
lines changed

fs_mgr/libsnapshot/include/libsnapshot/snapshot.h

+15
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,14 @@ class SnapshotManager final : public ISnapshotManager {
345345
bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
346346
bool UnmapAllSnapshots() override;
347347

348+
// We can't use WaitForFile during first-stage init, because ueventd is not
349+
// running and therefore will not automatically create symlinks. Instead,
350+
// we let init provide us with the correct function to use to ensure
351+
// uevents have been processed and symlink/mknod calls completed.
352+
void SetUeventRegenCallback(std::function<bool(const std::string&)> callback) {
353+
uevent_regen_callback_ = callback;
354+
}
355+
348356
private:
349357
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
350358
FRIEND_TEST(SnapshotTest, CreateSnapshot);
@@ -676,13 +684,20 @@ class SnapshotManager final : public ISnapshotManager {
676684
// Same as above, but for paths only (no major:minor device strings).
677685
bool GetMappedImageDevicePath(const std::string& device_name, std::string* device_path);
678686

687+
// Wait for a device to be created by ueventd (eg, its symlink or node to be populated).
688+
// This is needed for any code that uses device-mapper path in first-stage init. If
689+
// |timeout_ms| is empty or the given device is not a path, WaitForDevice immediately
690+
// returns true.
691+
bool WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms);
692+
679693
std::string gsid_dir_;
680694
std::string metadata_dir_;
681695
std::unique_ptr<IDeviceInfo> device_;
682696
std::unique_ptr<IImageManager> images_;
683697
bool has_local_image_manager_ = false;
684698
bool use_first_stage_snapuserd_ = false;
685699
bool in_factory_data_reset_ = false;
700+
std::function<bool(const std::string&)> uevent_regen_callback_;
686701
std::unique_ptr<SnapuserdClient> snapuserd_client_;
687702
};
688703

fs_mgr/libsnapshot/snapshot.cpp

+55-11
Original file line numberDiff line numberDiff line change
@@ -408,10 +408,12 @@ bool SnapshotManager::MapDmUserCow(LockedFile* lock, const std::string& name,
408408
if (!dm.CreateDevice(name, table, path, timeout_ms)) {
409409
return false;
410410
}
411+
if (!WaitForDevice(*path, timeout_ms)) {
412+
return false;
413+
}
411414

412415
auto control_device = "/dev/dm-user/" + misc_name;
413-
if (!android::fs_mgr::WaitForFile(control_device, timeout_ms)) {
414-
LOG(ERROR) << "Timed out waiting for dm-user misc device: " << control_device;
416+
if (!WaitForDevice(control_device, timeout_ms)) {
415417
return false;
416418
}
417419

@@ -1339,10 +1341,12 @@ bool SnapshotManager::PerformSecondStageTransition() {
13391341
continue;
13401342
}
13411343

1344+
auto misc_name = user_cow_name;
1345+
13421346
DmTable table;
1343-
table.Emplace<DmTargetUser>(0, target.spec.length, user_cow_name);
1347+
table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
13441348
if (!dm.LoadTableAndActivate(user_cow_name, table)) {
1345-
LOG(ERROR) << "Unable to swap tables for " << user_cow_name;
1349+
LOG(ERROR) << "Unable to swap tables for " << misc_name;
13461350
continue;
13471351
}
13481352

@@ -1368,14 +1372,13 @@ bool SnapshotManager::PerformSecondStageTransition() {
13681372
}
13691373

13701374
// Wait for ueventd to acknowledge and create the control device node.
1371-
std::string control_device = "/dev/dm-user/" + user_cow_name;
1372-
if (!android::fs_mgr::WaitForFile(control_device, 10s)) {
1373-
LOG(ERROR) << "Could not find control device: " << control_device;
1375+
std::string control_device = "/dev/dm-user/" + misc_name;
1376+
if (!WaitForDevice(control_device, 10s)) {
13741377
continue;
13751378
}
13761379

13771380
uint64_t base_sectors =
1378-
snapuserd_client_->InitDmUserCow(user_cow_name, cow_image_device, backing_device);
1381+
snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, backing_device);
13791382
if (base_sectors == 0) {
13801383
// Unrecoverable as metadata reads from cow device failed
13811384
LOG(FATAL) << "Failed to retrieve base_sectors from Snapuserd";
@@ -1384,7 +1387,7 @@ bool SnapshotManager::PerformSecondStageTransition() {
13841387

13851388
CHECK(base_sectors == target.spec.length);
13861389

1387-
if (!snapuserd_client_->AttachDmUser(user_cow_name)) {
1390+
if (!snapuserd_client_->AttachDmUser(misc_name)) {
13881391
// This error is unrecoverable. We cannot proceed because reads to
13891392
// the underlying device will fail.
13901393
LOG(FATAL) << "Could not initialize snapuserd for " << user_cow_name;
@@ -1923,13 +1926,20 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
19231926
if (IsCompressionEnabled()) {
19241927
auto name = GetDmUserCowName(params.GetPartitionName());
19251928

1926-
// :TODO: need to force init to process uevents for these in first-stage.
19271929
std::string cow_path;
19281930
if (!GetMappedImageDevicePath(cow_name, &cow_path)) {
19291931
LOG(ERROR) << "Could not determine path for: " << cow_name;
19301932
return false;
19311933
}
19321934

1935+
// Ensure both |base_path| and |cow_path| are created, for snapuserd.
1936+
if (!WaitForDevice(base_path, remaining_time)) {
1937+
return false;
1938+
}
1939+
if (!WaitForDevice(cow_path, remaining_time)) {
1940+
return false;
1941+
}
1942+
19331943
std::string new_cow_device;
19341944
if (!MapDmUserCow(lock, name, cow_path, base_path, remaining_time, &new_cow_device)) {
19351945
LOG(ERROR) << "Could not map dm-user device for partition "
@@ -2069,7 +2079,7 @@ bool SnapshotManager::UnmapCowDevices(LockedFile* lock, const std::string& name)
20692079
if (!EnsureSnapuserdConnected()) {
20702080
return false;
20712081
}
2072-
if (!dm.DeleteDevice(dm_user_name)) {
2082+
if (!dm.DeleteDeviceIfExists(dm_user_name)) {
20732083
LOG(ERROR) << "Cannot unmap " << dm_user_name;
20742084
return false;
20752085
}
@@ -3293,5 +3303,39 @@ bool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device
32933303
return true;
32943304
}
32953305

3306+
bool SnapshotManager::WaitForDevice(const std::string& device,
3307+
std::chrono::milliseconds timeout_ms) {
3308+
if (!android::base::StartsWith(device, "/")) {
3309+
return true;
3310+
}
3311+
3312+
// In first-stage init, we rely on init setting a callback which can
3313+
// regenerate uevents and populate /dev for us.
3314+
if (uevent_regen_callback_) {
3315+
if (!uevent_regen_callback_(device)) {
3316+
LOG(ERROR) << "Failed to find device after regenerating uevents: " << device;
3317+
return false;
3318+
}
3319+
return true;
3320+
}
3321+
3322+
// Otherwise, the only kind of device we need to wait for is a dm-user
3323+
// misc device. Normal calls to DeviceMapper::CreateDevice() guarantee
3324+
// the path has been created.
3325+
if (!android::base::StartsWith(device, "/dev/dm-user/")) {
3326+
return true;
3327+
}
3328+
3329+
if (timeout_ms.count() == 0) {
3330+
LOG(ERROR) << "No timeout was specified to wait for device: " << device;
3331+
return false;
3332+
}
3333+
if (!android::fs_mgr::WaitForFile(device, timeout_ms)) {
3334+
LOG(ERROR) << "Timed out waiting for device to appear: " << device;
3335+
return false;
3336+
}
3337+
return true;
3338+
}
3339+
32963340
} // namespace snapshot
32973341
} // namespace android

0 commit comments

Comments
 (0)