Skip to content

Commit 314ea71

Browse files
committed
ptfs: refine the way to support O_DIRECT
We have special support of the O_DIRECT flag, but the implementation has race conditions due to `Time To Check & Time To Use` issues. So refine the implementation by using a RwLock. Signed-off-by: Jiang Liu <[email protected]>
1 parent cc0a191 commit 314ea71

File tree

3 files changed

+53
-26
lines changed

3 files changed

+53
-26
lines changed

src/passthrough/mod.rs

+8-17
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ use std::os::fd::{AsFd, BorrowedFd};
2222
use std::os::unix::ffi::OsStringExt;
2323
use std::os::unix::io::{AsRawFd, RawFd};
2424
use std::path::PathBuf;
25-
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
26-
use std::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockWriteGuard};
25+
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
26+
use std::sync::{Arc, RwLock, RwLockWriteGuard};
2727
use std::time::Duration;
2828

2929
use vm_memory::{bitmap::BitmapSlice, ByteValued};
@@ -35,7 +35,7 @@ use self::mount_fd::MountFds;
3535
use self::statx::{statx, StatExt};
3636
use self::util::{
3737
ebadf, einval, enosys, eperm, is_dir, is_safe_inode, openat, reopen_fd_through_proc, stat_fd,
38-
UniqueInodeGenerator,
38+
FileFlagGuard, UniqueInodeGenerator,
3939
};
4040
use crate::abi::fuse_abi as fuse;
4141
use crate::abi::fuse_abi::Opcode;
@@ -256,39 +256,30 @@ impl InodeMap {
256256
struct HandleData {
257257
inode: Inode,
258258
file: File,
259-
lock: Mutex<()>,
260-
open_flags: AtomicU32,
259+
open_flags: RwLock<u32>,
261260
}
262261

263262
impl HandleData {
264263
fn new(inode: Inode, file: File, flags: u32) -> Self {
265264
HandleData {
266265
inode,
267266
file,
268-
lock: Mutex::new(()),
269-
open_flags: AtomicU32::new(flags),
267+
open_flags: RwLock::new(flags),
270268
}
271269
}
272270

273271
fn get_file(&self) -> &File {
274272
&self.file
275273
}
276274

277-
fn get_file_mut(&self) -> (MutexGuard<()>, &File) {
278-
(self.lock.lock().unwrap(), &self.file)
275+
fn get_file_mut(&self) -> (FileFlagGuard<u32>, &File) {
276+
let guard = self.open_flags.write().unwrap();
277+
(FileFlagGuard::Writer(guard), &self.file)
279278
}
280279

281280
fn borrow_fd(&self) -> BorrowedFd {
282281
self.file.as_fd()
283282
}
284-
285-
fn get_flags(&self) -> u32 {
286-
self.open_flags.load(Ordering::Relaxed)
287-
}
288-
289-
fn set_flags(&self, flags: u32) {
290-
self.open_flags.store(flags, Ordering::Relaxed);
291-
}
292283
}
293284

294285
struct HandleMap {

src/passthrough/sync_io.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,34 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
4343
/// if these do not match update the file descriptor flags and store the new
4444
/// result in the HandleData entry
4545
#[inline(always)]
46-
fn check_fd_flags(&self, data: Arc<HandleData>, fd: RawFd, flags: u32) -> io::Result<()> {
47-
let open_flags = data.get_flags();
48-
if open_flags != flags {
49-
let ret = unsafe { libc::fcntl(fd, libc::F_SETFL, flags) };
46+
fn ensure_file_flags<'a>(
47+
&self,
48+
data: &'a Arc<HandleData>,
49+
file: &File,
50+
mut flags: u32,
51+
) -> io::Result<FileFlagGuard<'a, u32>> {
52+
let guard = data.open_flags.read().unwrap();
53+
if *guard & libc::O_DIRECT as u32 == flags & libc::O_DIRECT as u32 {
54+
return Ok(FileFlagGuard::Reader(guard));
55+
}
56+
drop(guard);
57+
58+
let mut guard = data.open_flags.write().unwrap();
59+
// Update the O_DIRECT flag if needed
60+
if *guard & libc::O_DIRECT as u32 != flags & libc::O_DIRECT as u32 {
61+
if flags & libc::O_DIRECT as u32 != 0 {
62+
flags = *guard | libc::O_DIRECT as u32;
63+
} else {
64+
flags = *guard & !libc::O_DIRECT as u32;
65+
}
66+
let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETFL, flags) };
5067
if ret != 0 {
5168
return Err(io::Error::last_os_error());
5269
}
53-
data.set_flags(flags);
70+
*guard = flags;
5471
}
55-
Ok(())
72+
73+
Ok(FileFlagGuard::Writer(guard))
5674
}
5775

5876
fn do_readdir(
@@ -665,7 +683,7 @@ impl<S: BitmapSlice + Send + Sync> FileSystem for PassthroughFs<S> {
665683
// so data.file won't be closed.
666684
let f = unsafe { File::from_raw_fd(data.borrow_fd().as_raw_fd()) };
667685

668-
self.check_fd_flags(data, f.as_raw_fd(), flags)?;
686+
self.ensure_file_flags(&data, &f, flags)?;
669687

670688
let mut f = ManuallyDrop::new(f);
671689

@@ -692,7 +710,7 @@ impl<S: BitmapSlice + Send + Sync> FileSystem for PassthroughFs<S> {
692710
// so data.file won't be closed.
693711
let f = unsafe { File::from_raw_fd(data.borrow_fd().as_raw_fd()) };
694712

695-
self.check_fd_flags(data, f.as_raw_fd(), flags)?;
713+
self.ensure_file_flags(&data, &f, flags)?;
696714

697715
if self.seal_size.load(Ordering::Relaxed) {
698716
let st = stat_fd(&f, None)?;

src/passthrough/util.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ use std::ffi::{CStr, CString};
77
use std::fs::File;
88
use std::io;
99
use std::mem::MaybeUninit;
10+
use std::ops::Deref;
1011
use std::os::unix::io::{AsRawFd, FromRawFd};
1112
use std::sync::atomic::{AtomicU64, AtomicU8, Ordering};
12-
use std::sync::Mutex;
13+
use std::sync::{Mutex, RwLockReadGuard, RwLockWriteGuard};
1314

1415
use super::inode_store::InodeId;
1516
use super::MAX_HOST_INO;
@@ -225,6 +226,23 @@ pub fn eperm() -> io::Error {
225226
io::Error::from_raw_os_error(libc::EPERM)
226227
}
227228

229+
/// A helper structure to hold RwLock guard.
230+
pub enum FileFlagGuard<'a, T> {
231+
Reader(RwLockReadGuard<'a, T>),
232+
Writer(RwLockWriteGuard<'a, T>),
233+
}
234+
235+
impl<'a, T> Deref for FileFlagGuard<'a, T> {
236+
type Target = T;
237+
238+
fn deref(&self) -> &Self::Target {
239+
match self {
240+
FileFlagGuard::Reader(v) => v.deref(),
241+
FileFlagGuard::Writer(v) => v.deref(),
242+
}
243+
}
244+
}
245+
228246
#[cfg(test)]
229247
mod tests {
230248
use super::*;

0 commit comments

Comments
 (0)