|
|
887953 |
From 77716a11910ca2b88f37ff549776f7778cc17dae Mon Sep 17 00:00:00 2001
|
|
|
887953 |
From: Csaba Henk <csaba@redhat.com>
|
|
|
887953 |
Date: Thu, 9 Aug 2018 11:46:33 +0200
|
|
|
887953 |
Subject: [PATCH 526/529] fuse: interrupt handling framework
|
|
|
887953 |
|
|
|
887953 |
- add sub-framework to send timed responses to kernel
|
|
|
887953 |
- add interrupt handler queue
|
|
|
887953 |
- implement INTERRUPT
|
|
|
887953 |
|
|
|
887953 |
fuse_interrupt looks up handlers for interrupted messages
|
|
|
887953 |
in the queue. If found, it invokes the handler function.
|
|
|
887953 |
Else responds with EAGAIN with a delay.
|
|
|
887953 |
|
|
|
887953 |
See spec at
|
|
|
887953 |
|
|
|
887953 |
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/filesystems/fuse.txt?h=v4.17#n148
|
|
|
887953 |
|
|
|
887953 |
and explanation in comments.
|
|
|
887953 |
|
|
|
887953 |
Upstream: https://review.gluster.org/20686
|
|
|
887953 |
> Change-Id: I1a79d3679b31f36e14b4ac8f60b7f2c1ea2badfb
|
|
|
887953 |
> updates: #465
|
|
|
887953 |
> Signed-off-by: Csaba Henk <csaba@redhat.com>
|
|
|
887953 |
|
|
|
887953 |
Change-Id: Idff76920aaa9f87b185dabb0b431a31fcd2a2c77
|
|
|
887953 |
BUG: 1595246
|
|
|
887953 |
Signed-off-by: Csaba Henk <csaba@redhat.com>
|
|
|
887953 |
Reviewed-on: https://code.engineering.redhat.com/gerrit/162549
|
|
|
887953 |
Tested-by: RHGS Build Bot <nigelb@redhat.com>
|
|
|
887953 |
Reviewed-by: Amar Tumballi Suryanarayan <amarts@redhat.com>
|
|
|
887953 |
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
|
|
|
887953 |
---
|
|
|
887953 |
libglusterfs/src/timespec.c | 16 ++
|
|
|
887953 |
libglusterfs/src/timespec.h | 1 +
|
|
|
887953 |
xlators/mount/fuse/src/fuse-bridge.c | 464 +++++++++++++++++++++++++++++++-
|
|
|
887953 |
xlators/mount/fuse/src/fuse-bridge.h | 39 +++
|
|
|
887953 |
xlators/mount/fuse/src/fuse-mem-types.h | 2 +
|
|
|
887953 |
5 files changed, 521 insertions(+), 1 deletion(-)
|
|
|
887953 |
|
|
|
887953 |
diff --git a/libglusterfs/src/timespec.c b/libglusterfs/src/timespec.c
|
|
|
887953 |
index 903303d..55f7575 100644
|
|
|
887953 |
--- a/libglusterfs/src/timespec.c
|
|
|
887953 |
+++ b/libglusterfs/src/timespec.c
|
|
|
887953 |
@@ -72,3 +72,19 @@ void timespec_sub (const struct timespec *begin, const struct timespec *end,
|
|
|
887953 |
res->tv_nsec = end->tv_nsec - begin->tv_nsec;
|
|
|
887953 |
}
|
|
|
887953 |
}
|
|
|
887953 |
+
|
|
|
887953 |
+int
|
|
|
887953 |
+timespec_cmp(const struct timespec *lhs_ts, const struct timespec *rhs_ts)
|
|
|
887953 |
+{
|
|
|
887953 |
+ if (lhs_ts->tv_sec < rhs_ts->tv_sec) {
|
|
|
887953 |
+ return -1;
|
|
|
887953 |
+ } else if (lhs_ts->tv_sec > rhs_ts->tv_sec) {
|
|
|
887953 |
+ return 1;
|
|
|
887953 |
+ } else if (lhs_ts->tv_nsec < rhs_ts->tv_nsec) {
|
|
|
887953 |
+ return -1;
|
|
|
887953 |
+ } else if (lhs_ts->tv_nsec > rhs_ts->tv_nsec) {
|
|
|
887953 |
+ return 1;
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ return 0;
|
|
|
887953 |
+}
|
|
|
887953 |
diff --git a/libglusterfs/src/timespec.h b/libglusterfs/src/timespec.h
|
|
|
887953 |
index 9c393ee..aa37951 100644
|
|
|
887953 |
--- a/libglusterfs/src/timespec.h
|
|
|
887953 |
+++ b/libglusterfs/src/timespec.h
|
|
|
887953 |
@@ -23,5 +23,6 @@ void timespec_adjust_delta (struct timespec *ts, struct timespec delta);
|
|
|
887953 |
void timespec_sub (const struct timespec *begin,
|
|
|
887953 |
const struct timespec *end,
|
|
|
887953 |
struct timespec *res);
|
|
|
887953 |
+int timespec_cmp(const struct timespec *lhs_ts, const struct timespec *rhs_ts);
|
|
|
887953 |
|
|
|
887953 |
#endif /* __INCLUDE_TIMESPEC_H__ */
|
|
|
887953 |
diff --git a/xlators/mount/fuse/src/fuse-bridge.c b/xlators/mount/fuse/src/fuse-bridge.c
|
|
|
887953 |
index f3188d6..0d4b9db 100644
|
|
|
887953 |
--- a/xlators/mount/fuse/src/fuse-bridge.c
|
|
|
887953 |
+++ b/xlators/mount/fuse/src/fuse-bridge.c
|
|
|
887953 |
@@ -15,6 +15,7 @@
|
|
|
887953 |
#include "compat-errno.h"
|
|
|
887953 |
#include "glusterfs-acl.h"
|
|
|
887953 |
#include "syscall.h"
|
|
|
887953 |
+#include "timespec.h"
|
|
|
887953 |
|
|
|
887953 |
#ifdef __NetBSD__
|
|
|
887953 |
#undef open /* in perfuse.h, pulled from mount-gluster-compat.h */
|
|
|
887953 |
@@ -426,6 +427,361 @@ fuse_inode_invalidate_fn(xlator_t *this, inode_t *inode)
|
|
|
887953 |
}
|
|
|
887953 |
#endif
|
|
|
887953 |
|
|
|
887953 |
+static fuse_timed_message_t *
|
|
|
887953 |
+fuse_timed_message_new (void)
|
|
|
887953 |
+{
|
|
|
887953 |
+ fuse_timed_message_t *dmsg = NULL;
|
|
|
887953 |
+
|
|
|
887953 |
+ dmsg = GF_MALLOC (sizeof (*dmsg), gf_fuse_mt_timed_message_t);
|
|
|
887953 |
+ if (!dmsg) {
|
|
|
887953 |
+ return NULL;
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ /* should be NULL if not set */
|
|
|
887953 |
+ dmsg->fuse_message_body = NULL;
|
|
|
887953 |
+ INIT_LIST_HEAD (&dmsg->next);
|
|
|
887953 |
+
|
|
|
887953 |
+ return dmsg;
|
|
|
887953 |
+}
|
|
|
887953 |
+
|
|
|
887953 |
+static void
|
|
|
887953 |
+fuse_timed_message_free (fuse_timed_message_t *dmsg)
|
|
|
887953 |
+{
|
|
|
887953 |
+ GF_FREE (dmsg->fuse_message_body);
|
|
|
887953 |
+ GF_FREE (dmsg);
|
|
|
887953 |
+}
|
|
|
887953 |
+
|
|
|
887953 |
+static void
|
|
|
887953 |
+send_fuse_timed (xlator_t *this, fuse_timed_message_t *dmsg)
|
|
|
887953 |
+{
|
|
|
887953 |
+ fuse_private_t *priv = NULL;
|
|
|
887953 |
+
|
|
|
887953 |
+ priv = this->private;
|
|
|
887953 |
+
|
|
|
887953 |
+ if (!priv->timed_response_fuse_thread_started) {
|
|
|
887953 |
+ return;
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ pthread_mutex_lock (&priv->timed_mutex);
|
|
|
887953 |
+ {
|
|
|
887953 |
+ list_add_tail (&dmsg->next, &priv->timed_list);
|
|
|
887953 |
+ pthread_cond_signal (&priv->timed_cond);
|
|
|
887953 |
+ }
|
|
|
887953 |
+ pthread_mutex_unlock (&priv->timed_mutex);
|
|
|
887953 |
+}
|
|
|
887953 |
+
|
|
|
887953 |
+fuse_interrupt_record_t *
|
|
|
887953 |
+fuse_interrupt_record_new (fuse_in_header_t *finh,
|
|
|
887953 |
+ fuse_interrupt_handler_t handler)
|
|
|
887953 |
+{
|
|
|
887953 |
+ fuse_interrupt_record_t *fir = NULL;
|
|
|
887953 |
+
|
|
|
887953 |
+ fir = GF_MALLOC (sizeof (*fir), gf_fuse_mt_interrupt_record_t);
|
|
|
887953 |
+ if (!fir) {
|
|
|
887953 |
+ return NULL;
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ fir->hit = _gf_false;
|
|
|
887953 |
+ fir->interrupt_state = INTERRUPT_NONE;
|
|
|
887953 |
+ fir->data = NULL;
|
|
|
887953 |
+
|
|
|
887953 |
+ fir->interrupt_handler = handler;
|
|
|
887953 |
+ memcpy (&fir->fuse_in_header, finh, sizeof (*finh));
|
|
|
887953 |
+ pthread_cond_init (&fir->handler_cond, NULL);
|
|
|
887953 |
+ pthread_mutex_init (&fir->handler_mutex, NULL);
|
|
|
887953 |
+ INIT_LIST_HEAD (&fir->next);
|
|
|
887953 |
+
|
|
|
887953 |
+ return fir;
|
|
|
887953 |
+}
|
|
|
887953 |
+
|
|
|
887953 |
+static void
|
|
|
887953 |
+fuse_interrupt_record_free (fuse_interrupt_record_t *fir, void **datap)
|
|
|
887953 |
+{
|
|
|
887953 |
+ /*
|
|
|
887953 |
+ * If caller wishes, we give back the private data to let them deal with it
|
|
|
887953 |
+ * however they want; otherwise we take care of freeing it.
|
|
|
887953 |
+ */
|
|
|
887953 |
+ if (datap) {
|
|
|
887953 |
+ *datap = fir->data;
|
|
|
887953 |
+ } else {
|
|
|
887953 |
+ GF_FREE (fir->data);
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ GF_FREE (fir);
|
|
|
887953 |
+}
|
|
|
887953 |
+
|
|
|
887953 |
+void
|
|
|
887953 |
+fuse_interrupt_record_insert (xlator_t *this, fuse_interrupt_record_t *fir)
|
|
|
887953 |
+{
|
|
|
887953 |
+ fuse_private_t *priv = NULL;
|
|
|
887953 |
+
|
|
|
887953 |
+ priv = this->private;
|
|
|
887953 |
+ pthread_mutex_lock (&priv->interrupt_mutex);
|
|
|
887953 |
+ {
|
|
|
887953 |
+ list_add_tail (&fir->next, &priv->interrupt_list);
|
|
|
887953 |
+ }
|
|
|
887953 |
+ pthread_mutex_unlock (&priv->interrupt_mutex);
|
|
|
887953 |
+}
|
|
|
887953 |
+
|
|
|
887953 |
+static fuse_interrupt_record_t *
|
|
|
887953 |
+fuse_interrupt_record_fetch (xlator_t *this, uint64_t unique, gf_boolean_t reap)
|
|
|
887953 |
+{
|
|
|
887953 |
+ fuse_interrupt_record_t *fir = NULL;
|
|
|
887953 |
+ gf_boolean_t found = _gf_false;
|
|
|
887953 |
+ fuse_private_t *priv = NULL;
|
|
|
887953 |
+
|
|
|
887953 |
+ priv = this->private;
|
|
|
887953 |
+ pthread_mutex_lock (&priv->interrupt_mutex);
|
|
|
887953 |
+ {
|
|
|
887953 |
+ list_for_each_entry (fir, &priv->interrupt_list, next)
|
|
|
887953 |
+ {
|
|
|
887953 |
+ if (fir->fuse_in_header.unique == unique) {
|
|
|
887953 |
+ /*
|
|
|
887953 |
+ * If we are to reap, we do it regardless the
|
|
|
887953 |
+ * hit flag; otherwise we take the record only
|
|
|
887953 |
+ * hasn't yet flagged hit.
|
|
|
887953 |
+ */
|
|
|
887953 |
+ if (reap || !fir->hit) {
|
|
|
887953 |
+ found = _gf_true;
|
|
|
887953 |
+ }
|
|
|
887953 |
+ /*
|
|
|
887953 |
+ * If we are not reaping (coming from handler
|
|
|
887953 |
+ * context), we set the hit flag.
|
|
|
887953 |
+ */
|
|
|
887953 |
+ if (!reap) {
|
|
|
887953 |
+ fir->hit = _gf_true;
|
|
|
887953 |
+ }
|
|
|
887953 |
+ break;
|
|
|
887953 |
+ }
|
|
|
887953 |
+ }
|
|
|
887953 |
+ if (found && reap) {
|
|
|
887953 |
+ list_del (&fir->next);
|
|
|
887953 |
+ }
|
|
|
887953 |
+ }
|
|
|
887953 |
+ pthread_mutex_unlock (&priv->interrupt_mutex);
|
|
|
887953 |
+
|
|
|
887953 |
+ if (found) {
|
|
|
887953 |
+ return fir;
|
|
|
887953 |
+ }
|
|
|
887953 |
+ return NULL;
|
|
|
887953 |
+}
|
|
|
887953 |
+
|
|
|
887953 |
+static fuse_interrupt_record_t *
|
|
|
887953 |
+fuse_interrupt_record_get (xlator_t *this, uint64_t unique)
|
|
|
887953 |
+{
|
|
|
887953 |
+ return fuse_interrupt_record_fetch (this, unique, _gf_false);
|
|
|
887953 |
+}
|
|
|
887953 |
+
|
|
|
887953 |
+static fuse_interrupt_record_t *
|
|
|
887953 |
+fuse_interrupt_record_reap (xlator_t *this, uint64_t unique)
|
|
|
887953 |
+{
|
|
|
887953 |
+ return fuse_interrupt_record_fetch (this, unique, _gf_true);
|
|
|
887953 |
+}
|
|
|
887953 |
+
|
|
|
887953 |
+static void
|
|
|
887953 |
+fuse_interrupt (xlator_t *this, fuse_in_header_t *finh, void *msg,
|
|
|
887953 |
+ struct iobuf *iobuf)
|
|
|
887953 |
+{
|
|
|
887953 |
+ struct fuse_interrupt_in *fii = msg;
|
|
|
887953 |
+ fuse_interrupt_record_t *fir = NULL;
|
|
|
887953 |
+
|
|
|
887953 |
+ gf_log ("glusterfs-fuse", GF_LOG_TRACE,
|
|
|
887953 |
+ "unique %" PRIu64 " INTERRUPT for %" PRIu64, finh->unique,
|
|
|
887953 |
+ fii->unique);
|
|
|
887953 |
+
|
|
|
887953 |
+ fir = fuse_interrupt_record_get (this, fii->unique);
|
|
|
887953 |
+ if (fir) {
|
|
|
887953 |
+ gf_log ("glusterfs-fuse", GF_LOG_DEBUG,
|
|
|
887953 |
+ "unique %" PRIu64 " INTERRUPT for %" PRIu64
|
|
|
887953 |
+ ": handler triggered",
|
|
|
887953 |
+ finh->unique, fii->unique);
|
|
|
887953 |
+
|
|
|
887953 |
+ fir->interrupt_handler (this, fir);
|
|
|
887953 |
+ } else {
|
|
|
887953 |
+ fuse_timed_message_t *dmsg = NULL;
|
|
|
887953 |
+
|
|
|
887953 |
+ /*
|
|
|
887953 |
+ * No record found for this interrupt request.
|
|
|
887953 |
+ *
|
|
|
887953 |
+ * It's either because the handler for the interrupted message
|
|
|
887953 |
+ * does not want to handle interrupt, or this interrupt
|
|
|
887953 |
+ * message beat the interrupted which hasn't yet added a record
|
|
|
887953 |
+ * to the interrupt queue. Either case we reply with error
|
|
|
887953 |
+ * EAGAIN with some (0.01 sec) delay. That will have this
|
|
|
887953 |
+ * interrupt request resent, unless the interrupted message
|
|
|
887953 |
+ * has been already answered.
|
|
|
887953 |
+ *
|
|
|
887953 |
+ * So effectively we are looping in between kernel and
|
|
|
887953 |
+ * userspace, which will be exited either when the interrupted
|
|
|
887953 |
+ * message handler has added an interrupt record, or has
|
|
|
887953 |
+ * replied to kernel. See
|
|
|
887953 |
+ *
|
|
|
887953 |
+ * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/
|
|
|
887953 |
+ * linux.git/tree/Documentation/filesystems/fuse.txt?h=v4.18#n148
|
|
|
887953 |
+ */
|
|
|
887953 |
+
|
|
|
887953 |
+ gf_log ("glusterfs-fuse", GF_LOG_DEBUG,
|
|
|
887953 |
+ "unique %" PRIu64 " INTERRUPT for %" PRIu64 ": no handler found",
|
|
|
887953 |
+ finh->unique, fii->unique);
|
|
|
887953 |
+
|
|
|
887953 |
+ dmsg = fuse_timed_message_new ();
|
|
|
887953 |
+ if (!dmsg) {
|
|
|
887953 |
+ gf_log ("glusterfs-fuse", GF_LOG_ERROR,
|
|
|
887953 |
+ "unique %" PRIu64 " INTERRUPT for %" PRIu64
|
|
|
887953 |
+ ":"
|
|
|
887953 |
+ " failed to allocate timed message",
|
|
|
887953 |
+ finh->unique, fii->unique);
|
|
|
887953 |
+
|
|
|
887953 |
+ return;
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ dmsg->fuse_out_header.unique = finh->unique;
|
|
|
887953 |
+ dmsg->fuse_out_header.len = sizeof (dmsg->fuse_out_header);
|
|
|
887953 |
+ dmsg->fuse_out_header.error = -EAGAIN;
|
|
|
887953 |
+ timespec_now (&dmsg->scheduled_ts);
|
|
|
887953 |
+ timespec_adjust_delta (&dmsg->scheduled_ts,
|
|
|
887953 |
+ (struct timespec){0, 10000000});
|
|
|
887953 |
+
|
|
|
887953 |
+ send_fuse_timed (this, dmsg);
|
|
|
887953 |
+ }
|
|
|
887953 |
+}
|
|
|
887953 |
+
|
|
|
887953 |
+/*
|
|
|
887953 |
+ * Function to be called in fop cbk context (if the fop engages
|
|
|
887953 |
+ * with interrupt handling).
|
|
|
887953 |
+ */
|
|
|
887953 |
+gf_boolean_t
|
|
|
887953 |
+fuse_interrupt_finish_fop (call_frame_t *frame, xlator_t *this,
|
|
|
887953 |
+ gf_boolean_t sync, void **datap)
|
|
|
887953 |
+{
|
|
|
887953 |
+ fuse_interrupt_record_t *fir = NULL;
|
|
|
887953 |
+ fuse_state_t *state = frame->root->state;
|
|
|
887953 |
+ fuse_in_header_t *finh = state->finh;
|
|
|
887953 |
+ gf_boolean_t hit = _gf_false;
|
|
|
887953 |
+ gf_boolean_t handled = _gf_false;
|
|
|
887953 |
+ fuse_interrupt_state_t intstat_orig = INTERRUPT_NONE;
|
|
|
887953 |
+
|
|
|
887953 |
+ fir = fuse_interrupt_record_reap (this, finh->unique);
|
|
|
887953 |
+ if (!fir) {
|
|
|
887953 |
+ /*
|
|
|
887953 |
+ * No interrupt record was inserted (however, caller would usually know
|
|
|
887953 |
+ * about that and there is no point then in calling this function).
|
|
|
887953 |
+ */
|
|
|
887953 |
+ return _gf_false;
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ /*
|
|
|
887953 |
+ * The interrupt handler (if finds the record) modifies fir->hit; however,
|
|
|
887953 |
+ * that could have occurred only before fuse_interrupt_record_reap (), so
|
|
|
887953 |
+ * we are safe here with a lock-free access.
|
|
|
887953 |
+ */
|
|
|
887953 |
+ hit = fir->hit;
|
|
|
887953 |
+ if (hit) {
|
|
|
887953 |
+ pthread_mutex_lock (&fir->handler_mutex);
|
|
|
887953 |
+ {
|
|
|
887953 |
+ intstat_orig = fir->interrupt_state;
|
|
|
887953 |
+ if (fir->interrupt_state == INTERRUPT_NONE) {
|
|
|
887953 |
+ fir->interrupt_state = INTERRUPT_SQUELCHED;
|
|
|
887953 |
+ if (sync) {
|
|
|
887953 |
+ while (fir->interrupt_state == INTERRUPT_NONE) {
|
|
|
887953 |
+ pthread_cond_wait (&fir->handler_cond,
|
|
|
887953 |
+ &fir->handler_mutex);
|
|
|
887953 |
+ }
|
|
|
887953 |
+ }
|
|
|
887953 |
+ }
|
|
|
887953 |
+ }
|
|
|
887953 |
+ pthread_mutex_unlock (&fir->handler_mutex);
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "intstat_orig=%d", intstat_orig);
|
|
|
887953 |
+
|
|
|
887953 |
+ /*
|
|
|
887953 |
+ * From this on fir can only be referred under the conditions that imply
|
|
|
887953 |
+ * we are to free it (otherwise interrupt handler might have already freed
|
|
|
887953 |
+ * it).
|
|
|
887953 |
+ */
|
|
|
887953 |
+
|
|
|
887953 |
+ if (/* there was no interrupt */
|
|
|
887953 |
+ !hit ||
|
|
|
887953 |
+ /* lost the race against interrupt handler */
|
|
|
887953 |
+ intstat_orig != INTERRUPT_NONE ||
|
|
|
887953 |
+ /* we took cleaning up on us */
|
|
|
887953 |
+ sync) {
|
|
|
887953 |
+ /* cleaning up */
|
|
|
887953 |
+ fuse_interrupt_record_free (fir, datap);
|
|
|
887953 |
+ } else if (datap) {
|
|
|
887953 |
+ *datap = NULL;
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ handled = (intstat_orig == INTERRUPT_HANDLED);
|
|
|
887953 |
+ if (handled) {
|
|
|
887953 |
+ /*
|
|
|
887953 |
+ * Fuse request was answered already from interrupt context, we can do
|
|
|
887953 |
+ * away with the stack.
|
|
|
887953 |
+ */
|
|
|
887953 |
+ free_fuse_state (state);
|
|
|
887953 |
+ STACK_DESTROY (frame->root);
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ /*
|
|
|
887953 |
+ * Let caller know if they have to answer the fuse request.
|
|
|
887953 |
+ */
|
|
|
887953 |
+ return handled;
|
|
|
887953 |
+}
|
|
|
887953 |
+
|
|
|
887953 |
+/*
|
|
|
887953 |
+ * Function to be called in interrupt handler context.
|
|
|
887953 |
+ */
|
|
|
887953 |
+void
|
|
|
887953 |
+fuse_interrupt_finish_interrupt (xlator_t *this, fuse_interrupt_record_t *fir,
|
|
|
887953 |
+ fuse_interrupt_state_t intstat,
|
|
|
887953 |
+ gf_boolean_t sync, void **datap)
|
|
|
887953 |
+{
|
|
|
887953 |
+ fuse_in_header_t finh = {
|
|
|
887953 |
+ 0,
|
|
|
887953 |
+ };
|
|
|
887953 |
+ fuse_interrupt_state_t intstat_orig = INTERRUPT_NONE;
|
|
|
887953 |
+
|
|
|
887953 |
+ pthread_mutex_lock (&fir->handler_mutex);
|
|
|
887953 |
+ {
|
|
|
887953 |
+ intstat_orig = fir->interrupt_state;
|
|
|
887953 |
+ if (fir->interrupt_state == INTERRUPT_NONE) {
|
|
|
887953 |
+ fir->interrupt_state = intstat;
|
|
|
887953 |
+ if (sync) {
|
|
|
887953 |
+ pthread_cond_signal (&fir->handler_cond);
|
|
|
887953 |
+ }
|
|
|
887953 |
+ }
|
|
|
887953 |
+ finh = fir->fuse_in_header;
|
|
|
887953 |
+ }
|
|
|
887953 |
+ pthread_mutex_unlock (&fir->handler_mutex);
|
|
|
887953 |
+
|
|
|
887953 |
+ gf_log ("glusterfs-fuse", GF_LOG_DEBUG, "intstat_orig=%d", intstat_orig);
|
|
|
887953 |
+
|
|
|
887953 |
+ /*
|
|
|
887953 |
+ * From this on fir can only be referred under the conditions that imply
|
|
|
887953 |
+ * we are to free it (otherwise fop handler might have already freed it).
|
|
|
887953 |
+ */
|
|
|
887953 |
+
|
|
|
887953 |
+ if (/* we won the race, response is up to us */
|
|
|
887953 |
+ intstat_orig == INTERRUPT_NONE &&
|
|
|
887953 |
+ /* interrupt handling was successful, let the kernel know */
|
|
|
887953 |
+ intstat == INTERRUPT_HANDLED) {
|
|
|
887953 |
+ send_fuse_err (this, &finh, EINTR);
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ if (/* lost the race ... */
|
|
|
887953 |
+ intstat_orig != INTERRUPT_NONE &&
|
|
|
887953 |
+ /*
|
|
|
887953 |
+ * ... and there is no contract with fop handler that it does the
|
|
|
887953 |
+ * cleanup ...
|
|
|
887953 |
+ */
|
|
|
887953 |
+ !sync) {
|
|
|
887953 |
+ /* ... so we do! */
|
|
|
887953 |
+ fuse_interrupt_record_free (fir, datap);
|
|
|
887953 |
+ } else if (datap) {
|
|
|
887953 |
+ *datap = NULL;
|
|
|
887953 |
+ }
|
|
|
887953 |
+}
|
|
|
887953 |
|
|
|
887953 |
int
|
|
|
887953 |
send_fuse_err (xlator_t *this, fuse_in_header_t *finh, int error)
|
|
|
887953 |
@@ -4100,6 +4456,89 @@ notify_kernel_loop (void *data)
|
|
|
887953 |
}
|
|
|
887953 |
#endif
|
|
|
887953 |
|
|
|
887953 |
+static void *
|
|
|
887953 |
+timed_response_loop (void *data)
|
|
|
887953 |
+{
|
|
|
887953 |
+ ssize_t rv = 0;
|
|
|
887953 |
+ size_t len = 0;
|
|
|
887953 |
+ xlator_t *this = NULL;
|
|
|
887953 |
+ fuse_private_t *priv = NULL;
|
|
|
887953 |
+ fuse_timed_message_t *dmsg = NULL;
|
|
|
887953 |
+ fuse_timed_message_t *tmp = NULL;
|
|
|
887953 |
+ struct timespec now = {0,};
|
|
|
887953 |
+ struct timespec delta = {0,};
|
|
|
887953 |
+ struct iovec iovs[2] = {{0,},};
|
|
|
887953 |
+ fuse_in_header_t finh = {0,};
|
|
|
887953 |
+
|
|
|
887953 |
+ this = data;
|
|
|
887953 |
+ priv = this->private;
|
|
|
887953 |
+
|
|
|
887953 |
+ for (;;) {
|
|
|
887953 |
+ pthread_mutex_lock (&priv->timed_mutex);
|
|
|
887953 |
+ {
|
|
|
887953 |
+ while (list_empty (&priv->timed_list)) {
|
|
|
887953 |
+ pthread_cond_wait (&priv->timed_cond, &priv->timed_mutex);
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ dmsg = list_entry (priv->timed_list.next, fuse_timed_message_t,
|
|
|
887953 |
+ next);
|
|
|
887953 |
+ list_for_each_entry (tmp, &priv->timed_list, next)
|
|
|
887953 |
+ {
|
|
|
887953 |
+ if (timespec_cmp (&tmp->scheduled_ts, &dmsg->scheduled_ts) < 0) {
|
|
|
887953 |
+ dmsg = tmp;
|
|
|
887953 |
+ }
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ list_del_init (&dmsg->next);
|
|
|
887953 |
+ }
|
|
|
887953 |
+ pthread_mutex_unlock (&priv->timed_mutex);
|
|
|
887953 |
+
|
|
|
887953 |
+ timespec_now (&now;;
|
|
|
887953 |
+ if (timespec_cmp (&now, &dmsg->scheduled_ts) < 0) {
|
|
|
887953 |
+ timespec_sub (&now, &dmsg->scheduled_ts, &delta);
|
|
|
887953 |
+ nanosleep (&delta, NULL);
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ gf_log ("glusterfs-fuse", GF_LOG_TRACE,
|
|
|
887953 |
+ "sending timed message of unique %"PRIu64,
|
|
|
887953 |
+ dmsg->fuse_out_header.unique);
|
|
|
887953 |
+
|
|
|
887953 |
+ len = dmsg->fuse_out_header.len;
|
|
|
887953 |
+ iovs[0] = (struct iovec){&dmsg->fuse_out_header,
|
|
|
887953 |
+ sizeof (struct fuse_out_header)};
|
|
|
887953 |
+ iovs[1] = (struct iovec){dmsg->fuse_message_body,
|
|
|
887953 |
+ len - sizeof (struct fuse_out_header)};
|
|
|
887953 |
+ /*
|
|
|
887953 |
+ * Nasty hack to allow us to use the send_fuse_iov API,
|
|
|
887953 |
+ * which we resort to, as the API used in original upstream
|
|
|
887953 |
+ * code used is not available in this codebase.
|
|
|
887953 |
+ */
|
|
|
887953 |
+ finh.unique = dmsg->fuse_out_header.unique;
|
|
|
887953 |
+ rv = send_fuse_iov (this, &finh, iovs, 2);
|
|
|
887953 |
+
|
|
|
887953 |
+ fuse_timed_message_free (dmsg);
|
|
|
887953 |
+
|
|
|
887953 |
+ if (rv == EBADF) {
|
|
|
887953 |
+ break;
|
|
|
887953 |
+ }
|
|
|
887953 |
+ }
|
|
|
887953 |
+
|
|
|
887953 |
+ gf_log ("glusterfs-fuse", GF_LOG_ERROR, "timed response loop terminated");
|
|
|
887953 |
+
|
|
|
887953 |
+ pthread_mutex_lock (&priv->timed_mutex);
|
|
|
887953 |
+ {
|
|
|
887953 |
+ priv->timed_response_fuse_thread_started = _gf_false;
|
|
|
887953 |
+ list_for_each_entry_safe (dmsg, tmp, &priv->timed_list, next)
|
|
|
887953 |
+ {
|
|
|
887953 |
+ list_del_init (&dmsg->next);
|
|
|
887953 |
+ fuse_timed_message_free (dmsg);
|
|
|
887953 |
+ }
|
|
|
887953 |
+ }
|
|
|
887953 |
+ pthread_mutex_unlock (&priv->timed_mutex);
|
|
|
887953 |
+
|
|
|
887953 |
+ return NULL;
|
|
|
887953 |
+}
|
|
|
887953 |
+
|
|
|
887953 |
static void
|
|
|
887953 |
fuse_init (xlator_t *this, fuse_in_header_t *finh, void *msg,
|
|
|
887953 |
struct iobuf *iobuf)
|
|
|
887953 |
@@ -4112,6 +4551,7 @@ fuse_init (xlator_t *this, fuse_in_header_t *finh, void *msg,
|
|
|
887953 |
#if FUSE_KERNEL_MINOR_VERSION >= 9
|
|
|
887953 |
pthread_t messenger;
|
|
|
887953 |
#endif
|
|
|
887953 |
+ pthread_t delayer;
|
|
|
887953 |
|
|
|
887953 |
priv = this->private;
|
|
|
887953 |
|
|
|
887953 |
@@ -4160,6 +4600,19 @@ fuse_init (xlator_t *this, fuse_in_header_t *finh, void *msg,
|
|
|
887953 |
fino.flags |= FUSE_BIG_WRITES;
|
|
|
887953 |
}
|
|
|
887953 |
|
|
|
887953 |
+ /* Start the thread processing timed responses */
|
|
|
887953 |
+ ret = gf_thread_create (&delayer, NULL, timed_response_loop, this,
|
|
|
887953 |
+ "fusedlyd");
|
|
|
887953 |
+ if (ret != 0) {
|
|
|
887953 |
+ gf_log ("glusterfs-fuse", GF_LOG_ERROR,
|
|
|
887953 |
+ "failed to start timed response thread (%s)",
|
|
|
887953 |
+ strerror (errno));
|
|
|
887953 |
+
|
|
|
887953 |
+ sys_close (priv->fd);
|
|
|
887953 |
+ goto out;
|
|
|
887953 |
+ }
|
|
|
887953 |
+ priv->timed_response_fuse_thread_started = _gf_true;
|
|
|
887953 |
+
|
|
|
887953 |
/* Used for 'reverse invalidation of inode' */
|
|
|
887953 |
if (fini->minor >= 12) {
|
|
|
887953 |
ret = gf_thread_create (&messenger, NULL, notify_kernel_loop,
|
|
|
887953 |
@@ -5229,6 +5682,8 @@ fuse_priv_dump (xlator_t *this)
|
|
|
887953 |
(int)private->init_recvd);
|
|
|
887953 |
gf_proc_dump_write("strict_volfile_check", "%d",
|
|
|
887953 |
(int)private->strict_volfile_check);
|
|
|
887953 |
+ gf_proc_dump_write("timed_response_thread_started", "%d",
|
|
|
887953 |
+ (int)private->timed_response_fuse_thread_started);
|
|
|
887953 |
gf_proc_dump_write("reverse_thread_started", "%d",
|
|
|
887953 |
(int)private->reverse_fuse_thread_started);
|
|
|
887953 |
gf_proc_dump_write("use_readdirp", "%d", private->use_readdirp);
|
|
|
887953 |
@@ -5486,7 +5941,7 @@ static fuse_handler_t *fuse_std_ops[FUSE_OP_HIGH] = {
|
|
|
887953 |
[FUSE_SETLKW] = fuse_setlk,
|
|
|
887953 |
[FUSE_ACCESS] = fuse_access,
|
|
|
887953 |
[FUSE_CREATE] = fuse_create,
|
|
|
887953 |
- /* [FUSE_INTERRUPT] */
|
|
|
887953 |
+ [FUSE_INTERRUPT] = fuse_interrupt,
|
|
|
887953 |
/* [FUSE_BMAP] */
|
|
|
887953 |
[FUSE_DESTROY] = fuse_destroy,
|
|
|
887953 |
/* [FUSE_IOCTL] */
|
|
|
887953 |
@@ -5611,6 +6066,13 @@ init (xlator_t *this_xl)
|
|
|
887953 |
pthread_cond_init (&priv->invalidate_cond, NULL);
|
|
|
887953 |
pthread_mutex_init (&priv->invalidate_mutex, NULL);
|
|
|
887953 |
|
|
|
887953 |
+ INIT_LIST_HEAD (&priv->timed_list);
|
|
|
887953 |
+ pthread_cond_init (&priv->timed_cond, NULL);
|
|
|
887953 |
+ pthread_mutex_init (&priv->timed_mutex, NULL);
|
|
|
887953 |
+
|
|
|
887953 |
+ INIT_LIST_HEAD (&priv->interrupt_list);
|
|
|
887953 |
+ pthread_mutex_init (&priv->interrupt_mutex, NULL);
|
|
|
887953 |
+
|
|
|
887953 |
/* get options from option dictionary */
|
|
|
887953 |
ret = dict_get_str (options, ZR_MOUNTPOINT_OPT, &value_string);
|
|
|
887953 |
if (ret == -1 || value_string == NULL) {
|
|
|
887953 |
diff --git a/xlators/mount/fuse/src/fuse-bridge.h b/xlators/mount/fuse/src/fuse-bridge.h
|
|
|
887953 |
index 4e32a7f..ba3e000 100644
|
|
|
887953 |
--- a/xlators/mount/fuse/src/fuse-bridge.h
|
|
|
887953 |
+++ b/xlators/mount/fuse/src/fuse-bridge.h
|
|
|
887953 |
@@ -147,6 +147,16 @@ struct fuse_private {
|
|
|
887953 |
|
|
|
887953 |
/* LRU Limit, if not set, default is 128k for now */
|
|
|
887953 |
uint32_t lru_limit;
|
|
|
887953 |
+
|
|
|
887953 |
+ /* Delayed fuse response */
|
|
|
887953 |
+ struct list_head timed_list;
|
|
|
887953 |
+ pthread_cond_t timed_cond;
|
|
|
887953 |
+ pthread_mutex_t timed_mutex;
|
|
|
887953 |
+ gf_boolean_t timed_response_fuse_thread_started;
|
|
|
887953 |
+
|
|
|
887953 |
+ /* Interrupt subscription */
|
|
|
887953 |
+ struct list_head interrupt_list;
|
|
|
887953 |
+ pthread_mutex_t interrupt_mutex;
|
|
|
887953 |
};
|
|
|
887953 |
typedef struct fuse_private fuse_private_t;
|
|
|
887953 |
|
|
|
887953 |
@@ -162,6 +172,35 @@ struct fuse_invalidate_node {
|
|
|
887953 |
};
|
|
|
887953 |
typedef struct fuse_invalidate_node fuse_invalidate_node_t;
|
|
|
887953 |
|
|
|
887953 |
+struct fuse_timed_message {
|
|
|
887953 |
+ struct fuse_out_header fuse_out_header;
|
|
|
887953 |
+ void *fuse_message_body;
|
|
|
887953 |
+ struct timespec scheduled_ts;
|
|
|
887953 |
+ struct list_head next;
|
|
|
887953 |
+};
|
|
|
887953 |
+typedef struct fuse_timed_message fuse_timed_message_t;
|
|
|
887953 |
+
|
|
|
887953 |
+enum fuse_interrupt_state {
|
|
|
887953 |
+ INTERRUPT_NONE,
|
|
|
887953 |
+ INTERRUPT_SQUELCHED,
|
|
|
887953 |
+ INTERRUPT_HANDLED,
|
|
|
887953 |
+};
|
|
|
887953 |
+typedef enum fuse_interrupt_state fuse_interrupt_state_t;
|
|
|
887953 |
+struct fuse_interrupt_record;
|
|
|
887953 |
+typedef struct fuse_interrupt_record fuse_interrupt_record_t;
|
|
|
887953 |
+typedef void (*fuse_interrupt_handler_t) (xlator_t *this,
|
|
|
887953 |
+ fuse_interrupt_record_t *);
|
|
|
887953 |
+struct fuse_interrupt_record {
|
|
|
887953 |
+ struct fuse_in_header fuse_in_header;
|
|
|
887953 |
+ void *data;
|
|
|
887953 |
+ gf_boolean_t hit;
|
|
|
887953 |
+ fuse_interrupt_state_t interrupt_state;
|
|
|
887953 |
+ fuse_interrupt_handler_t interrupt_handler;
|
|
|
887953 |
+ pthread_cond_t handler_cond;
|
|
|
887953 |
+ pthread_mutex_t handler_mutex;
|
|
|
887953 |
+ struct list_head next;
|
|
|
887953 |
+};
|
|
|
887953 |
+
|
|
|
887953 |
struct fuse_graph_switch_args {
|
|
|
887953 |
xlator_t *this;
|
|
|
887953 |
xlator_t *old_subvol;
|
|
|
887953 |
diff --git a/xlators/mount/fuse/src/fuse-mem-types.h b/xlators/mount/fuse/src/fuse-mem-types.h
|
|
|
887953 |
index 721b9a3..4ded879 100644
|
|
|
887953 |
--- a/xlators/mount/fuse/src/fuse-mem-types.h
|
|
|
887953 |
+++ b/xlators/mount/fuse/src/fuse-mem-types.h
|
|
|
887953 |
@@ -24,6 +24,8 @@ enum gf_fuse_mem_types_ {
|
|
|
887953 |
gf_fuse_mt_gids_t,
|
|
|
887953 |
gf_fuse_mt_invalidate_node_t,
|
|
|
887953 |
gf_fuse_mt_pthread_t,
|
|
|
887953 |
+ gf_fuse_mt_timed_message_t,
|
|
|
887953 |
+ gf_fuse_mt_interrupt_record_t,
|
|
|
887953 |
gf_fuse_mt_end
|
|
|
887953 |
};
|
|
|
887953 |
#endif
|
|
|
887953 |
--
|
|
|
887953 |
1.8.3.1
|
|
|
887953 |
|