Skip to content

Commit 761fc89

Browse files
Iulian Barbualindima
authored andcommitted
block: fixed used bytes reporting
Signed-off-by: Iulian Barbu <[email protected]> Signed-off-by: Laura Loghin <[email protected]>
1 parent 775b82f commit 761fc89

File tree

3 files changed

+161
-36
lines changed

3 files changed

+161
-36
lines changed

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

Lines changed: 134 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use logger::{error, warn, Metric, METRICS};
1919
use rate_limiter::{RateLimiter, TokenType};
2020
use utils::eventfd::EventFd;
2121
use virtio_gen::virtio_blk::*;
22-
use vm_memory::{Bytes, GuestMemoryMmap};
22+
use vm_memory::{Bytes, GuestMemoryError, GuestMemoryMmap};
2323

2424
use super::{
2525
super::{ActivateResult, DeviceState, Queue, VirtioDevice, TYPE_BLOCK, VIRTIO_MMIO_INT_VRING},
@@ -257,15 +257,46 @@ impl Block {
257257
break;
258258
}
259259
}
260+
260261
let status = match request.execute(&mut self.disk, mem) {
261262
Ok(l) => {
262-
len = l;
263-
VIRTIO_BLK_S_OK
263+
// Account for the status byte as well.
264+
// With a non-faulty driver, we shouldn't get to the point where we
265+
// overflow here (since data len must be a multiple of 512 bytes, so
266+
// it can't be u32::MAX). In the future, this should be fixed at the
267+
// request parsing level, so no data will actually be transferred in
268+
// scenarios like this one.
269+
if let Some(l) = l.checked_add(1) {
270+
len = l;
271+
VIRTIO_BLK_S_OK
272+
} else {
273+
len = l;
274+
VIRTIO_BLK_S_IOERR
275+
}
264276
}
265277
Err(e) => {
266-
error!("Failed to execute request: {:?}", e);
267278
METRICS.block.invalid_reqs_count.inc();
268-
len = 1; // We need at least 1 byte for the status.
279+
match e {
280+
ExecuteError::Read(GuestMemoryError::PartialBuffer {
281+
completed,
282+
expected,
283+
}) => {
284+
error!(
285+
"Failed to execute virtio block read request: can only \
286+
write {} of {} bytes.",
287+
completed, expected
288+
);
289+
METRICS.block.read_bytes.add(completed);
290+
// This can not overflow since `completed` < data len which is
291+
// an u32.
292+
len = completed as u32 + 1;
293+
}
294+
_ => {
295+
error!("Failed to execute virtio block request: {:?}", e);
296+
// Status byte only.
297+
len = 1;
298+
}
299+
};
269300
e.status()
270301
}
271302
};
@@ -766,7 +797,7 @@ pub(crate) mod tests {
766797

767798
assert_eq!(vq.used.idx.get(), 1);
768799
assert_eq!(vq.used.ring[0].get().id, 0);
769-
assert_eq!(vq.used.ring[0].get().len, 0);
800+
assert_eq!(vq.used.ring[0].get().len, 1);
770801
assert_eq!(mem.read_obj::<u32>(status_addr).unwrap(), VIRTIO_BLK_S_OK);
771802
}
772803

