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 | 56 |
3 files changed, 53 insertions, 5 deletions
diff --git a/booster/src/booster-fd.h b/booster/src/booster-fd.h index 43592e825..0ccc28121 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 1770658e8..6b0571351 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 dcb4e2950..107982947 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; } |