All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events
@ 2017-05-16 10:35 Mike Rapoport
  2017-05-16 10:35 ` [RFC PATCH 1/5] userfaultfd: introduce userfault_init_waitqueue helper Mike Rapoport
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: Mike Rapoport @ 2017-05-16 10:35 UTC (permalink / raw)
  To: Andrea Arcangeli; +Cc: Pavel Emelyanov, linux-mm, Mike Rapoport

Hi,

These patches add ability to generate userfaultfd events so that thier
processing will be synchronized with the non-cooperative thread that caused
the event.

In the non-cooperative case userfaultfd resumes execution of the thread
that caused an event when the notification is read() by the uffd monitor.
In some cases, like, for example, madvise(MADV_REMOVE), it might be
desirable to keep the thread that caused the event suspended until the
uffd monitor had the event handled.

The first two patches just shuffle the code a bit to make subsequent
changes easier.
The patches 3 and 4 create some unification in the way the threads are
queued into waitqueues either after page fault or after a non-cooperative
event.
The fifth patch extends the userfaultfd API with an implementation of
UFFD_EVENT_REMOVE_SYNC that allows to keep the thread that triggered
UFFD_EVENT_REMOVE until the uffd monitor would not wake it explicitly.

Mike Rapoport (5):
  userfaultfd: introduce userfault_init_waitqueue helper
  userfaultfd: introduce userfaultfd_should_wait helper
  userfaultfd: non-cooperative: generalize wake key structure
  userfaultfd: non-cooperative: use fault_pending_wqh for all events
  userfaultfd: non-cooperative: allow synchronous EVENT_REMOVE

 fs/userfaultfd.c                 | 205 ++++++++++++++++++++++++---------------
 include/uapi/linux/userfaultfd.h |  11 +++
 2 files changed, 136 insertions(+), 80 deletions(-)

-- 
2.7.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 1/5] userfaultfd: introduce userfault_init_waitqueue helper
  2017-05-16 10:35 [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events Mike Rapoport
@ 2017-05-16 10:35 ` Mike Rapoport
  2017-05-16 10:35 ` [RFC PATCH 2/5] userfaultfd: introduce userfaultfd_should_wait helper Mike Rapoport
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Mike Rapoport @ 2017-05-16 10:35 UTC (permalink / raw)
  To: Andrea Arcangeli; +Cc: Pavel Emelyanov, linux-mm, Mike Rapoport

Signed-off-by: Mike Rapoport <rppt@linux.vnet.ibm.com>
---
 fs/userfaultfd.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
index 1446e9d..b061e96 100644
--- a/fs/userfaultfd.c
+++ b/fs/userfaultfd.c
@@ -134,6 +134,15 @@ static int userfaultfd_wake_function(wait_queue_t *wq, unsigned mode,
 	return ret;
 }
 
+static inline void userfaultfd_init_waitqueue(struct userfaultfd_ctx *ctx,
+					      struct userfaultfd_wait_queue *uwq)
+{
+	init_waitqueue_func_entry(&uwq->wq, userfaultfd_wake_function);
+	uwq->wq.private = current;
+	uwq->ctx = ctx;
+	uwq->waken = false;
+}
+
 /**
  * userfaultfd_ctx_get - Acquires a reference to the internal userfaultfd
  * context.
@@ -405,11 +414,8 @@ int handle_userfault(struct vm_fault *vmf, unsigned long reason)
 	/* take the reference before dropping the mmap_sem */
 	userfaultfd_ctx_get(ctx);
 
-	init_waitqueue_func_entry(&uwq.wq, userfaultfd_wake_function);
-	uwq.wq.private = current;
+	userfaultfd_init_waitqueue(ctx, &uwq);
 	uwq.msg = userfault_msg(vmf->address, vmf->flags, reason);
-	uwq.ctx = ctx;
-	uwq.waken = false;
 
 	return_to_userland =
 		(vmf->flags & (FAULT_FLAG_USER|FAULT_FLAG_KILLABLE)) ==
-- 
2.7.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 2/5] userfaultfd: introduce userfaultfd_should_wait helper
  2017-05-16 10:35 [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events Mike Rapoport
  2017-05-16 10:35 ` [RFC PATCH 1/5] userfaultfd: introduce userfault_init_waitqueue helper Mike Rapoport
@ 2017-05-16 10:35 ` Mike Rapoport
  2017-05-16 10:36 ` [RFC PATCH 3/5] userfaultfd: non-cooperative: generalize wake key structure Mike Rapoport
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Mike Rapoport @ 2017-05-16 10:35 UTC (permalink / raw)
  To: Andrea Arcangeli; +Cc: Pavel Emelyanov, linux-mm, Mike Rapoport

Signed-off-by: Mike Rapoport <rppt@linux.vnet.ibm.com>
---
 fs/userfaultfd.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
index b061e96..fee5f08 100644
--- a/fs/userfaultfd.c
+++ b/fs/userfaultfd.c
@@ -91,21 +91,31 @@ struct userfaultfd_wake_range {
 	unsigned long len;
 };
 