@@ -789,10 +820,100 @@ pub(crate) mod tests {
789820

790821
assert_eq!(vq.used.idx.get(), 1);
791822
assert_eq!(vq.used.ring[0].get().id, 0);
792-
assert_eq!(vq.used.ring[0].get().len, vq.dtable[1].len.get());
823+
// Added status byte length.
824+
assert_eq!(vq.used.ring[0].get().len, vq.dtable[1].len.get() + 1);
793825
assert_eq!(mem.read_obj::<u32>(status_addr).unwrap(), VIRTIO_BLK_S_OK);
826+
}
827+
828+
// Read with error.
829+
{
830+
vq.used.idx.set(0);
831+
block.set_queue(0, vq.create_queue());
832+
833+
mem.write_obj::<u32>(VIRTIO_BLK_T_IN, request_type_addr)
834+
.unwrap();
835+
vq.dtable[1]
836+
.flags
837+
.set(VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE);
838+
839+
let size = block.disk.file.seek(SeekFrom::End(0)).unwrap();
840+
block.disk.file.set_len(size / 2).unwrap();
841+
mem.write_obj(10, GuestAddress(request_type_addr.0 + 8))
842+
.unwrap();
843+
844+
invoke_handler_for_queue_event(&mut block);
845+
846+
assert_eq!(vq.used.idx.get(), 1);
847+
assert_eq!(vq.used.ring[0].get().id, 0);
848+
849+
// Only status byte length.
850+
assert_eq!(vq.used.ring[0].get().len, 1);
851+
assert_eq!(
852+
mem.read_obj::<u32>(status_addr).unwrap(),
853+
VIRTIO_BLK_S_IOERR
854+
);
855+
assert_eq!(mem.read_obj::<u64>(data_addr).unwrap(), 123_456_789);
856+
}
857+
858+
// Partial buffer error on read.
859+
{
860+
vq.used.idx.set(0);
861+
block.set_queue(0, vq.create_queue());
862+
863+
mem.write_obj::<u32>(VIRTIO_BLK_T_IN, request_type_addr)
864+
.unwrap();
865+
vq.dtable[1]
866+
.flags
867+
.set(VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE);
868+
869+
let size = block.disk.file.seek(SeekFrom::End(0)).unwrap();
870+
block.disk.file.set_len(size / 2).unwrap();
871+
// Update sector number: stored at `request_type_addr.0 + 8`
872+
mem.write_obj(5, GuestAddress(request_type_addr.0 + 8))
873+
.unwrap();
874+
875+
// This will attempt to read past end of file.
876+
invoke_handler_for_queue_event(&mut block);
877+
878+
assert_eq!(vq.used.idx.get(), 1);
879+
assert_eq!(vq.used.ring[0].get().id, 0);
880+
881+
// No data since can't read past end of file, only status byte length.
882+
assert_eq!(vq.used.ring[0].get().len, 1);
883+
assert_eq!(
884+
mem.read_obj::<u32>(status_addr).unwrap(),
885+
VIRTIO_BLK_S_IOERR
886+
);
794887
assert_eq!(mem.read_obj::<u64>(data_addr).unwrap(), 123_456_789);
795888
}
889+
890+
{
891+
vq.used.idx.set(0);
892+
block.set_queue(0, vq.create_queue());
893+
894+
mem.write_obj::<u32>(VIRTIO_BLK_T_IN, request_type_addr)
895+
.unwrap();
896+
vq.dtable[1]
897+
.flags
898+
.set(VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE);
899+
vq.dtable[1].len.set(1024);
900+
901+
mem.write_obj(1, GuestAddress(request_type_addr.0 + 8))
902+
.unwrap();
903+
904+
invoke_handler_for_queue_event(&mut block);
905+
906+
assert_eq!(vq.used.idx.get(), 1);
907+
assert_eq!(vq.used.ring[0].get().id, 0);
908+
909+
// File has 2 sectors and we try to read from the second sector, which means we will
910+
// read 512 bytes (instead of 1024).
911+
assert_eq!(vq.used.ring[0].get().len, 513);
912+
assert_eq!(
913+
mem.read_obj::<u32>(status_addr).unwrap(),
914+
VIRTIO_BLK_S_IOERR
915+
);
916+
}
796917
}
797918

798919
#[test]
@@ -817,7 +938,7 @@ pub(crate) mod tests {
817938
invoke_handler_for_queue_event(&mut block);
818939
assert_eq!(vq.used.idx.get(), 1);
819940
assert_eq!(vq.used.ring[0].get().id, 0);
820-
assert_eq!(vq.used.ring[0].get().len, 0);
941+
assert_eq!(vq.used.ring[0].get().len, 1);
821942
assert_eq!(mem.read_obj::<u32>(status_addr).unwrap(), VIRTIO_BLK_S_OK);
822943
}
823944

