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 => { aster_block::bio::BioEnqueueError::Refused => {
Error::with_message(Errno::EBUSY, "Refuse to enqueue the bio") 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); bio.complete(BioStatus::Complete);
Ok(()) Ok(())
} }
fn max_nr_segments_per_bio(&self) -> usize {
usize::MAX
}
} }
/// Exfat disk image /// Exfat disk image
static EXFAT_IMAGE: &[u8] = include_bytes!("../../../../../regression/build/exfat.img"); static EXFAT_IMAGE: &[u8] = include_bytes!("../../../../../regression/build/exfat.img");

View File

@ -136,6 +136,8 @@ pub enum BioEnqueueError {
IsFull, IsFull,
/// Refuse to enqueue the bio /// Refuse to enqueue the bio
Refused, Refused,
/// Too big bio
TooBig,
} }
impl From<BioEnqueueError> for aster_frame::Error { 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 { pub trait BlockDevice: Send + Sync + Any + Debug {
/// Enqueues a new `SubmittedBio` to the block device. /// Enqueues a new `SubmittedBio` to the block device.
fn enqueue(&self, bio: SubmittedBio) -> Result<(), BioEnqueueError>; 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 { impl dyn BlockDevice {

View File

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

View File

@ -43,7 +43,11 @@ impl BlockDevice {
let block_device = Arc::new(Self { let block_device = Arc::new(Self {
device, 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); 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> { fn enqueue(&self, bio: SubmittedBio) -> Result<(), BioEnqueueError> {
self.queue.enqueue(bio) self.queue.enqueue(bio)
} }
fn max_nr_segments_per_bio(&self) -> usize {
self.queue.max_nr_segments_per_bio()
}
} }
#[derive(Debug)] #[derive(Debug)]