mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 02:50:49 +09:00
video: rockchip: mpp: add devices load statistics
1.The default statistical time is 0, means the load statistics function is disabled. If want to enable the function, need echo a valid value to load_interval. e.g. 1000ms: $echo 1000 > /proc/mpp_service/load_interval 2.show load info: rk3588_t_evb7:/ # cat /proc/mpp_service/load fdb51000.avsd-plus load: 0.00% utilization: 0.00% fdb50400.vdpu load: 0.00% utilization: 0.00% fdb50000.vepu load: 0.00% utilization: 0.00% fdb90000.jpegd load: 0.00% utilization: 0.00% fdba0000.jpege-core load: 0.00% utilization: 0.00% fdba4000.jpege-core load: 0.00% utilization: 0.00% fdba8000.jpege-core load: 0.00% utilization: 0.00% fdbac000.jpege-core load: 0.00% utilization: 0.00% fdbb0000.iep load: 0.00% utilization: 0.00% fdbd0000.rkvenc-core load: 97.56% utilization: 93.45% fdbe0000.rkvenc-core load: 97.33% utilization: 92.51% fdc38100.rkvdec-core load: 0.00% utilization: 0.00% fdc48100.rkvdec-core load: 0.00% utilization: 0.00% av1d-master load: 0.00% utilization: 0.00% Signed-off-by: Yandong Lin <yandong.lin@rock-chips.com> Change-Id: I0b1c6d3efc7cd7708e7f367ad917b865db67a08a
This commit is contained in:
@@ -761,13 +761,11 @@ static int mpp_task_run(struct mpp_dev *mpp,
|
||||
struct mpp_task *task)
|
||||
{
|
||||
int ret;
|
||||
u32 timing_en;
|
||||
struct mpp_session *session = task->session;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
timing_en = mpp->srv->timing_en;
|
||||
if (timing_en) {
|
||||
if (mpp->srv->timing_en) {
|
||||
task->on_run = ktime_get();
|
||||
set_bit(TASK_TIMING_RUN, &task->state);
|
||||
}
|
||||
@@ -819,6 +817,71 @@ static int mpp_task_run(struct mpp_dev *mpp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mpp_dev_load(struct mpp_dev *mpp, struct mpp_task *mpp_task)
|
||||
{
|
||||
struct mpp_load_info *load_info = &mpp->load_info;
|
||||
ktime_t now;
|
||||
s64 time_diff_us;
|
||||
|
||||
if (!mpp->srv->load_interval) {
|
||||
if (mpp->load_en) {
|
||||
mpp_dev_load_clear(mpp);
|
||||
mpp->srv->timing_en = 0;
|
||||
mpp->load_en = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mpp->load_en) {
|
||||
mpp->srv->timing_en = 1;
|
||||
mpp->load_en = 1;
|
||||
load_info->load_time = ktime_get();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mpp_task->on_run)
|
||||
return;
|
||||
|
||||
now = ktime_get();
|
||||
time_diff_us = ktime_us_delta(now, load_info->load_time);
|
||||
load_info->busy_time += ktime_us_delta(now, mpp_task->on_run);
|
||||
if (mpp_task->hw_time)
|
||||
load_info->hw_busy_time += mpp_task->hw_time;
|
||||
else
|
||||
load_info->hw_busy_time += ktime_us_delta(mpp_task->on_irq,
|
||||
mpp_task->on_sched_timeout);
|
||||
/* 1s update */
|
||||
if (time_diff_us > mpp->srv->load_interval * 1000) {
|
||||
u32 tmp = div64_s64(load_info->busy_time * 10000, time_diff_us);
|
||||
u32 load = tmp / 100;
|
||||
u32 load_frac = tmp % 100;
|
||||
u32 max_load = 100;
|
||||
|
||||
if (mpp->queue->core_count > 1)
|
||||
max_load *= mpp->queue->core_count;
|
||||
|
||||
load_info->load = load > max_load ? max_load : load;
|
||||
load_info->load_frac = load > max_load ? 0 : load_frac;
|
||||
|
||||
tmp = div64_s64(load_info->hw_busy_time * 10000, time_diff_us);
|
||||
load = tmp / 100;
|
||||
load_frac = tmp % 100;
|
||||
load_info->utilization = load > max_load ? max_load : load;
|
||||
load_info->utilization_frac = load > max_load ? 0 : load_frac;
|
||||
|
||||
load_info->busy_time = 0;
|
||||
load_info->hw_busy_time = 0;
|
||||
load_info->load_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
void mpp_dev_load_clear(struct mpp_dev *mpp)
|
||||
{
|
||||
struct mpp_load_info *load_info = &mpp->load_info;
|
||||
|
||||
memset(load_info, 0, sizeof(*load_info));
|
||||
}
|
||||
|
||||
static void try_process_running_task(struct mpp_dev *mpp)
|
||||
{
|
||||
struct mpp_task *mpp_task, *n;
|
||||
@@ -842,7 +905,7 @@ static void try_process_running_task(struct mpp_dev *mpp)
|
||||
if (mpp->auto_freq_en && mpp->hw_ops->reduce_freq &&
|
||||
list_empty(&mpp->queue->pending_list))
|
||||
mpp->hw_ops->reduce_freq(mpp);
|
||||
|
||||
mpp_dev_load(mpp, mpp_task);
|
||||
if (mpp->dev_ops->isr)
|
||||
mpp->dev_ops->isr(mpp);
|
||||
enable_irq(mpp->irq);
|
||||
@@ -2554,6 +2617,25 @@ int mpp_clk_set_rate(struct mpp_clk_info *clk_info,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mpp_common_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct mpp_dev *mpp = dev_get_drvdata(dev);
|
||||
|
||||
mpp_dev_load_clear(mpp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mpp_common_runtime_resume(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct dev_pm_ops mpp_common_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(mpp_common_runtime_suspend, mpp_common_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ROCKCHIP_MPP_PROC_FS
|
||||
static int fops_show_u32(struct seq_file *file, void *v)
|
||||
{
|
||||
|
||||
@@ -264,6 +264,15 @@ struct mpp_mem_region {
|
||||
bool is_dup;
|
||||
};
|
||||
|
||||
struct mpp_load_info {
|
||||
s64 busy_time;
|
||||
s64 hw_busy_time;
|
||||
ktime_t load_time;
|
||||
u32 load;
|
||||
u32 load_frac;
|
||||
u32 utilization;
|
||||
u32 utilization_frac;
|
||||
};
|
||||
|
||||
struct mpp_dev {
|
||||
struct device *dev;
|
||||
@@ -322,6 +331,8 @@ struct mpp_dev {
|
||||
/* common per-device procfs */
|
||||
u32 disable;
|
||||
u32 timing_check;
|
||||
u32 load_en;
|
||||
struct mpp_load_info load_info;
|
||||
};
|
||||
|
||||
struct mpp_session {
|
||||
@@ -450,6 +461,7 @@ struct mpp_task {
|
||||
s32 core_id;
|
||||
/* hw cycles */
|
||||
u32 hw_cycles;
|
||||
u32 hw_time;
|
||||
};
|
||||
|
||||
struct mpp_taskqueue {
|
||||
@@ -534,6 +546,7 @@ struct mpp_service {
|
||||
|
||||
/* global timing record flag */
|
||||
u32 timing_en;
|
||||
u32 load_interval;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -688,6 +701,8 @@ unsigned long mpp_get_clk_info_rate_hz(struct mpp_clk_info *clk_info,
|
||||
enum MPP_CLOCK_MODE mode);
|
||||
int mpp_clk_set_rate(struct mpp_clk_info *clk_info,
|
||||
enum MPP_CLOCK_MODE mode);
|
||||
void mpp_dev_load(struct mpp_dev *mpp, struct mpp_task *mpp_task);
|
||||
void mpp_dev_load_clear(struct mpp_dev *mpp);
|
||||
|
||||
static inline int mpp_write(struct mpp_dev *mpp, u32 reg, u32 val)
|
||||
{
|
||||
@@ -853,4 +868,6 @@ extern struct platform_driver rockchip_av1dec_driver;
|
||||
extern struct platform_driver rockchip_jpgenc_driver;
|
||||
extern struct platform_driver rockchip_vdpp_driver;
|
||||
|
||||
extern const struct dev_pm_ops mpp_common_pm_ops;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1129,6 +1129,7 @@ struct platform_driver rockchip_iep2_driver = {
|
||||
.driver = {
|
||||
.name = IEP2_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_iep2_match),
|
||||
.pm = &mpp_common_pm_ops,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(rockchip_iep2_driver);
|
||||
|
||||
@@ -654,6 +654,7 @@ struct platform_driver rockchip_jpgdec_driver = {
|
||||
.driver = {
|
||||
.name = JPGDEC_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_jpgdec_dt_match),
|
||||
.pm = &mpp_common_pm_ops,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(rockchip_jpgdec_driver);
|
||||
|
||||
@@ -1922,6 +1922,7 @@ struct platform_driver rockchip_rkvdec_driver = {
|
||||
.driver = {
|
||||
.name = RKVDEC_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_rkvdec_dt_match),
|
||||
.pm = &mpp_common_pm_ops,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(rockchip_rkvdec_driver);
|
||||
|
||||
@@ -630,6 +630,7 @@ static int rkvdec_vdpu383_isr(struct mpp_dev *mpp)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
mpp_task->hw_cycles = mpp_read(mpp, RKVDEC_PERF_WORKING_CNT);
|
||||
mpp_task->hw_time = mpp_task->hw_cycles / (dec->cycle_clk->real_rate_hz / 1000000);
|
||||
mpp_time_diff_with_hw_time(mpp_task, dec->cycle_clk->real_rate_hz);
|
||||
mpp->cur_task = NULL;
|
||||
task = to_rkvdec2_task(mpp_task);
|
||||
@@ -2043,6 +2044,8 @@ static int __maybe_unused rkvdec2_runtime_suspend(struct device *dev)
|
||||
|
||||
if (mpp->hw_ops->clk_off)
|
||||
mpp->hw_ops->clk_off(mpp);
|
||||
|
||||
mpp_dev_load_clear(mpp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1098,6 +1098,8 @@ static void rkvdec2_link_try_dequeue(struct mpp_dev *mpp)
|
||||
|
||||
task->irq_status = irq_status;
|
||||
mpp_task->hw_cycles = tb_reg[info->tb_reg_cycle];
|
||||
mpp_task->hw_time = mpp_task->hw_cycles /
|
||||
(dec->cycle_clk->real_rate_hz / 1000000);
|
||||
mpp_time_diff_with_hw_time(mpp_task, dec->cycle_clk->real_rate_hz);
|
||||
rkvdec2_link_finish(mpp, mpp_task);
|
||||
|
||||
@@ -1128,6 +1130,7 @@ static void rkvdec2_link_try_dequeue(struct mpp_dev *mpp)
|
||||
}
|
||||
|
||||
wake_up(&mpp_task->wait);
|
||||
mpp_dev_load(mpp, mpp_task);
|
||||
kref_put(&mpp_task->ref, rkvdec2_link_free_task);
|
||||
}
|
||||
|
||||
@@ -1190,6 +1193,10 @@ static int mpp_task_queue(struct mpp_dev *mpp, struct mpp_task *mpp_task)
|
||||
if (!rkvdec2_link_prepare(mpp, mpp_task))
|
||||
return -EBUSY;
|
||||
|
||||
if (mpp->srv->timing_en) {
|
||||
mpp_task->on_run = ktime_get();
|
||||
set_bit(TASK_TIMING_RUN, &mpp_task->state);
|
||||
}
|
||||
rkvdec2_link_enqueue(link_dec, mpp_task);
|
||||
|
||||
set_bit(TASK_STATE_RUNNING, &mpp_task->state);
|
||||
@@ -1649,6 +1656,8 @@ static int rkvdec2_soft_ccu_dequeue(struct mpp_taskqueue *queue)
|
||||
set_bit(TASK_STATE_HANDLE, &mpp_task->state);
|
||||
cancel_delayed_work(&mpp_task->timeout_work);
|
||||
mpp_task->hw_cycles = mpp_read(mpp, RKVDEC_PERF_WORKING_CNT);
|
||||
mpp_task->hw_time = mpp_task->hw_cycles /
|
||||
(dec->cycle_clk->real_rate_hz / 1000000);
|
||||
mpp_time_diff_with_hw_time(mpp_task, dec->cycle_clk->real_rate_hz);
|
||||
task->irq_status = irq_status;
|
||||
mpp_debug(DEBUG_IRQ_CHECK, "irq_status=%08x, timeout=%u, abort=%u\n",
|
||||
@@ -1669,6 +1678,7 @@ static int rkvdec2_soft_ccu_dequeue(struct mpp_taskqueue *queue)
|
||||
spin_lock_irqsave(&queue->running_lock, flags);
|
||||
list_del_init(&mpp_task->queue_link);
|
||||
spin_unlock_irqrestore(&queue->running_lock, flags);
|
||||
mpp_dev_load(mpp, mpp_task);
|
||||
kref_put(&mpp_task->ref, mpp_free_task);
|
||||
} else {
|
||||
/* NOTE: break when meet not finish */
|
||||
@@ -2024,7 +2034,6 @@ void rkvdec2_soft_ccu_worker(struct kthread_work *work_s)
|
||||
struct mpp_dev *mpp = container_of(work_s, struct mpp_dev, work);
|
||||
struct mpp_taskqueue *queue = mpp->queue;
|
||||
struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp);
|
||||
u32 timing_en = mpp->srv->timing_en;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
@@ -2068,7 +2077,7 @@ void rkvdec2_soft_ccu_worker(struct kthread_work *work_s)
|
||||
if (!mpp)
|
||||
break;
|
||||
|
||||
if (timing_en) {
|
||||
if (mpp->srv->timing_en) {
|
||||
mpp_task->on_run = ktime_get();
|
||||
set_bit(TASK_TIMING_RUN, &mpp_task->state);
|
||||
}
|
||||
@@ -2246,6 +2255,8 @@ static int rkvdec2_hard_ccu_dequeue(struct mpp_taskqueue *queue,
|
||||
set_bit(TASK_STATE_HANDLE, &mpp_task->state);
|
||||
cancel_delayed_work(&mpp_task->timeout_work);
|
||||
mpp_task->hw_cycles = tb_reg[hw->tb_reg_cycle];
|
||||
mpp_task->hw_time = mpp_task->hw_cycles /
|
||||
(dec->cycle_clk->real_rate_hz / 1000000);
|
||||
mpp_time_diff_with_hw_time(mpp_task, dec->cycle_clk->real_rate_hz);
|
||||
task->irq_status = irq_status ? irq_status : RKVDEC_ERROR_STA;
|
||||
|
||||
@@ -2280,7 +2291,7 @@ static int rkvdec2_hard_ccu_dequeue(struct mpp_taskqueue *queue,
|
||||
irq_status, timeout_flag, abort_flag);
|
||||
atomic_inc(&queue->reset_request);
|
||||
}
|
||||
|
||||
mpp_dev_load(mpp_task->session->mpp, mpp_task);
|
||||
kref_put(&mpp_task->ref, mpp_free_task);
|
||||
} else {
|
||||
dequeue_none++;
|
||||
@@ -2644,12 +2655,14 @@ void rkvdec2_hard_ccu_worker(struct kthread_work *work_s)
|
||||
mpp_task = rkvdec2_hard_ccu_prepare(mpp_task, dec->ccu, dec->link_dec->info);
|
||||
if (!mpp_task)
|
||||
break;
|
||||
|
||||
if (mpp->srv->timing_en) {
|
||||
mpp_task->on_run = ktime_get();
|
||||
set_bit(TASK_TIMING_RUN, &mpp_task->state);
|
||||
}
|
||||
rkvdec2_ccu_power_on(queue, dec->ccu);
|
||||
rkvdec2_hard_ccu_enqueue(dec->ccu, mpp_task, queue, mpp);
|
||||
mpp_taskqueue_pending_to_run(queue, mpp_task);
|
||||
}
|
||||
|
||||
/* 4. poweroff when running and pending list are empty */
|
||||
mutex_lock(&queue->pending_lock);
|
||||
if (list_empty(&queue->running_list) &&
|
||||
|
||||
@@ -1461,5 +1461,6 @@ struct platform_driver rockchip_rkvenc_driver = {
|
||||
.driver = {
|
||||
.name = RKVENC_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_rkvenc_dt_match),
|
||||
.pm = &mpp_common_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2973,5 +2973,6 @@ struct platform_driver rockchip_rkvenc2_driver = {
|
||||
.driver = {
|
||||
.name = RKVENC_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_rkvenc_dt_match),
|
||||
.pm = &mpp_common_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -309,6 +309,39 @@ static int mpp_show_support_device(struct seq_file *file, void *v)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpp_show_device_load(struct seq_file *file, void *v)
|
||||
{
|
||||
u32 i, j;
|
||||
struct mpp_service *srv = file->private;
|
||||
|
||||
if (!srv->load_interval) {
|
||||
seq_puts(file, "please set load_interval first!!!\n");
|
||||
seq_puts(file, "e.g. set 1000ms to load_interval:\n");
|
||||
seq_puts(file, "echo 1000 > /proc/mpp_service/load_interval\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < MPP_DEVICE_BUTT; i++) {
|
||||
struct mpp_taskqueue *queue = srv->task_queues[i];
|
||||
|
||||
if (!queue)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < MPP_MAX_CORE_NUM; j++) {
|
||||
struct mpp_dev *mpp = queue->cores[j];
|
||||
|
||||
if (!mpp)
|
||||
continue;
|
||||
seq_printf(file, "%-25s load: %3d.%02d%% utilization: %3d.%02d%%\n",
|
||||
dev_name(mpp->dev),
|
||||
mpp->load_info.load, mpp->load_info.load_frac,
|
||||
mpp->load_info.utilization, mpp->load_info.utilization_frac);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpp_procfs_init(struct mpp_service *srv)
|
||||
{
|
||||
srv->procfs = proc_mkdir(MPP_SERVICE_NAME, NULL);
|
||||
@@ -327,7 +360,12 @@ static int mpp_procfs_init(struct mpp_service *srv)
|
||||
/* show support devices */
|
||||
proc_create_single_data("supports-device", 0444,
|
||||
srv->procfs, mpp_show_support_device, srv);
|
||||
srv->timing_en = 1;
|
||||
mpp_procfs_create_u32("timing_en", 0644, srv->procfs, &srv->timing_en);
|
||||
/* show per device load info */
|
||||
proc_create_single_data("load", 0444, srv->procfs, mpp_show_device_load, srv);
|
||||
srv->load_interval = 0;
|
||||
mpp_procfs_create_u32("load_interval", 0644, srv->procfs, &srv->load_interval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -822,6 +822,7 @@ struct platform_driver rockchip_vdpp_driver = {
|
||||
.driver = {
|
||||
.name = VDPP_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_vdpp_dt_match),
|
||||
.pm = &mpp_common_pm_ops,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(rockchip_vdpp_driver);
|
||||
|
||||
@@ -967,6 +967,7 @@ struct platform_driver rockchip_vdpu1_driver = {
|
||||
.driver = {
|
||||
.name = VDPU1_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_vdpu1_dt_match),
|
||||
.pm = &mpp_common_pm_ops,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(rockchip_vdpu1_driver);
|
||||
|
||||
@@ -803,6 +803,7 @@ struct platform_driver rockchip_vdpu2_driver = {
|
||||
.driver = {
|
||||
.name = VDPU2_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_vdpu2_dt_match),
|
||||
.pm = &mpp_common_pm_ops,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(rockchip_vdpu2_driver);
|
||||
|
||||
@@ -790,6 +790,7 @@ struct platform_driver rockchip_vepu1_driver = {
|
||||
.driver = {
|
||||
.name = VEPU1_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_vepu1_dt_match),
|
||||
.pm = &mpp_common_pm_ops,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(rockchip_vepu1_driver);
|
||||
|
||||
@@ -1275,6 +1275,7 @@ struct platform_driver rockchip_vepu2_driver = {
|
||||
.driver = {
|
||||
.name = VEPU2_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_vepu2_dt_match),
|
||||
.pm = &mpp_common_pm_ops,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(rockchip_vepu2_driver);
|
||||
|
||||
Reference in New Issue
Block a user