Skip to content

Commit f3a25b8

Browse files
eryugeybergwolf
authored andcommitted
passthroughfs: add config to specify entry and attr timeout for dir
For AI training workloads, APP reads large number of files from dataset and only reads them once. So entry and attr cache for such files are not needed, and large number of cached entry will make InodeMap bigger and bigger, and make InodeMap re-allocate its memory over and over again, which hits performance. On the other hand, the entry cache for dir is very helpful. But entry_timeout and attr_timeout will set the same timeout value for both dirs and regular files. So introduce 'dir_entry_timeout' and 'dir_attr_timeout' config options to config dir entry/attr separately. So that we could use cache=none (which indicates entry/attr timeout as 0 and bypass guest pagecache to avoid massive memory copy) and set dir_entry_timeout and dir_attr_timeout as none-zero to cache dirs. Signed-off-by: Eryu Guan <[email protected]>
1 parent 6c4c726 commit f3a25b8

File tree

1 file changed

+108
-3
lines changed

1 file changed

+108
-3
lines changed

src/passthrough/mod.rs

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ fn is_safe_inode(mode: u32) -> bool {
137137
matches!(mode & libc::S_IFMT, libc::S_IFREG | libc::S_IFDIR)
138138
}
139139

140+
fn is_dir(mode: u32) -> bool {
141+
(mode & libc::S_IFMT) == libc::S_IFDIR
142+
}
143+
140144
impl InodeData {
141145
fn new(inode: Inode, f: FileOrHandle, refcount: u64, altkey: InodeAltKey, mode: u32) -> Self {
142146
InodeData {
@@ -391,6 +395,15 @@ pub struct Config {
391395
/// The default value for this option is 5 seconds.
392396
pub attr_timeout: Duration,
393397

398+
/// Same as `entry_timeout`, override `entry_timeout` config, but only take effect on
399+
/// directories when specified. This is useful to set different timeouts for directories and
400+
/// regular files.
401+
pub dir_entry_timeout: Option<Duration>,
402+
403+
/// Same as `attr_timeout`, override `attr_timeout` config, but only take effect on directories
404+
/// when specified. This is useful to set different timeouts for directories and regular files.
405+
pub dir_attr_timeout: Option<Duration>,
406+
394407
/// The caching policy the file system should use. See the documentation of `CachePolicy` for
395408
/// more details.
396409
pub cache_policy: CachePolicy,
@@ -499,6 +512,8 @@ impl Default for Config {
499512
seal_size: false,
500513
enable_mntid: false,
501514
dax_file_size: None,
515+
dir_entry_timeout: None,
516+
dir_attr_timeout: None,
502517
}
503518
}
504519
}
@@ -555,6 +570,9 @@ pub struct PassthroughFs<S: BitmapSlice + Send + Sync = ()> {
555570
// Init from guest kernel Init cmd of fuse fs.
556571
perfile_dax: AtomicBool,
557572

573+
dir_entry_timeout: Duration,
574+
dir_attr_timeout: Duration,
575+
558576
cfg: Config,
559577

560578
phantom: PhantomData<S>,
@@ -572,6 +590,13 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
572590
0,
573591
)?;
574592

593+
let (dir_entry_timeout, dir_attr_timeout) =
594+
match (cfg.dir_entry_timeout, cfg.dir_attr_timeout) {
595+
(Some(e), Some(a)) => (e, a),
596+
(Some(e), None) => (e, cfg.attr_timeout),
597+
(None, Some(a)) => (cfg.entry_timeout, a),
598+
(None, None) => (cfg.entry_timeout, cfg.attr_timeout),
599+
};
575600
Ok(PassthroughFs {
576601
inode_map: InodeMap::new(),
577602
next_inode: AtomicU64::new(fuse::ROOT_ID + 1),
@@ -589,6 +614,8 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
589614
no_readdir: AtomicBool::new(cfg.no_readdir),
590615
seal_size: AtomicBool::new(cfg.seal_size),
591616
perfile_dax: AtomicBool::new(false),
617+
dir_entry_timeout,
618+
dir_attr_timeout,
592619
cfg,
593620

594621
phantom: PhantomData,
@@ -970,13 +997,19 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
970997
}
971998
};
972999

1000+
let attr = st.get_stat();
1001+
let (entry_timeout, attr_timeout) = if is_dir(attr.st_mode) {
1002+
(self.dir_entry_timeout, self.dir_attr_timeout)
1003+
} else {
1004+
(self.cfg.entry_timeout, self.cfg.attr_timeout)
1005+
};
9731006
Ok(Entry {
9741007
inode,
9751008
generation: 0,
976-
attr: st.get_stat(),
1009+
attr,
9771010
attr_flags,
978-
attr_timeout: self.cfg.attr_timeout,
979-
entry_timeout: self.cfg.entry_timeout,
1011+
attr_timeout,
1012+
entry_timeout,
9801013
})
9811014
}
9821015

@@ -1515,4 +1548,76 @@ mod tests {
15151548
let n = f.read(&mut buf).unwrap();
15161549
assert_eq!(n, 0);
15171550
}
1551+
1552+
#[test]
1553+
fn test_is_dir() {
1554+
let mode = libc::S_IFREG;
1555+
assert!(!is_dir(mode));
1556+
1557+
let mode = libc::S_IFDIR;
1558+
assert!(is_dir(mode));
1559+
}
1560+
1561+
#[test]
1562+
fn test_passthroughfs_dir_timeout() {
1563+
log::set_max_level(log::LevelFilter::Trace);
1564+
1565+
let source = TempDir::new().expect("Cannot create temporary directory.");
1566+
let parent_path =
1567+
TempDir::new_in(source.as_path()).expect("Cannot create temporary directory.");
1568+
let child_path =
1569+
TempFile::new_in(parent_path.as_path()).expect("Cannot create temporary file.");
1570+
1571+
// passthroughfs with cache=none, but non-zero dir entry/attr timeout.
1572+
let fs_cfg = Config {
1573+
writeback: false,
1574+
do_import: true,
1575+
no_open: false,
1576+
root_dir: source
1577+
.as_path()
1578+
.to_str()
1579+
.expect("source path to string")
1580+
.to_string(),
1581+
cache_policy: CachePolicy::Never,
1582+
entry_timeout: Duration::from_secs(0),
1583+
attr_timeout: Duration::from_secs(0),
1584+
dir_entry_timeout: Some(Duration::from_secs(1)),
1585+
dir_attr_timeout: Some(Duration::from_secs(2)),
1586+
..Default::default()
1587+
};
1588+
let fs = PassthroughFs::<()>::new(fs_cfg).unwrap();
1589+
fs.import().unwrap();
1590+
1591+
let ctx = Context::default();
1592+
1593+
// parent entry should have non-zero timeouts
1594+
let parent = CString::new(
1595+
parent_path
1596+
.as_path()
1597+
.file_name()
1598+
.unwrap()
1599+
.to_str()
1600+
.expect("path to string"),
1601+
)
1602+
.unwrap();
1603+
let p_entry = fs.lookup(&ctx, ROOT_ID, &parent).unwrap();
1604+
assert_eq!(p_entry.entry_timeout, Duration::from_secs(1));
1605+
assert_eq!(p_entry.attr_timeout, Duration::from_secs(2));
1606+
1607+
// regular file has zero timeout value
1608+
let child = CString::new(
1609+
child_path
1610+
.as_path()
1611+
.file_name()
1612+
.unwrap()
1613+
.to_str()
1614+
.expect("path to string"),
1615+
)
1616+
.unwrap();
1617+
let c_entry = fs.lookup(&ctx, p_entry.inode, &child).unwrap();
1618+
assert_eq!(c_entry.entry_timeout, Duration::from_secs(0));
1619+
assert_eq!(c_entry.attr_timeout, Duration::from_secs(0));
1620+
1621+
fs.destroy();
1622+
}
15181623
}

0 commit comments

Comments
 (0)