From 298f78895b081911e0b3605f07d79ebd3d4cf7b0 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 2 Jul 2019 16:43:34 +0100 Subject: iommu/io-pgtable: Rename iommu_gather_ops to iommu_flush_ops In preparation for TLB flush gathering in the IOMMU API, rename the iommu_gather_ops structure in io-pgtable to iommu_flush_ops, which better describes its purpose and avoids the potential for confusion between different levels of the API. $ find linux/ -type f -name '*.[ch]' | xargs sed -i 's/gather_ops/flush_ops/g' Signed-off-by: Will Deacon --- drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index 92ac995dd9c6..17bceb11e708 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -257,7 +257,7 @@ static void mmu_tlb_sync_context(void *cookie) // TODO: Wait 1000 GPU cycles for HW_ISSUE_6367/T60X } -static const struct iommu_gather_ops mmu_tlb_ops = { +static const struct iommu_flush_ops mmu_tlb_ops = { .tlb_flush_all = mmu_tlb_inv_context_s1, .tlb_add_flush = mmu_tlb_inv_range_nosync, .tlb_sync = mmu_tlb_sync_context, -- cgit From 05aed9412b0bd0d9a985d94010c42ff0a5c6cc29 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 2 Jul 2019 16:44:25 +0100 Subject: iommu/io-pgtable: Hook up ->tlb_flush_walk() and ->tlb_flush_leaf() in drivers Hook up ->tlb_flush_walk() and ->tlb_flush_leaf() in drivers using the io-pgtable API so that we can start making use of them in the page-table code. For now, they can just wrap the implementations of ->tlb_add_flush and ->tlb_sync pending future optimisation in each driver. Signed-off-by: Will Deacon --- drivers/gpu/drm/panfrost/panfrost_mmu.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index 17bceb11e708..651858147bd6 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -257,8 +257,22 @@ static void mmu_tlb_sync_context(void *cookie) // TODO: Wait 1000 GPU cycles for HW_ISSUE_6367/T60X } +static void mmu_tlb_flush_walk(unsigned long iova, size_t size, size_t granule, + void *cookie) +{ + mmu_tlb_sync_context(cookie); +} + +static void mmu_tlb_flush_leaf(unsigned long iova, size_t size, size_t granule, + void *cookie) +{ + mmu_tlb_sync_context(cookie); +} + static const struct iommu_flush_ops mmu_tlb_ops = { .tlb_flush_all = mmu_tlb_inv_context_s1, + .tlb_flush_walk = mmu_tlb_flush_walk, + .tlb_flush_leaf = mmu_tlb_flush_leaf, .tlb_add_flush = mmu_tlb_inv_range_nosync, .tlb_sync = mmu_tlb_sync_context, }; -- cgit From abfd6fe0cd535d31ee83b668be6eb59ce6a8469d Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 2 Jul 2019 16:44:41 +0100 Subject: iommu/io-pgtable: Replace ->tlb_add_flush() with ->tlb_add_page() The ->tlb_add_flush() callback in the io-pgtable API now looks a bit silly: - It takes a size and a granule, which are always the same - It takes a 'bool leaf', which is always true - It only ever flushes a single page With that in mind, replace it with an optional ->tlb_add_page() callback that drops the useless parameters. Signed-off-by: Will Deacon --- drivers/gpu/drm/panfrost/panfrost_mmu.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index 651858147bd6..ff9af320cacc 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -247,10 +247,6 @@ static void mmu_tlb_inv_context_s1(void *cookie) mmu_hw_do_operation(pfdev, 0, 0, ~0UL, AS_COMMAND_FLUSH_MEM); } -static void mmu_tlb_inv_range_nosync(unsigned long iova, size_t size, - size_t granule, bool leaf, void *cookie) -{} - static void mmu_tlb_sync_context(void *cookie) { //struct panfrost_device *pfdev = cookie; @@ -273,7 +269,6 @@ static const struct iommu_flush_ops mmu_tlb_ops = { .tlb_flush_all = mmu_tlb_inv_context_s1, .tlb_flush_walk = mmu_tlb_flush_walk, .tlb_flush_leaf = mmu_tlb_flush_leaf, - .tlb_add_flush = mmu_tlb_inv_range_nosync, .tlb_sync = mmu_tlb_sync_context, }; -- cgit From e953f7f2fa78d1c7fd064171f88457c6b1e21af9 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 2 Jul 2019 16:44:50 +0100 Subject: iommu/io-pgtable: Remove unused ->tlb_sync() callback The ->tlb_sync() callback is no longer used, so it can be removed. Signed-off-by: Will Deacon --- drivers/gpu/drm/panfrost/panfrost_mmu.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index ff9af320cacc..de22a2276e00 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -269,7 +269,6 @@ static const struct iommu_flush_ops mmu_tlb_ops = { .tlb_flush_all = mmu_tlb_inv_context_s1, .tlb_flush_walk = mmu_tlb_flush_walk, .tlb_flush_leaf = mmu_tlb_flush_leaf, - .tlb_sync = mmu_tlb_sync_context, }; static const char *access_type_name(struct panfrost_device *pfdev, -- cgit From a2d3a382d6c682e22b263c9e7f0d857c3fa6c9d6 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 2 Jul 2019 16:44:58 +0100 Subject: iommu/io-pgtable: Pass struct iommu_iotlb_gather to ->unmap() Update the io-pgtable ->unmap() function to take an iommu_iotlb_gather pointer as an argument, and update the callers as appropriate. Signed-off-by: Will Deacon --- drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index de22a2276e00..6e8145c36e93 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -222,7 +222,7 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo) size_t unmapped_page; size_t pgsize = get_pgsize(iova, len - unmapped_len); - unmapped_page = ops->unmap(ops, iova, pgsize); + unmapped_page = ops->unmap(ops, iova, pgsize, NULL); if (!unmapped_page) break; -- cgit From c90f30812a79abeb9f7f0f78e69d314104b825be Mon Sep 17 00:00:00 2001 From: Steven Price Date: Thu, 22 Aug 2019 10:32:18 +0100 Subject: drm/panfrost: Add missing check for pfdev->regulator When modifying panfrost_devfreq_target() to support a device without a regulator defined I missed the check on the error path. Let's add it. Reported-by: Dan Carpenter Fixes: e21dd290881b ("drm/panfrost: Enable devfreq to work without regulator") Signed-off-by: Steven Price Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20190822093218.26014-1-steven.price@arm.com --- drivers/gpu/drm/panfrost/panfrost_devfreq.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c index 710d903f8e0d..a1f5fa6a742a 100644 --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c @@ -53,8 +53,10 @@ static int panfrost_devfreq_target(struct device *dev, unsigned long *freq, if (err) { dev_err(dev, "Cannot set frequency %lu (%d)\n", target_rate, err); - regulator_set_voltage(pfdev->regulator, pfdev->devfreq.cur_volt, - pfdev->devfreq.cur_volt); + if (pfdev->regulator) + regulator_set_voltage(pfdev->regulator, + pfdev->devfreq.cur_volt, + pfdev->devfreq.cur_volt); return err; } -- cgit From aebe8c22a9121e89cdd5c725f2ebed1ee237ff16 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 22 Aug 2019 21:12:09 -0500 Subject: drm/panfrost: Fix possible suspend in panfrost_remove Calls to panfrost_device_fini() access the h/w, but we already done a pm_runtime_put_sync_autosuspend() beforehand. This only works if the autosuspend delay is long enough. A 0ms delay will hang the system when removing the device. Fix this by moving the pm_runtime_put_sync_suspend() after the panfrost_device_fini() call. Cc: Tomeu Vizoso Cc: David Airlie Cc: Daniel Vetter Signed-off-by: Rob Herring Reviewed-by: Steven Price Acked-by: Alyssa Rosenzweig Link: https://patchwork.freedesktop.org/patch/msgid/20190823021216.5862-2-robh@kernel.org --- drivers/gpu/drm/panfrost/panfrost_drv.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 44a558c6e17e..d74442d71048 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -570,11 +570,13 @@ static int panfrost_remove(struct platform_device *pdev) drm_dev_unregister(ddev); panfrost_gem_shrinker_cleanup(ddev); + pm_runtime_get_sync(pfdev->dev); - pm_runtime_put_sync_autosuspend(pfdev->dev); - pm_runtime_disable(pfdev->dev); panfrost_devfreq_fini(pfdev); panfrost_device_fini(pfdev); + pm_runtime_put_sync_suspend(pfdev->dev); + pm_runtime_disable(pfdev->dev); + drm_dev_put(ddev); return 0; } -- cgit From 55b9a0549e4303ca43481f9ba2cdfa305277d97d Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 22 Aug 2019 21:12:14 -0500 Subject: drm/panfrost: Use mutex_trylock in panfrost_gem_purge Lockdep reports a circular locking dependency with pages_lock taken in the shrinker callback. The deadlock can't actually happen with current users at least as a BO will never be purgeable when pages_lock is held. To be safe, let's use mutex_trylock() instead and bail if a BO is locked already. Cc: Tomeu Vizoso Cc: David Airlie Cc: Daniel Vetter Signed-off-by: Rob Herring Reviewed-by: Steven Price Acked-by: Alyssa Rosenzweig Link: https://patchwork.freedesktop.org/patch/msgid/20190823021216.5862-7-robh@kernel.org --- drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c index d191632b6197..458f0fa68111 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c +++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c @@ -36,15 +36,18 @@ panfrost_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc return count; } -static void panfrost_gem_purge(struct drm_gem_object *obj) +static bool panfrost_gem_purge(struct drm_gem_object *obj) { struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); - mutex_lock(&shmem->pages_lock); + + if (!mutex_trylock(&shmem->pages_lock)) + return false; panfrost_mmu_unmap(to_panfrost_bo(obj)); drm_gem_shmem_purge_locked(obj); mutex_unlock(&shmem->pages_lock); + return true; } static unsigned long @@ -61,8 +64,8 @@ panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) list_for_each_entry_safe(shmem, tmp, &pfdev->shrinker_list, madv_list) { if (freed >= sc->nr_to_scan) break; - if (drm_gem_shmem_is_purgeable(shmem)) { - panfrost_gem_purge(&shmem->base); + if (drm_gem_shmem_is_purgeable(shmem) && + panfrost_gem_purge(&shmem->base)) { freed += shmem->base.size >> PAGE_SHIFT; list_del_init(&shmem->madv_list); } -- cgit From 635430797d3fccb958e929d3911e39cb9c1ea641 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2019 17:33:10 -0500 Subject: drm/panfrost: Rework runtime PM initialization There's a few issues with the runtime PM initialization. The documentation states pm_runtime_set_active() should be called before pm_runtime_enable(). The pm_runtime_put_autosuspend() could suspend the GPU before panfrost_perfcnt_init() is called which touches the h/w. The autosuspend delay keeps things from breaking. There's no need explicitly power off the GPU only to wake back up with pm_runtime_get_sync(). Just delaying pm_runtime_enable to the end of probe is sufficient. Lets move all the runtime PM calls into the probe() function so they are all in one place and are done after all initialization. Cc: Tomeu Vizoso Cc: Steven Price Cc: David Airlie Cc: Daniel Vetter Acked-by: Alyssa Rosenzweig Signed-off-by: Rob Herring Reviewed-by: Robin Murphy Reviewed-by: Steven Price Link: https://patchwork.freedesktop.org/patch/msgid/20190826223317.28509-2-robh@kernel.org --- drivers/gpu/drm/panfrost/panfrost_device.c | 9 --------- drivers/gpu/drm/panfrost/panfrost_drv.c | 10 ++++++---- 2 files changed, 6 insertions(+), 13 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index 4da71bb56c20..73805210834e 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include "panfrost_device.h" @@ -166,14 +165,6 @@ int panfrost_device_init(struct panfrost_device *pfdev) if (err) goto err_out4; - /* runtime PM will wake us up later */ - panfrost_gpu_power_off(pfdev); - - pm_runtime_set_active(pfdev->dev); - pm_runtime_get_sync(pfdev->dev); - pm_runtime_mark_last_busy(pfdev->dev); - pm_runtime_put_autosuspend(pfdev->dev); - err = panfrost_perfcnt_init(pfdev); if (err) goto err_out5; diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index d74442d71048..bc2ddeb55f5d 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -523,10 +523,6 @@ static int panfrost_probe(struct platform_device *pdev) mutex_init(&pfdev->shrinker_lock); INIT_LIST_HEAD(&pfdev->shrinker_list); - pm_runtime_use_autosuspend(pfdev->dev); - pm_runtime_set_autosuspend_delay(pfdev->dev, 50); /* ~3 frames */ - pm_runtime_enable(pfdev->dev); - err = panfrost_device_init(pfdev); if (err) { if (err != -EPROBE_DEFER) @@ -541,6 +537,12 @@ static int panfrost_probe(struct platform_device *pdev) goto err_out1; } + pm_runtime_set_active(pfdev->dev); + pm_runtime_mark_last_busy(pfdev->dev); + pm_runtime_enable(pfdev->dev); + pm_runtime_set_autosuspend_delay(pfdev->dev, 50); /* ~3 frames */ + pm_runtime_use_autosuspend(pfdev->dev); + /* * Register the DRM device with the core and the connectors with * sysfs -- cgit From 330bec4b7ccf4a556469cfec2ad695a180e35954 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2019 17:33:11 -0500 Subject: drm/panfrost: Hold runtime PM reference until jobs complete Doing a pm_runtime_put as soon as a job is submitted is wrong as it should not happen until the job completes. It works currently because we are relying on the autosuspend timeout to keep the h/w enabled. Fixes: f3ba91228e8e ("drm/panfrost: Add initial panfrost driver") Cc: Tomeu Vizoso Cc: Steven Price Cc: Alyssa Rosenzweig Cc: David Airlie Cc: Daniel Vetter Signed-off-by: Rob Herring Acked-by: Alyssa Rosenzweig Reviewed-by: Steven Price Link: https://patchwork.freedesktop.org/patch/msgid/20190826223317.28509-3-robh@kernel.org --- drivers/gpu/drm/panfrost/panfrost_job.c | 39 +++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c index 05c85f45a0de..18bcc9bac6d2 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.c +++ b/drivers/gpu/drm/panfrost/panfrost_job.c @@ -150,8 +150,10 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js) if (ret < 0) return; - if (WARN_ON(job_read(pfdev, JS_COMMAND_NEXT(js)))) - goto end; + if (WARN_ON(job_read(pfdev, JS_COMMAND_NEXT(js)))) { + pm_runtime_put_sync_autosuspend(pfdev->dev); + return; + } cfg = panfrost_mmu_as_get(pfdev, &job->file_priv->mmu); @@ -187,10 +189,6 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js) job_write(pfdev, JS_COMMAND_NEXT(js), JS_COMMAND_START); spin_unlock_irqrestore(&pfdev->hwaccess_lock, flags); - -end: - pm_runtime_mark_last_busy(pfdev->dev); - pm_runtime_put_autosuspend(pfdev->dev); } static void panfrost_acquire_object_fences(struct drm_gem_object **bos, @@ -369,6 +367,7 @@ static void panfrost_job_timedout(struct drm_sched_job *sched_job) struct panfrost_job *job = to_panfrost_job(sched_job); struct panfrost_device *pfdev = job->pfdev; int js = panfrost_job_get_slot(job); + unsigned long flags; int i; /* @@ -394,6 +393,15 @@ static void panfrost_job_timedout(struct drm_sched_job *sched_job) if (sched_job) drm_sched_increase_karma(sched_job); + spin_lock_irqsave(&pfdev->js->job_lock, flags); + for (i = 0; i < NUM_JOB_SLOTS; i++) { + if (pfdev->jobs[i]) { + pm_runtime_put_noidle(pfdev->dev); + pfdev->jobs[i] = NULL; + } + } + spin_unlock_irqrestore(&pfdev->js->job_lock, flags); + /* panfrost_core_dump(pfdev); */ panfrost_devfreq_record_transition(pfdev, js); @@ -450,12 +458,21 @@ static irqreturn_t panfrost_job_irq_handler(int irq, void *data) } if (status & JOB_INT_MASK_DONE(j)) { - struct panfrost_job *job = pfdev->jobs[j]; + struct panfrost_job *job; + + spin_lock(&pfdev->js->job_lock); + job = pfdev->jobs[j]; + /* Only NULL if job timeout occurred */ + if (job) { + pfdev->jobs[j] = NULL; + + panfrost_mmu_as_put(pfdev, &job->file_priv->mmu); + panfrost_devfreq_record_transition(pfdev, j); - pfdev->jobs[j] = NULL; - panfrost_mmu_as_put(pfdev, &job->file_priv->mmu); - panfrost_devfreq_record_transition(pfdev, j); - dma_fence_signal(job->done_fence); + dma_fence_signal_locked(job->done_fence); + pm_runtime_put_autosuspend(pfdev->dev); + } + spin_unlock(&pfdev->js->job_lock); } status &= ~mask; -- cgit From e316f08f1abf5f1116118e25dce7bc3e9ab03246 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2019 17:33:12 -0500 Subject: drm/panfrost: Remove unnecessary mmu->lock mutex There's no need to serialize io-pgtable calls and the as_lock is sufficient to serialize flush operations, so we can remove the per page table lock. Fixes: 7282f7645d06 ("drm/panfrost: Implement per FD address spaces") Suggested-by: Robin Murphy Cc: Tomeu Vizoso Cc: David Airlie Cc: Daniel Vetter Signed-off-by: Rob Herring Acked-by: Alyssa Rosenzweig Reviewed-by: Robin Murphy Reviewed-by: Steven Price Link: https://patchwork.freedesktop.org/patch/msgid/20190826223317.28509-4-robh@kernel.org --- drivers/gpu/drm/panfrost/panfrost_device.h | 1 - drivers/gpu/drm/panfrost/panfrost_mmu.c | 9 --------- 2 files changed, 10 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h index f503c566e99f..b7fa08ed3a23 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.h +++ b/drivers/gpu/drm/panfrost/panfrost_device.h @@ -104,7 +104,6 @@ struct panfrost_device { struct panfrost_mmu { struct io_pgtable_cfg pgtbl_cfg; struct io_pgtable_ops *pgtbl_ops; - struct mutex lock; int as; atomic_t as_count; struct list_head list; diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index 842bdd7cf6be..3a8bcfa7e7b6 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -228,8 +228,6 @@ static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu, struct io_pgtable_ops *ops = mmu->pgtbl_ops; u64 start_iova = iova; - mutex_lock(&mmu->lock); - for_each_sg(sgt->sgl, sgl, sgt->nents, count) { unsigned long paddr = sg_dma_address(sgl); size_t len = sg_dma_len(sgl); @@ -249,8 +247,6 @@ static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu, mmu_hw_do_operation(pfdev, mmu, start_iova, iova - start_iova, AS_COMMAND_FLUSH_PT); - mutex_unlock(&mmu->lock); - return 0; } @@ -304,8 +300,6 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo) if (ret < 0) return; - mutex_lock(&bo->mmu->lock); - while (unmapped_len < len) { size_t unmapped_page; size_t pgsize = get_pgsize(iova, len - unmapped_len); @@ -321,8 +315,6 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo) mmu_hw_do_operation(pfdev, bo->mmu, bo->node.start << PAGE_SHIFT, bo->node.size << PAGE_SHIFT, AS_COMMAND_FLUSH_PT); - mutex_unlock(&bo->mmu->lock); - pm_runtime_mark_last_busy(pfdev->dev); pm_runtime_put_autosuspend(pfdev->dev); bo->is_mapped = false; @@ -356,7 +348,6 @@ int panfrost_mmu_pgtable_alloc(struct panfrost_file_priv *priv) struct panfrost_mmu *mmu = &priv->mmu; struct panfrost_device *pfdev = priv->pfdev; - mutex_init(&mmu->lock); INIT_LIST_HEAD(&mmu->list); mmu->as = -1; -- cgit From ec7eba47da867e1dd0ff7d442a8717d70c064658 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2019 17:33:13 -0500 Subject: drm/panfrost: Rework page table flushing and runtime PM interaction There is no point in resuming the h/w just to do flush operations and doing so takes several locks which cause lockdep issues with the shrinker. Rework the flush operations to only happen when the h/w is already awake. This avoids taking any locks associated with resuming which trigger lockdep warnings. Fixes: 013b65101315 ("drm/panfrost: Add madvise and shrinker support") Cc: Tomeu Vizoso Cc: David Airlie Cc: Daniel Vetter Signed-off-by: Rob Herring Acked-by: Alyssa Rosenzweig Reviewed-by: Robin Murphy Reviewed-by: Steven Price Link: https://patchwork.freedesktop.org/patch/msgid/20190826223317.28509-5-robh@kernel.org --- drivers/gpu/drm/panfrost/panfrost_mmu.c | 38 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 20 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index 3a8bcfa7e7b6..2204e60f7808 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -220,6 +220,22 @@ static size_t get_pgsize(u64 addr, size_t size) return SZ_2M; } +void panfrost_mmu_flush_range(struct panfrost_device *pfdev, + struct panfrost_mmu *mmu, + u64 iova, size_t size) +{ + if (mmu->as < 0) + return; + + pm_runtime_get_noresume(pfdev->dev); + + /* Flush the PTs only if we're already awake */ + if (pm_runtime_active(pfdev->dev)) + mmu_hw_do_operation(pfdev, mmu, iova, size, AS_COMMAND_FLUSH_PT); + + pm_runtime_put_sync_autosuspend(pfdev->dev); +} + static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu, u64 iova, int prot, struct sg_table *sgt) { @@ -244,8 +260,7 @@ static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu, } } - mmu_hw_do_operation(pfdev, mmu, start_iova, iova - start_iova, - AS_COMMAND_FLUSH_PT); + panfrost_mmu_flush_range(pfdev, mmu, start_iova, iova - start_iova); return 0; } @@ -255,7 +270,6 @@ int panfrost_mmu_map(struct panfrost_gem_object *bo) struct drm_gem_object *obj = &bo->base.base; struct panfrost_device *pfdev = to_panfrost_device(obj->dev); struct sg_table *sgt; - int ret; int prot = IOMMU_READ | IOMMU_WRITE; if (WARN_ON(bo->is_mapped)) @@ -268,14 +282,7 @@ int panfrost_mmu_map(struct panfrost_gem_object *bo) if (WARN_ON(IS_ERR(sgt))) return PTR_ERR(sgt); - ret = pm_runtime_get_sync(pfdev->dev); - if (ret < 0) - return ret; - mmu_map_sg(pfdev, bo->mmu, bo->node.start << PAGE_SHIFT, prot, sgt); - - pm_runtime_mark_last_busy(pfdev->dev); - pm_runtime_put_autosuspend(pfdev->dev); bo->is_mapped = true; return 0; @@ -289,17 +296,12 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo) u64 iova = bo->node.start << PAGE_SHIFT; size_t len = bo->node.size << PAGE_SHIFT; size_t unmapped_len = 0; - int ret; if (WARN_ON(!bo->is_mapped)) return; dev_dbg(pfdev->dev, "unmap: as=%d, iova=%llx, len=%zx", bo->mmu->as, iova, len); - ret = pm_runtime_get_sync(pfdev->dev); - if (ret < 0) - return; - while (unmapped_len < len) { size_t unmapped_page; size_t pgsize = get_pgsize(iova, len - unmapped_len); @@ -312,11 +314,7 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo) unmapped_len += pgsize; } - mmu_hw_do_operation(pfdev, bo->mmu, bo->node.start << PAGE_SHIFT, - bo->node.size << PAGE_SHIFT, AS_COMMAND_FLUSH_PT); - - pm_runtime_mark_last_busy(pfdev->dev); - pm_runtime_put_autosuspend(pfdev->dev); + panfrost_mmu_flush_range(pfdev, bo->mmu, bo->node.start << PAGE_SHIFT, len); bo->is_mapped = false; } -- cgit From 86df65f39b009e00cf2826eac72c5a969b878065 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2019 17:33:14 -0500 Subject: drm/panfrost: Split mmu_hw_do_operation into locked and unlocked version In preparation to call mmu_hw_do_operation with the as_lock already held, Add a mmu_hw_do_operation_locked function. Fixes: 7282f7645d06 ("drm/panfrost: Implement per FD address spaces") Cc: Tomeu Vizoso Cc: David Airlie Cc: Daniel Vetter Signed-off-by: Rob Herring Acked-by: Alyssa Rosenzweig Reviewed-by: Steven Price Link: https://patchwork.freedesktop.org/patch/msgid/20190826223317.28509-6-robh@kernel.org --- drivers/gpu/drm/panfrost/panfrost_mmu.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index 2204e60f7808..3407b00d0a3a 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -80,19 +80,11 @@ static void lock_region(struct panfrost_device *pfdev, u32 as_nr, } -static int mmu_hw_do_operation(struct panfrost_device *pfdev, - struct panfrost_mmu *mmu, - u64 iova, size_t size, u32 op) +static int mmu_hw_do_operation_locked(struct panfrost_device *pfdev, int as_nr, + u64 iova, size_t size, u32 op) { - int ret, as_nr; - - spin_lock(&pfdev->as_lock); - as_nr = mmu->as; - - if (as_nr < 0) { - spin_unlock(&pfdev->as_lock); + if (as_nr < 0) return 0; - } if (op != AS_COMMAND_UNLOCK) lock_region(pfdev, as_nr, iova, size); @@ -101,10 +93,18 @@ static int mmu_hw_do_operation(struct panfrost_device *pfdev, write_cmd(pfdev, as_nr, op); /* Wait for the flush to complete */ - ret = wait_ready(pfdev, as_nr); + return wait_ready(pfdev, as_nr); +} - spin_unlock(&pfdev->as_lock); +static int mmu_hw_do_operation(struct panfrost_device *pfdev, + struct panfrost_mmu *mmu, + u64 iova, size_t size, u32 op) +{ + int ret; + spin_lock(&pfdev->as_lock); + ret = mmu_hw_do_operation_locked(pfdev, mmu->as, iova, size, op); + spin_unlock(&pfdev->as_lock); return ret; } -- cgit From 5924d40958dfc2b8996fbf788a9d58e411a6db71 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2019 17:33:15 -0500 Subject: drm/panfrost: Add cache/TLB flush before switching address space It's not entirely clear if this is required, but add a flush of GPU caches and TLBs before we change an address space to new page tables. Fixes: 7282f7645d06 ("drm/panfrost: Implement per FD address spaces") Cc: Tomeu Vizoso Cc: David Airlie Cc: Daniel Vetter Signed-off-by: Rob Herring Acked-by: Alyssa Rosenzweig Reviewed-by: Robin Murphy Reviewed-by: Steven Price Link: https://patchwork.freedesktop.org/patch/msgid/20190826223317.28509-7-robh@kernel.org --- drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index 3407b00d0a3a..d1ebde3327fe 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -115,6 +115,8 @@ static void panfrost_mmu_enable(struct panfrost_device *pfdev, struct panfrost_m u64 transtab = cfg->arm_mali_lpae_cfg.transtab; u64 memattr = cfg->arm_mali_lpae_cfg.memattr; + mmu_hw_do_operation_locked(pfdev, as_nr, 0, ~0UL, AS_COMMAND_FLUSH_MEM); + mmu_write(pfdev, AS_TRANSTAB_LO(as_nr), transtab & 0xffffffffUL); mmu_write(pfdev, AS_TRANSTAB_HI(as_nr), transtab >> 32); -- cgit From 62f1089f3cbe7d99ced92bf96a8158813b75e5e8 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2019 17:33:16 -0500 Subject: drm/panfrost: Flush and disable address space when freeing page tables Currently, page tables are freed without disabling the address space first. This probably is fine as we'll switch to new page tables when the address space is allocated again and runtime PM suspend will reset the GPU clearing the registers. However, it's better to clean up after ourselves. There is also a problem that we could be accessing the h/w in tlb_inv_context() when suspended. Rework the disable code to make sure we flush caches/TLBs and disable the address space before freeing the page tables if we are not suspended. As the tlb_inv_context() hook is only called when freeing the page tables and we do a flush before disabling the AS, lets remove the flush from tlb_inv_context and avoid any runtime PM issues. Fixes: 7282f7645d06 ("drm/panfrost: Implement per FD address spaces") Cc: Tomeu Vizoso Cc: David Airlie Cc: Daniel Vetter Signed-off-by: Rob Herring Acked-by: Alyssa Rosenzweig Reviewed-by: Robin Murphy Reviewed-by: Steven Price Link: https://patchwork.freedesktop.org/patch/msgid/20190826223317.28509-8-robh@kernel.org --- drivers/gpu/drm/panfrost/panfrost_mmu.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index d1ebde3327fe..387d830cb7cf 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -129,8 +129,10 @@ static void panfrost_mmu_enable(struct panfrost_device *pfdev, struct panfrost_m write_cmd(pfdev, as_nr, AS_COMMAND_UPDATE); } -static void mmu_disable(struct panfrost_device *pfdev, u32 as_nr) +static void panfrost_mmu_disable(struct panfrost_device *pfdev, u32 as_nr) { + mmu_hw_do_operation_locked(pfdev, as_nr, 0, ~0UL, AS_COMMAND_FLUSH_MEM); + mmu_write(pfdev, AS_TRANSTAB_LO(as_nr), 0); mmu_write(pfdev, AS_TRANSTAB_HI(as_nr), 0); @@ -321,11 +323,7 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo) } static void mmu_tlb_inv_context_s1(void *cookie) -{ - struct panfrost_file_priv *priv = cookie; - - mmu_hw_do_operation(priv->pfdev, &priv->mmu, 0, ~0UL, AS_COMMAND_FLUSH_MEM); -} +{} static void mmu_tlb_inv_range_nosync(unsigned long iova, size_t size, size_t granule, bool leaf, void *cookie) @@ -374,6 +372,11 @@ void panfrost_mmu_pgtable_free(struct panfrost_file_priv *priv) spin_lock(&pfdev->as_lock); if (mmu->as >= 0) { + pm_runtime_get_noresume(pfdev->dev); + if (pm_runtime_active(pfdev->dev)) + panfrost_mmu_disable(pfdev, mmu->as); + pm_runtime_put_autosuspend(pfdev->dev); + clear_bit(mmu->as, &pfdev->as_alloc_mask); clear_bit(mmu->as, &pfdev->as_in_use_mask); list_del(&mmu->list); @@ -618,5 +621,4 @@ int panfrost_mmu_init(struct panfrost_device *pfdev) void panfrost_mmu_fini(struct panfrost_device *pfdev) { mmu_write(pfdev, MMU_INT_MASK, 0); - mmu_disable(pfdev, 0); } -- cgit From 45d0dbd15a3b89ced53e058bc71b751e6473b664 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Aug 2019 17:33:17 -0500 Subject: drm/panfrost: Remove unnecessary hwaccess_lock spin_lock With the introduction of the as_lock to serialize address space registers, the hwaccess_lock is only used within the job code and is not protecting anything. panfrost_job_hw_submit() only accesses registers for 1 job slot and it's already serialized by drm_sched. Fixes: 7282f7645d06 ("drm/panfrost: Implement per FD address spaces") Cc: Tomeu Vizoso Cc: David Airlie Cc: Daniel Vetter Signed-off-by: Rob Herring Acked-by: Alyssa Rosenzweig Reviewed-by: Steven Price Link: https://patchwork.freedesktop.org/patch/msgid/20190826223317.28509-9-robh@kernel.org --- drivers/gpu/drm/panfrost/panfrost_device.c | 1 - drivers/gpu/drm/panfrost/panfrost_device.h | 2 -- drivers/gpu/drm/panfrost/panfrost_job.c | 4 ---- 3 files changed, 7 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index 73805210834e..46b0b02e4289 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -124,7 +124,6 @@ int panfrost_device_init(struct panfrost_device *pfdev) INIT_LIST_HEAD(&pfdev->scheduled_jobs); INIT_LIST_HEAD(&pfdev->as_lru_list); - spin_lock_init(&pfdev->hwaccess_lock); spin_lock_init(&pfdev->as_lock); err = panfrost_clk_init(pfdev); diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h index b7fa08ed3a23..9c39b9794811 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.h +++ b/drivers/gpu/drm/panfrost/panfrost_device.h @@ -63,8 +63,6 @@ struct panfrost_device { struct drm_device *ddev; struct platform_device *pdev; - spinlock_t hwaccess_lock; - void __iomem *iomem; struct clk *clock; struct clk *bus_clock; diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c index 18bcc9bac6d2..a58551668d9a 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.c +++ b/drivers/gpu/drm/panfrost/panfrost_job.c @@ -141,7 +141,6 @@ static void panfrost_job_write_affinity(struct panfrost_device *pfdev, static void panfrost_job_hw_submit(struct panfrost_job *job, int js) { struct panfrost_device *pfdev = job->pfdev; - unsigned long flags; u32 cfg; u64 jc_head = job->jc; int ret; @@ -158,7 +157,6 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js) cfg = panfrost_mmu_as_get(pfdev, &job->file_priv->mmu); panfrost_devfreq_record_transition(pfdev, js); - spin_lock_irqsave(&pfdev->hwaccess_lock, flags); job_write(pfdev, JS_HEAD_NEXT_LO(js), jc_head & 0xFFFFFFFF); job_write(pfdev, JS_HEAD_NEXT_HI(js), jc_head >> 32); @@ -187,8 +185,6 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js) job, js, jc_head); job_write(pfdev, JS_COMMAND_NEXT(js), JS_COMMAND_START); - - spin_unlock_irqrestore(&pfdev->hwaccess_lock, flags); } static void panfrost_acquire_object_fences(struct drm_gem_object **bos, -- cgit From edfa07504c5bb188b262fb2f4fc487de966f89a2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 4 Sep 2019 13:30:32 +0100 Subject: drm/panfrost: Fix regulator_get_optional() misuse The panfrost driver requests a supply using regulator_get_optional() but both the name of the supply and the usage pattern suggest that it is being used for the main power for the device and is not at all optional for the device for function, there is no meaningful handling for absent supplies. Such regulators should use the vanilla regulator_get() interface, it will ensure that even if a supply is not described in the system integration one will be provided in software. Signed-off-by: Mark Brown Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20190904123032.23263-1-broonie@kernel.org --- drivers/gpu/drm/panfrost/panfrost_device.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index 46b0b02e4289..238fb6d54df4 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -89,12 +89,9 @@ static int panfrost_regulator_init(struct panfrost_device *pfdev) { int ret; - pfdev->regulator = devm_regulator_get_optional(pfdev->dev, "mali"); + pfdev->regulator = devm_regulator_get(pfdev->dev, "mali"); if (IS_ERR(pfdev->regulator)) { ret = PTR_ERR(pfdev->regulator); - pfdev->regulator = NULL; - if (ret == -ENODEV) - return 0; dev_err(pfdev->dev, "failed to get regulator: %d\n", ret); return ret; } @@ -110,8 +107,7 @@ static int panfrost_regulator_init(struct panfrost_device *pfdev) static void panfrost_regulator_fini(struct panfrost_device *pfdev) { - if (pfdev->regulator) - regulator_disable(pfdev->regulator); + regulator_disable(pfdev->regulator); } int panfrost_device_init(struct panfrost_device *pfdev) -- cgit From d18a96620411298f7925bf9a9e26e9e7add8ea2b Mon Sep 17 00:00:00 2001 From: Steven Price Date: Fri, 6 Sep 2019 15:20:53 +0100 Subject: drm/panfrost: Remove NULL checks for regulator devm_regulator_get() is now used to populate pfdev->regulator which ensures that this cannot be NULL (a dummy regulator will be returned if necessary). So remove the checks in panfrost_devfreq_target(). Signed-off-by: Steven Price Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/3e3a2c8a-b4fc-8af6-39e1-b26160db2c7c@arm.com --- drivers/gpu/drm/panfrost/panfrost_devfreq.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c index a1f5fa6a742a..12ff77dacc95 100644 --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c @@ -39,7 +39,7 @@ static int panfrost_devfreq_target(struct device *dev, unsigned long *freq, * If frequency scaling from low to high, adjust voltage first. * If frequency scaling from high to low, adjust frequency first. */ - if (old_clk_rate < target_rate && pfdev->regulator) { + if (old_clk_rate < target_rate) { err = regulator_set_voltage(pfdev->regulator, target_volt, target_volt); if (err) { @@ -53,14 +53,12 @@ static int panfrost_devfreq_target(struct device *dev, unsigned long *freq, if (err) { dev_err(dev, "Cannot set frequency %lu (%d)\n", target_rate, err); - if (pfdev->regulator) - regulator_set_voltage(pfdev->regulator, - pfdev->devfreq.cur_volt, - pfdev->devfreq.cur_volt); + regulator_set_voltage(pfdev->regulator, pfdev->devfreq.cur_volt, + pfdev->devfreq.cur_volt); return err; } - if (old_clk_rate > target_rate && pfdev->regulator) { + if (old_clk_rate > target_rate) { err = regulator_set_voltage(pfdev->regulator, target_volt, target_volt); if (err) -- cgit From 65e51e30d8625c82ddfe405da46124e9bbffaa71 Mon Sep 17 00:00:00 2001 From: Steven Price Date: Fri, 13 Sep 2019 17:03:10 +0100 Subject: drm/panfrost: Prevent race when handling page fault When handling a GPU page fault addr_to_drm_mm_node() is used to translate the GPU address to a buffer object. However it is possible for the buffer object to be freed after the function has returned resulting in a use-after-free of the BO. Change addr_to_drm_mm_node to return the panfrost_gem_object with an extra reference on it, preventing the BO from being freed until after the page fault has been handled. Signed-off-by: Steven Price Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20190913160310.50444-1-steven.price@arm.com --- drivers/gpu/drm/panfrost/panfrost_mmu.c | 55 +++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 19 deletions(-) (limited to 'drivers/gpu/drm/panfrost') diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index 387d830cb7cf..6e7891ded464 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -386,28 +386,40 @@ void panfrost_mmu_pgtable_free(struct panfrost_file_priv *priv) free_io_pgtable_ops(mmu->pgtbl_ops); } -static struct drm_mm_node *addr_to_drm_mm_node(struct panfrost_device *pfdev, int as, u64 addr) +static struct panfrost_gem_object * +addr_to_drm_mm_node(struct panfrost_device *pfdev, int as, u64 addr) { - struct drm_mm_node *node = NULL; + struct panfrost_gem_object *bo = NULL; + struct panfrost_file_priv *priv; + struct drm_mm_node *node; u64 offset = addr >> PAGE_SHIFT; struct panfrost_mmu *mmu; spin_lock(&pfdev->as_lock); list_for_each_entry(mmu, &pfdev->as_lru_list, list) { - struct panfrost_file_priv *priv; - if (as != mmu->as) - continue; + if (as == mmu->as) + break; + } + if (as != mmu->as) + goto out; + + priv = container_of(mmu, struct panfrost_file_priv, mmu); - priv = container_of(mmu, struct panfrost_file_priv, mmu); - drm_mm_for_each_node(node, &priv->mm) { - if (offset >= node->start && offset < (node->start + node->size)) - goto out; + spin_lock(&priv->mm_lock); + + drm_mm_for_each_node(node, &priv->mm) { + if (offset >= node->start && + offset < (node->start + node->size)) { + bo = drm_mm_node_to_panfrost_bo(node); + drm_gem_object_get(&bo->base.base); + break; } } + spin_unlock(&priv->mm_lock); out: spin_unlock(&pfdev->as_lock); - return node; + return bo; } #define NUM_FAULT_PAGES (SZ_2M / PAGE_SIZE) @@ -415,29 +427,28 @@ out: int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, u64 addr) { int ret, i; - struct drm_mm_node *node; struct panfrost_gem_object *bo; struct address_space *mapping; pgoff_t page_offset; struct sg_table *sgt; struct page **pages; - node = addr_to_drm_mm_node(pfdev, as, addr); - if (!node) + bo = addr_to_drm_mm_node(pfdev, as, addr); + if (!bo) return -ENOENT; - bo = drm_mm_node_to_panfrost_bo(node); if (!bo->is_heap) { dev_WARN(pfdev->dev, "matching BO is not heap type (GPU VA = %llx)", - node->start << PAGE_SHIFT); - return -EINVAL; + bo->node.start << PAGE_SHIFT); + ret = -EINVAL; + goto err_bo; } WARN_ON(bo->mmu->as != as); /* Assume 2MB alignment and size multiple */ addr &= ~((u64)SZ_2M - 1); page_offset = addr >> PAGE_SHIFT; - page_offset -= node->start; + page_offset -= bo->node.start; mutex_lock(&bo->base.pages_lock); @@ -446,7 +457,8 @@ int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, u64 addr) sizeof(struct sg_table), GFP_KERNEL | __GFP_ZERO); if (!bo->sgts) { mutex_unlock(&bo->base.pages_lock); - return -ENOMEM; + ret = -ENOMEM; + goto err_bo; } pages = kvmalloc_array(bo->base.base.size >> PAGE_SHIFT, @@ -455,7 +467,8 @@ int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, u64 addr) kfree(bo->sgts); bo->sgts = NULL; mutex_unlock(&bo->base.pages_lock); - return -ENOMEM; + ret = -ENOMEM; + goto err_bo; } bo->base.pages = pages; bo->base.pages_use_count = 1; @@ -493,12 +506,16 @@ int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as, u64 addr) dev_dbg(pfdev->dev, "mapped page fault @ AS%d %llx", as, addr); + drm_gem_object_put_unlocked(&bo->base.base); + return 0; err_map: sg_free_table(sgt); err_pages: drm_gem_shmem_put_pages(&bo->base); +err_bo: + drm_gem_object_put_unlocked(&bo->base.base); return ret; } -- cgit