Skip to content

Commit 5e50391

Browse files
committed
Add FUSE_DEV_IOC_CLONE support for parallel request processing
Address review feedback on PR #636: - Use existing fuse_dev_ioc_clone from ll/ioctl.rs instead of reimplementing the ioctl - Make clone_fd opt-in via Config.clone_fd (default false) - Add graceful fallback when FUSE_DEV_IOC_CLONE is not supported: detect ENOTTY/EINVAL and fall back to shared fd - Remove separate test file (tests live in fuser-tests now) When config.clone_fd is true and n_threads > 1, each worker thread gets its own cloned fd via FUSE_DEV_IOC_CLONE. The kernel distributes requests across all cloned fds, enabling true parallel processing. Requires Linux 4.5+. Falls back gracefully on older kernels.
1 parent e778f12 commit 5e50391

4 files changed

Lines changed: 55 additions & 2 deletions

File tree

src/channel.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use std::io;
22
use std::os::fd::AsFd;
3+
use std::os::fd::AsRawFd;
34
use std::os::fd::BorrowedFd;
45
use std::sync::Arc;
56

67
use nix::errno::Errno;
78

89
use crate::dev_fuse::DevFuse;
10+
use crate::ll::ioctl::fuse_dev_ioc_clone;
911
use crate::passthrough::BackingId;
1012

1113
/// A raw communication channel to the FUSE kernel driver
@@ -55,6 +57,29 @@ impl Channel {
5557
// a sender by using the same file and use it in other threads.
5658
ChannelSender(self.0.clone())
5759
}
60+
61+
/// Clone the FUSE file descriptor using FUSE_DEV_IOC_CLONE.
62+
///
63+
/// This creates a new /dev/fuse fd that shares the same FUSE connection,
64+
/// allowing multiple threads to read requests in parallel. The kernel
65+
/// distributes requests across all cloned fds.
66+
///
67+
/// Requires Linux 4.5+. Returns an error on older kernels or non-Linux.
68+
#[cfg(target_os = "linux")]
69+
pub(crate) fn clone_fd(&self) -> io::Result<Self> {
70+
// Open a new /dev/fuse fd
71+
let new_dev = DevFuse::open()?;
72+
73+
// Clone the connection to the new fd
74+
let mut source_fd = self.0.as_raw_fd() as u32;
75+
// SAFETY: fuse_dev_ioc_clone is a valid ioctl for /dev/fuse
76+
unsafe {
77+
fuse_dev_ioc_clone(new_dev.as_raw_fd(), &mut source_fd)
78+
.map_err(|e| io::Error::from_raw_os_error(e as i32))?;
79+
}
80+
81+
Ok(Self(Arc::new(new_dev)))
82+
}
5883
}
5984

6085
#[derive(Clone, Debug)]

src/ll/ioctl.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ pub(crate) struct fuse_backing_map {
66
}
77

88
pub(crate) const FUSE_DEV_IOC_MAGIC: u8 = 229;
9-
#[expect(dead_code)]
109
pub(crate) const FUSE_DEV_IOC_CLONE: u8 = 0;
1110
pub(crate) const FUSE_DEV_IOC_BACKING_OPEN: u8 = 1;
1211
pub(crate) const FUSE_DEV_IOC_BACKING_CLOSE: u8 = 2;

src/mnt/mount_options.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ pub struct Config {
1515
pub acl: SessionACL,
1616
/// Number of event loop threads. If unspecified, one thread is used.
1717
pub n_threads: Option<usize>,
18+
/// Use FUSE_DEV_IOC_CLONE to give each worker thread its own fd.
19+
/// This enables true parallel request processing from the kernel.
20+
/// Requires Linux 4.5+. Falls back to shared fd on older kernels.
21+
pub clone_fd: bool,
1822
}
1923

2024
/// Mount options accepted by the FUSE filesystem type
@@ -210,6 +214,7 @@ pub(crate) fn parse_options_from_args(args: &[&OsStr]) -> io::Result<Config> {
210214
mount_options: out,
211215
acl,
212216
n_threads: None,
217+
clone_fd: false,
213218
})
214219
}
215220

src/session.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,32 @@ impl<FS: Filesystem> Session<FS> {
266266
let mut filesystem = Arc::new(filesystem);
267267

268268
let mut channels = Vec::with_capacity(n_threads);
269+
#[cfg(target_os = "linux")]
270+
let mut use_clone_fd = config.clone_fd;
271+
#[cfg(not(target_os = "linux"))]
272+
let mut use_clone_fd = false;
273+
269274
for _ in 0..n_threads_minus_one {
270-
// TODO: fuse_dev_ioc_clone
275+
if use_clone_fd {
276+
match ch.clone_fd() {
277+
Ok(cloned) => {
278+
channels.push(cloned);
279+
continue;
280+
}
281+
Err(e) => {
282+
// ENOTTY/EINVAL indicate kernel doesn't support clone_fd
283+
// Fall back to shared fd for this and all remaining threads
284+
use_clone_fd = false;
285+
if e.raw_os_error() == Some(libc::ENOTTY)
286+
|| e.raw_os_error() == Some(libc::EINVAL)
287+
{
288+
info!("FUSE_DEV_IOC_CLONE not supported, falling back to shared fd");
289+
} else {
290+
warn!("clone_fd failed: {e}, falling back to shared fd");
291+
}
292+
}
293+
}
294+
}
271295
channels.push(ch.clone());
272296
}
273297
channels.push(ch);

0 commit comments

Comments
 (0)