Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fe032d03 authored by David Anderson's avatar David Anderson
Browse files

snapuserd: Add a harness to run tests without dm-user specific code.

This patch adds an abstraction layer around Tempdevice (which wraps
device-mapper), and a layer to replace hardcoding of DmUserBlockServer.

The only implementation of the new layer, currently, is for dm-user.
However this will allow the harness to run with a backend chosen at
runtime, making testing on the host or of ublk much easier.

Bug: 288273605
Test: snapuserd_test
Change-Id: I8735ef6c373f3e5c5cdf3df461668ddd8e551f63
parent 384b22ce
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -219,6 +219,7 @@ cc_test {
        "libsnapshot_cow_defaults",
    ],
    srcs: [
        "testing/dm_user_harness.cpp",
        "user-space-merge/snapuserd_test.cpp",
    ],
    shared_libs: [
+67 −0
Original line number Diff line number Diff line
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "dm_user_harness.h"

#include <fcntl.h>

#include <android-base/file.h>
#include <fs_mgr/file_wait.h>
#include <libdm/dm.h>
#include <snapuserd/dm_user_block_server.h>

namespace android {
namespace snapshot {

using namespace std::chrono_literals;
using android::base::unique_fd;

DmUserDevice::DmUserDevice(std::unique_ptr<Tempdevice>&& dev) : dev_(std::move(dev)) {}

const std::string& DmUserDevice::GetPath() {
    return dev_->path();
}

bool DmUserDevice::Destroy() {
    return dev_->Destroy();
}

DmUserTestHarness::DmUserTestHarness() {
    block_server_factory_ = std::make_unique<DmUserBlockServerFactory>();
}

std::unique_ptr<IUserDevice> DmUserTestHarness::CreateUserDevice(const std::string& dev_name,
                                                                 const std::string& misc_name,
                                                                 uint64_t num_sectors) {
    android::dm::DmTable dmuser_table;
    dmuser_table.Emplace<android::dm::DmTargetUser>(0, num_sectors, misc_name);
    auto dev = std::make_unique<Tempdevice>(dev_name, dmuser_table);
    if (!dev->valid()) {
        return nullptr;
    }

    auto misc_device = "/dev/dm-user/" + misc_name;
    if (!android::fs_mgr::WaitForFile(misc_device, 10s)) {
        return nullptr;
    }

    return std::make_unique<DmUserDevice>(std::move(dev));
}

IBlockServerFactory* DmUserTestHarness::GetBlockServerFactory() {
    return block_server_factory_.get();
}

}  // namespace snapshot
}  // namespace android
+54 −0
Original line number Diff line number Diff line
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <android-base/unique_fd.h>

#include "harness.h"
#include "temp_device.h"

namespace android {
namespace snapshot {

using android::base::unique_fd;

class DmUserBlockServerFactory;

class DmUserDevice final : public IUserDevice {
  public:
    explicit DmUserDevice(std::unique_ptr<Tempdevice>&& dev);
    const std::string& GetPath() override;
    bool Destroy() override;

  private:
    std::unique_ptr<Tempdevice> dev_;
};

class DmUserTestHarness final : public ITestHarness {
  public:
    DmUserTestHarness();

    std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
                                                  const std::string& misc_name,
                                                  uint64_t num_sectors) override;
    IBlockServerFactory* GetBlockServerFactory() override;
    bool HasUserDevice() override { return true; }

  private:
    std::unique_ptr<DmUserBlockServerFactory> block_server_factory_;
};

}  // namespace snapshot
}  // namespace android
+47 −0
Original line number Diff line number Diff line
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <stddef.h>
#include <sys/types.h>

#include <memory>

#include <android-base/unique_fd.h>
#include <snapuserd/block_server.h>

namespace android {
namespace snapshot {

// Interface for a "block driver in userspace" device.
class IUserDevice {
  public:
    virtual ~IUserDevice() {}
    virtual const std::string& GetPath() = 0;
    virtual bool Destroy() = 0;
};

class ITestHarness {
  public:
    virtual ~ITestHarness() {}
    virtual std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
                                                          const std::string& misc_name,
                                                          uint64_t num_sectors) = 0;
    virtual IBlockServerFactory* GetBlockServerFactory() = 0;
    virtual bool HasUserDevice() = 0;
};

}  // namespace snapshot
}  // namespace android
+28 −23
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@
#include <storage_literals/storage_literals.h>
#include "handler_manager.h"
#include "snapuserd_core.h"
#include "testing/dm_user_harness.h"
#include "testing/temp_device.h"

