Skip to content

Commit e3958f3

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

File tree

3 files changed

+79
-37
lines changed

3 files changed

+79
-37
lines changed

src/drivers/virtio/virtqueue/mod.rs

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -512,21 +512,61 @@ 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 alloc::boxed::Box;
517+
518+
use align_address::Align;
519519

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);
520+
/// This type allows allocating indices.
521+
///
522+
/// The indices can be used as descriptor IDs.
523+
pub struct IndexAlloc {
524+
/// Zero bits are available.
525+
bits: Box<[usize]>,
524526
}
525527

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(),
528+
const USIZE_BITS: usize = usize::BITS as usize;
529+
530+
impl IndexAlloc {
531+
pub fn new(len: usize) -> Self {
532+
let len = len.align_up(USIZE_BITS) / USIZE_BITS;
533+
let mut bits = vec![0; len].into_boxed_slice();
534+
if !len.is_multiple_of(USIZE_BITS) {
535+
*bits.last_mut().unwrap() |= usize::MAX >> (len % USIZE_BITS);
536+
}
537+
Self { bits }
538+
}
539+
540+
#[inline]
541+
pub fn allocate(&mut self) -> Option<usize> {
542+
for (word_index, word) in self.bits.iter_mut().enumerate() {
543+
let leading_ones = word.leading_ones();
544+
if leading_ones < usize::BITS {
545+
let mask = 1 << leading_ones;
546+
*word |= mask;
547+
let index = word_index * USIZE_BITS + usize::try_from(leading_ones).unwrap();
548+
return Some(index);
549+
}
550+
}
551+
552+
None
553+
}
554+
555+
#[inline]
556+
pub unsafe fn deallocate(&mut self, index: usize) {
557+
let word_index = index / 64;
558+
let bit = index % 64;
559+
let mask = 1 << bit;
560+
561+
if cfg!(debug_assertions) {
562+
let old = self.bits[word_index];
563+
assert!(old & mask == mask);
564+
self.bits[word_index] = old ^ mask;
565+
} else {
566+
unsafe {
567+
*self.bits.get_unchecked_mut(word_index) &= !mask;
568+
}
569+
}
530570
}
531571
}
532572
}

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.deallocate(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.deallocate(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)