Skip to content

Commit

Permalink
add zero copy optimzation for linux image format copy restoration
Browse files Browse the repository at this point in the history
  • Loading branch information
XUranus committed Mar 16, 2024
1 parent 3321ea3 commit 5a2ec7e
Show file tree
Hide file tree
Showing 12 changed files with 407 additions and 82 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ if(${CMAKE_HOST_WIN32})
VOLUMEPROTECTPROTECT_SOURCES
"src/*.cpp"
"src/common/*.cpp"
"src/task*.cpp"
"src/task/*.cpp"
"src/native/*.cpp"
"src/native/win32/*.cpp"
)
Expand All @@ -60,7 +60,7 @@ else()
VOLUMEPROTECTPROTECT_SOURCES
"src/*.cpp"
"src/common/*.cpp"
"src/task*.cpp"
"src/task/*.cpp"
"src/native/*.cpp"
"src/native/linux/*.cpp"
)
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ Volume backup/restore library and cli tools for Windows and Linux

- [X] **FULL BACKUP** and **FOREVER INCREMENT BACKUP** support
- [X] `*.img`,`*.vhd`,`*.vhdx` copy format support
- [X] volume copy mount support
- [X] checkpoint support
- [X] Volume copy mount support
- [X] Checkpoint support
- [ ] Zero copy optimization
- [ ] Qt GUI
- [ ] Auto snapshot creation of LVM,BTRFS for Linux and VSS for Windows
- [ ] Auto filesystem type detection


<div align="center">
<img src="https://github.com/XUranus/VolumeBackup/actions/workflows/cmake-multi-platform.yml/badge.svg" alt="VolumeBackup" title="VolumeBackup">&thinsp;
Expand Down
46 changes: 34 additions & 12 deletions cli/vbackup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ static const char* g_helpMessage =
#endif
"-d | --data= \t specify copy data directory\n"
"-m | --meta= \t specify copy meta directory\n"
"-k | --checkpoint= \t specify checkpoint directory"
"-k | --checkpoint= \t specify checkpoint directory\n"
"-p | --prevmeta= \t specify previous copy meta directory\n"
"-r | --restore \t used when performing restore operation\n"
"-z | --zerocopy \t enable zero copy during restore\n"
"-l | --loglevel= \t specify logger level [INFO, DEBUG]\n"
"-h | --help \t print help\n";

Expand All @@ -51,8 +52,9 @@ struct CliArgs {
std::string copyMetaDirPath;
std::string checkpointDirPath;
std::string prevCopyMetaDirPath;
LoggerLevel logLevel { LoggerLevel::DEBUG };
LoggerLevel logLevel { LoggerLevel::INFO };
bool isRestore { false };
bool enableZeroCopy { false };
bool printHelp { false };
};

