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}