Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(197)

Unified Diff: delta_diff_generator.cc

Issue 3597014: AU: reuse scratch blocks in delta generation, tolerate insufficient scratch. (Closed) Base URL: ssh://git@chromiumos-git/update_engine.git
Patch Set: address comments, fix code duplication Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « delta_diff_generator.h ('k') | delta_diff_generator_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: delta_diff_generator.cc
diff --git a/delta_diff_generator.cc b/delta_diff_generator.cc
index 4d67c4ed5779fa82108b15f133b45d23ec9bf3a5..676629aa03658b7490bd2e9430245d64ae281a4d 100644
--- a/delta_diff_generator.cc
+++ b/delta_diff_generator.cc
@@ -10,6 +10,7 @@
#include <sys/types.h>
#include <algorithm>
+#include <map>
#include <set>
#include <string>
#include <utility>
@@ -22,6 +23,7 @@
#include "update_engine/bzip.h"
#include "update_engine/cycle_breaker.h"
#include "update_engine/extent_mapper.h"
+#include "update_engine/extent_ranges.h"
#include "update_engine/file_writer.h"
#include "update_engine/filesystem_iterator.h"
#include "update_engine/graph_types.h"
@@ -33,6 +35,7 @@
#include "update_engine/utils.h"
using std::make_pair;
+using std::map;
using std::max;
using std::min;
using std::set;
@@ -135,14 +138,16 @@ bool AddInstallOpToBlocksVector(
return true;
}
-// For a given regular file which must exist at new_root + path, and may
-// exist at old_root + path, creates a new InstallOperation and adds it to
-// the graph. Also, populates the 'blocks' array as necessary.
-// Also, writes the data necessary to send the file down to the client
-// into data_fd, which has length *data_file_size. *data_file_size is
-// updated appropriately.
-// Returns true on success.
+// For a given regular file which must exist at new_root + path, and
+// may exist at old_root + path, creates a new InstallOperation and
+// adds it to the graph. Also, populates the |blocks| array as
+// necessary, if |blocks| is non-NULL. Also, writes the data
+// necessary to send the file down to the client into data_fd, which
+// has length *data_file_size. *data_file_size is updated
+// appropriately. If |existing_vertex| is no kInvalidIndex, use that
+// rather than allocating a new vertex. Returns true on success.
bool DeltaReadFile(Graph* graph,
+ Vertex::Index existing_vertex,
vector<Block>* blocks,
const string& old_root,
const string& new_root,
@@ -167,15 +172,20 @@ bool DeltaReadFile(Graph* graph,
*data_file_size += data.size();
// Now, insert into graph and blocks vector
- graph->resize(graph->size() + 1);
- graph->back().op = operation;
- CHECK(graph->back().op.has_type());
- graph->back().file_name = path;
-
- TEST_AND_RETURN_FALSE(AddInstallOpToBlocksVector(graph->back().op,
- blocks,
- *graph,
- graph->size() - 1));
+ Vertex::Index vertex = existing_vertex;
+ if (vertex == Vertex::kInvalidIndex) {
+ graph->resize(graph->size() + 1);
+ vertex = graph->size() - 1;
+ }
+ (*graph)[vertex].op = operation;
+ CHECK((*graph)[vertex].op.has_type());
+ (*graph)[vertex].file_name = path;
+
+ if (blocks)
+ TEST_AND_RETURN_FALSE(AddInstallOpToBlocksVector((*graph)[vertex].op,
+ blocks,
+ *graph,
+ vertex));
return true;
}
@@ -205,6 +215,7 @@ bool DeltaReadFiles(Graph* graph,
LOG(INFO) << "Encoding file " << fs_iter.GetPartialPath();
TEST_AND_RETURN_FALSE(DeltaReadFile(graph,
+ Vertex::kInvalidIndex,
blocks,
old_root,
new_root,
@@ -215,74 +226,23 @@ bool DeltaReadFiles(Graph* graph,
return true;
}
-// Attempts to find |block_count| blocks to use as scratch space. Returns true
-// on success. Right now we return exactly as many blocks as are required.
-//
-// TODO(adlr): Consider returning all scratch blocks, even if there are extras,
-// to make it easier for a scratch allocator to find contiguous regions for
-// specific scratch writes.
-bool FindScratchSpace(const vector<Block>& blocks,
- vector<Block>::size_type block_count,
- vector<Extent>* out) {
- // Scan |blocks| for blocks that are neither read, nor written. If we don't
- // find enough of those, look past the end of |blocks| till the end of the
- // partition. If we don't find |block_count| scratch blocks, return false.
- //
- // TODO(adlr): Return blocks that are written by operations that don't have
- // incoming edges (and thus, can be deferred until all old blocks are read by
- // other operations).
- vector<Extent> ret;
- vector<Block>::size_type blocks_found = 0;
- const size_t kPartitionBlocks = kRootFSPartitionSize / kBlockSize;
- for (vector<Block>::size_type i = 0;
- i < kPartitionBlocks && blocks_found < block_count; i++) {
- if (i >= blocks.size() ||
- (blocks[i].reader == Vertex::kInvalidIndex &&
- blocks[i].writer == Vertex::kInvalidIndex)) {
- graph_utils::AppendBlockToExtents(&ret, i);
- blocks_found++;
- }
- }
- LOG(INFO) << "found " << blocks_found << " scratch blocks";
- if (blocks_found == block_count) {
- out->swap(ret);
- return true;
- }
- return false;
-}
-
-// This class takes a collection of Extents and allows the client to
-// allocate space from these extents. The client must not request more
-// space then exists in the source extents. Space is allocated from the
-// beginning of the source extents on; no consideration is paid to
-// fragmentation.
-class LinearExtentAllocator {
+// This class allocates non-existent temp blocks, starting from
+// kTempBlockStart. Other code is responsible for converting these
+// temp blocks into real blocks, as the client can't read or write to
+// these blocks.
+class DummyExtentAllocator {
public:
- explicit LinearExtentAllocator(const vector<Extent>& extents)
- : extents_(extents),
- extent_index_(0),
- extent_blocks_allocated_(0) {}
+ explicit DummyExtentAllocator()
+ : next_block_(kTempBlockStart) {}
vector<Extent> Allocate(const uint64_t block_count) {
- vector<Extent> ret;
- for (uint64_t blocks = 0; blocks < block_count; blocks++) {
- CHECK_LT(extent_index_, extents_.size());
- CHECK_LT(extent_blocks_allocated_, extents_[extent_index_].num_blocks());
- graph_utils::AppendBlockToExtents(
- &ret,
- extents_[extent_index_].start_block() + extent_blocks_allocated_);
- extent_blocks_allocated_++;
- if (extent_blocks_allocated_ >= extents_[extent_index_].num_blocks()) {
- extent_blocks_allocated_ = 0;
- extent_index_++;
- }
- }
+ vector<Extent> ret(1);
+ ret[0].set_start_block(next_block_);
+ ret[0].set_num_blocks(block_count);
+ next_block_ += block_count;
return ret;
}
private:
- const vector<Extent> extents_;
- vector<Extent>::size_type extent_index_; // current Extent
- // number of blocks allocated from the current extent
- uint64_t extent_blocks_allocated_;
+ uint64_t next_block_;
};
// Reads blocks from image_path that are not yet marked as being written
@@ -296,7 +256,8 @@ bool ReadUnwrittenBlocks(const vector<Block>& blocks,
int blobs_fd,
off_t* blobs_length,
const string& image_path,
- DeltaArchiveManifest_InstallOperation* out_op) {
+ Vertex* vertex) {
+ DeltaArchiveManifest_InstallOperation* out_op = &vertex->op;
int image_fd = open(image_path.c_str(), O_RDONLY, 000);
TEST_AND_RETURN_FALSE_ERRNO(image_fd >= 0);
ScopedFdCloser image_fd_closer(&image_fd);
@@ -324,6 +285,9 @@ bool ReadUnwrittenBlocks(const vector<Block>& blocks,
for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
if (blocks[i].writer != Vertex::kInvalidIndex)
continue;
+ if (blocks[i].reader != Vertex::kInvalidIndex) {
+ graph_utils::AddReadBeforeDep(vertex, blocks[i].reader, i);
+ }
graph_utils::AppendBlockToExtents(&extents, i);
block_count++;
}
@@ -377,6 +341,8 @@ bool ReadUnwrittenBlocks(const vector<Block>& blocks,
out_op->set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
out_op->set_data_offset(*blobs_length);
out_op->set_data_length(compressed_data.size());
+ LOG(INFO) << "Rootfs non-data blocks compressed take up "
+ << compressed_data.size();
*blobs_length += compressed_data.size();
out_op->set_dst_length(kBlockSize * block_count);
DeltaDiffGenerator::StoreExtents(extents, out_op->mutable_dst_extents());
@@ -618,81 +584,85 @@ bool DeltaDiffGenerator::ReadFileToDiff(
return true;
}
-void DeltaDiffGenerator::SubstituteBlocks(
- DeltaArchiveManifest_InstallOperation* op,
- const vector<Extent>& remove_extents,
- const vector<Extent>& replace_extents) {
- // First, expand out the blocks that op reads from
- vector<uint64_t> read_blocks;
- for (int i = 0; i < op->src_extents_size(); i++) {
- const Extent& extent = op->src_extents(i);
+namespace {
+
+// Takes a collection (vector or RepeatedPtrField) of Extent and
+// returns a vector of the blocks referenced, in order.
+template<typename T>
+vector<uint64_t> ExpandExtents(const T& extents) {
+ vector<uint64_t> ret;
+ for (size_t i = 0, e = static_cast<size_t>(extents.size()); i != e; ++i) {
+ const Extent extent = graph_utils::GetElement(extents, i);
if (extent.start_block() == kSparseHole) {
- read_blocks.resize(read_blocks.size() + extent.num_blocks(), kSparseHole);
+ ret.resize(ret.size() + extent.num_blocks(), kSparseHole);
} else {
for (uint64_t block = extent.start_block();
block < (extent.start_block() + extent.num_blocks()); block++) {
- read_blocks.push_back(block);
+ ret.push_back(block);
}
}
}
+ return ret;
+}
+
+// Takes a vector of blocks and returns an equivalent vector of Extent
+// objects.
+vector<Extent> CompressExtents(const vector<uint64_t>& blocks) {
+ vector<Extent> new_extents;
+ for (vector<uint64_t>::const_iterator it = blocks.begin(), e = blocks.end();
+ it != e; ++it) {
+ graph_utils::AppendBlockToExtents(&new_extents, *it);
+ }
+ return new_extents;
+}
+
+} // namespace {}
+
+void DeltaDiffGenerator::SubstituteBlocks(
+ Vertex* vertex,
+ const vector<Extent>& remove_extents,
+ const vector<Extent>& replace_extents) {
+ // First, expand out the blocks that op reads from
+ vector<uint64_t> read_blocks = ExpandExtents(vertex->op.src_extents());
{
// Expand remove_extents and replace_extents
- vector<uint64_t> remove_extents_expanded;
- for (vector<Extent>::const_iterator it = remove_extents.begin();
- it != remove_extents.end(); ++it) {
- const Extent& extent = *it;
- for (uint64_t block = extent.start_block();
- block < (extent.start_block() + extent.num_blocks()); block++) {
- remove_extents_expanded.push_back(block);
- }
- }
- vector<uint64_t> replace_extents_expanded;
- for (vector<Extent>::const_iterator it = replace_extents.begin();
- it != replace_extents.end(); ++it) {
- const Extent& extent = *it;
- for (uint64_t block = extent.start_block();
- block < (extent.start_block() + extent.num_blocks()); block++) {
- replace_extents_expanded.push_back(block);
- }
- }
+ vector<uint64_t> remove_extents_expanded =
+ ExpandExtents(remove_extents);
+ vector<uint64_t> replace_extents_expanded =
+ ExpandExtents(replace_extents);
CHECK_EQ(remove_extents_expanded.size(), replace_extents_expanded.size());
+ map<uint64_t, uint64_t> conversion;
for (vector<uint64_t>::size_type i = 0;
i < replace_extents_expanded.size(); i++) {
- vector<uint64_t>::size_type index = 0;
- CHECK(utils::VectorIndexOf(read_blocks,
- remove_extents_expanded[i],
- &index));
- CHECK(read_blocks[index] == remove_extents_expanded[i]);
- read_blocks[index] = replace_extents_expanded[i];
+ conversion[remove_extents_expanded[i]] = replace_extents_expanded[i];
+ }
+ utils::ApplyMap(&read_blocks, conversion);
+ for (Vertex::EdgeMap::iterator it = vertex->out_edges.begin(),
+ e = vertex->out_edges.end(); it != e; ++it) {
+ vector<uint64_t> write_before_deps_expanded =
+ ExpandExtents(it->second.write_extents);
+ utils::ApplyMap(&write_before_deps_expanded, conversion);
+ it->second.write_extents = CompressExtents(write_before_deps_expanded);
}
}
// Convert read_blocks back to extents
- op->clear_src_extents();
- vector<Extent> new_extents;
- for (vector<uint64_t>::const_iterator it = read_blocks.begin();
- it != read_blocks.end(); ++it) {
- graph_utils::AppendBlockToExtents(&new_extents, *it);
- }
- DeltaDiffGenerator::StoreExtents(new_extents, op->mutable_src_extents());
+ vertex->op.clear_src_extents();
+ vector<Extent> new_extents = CompressExtents(read_blocks);
+ DeltaDiffGenerator::StoreExtents(new_extents,
+ vertex->op.mutable_src_extents());
}
bool DeltaDiffGenerator::CutEdges(Graph* graph,
- const vector<Block>& blocks,
- const set<Edge>& edges) {
- // First, find enough scratch space for the edges we'll be cutting.
- vector<Block>::size_type blocks_required = 0;
- for (set<Edge>::const_iterator it = edges.begin(); it != edges.end(); ++it) {
- blocks_required += graph_utils::EdgeWeight(*graph, *it);
- }
- vector<Extent> scratch_extents;
- LOG(INFO) << "requesting " << blocks_required << " blocks of scratch";
- TEST_AND_RETURN_FALSE(
- FindScratchSpace(blocks, blocks_required, &scratch_extents));
- LinearExtentAllocator scratch_allocator(scratch_extents);
+ const set<Edge>& edges,
+ vector<CutEdgeVertexes>* out_cuts) {
+ DummyExtentAllocator scratch_allocator;
+ vector<CutEdgeVertexes> cuts;
+ cuts.reserve(edges.size());
uint64_t scratch_blocks_used = 0;
for (set<Edge>::const_iterator it = edges.begin();
it != edges.end(); ++it) {
+ cuts.resize(cuts.size() + 1);
vector<Extent> old_extents =
(*graph)[it->first].out_edges[it->second].extents;
// Choose some scratch space
@@ -700,21 +670,33 @@ bool DeltaDiffGenerator::CutEdges(Graph* graph,
LOG(INFO) << "using " << graph_utils::EdgeWeight(*graph, *it)
<< " scratch blocks ("
<< scratch_blocks_used << ")";
- vector<Extent> scratch =
+ cuts.back().tmp_extents =
scratch_allocator.Allocate(graph_utils::EdgeWeight(*graph, *it));
// create vertex to copy original->scratch
+ cuts.back().new_vertex = graph->size();
graph->resize(graph->size() + 1);
+ cuts.back().old_src = it->first;
+ cuts.back().old_dst = it->second;
+
+ EdgeProperties& cut_edge_properties =
+ (*graph)[it->first].out_edges.find(it->second)->second;
+
+ // This should never happen, as we should only be cutting edges between
+ // real file nodes, and write-before relationships are created from
+ // a real file node to a temp copy node:
+ CHECK(cut_edge_properties.write_extents.empty())
+ << "Can't cut edge that has write-before relationship.";
// make node depend on the copy operation
(*graph)[it->first].out_edges.insert(make_pair(graph->size() - 1,
- EdgeProperties()));
+ cut_edge_properties));
// Set src/dst extents and other proto variables for copy operation
graph->back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
DeltaDiffGenerator::StoreExtents(
- (*graph)[it->first].out_edges[it->second].extents,
+ cut_edge_properties.extents,
graph->back().op.mutable_src_extents());
- DeltaDiffGenerator::StoreExtents(scratch,
+ DeltaDiffGenerator::StoreExtents(cuts.back().tmp_extents,
graph->back().op.mutable_dst_extents());
graph->back().op.set_src_length(
graph_utils::EdgeWeight(*graph, *it) * kBlockSize);
@@ -722,23 +704,26 @@ bool DeltaDiffGenerator::CutEdges(Graph* graph,
// make the dest node read from the scratch space
DeltaDiffGenerator::SubstituteBlocks(
- &((*graph)[it->second].op),
+ &((*graph)[it->second]),
(*graph)[it->first].out_edges[it->second].extents,
- scratch);
+ cuts.back().tmp_extents);
// delete the old edge
CHECK_EQ(1, (*graph)[it->first].out_edges.erase(it->second));
// Add an edge from dst to copy operation
- (*graph)[it->second].out_edges.insert(make_pair(graph->size() - 1,
- EdgeProperties()));
+ EdgeProperties write_before_edge_properties;
+ write_before_edge_properties.write_extents = cuts.back().tmp_extents;
+ (*graph)[it->second].out_edges.insert(
+ make_pair(graph->size() - 1, write_before_edge_properties));
}
+ out_cuts->swap(cuts);
return true;
}
// Stores all Extents in 'extents' into 'out'.
void DeltaDiffGenerator::StoreExtents(
- vector<Extent>& extents,
+ const vector<Extent>& extents,
google::protobuf::RepeatedPtrField<Extent>* out) {
for (vector<Extent>::const_iterator it = extents.begin();
it != extents.end(); ++it) {
@@ -774,6 +759,244 @@ void DeltaDiffGenerator::CreateEdges(Graph* graph,
}
}
+namespace {
+
+class SortCutsByTopoOrderLess {
+ public:
+ SortCutsByTopoOrderLess(vector<vector<Vertex::Index>::size_type>& table)
+ : table_(table) {}
+ bool operator()(const CutEdgeVertexes& a, const CutEdgeVertexes& b) {
+ return table_[a.old_dst] < table_[b.old_dst];
+ }
+ private:
+ vector<vector<Vertex::Index>::size_type>& table_;
+};
+
+} // namespace {}
+
+void DeltaDiffGenerator::GenerateReverseTopoOrderMap(
+ vector<Vertex::Index>& op_indexes,
+ vector<vector<Vertex::Index>::size_type>* reverse_op_indexes) {
+ vector<vector<Vertex::Index>::size_type> table(op_indexes.size());
+ for (vector<Vertex::Index>::size_type i = 0, e = op_indexes.size();
+ i != e; ++i) {
+ Vertex::Index node = op_indexes[i];
+ if (table.size() < (node + 1)) {
+ table.resize(node + 1);
+ }
+ table[node] = i;
+ }
+ reverse_op_indexes->swap(table);
+}
+
+void DeltaDiffGenerator::SortCutsByTopoOrder(vector<Vertex::Index>& op_indexes,
+ vector<CutEdgeVertexes>* cuts) {
+ // first, make a reverse lookup table.
+ vector<vector<Vertex::Index>::size_type> table;
+ GenerateReverseTopoOrderMap(op_indexes, &table);
+ SortCutsByTopoOrderLess less(table);
+ sort(cuts->begin(), cuts->end(), less);
+}
+
+void DeltaDiffGenerator::MoveFullOpsToBack(Graph* graph,
+ vector<Vertex::Index>* op_indexes) {
+ vector<Vertex::Index> ret;
+ vector<Vertex::Index> full_ops;
+ ret.reserve(op_indexes->size());
+ for (vector<Vertex::Index>::size_type i = 0, e = op_indexes->size(); i != e;
+ ++i) {
+ DeltaArchiveManifest_InstallOperation_Type type =
+ (*graph)[(*op_indexes)[i]].op.type();
+ if (type == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
+ type == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
+ full_ops.push_back((*op_indexes)[i]);
+ } else {
+ ret.push_back((*op_indexes)[i]);
+ }
+ }
+ LOG(INFO) << "Stats: " << full_ops.size() << " full ops out of "
+ << (full_ops.size() + ret.size()) << " total ops.";
+ ret.insert(ret.end(), full_ops.begin(), full_ops.end());
+ op_indexes->swap(ret);
+}
+
+namespace {
+
+template<typename T>
+bool TempBlocksExistInExtents(const T& extents) {
+ for (int i = 0, e = extents.size(); i < e; ++i) {
+ Extent extent = graph_utils::GetElement(extents, i);
+ uint64_t start = extent.start_block();
+ uint64_t num = extent.num_blocks();
+ if (start == kSparseHole)
+ continue;
+ if (start >= kTempBlockStart ||
+ (start + num) >= kTempBlockStart) {
+ LOG(ERROR) << "temp block!";
+ LOG(ERROR) << "start: " << start << ", num: " << num;
+ LOG(ERROR) << "kTempBlockStart: " << kTempBlockStart;
+ LOG(ERROR) << "returning true";
+ return true;
+ }
+ // check for wrap-around, which would be a bug:
+ CHECK(start <= (start + num));
+ }
+ return false;
+}
+
+} // namespace {}
+
+bool DeltaDiffGenerator::AssignTempBlocks(
+ Graph* graph,
+ const string& new_root,
+ int data_fd,
+ off_t* data_file_size,
+ vector<Vertex::Index>* op_indexes,
+ vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
+ vector<CutEdgeVertexes>& cuts) {
+ CHECK(!cuts.empty());
+ for (vector<CutEdgeVertexes>::size_type i = cuts.size() - 1, e = 0;
+ true ; --i) {
+ LOG(INFO) << "Fixing temp blocks in cut " << i
+ << ": old dst: " << cuts[i].old_dst << " new vertex: "
+ << cuts[i].new_vertex;
+ const uint64_t blocks_needed =
+ graph_utils::BlocksInExtents(cuts[i].tmp_extents);
+ LOG(INFO) << "Scanning for usable blocks (" << blocks_needed << " needed)";
+ // For now, just look for a single op w/ sufficient blocks, not
+ // considering blocks from outgoing read-before deps.
+ Vertex::Index node = cuts[i].old_dst;
+ DeltaArchiveManifest_InstallOperation_Type node_type =
+ (*graph)[node].op.type();
+ if (node_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
+ node_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
+ LOG(INFO) << "This was already converted to full, so skipping.";
+ // Delete the temp node and pointer to it from old src
+ if (!(*graph)[cuts[i].old_src].out_edges.erase(cuts[i].new_vertex)) {
+ LOG(INFO) << "Odd. node " << cuts[i].old_src << " didn't point to "
+ << cuts[i].new_vertex;
+ }
+ (*graph)[cuts[i].new_vertex].valid = false;
+ vector<Vertex::Index>::size_type new_topo_idx =
+ (*reverse_op_indexes)[cuts[i].new_vertex];
+ op_indexes->erase(op_indexes->begin() + new_topo_idx);
+ GenerateReverseTopoOrderMap(*op_indexes, reverse_op_indexes);
+ continue;
+ }
+ bool found_node = false;
+ for (vector<Vertex::Index>::size_type j = (*reverse_op_indexes)[node] + 1,
+ je = op_indexes->size(); j < je; ++j) {
+ Vertex::Index test_node = (*op_indexes)[j];
+ // See if this node has sufficient blocks
+ ExtentRanges ranges;
+ ranges.AddRepeatedExtents((*graph)[test_node].op.dst_extents());
+ ranges.SubtractExtent(ExtentForRange(
+ kTempBlockStart, kSparseHole - kTempBlockStart));
+ ranges.SubtractRepeatedExtents((*graph)[test_node].op.src_extents());
+ // For now, for simplicity, subtract out all blocks in read-before
+ // dependencies.
+ for (Vertex::EdgeMap::const_iterator edge_i =
+ (*graph)[test_node].out_edges.begin(),
+ edge_e = (*graph)[test_node].out_edges.end();
+ edge_i != edge_e; ++edge_i) {
+ ranges.SubtractExtents(edge_i->second.extents);
+ }
+
+ uint64_t blocks_found = ranges.blocks();
+ if (blocks_found < blocks_needed) {
+ if (blocks_found > 0)
+ LOG(INFO) << "insufficient blocks found in topo node " << j
+ << " (node " << (*op_indexes)[j] << "). Found only "
+ << blocks_found;
+ continue;
+ }
+ found_node = true;
+ LOG(INFO) << "Found sufficient blocks in topo node " << j
+ << " (node " << (*op_indexes)[j] << ")";
+ // Sub in the blocks, and make the node supplying the blocks
+ // depend on old_dst.
+ vector<Extent> real_extents =
+ ranges.GetExtentsForBlockCount(blocks_needed);
+
+ // Fix the old dest node w/ the real blocks
+ SubstituteBlocks(&(*graph)[node],
+ cuts[i].tmp_extents,
+ real_extents);
+
+ // Fix the new node w/ the real blocks. Since the new node is just a
+ // copy operation, we can replace all the dest extents w/ the real
+ // blocks.
+ DeltaArchiveManifest_InstallOperation *op =
+ &(*graph)[cuts[i].new_vertex].op;
+ op->clear_dst_extents();
+ StoreExtents(real_extents, op->mutable_dst_extents());
+
+ // Add an edge from the real-block supplier to the old dest block.
+ graph_utils::AddReadBeforeDepExtents(&(*graph)[test_node],
+ node,
+ real_extents);
+ break;
+ }
+ if (!found_node) {
+ // convert to full op
+ LOG(WARNING) << "Failed to find enough temp blocks for cut " << i
+ << " with old dest (graph node " << node
+ << "). Converting to a full op, at the expense of a "
+ << "good compression ratio.";
+ TEST_AND_RETURN_FALSE(ConvertCutToFullOp(graph,
+ cuts[i],
+ new_root,
+ data_fd,
+ data_file_size));
+ // move the full op to the back
+ vector<Vertex::Index> new_op_indexes;
+ for (vector<Vertex::Index>::const_iterator iter_i = op_indexes->begin(),
+ iter_e = op_indexes->end(); iter_i != iter_e; ++iter_i) {
+ if ((*iter_i == cuts[i].old_dst) || (*iter_i == cuts[i].new_vertex))
+ continue;
+ new_op_indexes.push_back(*iter_i);
+ }
+ new_op_indexes.push_back(cuts[i].old_dst);
+ op_indexes->swap(new_op_indexes);
+
+ GenerateReverseTopoOrderMap(*op_indexes, reverse_op_indexes);
+ }
+ if (i == e) {
+ // break out of for() loop
+ break;
+ }
+ }
+ return true;
+}
+
+bool DeltaDiffGenerator::NoTempBlocksRemain(const Graph& graph) {
+ size_t idx = 0;
+ for (Graph::const_iterator it = graph.begin(), e = graph.end(); it != e;
+ ++it, ++idx) {
+ if (!it->valid)
+ continue;
+ const DeltaArchiveManifest_InstallOperation& op = it->op;
+ if (TempBlocksExistInExtents(op.dst_extents()) ||
+ TempBlocksExistInExtents(op.src_extents())) {
+ LOG(INFO) << "bad extents in node " << idx;
+ LOG(INFO) << "so yeah";
+ return false;
+ }
+
+ // Check out-edges:
+ for (Vertex::EdgeMap::const_iterator jt = it->out_edges.begin(),
+ je = it->out_edges.end(); jt != je; ++jt) {
+ if (TempBlocksExistInExtents(jt->second.extents) ||
+ TempBlocksExistInExtents(jt->second.write_extents)) {
+ LOG(INFO) << "bad out edge in node " << idx;
+ LOG(INFO) << "so yeah";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
bool DeltaDiffGenerator::ReorderDataBlobs(
DeltaArchiveManifest* manifest,
const std::string& data_blobs_path,
@@ -814,6 +1037,88 @@ bool DeltaDiffGenerator::ReorderDataBlobs(
return true;
}
+bool DeltaDiffGenerator::ConvertCutToFullOp(Graph* graph,
+ const CutEdgeVertexes& cut,
+ const string& new_root,
+ int data_fd,
+ off_t* data_file_size) {
+ // Drop all incoming edges, keep all outgoing edges
+
+ // Keep all outgoing edges
+ Vertex::EdgeMap out_edges = (*graph)[cut.old_dst].out_edges;
+ graph_utils::DropWriteBeforeDeps(&out_edges);
+
+ TEST_AND_RETURN_FALSE(DeltaReadFile(graph,
+ cut.old_dst,
+ NULL,
+ "/-!@:&*nonexistent_path",
+ new_root,
+ (*graph)[cut.old_dst].file_name,
+ data_fd,
+ data_file_size));
+
+ (*graph)[cut.old_dst].out_edges = out_edges;
+
+ // Right now we don't have doubly-linked edges, so we have to scan
+ // the whole graph.
+ graph_utils::DropIncomingEdgesTo(graph, cut.old_dst);
+
+ // Delete temp node
+ (*graph)[cut.old_src].out_edges.erase(cut.new_vertex);
+ CHECK((*graph)[cut.old_dst].out_edges.find(cut.new_vertex) ==
+ (*graph)[cut.old_dst].out_edges.end());
+ (*graph)[cut.new_vertex].valid = false;
+ return true;
+}
+
+bool DeltaDiffGenerator::ConvertGraphToDag(Graph* graph,
+ const string& new_root,
+ int fd,
+ off_t* data_file_size,
+ vector<Vertex::Index>* final_order) {
+ CycleBreaker cycle_breaker;
+ LOG(INFO) << "Finding cycles...";
+ set<Edge> cut_edges;
+ cycle_breaker.BreakCycles(*graph, &cut_edges);
+ LOG(INFO) << "done finding cycles";
+ CheckGraph(*graph);
+
+ // Calculate number of scratch blocks needed
+
+ LOG(INFO) << "Cutting cycles...";
+ vector<CutEdgeVertexes> cuts;
+ TEST_AND_RETURN_FALSE(CutEdges(graph, cut_edges, &cuts));
+ LOG(INFO) << "done cutting cycles";
+ LOG(INFO) << "There are " << cuts.size() << " cuts.";
+ CheckGraph(*graph);
+
+ LOG(INFO) << "Creating initial topological order...";
+ TopologicalSort(*graph, final_order);
+ LOG(INFO) << "done with initial topo order";
+ CheckGraph(*graph);
+
+ LOG(INFO) << "Moving full ops to the back";
+ MoveFullOpsToBack(graph, final_order);
+ LOG(INFO) << "done moving full ops to back";
+
+ vector<vector<Vertex::Index>::size_type> inverse_final_order;
+ GenerateReverseTopoOrderMap(*final_order, &inverse_final_order);
+
+ if (!cuts.empty())
+ TEST_AND_RETURN_FALSE(AssignTempBlocks(graph,
+ new_root,
+ fd,
+ data_file_size,
+ final_order,
+ &inverse_final_order,
+ cuts));
+ LOG(INFO) << "Making sure all temp blocks have been allocated";
+ graph_utils::DumpGraph(*graph);
+ CHECK(NoTempBlocksRemain(*graph));
+ LOG(INFO) << "done making sure all temp blocks are allocated";
+ return true;
+}
+
bool DeltaDiffGenerator::GenerateDeltaUpdateFile(
const string& old_root,
const string& old_image,
@@ -857,7 +1162,7 @@ bool DeltaDiffGenerator::GenerateDeltaUpdateFile(
vector<DeltaArchiveManifest_InstallOperation> kernel_ops;
- DeltaArchiveManifest_InstallOperation final_op;
+ vector<Vertex::Index> final_order;
{
int fd;
TEST_AND_RETURN_FALSE(
@@ -871,13 +1176,15 @@ bool DeltaDiffGenerator::GenerateDeltaUpdateFile(
new_root,
fd,
&data_file_size));
+ LOG(INFO) << "done reading normal files";
CheckGraph(graph);
+ graph.resize(graph.size() + 1);
TEST_AND_RETURN_FALSE(ReadUnwrittenBlocks(blocks,
fd,
&data_file_size,
new_image,
- &final_op));
+ &graph.back()));
// Read kernel partition
TEST_AND_RETURN_FALSE(DeltaCompressKernelPartition(old_kernel_part,
@@ -885,42 +1192,26 @@ bool DeltaDiffGenerator::GenerateDeltaUpdateFile(
&kernel_ops,
fd,
&data_file_size));
- }
- CheckGraph(graph);
-
- LOG(INFO) << "Creating edges...";
- CreateEdges(&graph, blocks);
- CheckGraph(graph);
- CycleBreaker cycle_breaker;
- LOG(INFO) << "Finding cycles...";
- set<Edge> cut_edges;
- cycle_breaker.BreakCycles(graph, &cut_edges);
- CheckGraph(graph);
-
- // Calculate number of scratch blocks needed
+ LOG(INFO) << "done reading kernel";
+ CheckGraph(graph);
- LOG(INFO) << "Cutting cycles...";
- TEST_AND_RETURN_FALSE(CutEdges(&graph, blocks, cut_edges));
- CheckGraph(graph);
+ LOG(INFO) << "Creating edges...";
+ CreateEdges(&graph, blocks);
+ LOG(INFO) << "Done creating edges";
+ CheckGraph(graph);
- vector<Vertex::Index> final_order;
- LOG(INFO) << "Ordering...";
- TopologicalSort(graph, &final_order);
- CheckGraph(graph);
+ TEST_AND_RETURN_FALSE(ConvertGraphToDag(&graph,
+ new_root,
+ fd,
+ &data_file_size,
+ &final_order));
+ }
// Convert to protobuf Manifest object
DeltaArchiveManifest manifest;
CheckGraph(graph);
InstallOperationsToManifest(graph, final_order, kernel_ops, &manifest);
- {
- // Write final operation
- DeltaArchiveManifest_InstallOperation* op =
- manifest.add_install_operations();
- *op = final_op;
- CHECK(op->has_type());
- LOG(INFO) << "final op length: " << op->data_length();
- }
CheckGraph(graph);
manifest.set_block_size(kBlockSize);
« no previous file with comments | « delta_diff_generator.h ('k') | delta_diff_generator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698