Skip to content

Commit 7320231

Browse files
committed
perf(virtqueue): replace vec-based MemPools with bitmap-based IndexAlloc
1 parent b1494dd commit 7320231

File tree

5 files changed

+104
-38
lines changed

5 files changed

+104
-38
lines changed

Cargo.lock

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ tcp = ["net", "smoltcp", "smoltcp/socket-tcp"]
7373
trace = ["smoltcp?/log", "smoltcp?/verbose"]
7474
udp = ["net", "smoltcp", "smoltcp/socket-udp"]
7575
vga = []
76-
virtio = ["dep:virtio"]
76+
virtio = ["dep:virtio", "dep:bitvec"]
7777
virtio-net = ["net", "virtio"]
7878
vsock = ["virtio", "pci"]
7979

@@ -117,6 +117,7 @@ async-lock = { version = "3.4.1", default-features = false }
117117
async-trait = "0.1.89"
118118
bit_field = "0.10"
119119
bitflags = "2"
120+
bitvec = { version = "1", default-features = false, features = ["alloc"], optional = true }
120121
build-time = "0.1.3"
121122
cfg-if = "1"
122123
crossbeam-utils = { version = "0.8", default-features = false }

src/drivers/virtio/virtqueue/mod.rs

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -512,21 +512,44 @@ pub enum BufferType {
512512
Indirect,
513513
}
514514

