• 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 
21 #include "update_engine/payload_generator/extent_utils.h"
22 
23 namespace chromeos_update_engine {
24 
CreateCowMergeOperation(const Extent & src_extent,const Extent & dst_extent)25 CowMergeOperation CreateCowMergeOperation(const Extent& src_extent,
26                                           const Extent& dst_extent) {
27   CowMergeOperation ret;
28   ret.set_type(CowMergeOperation::COW_COPY);
29   *ret.mutable_src_extent() = src_extent;
30   *ret.mutable_dst_extent() = dst_extent;
31   return ret;
32 }
33 
operator <<(std::ostream & os,const CowMergeOperation & merge_operation)34 std::ostream& operator<<(std::ostream& os,
35                          const CowMergeOperation& merge_operation) {
36   os << "CowMergeOperation src extent: "
37      << ExtentsToString({merge_operation.src_extent()})
38      << ", dst extent: " << ExtentsToString({merge_operation.dst_extent()});
39   return os;
40 }
41 
42 // The OTA generation guarantees that all blocks in the dst extent will be
43 // written only once. So we can use it to order the CowMergeOperation.
operator <(const CowMergeOperation & op1,const CowMergeOperation & op2)44 bool operator<(const CowMergeOperation& op1, const CowMergeOperation& op2) {
45   return op1.dst_extent().start_block() < op2.dst_extent().start_block();
46 }
47 
operator ==(const CowMergeOperation & op1,const CowMergeOperation & op2)48 bool operator==(const CowMergeOperation& op1, const CowMergeOperation& op2) {
49   return op1.type() == op2.type() && op1.src_extent() == op2.src_extent() &&
50          op1.dst_extent() == op2.dst_extent();
51 }
52 
53 template <typename T>
GetDifference(T first,T second)54 constexpr T GetDifference(T first, T second) {
55   T abs_diff = (first > second) ? (first - second) : (second - first);
56   return abs_diff;
57 }
58 
SplitSelfOverlapping(const Extent & src_extent,const Extent & dst_extent,std::vector<CowMergeOperation> * sequence)59 void SplitSelfOverlapping(const Extent& src_extent,
60                           const Extent& dst_extent,
61                           std::vector<CowMergeOperation>* sequence) {
62   CHECK_EQ(src_extent.num_blocks(), dst_extent.num_blocks());
63   if (src_extent.start_block() == dst_extent.start_block()) {
64     sequence->emplace_back(CreateCowMergeOperation(src_extent, dst_extent));
65     return;
66   }
67 
68   const size_t diff =
69       GetDifference(src_extent.start_block(), dst_extent.start_block());
70   for (size_t i = 0; i < src_extent.num_blocks(); i += diff) {
71     auto num_blocks = std::min<size_t>(diff, src_extent.num_blocks() - i);
72     sequence->emplace_back(CreateCowMergeOperation(
73         ExtentForRange(i + src_extent.start_block(), num_blocks),
74         ExtentForRange(i + dst_extent.start_block(), num_blocks)));
75   }
76 }
77 
Create(const std::vector<AnnotatedOperation> & aops)78 std::unique_ptr<MergeSequenceGenerator> MergeSequenceGenerator::Create(
79     const std::vector<AnnotatedOperation>& aops) {
80   std::vector<CowMergeOperation> sequence;
81   for (const auto& aop : aops) {
82     // Only handle SOURCE_COPY now for the cow size optimization.
83     if (aop.op.type() != InstallOperation::SOURCE_COPY) {
84       continue;
85     }
86     if (aop.op.dst_extents().size() != 1) {
87       std::vector<Extent> out_extents;
88       ExtentsToVector(aop.op.dst_extents(), &out_extents);
89       LOG(ERROR) << "The dst extents for source_copy expects to be contiguous,"
90                  << " dst extents: " << ExtentsToString(out_extents);
91       return nullptr;
92     }
93 
94     // Split the source extents.
95     size_t used_blocks = 0;
96     for (const auto& src_extent : aop.op.src_extents()) {
97       // The dst_extent in the merge sequence will be a subset of
98       // InstallOperation's dst_extent. This will simplify the OTA -> COW
99       // conversion when we install the payload.
100       Extent dst_extent =
101           ExtentForRange(aop.op.dst_extents(0).start_block() + used_blocks,
102                          src_extent.num_blocks());
103 
104       // Self-overlapping SOURCE_COPY, must split into multiple non
105       // self-overlapping ops
106       if (ExtentRanges::ExtentsOverlap(src_extent, dst_extent)) {
107         SplitSelfOverlapping(src_extent, dst_extent, &sequence);
108       } else {
109         sequence.emplace_back(CreateCowMergeOperation(src_extent, dst_extent));
110       }
111       used_blocks += src_extent.num_blocks();
112     }
113 
114     if (used_blocks != aop.op.dst_extents(0).num_blocks()) {
115       LOG(ERROR) << "Number of blocks in src extents doesn't equal to the"
116                  << " ones in the dst extents, src blocks " << used_blocks
117                  << ", dst blocks " << aop.op.dst_extents(0).num_blocks();
118       return nullptr;
119     }
120   }
121 
122   std::sort(sequence.begin(), sequence.end());
123   return std::unique_ptr<MergeSequenceGenerator>(
124       new MergeSequenceGenerator(sequence));
125 }
126 
FindDependency(std::map<CowMergeOperation,std::set<CowMergeOperation>> * result) const127 bool MergeSequenceGenerator::FindDependency(
128     std::map<CowMergeOperation, std::set<CowMergeOperation>>* result) const {
129   CHECK(result);
130   LOG(INFO) << "Finding dependencies";
131 
132   // Since the OTA operation may reuse some source blocks, use the binary
133   // search on sorted dst extents to find overlaps.
134   std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
135   for (const auto& op : operations_) {
136     // lower bound (inclusive): dst extent's end block >= src extent's start
137     // block.
138     const auto lower_it = std::lower_bound(
139         operations_.begin(),
140         operations_.end(),
141         op,
142         [](const CowMergeOperation& it, const CowMergeOperation& op) {
143           auto dst_end_block =
144               it.dst_extent().start_block() + it.dst_extent().num_blocks() - 1;
145           return dst_end_block < op.src_extent().start_block();
146         });
147     // upper bound: dst extent's start block > src extent's end block
148     const auto upper_it = std::upper_bound(
149         lower_it,
150         operations_.end(),
151         op,
152         [](const CowMergeOperation& op, const CowMergeOperation& it) {
153           auto src_end_block =
154               op.src_extent().start_block() + op.src_extent().num_blocks() - 1;
155           return src_end_block < it.dst_extent().start_block();
156         });
157 
158     // TODO(xunchang) skip inserting the empty set to merge_after.
159     if (lower_it == upper_it) {
160       merge_after.insert({op, {}});
161     } else {
162       std::set<CowMergeOperation> operations(lower_it, upper_it);
163       auto it = operations.find(op);
164       if (it != operations.end()) {
165         LOG(INFO) << "Self overlapping " << op;
166         operations.erase(it);
167       }
168       auto ret = merge_after.emplace(op, std::move(operations));
169       // Check the insertion indeed happens.
170       CHECK(ret.second);
171     }
172   }
173 
174   *result = std::move(merge_after);
175   return true;
176 }
177 
Generate(std::vector<CowMergeOperation> * sequence) const178 bool MergeSequenceGenerator::Generate(
179     std::vector<CowMergeOperation>* sequence) const {
180   sequence->clear();
181   std::map<CowMergeOperation, std::set<CowMergeOperation>> merge_after;
182   if (!FindDependency(&merge_after)) {
183     LOG(ERROR) << "Failed to find dependencies";
184     return false;
185   }
186 
187   LOG(INFO) << "Generating sequence";
188 
189   // Use the non-DFS version of the topology sort. So we can control the
190   // operations to discard to break cycles; thus yielding a deterministic
191   // sequence.
192   std::map<CowMergeOperation, int> incoming_edges;
193   for (const auto& it : merge_after) {
194     for (const auto& blocked : it.second) {
195       // Value is default initialized to 0.
196       incoming_edges[blocked] += 1;
197     }
198   }
199 
200   std::set<CowMergeOperation> free_operations;
201   for (const auto& op : operations_) {
202     if (incoming_edges.find(op) == incoming_edges.end()) {
203       free_operations.insert(op);
204     }
205   }
206 
207   std::vector<CowMergeOperation> merge_sequence;
208   std::set<CowMergeOperation> convert_to_raw;
209   while (!incoming_edges.empty()) {
210     if (!free_operations.empty()) {
211       merge_sequence.insert(
212           merge_sequence.end(), free_operations.begin(), free_operations.end());
213     } else {
214       auto to_convert = incoming_edges.begin()->first;
215       free_operations.insert(to_convert);
216       convert_to_raw.insert(to_convert);
217       LOG(INFO) << "Converting operation to raw " << to_convert;
218     }
219 
220     std::set<CowMergeOperation> next_free_operations;
221     for (const auto& op : free_operations) {
222       incoming_edges.erase(op);
223 
224       // Now that this particular operation is merged, other operations blocked
225       // by this one may be free. Decrement the count of blocking operations,
226       // and set up the free operations for the next iteration.
227       for (const auto& blocked : merge_after[op]) {
228         auto it = incoming_edges.find(blocked);
229         if (it == incoming_edges.end()) {
230           continue;
231         }
232 
233         auto blocking_transfer_count = &it->second;
234         if (*blocking_transfer_count <= 0) {
235           LOG(ERROR) << "Unexpected count in merge after map "
236                      << blocking_transfer_count;
237           return false;
238         }
239         // This operation is no longer blocked by anyone. Add it to the merge
240         // sequence in the next iteration.
241         *blocking_transfer_count -= 1;
242         if (*blocking_transfer_count == 0) {
243           next_free_operations.insert(blocked);
244         }
245       }
246     }
247 
248     LOG(INFO) << "Remaining transfers " << incoming_edges.size()
249               << ", free transfers " << free_operations.size()
250               << ", merge_sequence size " << merge_sequence.size();
251     free_operations = std::move(next_free_operations);
252   }
253 
254   if (!free_operations.empty()) {
255     merge_sequence.insert(
256         merge_sequence.end(), free_operations.begin(), free_operations.end());
257   }
258 
259   CHECK_EQ(operations_.size(), merge_sequence.size() + convert_to_raw.size());
260 
261   size_t blocks_in_sequence = 0;
262   for (const CowMergeOperation& transfer : merge_sequence) {
263     blocks_in_sequence += transfer.dst_extent().num_blocks();
264   }
265 
266   size_t blocks_in_raw = 0;
267   for (const CowMergeOperation& transfer : convert_to_raw) {
268     blocks_in_raw += transfer.dst_extent().num_blocks();
269   }
270 
271   LOG(INFO) << "Blocks in merge sequence " << blocks_in_sequence
272             << ", blocks in raw " << blocks_in_raw;
273   if (!ValidateSequence(merge_sequence)) {
274     return false;
275   }
276 
277   *sequence = std::move(merge_sequence);
278   return true;
279 }
280 
ValidateSequence(const std::vector<CowMergeOperation> & sequence)281 bool MergeSequenceGenerator::ValidateSequence(
282     const std::vector<CowMergeOperation>& sequence) {
283   LOG(INFO) << "Validating merge sequence";
284   ExtentRanges visited;
285   for (const auto& op : sequence) {
286     if (visited.OverlapsWithExtent(op.src_extent())) {
287       LOG(ERROR) << "Transfer violates the merge sequence " << op
288                  << "Visited extent ranges: ";
289       visited.Dump();
290       return false;
291     }
292 
293     CHECK(!visited.OverlapsWithExtent(op.dst_extent()))
294         << "dst extent should write only once.";
295     visited.AddExtent(op.dst_extent());
296   }
297 
298   return true;
299 }
300 
301 }  // namespace chromeos_update_engine
302