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.c56
3 files changed, 53 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 1770658e820..6b0571351a3 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;
gf_lock_t lock; /* used ONLY for manipulating
diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c
index dcb4e2950f6..107982947df 100644
--- a/xlators/mount/fuse/src/fuse-bridge.c
+++ b/xlators/mount/fuse/src/fuse-bridge.c
@@ -1811,6 +1811,8 @@ fuse_flush (xlator_t *this, fuse_in_header_t *finh, void *msg)
GET_STATE (this, finh, state);
fd = FH_TO_FD (ffi->fh);
state->fd = fd;
+ if (fd)
+ fd->flush_unique = finh->unique;
gf_log ("glusterfs-fuse", GF_LOG_TRACE,
"%"PRIu64": FLUSH %p", finh->unique, fd);
@@ -1827,19 +1829,63 @@ fuse_release (xlator_t *this, fuse_in_header_t *finh, void *msg)
{
struct fuse_release_in *fri = msg;
+ fd_t *fd = NULL;
+ int do_flush = 0;
+
fuse_state_t *state = NULL;
GET_STATE (this, finh, state);
- state->fd = FH_TO_FD (fri->fh);
+ fd = FH_TO_FD (fri->fh);
+ 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 != finh->unique)
+ do_flush = 1;
+#endif
gf_log ("glusterfs-fuse", GF_LOG_TRACE,
- "%"PRIu64": RELEASE %p", finh->unique, state->fd);
+ "%"PRIu64": RELEASE %p%s", finh->unique, 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);
- send_fuse_err (this, finh, 0);
+ send_fuse_err (this, finh, 0);
+
+ free_state (state);
+ }
- free_state (state);
return;
}