diff --git a/Cargo.lock b/Cargo.lock index 31b8a22..c1505f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -884,6 +884,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ "windows-sys", + "windows-sys 0.59.0", ] [[package]] diff --git a/src/controller.rs b/src/controller.rs index e8271af..4a31cc7 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -155,6 +155,27 @@ pub enum ControllerError { VmError(String), } +/// Return queue count, total, fixed-point average, and median depth. +fn summarize_vq_depths( + depths: Option<&[u64]>, +) -> (Option, Option, Option, Option) { + let Some(depths) = depths.filter(|depths| !depths.is_empty()) else { + return (None, None, None, None); + }; + let count = depths.len() as u32; + let total = depths.iter().copied().fold(0u64, u64::saturating_add); + let average_x100 = ((u128::from(total) * 100) / depths.len() as u128) as u64; + let median = median( + &depths + .iter() + .copied() + .map(|depth| depth as f64) + .collect::>(), + ) + .floor() as u64; + (Some(count), Some(total), Some(average_x100), Some(median)) +} + /// Construct a [`Duration`] from whole minutes. const fn from_mins(minutes: u64) -> Duration { Duration::from_secs(minutes * 60) @@ -467,6 +488,8 @@ impl Controller { let instance = &self.instances[id]; let s = instance.status.read().await; let cpu_stats = compute_cpu_stats(&s); + let (num_queues, derived_qd_total, qd_avg_x100, qd_median) = + summarize_vq_depths(s.per_vq_depth.as_deref()); payload.vms.push(SnapshotVm { id: id.clone(), vcpu_count: s.vcpu_count, @@ -498,11 +521,11 @@ impl Controller { p99: latency.p99, avg: latency.avg, }), - num_queues: None, - per_vq_depth: None, - qd_total: None, - qd_avg_x100: None, - qd_median: None, + num_queues, + per_vq_depth: s.per_vq_depth.clone(), + qd_total: s.vq_depth_total.or(derived_qd_total), + qd_avg_x100, + qd_median, cpu_pct_avg: Some(cpu_stats.avg_pct), cpu_pct_median: Some(cpu_stats.median_pct), cpu_pct_total: Some(cpu_stats.total_pct), diff --git a/src/instance.rs b/src/instance.rs index 4d6d73a..a189525 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -267,6 +267,11 @@ impl Instance { .perf .as_ref() .and_then(|perf| perf.write_latency_us); + status.per_vq_depth = snapshot + .perf + .as_ref() + .and_then(|perf| perf.per_vq_depth.clone()); + status.vq_depth_total = snapshot.perf.as_ref().and_then(|perf| perf.vq_depth_total); status.alive = true; let backend_util = snapshot.per_thread_util.map(|util| util.clamp(0.0, 1.0)); if let Some(backend_util) = backend_util { @@ -476,6 +481,10 @@ pub struct InstanceStatus { pub read_latency_us: Option, /// Latest write-latency histogram digest. pub write_latency_us: Option, + /// Latest in-flight depth for each backend virtqueue. + pub per_vq_depth: Option>, + /// Latest sum of all virtqueue depths. + pub vq_depth_total: Option, /// Latest read bandwidth in bytes per second. pub read_bytes_per_second: u64, /// Latest write bandwidth in bytes per second. @@ -555,6 +564,10 @@ pub struct InstancePerfSample { pub read_latency_us: Option, /// Write-latency digest, when the backend has write samples. pub write_latency_us: Option, + /// In-flight depth for each backend virtqueue. + pub per_vq_depth: Option>, + /// Sum of all virtqueue depths. + pub vq_depth_total: Option, } impl InstancePerfSample {