Skip to content

Commit f8f8186

Browse files
committed
fix(virtio): use full IovDeque capacity
The `L` const generic was determining the maximum number of `iov` elements in the `IovDeque`. This cases the issue when the host kernel uses pages which can contain more entries than `L`. For example usual 4K pages can contain 256 `iov`s while 16K pages can contain 1024 `iov`s. Current implementation on 16K (and any other bigger than 4K page size) will continue wrap `IovDeque` when it reaches 256'th element. This breaks the implementation since elements written past 256'th index will not be 'duplicated' at the beginning of the queue. Curren implementation expects this behavior: page 1 page 2 |ABCD|#|ABCD| ^ will wrap here With big page sizes current impl will: page 1 page2 |ABCD|EFGD________|#|ABCDEFGD________| ^ sill wrap here ^ but should wrap here The solution is to calculate the maximum capacity the `IovDeque` can hold, and use it for wrapping purposes. This capacity is allowed to be bigger than `L`. The actual used number of entries in the queue will still be guarded by the `L` parameter used in the `is_full` method. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 184dc54 commit f8f8186

File tree

1 file changed

+10
-3
lines changed

1 file changed

+10
-3
lines changed

src/vmm/src/devices/virtio/iov_deque.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ pub enum IovDequeError {
6969
// Like that, the elements stored in the buffer are always laid out in contiguous virtual memory,
7070
// so making a slice out of them does not require any copies.
7171
//
72-
// The `L` const generic determines the maximum number of `iovec` elements the queue should hold.
72+
// The `L` const generic determines the maximum number of `iovec` elements the queue should hold
73+
// at any point in time. The actual capacity of the queue may differ and will depend on the host
74+
// page size.
7375
//
7476
// ```Rust
7577
// pub struct iovec {
@@ -83,6 +85,7 @@ pub struct IovDeque<const L: u16> {
8385
pub iov: *mut libc::iovec,
8486
pub start: u16,
8587
pub len: u16,
88+
pub capacity: u16,
8689
}
8790

8891
// SAFETY: This is `Send`. We hold sole ownership of the underlying buffer.
@@ -158,6 +161,9 @@ impl<const L: u16> IovDeque<L> {
158161
/// Create a new [`IovDeque`] that can hold memory described by a single VirtIO queue.
159162
pub fn new() -> Result<Self, IovDequeError> {
160163
let pages_bytes = Self::pages_bytes();
164+
let capacity = pages_bytes / std::mem::size_of::<iovec>();
165+
let capacity: u16 = capacity.try_into().unwrap();
166+
assert!(L <= capacity, "Actual capacity {} is smaller than requested capacity {}", capacity, L);
161167

162168
let memfd = Self::create_memfd(pages_bytes)?;
163169
let raw_memfd = memfd.as_file().as_raw_fd();
@@ -201,6 +207,7 @@ impl<const L: u16> IovDeque<L> {
201207
iov: buffer.cast(),
202208
start: 0,
203209
len: 0,
210+
capacity,
204211
})
205212
}
206213

@@ -258,8 +265,8 @@ impl<const L: u16> IovDeque<L> {
258265

259266
self.start += nr_iovecs;
260267
self.len -= nr_iovecs;
261-
if self.start >= L {
262-
self.start -= L;
268+
if self.capacity <= self.start {
269+
self.start -= self.capacity;
263270
}
264271
}
265272

0 commit comments

Comments
 (0)