1use std::collections::BTreeMap;
7use std::fs;
8use std::path::Path;
9
10use crate::misc::read_from_file;
11use crate::Cpumask;
12use anyhow::Result;
13
14#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
15pub struct NetDev {
16 iface: String,
17 node: usize,
18 pub irqs: BTreeMap<usize, Cpumask>,
19 irq_hints: BTreeMap<usize, Cpumask>,
20 original_irqs: BTreeMap<usize, Cpumask>,
21}
22
23impl NetDev {
24 pub fn iface(&self) -> &str {
25 &self.iface
26 }
27
28 pub fn node(&self) -> usize {
29 self.node
30 }
31
32 pub fn irq_hints(&self) -> &BTreeMap<usize, Cpumask> {
33 &self.irq_hints
34 }
35
36 pub fn update_irq_cpumask(&mut self, irq: usize, cpumask: Cpumask) {
37 if let Some(cur_cpumask) = self.irqs.get_mut(&irq) {
38 *cur_cpumask = cpumask;
39 }
40 }
41
42 pub fn apply_cpumasks(&self) -> Result<()> {
43 for (irq, cpumask) in self.irqs.iter() {
44 let irq_path = format!("/proc/irq/{irq}/smp_affinity");
45 fs::write(irq_path, format!("{cpumask:#x}"))?
46 }
47 Ok(())
48 }
49
50 pub fn restore_cpumasks(&self) -> Result<()> {
51 for (irq, cpumask) in self.original_irqs.iter() {
52 let irq_path = format!("/proc/irq/{irq}/smp_affinity");
53 fs::write(irq_path, format!("{cpumask:#x}"))?;
54 }
55 Ok(())
56 }
57}
58
59pub fn read_netdevs() -> Result<BTreeMap<String, NetDev>> {
60 let mut netdevs: BTreeMap<String, NetDev> = BTreeMap::new();
61
62 for entry in fs::read_dir("/sys/class/net")? {
63 let entry = entry?;
64 let iface = entry.file_name().to_string_lossy().into_owned();
65 let iface_path_raw = format!("/sys/class/net/{iface}/device/enable");
66 let iface_path = Path::new(&iface_path_raw);
67 let is_enabled = read_from_file(iface_path).unwrap_or(0_usize);
68 if is_enabled < 1 {
69 continue;
70 }
71 let raw_path = format!("/sys/class/net/{iface}/device/msi_irqs");
72 let msi_irqs_path = Path::new(&raw_path);
73 if !msi_irqs_path.exists() {
74 continue;
75 }
76
77 let node_path_raw = format!("/sys/class/net/{iface}/device/numa_node");
78 let node_path = Path::new(&node_path_raw);
79 let node = read_from_file(node_path).unwrap_or(0_usize);
80 let mut irqs = BTreeMap::new();
81 let mut irq_hints = BTreeMap::new();
82
83 for entry in fs::read_dir(msi_irqs_path)? {
84 let entry = entry.unwrap();
85 let irq = entry.file_name().to_string_lossy().into_owned();
86 if let Ok(irq) = irq.parse::<usize>() {
87 let irq_path_raw = format!("/proc/irq/{irq}");
88 let irq_path = Path::new(&irq_path_raw);
89 if !irq_path.exists() {
90 continue;
91 }
92 let affinity_raw_path = format!("/proc/irq/{irq}/smp_affinity");
93 let smp_affinity_path = Path::new(&affinity_raw_path);
94 let smp_affinity = fs::read_to_string(smp_affinity_path)?
95 .replace(",", "")
96 .replace("\n", "");
97 let cpumask = Cpumask::from_str(&smp_affinity)?;
98 irqs.insert(irq, cpumask);
99
100 let affinity_hint_raw_path = format!("/proc/irq/{irq}/affinity_hint");
102 let affinity_hint_path = Path::new(&affinity_hint_raw_path);
103 let affinity_hint = fs::read_to_string(affinity_hint_path)?
104 .replace(",", "")
105 .replace("\n", "");
106 let hint_cpumask = Cpumask::from_str(&affinity_hint)?;
107 irq_hints.insert(irq, hint_cpumask);
108 }
109 }
110 netdevs.insert(
111 iface.clone(),
112 NetDev {
113 iface,
114 node,
115 original_irqs: irqs.clone(),
116 irqs,
117 irq_hints,
118 },
119 );
120 }
121 Ok(netdevs)
122}