Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ec73c4cf authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Thierry Reding
Browse files

drm/tegra: Prevent BOs from being freed during job submission



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: default avatarDmitry Osipenko <digetx@gmail.com>
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent a8bc8c65
Loading
Loading
Loading
Loading
+31 −9
Original line number Diff line number Diff line
@@ -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;
}