1use 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#[::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#[::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#[::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#[::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}