diff options
Diffstat (limited to 'devices/src/virtio/block.rs')
-rw-r--r-- | devices/src/virtio/block.rs | 139 |
1 files changed, 110 insertions, 29 deletions
diff --git a/devices/src/virtio/block.rs b/devices/src/virtio/block.rs index ae212c3..b022fa9 100644 --- a/devices/src/virtio/block.rs +++ b/devices/src/virtio/block.rs @@ -6,6 +6,7 @@ use std::cmp; use std::io::{self, Read, Seek, SeekFrom, Write}; use std::mem::{size_of, size_of_val}; use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::net::UnixDatagram; use std::result; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -17,15 +18,17 @@ use sync::Mutex; use sys_util::Error as SysError; use sys_util::Result as SysResult; use sys_util::{ - EventFd, FileSync, GuestAddress, GuestMemory, GuestMemoryError, PollContext, PollToken, - PunchHole, TimerFd, WriteZeroes, + EventFd, FileSetLen, FileSync, GuestAddress, GuestMemory, GuestMemoryError, PollContext, + PollToken, PunchHole, TimerFd, WriteZeroes, }; use data_model::{DataInit, Le16, Le32, Le64}; +use msg_socket::{MsgReceiver, MsgSender, MsgSocket}; +use vm_control::{VmRequest, VmResponse}; use super::{ - DescriptorChain, Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TYPE_BLOCK, - VIRTIO_F_VERSION_1, + DescriptorChain, Queue, VirtioDevice, INTERRUPT_STATUS_CONFIG_CHANGED, + INTERRUPT_STATUS_USED_RING, TYPE_BLOCK, VIRTIO_F_VERSION_1, }; const QUEUE_SIZE: u16 = 256; @@ -112,8 +115,8 @@ const VIRTIO_BLK_DISCARD_WRITE_ZEROES_FLAG_UNMAP: u32 = 1 << 0; // Safe because it only has data and has no implicit padding. unsafe impl DataInit for virtio_blk_discard_write_zeroes {} -pub trait DiskFile: FileSync + PunchHole + Read + Seek + Write + WriteZeroes {} -impl<D: FileSync + PunchHole + Read + Seek + Write + WriteZeroes> DiskFile for D {} +pub trait DiskFile: FileSetLen + FileSync + PunchHole + Read + Seek + Write + WriteZeroes {} +impl<D: FileSetLen + FileSync + PunchHole + Read + Seek + Write + WriteZeroes> DiskFile for D {} #[derive(Copy, Clone, Debug, PartialEq)] enum RequestType { @@ -470,6 +473,7 @@ struct Worker<T: DiskFile> { queues: Vec<Queue>, mem: GuestMemory, disk_image: T, + disk_size: Arc<Mutex<u64>>, read_only: bool, interrupt_status: Arc<AtomicUsize>, interrupt_evt: EventFd, @@ -529,17 +533,44 @@ impl<T: DiskFile> Worker<T> { used_count > 0 } + fn resize(&mut self, new_size: u64) -> VmResponse { + if self.read_only { + error!("Attempted to resize read-only block device"); + return VmResponse::Err(SysError::new(libc::EROFS)); + } + + info!("Resizing block device to {} bytes", new_size); + + if let Err(e) = self.disk_image.set_len(new_size) { + error!("Resizing disk failed! {}", e); + return VmResponse::Err(SysError::new(libc::EIO)); + } + + if let Ok(new_disk_size) = self.disk_image.seek(SeekFrom::End(0)) { + let mut disk_size = self.disk_size.lock(); + *disk_size = new_disk_size; + } + VmResponse::Ok + } + fn signal_used_queue(&self) { self.interrupt_status .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst); self.interrupt_evt.write(1).unwrap(); } - fn run(&mut self, queue_evt: EventFd, kill_evt: EventFd) { + fn signal_config_changed(&self) { + self.interrupt_status + .fetch_or(INTERRUPT_STATUS_CONFIG_CHANGED as usize, Ordering::SeqCst); + self.interrupt_evt.write(1).unwrap(); + } + + fn run(&mut self, queue_evt: EventFd, kill_evt: EventFd, control_socket: UnixDatagram) { #[derive(PollToken)] enum Token { FlushTimer, QueueAvailable, + ControlRequest, InterruptResample, Kill, } @@ -553,9 +584,12 @@ impl<T: DiskFile> Worker<T> { }; let mut flush_timer_armed = false; + let control_socket = MsgSocket::<VmResponse, VmRequest>::new(control_socket); + let poll_ctx: PollContext<Token> = match PollContext::new() .and_then(|pc| pc.add(&flush_timer, Token::FlushTimer).and(Ok(pc))) .and_then(|pc| pc.add(&queue_evt, Token::QueueAvailable).and(Ok(pc))) + .and_then(|pc| pc.add(&control_socket, Token::ControlRequest).and(Ok(pc))) .and_then(|pc| { pc.add(&self.interrupt_resample_evt, Token::InterruptResample) .and(Ok(pc)) @@ -579,6 +613,7 @@ impl<T: DiskFile> Worker<T> { }; let mut needs_interrupt = false; + let mut needs_config_interrupt = false; for event in events.iter_readable() { match event.token() { Token::FlushTimer => { @@ -599,6 +634,35 @@ impl<T: DiskFile> Worker<T> { needs_interrupt |= self.process_queue(0, &mut flush_timer, &mut flush_timer_armed); } + Token::ControlRequest => { + let req = match control_socket.recv() { + Ok(req) => req, + Err(e) => { + error!("control socket failed recv: {:?}", e); + break 'poll; + } + }; + + let resp = match req { + VmRequest::DiskResize { + disk_index: _, + new_size, + } => { + needs_config_interrupt = true; + self.resize(new_size) + } + // Only DiskResize makes sense - fail any other requests + _ => { + error!("block device received unexpected VmRequest"); + VmResponse::Err(SysError::new(libc::EINVAL)) + } + }; + + if let Err(e) = control_socket.send(&resp) { + error!("control socket failed send: {:?}", e); + break 'poll; + } + } Token::InterruptResample => { let _ = self.interrupt_resample_evt.read(); if self.interrupt_status.load(Ordering::SeqCst) != 0 { @@ -611,6 +675,9 @@ impl<T: DiskFile> Worker<T> { if needs_interrupt { self.signal_used_queue(); } + if needs_config_interrupt { + self.signal_config_changed(); + } } } } @@ -622,6 +689,7 @@ pub struct Block<T: DiskFile> { disk_size: Arc<Mutex<u64>>, avail_features: u64, read_only: bool, + control_socket: Option<UnixDatagram>, } fn build_config_space(disk_size: u64) -> virtio_blk_config { @@ -643,7 +711,11 @@ impl<T: DiskFile> Block<T> { /// Create a new virtio block device that operates on the given file. /// /// The given file must be seekable and sizable. - pub fn new(mut disk_image: T, read_only: bool) -> SysResult<Block<T>> { + pub fn new( + mut disk_image: T, + read_only: bool, + control_socket: Option<UnixDatagram>, + ) -> SysResult<Block<T>> { let disk_size = disk_image.seek(SeekFrom::End(0))? as u64; if disk_size % SECTOR_SIZE != 0 { warn!( @@ -668,6 +740,7 @@ impl<T: DiskFile> Block<T> { disk_size: Arc::new(Mutex::new(disk_size)), avail_features, read_only, + control_socket, }) } } @@ -689,6 +762,10 @@ impl<T: 'static + AsRawFd + DiskFile + Send> VirtioDevice for Block<T> { keep_fds.push(disk_image.as_raw_fd()); } + if let Some(ref control_socket) = self.control_socket { + keep_fds.push(control_socket.as_raw_fd()); + } + keep_fds } @@ -746,26 +823,30 @@ impl<T: 'static + AsRawFd + DiskFile + Send> VirtioDevice for Block<T> { self.kill_evt = Some(self_kill_evt); let read_only = self.read_only; + let disk_size = self.disk_size.clone(); if let Some(disk_image) = self.disk_image.take() { - let worker_result = - thread::Builder::new() - .name("virtio_blk".to_string()) - .spawn(move || { - let mut worker = Worker { - queues, - mem, - disk_image, - read_only, - interrupt_status: status, - interrupt_evt, - interrupt_resample_evt, - }; - worker.run(queue_evts.remove(0), kill_evt); - }); + if let Some(control_socket) = self.control_socket.take() { + let worker_result = + thread::Builder::new() + .name("virtio_blk".to_string()) + .spawn(move || { + let mut worker = Worker { + queues, + mem, + disk_image, + disk_size, + read_only, + interrupt_status: status, + interrupt_evt, + interrupt_resample_evt, + }; + worker.run(queue_evts.remove(0), kill_evt, control_socket); + }); - if let Err(e) = worker_result { - error!("failed to spawn virtio_blk worker: {}", e); - return; + if let Err(e) = worker_result { + error!("failed to spawn virtio_blk worker: {}", e); + return; + } } } } @@ -787,7 +868,7 @@ mod tests { let f = File::create(&path).unwrap(); f.set_len(0x1000).unwrap(); - let b = Block::new(f, true).unwrap(); + let b = Block::new(f, true, None).unwrap(); let mut num_sectors = [0u8; 4]; b.read_config(0, &mut num_sectors); // size is 0x1000, so num_sectors is 8 (4096/512). @@ -807,7 +888,7 @@ mod tests { // read-write block device { let f = File::create(&path).unwrap(); - let b = Block::new(f, false).unwrap(); + let b = Block::new(f, false, None).unwrap(); // writable device should set VIRTIO_BLK_F_FLUSH + VIRTIO_BLK_F_DISCARD // + VIRTIO_BLK_F_WRITE_ZEROES + VIRTIO_F_VERSION_1 assert_eq!(0x100006200, b.features()); @@ -816,7 +897,7 @@ mod tests { // read-only block device { let f = File::create(&path).unwrap(); - let b = Block::new(f, true).unwrap(); + let b = Block::new(f, true, None).unwrap(); // read-only device should set VIRTIO_BLK_F_FLUSH and VIRTIO_BLK_F_RO // + VIRTIO_F_VERSION_1 assert_eq!(0x100000220, b.features()); |