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}