+static bool userfaultfd_should_wake(struct userfaultfd_wait_queue *uwq,
+				    struct userfaultfd_wake_range *range)
+{
+	unsigned long start, len, address;
+
+	/* len == 0 means wake all */
+	address = uwq->msg.arg.pagefault.address;
+	start = range->start;
+	len = range->len;
+	if (len && (start > address || start + len <= address))
+		return false;
+
+	return true;
+}
+
 static int userfaultfd_wake_function(wait_queue_t *wq, unsigned mode,
 				     int wake_flags, void *key)
 {
 	struct userfaultfd_wake_range *range = key;
 	int ret;
 	struct userfaultfd_wait_queue *uwq;
-	unsigned long start, len;
 
 	uwq = container_of(wq, struct userfaultfd_wait_queue, wq);
 	ret = 0;
-	/* len == 0 means wake all */
-	start = range->start;
-	len = range->len;
-	if (len && (start > uwq->msg.arg.pagefault.address ||
-		    start + len <= uwq->msg.arg.pagefault.address))
+	if (!userfaultfd_should_wake(uwq, range))
 		goto out;
 	WRITE_ONCE(uwq->waken, true);
 	/*
-- 
2.7.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 3/5] userfaultfd: non-cooperative: generalize wake key structure
  2017-05-16 10:35 [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events Mike Rapoport
  2017-05-16 10:35 ` [RFC PATCH 1/5] userfaultfd: introduce userfault_init_waitqueue helper Mike Rapoport
  2017-05-16 10:35 ` [RFC PATCH 2/5] userfaultfd: introduce userfaultfd_should_wait helper Mike Rapoport
@ 2017-05-16 10:36 ` Mike Rapoport
  2017-05-16 10:36 ` [RFC PATCH 4/5] userfaultfd: non-cooperative: use fault_pending_wqh for all events Mike Rapoport
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Mike Rapoport @ 2017-05-16 10:36 UTC (permalink / raw)
  To: Andrea Arcangeli; +Cc: Pavel Emelyanov, linux-mm, Mike Rapoport

Upcoming support for synchronous non-page-fault events will require
userfaultfd_wake_function to be able to differentiate between the event
types. Depending on the event type, different parameters will define if the
wait queue element should be awaken. This requires usage of more general
structure than userfaultfd_wake_range to be used as the "key" parameter for
userfaultfd_wake_function.

Signed-off-by: Mike Rapoport <rppt@linux.vnet.ibm.com>
---
 fs/userfaultfd.c | 96 +++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 57 insertions(+), 39 deletions(-)

diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
index fee5f08..1bd772a 100644
--- a/fs/userfaultfd.c
+++ b/fs/userfaultfd.c
@@ -91,31 +91,40 @@ struct userfaultfd_wake_range {
 	unsigned long len;
 };
 
+struct userfaultfd_wake_key {
+	u8 event;
+	union {
+		struct userfaultfd_wake_range range;
+	} arg;
+};
+
 static bool userfaultfd_should_wake(struct userfaultfd_wait_queue *uwq,
-				    struct userfaultfd_wake_range *range)
+				    struct userfaultfd_wake_key *key)
 {
-	unsigned long start, len, address;
-
-	/* len == 0 means wake all */
-	address = uwq->msg.arg.pagefault.address;
-	start = range->start;
-	len = range->len;
-	if (len && (start > address || start + len <= address))
-		return false;
+	if (key->event == UFFD_EVENT_PAGEFAULT) {
+		unsigned long start, len, address;
+
+		/* len == 0 means wake all */
+		address = uwq->msg.arg.pagefault.address;
+		start = key->arg.range.start;
+		len = key->arg.range.len;
+		if (len && (start > address || start + len <= address))
+			return false;
+	}
 
 	return true;
 }
 
 static int userfaultfd_wake_function(wait_queue_t *wq, unsigned mode,
-				     int wake_flags, void *key)
+				     int wake_flags, void *_key)
 {
-	struct userfaultfd_wake_range *range = key;
+	struct userfaultfd_wake_key *key = _key;
 	int ret;
 	struct userfaultfd_wait_queue *uwq;
 
 	uwq = container_of(wq, struct userfaultfd_wait_queue, wq);
 	ret = 0;
-	if (!userfaultfd_should_wake(uwq, range))
+	if (!userfaultfd_should_wake(uwq, key))
 		goto out;
 	WRITE_ONCE(uwq->waken, true);
 	/*
@@ -802,7 +811,12 @@ static int userfaultfd_release(struct inode *inode, struct file *file)
 	struct mm_struct *mm = ctx->mm;
 	struct vm_area_struct *vma, *prev;
 	/* len == 0 means wake all */
-	struct userfaultfd_wake_range range = { .len = 0, };
+	struct userfaultfd_wake_key key = {
+		.event = UFFD_EVENT_PAGEFAULT,
+		.arg.range = {
+			.len = 0,
+		},
+	};
 	unsigned long new_flags;
 
 	ACCESS_ONCE(ctx->released) = true;
@@ -850,8 +864,8 @@ static int userfaultfd_release(struct inode *inode, struct file *file)
 	 * the fault_*wqh.
 	 */
 	spin_lock(&ctx->fault_pending_wqh.lock);
-	__wake_up_locked_key(&ctx->fault_pending_wqh, TASK_NORMAL, &range);
-	__wake_up_locked_key(&ctx->fault_wqh, TASK_NORMAL, &range);
+	__wake_up_locked_key(&ctx->fault_pending_wqh, TASK_NORMAL, &key);
+	__wake_up_locked_key(&ctx->fault_wqh, TASK_NORMAL, &key);
 	spin_unlock(&ctx->fault_pending_wqh.lock);
 
 	wake_up_poll(&ctx->fd_wqh, POLLHUP);
@@ -1115,20 +1129,20 @@ static ssize_t userfaultfd_read(struct file *file, char __user *buf,
 }
 
 static void __wake_userfault(struct userfaultfd_ctx *ctx,
-			     struct userfaultfd_wake_range *range)
+			     struct userfaultfd_wake_key *key)
 {
 	spin_lock(&ctx->fault_pending_wqh.lock);
 	/* wake all in the range and autoremove */
 	if (waitqueue_active(&ctx->fault_pending_wqh))
 		__wake_up_locked_key(&ctx->fault_pending_wqh, TASK_NORMAL,
-				     range);
+				     key);
 	if (waitqueue_active(&ctx->fault_wqh))
