Skip to content

Commit 78d301c

Browse files
committed
perf(virtqueue): replace vec-based MemPools with bitmap-based IndexAlloc
1 parent a52430a commit 78d301c

File tree

3 files changed

+78
-37
lines changed

3 files changed

+78
-37
lines changed

src/drivers/virtio/virtqueue/mod.rs

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -512,21 +512,60 @@ 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 usizes = len.div_ceil(USIZE_BITS);
533+
let extra_bits = len % USIZE_BITS;
534+
535+
let mut bits = vec![0; usizes].into_boxed_slice();
536+
537+
if extra_bits != 0 {
538+
*bits.last_mut().unwrap() = usize::MAX >> extra_bits;
539+
}
540+
541+
Self { bits }
542+
}
543+
544+
#[inline]
545+
pub fn allocate(&mut self) -> Option<usize> {
546+
for (word_index, word) in self.bits.iter_mut().enumerate() {
547+
let trailing_ones = word.trailing_ones();
548+
if trailing_ones < usize::BITS {
549+
let mask = 1 << trailing_ones;
550+
*word |= mask;
551+
let index = word_index * USIZE_BITS + usize::try_from(trailing_ones).unwrap();
552+
return Some(index);
553+
}
554+
}
555+
556+
None
557+
}
558+
559+
#[inline]
560+
pub unsafe fn deallocate(&mut self, index: usize) {
561+
let word_index = index / USIZE_BITS;
562+
let bit = index % USIZE_BITS;
563+
let mask = 1 << bit;
564+
565+
debug_assert!(self.bits[word_index] & mask == mask);
566+
unsafe {
567+
*self.bits.get_unchecked_mut(word_index) &= !mask;
568+
}
530569
}
531570
}
532571
}

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
descr_table_cell: Box<UnsafeCell<[MaybeUninit<virtq::Desc>]>, DeviceAlloc>,
3231
avail_ring_cell: Box<UnsafeCell<virtq::Avail>, DeviceAlloc>,
@@ -52,8 +51,8 @@ impl DescrRing {
5251
if let Some(ctrl_desc) = tkn.ctrl_desc.as_ref() {
5352
let descriptor = SplitVq::indirect_desc(ctrl_desc.as_ref());
5453

55-
index = self.mem_pool.pool.pop().ok_or(VirtqError::NoDescrAvail)?;
56-
self.descr_table_mut()[usize::from(index)] = MaybeUninit::new(descriptor);
54+
index = self.indexes.allocate().ok_or(VirtqError::NoDescrAvail)?;
55+
self.descr_table_mut()[index] = MaybeUninit::new(descriptor);
5756
} else {
5857
let mut rev_all_desc_iter = SplitVq::descriptor_iter(&tkn.buff_tkn)?.rev();
5958

@@ -62,25 +61,26 @@ impl DescrRing {
6261
// If the [AvailBufferToken] is empty, we panic
6362
let descriptor = rev_all_desc_iter.next().unwrap();
6463

65-
index = self.mem_pool.pool.pop().ok_or(VirtqError::NoDescrAvail)?;
66-
self.descr_table_mut()[usize::from(index)] = MaybeUninit::new(descriptor);
64+
index = self.indexes.allocate().ok_or(VirtqError::NoDescrAvail)?;
65+
self.descr_table_mut()[index] = MaybeUninit::new(descriptor);
6766
}
6867
for mut descriptor in rev_all_desc_iter {
6968
// We have not updated `index` yet, so it is at this point the index of the previous descriptor that had been written.
70-
descriptor.next = le16::from(index);
69+
descriptor.next = le16::from_ne(index.try_into().unwrap());
7170

72-
index = self.mem_pool.pool.pop().ok_or(VirtqError::NoDescrAvail)?;
73-
self.descr_table_mut()[usize::from(index)] = MaybeUninit::new(descriptor);
71+
index = self.indexes.allocate().ok_or(VirtqError::NoDescrAvail)?;
72+
self.descr_table_mut()[index] = MaybeUninit::new(descriptor);
7473
}
7574
// At this point, `index` is the index of the last element of the reversed iterator,
7675
// thus the head of the descriptor chain.
7776
}
7877

79-
self.token_ring[usize::from(index)] = Some(Box::new(tkn));
78+
self.token_ring[index] = Some(Box::new(tkn));
8079

8180
let len = self.token_ring.len();
8281
let idx = self.avail_ring_mut().idx.to_ne();
83-
self.avail_ring_mut().ring_mut(true)[idx as usize % len] = index.into();
82+
self.avail_ring_mut().ring_mut(true)[idx as usize % len] =
83+
le16::from_ne(index.try_into().unwrap());
8484

8585
memory_barrier();
8686
let next_idx = idx.wrapping_add(1);
@@ -105,7 +105,9 @@ impl DescrRing {
105105
// We return the indices of the now freed ring slots back to `mem_pool.`
106106
let mut id_ret_idx = u16::try_from(used_elem.id.to_ne()).unwrap();
107107
loop {
108-
self.mem_pool.ret_id(id_ret_idx);
108+
unsafe {
109+
self.indexes.deallocate(id_ret_idx.into());
110+
}
109111
let cur_chain_elem =
110112
unsafe { self.descr_table_mut()[usize::from(id_ret_idx)].assume_init() };
111113
if cur_chain_elem.flags.contains(virtq::DescF::NEXT) {
@@ -289,7 +291,7 @@ impl SplitVq {
289291
.take(size.into())
290292
.collect::<Vec<_>>()
291293
.into_boxed_slice(),
292-
mem_pool: MemPool::new(size),
294+
indexes: IndexAlloc::new(size.into()),
293295

294296
descr_table_cell,
295297
avail_ring_cell,

0 commit comments

Comments
 (0)