diff options
Diffstat (limited to 'drivers/gpu/drm/lima')
-rw-r--r-- | drivers/gpu/drm/lima/Kconfig | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/Makefile | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_ctx.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_ctx.h | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_devfreq.c | 234 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_devfreq.h | 41 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_device.c | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_device.h | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_drv.c | 112 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_drv.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_dump.h | 77 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_sched.c | 142 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_sched.h | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_trace.c | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/lima/lima_trace.h | 50 |
15 files changed, 713 insertions, 4 deletions
diff --git a/drivers/gpu/drm/lima/Kconfig b/drivers/gpu/drm/lima/Kconfig index d589f09d04d9..fa1d4f5df31e 100644 --- a/drivers/gpu/drm/lima/Kconfig +++ b/drivers/gpu/drm/lima/Kconfig @@ -10,5 +10,7 @@ config DRM_LIMA depends on OF select DRM_SCHED select DRM_GEM_SHMEM_HELPER + select PM_DEVFREQ + select DEVFREQ_GOV_SIMPLE_ONDEMAND help DRM driver for ARM Mali 400/450 GPUs. diff --git a/drivers/gpu/drm/lima/Makefile b/drivers/gpu/drm/lima/Makefile index a85444b0a1d4..ca2097b8e1ad 100644 --- a/drivers/gpu/drm/lima/Makefile +++ b/drivers/gpu/drm/lima/Makefile @@ -14,6 +14,8 @@ lima-y := \ lima_sched.o \ lima_ctx.o \ lima_dlbu.o \ - lima_bcast.o + lima_bcast.o \ + lima_trace.o \ + lima_devfreq.o obj-$(CONFIG_DRM_LIMA) += lima.o diff --git a/drivers/gpu/drm/lima/lima_ctx.c b/drivers/gpu/drm/lima/lima_ctx.c index 22fff6caa961..891d5cd5019a 100644 --- a/drivers/gpu/drm/lima/lima_ctx.c +++ b/drivers/gpu/drm/lima/lima_ctx.c @@ -27,6 +27,9 @@ int lima_ctx_create(struct lima_device *dev, struct lima_ctx_mgr *mgr, u32 *id) if (err < 0) goto err_out0; + ctx->pid = task_pid_nr(current); + get_task_comm(ctx->pname, current); + return 0; err_out0: diff --git a/drivers/gpu/drm/lima/lima_ctx.h b/drivers/gpu/drm/lima/lima_ctx.h index 6154e5c9bfe4..74e2be09090f 100644 --- a/drivers/gpu/drm/lima/lima_ctx.h +++ b/drivers/gpu/drm/lima/lima_ctx.h @@ -5,6 +5,7 @@ #define __LIMA_CTX_H__ #include <linux/xarray.h> +#include <linux/sched.h> #include "lima_device.h" @@ -13,6 +14,10 @@ struct lima_ctx { struct lima_device *dev; struct lima_sched_context context[lima_pipe_num]; atomic_t guilty; + + /* debug info */ + char pname[TASK_COMM_LEN]; + pid_t pid; }; struct lima_ctx_mgr { diff --git a/drivers/gpu/drm/lima/lima_devfreq.c b/drivers/gpu/drm/lima/lima_devfreq.c new file mode 100644 index 000000000000..8c4d21d07529 --- /dev/null +++ b/drivers/gpu/drm/lima/lima_devfreq.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + * + * Based on panfrost_devfreq.c: + * Copyright 2019 Collabora ltd. + */ +#include <linux/clk.h> +#include <linux/devfreq.h> +#include <linux/devfreq_cooling.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/pm_opp.h> +#include <linux/property.h> + +#include "lima_device.h" +#include "lima_devfreq.h" + +static void lima_devfreq_update_utilization(struct lima_devfreq *devfreq) +{ + ktime_t now, last; + + now = ktime_get(); + last = devfreq->time_last_update; + + if (devfreq->busy_count > 0) + devfreq->busy_time += ktime_sub(now, last); + else + devfreq->idle_time += ktime_sub(now, last); + + devfreq->time_last_update = now; +} + +static int lima_devfreq_target(struct device *dev, unsigned long *freq, + u32 flags) +{ + struct dev_pm_opp *opp; + int err; + + opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(opp)) + return PTR_ERR(opp); + dev_pm_opp_put(opp); + + err = dev_pm_opp_set_rate(dev, *freq); + if (err) + return err; + + return 0; +} + +static void lima_devfreq_reset(struct lima_devfreq *devfreq) +{ + devfreq->busy_time = 0; + devfreq->idle_time = 0; + devfreq->time_last_update = ktime_get(); +} + +static int lima_devfreq_get_dev_status(struct device *dev, + struct devfreq_dev_status *status) +{ + struct lima_device *ldev = dev_get_drvdata(dev); + struct lima_devfreq *devfreq = &ldev->devfreq; + unsigned long irqflags; + + status->current_frequency = clk_get_rate(ldev->clk_gpu); + + spin_lock_irqsave(&devfreq->lock, irqflags); + + lima_devfreq_update_utilization(devfreq); + + status->total_time = ktime_to_ns(ktime_add(devfreq->busy_time, + devfreq->idle_time)); + status->busy_time = ktime_to_ns(devfreq->busy_time); + + lima_devfreq_reset(devfreq); + + spin_unlock_irqrestore(&devfreq->lock, irqflags); + + dev_dbg(ldev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n", + status->busy_time, status->total_time, + status->busy_time / (status->total_time / 100), + status->current_frequency / 1000 / 1000); + + return 0; +} + +static struct devfreq_dev_profile lima_devfreq_profile = { + .polling_ms = 50, /* ~3 frames */ + .target = lima_devfreq_target, + .get_dev_status = lima_devfreq_get_dev_status, +}; + +void lima_devfreq_fini(struct lima_device *ldev) +{ + struct lima_devfreq *devfreq = &ldev->devfreq; + + if (devfreq->cooling) { + devfreq_cooling_unregister(devfreq->cooling); + devfreq->cooling = NULL; + } + + if (devfreq->devfreq) { + devm_devfreq_remove_device(&ldev->pdev->dev, + devfreq->devfreq); + devfreq->devfreq = NULL; + } + + if (devfreq->opp_of_table_added) { + dev_pm_opp_of_remove_table(&ldev->pdev->dev); + devfreq->opp_of_table_added = false; + } + + if (devfreq->regulators_opp_table) { + dev_pm_opp_put_regulators(devfreq->regulators_opp_table); + devfreq->regulators_opp_table = NULL; + } + + if (devfreq->clkname_opp_table) { + dev_pm_opp_put_clkname(devfreq->clkname_opp_table); + devfreq->clkname_opp_table = NULL; + } +} + +int lima_devfreq_init(struct lima_device *ldev) +{ + struct thermal_cooling_device *cooling; + struct device *dev = &ldev->pdev->dev; + struct opp_table *opp_table; + struct devfreq *devfreq; + struct lima_devfreq *ldevfreq = &ldev->devfreq; + struct dev_pm_opp *opp; + unsigned long cur_freq; + int ret; + + if (!device_property_present(dev, "operating-points-v2")) + /* Optional, continue without devfreq */ + return 0; + + spin_lock_init(&ldevfreq->lock); + + opp_table = dev_pm_opp_set_clkname(dev, "core"); + if (IS_ERR(opp_table)) { + ret = PTR_ERR(opp_table); + goto err_fini; + } + + ldevfreq->clkname_opp_table = opp_table; + + opp_table = dev_pm_opp_set_regulators(dev, + (const char *[]){ "mali" }, + 1); + if (IS_ERR(opp_table)) { + ret = PTR_ERR(opp_table); + + /* Continue if the optional regulator is missing */ + if (ret != -ENODEV) + goto err_fini; + } else { + ldevfreq->regulators_opp_table = opp_table; + } + + ret = dev_pm_opp_of_add_table(dev); + if (ret) + goto err_fini; + ldevfreq->opp_of_table_added = true; + + lima_devfreq_reset(ldevfreq); + + cur_freq = clk_get_rate(ldev->clk_gpu); + + opp = devfreq_recommended_opp(dev, &cur_freq, 0); + if (IS_ERR(opp)) { + ret = PTR_ERR(opp); + goto err_fini; + } + + lima_devfreq_profile.initial_freq = cur_freq; + dev_pm_opp_put(opp); + + devfreq = devm_devfreq_add_device(dev, &lima_devfreq_profile, + DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL); + if (IS_ERR(devfreq)) { + dev_err(dev, "Couldn't initialize GPU devfreq\n"); + ret = PTR_ERR(devfreq); + goto err_fini; + } + + ldevfreq->devfreq = devfreq; + + cooling = of_devfreq_cooling_register(dev->of_node, devfreq); + if (IS_ERR(cooling)) + dev_info(dev, "Failed to register cooling device\n"); + else + ldevfreq->cooling = cooling; + + return 0; + +err_fini: + lima_devfreq_fini(ldev); + return ret; +} + +void lima_devfreq_record_busy(struct lima_devfreq *devfreq) +{ + unsigned long irqflags; + + if (!devfreq->devfreq) + return; + + spin_lock_irqsave(&devfreq->lock, irqflags); + + lima_devfreq_update_utilization(devfreq); + + devfreq->busy_count++; + + spin_unlock_irqrestore(&devfreq->lock, irqflags); +} + +void lima_devfreq_record_idle(struct lima_devfreq *devfreq) +{ + unsigned long irqflags; + + if (!devfreq->devfreq) + return; + + spin_lock_irqsave(&devfreq->lock, irqflags); + + lima_devfreq_update_utilization(devfreq); + + WARN_ON(--devfreq->busy_count < 0); + + spin_unlock_irqrestore(&devfreq->lock, irqflags); +} diff --git a/drivers/gpu/drm/lima/lima_devfreq.h b/drivers/gpu/drm/lima/lima_devfreq.h new file mode 100644 index 000000000000..8d71ba9fb22a --- /dev/null +++ b/drivers/gpu/drm/lima/lima_devfreq.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com> */ + +#ifndef __LIMA_DEVFREQ_H__ +#define __LIMA_DEVFREQ_H__ + +#include <linux/spinlock.h> +#include <linux/ktime.h> + +struct devfreq; +struct opp_table; +struct thermal_cooling_device; + +struct lima_device; + +struct lima_devfreq { + struct devfreq *devfreq; + struct opp_table *clkname_opp_table; + struct opp_table *regulators_opp_table; + struct thermal_cooling_device *cooling; + bool opp_of_table_added; + + ktime_t busy_time; + ktime_t idle_time; + ktime_t time_last_update; + int busy_count; + /* + * Protect busy_time, idle_time, time_last_update and busy_count + * because these can be updated concurrently, for example by the GP + * and PP interrupts. + */ + spinlock_t lock; +}; + +int lima_devfreq_init(struct lima_device *ldev); +void lima_devfreq_fini(struct lima_device *ldev); + +void lima_devfreq_record_busy(struct lima_devfreq *devfreq); +void lima_devfreq_record_idle(struct lima_devfreq *devfreq); + +#endif diff --git a/drivers/gpu/drm/lima/lima_device.c b/drivers/gpu/drm/lima/lima_device.c index 19829b543024..247f51fd40a2 100644 --- a/drivers/gpu/drm/lima/lima_device.c +++ b/drivers/gpu/drm/lima/lima_device.c @@ -214,6 +214,8 @@ static int lima_init_gp_pipe(struct lima_device *dev) struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_gp; int err; + pipe->ldev = dev; + err = lima_sched_pipe_init(pipe, "gp"); if (err) return err; @@ -244,6 +246,8 @@ static int lima_init_pp_pipe(struct lima_device *dev) struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_pp; int err, i; + pipe->ldev = dev; + err = lima_sched_pipe_init(pipe, "pp"); if (err) return err; @@ -344,6 +348,12 @@ int lima_device_init(struct lima_device *ldev) if (err) goto err_out5; + ldev->dump.magic = LIMA_DUMP_MAGIC; + ldev->dump.version_major = LIMA_DUMP_MAJOR; + ldev->dump.version_minor = LIMA_DUMP_MINOR; + INIT_LIST_HEAD(&ldev->error_task_list); + mutex_init(&ldev->error_task_list_lock); + dev_info(ldev->dev, "bus rate = %lu\n", clk_get_rate(ldev->clk_bus)); dev_info(ldev->dev, "mod rate = %lu", clk_get_rate(ldev->clk_gpu)); @@ -370,6 +380,13 @@ err_out0: void lima_device_fini(struct lima_device *ldev) { int i; + struct lima_sched_error_task *et, *tmp; + + list_for_each_entry_safe(et, tmp, &ldev->error_task_list, list) { + list_del(&et->list); + kvfree(et); + } + mutex_destroy(&ldev->error_task_list_lock); lima_fini_pp_pipe(ldev); lima_fini_gp_pipe(ldev); diff --git a/drivers/gpu/drm/lima/lima_device.h b/drivers/gpu/drm/lima/lima_device.h index 31158d86271c..06fd9636dd72 100644 --- a/drivers/gpu/drm/lima/lima_device.h +++ b/drivers/gpu/drm/lima/lima_device.h @@ -6,8 +6,12 @@ #include <drm/drm_device.h> #include <linux/delay.h> +#include <linux/list.h> +#include <linux/mutex.h> #include "lima_sched.h" +#include "lima_dump.h" +#include "lima_devfreq.h" enum lima_gpu_id { lima_gpu_mali400 = 0, @@ -94,6 +98,13 @@ struct lima_device { u32 *dlbu_cpu; dma_addr_t dlbu_dma; + + struct lima_devfreq devfreq; + + /* debug info */ + struct lima_dump_head dump; + struct list_head error_task_list; + struct mutex error_task_list_lock; }; static inline struct lima_device * diff --git a/drivers/gpu/drm/lima/lima_drv.c b/drivers/gpu/drm/lima/lima_drv.c index 2daac64d8955..bbbdc8455e2f 100644 --- a/drivers/gpu/drm/lima/lima_drv.c +++ b/drivers/gpu/drm/lima/lima_drv.c @@ -10,12 +10,14 @@ #include <drm/drm_prime.h> #include <drm/lima_drm.h> +#include "lima_device.h" #include "lima_drv.h" #include "lima_gem.h" #include "lima_vm.h" int lima_sched_timeout_ms; uint lima_heap_init_nr_pages = 8; +uint lima_max_error_tasks; MODULE_PARM_DESC(sched_timeout_ms, "task run timeout in ms"); module_param_named(sched_timeout_ms, lima_sched_timeout_ms, int, 0444); @@ -23,6 +25,9 @@ module_param_named(sched_timeout_ms, lima_sched_timeout_ms, int, 0444); MODULE_PARM_DESC(heap_init_nr_pages, "heap buffer init number of pages"); module_param_named(heap_init_nr_pages, lima_heap_init_nr_pages, uint, 0444); +MODULE_PARM_DESC(max_error_tasks, "max number of error tasks to save"); +module_param_named(max_error_tasks, lima_max_error_tasks, uint, 0644); + static int lima_ioctl_get_param(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_lima_get_param *args = data; @@ -272,6 +277,93 @@ static struct drm_driver lima_drm_driver = { .gem_prime_mmap = drm_gem_prime_mmap, }; +struct lima_block_reader { + void *dst; + size_t base; + size_t count; + size_t off; + ssize_t read; +}; + +static bool lima_read_block(struct lima_block_reader *reader, + void *src, size_t src_size) +{ + size_t max_off = reader->base + src_size; + + if (reader->off < max_off) { + size_t size = min_t(size_t, max_off - reader->off, + reader->count); + + memcpy(reader->dst, src + (reader->off - reader->base), size); + + reader->dst += size; + reader->off += size; + reader->read += size; + reader->count -= size; + } + + reader->base = max_off; + + return !!reader->count; +} + +static ssize_t lima_error_state_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct lima_device *ldev = dev_get_drvdata(dev); + struct lima_sched_error_task *et; + struct lima_block_reader reader = { + .dst = buf, + .count = count, + .off = off, + }; + + mutex_lock(&ldev->error_task_list_lock); + + if (lima_read_block(&reader, &ldev->dump, sizeof(ldev->dump))) { + list_for_each_entry(et, &ldev->error_task_list, list) { + if (!lima_read_block(&reader, et->data, et->size)) + break; + } + } + + mutex_unlock(&ldev->error_task_list_lock); + return reader.read; +} + +static ssize_t lima_error_state_write(struct file *file, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct lima_device *ldev = dev_get_drvdata(dev); + struct lima_sched_error_task *et, *tmp; + + mutex_lock(&ldev->error_task_list_lock); + + list_for_each_entry_safe(et, tmp, &ldev->error_task_list, list) { + list_del(&et->list); + kvfree(et); + } + + ldev->dump.size = 0; + ldev->dump.num_tasks = 0; + + mutex_unlock(&ldev->error_task_list_lock); + + return count; +} + +static const struct bin_attribute lima_error_state_attr = { + .attr.name = "error", + .attr.mode = 0600, + .size = 0, + .read = lima_error_state_read, + .write = lima_error_state_write, +}; + static int lima_pdev_probe(struct platform_device *pdev) { struct lima_device *ldev; @@ -306,18 +398,31 @@ static int lima_pdev_probe(struct platform_device *pdev) if (err) goto err_out1; + err = lima_devfreq_init(ldev); + if (err) { + dev_err(&pdev->dev, "Fatal error during devfreq init\n"); + goto err_out2; + } + /* * Register the DRM device with the core and the connectors with * sysfs. */ err = drm_dev_register(ddev, 0); if (err < 0) - goto err_out2; + goto err_out3; + + platform_set_drvdata(pdev, ldev); + + if (sysfs_create_bin_file(&ldev->dev->kobj, &lima_error_state_attr)) + dev_warn(ldev->dev, "fail to create error state sysfs\n"); return 0; -err_out2: +err_out3: lima_device_fini(ldev); +err_out2: + lima_devfreq_fini(ldev); err_out1: drm_dev_put(ddev); err_out0: @@ -330,7 +435,10 @@ static int lima_pdev_remove(struct platform_device *pdev) struct lima_device *ldev = platform_get_drvdata(pdev); struct drm_device *ddev = ldev->ddev; + sysfs_remove_bin_file(&ldev->dev->kobj, &lima_error_state_attr); + platform_set_drvdata(pdev, NULL); drm_dev_unregister(ddev); + lima_devfreq_fini(ldev); lima_device_fini(ldev); drm_dev_put(ddev); lima_sched_slab_fini(); diff --git a/drivers/gpu/drm/lima/lima_drv.h b/drivers/gpu/drm/lima/lima_drv.h index f492ecc6a5d9..fdbd4077c768 100644 --- a/drivers/gpu/drm/lima/lima_drv.h +++ b/drivers/gpu/drm/lima/lima_drv.h @@ -10,6 +10,7 @@ extern int lima_sched_timeout_ms; extern uint lima_heap_init_nr_pages; +extern uint lima_max_error_tasks; struct lima_vm; struct lima_bo; diff --git a/drivers/gpu/drm/lima/lima_dump.h b/drivers/gpu/drm/lima/lima_dump.h new file mode 100644 index 000000000000..ca243d99c51b --- /dev/null +++ b/drivers/gpu/drm/lima/lima_dump.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* Copyright 2020 Qiang Yu <yuq825@gmail.com> */ + +#ifndef __LIMA_DUMP_H__ +#define __LIMA_DUMP_H__ + +#include <linux/types.h> + +/** + * dump file format for all the information to start a lima task + * + * top level format + * | magic code "LIMA" | format version | num tasks | data size | + * | reserved | reserved | reserved | reserved | + * | task 1 ID | task 1 size | num chunks | reserved | task 1 data | + * | task 2 ID | task 2 size | num chunks | reserved | task 2 data | + * ... + * + * task data format + * | chunk 1 ID | chunk 1 size | reserved | reserved | chunk 1 data | + * | chunk 2 ID | chunk 2 size | reserved | reserved | chunk 2 data | + * ... + * + */ + +#define LIMA_DUMP_MAJOR 1 +#define LIMA_DUMP_MINOR 0 + +#define LIMA_DUMP_MAGIC 0x414d494c + +struct lima_dump_head { + __u32 magic; + __u16 version_major; + __u16 version_minor; + __u32 num_tasks; + __u32 size; + __u32 reserved[4]; +}; + +#define LIMA_DUMP_TASK_GP 0 +#define LIMA_DUMP_TASK_PP 1 +#define LIMA_DUMP_TASK_NUM 2 + +struct lima_dump_task { + __u32 id; + __u32 size; + __u32 num_chunks; + __u32 reserved; +}; + +#define LIMA_DUMP_CHUNK_FRAME 0 +#define LIMA_DUMP_CHUNK_BUFFER 1 +#define LIMA_DUMP_CHUNK_PROCESS_NAME 2 +#define LIMA_DUMP_CHUNK_PROCESS_ID 3 +#define LIMA_DUMP_CHUNK_NUM 4 + +struct lima_dump_chunk { + __u32 id; + __u32 size; + __u32 reserved[2]; +}; + +struct lima_dump_chunk_buffer { + __u32 id; + __u32 size; + __u32 va; + __u32 reserved; +}; + +struct lima_dump_chunk_pid { + __u32 id; + __u32 size; + __u32 pid; + __u32 reserved; +}; + +#endif diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c index 3886999b4533..a2db1c937424 100644 --- a/drivers/gpu/drm/lima/lima_sched.c +++ b/drivers/gpu/drm/lima/lima_sched.c @@ -3,14 +3,16 @@ #include <linux/kthread.h> #include <linux/slab.h> -#include <linux/xarray.h> +#include <linux/vmalloc.h> +#include "lima_devfreq.h" #include "lima_drv.h" #include "lima_sched.h" #include "lima_vm.h" #include "lima_mmu.h" #include "lima_l2_cache.h" #include "lima_gem.h" +#include "lima_trace.h" struct lima_fence { struct dma_fence base; @@ -176,6 +178,7 @@ struct dma_fence *lima_sched_context_queue_task(struct lima_sched_context *conte { struct dma_fence *fence = dma_fence_get(&task->base.s_fence->finished); + trace_lima_task_submit(task); drm_sched_entity_push_job(&task->base, &context->base); return fence; } @@ -214,6 +217,8 @@ static struct dma_fence *lima_sched_run_job(struct drm_sched_job *job) */ ret = dma_fence_get(task->fence); + lima_devfreq_record_busy(&pipe->ldev->devfreq); + pipe->current_task = task; /* this is needed for MMU to work correctly, otherwise GP/PP @@ -250,12 +255,141 @@ static struct dma_fence *lima_sched_run_job(struct drm_sched_job *job) if (last_vm) lima_vm_put(last_vm); + trace_lima_task_run(task); + pipe->error = false; pipe->task_run(pipe, task); return task->fence; } +static void lima_sched_build_error_task_list(struct lima_sched_task *task) +{ + struct lima_sched_error_task *et; + struct lima_sched_pipe *pipe = to_lima_pipe(task->base.sched); + struct lima_ip *ip = pipe->processor[0]; + int pipe_id = ip->id == lima_ip_gp ? lima_pipe_gp : lima_pipe_pp; + struct lima_device *dev = ip->dev; + struct lima_sched_context *sched_ctx = + container_of(task->base.entity, + struct lima_sched_context, base); + struct lima_ctx *ctx = + container_of(sched_ctx, struct lima_ctx, context[pipe_id]); + struct lima_dump_task *dt; + struct lima_dump_chunk *chunk; + struct lima_dump_chunk_pid *pid_chunk; + struct lima_dump_chunk_buffer *buffer_chunk; + u32 size, task_size, mem_size; + int i; + + mutex_lock(&dev->error_task_list_lock); + + if (dev->dump.num_tasks >= lima_max_error_tasks) { + dev_info(dev->dev, "fail to save task state: error task list is full\n"); + goto out; + } + + /* frame chunk */ + size = sizeof(struct lima_dump_chunk) + pipe->frame_size; + /* process name chunk */ + size += sizeof(struct lima_dump_chunk) + sizeof(ctx->pname); + /* pid chunk */ + size += sizeof(struct lima_dump_chunk); + /* buffer chunks */ + for (i = 0; i < task->num_bos; i++) { + struct lima_bo *bo = task->bos[i]; + + size += sizeof(struct lima_dump_chunk); + size += bo->heap_size ? bo->heap_size : lima_bo_size(bo); + } + + task_size = size + sizeof(struct lima_dump_task); + mem_size = task_size + sizeof(*et); + et = kvmalloc(mem_size, GFP_KERNEL); + if (!et) { + dev_err(dev->dev, "fail to alloc task dump buffer of size %x\n", + mem_size); + goto out; + } + + et->data = et + 1; + et->size = task_size; + + dt = et->data; + memset(dt, 0, sizeof(*dt)); + dt->id = pipe_id; + dt->size = size; + + chunk = (struct lima_dump_chunk *)(dt + 1); + memset(chunk, 0, sizeof(*chunk)); + chunk->id = LIMA_DUMP_CHUNK_FRAME; + chunk->size = pipe->frame_size; + memcpy(chunk + 1, task->frame, pipe->frame_size); + dt->num_chunks++; + + chunk = (void *)(chunk + 1) + chunk->size; + memset(chunk, 0, sizeof(*chunk)); + chunk->id = LIMA_DUMP_CHUNK_PROCESS_NAME; + chunk->size = sizeof(ctx->pname); + memcpy(chunk + 1, ctx->pname, sizeof(ctx->pname)); + dt->num_chunks++; + + pid_chunk = (void *)(chunk + 1) + chunk->size; + memset(pid_chunk, 0, sizeof(*pid_chunk)); + pid_chunk->id = LIMA_DUMP_CHUNK_PROCESS_ID; + pid_chunk->pid = ctx->pid; + dt->num_chunks++; + + buffer_chunk = (void *)(pid_chunk + 1) + pid_chunk->size; + for (i = 0; i < task->num_bos; i++) { + struct lima_bo *bo = task->bos[i]; + void *data; + + memset(buffer_chunk, 0, sizeof(*buffer_chunk)); + buffer_chunk->id = LIMA_DUMP_CHUNK_BUFFER; + buffer_chunk->va = lima_vm_get_va(task->vm, bo); + + if (bo->heap_size) { + buffer_chunk->size = bo->heap_size; + + data = vmap(bo->base.pages, bo->heap_size >> PAGE_SHIFT, + VM_MAP, pgprot_writecombine(PAGE_KERNEL)); + if (!data) { + kvfree(et); + goto out; + } + + memcpy(buffer_chunk + 1, data, buffer_chunk->size); + + vunmap(data); + } else { + buffer_chunk->size = lima_bo_size(bo); + + data = drm_gem_shmem_vmap(&bo->base.base); + if (IS_ERR_OR_NULL(data)) { + kvfree(et); + goto out; + } + + memcpy(buffer_chunk + 1, data, buffer_chunk->size); + + drm_gem_shmem_vunmap(&bo->base.base, data); + } + + buffer_chunk = (void *)(buffer_chunk + 1) + buffer_chunk->size; + dt->num_chunks++; + } + + list_add(&et->list, &dev->error_task_list); + dev->dump.size += et->size; + dev->dump.num_tasks++; + + dev_info(dev->dev, "save error task state success\n"); + +out: + mutex_unlock(&dev->error_task_list_lock); +} + static void lima_sched_timedout_job(struct drm_sched_job *job) { struct lima_sched_pipe *pipe = to_lima_pipe(job->sched); @@ -268,6 +402,8 @@ static void lima_sched_timedout_job(struct drm_sched_job *job) drm_sched_increase_karma(&task->base); + lima_sched_build_error_task_list(task); + pipe->task_error(pipe); if (pipe->bcast_mmu) @@ -285,6 +421,8 @@ static void lima_sched_timedout_job(struct drm_sched_job *job) pipe->current_vm = NULL; pipe->current_task = NULL; + lima_devfreq_record_idle(&pipe->ldev->devfreq); + drm_sched_resubmit_jobs(&pipe->base); drm_sched_start(&pipe->base, true); } @@ -364,5 +502,7 @@ void lima_sched_pipe_task_done(struct lima_sched_pipe *pipe) } else { pipe->task_fini(pipe); dma_fence_signal(task->fence); + + lima_devfreq_record_idle(&pipe->ldev->devfreq); } } diff --git a/drivers/gpu/drm/lima/lima_sched.h b/drivers/gpu/drm/lima/lima_sched.h index d64393fb50a9..90f03c48ef4a 100644 --- a/drivers/gpu/drm/lima/lima_sched.h +++ b/drivers/gpu/drm/lima/lima_sched.h @@ -5,9 +5,18 @@ #define __LIMA_SCHED_H__ #include <drm/gpu_scheduler.h> +#include <linux/list.h> +#include <linux/xarray.h> +struct lima_device; struct lima_vm; +struct lima_sched_error_task { + struct list_head list; + void *data; + u32 size; +}; + struct lima_sched_task { struct drm_sched_job base; @@ -44,6 +53,8 @@ struct lima_sched_pipe { u32 fence_seqno; spinlock_t fence_lock; + struct lima_device *ldev; + struct lima_sched_task *current_task; struct lima_vm *current_vm; diff --git a/drivers/gpu/drm/lima/lima_trace.c b/drivers/gpu/drm/lima/lima_trace.c new file mode 100644 index 000000000000..ea1c7289bebc --- /dev/null +++ b/drivers/gpu/drm/lima/lima_trace.c @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* Copyright 2020 Qiang Yu <yuq825@gmail.com> */ + +#include "lima_sched.h" + +#define CREATE_TRACE_POINTS +#include "lima_trace.h" diff --git a/drivers/gpu/drm/lima/lima_trace.h b/drivers/gpu/drm/lima/lima_trace.h new file mode 100644 index 000000000000..3a430e93d384 --- /dev/null +++ b/drivers/gpu/drm/lima/lima_trace.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* Copyright 2020 Qiang Yu <yuq825@gmail.com> */ + +#if !defined(_LIMA_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _LIMA_TRACE_H_ + +#include <linux/tracepoint.h> + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM lima +#define TRACE_INCLUDE_FILE lima_trace + +DECLARE_EVENT_CLASS(lima_task, + TP_PROTO(struct lima_sched_task *task), + TP_ARGS(task), + TP_STRUCT__entry( + __field(uint64_t, task_id) + __field(unsigned int, context) + __field(unsigned int, seqno) + __string(pipe, task->base.sched->name) + ), + + TP_fast_assign( + __entry->task_id = task->base.id; + __entry->context = task->base.s_fence->finished.context; + __entry->seqno = task->base.s_fence->finished.seqno; + __assign_str(pipe, task->base.sched->name) + ), + + TP_printk("task=%llu, context=%u seqno=%u pipe=%s", + __entry->task_id, __entry->context, __entry->seqno, + __get_str(pipe)) +); + +DEFINE_EVENT(lima_task, lima_task_submit, + TP_PROTO(struct lima_sched_task *task), + TP_ARGS(task) +); + +DEFINE_EVENT(lima_task, lima_task_run, + TP_PROTO(struct lima_sched_task *task), + TP_ARGS(task) +); + +#endif + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm/lima +#include <trace/define_trace.h> |