Skip to content

Commit 260dcdb

Browse files
dianpopaacatangiu
authored andcommitted
virtio_blk: negotiate virtio feature for RO disk
In order to mark a virtio block device as read only, we need to negotiate the VIRTIO_BLK_F_RO feature as per virtio 1.0 spec. Signed-off-by: Diana Popa <[email protected]>
1 parent ac82fea commit 260dcdb

File tree

2 files changed

+64
-5
lines changed

2 files changed

+64
-5
lines changed

devices/src/virtio/block.rs

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use super::{DescriptorChain, Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TY
1818
use sys_util::Result as SysResult;
1919
use sys_util::{EventFd, GuestAddress, GuestMemory, GuestMemoryError};
2020
use virtio_sys::virtio_blk::*;
21+
use virtio_sys::virtio_config::*;
2122

2223
const SECTOR_SHIFT: u8 = 9;
2324
const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
@@ -313,6 +314,8 @@ impl EpollConfig {
313314
pub struct Block {
314315
kill_evt: Option<EventFd>,
315316
disk_image: Option<File>,
317+
avail_features: u64,
318+
acked_features: u64,
316319
config_space: Vec<u8>,
317320
epoll_config: EpollConfig,
318321
}
@@ -333,7 +336,11 @@ impl Block {
333336
/// Create a new virtio block device that operates on the given file.
334337
///
335338
/// The given file must be seekable and sizable.
336-
pub fn new(mut disk_image: File, epoll_config: EpollConfig) -> SysResult<Block> {
339+
pub fn new(
340+
mut disk_image: File,
341+
is_disk_read_only: bool,
342+
epoll_config: EpollConfig,
343+
) -> SysResult<Block> {
337344
let disk_size = disk_image.seek(SeekFrom::End(0))? as u64;
338345
if disk_size % SECTOR_SIZE != 0 {
339346
warn!(
@@ -342,9 +349,18 @@ impl Block {
342349
disk_size, SECTOR_SIZE
343350
);
344351
}
352+
353+
let mut avail_features = 1 << VIRTIO_F_VERSION_1;
354+
355+
if is_disk_read_only {
356+
avail_features |= 1 << VIRTIO_BLK_F_RO;
357+
};
358+
345359
Ok(Block {
346360
kill_evt: None,
347361
disk_image: Some(disk_image),
362+
avail_features,
363+
acked_features: 0u64,
348364
config_space: build_config_space(disk_size),
349365
epoll_config,
350366
})
@@ -369,6 +385,46 @@ impl VirtioDevice for Block {
369385
QUEUE_SIZES
370386
}
371387

388+
fn features(&self, page: u32) -> u32 {
389+
match page {
390+
// Get the lower 32-bits of the features bitfield.
391+
0 => self.avail_features as u32,
392+
// Get the upper 32-bits of the features bitfield.
393+
1 => (self.avail_features >> 32) as u32,
394+
_ => {
395+
warn!(
396+
"block: virtio-block got request for features page: {}",
397+
page
398+
);
399+
0u32
400+
}
401+
}
402+
}
403+
404+
fn ack_features(&mut self, page: u32, value: u32) {
405+
let mut v = match page {
406+
0 => value as u64,
407+
1 => (value as u64) << 32,
408+
_ => {
409+
warn!(
410+
"block: virtio-block device cannot ack unknown feature page: {}",
411+
page
412+
);
413+
0u64
414+
}
415+
};
416+
417+
// Check if the guest is ACK'ing a feature that we didn't claim to have.
418+
let unrequested_features = v & !self.avail_features;
419+
if unrequested_features != 0 {
420+
warn!("block: virtio-block got unknown feature ack: {:x}", v);
421+
422+
// Don't count these features as acked.
423+
v &= !unrequested_features;
424+
}
425+
self.acked_features |= v;
426+
}
427+
372428
fn read_config(&self, offset: u64, mut data: &mut [u8]) {
373429
let config_len = self.config_space.len() as u64;
374430
if offset >= config_len {
@@ -504,7 +560,7 @@ mod tests {
504560
f.set_len(0x1000).unwrap();
505561

506562
DummyBlock {
507-
block: Block::new(f, epoll_config).unwrap(),
563+
block: Block::new(f, false, epoll_config).unwrap(),
508564
epoll_raw_fd,
509565
_receiver,
510566
}

vmm/src/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,16 +417,19 @@ impl Vmm {
417417

418418
let epoll_context = &mut self.epoll_context;
419419
for drive_config in self.block_device_configs.config_list.iter() {
420-
// adding root blk device from file (currently always opened as read + write)
420+
// adding root blk device from file
421421
let root_image = OpenOptions::new()
422422
.read(true)
423423
.write(!drive_config.is_read_only)
424424
.open(&drive_config.path_on_host)
425425
.map_err(Error::RootDiskImage)?;
426426
let epoll_config = epoll_context.allocate_virtio_block_tokens();
427427

428-
let block_box = Box::new(devices::virtio::Block::new(root_image, epoll_config)
429-
.map_err(Error::RootBlockDeviceNew)?);
428+
let block_box = Box::new(devices::virtio::Block::new(
429+
root_image,
430+
drive_config.is_read_only,
431+
epoll_config,
432+
).map_err(Error::RootBlockDeviceNew)?);
430433
device_manager
431434
.register_mmio(block_box, &mut kernel_config.cmdline)
432435
.map_err(Error::RegisterBlock)?;

0 commit comments

Comments
 (0)