Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

io_tester: implement unlinking via blocks discarding #2141

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 112 additions & 6 deletions apps/io_tester/io_tester.cc
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ static thread_local std::default_random_engine random_generator(random_seed);
class context;
enum class request_type { seqread, seqwrite, randread, randwrite, append, cpu, unlink };

enum class unlink_type { ordinary, discard_blocks };

namespace std {

template <>
Expand Down Expand Up @@ -228,6 +230,7 @@ struct shard_info {
std::chrono::duration<float> think_after = 0ms;
std::chrono::duration<float> execution_time = 1ms;
seastar::scheduling_group scheduling_group = seastar::default_scheduling_group();
::unlink_type unlink_type = unlink_type::ordinary;
};

struct options {
Expand Down Expand Up @@ -699,11 +702,20 @@ class unlink_class_data : public class_data {
sstring _dir_path{};
uint64_t _file_id_to_remove{0u};

uint64_t _discard_block_size{32u << 20u};
std::optional<file> _currently_discarded_file;
uint64_t _discarded_file_size{0u};
uint64_t _next_block_to_discard{0u};

public:
unlink_class_data(job_config cfg) : class_data(std::move(cfg)) {
if (!_config.files_count.has_value()) {
throw std::runtime_error("request_type::unlink requires specifying 'files_count'");
}

if (blocks_discard_mode() && parallelism() > 1) {
throw std::runtime_error("unlink_class_data: unlink_type::discard_blocks is supported only for a single fiber");
}
}

future<> do_start(sstring path, directory_entry_type type) override {
Expand All @@ -720,12 +732,11 @@ class unlink_class_data : public class_data {
return make_ready_future<size_t>(0u);
}

const auto fname = get_filename(_file_id_to_remove);
++_file_id_to_remove;

return remove_file(fname).then([]{
return make_ready_future<size_t>(0u);
});
if (!blocks_discard_mode()) {
return issue_ordinary_unlink();
} else {
return issue_blocks_discarding_unlink();
}
}

void emit_results(YAML::Emitter& out) override {
Expand All @@ -742,6 +753,76 @@ class unlink_class_data : public class_data {
}

private:
bool blocks_discard_mode() const {
return _config.shard_info.unlink_type == unlink_type::discard_blocks;
}

future<size_t> issue_ordinary_unlink() {
const auto fname = get_filename(_file_id_to_remove);
++_file_id_to_remove;

return remove_file(fname).then([]{
return make_ready_future<size_t>(0u);
});
}

future<size_t> issue_blocks_discarding_unlink() {
return ensure_file_to_discard_open().then([this]() {
return discard_next_block();
}).then([this]() {
return close_file_if_all_blocks_discarded();
}).then([]() {
return make_ready_future<size_t>(0u);
});
}
Comment on lines +769 to +777
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly, the behavior of these lines of code differs from the behavior of fallocate --punch-hole tool.

When the workload is inspected via blktrace, the trim requests are issued only after the flush completes via xfsaild.

I also tested calling unlink() when all blocks are discarded. No difference.

When I added a call to fsync() after each discard is issued, then I saw trim requests issued with certain frequency.


future<> ensure_file_to_discard_open() {
if (_currently_discarded_file) {
return make_ready_future<>();
}

const auto fname = get_filename(_file_id_to_remove);
++_file_id_to_remove;

file_open_options options{};
options.append_is_unlikely = true;

return open_file_dma(fname, open_flags::rw, options).then([this, fname] (auto f) mutable {
_currently_discarded_file = f;

// The file lives as long as _currently_discarded_file object holds its handle.
return remove_file(fname).then([this]() mutable {
return _currently_discarded_file->size().then([this](uint64_t fsize) mutable {
_discarded_file_size = fsize;
return make_ready_future<>();
});
});
});
}

future<> discard_next_block() {
uint64_t offset = _next_block_to_discard * _discard_block_size;
uint64_t discard_end = std::min(offset + _discard_block_size, _discarded_file_size);
uint64_t length = discard_end - offset;

_next_block_to_discard++;

return _currently_discarded_file->discard(offset, length);
}

future<> close_file_if_all_blocks_discarded() {
uint64_t next_offset = _next_block_to_discard * _discard_block_size;
if (next_offset < _discarded_file_size) {
return make_ready_future<>();
}

auto f = *_currently_discarded_file;
_currently_discarded_file = std::nullopt;
_next_block_to_discard = 0u;

return f.close();
}

future<> stop_hook() override {
if (all_files_removed() || keep_files) {
return make_ready_future<>();
Expand All @@ -764,6 +845,10 @@ class unlink_class_data : public class_data {
}

bool all_files_removed() const {
if (blocks_discard_mode() && _currently_discarded_file) {
return false;
}

return files_count() <= _file_id_to_remove;
}

Expand Down Expand Up @@ -911,6 +996,24 @@ struct convert<request_type> {
}
};

template<>
struct convert<unlink_type> {
static bool decode(const Node& node, unlink_type& ut) {
static const std::unordered_map<std::string, unlink_type> mappings = {
{ "ordinary", unlink_type::ordinary },
{ "discard_blocks", unlink_type::discard_blocks },
};

auto unlink_type_str = node.as<std::string>();
if (!mappings.count(unlink_type_str)) {
return false;
}

ut = mappings.at(unlink_type_str);
return true;
}
};

template<>
struct convert<shard_info> {
static bool decode(const Node& node, shard_info& sl) {
Expand Down Expand Up @@ -944,6 +1047,9 @@ struct convert<shard_info> {
if (node["execution_time"]) {
sl.execution_time = node["execution_time"].as<duration_time>().time;
}
if (node["unlink_type"]) {
sl.unlink_type = node["unlink_type"].as<unlink_type>();
}
return true;
}
};
Expand Down