fuse_backend_rs/common/
file_buf.rs

1// Copyright (C) 2021-2022 Alibaba Cloud. All rights reserved.
2//
3// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
4
5//! Provide data buffers to support [tokio] and [tokio-uring] based async io.
6//!
7//! The vm-memory v0.6.0 introduced support of dirty page tracking by using `Bitmap`, which adds a
8//! generic type parameters to several APIs. That's a breaking change and  makes the rust compiler
9//! fail to compile our code. So introduce [FileVolatileSlice] to mask out the `BitmapSlice`
10//! generic type parameter. Dirty page tracking is handled at higher level in `IoBuffers`.
11//!
12//! The [tokio-uring] crates uses [io-uring] for actual IO operations. And the [io-uring] APIs
13//! require passing ownership of buffers to the runtime. So [FileVolatileBuf] is introduced to
14//! support [tokio-uring] based async io.
15//!
16//! [io-uring]: https://github.com/tokio-rs/io-uring
17//! [tokio]: https://tokio.rs/
18//! [tokio-uring]: https://github.com/tokio-rs/tokio-uring
19
20use std::io::{IoSlice, IoSliceMut, Read, Write};
21use std::marker::PhantomData;
22use std::sync::atomic::Ordering;
23use std::{error, fmt, slice};
24
25use vm_memory::{
26    bitmap::BitmapSlice, volatile_memory::Error as VError, AtomicAccess, Bytes, VolatileSlice,
27};
28
29/// Error codes related to buffer management.
30#[allow(missing_docs)]
31#[derive(Debug)]
32pub enum Error {
33    /// `addr` is out of bounds of the volatile memory slice.
34    OutOfBounds { addr: usize },
35    /// Taking a slice at `base` with `offset` would overflow `usize`.
36    Overflow { base: usize, offset: usize },
37    /// The error of VolatileSlice.
38    VolatileSlice(VError),
39}
40
41impl fmt::Display for Error {
42    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43        match self {
44            Error::OutOfBounds { addr } => write!(f, "address 0x{addr:x} is out of bounds"),
45            Error::Overflow { base, offset } => write!(
46                f,
47                "address 0x{base:x} offset by 0x{offset:x} would overflow"
48            ),
49            Error::VolatileSlice(e) => write!(f, "{e}"),
50        }
51    }
52}
53
54impl error::Error for Error {}
55
56/// An adapter structure to work around limitations of the `vm-memory` crate.
57///
58/// It solves the compilation failure by masking out the  [`vm_memory::BitmapSlice`] generic type
59/// parameter of [`vm_memory::VolatileSlice`].
60///
61/// [`vm_memory::BitmapSlice`]: https://docs.rs/vm-memory/latest/vm_memory/bitmap/trait.BitmapSlice.html
62/// [`vm_memory::VolatileSlice`]: https://docs.rs/vm-memory/latest/vm_memory/volatile_memory/struct.VolatileSlice.html
63#[derive(Clone, Copy, Debug)]
64pub struct FileVolatileSlice<'a> {
65    addr: usize,
66    size: usize,
67    phantom: PhantomData<&'a u8>,
68}
69
70impl<'a> FileVolatileSlice<'a> {
71    fn new(addr: *mut u8, size: usize) -> Self {
72        Self {
73            addr: addr as usize,
74            size,
75            phantom: PhantomData,
76        }
77    }
78
79    /// Create a new instance of [`FileVolatileSlice`] from a raw pointer.
80    ///
81    /// # Safety
82    /// To use this safely, the caller must guarantee that the memory at `addr` is `size` bytes long
83    /// and is available for the duration of the lifetime of the new [FileVolatileSlice].
84    /// The caller must also guarantee that all other users of the given chunk of memory are using
85    /// volatile accesses.
86    ///
87    /// ### Example
88    /// ```rust
89    /// # use fuse_backend_rs::file_buf::FileVolatileSlice;
90    /// # use vm_memory::bytes::Bytes;
91    /// # use std::sync::atomic::Ordering;
92    /// let mut buffer = [0u8; 1024];
93    /// let s = unsafe { FileVolatileSlice::from_raw_ptr(buffer.as_mut_ptr(), buffer.len()) };
94    ///
95    /// {
96    ///     let o: u32 = s.load(0x10, Ordering::Acquire).unwrap();
97    ///     assert_eq!(o, 0);
98    ///     s.store(1u8, 0x10, Ordering::Release).unwrap();
99    ///
100    ///     let s2 = s.as_volatile_slice();
101    ///     let s3 = FileVolatileSlice::from_volatile_slice(&s2);
102    ///     assert_eq!(s3.len(), 1024);
103    /// }
104    ///
105    /// assert_eq!(buffer[0x10], 1);
106    /// ```
107    pub unsafe fn from_raw_ptr(addr: *mut u8, size: usize) -> Self {
108        Self::new(addr, size)
109    }
110
111    /// Create a new instance of [`FileVolatileSlice`] from a mutable slice.
112    ///
113    /// # Safety
114    /// The caller must guarantee that all other users of the given chunk of memory are using
115    /// volatile accesses.
116    pub unsafe fn from_mut_slice(buf: &'a mut [u8]) -> Self {
117        Self::new(buf.as_mut_ptr(), buf.len())
118    }
119
120    /// Create a new [`FileVolatileSlice`] from [`vm_memory::VolatileSlice`] and strip off the
121    /// [`vm_memory::BitmapSlice`].
122    ///
123    /// The caller needs to handle dirty page tracking for the data buffer.
124    ///
125    /// [`vm_memory::BitmapSlice`]: https://docs.rs/vm-memory/latest/vm_memory/bitmap/trait.BitmapSlice.html
126    /// [`vm_memory::VolatileSlice`]: https://docs.rs/vm-memory/latest/vm_memory/volatile_memory/struct.VolatileSlice.html
127    pub fn from_volatile_slice<S: BitmapSlice>(s: &VolatileSlice<'a, S>) -> Self {
128        #[allow(deprecated)]
129        Self::new(s.as_ptr(), s.len())
130    }
131
132    /// Create a [`vm_memory::VolatileSlice`] from [FileVolatileSlice] without dirty page tracking.
133    ///
134    /// [`vm_memory::VolatileSlice`]: https://docs.rs/vm-memory/latest/vm_memory/volatile_memory/struct.VolatileSlice.html
135    pub fn as_volatile_slice(&self) -> VolatileSlice<'a, ()> {
136        unsafe { VolatileSlice::new(self.as_ptr(), self.len()) }
137    }
138
139    /// Borrow as a [FileVolatileSlice] object to temporarily elide the lifetime parameter.
140    ///
141    /// # Safety
142    /// The [FileVolatileSlice] is borrowed without a lifetime parameter, so the caller must
143    /// ensure that [FileVolatileBuf] doesn't out-live the borrowed [FileVolatileSlice] object.
144    pub unsafe fn borrow_as_buf(&self, inited: bool) -> FileVolatileBuf {
145        let size = if inited { self.size } else { 0 };
146
147        FileVolatileBuf {
148            addr: self.addr,
149            size,
150            cap: self.size,
151        }
152    }
153
154    /// Return a pointer to the start of the slice.
155    pub fn as_ptr(&self) -> *mut u8 {
156        self.addr as *mut u8
157    }
158
159    /// Get the size of the slice.
160    pub fn len(&self) -> usize {
161        self.size
162    }
163
164    /// Check if the slice is empty.
165    pub fn is_empty(&self) -> bool {
166        self.size == 0
167    }
168
169    /// Return a subslice of this [FileVolatileSlice] starting at `offset`.
170    pub fn offset(&self, count: usize) -> Result<Self, Error> {
171        let new_addr = self.addr.checked_add(count).ok_or(Error::Overflow {
172            base: self.addr,
173            offset: count,
174        })?;
175        let new_size = self
176            .size
177            .checked_sub(count)
178            .ok_or(Error::OutOfBounds { addr: new_addr })?;
179        Ok(Self::new(new_addr as *mut u8, new_size))
180    }
181}
182
183#[allow(clippy::needless_lifetimes)]
184impl<'a> Bytes<usize> for FileVolatileSlice<'a> {
185    type E = VError;
186
187    fn write(&self, buf: &[u8], addr: usize) -> Result<usize, Self::E> {
188        VolatileSlice::write(&self.as_volatile_slice(), buf, addr)
189    }
190
191    fn read(&self, buf: &mut [u8], addr: usize) -> Result<usize, Self::E> {
192        VolatileSlice::read(&self.as_volatile_slice(), buf, addr)
193    }
194
195    fn write_slice(&self, buf: &[u8], addr: usize) -> Result<(), Self::E> {
196        VolatileSlice::write_slice(&self.as_volatile_slice(), buf, addr)
197    }
198
199    fn read_slice(&self, buf: &mut [u8], addr: usize) -> Result<(), Self::E> {
200        VolatileSlice::write_slice(&self.as_volatile_slice(), buf, addr)
201    }
202
203    fn read_from<F>(&self, addr: usize, src: &mut F, count: usize) -> Result<usize, Self::E>
204    where
205        F: Read,
206    {
207        #[allow(deprecated)]
208        VolatileSlice::read_from(&self.as_volatile_slice(), addr, src, count)
209    }
210
211    fn read_exact_from<F>(&self, addr: usize, src: &mut F, count: usize) -> Result<(), Self::E>
212    where
213        F: Read,
214    {
215        #[allow(deprecated)]
216        VolatileSlice::read_exact_from(&self.as_volatile_slice(), addr, src, count)
217    }
218
219    fn write_to<F>(&self, addr: usize, dst: &mut F, count: usize) -> Result<usize, Self::E>
220    where
221        F: Write,
222    {
223        #[allow(deprecated)]
224        VolatileSlice::write_to(&self.as_volatile_slice(), addr, dst, count)
225    }
226
227    fn write_all_to<F>(&self, addr: usize, dst: &mut F, count: usize) -> Result<(), Self::E>
228    where
229        F: Write,
230    {
231        #[allow(deprecated)]
232        VolatileSlice::write_all_to(&self.as_volatile_slice(), addr, dst, count)
233    }
234
235    fn store<T: AtomicAccess>(&self, val: T, addr: usize, order: Ordering) -> Result<(), Self::E> {
236        VolatileSlice::store(&self.as_volatile_slice(), val, addr, order)
237    }
238
239    fn load<T: AtomicAccess>(&self, addr: usize, order: Ordering) -> Result<T, Self::E> {
240        VolatileSlice::load(&self.as_volatile_slice(), addr, order)
241    }
242}
243
244/// An adapter structure to support `io-uring` based asynchronous IO.
245///
246/// The [tokio-uring] framework needs to take ownership of data buffers during asynchronous IO
247/// operations. The [FileVolatileBuf] converts a referenced buffer to a buffer compatible with
248/// the [tokio-uring] APIs.
249///
250/// # Safety
251/// The buffer is borrowed without a lifetime parameter, so the caller must ensure that
252/// the [FileVolatileBuf] object doesn't out-live the borrowed buffer. And during the lifetime
253/// of the [FileVolatileBuf] object, the referenced buffer must be stable.
254///
255/// [tokio-uring]: https://github.com/tokio-rs/tokio-uring
256#[allow(dead_code)]
257#[derive(Clone, Copy, Debug)]
258pub struct FileVolatileBuf {
259    addr: usize,
260    size: usize,
261    cap: usize,
262}
263
264impl FileVolatileBuf {
265    /// Create a [FileVolatileBuf] object from a mutable slice, eliding the lifetime associated
266    /// with the slice.
267    ///
268    /// # Safety
269    /// The caller needs to guarantee that the returned `FileVolatileBuf` object doesn't out-live
270    /// the referenced buffer. The caller must also guarantee that all other users of the given
271    /// chunk of memory are using volatile accesses.
272    pub unsafe fn new(buf: &mut [u8]) -> Self {
273        Self {
274            addr: buf.as_mut_ptr() as usize,
275            size: 0,
276            cap: buf.len(),
277        }
278    }
279
280    /// Create a [FileVolatileBuf] object containing `size` bytes of initialized data from a mutable
281    /// slice, eliding the lifetime associated with the slice.
282    ///
283    /// # Safety
284    /// The caller needs to guarantee that the returned `FileVolatileBuf` object doesn't out-live
285    /// the referenced buffer. The caller must also guarantee that all other users of the given
286    /// chunk of memory are using volatile accesses.
287    ///
288    /// # Panic
289    /// Panic if `size` is bigger than `buf.len()`.
290    pub unsafe fn new_with_data(buf: &mut [u8], size: usize) -> Self {
291        assert!(size <= buf.len());
292        Self {
293            addr: buf.as_mut_ptr() as usize,
294            size,
295            cap: buf.len(),
296        }
297    }
298
299    /// Create a [FileVolatileBuf] object from a raw pointer.
300    ///
301    /// # Safety
302    /// The caller needs to guarantee that the returned `FileVolatileBuf` object doesn't out-live
303    /// the referenced buffer. The caller must also guarantee that all other users of the given
304    /// chunk of memory are using volatile accesses.
305    ///
306    /// # Panic
307    /// Panic if `size` is bigger than `cap`.
308    pub unsafe fn from_raw_ptr(addr: *mut u8, size: usize, cap: usize) -> Self {
309        assert!(size <= cap);
310        Self {
311            addr: addr as usize,
312            size,
313            cap,
314        }
315    }
316
317    /// Generate an `IoSlice` object to read data from the buffer.
318    pub fn io_slice(&self) -> IoSlice<'_> {
319        let buf = unsafe { slice::from_raw_parts(self.addr as *const u8, self.size) };
320        IoSlice::new(buf)
321    }
322
323    /// Generate an `IoSliceMut` object to write data into the buffer.
324    pub fn io_slice_mut(&self) -> IoSliceMut<'_> {
325        let buf = unsafe {
326            let ptr = (self.addr as *mut u8).add(self.size);
327            let sz = self.cap - self.size;
328            slice::from_raw_parts_mut(ptr, sz)
329        };
330
331        IoSliceMut::new(buf)
332    }
333
334    /// Get capacity of the buffer.
335    pub fn cap(&self) -> usize {
336        self.cap
337    }
338
339    /// Check whether the buffer is empty.
340    pub fn is_empty(&self) -> bool {
341        self.size == 0
342    }
343
344    /// Get size of initialized data in the buffer.
345    pub fn len(&self) -> usize {
346        self.size
347    }
348
349    /// Set size of initialized data in the buffer.
350    ///
351    /// # Safety
352    /// Caller needs to ensure size is less than or equal to `cap`.
353    pub unsafe fn set_size(&mut self, size: usize) {
354        if size <= self.cap {
355            self.size = size;
356        }
357    }
358}
359
360#[cfg(all(feature = "async-io", target_os = "linux"))]
361mod async_io {
362    use super::*;
363
364    unsafe impl tokio_uring::buf::IoBuf for FileVolatileBuf {
365        fn stable_ptr(&self) -> *const u8 {
366            self.addr as *const u8
367        }
368
369        fn bytes_init(&self) -> usize {
370            self.size
371        }
372
373        fn bytes_total(&self) -> usize {
374            self.cap
375        }
376    }
377
378    unsafe impl tokio_uring::buf::IoBufMut for FileVolatileBuf {
379        fn stable_mut_ptr(&mut self) -> *mut u8 {
380            self.addr as *mut u8
381        }
382
383        unsafe fn set_init(&mut self, pos: usize) {
384            self.set_size(pos)
385        }
386    }
387
388    #[cfg(test)]
389    mod tests {
390        use super::*;
391        use tokio_uring::buf::{IoBuf, IoBufMut};
392
393        #[test]
394        fn test_new_file_volatile_buf() {
395            let mut buf = [0u8; 1024];
396            let mut buf2 = unsafe { FileVolatileBuf::new(&mut buf) };
397            assert_eq!(buf2.bytes_total(), 1024);
398            assert_eq!(buf2.bytes_init(), 0);
399            assert_eq!(buf2.stable_ptr(), buf.as_ptr());
400            unsafe { *buf2.stable_mut_ptr() = b'a' };
401            assert_eq!(buf[0], b'a');
402        }
403
404        #[test]
405        fn test_file_volatile_slice_with_size() {
406            let mut buf = [0u8; 1024];
407            let mut buf2 = unsafe { FileVolatileBuf::new_with_data(&mut buf, 256) };
408
409            assert_eq!(buf2.bytes_total(), 1024);
410            assert_eq!(buf2.bytes_init(), 256);
411            assert_eq!(buf2.stable_ptr(), buf.as_ptr());
412            assert_eq!(buf2.stable_mut_ptr(), buf.as_mut_ptr());
413            unsafe { buf2.set_init(512) };
414            assert_eq!(buf2.bytes_init(), 512);
415            unsafe { buf2.set_init(2048) };
416            assert_eq!(buf2.bytes_init(), 512);
417        }
418
419        #[test]
420        fn test_file_volatile_slice_io_slice() {
421            let mut buf = [0u8; 1024];
422            let buf2 = unsafe { FileVolatileBuf::new_with_data(&mut buf, 256) };
423
424            let slice = buf2.io_slice_mut();
425            assert_eq!(slice.len(), 768);
426            assert_eq!(unsafe { buf2.stable_ptr().add(256) }, slice.as_ptr());
427
428            let slice2 = buf2.io_slice();
429            assert_eq!(slice2.len(), 256);
430            assert_eq!(buf2.stable_ptr(), slice2.as_ptr());
431        }
432    }
433}
434
435#[cfg(test)]
436mod tests {
437    use super::*;
438
439    #[test]
440    fn test_new_file_volatile_slice() {
441        let mut buffer = [0u8; 1024];
442        let s = unsafe { FileVolatileSlice::from_raw_ptr(buffer.as_mut_ptr(), buffer.len()) };
443
444        let o: u32 = s.load(0x10, Ordering::Acquire).unwrap();
445        assert_eq!(o, 0);
446        s.store(1u8, 0x10, Ordering::Release).unwrap();
447
448        let s2 = s.as_volatile_slice();
449        let s3 = FileVolatileSlice::from_volatile_slice(&s2);
450        assert_eq!(s3.len(), 1024);
451
452        assert!(s3.offset(2048).is_err());
453
454        assert_eq!(buffer[0x10], 1);
455    }
456}