summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--booster/src/booster-fd.h1
-rw-r--r--libglusterfs/src/fd.h1
-rw-r--r--xlators/mount/fuse/src/fuse-bridge.c55
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;
}