Skip to content

Commit ddb9041

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 <[email protected]>
1 parent 62ec01b commit ddb9041

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};
@@ -171,12 +172,15 @@ impl VirtioMem {
171172
}
172173

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

179182
if let Err(err) = self.process_mem_queue() {
183+
METRICS.queue_event_fails.inc();
180184
error!("virtio-mem: Failed to process queue: {err}");
181185
}
182186
}
@@ -264,7 +268,7 @@ impl VirtioDevice for VirtioMem {
264268
error!(
265269
"virtio-mem: VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE feature not acknowledged by guest"
266270
);
267-
// TODO(virtio-mem): activation failed metric
271+
METRICS.activate_fails.inc();
268272
return Err(ActivateError::RequiredFeatureNotAcked(
269273
"VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE",
270274
));
@@ -277,7 +281,7 @@ impl VirtioDevice for VirtioMem {
277281

278282
self.device_state = DeviceState::Activated(ActiveState { mem, interrupt });
279283
if self.activate_event.write(1).is_err() {
280-
// TODO(virtio-mem): activation failed metric
284+
METRICS.activate_fails.inc();
281285
self.device_state = DeviceState::Inactive;
282286
return Err(ActivateError::EventFd);
283287
}
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;
@@ -877,6 +878,7 @@ create_serialize_proxy!(BalloonMetricsSerializeProxy, balloon_metrics);
877878
create_serialize_proxy!(EntropyMetricsSerializeProxy, entropy_metrics);
878879
create_serialize_proxy!(VsockMetricsSerializeProxy, vsock_metrics);
879880
create_serialize_proxy!(LegacyDevMetricsSerializeProxy, legacy);
881+
create_serialize_proxy!(MemoryHotplugSerializeProxy, virtio_mem_metrics);
880882

881883
/// Structure storing all metrics while enforcing serialization support on them.
882884
#[derive(Debug, Default, Serialize)]
@@ -929,6 +931,9 @@ pub struct FirecrackerMetrics {
929931
pub vhost_user_ser: VhostUserMetricsSerializeProxy,
930932
/// Interrupt related metrics
931933
pub interrupts: InterruptMetrics,
934+
#[serde(flatten)]
935+
/// Virtio-mem device related metrics (memory hotplugging)
936+
pub memory_hotplug_ser: MemoryHotplugSerializeProxy,
932937
}
933938
impl FirecrackerMetrics {
934939
/// Const default construction.
@@ -955,6 +960,7 @@ impl FirecrackerMetrics {
955960
entropy_ser: EntropyMetricsSerializeProxy {},
956961
vhost_user_ser: VhostUserMetricsSerializeProxy {},
957962
interrupts: InterruptMetrics::new(),
963+
memory_hotplug_ser: MemoryHotplugSerializeProxy {},
958964
}
959965
}
960966
}

tests/host_tools/fcmetrics.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,11 @@ def validate_fc_metrics(metrics):
296296
"rate_limiter_event_count",
297297
],
298298
"interrupts": ["triggers", "config_updates"],
299+
"memory_hotplug": [
300+
"activate_fails",
301+
"queue_event_fails",
302+
"queue_event_count",
303+
],
299304
}
300305

301306
# validate timestamp before jsonschema validation which some more time

0 commit comments

Comments
 (0)