* [PATCH 1/4] fuse: remove FUSE_NOTIFY_CODE_MAX from <uapi/linux/fuse.h>
@ 2025-09-02 14:41 Miklos Szeredi
2025-09-02 14:41 ` [PATCH 2/4] fuse: fix possibly missing fuse_copy_finish() call in fuse_notify() Miklos Szeredi
` (3 more replies)
0 siblings, 4 replies; 15+ messages in thread
From: Miklos Szeredi @ 2025-09-02 14:41 UTC (permalink / raw)
To: linux-fsdevel; +Cc: Jim Harris
Constants that change value from version to version have no place in an
interface definition.
Hopefully this won't break anything.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
---
include/uapi/linux/fuse.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 6b9fb8b08768..30bf0846547f 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -680,7 +680,6 @@ enum fuse_notify_code {
FUSE_NOTIFY_DELETE = 6,
FUSE_NOTIFY_RESEND = 7,
FUSE_NOTIFY_INC_EPOCH = 8,
- FUSE_NOTIFY_CODE_MAX,
};
/* The read buffer is required to be at least 8k, but may be much larger */
--
2.49.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 2/4] fuse: fix possibly missing fuse_copy_finish() call in fuse_notify() 2025-09-02 14:41 [PATCH 1/4] fuse: remove FUSE_NOTIFY_CODE_MAX from <uapi/linux/fuse.h> Miklos Szeredi @ 2025-09-02 14:41 ` Miklos Szeredi 2025-09-02 21:48 ` Joanne Koong 2025-09-02 14:41 ` [PATCH 3/4] fuse: remove redundant calls to fuse_copy_finish() " Miklos Szeredi ` (2 subsequent siblings) 3 siblings, 1 reply; 15+ messages in thread From: Miklos Szeredi @ 2025-09-02 14:41 UTC (permalink / raw) To: linux-fsdevel; +Cc: Jim Harris, stable In case of FUSE_NOTIFY_RESEND and FUSE_NOTIFY_INC_EPOCH fuse_copy_finish() isn't called. Fix by always calling fuse_copy_finish() after fuse_notify(). It's a no-op if called a second time. Fixes: 760eac73f9f6 ("fuse: Introduce a new notification type for resend pending requests") Fixes: 2396356a945b ("fuse: add more control over cache invalidation behaviour") Cc: <stable@vger.kernel.org> # v6.9 Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> --- fs/fuse/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index df793003eb0c..85d05a5e40e9 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2178,7 +2178,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, */ if (!oh.unique) { err = fuse_notify(fc, oh.error, nbytes - sizeof(oh), cs); - goto out; + goto copy_finish; } err = -EINVAL; -- 2.49.0 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 2/4] fuse: fix possibly missing fuse_copy_finish() call in fuse_notify() 2025-09-02 14:41 ` [PATCH 2/4] fuse: fix possibly missing fuse_copy_finish() call in fuse_notify() Miklos Szeredi @ 2025-09-02 21:48 ` Joanne Koong 0 siblings, 0 replies; 15+ messages in thread From: Joanne Koong @ 2025-09-02 21:48 UTC (permalink / raw) To: Miklos Szeredi; +Cc: linux-fsdevel, Jim Harris, stable On Tue, Sep 2, 2025 at 7:44 AM Miklos Szeredi <mszeredi@redhat.com> wrote: > > In case of FUSE_NOTIFY_RESEND and FUSE_NOTIFY_INC_EPOCH fuse_copy_finish() > isn't called. > > Fix by always calling fuse_copy_finish() after fuse_notify(). It's a no-op > if called a second time. > > Fixes: 760eac73f9f6 ("fuse: Introduce a new notification type for resend pending requests") > Fixes: 2396356a945b ("fuse: add more control over cache invalidation behaviour") > Cc: <stable@vger.kernel.org> # v6.9 > Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Reviewed-by: Joanne Koong <joannelkoong@gmail.com> > --- > fs/fuse/dev.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c > index df793003eb0c..85d05a5e40e9 100644 > --- a/fs/fuse/dev.c > +++ b/fs/fuse/dev.c > @@ -2178,7 +2178,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, > */ > if (!oh.unique) { > err = fuse_notify(fc, oh.error, nbytes - sizeof(oh), cs); > - goto out; > + goto copy_finish; > } > > err = -EINVAL; > -- > 2.49.0 > > ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 3/4] fuse: remove redundant calls to fuse_copy_finish() in fuse_notify() 2025-09-02 14:41 [PATCH 1/4] fuse: remove FUSE_NOTIFY_CODE_MAX from <uapi/linux/fuse.h> Miklos Szeredi 2025-09-02 14:41 ` [PATCH 2/4] fuse: fix possibly missing fuse_copy_finish() call in fuse_notify() Miklos Szeredi @ 2025-09-02 14:41 ` Miklos Szeredi 2025-09-02 21:53 ` Joanne Koong 2025-09-03 10:25 ` Luis Henriques 2025-09-02 14:41 ` [PATCH 4/4] fuse: add prune notification Miklos Szeredi 2025-09-02 21:31 ` [PATCH 1/4] fuse: remove FUSE_NOTIFY_CODE_MAX from <uapi/linux/fuse.h> Joanne Koong 3 siblings, 2 replies; 15+ messages in thread From: Miklos Szeredi @ 2025-09-02 14:41 UTC (permalink / raw) To: linux-fsdevel; +Cc: Jim Harris Remove tail calls of fuse_copy_finish(), since it's now done from fuse_dev_do_write(). No functional change. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> --- fs/fuse/dev.c | 79 +++++++++++++++------------------------------------ 1 file changed, 23 insertions(+), 56 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 85d05a5e40e9..1258acee9704 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1622,35 +1622,31 @@ static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size, struct fuse_copy_state *cs) { struct fuse_notify_poll_wakeup_out outarg; - int err = -EINVAL; + int err; if (size != sizeof(outarg)) - goto err; + return -EINVAL; err = fuse_copy_one(cs, &outarg, sizeof(outarg)); if (err) - goto err; + return err; fuse_copy_finish(cs); return fuse_notify_poll_wakeup(fc, &outarg); - -err: - fuse_copy_finish(cs); - return err; } static int fuse_notify_inval_inode(struct fuse_conn *fc, unsigned int size, struct fuse_copy_state *cs) { struct fuse_notify_inval_inode_out outarg; - int err = -EINVAL; + int err; if (size != sizeof(outarg)) - goto err; + return -EINVAL; err = fuse_copy_one(cs, &outarg, sizeof(outarg)); if (err) - goto err; + return err; fuse_copy_finish(cs); down_read(&fc->killsb); @@ -1658,10 +1654,6 @@ static int fuse_notify_inval_inode(struct fuse_conn *fc, unsigned int size, outarg.off, outarg.len); up_read(&fc->killsb); return err; - -err: - fuse_copy_finish(cs); - return err; } static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size, @@ -1669,29 +1661,26 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size, { struct fuse_notify_inval_entry_out outarg; int err; - char *buf = NULL; + char *buf; struct qstr name; - err = -EINVAL; if (size < sizeof(outarg)) - goto err; + return -EINVAL; err = fuse_copy_one(cs, &outarg, sizeof(outarg)); if (err) - goto err; + return err; - err = -ENAMETOOLONG; if (outarg.namelen > fc->name_max) - goto err; + return -ENAMETOOLONG; err = -EINVAL; if (size != sizeof(outarg) + outarg.namelen + 1) - goto err; + return -EINVAL; - err = -ENOMEM; buf = kzalloc(outarg.namelen + 1, GFP_KERNEL); if (!buf) - goto err; + return -ENOMEM; name.name = buf; name.len = outarg.namelen; @@ -1704,12 +1693,8 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size, down_read(&fc->killsb); err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name, outarg.flags); up_read(&fc->killsb); - kfree(buf); - return err; - err: kfree(buf); - fuse_copy_finish(cs); return err; } @@ -1718,29 +1703,25 @@ static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size, { struct fuse_notify_delete_out outarg; int err; - char *buf = NULL; + char *buf; struct qstr name; - err = -EINVAL; if (size < sizeof(outarg)) - goto err; + return -EINVAL; err = fuse_copy_one(cs, &outarg, sizeof(outarg)); if (err) - goto err; + return err; - err = -ENAMETOOLONG; if (outarg.namelen > fc->name_max) - goto err; + return -ENAMETOOLONG; - err = -EINVAL; if (size != sizeof(outarg) + outarg.namelen + 1) - goto err; + return -EINVAL; - err = -ENOMEM; buf = kzalloc(outarg.namelen + 1, GFP_KERNEL); if (!buf) - goto err; + return -ENOMEM; name.name = buf; name.len = outarg.namelen; @@ -1753,12 +1734,8 @@ static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size, down_read(&fc->killsb); err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name, 0); up_read(&fc->killsb); - kfree(buf); - return err; - err: kfree(buf); - fuse_copy_finish(cs); return err; } @@ -1776,17 +1753,15 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, loff_t file_size; loff_t end; - err = -EINVAL; if (size < sizeof(outarg)) - goto out_finish; + return -EINVAL; err = fuse_copy_one(cs, &outarg, sizeof(outarg)); if (err) - goto out_finish; + return err; - err = -EINVAL; if (size - sizeof(outarg) != outarg.size) - goto out_finish; + return -EINVAL; nodeid = outarg.nodeid; @@ -1846,8 +1821,6 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, iput(inode); out_up_killsb: up_read(&fc->killsb); -out_finish: - fuse_copy_finish(cs); return err; } @@ -1962,13 +1935,12 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size, u64 nodeid; int err; - err = -EINVAL; if (size != sizeof(outarg)) - goto copy_finish; + return -EINVAL; err = fuse_copy_one(cs, &outarg, sizeof(outarg)); if (err) - goto copy_finish; + return err; fuse_copy_finish(cs); @@ -1984,10 +1956,6 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size, up_read(&fc->killsb); return err; - -copy_finish: - fuse_copy_finish(cs); - return err; } /* @@ -2098,7 +2066,6 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, return fuse_notify_inc_epoch(fc); default: - fuse_copy_finish(cs); return -EINVAL; } } -- 2.49.0 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 3/4] fuse: remove redundant calls to fuse_copy_finish() in fuse_notify() 2025-09-02 14:41 ` [PATCH 3/4] fuse: remove redundant calls to fuse_copy_finish() " Miklos Szeredi @ 2025-09-02 21:53 ` Joanne Koong 2025-09-03 10:25 ` Luis Henriques 1 sibling, 0 replies; 15+ messages in thread From: Joanne Koong @ 2025-09-02 21:53 UTC (permalink / raw) To: Miklos Szeredi; +Cc: linux-fsdevel, Jim Harris On Tue, Sep 2, 2025 at 7:44 AM Miklos Szeredi <mszeredi@redhat.com> wrote: > > Remove tail calls of fuse_copy_finish(), since it's now done from > fuse_dev_do_write(). > > No functional change. > > Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Reviewed-by: Joanne Koong <joannelkoong@gmail.com> > --- > fs/fuse/dev.c | 79 +++++++++++++++------------------------------------ > 1 file changed, 23 insertions(+), 56 deletions(-) > > diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c > index 85d05a5e40e9..1258acee9704 100644 > --- a/fs/fuse/dev.c > +++ b/fs/fuse/dev.c > @@ -1622,35 +1622,31 @@ static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size, > struct fuse_copy_state *cs) > { > struct fuse_notify_poll_wakeup_out outarg; > - int err = -EINVAL; > + int err; > > if (size != sizeof(outarg)) > - goto err; > + return -EINVAL; > > err = fuse_copy_one(cs, &outarg, sizeof(outarg)); > if (err) > - goto err; > + return err; > > fuse_copy_finish(cs); Maybe worth also removing fuse_copy_finish() here (and for the other notify handlers) since it's going to get called anyways? Thanks, Joanne > return fuse_notify_poll_wakeup(fc, &outarg); > - ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 3/4] fuse: remove redundant calls to fuse_copy_finish() in fuse_notify() 2025-09-02 14:41 ` [PATCH 3/4] fuse: remove redundant calls to fuse_copy_finish() " Miklos Szeredi 2025-09-02 21:53 ` Joanne Koong @ 2025-09-03 10:25 ` Luis Henriques 2025-09-03 10:43 ` Miklos Szeredi 1 sibling, 1 reply; 15+ messages in thread From: Luis Henriques @ 2025-09-03 10:25 UTC (permalink / raw) To: Miklos Szeredi; +Cc: linux-fsdevel, Jim Harris On Tue, Sep 02 2025, Miklos Szeredi wrote: > Remove tail calls of fuse_copy_finish(), since it's now done from > fuse_dev_do_write(). > > No functional change. > > Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> > --- > fs/fuse/dev.c | 79 +++++++++++++++------------------------------------ > 1 file changed, 23 insertions(+), 56 deletions(-) > > diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c > index 85d05a5e40e9..1258acee9704 100644 > --- a/fs/fuse/dev.c > +++ b/fs/fuse/dev.c > @@ -1622,35 +1622,31 @@ static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size, > struct fuse_copy_state *cs) > { > struct fuse_notify_poll_wakeup_out outarg; > - int err = -EINVAL; > + int err; > > if (size != sizeof(outarg)) > - goto err; > + return -EINVAL; > > err = fuse_copy_one(cs, &outarg, sizeof(outarg)); > if (err) > - goto err; > + return err; > > fuse_copy_finish(cs); > return fuse_notify_poll_wakeup(fc, &outarg); > - > -err: > - fuse_copy_finish(cs); > - return err; > } > > static int fuse_notify_inval_inode(struct fuse_conn *fc, unsigned int size, > struct fuse_copy_state *cs) > { > struct fuse_notify_inval_inode_out outarg; > - int err = -EINVAL; > + int err; > > if (size != sizeof(outarg)) > - goto err; > + return -EINVAL; > > err = fuse_copy_one(cs, &outarg, sizeof(outarg)); > if (err) > - goto err; > + return err; > fuse_copy_finish(cs); I wonder if these extra fuse_copy_finish() calls should also be removed. It doesn't seem to be a problem to call it twice, but maybe it's not needed, or am I missing something? This happens in a few places. Other than that, and FWIW, the series look good to me. Cheers, -- Luís > > down_read(&fc->killsb); > @@ -1658,10 +1654,6 @@ static int fuse_notify_inval_inode(struct fuse_conn *fc, unsigned int size, > outarg.off, outarg.len); > up_read(&fc->killsb); > return err; > - > -err: > - fuse_copy_finish(cs); > - return err; > } > > static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size, > @@ -1669,29 +1661,26 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size, > { > struct fuse_notify_inval_entry_out outarg; > int err; > - char *buf = NULL; > + char *buf; > struct qstr name; > > - err = -EINVAL; > if (size < sizeof(outarg)) > - goto err; > + return -EINVAL; > > err = fuse_copy_one(cs, &outarg, sizeof(outarg)); > if (err) > - goto err; > + return err; > > - err = -ENAMETOOLONG; > if (outarg.namelen > fc->name_max) > - goto err; > + return -ENAMETOOLONG; > > err = -EINVAL; > if (size != sizeof(outarg) + outarg.namelen + 1) > - goto err; > + return -EINVAL; > > - err = -ENOMEM; > buf = kzalloc(outarg.namelen + 1, GFP_KERNEL); > if (!buf) > - goto err; > + return -ENOMEM; > > name.name = buf; > name.len = outarg.namelen; > @@ -1704,12 +1693,8 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size, > down_read(&fc->killsb); > err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name, outarg.flags); > up_read(&fc->killsb); > - kfree(buf); > - return err; > - > err: > kfree(buf); > - fuse_copy_finish(cs); > return err; > } > > @@ -1718,29 +1703,25 @@ static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size, > { > struct fuse_notify_delete_out outarg; > int err; > - char *buf = NULL; > + char *buf; > struct qstr name; > > - err = -EINVAL; > if (size < sizeof(outarg)) > - goto err; > + return -EINVAL; > > err = fuse_copy_one(cs, &outarg, sizeof(outarg)); > if (err) > - goto err; > + return err; > > - err = -ENAMETOOLONG; > if (outarg.namelen > fc->name_max) > - goto err; > + return -ENAMETOOLONG; > > - err = -EINVAL; > if (size != sizeof(outarg) + outarg.namelen + 1) > - goto err; > + return -EINVAL; > > - err = -ENOMEM; > buf = kzalloc(outarg.namelen + 1, GFP_KERNEL); > if (!buf) > - goto err; > + return -ENOMEM; > > name.name = buf; > name.len = outarg.namelen; > @@ -1753,12 +1734,8 @@ static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size, > down_read(&fc->killsb); > err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name, 0); > up_read(&fc->killsb); > - kfree(buf); > - return err; > - > err: > kfree(buf); > - fuse_copy_finish(cs); > return err; > } > > @@ -1776,17 +1753,15 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, > loff_t file_size; > loff_t end; > > - err = -EINVAL; > if (size < sizeof(outarg)) > - goto out_finish; > + return -EINVAL; > > err = fuse_copy_one(cs, &outarg, sizeof(outarg)); > if (err) > - goto out_finish; > + return err; > > - err = -EINVAL; > if (size - sizeof(outarg) != outarg.size) > - goto out_finish; > + return -EINVAL; > > nodeid = outarg.nodeid; > > @@ -1846,8 +1821,6 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, > iput(inode); > out_up_killsb: > up_read(&fc->killsb); > -out_finish: > - fuse_copy_finish(cs); > return err; > } > > @@ -1962,13 +1935,12 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size, > u64 nodeid; > int err; > > - err = -EINVAL; > if (size != sizeof(outarg)) > - goto copy_finish; > + return -EINVAL; > > err = fuse_copy_one(cs, &outarg, sizeof(outarg)); > if (err) > - goto copy_finish; > + return err; > > fuse_copy_finish(cs); > > @@ -1984,10 +1956,6 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size, > up_read(&fc->killsb); > > return err; > - > -copy_finish: > - fuse_copy_finish(cs); > - return err; > } > > /* > @@ -2098,7 +2066,6 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, > return fuse_notify_inc_epoch(fc); > > default: > - fuse_copy_finish(cs); > return -EINVAL; > } > } > -- > 2.49.0 > ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 3/4] fuse: remove redundant calls to fuse_copy_finish() in fuse_notify() 2025-09-03 10:25 ` Luis Henriques @ 2025-09-03 10:43 ` Miklos Szeredi 0 siblings, 0 replies; 15+ messages in thread From: Miklos Szeredi @ 2025-09-03 10:43 UTC (permalink / raw) To: Luis Henriques; +Cc: Miklos Szeredi, linux-fsdevel, Jim Harris On Wed, 3 Sept 2025 at 12:26, Luis Henriques <luis@igalia.com> wrote: > I wonder if these extra fuse_copy_finish() calls should also be removed. > It doesn't seem to be a problem to call it twice, but maybe it's not > needed, or am I missing something? This happens in a few places. It's not a clear no-op, since the put_page() for the userspace buffer is being moved to after processing the notification. I don't think this should cause problems, but it's something that I wouldn't like to do without a bit of thought. Thanks, Miklos ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 4/4] fuse: add prune notification 2025-09-02 14:41 [PATCH 1/4] fuse: remove FUSE_NOTIFY_CODE_MAX from <uapi/linux/fuse.h> Miklos Szeredi 2025-09-02 14:41 ` [PATCH 2/4] fuse: fix possibly missing fuse_copy_finish() call in fuse_notify() Miklos Szeredi 2025-09-02 14:41 ` [PATCH 3/4] fuse: remove redundant calls to fuse_copy_finish() " Miklos Szeredi @ 2025-09-02 14:41 ` Miklos Szeredi 2025-09-02 22:40 ` Joanne Koong ` (2 more replies) 2025-09-02 21:31 ` [PATCH 1/4] fuse: remove FUSE_NOTIFY_CODE_MAX from <uapi/linux/fuse.h> Joanne Koong 3 siblings, 3 replies; 15+ messages in thread From: Miklos Szeredi @ 2025-09-02 14:41 UTC (permalink / raw) To: linux-fsdevel; +Cc: Jim Harris 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 Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> --- fs/fuse/dev.c | 39 +++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 6 ++++++ fs/fuse/inode.c | 11 +++++++++++ include/uapi/linux/fuse.h | 8 ++++++++ 4 files changed, 64 insertions(+) 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; } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 233c6111f768..fb6604120b53 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1413,6 +1413,12 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid, int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, u64 child_nodeid, struct qstr *name, u32 flags); +/* + * Try to prune this inode. If neither the inode itself nor dentries associated + * with this inode have any external reference, then the inode can be freed. + */ +void fuse_try_prune_one_inode(struct fuse_conn *fc, u64 nodeid); + int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file, bool isdir); diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 5b7897bf7e45..a4d361b34d06 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -585,6 +585,17 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid, return 0; } +void fuse_try_prune_one_inode(struct fuse_conn *fc, u64 nodeid) +{ + struct inode *inode; + + inode = fuse_ilookup(fc, nodeid, NULL); + if (!inode) + return; + d_prune_aliases(inode); + iput(inode); +} + bool fuse_lock_inode(struct inode *inode) { bool locked = false; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 30bf0846547f..c13e1f9a2f12 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -239,6 +239,7 @@ * 7.45 * - add FUSE_COPY_FILE_RANGE_64 * - add struct fuse_copy_file_range_out + * - add FUSE_NOTIFY_PRUNE */ #ifndef _LINUX_FUSE_H @@ -680,6 +681,7 @@ enum fuse_notify_code { FUSE_NOTIFY_DELETE = 6, FUSE_NOTIFY_RESEND = 7, FUSE_NOTIFY_INC_EPOCH = 8, + FUSE_NOTIFY_PRUNE = 9, }; /* The read buffer is required to be at least 8k, but may be much larger */ @@ -1118,6 +1120,12 @@ struct fuse_notify_retrieve_in { uint64_t dummy4; }; +struct fuse_notify_prune_out { + uint32_t count; + uint32_t padding; + uint64_t spare; +}; + struct fuse_backing_map { int32_t fd; uint32_t flags; -- 2.49.0 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 4/4] fuse: add prune notification 2025-09-02 14:41 ` [PATCH 4/4] fuse: add prune notification Miklos Szeredi @ 2025-09-02 22:40 ` Joanne Koong 2025-09-04 21:07 ` Jim Harris 2025-10-01 12:28 ` Bernd Schubert 2 siblings, 0 replies; 15+ messages in thread From: Joanne Koong @ 2025-09-02 22:40 UTC (permalink / raw) To: Miklos Szeredi; +Cc: linux-fsdevel, Jim Harris On Tue, Sep 2, 2025 at 7:44 AM Miklos Szeredi <mszeredi@redhat.com> wrote: > > 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 > > Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Reviewed-by: Joanne Koong <joannelkoong@gmail.com> ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 4/4] fuse: add prune notification 2025-09-02 14:41 ` [PATCH 4/4] fuse: add prune notification Miklos Szeredi 2025-09-02 22:40 ` Joanne Koong @ 2025-09-04 21:07 ` Jim Harris 2025-10-01 1:02 ` Jim Harris 2025-10-01 12:28 ` Bernd Schubert 2 siblings, 1 reply; 15+ messages in thread From: Jim Harris @ 2025-09-04 21:07 UTC (permalink / raw) To: Miklos Szeredi; +Cc: linux-fsdevel@vger.kernel.org [-- Attachment #1: Type: text/plain, Size: 1188 bytes --] > On Sep 2, 2025, at 7:41 AM, Miklos Szeredi <mszeredi@redhat.com> wrote: > > External email: Use caution opening links or attachments > > > 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 > > Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Thanks Miklos, this looks great. I’ll give this a spin in our virtio-fs FUSE device. <snip> [-- Attachment #2: smime.p7s --] [-- Type: application/pkcs7-signature, Size: 4312 bytes --] ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 4/4] fuse: add prune notification 2025-09-04 21:07 ` Jim Harris @ 2025-10-01 1:02 ` Jim Harris 2025-10-14 19:29 ` Jim Harris 0 siblings, 1 reply; 15+ messages in thread From: Jim Harris @ 2025-10-01 1:02 UTC (permalink / raw) To: Miklos Szeredi; +Cc: linux-fsdevel@vger.kernel.org [-- Attachment #1: Type: text/plain, Size: 1536 bytes --] > On Sep 4, 2025, at 2:07 PM, Jim Harris <jiharris@nvidia.com> wrote: > > > >> On Sep 2, 2025, at 7:41 AM, Miklos Szeredi <mszeredi@redhat.com> wrote: >> >> External email: Use caution opening links or attachments >> >> >> 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 >> >> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> > > Thanks Miklos, this looks great. I’ll give this a spin in our virtio-fs FUSE device. Hi Miklos, I was finally was able to give these patches a spin. They work great, a nice simplification compared to FUSE_NOTIFY_INVAL_ENTRY. Feel free to add: Tested-by: Jim Harris <jim.harris@nvidia.com> Best regards, -Jim [-- Attachment #2: smime.p7s --] [-- Type: application/pkcs7-signature, Size: 4312 bytes --] ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 4/4] fuse: add prune notification 2025-10-01 1:02 ` Jim Harris @ 2025-10-14 19:29 ` Jim Harris 0 siblings, 0 replies; 15+ messages in thread From: Jim Harris @ 2025-10-14 19:29 UTC (permalink / raw) To: Miklos Szeredi; +Cc: linux-fsdevel@vger.kernel.org [-- Attachment #1: Type: text/plain, Size: 1536 bytes --] > On Sep 4, 2025, at 2:07 PM, Jim Harris <jiharris@nvidia.com> wrote: > > > >> On Sep 2, 2025, at 7:41 AM, Miklos Szeredi <mszeredi@redhat.com> wrote: >> >> External email: Use caution opening links or attachments >> >> >> 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 >> >> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> > > Thanks Miklos, this looks great. I’ll give this a spin in our virtio-fs FUSE device. Hi Miklos, I was finally was able to give these patches a spin. They work great, a nice simplification compared to FUSE_NOTIFY_INVAL_ENTRY. Feel free to add: Tested-by: Jim Harris <jim.harris@nvidia.com> Best regards, -Jim [-- Attachment #2: smime.p7s --] [-- Type: application/pkcs7-signature, Size: 4312 bytes --] ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 4/4] fuse: add prune notification 2025-09-02 14:41 ` [PATCH 4/4] fuse: add prune notification Miklos Szeredi 2025-09-02 22:40 ` Joanne Koong 2025-09-04 21:07 ` Jim Harris @ 2025-10-01 12:28 ` Bernd Schubert 2025-10-01 14:44 ` Miklos Szeredi 2 siblings, 1 reply; 15+ messages in thread From: Bernd Schubert @ 2025-10-01 12:28 UTC (permalink / raw) To: Miklos Szeredi, linux-fsdevel; +Cc: Jim Harris, Yong Ze Chen Hi Miklos, I'm sorry for late review. On 9/2/25 16:41, Miklos Szeredi wrote: > 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 It actually solves another issue we had run into. FUSE_NOTIFY_INVAL_ENTRY takes the parent dir lock. Problem with the parent lock is our DLM and lock order. I.e. kernel holds parent lock for some dir operation, calls into fuse-server, which tries to acquires the DLM lock. DLM lock might be taken already and is doing FUSE_NOTIFY_INVAL_ENTRY - deadlock through the parent dir lock. We are running with an additional patch from Yong Ze, which is still for single entries. Thanks, Bernd > > Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> > --- > fs/fuse/dev.c | 39 +++++++++++++++++++++++++++++++++++++++ > fs/fuse/fuse_i.h | 6 ++++++ > fs/fuse/inode.c | 11 +++++++++++ > include/uapi/linux/fuse.h | 8 ++++++++ > 4 files changed, 64 insertions(+) > > 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; > } > diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h > index 233c6111f768..fb6604120b53 100644 > --- a/fs/fuse/fuse_i.h > +++ b/fs/fuse/fuse_i.h > @@ -1413,6 +1413,12 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid, > int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, > u64 child_nodeid, struct qstr *name, u32 flags); > > +/* > + * Try to prune this inode. If neither the inode itself nor dentries associated > + * with this inode have any external reference, then the inode can be freed. > + */ > +void fuse_try_prune_one_inode(struct fuse_conn *fc, u64 nodeid); > + > int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file, > bool isdir); > > diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c > index 5b7897bf7e45..a4d361b34d06 100644 > --- a/fs/fuse/inode.c > +++ b/fs/fuse/inode.c > @@ -585,6 +585,17 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid, > return 0; > } > > +void fuse_try_prune_one_inode(struct fuse_conn *fc, u64 nodeid) > +{ > + struct inode *inode; > + > + inode = fuse_ilookup(fc, nodeid, NULL); > + if (!inode) > + return; > + d_prune_aliases(inode); > + iput(inode); I also wonder if FUSE_NOTIFY_PRUNE shouldn't handle dir inodes and call d_invalidate() to also prune child entries. Thanks, Bernd ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 4/4] fuse: add prune notification 2025-10-01 12:28 ` Bernd Schubert @ 2025-10-01 14:44 ` Miklos Szeredi 0 siblings, 0 replies; 15+ messages in thread From: Miklos Szeredi @ 2025-10-01 14:44 UTC (permalink / raw) To: Bernd Schubert; +Cc: Miklos Szeredi, linux-fsdevel, Jim Harris, Yong Ze Chen On Wed, 1 Oct 2025 at 14:29, Bernd Schubert <bernd@bsbernd.com> wrote: > I also wonder if FUSE_NOTIFY_PRUNE shouldn't handle dir inodes and > call d_invalidate() to also prune child entries. d_invalidate unconditionally unhashes the dentry, which this interface should only do if the dentry is unused. However, it could call shrink_dcache_parent() before trying to prune the dentry. Thanks, Miklos ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/4] fuse: remove FUSE_NOTIFY_CODE_MAX from <uapi/linux/fuse.h> 2025-09-02 14:41 [PATCH 1/4] fuse: remove FUSE_NOTIFY_CODE_MAX from <uapi/linux/fuse.h> Miklos Szeredi ` (2 preceding siblings ...) 2025-09-02 14:41 ` [PATCH 4/4] fuse: add prune notification Miklos Szeredi @ 2025-09-02 21:31 ` Joanne Koong 3 siblings, 0 replies; 15+ messages in thread From: Joanne Koong @ 2025-09-02 21:31 UTC (permalink / raw) To: Miklos Szeredi; +Cc: linux-fsdevel, Jim Harris On Tue, Sep 2, 2025 at 7:46 AM Miklos Szeredi <mszeredi@redhat.com> wrote: > > Constants that change value from version to version have no place in an > interface definition. > > Hopefully this won't break anything. > > Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Reviewed-by: Joanne Koong <joannelkoong@gmail.com> > --- > include/uapi/linux/fuse.h | 1 - > 1 file changed, 1 deletion(-) > > diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h > index 6b9fb8b08768..30bf0846547f 100644 > --- a/include/uapi/linux/fuse.h > +++ b/include/uapi/linux/fuse.h > @@ -680,7 +680,6 @@ enum fuse_notify_code { > FUSE_NOTIFY_DELETE = 6, > FUSE_NOTIFY_RESEND = 7, > FUSE_NOTIFY_INC_EPOCH = 8, > - FUSE_NOTIFY_CODE_MAX, > }; > > /* The read buffer is required to be at least 8k, but may be much larger */ > -- > 2.49.0 > > ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2025-10-14 19:29 UTC | newest] Thread overview: 15+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-09-02 14:41 [PATCH 1/4] fuse: remove FUSE_NOTIFY_CODE_MAX from <uapi/linux/fuse.h> Miklos Szeredi 2025-09-02 14:41 ` [PATCH 2/4] fuse: fix possibly missing fuse_copy_finish() call in fuse_notify() Miklos Szeredi 2025-09-02 21:48 ` Joanne Koong 2025-09-02 14:41 ` [PATCH 3/4] fuse: remove redundant calls to fuse_copy_finish() " Miklos Szeredi 2025-09-02 21:53 ` Joanne Koong 2025-09-03 10:25 ` Luis Henriques 2025-09-03 10:43 ` Miklos Szeredi 2025-09-02 14:41 ` [PATCH 4/4] fuse: add prune notification Miklos Szeredi 2025-09-02 22:40 ` Joanne Koong 2025-09-04 21:07 ` Jim Harris 2025-10-01 1:02 ` Jim Harris 2025-10-14 19:29 ` Jim Harris 2025-10-01 12:28 ` Bernd Schubert 2025-10-01 14:44 ` Miklos Szeredi 2025-09-02 21:31 ` [PATCH 1/4] fuse: remove FUSE_NOTIFY_CODE_MAX from <uapi/linux/fuse.h> Joanne Koong
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).