-		__wake_up_locked_key(&ctx->fault_wqh, TASK_NORMAL, range);
+		__wake_up_locked_key(&ctx->fault_wqh, TASK_NORMAL, key);
 	spin_unlock(&ctx->fault_pending_wqh.lock);
 }
 
 static __always_inline void wake_userfault(struct userfaultfd_ctx *ctx,
-					   struct userfaultfd_wake_range *range)
+					   struct userfaultfd_wake_key *key)
 {
 	unsigned seq;
 	bool need_wakeup;
@@ -1155,7 +1169,7 @@ static __always_inline void wake_userfault(struct userfaultfd_ctx *ctx,
 		cond_resched();
 	} while (read_seqcount_retry(&ctx->refile_seq, seq));
 	if (need_wakeup)
-		__wake_userfault(ctx, range);
+		__wake_userfault(ctx, key);
 }
 
 static __always_inline int validate_range(struct mm_struct *mm,
@@ -1481,10 +1495,11 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
 			 * permanently and it avoids userland to call
 			 * UFFDIO_WAKE explicitly.
 			 */
-			struct userfaultfd_wake_range range;
-			range.start = start;
-			range.len = vma_end - start;
-			wake_userfault(vma->vm_userfaultfd_ctx.ctx, &range);
+			struct userfaultfd_wake_key key;
+			key.event = UFFD_EVENT_PAGEFAULT;
+			key.arg.range.start = start;
+			key.arg.range.len = vma_end - start;
+			wake_userfault(vma->vm_userfaultfd_ctx.ctx, &key);
 		}
 
 		new_flags = vma->vm_flags & ~(VM_UFFD_MISSING | VM_UFFD_WP);
