1use serde::{Deserialize, Serialize};
3use std::collections::BTreeMap;
4
5use rd_util::*;
6
7lazy_static::lazy_static! {
8 static ref CMD_DOC: String = {
9 format!("\
10//
11// rd-agent command file
12//
13// This file controls workloads and benchmarks. hashd benchmark should be run at
14// least once before other workloads can be started. Setting a bench sequence
15// higher than the current value in the bench.json file initiates the benchmark.
16// Setting it to a number equal to or lower than cancels if currently running.
17// While a benchmark is running, all other workloads are stopped.
18//
19// One or two rd-hashd instances are used as the latency sensitive primary
20// workloads. When both instances are active, between the two, resources are
21// distributed according to their relative weights.
22//
23// Any number of sysloads and sideloads can be used. The only difference between
24// sysloads and sideloads is that sysloads are run under system.slice without
25// further supervision while sideloads are run under sideload.slice under the
26// control of the sideloader which, among other things, enforces CPU headroom.
27//
28// Each sys/sideload must have a unique name. The actual workload is determined
29// by DEF_ID which points to an entry in sideload-defs.json file. Creating an
30// entry starts the workload. Removing stops it.
31//
32// cmd_seq: Written to cmd-ack.json once the commands are accepted
33// bench_hashd_seq: If > bench::hashd_seq, start benchmark; otherwise, cancel
34// bench_hashd_balloon_size: Memory balloon size during hashd benchmark, default ${dfl_bench_balloon}
35// bench_hashd_args: Extra arguments hashd benchmark
36// bench_iocost_seq: If > bench::iocost_seq, start benchmark; otherwise, cancel
37// sideloader.cpu_headroom: Sideload CPU headroom ratio [0.0, 1.0]
38// hashd[].active: On/off
39// hashd[].lat_target_pct: Latency target percentile
40// hashd[].lat_target: Latency target, defaults to 0.1 meaning 100ms
41// hashd[].rps_target_ratio: RPS target as a ratio of bench::hashd.rps_max,
42// if >> 1.0, no practical rps limit, default 0.5
43// hashd[].mem_ratio: Memory footprint adj [0.0, 1.0], null to use bench result
44// hashd[].file_ratio: Pagecache portion of memory [0.0, 1.0], default ${dfl_file_ratio}
45// hashd[].file_max_ratio: Max file_ratio, requires hashd restart [0.0, 1.0], default ${dfl_file_max_ratio}
46// hashd[].file_addr_stdev: Memory access stdev in ratio of mean, null to use ${dfl_file_addr_stdev}
47// hashd[].anon_addr_stdev: Memory access stdev in ratio of mean, null to use ${dfl_anon_addr_stdev}
48// hashd[].log_bps: IO write bandwidth, default ${dfl_log_bps}Mbps
49// hashd[].weight: Relative weight between the two hashd instances
50// sysloads{{}}: \"NAME\": \"DEF_ID\" pairs for active sysloads
51// sideloads{{}}: \"NAME\": \"DEF_ID\" pairs for active sideloads
52// swappiness: /proc/sys/vm/swappiness, null to leave as-is
53// zswap_enabled: zswap enable flag, null to leave as-is
54// balloon_ratio: Memory balloon size given as a ratio to total memory, default 0.0
55//
56",
57 dfl_bench_balloon = Cmd::default().bench_hashd_balloon_size,
58 dfl_file_ratio = rd_hashd_intf::Params::default().file_frac,
59 dfl_file_max_ratio = rd_hashd_intf::Args::default().file_max_frac,
60 dfl_file_addr_stdev = rd_hashd_intf::Params::default().file_addr_stdev_ratio,
61 dfl_anon_addr_stdev = rd_hashd_intf::Params::default().anon_addr_stdev_ratio,
62 dfl_log_bps = to_mb(rd_hashd_intf::Params::default().log_bps),
63 )
64 };
65}
66
67#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
68pub struct SideloaderCmd {
69 pub cpu_headroom: f64,
70}
71
72#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
73pub struct HashdCmd {
74 pub active: bool,
75 pub lat_target_pct: f64,
76 pub lat_target: f64,
77 pub rps_target_ratio: f64,
78 pub mem_ratio: Option<f64>,
79 pub file_addr_stdev: Option<f64>,
80 pub anon_addr_stdev: Option<f64>,
81 pub file_ratio: f64,
82 pub file_max_ratio: f64,
83 pub log_bps: u64,
84 pub weight: f64,
85}
86
87impl Default for HashdCmd {
88 fn default() -> Self {
89 Self {
90 active: false,
91 lat_target_pct: rd_hashd_intf::Params::default().lat_target_pct,
92 lat_target: rd_hashd_intf::Params::default().lat_target,
93 rps_target_ratio: 0.5,
94 mem_ratio: None,
95 file_addr_stdev: None,
96 anon_addr_stdev: None,
97 file_ratio: rd_hashd_intf::Params::default().file_frac,
98 file_max_ratio: rd_hashd_intf::Args::default().file_max_frac,
99 log_bps: rd_hashd_intf::Params::default().log_bps,
100 weight: 1.0,
101 }
102 }
103}
104
105#[derive(Clone, PartialEq, Serialize, Deserialize)]
106#[serde(default)]
107pub struct Cmd {
108 pub cmd_seq: u64,
109 pub bench_hashd_seq: u64,
110 pub bench_hashd_balloon_size: usize,
111 pub bench_hashd_args: Vec<String>,
112 pub bench_iocost_seq: u64,
113 pub sideloader: SideloaderCmd,
114 pub hashd: [HashdCmd; 2],
115 pub sysloads: BTreeMap<String, String>,
116 pub sideloads: BTreeMap<String, String>,
117 pub swappiness: Option<u32>,
118 pub zswap_enabled: Option<bool>,
119 pub balloon_ratio: f64,
120}
121
122impl Cmd {
123 pub fn bench_hashd_memory_slack(mem_share: usize) -> usize {
124 (mem_share / 8).min(1 << 30)
125 }
126}
127
128impl Default for Cmd {
129 fn default() -> Self {
130 Self {
131 cmd_seq: 0,
132 bench_hashd_seq: 0,
133 bench_hashd_balloon_size: Self::bench_hashd_memory_slack(total_memory()),
134 bench_hashd_args: vec![],
135 bench_iocost_seq: 0,
136 sideloader: SideloaderCmd { cpu_headroom: 0.2 },
137 hashd: Default::default(),
138 sysloads: BTreeMap::new(),
139 sideloads: BTreeMap::new(),
140 swappiness: None,
141 zswap_enabled: None,
142 balloon_ratio: 0.0,
143 }
144 }
145}
146
147impl JsonLoad for Cmd {}
148
149impl JsonSave for Cmd {
150 fn preamble() -> Option<String> {
151 Some(CMD_DOC.clone())
152 }
153}