rd_agent_intf/
slices.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2use anyhow::Result;
3use enum_iterator::IntoEnumIterator;
4use serde::{Deserialize, Serialize};
5use std::collections::BTreeMap;
6use std::ops::{Index, IndexMut};
7
8use rd_util::*;
9
10pub const ROOT_SLICE: &'static str = "-.slice";
11
12const SLICE_DOC: &str = "\
13//
14// rd-agent top-level systemd slice resource configurations
15//
16// Memory configuration can be either None or Bytes.
17//
18//  disable_seqs.cpu: Disable CPU control if >= report::seq
19//  disable_seqs.mem: Disable memory control if >= report::seq
20//  disable_seqs.io: Disable IO control if >= report::seq
21//  slices.SLICE_ID.cpu_weight: CPU weight [1..10000]
22//  slices.SLICE_ID.io_weight: IO weight [1..10000]
23//  slices.SLICE_ID.mem_min: memory.min
24//  slices.SLICE_ID.mem_low: memory.low
25//  slices.SLICE_ID.mem_high: memory.high
26//
27";
28
29#[derive(Debug, Copy, Clone, IntoEnumIterator, PartialEq, Eq)]
30pub enum Slice {
31    Init = 0,
32    Host = 1,
33    User = 2,
34    Sys = 3,
35    Work = 4,
36    Side = 5,
37}
38
39impl Slice {
40    pub fn name(&self) -> &'static str {
41        match self {
42            Slice::Init => "init.scope",
43            Slice::Host => "hostcritical.slice",
44            Slice::User => "user.slice",
45            Slice::Sys => "system.slice",
46            Slice::Work => "workload.slice",
47            Slice::Side => "sideload.slice",
48        }
49    }
50
51    pub fn cgrp(&self) -> &'static str {
52        match self {
53            Slice::Init => "/sys/fs/cgroup/init.scope",
54            Slice::Host => "/sys/fs/cgroup/hostcritical.slice",
55            Slice::User => "/sys/fs/cgroup/user.slice",
56            Slice::Sys => "/sys/fs/cgroup/system.slice",
57            Slice::Work => "/sys/fs/cgroup/workload.slice",
58            Slice::Side => "/sys/fs/cgroup/sideload.slice",
59        }
60    }
61}
62
63#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
64pub enum MemoryKnob {
65    None,
66    Bytes(u64),
67}
68
69impl Default for MemoryKnob {
70    fn default() -> Self {
71        Self::None
72    }
73}
74
75impl MemoryKnob {
76    pub fn nr_bytes(&self, is_limit: bool) -> u64 {
77        let nocfg = match is_limit {
78            true => std::u64::MAX,
79            false => 0,
80        };
81        match self {
82            Self::None => nocfg,
83            Self::Bytes(s) => *s,
84        }
85    }
86}
87
88#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
89pub struct SliceConfig {
90    pub cpu_weight: u32,
91    pub io_weight: u32,
92    pub mem_min: MemoryKnob,
93    pub mem_low: MemoryKnob,
94    pub mem_high: MemoryKnob,
95}
96
97impl Default for SliceConfig {
98    fn default() -> Self {
99        Self {
100            cpu_weight: 100,
101            io_weight: 100,
102            mem_min: Default::default(),
103            mem_low: Default::default(),
104            mem_high: Default::default(),
105        }
106    }
107}
108
109impl SliceConfig {
110    pub const DFL_SYS_CPU_RATIO: f64 = 0.1;
111    pub const DFL_SYS_IO_RATIO: f64 = 0.1;
112    pub const DFL_MEM_MARGIN: f64 = 0.25;
113
114    pub fn dfl_mem_margin(total: usize, fb_prod: bool) -> u64 {
115        let margin = (total as f64 * Self::DFL_MEM_MARGIN) as u64;
116        if fb_prod {
117            (margin + 2 << 30).min(total as u64 / 2)
118        } else {
119            margin
120        }
121    }
122
123    fn default(slice: Slice) -> Self {
124        let mut hostcrit_min = 768 << 20;
125        if *IS_FB_PROD {
126            hostcrit_min += 512 << 20;
127        }
128
129        match slice {
130            Slice::Init => Self {
131                cpu_weight: 10,
132                mem_min: MemoryKnob::Bytes(16 << 20),
133                ..Default::default()
134            },
135            Slice::Host => Self {
136                cpu_weight: 10,
137                mem_min: MemoryKnob::Bytes(hostcrit_min),
138                ..Default::default()
139            },
140            Slice::User => Self {
141                mem_low: MemoryKnob::Bytes(512 << 20),
142                ..Default::default()
143            },
144            Slice::Sys => Self {
145                cpu_weight: 10,
146                io_weight: 50,
147                ..Default::default()
148            },
149            Slice::Work => Self {
150                io_weight: 500,
151                mem_low: MemoryKnob::Bytes(
152                    total_memory() as u64 - Self::dfl_mem_margin(total_memory(), *IS_FB_PROD),
153                ),
154                ..Default::default()
155            },
156            Slice::Side => Self {
157                cpu_weight: 1,
158                io_weight: 1,
159                ..Default::default()
160            },
161        }
162    }
163}
164
165#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
166pub struct DisableSeqKnobs {
167    pub cpu: u64,
168    pub mem: u64,
169    pub io: u64,
170}
171
172#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
173#[serde(default)]
174pub struct SliceKnobs {
175    pub disable_seqs: DisableSeqKnobs,
176    pub slices: BTreeMap<String, SliceConfig>,
177    #[serde(skip)]
178    pub work_mem_low_none: bool,
179}
180
181impl Default for SliceKnobs {
182    fn default() -> Self {
183        let mut slices = BTreeMap::new();
184        for slc in Slice::into_enum_iter() {
185            slices.insert(slc.name().into(), SliceConfig::default(slc));
186        }
187        Self {
188            disable_seqs: Default::default(),
189            slices,
190            work_mem_low_none: false,
191        }
192    }
193}
194
195impl JsonLoad for SliceKnobs {
196    fn loaded(&mut self, _prev: Option<&mut Self>) -> Result<()> {
197        let sk = self.slices.get(Slice::Work.name()).unwrap();
198        self.work_mem_low_none = if let MemoryKnob::None = sk.mem_low {
199            true
200        } else {
201            false
202        };
203        Ok(())
204    }
205}
206
207impl JsonSave for SliceKnobs {
208    fn preamble() -> Option<String> {
209        Some(SLICE_DOC.to_string())
210    }
211}
212
213impl SliceKnobs {
214    pub fn controlls_disabled(&self, seq: u64) -> bool {
215        let dseqs = &self.disable_seqs;
216        dseqs.cpu >= seq || dseqs.mem >= seq || dseqs.io >= seq
217    }
218}
219
220impl Index<Slice> for SliceKnobs {
221    type Output = SliceConfig;
222
223    fn index(&self, slc: Slice) -> &Self::Output {
224        self.slices.get(slc.name()).unwrap()
225    }
226}
227
228impl IndexMut<Slice> for SliceKnobs {
229    fn index_mut(&mut self, slc: Slice) -> &mut Self::Output {
230        self.slices.get_mut(slc.name()).unwrap()
231    }
232}