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        Self::new(s.as_ptr(), s.len())
129    }
130
131    /// Create a [`vm_memory::VolatileSlice`] from [FileVolatileSlice] without dirty page tracking.
132    ///
133    /// [`vm_memory::VolatileSlice`]: https://docs.rs/vm-memory/latest/vm_memory/volatile_memory/struct.VolatileSlice.html
134    pub fn as_volatile_slice(&self) -> VolatileSlice<'a, ()> {
135        unsafe { VolatileSlice::new(self.as_ptr(), self.len()) }
136    }
137
138    /// Borrow as a [FileVolatileSlice] object to temporarily elide the lifetime parameter.
139    ///
140    /// # Safety
141    /// The [FileVolatileSlice] is borrowed without a lifetime parameter, so the caller must
142    /// ensure that [FileVolatileBuf] doesn't out-live the borrowed [FileVolatileSlice] object.
143    pub unsafe fn borrow_as_buf(&self, inited: bool) -> FileVolatileBuf {
144        let size = if inited { self.size } else { 0 };
145
146        FileVolatileBuf {
147            addr: self.addr,
148            size,
149            cap: self.size,
150        }
151    }
152
153    /// Return a pointer to the start of the slice.
154    pub fn as_ptr(&self) -> *mut u8 {
155        self.addr as *mut u8
156    }
157
158    /// Get the size of the slice.
159    pub fn len(&self) -> usize {
160        self.size
161    }
162
163    /// Check if the slice is empty.
164    pub fn is_empty(&self) -> bool {
165        self.size == 0
166    }
167
168    /// Return a subslice of this [FileVolatileSlice] starting at `offset`.
169    pub fn offset(&self, count: usize) -> Result<Self, Error> {
170        let new_addr = self.addr.checked_add(count).ok_or(Error::Overflow {
171            base: self.addr,
172            offset: count,
173        })?;
174        let new_size = self
175            .size
176            .checked_sub(count)
177            .ok_or(Error::OutOfBounds { addr: new_addr })?;
178        Ok(Self::new(new_addr as *mut u8, new_size))
179    }
180}
181
182impl<'a> Bytes<usize> for FileVolatileSlice<'a> {
183    type E = VError;
184
185    fn write(&self, buf: &[u8], addr: usize) -> Result<usize, Self::E> {
186        VolatileSlice::write(&self.as_volatile_slice(), buf, addr)
187    }
188
189    fn read(&self, buf: &mut [u8], addr: usize) -> Result<usize, Self::E> {
190        VolatileSlice::read(&self.as_volatile_slice(), buf, addr)
191    }
192
193    fn write_slice(&self, buf: &[u8], addr: usize) -> Result<(), Self::E> {
194        VolatileSlice::write_slice(&self.as_volatile_slice(), buf, addr)
195    }
196
197    fn read_slice(&self, buf: &mut [u8], addr: usize) -> Result<(), Self::E> {
198        VolatileSlice::write_slice(&self.as_volatile_slice(), buf, addr)
199    }
200
201    fn read_from<F>(&self, addr: usize, src: &mut F, count: usize) -> Result<usize, Self::E>
202    where
203        F: Read,
204    {
205        VolatileSlice::read_from(&self.as_volatile_slice(), addr, src, count)
206    }
207
208    fn read_exact_from<F>(&self, addr: usize, src: &mut F, count: usize) -> Result<(), Self::E>
209    where
210        F: Read,
211    {
212        VolatileSlice::read_exact_from(&self.as_volatile_slice(), addr, src, count)
213    }
214
215    fn write_to<F>(&self, addr: usize, dst: &mut F, count: usize) -> Result<usize, Self::E>
216    where
217        F: Write,
218    {
219        VolatileSlice::write_to(&self.as_volatile_slice(), addr, dst, count)
220    }
221
222    fn write_all_to<F>(&self, addr: usize, dst: &mut F, count: usize) -> Result<(), Self::E>
223    where
224        F: Write,
225    {
226        VolatileSlice::write_all_to(&self.as_volatile_slice(), addr, dst, count)
227    }
228
229    fn store<T: AtomicAccess>(&self, val: T, addr: usize, order: Ordering) -> Result<(), Self::E> {
230        VolatileSlice::store(&self.as_volatile_slice(), val, addr, order)
231    }
232
233    fn load<T: AtomicAccess>(&self, addr: usize, order: Ordering) -> Result<T, Self::E> {
234        VolatileSlice::load(&self.as_volatile_slice(), addr, order)
235    }
236}
237
238/// An adapter structure to support `io-uring` based asynchronous IO.
239///
240/// The [tokio-uring] framework needs to take ownership of data buffers during asynchronous IO
241/// operations. The [FileVolatileBuf] converts a referenced buffer to a buffer compatible with
242/// the [tokio-uring] APIs.
243///
244/// # Safety
245/// The buffer is borrowed without a lifetime parameter, so the caller must ensure that
246/// the [FileVolatileBuf] object doesn't out-live the borrowed buffer. And during the lifetime
247/// of the [FileVolatileBuf] object, the referenced buffer must be stable.
248///
249/// [tokio-uring]: https://github.com/tokio-rs/tokio-uring
250#[allow(dead_code)]
251#[derive(Clone, Copy, Debug)]
252pub struct FileVolatileBuf {
253    addr: usize,
254    size: usize,
255    cap: usize,
256}
257
258impl FileVolatileBuf {
259    /// Create a [FileVolatileBuf] object from a mutable slice, eliding the lifetime associated
260    /// with the slice.
261    ///
262    /// # Safety
263    /// The caller needs to guarantee that the returned `FileVolatileBuf` object doesn't out-live
264    /// the referenced buffer. The caller must also guarantee that all other users of the given
265    /// chunk of memory are using volatile accesses.
266    pub unsafe fn new(buf: &mut [u8]) -> Self {
267        Self {
268            addr: buf.as_mut_ptr() as usize,
269            size: 0,
270            cap: buf.len(),
271        }
272    }
273
274    /// Create a [FileVolatileBuf] object containing `size` bytes of initialized data from a mutable
275    /// slice, eliding the lifetime associated with the slice.
276    ///
277    /// # Safety
278    /// The caller needs to guarantee that the returned `FileVolatileBuf` object doesn't out-live
279    /// the referenced buffer. The caller must also guarantee that all other users of the given
280    /// chunk of memory are using volatile accesses.
281    ///
282    /// # Panic
283    /// Panic if `size` is bigger than `buf.len()`.
284    pub unsafe fn new_with_data(buf: &mut [u8], size: usize) -> Self {
285        assert!(size <= buf.len());
286        Self {
287            addr: buf.as_mut_ptr() as usize,
288            size,
289            cap: buf.len(),
290        }
291    }
292
293    /// Create a [FileVolatileBuf] object from a raw pointer.
294    ///
295    /// # Safety
296    /// The caller needs to guarantee that the returned `FileVolatileBuf` object doesn't out-live
297    /// the referenced buffer. The caller must also guarantee that all other users of the given
298    /// chunk of memory are using volatile accesses.
299    ///
300    /// # Panic
301    /// Panic if `size` is bigger than `cap`.
302    pub unsafe fn from_raw_ptr(addr: *mut u8, size: usize, cap: usize) -> Self {
303        assert!(size <= cap);
304        Self {
305            addr: addr as usize,
306            size,
307            cap,
308        }
309    }
310
311    /// Generate an `IoSlice` object to read data from the buffer.
312    pub fn io_slice(&self) -> IoSlice {
313        let buf = unsafe { slice::from_raw_parts(self.addr as *const u8, self.size) };
314        IoSlice::new(buf)
315    }
316
317    /// Generate an `IoSliceMut` object to write data into the buffer.
318    pub fn io_slice_mut(&self) -> IoSliceMut {
319        let buf = unsafe {
320            let ptr = (self.addr as *mut u8).add(self.size);
321            let sz = self.cap - self.size;
322            slice::from_raw_parts_mut(ptr, sz)
323        };
324
325        IoSliceMut::new(buf)
326    }
327
328    /// Get capacity of the buffer.
329    pub fn cap(&self) -> usize {
330        self.cap
331    }
332
333    /// Check whether the buffer is empty.
334    pub fn is_empty(&self) -> bool {
335        self.size == 0
336    }
337
338    /// Get size of initialized data in the buffer.
339    pub fn len(&self) -> usize {
340        self.size
341    }
342
343    /// Set size of initialized data in the buffer.
344    ///
345    /// # Safety
346    /// Caller needs to ensure size is less than or equal to `cap`.
347    pub unsafe fn set_size(&mut self, size: usize) {
348        if size <= self.cap {
349            self.size = size;
350        }
351    }
352}
353
354#[cfg(all(feature = "async-io", target_os = "linux"))]
355mod async_io {
356    use super::*;
357
358    unsafe impl tokio_uring::buf::IoBuf for FileVolatileBuf {
359        fn stable_ptr(&self) -> *const u8 {
360            self.addr as *const u8
361        }
362
363        fn bytes_init(&self) -> usize {
364            self.size
365        }
366
367        fn bytes_total(&self) -> usize {
368            self.cap
369        }
370    }
371
372    unsafe impl tokio_uring::buf::IoBufMut for FileVolatileBuf {
373        fn stable_mut_ptr(&mut self) -> *mut u8 {
374            self.addr as *mut u8
375        }
376
377        unsafe fn set_init(&mut self, pos: usize) {
378            self.set_size(pos)
379        }
380    }
381
382    #[cfg(test)]
383    mod tests {
384        use super::*;
385        use tokio_uring::buf::{IoBuf, IoBufMut};
386
387        #[test]
388        fn test_new_file_volatile_buf() {
389            let mut buf = [0u8; 1024];
390            let mut buf2 = unsafe { FileVolatileBuf::new(&mut buf) };
391            assert_eq!(buf2.bytes_total(), 1024);
392            assert_eq!(buf2.bytes_init(), 0);
393            assert_eq!(buf2.stable_ptr(), buf.as_ptr());
394            unsafe { *buf2.stable_mut_ptr() = b'a' };
395            assert_eq!(buf[0], b'a');
396        }
397
398        #[test]
399        fn test_file_volatile_slice_with_size() {
400            let mut buf = [0u8; 1024];
401            let mut buf2 = unsafe { FileVolatileBuf::new_with_data(&mut buf, 256) };
402
403            assert_eq!(buf2.bytes_total(), 1024);
404            assert_eq!(buf2.bytes_init(), 256);
405            assert_eq!(buf2.stable_ptr(), buf.as_ptr());
406            assert_eq!(buf2.stable_mut_ptr(), buf.as_mut_ptr());
407            unsafe { buf2.set_init(512) };
408            assert_eq!(buf2.bytes_init(), 512);
409            unsafe { buf2.set_init(2048) };
410            assert_eq!(buf2.bytes_init(), 512);
411        }
412
413        #[test]
414        fn test_file_volatile_slice_io_slice() {
415            let mut buf = [0u8; 1024];
416            let buf2 = unsafe { FileVolatileBuf::new_with_data(&mut buf, 256) };
417
418            let slice = buf2.io_slice_mut();
419            assert_eq!(slice.len(), 768);
420            assert_eq!(unsafe { buf2.stable_ptr().add(256) }, slice.as_ptr());
421
422            let slice2 = buf2.io_slice();
423            assert_eq!(slice2.len(), 256);
424            assert_eq!(buf2.stable_ptr(), slice2.as_ptr());
425        }
426    }
427}
428
429#[cfg(test)]
430mod tests {
431    use super::*;
432
433    #[test]
434    fn test_new_file_volatile_slice() {
435        let mut buffer = [0u8; 1024];
436        let s = unsafe { FileVolatileSlice::from_raw_ptr(buffer.as_mut_ptr(), buffer.len()) };
437
438        let o: u32 = s.load(0x10, Ordering::Acquire).unwrap();
439        assert_eq!(o, 0);
440        s.store(1u8, 0x10, Ordering::Release).unwrap();
441
442        let s2 = s.as_volatile_slice();
443        let s3 = FileVolatileSlice::from_volatile_slice(&s2);
444        assert_eq!(s3.len(), 1024);
445
446        assert!(s3.offset(2048).is_err());
447
448        assert_eq!(buffer[0x10], 1);
449    }
450}