Skip to content

Commit ec157e1

Browse files
committed
devices: block: activate through an activation event
Postpone block external events registration to device activation time. This makes the block device unaware of external events prior to its activation. During creation register a dedicated activation event which will notify the device when it's time to register the other external events sources. This activation event is unregistered after successful device activation. Signed-off-by: Adrian Catangiu <acatan@amazon.com>
1 parent 814d877 commit ec157e1

File tree

4 files changed

+109
-46
lines changed

4 files changed

+109
-46
lines changed

src/devices/src/virtio/block/device.rs

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use virtio_gen::virtio_blk::*;
2121
use vm_memory::{Bytes, GuestMemoryMmap};
2222

2323
use super::{
24-
super::{ActivateResult, Queue, VirtioDevice, TYPE_BLOCK, VIRTIO_MMIO_INT_VRING},
24+
super::{ActivateResult, DeviceState, Queue, VirtioDevice, TYPE_BLOCK, VIRTIO_MMIO_INT_VRING},
2525
request::*,
2626
Error, CONFIG_SPACE_SIZE, QUEUE_SIZES, SECTOR_SHIFT, SECTOR_SIZE,
2727
};
@@ -88,15 +88,15 @@ pub struct Block {
8888
avail_features: u64,
8989
acked_features: u64,
9090
config_space: Vec<u8>,
91+
pub(crate) activate_evt: EventFd,
9192

9293
// Transport related fields.
9394
queues: Vec<Queue>,
9495
interrupt_status: Arc<AtomicUsize>,
9596
interrupt_evt: EventFd,
9697
pub(crate) queue_evts: [EventFd; 1],
97-
mem: GuestMemoryMmap,
9898

99-
device_activated: bool,
99+
pub(crate) device_state: DeviceState,
100100

101101
// Implementation specific fields.
102102
pub(crate) rate_limiter: RateLimiter,
@@ -107,7 +107,6 @@ impl Block {
107107
///
108108
/// The given file must be seekable and sizable.
109109
pub fn new(
110-
mem: GuestMemoryMmap,
111110
mut disk_image: File,
112111
is_disk_read_only: bool,
113112
rate_limiter: RateLimiter,
@@ -132,12 +131,12 @@ impl Block {
132131
acked_features: 0u64,
133132
config_space: build_config_space(disk_size),
134133
rate_limiter,
135-
mem,
136134
interrupt_status: Arc::new(AtomicUsize::new(0)),
137135
interrupt_evt: EventFd::new(libc::EFD_NONBLOCK)?,
138136
queue_evts,
139137
queues,
140-
device_activated: false,
138+
device_state: DeviceState::Inactive,
139+
activate_evt: EventFd::new(libc::EFD_NONBLOCK)?,
141140
})
142141
}
143142

@@ -161,11 +160,16 @@ impl Block {
161160
}
162161

163162
pub(crate) fn process_queue(&mut self, queue_index: usize) -> bool {
163+
let mem = match self.device_state {
164+
DeviceState::Activated(ref mem) => mem,
165+
// This should never happen, it's been already validated in the event handler.
166+
DeviceState::Inactive => return false,
167+
};
164168
let queue = &mut self.queues[queue_index];
165169
let mut used_any = false;
166-
while let Some(head) = queue.pop(&self.mem) {
170+
while let Some(head) = queue.pop(mem) {
167171
let len;
168-
match Request::parse(&head, &self.mem) {
172+
match Request::parse(&head, mem) {
169173
Ok(request) => {
170174
// If limiter.consume() fails it means there is no more TokenType::Ops
171175
// budget and rate limiting is in effect.
@@ -196,7 +200,7 @@ impl Block {
196200
let status = match request.execute(
197201
&mut self.disk_image,
198202
self.disk_nsectors,
199-
&self.mem,
203+
mem,
200204
&self.disk_image_id,
201205
) {
202206
Ok(l) => {
@@ -212,15 +216,15 @@ impl Block {
212216
};
213217
// We use unwrap because the request parsing process already checked that the
214218
// status_addr was valid.
215-
self.mem.write_obj(status, request.status_addr).unwrap();
219+
mem.write_obj(status, request.status_addr).unwrap();
216220
}
217221
Err(e) => {
218222
error!("Failed to parse available descriptor chain: {:?}", e);
219223
METRICS.block.execute_fails.inc();
220224
len = 0;
221225
}
222226
}
223-
queue.add_used(&self.mem, head.index, len);
227+
queue.add_used(mem, head.index, len);
224228
used_any = true;
225229
}
226230

@@ -314,11 +318,18 @@ impl VirtioDevice for Block {
314318
}
315319

316320
fn is_activated(&self) -> bool {
317-
self.device_activated
321+
match self.device_state {
322+
DeviceState::Inactive => false,
323+
DeviceState::Activated(_) => true,
324+
}
318325
}
319326

320-
fn activate(&mut self, _: GuestMemoryMmap) -> ActivateResult {
321-
self.device_activated = true;
327+
fn activate(&mut self, mem: GuestMemoryMmap) -> ActivateResult {
328+
if self.activate_evt.write(1).is_err() {
329+
error!("Block: Cannot write to activate_evt");
330+
return Err(super::super::ActivateError::BadActivate);
331+
}
332+
self.device_state = DeviceState::Activated(mem);
322333
Ok(())
323334
}
324335
}
@@ -371,9 +382,11 @@ mod tests {
371382
// Rate limiting is enabled but with a high operation rate (10 million ops/s).
372383
let rate_limiter = RateLimiter::new(0, None, 0, 100_000, None, 10).unwrap();
373384

374-
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
385+
Block::new(block_file, true, rate_limiter).unwrap()
386+
}
375387

376-
Block::new(mem, block_file, true, rate_limiter).unwrap()
388+
fn default_mem() -> GuestMemoryMmap {
389+
GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap()
377390
}
378391

379392
fn initialize_virtqueue(vq: &VirtQueue) {
@@ -492,7 +505,7 @@ mod tests {
492505
#[test]
493506
fn test_invalid_request() {
494507
let mut block = default_block();
495-
let mem = block.mem.clone();
508+
let mem = default_mem();
496509
let vq = VirtQueue::new(GuestAddress(0), &mem, 16);
497510
block.set_queue(0, vq.create_queue());
498511
block.activate(mem.clone()).unwrap();
@@ -517,7 +530,7 @@ mod tests {
517530
#[test]
518531
fn test_request_execute_failures() {
519532
let mut block = default_block();
520-
let mem = block.mem.clone();
533+
let mem = default_mem();
521534
let vq = VirtQueue::new(GuestAddress(0), &mem, 16);
522535
block.set_queue(0, vq.create_queue());
523536
block.activate(mem.clone()).unwrap();
@@ -575,7 +588,7 @@ mod tests {
575588
#[test]
576589
fn test_unsupported_request_type() {
577590
let mut block = default_block();
578-
let mem = block.mem.clone();
591+
let mem = default_mem();
579592
let vq = VirtQueue::new(GuestAddress(0), &mem, 16);
580593
block.set_queue(0, vq.create_queue());
581594
block.activate(mem.clone()).unwrap();
@@ -605,7 +618,7 @@ mod tests {
605618
#[test]
606619
fn test_read_write() {
607620
let mut block = default_block();
608-
let mem = block.mem.clone();
621+
let mem = default_mem();
609622
let vq = VirtQueue::new(GuestAddress(0), &mem, 16);
610623
block.set_queue(0, vq.create_queue());
611624
block.activate(mem.clone()).unwrap();
@@ -664,7 +677,7 @@ mod tests {
664677
#[test]
665678
fn test_flush() {
666679
let mut block = default_block();
667-
let mem = block.mem.clone();
680+
let mem = default_mem();
668681
let vq = VirtQueue::new(GuestAddress(0), &mem, 16);
669682
block.set_queue(0, vq.create_queue());
670683
block.activate(mem.clone()).unwrap();
@@ -707,7 +720,7 @@ mod tests {
707720
#[test]
708721
fn test_get_device_id() {
709722
let mut block = default_block();
710-
let mem = block.mem.clone();
723+
let mem = default_mem();
711724
let vq = VirtQueue::new(GuestAddress(0), &mem, 16);
712725
block.set_queue(0, vq.create_queue());
713726
block.activate(mem.clone()).unwrap();
@@ -773,7 +786,7 @@ mod tests {
773786
#[test]
774787
fn test_bandwidth_rate_limiter() {
775788
let mut block = default_block();
776-
let mem = block.mem.clone();
789+
let mem = default_mem();
777790
let vq = VirtQueue::new(GuestAddress(0), &mem, 16);
778791
block.set_queue(0, vq.create_queue());
779792
block.activate(mem.clone()).unwrap();
@@ -838,7 +851,7 @@ mod tests {
838851
#[test]
839852
fn test_ops_rate_limiter() {
840853
let mut block = default_block();
841-
let mem = block.mem.clone();
854+
let mem = default_mem();
842855
let vq = VirtQueue::new(GuestAddress(0), &mem, 16);
843856
block.set_queue(0, vq.create_queue());
844857
block.activate(mem.clone()).unwrap();

src/devices/src/virtio/block/event_handler.rs

Lines changed: 65 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,50 @@ use polly::event_manager::{EventManager, Subscriber};
66
use utils::epoll::{EpollEvent, EventSet};
77

88
use crate::virtio::block::device::Block;
9-
use crate::virtio::VirtioDevice;
9+
use crate::virtio::DeviceState;
1010

11-
impl Subscriber for Block {
12-
// Handle an event for queue or rate limiter.
13-
fn process(&mut self, event: &EpollEvent, _: &mut EventManager) {
14-
if !self.is_activated() {
15-
warn!("The device is not yet activated. Events can not be handled.");
16-
return;
17-
}
11+
impl Block {
12+
fn process_activate_event(&self, event_manager: &mut EventManager) {
13+
// The subscriber must exist as we previously registered activate_evt via
14+
// `interest_list()`.
15+
let self_subscriber = event_manager
16+
.subscriber(self.activate_evt.as_raw_fd())
17+
.unwrap();
18+
19+
event_manager
20+
.register(
21+
self.queue_evts[0].as_raw_fd(),
22+
EpollEvent::new(EventSet::IN, self.queue_evts[0].as_raw_fd() as u64),
23+
self_subscriber.clone(),
24+
)
25+
.unwrap_or_else(|e| {
26+
error!("Failed to register block queue with event manager: {:?}", e);
27+
});
28+
29+
event_manager
30+
.register(
31+
self.rate_limiter.as_raw_fd(),
32+
EpollEvent::new(EventSet::IN, self.rate_limiter.as_raw_fd() as u64),
33+
self_subscriber.clone(),
34+
)
35+
.unwrap_or_else(|e| {
36+
error!(
37+
"Failed to register block rate limiter with event manager: {:?}",
38+
e
39+
);
40+
});
1841

19-
let queue_evt = self.queue_evts[0].as_raw_fd();
20-
let rate_limiter_evt = self.rate_limiter.as_raw_fd();
42+
event_manager
43+
.unregister(self.activate_evt.as_raw_fd())
44+
.unwrap_or_else(|e| {
45+
error!("Failed to unregister block activate evt: {:?}", e);
46+
})
47+
}
48+
}
2149

50+
impl Subscriber for Block {
51+
// Handle an event for queue or rate limiter.
52+
fn process(&mut self, event: &EpollEvent, evmgr: &mut EventManager) {
2253
let source = event.fd();
2354
let event_set = event.event_set();
2455

@@ -27,25 +58,38 @@ impl Subscriber for Block {
2758
let supported_events = EventSet::IN;
2859
if !supported_events.contains(event_set) {
2960
warn!(
30-
"Received unknown event: {:?} from source: {:?}",
61+
"Block: Received unknown event: {:?} from source: {:?}",
3162
event_set, source
3263
);
3364
return;
3465
}
3566

36-
// Looks better than C style if/else if/else.
37-
match source {
38-
_ if queue_evt == source => self.process_queue_event(),
39-
_ if rate_limiter_evt == source => self.process_rate_limiter_event(),
40-
_ => warn!("Spurious event received: {:?}", source),
41-
}
67+
match self.device_state {
68+
DeviceState::Activated(_) => {
69+
let queue_evt = self.queue_evts[0].as_raw_fd();
70+
let rate_limiter_evt = self.rate_limiter.as_raw_fd();
71+
let activate_fd = self.activate_evt.as_raw_fd();
72+
73+
// Looks better than C style if/else if/else.
74+
match source {
75+
_ if queue_evt == source => self.process_queue_event(),
76+
_ if rate_limiter_evt == source => self.process_rate_limiter_event(),
77+
_ if activate_fd == source => self.process_activate_event(evmgr),
78+
_ => warn!("Block: Spurious event received: {:?}", source),
79+
}
80+
}
81+
DeviceState::Inactive => warn!(
82+
"Block: The device is not yet activated. Spurious event received: {:?}",
83+
source
84+
),
85+
};
4286
}
4387

4488
// Returns the rate_limiter and queue event fds.
4589
fn interest_list(&self) -> Vec<EpollEvent> {
46-
vec![
47-
EpollEvent::new(EventSet::IN, self.rate_limiter.as_raw_fd() as u64),
48-
EpollEvent::new(EventSet::IN, self.queue_evts[0].as_raw_fd() as u64),
49-
]
90+
vec![EpollEvent::new(
91+
EventSet::IN,
92+
self.activate_evt.as_raw_fd() as u64,
93+
)]
5094
}
5195
}

src/devices/src/virtio/device.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ use crate::virtio::AsAny;
1212
use utils::eventfd::EventFd;
1313
use vm_memory::GuestMemoryMmap;
1414

15+
/// Enum that indicates if a VirtioDevice is inactive or has been activated
16+
/// and memory attached to it.
17+
pub enum DeviceState {
18+
Inactive,
19+
Activated(GuestMemoryMmap),
20+
}
21+
1522
/// Trait for virtio devices to be driven by a virtio transport.
1623
///
1724
/// The lifecycle of a virtio device is to be moved to a virtio transport, which will then query the

src/vmm/src/builder.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,6 @@ fn attach_block_devices(
708708

709709
let block_device = Arc::new(Mutex::new(
710710
devices::virtio::Block::new(
711-
vmm.guest_memory.clone(),
712711
block_file,
713712
drive_config.is_read_only,
714713
rate_limiter.unwrap_or_default(),

0 commit comments

Comments
 (0)