[cache_writeback] Support offset within the source and destination devices

This commit is contained in:
Ming-Hung Tsai 2021-01-18 01:11:01 +08:00
parent 76ac202463
commit 4c17076f09
4 changed files with 122 additions and 18 deletions

View File

@ -10,12 +10,14 @@ using namespace std;
copier::copier(io_engine &engine, copier::copier(io_engine &engine,
string const &src, string const &dest, string const &src, string const &dest,
sector_t block_size, size_t mem) sector_t block_size, size_t mem,
sector_t src_offset, sector_t dest_offset)
: pool_(block_size * 512, mem, PAGE_SIZE), : pool_(block_size * 512, mem, PAGE_SIZE),
block_size_(block_size), block_size_(block_size),
engine_(engine), engine_(engine),
src_handle_(engine_.open_file(src, io_engine::M_READ_ONLY)), src_handle_(engine_.open_file(src, io_engine::M_READ_ONLY)),
dest_handle_(engine_.open_file(dest, io_engine::M_READ_WRITE)), dest_handle_(engine_.open_file(dest, io_engine::M_READ_WRITE)),
src_offset_(src_offset), dest_offset_(dest_offset),
genkey_count_(0) genkey_count_(0)
{ {
} }
@ -45,8 +47,8 @@ copier::issue(copy_op const &op)
auto r = engine_.issue_io(src_handle_, auto r = engine_.issue_io(src_handle_,
io_engine::D_READ, io_engine::D_READ,
to_sector(op.src_b), to_src_sector(op.src_b),
to_sector(op.src_e), to_src_sector(op.src_e),
data, data,
key); key);
@ -151,8 +153,8 @@ copier::wait_successful(io_engine::wait_result const &p)
j.op.read_complete = true; j.op.read_complete = true;
if (!engine_.issue_io(dest_handle_, if (!engine_.issue_io(dest_handle_,
io_engine::D_WRITE, io_engine::D_WRITE,
to_sector(j.op.dest_b), to_dest_sector(j.op.dest_b),
to_sector(j.op.dest_b + (j.op.src_e - j.op.src_b)), to_dest_sector(j.op.dest_b + (j.op.src_e - j.op.src_b)),
j.data, j.data,
it->first)) { it->first)) {
complete(j); complete(j);
@ -177,9 +179,15 @@ copier::complete(copy_job const &j)
} }
sector_t sector_t
copier::to_sector(block_address b) const copier::to_src_sector(block_address b) const
{ {
return b * block_size_; return src_offset_ + b * block_size_;
}
sector_t
copier::to_dest_sector(block_address b) const
{
return dest_offset_ + b * block_size_;
} }
unsigned unsigned

View File

