scx_rusty/
domain.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.
5use std::collections::BTreeMap;
6
7use crate::bpf_skel::*;
8use anyhow::Result;
9use scx_utils::Cpumask;
10use scx_utils::Topology;
11use std::sync::Arc;
12use std::sync::Mutex;
13
14#[derive(Clone, Debug)]
15pub struct Domain {
16    id: usize,
17    mask: Cpumask,
18    pub ctx: Arc<Mutex<Option<*mut types::dom_ctx>>>,
19}
20
21impl Domain {
22    /// Get the Domain's ID.
23    pub fn id(&self) -> usize {
24        self.id
25    }
26
27    /// Get a copy of the domain's cpumask.
28    pub fn mask(&self) -> Cpumask {
29        self.mask.clone()
30    }
31
32    /// Get a raw slice of the domain's cpumask as a set of one or more u64
33    /// variables whose bits represent CPUs in the mask.
34    pub fn mask_slice(&self) -> &[u64] {
35        self.mask.as_raw_slice()
36    }
37
38    /// The number of CPUs in the domain.
39    pub fn weight(&self) -> usize {
40        self.mask.weight()
41    }
42
43    pub fn ctx(&self) -> Option<&mut types::dom_ctx> {
44        let domc = self.ctx.lock().unwrap();
45
46        // Ideally we would be storing the dom_ctx as a reference in struct Domain,
47        // in the first place. Rust makes embedding references to structs into other
48        // structs very difficult, so this is more pragmatic.
49        match *domc {
50            Some(ptr) => Some(unsafe { &mut *(ptr) }),
51            None => None,
52        }
53    }
54}
55
56#[derive(Debug)]
57pub struct DomainGroup {
58    doms: BTreeMap<usize, Domain>,
59    dom_numa_map: BTreeMap<usize, usize>,
60    num_numa_nodes: usize,
61    span: Cpumask,
62}
63
64impl DomainGroup {
65    pub fn new(top: &Topology, cpumasks: &[String]) -> Result<Self> {
66        let mut span = Cpumask::new();
67        let mut dom_numa_map = BTreeMap::new();
68        // Track the domain ID separate from the LLC ID, because LLC IDs can
69        // have gaps if there are offlined CPUs, and domain IDs need to be
70        // contiguous (at least for now, until we can update libraries to not
71        // return vectors of domain values).
72        let mut dom_id = 0;
73        let (doms, num_numa_nodes) = if !cpumasks.is_empty() {
74            let mut doms: BTreeMap<usize, Domain> = BTreeMap::new();
75            for mask_str in cpumasks.iter() {
76                let mask = Cpumask::from_str(mask_str)?;
77                span |= &mask;
78                doms.insert(
79                    dom_id,
80                    Domain {
81                        id: dom_id,
82                        mask,
83                        ctx: Arc::new(Mutex::new(None)),
84                    },
85                );
86                dom_numa_map.insert(dom_id, 0);
87                dom_id += 1;
88            }
89            (doms, 1)
90        } else {
91            let mut doms: BTreeMap<usize, Domain> = BTreeMap::new();
92            for (node_id, node) in &top.nodes {
93                for (_, llc) in node.llcs.iter() {
94                    let mask = llc.span.clone();
95                    span |= &mask;
96                    doms.insert(
97                        dom_id,
98                        Domain {
99                            id: dom_id,
100                            mask,
101                            ctx: Arc::new(Mutex::new(None)),
102                        },
103                    );
104                    dom_numa_map.insert(dom_id, *node_id);
105                    dom_id += 1;
106                }
107            }
108            (doms, top.nodes.len())
109        };
110
111        Ok(Self {
112            doms,
113            dom_numa_map,
114            num_numa_nodes,
115            span,
116        })
117    }
118
119    pub fn numa_doms(&self, numa_id: &usize) -> Vec<Domain> {
120        let mut numa_doms = Vec::new();
121        // XXX dom_numa_map never gets updated even if we cross NUMA nodes
122        for (d_id, n_id) in self.dom_numa_map.iter() {
123            if n_id == numa_id {
124                let dom = self.doms.get(d_id).unwrap();
125                numa_doms.push(dom.clone());
126            }
127        }
128
129        numa_doms
130    }
131
132    pub fn doms(&self) -> &BTreeMap<usize, Domain> {
133        &self.doms
134    }
135
136    pub fn nr_doms(&self) -> usize {
137        self.doms.len()
138    }
139
140    pub fn nr_nodes(&self) -> usize {
141        self.num_numa_nodes
142    }
143
144    pub fn dom_numa_id(&self, dom_id: &usize) -> Option<usize> {
145        self.dom_numa_map.get(dom_id).copied()
146    }
147
148    pub fn weight(&self) -> usize {
149        self.span.weight()
150    }
151}