Make restore validation fast by using a map
Test: Successfully restores device over reboots Change-Id: I4f1c5bbe6c07697a925a1a4efb92aefd15b61332
This commit is contained in:
parent
2268c285a5
commit
d41a939d51
1 changed files with 58 additions and 28 deletions
|
@ -242,9 +242,9 @@ const int kSectorSize = 512;
|
||||||
typedef uint64_t sector_t;
|
typedef uint64_t sector_t;
|
||||||
|
|
||||||
struct log_entry {
|
struct log_entry {
|
||||||
sector_t source;
|
sector_t source; // in sectors of size kSectorSize
|
||||||
sector_t dest;
|
sector_t dest; // in sectors of size kSectorSize
|
||||||
uint32_t size;
|
uint32_t size; // in bytes
|
||||||
uint32_t checksum;
|
uint32_t checksum;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
@ -313,21 +313,45 @@ void crc32(const void* data, size_t n_bytes, uint32_t* crc) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void read(std::fstream& device, std::vector<log_entry> const& logs, sector_t sector, char* buffer,
|
// A map of relocations.
|
||||||
uint32_t block_size) {
|
// The map must be initialized so that relocations[0] = 0
|
||||||
// Crude approach at first where we do this sector by sector and just scan
|
// During restore, we replay the log records in reverse, copying from dest to
|
||||||
// the entire logs for remappings each time
|
// source
|
||||||
for (auto l = logs.rbegin(); l != logs.rend(); l++)
|
// To validate, we must be able to read the 'dest' sectors as though they had
|
||||||
if (sector >= l->source && (sector - l->source) * kSectorSize < l->size)
|
// been copied but without actually copying. This map represents how the sectors
|
||||||
sector = sector - l->source + l->dest;
|
// would have been moved. To read a sector s, find the index <= s and read
|
||||||
|
// relocations[index] + s - index
|
||||||
|
typedef std::map<sector_t, sector_t> Relocations;
|
||||||
|
|
||||||
device.seekg(sector * kSectorSize);
|
void relocate(Relocations& relocations, sector_t dest, sector_t source, int count) {
|
||||||
device.read(buffer, block_size);
|
// Find first one we're equal to or greater than
|
||||||
|
auto s = --relocations.upper_bound(source);
|
||||||
|
|
||||||
|
// Take slice
|
||||||
|
Relocations slice;
|
||||||
|
slice[dest] = source - s->first + s->second;
|
||||||
|
++s;
|
||||||
|
|
||||||
|
// Add rest of elements
|
||||||
|
for (; s != relocations.end() && s->first < source + count; ++s)
|
||||||
|
slice[dest - source + s->first] = s->second;
|
||||||
|
|
||||||
|
// Split range at end of dest
|
||||||
|
auto dest_end = --relocations.upper_bound(dest + count);
|
||||||
|
relocations[dest + count] = dest + count - dest_end->first + dest_end->second;
|
||||||
|
|
||||||
|
// Remove all elements in [dest, dest + count)
|
||||||
|
relocations.erase(relocations.lower_bound(dest), relocations.lower_bound(dest + count));
|
||||||
|
|
||||||
|
// Add new elements
|
||||||
|
relocations.insert(slice.begin(), slice.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read from the device as though we were restoring, even if we are validating
|
// Read from the device
|
||||||
std::vector<char> read(std::fstream& device, std::vector<log_entry> const& logs, bool validating,
|
// If we are validating, the read occurs as though the relocations had happened
|
||||||
sector_t sector, uint32_t size, uint32_t block_size) {
|
std::vector<char> relocatedRead(std::fstream& device, Relocations const& relocations,
|
||||||
|
bool validating, sector_t sector, uint32_t size,
|
||||||
|
uint32_t block_size) {
|
||||||
if (!validating) {
|
if (!validating) {
|
||||||
std::vector<char> buffer(size);
|
std::vector<char> buffer(size);
|
||||||
device.seekg(sector * kSectorSize);
|
device.seekg(sector * kSectorSize);
|
||||||
|
@ -336,8 +360,11 @@ std::vector<char> read(std::fstream& device, std::vector<log_entry> const& logs,
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> buffer(size);
|
std::vector<char> buffer(size);
|
||||||
for (uint32_t i = 0; i < size; i += block_size, sector += block_size / kSectorSize)
|
for (uint32_t i = 0; i < size; i += block_size, sector += block_size / kSectorSize) {
|
||||||
read(device, logs, sector, &buffer[i], block_size);
|
auto relocation = --relocations.upper_bound(sector);
|
||||||
|
device.seekg((sector + relocation->second - relocation->first) * kSectorSize);
|
||||||
|
device.read(&buffer[i], block_size);
|
||||||
|
}
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
@ -349,7 +376,8 @@ Status cp_restoreCheckpoint(const std::string& blockDevice) {
|
||||||
std::string action = "Validating";
|
std::string action = "Validating";
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
std::vector<log_entry> logs;
|
Relocations relocations;
|
||||||
|
relocations[0] = 0;
|
||||||
Status status = Status::ok();
|
Status status = Status::ok();
|
||||||
|
|
||||||
LOG(INFO) << action << " checkpoint on " << blockDevice;
|
LOG(INFO) << action << " checkpoint on " << blockDevice;
|
||||||
|
@ -369,8 +397,8 @@ Status cp_restoreCheckpoint(const std::string& blockDevice) {
|
||||||
LOG(INFO) << action << " " << original_ls.sequence << " log sectors";
|
LOG(INFO) << action << " " << original_ls.sequence << " log sectors";
|
||||||
|
|
||||||
for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) {
|
for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) {
|
||||||
auto buffer =
|
auto buffer = relocatedRead(device, relocations, validating, 0, original_ls.block_size,
|
||||||
read(device, logs, validating, 0, original_ls.block_size, original_ls.block_size);
|
original_ls.block_size);
|
||||||
log_sector_v1_0 const& ls = *reinterpret_cast<log_sector_v1_0*>(&buffer[0]);
|
log_sector_v1_0 const& ls = *reinterpret_cast<log_sector_v1_0*>(&buffer[0]);
|
||||||
if (ls.magic != kMagic) {
|
if (ls.magic != kMagic) {
|
||||||
LOG(ERROR) << "No magic!";
|
LOG(ERROR) << "No magic!";
|
||||||
|
@ -399,10 +427,12 @@ Status cp_restoreCheckpoint(const std::string& blockDevice) {
|
||||||
reinterpret_cast<log_entry*>(&buffer[ls.header_size]) + ls.count - 1;
|
reinterpret_cast<log_entry*>(&buffer[ls.header_size]) + ls.count - 1;
|
||||||
le >= reinterpret_cast<log_entry*>(&buffer[ls.header_size]); --le) {
|
le >= reinterpret_cast<log_entry*>(&buffer[ls.header_size]); --le) {
|
||||||
// This is very noisy - limit to DEBUG only
|
// This is very noisy - limit to DEBUG only
|
||||||
LOG(DEBUG) << action << " " << le->size << " bytes from sector " << le->dest
|
LOG(VERBOSE) << action << " " << le->size << " bytes from sector " << le->dest
|
||||||
<< " to " << le->source << " with checksum " << std::hex << le->checksum;
|
<< " to " << le->source << " with checksum " << std::hex
|
||||||
|
<< le->checksum;
|
||||||
|
|
||||||
auto buffer = read(device, logs, validating, le->dest, le->size, ls.block_size);
|
auto buffer = relocatedRead(device, relocations, validating, le->dest, le->size,
|
||||||
|
ls.block_size);
|
||||||
uint32_t checksum = le->source / (ls.block_size / kSectorSize);
|
uint32_t checksum = le->source / (ls.block_size / kSectorSize);
|
||||||
for (size_t i = 0; i < le->size; i += ls.block_size) {
|
for (size_t i = 0; i < le->size; i += ls.block_size) {
|
||||||
crc32(&buffer[i], ls.block_size, &checksum);
|
crc32(&buffer[i], ls.block_size, &checksum);
|
||||||
|
@ -414,9 +444,9 @@ Status cp_restoreCheckpoint(const std::string& blockDevice) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
logs.push_back(*le);
|
if (validating) {
|
||||||
|
relocate(relocations, le->source, le->dest, le->size / kSectorSize);
|
||||||
if (!validating) {
|
} else {
|
||||||
device.seekg(le->source * kSectorSize);
|
device.seekg(le->source * kSectorSize);
|
||||||
device.write(&buffer[0], le->size);
|
device.write(&buffer[0], le->size);
|
||||||
}
|
}
|
||||||
|
@ -430,8 +460,8 @@ Status cp_restoreCheckpoint(const std::string& blockDevice) {
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward";
|
LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward";
|
||||||
auto buffer = read(device, logs, false, original_ls.sector0, original_ls.block_size,
|
auto buffer = relocatedRead(device, relocations, false, original_ls.sector0,
|
||||||
original_ls.block_size);
|
original_ls.block_size, original_ls.block_size);
|
||||||
device.seekg(0);
|
device.seekg(0);
|
||||||
device.write(&buffer[0], original_ls.block_size);
|
device.write(&buffer[0], original_ls.block_size);
|
||||||
return Status::ok();
|
return Status::ok();
|
||||||
|
|
Loading…
Reference in a new issue