• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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