Skip to content

Commit 397f1d2

Browse files
committed
feat(virtio-mem): add metrics for virtio-mem device
Wire support for virtio-mem metrics, adding a few basic metrics: queue events, queue event fails, activation fails. Signed-off-by: Riccardo Mancini <mancio@amazon.com>
1 parent 0fcb5ec commit 397f1d2

File tree

5 files changed

+96
-2
lines changed

5 files changed

+96
-2
lines changed

src/vmm/src/devices/virtio/mem/device.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::devices::virtio::generated::virtio_mem::{
2323
VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE, virtio_mem_config,
2424
};
2525
use crate::devices::virtio::iov_deque::IovDequeError;
26+
use crate::devices::virtio::mem::metrics::METRICS;
2627
use crate::devices::virtio::mem::{VIRTIO_MEM_DEV_ID, VIRTIO_MEM_GUEST_ADDRESS};
2728
use crate::devices::virtio::queue::{FIRECRACKER_MAX_QUEUE_SIZE, InvalidAvailIdx, Queue};
2829
use crate::devices::virtio::transport::{VirtioInterrupt, VirtioInterruptType};
@@ -170,12 +171,15 @@ impl VirtioMem {
170171
}
171172

172173
pub(crate) fn process_mem_queue_event(&mut self) {
174+
METRICS.queue_event_count.inc();
173175
if let Err(err) = self.queue_events[MEM_QUEUE].read() {
176+
METRICS.queue_event_fails.inc();
174177
error!("Failed to read mem queue event: {err}");
175178
return;
176179
}
177180

178181
if let Err(err) = self.process_mem_queue() {
182+
METRICS.queue_event_fails.inc();
179183
error!("virtio-mem: Failed to process queue: {err}");
180184
}
181185
}
@@ -263,7 +267,7 @@ impl VirtioDevice for VirtioMem {
263267
error!(
264268
"virtio-mem: VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE feature not acknowledged by guest"
265269
);
266-
// TODO(virtio-mem): activation failed metric
270+
METRICS.activate_fails.inc();
267271
return Err(ActivateError::RequiredFeatureNotAcked(
268272
"VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE",
269273
));
@@ -276,7 +280,7 @@ impl VirtioDevice for VirtioMem {
276280

277281
self.device_state = DeviceState::Activated(ActiveState { mem, interrupt });
278282
if self.activate_event.write(1).is_err() {
279-
// TODO(virtio-mem): activation failed metric
283+
METRICS.activate_fails.inc();
280284
self.device_state = DeviceState::Inactive;
281285
return Err(ActivateError::EventFd);
282286
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! Defines the metrics system for memory devices.
5+
//!
6+
//! # Metrics format
7+
//! The metrics are flushed in JSON when requested by vmm::logger::metrics::METRICS.write().
8+
//!
9+
//! ## JSON example with metrics:
10+
//! ```json
11+
//! "memory_hotplug": {
12+
//! "activate_fails": "SharedIncMetric",
13+
//! "queue_event_fails": "SharedIncMetric",
14+
//! "queue_event_count": "SharedIncMetric",
15+
//! ...
16+
//! }
17+
//! }
18+
//! ```
19+
//! Each `memory` field in the example above is a serializable `VirtioMemDeviceMetrics` structure
20+
//! collecting metrics such as `activate_fails`, `queue_event_fails` etc. for the memoty hotplug
21+
//! device.
22+
//! Since Firecrakcer only supports one virtio-mem device, there is no per device metrics and
23+
//! `memory_hotplug` represents the aggregate entropy metrics.
24+
25+
use serde::ser::SerializeMap;
26+
use serde::{Serialize, Serializer};
27+
28+
use crate::logger::{LatencyAggregateMetrics, SharedIncMetric};
29+
30+
/// Stores aggregated virtio-mem metrics
31+
pub(super) static METRICS: VirtioMemDeviceMetrics = VirtioMemDeviceMetrics::new();
32+
33+
/// Called by METRICS.flush(), this function facilitates serialization of virtio-mem device metrics.
34+
pub fn flush_metrics<S: Serializer>(serializer: S) -> Result<S::Ok, S::Error> {
35+
let mut seq = serializer.serialize_map(Some(1))?;
36+
seq.serialize_entry("memory_hotplug", &METRICS)?;
37+
seq.end()
38+
}
39+
40+
#[derive(Debug, Serialize)]
41+
pub(super) struct VirtioMemDeviceMetrics {
42+
/// Number of device activation failures
43+
pub activate_fails: SharedIncMetric,
44+
/// Number of queue event handling failures
45+
pub queue_event_fails: SharedIncMetric,
46+
/// Number of queue events handled
47+
pub queue_event_count: SharedIncMetric,
48+
}
49+
50+
impl VirtioMemDeviceMetrics {
51+
/// Const default construction.
52+
const fn new() -> Self {
53+
Self {
54+
activate_fails: SharedIncMetric::new(),
55+
queue_event_fails: SharedIncMetric::new(),
56+
queue_event_count: SharedIncMetric::new(),
57+
}
58+
}
59+
}
60+
61+
#[cfg(test)]
62+
pub mod tests {
63+
use super::*;
64+
use crate::logger::IncMetric;
65+
66+
#[test]
67+
fn test_memory_hotplug_metrics() {
68+
let mem_metrics: VirtioMemDeviceMetrics = VirtioMemDeviceMetrics::new();
69+
let mem_metrics_local: String = serde_json::to_string(&mem_metrics).unwrap();
70+
// the 1st serialize flushes the metrics and resets values to 0 so that
71+
// we can compare the values with local metrics.
72+
serde_json::to_string(&METRICS).unwrap();
73+
let mem_metrics_global: String = serde_json::to_string(&METRICS).unwrap();
74+
assert_eq!(mem_metrics_local, mem_metrics_global);
75+
mem_metrics.queue_event_count.inc();
76+
assert_eq!(mem_metrics.queue_event_count.count(), 1);
77+
}
78+
}

src/vmm/src/devices/virtio/mem/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
mod device;
55
mod event_handler;
6+
pub mod metrics;
67
pub mod persist;
78

89
use vm_memory::GuestAddress;

src/vmm/src/logger/metrics.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ use super::FcLineWriter;
7474
use crate::devices::legacy;
7575
use crate::devices::virtio::balloon::metrics as balloon_metrics;
7676
use crate::devices::virtio::block::virtio::metrics as block_metrics;
77+
use crate::devices::virtio::mem::metrics as virtio_mem_metrics;
7778
use crate::devices::virtio::net::metrics as net_metrics;
7879
use crate::devices::virtio::rng::metrics as entropy_metrics;
7980
use crate::devices::virtio::vhost_user_metrics;
@@ -873,6 +874,7 @@ create_serialize_proxy!(BalloonMetricsSerializeProxy, balloon_metrics);
873874
create_serialize_proxy!(EntropyMetricsSerializeProxy, entropy_metrics);
874875
create_serialize_proxy!(VsockMetricsSerializeProxy, vsock_metrics);
875876
create_serialize_proxy!(LegacyDevMetricsSerializeProxy, legacy);
877+
create_serialize_proxy!(MemoryHotplugSerializeProxy, virtio_mem_metrics);
876878

877879
/// Structure storing all metrics while enforcing serialization support on them.
878880
#[derive(Debug, Default, Serialize)]
@@ -923,6 +925,9 @@ pub struct FirecrackerMetrics {
923925
#[serde(flatten)]
924926
/// Vhost-user device related metrics.
925927
pub vhost_user_ser: VhostUserMetricsSerializeProxy,
928+
#[serde(flatten)]
929+
/// Virtio-mem device related metrics (memory hotplugging)
930+
pub memory_hotplug_ser: MemoryHotplugSerializeProxy,
926931
}
927932
impl FirecrackerMetrics {
928933
/// Const default construction.
@@ -948,6 +953,7 @@ impl FirecrackerMetrics {
948953
vsock_ser: VsockMetricsSerializeProxy {},
949954
entropy_ser: EntropyMetricsSerializeProxy {},
950955
vhost_user_ser: VhostUserMetricsSerializeProxy {},
956+
memory_hotplug_ser: MemoryHotplugSerializeProxy {},
951957
}
952958
}
953959
}

tests/host_tools/fcmetrics.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,11 @@ def validate_fc_metrics(metrics):
300300
"entropy_rate_limiter_throttled",
301301
"rate_limiter_event_count",
302302
],
303+
"memory_hotplug": [
304+
"activate_fails",
305+
"queue_event_fails",
306+
"queue_event_count",
307+
],
303308
}
304309

305310
# validate timestamp before jsonschema validation which some more time

0 commit comments

Comments
 (0)