blob: 2bf5d9706ce660bcfc9b8dfc26ccfbd5b70b6018 [file] [log] [blame]
// Copyright 2021 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 <lib/fdio/io.h>
#include <vector>
#include <gtest/gtest.h>
#include "src/storage/blobfs/mount.h"
#include "src/storage/blobfs/test/blob_utils.h"
#include "src/storage/blobfs/test/integration/fdio_test.h"
namespace blobfs {
namespace {
using ::testing::UnitTest;
// With 32KB chunks coming from blobfs, we only have 160 page faults available for a 5MB file.
constexpr int kFileSize = 5 << 20;
constexpr int kChunkSize = 32 << 10;
constexpr int kReadsPerFile = kFileSize / kChunkSize;
class BlobfsMultithreadedSmokeTest : public FdioTest, public testing::WithParamInterface<int> {
public:
BlobfsMultithreadedSmokeTest() {
MountOptions options;
options.paging_threads = NumThreads();
options.compression_settings = {CompressionAlgorithm::kChunked, 14};
set_mount_options(options);
}
int NumThreads() const { return GetParam(); }
};
struct ReadLocation {
size_t file;
size_t offset;
};
void PerformReads(const ReadLocation* locations, size_t num_reads, const zx::vmo* vmos) {
uint8_t ch;
for (size_t i = 0; i < num_reads; ++i) {
vmos[locations[i].file].read(&ch, locations[i].offset, 1);
}
}
TEST_P(BlobfsMultithreadedSmokeTest, MultithreadedReads) {
srand(testing::GTEST_FLAG(random_seed) + NumThreads());
std::vector<std::unique_ptr<BlobInfo>> file_info;
// Add more files for more threads. We'll need it for scaling up the number of available pages to
// fault in.
for (int i = 0; i < NumThreads(); ++i) {
// A fairly sized blob that should get realistically compressed.
std::unique_ptr<BlobInfo> info = GenerateRealisticBlob(".", kFileSize);
fbl::unique_fd fd(openat(root_fd(), info->path, O_CREAT | O_RDWR));
ASSERT_TRUE(fd.is_valid());
ASSERT_EQ(ftruncate(fd.get(), info->size_data), 0);
ASSERT_EQ(StreamAll(write, fd.get(), info->data.get(), info->size_data), 0)
<< "Failed to write Data";
file_info.push_back(std::move(info));
}
std::vector<zx::vmo> vmos;
for (const auto& info : file_info) {
fbl::unique_fd fd(openat(root_fd(), info->path, O_RDONLY));
ASSERT_TRUE(fd.is_valid());
zx_handle_t handle;
ASSERT_EQ(fdio_get_vmo_clone(fd.get(), &handle), ZX_OK);
vmos.emplace_back(handle);
}
// Generate every page fault possible, then scramble them up.
std::vector<ReadLocation> reads(static_cast<size_t>(NumThreads() * kReadsPerFile));
for (size_t file_id = 0; file_id < vmos.size(); ++file_id) {
for (size_t offset = 0; offset < kReadsPerFile; ++offset) {
reads[file_id * kReadsPerFile + offset] = {file_id, offset * kChunkSize};
}
}
ReadLocation tmp = reads[0];
size_t location = 0;
for (int i = 0; i < NumThreads() * kReadsPerFile - 1; ++i) {
size_t next = rand() % reads.size();
reads[location] = reads[next];
tmp = reads[next];
location = next;
}
std::vector<std::thread> threads;
for (size_t i = 0; static_cast<int>(i) < NumThreads(); i++) {
threads.emplace_back(PerformReads, &reads[i * kReadsPerFile], kReadsPerFile, vmos.data());
}
for (auto& t : threads) {
t.join();
}
}
INSTANTIATE_TEST_SUITE_P(/*no prefix*/, BlobfsMultithreadedSmokeTest, testing::Values(1, 2, 4),
testing::PrintToStringParamName());
} // namespace
} // namespace blobfs