diff options
-rw-r--r-- | booster/src/booster-fd.h | 1 | ||||
-rw-r--r-- | libglusterfs/src/fd.h | 1 | ||||
-rw-r--r-- | xlators/mount/fuse/src/fuse-bridge.c | 55 |
3 files changed, 52 insertions, 5 deletions
diff --git a/booster/src/booster-fd.h b/booster/src/booster-fd.h index 43592e8255a..0ccc28121af 100644 --- a/booster/src/booster-fd.h +++ b/booster/src/booster-fd.h @@ -33,6 +33,7 @@ struct _fd { pid_t pid; int32_t flags; int32_t refcount; + uint64_t flush_unique; struct list_head inode_list; struct _inode *inode; struct _dict *ctx; diff --git a/libglusterfs/src/fd.h b/libglusterfs/src/fd.h index 298b1b34765..66dc0ed9e4d 100644 --- a/libglusterfs/src/fd.h +++ b/libglusterfs/src/fd.h @@ -46,6 +46,7 @@ struct _fd { pid_t pid; int32_t flags; int32_t refcount; + uint64_t flush_unique; struct list_head inode_list; struct _inode *inode; struct _dict *ctx; diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c index 9d358984b46..e2f0a916de5 100644 --- a/xlators/mount/fuse/src/fuse-bridge.c +++ b/xlators/mount/fuse/src/fuse-bridge.c @@ -1587,6 +1587,8 @@ fuse_flush (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) state = state_from_req (req); fd = FI_TO_FD (fi); state->fd = fd; + if (fd) + fd->flush_unique = req_callid (req); gf_log ("glusterfs-fuse", GF_LOG_TRACE, "%"PRId64": FLUSH %p", req_callid (req), fd); @@ -1602,18 +1604,61 @@ static void fuse_release (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { fuse_state_t *state = NULL; + fd_t *fd = NULL; + int do_flush = 0; state = state_from_req (req); - state->fd = FI_TO_FD (fi); + fd = FI_TO_FD (fi); + state->fd = fd; +#ifdef GF_LINUX_HOST_OS + /* This is an ugly Linux specific hack, relying on subtle + * implementation details. + * + * The self-heal algorithm of replicate relies on being + * notified by means of a flush fop whenever a consumer + * of a file is done with that file. If this happens + * from userspace by means of close(2) or process termination, + * the kernel sends us a FLUSH message which we can handle with + * the flush fop (nb. this mechanism itself is Linux specific!!). + * + * However, if it happens from a kernel context, we get no FLUSH, + * just the final RELEASE when all references to the file are gone. + * We try to guess that this is the case by checking if the last FLUSH + * on the file was just the previous message. If not, we conjecture + * that this release is from a kernel context and call the flush fop + * here. + * + * Note #1: we check the above condition by means of looking at + * the "unique" values of the FUSE messages, relying on which is + * a big fat NO NO NO in any sane code. + * + * Note #2: there is no guarantee against false positives (in theory + * it's possible that the scheduler arranges an unrelated FUSE message + * in between FLUSH and RELEASE, although it seems to be unlikely), but + * extra flushes are not a problem. + * + * Note #3: cf. Bug #223. + */ + + if (fd && fd->flush_unique + 1 != req_callid (req)) + do_flush = 1; +#endif gf_log ("glusterfs-fuse", GF_LOG_TRACE, - "%"PRId64": RELEASE %p", req_callid (req), state->fd); + "%"PRId64": RELEASE %p%s", req_callid (req), fd, + do_flush ? " (FLUSH implied)" : ""); - fd_unref (state->fd); + if (do_flush) { + FUSE_FOP (state, fuse_err_cbk, GF_FOP_FLUSH, flush, fd); + fd_unref (fd); + } else { + fd_unref (fd); - fuse_reply_err (req, 0); + fuse_reply_err (req, 0); + + free_state (state); + } - free_state (state); return; } |