@ -61,13 +61,22 @@ namespace bcache {
public: public:
copier(io_engine &engine, copier(io_engine &engine,
std::string const &src, std::string const &dest, std::string const &src, std::string const &dest,
sector_t block_size, size_t mem); sector_t block_size, size_t mem,
sector_t src_offset, sector_t dest_offset);
~copier(); ~copier();
sector_t get_block_size() const { sector_t get_block_size() const {
return block_size_; return block_size_;
} }
sector_t get_src_offset() const {
return src_offset_;
}
sector_t get_dest_offset() const {
return dest_offset_;
}
// Blocks if out of memory. // Blocks if out of memory.
void issue(copy_op const &op); void issue(copy_op const &op);
@ -83,7 +92,8 @@ namespace bcache {
void wait_(); void wait_();
void complete(copy_job const &j); void complete(copy_job const &j);
sector_t to_sector(block_address b) const; sector_t to_src_sector(block_address b) const;
sector_t to_dest_sector(block_address b) const;
unsigned genkey(); unsigned genkey();
mempool pool_; mempool pool_;
@ -91,6 +101,8 @@ namespace bcache {
io_engine &engine_; io_engine &engine_;
io_engine::handle src_handle_; io_engine::handle src_handle_;
io_engine::handle dest_handle_; io_engine::handle dest_handle_;
sector_t src_offset_;
sector_t dest_offset_;
unsigned genkey_count_; unsigned genkey_count_;
using job_map = std::map<unsigned, copy_job>; using job_map = std::map<unsigned, copy_job>;

View File

@ -31,6 +31,8 @@ namespace {
flags() flags()
: cache_size(4 * 1024 * 1024), : cache_size(4 * 1024 * 1024),
sort_buffers(16 * 1024), sort_buffers(16 * 1024),
origin_dev_offset(0),
fast_dev_offset(0),
list_failed_blocks(false), list_failed_blocks(false),
update_metadata(true) { update_metadata(true) {
} }
@ -52,6 +54,8 @@ namespace {
maybe_string metadata_dev; maybe_string metadata_dev;
maybe_string origin_dev; maybe_string origin_dev;
maybe_string fast_dev; maybe_string fast_dev;
sector_t origin_dev_offset;
sector_t fast_dev_offset;
bool list_failed_blocks; bool list_failed_blocks;
bool update_metadata; bool update_metadata;
}; };
@ -303,7 +307,9 @@ namespace {
unsigned max_ios = f.cache_size / (md.sb_.data_block_size << SECTOR_SHIFT); unsigned max_ios = f.cache_size / (md.sb_.data_block_size << SECTOR_SHIFT);
aio_engine engine(max_ios); aio_engine engine(max_ios);
copier c(engine, *f.fast_dev, *f.origin_dev, copier c(engine, *f.fast_dev, *f.origin_dev,
md.sb_.data_block_size, f.cache_size); md.sb_.data_block_size, f.cache_size,
f.fast_dev_offset >> SECTOR_SHIFT,
f.origin_dev_offset >> SECTOR_SHIFT);
auto bar = create_progress_bar("Copying data"); auto bar = create_progress_bar("Copying data");
copy_visitor cv(c, f.sort_buffers, clean_shutdown(md), f.list_failed_blocks, copy_visitor cv(c, f.sort_buffers, clean_shutdown(md), f.list_failed_blocks,
@ -364,6 +370,8 @@ cache_writeback_cmd::usage(std::ostream &out) const
<< "\t\t--buffer-size-meg <size>\n" << "\t\t--buffer-size-meg <size>\n"
<< "\t\t--list-failed-blocks\n" << "\t\t--list-failed-blocks\n"
<< "\t\t--no-metadata-update\n" << "\t\t--no-metadata-update\n"
<< "\t\t--origin-device-offset <bytes>\n"
<< "\t\t--fast-device-offset <bytes>\n"
<< "Options:\n" << "Options:\n"
<< " {-h|--help}\n" << " {-h|--help}\n"
<< " {-V|--version}" << endl; << " {-V|--version}" << endl;
@ -382,6 +390,8 @@ cache_writeback_cmd::run(int argc, char **argv)
{ "buffer-size-meg", required_argument, NULL, 3 }, { "buffer-size-meg", required_argument, NULL, 3 },
{ "list-failed-blocks", no_argument, NULL, 4 }, { "list-failed-blocks", no_argument, NULL, 4 },
{ "no-metadata-update", no_argument, NULL, 5 }, { "no-metadata-update", no_argument, NULL, 5 },
{ "origin-device-offset", required_argument, NULL, 6 },
{ "fast-device-offset", required_argument, NULL, 7 },
{ "help", no_argument, NULL, 'h'}, { "help", no_argument, NULL, 'h'},
{ "version", no_argument, NULL, 'V'}, { "version", no_argument, NULL, 'V'},
{ NULL, no_argument, NULL, 0 } { NULL, no_argument, NULL, 0 }
@ -413,6 +423,14 @@ cache_writeback_cmd::run(int argc, char **argv)
fs.update_metadata = false; fs.update_metadata = false;
break; break;
case 6:
fs.origin_dev_offset = parse_uint64(optarg, "origin dev offset");
break;
case 7:
fs.fast_dev_offset = parse_uint64(optarg, "fast dev offset");
break;
case 'h': case 'h':
usage(cout); usage(cout);
return 0; return 0;
@ -452,6 +470,13 @@ cache_writeback_cmd::run(int argc, char **argv)
return 1; return 1;
} }
if (fs.origin_dev_offset & (SECTOR_SHIFT - 1) ||
fs.fast_dev_offset & (SECTOR_SHIFT - 1)) {
cerr << "Offset must be sector-aligned\n\n";
usage(cerr);
return 1;
}
return writeback(fs); return writeback(fs);
} }

View File

@ -59,6 +59,12 @@ namespace {
} }
unique_ptr<copier> make_copier() { unique_ptr<copier> make_copier() {
return make_copier(BLOCK_SIZE, 0, 0);
}
unique_ptr<copier> make_copier(sector_t block_size,
sector_t src_offset,
sector_t dest_offset) {
EXPECT_CALL(engine_, open_file(src_file_, io_engine::M_READ_ONLY, io_engine::EXCLUSIVE)). EXPECT_CALL(engine_, open_file(src_file_, io_engine::M_READ_ONLY, io_engine::EXCLUSIVE)).
WillOnce(Return(SRC_HANDLE)); WillOnce(Return(SRC_HANDLE));
EXPECT_CALL(engine_, open_file(dest_file_, io_engine::M_READ_WRITE, io_engine::EXCLUSIVE)). EXPECT_CALL(engine_, open_file(dest_file_, io_engine::M_READ_WRITE, io_engine::EXCLUSIVE)).
@ -69,20 +75,22 @@ namespace {
return unique_ptr<copier>(new copier(engine_, src_file_, return unique_ptr<copier>(new copier(engine_, src_file_,
dest_file_, dest_file_,
BLOCK_SIZE, 1 * 1024 * 1024)); block_size, 1 * 1024 * 1024,
src_offset, dest_offset));
} }
static optional<wait_result> make_wr(bool success, unsigned context) { static optional<wait_result> make_wr(bool success, unsigned context) {
return optional<wait_result>(wait_result(success, context)); return optional<wait_result>(wait_result(success, context));
} }
// issue a copy_op, and wait for its completion synchronously
void issue_successful_op(copier &c, copy_op &op, unsigned context) { void issue_successful_op(copier &c, copy_op &op, unsigned context) {
InSequence dummy; InSequence dummy;
unsigned nr_pending = c.nr_pending(); unsigned nr_pending = c.nr_pending();
EXPECT_CALL(engine_, issue_io(SRC_HANDLE, io_engine::D_READ, EXPECT_CALL(engine_, issue_io(SRC_HANDLE, io_engine::D_READ,
op.src_b * BLOCK_SIZE, expected_src_sector(c, op.src_b),
op.src_e * BLOCK_SIZE, _, context)). expected_src_sector(c, op.src_e), _, context)).
WillOnce(Return(true)); WillOnce(Return(true));
c.issue(op); c.issue(op);
@ -92,8 +100,9 @@ namespace {
WillOnce(Return(make_wr(true, context))); WillOnce(Return(make_wr(true, context)));
EXPECT_CALL(engine_, issue_io(DEST_HANDLE, io_engine::D_WRITE, EXPECT_CALL(engine_, issue_io(DEST_HANDLE, io_engine::D_WRITE,
op.dest_b * BLOCK_SIZE, expected_dest_sector(c, op.dest_b),
(op.dest_b + (op.src_e - op.src_b)) * BLOCK_SIZE, _, context)). expected_dest_sector(c, op.dest_b + (op.src_e - op.src_b)),
_, context)).
WillOnce(Return(true)); WillOnce(Return(true));
EXPECT_CALL(engine_, wait()). EXPECT_CALL(engine_, wait()).
@ -108,17 +117,41 @@ namespace {
unsigned const SRC_HANDLE = 10; unsigned const SRC_HANDLE = 10;
unsigned const DEST_HANDLE = 11; unsigned const DEST_HANDLE = 11;
StrictMock<io_engine_mock> engine_;
private:
sector_t expected_src_sector(const copier &c, block_address b) {
return c.get_src_offset() + b * c.get_block_size();
}
sector_t expected_dest_sector(const copier &c, block_address b) {
return c.get_dest_offset() + b * c.get_block_size();
}
string src_file_; string src_file_;
string dest_file_; string dest_file_;
StrictMock<io_engine_mock> engine_;
}; };
} }
//---------------------------------------------------------------- //----------------------------------------------------------------
TEST_F(CopierTests, empty_test) TEST_F(CopierTests, create_default_copier)
{ {
auto c = make_copier(); auto c = make_copier();
ASSERT_EQ(c->get_block_size(), BLOCK_SIZE);
ASSERT_EQ(c->get_src_offset(), 0u);
ASSERT_EQ(c->get_dest_offset(), 0u);
}
TEST_F(CopierTests, create_copier_with_offsets)
{
sector_t src_offset = 2048;
sector_t dest_offset = 8192;
auto c = make_copier(BLOCK_SIZE, src_offset, dest_offset);
ASSERT_EQ(c->get_block_size(), BLOCK_SIZE);
ASSERT_EQ(c->get_src_offset(), src_offset);
ASSERT_EQ(c->get_dest_offset(), dest_offset);
} }
TEST_F(CopierTests, successful_copy) TEST_F(CopierTests, successful_copy)
@ -130,6 +163,17 @@ TEST_F(CopierTests, successful_copy)
issue_successful_op(*c, op1, 0); issue_successful_op(*c, op1, 0);
} }
TEST_F(CopierTests, successful_copy_with_offsets)
{
copy_op op1(0, 1, 0);
auto c = make_copier(BLOCK_SIZE, 2048, 8192);
issue_successful_op(*c, op1, 0);
}
// Verify copier's error handling against unsucessful engine operations
// at different stages.
// Test the first stage (issue_read (failed) => read => issue_write => write)
TEST_F(CopierTests, unsuccessful_issue_read) TEST_F(CopierTests, unsuccessful_issue_read)
{ {
copy_op op1(0, 1, 0); copy_op op1(0, 1, 0);
@ -147,6 +191,7 @@ TEST_F(CopierTests, unsuccessful_issue_read)
ASSERT_FALSE(mop->success()); ASSERT_FALSE(mop->success());
} }
// Test the second stage (issue_read => read (failed) => issue_write => write)
TEST_F(CopierTests, unsuccessful_read) TEST_F(CopierTests, unsuccessful_read)
{ {
copy_op op1(0, 1, 0); copy_op op1(0, 1, 0);
@ -167,6 +212,7 @@ TEST_F(CopierTests, unsuccessful_read)
ASSERT_FALSE(mop->success()); ASSERT_FALSE(mop->success());
} }
// Test the third stage (issue_read => read => issue_write (failed) => write)
TEST_F(CopierTests, unsuccessful_issue_write) TEST_F(CopierTests, unsuccessful_issue_write)
{ {
copy_op op1(0, 1, 0); copy_op op1(0, 1, 0);
@ -191,6 +237,7 @@ TEST_F(CopierTests, unsuccessful_issue_write)
ASSERT_FALSE(mop->success()); ASSERT_FALSE(mop->success());
} }
// Test the last stage (issue_read => read => issue_write => write (failed))
TEST_F(CopierTests, unsuccessful_write) TEST_F(CopierTests, unsuccessful_write)
{ {
// Copy first block // Copy first block
@ -237,6 +284,18 @@ TEST_F(CopierTests, copy_different_blocks)
} }
} }
TEST_F(CopierTests, copy_different_blocks_with_offsets)
{
sector_t src_offset = 2048;
sector_t dest_offset = 8192;
auto c = make_copier(BLOCK_SIZE, src_offset, dest_offset);
for (unsigned i = 0; i < 5000; i++) {
copy_op op(i, i + 1, i);
issue_successful_op(*c, op, i);
}
}
TEST_F(CopierTests, wait_can_timeout) TEST_F(CopierTests, wait_can_timeout)
{ {
copy_op op1(0, 1, 0); copy_op op1(0, 1, 0);