[RH8,2/2] ve/aio: Add a handle to checkpoint/restore AIO context

Submitted by Alexander Mikhalitsyn on Dec. 3, 2020, 4:07 p.m.

Details

Message ID 20201203160725.10719-2-alexander.mikhalitsyn@virtuozzo.com
State New
Series "Series without cover letter"
Headers show

Commit Message

Alexander Mikhalitsyn Dec. 3, 2020, 4:07 p.m.
From: Stanislav Kinsburskiy <skinsbursky@virtuozzo.com>

This adds ioctl, which allows to set ring buffer tail
and to wait till aio requests are finished.

v2: Add pseudosuper check

https://jira.sw.ru/browse/PSBM-42488

Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com>
Reviewed-by: Cyrill Gorcunov <gorcunov@openvz.org>

khorenko@: we don't support migration of incomplete aio requests
           https://jira.sw.ru/browse/PSBM-41425

so using added instruments we wait till all AIO requests are completed
and migrate the results (AIO req contexts with status).

Patch hide | download patch | download mbox

======================================

ve/aio: Enumerate ioctl numbers right

Do not use common used numbers, use custom.
Also, make error codes different.

https://jira.sw.ru/browse/PSBM-42488

Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com>
Acked-by: Cyrill Gorcunov <gorcunov@openvz.org>

======================================

ve/aio: Kill ve_aio_set_tail()

Since tail is restored using submitting requests to write in /dev/null,
we do not need this interface anymore.

https://jira.sw.ru/browse/PSBM-42488

Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com>
Acked-by: Cyrill Gorcunov <gorcunov@openvz.org>

======================================

ve/aio: Wait for all inflight AIO reqs of a task

Make it wait all task's AIO contexts instead of a single AIO request.
This minimizes the number of syscall we do to dump aios.

https://jira.sw.ru/browse/PSBM-42488

Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com>
Acked-by: Cyrill Gorcunov <gorcunov@openvz.org>
======================================

Ported with respect to ms commits:
34e83fc ("aio: reqs_active -> reqs_available")
723be6e ("aio: percpu ioctx refcount")
db446a0 ("aio: convert the ioctx list to table lookup v3")

https://jira.sw.ru/browse/PSBM-123159

Signed-off-by: Alexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>
---
 fs/aio.c            | 98 +++++++++++++++++++++++++++++++++++++++++++++
 fs/proc/base.c      | 27 +++++++++++++
 include/linux/aio.h | 13 ++++++
 3 files changed, 138 insertions(+)

diff --git a/fs/aio.c b/fs/aio.c
index 492f1a8b7661..1afdf3dcd335 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -20,6 +20,7 @@ 
 #include <linux/backing-dev.h>
 #include <linux/uio.h>
 
+#include <linux/sched/mm.h>
 #include <linux/sched/signal.h>
 #include <linux/fs.h>
 #include <linux/file.h>
@@ -1892,6 +1893,103 @@  SYSCALL_DEFINE5(io_getevents, aio_context_t, ctx_id,
 	return ret;
 }
 
