1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright (c) Facebook, Inc. and its affiliates.
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

use rd_util::*;

lazy_static::lazy_static! {
    static ref CMD_DOC: String = {
        format!("\
//
// rd-agent command file
//
// This file controls workloads and benchmarks. hashd benchmark should be run at
// least once before other workloads can be started. Setting a bench sequence
// higher than the current value in the bench.json file initiates the benchmark.
// Setting it to a number equal to or lower than cancels if currently running.
// While a benchmark is running, all other workloads are stopped.
//
// One or two rd-hashd instances are used as the latency sensitive primary
// workloads. When both instances are active, between the two, resources are
// distributed according to their relative weights.
//
// Any number of sysloads and sideloads can be used. The only difference between
// sysloads and sideloads is that sysloads are run under system.slice without
// further supervision while sideloads are run under sideload.slice under the
// control of the sideloader which, among other things, enforces CPU headroom.
//
// Each sys/sideload must have a unique name. The actual workload is determined
// by DEF_ID which points to an entry in sideload-defs.json file. Creating an
// entry starts the workload. Removing stops it.
//
//  cmd_seq: Written to cmd-ack.json once the commands are accepted
//  bench_hashd_seq: If > bench::hashd_seq, start benchmark; otherwise, cancel
//  bench_hashd_balloon_size: Memory balloon size during hashd benchmark, default ${dfl_bench_balloon}
//  bench_hashd_args: Extra arguments hashd benchmark
//  bench_iocost_seq: If > bench::iocost_seq, start benchmark; otherwise, cancel
//  sideloader.cpu_headroom: Sideload CPU headroom ratio [0.0, 1.0]
//  hashd[].active: On/off
//  hashd[].lat_target_pct: Latency target percentile
//  hashd[].lat_target: Latency target, defaults to 0.1 meaning 100ms
//  hashd[].rps_target_ratio: RPS target as a ratio of bench::hashd.rps_max,
//                            if >> 1.0, no practical rps limit, default 0.5
//  hashd[].mem_ratio: Memory footprint adj [0.0, 1.0], null to use bench result
//  hashd[].file_ratio: Pagecache portion of memory [0.0, 1.0], default ${dfl_file_ratio}
//  hashd[].file_max_ratio: Max file_ratio, requires hashd restart [0.0, 1.0], default ${dfl_file_max_ratio}
//  hashd[].file_addr_stdev: Memory access stdev in ratio of mean, null to use ${dfl_file_addr_stdev}
//  hashd[].anon_addr_stdev: Memory access stdev in ratio of mean, null to use ${dfl_anon_addr_stdev}
//  hashd[].log_bps: IO write bandwidth, default ${dfl_log_bps}Mbps
//  hashd[].weight: Relative weight between the two hashd instances
//  sysloads{{}}: \"NAME\": \"DEF_ID\" pairs for active sysloads
//  sideloads{{}}: \"NAME\": \"DEF_ID\" pairs for active sideloads
//  swappiness: /proc/sys/vm/swappiness, null to leave as-is
//  zswap_enabled: zswap enable flag, null to leave as-is
//  balloon_ratio: Memory balloon size given as a ratio to total memory, default 0.0
//
",
                dfl_bench_balloon = Cmd::default().bench_hashd_balloon_size,
                dfl_file_ratio = rd_hashd_intf::Params::default().file_frac,
                dfl_file_max_ratio = rd_hashd_intf::Args::default().file_max_frac,
                dfl_file_addr_stdev = rd_hashd_intf::Params::default().file_addr_stdev_ratio,
                dfl_anon_addr_stdev = rd_hashd_intf::Params::default().anon_addr_stdev_ratio,
                dfl_log_bps = to_mb(rd_hashd_intf::Params::default().log_bps),
        )
    };
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SideloaderCmd {
    pub cpu_headroom: f64,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct HashdCmd {
    pub active: bool,
    pub lat_target_pct: f64,
    pub lat_target: f64,
    pub rps_target_ratio: f64,
    pub mem_ratio: Option<f64>,
    pub file_addr_stdev: Option<f64>,
    pub anon_addr_stdev: Option<f64>,
    pub file_ratio: f64,
    pub file_max_ratio: f64,
    pub log_bps: u64,
    pub weight: f64,
}

impl Default for HashdCmd {
    fn default() -> Self {
        Self {
            active: false,
            lat_target_pct: rd_hashd_intf::Params::default().lat_target_pct,
            lat_target: rd_hashd_intf::Params::default().lat_target,
            rps_target_ratio: 0.5,
            mem_ratio: None,
            file_addr_stdev: None,
            anon_addr_stdev: None,
            file_ratio: rd_hashd_intf::Params::default().file_frac,
            file_max_ratio: rd_hashd_intf::Args::default().file_max_frac,
            log_bps: rd_hashd_intf::Params::default().log_bps,
            weight: 1.0,
        }
    }
}

#[derive(Clone, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct Cmd {
    pub cmd_seq: u64,
    pub bench_hashd_seq: u64,
    pub bench_hashd_balloon_size: usize,
    pub bench_hashd_args: Vec<String>,
    pub bench_iocost_seq: u64,
    pub sideloader: SideloaderCmd,
    pub hashd: [HashdCmd; 2],
    pub sysloads: BTreeMap<String, String>,
    pub sideloads: BTreeMap<String, String>,
    pub swappiness: Option<u32>,
    pub zswap_enabled: Option<bool>,
    pub balloon_ratio: f64,
}

impl Cmd {
    pub fn bench_hashd_memory_slack(mem_share: usize) -> usize {
        (mem_share / 8).min(1 << 30)
    }
}

impl Default for Cmd {
    fn default() -> Self {
        Self {
            cmd_seq: 0,
            bench_hashd_seq: 0,
            bench_hashd_balloon_size: Self::bench_hashd_memory_slack(total_memory()),
            bench_hashd_args: vec![],
            bench_iocost_seq: 0,
            sideloader: SideloaderCmd { cpu_headroom: 0.2 },
            hashd: Default::default(),
            sysloads: BTreeMap::new(),
            sideloads: BTreeMap::new(),
            swappiness: None,
            zswap_enabled: None,
            balloon_ratio: 0.0,
        }
    }
}

impl JsonLoad for Cmd {}

impl JsonSave for Cmd {
    fn preamble() -> Option<String> {
        Some(CMD_DOC.clone())
    }
}