diff options
author | Terje Bergstrom <tbergstrom@nvidia.com> | 2011-10-17 11:27:43 +0300 |
---|---|---|
committer | Simone Willett <swillett@nvidia.com> | 2011-10-27 15:09:25 -0700 |
commit | a9469db8c4c04fa7cd8f080bafdca26d99a3018c (patch) | |
tree | fe4bdbf4ebd907c0c8583a4b0b4fb475c9db76bd | |
parent | 15292d96c52c0692ec8666d52d355201142245a7 (diff) |
video: tegra: host: Add timeout to low-pri throttle
Add timeout to throttling low-priority thread. Low priority threads are
allowed to push work either when push buffer is empty, or when they've
waited for a pre-defined period. Setting the period to 50ms for now.
Bug 864407
Change-Id: Id37bd2c3c229e359973ca10587c20737596f3e1b
Reviewed-on: http://git-master/r/58330
Tested-by: Gerrit_Virtual_Submit
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
Tested-by: Terje Bergstrom <tbergstrom@nvidia.com>
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
-rw-r--r-- | drivers/video/tegra/host/dev.c | 2 | ||||
-rw-r--r-- | drivers/video/tegra/host/nvhost_cdma.c | 66 | ||||
-rw-r--r-- | drivers/video/tegra/host/nvhost_cdma.h | 8 | ||||
-rw-r--r-- | drivers/video/tegra/host/nvhost_channel.c | 9 | ||||
-rw-r--r-- | drivers/video/tegra/host/t20/cdma_t20.c | 2 |
5 files changed, 56 insertions, 31 deletions
diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c index 582a0c58e601..d6877a6ec3de 100644 --- a/drivers/video/tegra/host/dev.c +++ b/drivers/video/tegra/host/dev.c @@ -476,7 +476,7 @@ static long nvhost_channelctl(struct file *filp, priv->timeout.has_timedout; break; case NVHOST_IOCTL_CHANNEL_SET_PRIORITY: - priv->timeout.timeout = + priv->priority = (u32)((struct nvhost_set_priority_args *)buf)->priority; break; default: diff --git a/drivers/video/tegra/host/nvhost_cdma.c b/drivers/video/tegra/host/nvhost_cdma.c index 769dba7c8b99..551230624eb9 100644 --- a/drivers/video/tegra/host/nvhost_cdma.c +++ b/drivers/video/tegra/host/nvhost_cdma.c @@ -267,7 +267,8 @@ dequeue_sync_queue_head(struct sync_queue *queue) * - pb space: returns the number of free slots in the channel's push buffer * Must be called with the cdma lock held. */ -static unsigned int cdma_status(struct nvhost_cdma *cdma, enum cdma_event event) +static unsigned int cdma_status_locked(struct nvhost_cdma *cdma, + enum cdma_event event) { switch (event) { case CDMA_EVENT_SYNC_QUEUE_EMPTY: @@ -293,10 +294,11 @@ static unsigned int cdma_status(struct nvhost_cdma *cdma, enum cdma_event event) * - Return the amount of space (> 0) * Must be called with the cdma lock held. */ -unsigned int nvhost_cdma_wait(struct nvhost_cdma *cdma, enum cdma_event event) +unsigned int nvhost_cdma_wait_locked(struct nvhost_cdma *cdma, + enum cdma_event event) { for (;;) { - unsigned int space = cdma_status(cdma, event); + unsigned int space = cdma_status_locked(cdma, event); if (space) return space; @@ -317,7 +319,7 @@ unsigned int nvhost_cdma_wait(struct nvhost_cdma *cdma, enum cdma_event event) * Start timer for a buffer submition that has completed yet. * Must be called with the cdma lock held. */ -void nvhost_cdma_start_timer(struct nvhost_cdma *cdma, u32 syncpt_id, +static void cdma_start_timer_locked(struct nvhost_cdma *cdma, u32 syncpt_id, u32 syncpt_val, struct nvhost_userctx_timeout *timeout) { @@ -340,7 +342,7 @@ void nvhost_cdma_start_timer(struct nvhost_cdma *cdma, u32 syncpt_id, * Stop timer when a buffer submition completes. * Must be called with the cdma lock held. */ -static void stop_cdma_timer(struct nvhost_cdma *cdma) +static void stop_cdma_timer_locked(struct nvhost_cdma *cdma) { cancel_delayed_work(&cdma->timeout.wq); cdma->timeout.ctx_timeout = NULL; @@ -356,7 +358,7 @@ static void stop_cdma_timer(struct nvhost_cdma *cdma) * called manually if necessary. * Must be called with the cdma lock held. */ -static void update_cdma(struct nvhost_cdma *cdma) +static void update_cdma_locked(struct nvhost_cdma *cdma) { bool signal = false; struct nvhost_master *dev = cdma_to_dev(cdma); @@ -396,7 +398,7 @@ static void update_cdma(struct nvhost_cdma *cdma) if (!nvhost_syncpt_min_cmp(sp, syncpt_id, syncpt_val)) { /* Start timer on next pending syncpt */ if (timeout) { - nvhost_cdma_start_timer(cdma, syncpt_id, + cdma_start_timer_locked(cdma, syncpt_id, syncpt_val, timeout_ref); } break; @@ -404,7 +406,7 @@ static void update_cdma(struct nvhost_cdma *cdma) /* Cancel timeout, when a buffer completes */ if (cdma->timeout.ctx_timeout) - stop_cdma_timer(cdma); + stop_cdma_timer_locked(cdma); nr_slots = sync[SQ_IDX_NUM_SLOTS]; nr_handles = sync[SQ_IDX_NUM_HANDLES]; @@ -713,7 +715,7 @@ void nvhost_cdma_push_gather(struct nvhost_cdma *cdma, BUG_ON(!cdma_op(cdma).kick); if (slots_free == 0) { cdma_op(cdma).kick(cdma); - slots_free = nvhost_cdma_wait(cdma, + slots_free = nvhost_cdma_wait_locked(cdma, CDMA_EVENT_PUSH_BUFFER_SPACE); } cdma->slots_free = slots_free - 1; @@ -746,7 +748,8 @@ void nvhost_cdma_end(struct nvhost_cdma *cdma, * Wait until there's enough room in the * sync queue to write something. */ - count = nvhost_cdma_wait(cdma, CDMA_EVENT_SYNC_QUEUE_SPACE); + count = nvhost_cdma_wait_locked(cdma, + CDMA_EVENT_SYNC_QUEUE_SPACE); /* Add reloc entries to sync queue (as many as will fit) */ if (count > nr_handles) @@ -765,7 +768,7 @@ void nvhost_cdma_end(struct nvhost_cdma *cdma, /* start timer on idle -> active transitions */ if (timeout->timeout && was_idle) { - nvhost_cdma_start_timer(cdma, sync_point_id, sync_point_value, + cdma_start_timer_locked(cdma, sync_point_id, sync_point_value, timeout); } @@ -778,23 +781,42 @@ void nvhost_cdma_end(struct nvhost_cdma *cdma, void nvhost_cdma_update(struct nvhost_cdma *cdma) { mutex_lock(&cdma->lock); - update_cdma(cdma); + update_cdma_locked(cdma); mutex_unlock(&cdma->lock); } /** - * Manually spin until all CDMA has finished. Used if an async update - * cannot be scheduled for any reason. + * Wait for push buffer to be empty. + * @cdma pointer to channel cdma + * @timeout timeout in ms + * Returns -ETIME if timeout was reached, zero if push buffer is empty. */ -void nvhost_cdma_flush(struct nvhost_cdma *cdma) +int nvhost_cdma_flush(struct nvhost_cdma *cdma, int timeout) { - mutex_lock(&cdma->lock); - while (sync_queue_head(&cdma->sync_queue)) { - update_cdma(cdma); - mutex_unlock(&cdma->lock); - schedule(); + unsigned int space, err = 0; + unsigned long end_jiffies = jiffies + msecs_to_jiffies(timeout); + + /* + * Wait for at most timeout ms. Recalculate timeout at each iteration + * to better keep within given timeout. + */ + while(!err && time_before(jiffies, end_jiffies)) { + int timeout_jiffies = end_jiffies - jiffies; + mutex_lock(&cdma->lock); + space = cdma_status_locked(cdma, + CDMA_EVENT_SYNC_QUEUE_EMPTY); + if (space) { + mutex_unlock(&cdma->lock); + return 0; + } + + BUG_ON(cdma->event != CDMA_EVENT_NONE); + cdma->event = CDMA_EVENT_SYNC_QUEUE_EMPTY; + + mutex_unlock(&cdma->lock); + err = down_timeout(&cdma->sem, + jiffies_to_msecs(timeout_jiffies)); } - mutex_unlock(&cdma->lock); + return err; } - diff --git a/drivers/video/tegra/host/nvhost_cdma.h b/drivers/video/tegra/host/nvhost_cdma.h index e5e1d5f88701..e0a034d4bdc6 100644 --- a/drivers/video/tegra/host/nvhost_cdma.h +++ b/drivers/video/tegra/host/nvhost_cdma.h @@ -144,13 +144,11 @@ void nvhost_cdma_end(struct nvhost_cdma *cdma, struct nvmap_handle **handles, unsigned int nr_handles, struct nvhost_userctx_timeout *timeout); void nvhost_cdma_update(struct nvhost_cdma *cdma); -void nvhost_cdma_flush(struct nvhost_cdma *cdma); +int nvhost_cdma_flush(struct nvhost_cdma *cdma, int timeout); void nvhost_cdma_peek(struct nvhost_cdma *cdma, u32 dmaget, int slot, u32 *out); -unsigned int nvhost_cdma_wait(struct nvhost_cdma *cdma, enum cdma_event event); -void nvhost_cdma_start_timer(struct nvhost_cdma *cdma, u32 syncpt_id, - u32 syncpt_val, - struct nvhost_userctx_timeout *timeout); +unsigned int nvhost_cdma_wait_locked(struct nvhost_cdma *cdma, + enum cdma_event event); void nvhost_cdma_update_sync_queue(struct nvhost_cdma *cdma, struct nvhost_syncpt *syncpt, struct device *dev); #endif diff --git a/drivers/video/tegra/host/nvhost_channel.c b/drivers/video/tegra/host/nvhost_channel.c index b982a8391c66..6cea23322960 100644 --- a/drivers/video/tegra/host/nvhost_channel.c +++ b/drivers/video/tegra/host/nvhost_channel.c @@ -28,6 +28,8 @@ #include <linux/platform_device.h> +#define NVHOST_CHANNEL_LOW_PRIO_MAX_WAIT 50 + int nvhost_channel_submit( struct nvhost_channel *channel, struct nvhost_hwctx *hwctx, @@ -48,9 +50,12 @@ int nvhost_channel_submit( { BUG_ON(!channel_op(channel).submit); - /* Low priority submits wait until sync queue is empty */ + /* Low priority submits wait until sync queue is empty. Ignores result + * from nvhost_cdma_flush, as we submit either when push buffer is + * empty or when we reach the timeout. */ if (priority < NVHOST_PRIORITY_MEDIUM) - nvhost_cdma_wait(&channel->cdma, CDMA_EVENT_SYNC_QUEUE_EMPTY); + (void)nvhost_cdma_flush(&channel->cdma, + NVHOST_CHANNEL_LOW_PRIO_MAX_WAIT); return channel_op(channel).submit(channel, hwctx, diff --git a/drivers/video/tegra/host/t20/cdma_t20.c b/drivers/video/tegra/host/t20/cdma_t20.c index 633d4e700f8c..ad198175e451 100644 --- a/drivers/video/tegra/host/t20/cdma_t20.c +++ b/drivers/video/tegra/host/t20/cdma_t20.c @@ -525,7 +525,7 @@ static void t20_cdma_stop(struct nvhost_cdma *cdma) mutex_lock(&cdma->lock); if (cdma->running) { - nvhost_cdma_wait(cdma, CDMA_EVENT_SYNC_QUEUE_EMPTY); + nvhost_cdma_wait_locked(cdma, CDMA_EVENT_SYNC_QUEUE_EMPTY); writel(nvhost_channel_dmactrl(true, false, false), chan_regs + HOST1X_CHANNEL_DMACTRL); cdma->running = false; |