mirror of
https://github.com/Evolution-X-Devices/kernel_google_b1c1
synced 2026-01-27 17:26:47 +00:00
Kalesh reported mm_event shows 5~8% regression in pft test of pms.
The most overheads comes from ktime_get when I investigated it.
Let's use jiffies instead of ktime_get.
The side effect is jiffies doesn't provides such low-resolution time
like ktime_get so avg_latency would be easier to be zero and not providing
exact max latency as well. However, the goal is not providing *exact*
latency but found some events were too slow at that time.
Thus, if the slow events continued to happen in the period, the stats
will represent the *trend*.
<idle>-0 [002] d.h2 696.836558: mm_event_record: ufs_read_send_cmd count=2 avg_lat=6666 max_lat=6666
<idle>-0 [002] d.h2 696.836559: mm_event_record: f2fs_read_data count=49 avg_lat=3333 max_lat=33333
<...>-27 [002] ..s. 696.836852: mm_event_record: f2fs_read_data count=6 avg_lat=6666 max_lat=10000
LightweightExec-25872 [000] .... 696.838052: mm_event_record: min_flt count=3 avg_lat=0 max_lat=3333
<...>-28336 [000] .... 696.843788: mm_event_record: min_flt count=1 avg_lat=0 max_lat=0
In coral, this patch reduces the overhead from 7% to %2 in pft
benchmark.
fault/sec stddev overhead
mm_event_disable 451513.86 2.63%
mm_event_enable 419609.68 2.37% 7.07%
mm_event_ktime_improve 443256.45 2.68% 1.83%
Bug: 169113282
Signed-off-by: Minchan Kim <minchan@google.com>
Change-Id: If058eff7d502c98286d103ab2937115d7dc63b90
189 lines
4.8 KiB
C
189 lines
4.8 KiB
C
#include <linux/mm.h>
|
|
#include <linux/mm_event.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/debugfs.h>
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/mm_event.h>
|
|
/* msec */
|
|
static unsigned long period_ms __read_mostly = 500;
|
|
static unsigned long vmstat_period_ms __read_mostly = 1000;
|
|
static unsigned long vmstat_next_period;
|
|
|
|
static DEFINE_SPINLOCK(vmstat_lock);
|
|
static DEFINE_RWLOCK(period_lock);
|
|
|
|
void mm_event_task_init(struct task_struct *tsk)
|
|
{
|
|
memset(tsk->mm_event, 0, sizeof(tsk->mm_event));
|
|
tsk->next_period = 0;
|
|
}
|
|
|
|
static void record_vmstat(void)
|
|
{
|
|
int cpu;
|
|
struct mm_event_vmstat vmstat;
|
|
|
|
if (time_is_after_jiffies(vmstat_next_period))
|
|
return;
|
|
|
|
/* Need double check under the lock */
|
|
spin_lock(&vmstat_lock);
|
|
if (time_is_after_jiffies(vmstat_next_period)) {
|
|
spin_unlock(&vmstat_lock);
|
|
return;
|
|
}
|
|
vmstat_next_period = jiffies + msecs_to_jiffies(vmstat_period_ms);
|
|
spin_unlock(&vmstat_lock);
|
|
|
|
memset(&vmstat, 0, sizeof(vmstat));
|
|
vmstat.free = global_page_state(NR_FREE_PAGES);
|
|
vmstat.slab = global_page_state(NR_SLAB_RECLAIMABLE) +
|
|
global_page_state(NR_SLAB_UNRECLAIMABLE);
|
|
|
|
vmstat.file = global_node_page_state(NR_ACTIVE_FILE) +
|
|
global_node_page_state(NR_INACTIVE_FILE);
|
|
vmstat.anon = global_node_page_state(NR_ACTIVE_ANON) +
|
|
global_node_page_state(NR_INACTIVE_ANON);
|
|
vmstat.ion = global_node_page_state(NR_ION_HEAP);
|
|
|
|
vmstat.ws_refault = global_node_page_state(WORKINGSET_REFAULT);
|
|
vmstat.ws_activate = global_node_page_state(WORKINGSET_ACTIVATE);
|
|
vmstat.mapped = global_node_page_state(NR_FILE_MAPPED);
|
|
|
|
for_each_online_cpu(cpu) {
|
|
struct vm_event_state *this = &per_cpu(vm_event_states, cpu);
|
|
|
|
/* sectors to kbytes for PGPGIN/PGPGOUT */
|
|
vmstat.pgin += this->event[PGPGIN] / 2;
|
|
vmstat.pgout += this->event[PGPGOUT] / 2;
|
|
vmstat.swpin += this->event[PSWPIN];
|
|
vmstat.swpout += this->event[PSWPOUT];
|
|
vmstat.reclaim_steal += this->event[PGSTEAL_DIRECT] +
|
|
this->event[PGSTEAL_KSWAPD];
|
|
vmstat.reclaim_scan += this->event[PGSCAN_DIRECT] +
|
|
this->event[PGSCAN_KSWAPD];
|
|
vmstat.compact_scan += this->event[COMPACTFREE_SCANNED] +
|
|
this->event[COMPACTMIGRATE_SCANNED];
|
|
}
|
|
trace_mm_event_vmstat_record(&vmstat);
|
|
}
|
|
|
|
static void record_stat(void)
|
|
{
|
|
int i;
|
|
|
|
if (time_is_after_jiffies(current->next_period))
|
|
return;
|
|
|
|
read_lock(&period_lock);
|
|
current->next_period = jiffies + msecs_to_jiffies(period_ms);
|
|
read_unlock(&period_lock);
|
|
|
|
for (i = 0; i < MM_TYPE_NUM; i++) {
|
|
if (current->mm_event[i].count == 0)
|
|
continue;
|
|
trace_mm_event_record(i, ¤t->mm_event[i]);
|
|
memset(¤t->mm_event[i], 0,
|
|
sizeof(struct mm_event_task));
|
|
}
|
|
|
|
record_vmstat();
|
|
}
|
|
|
|
void mm_event_record(enum mm_event_type event, unsigned long s_jiffies)
|
|
{
|
|
unsigned long elapsed = jiffies - s_jiffies;
|
|
|
|
current->mm_event[event].count++;
|
|
current->mm_event[event].accm_lat += elapsed;
|
|
if (elapsed > current->mm_event[event].max_lat)
|
|
current->mm_event[event].max_lat = elapsed;
|
|
record_stat();
|
|
}
|
|
|
|
void mm_event_count(enum mm_event_type event, int count)
|
|
{
|
|
current->mm_event[event].count += count;
|
|
record_stat();
|
|
}
|
|
|
|
static struct dentry *mm_event_root;
|
|
|
|
static int period_ms_set(void *data, u64 val)
|
|
{
|
|
if (val < 1 || val > ULONG_MAX)
|
|
return -EINVAL;
|
|
|
|
write_lock(&period_lock);
|
|
period_ms = (unsigned long)val;
|
|
write_unlock(&period_lock);
|
|
return 0;
|
|
}
|
|
|
|
static int period_ms_get(void *data, u64 *val)
|
|
{
|
|
read_lock(&period_lock);
|
|
*val = period_ms;
|
|
read_unlock(&period_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vmstat_period_ms_set(void *data, u64 val)
|
|
{
|
|
if (val < 1 || val > ULONG_MAX)
|
|
return -EINVAL;
|
|
|
|
spin_lock(&vmstat_lock);
|
|
vmstat_period_ms = (unsigned long)val;
|
|
spin_unlock(&vmstat_lock);
|
|
return 0;
|
|
}
|
|
|
|
static int vmstat_period_ms_get(void *data, u64 *val)
|
|
{
|
|
spin_lock(&vmstat_lock);
|
|
*val = vmstat_period_ms;
|
|
spin_unlock(&vmstat_lock);
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_SIMPLE_ATTRIBUTE(period_ms_operations, period_ms_get,
|
|
period_ms_set, "%llu\n");
|
|
DEFINE_SIMPLE_ATTRIBUTE(vmstat_period_ms_operations, vmstat_period_ms_get,
|
|
vmstat_period_ms_set, "%llu\n");
|
|
|
|
static int __init mm_event_init(void)
|
|
{
|
|
struct dentry *entry;
|
|
|
|
mm_event_root = debugfs_create_dir("mm_event", NULL);
|
|
if (!mm_event_root) {
|
|
pr_warn("debugfs dir <mm_event> creation failed\n");
|
|
return PTR_ERR(mm_event_root);
|
|
}
|
|
|
|
entry = debugfs_create_file("period_ms", 0644,
|
|
mm_event_root, NULL, &period_ms_operations);
|
|
|
|
if (IS_ERR(entry)) {
|
|
pr_warn("debugfs file mm_event_task creation failed\n");
|
|
debugfs_remove_recursive(mm_event_root);
|
|
return PTR_ERR(entry);
|
|
}
|
|
|
|
entry = debugfs_create_file("vmstat_period_ms", 0644,
|
|
mm_event_root, NULL, &vmstat_period_ms_operations);
|
|
if (IS_ERR(entry)) {
|
|
pr_warn("debugfs file vmstat_mm_event_task creation failed\n");
|
|
debugfs_remove_recursive(mm_event_root);
|
|
return PTR_ERR(entry);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
subsys_initcall(mm_event_init);
|