diff --git a/kernel/aster-nix/src/error.rs b/kernel/aster-nix/src/error.rs index 4dce8654b..072bee2be 100644 --- a/kernel/aster-nix/src/error.rs +++ b/kernel/aster-nix/src/error.rs @@ -212,6 +212,9 @@ impl From 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") + } } } } diff --git a/kernel/aster-nix/src/fs/exfat/mod.rs b/kernel/aster-nix/src/fs/exfat/mod.rs index 74b6ecf49..43b1ce1d3 100644 --- a/kernel/aster-nix/src/fs/exfat/mod.rs +++ b/kernel/aster-nix/src/fs/exfat/mod.rs @@ -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"); diff --git a/kernel/comps/block/src/bio.rs b/kernel/comps/block/src/bio.rs index f8ac61478..3a477ce56 100644 --- a/kernel/comps/block/src/bio.rs +++ b/kernel/comps/block/src/bio.rs @@ -136,6 +136,8 @@ pub enum BioEnqueueError { IsFull, /// Refuse to enqueue the bio Refused, + /// Too big bio + TooBig, } impl From for aster_frame::Error { diff --git a/kernel/comps/block/src/lib.rs b/kernel/comps/block/src/lib.rs index 4b4c6a937..78d4a6b73 100644 --- a/kernel/comps/block/src/lib.rs +++ b/kernel/comps/block/src/lib.rs @@ -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 { diff --git a/kernel/comps/block/src/request_queue.rs b/kernel/comps/block/src/request_queue.rs index 11c3edac4..53b9e7755 100644 --- a/kernel/comps/block/src/request_queue.rs +++ b/kernel/comps/block/src/request_queue.rs @@ -20,18 +20,30 @@ pub struct BioRequestSingleQueue { queue: Mutex>, 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, + /// The number of segments + num_segments: usize, /// The submitted bios bios: VecDeque, } @@ -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 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); diff --git a/kernel/comps/virtio/src/device/block/device.rs b/kernel/comps/virtio/src/device/block/device.rs index 732cc6bb9..0b15b09eb 100644 --- a/kernel/comps/virtio/src/device/block/device.rs +++ b/kernel/comps/virtio/src/device/block/device.rs @@ -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)]