in resctl-bench/src/bench/iocost_qos.rs [103:254]
fn parse(spec: &JobSpec, prev_data: Option<&JobData>) -> Result<Self> {
let mut stor_spec = JobSpec::new(
"storage",
None,
Some(format!("none,{}", spec.passive.as_deref().unwrap_or("")).trim_end_matches(',')),
JobSpec::props(&[&[("apply", "")]]),
);
let mut prot_spec = JobSpec::new(
"protection",
None,
spec.passive.as_deref(),
JobSpec::props(&[
&[],
&[
("scenario", "mem-hog-tune"),
("load", "1.0"),
("size-min", "1"),
("size-max", "1"),
],
]),
);
let mut vrate_min = 0.0;
let mut vrate_max = DFL_VRATE_MAX;
let mut vrate_intvs = 0;
let mut stor_base_loops = DFL_STOR_BASE_LOOPS;
let mut stor_loops = DFL_STOR_LOOPS;
let mut isol_pct = DFL_ISOL_PCT.to_owned();
let mut isol_thr = DFL_ISOL_THR;
let mut retries = DFL_RETRIES;
let mut allow_fail = false;
let mut runs = vec![IoCostQoSOvr {
off: true,
..Default::default()
}];
let mut dither = false;
let mut dither_dist = None;
let mut ign_min_perf = false;
for (k, v) in spec.props[0].iter() {
match k.as_str() {
"vrate-min" => vrate_min = v.parse::<f64>()?,
"vrate-max" => vrate_max = v.parse::<f64>()?,
"vrate-intvs" => vrate_intvs = v.parse::<u32>()?,
"dither" => {
dither = true;
if v.len() > 0 {
dither_dist = Some(v.parse::<f64>()?);
}
}
"storage-base-loops" => stor_base_loops = v.parse::<u32>()?,
"storage-loops" => stor_loops = v.parse::<u32>()?,
"isol-pct" => isol_pct = v.to_owned(),
"isol-thr" => isol_thr = parse_frac(v)?,
"retries" => retries = v.parse::<u32>()?,
"allow-fail" => allow_fail = v.parse::<bool>()?,
"ignore-min-perf" => ign_min_perf = v.len() == 0 || v.parse::<bool>()?,
k if k.starts_with("storage-") => {
stor_spec.props[0].insert(k[8..].into(), v.into());
}
k => bail!("unknown property key {:?}", k),
}
}
if vrate_min < 0.0 || vrate_max < 0.0 || vrate_min >= vrate_max {
bail!("invalid vrate range [{}, {}]", vrate_min, vrate_max);
}
for props in spec.props[1..].iter() {
let mut ovr = IoCostQoSOvr::default();
for (k, v) in props.iter() {
if !ovr.parse(k, v)? {
bail!("unknown property key {:?}", k);
}
}
runs.push(ovr);
}
prot_spec.props[1].insert("isol-pct".to_owned(), isol_pct.clone());
prot_spec.props[1].insert("isol-thr".to_owned(), format!("{}", isol_thr));
let stor_job = StorageJob::parse(&stor_spec)?;
let prot_job = ProtectionJob::parse(&prot_spec)?;
if runs.len() == 1 && vrate_intvs == 0 {
vrate_intvs = DFL_VRATE_INTVS;
}
if vrate_intvs > 0 {
// min of 0 is special case and means that we start at one
// click, so if min is 0, max is 10 and intvs is 5, the sequence
// is (10, 7.5, 5, 2.5). If min > 0, the range is inclusive -
// min 5, max 10, intvs 5 => (10, 9, 8, 7, 6, 5).
let click;
let mut dither_shift = 0.0;
if vrate_min == 0.0 {
click = vrate_max / vrate_intvs as f64;
vrate_min = click;
dither_shift = -click / 2.0;
} else {
click = (vrate_max - vrate_min) / (vrate_intvs - 1) as f64;
};
if dither {
if dither_dist.is_none() {
if let Some(pd) = prev_data.as_ref() {
// If prev has dither_dist set, use the prev dither_dist
// so that we can use results from it.
let prec: IoCostQoSRecord = pd.parse_record()?;
if let Some(pdd) = prec.dither_dist.as_ref() {
dither_dist = Some(*pdd);
}
}
}
if dither_dist.is_none() {
dither_dist = Some(
rand::thread_rng().gen_range(-click / 2.0..click / 2.0) + dither_shift,
);
}
vrate_min += dither_dist.as_ref().unwrap();
vrate_max += dither_dist.as_ref().unwrap();
}
vrate_min = vrate_min.max(VRATE_INTVS_MIN);
let mut vrate = vrate_max;
while vrate > vrate_min - 0.001 {
let mut ovr = IoCostQoSOvr {
min: Some(vrate),
max: Some(vrate),
..Default::default()
};
ovr.sanitize();
runs.push(ovr);
vrate -= click;
}
}
Ok(IoCostQoSJob {
stor_base_loops,
stor_loops,
isol_pct,
isol_thr,
dither_dist,
ign_min_perf,
retries,
allow_fail,
stor_job,
prot_job,
runs,
})
}