1 //
2 // Copyright (C) 2020 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 "update_engine/payload_generator/merge_sequence_generator.h"
18
19 #include <algorithm>
20 #include <limits>
21
22 #include "update_engine/payload_generator/delta_diff_generator.h"
23 #include "update_engine/payload_generator/extent_ranges.h"
24 #include "update_engine/payload_generator/extent_utils.h"
25 #include "update_engine/update_metadata.pb.h"
26
27 namespace chromeos_update_engine {
28
CreateCowMergeOperation(const Extent & src_extent,const Extent & dst_extent,CowMergeOperation::Type op_type,uint32_t src_offset)29 CowMergeOperation CreateCowMergeOperation(const Extent& src_extent,
30 const Extent& dst_extent,
31 CowMergeOperation::Type op_type,
32 uint32_t src_offset) {
33 CowMergeOperation ret;
34 ret.set_type(op_type);
35 *ret.mutable_src_extent() = src_extent;
36 *ret.mutable_dst_extent() = dst_extent;
37 ret.set_src_offset(src_offset);
38 return ret;
39 }
40
operator <<(std::ostream & os,const CowMergeOperation & merge_operation)41 std::ostream& operator<<(std::ostream& os,
42 const CowMergeOperation& merge_operation) {
43 os << "CowMergeOperation src extent: "
44 << ExtentsToString({merge_operation.src_extent()})
45 << ", dst extent: " << ExtentsToString({merge_operation.dst_extent()});
46 if (merge_operation.has_src_offset()) {
47 os << ", src offset: " << merge_operation.src_offset();
48 }
49 os << " op_type: ";
50 if (merge_operation.type() == CowMergeOperation::COW_COPY) {
51 os << "COW_COPY";
52 } else if (merge_operation.type() == CowMergeOperation::COW_XOR) {
53 os << "COW_XOR";
54 } else {
55 os << merge_operation.type();
56 }
57 return os;
58 }
59
60 // The OTA generation guarantees that all blocks in the dst extent will be
61 // written only once. So we can use it to order the CowMergeOperation.
operator <(const CowMergeOperation & op1,const CowMergeOperation & op2)62 bool operator<(const CowMergeOperation& op1, const CowMergeOperation& op2) {
63 return op1.dst_extent().start_block() < op2.dst_extent().start_block();
64 }
65
operator ==(const CowMergeOperation & op1,const CowMergeOperation & op2)66 bool operator==(const CowMergeOperation& op1, const CowMergeOperation& op2) {
67 return op1.type() == op2.type() && op1.src_extent() == op2.src_extent() &&
68 op1.dst_extent() == op2.dst_extent();
69 }
70
71 template <typename T>
GetDifference(T first,T second)72 constexpr T GetDifference(T first, T second) {
73 T abs_diff = (first > second) ? (first - second) : (second - first);
74 return abs_diff;
75 }
76
GetCowOpType(InstallOperation::Type install_op_type)77 CowMergeOperation::Type GetCowOpType(InstallOperation::Type install_op_type) {
78 switch (install_op_type) {
79 case InstallOperation::SOURCE_COPY:
80 return CowMergeOperation::COW_COPY;
81 case InstallOperation::SOURCE_BSDIFF:
82 case InstallOperation::BROTLI_BSDIFF:
83 case InstallOperation::PUFFDIFF:
84 return CowMergeOperation::COW_XOR;
85 default:
86 CHECK(false) << "Unknown install op type: " << install_op_type;
87 return CowMergeOperation::COW_REPLACE;
88 }
89 }
90
SplitSelfOverlapping(const Extent & src_extent,const Extent & dst_extent,std::vector<CowMergeOperation> * sequence)91 void SplitSelfOverlapping(const Extent& src_extent,
92 const Extent& dst_extent,
93 std::vector<CowMergeOperation>* sequence) {
94 CHECK_EQ(src_extent.num_blocks(), dst_extent.num_blocks());
95 if (src_extent.start_block() == dst_extent.start_block()) {
96 sequence->emplace_back(CreateCowMergeOperation(
97 src_extent, dst_extent, CowMergeOperation::COW_COPY));
98 return;
99 }
100
101 const size_t diff =
102 GetDifference(src_extent.start_block(), dst_extent.start_block());
103 for (size_t i = 0; i < src_extent.num_blocks(); i += diff) {
104 auto num_blocks = std::min<size_t>(diff, src_extent.num_blocks() - i);
105 sequence->emplace_back(CreateCowMergeOperation(
106 ExtentForRange(i + src_extent.start_block(), num_blocks),
107 ExtentForRange(i + dst_extent.start_block(), num_blocks),
108 CowMergeOperation::COW_COPY));
109 }
110 }
111
ProcessXorOps(std::vector<CowMergeOperation> * sequence,const AnnotatedOperation & aop)112 static bool ProcessXorOps(std::vector<CowMergeOperation>* sequence,
113 const AnnotatedOperation& aop) {
114 const auto size_before = sequence->size();
115 sequence->insert(sequence->end(), aop.xor_ops.begin(), aop.xor_ops.end());
116 std::for_each(
117 sequence->begin() + size_before,
118 sequence->end(),
119 [](CowMergeOperation& op) {
120 CHECK_EQ(op.type(), CowMergeOperation::COW_XOR);
121 // If |src_offset| is greater than 0, then we are reading 1
122 // extra block at the end of src_extent. This dependency must
123 // be honored during merge sequence generation, or we can end
124 // up with a corrupted device after merge.
125 if (op.src_offset() > 0) {
126 if (op.src_extent().num_blocks() == op.dst_extent().num_blocks()) {
127 op.mutable_src_extent()->set_num_blocks(
128 op.src_extent().num_blocks() + 1);
129 }
130 CHECK_EQ(op.src_extent().num_blocks(),
131 op.dst_extent().num_blocks() + 1);
132 }
133 CHECK_NE(op.src_extent().start_block(),
134 std::numeric_limits<uint64_t>::max());
135 });
136 return true;
137 }
138
ProcessCopyOps(std::vector<CowMergeOperation> * sequence,const AnnotatedOperation & aop)139 static bool ProcessCopyOps(std::vector<CowMergeOperation>* sequence,
140 const AnnotatedOperation& aop) {
141 CHECK_EQ(GetCowOpType(aop.op.type()), CowMergeOperation::COW_COPY);
142 if (aop.op.dst_extents().size() != 1) {
143 std::vector<Extent> out_extents;
144 ExtentsToVector(aop.op.dst_extents(), &out_extents);
145 LOG(ERROR)
146 << "The dst extents for source_copy are expected to be contiguous,"
147 << " dst extents: " << ExtentsToString(out_extents);
148 return false;
149 }
150 // Split the source extents.
151 size_t used_blocks = 0;
152 for (const auto& src_extent : aop.op.src_extents()) {
153 // The dst_extent in the merge sequence will be a subset of
154 // InstallOperation's dst_extent. This will simplify the OTA -> COW
155 // conversion when we install the payload.
156 Extent dst_extent =
157 ExtentForRange(aop.op.dst_extents(0).start_block() + used_blocks,
158 src_extent.num_blocks());
159 // Self-overlapping operation, must split into multiple non
160 // self-overlapping ops
161 if (ExtentRanges::ExtentsOverlap(src_extent, dst_extent)) {
162 SplitSelfOverlapping(src_extent, dst_extent, sequence);
163 } else {
164 sequence->emplace_back(CreateCowMergeOperation(
165 src_extent, dst_extent, CowMergeOperation::COW_COPY));
166 }
167 used_blocks += src_extent.num_blocks();
168 }
169
170 if (used_blocks != aop.op.dst_extents(0).num_blocks()) {
171 LOG(ERROR) << "Number of blocks in src extents doesn't equal to the"
172 << " ones in the dst extents, src blocks " << used_blocks
173 << ", dst blocks " << aop.op.dst_extents(0).num_blocks();
174 return false;
175 }
176 return true;
177 }
178
Create(const std::vector<AnnotatedOperation> & aops)179 std::unique_ptr<MergeSequenceGenerator> MergeSequenceGenerator::Create(
180 const std::vector<AnnotatedOperation>& aops) {
181 std::vector<CowMergeOperation> sequence;
182
183 for (const auto& aop : aops) {
184 if (aop.op.type() == InstallOperation::SOURCE_COPY) {
185 if (!ProcessCopyOps(&sequence, aop)) {
186 return nullptr;
187 }
188 } else if (!aop.xor_ops.empty()) {
189 if (!ProcessXorOps(&sequence, aop)) {
190 return nullptr;
191 }
192 }
193 }
194
195 std::sort(sequence.begin(), sequence.end());
196 return std::unique_ptr<MergeSequenceGenerator>(
197 new MergeSequenceGenerator(sequence));
198 }
199
FindDependency(std::map<CowMergeOperation,std::set<CowMergeOperation>> * result) const200 bool MergeSequenceGenerator::FindDependency(
201 std::map<CowMergeOperation, std::set<CowMergeOperation>>* result) const {
202 CHECK(result);
203 LOG(INFO) << "Finding dependencies";
204
205 // Since the OTA operation may reuse some source blocks, use the binary
206 // search on sorted dst extents to find overlaps.
207 std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
208 for (const auto& op : operations_) {
209 // lower bound (inclusive): dst extent's end block >= src extent's start
210 // block.
211 const auto lower_it = std::lower_bound(
212 operations_.begin(),
213 operations_.end(),
214 op,
215 [](const CowMergeOperation& it, const CowMergeOperation& op) {
216 auto dst_end_block =
217 it.dst_extent().start_block() + it.dst_extent().num_blocks() - 1;
218 return dst_end_block < op.src_extent().start_block();
219 });
220 // upper bound: dst extent's start block > src extent's end block
221 const auto upper_it = std::upper_bound(
222 lower_it,
223 operations_.end(),
224 op,
225 [](const CowMergeOperation& op, const CowMergeOperation& it) {
226 auto src_end_block =
227 op.src_extent().start_block() + op.src_extent().num_blocks() - 1;
228 return src_end_block < it.dst_extent().start_block();
229 });
230
231 // TODO(xunchang) skip inserting the empty set to merge_after.
232 if (lower_it == upper_it) {
233 merge_after.insert({op, {}});
234 } else {
235 std::set<CowMergeOperation> operations(lower_it, upper_it);
236 auto it = operations.find(op);
237 if (it != operations.end()) {
238 LOG(INFO) << "Self overlapping " << op;
239 operations.erase(it);
240 }
241 auto ret = merge_after.emplace(op, std::move(operations));
242 // Check the insertion indeed happens.
243 CHECK(ret.second) << op;
244 }
245 }
246
247 *result = std::move(merge_after);
248 return true;
249 }
250
Generate(std::vector<CowMergeOperation> * sequence) const251 bool MergeSequenceGenerator::Generate(
252 std::vector<CowMergeOperation>* sequence) const {
253 sequence->clear();
254 std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
255 if (!FindDependency(&merge_after)) {
256 LOG(ERROR) << "Failed to find dependencies";
257 return false;
258 }
259
260 LOG(INFO) << "Generating sequence";
261
262 // Use the non-DFS version of the topology sort. So we can control the
263 // operations to discard to break cycles; thus yielding a deterministic
264 // sequence.
265 std::map<CowMergeOperation, int> incoming_edges;
266 for (const auto& it : merge_after) {
267 for (const auto& blocked : it.second) {
268 // Value is default initialized to 0.
269 incoming_edges[blocked] += 1;
270 }
271 }
272
273 // Technically, we can use std::unordered_set or just a std::vector. but
274 // std::set gives the benefit where operations are sorted by dst blocks. This
275 // will ensure that operations that do not have dependency constraints appear
276 // in increasing block order. Such order would help snapuserd batch merges and
277 // improve boot time, but isn't strictly needed for correctness.
278 std::set<CowMergeOperation> free_operations;
279 for (const auto& op : operations_) {
280 if (incoming_edges.find(op) == incoming_edges.end()) {
281 free_operations.insert(op);
282 }
283 }
284
285 std::vector<CowMergeOperation> merge_sequence;
286 std::set<CowMergeOperation> convert_to_raw;
287 while (!incoming_edges.empty()) {
288 if (!free_operations.empty()) {
289 merge_sequence.insert(
290 merge_sequence.end(), free_operations.begin(), free_operations.end());
291 } else {
292 auto to_convert = incoming_edges.begin()->first;
293 free_operations.insert(to_convert);
294 convert_to_raw.insert(to_convert);
295 LOG(INFO) << "Converting operation to raw " << to_convert;
296 }
297
298 std::set<CowMergeOperation> next_free_operations;
299 for (const auto& op : free_operations) {
300 incoming_edges.erase(op);
301
302 // Now that this particular operation is merged, other operations
303 // blocked by this one may be free. Decrement the count of blocking
304 // operations, and set up the free operations for the next iteration.
305 for (const auto& blocked : merge_after[op]) {
306 auto it = incoming_edges.find(blocked);
307 if (it == incoming_edges.end()) {
308 continue;
309 }
310
311 auto blocking_transfer_count = &it->second;
312 if (*blocking_transfer_count <= 0) {
313 LOG(ERROR) << "Unexpected count in merge after map "
314 << blocking_transfer_count;
315 return false;
316 }
317 // This operation is no longer blocked by anyone. Add it to the merge
318 // sequence in the next iteration.
319 *blocking_transfer_count -= 1;
320 if (*blocking_transfer_count == 0) {
321 next_free_operations.insert(blocked);
322 }
323 }
324 }
325
326 LOG(INFO) << "Remaining transfers " << incoming_edges.size()
327 << ", free transfers " << free_operations.size()
328 << ", merge_sequence size " << merge_sequence.size();
329 free_operations = std::move(next_free_operations);
330 }
331
332 if (!free_operations.empty()) {
333 merge_sequence.insert(
334 merge_sequence.end(), free_operations.begin(), free_operations.end());
335 }
336
337 CHECK_EQ(operations_.size(), merge_sequence.size() + convert_to_raw.size());
338
339 size_t blocks_in_sequence = 0;
340 for (const CowMergeOperation& transfer : merge_sequence) {
341 blocks_in_sequence += transfer.dst_extent().num_blocks();
342 }
343
344 size_t blocks_in_raw = 0;
345 for (const CowMergeOperation& transfer : convert_to_raw) {
346 blocks_in_raw += transfer.dst_extent().num_blocks();
347 }
348
349 LOG(INFO) << "Blocks in merge sequence " << blocks_in_sequence
350 << ", blocks in raw " << blocks_in_raw;
351 if (!ValidateSequence(merge_sequence)) {
352 LOG(ERROR) << "Invalid Sequence";
353 return false;
354 }
355
356 *sequence = std::move(merge_sequence);
357 return true;
358 }
359
ValidateSequence(const std::vector<CowMergeOperation> & sequence)360 bool MergeSequenceGenerator::ValidateSequence(
361 const std::vector<CowMergeOperation>& sequence) {
362 LOG(INFO) << "Validating merge sequence";
363 ExtentRanges visited;
364 for (const auto& op : sequence) {
365 // If |src_offset| is greater than zero, dependency should include 1 extra
366 // block at end of src_extent, as the OP actually references data past
367 // original src_extent.
368 if (op.src_offset() > 0) {
369 CHECK_EQ(op.src_extent().num_blocks(), op.dst_extent().num_blocks() + 1)
370 << op;
371 } else {
372 CHECK_EQ(op.src_extent().num_blocks(), op.dst_extent().num_blocks())
373 << op;
374 }
375 if (visited.OverlapsWithExtent(op.src_extent())) {
376 LOG(ERROR) << "Transfer violates the merge sequence " << op
377 << "Visited extent ranges: ";
378 visited.Dump();
379 return false;
380 }
381
382 CHECK(!visited.OverlapsWithExtent(op.dst_extent()))
383 << "dst extent should write only once.";
384 visited.AddExtent(op.dst_extent());
385 }
386
387 return true;
388 }
389
390 } // namespace chromeos_update_engine
391