fuchsia_storage_benchmarks/filesystems/
fxfs.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::filesystems::FsManagementFilesystemInstance;
6use async_trait::async_trait;
7use fidl::endpoints::ClientEnd;
8use fidl_fuchsia_fxfs::{CryptManagementMarker, CryptMarker, KeyPurpose};
9use fuchsia_component::client::{connect_channel_to_protocol, connect_to_protocol_at_dir_root};
10
11use std::path::Path;
12use std::sync::{Arc, Once};
13use storage_benchmarks::{
14    BlockDeviceConfig, BlockDeviceFactory, CacheClearableFilesystem, Filesystem, FilesystemConfig,
15};
16
17/// Config object for starting Fxfs instances.
18#[derive(Clone)]
19pub struct Fxfs {
20    volume_size: u64,
21}
22
23impl Fxfs {
24    pub fn new(volume_size: u64) -> Self {
25        Fxfs { volume_size }
26    }
27}
28
29#[async_trait]
30impl FilesystemConfig for Fxfs {
31    type Filesystem = FxfsInstance;
32
33    async fn start_filesystem(
34        &self,
35        block_device_factory: &dyn BlockDeviceFactory,
36    ) -> FxfsInstance {
37        let block_device = block_device_factory
38            .create_block_device(&BlockDeviceConfig {
39                requires_fvm: false,
40                use_zxcrypt: false,
41                volume_size: Some(self.volume_size),
42            })
43            .await;
44        FxfsInstance {
45            fxfs: FsManagementFilesystemInstance::new(
46                fs_management::Fxfs::default(),
47                block_device,
48                Some(Arc::new(get_crypt_client)),
49                /*as_blob=*/ false,
50            )
51            .await,
52        }
53    }
54
55    fn name(&self) -> String {
56        "fxfs".to_owned()
57    }
58}
59
60fn get_crypt_client() -> ClientEnd<CryptMarker> {
61    static CRYPT_CLIENT_INITIALIZER: Once = Once::new();
62    CRYPT_CLIENT_INITIALIZER.call_once(|| {
63        let (client_end, server_end) = zx::Channel::create();
64        connect_channel_to_protocol::<CryptManagementMarker>(server_end)
65            .expect("Failed to connect to the crypt management service");
66        let crypt_management_service =
67            fidl_fuchsia_fxfs::CryptManagementSynchronousProxy::new(client_end);
68
69        let mut key = [0; 32];
70        zx::cprng_draw(&mut key);
71        let wrapping_key_id_0 = [0; 16];
72        match crypt_management_service
73            .add_wrapping_key(&wrapping_key_id_0, &key, zx::MonotonicInstant::INFINITE)
74            .expect("FIDL failed")
75            .map_err(zx::Status::from_raw)
76        {
77            Ok(()) => {}
78            Err(zx::Status::ALREADY_EXISTS) => {
79                // In tests, the binary is run multiple times which gets around the `Once`. The fxfs
80                // crypt component is not restarted for each test so it may already be initialized.
81                return;
82            }
83            Err(e) => panic!("add_wrapping_key failed: {:?}", e),
84        };
85        zx::cprng_draw(&mut key);
86        let mut wrapping_key_id_1 = [0; 16];
87        wrapping_key_id_1[0] = 1;
88        crypt_management_service
89            .add_wrapping_key(&wrapping_key_id_1, &key, zx::MonotonicInstant::INFINITE)
90            .expect("FIDL failed")
91            .map_err(zx::Status::from_raw)
92            .expect("add_wrapping_key failed");
93        crypt_management_service
94            .set_active_key(KeyPurpose::Data, &wrapping_key_id_0, zx::MonotonicInstant::INFINITE)
95            .expect("FIDL failed")
96            .map_err(zx::Status::from_raw)
97            .expect("set_active_key failed");
98        crypt_management_service
99            .set_active_key(
100                KeyPurpose::Metadata,
101                &wrapping_key_id_1,
102                zx::MonotonicInstant::INFINITE,
103            )
104            .expect("FIDL failed")
105            .map_err(zx::Status::from_raw)
106            .expect("set_active_key failed");
107    });
108    let (client_end, server_end) = fidl::endpoints::create_endpoints();
109    connect_channel_to_protocol::<CryptMarker>(server_end.into())
110        .expect("Failed to connect to crypt service");
111    client_end
112}
113
114pub struct FxfsInstance {
115    fxfs: FsManagementFilesystemInstance,
116}
117
118impl FxfsInstance {
119    async fn flush_journal(&self) {
120        connect_to_protocol_at_dir_root::<fidl_fuchsia_fxfs::DebugMarker>(
121            self.fxfs.exposed_services_dir(),
122        )
123        .expect("Connecting to debug protocol")
124        .compact()
125        .await
126        .expect("Sending journal flush message")
127        .map_err(zx::Status::from_raw)
128        .expect("Journal flush");
129    }
130}
131
132#[async_trait]
133impl Filesystem for FxfsInstance {
134    async fn shutdown(self) {
135        self.fxfs.shutdown().await
136    }
137
138    fn benchmark_dir(&self) -> &Path {
139        self.fxfs.benchmark_dir()
140    }
141}
142
143#[async_trait]
144impl CacheClearableFilesystem for FxfsInstance {
145    async fn clear_cache(&mut self) {
146        // Flush the journal, since otherwise metadata stays in the mutable in-memory layer and
147        // clearing cache doesn't remove it, even over remount. So to emulate accessing actually
148        // cold data, we push it down into the on-disk layer by forcing a journal flush.
149        self.flush_journal().await;
150        self.fxfs.clear_cache().await
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::Fxfs;
157    use crate::filesystems::testing::check_filesystem;
158
159    #[fuchsia::test]
160    async fn start_fxfs() {
161        check_filesystem(Fxfs { volume_size: 4 * 1024 * 1024 }).await;
162    }
163}