@@ -1536,7 +1551,7 @@ static int userfaultfd_wake(struct userfaultfd_ctx *ctx,
 {
 	int ret;
 	struct uffdio_range uffdio_wake;
-	struct userfaultfd_wake_range range;
+	struct userfaultfd_wake_key key;
 	const void __user *buf = (void __user *)arg;
 
 	ret = -EFAULT;
@@ -1547,16 +1562,17 @@ static int userfaultfd_wake(struct userfaultfd_ctx *ctx,
 	if (ret)
 		goto out;
 
-	range.start = uffdio_wake.start;
-	range.len = uffdio_wake.len;
+	key.event = UFFD_EVENT_PAGEFAULT;
+	key.arg.range.start = uffdio_wake.start;
+	key.arg.range.len = uffdio_wake.len;
 
 	/*
 	 * len == 0 means wake all and we don't want to wake all here,
 	 * so check it again to be sure.
 	 */
-	VM_BUG_ON(!range.len);
+	VM_BUG_ON(!key.arg.range.len);
 
-	wake_userfault(ctx, &range);
+	wake_userfault(ctx, &key);
 	ret = 0;
 
 out:
@@ -1569,7 +1585,7 @@ static int userfaultfd_copy(struct userfaultfd_ctx *ctx,
 	__s64 ret;
 	struct uffdio_copy uffdio_copy;
 	struct uffdio_copy __user *user_uffdio_copy;
-	struct userfaultfd_wake_range range;
+	struct userfaultfd_wake_key key;
 
 	user_uffdio_copy = (struct uffdio_copy __user *) arg;
 
@@ -1605,12 +1621,13 @@ static int userfaultfd_copy(struct userfaultfd_ctx *ctx,
 		goto out;
 	BUG_ON(!ret);
 	/* len == 0 would wake all */
-	range.len = ret;
+	key.event = UFFD_EVENT_PAGEFAULT;
+	key.arg.range.len = ret;
 	if (!(uffdio_copy.mode & UFFDIO_COPY_MODE_DONTWAKE)) {
-		range.start = uffdio_copy.dst;
-		wake_userfault(ctx, &range);
+		key.arg.range.start = uffdio_copy.dst;
+		wake_userfault(ctx, &key);
 	}
-	ret = range.len == uffdio_copy.len ? 0 : -EAGAIN;
+	ret = key.arg.range.len == uffdio_copy.len ? 0 : -EAGAIN;
 out:
 	return ret;
 }
@@ -1621,7 +1638,7 @@ static int userfaultfd_zeropage(struct userfaultfd_ctx *ctx,
 	__s64 ret;
 	struct uffdio_zeropage uffdio_zeropage;
 	struct uffdio_zeropage __user *user_uffdio_zeropage;
-	struct userfaultfd_wake_range range;
+	struct userfaultfd_wake_key key;
 
 	user_uffdio_zeropage = (struct uffdio_zeropage __user *) arg;
 
@@ -1650,12 +1667,13 @@ static int userfaultfd_zeropage(struct userfaultfd_ctx *ctx,
 		goto out;
 	/* len == 0 would wake all */
 	BUG_ON(!ret);
-	range.len = ret;
+	key.event = UFFD_EVENT_PAGEFAULT;
+	key.arg.range.len = ret;
 	if (!(uffdio_zeropage.mode & UFFDIO_ZEROPAGE_MODE_DONTWAKE)) {
-		range.start = uffdio_zeropage.range.start;
-		wake_userfault(ctx, &range);
+		key.arg.range.start = uffdio_zeropage.range.start;
+		wake_userfault(ctx, &key);
 	}
-	ret = range.len == uffdio_zeropage.range.len ? 0 : -EAGAIN;
+	ret = key.arg.range.len == uffdio_zeropage.range.len ? 0 : -EAGAIN;
 out:
 	return ret;
 }
-- 
2.7.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 4/5] userfaultfd: non-cooperative: use fault_pending_wqh for all events
  2017-05-16 10:35 [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events Mike Rapoport
                   ` (2 preceding siblings ...)
  2017-05-16 10:36 ` [RFC PATCH 3/5] userfaultfd: non-cooperative: generalize wake key structure Mike Rapoport
@ 2017-05-16 10:36 ` Mike Rapoport
  2017-05-16 10:36 ` [RFC PATCH 5/5] userfaultfd: non-cooperative: allow synchronous EVENT_REMOVE Mike Rapoport
  2017-06-27 13:39 ` [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events Mike Rapoport
  5 siblings, 0 replies; 9+ messages in thread
From: Mike Rapoport @ 2017-05-16 10:36 UTC (permalink / raw)
  To: Andrea Arcangeli; +Cc: Pavel Emelyanov, linux-mm, Mike Rapoport

Queuing page faults and non-cooperative events into different wait queues
does not have real value but rather makes the code more complicated.

Signed-off-by: Mike Rapoport <rppt@linux.vnet.ibm.com>
---
 fs/userfaultfd.c | 64 +++++++++++++++++++++-----------------------------------
 1 file changed, 24 insertions(+), 40 deletions(-)

diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
index 1bd772a..8868229 100644
--- a/fs/userfaultfd.c
+++ b/fs/userfaultfd.c
@@ -48,8 +48,6 @@ struct userfaultfd_ctx {
 	wait_queue_head_t fault_wqh;
 	/* waitqueue head for the pseudo fd to wakeup poll/read */
 	wait_queue_head_t fd_wqh;
-	/* waitqueue head for events */
-	wait_queue_head_t event_wqh;
 	/* a refile sequence protected by fault_pending_wqh lock */
 	struct seqcount refile_seq;
 	/* pseudo fd refcounting */
@@ -101,6 +99,9 @@ struct userfaultfd_wake_key {
 static bool userfaultfd_should_wake(struct userfaultfd_wait_queue *uwq,
 				    struct userfaultfd_wake_key *key)
 {
+	if (key->event != uwq->msg.event)
+		return false;
+
 	if (key->event == UFFD_EVENT_PAGEFAULT) {
 		unsigned long start, len, address;
 
@@ -188,8 +189,6 @@ static void userfaultfd_ctx_put(struct userfaultfd_ctx *ctx)
 		VM_BUG_ON(waitqueue_active(&ctx->fault_pending_wqh));
 		VM_BUG_ON(spin_is_locked(&ctx->fault_wqh.lock));
 		VM_BUG_ON(waitqueue_active(&ctx->fault_wqh));
-		VM_BUG_ON(spin_is_locked(&ctx->event_wqh.lock));
-		VM_BUG_ON(waitqueue_active(&ctx->event_wqh));
 		VM_BUG_ON(spin_is_locked(&ctx->fd_wqh.lock));
 		VM_BUG_ON(waitqueue_active(&ctx->fd_wqh));
 		mmdrop(ctx->mm);
@@ -560,22 +559,21 @@ static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx,
 	if (WARN_ON_ONCE(current->flags & PF_EXITING))
 		goto out;
 
-	ewq->ctx = ctx;
-	init_waitqueue_entry(&ewq->wq, current);
+	userfaultfd_init_waitqueue(ctx, ewq);
 
-	spin_lock(&ctx->event_wqh.lock);
+	spin_lock(&ctx->fault_pending_wqh.lock);
 	/*
 	 * After the __add_wait_queue the uwq is visible to userland
 	 * through poll/read().
 	 */
-	__add_wait_queue(&ctx->event_wqh, &ewq->wq);
+	__add_wait_queue(&ctx->fault_pending_wqh, &ewq->wq);
 	for (;;) {
 		set_current_state(TASK_KILLABLE);
-		if (ewq->msg.event == 0)
+		if (READ_ONCE(ewq->waken))
 			break;
 		if (ACCESS_ONCE(ctx->released) ||
 		    fatal_signal_pending(current)) {
-			__remove_wait_queue(&ctx->event_wqh, &ewq->wq);
+			__remove_wait_queue(&ctx->fault_pending_wqh, &ewq->wq);
 			if (ewq->msg.event == UFFD_EVENT_FORK) {
 				struct userfaultfd_ctx *new;
 
@@ -588,15 +586,15 @@ static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx,
 			break;
 		}
 
-		spin_unlock(&ctx->event_wqh.lock);
+		spin_unlock(&ctx->fault_pending_wqh.lock);
 
 		wake_up_poll(&ctx->fd_wqh, POLLIN);
 		schedule();
 
-		spin_lock(&ctx->event_wqh.lock);
+		spin_lock(&ctx->fault_pending_wqh.lock);
 	}
 	__set_current_state(TASK_RUNNING);
-	spin_unlock(&ctx->event_wqh.lock);
+	spin_unlock(&ctx->fault_pending_wqh.lock);
 
 	/*
 	 * ctx may go away after this if the userfault pseudo fd is
@@ -609,9 +607,10 @@ static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx,
 static void userfaultfd_event_complete(struct userfaultfd_ctx *ctx,
 				       struct userfaultfd_wait_queue *ewq)
 {
-	ewq->msg.event = 0;
-	wake_up_locked(&ctx->event_wqh);
-	__remove_wait_queue(&ctx->event_wqh, &ewq->wq);
+	struct userfaultfd_wake_key key = { 0 };
+
+	key.event = ewq->msg.event;
+	__wake_up_locked_key(&ctx->fault_pending_wqh, TASK_NORMAL, &key);
 }
 
 int dup_userfaultfd(struct vm_area_struct *vma, struct list_head *fcs)
@@ -898,12 +897,6 @@ static inline struct userfaultfd_wait_queue *find_userfault(
 	return find_userfault_in(&ctx->fault_pending_wqh);
 }
 
-static inline struct userfaultfd_wait_queue *find_userfault_evt(
-		struct userfaultfd_ctx *ctx)
-{
-	return find_userfault_in(&ctx->event_wqh);
-}
-
 static unsigned int userfaultfd_poll(struct file *file, poll_table *wait)
 {
 	struct userfaultfd_ctx *ctx = file->private_data;
@@ -935,8 +928,6 @@ static unsigned int userfaultfd_poll(struct file *file, poll_table *wait)
 		smp_mb();
 		if (waitqueue_active(&ctx->fault_pending_wqh))
 			ret = POLLIN;
-		else if (waitqueue_active(&ctx->event_wqh))
-			ret = POLLIN;
 
 		return ret;
 	default:
@@ -981,7 +972,7 @@ static ssize_t userfaultfd_ctx_read(struct userfaultfd_ctx *ctx, int no_wait,
 	struct userfaultfd_wait_queue *uwq;
 	/*
 	 * Handling fork event requires sleeping operations, so
-	 * we drop the event_wqh lock, then do these ops, then
+	 * we drop the fault_pending_wqh lock, then do these ops, then
 	 * lock it back and wake up the waiter. While the lock is
 	 * dropped the ewq may go away so we keep track of it
 	 * carefully.
@@ -996,7 +987,7 @@ static ssize_t userfaultfd_ctx_read(struct userfaultfd_ctx *ctx, int no_wait,
 		set_current_state(TASK_INTERRUPTIBLE);
 		spin_lock(&ctx->fault_pending_wqh.lock);
 		uwq = find_userfault(ctx);
-		if (uwq) {
+		if (uwq && uwq->msg.event == UFFD_EVENT_PAGEFAULT) {
 			/*
 			 * Use a seqcount to repeat the lockless check
 			 * in wake_userfault() to avoid missing
@@ -1037,12 +1028,7 @@ static ssize_t userfaultfd_ctx_read(struct userfaultfd_ctx *ctx, int no_wait,
 			spin_unlock(&ctx->fault_pending_wqh.lock);
 			ret = 0;
 			break;
-		}
-		spin_unlock(&ctx->fault_pending_wqh.lock);
-
-		spin_lock(&ctx->event_wqh.lock);
-		uwq = find_userfault_evt(ctx);
-		if (uwq) {
+		} else if (uwq) { /* non-pagefault event */
 			*msg = uwq->msg;
 
 			if (uwq->msg.event == UFFD_EVENT_FORK) {
@@ -1050,17 +1036,16 @@ static ssize_t userfaultfd_ctx_read(struct userfaultfd_ctx *ctx, int no_wait,
 					(unsigned long)
 					uwq->msg.arg.reserved.reserved1;
 				list_move(&uwq->wq.task_list, &fork_event);
-				spin_unlock(&ctx->event_wqh.lock);
+				spin_unlock(&ctx->fault_pending_wqh.lock);
 				ret = 0;
 				break;
 			}
-
 			userfaultfd_event_complete(ctx, uwq);
-			spin_unlock(&ctx->event_wqh.lock);
+			spin_unlock(&ctx->fault_pending_wqh.lock);
 			ret = 0;
 			break;
 		}
-		spin_unlock(&ctx->event_wqh.lock);
+		spin_unlock(&ctx->fault_pending_wqh.lock);
 
 		if (signal_pending(current)) {
 			ret = -ERESTARTSYS;
@@ -1082,16 +1067,16 @@ static ssize_t userfaultfd_ctx_read(struct userfaultfd_ctx *ctx, int no_wait,
 		ret = resolve_userfault_fork(ctx, fork_nctx, msg);
 
 		if (!ret) {
-			spin_lock(&ctx->event_wqh.lock);
+			spin_lock(&ctx->fault_pending_wqh.lock);
 			if (!list_empty(&fork_event)) {
 				uwq = list_first_entry(&fork_event,
 						       typeof(*uwq),
 						       wq.task_list);
 				list_del(&uwq->wq.task_list);
-				__add_wait_queue(&ctx->event_wqh, &uwq->wq);
+				__add_wait_queue(&ctx->fault_pending_wqh, &uwq->wq);
 				userfaultfd_event_complete(ctx, uwq);
 			}
-			spin_unlock(&ctx->event_wqh.lock);
+			spin_unlock(&ctx->fault_pending_wqh.lock);
 		}
 	}
 
@@ -1808,7 +1793,6 @@ static void init_once_userfaultfd_ctx(void *mem)
 
 	init_waitqueue_head(&ctx->fault_pending_wqh);
 	init_waitqueue_head(&ctx->fault_wqh);
-	init_waitqueue_head(&ctx->event_wqh);
 	init_waitqueue_head(&ctx->fd_wqh);
 	seqcount_init(&ctx->refile_seq);
 }
-- 
2.7.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 5/5] userfaultfd: non-cooperative: allow synchronous EVENT_REMOVE
  2017-05-16 10:35 [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events Mike Rapoport
                   ` (3 preceding siblings ...)
  2017-05-16 10:36 ` [RFC PATCH 4/5] userfaultfd: non-cooperative: use fault_pending_wqh for all events Mike Rapoport
@ 2017-05-16 10:36 ` Mike Rapoport
  2017-06-27 13:39 ` [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events Mike Rapoport
  5 siblings, 0 replies; 9+ messages in thread
From: Mike Rapoport @ 2017-05-16 10:36 UTC (permalink / raw)
  To: Andrea Arcangeli; +Cc: Pavel Emelyanov, linux-mm, Mike Rapoport

In non-cooperative case, multi-threaded userfaultfd monitor may encounter a
race between UFFDIO_COPY and the processing of UFFD_EVENT_REMOVE.
Unlike the page faults that suspend the faulting thread until the page
fault is resolved, other events resume exectution of the thread that caused
the event immediately after delivering the notification to the userfaultfd
monitor. The monitor may run UFFDIO_COPY in parallel with the event
processing and this may result in memory corruption.
With UFFD_EVENT_REMOVE_SYNC introduced by this patch, it would be possible
to block the non-cooperative thread until the userfaultfd monitor will
explicitly wake it.

Signed-off-by: Mike Rapoport <rppt@linux.vnet.ibm.com>
---
 fs/userfaultfd.c                 | 29 ++++++++++++++++++++++++++++-
 include/uapi/linux/userfaultfd.h | 11 +++++++++++
 2 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
index 8868229..1167d0e 100644
--- a/fs/userfaultfd.c
+++ b/fs/userfaultfd.c
@@ -609,6 +609,14 @@ static void userfaultfd_event_complete(struct userfaultfd_ctx *ctx,
 {
 	struct userfaultfd_wake_key key = { 0 };
 
+	/*
+	 * For synchronous events we don't wake up the thread that
+	 * caused the event. The userfault monitor has to explicitly
+	 * wake it with ioctl(UFFDIO_WAKE_SYNC_EVENT)
+	 */
+	if (ewq->msg.event & UFFD_EVENT_FLAG_SYNC)
+		return;
+
 	key.event = ewq->msg.event;
 	__wake_up_locked_key(&ctx->fault_pending_wqh, TASK_NORMAL, &key);
 }
@@ -729,7 +737,8 @@ bool userfaultfd_remove(struct vm_area_struct *vma,
 	struct userfaultfd_wait_queue ewq;
 
 	ctx = vma->vm_userfaultfd_ctx.ctx;
-	if (!ctx || !(ctx->features & UFFD_FEATURE_EVENT_REMOVE))
+	if (!ctx || !(ctx->features & UFFD_FEATURE_EVENT_REMOVE ||
+		      ctx->features & UFFD_FEATURE_EVENT_REMOVE_SYNC))
 		return true;
 
 	userfaultfd_ctx_get(ctx);
@@ -738,6 +747,9 @@ bool userfaultfd_remove(struct vm_area_struct *vma,
 	msg_init(&ewq.msg);
 
 	ewq.msg.event = UFFD_EVENT_REMOVE;
+	if (ctx->features & UFFD_FEATURE_EVENT_REMOVE_SYNC)
+		ewq.msg.event |= UFFD_EVENT_FLAG_SYNC;
+
 	ewq.msg.arg.remove.start = start;
 	ewq.msg.arg.remove.end = end;
 
@@ -1564,6 +1576,18 @@ static int userfaultfd_wake(struct userfaultfd_ctx *ctx,
 	return ret;
 }
 
+static int userfaultfd_wake_sync_event(struct userfaultfd_ctx *ctx,
+				       unsigned long arg)
+{
+	struct userfaultfd_wake_key key = {
+		.event = arg,
+	};
+
+	wake_userfault(ctx, &key);
+
+	return 0;
+}
+
 static int userfaultfd_copy(struct userfaultfd_ctx *ctx,
 			    unsigned long arg)
 {
@@ -1734,6 +1758,9 @@ static long userfaultfd_ioctl(struct file *file, unsigned cmd,
 	case UFFDIO_WAKE:
 		ret = userfaultfd_wake(ctx, arg);
 		break;
+	case UFFDIO_WAKE_SYNC_EVENT:
+		ret = userfaultfd_wake_sync_event(ctx, arg);
+		break;
 	case UFFDIO_COPY:
 		ret = userfaultfd_copy(ctx, arg);
 		break;
diff --git a/include/uapi/linux/userfaultfd.h b/include/uapi/linux/userfaultfd.h
index 3b05953..b1b15e4 100644
--- a/include/uapi/linux/userfaultfd.h
+++ b/include/uapi/linux/userfaultfd.h
@@ -21,6 +21,7 @@
 #define UFFD_API_FEATURES (UFFD_FEATURE_EVENT_FORK |		\
 			   UFFD_FEATURE_EVENT_REMAP |		\
 			   UFFD_FEATURE_EVENT_REMOVE |	\
+			   UFFD_FEATURE_EVENT_REMOVE_SYNC |	\
 			   UFFD_FEATURE_EVENT_UNMAP |		\
 			   UFFD_FEATURE_MISSING_HUGETLBFS |	\
 			   UFFD_FEATURE_MISSING_SHMEM)
@@ -49,6 +50,7 @@
 #define _UFFDIO_WAKE			(0x02)
 #define _UFFDIO_COPY			(0x03)
 #define _UFFDIO_ZEROPAGE		(0x04)
+#define _UFFDIO_WAKE_SYNC_EVENT		(0x05)
 #define _UFFDIO_API			(0x3F)
 
 /* userfaultfd ioctl ids */
@@ -65,6 +67,7 @@
 				      struct uffdio_copy)
 #define UFFDIO_ZEROPAGE		_IOWR(UFFDIO, _UFFDIO_ZEROPAGE,	\
 				      struct uffdio_zeropage)
+#define UFFDIO_WAKE_SYNC_EVENT	_IOR(UFFDIO, _UFFDIO_WAKE_SYNC_EVENT, __u32)
 
 /* read() structure */
 struct uffd_msg {
@@ -113,6 +116,13 @@ struct uffd_msg {
 #define UFFD_EVENT_REMOVE	0x15
 #define UFFD_EVENT_UNMAP	0x16
 
+/*
+ * Events that are delivered synchronously. The causing thread is
+ * blocked until the event is handled by the userfault monitor
+ */
+#define UFFD_EVENT_FLAG_SYNC	0x80
+#define UFFD_EVENT_REMOVE_SYNC	(UFFD_EVENT_REMOVE | UFFD_EVENT_FLAG_SYNC)
+
 /* flags for UFFD_EVENT_PAGEFAULT */
 #define UFFD_PAGEFAULT_FLAG_WRITE	(1<<0)	/* If this was a write fault */
 #define UFFD_PAGEFAULT_FLAG_WP		(1<<1)	/* If reason is VM_UFFD_WP */
@@ -161,6 +171,7 @@ struct uffdio_api {
 #define UFFD_FEATURE_MISSING_HUGETLBFS		(1<<4)
 #define UFFD_FEATURE_MISSING_SHMEM		(1<<5)
 #define UFFD_FEATURE_EVENT_UNMAP		(1<<6)
+#define UFFD_FEATURE_EVENT_REMOVE_SYNC		(1<<7)
 	__u64 features;
 
 	__u64 ioctls;
-- 
2.7.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events
  2017-05-16 10:35 [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events Mike Rapoport
                   ` (4 preceding siblings ...)
  2017-05-16 10:36 ` [RFC PATCH 5/5] userfaultfd: non-cooperative: allow synchronous EVENT_REMOVE Mike Rapoport
@ 2017-06-27 13:39 ` Mike Rapoport
  2017-08-11 13:46   ` Blake Caldwell
  5 siblings, 1 reply; 9+ messages in thread
From: Mike Rapoport @ 2017-06-27 13:39 UTC (permalink / raw)
  To: Andrea Arcangeli; +Cc: Pavel Emelyanov, linux-mm

On Tue, May 16, 2017 at 01:35:57PM +0300, Mike Rapoport wrote:
> Hi,

Any comments on this?
Shall I repost without the "RFC" prefix?
 
> These patches add ability to generate userfaultfd events so that thier
> processing will be synchronized with the non-cooperative thread that caused
> the event.
> 
> In the non-cooperative case userfaultfd resumes execution of the thread
> that caused an event when the notification is read() by the uffd monitor.
> In some cases, like, for example, madvise(MADV_REMOVE), it might be
> desirable to keep the thread that caused the event suspended until the
> uffd monitor had the event handled.
> 
> The first two patches just shuffle the code a bit to make subsequent
> changes easier.
> The patches 3 and 4 create some unification in the way the threads are
> queued into waitqueues either after page fault or after a non-cooperative
> event.
> The fifth patch extends the userfaultfd API with an implementation of
> UFFD_EVENT_REMOVE_SYNC that allows to keep the thread that triggered
> UFFD_EVENT_REMOVE until the uffd monitor would not wake it explicitly.
> 
> Mike Rapoport (5):
>   userfaultfd: introduce userfault_init_waitqueue helper
>   userfaultfd: introduce userfaultfd_should_wait helper
>   userfaultfd: non-cooperative: generalize wake key structure
>   userfaultfd: non-cooperative: use fault_pending_wqh for all events
>   userfaultfd: non-cooperative: allow synchronous EVENT_REMOVE
> 
>  fs/userfaultfd.c                 | 205 ++++++++++++++++++++++++---------------
>  include/uapi/linux/userfaultfd.h |  11 +++
>  2 files changed, 136 insertions(+), 80 deletions(-)
> 
> -- 
> 2.7.4
> 

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events
  2017-06-27 13:39 ` [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events Mike Rapoport
@ 2017-08-11 13:46   ` Blake Caldwell
  2017-08-14  4:58     ` Mike Rapoport
  0 siblings, 1 reply; 9+ messages in thread
From: Blake Caldwell @ 2017-08-11 13:46 UTC (permalink / raw)
  To: 'Mike Rapoport'
  Cc: 'Pavel Emelyanov', 'linux-mm',
	'Andrea Arcangeli'

> -----Original Message-----
> From: owner-linux-mm@kvack.org [mailto:owner-linux-mm@kvack.org] On
> Behalf Of Mike Rapoport
> Sent: Tuesday, June 27, 2017 9:40 AM
> To: Andrea Arcangeli <aarcange@redhat.com>
> Cc: Pavel Emelyanov <xemul@virtuozzo.com>; linux-mm <linux-
> mm@kvack.org>
> Subject: Re: [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous
events
> 
> On Tue, May 16, 2017 at 01:35:57PM +0300, Mike Rapoport wrote:
> > Hi,
> 
> Any comments on this?
> Shall I repost without the "RFC" prefix?
> 
I have a use case for this feature exactly like what you have described. The
process should be suspended until the event has been handled. I would like
to test this if there is a rebased patchset out there somewhere? I'm using
4.13.0_rc3 from
https://kernel.googlesource.com/pub/scm/linux/kernel/git/andrea/aa.git

I wasn't able to apply the patches without heavy modification (mostly patch
3/5).

Thanks for the work on this.
> > These patches add ability to generate userfaultfd events so that thier
> > processing will be synchronized with the non-cooperative thread that
> > caused the event.
> >
> > In the non-cooperative case userfaultfd resumes execution of the
> > thread that caused an event when the notification is read() by the uffd
> monitor.
> > In some cases, like, for example, madvise(MADV_REMOVE), it might be
> > desirable to keep the thread that caused the event suspended until the
> > uffd monitor had the event handled.
> >
> > The first two patches just shuffle the code a bit to make subsequent
> > changes easier.
> > The patches 3 and 4 create some unification in the way the threads are
> > queued into waitqueues either after page fault or after a
> > non-cooperative event.
> > The fifth patch extends the userfaultfd API with an implementation of
> > UFFD_EVENT_REMOVE_SYNC that allows to keep the thread that triggered
> > UFFD_EVENT_REMOVE until the uffd monitor would not wake it explicitly.
> >
> > Mike Rapoport (5):
> >   userfaultfd: introduce userfault_init_waitqueue helper
> >   userfaultfd: introduce userfaultfd_should_wait helper
> >   userfaultfd: non-cooperative: generalize wake key structure
> >   userfaultfd: non-cooperative: use fault_pending_wqh for all events
> >   userfaultfd: non-cooperative: allow synchronous EVENT_REMOVE
> >
> >  fs/userfaultfd.c                 | 205
++++++++++++++++++++++++---------------
> >  include/uapi/linux/userfaultfd.h |  11 +++
> >  2 files changed, 136 insertions(+), 80 deletions(-)
> >
> > --
> > 2.7.4
> >
> 
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo@kvack.org.  For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events
  2017-08-11 13:46   ` Blake Caldwell
@ 2017-08-14  4:58     ` Mike Rapoport
  0 siblings, 0 replies; 9+ messages in thread
From: Mike Rapoport @ 2017-08-14  4:58 UTC (permalink / raw)
  To: Blake Caldwell
  Cc: 'Pavel Emelyanov', 'linux-mm',
	'Andrea Arcangeli'

Hi,

On Fri, Aug 11, 2017 at 09:46:29AM -0400, Blake Caldwell wrote:
> > On Tue, May 16, 2017 at 01:35:57PM +0300, Mike Rapoport wrote:
> > > Hi,
> > 
> > Any comments on this?
> > Shall I repost without the "RFC" prefix?
> > 
> I have a use case for this feature exactly like what you have described. The
> process should be suspended until the event has been handled. I would like
> to test this if there is a rebased patchset out there somewhere? I'm using
> 4.13.0_rc3 from
> https://kernel.googlesource.com/pub/scm/linux/kernel/git/andrea/aa.git
> 
> I wasn't able to apply the patches without heavy modification (mostly patch
> 3/5).

I don't have a version rebased on Andrea's tree, sorry.
 
> Thanks for the work on this.
> > > These patches add ability to generate userfaultfd events so that thier
> > > processing will be synchronized with the non-cooperative thread that
> > > caused the event.
> > >
> > > In the non-cooperative case userfaultfd resumes execution of the
> > > thread that caused an event when the notification is read() by the uffd
> > monitor.
> > > In some cases, like, for example, madvise(MADV_REMOVE), it might be
> > > desirable to keep the thread that caused the event suspended until the
> > > uffd monitor had the event handled.
> > >
> > > The first two patches just shuffle the code a bit to make subsequent
> > > changes easier.
> > > The patches 3 and 4 create some unification in the way the threads are
> > > queued into waitqueues either after page fault or after a
> > > non-cooperative event.
> > > The fifth patch extends the userfaultfd API with an implementation of
> > > UFFD_EVENT_REMOVE_SYNC that allows to keep the thread that triggered
> > > UFFD_EVENT_REMOVE until the uffd monitor would not wake it explicitly.
> > >
> > > Mike Rapoport (5):
> > >   userfaultfd: introduce userfault_init_waitqueue helper
> > >   userfaultfd: introduce userfaultfd_should_wait helper
> > >   userfaultfd: non-cooperative: generalize wake key structure
> > >   userfaultfd: non-cooperative: use fault_pending_wqh for all events
> > >   userfaultfd: non-cooperative: allow synchronous EVENT_REMOVE
> > >
> > >  fs/userfaultfd.c                 | 205
> ++++++++++++++++++++++++---------------
> > >  include/uapi/linux/userfaultfd.h |  11 +++
> > >  2 files changed, 136 insertions(+), 80 deletions(-)
> > >
> > > --
> > > 2.7.4
> > >
> > 
> > --
> > To unsubscribe, send a message with 'unsubscribe linux-mm' in
> > the body to majordomo@kvack.org.  For more info on Linux MM,
> > see: http://www.linux-mm.org/ .
> > Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
> 

-- 
Sincerely yours,
Mike.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2017-08-14  4:58 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-16 10:35 [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events Mike Rapoport
2017-05-16 10:35 ` [RFC PATCH 1/5] userfaultfd: introduce userfault_init_waitqueue helper Mike Rapoport
2017-05-16 10:35 ` [RFC PATCH 2/5] userfaultfd: introduce userfaultfd_should_wait helper Mike Rapoport
2017-05-16 10:36 ` [RFC PATCH 3/5] userfaultfd: non-cooperative: generalize wake key structure Mike Rapoport
2017-05-16 10:36 ` [RFC PATCH 4/5] userfaultfd: non-cooperative: use fault_pending_wqh for all events Mike Rapoport
2017-05-16 10:36 ` [RFC PATCH 5/5] userfaultfd: non-cooperative: allow synchronous EVENT_REMOVE Mike Rapoport
2017-06-27 13:39 ` [RFC PATCH 0/5] userfaultfd: non-cooperative: syncronous events Mike Rapoport
2017-08-11 13:46   ` Blake Caldwell
2017-08-14  4:58     ` Mike Rapoport

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.