diff options
| author | Miklos Szeredi <mszeredi@redhat.com> | 2025-09-02 10:22:06 +0200 |
|---|---|---|
| committer | Miklos Szeredi <mszeredi@redhat.com> | 2025-09-05 09:11:28 +0200 |
| commit | 3f29d59e92a96d843c2ff10ebfed92ac26878658 (patch) | |
| tree | 75ce74356872ee6f3db147085c9c4090d6b3f647 /fs/fuse/dev.c | |
| parent | 60e1579a0dcf2c432286ef83ee470173d6db2f13 (diff) | |
fuse: add prune notification
Some fuse servers need to prune their caches, which can only be done if the
kernel's own dentry/inode caches are pruned first to avoid dangling
references.
Add FUSE_NOTIFY_PRUNE, which takes an array of node ID's to try and get rid
of. Inodes with active references are skipped.
A similar functionality is already provided by FUSE_NOTIFY_INVAL_ENTRY with
the FUSE_EXPIRE_ONLY flag. Differences in the interface are
FUSE_NOTIFY_INVAL_ENTRY:
- can only prune one dentry
- dentry is determined by parent ID and name
- if inode has multiple aliases (cached hard links), then they would have
to be invalidated individually to be able to get rid of the inode
FUSE_NOTIFY_PRUNE:
- can prune multiple inodes
- inodes determined by their node ID
- aliases are taken care of automatically
Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/fuse/dev.c')
| -rw-r--r-- | fs/fuse/dev.c | 39 |
1 files changed, 39 insertions, 0 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 1258acee9704..4229b38546bb 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2034,6 +2034,42 @@ static int fuse_notify_inc_epoch(struct fuse_conn *fc) return 0; } +static int fuse_notify_prune(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_prune_out outarg; + const unsigned int batch = 512; + u64 *nodeids __free(kfree) = kmalloc(sizeof(u64) * batch, GFP_KERNEL); + unsigned int num, i; + int err; + + if (!nodeids) + return -ENOMEM; + + if (size < sizeof(outarg)) + return -EINVAL; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + return err; + + if (size - sizeof(outarg) != outarg.count * sizeof(u64)) + return -EINVAL; + + for (; outarg.count; outarg.count -= num) { + num = min(batch, outarg.count); + err = fuse_copy_one(cs, nodeids, num * sizeof(u64)); + if (err) + return err; + + scoped_guard(rwsem_read, &fc->killsb) { + for (i = 0; i < num; i++) + fuse_try_prune_one_inode(fc, nodeids[i]); + } + } + return 0; +} + static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, unsigned int size, struct fuse_copy_state *cs) { @@ -2065,6 +2101,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, case FUSE_NOTIFY_INC_EPOCH: return fuse_notify_inc_epoch(fc); + case FUSE_NOTIFY_PRUNE: + return fuse_notify_prune(fc, size, cs); + default: return -EINVAL; } |