DEFINE_string(force_config, "", "Force testing mode with iouring disabled");
@@ -75,10 +76,9 @@ class SnapuserdTest : public ::testing::Test {
    static const uint64_t kSectorSize = 512;

  protected:
    void SetUp() override {}
    void SetUp() override;
    void TearDown() override { Shutdown(); }

  private:
    void SetupImpl();

    void SimulateDaemonRestart();
@@ -94,10 +94,10 @@ class SnapuserdTest : public ::testing::Test {
    void InitCowDevice();
    void SetDeviceControlName();
    void InitDaemon();
    void CreateDmUserDevice();
    void CreateUserDevice();

    unique_ptr<LoopDevice> base_loop_;
    unique_ptr<Tempdevice> dmuser_dev_;
    unique_ptr<IUserDevice> dmuser_dev_;

    std::string system_device_ctrl_name_;
    std::string system_device_name_;
@@ -112,6 +112,7 @@ class SnapuserdTest : public ::testing::Test {
    size_t size_ = 100_MiB;
    int cow_num_sectors_;
    int total_base_size_;
    std::unique_ptr<ITestHarness> harness_;
};

static unique_fd CreateTempFile(const std::string& name, size_t size) {
@@ -132,6 +133,10 @@ static unique_fd CreateTempFile(const std::string& name, size_t size) {
    return fd;
}

void SnapuserdTest::SetUp() {
    harness_ = std::make_unique<DmUserTestHarness>();
}

void SnapuserdTest::Shutdown() {
    ASSERT_TRUE(dmuser_dev_->Destroy());

@@ -173,7 +178,7 @@ bool SnapuserdTest::SetupCopyOverlap_2() {
bool SnapuserdTest::SetupDaemon() {
    SetDeviceControlName();

    CreateDmUserDevice();
    CreateUserDevice();
    InitCowDevice();
    InitDaemon();

@@ -206,7 +211,7 @@ void SnapuserdTest::CreateBaseDevice() {
}

void SnapuserdTest::ReadSnapshotDeviceAndValidate() {
    unique_fd fd(open(dmuser_dev_->path().c_str(), O_RDONLY));
    unique_fd fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY));
    ASSERT_GE(fd, 0);
    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);

@@ -535,9 +540,8 @@ void SnapuserdTest::InitCowDevice() {
        use_iouring = false;
    }

    DmUserBlockServerFactory factory;

    auto opener = factory.CreateOpener(system_device_ctrl_name_);
    auto factory = harness_->GetBlockServerFactory();
    auto opener = factory->CreateOpener(system_device_ctrl_name_);
    auto handler =
            handlers_.AddHandler(system_device_ctrl_name_, cow_system_->path, base_loop_->device(),
                                 base_loop_->device(), opener, 1, use_iouring, false);
@@ -560,7 +564,7 @@ void SnapuserdTest::SetDeviceControlName() {
    system_device_ctrl_name_ = system_device_name_ + "-ctrl";
}

void SnapuserdTest::CreateDmUserDevice() {
void SnapuserdTest::CreateUserDevice() {
    unique_fd fd(TEMP_FAILURE_RETRY(open(base_loop_->device().c_str(), O_RDONLY | O_CLOEXEC)));
    ASSERT_TRUE(fd > 0);

@@ -569,17 +573,9 @@ void SnapuserdTest::CreateDmUserDevice() {

    cow_num_sectors_ = dev_sz >> 9;

    DmTable dmuser_table;
    ASSERT_TRUE(dmuser_table.AddTarget(
            std::make_unique<DmTargetUser>(0, cow_num_sectors_, system_device_ctrl_name_)));
    ASSERT_TRUE(dmuser_table.valid());

    dmuser_dev_ = std::make_unique<Tempdevice>(system_device_name_, dmuser_table);
    ASSERT_TRUE(dmuser_dev_->valid());
    ASSERT_FALSE(dmuser_dev_->path().empty());

    auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
    ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
    dmuser_dev_ = harness_->CreateUserDevice(system_device_name_, system_device_ctrl_name_,
                                             cow_num_sectors_);
    ASSERT_NE(dmuser_dev_, nullptr);
}

void SnapuserdTest::InitDaemon() {
@@ -603,7 +599,7 @@ void SnapuserdTest::SetupImpl() {

    SetDeviceControlName();

    CreateDmUserDevice();
    CreateUserDevice();
    InitCowDevice();
    InitDaemon();

@@ -632,7 +628,7 @@ void SnapuserdTest::SimulateDaemonRestart() {
    Shutdown();
    std::this_thread::sleep_for(500ms);
    SetDeviceControlName();
    CreateDmUserDevice();
    CreateUserDevice();
    InitCowDevice();
    InitDaemon();
}
@@ -695,6 +691,9 @@ void SnapuserdTest::MergeInterrupt() {
}

TEST_F(SnapuserdTest, Snapshot_IO_TEST) {
    if (!harness_->HasUserDevice()) {
        GTEST_SKIP() << "Skipping snapshot read; not supported";
    }
    ASSERT_TRUE(SetupDefault());
    // I/O before merge
    ReadSnapshotDeviceAndValidate();
@@ -707,6 +706,9 @@ TEST_F(SnapuserdTest, Snapshot_IO_TEST) {
}

TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST) {
    if (!harness_->HasUserDevice()) {
        GTEST_SKIP() << "Skipping snapshot read; not supported";
    }
    ASSERT_TRUE(SetupDefault());
    // Issue I/O before merge begins
    std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
@@ -717,6 +719,9 @@ TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST) {
}

TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST_1) {
    if (!harness_->HasUserDevice()) {
        GTEST_SKIP() << "Skipping snapshot read; not supported";
    }
    ASSERT_TRUE(SetupDefault());
    // Start the merge
    StartMerge();