Skip to main content

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