• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "snapuserd_core.h"
18 
19 #include <sys/utsname.h>
20 
21 #include <android-base/chrono_utils.h>
22 #include <android-base/properties.h>
23 #include <android-base/scopeguard.h>
24 #include <android-base/strings.h>
25 
26 namespace android {
27 namespace snapshot {
28 
29 using namespace android;
30 using namespace android::dm;
31 using android::base::unique_fd;
32 
SnapshotHandler(std::string misc_name,std::string cow_device,std::string backing_device,std::string base_path_merge)33 SnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device,
34                                  std::string backing_device, std::string base_path_merge) {
35     misc_name_ = std::move(misc_name);
36     cow_device_ = std::move(cow_device);
37     backing_store_device_ = std::move(backing_device);
38     control_device_ = "/dev/dm-user/" + misc_name_;
39     base_path_merge_ = std::move(base_path_merge);
40 }
41 
InitializeWorkers()42 bool SnapshotHandler::InitializeWorkers() {
43     int num_worker_threads = kNumWorkerThreads;
44 
45     // We will need multiple worker threads only during
46     // device boot after OTA. For all other purposes,
47     // one thread is sufficient. We don't want to consume
48     // unnecessary memory especially during OTA install phase
49     // when daemon will be up during entire post install phase.
50     //
51     // During boot up, we need multiple threads primarily for
52     // update-verification.
53     if (is_socket_present_) {
54         num_worker_threads = 1;
55     }
56 
57     for (int i = 0; i < num_worker_threads; i++) {
58         std::unique_ptr<Worker> wt =
59                 std::make_unique<Worker>(cow_device_, backing_store_device_, control_device_,
60                                          misc_name_, base_path_merge_, GetSharedPtr());
61         if (!wt->Init()) {
62             SNAP_LOG(ERROR) << "Thread initialization failed";
63             return false;
64         }
65 
66         worker_threads_.push_back(std::move(wt));
67     }
68 
69     merge_thread_ = std::make_unique<Worker>(cow_device_, backing_store_device_, control_device_,
70                                              misc_name_, base_path_merge_, GetSharedPtr());
71 
72     read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
73                                                      GetSharedPtr());
74 
75     update_verify_ = std::make_unique<UpdateVerify>(misc_name_);
76 
77     return true;
78 }
79 
CloneReaderForWorker()80 std::unique_ptr<CowReader> SnapshotHandler::CloneReaderForWorker() {
81     return reader_->CloneCowReader();
82 }
83 
UpdateMergeCompletionPercentage()84 void SnapshotHandler::UpdateMergeCompletionPercentage() {
85     struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
86     merge_completion_percentage_ = (ch->num_merge_ops * 100.0) / reader_->get_num_total_data_ops();
87 
88     SNAP_LOG(DEBUG) << "Merge-complete %: " << merge_completion_percentage_
89                     << " num_merge_ops: " << ch->num_merge_ops
90                     << " total-ops: " << reader_->get_num_total_data_ops();
91 }
92 
CommitMerge(int num_merge_ops)93 bool SnapshotHandler::CommitMerge(int num_merge_ops) {
94     struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
95     ch->num_merge_ops += num_merge_ops;
96 
97     if (scratch_space_) {
98         if (ra_thread_) {
99             struct BufferState* ra_state = GetBufferState();
100             ra_state->read_ahead_state = kCowReadAheadInProgress;
101         }
102 
103         int ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);
104         if (ret < 0) {
105             SNAP_PLOG(ERROR) << "msync header failed: " << ret;
106             return false;
107         }
108     } else {
109         reader_->UpdateMergeOpsCompleted(num_merge_ops);
110         CowHeader header;
111         reader_->GetHeader(&header);
112 
113         if (lseek(cow_fd_.get(), 0, SEEK_SET) < 0) {
114             SNAP_PLOG(ERROR) << "lseek failed";
115             return false;
116         }
117 
118         if (!android::base::WriteFully(cow_fd_, &header, sizeof(CowHeader))) {
119             SNAP_PLOG(ERROR) << "Write to header failed";
120             return false;
121         }
122 
123         if (fsync(cow_fd_.get()) < 0) {
124             SNAP_PLOG(ERROR) << "fsync failed";
125             return false;
126         }
127     }
128 
129     // Update the merge completion - this is used by update engine
130     // to track the completion. No need to take a lock. It is ok
131     // even if there is a miss on reading a latest updated value.
132     // Subsequent polling will eventually converge to completion.
133     UpdateMergeCompletionPercentage();
134 
135     return true;
136 }
137 
PrepareReadAhead()138 void SnapshotHandler::PrepareReadAhead() {
139     struct BufferState* ra_state = GetBufferState();
140     // Check if the data has to be re-constructed from COW device
141     if (ra_state->read_ahead_state == kCowReadAheadDone) {
142         populate_data_from_cow_ = true;
143     } else {
144         populate_data_from_cow_ = false;
145     }
146 
147     NotifyRAForMergeReady();
148 }
149 
CheckMergeCompletionStatus()150 bool SnapshotHandler::CheckMergeCompletionStatus() {
151     if (!merge_initiated_) {
152         SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: "
153                        << reader_->get_num_total_data_ops();
154         return false;
155     }
156 
157     struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
158 
159     SNAP_LOG(INFO) << "Merge-status: Total-Merged-ops: " << ch->num_merge_ops
160                    << " Total-data-ops: " << reader_->get_num_total_data_ops();
161     return true;
162 }
163 
ReadMetadata()164 bool SnapshotHandler::ReadMetadata() {
165     reader_ = std::make_unique<CowReader>(CowReader::ReaderFlags::USERSPACE_MERGE);
166     CowHeader header;
167     CowOptions options;
168 
169     SNAP_LOG(DEBUG) << "ReadMetadata: Parsing cow file";
170 
171     if (!reader_->Parse(cow_fd_)) {
172         SNAP_LOG(ERROR) << "Failed to parse";
173         return false;
174     }
175 
176     if (!reader_->GetHeader(&header)) {
177         SNAP_LOG(ERROR) << "Failed to get header";
178         return false;
179     }
180 
181     if (!(header.block_size == BLOCK_SZ)) {
182         SNAP_LOG(ERROR) << "Invalid header block size found: " << header.block_size;
183         return false;
184     }
185 
186     SNAP_LOG(INFO) << "Merge-ops: " << header.num_merge_ops;
187 
188     if (!MmapMetadata()) {
189         SNAP_LOG(ERROR) << "mmap failed";
190         return false;
191     }
192 
193     UpdateMergeCompletionPercentage();
194 
195     // Initialize the iterator for reading metadata
196     std::unique_ptr<ICowOpIter> cowop_iter = reader_->GetMergeOpIter();
197 
198     int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
199     int ra_index = 0;
200 
201     size_t copy_ops = 0, replace_ops = 0, zero_ops = 0, xor_ops = 0;
202 
203     while (!cowop_iter->Done()) {
204         const CowOperation* cow_op = &cowop_iter->Get();
205 
206         if (cow_op->type == kCowCopyOp) {
207             copy_ops += 1;
208         } else if (cow_op->type == kCowReplaceOp) {
209             replace_ops += 1;
210         } else if (cow_op->type == kCowZeroOp) {
211             zero_ops += 1;
212         } else if (cow_op->type == kCowXorOp) {
213             xor_ops += 1;
214         }
215 
216         chunk_vec_.push_back(std::make_pair(ChunkToSector(cow_op->new_block), cow_op));
217 
218         if (IsOrderedOp(*cow_op)) {
219             ra_thread_ = true;
220             block_to_ra_index_[cow_op->new_block] = ra_index;
221             num_ra_ops_per_iter -= 1;
222 
223             if ((ra_index + 1) - merge_blk_state_.size() == 1) {
224                 std::unique_ptr<MergeGroupState> blk_state = std::make_unique<MergeGroupState>(
225                         MERGE_GROUP_STATE::GROUP_MERGE_PENDING, 0);
226 
227                 merge_blk_state_.push_back(std::move(blk_state));
228             }
229 
230             // Move to next RA block
231             if (num_ra_ops_per_iter == 0) {
232                 num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
233                 ra_index += 1;
234             }
235         }
236         cowop_iter->Next();
237     }
238 
239     chunk_vec_.shrink_to_fit();
240 
241     // Sort the vector based on sectors as we need this during un-aligned access
242     std::sort(chunk_vec_.begin(), chunk_vec_.end(), compare);
243 
244     PrepareReadAhead();
245 
246     SNAP_LOG(INFO) << "Merged-ops: " << header.num_merge_ops
247                    << " Total-data-ops: " << reader_->get_num_total_data_ops()
248                    << " Unmerged-ops: " << chunk_vec_.size() << " Copy-ops: " << copy_ops
249                    << " Zero-ops: " << zero_ops << " Replace-ops: " << replace_ops
250                    << " Xor-ops: " << xor_ops;
251 
252     return true;
253 }
254 
MmapMetadata()255 bool SnapshotHandler::MmapMetadata() {
256     CowHeader header;
257     reader_->GetHeader(&header);
258 
259     total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
260 
261     if (header.major_version >= 2 && header.buffer_size > 0) {
262         scratch_space_ = true;
263     }
264 
265     if (scratch_space_) {
266         mapped_addr_ = mmap(NULL, total_mapped_addr_length_, PROT_READ | PROT_WRITE, MAP_SHARED,
267                             cow_fd_.get(), 0);
268     } else {
269         mapped_addr_ = mmap(NULL, total_mapped_addr_length_, PROT_READ | PROT_WRITE,
270                             MAP_SHARED | MAP_ANONYMOUS, -1, 0);
271         struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
272         ch->num_merge_ops = header.num_merge_ops;
273     }
274 
275     if (mapped_addr_ == MAP_FAILED) {
276         SNAP_LOG(ERROR) << "mmap metadata failed";
277         return false;
278     }
279 
280     return true;
281 }
282 
UnmapBufferRegion()283 void SnapshotHandler::UnmapBufferRegion() {
284     int ret = munmap(mapped_addr_, total_mapped_addr_length_);
285     if (ret < 0) {
286         SNAP_PLOG(ERROR) << "munmap failed";
287     }
288 }
289 
InitCowDevice()290 bool SnapshotHandler::InitCowDevice() {
291     cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
292     if (cow_fd_ < 0) {
293         SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
294         return false;
295     }
296 
297     unique_fd fd(TEMP_FAILURE_RETRY(open(base_path_merge_.c_str(), O_RDONLY | O_CLOEXEC)));
298     if (fd < 0) {
299         SNAP_LOG(ERROR) << "Cannot open block device";
300         return false;
301     }
302 
303     uint64_t dev_sz = get_block_device_size(fd.get());
304     if (!dev_sz) {
305         SNAP_LOG(ERROR) << "Failed to find block device size: " << base_path_merge_;
306         return false;
307     }
308 
309     num_sectors_ = dev_sz >> SECTOR_SHIFT;
310 
311     return ReadMetadata();
312 }
313 
314 /*
315  * Entry point to launch threads
316  */
Start()317 bool SnapshotHandler::Start() {
318     std::vector<std::future<bool>> threads;
319     std::future<bool> ra_thread_status;
320 
321     if (ra_thread_) {
322         ra_thread_status =
323                 std::async(std::launch::async, &ReadAhead::RunThread, read_ahead_thread_.get());
324 
325         SNAP_LOG(INFO) << "Read-ahead thread started...";
326     }
327 
328     // Launch worker threads
329     for (int i = 0; i < worker_threads_.size(); i++) {
330         threads.emplace_back(
331                 std::async(std::launch::async, &Worker::RunThread, worker_threads_[i].get()));
332     }
333 
334     bool partition_verification = true;
335 
336     // We don't want to read the blocks during first stage init or
337     // during post-install phase.
338     if (android::base::EndsWith(misc_name_, "-init") || is_socket_present_) {
339         partition_verification = false;
340     }
341 
342     std::future<bool> merge_thread =
343             std::async(std::launch::async, &Worker::RunMergeThread, merge_thread_.get());
344 
345     // Now that the worker threads are up, scan the partitions.
346     if (partition_verification) {
347         update_verify_->VerifyUpdatePartition();
348     }
349 
350     bool ret = true;
351     for (auto& t : threads) {
352         ret = t.get() && ret;
353     }
354 
355     // Worker threads are terminated by this point - this can only happen:
356     //
357     // 1: If dm-user device is destroyed
358     // 2: We had an I/O failure when reading root partitions
359     //
360     // In case (1), this would be a graceful shutdown. In this case, merge
361     // thread and RA thread should have already terminated by this point. We will be
362     // destroying the dm-user device only _after_ merge is completed.
363     //
364     // In case (2), if merge thread had started, then it will be
365     // continuing to merge; however, since we had an I/O failure and the
366     // I/O on root partitions are no longer served, we will terminate the
367     // merge
368 
369     NotifyIOTerminated();
370 
371     bool read_ahead_retval = false;
372 
373     SNAP_LOG(INFO) << "Snapshot I/O terminated. Waiting for merge thread....";
374     bool merge_thread_status = merge_thread.get();
375 
376     if (ra_thread_) {
377         read_ahead_retval = ra_thread_status.get();
378     }
379 
380     SNAP_LOG(INFO) << "Worker threads terminated with ret: " << ret
381                    << " Merge-thread with ret: " << merge_thread_status
382                    << " RA-thread with ret: " << read_ahead_retval;
383     return ret;
384 }
385 
GetBufferMetadataOffset()386 uint64_t SnapshotHandler::GetBufferMetadataOffset() {
387     CowHeader header;
388     reader_->GetHeader(&header);
389 
390     return (header.header_size + sizeof(BufferState));
391 }
392 
393 /*
394  * Metadata for read-ahead is 16 bytes. For a 2 MB region, we will
395  * end up with 8k (2 PAGE) worth of metadata. Thus, a 2MB buffer
396  * region is split into:
397  *
398  * 1: 8k metadata
399  * 2: Scratch space
400  *
401  */
GetBufferMetadataSize()402 size_t SnapshotHandler::GetBufferMetadataSize() {
403     CowHeader header;
404     reader_->GetHeader(&header);
405     size_t buffer_size = header.buffer_size;
406 
407     // If there is no scratch space, then just use the
408     // anonymous memory
409     if (buffer_size == 0) {
410         buffer_size = BUFFER_REGION_DEFAULT_SIZE;
411     }
412 
413     return ((buffer_size * sizeof(struct ScratchMetadata)) / BLOCK_SZ);
414 }
415 
GetBufferDataOffset()416 size_t SnapshotHandler::GetBufferDataOffset() {
417     CowHeader header;
418     reader_->GetHeader(&header);
419 
420     return (header.header_size + GetBufferMetadataSize());
421 }
422 
423 /*
424  * (2MB - 8K = 2088960 bytes) will be the buffer region to hold the data.
425  */
GetBufferDataSize()426 size_t SnapshotHandler::GetBufferDataSize() {
427     CowHeader header;
428     reader_->GetHeader(&header);
429     size_t buffer_size = header.buffer_size;
430 
431     // If there is no scratch space, then just use the
432     // anonymous memory
433     if (buffer_size == 0) {
434         buffer_size = BUFFER_REGION_DEFAULT_SIZE;
435     }
436 
437     return (buffer_size - GetBufferMetadataSize());
438 }
439 
GetBufferState()440 struct BufferState* SnapshotHandler::GetBufferState() {
441     CowHeader header;
442     reader_->GetHeader(&header);
443 
444     struct BufferState* ra_state =
445             reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
446     return ra_state;
447 }
448 
IsIouringSupported()449 bool SnapshotHandler::IsIouringSupported() {
450     struct utsname uts;
451     unsigned int major, minor;
452 
453     if (android::base::GetBoolProperty("snapuserd.test.io_uring.force_disable", false)) {
454         SNAP_LOG(INFO) << "io_uring disabled for testing";
455         return false;
456     }
457 
458     if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
459         SNAP_LOG(ERROR) << "Could not parse the kernel version from uname. "
460                         << " io_uring not supported";
461         return false;
462     }
463 
464     // We will only support kernels from 5.6 onwards as IOSQE_ASYNC flag and
465     // IO_URING_OP_READ/WRITE opcodes were introduced only on 5.6 kernel
466     if (major >= 5) {
467         if (major == 5 && minor < 6) {
468             return false;
469         }
470     } else {
471         return false;
472     }
473 
474     // During selinux init transition, libsnapshot will propagate the
475     // status of io_uring enablement. As properties are not initialized,
476     // we cannot query system property.
477     if (is_io_uring_enabled_) {
478         return true;
479     }
480 
481     // Finally check the system property
482     return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
483 }
484 
485 }  // namespace snapshot
486 }  // namespace android
487