Limit the number of segments in single bio request

This commit is contained in:
LI Qing
2024-06-04 16:26:36 +08:00
committed by Tate, Hongliang Tian
parent a883b65187
commit c6aa9f9ee8
6 changed files with 51 additions and 2 deletions

View File

@ -212,6 +212,9 @@ impl From<aster_block::bio::BioEnqueueError> for Error {
aster_block::bio::BioEnqueueError::Refused => {
Error::with_message(Errno::EBUSY, "Refuse to enqueue the bio")
}
aster_block::bio::BioEnqueueError::TooBig => {
Error::with_message(Errno::EINVAL, "Bio is too big")
}
}
}
}

View File

@ -95,6 +95,10 @@ mod test {
bio.complete(BioStatus::Complete);
Ok(())
}
fn max_nr_segments_per_bio(&self) -> usize {
usize::MAX
}
}
/// Exfat disk image
static EXFAT_IMAGE: &[u8] = include_bytes!("../../../../../regression/build/exfat.img");

View File

@ -136,6 +136,8 @@ pub enum BioEnqueueError {
IsFull,
/// Refuse to enqueue the bio
Refused,
/// Too big bio
TooBig,
}
impl From<BioEnqueueError> for aster_frame::Error {

View File

@ -55,6 +55,8 @@ pub const SECTOR_SIZE: usize = 512;
pub trait BlockDevice: Send + Sync + Any + Debug {
/// Enqueues a new `SubmittedBio` to the block device.
fn enqueue(&self, bio: SubmittedBio) -> Result<(), BioEnqueueError>;
/// Returns the upper limit for the number of segments per bio.
fn max_nr_segments_per_bio(&self) -> usize;
}
impl dyn BlockDevice {

View File

@ -20,18 +20,30 @@ pub struct BioRequestSingleQueue {
queue: Mutex<VecDeque<BioRequest>>,
num_requests: AtomicUsize,
wait_queue: WaitQueue,
max_nr_segments_per_bio: usize,
}
impl BioRequestSingleQueue {
/// Creates an empty queue.
pub fn new() -> Self {
Self::with_max_nr_segments_per_bio(usize::MAX)
}
/// Creates an empty queue with the upper bound for the number of segments in a bio.
pub fn with_max_nr_segments_per_bio(max_nr_segments_per_bio: usize) -> Self {
Self {
queue: Mutex::new(VecDeque::new()),
num_requests: AtomicUsize::new(0),
wait_queue: WaitQueue::new(),
max_nr_segments_per_bio,
}
}
/// Returns the upper limit for the number of segments per bio.
pub fn max_nr_segments_per_bio(&self) -> usize {
self.max_nr_segments_per_bio
}
/// Returns the number of requests currently in this queue.
pub fn num_requests(&self) -> usize {
self.num_requests.load(Ordering::Relaxed)
@ -45,9 +57,15 @@ impl BioRequestSingleQueue {
///
/// This method will wake up the waiter if a new `BioRequest` is enqueued.
pub fn enqueue(&self, bio: SubmittedBio) -> Result<(), BioEnqueueError> {
if bio.segments().len() >= self.max_nr_segments_per_bio {
return Err(BioEnqueueError::TooBig);
}
let mut queue = self.queue.lock();
if let Some(request) = queue.front_mut() {
if request.can_merge(&bio) {
if request.can_merge(&bio)
&& request.num_segments() + bio.segments().len() <= self.max_nr_segments_per_bio
{
request.merge_bio(bio);
return Ok(());
}
@ -123,6 +141,8 @@ pub struct BioRequest {
type_: BioType,
/// The range of target sectors on the device
sid_range: Range<Sid>,
/// The number of segments
num_segments: usize,
/// The submitted bios
bios: VecDeque<SubmittedBio>,
}
@ -143,6 +163,11 @@ impl BioRequest {
self.bios.iter()
}
/// Returns the number of segments.
pub fn num_segments(&self) -> usize {
self.num_segments
}
/// Returns `true` if can merge the `SubmittedBio`, `false` otherwise.
pub fn can_merge(&self, rq_bio: &SubmittedBio) -> bool {
if rq_bio.type_() != self.type_ {
@ -163,6 +188,8 @@ impl BioRequest {
pub fn merge_bio(&mut self, rq_bio: SubmittedBio) {
assert!(self.can_merge(&rq_bio));
let rq_bio_nr_segments = rq_bio.segments().len();
if rq_bio.sid_range().start == self.sid_range.end {
self.sid_range.end = rq_bio.sid_range().end;
self.bios.push_back(rq_bio);
@ -170,6 +197,8 @@ impl BioRequest {
self.sid_range.start = rq_bio.sid_range().start;
self.bios.push_front(rq_bio);
}
self.num_segments += rq_bio_nr_segments;
}
}
@ -178,6 +207,7 @@ impl From<SubmittedBio> for BioRequest {
Self {
type_: bio.type_(),
sid_range: bio.sid_range().clone(),
num_segments: bio.segments().len(),
bios: {
let mut bios = VecDeque::with_capacity(1);
bios.push_front(bio);

View File

@ -43,7 +43,11 @@ impl BlockDevice {
let block_device = Arc::new(Self {
device,
queue: BioRequestSingleQueue::new(),
// Each bio request includes an additional 1 request and 1 response descriptor,
// therefore this upper bound is set to (QUEUE_SIZE - 2).
queue: BioRequestSingleQueue::with_max_nr_segments_per_bio(
(DeviceInner::QUEUE_SIZE - 2) as usize,
),
});
aster_block::register_device(device_id, block_device);
@ -74,6 +78,10 @@ impl aster_block::BlockDevice for BlockDevice {
fn enqueue(&self, bio: SubmittedBio) -> Result<(), BioEnqueueError> {
self.queue.enqueue(bio)
}
fn max_nr_segments_per_bio(&self) -> usize {
self.queue.max_nr_segments_per_bio()
}
}
#[derive(Debug)]