drm-misc-fixes for v7.2-rc2:

- Fix potential null pointer dereference in dma-buf.
 - Handle 0 in dma_fence_dedup_array.
 - Use the correct callback in dma_fence_timeline_name.
 - Fix device removal handling in amdxdna.
 - kernel-doc fixes.
 - Include header fix for drm_ras.h
 - Handle edids better in virtio.
 - Use the clk_bulk api for error handling in malidp.
 - More clk handling fixes for komeda.
 - panthor scheduler block fallout fixes.
 - panthor unplug fixes.
 - other panthor fixes.
 - Fix unnecessary WARN_ON in topology probe after teardown.
 - Add refcount to amdxdna job to fix use-after free.
 - Fix increasing args->size in ioctl's of drm/imagination.
 - Handle stride correctly in pvr_set_uobj_array.
 - Only call imagination's drm_sched_entity_fini once.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEuXvWqAysSYEJGuVH/lWMcqZwE8MFAmpGi4sACgkQ/lWMcqZw
 E8NhNA/+JHDKw3iO6f2+/2KWdgJseXXs2G+Y4Bm0lWgpl2eP1Sj6adwMStGvBj9o
 mVoO0CHCAxiIIj1UAY+pZ92aAaPpfH10Ka4gHT0mWzpLoH2Gzgt97Qb8jmVSF8sD
 yWwujaM4Z1hhlq6F3492WBqf13RmMMUUnqTBMr5LdnxKA9tFFXFVN2snO2JRUMWI
 8sfXIB83BYuXuJ3aJH0ToSctV2k5Ji6inbTODL5rAmhYxiyGiIV1NV72FeiiL7pC
 l+aDE14+FB1OZ/iw1fxFFmm1ox507IGnopv0coExEg1hK7HlaQgPJooS/CkjK2JM
 tpzQHaVhX3V3PABQD1HnWaCUBDdEqRCr1DNHNuXbM1vPCGlhvD/lpiN4i2UCEUyx
 g64GfIm7JA7AowaSV57651B1vZwQmVkg8y9PWYNn7avD4n5+ZwHkEyFO4wpRwwdZ
 tVHqVBgMJvCQolA269XNPtLwoIebdQuRZUKaxX2IjrEJJk23S1VDPqJY2cTvoi6L
 u1XUWM7iz3XjCTDU0uN7VZ0vCft0g0Ha1mY9GU2Gn+AMpxgi8qzMBSLDduS6K27v
 tzxQsduZ5bvUpclrX7jshJ0F3vQJnCnyQ5qVdF1I/J4/O/umcyuI+ZhKZPiFyhQe
 PW8SbHqJGXtMRIPR95sMoTkMZKes4QTfSF1qbU2NhM6w6MB6bpY=
 =heUt
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-fixes-2026-07-02' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-fixes

drm-misc-fixes for v7.2-rc2:
- Fix potential null pointer dereference in dma-buf.
- Handle 0 in dma_fence_dedup_array.
- Use the correct callback in dma_fence_timeline_name.
- Fix device removal handling in amdxdna.
- kernel-doc fixes.
- Include header fix for drm_ras.h
- Handle edids better in virtio.
- Use the clk_bulk api for error handling in malidp.
- More clk handling fixes for komeda.
- panthor scheduler block fallout fixes.
- panthor unplug fixes.
- other panthor fixes.
- Fix unnecessary WARN_ON in topology probe after teardown.
- Add refcount to amdxdna job to fix use-after free.
- Fix increasing args->size in ioctl's of drm/imagination.
- Handle stride correctly in pvr_set_uobj_array.
- Only call imagination's drm_sched_entity_fini once.

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patch.msgid.link/786bdc92-0ce3-4c0f-9668-b0fa8a0047ea@linux.intel.com
This commit is contained in:
Dave Airlie 2026-07-03 08:53:09 +10:00
commit acd7c71ba8
27 changed files with 265 additions and 158 deletions

View File

