1use std::sync::{Arc, Mutex};
5
6use serde::{Deserialize, Serialize};
7
8use crate::devices::virtio::pmem::device::{Pmem, PmemError};
9
10#[derive(Debug, thiserror::Error, displaydoc::Display)]
12pub enum PmemConfigError {
13 AddingSecondRootDevice,
15 RootPmemDeviceAlreadyExist,
17 CreateDevice(#[from] PmemError),
19 File(std::io::Error),
21}
22
23#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(deny_unknown_fields)]
26pub struct PmemConfig {
27 pub id: String,
29 pub path_on_host: String,
31 #[serde(default)]
33 pub root_device: bool,
34 #[serde(default)]
36 pub read_only: bool,
37}
38
39#[derive(Debug, Default)]
41pub struct PmemBuilder {
42 pub devices: Vec<Arc<Mutex<Pmem>>>,
44}
45
46impl PmemBuilder {
47 pub fn has_root_device(&self) -> bool {
49 self.devices
50 .iter()
51 .any(|d| d.lock().unwrap().config.root_device)
52 }
53
54 pub fn build(
56 &mut self,
57 config: PmemConfig,
58 has_block_root: bool,
59 ) -> Result<(), PmemConfigError> {
60 if config.root_device && has_block_root {
61 return Err(PmemConfigError::AddingSecondRootDevice);
62 }
63 let position = self
64 .devices
65 .iter()
66 .position(|d| d.lock().unwrap().config.id == config.id);
67 if let Some(index) = position {
68 if !self.devices[index].lock().unwrap().config.root_device
69 && config.root_device
70 && self.has_root_device()
71 {
72 return Err(PmemConfigError::RootPmemDeviceAlreadyExist);
73 }
74 let pmem = Pmem::new(config)?;
75 let pmem = Arc::new(Mutex::new(pmem));
76 self.devices[index] = pmem;
77 } else {
78 if config.root_device && self.has_root_device() {
79 return Err(PmemConfigError::RootPmemDeviceAlreadyExist);
80 }
81 let pmem = Pmem::new(config)?;
82 let pmem = Arc::new(Mutex::new(pmem));
83 self.devices.push(pmem);
84 }
85 Ok(())
86 }
87
88 pub fn add_device(&mut self, device: Arc<Mutex<Pmem>>) {
92 self.devices.push(device);
93 }
94
95 pub fn configs(&self) -> Vec<PmemConfig> {
97 self.devices
98 .iter()
99 .map(|b| b.lock().unwrap().config.clone())
100 .collect()
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use vmm_sys_util::tempfile::TempFile;
107
108 use super::*;
109
110 #[test]
111 fn test_pmem_builder_build() {
112 let mut builder = PmemBuilder::default();
113
114 let dummy_file = TempFile::new().unwrap();
115 dummy_file.as_file().set_len(Pmem::ALIGNMENT).unwrap();
116 let dummy_path = dummy_file.as_path().to_str().unwrap().to_string();
117 let mut config = PmemConfig {
118 id: "1".into(),
119 path_on_host: dummy_path,
120 root_device: true,
121 read_only: false,
122 };
123 builder.build(config.clone(), false).unwrap();
124 assert_eq!(builder.devices.len(), 1);
125 assert!(builder.has_root_device());
126
127 config.root_device = false;
129 builder.build(config, false).unwrap();
130 assert_eq!(builder.devices.len(), 1);
131 assert!(!builder.has_root_device());
132 }
133
134 #[test]
135 fn test_pmem_builder_build_seconde_root() {
136 let mut builder = PmemBuilder::default();
137
138 let dummy_file = TempFile::new().unwrap();
139 dummy_file.as_file().set_len(Pmem::ALIGNMENT).unwrap();
140 let dummy_path = dummy_file.as_path().to_str().unwrap().to_string();
141 let mut config = PmemConfig {
142 id: "1".into(),
143 path_on_host: dummy_path,
144 root_device: true,
145 read_only: false,
146 };
147 builder.build(config.clone(), false).unwrap();
148
149 config.id = "2".into();
150 assert!(matches!(
151 builder.build(config.clone(), false).unwrap_err(),
152 PmemConfigError::RootPmemDeviceAlreadyExist,
153 ));
154 }
155
156 #[test]
157 fn test_pmem_builder_build_root_with_block_already_a_root() {
158 let mut builder = PmemBuilder::default();
159
160 let dummy_file = TempFile::new().unwrap();
161 dummy_file.as_file().set_len(Pmem::ALIGNMENT).unwrap();
162 let dummy_path = dummy_file.as_path().to_str().unwrap().to_string();
163 let config = PmemConfig {
164 id: "1".into(),
165 path_on_host: dummy_path,
166 root_device: true,
167 read_only: false,
168 };
169 assert!(matches!(
170 builder.build(config, true).unwrap_err(),
171 PmemConfigError::AddingSecondRootDevice,
172 ));
173 }
174}