@@ -833,7 +954,8 @@ pub(crate) mod tests {
833954
invoke_handler_for_queue_event(&mut block);
834955
assert_eq!(vq.used.idx.get(), 1);
835956
assert_eq!(vq.used.ring[0].get().id, 0);
836-
assert_eq!(vq.used.ring[0].get().len, 0);
957+
// status byte length.
958+
assert_eq!(vq.used.ring[0].get().len, 1);
837959
assert_eq!(mem.read_obj::<u32>(status_addr).unwrap(), VIRTIO_BLK_S_OK);
838960
}
839961
}
@@ -862,7 +984,7 @@ pub(crate) mod tests {
862984
invoke_handler_for_queue_event(&mut block);
863985
assert_eq!(vq.used.idx.get(), 1);
864986
assert_eq!(vq.used.ring[0].get().id, 0);
865-
assert_eq!(vq.used.ring[0].get().len, 0);
987+
assert_eq!(vq.used.ring[0].get().len, 21);
866988
assert_eq!(mem.read_obj::<u32>(status_addr).unwrap(), VIRTIO_BLK_S_OK);
867989

868990
assert!(blk_metadata.is_ok());
@@ -973,7 +1095,7 @@ pub(crate) mod tests {
9731095
// Make sure the data queue advanced.
9741096
assert_eq!(vq.used.idx.get(), 1);
9751097
assert_eq!(vq.used.ring[0].get().id, 0);
976-
assert_eq!(vq.used.ring[0].get().len, 0);
1098+
assert_eq!(vq.used.ring[0].get().len, 1);
9771099
assert_eq!(mem.read_obj::<u32>(status_addr).unwrap(), VIRTIO_BLK_S_OK);
9781100
}
9791101
}
@@ -1064,7 +1186,7 @@ pub(crate) mod tests {
10641186
// Make sure the data queue advanced.
10651187
assert_eq!(vq.used.idx.get(), 1);
10661188
assert_eq!(vq.used.ring[0].get().id, 0);
1067-
assert_eq!(vq.used.ring[0].get().len, 0);
1189+
assert_eq!(vq.used.ring[0].get().len, 1);
10681190
assert_eq!(mem.read_obj::<u32>(status_addr).unwrap(), VIRTIO_BLK_S_OK);
10691191
}
10701192
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ pub mod tests {
174174

175175
assert_eq!(vq.used.idx.get(), 1);
176176
assert_eq!(vq.used.ring[0].get().id, 0);
177-
assert_eq!(vq.used.ring[0].get().len, 0);
177+
assert_eq!(vq.used.ring[0].get().len, 1);
178178
assert_eq!(mem.read_obj::<u32>(status_addr).unwrap(), VIRTIO_BLK_S_OK);
179179
}
180180
}

src/devices/src/virtio/block/request.rs

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -214,37 +214,40 @@ impl Request {
214214
.map_err(ExecuteError::Seek)?;
215215

216216
match self.request_type {
217-
RequestType::In => {
218-
mem.read_from(self.data_addr, diskfile, self.data_len as usize)
219-
.map_err(ExecuteError::Read)?;
220-
METRICS.block.read_bytes.add(self.data_len as usize);
221-
METRICS.block.read_count.inc();
222-
return Ok(self.data_len);
223-
}
224-
RequestType::Out => {
225-
mem.write_to(self.data_addr, diskfile, self.data_len as usize)
226-
.map_err(ExecuteError::Write)?;
227-
METRICS.block.write_bytes.add(self.data_len as usize);
228-
METRICS.block.write_count.inc();
229-
}
230-
RequestType::Flush => match diskfile.flush() {
231-
Ok(_) => {
217+
RequestType::In => mem
218+
.read_exact_from(self.data_addr, diskfile, self.data_len as usize)
219+
.map(|_| {
220+
METRICS.block.read_bytes.add(self.data_len as usize);
221+
METRICS.block.read_count.inc();
222+
self.data_len
223+
})
224+
.map_err(ExecuteError::Read),
225+
RequestType::Out => mem
226+
.write_all_to(self.data_addr, diskfile, self.data_len as usize)
227+
.map(|_| {
228+
METRICS.block.write_bytes.add(self.data_len as usize);
229+
METRICS.block.write_count.inc();
230+
0
231+
})
232+
.map_err(ExecuteError::Write),
233+
RequestType::Flush => diskfile
234+
.flush()
235+
.map(|_| {
232236
METRICS.block.flush_count.inc();
233-
return Ok(0);
234-
}
235-
Err(e) => return Err(ExecuteError::Flush(e)),
236-
},
237+
0
238+
})
239+
.map_err(ExecuteError::Flush),
237240
RequestType::GetDeviceID => {
238241
let disk_id = disk.image_id();
239242
if (self.data_len as usize) < disk_id.len() {
240243
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
241244
}
242245
mem.write_slice(disk_id, self.data_addr)
243-
.map_err(ExecuteError::Write)?;
246+
.map(|_| VIRTIO_BLK_ID_BYTES)
247+
.map_err(ExecuteError::Write)
244248
}
245-
RequestType::Unsupported(t) => return Err(ExecuteError::Unsupported(t)),
246-
};
247-
Ok(0)
249+
RequestType::Unsupported(t) => Err(ExecuteError::Unsupported(t)),
250+
}
248251
}
249252
}
250253

0 commit comments

Comments
 (0)