From 6eecaa2f51691cfbbb62f1b1e53e009f381b8bb0 Mon Sep 17 00:00:00 2001 From: Roy Guo Date: Fri, 17 Sep 2021 10:14:33 +0800 Subject: [PATCH] env: Add Zoned Namespace SSD support on TerarkDB (#129) * env: Add Zoned Namespace SSD support on TerarkDB Support zone namespace SSD by transplanting and modifying ZenFS from Hans Holmberg of WesternDigital Inc. https://github.com/westerndigitalcorporation/zenfs Since TerarkDB is based on RocksDB v5.18.3, we also made some adoption to the codebase. In this version, the `zenfs` is not fully functional yet but prepared the basic function & utilities for next stage integration. Signed-off-by: Changlong Chen Signed-off-by: Kuankuan Guo Co-authored-by: Hans Holmberg Co-authored-by: Yuanliang Wang Co-authored-by: Changlong Chen --- .gitignore | 1 + .gitmodules | 3 + CMakeLists.txt | 25 +- bench.py | 1 + build_tools/build_detect_platform | 13 + env/io_posix.cc | 37 +++ include/rocksdb/env.h | 4 + include/rocksdb/terark_namespace.h.in | 5 + src.mk | 3 + table/terark_zip_table.cc | 12 +- third-party/zenfs | 1 + tools/db_bench_tool.cc | 26 +- tools/zenfs.cc | 32 +++ tools/zenfs_tool.cc | 397 ++++++++++++++++++++++++++ util/split_dev.sh | 10 + 15 files changed, 563 insertions(+), 7 deletions(-) create mode 160000 third-party/zenfs create mode 100644 tools/zenfs.cc create mode 100644 tools/zenfs_tool.cc create mode 100755 util/split_dev.sh diff --git a/.gitignore b/.gitignore index 7ba9df8218..202ce073ad 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ build/ ldb manifest_dump +zenfs sst_dump blob_dump column_aware_encoding_exp diff --git a/.gitmodules b/.gitmodules index c1258646d1..e2b4424c3f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "third-party/gflags"] path = third-party/gflags url = https://github.com/gflags/gflags.git +[submodule "third-party/zenfs"] + path = third-party/zenfs + url = https://github.com/bzbd/zenfs.git diff --git a/CMakeLists.txt b/CMakeLists.txt index c83ecac76a..880bbe0740 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ include(CMakeDependentOption) CMAKE_DEPENDENT_OPTION(WITH_TESTS "build with tests" ON "CMAKE_BUILD_TYPE STREQUAL Debug" OFF) option(WITH_TOOLS "build with tools" OFF) option(WITH_TERARK_ZIP "build with TerarkZipTable support" ON) +option(WITH_ZENFS "build with experimental zenfs" OFF) option(WITH_DIAGNOSE_CACHE "build with diagnosable cache support" OFF) option(WITH_BOOSTLIB "build with boost, if WITH_TERARK_ZIP is ON, this will also set to ON" OFF) option(WITH_TERARKDB_NAMESPACE "namespace" "terarkdb") @@ -65,6 +66,10 @@ if (WITH_WINDOWS_UTF8_FILENAMES) add_definitions(-DROCKSDB_WINDOWS_UTF8_FILENAMES) endif() +if(WITH_ZENFS) + add_compile_definitions(ROCKSDB_NAMESPACE=${WITH_TERARKDB_NAMESPACE}) +endif() + IF(FORCE_TERARKDB_RELEASE_BUILD) SET(CMAKE_BUILD_TYPE "Release") ENDIF() @@ -374,6 +379,10 @@ if(WITH_TBB) list(APPEND THIRDPARTY_LIBS ${TBB_LIBRARIES}) endif() +if(WITH_ZENFS) + list(APPEND THIRDPARTY_LIBS zbd) +endif() + # Stall notifications eat some performance from inserts option(DISABLE_STALL_NOTIF "Build with stall notifications" OFF) if(DISABLE_STALL_NOTIF) @@ -827,6 +836,15 @@ if(WIN32) port/port_posix.cc env/env_posix.cc env/io_posix.cc) + + if(WITH_ZENFS) + list(APPEND SOURCES + third-party/zenfs/fs/fs_zenfs.cc + third-party/zenfs/fs/io_zenfs.cc + third-party/zenfs/fs/zbd_zenfs.cc + third-party/zenfs/util/zenfs.cc + tools/zenfs_tool.cc) + endif() endif() set(ROCKSDB_STATIC_LIB terarkdb${ARTIFACT_SUFFIX}) @@ -919,7 +937,7 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS) # We should also expose terarkdb's source dir INSTALL(DIRECTORY memtable monitoring env db cache options port table util utilities DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING + FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" ) @@ -1184,6 +1202,11 @@ if(WITH_TOOLS) $) target_link_libraries(db_bench${ARTIFACT_SUFFIX} gtest ${ROCKSDB_STATIC_LIB}) + add_executable(zenfs${ARTIFACT_SUFFIX} tools/zenfs.cc + tools/zenfs_tool.cc + $) + target_link_libraries(zenfs${ARTIFACT_SUFFIX} gtest ${ROCKSDB_STATIC_LIB}) + if(WITH_TERARK_ZIP) add_subdirectory(terark-tools/terark-test) endif() diff --git a/bench.py b/bench.py index 8320220d29..54ee6f9971 100755 --- a/bench.py +++ b/bench.py @@ -48,6 +48,7 @@ def bench(records, key_size, value_size, engine, db_dir, exist_db): """ % BENCH_ARGS[0] cmd = """ {db_bench} \ + --fs_uri=/dev/nvme3n2 --benchmarks={bench_type} --use_existing_db={exist_db} --sync=1 diff --git a/build_tools/build_detect_platform b/build_tools/build_detect_platform index 0599ef5460..5cafc82fe6 100755 --- a/build_tools/build_detect_platform +++ b/build_tools/build_detect_platform @@ -332,6 +332,19 @@ EOF fi fi + if ! test $ROCKSDB_DISABLE_LZBD; then + # Test whether libzbd is installed + $CXX $CFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null < + int main() {} +EOF + if [ "$?" = 0 ]; then + COMMON_FLAGS="$COMMON_FLAGS -DLIBZBD" + PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lzbd" + JAVA_LDFLAGS="$JAVA_LDFLAGS -lzbd" + fi + fi + if ! test $ROCKSDB_DISABLE_NUMA; then # Test whether numa is available $CXX $CFLAGS -x c++ - -o /dev/null -lnuma 2>/dev/null < zenfs_filesystem_reg = + ObjectLibrary::Default()->Register( + "zenfs://.*", [](const std::string& uri, std::unique_ptr* f, + std::string* errmsg) { + std::string devID = uri; + FileSystem* fs = nullptr; + Status s; + + devID.replace(0, strlen("zenfs://"), ""); + if (devID.rfind("dev:") == 0) { + devID.replace(0, strlen("dev:"), ""); + s = NewZenFS(&fs, devID); + if (!s.ok()) { + *errmsg = s.ToString(); + } + } else if (devID.rfind("uuid:") == 0) { + std::map zenFileSystems = + ListZenFileSystems(); + devID.replace(0, strlen("uuid:"), ""); + + if (zenFileSystems.find(devID) == zenFileSystems.end()) { + *errmsg = "UUID not found"; + } else { + s = NewZenFS(&fs, zenFileSystems[devID]); + if (!s.ok()) { + *errmsg = s.ToString(); + } + } + } else { + *errmsg = "Malformed URI"; + } + f->reset(fs); + return f->get(); + }); +#endif #endif diff --git a/include/rocksdb/env.h b/include/rocksdb/env.h index 320f046200..7473b338d7 100644 --- a/include/rocksdb/env.h +++ b/include/rocksdb/env.h @@ -1423,4 +1423,8 @@ Env* NewTimedEnv(Env* base_env); // This is a env forwarding method defined in env/env_io_prof.cc Env* NewIOProfEnv(Env* base_env); +// Returns a new environment that is used for ZENFS environment. +// This is a factory method for ZENFS declared in hdfs/env_zenfs.h +Status NewZenEnv(Env** env, const std::string& bdevname); + } // namespace TERARKDB_NAMESPACE diff --git a/include/rocksdb/terark_namespace.h.in b/include/rocksdb/terark_namespace.h.in index f523e2fffa..c213f05981 100644 --- a/include/rocksdb/terark_namespace.h.in +++ b/include/rocksdb/terark_namespace.h.in @@ -11,3 +11,8 @@ #cmakedefine WITH_TERARK_ZIP #cmakedefine WITH_BOOSTLIB #cmakedefine WITH_DIAGNOSE_CACHE +#cmakedefine WITH_ZENFS + +#ifdef WITH_ZENFS +#define LIBZBD +#endif diff --git a/src.mk b/src.mk index d57d389751..95ebf4fb7f 100644 --- a/src.mk +++ b/src.mk @@ -269,6 +269,9 @@ MOCK_LIB_SOURCES = \ BENCH_LIB_SOURCES = \ tools/db_bench_tool.cc \ +ZENFS_LIB_SOURCES = \ + tools/zenfs_tool.cc \ + EXP_LIB_SOURCES = \ utilities/col_buf_decoder.cc \ utilities/col_buf_encoder.cc \ diff --git a/table/terark_zip_table.cc b/table/terark_zip_table.cc index e3da262d74..128727c26c 100644 --- a/table/terark_zip_table.cc +++ b/table/terark_zip_table.cc @@ -488,11 +488,13 @@ Status TerarkZipTableFactory::SanitizeOptions( auto& tzto = *reinterpret_cast( table_factory->GetOptions()); try { - terark::TempFileDeleteOnClose test; - test.path = tzto.localTempDir + "/Terark-XXXXXX"; - test.open_temp(); - test.writer << "Terark"; - test.complete_write(); + if (tzto.terarkZipMinLevel != -2) { + terark::TempFileDeleteOnClose test; + test.path = tzto.localTempDir + "/Terark-XXXXXX"; + test.open_temp(); + test.writer << "Terark"; + test.complete_write(); + } } catch (...) { std::string msg = "ERROR: bad localTempDir : " + tzto.localTempDir; fprintf(stderr, "%s\n", msg.c_str()); diff --git a/third-party/zenfs b/third-party/zenfs new file mode 160000 index 0000000000..1bc2a113a1 --- /dev/null +++ b/third-party/zenfs @@ -0,0 +1 @@ +Subproject commit 1bc2a113a142273b9a938f47a7df6e313945aa98 diff --git a/tools/db_bench_tool.cc b/tools/db_bench_tool.cc index c7a9cb68d0..855480b8f2 100644 --- a/tools/db_bench_tool.cc +++ b/tools/db_bench_tool.cc @@ -82,6 +82,11 @@ #include // open/close #endif +#ifdef LIBZBD +#include "env/env_zenfs.h" +#include "env/zbd_zenfs.h" +#endif + using GFLAGS_NAMESPACE::ParseCommandLineFlags; using GFLAGS_NAMESPACE::RegisterFlagValidator; using GFLAGS_NAMESPACE::SetUsageMessage; @@ -826,10 +831,14 @@ DEFINE_uint64(prepare_log_writer_num, 1, ""); DEFINE_string(env_uri, "", "URI for registry Env lookup. Mutually exclusive" " with --hdfs."); +DEFINE_string(fs_uri, "", + "URI for registry Filesystem lookup. Mutually exclusive" + " with --hdfs and --env_uri." + " Creates a default environment with the specified filesystem."); #endif // ROCKSDB_LITE DEFINE_string(hdfs, "", "Name of hdfs environment. Mutually exclusive with" - " --env_uri."); + " --env_uri and --fs_uri"); static TERARKDB_NAMESPACE::Env* FLAGS_env = TERARKDB_NAMESPACE::Env::Default(); DEFINE_int64(stats_interval, 0, @@ -5817,6 +5826,13 @@ int db_bench_tool(int argc, char** argv) { StringToCompressionType(FLAGS_compression_type.c_str()); #ifndef ROCKSDB_LITE + int env_opts = + !FLAGS_hdfs.empty() + !FLAGS_env_uri.empty() + !FLAGS_fs_uri.empty(); + if (env_opts > 1) { + fprintf(stderr, + "Error: --hdfs, --env_uri and --fs_uri are mutually exclusive\n"); + exit(1); + } std::unique_ptr custom_env_guard; if (!FLAGS_hdfs.empty() && !FLAGS_env_uri.empty()) { fprintf(stderr, "Cannot provide both --hdfs and --env_uri.\n"); @@ -5827,6 +5843,14 @@ int db_bench_tool(int argc, char** argv) { fprintf(stderr, "No Env registered for URI: %s\n", FLAGS_env_uri.c_str()); exit(1); } + } else if (!FLAGS_fs_uri.empty()) { +#ifdef WITH_ZENFS + Status s = NewZenEnv(&FLAGS_env, FLAGS_fs_uri); + if (!s.ok()) { + fprintf(stderr, "Error: %s\n", s.ToString().c_str()); + exit(1); + } +#endif } #endif // ROCKSDB_LITE if (!FLAGS_hdfs.empty()) { diff --git a/tools/zenfs.cc b/tools/zenfs.cc new file mode 100644 index 0000000000..62237f10ef --- /dev/null +++ b/tools/zenfs.cc @@ -0,0 +1,32 @@ +// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. +// Copyright (c) 2019-present, Western Digital Corporation +// Copyright (c) 2021-present, Bytedance Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). + +#include +#ifdef ROCKSDB_LITE +int main() { + fprintf("Not supported in lite mode.\n"); + return 1; +} +#else +#ifdef GFLAGS +#ifdef LIBZBD +int zenfs_tool(int argc, char** argv); +int main(int argc, char** argv) { return zenfs_tool(argc, argv); } +#else +int main() { + fprintf(stderr, "Please install libzbd to run the zenfs tool\n"); + return 1; +} +#endif // LIBZBD +#else +int main() { + fprintf(stderr, "Please install gflags to run rocksdb tools\n"); + return 1; +} +#endif // GFLAGS +#endif // ROCKSDB_LITE + diff --git a/tools/zenfs_tool.cc b/tools/zenfs_tool.cc new file mode 100644 index 0000000000..b66e015a14 --- /dev/null +++ b/tools/zenfs_tool.cc @@ -0,0 +1,397 @@ +// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. +// Copyright (c) 2019-present, Western Digital Corporation +// Copyright (c) 2021-present, Bytedance Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). + +#if defined(GFLAGS) && !defined(ROCKSDB_LITE) && defined(LIBZBD) + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "env/env_zenfs.h" +#include "util/gflags_compat.h" +using GFLAGS_NAMESPACE::ParseCommandLineFlags; +using GFLAGS_NAMESPACE::RegisterFlagValidator; +using GFLAGS_NAMESPACE::SetUsageMessage; + +DEFINE_string(zbd, "", "Path to a zoned block device."); +DEFINE_string(aux_path, "", + "Path for auxiliary file storage (log and lock files)."); +DEFINE_bool(force, false, "Force file system creation."); +DEFINE_string(path, "", "Path to directory to list files under"); +DEFINE_int32(finish_threshold, 0, "Finish used zones if less than x% left"); +DEFINE_int32(max_active_zones, 0, "Max active zone limit"); +DEFINE_int32(max_open_zones, 0, "Max active zone limit"); + +namespace TERARKDB_NAMESPACE { + +ZonedBlockDevice *zbd_open(bool readonly) { + ZonedBlockDevice *zbd = new ZonedBlockDevice(FLAGS_zbd, nullptr); + Status open_status = zbd->Open(readonly); + + if (!open_status.ok()) { + fprintf(stderr, "Failed to open zoned block device: %s, error: %s\n", + FLAGS_zbd.c_str(), open_status.ToString().c_str()); + delete zbd; + return nullptr; + } + + return zbd; +} + +Status zenfs_mount(ZonedBlockDevice *zbd, ZenEnv **zenEnv, bool readonly) { + Status s; + + *zenEnv = new ZenEnv(zbd, Env::Default(), nullptr); + s = (*zenEnv)->Mount(readonly); + if (!s.ok()) { + delete *zenEnv; + *zenEnv = nullptr; + } + + return s; +} + +int zenfs_tool_mkfs() { + Status s; + DIR* aux_dir; + + if (FLAGS_aux_path.empty()) { + fprintf(stderr, "You need to specify --aux_path\n"); + return 1; + } + + aux_dir = opendir(FLAGS_aux_path.c_str()); + if (ENOENT != errno) { + fprintf(stderr, "Error: aux path exists\n"); + closedir(aux_dir); + return 1; + } + + ZonedBlockDevice *zbd = zbd_open(false); + if (zbd == nullptr) return 1; + + ZenEnv *zenEnv; + s = zenfs_mount(zbd, &zenEnv, false); + if ((s.ok() || !s.IsNotFound()) && !FLAGS_force) { + fprintf( + stderr, + "Existing filesystem found, use --force if you want to replace it.\n"); + return 1; + } + + if (zenEnv != nullptr) delete zenEnv; + + zbd = zbd_open(false); + zenEnv = new ZenEnv(zbd, Env::Default(), nullptr); + + if (FLAGS_aux_path.back() != '/') FLAGS_aux_path.append("/"); + + s = zenEnv->MkFS(FLAGS_aux_path, FLAGS_finish_threshold, + FLAGS_max_open_zones, FLAGS_max_active_zones); + if (!s.ok()) { + fprintf(stderr, "Failed to create file system, error: %s\n", + s.ToString().c_str()); + delete zenEnv; + return 1; + } + + fprintf(stdout, "ZenEnv file system created. Free space: %lu MB\n", + zbd->GetFreeSpace() / (1024 * 1024)); + + delete zenEnv; + return 0; +} + +void list_children(ZenEnv *zenEnv, std::string path) { + std::vector result; + Status io_status = zenEnv->GetChildren(path, &result); + + if (!io_status.ok()) return; + + for (const auto f : result) { + fprintf(stdout, "%s\n", f.c_str()); + } +} + +int zenfs_tool_list() { + Status s; + ZonedBlockDevice *zbd = zbd_open(true); + if (zbd == nullptr) return 1; + + ZenEnv *zenEnv; + s = zenfs_mount(zbd, &zenEnv, true); + if (!s.ok()) { + fprintf(stderr, "Failed to mount filesystem, error: %s\n", + s.ToString().c_str()); + return 1; + } + + list_children(zenEnv, FLAGS_path); + + return 0; +} + +int zenfs_tool_df() { + Status s; + ZonedBlockDevice *zbd = zbd_open(true); + if (zbd == nullptr) return 1; + + ZenEnv *zenEnv; + s = zenfs_mount(zbd, &zenEnv, true); + if (!s.ok()) { + fprintf(stderr, "Failed to mount filesystem, error: %s\n", + s.ToString().c_str()); + return 1; + } + uint64_t used = zbd->GetUsedSpace(); + uint64_t free = zbd->GetFreeSpace(); + uint64_t reclaimable = zbd->GetReclaimableSpace(); + + /* Avoid divide by zero */ + if (used == 0) used = 1; + + fprintf(stdout, "Free: %lu MB\nUsed: %lu MB\nReclaimable: %lu MB\nSpace amplification: %lu%%\n", + free / (1024 * 1024), used / (1024 * 1024), reclaimable / (1024 * 1024), + (100 * reclaimable) / used); + + return 0; +} + +int zenfs_tool_lsuuid() { + std::map::iterator it; + std::map zenFileSystems = ListZenFileSystems(); + + for (it = zenFileSystems.begin(); it != zenFileSystems.end(); it++) + fprintf(stdout, "%s\t%s\n", it->first.c_str(), it->second.c_str()); + + return 0; +} + +static std::map wlth_map; + + +Env::WriteLifeTimeHint GetWriteLifeTimeHint(std::string filename) { + if (wlth_map.find(filename) != wlth_map.end()) { + return wlth_map[filename]; + } + return Env::WriteLifeTimeHint::WLTH_NOT_SET; +} + +int SaveWriteLifeTimeHints() { + std::ofstream wlth_file(FLAGS_path + "/write_lifetime_hints.dat"); + + if (!wlth_file.is_open()) { + fprintf(stderr, "Failed to store time hints\n"); + return 1; + } + + for (auto it = wlth_map.begin(); it != wlth_map.end(); it++) { + wlth_file << it->first << "\t" << it->second << "\n"; + } + + wlth_file.close(); + return 0; +} + +void ReadWriteLifeTimeHints() { + std::ifstream wlth_file(FLAGS_path + "/write_lifetime_hints.dat"); + + if (!wlth_file.is_open()) { + fprintf(stderr, "WARNING: failed to read write life times\n"); + return; + } + + std::string filename; + uint32_t lth; + + while ( wlth_file >> filename >> lth ) { + wlth_map.insert(std::make_pair(filename, (Env::WriteLifeTimeHint)lth)); + fprintf(stdout, "read: %s %u \n", filename.c_str(), lth); + } + + wlth_file.close(); +} + +Status zenfs_tool_copy_file(Env *f_fs, std::string f, Env *t_fs, std::string t) { + EnvOptions eopt; + Status s; + std::unique_ptr f_file; + std::unique_ptr t_file; + size_t buffer_sz = 1024 * 1024; + uint64_t to_copy; + char *buffer; + + fprintf(stdout, "%s\n", f.c_str()); + + s = f_fs->GetFileSize(f, &to_copy); + if (!s.ok()) { return s; } + + s = f_fs->NewSequentialFile(f, &f_file, eopt); + if (!s.ok()) { return s; } + + s = t_fs->NewWritableFile(t, &t_file, eopt); + if (!s.ok()) { return s; } + + t_file->SetWriteLifeTimeHint(GetWriteLifeTimeHint(t)); + + buffer = (char *)malloc(buffer_sz); + if (buffer == nullptr) { + return Status::IOError("Failed to allocate copy buffer"); + } + + while (to_copy > 0) { + size_t chunk_sz = to_copy; + Slice chunk_slice; + + if (chunk_sz > buffer_sz) + chunk_sz = buffer_sz; + + s = f_file->Read(chunk_sz, &chunk_slice, buffer); + if (!s.ok()) { break; } + + s = t_file->Append(chunk_slice); + to_copy -= chunk_slice.size(); + } + + free(buffer); + if (!s.ok()) { return s; } + + return t_file->Fsync(); +} + +Status zenfs_tool_copy_dir(Env *f_fs, std::string f_dir, Env *t_fs, std::string t_dir) { + Status s; + std::vector files; + + s = f_fs->GetChildren(f_dir, &files); + if (!s.ok()) { return s; } + + for (const auto f : files) { + std::string filename = f_dir + f; + bool is_dir; + + if (f == "." || f == ".." || f == "write_lifetime_hints.dat") + continue; + + // s = f_fs->IsDirectory(filename, &is_dir); + // if (!s.ok()) { return s; } + + std::string dest_filename; + + if (t_dir == "") { + dest_filename = f; + } else { + dest_filename = t_dir + "/" + f; + } + + if (is_dir) { + s = t_fs->CreateDir(dest_filename); + if (!s.ok()) { return s; } + s = zenfs_tool_copy_dir(f_fs, filename + "/", t_fs, dest_filename); + if (!s.ok()) { return s; } + } else { + s = zenfs_tool_copy_file(f_fs, filename, t_fs, dest_filename); + if (!s.ok()) { return s; } + } + } + + return s; +} + +int zenfs_tool_backup() { + Status status; + Status io_status; + ZonedBlockDevice *zbd; + ZenEnv *zenEnv; + + zbd = zbd_open(false); + if (zbd == nullptr) return 1; + + status = zenfs_mount(zbd, &zenEnv, false); + if (!status.ok()) { + fprintf(stderr, "Failed to mount filesystem, error: %s\n", + status.ToString().c_str()); + return 1; + } + + io_status = zenfs_tool_copy_dir(zenEnv, "", Env::Default(), FLAGS_path); + if (!io_status.ok()) { + fprintf(stderr, "Copy failed, error: %s\n", io_status.ToString().c_str()); + return 1; + } + + wlth_map = zenEnv->GetWriteLifeTimeHints(); + return SaveWriteLifeTimeHints(); +} + +int zenfs_tool_restore() { + Status status; + ZonedBlockDevice *zbd; + ZenEnv *zenEnv; + + ReadWriteLifeTimeHints(); + + zbd = zbd_open(false); + if (zbd == nullptr) return 1; + + status = zenfs_mount(zbd, &zenEnv, false); + if (!status.ok()) { + fprintf(stderr, "Failed to mount filesystem, error: %s\n", + status.ToString().c_str()); + return 1; + } + + status = zenfs_tool_copy_dir(Env::Default(), FLAGS_path, zenEnv, ""); + if (!status.ok()) { + fprintf(stderr, "Copy failed, error: %s\n", status.ToString().c_str()); + return 1; + } + return 0; +} +} // namespace TERARKDB_NAMESPACE + +int zenfs_tool(int argc, char **argv) { + SetUsageMessage(std::string("\nUSAGE:\n") + std::string(argv[0]) + + +" [OPTIONS]...\nCommands: mkfs, list, ls-uuid"); + if (argc < 2) { + fprintf(stderr, "You need to specify a command.\n"); + return 1; + } + + std::string subcmd(argv[1]); + ParseCommandLineFlags(&argc, &argv, true); + + if (FLAGS_zbd.empty() && subcmd != "ls-uuid") { + fprintf(stderr, "You need to specify a zoned block device using --zbd\n"); + return 1; + } + if (subcmd == "mkfs") { + return TERARKDB_NAMESPACE::zenfs_tool_mkfs(); + } else if (subcmd == "list") { + return TERARKDB_NAMESPACE::zenfs_tool_list(); + } else if (subcmd == "ls-uuid") { + return TERARKDB_NAMESPACE::zenfs_tool_lsuuid(); + } else if (subcmd == "df") { + return TERARKDB_NAMESPACE::zenfs_tool_df(); + } else if (subcmd == "backup") { + return TERARKDB_NAMESPACE::zenfs_tool_backup(); + } else if (subcmd == "restore") { + return TERARKDB_NAMESPACE::zenfs_tool_restore(); + } else { + fprintf(stderr, "Subcommand not recognized: %s\n", subcmd.c_str()); + return 1; + } + + return 0; +} + +#endif // defined(GFLAGS) && !defined(ROCKSDB_LITE) && defined(LIBZBD) diff --git a/util/split_dev.sh b/util/split_dev.sh new file mode 100755 index 0000000000..0b20d2a296 --- /dev/null +++ b/util/split_dev.sh @@ -0,0 +1,10 @@ + +DEV=$1 +ZONE_SIZE=$(cat /sys/class/block/$DEV/queue/chunk_sectors) +NR_ZONES=$(cat /sys/class/block/$DEV/queue/nr_zones) +SPLIT_ZONES=$(($NR_ZONES / 2)) +SPLIT_SZ=$(($SPLIT_ZONES * $ZONE_SIZE)) + +echo "0 $SPLIT_SZ linear /dev/$DEV 0" | dmsetup create "$DEV-ONE" +echo "0 $SPLIT_SZ linear /dev/$DEV $SPLIT_SZ" | dmsetup create "$DEV-TWO" +