sched: ems: support PCF(Performance CPU Finder)
authorPark Bumgyu <bumgyu.park@samsung.com>
Fri, 23 Mar 2018 05:08:55 +0000 (14:08 +0900)
committerChungwoo Park <cww.park@samsung.com>
Mon, 21 May 2018 08:35:28 +0000 (17:35 +0900)
PCF(Performance CPU Finder) provides the ability to find the highest
performance cpu at any given moment. Currently, it is composed of a
selection algorithm based on distributed processing.

Change-Id: Iaaaedd12fee9fe0829db9dd1f7cb72993581a51e
Signed-off-by: Park Bumgyu <bumgyu.park@samsung.com>
include/trace/events/ems.h
kernel/sched/ems/Makefile
kernel/sched/ems/ems.h
kernel/sched/ems/pcf.c [new file with mode: 0644]

index 6b98fcf45624fde345771d77b5ed712b9f9a4715..e1831114520c2d9f24c087579d6136bbb252d6df 100644 (file)
@@ -42,6 +42,34 @@ TRACE_EVENT(ems_wakeup_balance,
                  __entry->comm, __entry->pid, __entry->target_cpu, __entry->state)
 );
 
+/*
+ * Tracepoint for performance cpu finder
+ */
+TRACE_EVENT(ems_select_perf_cpu,
+
+       TP_PROTO(struct task_struct *p, int best_cpu, int backup_cpu),
+
+       TP_ARGS(p, best_cpu, backup_cpu),
+
+       TP_STRUCT__entry(
+               __array(        char,           comm,   TASK_COMM_LEN   )
+               __field(        pid_t,          pid                     )
+               __field(        int,            best_cpu                )
+               __field(        int,            backup_cpu              )
+       ),
+
+       TP_fast_assign(
+               memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
+               __entry->pid            = p->pid;
+               __entry->best_cpu       = best_cpu;
+               __entry->backup_cpu     = backup_cpu;
+       ),
+
+       TP_printk("comm=%s pid=%d best_cpu=%d backup_cpu=%d",
+                 __entry->comm, __entry->pid, __entry->best_cpu, __entry->backup_cpu)
+);
+
+
 /*
  * Tracepoint for selection of boost cpu
  */
index 0771a344916c1542a331d1f9aa2a6fe7bc81dcf3..bbfe44f9efbecb07260eb59e286551ab5ed61e73 100644 (file)
@@ -1,4 +1,5 @@
-obj-y += core.o global_boost.o lbt.o ontime.o
+obj-y += core.o pcf.o global_boost.o lbt.o ontime.o
+
 obj-$(CONFIG_SCHED_TUNE) += st_addon.o
 obj-$(CONFIG_SCHED_EMS) += ehmp.o
 obj-$(CONFIG_FREQVAR_TUNE) += freqvar_tune.o
index 9cf27e7607460c75d71afdb5e4abc1db5a230c33..227e97067a59c80ae06e593e530a605e7e089098 100644 (file)
@@ -18,6 +18,7 @@
 extern struct kobject *ems_kobj;
 
 extern int ontime_task_wakeup(struct task_struct *p);
+extern int select_perf_cpu(struct task_struct *p);
 extern int global_boosting(struct task_struct *p);
 extern bool lbt_bring_overutilize(int cpu, struct task_struct *p);
 
diff --git a/kernel/sched/ems/pcf.c b/kernel/sched/ems/pcf.c
new file mode 100644 (file)
index 0000000..119c5b5
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Performance CPU Finder
+ *
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd
+ * Park Bumgyu <bumgyu.park@samsung.com>
+ */
+
+#include <trace/events/ems.h>
+
+#include "../sched.h"
+
+static int cpu_util_wake(int cpu, struct task_struct *p)
+{
+       unsigned long util, capacity;
+
+       /* Task has no contribution or is new */
+       if (cpu != task_cpu(p) || !p->se.avg.last_update_time)
+               return cpu_util(cpu);
+
+       capacity = capacity_orig_of(cpu);
+       util = max_t(long, cpu_rq(cpu)->cfs.avg.util_avg - p->se.avg.util_avg, 0);
+
+       return (util >= capacity) ? capacity : util;
+}
+
+/*
+ * Currently, PCF is composed of a selection algorithm based on distributed
+ * processing, for example, selecting idle cpu or cpu with biggest spare
+ * capacity. Although the current algorithm may suffice, it is necessary to
+ * examine a selection algorithm considering cache hot and migration cost.
+ */
+int select_perf_cpu(struct task_struct *p)
+{
+       int cpu;
+       unsigned long best_perf_cap_orig = 0;
+       unsigned long max_spare_cap = 0;
+       int best_perf_cstate = INT_MAX;
+       int best_perf_cpu = -1;
+       int backup_cpu = -1;
+
+       rcu_read_lock();
+
+       for_each_cpu_and(cpu, &p->cpus_allowed, cpu_active_mask) {
+               unsigned long capacity_orig = capacity_orig_of(cpu);
+               unsigned long wake_util;
+
+               /*
+                * A) Find best performance cpu.
+                *
+                * If the impact of cache hot and migration cost are excluded,
+                * distributed processing is the best way to achieve performance.
+                * To maximize performance, the idle cpu with the highest
+                * performance is selected first. If there are more than two idle
+                * cpus with the highest performance, choose the cpu with the
+                * shallowest idle state for fast reactivity.
+                */
+               if (idle_cpu(cpu)) {
+                       int idle_idx = idle_get_state_idx(cpu_rq(cpu));
+
+                       /* find biggest capacity cpu */
+                       if (capacity_orig < best_perf_cap_orig)
+                               continue;
+
+                       /*
+                        * if we find a better-performing cpu, re-initialize
+                        * best_perf_cstate
+                        */
+                       if (capacity_orig > best_perf_cap_orig) {
+                               best_perf_cap_orig = capacity_orig;
+                               best_perf_cstate = INT_MAX;
+                       }
+
+                       /* find shallowest idle state cpu */
+                       if (idle_idx >= best_perf_cstate)
+                               continue;
+
+                       /* Keep track of best idle CPU */
+                       best_perf_cstate = idle_idx;
+                       best_perf_cpu = cpu;
+                       continue;
+               }
+
+               /*
+                * B) Find backup performance cpu.
+                *
+                * Backup cpu also adopts distributed processing. In the absence
+                * of idle cpu, it is difficult to expect reactivity, so select
+                * the cpu with the biggest spare capacity to handle the most
+                * computations. Since a high performance cpu has a large capacity,
+                * cpu having a high performance is likely to be selected.
+                */
+               wake_util = cpu_util_wake(cpu, p);
+               if ((capacity_orig - wake_util) < max_spare_cap)
+                       continue;
+
+               max_spare_cap = capacity_orig - wake_util;
+               backup_cpu = cpu;
+       }
+
+       rcu_read_unlock();
+
+       trace_ems_select_perf_cpu(p, best_perf_cpu, backup_cpu);
+       if (best_perf_cpu == -1)
+               return backup_cpu;
+
+       return best_perf_cpu;
+}