Expand Down Expand Up @@ -109,9 +111,9 @@ static CliArgs ParseCliArgs(int argc, const char** argv)
CliArgs cliAgrs;
GetOptionResult result = GetOption(
argv + 1, argc - 1,
"v:n:f:d:m:k:p:h:r:l:",
"v:n:f:d:m:k:p:hzr:l:",
{"--volume=", "--name=", "--format=", "--data=", "--meta=", "--checkpoint=",
"--prevmeta=", "--help", "--restore", "--loglevel="});
"--prevmeta=", "--help", "--zerocopy", "--restore", "--loglevel="});
for (const OptionResult opt: result.opts) {
if (opt.option == "v" || opt.option == "volume") {
cliAgrs.volumePath = opt.value;
Expand All @@ -129,6 +131,8 @@ static CliArgs ParseCliArgs(int argc, const char** argv)
cliAgrs.prevCopyMetaDirPath = opt.value;
} else if (opt.option == "r" || opt.option == "restore") {
cliAgrs.isRestore = true;
} else if (opt.option == "z" || opt.option == "zerocopy") {
cliAgrs.enableZeroCopy = true;
} else if (opt.option == "l" || opt.option == "loglevel") {
cliAgrs.logLevel = ParseLoggerLevel(opt.value);
} else if (opt.option == "h" || opt.option == "help") {
Expand Down Expand Up @@ -176,11 +180,23 @@ void PrintTaskErrorCodeMessage(ErrCodeType errorCode)

static bool ValidateCliArgs(const CliArgs& cliArgs)
{
return !cliArgs.volumePath.empty()
&& !cliArgs.copyDataDirPath.empty()
&& !cliArgs.copyMetaDirPath.empty()
&& !cliArgs.checkpointDirPath.empty()
&& !cliArgs.copyName.empty();
if (cliArgs.volumePath.empty()) {
std::cerr << "Error: no volume path specified." << std::endl;
return false;
}
if (cliArgs.copyDataDirPath.empty()) {
std::cerr << "Error: no copy data path specified." << std::endl;
return false;
}
if (cliArgs.copyMetaDirPath.empty()) {
std::cerr << "Error: no copy meta path specified." << std::endl;
return false;
}
if (cliArgs.copyName.empty()) {
std::cerr << "Error: no volume copy name specified." << std::endl;
return false;
}
return true;
}

void InitLogger(const CliArgs& cliArgs)
Expand All @@ -192,9 +208,9 @@ void InitLogger(const CliArgs& cliArgs)
conf.archiveFilesNumMax = 10;
conf.fileName = "vbackup.log";
#ifdef _WIN32
conf.logDirPath = R"(C:\LoggerTest)";
conf.logDirPath = R"(C:\)";
#else
conf.logDirPath = "/tmp/LoggerTest";
conf.logDirPath = "/tmp";
#endif
if (!Logger::GetInstance()->Init(conf)) {
std::cerr << "Init logger failed" << std::endl;
Expand All @@ -213,12 +229,12 @@ static int ExecVolumeBackup(const CliArgs& cliArgs)
backupConfig.outputCopyDataDirPath = cliArgs.copyDataDirPath;
backupConfig.outputCopyMetaDirPath = cliArgs.copyMetaDirPath;
backupConfig.checkpointDirPath = cliArgs.checkpointDirPath;
backupConfig.enableCheckpoint = !cliArgs.checkpointDirPath.empty();
backupConfig.clearCheckpointsOnSucceed = true;
backupConfig.blockSize = DEFAULT_BLOCK_SIZE;
backupConfig.sessionSize = 3 * ONE_GB;
backupConfig.hasherNum = hasherWorkerNum;
backupConfig.hasherEnabled = true;
backupConfig.enableCheckpoint = true;

if (backupConfig.prevCopyMetaDirPath.empty()) {
std::cout << "----- Perform Full Backup -----" << std::endl;
Expand Down Expand Up @@ -258,6 +274,12 @@ static int ExecVolumeRestore(const CliArgs& cliAgrs)
restoreConfig.copyDataDirPath = cliAgrs.copyDataDirPath;
restoreConfig.copyMetaDirPath = cliAgrs.copyMetaDirPath;
restoreConfig.checkpointDirPath = cliAgrs.checkpointDirPath;
restoreConfig.enableCheckpoint = !cliAgrs.checkpointDirPath.empty();
restoreConfig.enableZeroCopy = cliAgrs.enableZeroCopy;

if (restoreConfig.enableZeroCopy) {
std::cout << "using zero copy optimization." << std::endl;
}

std::shared_ptr<VolumeProtectTask> task = VolumeProtectTask::BuildRestoreTask(restoreConfig);
if (task == nullptr) {
Expand Down
1 change: 1 addition & 0 deletions include/VolumeProtector.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ struct VOLUMEPROTECT_API VolumeRestoreConfig {
bool enableCheckpoint { true }; ///< start from checkpoint if exists
std::string checkpointDirPath; ///< directory path where checkpoint stores at
bool clearCheckpointsOnSucceed { true }; ///< if clear checkpoint files on succeed
bool enableZeroCopy { false }; ///< use zero copy optimization for CopyFormat::IMAGE restore
};

/**
Expand Down
20 changes: 15 additions & 5 deletions include/common/VolumeUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,24 @@ struct CopySegment {
};

struct VolumeCopyMeta {
/* basic meta */
std::string copyName;
int backupType; // cast BackupType to int
int copyFormat; // cast CopyFormat to int
uint64_t volumeSize; // volume size in bytes
uint32_t blockSize; // block size in bytes
std::string volumePath;
int backupType; ///< cast BackupType to int
int copyFormat; ///< cast CopyFormat to int
uint64_t volumeSize; ///< volume size in bytes
uint32_t blockSize; ///< block size in bytes
std::vector<CopySegment> segments;

std::string volumePath;
std::string label;
std::string uuid;

/* meta of the snapshot of the volume */
// TODO:: intergate fs uuid detection and snapshot auto creation in later version
std::string snapshotPath;
std::string snapshotLabel;
std::string snapshotUUID;

SERIALIZE_SECTION_BEGIN
SERIALIZE_FIELD(copyName, copyName);
SERIALIZE_FIELD(backupType, backupType);
Expand Down
9 changes: 9 additions & 0 deletions include/native/RawIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
#include "VolumeProtector.h"
#include <string>

#ifdef _WIN32
using HandleType = void*;
#else
using HandleType = int;
#endif

namespace volumeprotect {
/**
Expand All @@ -32,6 +37,8 @@ class RawDataReader {

virtual ErrCodeType Error() = 0;

virtual HandleType Handle() = 0;

virtual ~RawDataReader() = default;
};

Expand All @@ -49,6 +56,8 @@ class RawDataWriter {

virtual ErrCodeType Error() = 0;

virtual HandleType Handle() = 0;

virtual ~RawDataWriter() = default;
};

Expand Down
2 changes: 2 additions & 0 deletions include/native/linux/PosixRawIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class PosixRawDataReader : public RawDataReader {
~PosixRawDataReader();
bool Read(uint64_t offset, uint8_t* buffer, int length, ErrCodeType& errorCode) override;
bool Ok() override;
HandleType Handle() override;
ErrCodeType Error() override;

private:
Expand All @@ -42,6 +43,7 @@ class PosixRawDataWriter : public RawDataWriter {
~PosixRawDataWriter();
bool Write(uint64_t offset, uint8_t* buffer, int length, ErrCodeType& errorCode) override;
bool Ok() override;
HandleType Handle() override;
bool Flush() override;
ErrCodeType Error() override;

Expand Down
61 changes: 61 additions & 0 deletions include/task/VolumeZeroCopyRestoreTask.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* @file VolumeZeroCopyRestoreTask.h
* @brief Provide zero copy implement for CopyFormat::IMAGE copy restoration.
* @copyright Copyright 2023 XUranus. All rights reserved.
* @license This project is released under the Apache License.
* @author XUranus([email protected])
*/

#ifndef VOLUMEBACKUP_ZERO_COPY_RESTORE_TASK_HEADER
#define VOLUMEBACKUP_ZERO_COPY_RESTORE_TASK_HEADER

#include "VolumeProtector.h"
#include "VolumeProtectTaskContext.h"
#include "native/TaskResourceManager.h"
#include "VolumeUtils.h"
#include "native/RawIO.h"
#include <queue>

namespace volumeprotect {
namespace task {

/**
* @brief Control control volume restore procedure
*/
class VolumeZeroCopyRestoreTask : public VolumeProtectTask, public TaskStatisticTrait {
public:
using SessionQueue = std::queue<VolumeTaskSession>;

bool Start() override;

TaskStatistics GetStatistics() const override;

VolumeZeroCopyRestoreTask(const VolumeRestoreConfig& restoreConfig, const VolumeCopyMeta& volumeCopyMeta);

~VolumeZeroCopyRestoreTask();

private:
bool Prepare(); // split session and save meta

void ThreadFunc();

bool PerformZeroCopyRestore(
std::shared_ptr<volumeprotect::rawio::RawDataReader> copyDataReader,
std::shared_ptr<volumeprotect::rawio::RawDataWriter> volumeDataWriter,
const VolumeTaskSharedConfig& sessionConfig);

protected:
uint64_t m_volumeSize;
std::shared_ptr<VolumeRestoreConfig> m_restoreConfig;
std::shared_ptr<VolumeCopyMeta> m_volumeCopyMeta;
std::queue<VolumeTaskSharedConfig> m_sessionQueue;
std::thread m_thread;

std::shared_ptr<TaskResourceManager> m_resourceManager;
std::vector<std::string> m_checkpointFiles;
};

}
}

#endif
16 changes: 13 additions & 3 deletions src/VolumeProtector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
*/

#include "VolumeProtector.h"
#include "VolumeBackupTask.h"
#include "common/VolumeProtectMacros.h"
#include "VolumeBackupTask.h"
#include "VolumeZeroCopyRestoreTask.h"
#include "VolumeRestoreTask.h"
#include "VolumeUtils.h"
#include "native/FileSystemAPI.h"
#include <memory>

using namespace volumeprotect;
using namespace volumeprotect::common;
Expand Down Expand Up @@ -101,7 +103,7 @@ std::unique_ptr<VolumeProtectTask> VolumeProtectTask::BuildBackupTask(const Volu
return nullptr;
}

return std::unique_ptr<VolumeProtectTask>(new VolumeBackupTask(finalBackupConfig, volumeSize));
return exstd::make_unique<VolumeBackupTask>(finalBackupConfig, volumeSize);
}

std::unique_ptr<VolumeProtectTask> VolumeProtectTask::BuildRestoreTask(const VolumeRestoreConfig& restoreConfig)
Expand Down Expand Up @@ -136,7 +138,15 @@ std::unique_ptr<VolumeProtectTask> VolumeProtectTask::BuildRestoreTask(const Vol
return nullptr;
}

return std::unique_ptr<VolumeProtectTask>(new VolumeRestoreTask(restoreConfig, volumeCopyMeta));
if (restoreConfig.enableZeroCopy) {
if (static_cast<CopyFormat>(volumeCopyMeta.copyFormat) != CopyFormat::IMAGE) {
ERRLOG("zero copy only supported by CopyFormat::IMAGE copy");
return nullptr;
}
return exstd::make_unique<VolumeZeroCopyRestoreTask>(restoreConfig, volumeCopyMeta);
}

return exstd::make_unique<VolumeRestoreTask>(restoreConfig, volumeCopyMeta);
}

void StatefulTask::Abort()
Expand Down
Loading

0 comments on commit 5a2ec7e

Please sign in to comment.