mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 17:25:35 +03:00
gpu: host1x: Implement job tracking using DMA fences
In anticipation of removal of the intr API, implement job tracking using DMA fences instead. The main two things about this are making cdma_update schedule the work since fence completion can now be called from interrupt context, and some complication in ensuring the callback is not running when we free the fence. Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com> Change-Id: I25f7f5a6cad24a00563eed79e0e17b1df1eadcdc Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2786636 Reviewed-by: Jonathan Hunter <jonathanh@nvidia.com> GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
committed by
Laxman Dewangan
parent
242251a0e1
commit
303267d828
@@ -490,6 +490,15 @@ resume:
|
|||||||
host1x_hw_cdma_resume(host1x, cdma, restart_addr);
|
host1x_hw_cdma_resume(host1x, cdma, restart_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cdma_update_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct host1x_cdma *cdma = container_of(work, struct host1x_cdma, update_work);
|
||||||
|
|
||||||
|
mutex_lock(&cdma->lock);
|
||||||
|
update_cdma_locked(cdma);
|
||||||
|
mutex_unlock(&cdma->lock);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a cdma
|
* Create a cdma
|
||||||
*/
|
*/
|
||||||
@@ -499,6 +508,7 @@ int host1x_cdma_init(struct host1x_cdma *cdma)
|
|||||||
|
|
||||||
mutex_init(&cdma->lock);
|
mutex_init(&cdma->lock);
|
||||||
init_completion(&cdma->complete);
|
init_completion(&cdma->complete);
|
||||||
|
INIT_WORK(&cdma->update_work, cdma_update_work);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&cdma->sync_queue);
|
INIT_LIST_HEAD(&cdma->sync_queue);
|
||||||
|
|
||||||
@@ -679,7 +689,5 @@ void host1x_cdma_end(struct host1x_cdma *cdma,
|
|||||||
*/
|
*/
|
||||||
void host1x_cdma_update(struct host1x_cdma *cdma)
|
void host1x_cdma_update(struct host1x_cdma *cdma)
|
||||||
{
|
{
|
||||||
mutex_lock(&cdma->lock);
|
schedule_work(&cdma->update_work);
|
||||||
update_cdma_locked(cdma);
|
|
||||||
mutex_unlock(&cdma->lock);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
struct host1x_syncpt;
|
struct host1x_syncpt;
|
||||||
struct host1x_userctx_timeout;
|
struct host1x_userctx_timeout;
|
||||||
@@ -69,6 +70,7 @@ struct host1x_cdma {
|
|||||||
struct buffer_timeout timeout; /* channel's timeout state/wq */
|
struct buffer_timeout timeout; /* channel's timeout state/wq */
|
||||||
bool running;
|
bool running;
|
||||||
bool torndown;
|
bool torndown;
|
||||||
|
struct work_struct update_work;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define cdma_to_channel(cdma) container_of(cdma, struct host1x_channel, cdma)
|
#define cdma_to_channel(cdma) container_of(cdma, struct host1x_channel, cdma)
|
||||||
|
|||||||
@@ -278,6 +278,15 @@ static void channel_program_cdma(struct host1x_job *job)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void job_complete_callback(struct dma_fence *fence, struct dma_fence_cb *cb)
|
||||||
|
{
|
||||||
|
struct host1x_job *job = container_of(cb, struct host1x_job, fence_cb);
|
||||||
|
|
||||||
|
/* Schedules CDMA update. */
|
||||||
|
host1x_cdma_update(&job->channel->cdma);
|
||||||
|
complete(&job->fence_cb_done);
|
||||||
|
}
|
||||||
|
|
||||||
static int channel_submit(struct host1x_job *job)
|
static int channel_submit(struct host1x_job *job)
|
||||||
{
|
{
|
||||||
struct host1x_channel *ch = job->channel;
|
struct host1x_channel *ch = job->channel;
|
||||||
@@ -285,7 +294,6 @@ static int channel_submit(struct host1x_job *job)
|
|||||||
u32 prev_max = 0;
|
u32 prev_max = 0;
|
||||||
u32 syncval;
|
u32 syncval;
|
||||||
int err;
|
int err;
|
||||||
struct host1x_waitlist *completed_waiter = NULL;
|
|
||||||
struct host1x *host = dev_get_drvdata(ch->dev->parent);
|
struct host1x *host = dev_get_drvdata(ch->dev->parent);
|
||||||
|
|
||||||
trace_host1x_channel_submit(dev_name(ch->dev),
|
trace_host1x_channel_submit(dev_name(ch->dev),
|
||||||
@@ -298,14 +306,7 @@ static int channel_submit(struct host1x_job *job)
|
|||||||
/* get submit lock */
|
/* get submit lock */
|
||||||
err = mutex_lock_interruptible(&ch->submitlock);
|
err = mutex_lock_interruptible(&ch->submitlock);
|
||||||
if (err)
|
if (err)
|
||||||
goto error;
|
return err;
|
||||||
|
|
||||||
completed_waiter = kzalloc(sizeof(*completed_waiter), GFP_KERNEL);
|
|
||||||
if (!completed_waiter) {
|
|
||||||
mutex_unlock(&ch->submitlock);
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
host1x_channel_set_streamid(ch);
|
host1x_channel_set_streamid(ch);
|
||||||
host1x_enable_gather_filter(ch);
|
host1x_enable_gather_filter(ch);
|
||||||
@@ -317,31 +318,37 @@ static int channel_submit(struct host1x_job *job)
|
|||||||
err = host1x_cdma_begin(&ch->cdma, job);
|
err = host1x_cdma_begin(&ch->cdma, job);
|
||||||
if (err) {
|
if (err) {
|
||||||
mutex_unlock(&ch->submitlock);
|
mutex_unlock(&ch->submitlock);
|
||||||
goto error;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel_program_cdma(job);
|
channel_program_cdma(job);
|
||||||
syncval = host1x_syncpt_read_max(sp);
|
syncval = host1x_syncpt_read_max(sp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create fence before submitting job to HW to avoid job completing
|
||||||
|
* before the fence is set up.
|
||||||
|
*/
|
||||||
|
job->fence = host1x_fence_create(sp, syncval);
|
||||||
|
if (WARN(IS_ERR(job->fence), "Failed to create submit complete fence")) {
|
||||||
|
job->fence = NULL;
|
||||||
|
} else {
|
||||||
|
err = dma_fence_add_callback(job->fence, &job->fence_cb,
|
||||||
|
job_complete_callback);
|
||||||
|
}
|
||||||
|
|
||||||
/* end CDMA submit & stash pinned hMems into sync queue */
|
/* end CDMA submit & stash pinned hMems into sync queue */
|
||||||
host1x_cdma_end(&ch->cdma, job);
|
host1x_cdma_end(&ch->cdma, job);
|
||||||
|
|
||||||
trace_host1x_channel_submitted(dev_name(ch->dev), prev_max, syncval);
|
trace_host1x_channel_submitted(dev_name(ch->dev), prev_max, syncval);
|
||||||
|
|
||||||
/* schedule a submit complete interrupt */
|
|
||||||
err = host1x_intr_add_action(host, sp, syncval,
|
|
||||||
HOST1X_INTR_ACTION_SUBMIT_COMPLETE, ch,
|
|
||||||
completed_waiter, &job->waiter);
|
|
||||||
completed_waiter = NULL;
|
|
||||||
WARN(err, "Failed to set submit complete interrupt");
|
|
||||||
|
|
||||||
mutex_unlock(&ch->submitlock);
|
mutex_unlock(&ch->submitlock);
|
||||||
|
|
||||||
return 0;
|
if (err == -ENOENT)
|
||||||
|
host1x_cdma_update(&ch->cdma);
|
||||||
|
else
|
||||||
|
WARN(err, "Failed to set submit complete interrupt");
|
||||||
|
|
||||||
error:
|
return 0;
|
||||||
kfree(completed_waiter);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev,
|
static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/dma-direction.h>
|
#include <linux/dma-direction.h>
|
||||||
|
#include <linux/dma-fence.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
@@ -296,8 +297,10 @@ struct host1x_job {
|
|||||||
/* Non-job tracking related syncpoint */
|
/* Non-job tracking related syncpoint */
|
||||||
struct host1x_syncpt *secondary_syncpt;
|
struct host1x_syncpt *secondary_syncpt;
|
||||||
|
|
||||||
/* Completion waiter ref */
|
/* Completion fence for job tracking */
|
||||||
void *waiter;
|
struct dma_fence *fence;
|
||||||
|
struct dma_fence_cb fence_cb;
|
||||||
|
struct completion fence_cb_done;
|
||||||
|
|
||||||
/* Maximum time to wait for this job */
|
/* Maximum time to wait for this job */
|
||||||
unsigned int timeout;
|
unsigned int timeout;
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
|
|||||||
job->enable_firewall = enable_firewall;
|
job->enable_firewall = enable_firewall;
|
||||||
|
|
||||||
kref_init(&job->ref);
|
kref_init(&job->ref);
|
||||||
|
init_completion(&job->fence_cb_done);
|
||||||
job->channel = ch;
|
job->channel = ch;
|
||||||
|
|
||||||
/* Redistribute memory to the structs */
|
/* Redistribute memory to the structs */
|
||||||
@@ -84,13 +85,24 @@ EXPORT_SYMBOL(host1x_job_get);
|
|||||||
static void job_free(struct kref *ref)
|
static void job_free(struct kref *ref)
|
||||||
{
|
{
|
||||||
struct host1x_job *job = container_of(ref, struct host1x_job, ref);
|
struct host1x_job *job = container_of(ref, struct host1x_job, ref);
|
||||||
|
bool removed;
|
||||||
|
|
||||||
if (job->release)
|
if (job->release)
|
||||||
job->release(job);
|
job->release(job);
|
||||||
|
|
||||||
if (job->waiter)
|
if (job->fence) {
|
||||||
host1x_intr_put_ref(job->syncpt->host, job->syncpt->id,
|
removed = dma_fence_remove_callback(job->fence, &job->fence_cb);
|
||||||
job->waiter, false);
|
if (!removed) {
|
||||||
|
/*
|
||||||
|
* Wait until possible pending callback is no longer
|
||||||
|
* using the job structure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
wait_for_completion(&job->fence_cb_done);
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_fence_put(job->fence);
|
||||||
|
}
|
||||||
|
|
||||||
if (job->syncpt)
|
if (job->syncpt)
|
||||||
host1x_syncpt_put(job->syncpt);
|
host1x_syncpt_put(job->syncpt);
|
||||||
|
|||||||
Reference in New Issue
Block a user