+#ifdef CONFIG_VE
+static bool has_reqs_active(struct kioctx *ctx)
+{
+	unsigned long flags;
+	unsigned nr;
+
+	spin_lock_irqsave(&ctx->completion_lock, flags);
+	nr = (ctx->nr_events - 1) - atomic_read(&ctx->reqs_available);
+	nr -= ctx->completed_events;
+	spin_unlock_irqrestore(&ctx->completion_lock, flags);
+
+	return !!nr;
+}
+
+static int ve_aio_wait_inflight_reqs(struct task_struct *p)
+{
+	struct mm_struct *mm;
+	struct kioctx_table *table;
+	int ret, i;
+
+	if (p->flags & PF_KTHREAD)
+		return -EINVAL;
+
+	task_lock(p);
+	mm = p->mm;
+	if (mm)
+		atomic_inc(&mm->mm_count);
+	task_unlock(p);
+	if (!mm)
+		return -ESRCH;
+
+again:
+	spin_lock_irq(&mm->ioctx_lock);
+#if 0
+	hlist_for_each_entry_rcu(ctx, &mm->ioctx_list, list) {
+		if (!has_reqs_active(ctx))
+			continue;
+
+		atomic_inc(&ctx->users);
+		spin_unlock_irq(&mm->ioctx_lock);
+
+		ret = wait_event_interruptible(ctx->wait, !has_reqs_active(ctx));
+		put_ioctx(ctx);
+
+		if (ret)
+			goto mmdrop;
+		goto again;
+	}
+#endif
+	rcu_read_lock();
+	table = rcu_dereference(mm->ioctx_table);
+	for (i = 0; i < table->nr; i++) {
+		struct kioctx *ctx;
+
+		ctx = rcu_dereference(table->table[i]);
+		if (!ctx)
+			continue;
+
+		if (!has_reqs_active(ctx))
+			continue;
+
+		percpu_ref_get(&ctx->users);
+		rcu_read_unlock();
+		spin_unlock_irq(&mm->ioctx_lock);
+
+		ret = wait_event_interruptible(ctx->wait, !has_reqs_active(ctx));
+		percpu_ref_put(&ctx->users);
+
+		if (ret)
+			goto mmdrop;
+		goto again;
+	}
+
+	rcu_read_unlock();
+	spin_unlock_irq(&mm->ioctx_lock);
+	ret = 0;
+mmdrop:
+	mmdrop(mm);
+	return ret;
+}
+
+int ve_aio_ioctl(struct task_struct *task, unsigned int cmd, unsigned long arg)
+{
+	int ret;
+
+	switch (cmd) {
+		case VE_AIO_IOC_WAIT_ACTIVE:
+			ret = ve_aio_wait_inflight_reqs(task);
+			break;
+		default:
+			ret = -EINVAL;
+	}
+
+	return ret;
+}
+#endif
+
 struct __aio_sigset {
 	const sigset_t __user	*sigmask;
 	size_t		sigsetsize;
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 85fee7396e90..0c6dabda99cd 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -94,6 +94,7 @@ 
 #include <linux/sched/stat.h>
 #include <linux/flex_array.h>
 #include <linux/posix-timers.h>
+#include <linux/aio.h>
 #include <trace/events/oom.h>
 #include "internal.h"
 #include "fd.h"
@@ -2422,6 +2423,29 @@  static const struct file_operations proc_pid_set_timerslack_ns_operations = {
 	.release	= single_release,
 };
 
+#ifdef CONFIG_VE
+static long proc_aio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct inode *inode = file_inode(file);
+	struct task_struct *task;
+	int ret;
+
+	task = get_proc_task(inode);
+	if (!task)
+		return -ESRCH;
+
+	ret = ve_aio_ioctl(task, cmd, arg);
+
+	put_task_struct(task);
+
+	return ret;
+}
+
+static const struct file_operations proc_aio_operations = {
+	.unlocked_ioctl		= proc_aio_ioctl,
+};
+#endif /* CONFIG_VE */
+
 static struct dentry *proc_pident_instantiate(struct dentry *dentry,
 	struct task_struct *task, const void *ptr)
 {
@@ -3000,6 +3024,9 @@  static const struct pid_entry tgid_base_stuff[] = {
 	REG("timers",	  S_IRUGO, proc_timers_operations),
 #endif
 	REG("timerslack_ns", S_IRUGO|S_IWUGO, proc_pid_set_timerslack_ns_operations),
+#ifdef CONFIG_CHECKPOINT_RESTORE
+	REG("aio",	  S_IRUGO|S_IWUSR, proc_aio_operations),
+#endif
 #ifdef CONFIG_LIVEPATCH
 	ONE("patch_state",  S_IRUSR, proc_pid_patch_state),
 #endif
diff --git a/include/linux/aio.h b/include/linux/aio.h
index 4b7a331156ff..ccaaae0db31d 100644
--- a/include/linux/aio.h
+++ b/include/linux/aio.h
@@ -12,14 +12,27 @@  typedef int (kiocb_cancel_fn)(struct kiocb *);
 
 #define AIO_MAX_NR_DEFAULT	0x10000
 
+struct ve_ioc_arg
+{
+	aio_context_t	ctx_id;
+	unsigned	val;
+};
+
+#define VE_AIO_IOC_WAIT_ACTIVE	_IOW('a',  1, struct ve_ioc_arg)
+
 /* prototypes */
 #ifdef CONFIG_AIO
 extern void exit_aio(struct mm_struct *mm);
 void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel);
+#ifdef CONFIG_VE
+int ve_aio_ioctl(struct task_struct *, unsigned int, unsigned long);
+#endif
 #else
 static inline void exit_aio(struct mm_struct *mm) { }
 static inline void kiocb_set_cancel_fn(struct kiocb *req,
 				       kiocb_cancel_fn *cancel) { }
+static int ve_aio_ioctl(struct task_struct *task, unsigned int cmd,
+			unsigned long arg) { return 0; }
 #endif /* CONFIG_AIO */
 
 #endif /* __LINUX__AIO_H */