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:
Yandong Lin
2024-09-03 11:24:07 +08:00
committed by Tao Huang
parent 14dac2f903
commit 22c5e525ac
15 changed files with 172 additions and 9 deletions

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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) &&

View File

@@ -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,
},
};

View File

@@ -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,
},
};

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);