below_model/
resctrl.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::*;
16
17#[::below_derive::queriable_derives]
18pub struct ResctrlL3MonModel {
19    pub llc_occupancy_bytes: Option<u64>,
20    pub mbm_total_bytes_per_sec: Option<u64>,
21    pub mbm_local_bytes_per_sec: Option<u64>,
22}
23
24/// Model for mon data
25#[::below_derive::queriable_derives]
26pub struct ResctrlMonModel {
27    #[queriable(subquery)]
28    pub total: ResctrlL3MonModel,
29    #[queriable(subquery)]
30    pub per_l3: BTreeMap<u64, ResctrlL3MonModel>,
31}
32
33/// Collection of all data about a single MON group
34#[::below_derive::queriable_derives]
35pub struct ResctrlMonGroupModel {
36    pub name: String,
37    pub full_path: String,
38    #[queriable(subquery)]
39    pub mon: ResctrlMonModel,
40}
41
42/// Collection of all data about a single CTRL_MON group and descendents
43#[::below_derive::queriable_derives]
44pub struct ResctrlCtrlMonGroupModel {
45    pub name: String,
46    pub full_path: String,
47    pub cpuset: Option<resctrlfs::Cpuset>,
48    pub mode: Option<resctrlfs::GroupMode>,
49    #[queriable(subquery)]
50    pub mon: ResctrlMonModel,
51    #[queriable(subquery)]
52    pub mon_groups: BTreeMap<String, ResctrlMonGroupModel>,
53}
54
55/// All data about the entire resctrl filesystem
56#[::below_derive::queriable_derives]
57pub struct ResctrlModel {
58    pub cpuset: Option<resctrlfs::Cpuset>,
59    pub mode: Option<resctrlfs::GroupMode>,
60    #[queriable(subquery)]
61    pub mon: Option<ResctrlMonModel>,
62    #[queriable(subquery)]
63    pub mon_groups: BTreeMap<String, ResctrlMonGroupModel>,
64    #[queriable(subquery)]
65    pub ctrl_mon_groups: BTreeMap<String, ResctrlCtrlMonGroupModel>,
66}
67
68fn rmid_bytes_to_opt(rmid_bytes: &Option<resctrlfs::RmidBytes>) -> Option<u64> {
69    match rmid_bytes {
70        Some(resctrlfs::RmidBytes::Bytes(b)) => Some(*b),
71        Some(resctrlfs::RmidBytes::Unavailable) => None,
72        None => None,
73    }
74}
75
76impl std::ops::Add<&ResctrlL3MonModel> for ResctrlL3MonModel {
77    type Output = Self;
78
79    fn add(self, other: &Self) -> Self {
80        Self {
81            llc_occupancy_bytes: opt_add(self.llc_occupancy_bytes, other.llc_occupancy_bytes),
82            mbm_total_bytes_per_sec: opt_add(
83                self.mbm_total_bytes_per_sec,
84                other.mbm_total_bytes_per_sec,
85            ),
86            mbm_local_bytes_per_sec: opt_add(
87                self.mbm_local_bytes_per_sec,
88                other.mbm_local_bytes_per_sec,
89            ),
90        }
91    }
92}
93
94impl ResctrlModel {
95    pub fn new(
96        sample: &resctrlfs::ResctrlSample,
97        last: Option<(&resctrlfs::ResctrlSample, Duration)>,
98    ) -> ResctrlModel {
99        ResctrlModel {
100            cpuset: sample.cpuset.clone(),
101            mode: sample.mode.clone(),
102            mon: sample.mon_stat.as_ref().map(|mon_stat| {
103                ResctrlMonModel::new(
104                    mon_stat,
105                    last.and_then(|(s, d)| s.mon_stat.as_ref().map(|v| (v, d))),
106                )
107            }),
108            mon_groups: sample
109                .mon_groups
110                .as_ref()
111                .unwrap_or(&Default::default())
112                .iter()
113                .map(|(name, stat)| {
114                    (
115                        name.clone(),
116                        ResctrlMonGroupModel::new(
117                            name.clone(),
118                            name.to_string(),
119                            stat,
120                            last.and_then(|(s, d)| {
121                                s.mon_groups
122                                    .as_ref()
123                                    .and_then(|v| v.get(name))
124                                    .map(|v| (v, d))
125                            }),
126                        ),
127                    )
128                })
129                .collect(),
130            ctrl_mon_groups: sample
131                .ctrl_mon_groups
132                .as_ref()
133                .unwrap_or(&Default::default())
134                .iter()
135                .map(|(name, stat)| {
136                    (
137                        name.clone(),
138                        ResctrlCtrlMonGroupModel::new(
139                            name.clone(),
140                            name.to_string(),
141                            stat,
142                            last.and_then(|(s, d)| {
143                                s.ctrl_mon_groups
144                                    .as_ref()
145                                    .and_then(|v| v.get(name))
146                                    .map(|v| (v, d))
147                            }),
148                        ),
149                    )
150                })
151                .collect(),
152        }
153    }
154}
155
156impl ResctrlCtrlMonGroupModel {
157    pub fn new(
158        name: String,
159        full_path: String,
160        sample: &resctrlfs::CtrlMonGroupStat,
161        last: Option<(&resctrlfs::CtrlMonGroupStat, Duration)>,
162    ) -> ResctrlCtrlMonGroupModel {
163        let last_if_inode_matches =
164            last.and_then(|(s, d)| match (s.inode_number, sample.inode_number) {
165                (Some(prev_inode), Some(current_inode)) if prev_inode == current_inode => {
166                    Some((s, d))
167                }
168                (None, None) => Some((s, d)),
169                _ => None,
170            });
171        ResctrlCtrlMonGroupModel {
172            name,
173            full_path: full_path.clone(),
174            cpuset: sample.cpuset.clone(),
175            mode: sample.mode.clone(),
176            mon: sample
177                .mon_stat
178                .as_ref()
179                .map(|mon_stat| {
180                    if let Some((last, delta)) = last_if_inode_matches {
181                        if let Some(last_mon_stat) = last.mon_stat.as_ref() {
182                            ResctrlMonModel::new(mon_stat, Some((last_mon_stat, delta)))
183                        } else {
184                            ResctrlMonModel::new(mon_stat, None)
185                        }
186                    } else {
187                        ResctrlMonModel::new(mon_stat, None)
188                    }
189                })
190                .unwrap_or_default(),
191            mon_groups: sample
192                .mon_groups
193                .as_ref()
194                .unwrap_or(&Default::default())
195                .iter()
196                .map(|(name, stat)| {
197                    (
198                        name.clone(),
199                        ResctrlMonGroupModel::new(
200                            name.clone(),
201                            format!("{}/{}", full_path, name),
202                            stat,
203                            last_if_inode_matches.and_then(|(s, d)| {
204                                s.mon_groups
205                                    .as_ref()
206                                    .and_then(|v| v.get(name))
207                                    .map(|v| (v, d))
208                            }),
209                        ),
210                    )
211                })
212                .collect(),
213        }
214    }
215}
216
217impl ResctrlMonGroupModel {
218    pub fn new(
219        name: String,
220        full_path: String,
221        sample: &resctrlfs::MonGroupStat,
222        last: Option<(&resctrlfs::MonGroupStat, Duration)>,
223    ) -> ResctrlMonGroupModel {
224        let last_if_inode_matches =
225            last.and_then(|(s, d)| match (s.inode_number, sample.inode_number) {
226                (Some(prev_inode), Some(current_inode)) if prev_inode == current_inode => {
227                    Some((s, d))
228                }
229                (None, None) => Some((s, d)),
230                _ => None,
231            });
232        ResctrlMonGroupModel {
233            name,
234            full_path,
235            mon: sample
236                .mon_stat
237                .as_ref()
238                .map(|mon_stat| {
239                    if let Some((last, delta)) = last_if_inode_matches {
240                        if let Some(last_mon_stat) = last.mon_stat.as_ref() {
241                            ResctrlMonModel::new(mon_stat, Some((last_mon_stat, delta)))
242                        } else {
243                            ResctrlMonModel::new(mon_stat, None)
244                        }
245                    } else {
246                        ResctrlMonModel::new(mon_stat, None)
247                    }
248                })
249                .unwrap_or_default(),
250        }
251    }
252}
253
254impl ResctrlMonModel {
255    pub fn new(
256        sample: &resctrlfs::MonStat,
257        last: Option<(&resctrlfs::MonStat, Duration)>,
258    ) -> ResctrlMonModel {
259        let mut model = ResctrlMonModel::default();
260        for (l3, end_l3_sample) in sample
261            .l3_mon_stat
262            .as_ref()
263            .unwrap_or(&Default::default())
264            .iter()
265        {
266            let last_l3 = if let Some((last_l3_sample, delta)) = &last {
267                last_l3_sample
268                    .l3_mon_stat
269                    .as_ref()
270                    .and_then(|v| v.get(l3))
271                    .map(|v| (v, *delta))
272            } else {
273                None
274            };
275            model
276                .per_l3
277                .insert(*l3, ResctrlL3MonModel::new(end_l3_sample, last_l3));
278        }
279        model.total = model
280            .per_l3
281            .values()
282            .fold(ResctrlL3MonModel::default(), |acc, model| acc + model);
283        model
284    }
285}
286
287impl ResctrlL3MonModel {
288    pub fn new(
289        sample: &resctrlfs::L3MonStat,
290        last: Option<(&resctrlfs::L3MonStat, Duration)>,
291    ) -> ResctrlL3MonModel {
292        if let Some((begin, delta)) = last {
293            ResctrlL3MonModel {
294                llc_occupancy_bytes: rmid_bytes_to_opt(&sample.llc_occupancy_bytes),
295                mbm_total_bytes_per_sec: count_per_sec!(
296                    rmid_bytes_to_opt(&begin.mbm_total_bytes),
297                    rmid_bytes_to_opt(&sample.mbm_total_bytes),
298                    delta,
299                    u64
300                ),
301                mbm_local_bytes_per_sec: count_per_sec!(
302                    rmid_bytes_to_opt(&begin.mbm_local_bytes),
303                    rmid_bytes_to_opt(&sample.mbm_local_bytes),
304                    delta,
305                    u64
306                ),
307            }
308        } else {
309            ResctrlL3MonModel {
310                llc_occupancy_bytes: rmid_bytes_to_opt(&sample.llc_occupancy_bytes),
311                mbm_total_bytes_per_sec: None,
312                mbm_local_bytes_per_sec: None,
313            }
314        }
315    }
316}