blob: 5b8093226acf22b3c498b79f189139754e93fbf3 [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.
use {
crate::{
blob_actor::BlobActor, deletion_actor::DeletionActor, instance_actor::InstanceActor,
read_actor::ReadActor, Args, BLOBFS_MOUNT_PATH,
},
async_trait::async_trait,
fidl_fuchsia_io as fio,
fs_management::Blobfs,
fuchsia_zircon::Vmo,
futures::lock::Mutex,
rand::{rngs::SmallRng, Rng, SeedableRng},
std::sync::Arc,
std::time::Duration,
storage_stress_test_utils::{
data::{Compressibility, FileFactory, UncompressedSize},
fvm::{get_volume_path, FvmInstance, Guid},
io::Directory,
},
stress_test::{actor::ActorRunner, environment::Environment, random_seed},
};
// All partitions in this test have their type set to this arbitrary GUID.
const TYPE_GUID: Guid =
[0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf];
const EIGHT_KIB: u64 = 8192;
const ONE_MIB: u64 = 1048576;
const FOUR_MIB: u64 = 4 * ONE_MIB;
/// Describes the environment that this blobfs stress test will run under.
pub struct BlobfsEnvironment {
seed: u64,
args: Args,
vmo: Vmo,
volume_guid: Guid,
small_blob_actor: Arc<Mutex<BlobActor>>,
medium_blob_actor: Arc<Mutex<BlobActor>>,
large_blob_actor: Arc<Mutex<BlobActor>>,
deletion_actor: Arc<Mutex<DeletionActor>>,
instance_actor: Arc<Mutex<InstanceActor>>,
read_actor: Arc<Mutex<ReadActor>>,
}
pub fn open_blobfs_root() -> Directory {
Directory::from_namespace(
BLOBFS_MOUNT_PATH,
fio::OpenFlags::RIGHT_WRITABLE | fio::OpenFlags::RIGHT_READABLE,
)
.unwrap()
}
impl BlobfsEnvironment {
pub async fn new(args: Args) -> Self {
// Create the VMO that the ramdisk is backed by
let disk_size = args.ramdisk_block_count * args.ramdisk_block_size;
let vmo = Vmo::create(disk_size).unwrap();
// Initialize the VMO with FVM partition style and a single blobfs partition
// Create a ramdisk and setup FVM.
let mut fvm =
FvmInstance::new(true, &vmo, args.fvm_slice_size, args.ramdisk_block_size).await;
// Create a blobfs volume
let volume_guid = fvm.new_volume("blobfs", &TYPE_GUID, None).await;
// Find the path to the volume
let volume_path = get_volume_path(&volume_guid).await;
// Initialize blobfs on volume
let controller = fuchsia_component::client::connect_to_protocol_at_path::<
fidl_fuchsia_device::ControllerMarker,
>(&format!("{}/device_controller", volume_path.to_str().unwrap()))
.unwrap();
let mut blobfs = Blobfs::new(controller);
blobfs.format().await.unwrap();
let seed = match args.seed {
Some(seed) => seed,
None => random_seed(),
};
// Mount the blobfs volume
blobfs.fsck().await.unwrap();
let mut blobfs = blobfs.serve().await.unwrap();
blobfs.bind_to_path(BLOBFS_MOUNT_PATH).unwrap();
// Create the instance actor
let instance_actor = Arc::new(Mutex::new(InstanceActor::new(fvm, blobfs)));
let mut rng = SmallRng::seed_from_u64(seed);
// Create the blob actors
let small_blob_actor = {
let rng = SmallRng::from_seed(rng.gen());
let uncompressed_size = UncompressedSize::Exact(EIGHT_KIB);
let compressibility = Compressibility::Uncompressible;
let factory = FileFactory::new(rng, uncompressed_size, compressibility);
let root_dir = open_blobfs_root();
Arc::new(Mutex::new(BlobActor::new(factory, root_dir)))
};
let medium_blob_actor = {
let rng = SmallRng::from_seed(rng.gen());
let uncompressed_size = UncompressedSize::InRange(ONE_MIB, FOUR_MIB);
let compressibility = Compressibility::Compressible;
let factory = FileFactory::new(rng, uncompressed_size, compressibility);
let root_dir = open_blobfs_root();
Arc::new(Mutex::new(BlobActor::new(factory, root_dir)))
};
let large_blob_actor = {
let rng = SmallRng::from_seed(rng.gen());
let uncompressed_size = UncompressedSize::Exact(2 * disk_size);
let compressibility = Compressibility::Compressible;
let factory = FileFactory::new(rng, uncompressed_size, compressibility);
let root_dir = open_blobfs_root();
Arc::new(Mutex::new(BlobActor::new(factory, root_dir)))
};
// Create the read actor
let read_actor = {
let rng = SmallRng::from_seed(rng.gen());
let root_dir = open_blobfs_root();
Arc::new(Mutex::new(ReadActor::new(rng, root_dir)))
};
// Create the deletion actor
let deletion_actor = {
let rng = SmallRng::from_seed(rng.gen());
let root_dir = open_blobfs_root();
Arc::new(Mutex::new(DeletionActor::new(rng, root_dir)))
};
Self {
seed,
args,
vmo,
volume_guid,
instance_actor,
small_blob_actor,
medium_blob_actor,
large_blob_actor,
read_actor,
deletion_actor,
}
}
}
impl std::fmt::Debug for BlobfsEnvironment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Environment").field("seed", &self.seed).field("args", &self.args).finish()
}
}
#[async_trait]
impl Environment for BlobfsEnvironment {
fn target_operations(&self) -> Option<u64> {
self.args.num_operations
}
fn timeout_seconds(&self) -> Option<u64> {
self.args.time_limit_secs
}
async fn actor_runners(&mut self) -> Vec<ActorRunner> {
let mut runners = vec![
ActorRunner::new("small_blob_actor", None, self.small_blob_actor.clone()),
ActorRunner::new("medium_blob_actor", None, self.medium_blob_actor.clone()),
ActorRunner::new("large_blob_actor", None, self.large_blob_actor.clone()),
ActorRunner::new("read_actor", None, self.read_actor.clone()),
ActorRunner::new(
"deletion_actor",
Some(Duration::from_secs(10)),
self.deletion_actor.clone(),
),
];
if let Some(secs) = self.args.disconnect_secs {
if secs > 0 {
let runner = ActorRunner::new(
"instance_actor",
Some(Duration::from_secs(secs)),
self.instance_actor.clone(),
);
runners.push(runner);
}
}
runners
}
async fn reset(&mut self) {
{
let mut actor = self.instance_actor.lock().await;
// The environment is only reset when the instance is killed.
// TODO(72385): Pass the actor error here, so it can be printed out on assert failure.
assert!(actor.instance.is_none());
// Create a ramdisk and setup FVM.
let fvm = FvmInstance::new(
false,
&self.vmo,
self.args.fvm_slice_size,
self.args.ramdisk_block_size,
)
.await;
// Find the path to the volume
let volume_path = get_volume_path(&self.volume_guid).await;
// Initialize blobfs on volume
let controller =
fuchsia_component::client::connect_to_protocol_at_path::<
fidl_fuchsia_device::ControllerMarker,
>(&format!("{}/device_controller", volume_path.to_str().unwrap()))
.unwrap();
let mut blobfs = Blobfs::new(controller);
// Mount the blobfs volume
blobfs.fsck().await.unwrap();
let mut blobfs = blobfs.serve().await.unwrap();
blobfs.bind_to_path(BLOBFS_MOUNT_PATH).unwrap();
// Replace the fvm and blobfs instances
actor.instance = Some((blobfs, fvm));
}
// Replace the root directory with a new one
{
let mut actor = self.small_blob_actor.lock().await;
actor.root_dir = open_blobfs_root();
}
{
let mut actor = self.medium_blob_actor.lock().await;
actor.root_dir = open_blobfs_root();
}
{
let mut actor = self.large_blob_actor.lock().await;
actor.root_dir = open_blobfs_root();
}
{
let mut actor = self.read_actor.lock().await;
actor.root_dir = open_blobfs_root();
}
{
let mut actor = self.deletion_actor.lock().await;
actor.root_dir = open_blobfs_root();
}
}
}