@ -59,6 +59,18 @@ static bool aie2_tdr_detect(struct amdxdna_dev *xdna)
return false;
}
static void aie2_cmd_release(struct kref *ref)
{
struct amdxdna_drv_cmd *drv_cmd = container_of(ref, struct amdxdna_drv_cmd, refcnt);
kfree(drv_cmd);
}
static void aie2_cmd_put(struct amdxdna_drv_cmd *drv_cmd)
{
kref_put(&drv_cmd->refcnt, aie2_cmd_release);
}
static void aie2_job_release(struct kref *ref)
{
struct amdxdna_sched_job *job;
@ -70,6 +82,8 @@ static void aie2_job_release(struct kref *ref)
wake_up(&job->hwctx->priv->job_free_wq);
if (job->out_fence)
dma_fence_put(job->out_fence);
if (job->drv_cmd)
aie2_cmd_put(job->drv_cmd);
kfree(job->aie2_job_health);
kfree(job);
}
@ -901,7 +915,7 @@ static int aie2_hwctx_cfg_debug_bo(struct amdxdna_hwctx *hwctx, u32 bo_hdl,
{
struct amdxdna_client *client = hwctx->client;
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_drv_cmd cmd = { 0 };
struct amdxdna_drv_cmd *cmd;
struct amdxdna_gem_obj *abo;
u64 seq;
int ret;
@ -912,32 +926,39 @@ static int aie2_hwctx_cfg_debug_bo(struct amdxdna_hwctx *hwctx, u32 bo_hdl,
return -EINVAL;
}
cmd = kzalloc_obj(*cmd);
if (!cmd) {
ret = -ENOMEM;
goto put_obj;
}
kref_init(&cmd->refcnt);
if (attach) {
if (abo->assigned_hwctx != AMDXDNA_INVALID_CTX_HANDLE) {
ret = -EBUSY;
goto put_obj;
goto put_cmd;
}
cmd.opcode = ATTACH_DEBUG_BO;
cmd->opcode = ATTACH_DEBUG_BO;
} else {
if (abo->assigned_hwctx != hwctx->id) {
ret = -EINVAL;
goto put_obj;
goto put_cmd;
}
cmd.opcode = DETACH_DEBUG_BO;
cmd->opcode = DETACH_DEBUG_BO;
}
ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE,
ret = amdxdna_cmd_submit(client, cmd, AMDXDNA_INVALID_BO_HANDLE,
&bo_hdl, 1, hwctx->id, &seq);
if (ret) {
XDNA_ERR(xdna, "Submit command failed");
goto put_obj;
goto put_cmd;
}
aie2_cmd_wait(hwctx, seq);
if (cmd.result) {
XDNA_ERR(xdna, "Response failure 0x%x", cmd.result);
if (cmd->result) {
XDNA_ERR(xdna, "Response failure 0x%x", cmd->result);
ret = -EINVAL;
goto put_obj;
goto put_cmd;
}
if (attach)
@ -947,6 +968,8 @@ static int aie2_hwctx_cfg_debug_bo(struct amdxdna_hwctx *hwctx, u32 bo_hdl,
XDNA_DBG(xdna, "Config debug BO %d to %s", bo_hdl, hwctx->name);
put_cmd:
aie2_cmd_put(cmd);
put_obj:
amdxdna_gem_put_obj(abo);
return ret;
@ -974,25 +997,32 @@ int aie2_hwctx_sync_debug_bo(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl)
{
struct amdxdna_client *client = hwctx->client;
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_drv_cmd cmd = { 0 };
struct amdxdna_drv_cmd *cmd;
u64 seq;
int ret;
cmd.opcode = SYNC_DEBUG_BO;
ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE,
cmd = kzalloc_obj(*cmd);
if (!cmd)
return -ENOMEM;
kref_init(&cmd->refcnt);
cmd->opcode = SYNC_DEBUG_BO;
ret = amdxdna_cmd_submit(client, cmd, AMDXDNA_INVALID_BO_HANDLE,
&debug_bo_hdl, 1, hwctx->id, &seq);
if (ret) {
XDNA_ERR(xdna, "Submit command failed");
return ret;
goto put_cmd;
}
aie2_cmd_wait(hwctx, seq);
if (cmd.result) {
XDNA_ERR(xdna, "Response failure 0x%x", cmd.result);
return -EINVAL;
if (cmd->result) {
XDNA_ERR(xdna, "Response failure 0x%x", cmd->result);
ret = -EINVAL;
}
return 0;
put_cmd:
aie2_cmd_put(cmd);
return ret;
}
static int aie2_populate_range(struct amdxdna_gem_obj *abo)
@ -1142,6 +1172,8 @@ int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
dma_resv_add_fence(job->bos[i]->resv, job->out_fence, DMA_RESV_USAGE_WRITE);
job->seq = hwctx->priv->seq++;
kref_get(&job->refcnt);
if (job->drv_cmd)
kref_get(&job->drv_cmd->refcnt);
drm_sched_entity_push_job(&job->base);
*seq = job->seq;

View File

@ -132,6 +132,7 @@ enum amdxdna_job_opcode {
struct amdxdna_drv_cmd {
enum amdxdna_job_opcode opcode;
u32 result;
struct kref refcnt;
};
struct app_health_report;

View File

@ -4,6 +4,7 @@
*/
#include <drm/amdxdna_accel.h>
#include <drm/drm_managed.h>
#include <linux/iommu.h>
#include <linux/iova.h>
@ -153,10 +154,30 @@ void amdxdna_iommu_free(struct amdxdna_dev *xdna, size_t size,
free_pages((unsigned long)cpu_addr, get_order(size));
}
static void amdxdna_cleanup_force_iova(struct drm_device *dev, void *res)
{
struct amdxdna_dev *xdna = to_xdna_dev(dev);
if (xdna->domain) {
iommu_detach_group(xdna->domain, xdna->group);
put_iova_domain(&xdna->iovad);
iova_cache_put();
iommu_domain_free(xdna->domain);
}
iommu_group_put(xdna->group);
}
void amdxdna_iommu_fini(struct amdxdna_dev *xdna)
{
if (xdna->group && !xdna->domain)
iommu_group_put(xdna->group);
}
int amdxdna_iommu_init(struct amdxdna_dev *xdna)
{
unsigned long order;
int ret;
int ret = 0;
xdna->group = iommu_group_get(xdna->ddev.dev);
if (!xdna->group || !force_iova)
@ -182,8 +203,14 @@ int amdxdna_iommu_init(struct amdxdna_dev *xdna)
if (ret)
goto put_iova;
ret = drmm_add_action(&xdna->ddev, amdxdna_cleanup_force_iova, NULL);
if (ret)
goto detach_group;
return 0;
detach_group:
iommu_detach_group(xdna->domain, xdna->group);
put_iova:
put_iova_domain(&xdna->iovad);
iova_cache_put();
@ -191,20 +218,8 @@ int amdxdna_iommu_init(struct amdxdna_dev *xdna)
iommu_domain_free(xdna->domain);
put_group:
iommu_group_put(xdna->group);
xdna->group = NULL;
xdna->domain = NULL;
return ret;
}
void amdxdna_iommu_fini(struct amdxdna_dev *xdna)
{
if (xdna->domain) {
iommu_detach_group(xdna->domain, xdna->group);
put_iova_domain(&xdna->iovad);
iova_cache_put();
iommu_domain_free(xdna->domain);
}
if (xdna->group)
iommu_group_put(xdna->group);
}

View File

@ -138,9 +138,11 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
xdna->dev_info->dev_heap_max_size);
mutex_init(&client->mm_lock);
mutex_lock(&xdna->client_lock);
mutex_lock(&xdna->dev_lock);
list_add_tail(&client->node, &xdna->client_list);
mutex_unlock(&xdna->dev_lock);
mutex_unlock(&xdna->client_lock);
filp->driver_priv = client;
client->filp = filp;
@ -174,18 +176,14 @@ static void amdxdna_drm_close(struct drm_device *ddev, struct drm_file *filp)
{
struct amdxdna_client *client = filp->driver_priv;
struct amdxdna_dev *xdna = to_xdna_dev(ddev);
int idx;
XDNA_DBG(xdna, "closing pid %d", client->pid);
if (!drm_dev_enter(&xdna->ddev, &idx))
return;
mutex_lock(&xdna->client_lock);
mutex_lock(&xdna->dev_lock);
amdxdna_client_cleanup(client);
mutex_unlock(&xdna->dev_lock);
drm_dev_exit(idx);
mutex_unlock(&xdna->client_lock);
}
static int amdxdna_drm_get_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
@ -371,6 +369,10 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (!xdna->dev_info)
return -ENODEV;
ret = drmm_mutex_init(ddev, &xdna->client_lock);
if (ret)
return ret;
drmm_mutex_init(ddev, &xdna->dev_lock);
init_rwsem(&xdna->notifier_lock);
INIT_LIST_HEAD(&xdna->client_list);
@ -390,9 +392,9 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (ret)
return ret;
xdna->notifier_wq = alloc_ordered_workqueue("notifier_wq", WQ_MEM_RECLAIM);
if (!xdna->notifier_wq) {
ret = -ENOMEM;
xdna->notifier_wq = drmm_alloc_ordered_workqueue(ddev, "notifier_wq", WQ_MEM_RECLAIM);
if (IS_ERR(xdna->notifier_wq)) {
ret = PTR_ERR(xdna->notifier_wq);
goto iommu_fini;
}
@ -401,7 +403,7 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mutex_unlock(&xdna->dev_lock);
if (ret) {
XDNA_ERR(xdna, "Hardware init failed, ret %d", ret);
goto destroy_notifier_wq;
goto iommu_fini;
}
ret = amdxdna_sysfs_init(xdna);
@ -425,8 +427,6 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mutex_lock(&xdna->dev_lock);
xdna->dev_info->ops->fini(xdna);
mutex_unlock(&xdna->dev_lock);
destroy_notifier_wq:
destroy_workqueue(xdna->notifier_wq);
iommu_fini:
amdxdna_iommu_fini(xdna);
return ret;
@ -437,23 +437,19 @@ static void amdxdna_remove(struct pci_dev *pdev)
struct amdxdna_dev *xdna = pci_get_drvdata(pdev);
struct amdxdna_client *client;
destroy_workqueue(xdna->notifier_wq);
drm_dev_unplug(&xdna->ddev);
amdxdna_sysfs_fini(xdna);
mutex_lock(&xdna->client_lock);
mutex_lock(&xdna->dev_lock);
client = list_first_entry_or_null(&xdna->client_list,
struct amdxdna_client, node);
while (client) {
amdxdna_client_cleanup(client);
client = list_first_entry_or_null(&xdna->client_list,
struct amdxdna_client, node);
list_for_each_entry(client, &xdna->client_list, node) {
amdxdna_hwctx_remove_all(client);
amdxdna_sva_fini(client);
}
xdna->dev_info->ops->fini(xdna);
mutex_unlock(&xdna->dev_lock);
mutex_unlock(&xdna->client_lock);
amdxdna_iommu_fini(xdna);
}

View File

@ -120,6 +120,7 @@ struct amdxdna_dev {
struct mutex dev_lock; /* per device lock */
struct list_head client_list;
struct mutex client_lock; /* client_list */
struct amdxdna_fw_ver fw_ver;
struct rw_semaphore notifier_lock; /* for mmu notifier*/
struct workqueue_struct *notifier_wq;

View File

@ -97,6 +97,9 @@ int dma_fence_dedup_array(struct dma_fence **fences, int num_fences)
{
int i, j;
if (!num_fences)
return 0;
sort(fences, num_fences, sizeof(*fences), fence_cmp, NULL);
/*

View File

@ -1168,7 +1168,7 @@ const char __rcu *dma_fence_driver_name(struct dma_fence *fence)
/* RCU protection is required for safe access to returned string */
ops = rcu_dereference(fence->ops);
if (!dma_fence_test_signaled_flag(fence))
if (ops)
return (const char __rcu *)ops->get_driver_name(fence);
else
return (const char __rcu *)"detached-driver";
@ -1201,8 +1201,8 @@ const char __rcu *dma_fence_timeline_name(struct dma_fence *fence)
/* RCU protection is required for safe access to returned string */
ops = rcu_dereference(fence->ops);
if (!dma_fence_test_signaled_flag(fence))
return (const char __rcu *)ops->get_driver_name(fence);
if (ops)
return (const char __rcu *)ops->get_timeline_name(fence);
else
return (const char __rcu *)"signaled-timeline";
}

View File

@ -313,7 +313,11 @@ void komeda_dev_destroy(struct komeda_dev *mdev)
int komeda_dev_resume(struct komeda_dev *mdev)
{
clk_prepare_enable(mdev->aclk);
int err;
err = clk_prepare_enable(mdev->aclk);
if (err)
return err;
mdev->funcs->enable_irq(mdev);

View File

@ -74,8 +74,11 @@ static int komeda_platform_probe(struct platform_device *pdev)
}
pm_runtime_enable(dev);
if (!pm_runtime_enabled(dev))
komeda_dev_resume(mdrv->mdev);
if (!pm_runtime_enabled(dev)) {
err = komeda_dev_resume(mdrv->mdev);
if (err)
goto err_destroy_mdev;
}
mdrv->kms = komeda_kms_attach(mdrv->mdev);
if (IS_ERR(mdrv->kms)) {
@ -93,7 +96,7 @@ static int komeda_platform_probe(struct platform_device *pdev)
pm_runtime_disable(dev);
else
komeda_dev_suspend(mdrv->mdev);
err_destroy_mdev:
komeda_dev_destroy(mdrv->mdev);
free_mdrv:
@ -140,11 +143,12 @@ static int __maybe_unused komeda_pm_suspend(struct device *dev)
static int __maybe_unused komeda_pm_resume(struct device *dev)
{
struct komeda_drv *mdrv = dev_get_drvdata(dev);
int err = 0;
if (!pm_runtime_status_suspended(dev))
komeda_dev_resume(mdrv->mdev);
err = komeda_dev_resume(mdrv->mdev);
return drm_mode_config_helper_resume(&mdrv->kms->base);
return err ? err : drm_mode_config_helper_resume(&mdrv->kms->base);
}
static const struct dev_pm_ops komeda_pm_ops = {

View File

@ -670,6 +670,11 @@ static int malidp_runtime_pm_suspend(struct device *dev)
struct drm_device *drm = dev_get_drvdata(dev);
struct malidp_drm *malidp = drm_to_malidp(drm);
struct malidp_hw_device *hwdev = malidp->dev;
struct clk_bulk_data clks[] = {
{ .clk = hwdev->pclk },
{ .clk = hwdev->aclk },
{ .clk = hwdev->mclk },
};
/* we can only suspend if the hardware is in config mode */
WARN_ON(!hwdev->hw->in_config_mode(hwdev));
@ -677,9 +682,7 @@ static int malidp_runtime_pm_suspend(struct device *dev)
malidp_se_irq_fini(hwdev);
malidp_de_irq_fini(hwdev);
hwdev->pm_suspended = true;
clk_disable_unprepare(hwdev->mclk);
clk_disable_unprepare(hwdev->aclk);
clk_disable_unprepare(hwdev->pclk);
clk_bulk_disable_unprepare(ARRAY_SIZE(clks), clks);
return 0;
}
@ -689,10 +692,17 @@ static int malidp_runtime_pm_resume(struct device *dev)
struct drm_device *drm = dev_get_drvdata(dev);
struct malidp_drm *malidp = drm_to_malidp(drm);
struct malidp_hw_device *hwdev = malidp->dev;
struct clk_bulk_data clks[] = {
{ .clk = hwdev->pclk },
{ .clk = hwdev->aclk },
{ .clk = hwdev->mclk },
};
int err;
err = clk_bulk_prepare_enable(ARRAY_SIZE(clks), clks);
if (err)
return err;
clk_prepare_enable(hwdev->pclk);
clk_prepare_enable(hwdev->aclk);
clk_prepare_enable(hwdev->mclk);
hwdev->pm_suspended = false;
malidp_de_irq_hw_init(hwdev);
malidp_se_irq_hw_init(hwdev);

View File

@ -3740,8 +3740,10 @@ void drm_dp_mst_topology_queue_probe(struct drm_dp_mst_topology_mgr *mgr)
{
mutex_lock(&mgr->lock);
if (drm_WARN_ON(mgr->dev, !mgr->mst_state || !mgr->mst_primary))
if (!mgr->mst_state || !mgr->mst_primary) {
drm_dbg_kms(mgr->dev, "queue_probe skipped: topology torn down\n");
goto out_unlock;
}
drm_dp_mst_topology_mgr_invalidate_mstb(mgr->mst_primary);
drm_dp_mst_queue_probe_work(mgr);

View File

@ -161,22 +161,24 @@ ctx_fw_data_init(void *cpu_ptr, void *priv)
/**
* pvr_context_destroy_queues() - Destroy all queues attached to a context.
* @ctx: Context to destroy queues on.
* @cleanup_queue_entity: Whether to cleanup the queue entity e.g. context
* creation failure path.
*
* Should be called when the last reference to a context object is dropped.
* It releases all resources attached to the queues bound to this context.
*/
static void pvr_context_destroy_queues(struct pvr_context *ctx)
static void pvr_context_destroy_queues(struct pvr_context *ctx, bool cleanup_queue_entity)
{
switch (ctx->type) {
case DRM_PVR_CTX_TYPE_RENDER:
pvr_queue_destroy(ctx->queues.fragment);
pvr_queue_destroy(ctx->queues.geometry);
pvr_queue_destroy(ctx->queues.fragment, cleanup_queue_entity);
pvr_queue_destroy(ctx->queues.geometry, cleanup_queue_entity);
break;
case DRM_PVR_CTX_TYPE_COMPUTE:
pvr_queue_destroy(ctx->queues.compute);
pvr_queue_destroy(ctx->queues.compute, cleanup_queue_entity);
break;
case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
pvr_queue_destroy(ctx->queues.transfer);
pvr_queue_destroy(ctx->queues.transfer, cleanup_queue_entity);
break;
}
}
@ -240,7 +242,7 @@ static int pvr_context_create_queues(struct pvr_context *ctx,
return -EINVAL;
err_destroy_queues:
pvr_context_destroy_queues(ctx);
pvr_context_destroy_queues(ctx, true);
return err;
}
@ -349,7 +351,7 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co
pvr_fw_object_destroy(ctx->fw_obj);
err_destroy_queues:
pvr_context_destroy_queues(ctx);
pvr_context_destroy_queues(ctx, true);
err_free_ctx_id:
/*
@ -384,7 +386,7 @@ pvr_context_release(struct kref *ref_count)
spin_unlock(&pvr_dev->ctx_list_lock);
xa_erase(&pvr_dev->ctx_ids, ctx->ctx_id);
pvr_context_destroy_queues(ctx);
pvr_context_destroy_queues(ctx, false);
pvr_fw_object_destroy(ctx->fw_obj);
kfree(ctx->data);
pvr_vm_context_put(ctx->vm_ctx);

View File

@ -515,7 +515,8 @@ pvr_dev_query_quirks_get(struct pvr_device *pvr_dev,
if (err < 0)
return err;
args->size = sizeof(query);
if (args->size > sizeof(query))
args->size = sizeof(query);
return 0;
}
@ -596,7 +597,8 @@ pvr_dev_query_enhancements_get(struct pvr_device *pvr_dev,
if (err < 0)
return err;
args->size = sizeof(query);
if (args->size > sizeof(query))
args->size = sizeof(query);
return 0;
}
@ -1255,14 +1257,13 @@ pvr_set_uobj_array(const struct drm_pvr_obj_array *out, u32 min_stride, u32 obj_
if (copy_to_user(out_ptr, in_ptr, cpy_elem_size))
return -EFAULT;
out_ptr += obj_size;
in_ptr += out->stride;
}
if (out->stride > obj_size &&
clear_user(out_ptr + cpy_elem_size, out->stride - obj_size)) {
return -EFAULT;
}
if (out->stride > obj_size &&
clear_user(u64_to_user_ptr(out->array + obj_size),
out->stride - obj_size)) {
return -EFAULT;
out_ptr += out->stride;
in_ptr += obj_size;
}
}

View File

@ -1439,11 +1439,12 @@ void pvr_queue_kill(struct pvr_queue *queue)
/**
* pvr_queue_destroy() - Destroy a queue.
* @queue: The queue to destroy.
* @cleanup_queue_entity: Whether to cleanup the queue entity.
*
* Cleanup the queue and free the resources attached to it. Should be
* called from the context release function.
*/
void pvr_queue_destroy(struct pvr_queue *queue)
void pvr_queue_destroy(struct pvr_queue *queue, bool cleanup_queue_entity)
{
if (!queue)
return;
@ -1453,7 +1454,8 @@ void pvr_queue_destroy(struct pvr_queue *queue)
mutex_unlock(&queue->ctx->pvr_dev->queues.lock);
drm_sched_fini(&queue->scheduler);
drm_sched_entity_fini(&queue->entity);
if (cleanup_queue_entity)
drm_sched_entity_fini(&queue->entity);
if (WARN_ON(queue->last_queued_job_scheduled_fence))
dma_fence_put(queue->last_queued_job_scheduled_fence);

View File

@ -158,7 +158,7 @@ struct pvr_queue *pvr_queue_create(struct pvr_context *ctx,
void pvr_queue_kill(struct pvr_queue *queue);
void pvr_queue_destroy(struct pvr_queue *queue);
void pvr_queue_destroy(struct pvr_queue *queue, bool cleanup_queue_entity);
void pvr_queue_process(struct pvr_queue *queue);

View File

@ -1019,7 +1019,8 @@ pvr_static_data_areas_get(const struct pvr_device *pvr_dev,
if (err < 0)
return err;
args->size = sizeof(query);
if (args->size > sizeof(query))
args->size = sizeof(query);
return 0;
}
@ -1069,7 +1070,8 @@ pvr_heap_info_get(const struct pvr_device *pvr_dev,
if (err < 0)
return err;
args->size = sizeof(query);
if (args->size > sizeof(query))
args->size = sizeof(query);
return 0;
}

View File

@ -207,6 +207,7 @@ int panthor_device_init(struct panthor_device *ptdev)
*dummy_page_virt = 1;
INIT_WORK(&ptdev->reset.work, panthor_device_reset_work);
disable_work(&ptdev->reset.work);
ptdev->reset.wq = alloc_ordered_workqueue("panthor-reset-wq", 0);
if (!ptdev->reset.wq)
return -ENOMEM;
@ -285,6 +286,9 @@ int panthor_device_init(struct panthor_device *ptdev)
panthor_gem_init(ptdev);
/* Now that everything is initialized, we can enable the reset work. */
enable_work(&ptdev->reset.work);
/* ~3 frames */
pm_runtime_set_autosuspend_delay(ptdev->base.dev, 50);
pm_runtime_use_autosuspend(ptdev->base.dev);

View File

@ -509,9 +509,6 @@ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data)
struct panthor_irq *pirq = data; \
enum panthor_irq_state old_state; \
\
if (!gpu_read(pirq->iomem, INT_STAT)) \
return IRQ_NONE; \
\
guard(spinlock_irqsave)(&pirq->mask_lock); \
old_state = atomic_cmpxchg(&pirq->state, \
PANTHOR_IRQ_STATE_ACTIVE, \
@ -519,6 +516,13 @@ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data)
if (old_state != PANTHOR_IRQ_STATE_ACTIVE) \
return IRQ_NONE; \
\
if (!gpu_read(pirq->iomem, INT_STAT)) { \
atomic_cmpxchg(&pirq->state, \
PANTHOR_IRQ_STATE_PROCESSING, \
PANTHOR_IRQ_STATE_ACTIVE); \
return IRQ_NONE; \
} \
\
gpu_write(pirq->iomem, INT_MASK, 0); \
return IRQ_WAKE_THREAD; \
} \
@ -581,14 +585,15 @@ static inline void panthor_ ## __name ## _irq_resume(struct panthor_irq *pirq)
\
static int panthor_request_ ## __name ## _irq(struct panthor_device *ptdev, \
struct panthor_irq *pirq, \
int irq, u32 mask, void __iomem *iomem) \
int irq, void __iomem *iomem) \
{ \
pirq->ptdev = ptdev; \
pirq->irq = irq; \
pirq->mask = mask; \
pirq->mask = 0; \
pirq->iomem = iomem; \
spin_lock_init(&pirq->mask_lock); \
panthor_ ## __name ## _irq_resume(pirq); \
atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED); \
gpu_write(pirq->iomem, INT_MASK, 0); \
\
return devm_request_threaded_irq(ptdev->base.dev, irq, \
panthor_ ## __name ## _irq_raw_handler, \

View File

@ -1279,9 +1279,7 @@ void panthor_fw_unplug(struct panthor_device *ptdev)
if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) {
/* Make sure the IRQ handler cannot be called after that point. */
if (ptdev->fw->irq.irq)
panthor_job_irq_suspend(&ptdev->fw->irq);
panthor_job_irq_suspend(&ptdev->fw->irq);
panthor_fw_stop(ptdev);
}
@ -1476,7 +1474,7 @@ int panthor_fw_init(struct panthor_device *ptdev)
if (irq <= 0)
return -ENODEV;
ret = panthor_request_job_irq(ptdev, &fw->irq, irq, 0,
ret = panthor_request_job_irq(ptdev, &fw->irq, irq,
ptdev->iomem + JOB_INT_BASE);
if (ret) {
drm_err(&ptdev->base, "failed to request job irq");

View File

@ -170,11 +170,12 @@ int panthor_gpu_init(struct panthor_device *ptdev)
return irq;
ret = panthor_request_gpu_irq(ptdev, &ptdev->gpu->irq, irq,
GPU_INTERRUPTS_MASK,
ptdev->iomem + GPU_INT_BASE);
if (ret)
return ret;
panthor_gpu_irq_enable_events(&ptdev->gpu->irq, GPU_INTERRUPTS_MASK);
panthor_gpu_irq_resume(&ptdev->gpu->irq);
return 0;
}

View File

@ -3262,7 +3262,6 @@ int panthor_mmu_init(struct panthor_device *ptdev)
return -ENODEV;
ret = panthor_request_mmu_irq(ptdev, &mmu->irq, irq,
panthor_mmu_fault_mask(ptdev, ~0),
ptdev->iomem + MMU_INT_BASE);
if (ret)
return ret;
@ -3280,7 +3279,13 @@ int panthor_mmu_init(struct panthor_device *ptdev)
ptdev->gpu_info.mmu_features |= BITS_PER_LONG;
}
return drmm_add_action_or_reset(&ptdev->base, panthor_mmu_release_wq, mmu->vm.wq);
ret = drmm_add_action_or_reset(&ptdev->base, panthor_mmu_release_wq, mmu->vm.wq);
if (ret)
return ret;
panthor_mmu_irq_enable_events(&mmu->irq, panthor_mmu_fault_mask(ptdev, ~0));
panthor_mmu_irq_resume(&mmu->irq);
return 0;
}
#ifdef CONFIG_DEBUG_FS

View File

@ -453,7 +453,8 @@ void panthor_pwr_unplug(struct panthor_device *ptdev)
return;
/* Make sure the IRQ handler is not running after that point. */
panthor_pwr_irq_suspend(&ptdev->pwr->irq);
if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev))
panthor_pwr_irq_suspend(&ptdev->pwr->irq);
/* Wake-up all waiters. */
spin_lock_irqsave(&ptdev->pwr->reqs_lock, flags);
@ -483,12 +484,13 @@ int panthor_pwr_init(struct panthor_device *ptdev)
if (irq < 0)
return irq;
err = panthor_request_pwr_irq(
ptdev, &pwr->irq, irq, PWR_INTERRUPTS_MASK,
pwr->iomem + PWR_INT_BASE);
err = panthor_request_pwr_irq(ptdev, &pwr->irq, irq,
pwr->iomem + PWR_INT_BASE);
if (err)
return err;
panthor_pwr_irq_enable_events(&pwr->irq, PWR_INTERRUPTS_MASK);
panthor_pwr_irq_resume(&pwr->irq);
return 0;
}

View File

@ -1057,7 +1057,8 @@ group_unbind_locked(struct panthor_group *group)
/* Tiler OOM events will be re-issued next time the group is scheduled. */
atomic_set(&group->tiler_oom, 0);
cancel_work(&group->tiler_oom_work);
if (cancel_work(&group->tiler_oom_work))
group_put(group);
for (u32 i = 0; i < group->queue_count; i++)
group->queues[i]->doorbell_id = -1;
@ -1151,15 +1152,14 @@ queue_suspend_timeout_locked(struct panthor_queue *queue)
static void
queue_suspend_timeout(struct panthor_queue *queue)
{
spin_lock(&queue->fence_ctx.lock);
guard(spinlock_irqsave)(&queue->fence_ctx.lock);
queue_suspend_timeout_locked(queue);
spin_unlock(&queue->fence_ctx.lock);
}
static void
queue_resume_timeout(struct panthor_queue *queue)
{
spin_lock(&queue->fence_ctx.lock);
guard(spinlock_irqsave)(&queue->fence_ctx.lock);
if (queue_timeout_is_suspended(queue)) {
mod_delayed_work(queue->scheduler.timeout_wq,
@ -1168,8 +1168,6 @@ queue_resume_timeout(struct panthor_queue *queue)
queue->timeout.remaining = MAX_SCHEDULE_TIMEOUT;
}
spin_unlock(&queue->fence_ctx.lock);
}
/**
@ -1542,7 +1540,7 @@ cs_slot_process_fault_event_locked(struct panthor_device *ptdev,
u64 cs_extract = queue->iface.output->extract;
struct panthor_job *job;
spin_lock(&queue->fence_ctx.lock);
guard(spinlock_irqsave)(&queue->fence_ctx.lock);
list_for_each_entry(job, &queue->fence_ctx.in_flight_jobs, node) {
if (cs_extract >= job->ringbuf.end)
continue;
@ -1552,7 +1550,6 @@ cs_slot_process_fault_event_locked(struct panthor_device *ptdev,
dma_fence_set_error(job->done_fence, -EINVAL);
}
spin_unlock(&queue->fence_ctx.lock);
}
if (group) {
@ -1604,7 +1601,10 @@ static int group_process_tiler_oom(struct panthor_group *group, u32 cs_id)
if (unlikely(csg_id < 0))
return 0;
if (IS_ERR(heaps) || frag_end > vt_end || vt_end >= vt_start) {
if (IS_ERR(heaps)) {
ret = -EINVAL;
heaps = NULL;
} else if (frag_end > vt_end || vt_end >= vt_start) {
ret = -EINVAL;
} else {
/* We do the allocation without holding the scheduler lock to avoid
@ -2183,13 +2183,13 @@ group_term_post_processing(struct panthor_group *group)
if (!queue)
continue;
spin_lock(&queue->fence_ctx.lock);
list_for_each_entry_safe(job, tmp, &queue->fence_ctx.in_flight_jobs, node) {
list_move_tail(&job->node, &faulty_jobs);
dma_fence_set_error(job->done_fence, err);
dma_fence_signal_locked(job->done_fence);
scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) {
list_for_each_entry_safe(job, tmp, &queue->fence_ctx.in_flight_jobs, node) {
list_move_tail(&job->node, &faulty_jobs);
dma_fence_set_error(job->done_fence, err);
dma_fence_signal_locked(job->done_fence);
}
}
spin_unlock(&queue->fence_ctx.lock);
/* Manually update the syncobj seqno to unblock waiters. */
syncobj = group->syncobjs->kmap + (i * sizeof(*syncobj));
@ -2368,7 +2368,13 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c
csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id);
csg_slot = &sched->csg_slots[csg_id];
group_bind_locked(group, csg_id);
ret = group_bind_locked(group, csg_id);
if (ret) {
panthor_device_schedule_reset(ptdev);
ctx->csg_upd_failed_mask |= BIT(csg_id);
return;
}
csg_slot_prog_locked(ptdev, csg_id, new_csg_prio--);
csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id,
group->state == PANTHOR_CS_GROUP_SUSPENDED ?
@ -2668,7 +2674,14 @@ static void sched_resume_tick(struct panthor_device *ptdev)
else
delay_jiffies = 0;
sched_queue_delayed_work(sched, tick, delay_jiffies);
/* We schedule immediate ticks when we need to process events on CSGs,
* but those don't change the resched_target because we want the other
* groups to stay scheduled for the remaining of the GPU timeslot they
* were given. Make sure those immediate ticks don't get overruled by
* a sched_queue_delayed_work() that would delay the tick execution.
*/
if (!delayed_work_pending(&sched->tick_work))
sched_queue_delayed_work(sched, tick, delay_jiffies);
}
static void group_schedule_locked(struct panthor_group *group, u32 queue_mask)
@ -3049,39 +3062,39 @@ static bool queue_check_job_completion(struct panthor_queue *queue)
LIST_HEAD(done_jobs);
cookie = dma_fence_begin_signalling();
spin_lock(&queue->fence_ctx.lock);
list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) {
if (!syncobj) {
struct panthor_group *group = job->group;
scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) {
list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) {
if (!syncobj) {
struct panthor_group *group = job->group;
syncobj = group->syncobjs->kmap +
(job->queue_idx * sizeof(*syncobj));
syncobj = group->syncobjs->kmap +
(job->queue_idx * sizeof(*syncobj));
}
if (syncobj->seqno < job->done_fence->seqno)
break;
list_move_tail(&job->node, &done_jobs);
dma_fence_signal_locked(job->done_fence);
}
if (syncobj->seqno < job->done_fence->seqno)
break;
if (list_empty(&queue->fence_ctx.in_flight_jobs)) {
/* If we have no job left, we cancel the timer, and reset remaining
* time to its default so it can be restarted next time
* queue_resume_timeout() is called.
*/
queue_suspend_timeout_locked(queue);
list_move_tail(&job->node, &done_jobs);
dma_fence_signal_locked(job->done_fence);
/* If there's no job pending, we consider it progress to avoid a
* spurious timeout if the timeout handler and the sync update
* handler raced.
*/
progress = true;
} else if (!list_empty(&done_jobs)) {
queue_reset_timeout_locked(queue);
progress = true;
}
}
if (list_empty(&queue->fence_ctx.in_flight_jobs)) {
/* If we have no job left, we cancel the timer, and reset remaining
* time to its default so it can be restarted next time
* queue_resume_timeout() is called.
*/
queue_suspend_timeout_locked(queue);
/* If there's no job pending, we consider it progress to avoid a
* spurious timeout if the timeout handler and the sync update
* handler raced.
*/
progress = true;
} else if (!list_empty(&done_jobs)) {
queue_reset_timeout_locked(queue);
progress = true;
}
spin_unlock(&queue->fence_ctx.lock);
dma_fence_end_signalling(cookie);
list_for_each_entry_safe(job, job_tmp, &done_jobs, node) {
@ -3346,9 +3359,8 @@ queue_run_job(struct drm_sched_job *sched_job)
job->ringbuf.end = job->ringbuf.start + (instrs.count * sizeof(u64));
panthor_job_get(&job->base);
spin_lock(&queue->fence_ctx.lock);
list_add_tail(&job->node, &queue->fence_ctx.in_flight_jobs);
spin_unlock(&queue->fence_ctx.lock);
scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock)
list_add_tail(&job->node, &queue->fence_ctx.in_flight_jobs);
/* Make sure the ring buffer is updated before the INSERT
* register.

View File

@ -897,7 +897,8 @@ static int virtio_get_edid_block(void *data, u8 *buf,
struct virtio_gpu_resp_edid *resp = data;
size_t start = block * EDID_LENGTH;
if (start + len > le32_to_cpu(resp->size))
if (start + len > le32_to_cpu(resp->size) ||
start + len > sizeof(resp->edid))
return -EINVAL;
memcpy(buf, resp->edid + start, len);
return 0;

View File

@ -115,6 +115,7 @@ struct drm_dp_vsc_sdp {
* @duration_decr_ms: Successive frame duration decrease
* @target_rr_divider: Target refresh rate divider
* @mode: Adaptive Sync Operation Mode
* @coasting_vtotal: Coasting vtotal
*/
struct drm_dp_as_sdp {
unsigned char sdp_type;

View File

@ -79,7 +79,8 @@ static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B)
#define DRM_FIXED_ALMOST_ONE (DRM_FIXED_ONE - DRM_FIXED_EPSILON)
/**
* @drm_sm2fixp
* drm_sm2fixp() - convert signed-magnitude to fixed point
* @a: 1.31.32 signed-magnitude fixed point
*
* Convert a 1.31.32 signed-magnitude fixed point to 32.32
* 2s-complement fixed point

View File

@ -6,6 +6,8 @@
#ifndef __DRM_RAS_H__
#define __DRM_RAS_H__
#include <linux/types.h>
#include <uapi/drm/drm_ras.h>
/**