rd-agent-intf 2.0.0-alpha

Management agent for resctl-demo (interface library)
// 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())
    }
}