kgsl负载分析

1,075 阅读2分钟

gpubusy

GPU有个寄存器叫GEN7_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_L,专门用来记录GPU的运行时间。

驱动更新GPU频率时,都会执行到tz_get_target_freq函数,tz_get_target_freq函数会调用kgsl_devfreq_get_dev_status函数。

kgsl_devfreq_get_dev_status函数会取寄存器的值,然后保存到下面的busy_time变量。同时,该函数还会保存函数调用时间到下面的time变量,然后计算本次调用时间和上一次的调用时间差值并保存到下面的total_time变量。当total_time时间大于UPDATE_BUSY_VAL(UPDATE_BUSY_VAL等于1ms)时,还会把busy_time和total_time值保存到下面的busy_old变量和total_old变量。

image.png

上面流程的时序如下:

我们可以把busy_old理解成GPU在上一个频率实际的运行时间,total_old理解成GPU在上一个频率的持续时间。

可以通过cat /sys/class/kgsl/kgsl-3d0/gpubusy查看上面的busy_old变量和total_old变量的值。

static ssize_t gpubusy_show(struct device *dev,
                struct device_attribute *attr, char *buf)
{
    int ret;
    struct kgsl_device *device = dev_get_drvdata(dev);
    struct kgsl_clk_stats *stats = &device->pwrctrl.clk_stats;

    ret = scnprintf(buf, PAGE_SIZE, "%7d %7d\n",
            stats->busy_old, stats->total_old);
    if (!test_bit(KGSL_PWRFLAGS_AXI_ON, &device->pwrctrl.power_flags)) {
        stats->busy_old = 0;
        stats->total_old = 0;
    }
    return ret;
}

也可以通过kgsl_gpubusy event查看上面的busy_old变量和total_old变量的值。

adb shell "echo 1 > /sys/kernel/tracing/tracing_on"
adb shell "echo 1 > /sys/kernel/tracing/events/kgsl/kgsl_gpubusy/enable"
adb shell "cat /sys/kernel/tracing/trace_pipe | grep kgsl_gpubusy"

===>输出格式如下
kgsl_gpubusy: device_name=kgsl-3d0 busy=68997 elapsed=1012709

基本上每次给gpu硬件发送绘制命令都会触发gpu驱动申请更新gpu频率,从而触发kgsl_devfreq_get_dev_status函数执行

gpu_load

驱动更新GPU频率时,都会执行到tz_get_target_freq函数,tz_get_target_freq函数调用kgsl_devfreq_get_dev_status函数更新busy_time和total_time,还会调用compute_work_load计算GPU的负载。

compute_work_load会把busy_time归一化,然后叠加到下面的acc_relative_busy变量。把total_time叠加到下面的acc_total变量。如图:

image.png

梳理一下acc_relative_busy计算步骤:

1. busy_time = (GEN7_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_L*10)/192
2. busy_time = (busy_time * mod_percent) / mod_percent
3. acc_relative_busy = (busy_time * current_frequency) / freq_table[0] //归一化

可以通过cat /sys/class/devfreq/3d00000.qcom,kgsl-3d0/gpu_load节点计算的gpu的负载:

static ssize_t gpu_load_show(struct device *dev,
        struct device_attribute *attr,
        char *buf)
{
    unsigned long sysfs_busy_perc = 0;
    spin_lock(&sample_lock);
    if (acc_total)
        sysfs_busy_perc = (acc_relative_busy * 100) / acc_total;
    acc_total = 0;
    acc_relative_busy = 0;
    spin_unlock(&sample_lock);
    return snprintf(buf, PAGE_SIZE, "%lu\n", sysfs_busy_perc);
}

每次cat gpu_load节点后,acc_total和acc_relative_busy重置。故该节点其统计的上一次cat gpu_load操作到本次cat gpu_load操作之间负载。