357 lines
6.9 KiB
C
357 lines
6.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2020 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/mutex.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kernel_stat.h>
|
|
#include <linux/tick.h>
|
|
#include <linux/sched/stat.h>
|
|
#include <linux/sched/clock.h>
|
|
#include <linux/preempt.h>
|
|
#include <linux/slab.h>
|
|
#include <asm/div64.h>
|
|
#include <linux/sched/task.h>
|
|
//#include <trace/events/mtk_events.h>
|
|
#include <trace/events/block.h>
|
|
#include <linux/blk_types.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/swap.h>
|
|
#include <mt-plat/mtk_blocktag.h>
|
|
|
|
//#include <perf_tracker_trace.h>
|
|
#include "rs_index.h"
|
|
#include "rs_trace.h"
|
|
|
|
#define TIME_5S 5000000000
|
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
|
|
|
enum {
|
|
RS_RET_SUCCESS = 0,
|
|
RS_RET_ERROR = -1,
|
|
RS_RET_INVALID = -2,
|
|
};
|
|
|
|
enum {
|
|
RS_STATE_INACTIVE = 0,
|
|
RS_STATE_ACTIVE = 1,
|
|
RS_STATE_READY = 2,
|
|
};
|
|
|
|
struct rs_io_stat {
|
|
unsigned long long dur_ns;
|
|
|
|
int io_wl;
|
|
int io_top;
|
|
|
|
int io_reqc_r;
|
|
int io_reqc_w;
|
|
|
|
int io_q_dept;
|
|
|
|
struct list_head entry;
|
|
};
|
|
|
|
static DEFINE_MUTEX(rs_loading_mutex);
|
|
static DEFINE_SPINLOCK(rs_loading_slock);
|
|
|
|
static int mask;
|
|
static int is_active;
|
|
|
|
static void wq_func(struct work_struct *data);
|
|
static DECLARE_WORK(rs_work, (void *) wq_func);
|
|
static unsigned long long last_access_ts;
|
|
|
|
static struct list_head io_stat_list;
|
|
|
|
static unsigned long long prev_ts;
|
|
|
|
#define RSL_TAG "RSL:"
|
|
#define rsi_systrace(mask, pid, val, fmt...) \
|
|
do { \
|
|
if (mask) \
|
|
__rs_systrace_c_uint64(pid, val, fmt); \
|
|
} while (0)
|
|
|
|
#define rsi_systrace_log(pid, val, fmt...) \
|
|
rsi_systrace(mask, pid, val, RSL_TAG fmt)
|
|
#define rsi_systrace_def(pid, val, fmt...) \
|
|
rsi_systrace(1, pid, val, RSL_TAG fmt)
|
|
|
|
static void rs_lockprove(const char *tag)
|
|
{
|
|
WARN_ON(!mutex_is_locked(&rs_loading_mutex));
|
|
}
|
|
|
|
static u64 rs_get_time(void)
|
|
{
|
|
unsigned long long temp;
|
|
|
|
preempt_disable();
|
|
temp = cpu_clock(smp_processor_id());
|
|
preempt_enable();
|
|
|
|
return temp;
|
|
}
|
|
|
|
static void wq_func(struct work_struct *data)
|
|
{
|
|
unsigned long long cur_ts = rs_get_time();
|
|
unsigned long long diff, last;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&rs_loading_slock, flags);
|
|
last = last_access_ts;
|
|
spin_unlock_irqrestore(&rs_loading_slock, flags);
|
|
|
|
if (cur_ts < last)
|
|
return;
|
|
|
|
diff = cur_ts - last;
|
|
|
|
if (diff <= TIME_5S)
|
|
return;
|
|
|
|
perf_rsi_switch_collect(0);
|
|
|
|
}
|
|
|
|
static void rs_reset_io_list_locked(void)
|
|
{
|
|
struct rs_io_stat *pos, *next;
|
|
|
|
list_for_each_entry_safe(pos, next, &(io_stat_list), entry) {
|
|
list_del(&pos->entry);
|
|
kfree(pos);
|
|
}
|
|
}
|
|
|
|
static void rs_get_io_stat_oneshot(struct rs_sys_data *stat)
|
|
{
|
|
#ifdef CONFIG_MTK_BLOCK_TAG
|
|
struct mtk_btag_mictx_iostat_struct iostat = {0};
|
|
|
|
if (mtk_btag_mictx_get_data(&iostat))
|
|
mtk_btag_mictx_enable(1);
|
|
|
|
stat->io_wl = iostat.wl;
|
|
stat->io_top = iostat.top;
|
|
stat->io_reqc_r = iostat.reqcnt_r;
|
|
stat->io_reqc_w = iostat.reqcnt_w;
|
|
stat->io_q_dept = iostat.q_depth;
|
|
#endif
|
|
}
|
|
|
|
static int rs_get_io_stat(struct rs_sys_data *stat)
|
|
{
|
|
int ret = 0;
|
|
struct rs_io_stat *pos, *next;
|
|
unsigned long flags;
|
|
unsigned long long io_dur = 0;
|
|
long long io_wl = 0;
|
|
long long io_top = 0;
|
|
long long io_reqc_r = 0;
|
|
long long io_reqc_w = 0;
|
|
long long io_q_dept = 0;
|
|
int cnt = 0;
|
|
|
|
spin_lock_irqsave(&rs_loading_slock, flags);
|
|
|
|
if (list_empty(&(io_stat_list))) {
|
|
spin_unlock_irqrestore(&rs_loading_slock, flags);
|
|
rs_get_io_stat_oneshot(stat);
|
|
ret = -1;
|
|
goto EXIT;
|
|
}
|
|
|
|
list_for_each_entry_safe(pos, next, &(io_stat_list), entry) {
|
|
io_dur += pos->dur_ns;
|
|
io_wl += pos->io_wl;
|
|
io_top += pos->io_top;
|
|
io_reqc_r += pos->io_reqc_r;
|
|
io_reqc_w += pos->io_reqc_w;
|
|
io_q_dept += pos->io_q_dept;
|
|
cnt++;
|
|
}
|
|
|
|
rs_reset_io_list_locked();
|
|
|
|
spin_unlock_irqrestore(&rs_loading_slock, flags);
|
|
|
|
stat->io_wl = div64_u64(io_wl, cnt);
|
|
stat->io_top = div64_u64(io_top, cnt);
|
|
stat->io_reqc_r = div64_u64(io_reqc_r, cnt);
|
|
stat->io_reqc_w = div64_u64(io_reqc_w, cnt);
|
|
stat->io_q_dept = div64_u64(io_q_dept, cnt);
|
|
|
|
EXIT:
|
|
return ret;
|
|
}
|
|
|
|
int perf_rsi_get_data(struct rs_sys_data *sysdata)
|
|
{
|
|
int ret = RS_RET_SUCCESS;
|
|
int ret_io;
|
|
u64 cur_ts;
|
|
long long dur = 0;
|
|
unsigned long flags;
|
|
|
|
rs_lockprove(__func__);
|
|
|
|
cur_ts = rs_get_time();
|
|
if (prev_ts)
|
|
dur = cur_ts - prev_ts;
|
|
prev_ts = cur_ts;
|
|
|
|
spin_lock_irqsave(&rs_loading_slock, flags);
|
|
last_access_ts = cur_ts;
|
|
if (is_active == RS_STATE_ACTIVE) {
|
|
is_active = RS_STATE_READY;
|
|
ret = RS_RET_INVALID;
|
|
}
|
|
spin_unlock_irqrestore(&rs_loading_slock, flags);
|
|
|
|
ret_io = rs_get_io_stat(sysdata);
|
|
|
|
if (ret_io)
|
|
ret = RS_RET_ERROR;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void rs_start_collect(void)
|
|
{
|
|
struct rs_sys_data sysdata = {0};
|
|
unsigned long flags;
|
|
|
|
rs_lockprove(__func__);
|
|
|
|
spin_lock_irqsave(&rs_loading_slock, flags);
|
|
|
|
if (is_active != RS_STATE_INACTIVE) {
|
|
spin_unlock_irqrestore(&rs_loading_slock, flags);
|
|
return;
|
|
}
|
|
|
|
is_active = RS_STATE_ACTIVE;
|
|
|
|
spin_unlock_irqrestore(&rs_loading_slock, flags);
|
|
|
|
//register_trace_perf_index_l(rs_update_io_stat, NULL);
|
|
|
|
perf_rsi_get_data(&sysdata);
|
|
}
|
|
|
|
static void rs_stop_collect(void)
|
|
{
|
|
unsigned long flags;
|
|
|
|
rs_lockprove(__func__);
|
|
|
|
spin_lock_irqsave(&rs_loading_slock, flags);
|
|
|
|
if (is_active == RS_STATE_INACTIVE) {
|
|
spin_unlock_irqrestore(&rs_loading_slock, flags);
|
|
return;
|
|
}
|
|
is_active = RS_STATE_INACTIVE;
|
|
|
|
rs_reset_io_list_locked();
|
|
spin_unlock_irqrestore(&rs_loading_slock, flags);
|
|
|
|
//unregister_trace_perf_index_l(rs_update_io_stat, NULL);
|
|
|
|
prev_ts = 0;
|
|
}
|
|
|
|
void perf_rsi_switch_collect(int cmd)
|
|
{
|
|
mutex_lock(&rs_loading_mutex);
|
|
|
|
if (cmd)
|
|
rs_start_collect();
|
|
else
|
|
rs_stop_collect();
|
|
|
|
mutex_unlock(&rs_loading_mutex);
|
|
}
|
|
|
|
void perf_rsi_trans_index(__s32 *data, __s32 input_size)
|
|
{
|
|
struct rs_sys_data sysdata = {0};
|
|
int limit_size;
|
|
int ret;
|
|
|
|
mutex_lock(&rs_loading_mutex);
|
|
ret = perf_rsi_get_data(&sysdata);
|
|
mutex_unlock(&rs_loading_mutex);
|
|
|
|
if (ret == RS_RET_INVALID)
|
|
return;
|
|
|
|
limit_size = MIN(input_size, sizeof(struct rs_sys_data));
|
|
|
|
memcpy(data, &sysdata, limit_size);
|
|
}
|
|
|
|
#define RS_DEBUGFS_ENTRY(name) \
|
|
static int rs_##name##_open(struct inode *i, struct file *file) \
|
|
{ \
|
|
return single_open(file, rs_##name##_show, i->i_private); \
|
|
} \
|
|
\
|
|
static const struct file_operations rs_##name##_fops = { \
|
|
.owner = THIS_MODULE, \
|
|
.open = rs_##name##_open, \
|
|
.read = seq_read, \
|
|
.write = rs_##name##_write, \
|
|
.llseek = seq_lseek, \
|
|
.release = single_release, \
|
|
}
|
|
|
|
static int rs_mask_show(struct seq_file *m, void *unused)
|
|
{
|
|
seq_printf(m, "%d\n", mask);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t rs_mask_write(struct file *flip,
|
|
const char *ubuf, size_t cnt, loff_t *data)
|
|
{
|
|
uint32_t val;
|
|
int ret;
|
|
|
|
ret = kstrtou32_from_user(ubuf, cnt, 16, &val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
mask = val;
|
|
|
|
return cnt;
|
|
}
|
|
|
|
RS_DEBUGFS_ENTRY(mask);
|
|
|
|
int __init perf_rs_index_init(void)
|
|
{
|
|
INIT_LIST_HEAD(&(io_stat_list));
|
|
|
|
perf_rsi_getindex_fp = perf_rsi_trans_index;
|
|
perf_rsi_switch_collect_fp = perf_rsi_switch_collect;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void rs_index_exit(void)
|
|
{
|
|
}
|
|
|
|
MODULE_AUTHOR("Chiayu Gu <chiayu.gu@mediatek.com>");
|
|
MODULE_AUTHOR("Stanley Chu <stanley.chu@mediatek.com>");
|
|
MODULE_DESCRIPTION("MediaTek Performance Index Collector");
|
|
MODULE_LICENSE("GPL v2");
|
|
|