[FEATURE] task context for KS
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Tue, 22 Apr 2014 19:14:30 +0000 (23:14 +0400)
committerDmitry Kovalenko <d.kovalenko@samsung.com>
Thu, 29 Oct 2015 13:05:17 +0000 (06:05 -0700)
 Create functionality for execution function in context either
thread context (only for US threads).

 Send fake signal to the thread and catch signal handler.
It make possible to execute function in thread context.

Change-Id: I9eb48dad7dae4658f4452c2c74da7e156bb6d80c
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
Kbuild
packaging/swap-modules.spec
task_ctx/Kbuild [new file with mode: 0644]
task_ctx/task_ctx.c [new file with mode: 0644]
task_ctx/task_ctx.h [new file with mode: 0644]

diff --git a/Kbuild b/Kbuild
index 7c07db7..9fd50c0 100644 (file)
--- a/Kbuild
+++ b/Kbuild
@@ -19,4 +19,5 @@ obj-m := master/ \
          preload/ \
          fbiprobe/ \
          wsp/ \
-         nsp/
+         nsp/ \
+         task_ctx/
index d4271d0..a39ca37 100755 (executable)
@@ -50,6 +50,7 @@ install -m 666 preload/swap_preload.ko -t %{buildroot}/opt/swap/sdk
 install -m 666 fbiprobe/swap_fbiprobe.ko -t %{buildroot}/opt/swap/sdk
 install -m 666 wsp/swap_wsp.ko -t %{buildroot}/opt/swap/sdk
 install -m 666 nsp/swap_nsp.ko -t %{buildroot}/opt/swap/sdk
+install -m 666 task_ctx/swap_taskctx.ko -t %{buildroot}/opt/swap/sdk
 
 mkdir -p %{buildroot}/usr/share/license
 cp LICENSE.GPL-2.0+ %{buildroot}/usr/share/license/%{name}
@@ -77,3 +78,4 @@ cp LICENSE.GPL-2.0+ %{buildroot}/usr/share/license/%{name}
 /opt/swap/sdk/swap_fbiprobe.ko
 /opt/swap/sdk/swap_wsp.ko
 /opt/swap/sdk/swap_nsp.ko
