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