Skip to content

Commit 34bc630

Browse files
jiangliuandreeaflorescu
authored andcommitted
virtio: complete the virtio device state machine
Complete the virtio device state machine by: 1) Handle writing FAILED flag into "driver status" control field. 2) Handle writing 0 into "driver status" control field to reset the device. If the the backend driver supports reset operation, the device will be reset. Otherwise the device will be put into FAILED state. With these changes, the virtio mmio transport layer implements a full state machine according to Virtio Spec. The backend driver is still optional to support reset operation. Signed-off-by: Liu Jiang <[email protected]>
1 parent 7492a15 commit 34bc630

File tree

1 file changed

+115
-9
lines changed

1 file changed

+115
-9
lines changed

devices/src/virtio/mmio.rs

Lines changed: 115 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,25 @@ impl MmioDevice {
182182
}
183183
}
184184

185+
fn reset(&mut self) {
186+
if self.device_activated {
187+
warn!("reset device while it's still in active state");
188+
return;
189+
}
190+
self.features_select = 0;
191+
self.acked_features_select = 0;
192+
self.queue_select = 0;
193+
self.interrupt_status.store(0, Ordering::SeqCst);
194+
self.driver_status = 0;
195+
// . Keep interrupt_evt and queue_evts as is. There may be pending
196+
// notifications in those eventfds, but nothing will happen other
197+
// than supurious wakeups.
198+
// . Do not reset config_generation and keep it monotonically increasing
199+
for queue in self.queues.as_mut_slice() {
200+
*queue = Queue::new(queue.get_max_size());
201+
}
202+
}
203+
185204
/// Update driver status according to the state machine defined by VirtIO Spec 1.0.
186205
/// Please refer to VirtIO Spec 1.0, section 2.1.1 and 3.1.1.
187206
///
@@ -210,10 +229,10 @@ impl MmioDevice {
210229
// check will fail and take the device into an unusable state.
211230
if !self.device_activated && self.are_queues_valid() {
212231
if let Some(ref interrupt_evt) = self.interrupt_evt {
213-
if let Some(mem) = self.mem.take() {
232+
if let Some(ref mem) = self.mem {
214233
self.device
215234
.activate(
216-
mem,
235+
mem.clone(),
217236
interrupt_evt.try_clone().expect("Failed to clone eventfd"),
218237
self.interrupt_status.clone(),
219238
self.queues.clone(),
@@ -226,11 +245,25 @@ impl MmioDevice {
226245
}
227246
}
228247
_ if (v & DEVICE_FAILED) != 0 => {
229-
// TODO: stop device
248+
// TODO: notify backend driver to stop the device
230249
self.driver_status |= DEVICE_FAILED;
231250
}
232251
_ if v == 0 => {
233-
return; // TODO reset device status
252+
if self.device_activated {
253+
match self.device.reset() {
254+
Some((_interrupt_evt, mut queue_evts)) => {
255+
self.device_activated = false;
256+
self.queue_evts.append(&mut queue_evts);
257+
}
258+
// Backend device driver doesn't support reset,
259+
// just mark the device as FAILED.
260+
None => {
261+
self.driver_status |= DEVICE_FAILED;
262+
return;
263+
}
264+
}
265+
}
266+
self.reset();
234267
}
235268
_ => {
236269
warn!(
@@ -363,18 +396,25 @@ impl BusDevice for MmioDevice {
363396
#[cfg(test)]
364397
mod tests {
365398
use byteorder::{ByteOrder, LittleEndian};
399+
use std::sync::atomic::ATOMIC_USIZE_INIT;
366400

367401
use super::*;
368402

403+
static DEVICE_RESET_ENABLED: AtomicUsize = ATOMIC_USIZE_INIT;
404+
369405
struct DummyDevice {
370406
acked_features: u32,
407+
interrupt_evt: Option<EventFd>,
408+
queue_evts: Option<Vec<EventFd>>,
371409
config_bytes: [u8; 0xeff],
372410
}
373411

374412
impl DummyDevice {
375413
fn new() -> Self {
376414
DummyDevice {
377415
acked_features: 0,
416+
interrupt_evt: None,
417+
queue_evts: None,
378418
config_bytes: [0; 0xeff],
379419
}
380420
}
@@ -408,13 +448,26 @@ mod tests {
408448
fn activate(
409449
&mut self,
410450
_mem: GuestMemory,
411-
_interrupt_evt: EventFd,
451+
interrupt_evt: EventFd,
412452
_status: Arc<AtomicUsize>,
413453
_queues: Vec<Queue>,
414-
_queue_evts: Vec<EventFd>,
454+
queue_evts: Vec<EventFd>,
415455
) -> ActivateResult {
456+
self.interrupt_evt = Some(interrupt_evt);
457+
self.queue_evts = Some(queue_evts);
416458
Ok(())
417459
}
460+
461+
fn reset(&mut self) -> Option<(EventFd, Vec<EventFd>)> {
462+
if self.interrupt_evt.is_some() && DEVICE_RESET_ENABLED.load(Ordering::SeqCst) != 0 {
463+
Some((
464+
self.interrupt_evt.take().unwrap(),
465+
self.queue_evts.take().unwrap(),
466+
))
467+
} else {
468+
None
469+
}
470+
}
418471
}
419472

420473
fn set_driver_status(d: &mut MmioDevice, status: u32) {
@@ -747,15 +800,68 @@ mod tests {
747800
d.write(0x44, &buf[..]);
748801
d.read(0x44, &mut buf[..]);
749802
assert_eq!(LittleEndian::read_u32(&buf[..]), 1);
803+
}
750804

805+
fn activate_device(d: &mut MmioDevice) {
806+
set_driver_status(d, DEVICE_ACKNOWLEDGE);
807+
set_driver_status(d, DEVICE_ACKNOWLEDGE | DEVICE_DRIVER);
808+
set_driver_status(d, DEVICE_ACKNOWLEDGE | DEVICE_DRIVER | DEVICE_FEATURES_OK);
809+
810+
// Setup queue data structures
811+
let mut buf = vec![0; 4];
812+
for q in 0..d.queues.len() {
813+
d.queue_select = q as u32;
814+
LittleEndian::write_u32(&mut buf[..], 16);
815+
d.write(0x38, &buf[..]);
816+
LittleEndian::write_u32(&mut buf[..], 1);
817+
d.write(0x44, &buf[..]);
818+
}
819+
assert!(d.are_queues_valid());
820+
assert!(!d.device_activated);
821+
822+
// Device should be ready for activation now.
823+
set_driver_status(
824+
d,
825+
DEVICE_ACKNOWLEDGE | DEVICE_DRIVER | DEVICE_FEATURES_OK | DEVICE_DRIVER_OK,
826+
);
827+
assert_eq!(
828+
d.driver_status,
829+
DEVICE_ACKNOWLEDGE | DEVICE_DRIVER | DEVICE_FEATURES_OK | DEVICE_DRIVER_OK
830+
);
831+
assert!(d.device_activated);
832+
}
833+
834+
#[test]
835+
fn test_bus_device_reset() {
836+
let m = GuestMemory::new(&[(GuestAddress(0), 0x1000)]).unwrap();
837+
let mut d = MmioDevice::new(m, Box::new(DummyDevice::new())).unwrap();
838+
let mut buf = vec![0; 4];
839+
840+
assert!(!d.are_queues_valid());
841+
assert!(!d.device_activated);
842+
assert_eq!(d.driver_status, 0);
843+
activate_device(&mut d);
844+
845+
// Marking device as FAILED should not affect device_activated state
751846
LittleEndian::write_u32(&mut buf[..], 0x8f);
752847
d.write(0x70, &buf[..]);
753848
assert_eq!(d.driver_status, 0x8f);
754-
// TODO: assert!(d.device_activated);
849+
assert!(d.device_activated);
755850

851+
// Nothing happens when backend driver doesn't support reset
852+
DEVICE_RESET_ENABLED.store(0, Ordering::SeqCst);
756853
LittleEndian::write_u32(&mut buf[..], 0x0);
757854
d.write(0x70, &buf[..]);
758-
// TODO: assert_eq!(d.driver_status, 0x0);
759-
// TODO: assert!(d.device_activated);
855+
assert_eq!(d.driver_status, 0x8f);
856+
assert!(d.device_activated);
857+
858+
DEVICE_RESET_ENABLED.store(1, Ordering::SeqCst);
859+
LittleEndian::write_u32(&mut buf[..], 0x0);
860+
d.write(0x70, &buf[..]);
861+
assert_eq!(d.driver_status, 0x0);
862+
assert!(!d.device_activated);
863+
864+
DEVICE_RESET_ENABLED.store(0, Ordering::SeqCst);
865+
activate_device(&mut d);
760866
}
761867
}

0 commit comments

Comments
 (0)