[cache_writeback] Support offset within the source and destination devices
This commit is contained in:
parent
76ac202463
commit
4c17076f09
@ -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
|
||||||
|
@ -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>;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user