Skip to content

Commit e73562c

Browse files
xyn-ackjannau
authored andcommitted
rust: dma: add as_slice/write functions for CoherentAllocation
Add unsafe accessors for the region for reading or writing large blocks of data. Signed-off-by: Abdiel Janulgue <abdiel.janulgue@gmail.com>
1 parent a555d92 commit e73562c

1 file changed

Lines changed: 95 additions & 8 deletions

File tree

rust/kernel/dma.rs

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,9 @@ pub trait Device: AsRef<device::Device> {
5757
/// # Examples
5858
///
5959
/// ```
60-
/// use kernel::device::Device;
61-
/// use kernel::dma::{attrs::*, CoherentAllocation};
60+
/// use kernel::dma::{attrs::*, Device, CoherentAllocation};
6261
///
63-
/// # fn test(dev: &Device) -> Result {
62+
/// # fn test(dev: &dyn Device) -> Result {
6463
/// let attribs = DMA_ATTR_FORCE_CONTIGUOUS | DMA_ATTR_NO_WARN;
6564
/// let c: CoherentAllocation<u64> =
6665
/// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, attribs)?;
@@ -178,16 +177,15 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
178177
/// # Examples
179178
///
180179
/// ```
181-
/// use kernel::device::Device;
182-
/// use kernel::dma::{attrs::*, CoherentAllocation};
180+
/// use kernel::dma::{attrs::*, Device, CoherentAllocation};
183181
///
184-
/// # fn test(dev: &Device) -> Result {
182+
/// # fn test(dev: &dyn Device) -> Result {
185183
/// let c: CoherentAllocation<u64> =
186184
/// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, DMA_ATTR_NO_WARN)?;
187185
/// # Ok::<(), Error>(()) }
188186
/// ```
189187
pub fn alloc_attrs(
190-
dev: &device::Device,
188+
dev: &dyn Device,
191189
count: usize,
192190
gfp_flags: kernel::alloc::Flags,
193191
dma_attrs: Attrs,
@@ -197,6 +195,8 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
197195
"It doesn't make sense for the allocated type to be a ZST"
198196
);
199197

198+
let dev = dev.as_ref();
199+
200200
let size = count
201201
.checked_mul(core::mem::size_of::<T>())
202202
.ok_or(EOVERFLOW)?;
@@ -229,7 +229,7 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
229229
/// Performs the same functionality as [`CoherentAllocation::alloc_attrs`], except the
230230
/// `dma_attrs` is 0 by default.
231231
pub fn alloc_coherent(
232-
dev: &device::Device,
232+
dev: &dyn Device,
233233
count: usize,
234234
gfp_flags: kernel::alloc::Flags,
235235
) -> Result<CoherentAllocation<T>> {
@@ -253,6 +253,93 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
253253
self.dma_handle
254254
}
255255

256+
/// Returns the data from the region starting from `offset` as a slice.
257+
/// `offset` and `count` are in units of `T`, not the number of bytes.
258+
///
259+
/// Due to the safety requirements of slice, the caller should consider that the region could
260+
/// be modified by the device at anytime. For ringbuffer type of r/w access or use-cases where
261+
/// the pointer to the live data is needed, `start_ptr()` or `start_ptr_mut()` could be
262+
/// used instead.
263+
///
264+
/// # Safety
265+
///
266+
/// * Callers must ensure that no hardware operations that involve the buffer are currently
267+
/// taking place while the returned slice is live.
268+
/// * Callers must ensure that this call does not race with a write to the same region while
269+
/// while the returned slice is live.
270+
pub unsafe fn as_slice(&self, offset: usize, count: usize) -> Result<&[T]> {
271+
let end = offset.checked_add(count).ok_or(EOVERFLOW)?;
272+
if end >= self.count {
273+
return Err(EINVAL);
274+
}
275+
// SAFETY:
276+
// - The pointer is valid due to type invariant on `CoherentAllocation`,
277+
// we've just checked that the range and index is within bounds. The immutability of the
278+
// of data is also guaranteed by the safety requirements of the function.
279+
// - `offset` can't overflow since it is smaller than `self.count` and we've checked
280+
// that `self.count` won't overflow early in the constructor.
281+
Ok(unsafe { core::slice::from_raw_parts(self.cpu_addr.add(offset), count) })
282+
}
283+
284+
/// Performs the same functionality as [`CoherentAllocation::as_slice`], except that a mutable
285+
/// slice is returned.
286+
///
287+
/// # Safety
288+
///
289+
/// * Callers must ensure that no hardware operations that involve the buffer are currently
290+
/// taking place while the returned slice is live.
291+
/// * Callers must ensure that this call does not race with a read or write to the same region
292+
/// while the returned slice is live.
293+
pub unsafe fn as_slice_mut(&self, offset: usize, count: usize) -> Result<&mut [T]> {
294+
let end = offset.checked_add(count).ok_or(EOVERFLOW)?;
295+
if end >= self.count {
296+
return Err(EINVAL);
297+
}
298+
// SAFETY:
299+
// - The pointer is valid due to type invariant on `CoherentAllocation`,
300+
// we've just checked that the range and index is within bounds. The immutability of the
301+
// of data is also guaranteed by the safety requirements of the function.
302+
// - `offset` can't overflow since it is smaller than `self.count` and we've checked
303+
// that `self.count` won't overflow early in the constructor.
304+
Ok(unsafe { core::slice::from_raw_parts_mut(self.cpu_addr.add(offset), count) })
305+
}
306+
307+
/// Writes data to the region starting from `offset`. `offset` is in units of `T`, not the
308+
/// number of bytes.
309+
///
310+
/// # Safety
311+
///
312+
/// * Callers must ensure that no hardware operations that involve the buffer overlaps with
313+
/// this write.
314+
/// * Callers must ensure that this call does not race with a read or write to the same region
315+
/// that overlaps with this write.
316+
///
317+
/// # Examples
318+
///
319+
/// ```
320+
/// # fn test(alloc: &mut kernel::dma::CoherentAllocation<u8>) -> Result {
321+
/// let somedata: [u8; 4] = [0xf; 4];
322+
/// let buf: &[u8] = &somedata;
323+
/// // SAFETY: No hw operation on the device and no other r/w access to the region at this point.
324+
/// unsafe { alloc.write(buf, 0)?; }
325+
/// # Ok::<(), Error>(()) }
326+
/// ```
327+
pub unsafe fn write(&self, src: &[T], offset: usize) -> Result {
328+
let end = offset.checked_add(src.len()).ok_or(EOVERFLOW)?;
329+
if end >= self.count {
330+
return Err(EINVAL);
331+
}
332+
// SAFETY:
333+
// - The pointer is valid due to type invariant on `CoherentAllocation`
334+
// and we've just checked that the range and index is within bounds.
335+
// - `offset` can't overflow since it is smaller than `self.count` and we've checked
336+
// that `self.count` won't overflow early in the constructor.
337+
unsafe {
338+
core::ptr::copy_nonoverlapping(src.as_ptr(), self.cpu_addr.add(offset), src.len())
339+
};
340+
Ok(())
341+
}
342+
256343
/// Returns a pointer to an element from the region with bounds checking. `offset` is in
257344
/// units of `T`, not the number of bytes.
258345
///

0 commit comments

Comments
 (0)