On 2025-10-09 at 03:41 +1100, Danilo Krummrich <d...@kernel.org> wrote... > On Wed Oct 8, 2025 at 2:12 AM CEST, Alistair Popple wrote: > > diff --git a/drivers/gpu/nova-core/sbuffer.rs > > b/drivers/gpu/nova-core/sbuffer.rs > > new file mode 100644 > > index 000000000000..e82f9d97ad21 > > --- /dev/null > > +++ b/drivers/gpu/nova-core/sbuffer.rs > > @@ -0,0 +1,191 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > + > > +use core::ops::Deref; > > + > > +use kernel::alloc::KVec; > > +use kernel::error::code::*; > > +use kernel::prelude::*; > > + > > +/// A buffer abstraction for discontiguous byte slices. > > +/// > > +/// This allows you to treat multiple non-contiguous `&mut [u8]` slices > > +/// as a single stream-like read/write buffer. > > +/// > > +/// Example: > > +/// > > +/// let mut buf1 = [0u8; 3]; > > +/// let mut buf2 = [0u8; 5]; > > +/// let mut sbuffer = SWriteBuffer::new([&buf1, &buf2]); > > +/// > > +/// let data = b"hellowo"; > > Not that it matters, but "hellowo"? :)
Ugh. I fixed that in the commit message but not here! > > +/// let result = sbuffer.write_all(0, data); > > +/// > > +/// A sliding window of slices to proceed. > > +/// > > +/// Both read and write buffers are implemented in terms of operating on > > slices of a requested > > +/// size. This base class implements logic that can be shared between the > > two to support that. > > +/// > > +/// `S` is a slice type, `I` is an iterator yielding `S`. > > +pub(crate) struct SBuffer<I: Iterator> { > > + /// `Some` if we are not at the end of the data yet. > > + cur_slice: Option<I::Item>, > > + /// All the slices remaining after `cur_slice`. > > + slices: I, > > +} > > Does it make sense to split SBuffer into itself and a separate SBufferIter > that > keeps a reference to the SBuffer? If not, I'd rename it to SBufferIter to make > it obvious to the user that it is an iterator type. We had that originally before this was sent to the list for review but there wasn't a use for it so will rename it. If memory serves me Alex removed the separate SBuffer so maybe he can comment further why we removed it. > > +impl<'a, I> SBuffer<I> > > +where > > + I: Iterator, > > +{ > > + #[expect(unused)] > > + pub(crate) fn new_reader(slices: impl IntoIterator<IntoIter = I>) -> > > Self > > + where > > + I: Iterator<Item = &'a [u8]>, > > + { > > + Self::new(slices) > > + } > > + > > + #[expect(unused)] > > + pub(crate) fn new_writer(slices: impl IntoIterator<IntoIter = I>) -> > > Self > > + where > > + I: Iterator<Item = &'a mut [u8]>, > > + { > > + Self::new(slices) > > + } > > Please add some documentation for the constructors. Ok. > > + fn new(slices: impl IntoIterator<IntoIter = I>) -> Self > > + where > > + I::Item: Deref<Target = [u8]>, > > + { > > + let mut slices = slices.into_iter(); > > + > > + Self { > > + // Skip empty slices to avoid trouble down the road. > > + cur_slice: slices.find(|s| !s.deref().is_empty()), > > + slices, > > + } > > + } > > + > > + fn get_slice_internal( > > + &mut self, > > + len: usize, > > + mut f: impl FnMut(I::Item, usize) -> (I::Item, I::Item), > > + ) -> Option<I::Item> > > + where > > + I::Item: Deref<Target = [u8]>, > > + { > > + match self.cur_slice.take() { > > + None => None, > > + Some(cur_slice) => { > > + if len >= cur_slice.len() { > > + // Caller requested more data than is in the current > > slice, return it entirely > > + // and prepare the following slice for being used. > > Skip empty slices to avoid > > + // trouble. > > + self.cur_slice = self.slices.find(|s| !s.is_empty()); > > + > > + Some(cur_slice) > > + } else { > > + // The current slice can satisfy the request, split it > > and return a slice of > > + // the requested size. > > + let (ret, next) = f(cur_slice, len); > > + self.cur_slice = Some(next); > > + > > + Some(ret) > > + } > > + } > > + } > > + } > > +} > > + > > +/// Provides a way to get non-mutable slices of data to read from. > > +impl<'a, I> SBuffer<I> > > +where > > + I: Iterator<Item = &'a [u8]>, > > +{ > > + /// Returns a slice of at most `len` bytes, or `None` if we are at the > > end of the data. > > + /// > > + /// If a slice shorter than `len` bytes has been returned, the caller > > can call this method > > + /// again until it returns `None` to try and obtain the remainder of > > the data. > > + fn get_slice(&mut self, len: usize) -> Option<&'a [u8]> { > > + self.get_slice_internal(len, |s, pos| s.split_at(pos)) > > + } > > + > > + /// Ideally we would implement `Read`, but it is not available in > > `core`. > > + /// So mimic `std::io::Read::read_exact`. > > + #[expect(unused)] > > + pub(crate) fn read_exact(&mut self, mut dst: &mut [u8]) -> Result { > > + while !dst.is_empty() { > > + match self.get_slice(dst.len()) { > > + None => return Err(ETOOSMALL), > > ETOOSMALL is an NFS error code (it should also never be exposed to > userspace). I > suggest to implement a custom error type instead and make it resolve to ENOSPC > or probably just EINVAL instead. Will do. > > + Some(src) => { > > + let dst_slice; > > + (dst_slice, dst) = dst.split_at_mut(src.len()); > > + dst_slice.copy_from_slice(src); > > + } > > + } > > + } > > + > > + Ok(()) > > + } > > + > > + /// Read all the remaining data into a `KVec`. > > + /// > > + /// `self` will be empty after this operation. > > + #[expect(unused)] > > + pub(crate) fn read_into_kvec(&mut self, flags: kernel::alloc::Flags) > > -> Result<KVec<u8>> { > > + let mut buf = KVec::<u8>::new(); > > + > > + if let Some(slice) = core::mem::take(&mut self.cur_slice) { > > + buf.extend_from_slice(slice, flags)?; > > + } > > + for slice in &mut self.slices { > > + buf.extend_from_slice(slice, flags)?; > > + } > > + > > + Ok(buf) > > + } > > +} > > + > > +/// Provides a way to get mutable slices of data to write into. > > +impl<'a, I> SBuffer<I> > > +where > > + I: Iterator<Item = &'a mut [u8]>, > > +{ > > + /// Returns a mutable slice of at most `len` bytes, or `None` if we > > are at the end of the data. > > + /// > > + /// If a slice shorter than `len` bytes has been returned, the caller > > can call this method > > + /// again until it returns `None` to try and obtain the remainder of > > the data. > > + fn get_slice_mut(&mut self, len: usize) -> Option<&'a mut [u8]> { > > + self.get_slice_internal(len, |s, pos| s.split_at_mut(pos)) > > + } > > + > > + /// Ideally we would implement `Write`, but it is not available in > > `core`. > > + /// So mimic `std::io::Write::write_all`. > > + #[expect(unused)] > > + pub(crate) fn write_all(&mut self, mut src: &[u8]) -> Result { > > + while !src.is_empty() { > > + match self.get_slice_mut(src.len()) { > > + None => return Err(ETOOSMALL), > > + Some(dst) => { > > + let src_slice; > > + (src_slice, src) = src.split_at(dst.len()); > > + dst.copy_from_slice(src_slice); > > + } > > + } > > + } > > + > > + Ok(()) > > + } > > +} > > + > > +impl<'a, I> Iterator for SBuffer<I> > > +where > > + I: Iterator<Item = &'a [u8]>, > > +{ > > + type Item = u8; > > + > > + fn next(&mut self) -> Option<Self::Item> { > > + // Returned slices are guaranteed to not be empty so we can safely > > index the first entry. > > + self.get_slice(1).map(|s| s[0]) > > + } > > +} > > -- > > 2.50.1 >