515-
/// MemPool allows to easily control, request and provide memory for Virtqueues.
516-
struct MemPool {
517-
pool: Vec<u16>,
518-
}
515+
mod index_alloc {
516+
use bitvec::bitbox;
517+
use bitvec::boxed::BitBox;
519518

520-
impl MemPool {
521-
/// Returns a given id to the id pool
522-
fn ret_id(&mut self, id: u16) {
523-
self.pool.push(id);
519+
/// This type allows allocating indices.
520+
///
521+
/// The indices can be used as descriptor IDs.
522+
pub struct IndexAlloc {
523+
bits: BitBox,
524524
}
525525

526-
/// Returns a new instance, with a pool of the specified size.
527-
fn new(size: u16) -> MemPool {
528-
MemPool {
529-
pool: (0..size).collect(),
526+
impl IndexAlloc {
527+
pub fn new(len: usize) -> Self {
528+
let bits = bitbox![0; len];
529+
Self { bits }
530+
}
531+
532+
pub fn allocate(&mut self) -> Option<usize> {
533+
let index = self.bits.last_zero()?;
534+
self.bits.get_mut(index)?.set(true);
535+
Some(index)
536+
}
537+
538+
unsafe fn try_free(&mut self, id: usize) -> Result<(), usize> {
539+
let mut bit_ref = self.bits.get_mut(id).ok_or(id)?;
540+
541+
let was_set = bit_ref.replace(true);
542+
if was_set {
543+
return Err(id);
544+
}
545+
546+
Ok(())
547+
}
548+
549+
pub unsafe fn free(&mut self, id: usize) {
550+
unsafe {
551+
self.try_free(id).unwrap();
552+
}
530553
}
531554
}
532555
}

src/drivers/virtio/virtqueue/packed.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ use super::super::transport::mmio::{ComCfg, NotifCfg, NotifCtrl};
2222
#[cfg(feature = "pci")]
2323
use super::super::transport::pci::{ComCfg, NotifCfg, NotifCtrl};
2424
use super::error::VirtqError;
25-
use super::{
26-
AvailBufferToken, BufferType, MemPool, TransferToken, UsedBufferToken, Virtq, VirtqPrivate,
27-
};
25+
use super::index_alloc::IndexAlloc;
26+
use super::{AvailBufferToken, BufferType, TransferToken, UsedBufferToken, Virtq, VirtqPrivate};
2827
use crate::arch::mm::paging::{BasePageSize, PageSize};
2928
use crate::mm::device_alloc::DeviceAlloc;
3029

@@ -69,9 +68,8 @@ struct DescriptorRing {
6968
/// See Virtio specification v1.1. - 2.7.1
7069
drv_wc: bool,
7170
dev_wc: bool,
72-
/// Memory pool controls the amount of "free floating" descriptors
73-
/// See [MemPool] docs for detail.
74-
mem_pool: MemPool,
71+
/// This allocates available descriptors.
72+
indexes: IndexAlloc,
7573
}
7674

7775
impl DescriptorRing {
@@ -92,7 +90,7 @@ impl DescriptorRing {
9290
poll_index: 0,
9391
drv_wc: true,
9492
dev_wc: true,
95-
mem_pool: MemPool::new(size),
93+
indexes: IndexAlloc::new(size.into()),
9694
}
9795
}
9896

@@ -186,13 +184,13 @@ impl DescriptorRing {
186184
/// Returns an initialized write controller in order
187185
/// to write the queue correctly.
188186
fn get_write_ctrler(&mut self) -> Result<WriteCtrl<'_>, VirtqError> {
189-
let desc_id = self.mem_pool.pool.pop().ok_or(VirtqError::NoDescrAvail)?;
187+
let desc_id = self.indexes.allocate().ok_or(VirtqError::NoDescrAvail)?;
190188
Ok(WriteCtrl {
191189
start: self.write_index,
192190
position: self.write_index,
193191
modulo: u16::try_from(self.ring.len()).unwrap(),
194192
first_flags: DescF::empty(),
195-
buff_id: desc_id,
193+
buff_id: u16::try_from(desc_id).unwrap(),
196194

197195
desc_ring: self,
198196
})
@@ -303,7 +301,9 @@ impl ReadCtrl<'_> {
303301
for _ in 0..tkn.num_consuming_descr() {
304302
self.incrmt();
305303
}
306-
self.desc_ring.mem_pool.ret_id(buff_id);
304+
unsafe {
305+
self.desc_ring.indexes.free(buff_id.into());
306+
}
307307

308308
Some((tkn, write_len))
309309
} else {

src/drivers/virtio/virtqueue/split.rs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,15 @@ use super::super::transport::mmio::{ComCfg, NotifCfg, NotifCtrl};
1717
#[cfg(feature = "pci")]
1818
use super::super::transport::pci::{ComCfg, NotifCfg, NotifCtrl};
1919
use super::error::VirtqError;
20-
use super::{
21-
AvailBufferToken, BufferType, MemPool, TransferToken, UsedBufferToken, Virtq, VirtqPrivate,
22-
};
20+
use super::index_alloc::IndexAlloc;
21+
use super::{AvailBufferToken, BufferType, TransferToken, UsedBufferToken, Virtq, VirtqPrivate};
2322
use crate::arch::memory_barrier;
2423
use crate::mm::device_alloc::DeviceAlloc;
2524

2625
struct DescrRing {
2726
read_idx: u16,
2827
token_ring: Box<[Option<Box<TransferToken<virtq::Desc>>>]>,
29-
mem_pool: MemPool,
28+
indexes: IndexAlloc,
3029

3130
/// Descriptor Tables
3231
///
@@ -58,8 +57,8 @@ impl DescrRing {
5857
if let Some(ctrl_desc) = tkn.ctrl_desc.as_ref() {
5958
let descriptor = SplitVq::indirect_desc(ctrl_desc.as_ref());
6059

61-
index = self.mem_pool.pool.pop().ok_or(VirtqError::NoDescrAvail)?;
62-
self.descr_table_mut()[usize::from(index)] = MaybeUninit::new(descriptor);
60+
index = self.indexes.allocate().ok_or(VirtqError::NoDescrAvail)?;
61+
self.descr_table_mut()[index] = MaybeUninit::new(descriptor);
6362
} else {
6463
let mut rev_all_desc_iter = SplitVq::descriptor_iter(&tkn.buff_tkn)?.rev();
6564

@@ -68,25 +67,26 @@ impl DescrRing {
6867
// If the [AvailBufferToken] is empty, we panic
6968
let descriptor = rev_all_desc_iter.next().unwrap();
7069

71-
index = self.mem_pool.pool.pop().ok_or(VirtqError::NoDescrAvail)?;
72-
self.descr_table_mut()[usize::from(index)] = MaybeUninit::new(descriptor);
70+
index = self.indexes.allocate().ok_or(VirtqError::NoDescrAvail)?;
71+
self.descr_table_mut()[index] = MaybeUninit::new(descriptor);
7372
}
7473
for mut descriptor in rev_all_desc_iter {
7574
// We have not updated `index` yet, so it is at this point the index of the previous descriptor that had been written.
76-
descriptor.next = le16::from(index);
75+
descriptor.next = le16::from_ne(index.try_into().unwrap());
7776

78-
index = self.mem_pool.pool.pop().ok_or(VirtqError::NoDescrAvail)?;
79-
self.descr_table_mut()[usize::from(index)] = MaybeUninit::new(descriptor);
77+
index = self.indexes.allocate().ok_or(VirtqError::NoDescrAvail)?;
78+
self.descr_table_mut()[index] = MaybeUninit::new(descriptor);
8079
}
8180
// At this point, `index` is the index of the last element of the reversed iterator,
8281
// thus the head of the descriptor chain.
8382
}
8483

85-
self.token_ring[usize::from(index)] = Some(Box::new(tkn));
84+
self.token_ring[index] = Some(Box::new(tkn));
8685

8786
let len = self.token_ring.len();
8887
let idx = self.avail_ring_mut().idx.to_ne();
89-
self.avail_ring_mut().ring_mut(true)[idx as usize % len] = index.into();
88+
self.avail_ring_mut().ring_mut(true)[idx as usize % len] =
89+
le16::from_ne(index.try_into().unwrap());
9090

9191
memory_barrier();
9292
let next_idx = idx.wrapping_add(1);
@@ -111,7 +111,9 @@ impl DescrRing {
111111
// We return the indices of the now freed ring slots back to `mem_pool.`
112112
let mut id_ret_idx = u16::try_from(used_elem.id.to_ne()).unwrap();
113113
loop {
114-
self.mem_pool.ret_id(id_ret_idx);
114+
unsafe {
115+
self.indexes.free(id_ret_idx.into());
116+
}
115117
let cur_chain_elem =
116118
unsafe { self.descr_table_mut()[usize::from(id_ret_idx)].assume_init() };
117119
if cur_chain_elem.flags.contains(virtq::DescF::NEXT) {
@@ -295,7 +297,7 @@ impl SplitVq {
295297
.take(size.into())
296298
.collect::<Vec<_>>()
297299
.into_boxed_slice(),
298-
mem_pool: MemPool::new(size),
300+
indexes: IndexAlloc::new(size.into()),
299301

300302
descr_table_cell,
301303
avail_ring_cell,

0 commit comments

Comments
 (0)