Skip to content

Commit 89c55dc

Browse files
committed
fuse: add more control over cache invalidation behaviour
jira SECO-478 RFBugFix: FUSE commit-author Luis Henriques <luis@igalia.com> commit 2396356 upstream-diff | conflict in fs/fuse/dir.c due to missing this piece: d701902 - fuse: return correct dentry for ->mkdir Which is a part of a larger changeset here that we're not going to take: https://lore.kernel.org/all/20250227013949.536172-1-neilb@suse.de/ | Additionally this bumps the Kernel FUSE API minor version from 41 to 44. The interface into via fuse3 currently in Rocky 10.1 is limited to API 38 anyways at 3.16.2. | There is a build conflict due to a major rewrite of the d_revalidate calls which now includes the parent directory being passed. 5be1fa8 Pass parent directory inode and expected name to ->d_revalidate() In this case we can use the dentry->i_sb because we only need the superblock for get_fuse_conn_super(). Currently userspace is able to notify the kernel to invalidate the cache for an inode. This means that, if all the inodes in a filesystem need to be invalidated, then userspace needs to iterate through all of them and do this kernel notification separately. This patch adds the concept of 'epoch': each fuse connection will have the current epoch initialized and every new dentry will have it's d_time set to the current epoch value. A new operation will then allow userspace to increment the epoch value. Every time a dentry is d_revalidate()'ed, it's epoch is compared with the current connection epoch and invalidated if it's value is different. Signed-off-by: Luis Henriques <luis@igalia.com> Tested-by: Laura Promberger <laura.promberger@cern.ch> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> (cherry picked from commit 2396356) Signed-off-by: Jonathan Maple <jmaple@ciq.com> build fix: fuse: add more control over cache invalidation behaviour
1 parent ab28a32 commit 89c55dc

6 files changed

Lines changed: 47 additions & 4 deletions

File tree

fs/fuse/dev.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1887,6 +1887,19 @@ static int fuse_notify_resend(struct fuse_conn *fc)
18871887
return 0;
18881888
}
18891889

1890+
/*
1891+
* Increments the fuse connection epoch. This will result of dentries from
1892+
* previous epochs to be invalidated.
1893+
*
1894+
* XXX optimization: add call to shrink_dcache_sb()?
1895+
*/
1896+
static int fuse_notify_inc_epoch(struct fuse_conn *fc)
1897+
{
1898+
atomic_inc(&fc->epoch);
1899+
1900+
return 0;
1901+
}
1902+
18901903
static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
18911904
unsigned int size, struct fuse_copy_state *cs)
18921905
{
@@ -1915,6 +1928,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
19151928
case FUSE_NOTIFY_RESEND:
19161929
return fuse_notify_resend(fc);
19171930

1931+
case FUSE_NOTIFY_INC_EPOCH:
1932+
return fuse_notify_inc_epoch(fc);
1933+
19181934
default:
19191935
fuse_copy_finish(cs);
19201936
return -EINVAL;

fs/fuse/dir.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,14 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
197197
struct inode *inode;
198198
struct dentry *parent;
199199
struct fuse_mount *fm;
200+
struct fuse_conn *fc;
200201
struct fuse_inode *fi;
201202
int ret;
202203

204+
fc = get_fuse_conn_super(entry->d_sb);
205+
if (entry->d_time < atomic_read(&fc->epoch))
206+
goto invalid;
207+
203208
inode = d_inode_rcu(entry);
204209
if (inode && fuse_is_bad(inode))
205210
goto invalid;
@@ -415,16 +420,20 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
415420
static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
416421
unsigned int flags)
417422
{
418-
int err;
419423
struct fuse_entry_out outarg;
424+
struct fuse_conn *fc;
420425
struct inode *inode;
421426
struct dentry *newent;
427+
int err, epoch;
422428
bool outarg_valid = true;
423429
bool locked;
424430

425431
if (fuse_is_bad(dir))
426432
return ERR_PTR(-EIO);
427433

434+
fc = get_fuse_conn_super(dir->i_sb);
435+
epoch = atomic_read(&fc->epoch);
436+
428437
locked = fuse_lock_inode(dir);
429438
err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name,
430439
&outarg, &inode);
@@ -446,6 +455,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
446455
goto out_err;
447456

