summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Osipenko <digetx@gmail.com>2017-08-11 19:54:56 +0200
committerThierry Reding <treding@nvidia.com>2017-08-17 17:57:18 +0200
commitec73c4cfe7de6229e49989f7d7754a7039cd5c28 (patch)
treefd877987480f0f64a6dfdff08734d3451b878da5
parenta8bc8c6510d64b759644647a03f8aec55056bded (diff)
drm/tegra: Prevent BOs from being freed during job submissiondrm/tegra/for-4.14-rc1
Since DRM IOCTL's are lockless, there is a chance that BOs could be released while a job submission is in progress. To avoid that, keep the GEM reference until the job has been pinned, part of which will be to take another reference. v2: remove redundant check and avoid memory leak Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r--drivers/gpu/drm/tegra/drm.c40
1 files changed, 31 insertions, 9 deletions
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 0251bb1cb545..ba46414e1897 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -304,8 +304,6 @@ host1x_bo_lookup(struct drm_file *file, u32 handle)
if (!gem)
return NULL;
- drm_gem_object_put_unlocked(gem);
-
bo = to_tegra_bo(gem);
return &bo->base;
}
@@ -394,8 +392,10 @@ int tegra_drm_submit(struct tegra_drm_context *context,
(void __user *)(uintptr_t)args->waitchks;
struct drm_tegra_syncpt syncpt;
struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
+ struct drm_gem_object **refs;
struct host1x_syncpt *sp;
struct host1x_job *job;
+ unsigned int num_refs;
int err;
/* We don't yet support other than one syncpt_incr struct per submit */
@@ -417,6 +417,21 @@ int tegra_drm_submit(struct tegra_drm_context *context,
job->class = context->client->base.class;
job->serialize = true;
+ /*
+ * Track referenced BOs so that they can be unreferenced after the
+ * submission is complete.
+ */
+ num_refs = num_cmdbufs + num_relocs * 2 + num_waitchks;
+
+ refs = kmalloc_array(num_refs, sizeof(*refs), GFP_KERNEL);
+ if (!refs) {
+ err = -ENOMEM;
+ goto put;
+ }
+
+ /* reuse as an iterator later */
+ num_refs = 0;
+
while (num_cmdbufs) {
struct drm_tegra_cmdbuf cmdbuf;
struct host1x_bo *bo;
@@ -445,6 +460,7 @@ int tegra_drm_submit(struct tegra_drm_context *context,
offset = (u64)cmdbuf.offset + (u64)cmdbuf.words * sizeof(u32);
obj = host1x_to_tegra_bo(bo);
+ refs[num_refs++] = &obj->gem;
/*
* Gather buffer base address must be 4-bytes aligned,
@@ -474,6 +490,7 @@ int tegra_drm_submit(struct tegra_drm_context *context,
reloc = &job->relocarray[num_relocs];
obj = host1x_to_tegra_bo(reloc->cmdbuf.bo);
+ refs[num_refs++] = &obj->gem;
/*
* The unaligned cmdbuf offset will cause an unaligned write
@@ -487,6 +504,7 @@ int tegra_drm_submit(struct tegra_drm_context *context,
}
obj = host1x_to_tegra_bo(reloc->target.bo);
+ refs[num_refs++] = &obj->gem;
if (reloc->target.offset >= obj->gem.size) {
err = -EINVAL;
@@ -506,6 +524,7 @@ int tegra_drm_submit(struct tegra_drm_context *context,
goto fail;
obj = host1x_to_tegra_bo(wait->bo);
+ refs[num_refs++] = &obj->gem;
/*
* The unaligned offset will cause an unaligned write during
@@ -545,17 +564,20 @@ int tegra_drm_submit(struct tegra_drm_context *context,
goto fail;
err = host1x_job_submit(job);
- if (err)
- goto fail_submit;
+ if (err) {
+ host1x_job_unpin(job);
+ goto fail;
+ }
args->fence = job->syncpt_end;
- host1x_job_put(job);
- return 0;
-
-fail_submit:
- host1x_job_unpin(job);
fail:
+ while (num_refs--)
+ drm_gem_object_put_unlocked(refs[num_refs]);
+
+ kfree(refs);
+
+put:
host1x_job_put(job);
return err;
}