1use crate::parsers::ARG_NUMA;
2use bon::Builder;
3use proptest_derive::Arbitrary;
4use std::str::FromStr;
5
6use crate::parsers::DELIM_COMMA;
7use crate::to_command::ToCommand;
8
9#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
10pub struct NUMANodeMem {
11 mem_size: Option<usize>,
12 cpu_first: Option<usize>,
13 cpu_last: Option<usize>,
14 node_id: Option<usize>,
15 initiator: Option<usize>,
16}
17
18#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
19pub struct NUMANodeMemDev {
20 mem_id: Option<usize>,
21 cpu_first: Option<usize>,
22 cpu_last: Option<usize>,
23 node_id: Option<usize>,
24 initiator: Option<usize>,
25}
26#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
27pub struct NUMADist {
28 src: usize,
29 dst: usize,
30 val: usize,
31}
32
33#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
34pub struct NUMACPU {
35 node_id: usize,
36 socket_id: Option<usize>,
37 core_id: Option<usize>,
38 thread_id: Option<usize>,
39}
40
41#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
43pub enum NUMAHierarchy {
44 Memory,
45 FirstLevel,
46 SecondLevel,
47 ThirdLevel,
48}
49#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
51pub enum NUMADataType {
52 AccessLatency,
53 ReadLatency,
54 WriteLatency,
55}
56
57#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
58pub struct NUMAHMATLb {
59 initiator: usize,
60 target: usize,
61 hierarchy: NUMAHierarchy,
62 data_type: NUMADataType,
63 latency: Option<usize>,
64 bandwidth: Option<usize>,
65}
66
67#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
68pub enum HMATCacheAssociativity {
69 None,
70 Direct,
71 Complex,
72}
73#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
74pub enum HMATCachePolicy {
75 None,
76 WriteBack,
77 WriteThrough,
78}
79
80#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
81pub struct NUMAHMATCache {
82 node_id: usize,
83 size: usize,
84 level: usize,
85 associativity: Option<HMATCacheAssociativity>,
86 policy: Option<HMATCachePolicy>,
87 line: Option<usize>,
88}
89
90#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
95pub enum NUMA {
96 NodeMem(NUMANodeMem),
97 NodeMemDev(NUMANodeMemDev),
98 Dist(NUMADist),
99 Cpu(NUMACPU),
100 HMATLB(NUMAHMATLb),
101 HMATCache(NUMAHMATCache),
102}
103
104impl ToCommand for NUMA {
105 fn command(&self) -> String {
106 ARG_NUMA.to_string()
107 }
108 fn to_args(&self) -> Vec<String> {
109 match self {
110 NUMA::NodeMem(node_mem) => {
111 let mut node_mem_args = "node".to_string();
112 if let Some(mem) = &node_mem.mem_size {
113 node_mem_args.push_str(format!(",mem={}", mem).as_str());
114 }
115 if let Some(cpu) = &node_mem.cpu_first {
116 node_mem_args.push_str(format!(",cpu={}", cpu).as_str());
117 }
118 if let Some(cpu) = &node_mem.cpu_last {
119 node_mem_args.push_str(format!("-{}", cpu).as_str());
120 }
121 if let Some(node_id) = &node_mem.node_id {
122 node_mem_args.push_str(format!(",nodeid={}", node_id).as_str());
123 }
124 if let Some(initiator) = &node_mem.initiator {
125 node_mem_args.push_str(format!(",initiator={}", initiator).as_str());
126 }
127 vec![node_mem_args.to_string()]
128 }
129 NUMA::NodeMemDev(node_memdev) => {
130 let mut node_memdev_args = "node".to_string();
131 if let Some(memdev) = &node_memdev.mem_id {
132 node_memdev_args.push_str(format!(",memdev={}", memdev).as_str());
133 }
134 if let Some(cpu) = &node_memdev.cpu_first {
135 node_memdev_args.push_str(format!(",cpu={}", cpu).as_str());
136 }
137 if let Some(cpu) = &node_memdev.cpu_last {
138 node_memdev_args.push_str(format!("-{}", cpu).as_str());
139 }
140 if let Some(node_id) = &node_memdev.node_id {
141 node_memdev_args.push_str(format!(",nodeid={}", node_id).as_str());
142 }
143 if let Some(initiator) = &node_memdev.initiator {
144 node_memdev_args.push_str(format!(",initiator={}", initiator).as_str());
145 }
146 vec![node_memdev_args.to_string()]
147 }
148 NUMA::Dist(dist) => {
149 vec![format!("dist,src={},dst={},val={}", dist.src, dist.dst, dist.val)]
150 }
151 NUMA::Cpu(cpu) => {
152 let mut cpu_args = "cpu".to_string();
153
154 cpu_args.push_str(format!(",node-id={}", cpu.node_id).as_str());
155 if let Some(socket_id) = &cpu.socket_id {
156 cpu_args.push_str(format!(",socket-id={}", socket_id).as_str());
157 }
158 if let Some(core_id) = &cpu.core_id {
159 cpu_args.push_str(format!(",core-id={}", core_id).as_str());
160 }
161 if let Some(thread_id) = &cpu.thread_id {
162 cpu_args.push_str(format!(",thread-id={}", thread_id).as_str());
163 }
164 vec![cpu_args.to_string()]
165 }
166 NUMA::HMATLB(hmat_lb) => {
167 let mut hmat_lb_args = "hmat-lb".to_string();
168 hmat_lb_args.push_str(format!(",initiator={},target={},hierarchy=", hmat_lb.initiator, hmat_lb.target).as_str());
169 match hmat_lb.hierarchy {
170 NUMAHierarchy::Memory => hmat_lb_args.push_str("memory"),
171 NUMAHierarchy::FirstLevel => hmat_lb_args.push_str("first-level"),
172 NUMAHierarchy::SecondLevel => hmat_lb_args.push_str("second-level"),
173 NUMAHierarchy::ThirdLevel => hmat_lb_args.push_str("third-level"),
174 }
175 hmat_lb_args.push_str(",data-type=");
176 match hmat_lb.data_type {
177 NUMADataType::AccessLatency => hmat_lb_args.push_str("access-latency"),
178 NUMADataType::ReadLatency => hmat_lb_args.push_str("read-latency"),
179 NUMADataType::WriteLatency => hmat_lb_args.push_str("write-latency"),
180 }
181 if let Some(lat) = &hmat_lb.latency {
182 hmat_lb_args.push_str(format!(",latency={}", lat).as_str());
183 }
184 if let Some(bw) = &hmat_lb.bandwidth {
185 hmat_lb_args.push_str(format!(",bandwidth={}", bw).as_str());
186 }
187 vec![hmat_lb_args]
188 }
189 NUMA::HMATCache(hmat_cache) => {
190 let mut hmat_cache_args = "hmat-cache".to_string();
191 hmat_cache_args.push_str(format!(",node-id={},size={},level={}", hmat_cache.node_id, hmat_cache.size, hmat_cache.level).as_str());
192 if let Some(assoc) = &hmat_cache.associativity {
193 hmat_cache_args.push_str(",associativity=");
194 match assoc {
195 HMATCacheAssociativity::None => hmat_cache_args.push_str("none"),
196 HMATCacheAssociativity::Direct => hmat_cache_args.push_str("direct"),
197 HMATCacheAssociativity::Complex => hmat_cache_args.push_str("complex"),
198 }
199 }
200 if let Some(policy) = &hmat_cache.policy {
201 hmat_cache_args.push_str(",policy=");
202 match policy {
203 HMATCachePolicy::None => hmat_cache_args.push_str("none"),
204 HMATCachePolicy::WriteBack => hmat_cache_args.push_str("write-back"),
205 HMATCachePolicy::WriteThrough => hmat_cache_args.push_str("write-through"),
206 }
207 }
208 if let Some(line) = &hmat_cache.line {
209 hmat_cache_args.push_str(format!(",line={}", line).as_str());
210 }
211 vec![hmat_cache_args]
212 }
213 }
214 }
215}
216
217impl FromStr for NUMA {
218 type Err = String;
219
220 fn from_str(s: &str) -> Result<Self, Self::Err> {
221 let mut parts = s.split(DELIM_COMMA);
222 let kind = parts.next().ok_or_else(|| "empty numa argument".to_string())?;
223
224 match kind {
225 "node" => {
226 let mut mem_size = None;
227 let mut memdev = None;
228 let mut cpu_first = None;
229 let mut cpu_last = None;
230 let mut node_id = None;
231 let mut initiator = None;
232
233 for part in parts {
234 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid numa node option: {part}"))?;
235 match key {
236 "mem" => mem_size = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
237 "memdev" => memdev = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
238 "cpu" => {
239 if let Some((first, last)) = raw.split_once('-') {
240 cpu_first = Some(first.parse::<usize>().map_err(|e| e.to_string())?);
241 cpu_last = Some(last.parse::<usize>().map_err(|e| e.to_string())?);
242 } else {
243 cpu_first = Some(raw.parse::<usize>().map_err(|e| e.to_string())?);
244 }
245 }
246 "nodeid" => node_id = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
247 "initiator" => initiator = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
248 other => return Err(format!("unsupported numa node option: {other}")),
249 }
250 }
251
252 if let Some(mem_id) = memdev {
253 Ok(Self::NodeMemDev(NUMANodeMemDev {
254 mem_id: Some(mem_id),
255 cpu_first,
256 cpu_last,
257 node_id,
258 initiator,
259 }))
260 } else {
261 Ok(Self::NodeMem(NUMANodeMem {
262 mem_size,
263 cpu_first,
264 cpu_last,
265 node_id,
266 initiator,
267 }))
268 }
269 }
270 "dist" => {
271 let mut src = None;
272 let mut dst = None;
273 let mut val = None;
274
275 for part in parts {
276 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid numa dist option: {part}"))?;
277 match key {
278 "src" => src = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
279 "dst" => dst = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
280 "val" => val = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
281 other => return Err(format!("unsupported numa dist option: {other}")),
282 }
283 }
284
285 Ok(Self::Dist(NUMADist {
286 src: src.ok_or_else(|| "numa dist requires src=".to_string())?,
287 dst: dst.ok_or_else(|| "numa dist requires dst=".to_string())?,
288 val: val.ok_or_else(|| "numa dist requires val=".to_string())?,
289 }))
290 }
291 "cpu" => {
292 let mut node_id = None;
293 let mut socket_id = None;
294 let mut core_id = None;
295 let mut thread_id = None;
296
297 for part in parts {
298 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid numa cpu option: {part}"))?;
299 match key {
300 "node-id" => node_id = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
301 "socket-id" => socket_id = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
302 "core-id" => core_id = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
303 "thread-id" => thread_id = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
304 other => return Err(format!("unsupported numa cpu option: {other}")),
305 }
306 }
307
308 Ok(Self::Cpu(NUMACPU {
309 node_id: node_id.ok_or_else(|| "numa cpu requires node-id=".to_string())?,
310 socket_id,
311 core_id,
312 thread_id,
313 }))
314 }
315 "hmat-lb" => {
316 let mut initiator = None;
317 let mut target = None;
318 let mut hierarchy = None;
319 let mut data_type = None;
320 let mut latency = None;
321 let mut bandwidth = None;
322
323 for part in parts {
324 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid numa hmat-lb option: {part}"))?;
325 match key {
326 "initiator" => initiator = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
327 "target" => target = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
328 "hierarchy" => hierarchy = Some(parse_numa_hierarchy(raw)?),
329 "data-type" => data_type = Some(parse_numa_data_type(raw)?),
330 "latency" => latency = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
331 "bandwidth" => bandwidth = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
332 other => return Err(format!("unsupported numa hmat-lb option: {other}")),
333 }
334 }
335
336 Ok(Self::HMATLB(NUMAHMATLb {
337 initiator: initiator.ok_or_else(|| "numa hmat-lb requires initiator=".to_string())?,
338 target: target.ok_or_else(|| "numa hmat-lb requires target=".to_string())?,
339 hierarchy: hierarchy.ok_or_else(|| "numa hmat-lb requires hierarchy=".to_string())?,
340 data_type: data_type.ok_or_else(|| "numa hmat-lb requires data-type=".to_string())?,
341 latency,
342 bandwidth,
343 }))
344 }
345 "hmat-cache" => {
346 let mut node_id = None;
347 let mut size = None;
348 let mut level = None;
349 let mut associativity = None;
350 let mut policy = None;
351 let mut line = None;
352
353 for part in parts {
354 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid numa hmat-cache option: {part}"))?;
355 match key {
356 "node-id" => node_id = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
357 "size" => size = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
358 "level" => level = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
359 "associativity" => associativity = Some(parse_hmat_cache_associativity(raw)?),
360 "policy" => policy = Some(parse_hmat_cache_policy(raw)?),
361 "line" => line = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
362 other => return Err(format!("unsupported numa hmat-cache option: {other}")),
363 }
364 }
365
366 Ok(Self::HMATCache(NUMAHMATCache {
367 node_id: node_id.ok_or_else(|| "numa hmat-cache requires node-id=".to_string())?,
368 size: size.ok_or_else(|| "numa hmat-cache requires size=".to_string())?,
369 level: level.ok_or_else(|| "numa hmat-cache requires level=".to_string())?,
370 associativity,
371 policy,
372 line,
373 }))
374 }
375 other => Err(format!("unsupported numa kind: {other}")),
376 }
377 }
378}
379
380fn parse_numa_hierarchy(value: &str) -> Result<NUMAHierarchy, String> {
381 match value {
382 "memory" => Ok(NUMAHierarchy::Memory),
383 "first-level" => Ok(NUMAHierarchy::FirstLevel),
384 "second-level" => Ok(NUMAHierarchy::SecondLevel),
385 "third-level" => Ok(NUMAHierarchy::ThirdLevel),
386 other => Err(format!("invalid numa hierarchy: {other}")),
387 }
388}
389
390fn parse_numa_data_type(value: &str) -> Result<NUMADataType, String> {
391 match value {
392 "access-latency" => Ok(NUMADataType::AccessLatency),
393 "read-latency" => Ok(NUMADataType::ReadLatency),
394 "write-latency" => Ok(NUMADataType::WriteLatency),
395 other => Err(format!("invalid numa data-type: {other}")),
396 }
397}
398
399fn parse_hmat_cache_associativity(value: &str) -> Result<HMATCacheAssociativity, String> {
400 match value {
401 "none" => Ok(HMATCacheAssociativity::None),
402 "direct" => Ok(HMATCacheAssociativity::Direct),
403 "complex" => Ok(HMATCacheAssociativity::Complex),
404 other => Err(format!("invalid hmat-cache associativity: {other}")),
405 }
406}
407
408fn parse_hmat_cache_policy(value: &str) -> Result<HMATCachePolicy, String> {
409 match value {
410 "none" => Ok(HMATCachePolicy::None),
411 "write-back" => Ok(HMATCachePolicy::WriteBack),
412 "write-through" => Ok(HMATCachePolicy::WriteThrough),
413 other => Err(format!("invalid hmat-cache policy: {other}")),
414 }
415}