summaryrefslogtreecommitdiff
path: root/drivers/video/tegra/host/t20/channel_t20.c
diff options
context:
space:
mode:
authorTerje Bergstrom <tbergstrom@nvidia.com>2011-09-22 15:37:17 +0300
committerVarun Colbert <vcolbert@nvidia.com>2011-10-18 18:09:14 -0700
commitfc2d6411c6bc4e77617d1c12723334c2ebdab00c (patch)
tree9d3f7a2b0c98671f49a24f0e854efd103fd19495 /drivers/video/tegra/host/t20/channel_t20.c
parent3d049a55d3428c17fa441ec9021ce201d340a41d (diff)
video: tegra: host: Check for allocation failures
Adds checks for memory allocation failures, and proper propagation of error conditions. Adds clearing of pointers after free or unpin has been called to catch use after free. Bug 877551 Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com> Reviewed-on: http://git-master/r/54027 Reviewed-by: Antti Miettinen <amiettinen@nvidia.com> Reviewed-by: Hiroshi Doyu <hdoyu@nvidia.com> Reviewed-by: Scott Williams <scwilliams@nvidia.com> Reviewed-by: Krishna Reddy <vdumpa@nvidia.com> Tested-by: Gerrit_Virtual_Submit (cherry-picked from commit bfbf2766d11a5f85781532ddce3a87b7ae762ba3) Change-Id: Ibb8d14ef9a21d38d08748941e23d86c7c3a700ea Reviewed-on: http://git-master/r/58309 Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com> Tested-by: Terje Bergstrom <tbergstrom@nvidia.com> Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'drivers/video/tegra/host/t20/channel_t20.c')
-rw-r--r--drivers/video/tegra/host/t20/channel_t20.c111
1 files changed, 87 insertions, 24 deletions
diff --git a/drivers/video/tegra/host/t20/channel_t20.c b/drivers/video/tegra/host/t20/channel_t20.c
index 66e4b78dcf11..9e3bf140ea34 100644
--- a/drivers/video/tegra/host/t20/channel_t20.c
+++ b/drivers/video/tegra/host/t20/channel_t20.c
@@ -25,6 +25,7 @@
#include "../nvhost_hwctx.h"
#include <trace/events/nvhost.h>
#include <mach/powergate.h>
+#include <linux/slab.h>
#include "hardware_t20.h"
#include "syncpt_t20.h"
@@ -194,6 +195,15 @@ static int t20_channel_submit(struct nvhost_channel *channel,
bool need_restore = false;
u32 syncval;
int err;
+ void *ctxrestore_waiter = NULL;
+ void *ctxsave_waiter, *completed_waiter;
+
+ ctxsave_waiter = nvhost_intr_alloc_waiter();
+ completed_waiter = nvhost_intr_alloc_waiter();
+ if (!ctxsave_waiter || !completed_waiter) {
+ err = -ENOMEM;
+ goto done;
+ }
/* keep module powered */
nvhost_module_busy(&channel->mod);
@@ -207,7 +217,19 @@ static int t20_channel_submit(struct nvhost_channel *channel,
err = mutex_lock_interruptible(&channel->submitlock);
if (err) {
nvhost_module_idle(&channel->mod);
- return err;
+ goto done;
+ }
+
+ /* If we are going to need a restore, allocate a waiter for it */
+ if (channel->cur_ctx != hwctx && hwctx && hwctx->valid) {
+ ctxrestore_waiter = nvhost_intr_alloc_waiter();
+ if (!ctxrestore_waiter) {
+ mutex_unlock(&channel->submitlock);
+ nvhost_module_idle(&channel->mod);
+ err = -ENOMEM;
+ goto done;
+ }
+ need_restore = true;
}
/* remove stale waits */
@@ -221,7 +243,7 @@ static int t20_channel_submit(struct nvhost_channel *channel,
"nvhost_syncpt_wait_check failed: %d\n", err);
mutex_unlock(&channel->submitlock);
nvhost_module_idle(&channel->mod);
- return err;
+ goto done;
}
}
@@ -230,7 +252,7 @@ static int t20_channel_submit(struct nvhost_channel *channel,
if (err) {
mutex_unlock(&channel->submitlock);
nvhost_module_idle(&channel->mod);
- return err;
+ goto done;
}
/* context switch */
@@ -251,10 +273,8 @@ static int t20_channel_submit(struct nvhost_channel *channel,
channel->ctxhandler.get(hwctx_to_save);
}
channel->cur_ctx = hwctx;
- if (channel->cur_ctx && channel->cur_ctx->valid) {
- need_restore = true;
+ if (need_restore)
syncpt_incrs += channel->cur_ctx->restore_incrs;
- }
}
/* get absolute sync value */
@@ -334,27 +354,44 @@ static int t20_channel_submit(struct nvhost_channel *channel,
* schedule a context save interrupt (to drain the host FIFO
* if necessary, and to release the restore buffer)
*/
- if (hwctx_to_save)
- nvhost_intr_add_action(&channel->dev->intr, syncpt_id,
+ if (hwctx_to_save) {
+ err = nvhost_intr_add_action(&channel->dev->intr, syncpt_id,
syncval - syncpt_incrs + hwctx_to_save->save_thresh,
- NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save, NULL);
+ NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save,
+ ctxsave_waiter,
+ NULL);
+ ctxsave_waiter = NULL;
+ WARN(err, "Failed to set ctx save interrupt");
+ }
- if (need_restore)
- nvhost_intr_add_action(&channel->dev->intr, syncpt_id,
+ if (need_restore) {
+ BUG_ON(!ctxrestore_waiter);
+ err = nvhost_intr_add_action(&channel->dev->intr, syncpt_id,
syncval - user_syncpt_incrs,
- NVHOST_INTR_ACTION_CTXRESTORE, channel->cur_ctx, NULL);
+ NVHOST_INTR_ACTION_CTXRESTORE, channel->cur_ctx,
+ ctxrestore_waiter,
+ NULL);
+ ctxrestore_waiter = NULL;
+ WARN(err, "Failed to set ctx restore interrupt");
+ }
/* schedule a submit complete interrupt */
err = nvhost_intr_add_action(&channel->dev->intr, syncpt_id, syncval,
- NVHOST_INTR_ACTION_SUBMIT_COMPLETE, channel, NULL);
- /* if add_action failed, the submit has been already completed */
- if (err)
- trace_nvhost_channel_submit_complete(channel->desc->name, 1);
+ NVHOST_INTR_ACTION_SUBMIT_COMPLETE, channel,
+ completed_waiter,
+ NULL);
+ completed_waiter = NULL;
+ WARN(err, "Failed to set submit complete interrupt");
mutex_unlock(&channel->submitlock);
*syncpt_value = syncval;
- return 0;
+
+done:
+ kfree(ctxrestore_waiter);
+ kfree(ctxsave_waiter);
+ kfree(completed_waiter);
+ return err;
}
void power_off_3d(struct nvhost_module *mod)
@@ -375,9 +412,18 @@ static int t20_channel_read_3d_reg(
unsigned int pending = 0;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
void *ref;
+ void *ctx_waiter, *read_waiter, *completed_waiter;
u32 syncval;
int err;
+ ctx_waiter = nvhost_intr_alloc_waiter();
+ read_waiter = nvhost_intr_alloc_waiter();
+ completed_waiter = nvhost_intr_alloc_waiter();
+ if (!ctx_waiter || !read_waiter || !completed_waiter) {
+ err = -ENOMEM;
+ goto done;
+ }
+
/* keep module powered */
nvhost_module_busy(&channel->mod);
@@ -463,14 +509,24 @@ static int t20_channel_read_3d_reg(
* schedule a context save interrupt (to drain the host FIFO
* if necessary, and to release the restore buffer)
*/
- if (hwctx_to_save)
- nvhost_intr_add_action(&channel->dev->intr, NVSYNCPT_3D,
+ if (hwctx_to_save) {
+ err = nvhost_intr_add_action(&channel->dev->intr, NVSYNCPT_3D,
syncval - syncpt_incrs + hwctx_to_save->save_incrs - 1,
- NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save, NULL);
+ NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save,
+ ctx_waiter,
+ NULL);
+ ctx_waiter = NULL;
+ WARN(err, "Failed to set context save interrupt");
+ }
/* Wait for FIFO to be ready */
- nvhost_intr_add_action(&channel->dev->intr, NVSYNCPT_3D, syncval - 2,
- NVHOST_INTR_ACTION_WAKEUP, &wq, &ref);
+ err = nvhost_intr_add_action(&channel->dev->intr, NVSYNCPT_3D,
+ syncval - 2,
+ NVHOST_INTR_ACTION_WAKEUP, &wq,
+ read_waiter,
+ &ref);
+ read_waiter = NULL;
+ WARN(err, "Failed to set wakeup interrupt");
wait_event(wq,
nvhost_syncpt_min_cmp(&channel->dev->syncpt,
NVSYNCPT_3D, syncval - 2));
@@ -484,11 +540,18 @@ static int t20_channel_read_3d_reg(
nvhost_syncpt_cpu_incr(&channel->dev->syncpt, NVSYNCPT_3D);
/* Schedule a submit complete interrupt */
- nvhost_intr_add_action(&channel->dev->intr, NVSYNCPT_3D, syncval,
- NVHOST_INTR_ACTION_SUBMIT_COMPLETE, channel, NULL);
+ err = nvhost_intr_add_action(&channel->dev->intr, NVSYNCPT_3D, syncval,
+ NVHOST_INTR_ACTION_SUBMIT_COMPLETE, channel,
+ completed_waiter, NULL);
+ completed_waiter = NULL;
+ WARN(err, "Failed to set submit complete interrupt");
mutex_unlock(&channel->submitlock);
+done:
+ kfree(ctx_waiter);
+ kfree(read_waiter);
+ kfree(completed_waiter);
return err;
}