448457
entry = newent ? newent : entry;
458+
entry->d_time = epoch;
449459
if (outarg_valid)
450460
fuse_change_entry_timeout(entry, &outarg);
451461
else
@@ -618,7 +628,6 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir,
618628
struct dentry *entry, struct file *file,
619629
unsigned int flags, umode_t mode, u32 opcode)
620630
{
621-
int err;
622631
struct inode *inode;
623632
struct fuse_mount *fm = get_fuse_mount(dir);
624633
FUSE_ARGS(args);
@@ -628,11 +637,13 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir,
628637
struct fuse_entry_out outentry;
629638
struct fuse_inode *fi;
630639
struct fuse_file *ff;
640+
int epoch, err;
631641
bool trunc = flags & O_TRUNC;
632642

633643
/* Userspace expects S_IFREG in create mode */
634644
BUG_ON((mode & S_IFMT) != S_IFREG);
635645

646+
epoch = atomic_read(&fm->fc->epoch);
636647
forget = fuse_alloc_forget();
637648
err = -ENOMEM;
638649
if (!forget)
@@ -701,6 +712,7 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir,
701712
}
702713
kfree(forget);
703714
d_instantiate(entry, inode);
715+
entry->d_time = epoch;
704716
fuse_change_entry_timeout(entry, &outentry);
705717
fuse_dir_changed(dir);
706718
err = generic_file_open(inode, file);
@@ -787,12 +799,14 @@ static int create_new_entry(struct mnt_idmap *idmap, struct fuse_mount *fm,
787799
struct fuse_entry_out outarg;
788800
struct inode *inode;
789801
struct dentry *d;
790-
int err;
791802
struct fuse_forget_link *forget;
803+
int epoch, err;
792804

793805
if (fuse_is_bad(dir))
794806
return -EIO;
795807

808+
epoch = atomic_read(&fm->fc->epoch);
809+
796810
forget = fuse_alloc_forget();
797811
if (!forget)
798812
return -ENOMEM;
@@ -835,9 +849,11 @@ static int create_new_entry(struct mnt_idmap *idmap, struct fuse_mount *fm,
835849
return PTR_ERR(d);
836850

837851
if (d) {
852+
d->d_time = epoch;
838853
fuse_change_entry_timeout(d, &outarg);
839854
dput(d);
840855
} else {
856+
entry->d_time = epoch;
841857
fuse_change_entry_timeout(entry, &outarg);
842858
}
843859
fuse_dir_changed(dir);

fs/fuse/fuse_i.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,9 @@ struct fuse_conn {
604604
/** Number of fuse_dev's */
605605
atomic_t dev_count;
606606

607+
/** Current epoch for up-to-date dentries */
608+
atomic_t epoch;
609+
607610
struct rcu_head rcu;
608611

609612
/** The user id for this mount */

fs/fuse/inode.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
926926
init_rwsem(&fc->killsb);
927927
refcount_set(&fc->count, 1);
928928
atomic_set(&fc->dev_count, 1);
929+
atomic_set(&fc->epoch, 1);
929930
init_waitqueue_head(&fc->blocked_waitq);
930931
fuse_iqueue_init(&fc->iq, fiq_ops, fiq_priv);
931932
INIT_LIST_HEAD(&fc->bg_queue);

fs/fuse/readdir.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ static int fuse_direntplus_link(struct file *file,
161161
struct fuse_conn *fc;
162162
struct inode *inode;
163163
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
164+
int epoch;
164165

165166
if (!o->nodeid) {
166167
/*
@@ -190,6 +191,7 @@ static int fuse_direntplus_link(struct file *file,
190191
return -EIO;
191192

192193
fc = get_fuse_conn(dir);
194+
epoch = atomic_read(&fc->epoch);
193195

194196
name.hash = full_name_hash(parent, name.name, name.len);
195197
dentry = d_lookup(parent, &name);
@@ -256,6 +258,7 @@ static int fuse_direntplus_link(struct file *file,
256258
}
257259
if (fc->readdirplus_auto)
258260
set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
261+
dentry->d_time = epoch;
259262
fuse_change_entry_timeout(dentry, o);
260263

261264
dput(dentry);

include/uapi/linux/fuse.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@
220220
*
221221
* 7.41
222222
* - add FUSE_ALLOW_IDMAP
223+
*
224+
* 7.44
225+
* - add FUSE_NOTIFY_INC_EPOCH
223226
*/
224227

225228
#ifndef _LINUX_FUSE_H
@@ -255,7 +258,7 @@
255258
#define FUSE_KERNEL_VERSION 7
256259

257260
/** Minor version number of this interface */
258-
#define FUSE_KERNEL_MINOR_VERSION 41
261+
#define FUSE_KERNEL_MINOR_VERSION 44
259262

260263
/** The node ID of the root inode */
261264
#define FUSE_ROOT_ID 1
@@ -655,6 +658,7 @@ enum fuse_notify_code {
655658
FUSE_NOTIFY_RETRIEVE = 5,
656659
FUSE_NOTIFY_DELETE = 6,
657660
FUSE_NOTIFY_RESEND = 7,
661+
FUSE_NOTIFY_INC_EPOCH = 8,
658662
FUSE_NOTIFY_CODE_MAX,
659663
};
660664

0 commit comments

Comments
 (0)