<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Hello, </div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
#1, yeah, it's intentional, both close_wait and fuse_link_rw_file() are our proprietary extention that are not applicable to other client.</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
the FIEMAP case is a different issue that's been fix in v3</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
#2 #3. again it's intentional.</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
#4. there is no no_open case in vStorage, the no_open case in non-vStorage daemon is irrelevant here</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
#5. it's exactly what we want. </div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
#6. Addressed in v3. </div>
<div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<hr style="display: inline-block; width: 98%;">
<div id="divRplyFwdMsg">
<div style="direction: ltr; font-family: Calibri, sans-serif; font-size: 11pt; color: rgb(0, 0, 0);">
<b>From:</b> Konstantin Khorenko <khorenko@virtuozzo.com><br>
<b>Sent:</b> 19 June 2026 04:11<br>
<b>To:</b> Kui Liu <kui.liu@virtuozzo.com>; devel@openvz.org <devel@openvz.org><br>
<b>Cc:</b> Alexey Kuznetsov <kuznet@virtuozzo.com>; Andrey Zaitsev <azaitsev@virtuozzo.com><br>
<b>Subject:</b> Re: [PATCH VZ10 v2] fs/fuse: order FUSE_OPEN reply against FUSE_NOTIFY_INVAL_FILES</div>
<div style="direction: ltr;"> </div>
</div>
<div style="font-size: 11pt;"> Concerns (in decreasing order of importance)<br>
<br>
1. fuse_link_rw_file() is now under if (fc->close_wait) - possibly an unintended regression.<br>
Previously fuse_link_rw_file() was called unconditionally in fuse_finish_open(), and fi->rw_files was populated for all<br>
fuse mounts. Now both the linking and the wait are hidden under close_wait. But rw_files is read not only by invalidation:<br>
<br>
fs/fuse/file.c:3864 (FIEMAP helper) - finds any ff for the inode among rw_files<br>
<br>
For a non-close_wait mount where only readers are open, this path will now get an empty list where before it would have<br>
found an ff. The "Only apply to vStorage" comment refers to the wait, but the braces captured the linking as well. Most<br>
likely fuse_link_rw_file(file) should stay unconditional, and only the new fuse_wait_on_inval_files() should be hidden<br>
under close_wait. Please confirm that capturing the linking is intentional.<br>
<br>
2. Per-fuse_dev write_seq - the comparison is valid only if the OPEN reply and the INVAL_FILES notification go over the<br>
same fd.<br>
Each /dev/fuse clone (FUSE_DEV_IOC_CLONE) is a separate fuse_dev with its own counter starting at 0 (fuse_dev_alloc() now<br>
explicitly sets write_seq = 0). If a multithreaded daemon sends the OPEN reply on one device and the notification on<br>
another, then ff->open_seq and fi->inval_files_seq are taken from independent counters and are incomparable - the<br>
comparison result is random. This is the key correctness question: does the vStorage daemon guarantee that for a given<br>
inode both messages go over the same fud? If not - the mechanism is unsafe. This requirement should at least be pinned<br>
down with a comment in the code.<br>
<br>
3. fud->write_seq++ is non-atomic.<br>
The increment is done without a lock. Parallel write()s to the same fd will race on the ++ (lost/duplicated seq). In<br>
practice there is usually one thread per clone device, so it is fine, but formally this is a data race on a shared field -<br>
it is worth at least noting the "single writer per fud" assumption in a comment, or using WRITE_ONCE.<br>
<br>
4. The default open_seq = 0 is a latent fragility.<br>
fuse_file_alloc() zeroes ff via kzalloc, and open_seq stays 0 on paths where OPEN is not sent (fc->no_open). Then 0 <<br>
inval_files_seq will falsely flag the open as stale forever after the first invalidation. For close_wait filesystems OPEN<br>
is always implemented, so it does not actually trigger, but the "no_open + invalidate_files" combination would be broken.<br>
One could explicitly initialize open_seq to some "fresh" value on the no-open path, or at least leave a comment.<br>
<br>
5. The FAIL loop in fuse_invalidate_files() is seq-blind - a benign false positive.<br>
A valid new open (open_seq > inval_files_seq) that managed to link into rw_files before the notification ran will be<br>
flagged FUSE_S_FAIL_IMMEDIATELY by the unconditional loop -> an extra reopen on the client. Not a regression (the loop was<br>
unconditional in the old code too) and not data corruption, but the loop could be made seq-aware (if (ff->open_seq <=<br>
fi->inval_files_seq)) so it does not hit valid races. Your call.<br>
<br>
6. Style: uint64_t instead of the kernel u64 in the fuse_send_open() signature and in the struct fields. A minor thing,<br>
but u64 is more consistent.<br>
<br>
--<br>
Best regards,<br>
<br>
Konstantin Khorenko,<br>
Virtuozzo Linux Kernel Team<br>
<br>
On 6/16/26 18:51, Liu Kui wrote:<br>
> The userspace daemon's send order of a FUSE_OPEN reply and a<br>
> FUSE_NOTIFY_INVAL_FILES notification can be reversed by the time the<br>
> kernel processes them. The kernel must distinguish the two cases:<br>
><br>
> - reply sent before the notification: the open is stale and must<br>
> be flagged FUSE_S_FAIL_IMMEDIATELY;<br>
> - reply sent after the notification: the open is valid but must<br>
> wait for fuse_inval_files_work() to complete before returning,<br>
> or the caller hits IO errors on an inode still being invalidated.<br>
><br>
> The FUSE_I_INVAL_FILES bit carries no ordering and cannot tell these<br>
> apart. Add a per-fuse_dev write_seq bumped on every reply/notification<br>
> write, saved to ff->open_seq on OPEN replies and fi->inval_files_seq<br>
> on FUSE_NOTIFY_INVAL_FILES, and compared in fuse_link_rw_file() to<br>
> flag stale opens. fuse_finish_open() then waits on FUSE_I_INVAL_FILES<br>
> for the remaining case.<br>
><br>
> <a href="https://virtuozzo.atlassian.net/browse/VSTOR-133093" id="OWAeea710d0-5401-711d-880e-fbf9e1282d63" class="OWAAutoLink" data-auth="NotApplicable">
https://virtuozzo.atlassian.net/browse/VSTOR-133093</a><br>
> Signed-off-by: Liu Kui <kui.liu@virtuozzo.com><br>
> ---<br>
> Changes in v2:<br>
> - Rename write_counter / *_ts fields to write_seq / *_seq throughout<br>
> (write_counter -> write_seq, reply_ts -> reply_seq, open_ts -><br>
> open_seq, inval_file_ts -> inval_files_seq). The values are a<br>
> logical sequence number, not a wall-clock timestamp; "_seq" is the<br>
> kernel-idiomatic name for a Lamport-style monotonic counter. Also<br>
> pluralise inval_file_ts -> inval_files_seq to match the surrounding<br>
> inval_files_* family.<br>
> - Propagate args.reply_seq into ff->open_seq from fuse_create_open()<br>
> in dir.c (the create+open path missed it in v1).<br>
> - Gate the new fuse_link_rw_file() / fuse_wait_on_inval_files() block<br>
> in fuse_finish_open() behind fc->close_wait so only vStorage runs<br>
> the new ordering work.<br>
> - Also initialise fi->inval_files_seq in fuse_init_inode(), not only<br>
> in fuse_alloc_inode().<br>
> - Update kernel-doc and inline comments to use "sequence number" /<br>
> write_seq wording instead of "timestamp" / "write counter".<br>
><br>
> fs/fuse/dev.c | 17 +++++++++----<br>
> fs/fuse/dir.c | 1 +<br>
> fs/fuse/file.c | 65 +++++++++++++++++++++++++++++++++++-------------<br>
> fs/fuse/fuse_i.h | 14 ++++++++++-<br>
> fs/fuse/inode.c | 26 ++++++++++++-------<br>
> 5 files changed, 91 insertions(+), 32 deletions(-)<br>
><br>
> diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c<br>
> index 4fcfd644dcf6..3304082a635b 100644<br>
> --- a/fs/fuse/dev.c<br>
> +++ b/fs/fuse/dev.c<br>
> @@ -2003,9 +2003,10 @@ static int fuse_notify_resend(struct fuse_conn *fc)<br>
> return 0;<br>
> }<br>
> <br>
> -static int fuse_notify_inval_files(struct fuse_conn *fc, unsigned int size,<br>
> +static int fuse_notify_inval_files(struct fuse_dev *fud, unsigned int size,<br>
> struct fuse_copy_state *cs)<br>
> {<br>
> + struct fuse_conn *fc = fud->fc;<br>
> struct fuse_notify_inval_files_out outarg;<br>
> int err = -EINVAL;<br>
> <br>
> @@ -2019,7 +2020,7 @@ static int fuse_notify_inval_files(struct fuse_conn *fc, unsigned int size,<br>
> <br>
> down_read(&fc->killsb);<br>
> <br>
> - err = fuse_invalidate_files(fc, outarg.ino);<br>
> + err = fuse_invalidate_files(fud, outarg.ino);<br>
> <br>
> up_read(&fc->killsb);<br>
> return err;<br>
> @@ -2029,9 +2030,11 @@ static int fuse_notify_inval_files(struct fuse_conn *fc, unsigned int size,<br>
> return err;<br>
> }<br>
> <br>
> -static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,<br>
> +static int fuse_notify(struct fuse_dev *fud, enum fuse_notify_code code,<br>
> unsigned int size, struct fuse_copy_state *cs)<br>
> {<br>
> + struct fuse_conn *fc = fud->fc;<br>
> +<br>
> /* Don't try to move pages (yet) */<br>
> cs->move_pages = 0;<br>
> <br>
> @@ -2062,7 +2065,7 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,<br>
> return fuse_notify_resend(fc);<br>
> <br>
> case FUSE_NOTIFY_INVAL_FILES:<br>
> - return fuse_notify_inval_files(fc, size, cs);<br>
> + return fuse_notify_inval_files(fud, size, cs);<br>
> <br>
> default:<br>
> fuse_copy_finish(cs);<br>
> @@ -2375,6 +2378,9 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,<br>
> struct fuse_req *req;<br>
> struct fuse_out_header oh;<br>
> <br>
> + /* Monotonic sequence number matching the daemon's reply/notification send order. */<br>
> + fud->write_seq++;<br>
> +<br>
> err = -EINVAL;<br>
> if (nbytes < sizeof(struct fuse_out_header))<br>
> goto out;<br>
> @@ -2392,7 +2398,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,<br>
> * and error contains notification code.<br>
> */<br>
> if (!oh.unique) {<br>
> - err = fuse_notify(fc, oh.error, nbytes - sizeof(oh), cs);<br>
> + err = fuse_notify(fud, oh.error, nbytes - sizeof(oh), cs);<br>
> goto out;<br>
> }<br>
> <br>
> @@ -2462,6 +2468,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,<br>
> list_del_init(&req->list);<br>
> spin_unlock(&fpq->lock);<br>
> <br>
> + req->args->reply_seq = fud->write_seq;<br>
> fuse_request_end(req);<br>
> out:<br>
> return err ? err : nbytes;<br>
> diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c<br>
> index 6637914c8f28..45ee4f1e4b68 100644<br>
> --- a/fs/fuse/dir.c<br>
> +++ b/fs/fuse/dir.c<br>
> @@ -693,6 +693,7 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir,<br>
> ff->fh = outopenp->fh;<br>
> ff->nodeid = outentry.nodeid;<br>
> ff->open_flags = outopenp->open_flags;<br>
> + ff->open_seq = args.reply_seq;<br>
> inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,<br>
> &outentry.attr, ATTR_TIMEOUT(&outentry), 0);<br>
> if (!inode) {<br>
> diff --git a/fs/fuse/file.c b/fs/fuse/file.c<br>
> index edea59ee4839..504492845159 100644<br>
> --- a/fs/fuse/file.c<br>
> +++ b/fs/fuse/file.c<br>
> @@ -45,10 +45,11 @@ static DECLARE_WORK(fuse_fput_work, fuse_fput_routine);<br>
> <br>
> static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,<br>
> unsigned int open_flags, int opcode,<br>
> - struct fuse_open_out *outargp)<br>
> + struct fuse_open_out *outargp, uint64_t *reply_seq)<br>
> {<br>
> struct fuse_open_in inarg;<br>
> FUSE_ARGS(args);<br>
> + int err;<br>
> <br>
> memset(&inarg, 0, sizeof(inarg));<br>
> inarg.flags = open_flags & ~(O_CREAT | O_EXCL | O_NOCTTY);<br>
> @@ -69,7 +70,14 @@ static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,<br>
> args.out_args[0].size = sizeof(*outargp);<br>
> args.out_args[0].value = outargp;<br>
> <br>
> - return fuse_simple_request(fm, &args);<br>
> + err = fuse_simple_request(fm, &args);<br>
> + /*<br>
> + * Propagate the dev write_seq so fuse_link_rw_file() can order<br>
> + * this open against any concurrent FUSE_NOTIFY_INVAL_FILES.<br>
> + */<br>
> + *reply_seq = args.reply_seq;<br>
> +<br>
> + return err;<br>
> }<br>
> <br>
> struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release)<br>
> @@ -192,7 +200,7 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,<br>
> struct fuse_open_out *outargp = &ff->args->open_outarg;<br>
> int err;<br>
> <br>
> - err = fuse_send_open(fm, nodeid, open_flags, opcode, outargp);<br>
> + err = fuse_send_open(fm, nodeid, open_flags, opcode, outargp, &ff->open_seq);<br>
> if (!err) {<br>
> ff->fh = outargp->fh;<br>
> ff->open_flags = outargp->open_flags;<br>
> @@ -245,6 +253,18 @@ static void fuse_link_write_file(struct file *file)<br>
> spin_unlock(&fi->lock);<br>
> }<br>
> <br>
> +static int fuse_wait_on_inval_files(struct inode *inode)<br>
> +{<br>
> + struct fuse_inode *fi = get_fuse_inode(inode);<br>
> + struct fuse_conn *fc = get_fuse_mount(inode)->fc;<br>
> +<br>
> + if (likely(!test_bit(FUSE_I_INVAL_FILES, &fi->state)))<br>
> + return 0;<br>
> +<br>
> + fuse_ktrace(fc, "waiting for invalidate_files on [%llu] to complete", fi->nodeid);<br>
> + return wait_on_bit(&fi->state, FUSE_I_INVAL_FILES, TASK_KILLABLE);<br>
> +}<br>
> +<br>
> static void fuse_link_rw_file(struct file *file)<br>
> {<br>
> struct inode *inode = file_inode(file);<br>
> @@ -252,14 +272,17 @@ static void fuse_link_rw_file(struct file *file)<br>
> struct fuse_file *ff = file->private_data;<br>
> <br>
> spin_lock(&fi->lock);<br>
> - if (unlikely(test_bit(FUSE_I_INVAL_FILES, &fi->state))) {<br>
> - spin_lock(&ff->lock);<br>
> - set_bit(FUSE_S_FAIL_IMMEDIATELY, &ff->ff_state);<br>
> - spin_unlock(&ff->lock);<br>
> - fuse_ktrace(ff->fm->fc, "fuse_file[%llu] --> invalidate_file on [%llu] pending", ff->fh, ff->nodeid);<br>
> - }<br>
> if (list_empty(&ff->rw_entry))<br>
> list_add(&ff->rw_entry, &fi->rw_files);<br>
> +<br>
> + /*<br>
> + * If this open reply was received before the last<br>
> + * FUSE_NOTIFY_INVAL_FILES on this inode, the open is<br>
> + * stale, fail it immediately.<br>
> + */<br>
> + if (ff->open_seq < fi->inval_files_seq)<br>
> + set_bit(FUSE_S_FAIL_IMMEDIATELY, &ff->ff_state);<br>
> +<br>
> spin_unlock(&fi->lock);<br>
> }<br>
> <br>
> @@ -285,9 +308,20 @@ int fuse_finish_open(struct inode *inode, struct file *file)<br>
> if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache)<br>
> fuse_link_write_file(file);<br>
> <br>
> - fuse_link_rw_file(file);<br>
> + /* Only apply to vStorage */<br>
> + if (fc->close_wait) {<br>
> + fuse_link_rw_file(file);<br>
> <br>
> - return 0;<br>
> + /*<br>
> + * Wait for fuse_inval_files_work() on this inode to complete<br>
> + * before returning, otherwise the caller can hit IO errors<br>
> + * on an inode still being invalidated.<br>
> + */<br>
> + if (!test_bit(FUSE_S_FAIL_IMMEDIATELY, &ff->ff_state))<br>
> + err = fuse_wait_on_inval_files(inode);<br>
> + }<br>
> +<br>
> + return err;<br>
> }<br>
> <br>
> static void fuse_truncate_update_attr(struct inode *inode, struct file *file)<br>
> @@ -320,12 +354,9 @@ static int fuse_open(struct inode *inode, struct file *file)<br>
> if ((file->f_flags & O_DIRECT) && !fc->direct_enable)<br>
> return -EINVAL;<br>
> <br>
> - if (unlikely(test_bit(FUSE_I_INVAL_FILES, &fi->state))) {<br>
> - fuse_ktrace(fc, "waiting for invalidate_file on [%llu] to complete", fi->nodeid);<br>
> - err = wait_on_bit(&fi->state, FUSE_I_INVAL_FILES, TASK_KILLABLE);<br>
> - if (err)<br>
> - return err;<br>
> - }<br>
> + err = fuse_wait_on_inval_files(inode);<br>
> + if (err)<br>
> + return err;<br>
> <br>
> err = generic_file_open(inode, file);<br>
> if (err)<br>
> diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h<br>
> index 966c8e6c2ab7..693ec756dcf3 100644<br>
> --- a/fs/fuse/fuse_i.h<br>
> +++ b/fs/fuse/fuse_i.h<br>
> @@ -218,6 +218,9 @@ struct fuse_inode {<br>
> <br>
> /** Entry on fc->inval_files_list list */<br>
> struct list_head inval_files_entry;<br>
> +<br>
> + /** fud->write_seq when the last FUSE_NOTIFY_INVAL_FILES notification was received */<br>
> + uint64_t inval_files_seq;<br>
> };<br>
> <br>
> /** FUSE inode state bits */<br>
> @@ -315,6 +318,9 @@ struct fuse_file {<br>
> <br>
> /** List of requests that may be killed **/<br>
> struct list_head revoke_list;<br>
> +<br>
> + /** fud->write_seq when the FUSE_OPEN reply was received */<br>
> + uint64_t open_seq;<br>
> };<br>
> <br>
> /** FUSE file states (ff_state) */<br>
> @@ -379,6 +385,9 @@ struct fuse_args {<br>
> <br>
> /** Fuse file used in the request or NULL*/<br>
> struct fuse_file *ff;<br>
> +<br>
> + /** fud->write_seq when the reply was received */<br>
> + uint64_t reply_seq;<br>
> };<br>
> <br>
> struct fuse_args_pages {<br>
> @@ -625,6 +634,9 @@ struct fuse_dev {<br>
> <br>
> /** list entry on fc->devices */<br>
> struct list_head entry;<br>
> +<br>
> + /** Monotonic sequence number, bumped on every reply/notification write */<br>
> + uint64_t write_seq;<br>
> };<br>
> <br>
> enum fuse_dax_mode {<br>
> @@ -1604,7 +1616,7 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,<br>
> * File-system tells the kernel to invalidate all fuse-files (and cache)<br>
> * for the given node id.<br>
> */<br>
> -int fuse_invalidate_files(struct fuse_conn *fc, u64 nodeid);<br>
> +int fuse_invalidate_files(struct fuse_dev *fud, u64 nodeid);<br>
> <br>
> int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file,<br>
> bool isdir);<br>
> diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c<br>
> index 1e0c86f4b37a..841dfaca96a7 100644<br>
> --- a/fs/fuse/inode.c<br>
> +++ b/fs/fuse/inode.c<br>
> @@ -118,6 +118,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)<br>
> fi->submount_lookup = NULL;<br>
> fi->i_size_unstable = 0;<br>
> fi->private = NULL;<br>
> + fi->inval_files_seq = 0;<br>
> INIT_LIST_HEAD(&fi->rw_files);<br>
> INIT_LIST_HEAD(&fi->inval_files_entry);<br>
> mutex_init(&fi->mutex);<br>
> @@ -424,6 +425,7 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr,<br>
> struct fuse_inode *fi = get_fuse_inode(inode);<br>
> <br>
> fi->num_openers = 0;<br>
> + fi->inval_files_seq = 0;<br>
> inode->i_mode = attr->mode & S_IFMT;<br>
> inode->i_size = attr->size;<br>
> inode_set_mtime(inode, attr->mtime, attr->mtimensec);<br>
> @@ -623,7 +625,7 @@ static void fuse_inval_files_work(struct work_struct *w)<br>
> <br>
> fi = list_first_entry(&inval_files_list, struct fuse_inode, inval_files_entry);<br>
> list_del(&fi->inval_files_entry);<br>
> - fuse_ktrace(fc, "invalidate_file on [%llu] starts", fi->nodeid);<br>
> + fuse_ktrace(fc, "invalidate_files on [%llu] starts", fi->nodeid);<br>
> <br>
> spin_lock(&fi->lock);<br>
> list_for_each_entry(ff, &fi->rw_files, rw_entry)<br>
> @@ -640,15 +642,16 @@ static void fuse_inval_files_work(struct work_struct *w)<br>
> wake_up_bit(&fi->state, FUSE_I_INVAL_FILES);<br>
> spin_unlock(&fi->lock);<br>
> <br>
> - fuse_ktrace(fc, "invalidate_file on [%llu] ends", fi->nodeid);<br>
> + fuse_ktrace(fc, "invalidate_files on [%llu] ends", fi->nodeid);<br>
> iput(&fi->inode);<br>
> }<br>
> <br>
> fuse_drop_waiting(fc);<br>
> }<br>
> <br>
> -int fuse_invalidate_files(struct fuse_conn *fc, u64 nodeid)<br>
> +int fuse_invalidate_files(struct fuse_dev *fud, u64 nodeid)<br>
> {<br>
> + struct fuse_conn *fc = fud->fc;<br>
> struct inode *inode;<br>
> struct fuse_inode *fi;<br>
> struct fuse_file *ff;<br>
> @@ -666,19 +669,23 @@ int fuse_invalidate_files(struct fuse_conn *fc, u64 nodeid)<br>
> <br>
> fi = get_fuse_inode(inode);<br>
> <br>
> - /* Mark that invalidate files is in progress */<br>
> + /*<br>
> + * Save this notification's write_seq and fail every fuse_file<br>
> + * currently linked on the inode.<br>
> + */<br>
> spin_lock(&fi->lock);<br>
> + fi->inval_files_seq = fud->write_seq;<br>
> + list_for_each_entry(ff, &fi->rw_files, rw_entry) {<br>
> + spin_lock(&ff->lock);<br>
> + set_bit(FUSE_S_FAIL_IMMEDIATELY, &ff->ff_state);<br>
> + spin_unlock(&ff->lock);<br>
> + }<br>
> if (test_bit(FUSE_I_INVAL_FILES, &fi->state)) {<br>
> spin_unlock(&fi->lock);<br>
> iput(inode);<br>
> return 0;<br>
> }<br>
> set_bit(FUSE_I_INVAL_FILES, &fi->state);<br>
> - list_for_each_entry(ff, &fi->rw_files, rw_entry) {<br>
> - spin_lock(&ff->lock);<br>
> - set_bit(FUSE_S_FAIL_IMMEDIATELY, &ff->ff_state);<br>
> - spin_unlock(&ff->lock);<br>
> - }<br>
> fi->i_size_unstable = 1;<br>
> spin_unlock(&fi->lock);<br>
> <br>
> @@ -1921,6 +1928,7 @@ struct fuse_dev *fuse_dev_alloc(void)<br>
> <br>
> fud->pq.processing = pq;<br>
> fuse_pqueue_init(&fud->pq);<br>
> + fud->write_seq = 0;<br>
> <br>
> return fud;<br>
> }<br>
<br>
</div>
</body>
</html>