blob: 9c0f07b33a4b15a8f18d989019ddb644a83cf074 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fuchsia/hardware/block/volume/llcpp/fidl.h>
#include <lib/devmgr-integration-test/fixture.h>
#include <lib/devmgr-launcher/launch.h>
#include <lib/fdio/cpp/caller.h>
#include <sys/types.h>
#include <algorithm>
#include <cstdint>
#include <memory>
#include <utility>
#include <fvm/test/device-ref.h>
#include <zxtest/zxtest.h>
#include "src/storage/fvm/format.h"
namespace fvm {
namespace {
constexpr uint64_t kBlockSize = 512;
constexpr uint64_t kSliceSize = 1 << 20;
// using Partition = fuchsia_hardware_block_partition::Partition;
using Volume = fuchsia_hardware_block_volume::Volume;
using VolumeManager = fuchsia_hardware_block_volume::VolumeManager;
class FvmVolumeManagerApiTest : public zxtest::Test {
public:
static void SetUpTestCase() {
devmgr_launcher::Args args = devmgr_integration_test::IsolatedDevmgr::DefaultArgs();
args.disable_block_watcher = true;
args.sys_device_driver = devmgr_integration_test::IsolatedDevmgr::kSysdevDriver;
args.load_drivers.push_back(devmgr_integration_test::IsolatedDevmgr::kSysdevDriver);
args.driver_search_paths.push_back("/boot/driver");
devmgr_ = std::make_unique<devmgr_integration_test::IsolatedDevmgr>();
ASSERT_OK(devmgr_integration_test::IsolatedDevmgr::Create(std::move(args), devmgr_.get()));
}
static void TearDownTestCase() { devmgr_.reset(); }
protected:
static std::unique_ptr<devmgr_integration_test::IsolatedDevmgr> devmgr_;
};
std::unique_ptr<devmgr_integration_test::IsolatedDevmgr> FvmVolumeManagerApiTest::devmgr_ = nullptr;
TEST_F(FvmVolumeManagerApiTest, GetInfoNonPreallocatedMetadata) {
constexpr uint64_t kBlockCount = (50 * kSliceSize) / kBlockSize;
std::unique_ptr<RamdiskRef> ramdisk =
RamdiskRef::Create(devmgr_->devfs_root(), kBlockSize, kBlockCount);
ASSERT_TRUE(ramdisk);
std::unique_ptr<FvmAdapter> fvm =
FvmAdapter::Create(devmgr_->devfs_root(), kBlockSize, kBlockCount, kSliceSize, ramdisk.get());
ASSERT_TRUE(fvm);
const fvm::Header kExpectedFormat =
fvm::Header::FromDiskSize(fvm::kMaxUsablePartitions, kBlockSize * kBlockCount, kSliceSize);
fidl::WireResult<VolumeManager::GetInfo> result =
fidl::WireCall<VolumeManager>(fvm->device()->channel()).GetInfo();
ASSERT_OK(result.status(), "Transport layer error");
ASSERT_OK(result->status, "Service returned error.");
// Check API returns the correct information for a non preallocated FVM.
EXPECT_EQ(kExpectedFormat.slice_size, result->info->slice_size);
// Less or equal, because the metadata size is rounded to the nearest block boundary.
EXPECT_LE(result->info->current_slice_count, result->info->maximum_slice_count);
EXPECT_EQ(kExpectedFormat.GetMaxAllocationTableEntriesForDiskSize(kBlockSize * kBlockCount),
result->info->current_slice_count);
EXPECT_EQ(kExpectedFormat.GetAllocationTableAllocatedEntryCount(),
result->info->maximum_slice_count);
}
TEST_F(FvmVolumeManagerApiTest, GetInfoWithPreallocatedMetadata) {
constexpr uint64_t kBlockCount = (50 * kSliceSize) / kBlockSize;
constexpr uint64_t kMaxBlockCount = 1024 * kSliceSize / kBlockSize;
std::unique_ptr<RamdiskRef> ramdisk =
RamdiskRef::Create(devmgr_->devfs_root(), kBlockSize, kBlockCount);
ASSERT_TRUE(ramdisk);
std::unique_ptr<FvmAdapter> fvm = FvmAdapter::CreateGrowable(
devmgr_->devfs_root(), kBlockSize, kBlockCount, kMaxBlockCount, kSliceSize, ramdisk.get());
ASSERT_TRUE(fvm);
const fvm::Header kExpectedFormat = fvm::Header::FromGrowableDiskSize(
fvm::kMaxUsablePartitions, kBlockSize * kBlockCount, kBlockSize * kMaxBlockCount, kSliceSize);
fidl::WireResult<VolumeManager::GetInfo> result =
fidl::WireCall<VolumeManager>(fvm->device()->channel()).GetInfo();
ASSERT_OK(result.status(), "Transport layer error");
ASSERT_OK(result->status, "Service returned error.");
// Check API returns the correct information for a preallocated FVM.
EXPECT_EQ(kExpectedFormat.slice_size, result->info->slice_size);
// Less than because we picked sizes that enforce a difference.
EXPECT_LT(result->info->current_slice_count, result->info->maximum_slice_count);
EXPECT_EQ(kExpectedFormat.pslice_count, result->info->current_slice_count);
EXPECT_EQ(kExpectedFormat.GetAllocationTableAllocatedEntryCount(),
result->info->maximum_slice_count);
}
// Tests that the maximum extents apply to partition growth properly.
TEST_F(FvmVolumeManagerApiTest, PartitionLimit) {
constexpr uint64_t kBlockCount = (50 * kSliceSize) / kBlockSize;
constexpr uint64_t kMaxBlockCount = 1024 * kSliceSize / kBlockSize;
std::unique_ptr<RamdiskRef> ramdisk =
RamdiskRef::Create(devmgr_->devfs_root(), kBlockSize, kBlockCount);
ASSERT_TRUE(ramdisk);
std::unique_ptr<FvmAdapter> fvm = FvmAdapter::CreateGrowable(
devmgr_->devfs_root(), kBlockSize, kBlockCount, kMaxBlockCount, kSliceSize, ramdisk.get());
ASSERT_TRUE(fvm);
// Type GUID for partition.
fuchsia_hardware_block_partition::wire::Guid type_guid;
std::fill(std::begin(type_guid.value), std::end(type_guid.value), 0x11);
// Instance GUID for partition.
fuchsia_hardware_block_partition::wire::Guid guid;
std::fill(std::begin(guid.value), std::end(guid.value), 0x12);
// The partition hasn't been created yet, the result should be "not found".
fidl::WireResult<VolumeManager::GetPartitionLimit> unfound_result =
fidl::WireCall<VolumeManager>(fvm->device()->channel()).GetPartitionLimit(guid);
ASSERT_OK(unfound_result.status(), "Transport layer error");
ASSERT_EQ(unfound_result->status, ZX_ERR_NOT_FOUND);
// Create the partition inside FVM with one slice.
const char kPartitionName[] = "mypart";
fidl::WireResult<VolumeManager::AllocatePartition> alloc_result =
fidl::WireCall<VolumeManager>(fvm->device()->channel())
.AllocatePartition(1, type_guid, guid, kPartitionName, 0);
ASSERT_OK(alloc_result.status(), "Transport layer error");
ASSERT_OK(alloc_result->status, "Service returned error.");
// That partition's initial limit should be 0 (no limit).
fidl::WireResult<VolumeManager::GetPartitionLimit> get_result =
fidl::WireCall<VolumeManager>(fvm->device()->channel()).GetPartitionLimit(guid);
ASSERT_OK(get_result.status(), "Transport layer error");
ASSERT_OK(get_result->status, "Service returned error.");
EXPECT_EQ(get_result->byte_count, 0, "Expected 0 limit on init.");
// Set the limit to two slices.
fidl::WireResult<VolumeManager::SetPartitionLimit> set_result =
fidl::WireCall<VolumeManager>(fvm->device()->channel())
.SetPartitionLimit(guid, kSliceSize * 2);
ASSERT_OK(set_result.status(), "Transport layer error");
// Validate the new value can be retrieved.
fidl::WireResult<VolumeManager::GetPartitionLimit> get_result2 =
fidl::WireCall<VolumeManager>(fvm->device()->channel()).GetPartitionLimit(guid);
ASSERT_OK(get_result2.status(), "Transport layer error");
ASSERT_OK(get_result2->status, "Service returned error.");
EXPECT_EQ(get_result2->byte_count, kSliceSize * 2, "Expected the limit we set.");
// Find the partition we just created. Should be "<ramdisk-path>/fvm/<name>-p-1/block"
fbl::unique_fd volume_fd;
std::string device_name(ramdisk->path());
device_name.append("/fvm/");
device_name.append(kPartitionName);
device_name.append("-p-1/block");
ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(devmgr_->devfs_root(),
device_name.c_str(), &volume_fd));
fdio_cpp::UnownedFdioCaller volume(volume_fd.get());
// Try to expand it by one slice. Since the initial size was one slice and the limit is two, this
// should succeed.
fidl::WireResult<Volume::Extend> good_extend =
fidl::WireCall<Volume>(volume.channel()->borrow()).Extend(100, 1);
ASSERT_OK(good_extend.status(), "Transport error");
ASSERT_OK(good_extend->status, "Expected Expand() call to succeed.");
// Adding a third slice should fail since it's already at the max size.
fidl::WireResult<Volume::Extend> bad_extend =
fidl::WireCall<Volume>(volume.channel()->borrow()).Extend(200, 1);
ASSERT_OK(bad_extend.status(), "Transport error");
ASSERT_EQ(bad_extend->status, ZX_ERR_NO_SPACE, "Expected Expand() call to fail.");
// Delete and re-create the partition. It should have no limit.
fidl::WireResult<Volume::Destroy> destroy_result =
fidl::WireCall<Volume>(volume.channel()->borrow()).Destroy();
ASSERT_OK(destroy_result.status(), "Transport layer error");
ASSERT_OK(destroy_result->status, "Can't destroy partition.");
volume_fd.reset();
fidl::WireResult<VolumeManager::AllocatePartition> alloc2_result =
fidl::WireCall<VolumeManager>(fvm->device()->channel())
.AllocatePartition(1, type_guid, guid,
/*kPartitionName*/ "thepart", 0);
ASSERT_OK(alloc2_result.status(), "Transport layer error");
ASSERT_OK(alloc2_result->status, "Service returned error.");
// That partition's initial limit should be 0 (no limit).
fidl::WireResult<VolumeManager::GetPartitionLimit> last_get_result =
fidl::WireCall<VolumeManager>(fvm->device()->channel()).GetPartitionLimit(guid);
ASSERT_OK(last_get_result.status(), "Transport layer error");
ASSERT_OK(last_get_result->status, "Service returned error.");
EXPECT_EQ(last_get_result->byte_count, 0, "Expected 0 limit on new partition.");
}
} // namespace
} // namespace fvm