Skip to content

Commit d21c61d

Browse files
committed
Allocate virtio-block read buffers on the fly
1 parent 44638e6 commit d21c61d

File tree

2 files changed

+30
-39
lines changed

2 files changed

+30
-39
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,11 @@ make test
9393

9494
- VirtIO improvements:
9595
- Abstract and improve request/response queue from rng
96+
- Don't leak memory on purpose in virtio-blk device
9697
- Ensure we zero out descriptors after using them so we don't accidentally reuse the buffer
9798
- Improve memory "ownership" ergonomics, ensuring buffers are dropped after use. Maybe abstract into a common layer somehow?
9899
- Create a physically contiguous heap, or slab allocator, or something for virtio buffer requests so we don't waste an entire page per tiny allocation.
100+
- Ensure we are still satisfying any alignment requirements for buffers. Read the spec!
99101
- Locking: we need to lock writes (I think?), but we should be able to read from the queue without locking. This should be ergonomic. I don't necessarily want to bury a mutex deep in the code.
100102
- Investigate how Linux or other OS virtio drivers do locking
101103
- Ensure we don't accidentally reuse descriptors while we are waiting for a response from the device. Don't automatically just wrap around! This is what might require a mutex rather than just atomic integers?

kernel/src/virtio/block.rs

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
use alloc::vec::Vec;
22
use bitflags::bitflags;
3-
use core::{mem, ptr};
3+
use core::mem;
44
use spin::RwLock;
5-
use x86_64::{PhysAddr, VirtAddr};
5+
use x86_64::PhysAddr;
66

77
use crate::interrupts::InterruptHandlerID;
88
use crate::memory::PhysicalBuffer;
99
use crate::registers::RegisterRO;
10-
use crate::{memory, register_struct, serial_println, strings};
10+
use crate::{register_struct, serial_println, strings};
1111

1212
use super::device::VirtIOInitializedDevice;
1313
use super::queue::{ChainedVirtQueueDescriptorElem, VirtQueueDescriptorFlags, VirtQueueIndex};
1414
use super::VirtIODeviceConfig;
1515

16-
// TODO: Support multiple block devices
1716
static VIRTIO_BLOCK: RwLock<Vec<VirtIOBlockDevice>> = RwLock::new(Vec::new());
1817

1918
pub(crate) fn try_init_virtio_block(device_config: VirtIODeviceConfig) {
@@ -47,8 +46,6 @@ pub(crate) fn virtio_block_print_devices() {
4746
serial_println!("virtio block devices: {:#x?}", devices);
4847
}
4948

50-
static READ_BUFFER: [u8; 512] = [0; 512];
51-
5249
pub(crate) fn virtio_block_get_id(device_index: usize) {
5350
let device_lock = VIRTIO_BLOCK.read();
5451
let device = device_lock.get(device_index).expect("invalid device index");
@@ -78,18 +75,16 @@ pub(crate) fn virtio_block_read(device_index: usize, sector: u64) {
7875
.initialized_device
7976
.get_virtqueue(VirtQueueIndex(0))
8077
.unwrap();
81-
let buffer_virt_addr = VirtAddr::new(ptr::addr_of!(READ_BUFFER) as u64);
82-
let buffer_phys_addr = memory::translate_addr(buffer_virt_addr)
83-
.expect("failed to get VirtIO device ID buffer physical address");
84-
let buffer_size = core::mem::size_of_val(&READ_BUFFER) as u32;
85-
86-
let request = BlockRequest::Read {
87-
sector,
88-
data_addr: buffer_phys_addr,
89-
data_len: buffer_size,
90-
};
78+
79+
let data_addr = PhysicalBuffer::allocate_zeroed(BlockRequest::READ_DATA_LEN as usize)
80+
.expect("failed to allocate GetID buffer")
81+
// TODO: Don't leak here
82+
.leak();
83+
84+
let request = BlockRequest::Read { sector, data_addr };
9185
let raw_request = request.to_raw();
92-
virtq.add_buffer(&raw_request.to_descriptor_chain());
86+
let desc_chain = raw_request.to_descriptor_chain();
87+
virtq.add_buffer(&desc_chain);
9388
virtq.notify_device();
9489
}
9590

@@ -115,10 +110,12 @@ fn virtio_block_interrupt(_vector: u8, handler_id: InterruptHandlerID) {
115110
BlockRequest::Read {
116111
sector: _,
117112
data_addr,
118-
data_len,
119113
} => {
120114
let buffer = unsafe {
121-
core::slice::from_raw_parts(data_addr.as_u64() as *const u8, data_len as usize)
115+
core::slice::from_raw_parts(
116+
data_addr.as_u64() as *const u8,
117+
BlockRequest::READ_DATA_LEN as usize,
118+
)
122119
};
123120
serial_println!("Read response: {:x?}", buffer);
124121

@@ -264,33 +261,26 @@ struct BlockConfigTopology {
264261

265262
#[derive(Debug)]
266263
enum BlockRequest {
267-
Read {
268-
sector: u64,
269-
data_addr: PhysAddr,
270-
data_len: u32,
271-
},
272-
GetID {
273-
data_addr: PhysAddr,
274-
},
264+
Read { sector: u64, data_addr: PhysAddr },
265+
GetID { data_addr: PhysAddr },
275266
}
276267

277268
impl BlockRequest {
278269
/// All GET_ID requests MUST have a length of 20 bytes.
279270
const GET_ID_DATA_LEN: u32 = 20;
280271

272+
/// All read requests MUST be a multiple of 512 bytes. We just use 512 for
273+
/// now.
274+
const READ_DATA_LEN: u32 = 512;
275+
281276
fn to_raw(&self) -> RawBlockRequest {
282277
match self {
283-
Self::Read {
284-
sector,
285-
data_addr,
286-
data_len,
287-
} => {
288-
assert!(
289-
*data_len % 512 == 0,
290-
"Data length for read requests must be a multiple of 512"
291-
);
292-
RawBlockRequest::new(BlockRequestType::In, *sector, *data_addr, *data_len)
293-
}
278+
Self::Read { sector, data_addr } => RawBlockRequest::new(
279+
BlockRequestType::In,
280+
*sector,
281+
*data_addr,
282+
Self::READ_DATA_LEN,
283+
),
294284
Self::GetID { data_addr } => RawBlockRequest::new(
295285
BlockRequestType::GetID,
296286
0,
@@ -305,7 +295,6 @@ impl BlockRequest {
305295
BlockRequestType::In => Self::Read {
306296
sector: raw.sector,
307297
data_addr: raw.data_addr,
308-
data_len: raw.data_len,
309298
},
310299
BlockRequestType::GetID => Self::GetID {
311300
data_addr: raw.data_addr,

0 commit comments

Comments
 (0)