xref: /aosp_15_r20/system/core/init/block_dev_initializer.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
1 // Copyright (C) 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <chrono>
16 #include <string_view>
17 #include <vector>
18 
19 #include <android-base/chrono_utils.h>
20 #include <android-base/logging.h>
21 #include <android-base/strings.h>
22 #include <fs_mgr.h>
23 
24 #include "block_dev_initializer.h"
25 
26 namespace android {
27 namespace init {
28 
29 using android::base::Timer;
30 using namespace std::chrono_literals;
31 
BlockDevInitializer()32 BlockDevInitializer::BlockDevInitializer() : uevent_listener_(16 * 1024 * 1024) {
33     auto boot_devices = android::fs_mgr::GetBootDevices();
34     device_handler_ = std::make_unique<DeviceHandler>(
35             std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
36             std::vector<Subsystem>{}, std::move(boot_devices), android::fs_mgr::GetBootPartUuid(),
37             false);
38 }
39 
40 // If boot_part_uuid is specified, use it to set boot_devices
41 //
42 // When `androidboot.boot_part_uuid` is specified then that's the partition UUID
43 // of the kernel. Look for that partition and then set `boot_devices` to be
44 // exactly one item: the block device containing that partition.
45 //
46 // NOTE that `boot_part_uuid` is only specified on newer devices. Older devices
47 // specified `boot_devices` directly.
InitBootDevicesFromPartUuid()48 bool BlockDevInitializer::InitBootDevicesFromPartUuid() {
49     bool uuid_check_done = false;
50 
51     auto boot_part_callback = [&, this](const Uevent& uevent) -> ListenerAction {
52         uuid_check_done = device_handler_->CheckUeventForBootPartUuid(uevent);
53         return uuid_check_done ? ListenerAction::kStop : ListenerAction::kContinue;
54     };
55 
56     // Re-run already arrived uevents looking for the boot partition UUID.
57     //
58     // NOTE: If we're not using the boot partition UUID to find the boot
59     // device then the first uevent we analyze will cause us to stop looking
60     // and set `uuid_check_done`. This will shortcut all of the UUID logic.
61     // Replaying one uevent is not expected to be slow.
62     uevent_listener_.RegenerateUevents(boot_part_callback);
63 
64     // If we're not done looking, poll for uevents for longer
65     if (!uuid_check_done) {
66         Timer t;
67         uevent_listener_.Poll(boot_part_callback, 10s);
68         LOG(INFO) << "Wait for boot partition returned after " << t;
69     }
70 
71     // Give a nicer error message if we were expecting to find the kernel boot
72     // partition but didn't. Later code would fail too but the message there
73     // is a bit further from the root cause of the problem.
74     if (!uuid_check_done) {
75         LOG(ERROR) << __PRETTY_FUNCTION__ << ": boot partition not found after polling timeout.";
76         return false;
77     }
78 
79     return true;
80 }
81 
InitDeviceMapper()82 bool BlockDevInitializer::InitDeviceMapper() {
83     return InitMiscDevice("device-mapper");
84 }
85 
InitDmUser(const std::string & name)86 bool BlockDevInitializer::InitDmUser(const std::string& name) {
87     return InitMiscDevice("dm-user!" + name);
88 }
89 
InitMiscDevice(const std::string & name)90 bool BlockDevInitializer::InitMiscDevice(const std::string& name) {
91     const std::string dm_path = "/devices/virtual/misc/" + name;
92     bool found = false;
93     auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
94         if (uevent.path == dm_path) {
95             device_handler_->HandleUevent(uevent);
96             found = true;
97             return ListenerAction::kStop;
98         }
99         return ListenerAction::kContinue;
100     };
101     uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
102     if (!found) {
103         LOG(INFO) << name << " device not found in /sys, waiting for its uevent";
104         Timer t;
105         uevent_listener_.Poll(dm_callback, 10s);
106         LOG(INFO) << "Wait for " << name << " returned after " << t;
107     }
108     if (!found) {
109         LOG(ERROR) << name << " device not found after polling timeout";
110         return false;
111     }
112     return true;
113 }
114 
HandleUevent(const Uevent & uevent,std::set<std::string> * devices)115 ListenerAction BlockDevInitializer::HandleUevent(const Uevent& uevent,
116                                                  std::set<std::string>* devices) {
117     // Ignore everything that is not a block device.
118     if (uevent.subsystem != "block") {
119         return ListenerAction::kContinue;
120     }
121 
122     auto name = uevent.partition_name;
123     if (name.empty()) {
124         size_t base_idx = uevent.path.rfind('/');
125         if (base_idx == std::string::npos) {
126             return ListenerAction::kContinue;
127         }
128         name = uevent.path.substr(base_idx + 1);
129     }
130 
131     auto iter = devices->find(name);
132     if (iter == devices->end()) {
133         auto partition_name = DeviceHandler::GetPartitionNameForDevice(uevent.device_name);
134         if (!partition_name.empty()) {
135             iter = devices->find(partition_name);
136         }
137         if (iter == devices->end()) {
138             return ListenerAction::kContinue;
139         }
140     }
141 
142     LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << name;
143 
144     // Remove the partition from the list of partitions we're waiting for.
145     //
146     // Partitions that we're waiting for here are expected to be on the boot
147     // device, so only remove from the list if they're on the boot device.
148     // This prevents us from being confused if there are multiple disks (some
149     // perhaps connected via USB) that have matching partition names.
150     //
151     // ...but...
152     //
153     // Some products (especialy emulators) don't seem to set up boot_devices
154     // or possibly not all the partitions that we need to wait for are on the
155     // specified boot device. Thus, only require partitions to be on the boot
156     // device in "strict" mode, which should be used on newer systems.
157     if (device_handler_->IsBootDevice(uevent) || !device_handler_->IsBootDeviceStrict()) {
158         devices->erase(iter);
159     }
160 
161     device_handler_->HandleUevent(uevent);
162     return devices->empty() ? ListenerAction::kStop : ListenerAction::kContinue;
163 }
164 
165 // Wait for partitions that are expected to be on the "boot device" to initialize.
166 //
167 // Wait (for up to 10 seconds) for partitions passed in `devices` to show up.
168 // All block devices found while waiting will be initialized, which includes
169 // creating symlinks for them in /dev/block. Once all `devices` are found we'll
170 // return success (true). If any devices aren't found we'll return failure
171 // (false). As devices are found they will be removed from `devices`.
172 //
173 // The contents of `devices` is the names of the partitions. This can be:
174 // - The `partition_name` reported by a uevent, or the final component in the
175 //   `path` reported by a uevent if the `partition_name` is blank.
176 // - The result of DeviceHandler::GetPartitionNameForDevice() on the
177 //   `device_name` reported by a uevent.
178 //
179 // NOTE: on newer systems partitions _must_ be on the "boot device". See
180 // comments inside HandleUevent().
InitDevices(std::set<std::string> devices)181 bool BlockDevInitializer::InitDevices(std::set<std::string> devices) {
182     auto uevent_callback = [&, this](const Uevent& uevent) -> ListenerAction {
183         return HandleUevent(uevent, &devices);
184     };
185     uevent_listener_.RegenerateUevents(uevent_callback);
186 
187     // UeventCallback() will remove found partitions from |devices|. So if it
188     // isn't empty here, it means some partitions are not found.
189     if (!devices.empty()) {
190         LOG(INFO) << __PRETTY_FUNCTION__
191                   << ": partition(s) not found in /sys, waiting for their uevent(s): "
192                   << android::base::Join(devices, ", ");
193         Timer t;
194         uevent_listener_.Poll(uevent_callback, 10s);
195         LOG(INFO) << "Wait for partitions returned after " << t;
196     }
197 
198     if (!devices.empty()) {
199         LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
200                    << android::base::Join(devices, ", ");
201         return false;
202     }
203     return true;
204 }
205 
206 // Creates "/dev/block/dm-XX" for dm nodes by running coldboot on /sys/block/dm-XX.
InitDmDevice(const std::string & device)207 bool BlockDevInitializer::InitDmDevice(const std::string& device) {
208     const std::string device_name(basename(device.c_str()));
209     const std::string syspath = "/sys/block/" + device_name;
210     return InitDevice(syspath, device_name);
211 }
212 
InitPlatformDevice(const std::string & dev_name)213 bool BlockDevInitializer::InitPlatformDevice(const std::string& dev_name) {
214     return InitDevice("/sys/devices/platform", dev_name);
215 }
216 
InitHvcDevice(const std::string & dev_name)217 bool BlockDevInitializer::InitHvcDevice(const std::string& dev_name) {
218     return InitDevice("/sys/devices/virtual/tty", dev_name);
219 }
220 
InitDevice(const std::string & syspath,const std::string & device_name)221 bool BlockDevInitializer::InitDevice(const std::string& syspath, const std::string& device_name) {
222     bool found = false;
223 
224     auto uevent_callback = [&device_name, this, &found](const Uevent& uevent) {
225         if (uevent.device_name == device_name) {
226             LOG(VERBOSE) << "Creating device : " << device_name;
227             device_handler_->HandleUevent(uevent);
228             found = true;
229             return ListenerAction::kStop;
230         }
231         return ListenerAction::kContinue;
232     };
233 
234     uevent_listener_.RegenerateUeventsForPath(syspath, uevent_callback);
235     if (!found) {
236         LOG(INFO) << "device '" << device_name << "' not found in /sys, waiting for its uevent";
237         Timer t;
238         uevent_listener_.Poll(uevent_callback, 10s);
239         LOG(INFO) << "wait for device '" << device_name << "' returned after " << t;
240     }
241     if (!found) {
242         LOG(ERROR) << "device '" << device_name << "' not found after polling timeout";
243         return false;
244     }
245     return true;
246 }
247 
248 }  // namespace init
249 }  // namespace android
250