[FEATURE] SWAP Sampler port to kernel swap
authorAlexander Aksenov <a.aksenov@samsung.com>
Fri, 12 Jul 2013 14:29:55 +0000 (18:29 +0400)
committerAlexander Aksenov <a.aksenov@samsung.com>
Fri, 12 Jul 2013 14:29:55 +0000 (18:29 +0400)
It works, but is not bound with other modules

sampler/Kbuild [new file with mode: 0644]
sampler/Makefile.am [new file with mode: 0644]
sampler/kernel_operations.h [new file with mode: 0644]
sampler/swap_sampler_errors.h [new file with mode: 0644]
sampler/swap_sampler_module.c [new file with mode: 0644]
sampler/swap_sampler_module.h [new file with mode: 0644]

diff --git a/sampler/Kbuild b/sampler/Kbuild
new file mode 100644 (file)
index 0000000..2200ea9
--- /dev/null
@@ -0,0 +1,4 @@
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_sampler.o
+swap_sampler-y := swap_sampler_module.o
diff --git a/sampler/Makefile.am b/sampler/Makefile.am
new file mode 100644 (file)
index 0000000..950f6a5
--- /dev/null
@@ -0,0 +1,23 @@
+board_opt = -DBOARD_@BOARD@
+target_kernel_src = @KERNEL@
+target_arch = @ARCH@
+module_dir = $(realpath $(top_srcdir)/src/modules/sampler)
+module_name = swap_sampler
+cross_compiler = $(subst gcc,,$(CC))
+
+#inlude_opt = -I$(realpath $(top_srcdir)/src/modules/)
+extra_cflags = "$(inlude_opt) $(board_opt)"
+
+all-local:
+       $(MAKE) CROSS_COMPILE=$(cross_compiler) ARCH=$(target_arch) extra_cflags=$(extra_cflags) \
+               $(AM_MAKEFLAGS) -C $(target_kernel_src) M=$(module_dir) modules
+
+       echo "generate data for version patching <$(OBJDUMP)><$(READELF)>"
+       PATH=$(PATH) $(top_srcdir)/src/modules/driver/patchko.sh -g $(module_dir)/$(module_name).ko $(OBJDUMP) $(READELF)
+
+clean-local:
+       $(MAKE) CROSS_COMPILE=$(cross_compiler) ARCH=$(target_arch) $(AM_MAKEFLAGS) -C $(target_kernel_src) M=$(module_dir) clean
+
+install-exec-local:
+       install -m 644 $(module_dir)/$(module_name).ko $(prefix)
+       install -m 644 $(module_dir)/$(module_name).ko.addr $(prefix)
diff --git a/sampler/kernel_operations.h b/sampler/kernel_operations.h
new file mode 100644 (file)
index 0000000..5170d36
--- /dev/null
@@ -0,0 +1,12 @@
+#include <linux/kernel.h>
+
+#define print_debug(msg, args...) \
+       printk(KERN_DEBUG "SWAP_SAMPLER DEBUG : " msg, ##args)
+#define print_msg(msg, args...)   \
+       printk(KERN_INFO "SWAP_SAMPLER : " msg, ##args)
+#define print_warn(msg, args...)  \
+       printk(KERN_WARNING "SWAP_SAMPLER WARNING : " msg, ##args)
+#define print_err(msg, args...)   \
+       printk(KERN_ERR "SWAP_SAMPLER ERROR : " msg, ##args)
+#define print_crit(msg, args...)  \
+       printk(KERN_CRIT "SWAP_SAMPLER CRITICAL : " msg, ##args)
diff --git a/sampler/swap_sampler_errors.h b/sampler/swap_sampler_errors.h
new file mode 100644 (file)
index 0000000..f387af5
--- /dev/null
@@ -0,0 +1,4 @@
+enum _swap_sampler_errors {
+    E_SS_SUCCESS = 0,           /* Success */
+    E_SS_WRONG_QUANTUM = 1      /* Wrong timer quantum set */
+};
diff --git a/sampler/swap_sampler_module.c b/sampler/swap_sampler_module.c
new file mode 100644 (file)
index 0000000..3a7d098
--- /dev/null
@@ -0,0 +1,258 @@
+
+/*
+ * Sampling implementation for SWAP
+ * Two approach: timer-based and event-based on counters callbacks
+ *
+ * NOTE:
+*   1. timer-based approach uses high resolution timers if its available
+ *  2. event-based approach that uses counters overflow callbacks doesn't work
+ *     on some hardware; it requires PMU functionality for work
+ *
+ * Written by Andreev S.V., 2012
+ */
+
+#include <linux/timer.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/types.h>
+#include <linux/jiffies.h>
+#include <linux/cpumask.h>
+#include <linux/sched.h>
+#include <linux/hrtimer.h>
+#include <linux/notifier.h>
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/init.h>
+#include <linux/cpu.h>
+#include <linux/hash.h>
+#include <linux/list.h>
+
+#include <linux/module.h>
+
+#include "swap_sampler_module.h"
+#include "swap_sampler_errors.h"
+#include "kernel_operations.h"
+
+unsigned int dbi_timer_quantum = 0;
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+static DEFINE_PER_CPU(struct hrtimer, dbi_hrtimer);
+static int dbi_hrtimer_running;
+#else
+//struct timer_list dbi_timer;
+static DEFINE_PER_CPU(struct timer_list, dbi_timer);
+static int dbi_timer_running;
+#endif
+
+static BLOCKING_NOTIFIER_HEAD(swap_sampler_notifier_list);
+
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+static enum hrtimer_restart dbi_hrtimer_notify(struct hrtimer *hrtimer)
+{
+#ifdef CONFIG_ARM
+    print_debug("lr : 0x%x, pc : 0x%x\n", (task_pt_regs(current))->ARM_lr,
+                                   (task_pt_regs(current))->ARM_pc);
+#elif CONFIG_X86
+    print_debug("lr : 0x%x, pc : 0x%x\n", (task_pt_regs(current))->sp,
+                                   (task_pt_regs(current))->bp);
+#endif /* CONFIG_arch */
+
+    hrtimer_forward_now(hrtimer, ns_to_ktime(dbi_timer_quantum));
+    return HRTIMER_RESTART;
+}
+
+static void __dbi_hrtimer_start(void *unused)
+{
+    struct hrtimer *hrtimer = &__get_cpu_var(dbi_hrtimer);
+
+    if (!dbi_hrtimer_running)
+        return;
+    hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+    hrtimer->function = dbi_hrtimer_notify;
+    hrtimer_start(hrtimer, ns_to_ktime(dbi_timer_quantum), HRTIMER_MODE_REL_PINNED);
+}
+
+static int dbi_hrtimer_start(void)
+{
+    get_online_cpus();
+    dbi_hrtimer_running = 1;
+    on_each_cpu(__dbi_hrtimer_start, NULL, 1);
+    put_online_cpus();
+
+    return E_SS_SUCCESS;
+}
+
+static void __dbi_hrtimer_stop(int cpu)
+{
+    struct hrtimer *hrtimer = &per_cpu(dbi_hrtimer, cpu);
+
+    if (!dbi_hrtimer_running)
+        return;
+    hrtimer_cancel(hrtimer);
+}
+
+static void dbi_hrtimer_stop(void)
+{
+    int cpu;
+
+    get_online_cpus();
+    for_each_online_cpu(cpu)
+        __dbi_hrtimer_stop(cpu);
+    dbi_hrtimer_running = 0;
+    put_online_cpus();
+}
+
+#else
+
+void dbi_write_sample_data(unsigned long data)
+{
+    struct timer_list *timer = (struct timer_list *)data;
+
+#ifdef CONFIG_ARM
+    print_debug("lr : 0x%x, pc : 0x%x\n", (task_pt_regs(current))->ARM_lr,
+                                   (task_pt_regs(current))->ARM_pc);
+#elif CONFIG_X86
+    print_debug("lr : 0x%x, pc : 0x%x\n", (task_pt_regs(current))->sp,
+                                   (task_pt_regs(current))->bp);
+#endif /* CONFIG_arch */
+
+    // TODO: test pinning
+    mod_timer_pinned(timer, jiffies + dbi_timer_quantum);
+}
+
+static void __dbi_timer_start(void *unused)
+{
+    struct timer_list *timer = &__get_cpu_var(dbi_timer);
+
+    if (!dbi_timer_running)
+        return;
+    init_timer(timer);
+    timer->data = timer;
+    timer->function = dbi_write_sample_data;
+    // TODO: test pinning
+    mod_timer_pinned(timer, jiffies + dbi_timer_quantum);
+}
+
+static int dbi_timer_start(void)
+{
+    get_online_cpus();
+    dbi_timer_running = 1;
+    on_each_cpu(__dbi_timer_start, NULL, 1);
+    put_online_cpus();
+    return E_SS_SUCCESS;
+}
+
+static void __dbi_timer_stop(int cpu)
+{
+    struct timer_list *timer = &per_cpu(dbi_timer, cpu);
+
+    if (!dbi_timer_running)
+        return;
+    del_timer_sync(timer);
+}
+
+static void dbi_timer_stop(void)
+{
+    int cpu;
+
+    get_online_cpus();
+    for_each_online_cpu(cpu)
+        __dbi_timer_stop(cpu);
+    dbi_timer_running = 0;
+    put_online_cpus();
+}
+
+#endif
+
+static int __cpuinit dbi_cpu_notify(struct notifier_block *self,
+                     unsigned long action, void *hcpu)
+{
+    long cpu = (long) hcpu;
+
+    switch (action) {
+    case CPU_ONLINE:
+    case CPU_ONLINE_FROZEN:
+#ifdef CONFIG_HIGH_RES_TIMERS
+        smp_call_function_single(cpu, __dbi_hrtimer_start, NULL, 1);
+#else
+        smp_call_function_single(cpu, __dbi_timer_start, NULL, 1);
+#endif
+        break;
+    case CPU_DEAD:
+    case CPU_DEAD_FROZEN:
+#ifdef CONFIG_HIGH_RES_TIMERS
+        __dbi_hrtimer_stop(cpu);
+#else
+        __dbi_timer_stop(cpu);
+#endif
+        break;
+    }
+    return NOTIFY_OK;
+}
+
+static struct notifier_block __refdata dbi_cpu_notifier = {
+    .notifier_call = dbi_cpu_notify,
+};
+
+int swap_sampler_start(unsigned int timer_quantum)
+{
+    if (timer_quantum <= 0)
+        return -E_SS_WRONG_QUANTUM;
+
+    dbi_timer_quantum = timer_quantum;
+
+    if (!try_module_get(THIS_MODULE))
+        print_err("Error of try_module_get() for sampling module\n");
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+    dbi_hrtimer_start();
+#else
+    dbi_timer_start();
+#endif
+
+    return E_SS_SUCCESS;
+}
+
+int swap_sampler_stop(void)
+{
+#ifdef CONFIG_HIGH_RES_TIMERS
+    dbi_hrtimer_stop();
+#else
+    dbi_timer_stop();
+#endif
+    module_put(THIS_MODULE);
+
+    return E_SS_SUCCESS;
+}
+
+static int __init sampler_init(void)
+{
+    int retval;
+
+    retval = register_hotcpu_notifier(&dbi_cpu_notifier);
+    if (retval) {
+        print_err("Error of register_hotcpu_notifier()\n");
+        return retval;
+    }
+
+    print_msg("Sample ininitialization success\n");
+
+    return E_SS_SUCCESS;
+}
+
+static void __exit sampler_exit(void)
+{
+    unregister_hotcpu_notifier(&dbi_cpu_notifier);
+
+    swap_sampler_stop();
+
+    print_msg("Sampler uninitialized\n");
+}
+
+module_init(sampler_init);
+module_exit(sampler_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP sampling module");
+MODULE_AUTHOR("Andreev S.V., Aksenov A.S.");
diff --git a/sampler/swap_sampler_module.h b/sampler/swap_sampler_module.h
new file mode 100644 (file)
index 0000000..c534102
--- /dev/null
@@ -0,0 +1,7 @@
+/* SWAP Sampler interface */
+
+/* Starts the SWAP Sampler */
+int swap_sampler_start(unsigned int timer_quantum);
+
+/* Stops the SWAP Sampler */
+int swap_sampler_stop(void);