+/opt/swap/sdk/swap_taskctx.ko
diff --git a/task_ctx/Kbuild b/task_ctx/Kbuild
new file mode 100644 (file)
index 0000000..b90f7a6
--- /dev/null
@@ -0,0 +1,5 @@
+EXTRA_CFLAGS := $(extra_cflags)
+KBUILD_EXTRA_SYMBOLS = $(src)/../kprobe/Module.symvers
+
+obj-m := swap_taskctx.o
+swap_taskctx-y := task_ctx.o
diff --git a/task_ctx/task_ctx.c b/task_ctx/task_ctx.c
new file mode 100644 (file)
index 0000000..bf64106
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <ksyms/ksyms.h>
+#include <kprobe/swap_ktd.h>
+#include <kprobe/swap_kprobes.h>
+#include <master/swap_initializer.h>
+#include "task_ctx.h"
+
+
+enum { WAIT_TIMEOUT = 1500 };  /* max waiting time the signal delivery */
+
+struct call_task {
+       taskctx_t func;
+       void *data;
+
+       bool is_running;
+       struct completion comp0;
+       struct completion comp1;
+};
+
+
+static void (*swap_signal_wake_up_state)(struct task_struct *t,
+                                        unsigned int state);
+
+static struct sighand_struct *swap_lock_task_sighand(struct task_struct *tsk,
+                                                    unsigned long *flags)
+{
+       struct sighand_struct *sighand;
+
+       for (;;) {
+               local_irq_save(*flags);
+               rcu_read_lock();
+
+               sighand = rcu_dereference(tsk->sighand);
+               if (unlikely(sighand == NULL)) {
+                       rcu_read_unlock();
+                       local_irq_restore(*flags);
+                       break;
+               }
+
+               spin_lock(&sighand->siglock);
+               if (likely(sighand == tsk->sighand)) {
+                       rcu_read_unlock();
+                       break;
+               }
+               spin_unlock(&sighand->siglock);
+
+               rcu_read_unlock();
+               local_irq_restore(*flags);
+       }
+
+       return sighand;
+}
+
+static inline void swap_unlock_task_sighand(struct task_struct *tsk,
+                                           unsigned long *flags)
+{
+        spin_unlock_irqrestore(&tsk->sighand->siglock, *flags);
+}
+
+static int fake_signal_wake_up(struct task_struct *p)
+{
+       unsigned long flags;
+
+       if (swap_lock_task_sighand(p, &flags) == NULL)
+               return -ESRCH;
+
+       swap_signal_wake_up_state(p, 0);
+       swap_unlock_task_sighand(p, &flags);
+
+       return 0;
+}
+
+
+static void ktd_init(struct task_struct *task, void *data)
+{
+       struct call_task **call = (struct call_task **)data;
+
+       *call = NULL;
+}
+
+static void ktd_exit(struct task_struct *task, void *data)
+{
+       struct call_task **call = (struct call_task **)data;
+
+       WARN(*call, "call is not NULL");
+}
+
+struct ktask_data ktd = {
+       .init = ktd_init,
+       .exit = ktd_exit,
+       .size = sizeof(struct call_task *),
+};
+
+static void call_set(struct task_struct *task, struct call_task *call)
+{
+       *(struct call_task **)swap_ktd(&ktd, task) = call;
+}
+
+static struct call_task *call_get(struct task_struct *task)
+{
+       return *(struct call_task **)swap_ktd(&ktd, task);
+}
+
+
+int taskctx_run(struct task_struct *task, taskctx_t func, void *data)
+{
+       if (task == current) {
+               func(data);
+       } else {
+               int ret;
+               unsigned long jiff;
+               struct call_task call = {
+                       .func = func,
+                       .data = data,
+                       .comp0 = COMPLETION_INITIALIZER(call.comp0),
+                       .comp1 = COMPLETION_INITIALIZER(call.comp1),
+                       .is_running = false,
+               };
+
+               /* check task possibility to receive signals */
+               if (task->flags & (PF_KTHREAD | PF_EXITING | PF_SIGNALED))
+                       return -EINVAL;
+
+               /* set call by task */
+               call_set(task, &call);
+
+               ret = fake_signal_wake_up(task);
+               if (ret) {
+                       /* reset call by task */
+                       call_set(task, NULL);
+                       pr_err("cannot send signal to task[%u %u %s] flags=%08x state=%08lx\n",
+                              task->tgid, task->pid, task->comm,
+                              task->flags, task->state);
+                       return ret;
+               }
+
+               jiff = msecs_to_jiffies(WAIT_TIMEOUT);
+               wait_for_completion_timeout(&call.comp0, jiff);
+
+               /* reset call by task */
+               call_set(task, NULL);
+
+               /* wait the return from sig_pre_handler() */
+               synchronize_sched();
+
+               if (call.is_running)
+                       wait_for_completion(&call.comp1);
+
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(taskctx_run);
+
+
+static unsigned long cb_sig(void *data)
+{
+       struct call_task *call = *(struct call_task **)data;
+
+       complete(&call->comp0);
+       call->func(call->data);
+       complete(&call->comp1);
+
+       return 0;
+}
+
+static int sig_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+       struct call_task *call = call_get(current);
+
+       if (call) {
+               call_set(current, NULL);
+               call->is_running = true;
+
+               return set_kjump_cb(regs, cb_sig, &call, sizeof(call));
+       }
+
+       return 0;
+}
+
+
+static struct kprobe sig_kprobe = {
+       .pre_handler = sig_pre_handler,
+};
+
+static int register_signal(void)
+{
+       int ret = 0;
+
+       ret = swap_register_kprobe(&sig_kprobe);
+       if (ret)
+               pr_err("register sig_kprobe ret=%d\n", ret);
+
+       return ret;
+}
+
+static void unregister_sig(void)
+{
+       swap_unregister_kprobe(&sig_kprobe);
+}
+
+
+static int use_cnt = 0;
+static DEFINE_MUTEX(use_lock);
+
+int taskctx_get(void)
+{
+       int ret = 0;
+
+       mutex_lock(&use_lock);
+       if (use_cnt == 0) {
+               ret = register_signal();
+               if (ret)
+                       goto unlock;
+       }
+
+       ++use_cnt;
+
+unlock:
+       mutex_unlock(&use_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(taskctx_get);
+
+void taskctx_put(void)
+{
+       mutex_lock(&use_lock);
+       if (use_cnt == 0) {
+               WARN_ON("call put_task_context() without get_task_context");
+               goto unlock;
+       }
+
+       --use_cnt;
+       if (use_cnt == 0)
+               unregister_sig();
+
+unlock:
+       mutex_unlock(&use_lock);
+}
+EXPORT_SYMBOL_GPL(taskctx_put);
+
+
+static int taskctx_once(void)
+{
+       const char *sym;
+
+       sym = "signal_wake_up_state";
+       swap_signal_wake_up_state = (void *)swap_ksyms(sym);
+       if (swap_signal_wake_up_state == NULL)
+               goto not_found;
+
+       sym = "get_signal_to_deliver";
+       sig_kprobe.addr = (kprobe_opcode_t *)swap_ksyms(sym);
+       if (sig_kprobe.addr == NULL)
+               goto not_found;
+
+       return 0;
+
+not_found:
+       printk("Cannot find address for '%s'!\n", sym);
+       return -ESRCH;
+}
+
+static int taskctx_init(void)
+{
+       return swap_ktd_reg(&ktd);
+}
+
+static void taskctx_uninit(void)
+{
+       WARN(use_cnt, "use_cnt=%d\n", use_cnt);
+       swap_ktd_unreg(&ktd);
+}
+
+SWAP_LIGHT_INIT_MODULE(taskctx_once, taskctx_init, taskctx_uninit, NULL, NULL);
+
+MODULE_LICENSE("GPL");
diff --git a/task_ctx/task_ctx.h b/task_ctx/task_ctx.h
new file mode 100644 (file)
index 0000000..a4c84b6
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _TASK_CTX_H
+#define _TASK_CTX_H
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2015
+ *
+ * 2015         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
+ *
+ */
+
+
+struct task_struct;
+
+typedef void (*taskctx_t)(void *info);
+
+
+int taskctx_run(struct task_struct *task, taskctx_t func, void *data);
+
+int taskctx_get(void);
+void taskctx_put(void);
+
+
+#endif /* _TASK_CTX_H */