scx_utils/
netdev.rs

1// Copyright (c) Meta Platforms, Inc. and affiliates.
2
3// This software may be used and distributed according to the terms of the
4// GNU General Public License version 2.
5
6use 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}
21
22impl NetDev {
23    pub fn iface(&self) -> &str {
24        &self.iface
25    }
26
27    pub fn node(&self) -> usize {
28        self.node
29    }
30
31    pub fn irq_hints(&self) -> &BTreeMap<usize, Cpumask> {
32        &self.irq_hints
33    }
34
35    pub fn update_irq_cpumask(&mut self, irq: usize, cpumask: Cpumask) {
36        if let Some(cur_cpumask) = self.irqs.get_mut(&irq) {
37            *cur_cpumask = cpumask;
38        }
39    }
40
41    pub fn apply_cpumasks(&self) -> Result<()> {
42        for (irq, cpumask) in self.irqs.iter() {
43            let irq_path = format!("/proc/irq/{}/smp_affinity", irq);
44            fs::write(irq_path, format!("{:#x}", cpumask))?
45        }
46        Ok(())
47    }
48}
49
50pub fn read_netdevs() -> Result<BTreeMap<String, NetDev>> {
51    let mut netdevs: BTreeMap<String, NetDev> = BTreeMap::new();
52
53    for entry in fs::read_dir("/sys/class/net")? {
54        let entry = entry?;
55        let iface = entry.file_name().to_string_lossy().into_owned();
56        let iface_path_raw = format!("/sys/class/net/{}/device/enable", iface);
57        let iface_path = Path::new(&iface_path_raw);
58        let is_enabled = read_from_file(iface_path).unwrap_or(0_usize);
59        if is_enabled < 1 {
60            continue;
61        }
62        let raw_path = format!("/sys/class/net/{}/device/msi_irqs", iface);
63        let msi_irqs_path = Path::new(&raw_path);
64        if !msi_irqs_path.exists() {
65            continue;
66        }
67
68        let node_path_raw = format!("/sys/class/net/{}/device/numa_node", iface);
69        let node_path = Path::new(&node_path_raw);
70        let node = read_from_file(node_path).unwrap_or(0_usize);
71        let mut irqs = BTreeMap::new();
72        let mut irq_hints = BTreeMap::new();
73
74        for entry in fs::read_dir(msi_irqs_path)? {
75            let entry = entry.unwrap();
76            let irq = entry.file_name().to_string_lossy().into_owned();
77            if let Ok(irq) = irq.parse::<usize>() {
78                let irq_path_raw = format!("/proc/irq/{}", irq);
79                let irq_path = Path::new(&irq_path_raw);
80                if !irq_path.exists() {
81                    continue;
82                }
83                let affinity_raw_path = format!("/proc/irq/{}/smp_affinity", irq);
84                let smp_affinity_path = Path::new(&affinity_raw_path);
85                let smp_affinity = fs::read_to_string(smp_affinity_path)?
86                    .replace(",", "")
87                    .replace("\n", "");
88                let cpumask = Cpumask::from_str(&smp_affinity)?;
89                irqs.insert(irq, cpumask);
90
91                // affinity hints
92                let affinity_hint_raw_path = format!("/proc/irq/{}/affinity_hint", irq);
93                let affinity_hint_path = Path::new(&affinity_hint_raw_path);
94                let affinity_hint = fs::read_to_string(affinity_hint_path)?
95                    .replace(",", "")
96                    .replace("\n", "");
97                let hint_cpumask = Cpumask::from_str(&affinity_hint)?;
98                irq_hints.insert(irq, hint_cpumask);
99            }
100        }
101        netdevs.insert(
102            iface.clone(),
103            NetDev {
104                iface,
105                node,
106                irqs,
107                irq_hints,
108            },
109        );
110    }
111    Ok(netdevs)
112}