gpu: nvgpu: Add gk20a_fence type

When moving compression state tracking and compbit management ops to
kernel, we need to attach a fence to dma-buf metadata, along with the
compbit state.

To make in-kernel fence management easier, introduce a new gk20a_fence
abstraction. A gk20a_fence may be backed by a semaphore or a syncpoint
(id, value) pair. If the kernel is configured with CONFIG_SYNC, it will
also contain a sync_fence. The gk20a_fence can easily be converted back
to a syncpoint (id, value) parir or sync FD when we need to return it to
user space.

Change gk20a_submit_channel_gpfifo to return a gk20a_fence instead of
nvhost_fence. This is to facilitate work submission initiated from
kernel.

Bug 1509620

Change-Id: I6154764a279dba83f5e91ba9e0cb5e227ca08e1b
Signed-off-by: Lauri Peltonen <lpeltonen@nvidia.com>
Reviewed-on: http://git-master/r/439846
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
Tested-by: Terje Bergstrom <tbergstrom@nvidia.com>
This commit is contained in:
Lauri Peltonen
2014-07-18 02:21:34 +03:00
committed by Dan Willemsen
parent 55295c6087
commit bcf60a22c3
9 changed files with 477 additions and 293 deletions

View File

@@ -21,6 +21,7 @@ nvgpu-y := \
pmu_gk20a.o \
priv_ring_gk20a.o \
semaphore_gk20a.o \
fence_gk20a.o \
clk_gk20a.o \
therm_gk20a.o \
gr_ctx_gk20a_sim.o \

View File

@@ -33,6 +33,7 @@
#include "gk20a.h"
#include "dbg_gpu_gk20a.h"
#include "fence_gk20a.h"
#include "semaphore_gk20a.h"
#include "hw_ram_gk20a.h"
@@ -418,8 +419,8 @@ void gk20a_channel_abort(struct channel_gk20a *ch)
semaphore synchronization) */
mutex_lock(&ch->jobs_lock);
list_for_each_entry_safe(job, n, &ch->jobs, list) {
if (job->post_fence.semaphore) {
gk20a_semaphore_release(job->post_fence.semaphore);
if (job->post_fence->semaphore) {
gk20a_semaphore_release(job->post_fence->semaphore);
released_job_semaphore = true;
}
}
@@ -685,8 +686,12 @@ unbind:
ch->vpr = false;
ch->vm = NULL;
gk20a_channel_fence_close(&ch->last_submit.pre_fence);
gk20a_channel_fence_close(&ch->last_submit.post_fence);
mutex_lock(&ch->submit_lock);
gk20a_fence_put(ch->last_submit.pre_fence);
gk20a_fence_put(ch->last_submit.post_fence);
ch->last_submit.pre_fence = NULL;
ch->last_submit.post_fence = NULL;
mutex_unlock(&ch->submit_lock);
WARN_ON(ch->sync);
/* unlink all debug sessions */
@@ -1119,8 +1124,12 @@ int gk20a_alloc_channel_gpfifo(struct channel_gk20a *c,
ch_vm = c->vm;
c->cmds_pending = false;
gk20a_channel_fence_close(&c->last_submit.pre_fence);
gk20a_channel_fence_close(&c->last_submit.post_fence);
mutex_lock(&c->submit_lock);
gk20a_fence_put(c->last_submit.pre_fence);
gk20a_fence_put(c->last_submit.post_fence);
c->last_submit.pre_fence = NULL;
c->last_submit.post_fence = NULL;
mutex_unlock(&c->submit_lock);
c->ramfc.offset = 0;
c->ramfc.size = ram_in_ramfc_s() / 8;
@@ -1303,8 +1312,10 @@ static int gk20a_channel_submit_wfi(struct channel_gk20a *c)
}
}
gk20a_channel_fence_close(&c->last_submit.pre_fence);
gk20a_channel_fence_close(&c->last_submit.post_fence);
gk20a_fence_put(c->last_submit.pre_fence);
gk20a_fence_put(c->last_submit.post_fence);
c->last_submit.pre_fence = NULL;
c->last_submit.post_fence = NULL;
err = c->sync->incr_wfi(c->sync, &cmd, &c->last_submit.post_fence);
if (unlikely(err)) {
@@ -1312,7 +1323,7 @@ static int gk20a_channel_submit_wfi(struct channel_gk20a *c)
return err;
}
WARN_ON(!c->last_submit.post_fence.wfi);
WARN_ON(!c->last_submit.post_fence->wfi);
c->gpfifo.cpu_va[c->gpfifo.put].entry0 = u64_lo32(cmd->gva);
c->gpfifo.cpu_va[c->gpfifo.put].entry1 = u64_hi32(cmd->gva) |
@@ -1378,8 +1389,8 @@ static void trace_write_pushbuffer(struct channel_gk20a *c, struct gpfifo *g)
}
static int gk20a_channel_add_job(struct channel_gk20a *c,
struct gk20a_channel_fence *pre_fence,
struct gk20a_channel_fence *post_fence)
struct gk20a_fence *pre_fence,
struct gk20a_fence *post_fence)
{
struct vm_gk20a *vm = c->vm;
struct channel_gk20a_job *job = NULL;
@@ -1404,8 +1415,8 @@ static int gk20a_channel_add_job(struct channel_gk20a *c,
job->num_mapped_buffers = num_mapped_buffers;
job->mapped_buffers = mapped_buffers;
gk20a_channel_fence_dup(pre_fence, &job->pre_fence);
gk20a_channel_fence_dup(post_fence, &job->post_fence);
job->pre_fence = gk20a_fence_get(pre_fence);
job->post_fence = gk20a_fence_get(post_fence);
mutex_lock(&c->jobs_lock);
list_add_tail(&job->list, &c->jobs);
@@ -1424,18 +1435,19 @@ void gk20a_channel_update(struct channel_gk20a *c, int nr_completed)
mutex_lock(&c->submit_lock);
mutex_lock(&c->jobs_lock);
list_for_each_entry_safe(job, n, &c->jobs, list) {
bool completed = WARN_ON(!c->sync) ||
c->sync->is_expired(c->sync, &job->post_fence);
bool completed = gk20a_fence_is_expired(job->post_fence);
if (!completed)
break;
c->sync->signal_timeline(c->sync);
gk20a_vm_put_buffers(vm, job->mapped_buffers,
job->num_mapped_buffers);
/* Close the fences (this will unref the semaphores and release
* them to the pool). */
gk20a_channel_fence_close(&job->pre_fence);
gk20a_channel_fence_close(&job->post_fence);
gk20a_fence_put(job->pre_fence);
gk20a_fence_put(job->post_fence);
/* job is done. release its reference to vm */
gk20a_vm_put(vm);
@@ -1453,7 +1465,7 @@ void gk20a_channel_update(struct channel_gk20a *c, int nr_completed)
*/
if (list_empty(&c->jobs)) {
if (c->sync && c->sync->aggressive_destroy &&
c->sync->is_expired(c->sync, &c->last_submit.post_fence)) {
gk20a_fence_is_expired(c->last_submit.post_fence)) {
c->sync->destroy(c->sync);
c->sync = NULL;
}
@@ -1477,8 +1489,9 @@ void add_wait_cmd(u32 *ptr, u32 id, u32 thresh)
int gk20a_submit_channel_gpfifo(struct channel_gk20a *c,
struct nvhost_gpfifo *gpfifo,
u32 num_entries,
u32 flags,
struct nvhost_fence *fence,
u32 flags)
struct gk20a_fence **fence_out)
{
struct gk20a *g = c->g;
struct device *d = dev_from_gk20a(g);
@@ -1487,8 +1500,8 @@ int gk20a_submit_channel_gpfifo(struct channel_gk20a *c,
int wait_fence_fd = -1;
struct priv_cmd_entry *wait_cmd = NULL;
struct priv_cmd_entry *incr_cmd = NULL;
struct gk20a_channel_fence pre_fence = { 0 };
struct gk20a_channel_fence post_fence = { 0 };
struct gk20a_fence *pre_fence = NULL;
struct gk20a_fence *post_fence = NULL;
/* we might need two extra gpfifo entries - one for pre fence
* and one for post fence. */
const int extra_entries = 2;
@@ -1591,18 +1604,9 @@ int gk20a_submit_channel_gpfifo(struct channel_gk20a *c,
/* always insert syncpt increment at end of gpfifo submission
to keep track of method completion for idle railgating */
if (flags & NVHOST_SUBMIT_GPFIFO_FLAGS_FENCE_GET &&
flags & NVHOST_SUBMIT_GPFIFO_FLAGS_SYNC_FENCE)
err = c->sync->incr_user_fd(c->sync, wait_fence_fd, &incr_cmd,
&post_fence,
need_wfi,
&fence->syncpt_id);
else if (flags & NVHOST_SUBMIT_GPFIFO_FLAGS_FENCE_GET)
err = c->sync->incr_user_syncpt(c->sync, &incr_cmd,
&post_fence,
need_wfi,
&fence->syncpt_id,
&fence->value);
if (flags & NVHOST_SUBMIT_GPFIFO_FLAGS_FENCE_GET)
err = c->sync->incr_user(c->sync, wait_fence_fd, &incr_cmd,
&post_fence, need_wfi);
else
err = c->sync->incr(c->sync, &incr_cmd,
&post_fence);
@@ -1653,13 +1657,15 @@ int gk20a_submit_channel_gpfifo(struct channel_gk20a *c,
incr_cmd->gp_put = c->gpfifo.put;
}
gk20a_channel_fence_close(&c->last_submit.pre_fence);
gk20a_channel_fence_close(&c->last_submit.post_fence);
gk20a_fence_put(c->last_submit.pre_fence);
gk20a_fence_put(c->last_submit.post_fence);
c->last_submit.pre_fence = pre_fence;
c->last_submit.post_fence = post_fence;
if (fence_out)
*fence_out = gk20a_fence_get(post_fence);
/* TODO! Check for errors... */
gk20a_channel_add_job(c, &pre_fence, &post_fence);
gk20a_channel_add_job(c, pre_fence, post_fence);
c->cmds_pending = true;
gk20a_bar1_writel(g,
@@ -1672,8 +1678,8 @@ int gk20a_submit_channel_gpfifo(struct channel_gk20a *c,
c->hw_chid,
num_entries,
flags,
fence ? fence->syncpt_id : 0,
fence ? fence->value : 0);
post_fence->syncpt_id,
post_fence->syncpt_value);
gk20a_dbg_info("post-submit put %d, get %d, size %d",
c->gpfifo.put, c->gpfifo.get, c->gpfifo.entry_num);
@@ -1685,8 +1691,8 @@ clean_up:
gk20a_err(d, "fail");
free_priv_cmdbuf(c, wait_cmd);
free_priv_cmdbuf(c, incr_cmd);
gk20a_channel_fence_close(&pre_fence);
gk20a_channel_fence_close(&post_fence);
gk20a_fence_put(pre_fence);
gk20a_fence_put(post_fence);
gk20a_idle(g->dev);
return err;
}
@@ -1719,7 +1725,7 @@ int gk20a_init_channel_support(struct gk20a *g, u32 chid)
int gk20a_channel_finish(struct channel_gk20a *ch, unsigned long timeout)
{
int err = 0;
struct gk20a_channel_fence *fence = &ch->last_submit.post_fence;
struct gk20a_fence *fence = ch->last_submit.post_fence;
if (!ch->cmds_pending)
return 0;
@@ -1728,26 +1734,25 @@ int gk20a_channel_finish(struct channel_gk20a *ch, unsigned long timeout)
if (ch->has_timedout)
return -ETIMEDOUT;
if (!(fence->valid && fence->wfi) && ch->obj_class != KEPLER_C) {
if (!(fence && fence->wfi) && ch->obj_class != KEPLER_C) {
gk20a_dbg_fn("issuing wfi, incr to finish the channel");
err = gk20a_channel_submit_wfi(ch);
fence = ch->last_submit.post_fence;
}
if (err)
return err;
BUG_ON(!(fence->valid && fence->wfi) && ch->obj_class != KEPLER_C);
BUG_ON(!(fence && fence->wfi) && ch->obj_class != KEPLER_C);
gk20a_dbg_fn("waiting for channel to finish thresh:%d sema:%p",
fence->thresh, fence->semaphore);
fence->syncpt_value, fence->semaphore);
if (ch->sync) {
err = ch->sync->wait_cpu(ch->sync, fence, timeout);
err = gk20a_fence_wait(fence, timeout);
if (WARN_ON(err))
dev_warn(dev_from_gk20a(ch->g),
"timed out waiting for gk20a channel to finish");
else
ch->cmds_pending = false;
}
return err;
}
@@ -2014,6 +2019,7 @@ static int gk20a_ioctl_channel_submit_gpfifo(
struct channel_gk20a *ch,
struct nvhost_submit_gpfifo_args *args)
{
struct gk20a_fence *fence_out;
void *gpfifo;
u32 size;
int ret = 0;
@@ -2036,7 +2042,26 @@ static int gk20a_ioctl_channel_submit_gpfifo(
}
ret = gk20a_submit_channel_gpfifo(ch, gpfifo, args->num_entries,
&args->fence, args->flags);
args->flags, &args->fence,
&fence_out);
if (ret)
goto clean_up;
/* Convert fence_out to something we can pass back to user space. */
if (args->flags & NVHOST_SUBMIT_GPFIFO_FLAGS_FENCE_GET) {
if (args->flags & NVHOST_SUBMIT_GPFIFO_FLAGS_SYNC_FENCE) {
int fd = gk20a_fence_install_fd(fence_out);
if (fd < 0)
ret = fd;
else
args->fence.syncpt_id = fd;
} else {
args->fence.syncpt_id = fence_out->syncpt_id;
args->fence.value = fence_out->syncpt_value;
}
}
gk20a_fence_put(fence_out);
clean_up:
kfree(gpfifo);

View File

@@ -29,6 +29,7 @@
struct gk20a;
struct gr_gk20a;
struct dbg_session_gk20a;
struct gk20a_fence;
#include "channel_sync_gk20a.h"
@@ -68,8 +69,8 @@ struct channel_ctx_gk20a {
struct channel_gk20a_job {
struct mapped_buffer_node **mapped_buffers;
int num_mapped_buffers;
struct gk20a_channel_fence pre_fence;
struct gk20a_channel_fence post_fence;
struct gk20a_fence *pre_fence;
struct gk20a_fence *post_fence;
struct list_head list;
};
@@ -117,8 +118,9 @@ struct channel_gk20a {
bool cmds_pending;
struct {
struct gk20a_channel_fence pre_fence;
struct gk20a_channel_fence post_fence;
/* These fences should be accessed with submit_lock held. */
struct gk20a_fence *pre_fence;
struct gk20a_fence *post_fence;
} last_submit;
void (*remove_support)(struct channel_gk20a *);
@@ -184,8 +186,9 @@ void channel_gk20a_unbind(struct channel_gk20a *ch_gk20a);
int gk20a_submit_channel_gpfifo(struct channel_gk20a *c,
struct nvhost_gpfifo *gpfifo,
u32 num_entries,
u32 flags,
struct nvhost_fence *fence,
u32 flags);
struct gk20a_fence **fence_out);
int gk20a_alloc_channel_gpfifo(struct channel_gk20a *c,
struct nvhost_alloc_gpfifo_args *args);

View File

@@ -19,6 +19,7 @@
#include "channel_sync_gk20a.h"
#include "gk20a.h"
#include "fence_gk20a.h"
#include "semaphore_gk20a.h"
#include "sync_gk20a.h"
#include "mm_gk20a.h"
@@ -52,33 +53,9 @@ static void add_wait_cmd(u32 *ptr, u32 id, u32 thresh)
ptr[3] = (id << 8) | 0x10;
}
int gk20a_channel_syncpt_wait_cpu(struct gk20a_channel_sync *s,
struct gk20a_channel_fence *fence,
int timeout)
{
struct gk20a_channel_syncpt *sp =
container_of(s, struct gk20a_channel_syncpt, ops);
if (!fence->valid)
return 0;
return nvhost_syncpt_wait_timeout_ext(
sp->host1x_pdev, sp->id, fence->thresh,
timeout, NULL, NULL);
}
bool gk20a_channel_syncpt_is_expired(struct gk20a_channel_sync *s,
struct gk20a_channel_fence *fence)
{
struct gk20a_channel_syncpt *sp =
container_of(s, struct gk20a_channel_syncpt, ops);
if (!fence->valid)
return true;
return nvhost_syncpt_is_expired_ext(sp->host1x_pdev, sp->id,
fence->thresh);
}
int gk20a_channel_syncpt_wait_syncpt(struct gk20a_channel_sync *s, u32 id,
u32 thresh, struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence)
struct gk20a_fence **fence)
{
struct gk20a_channel_syncpt *sp =
container_of(s, struct gk20a_channel_syncpt, ops);
@@ -103,13 +80,13 @@ int gk20a_channel_syncpt_wait_syncpt(struct gk20a_channel_sync *s, u32 id,
add_wait_cmd(&wait_cmd->ptr[0], id, thresh);
*entry = wait_cmd;
fence->valid = false;
*fence = NULL;
return 0;
}
int gk20a_channel_syncpt_wait_fd(struct gk20a_channel_sync *s, int fd,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence)
struct gk20a_fence **fence)
{
#ifdef CONFIG_SYNC
int i;
@@ -164,7 +141,7 @@ int gk20a_channel_syncpt_wait_fd(struct gk20a_channel_sync *s, int fd,
sync_fence_put(sync_fence);
*entry = wait_cmd;
fence->valid = false;
*fence = NULL;
return 0;
#else
return -ENODEV;
@@ -181,7 +158,7 @@ static int __gk20a_channel_syncpt_incr(struct gk20a_channel_sync *s,
bool gfx_class, bool wfi_cmd,
bool register_irq,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence)
struct gk20a_fence **fence)
{
u32 thresh;
int incr_cmd_size;
@@ -253,16 +230,15 @@ static int __gk20a_channel_syncpt_incr(struct gk20a_channel_sync *s,
WARN(err, "failed to set submit complete interrupt");
}
fence->thresh = thresh;
fence->valid = true;
fence->wfi = wfi_cmd;
*fence = gk20a_fence_from_syncpt(sp->host1x_pdev, sp->id, thresh,
wfi_cmd);
*entry = incr_cmd;
return 0;
}
int gk20a_channel_syncpt_incr_wfi(struct gk20a_channel_sync *s,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence)
struct gk20a_fence **fence)
{
return __gk20a_channel_syncpt_incr(s,
false /* use host class */,
@@ -273,7 +249,7 @@ int gk20a_channel_syncpt_incr_wfi(struct gk20a_channel_sync *s,
int gk20a_channel_syncpt_incr(struct gk20a_channel_sync *s,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence)
struct gk20a_fence **fence)
{
struct gk20a_channel_syncpt *sp =
container_of(s, struct gk20a_channel_syncpt, ops);
@@ -286,51 +262,23 @@ int gk20a_channel_syncpt_incr(struct gk20a_channel_sync *s,
entry, fence);
}
int gk20a_channel_syncpt_incr_user_syncpt(struct gk20a_channel_sync *s,
int gk20a_channel_syncpt_incr_user(struct gk20a_channel_sync *s,
int wait_fence_fd,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence,
bool wfi,
u32 *id, u32 *thresh)
struct gk20a_fence **fence,
bool wfi)
{
struct gk20a_channel_syncpt *sp =
container_of(s, struct gk20a_channel_syncpt, ops);
/* Need to do 'host incr + wfi' or 'gfx incr' since we return the fence
* to user space. */
int err = __gk20a_channel_syncpt_incr(s,
return __gk20a_channel_syncpt_incr(s,
wfi &&
sp->c->obj_class == KEPLER_C /* use gfx class? */,
wfi &&
sp->c->obj_class != KEPLER_C /* wfi if host class */,
true /* register irq */,
entry, fence);
if (err)
return err;
*id = sp->id;
*thresh = fence->thresh;
return 0;
}
int gk20a_channel_syncpt_incr_user_fd(struct gk20a_channel_sync *s,
int wait_fence_fd,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence,
bool wfi,
int *fd)
{
#ifdef CONFIG_SYNC
int err;
struct nvhost_ctrl_sync_fence_info pt;
struct gk20a_channel_syncpt *sp =
container_of(s, struct gk20a_channel_syncpt, ops);
err = gk20a_channel_syncpt_incr_user_syncpt(s, entry, fence, wfi,
&pt.id, &pt.thresh);
if (err)
return err;
return nvhost_sync_create_fence_fd(sp->host1x_pdev, &pt, 1,
"fence", fd);
#else
return -ENODEV;
#endif
}
void gk20a_channel_syncpt_set_min_eq_max(struct gk20a_channel_sync *s)
@@ -340,6 +288,12 @@ void gk20a_channel_syncpt_set_min_eq_max(struct gk20a_channel_sync *s)
nvhost_syncpt_set_min_eq_max_ext(sp->host1x_pdev, sp->id);
}
static void gk20a_channel_syncpt_signal_timeline(
struct gk20a_channel_sync *s)
{
/* Nothing to do. */
}
static void gk20a_channel_syncpt_destroy(struct gk20a_channel_sync *s)
{
struct gk20a_channel_syncpt *sp =
@@ -366,15 +320,13 @@ gk20a_channel_syncpt_create(struct channel_gk20a *c)
return NULL;
}
sp->ops.wait_cpu = gk20a_channel_syncpt_wait_cpu;
sp->ops.is_expired = gk20a_channel_syncpt_is_expired;
sp->ops.wait_syncpt = gk20a_channel_syncpt_wait_syncpt;
sp->ops.wait_fd = gk20a_channel_syncpt_wait_fd;
sp->ops.incr = gk20a_channel_syncpt_incr;
sp->ops.incr_wfi = gk20a_channel_syncpt_incr_wfi;
sp->ops.incr_user_syncpt = gk20a_channel_syncpt_incr_user_syncpt;
sp->ops.incr_user_fd = gk20a_channel_syncpt_incr_user_fd;
sp->ops.incr_user = gk20a_channel_syncpt_incr_user;
sp->ops.set_min_eq_max = gk20a_channel_syncpt_set_min_eq_max;
sp->ops.signal_timeline = gk20a_channel_syncpt_signal_timeline;
sp->ops.destroy = gk20a_channel_syncpt_destroy;
sp->ops.aggressive_destroy = true;
@@ -460,48 +412,10 @@ static int add_sema_cmd(u32 *ptr, u64 sema, u32 payload,
return i;
}
static int gk20a_channel_semaphore_wait_cpu(
struct gk20a_channel_sync *s,
struct gk20a_channel_fence *fence,
int timeout)
{
int remain;
struct gk20a_channel_semaphore *sp =
container_of(s, struct gk20a_channel_semaphore, ops);
if (!fence->valid || WARN_ON(!fence->semaphore))
return 0;
remain = wait_event_interruptible_timeout(
sp->c->semaphore_wq,
!gk20a_semaphore_is_acquired(fence->semaphore),
timeout);
if (remain == 0 && gk20a_semaphore_is_acquired(fence->semaphore))
return -ETIMEDOUT;
else if (remain < 0)
return remain;
return 0;
}
static bool gk20a_channel_semaphore_is_expired(
struct gk20a_channel_sync *s,
struct gk20a_channel_fence *fence)
{
bool expired;
struct gk20a_channel_semaphore *sp =
container_of(s, struct gk20a_channel_semaphore, ops);
if (!fence->valid || WARN_ON(!fence->semaphore))
return true;
expired = !gk20a_semaphore_is_acquired(fence->semaphore);
if (expired)
gk20a_sync_timeline_signal(sp->timeline);
return expired;
}
static int gk20a_channel_semaphore_wait_syncpt(
struct gk20a_channel_sync *s, u32 id,
u32 thresh, struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence)
struct gk20a_fence **fence)
{
struct gk20a_channel_semaphore *sema =
container_of(s, struct gk20a_channel_semaphore, ops);
@@ -513,7 +427,7 @@ static int gk20a_channel_semaphore_wait_syncpt(
static int gk20a_channel_semaphore_wait_fd(
struct gk20a_channel_sync *s, int fd,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence)
struct gk20a_fence **fence)
{
struct gk20a_channel_semaphore *sema =
container_of(s, struct gk20a_channel_semaphore, ops);
@@ -558,6 +472,11 @@ static int gk20a_channel_semaphore_wait_fd(
WARN_ON(written != wait_cmd->size);
sync_fence_wait_async(sync_fence, &w->waiter);
/* XXX - this fixes an actual bug, we need to hold a ref to this
semaphore while the job is in flight. */
*fence = gk20a_fence_from_semaphore(sema->timeline, w->sema,
&c->semaphore_wq,
NULL, false);
*entry = wait_cmd;
return 0;
fail:
@@ -575,8 +494,9 @@ fail:
static int __gk20a_channel_semaphore_incr(
struct gk20a_channel_sync *s, bool wfi_cmd,
struct sync_fence *dependency,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence)
struct gk20a_fence **fence)
{
u64 va;
int incr_cmd_size;
@@ -608,9 +528,9 @@ static int __gk20a_channel_semaphore_incr(
written = add_sema_cmd(incr_cmd->ptr, va, 1, false, wfi_cmd);
WARN_ON(written != incr_cmd_size);
fence->valid = true;
fence->wfi = wfi_cmd;
fence->semaphore = semaphore;
*fence = gk20a_fence_from_semaphore(sp->timeline, semaphore,
&c->semaphore_wq,
dependency, wfi_cmd);
*entry = incr_cmd;
return 0;
}
@@ -618,72 +538,54 @@ static int __gk20a_channel_semaphore_incr(
static int gk20a_channel_semaphore_incr_wfi(
struct gk20a_channel_sync *s,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence)
struct gk20a_fence **fence)
{
return __gk20a_channel_semaphore_incr(s,
true /* wfi */,
NULL,
entry, fence);
}
static int gk20a_channel_semaphore_incr(
struct gk20a_channel_sync *s,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence)
struct gk20a_fence **fence)
{
/* Don't put wfi cmd to this one since we're not returning
* a fence to user space. */
return __gk20a_channel_semaphore_incr(s, false /* no wfi */,
entry, fence);
NULL, entry, fence);
}
static int gk20a_channel_semaphore_incr_user_syncpt(
struct gk20a_channel_sync *s,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence,
bool wfi,
u32 *id, u32 *thresh)
{
struct gk20a_channel_semaphore *sema =
container_of(s, struct gk20a_channel_semaphore, ops);
struct device *dev = dev_from_gk20a(sema->c->g);
gk20a_err(dev, "trying to use syncpoint synchronization");
return -ENODEV;
}
static int gk20a_channel_semaphore_incr_user_fd(
static int gk20a_channel_semaphore_incr_user(
struct gk20a_channel_sync *s,
int wait_fence_fd,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence,
bool wfi,
int *fd)
struct gk20a_fence **fence,
bool wfi)
{
struct gk20a_channel_semaphore *sema =
container_of(s, struct gk20a_channel_semaphore, ops);
#ifdef CONFIG_SYNC
struct sync_fence *dependency = NULL;
int err;
err = __gk20a_channel_semaphore_incr(s, wfi,
entry, fence);
if (err)
return err;
if (wait_fence_fd >= 0) {
dependency = gk20a_sync_fence_fdget(wait_fence_fd);
if (!dependency)
return -EINVAL;
}
*fd = gk20a_sync_fence_create(sema->timeline, fence->semaphore,
dependency, "fence");
if (*fd < 0) {
err = __gk20a_channel_semaphore_incr(s, wfi, dependency,
entry, fence);
if (err) {
if (dependency)
sync_fence_put(dependency);
return *fd;
return err;
}
return 0;
#else
struct gk20a_channel_semaphore *sema =
container_of(s, struct gk20a_channel_semaphore, ops);
gk20a_err(dev_from_gk20a(sema->c->g),
"trying to use sync fds with CONFIG_SYNC disabled");
return -ENODEV;
@@ -695,6 +597,14 @@ static void gk20a_channel_semaphore_set_min_eq_max(struct gk20a_channel_sync *s)
/* Nothing to do. */
}
static void gk20a_channel_semaphore_signal_timeline(
struct gk20a_channel_sync *s)
{
struct gk20a_channel_semaphore *sp =
container_of(s, struct gk20a_channel_semaphore, ops);
gk20a_sync_timeline_signal(sp->timeline);
}
static void gk20a_channel_semaphore_destroy(struct gk20a_channel_sync *s)
{
struct gk20a_channel_semaphore *sema =
@@ -746,15 +656,13 @@ gk20a_channel_semaphore_create(struct channel_gk20a *c)
if (!sema->timeline)
goto clean_up;
#endif
sema->ops.wait_cpu = gk20a_channel_semaphore_wait_cpu;
sema->ops.is_expired = gk20a_channel_semaphore_is_expired;
sema->ops.wait_syncpt = gk20a_channel_semaphore_wait_syncpt;
sema->ops.wait_fd = gk20a_channel_semaphore_wait_fd;
sema->ops.incr = gk20a_channel_semaphore_incr;
sema->ops.incr_wfi = gk20a_channel_semaphore_incr_wfi;
sema->ops.incr_user_syncpt = gk20a_channel_semaphore_incr_user_syncpt;
sema->ops.incr_user_fd = gk20a_channel_semaphore_incr_user_fd;
sema->ops.incr_user = gk20a_channel_semaphore_incr_user;
sema->ops.set_min_eq_max = gk20a_channel_semaphore_set_min_eq_max;
sema->ops.signal_timeline = gk20a_channel_semaphore_signal_timeline;
sema->ops.destroy = gk20a_channel_semaphore_destroy;
/* Aggressively destroying the semaphore sync would cause overhead
@@ -775,26 +683,3 @@ struct gk20a_channel_sync *gk20a_channel_sync_create(struct channel_gk20a *c)
#endif
return gk20a_channel_semaphore_create(c);
}
static inline bool gk20a_channel_fence_is_closed(struct gk20a_channel_fence *f)
{
if (f->valid || f->semaphore)
return false;
return true;
}
void gk20a_channel_fence_close(struct gk20a_channel_fence *f)
{
if (f->semaphore)
gk20a_semaphore_put(f->semaphore);
memset(f, 0, sizeof(*f));
}
void gk20a_channel_fence_dup(struct gk20a_channel_fence *from,
struct gk20a_channel_fence *to)
{
WARN_ON(!gk20a_channel_fence_is_closed(to));
*to = *from;
if (to->semaphore)
gk20a_semaphore_get(to->semaphore);
}

View File

@@ -24,34 +24,28 @@ struct gk20a_channel_sync;
struct priv_cmd_entry;
struct channel_gk20a;
struct gk20a_semaphore;
struct gk20a_channel_fence {
bool valid;
bool wfi; /* was issued with preceding wfi */
u32 thresh; /* syncpoint fences only */
struct gk20a_semaphore *semaphore; /* semaphore fences only */
};
struct gk20a_fence;
struct gk20a_channel_sync {
/* CPU wait for a fence returned by incr_syncpt() or incr_fd(). */
int (*wait_cpu)(struct gk20a_channel_sync *s,
struct gk20a_channel_fence *fence,
int timeout);
/* Test whether a fence returned by incr_syncpt() or incr_fd() is
* expired. */
bool (*is_expired)(struct gk20a_channel_sync *s,
struct gk20a_channel_fence *fence);
/* Generate a gpu wait cmdbuf from syncpoint. */
/* Generate a gpu wait cmdbuf from syncpoint.
* Returns
* - a gpu cmdbuf that performs the wait when executed,
* - possibly a helper fence that the caller must hold until the
* cmdbuf is executed.
*/
int (*wait_syncpt)(struct gk20a_channel_sync *s, u32 id, u32 thresh,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence);
struct gk20a_fence **fence);
/* Generate a gpu wait cmdbuf from sync fd. */
/* Generate a gpu wait cmdbuf from sync fd.
* Returns
* - a gpu cmdbuf that performs the wait when executed,
* - possibly a helper fence that the caller must hold until the
* cmdbuf is executed.
*/
int (*wait_fd)(struct gk20a_channel_sync *s, int fd,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence);
struct gk20a_fence **fence);
/* Increment syncpoint/semaphore.
* Returns
@@ -60,7 +54,7 @@ struct gk20a_channel_sync {
*/
int (*incr)(struct gk20a_channel_sync *s,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence);
struct gk20a_fence **fence);
/* Increment syncpoint/semaphore, preceded by a wfi.
* Returns
@@ -69,38 +63,29 @@ struct gk20a_channel_sync {
*/
int (*incr_wfi)(struct gk20a_channel_sync *s,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence);
/* Increment syncpoint, so that the returned fence represents
* work completion (may need wfi) and can be returned to user space.
* Returns
* - a gpu cmdbuf that performs the increment when executed,
* - a fence that can be passed to wait_cpu() and is_expired(),
* - a syncpoint id/value pair that can be returned to user space.
*/
int (*incr_user_syncpt)(struct gk20a_channel_sync *s,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence,
bool wfi,
u32 *id, u32 *thresh);
struct gk20a_fence **fence);
/* Increment syncpoint/semaphore, so that the returned fence represents
* work completion (may need wfi) and can be returned to user space.
* Returns
* - a gpu cmdbuf that performs the increment when executed,
* - a fence that can be passed to wait_cpu() and is_expired(),
* - a sync fd that can be returned to user space.
* - a gk20a_fence that signals when the incr has happened.
*/
int (*incr_user_fd)(struct gk20a_channel_sync *s,
int (*incr_user)(struct gk20a_channel_sync *s,
int wait_fence_fd,
struct priv_cmd_entry **entry,
struct gk20a_channel_fence *fence,
bool wfi,
int *fd);
struct gk20a_fence **fence,
bool wfi);
/* Reset the channel syncpoint/semaphore. */
void (*set_min_eq_max)(struct gk20a_channel_sync *s);
/* Signals the sync timeline (if owned by the gk20a_channel_sync layer).
* This should be called when we notice that a gk20a_fence is
* expired. */
void (*signal_timeline)(struct gk20a_channel_sync *s);
/* flag to set sync destroy aggressiveness */
bool aggressive_destroy;
@@ -110,7 +95,4 @@ struct gk20a_channel_sync {
struct gk20a_channel_sync *gk20a_channel_sync_create(struct channel_gk20a *c);
void gk20a_channel_fence_close(struct gk20a_channel_fence *f);
void gk20a_channel_fence_dup(struct gk20a_channel_fence *from,
struct gk20a_channel_fence *to);
#endif

View File

@@ -0,0 +1,229 @@
/*
* drivers/video/tegra/host/gk20a/fence_gk20a.c
*
* GK20A Fences
*
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include "fence_gk20a.h"
#include <linux/gk20a.h>
#include <linux/file.h>
#include "gk20a.h"
#include "semaphore_gk20a.h"
#include "channel_gk20a.h"
#include "sync_gk20a.h"
#ifdef CONFIG_SYNC
#include "../../../staging/android/sync.h"
#endif
#ifdef CONFIG_TEGRA_GK20A
#include <linux/nvhost.h>
#endif
struct gk20a_fence_ops {
int (*wait)(struct gk20a_fence *, int timeout);
bool (*is_expired)(struct gk20a_fence *);
void *(*free)(struct kref *);
};
static void gk20a_fence_free(struct kref *ref)
{
struct gk20a_fence *f =
container_of(ref, struct gk20a_fence, ref);
#ifdef CONFIG_SYNC
if (f->sync_fence)
sync_fence_put(f->sync_fence);
#endif
if (f->semaphore)
gk20a_semaphore_put(f->semaphore);
kfree(f);
}
void gk20a_fence_put(struct gk20a_fence *f)
{
if (f)
kref_put(&f->ref, gk20a_fence_free);
}
struct gk20a_fence *gk20a_fence_get(struct gk20a_fence *f)
{
if (f)
kref_get(&f->ref);
return f;
}
int gk20a_fence_wait(struct gk20a_fence *f, int timeout)
{
return f->ops->wait(f, timeout);
}
bool gk20a_fence_is_expired(struct gk20a_fence *f)
{
return f->ops->is_expired(f);
}
int gk20a_fence_install_fd(struct gk20a_fence *f)
{
#ifdef CONFIG_SYNC
int fd;
if (!f->sync_fence)
return -EINVAL;
fd = get_unused_fd();
if (fd < 0)
return fd;
sync_fence_get(f->sync_fence);
sync_fence_install(f->sync_fence, fd);
return fd;
#else
return -ENODEV;
#endif
}
static struct gk20a_fence *alloc_fence(const struct gk20a_fence_ops *ops,
struct sync_fence *sync_fence, bool wfi)
{
struct gk20a_fence *f = kzalloc(sizeof(*f), GFP_KERNEL);
if (!f)
return NULL;
kref_init(&f->ref);
f->ops = ops;
f->sync_fence = sync_fence;
f->wfi = wfi;
f->syncpt_id = -1;
return f;
}
/* Fences that are backed by GPU semaphores: */
static int gk20a_semaphore_fence_wait(struct gk20a_fence *f, int timeout)
{
int remain;
if (!gk20a_semaphore_is_acquired(f->semaphore))
return 0;
remain = wait_event_interruptible_timeout(
*f->semaphore_wq,
!gk20a_semaphore_is_acquired(f->semaphore),
timeout);
if (remain == 0 && gk20a_semaphore_is_acquired(f->semaphore))
return -ETIMEDOUT;
else if (remain < 0)
return remain;
return 0;
}
static bool gk20a_semaphore_fence_is_expired(struct gk20a_fence *f)
{
return !gk20a_semaphore_is_acquired(f->semaphore);
}
static const struct gk20a_fence_ops gk20a_semaphore_fence_ops = {
.wait = &gk20a_semaphore_fence_wait,
.is_expired = &gk20a_semaphore_fence_is_expired,
};
struct gk20a_fence *gk20a_fence_from_semaphore(
struct sync_timeline *timeline,
struct gk20a_semaphore *semaphore,
wait_queue_head_t *semaphore_wq,
struct sync_fence *dependency,
bool wfi)
{
struct gk20a_fence *f;
struct sync_fence *sync_fence = NULL;
#ifdef CONFIG_SYNC
sync_fence = gk20a_sync_fence_create(timeline, semaphore,
dependency, "fence");
if (!sync_fence)
return NULL;
#endif
f = alloc_fence(&gk20a_semaphore_fence_ops, sync_fence, wfi);
if (!f) {
#ifdef CONFIG_SYNC
sync_fence_put(sync_fence);
#endif
return NULL;
}
gk20a_semaphore_get(semaphore);
f->semaphore = semaphore;
f->semaphore_wq = semaphore_wq;
return f;
}
#ifdef CONFIG_TEGRA_GK20A
/* Fences that are backed by host1x syncpoints: */
static int gk20a_syncpt_fence_wait(struct gk20a_fence *f, int timeout)
{
return nvhost_syncpt_wait_timeout_ext(
f->host1x_pdev, f->syncpt_id, f->syncpt_value,
timeout, NULL, NULL);
}
static bool gk20a_syncpt_fence_is_expired(struct gk20a_fence *f)
{
return nvhost_syncpt_is_expired_ext(f->host1x_pdev, f->syncpt_id,
f->syncpt_value);
}
static const struct gk20a_fence_ops gk20a_syncpt_fence_ops = {
.wait = &gk20a_syncpt_fence_wait,
.is_expired = &gk20a_syncpt_fence_is_expired,
};
struct gk20a_fence *gk20a_fence_from_syncpt(struct platform_device *host1x_pdev,
u32 id, u32 value, bool wfi)
{
struct gk20a_fence *f;
struct sync_fence *sync_fence = NULL;
#ifdef CONFIG_SYNC
struct nvhost_ctrl_sync_fence_info pt = {
.id = id,
.thresh = value
};
sync_fence = nvhost_sync_create_fence(host1x_pdev, &pt, 1,
"fence");
if (!sync_fence)
return NULL;
#endif
f = alloc_fence(&gk20a_syncpt_fence_ops, sync_fence, wfi);
if (!f) {
#ifdef CONFIG_SYNC
sync_fence_put(sync_fence);
#endif
return NULL;
}
f->host1x_pdev = host1x_pdev;
f->syncpt_id = id;
f->syncpt_value = value;
return f;
}
#else
struct gk20a_fence *gk20a_fence_from_syncpt(struct platform_device *host1x_pdev,
u32 id, u32 value, bool wfi)
{
return NULL;
}
#endif

View File

@@ -0,0 +1,68 @@
/*
* drivers/video/tegra/host/gk20a/fence_gk20a.h
*
* GK20A Fences
*
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef _GK20A_FENCE_H_
#define _GK20A_FENCE_H_
#include <linux/types.h>
#include <linux/kref.h>
#include <linux/wait.h>
struct platform_device;
struct sync_timeline;
struct sync_fence;
struct gk20a_semaphore;
struct channel_gk20a;
struct gk20a_fence_ops;
struct gk20a_fence {
/* Valid for all fence types: */
struct kref ref;
bool wfi;
struct sync_fence *sync_fence;
const struct gk20a_fence_ops *ops;
/* Valid for fences created from semaphores: */
struct gk20a_semaphore *semaphore;
wait_queue_head_t *semaphore_wq;
/* Valid for fences created from syncpoints: */
struct platform_device *host1x_pdev;
u32 syncpt_id;
u32 syncpt_value;
};
/* Fences can be created from semaphores or syncpoint (id, value) pairs */
struct gk20a_fence *gk20a_fence_from_semaphore(
struct sync_timeline *timeline,
struct gk20a_semaphore *semaphore,
wait_queue_head_t *semaphore_wq,
struct sync_fence *dependency,
bool wfi);
struct gk20a_fence *gk20a_fence_from_syncpt(
struct platform_device *host1x_pdev,
u32 id, u32 value, bool wfi);
/* Fence operations */
void gk20a_fence_put(struct gk20a_fence *f);
struct gk20a_fence *gk20a_fence_get(struct gk20a_fence *f);
int gk20a_fence_wait(struct gk20a_fence *f, int timeout);
bool gk20a_fence_is_expired(struct gk20a_fence *f);
int gk20a_fence_install_fd(struct gk20a_fence *f);
#endif

View File

@@ -371,12 +371,11 @@ struct sync_timeline *gk20a_sync_timeline_create(
return &obj->obj;
}
int gk20a_sync_fence_create(struct sync_timeline *obj,
struct sync_fence *gk20a_sync_fence_create(struct sync_timeline *obj,
struct gk20a_semaphore *sema,
struct sync_fence *dependency,
const char *fmt, ...)
{
int fd;
char name[30];
va_list args;
struct sync_pt *pt;
@@ -385,7 +384,7 @@ int gk20a_sync_fence_create(struct sync_timeline *obj,
pt = gk20a_sync_pt_create_inst(timeline, sema, dependency);
if (pt == NULL)
return -ENOMEM;
return NULL;
va_start(args, fmt);
vsnprintf(name, sizeof(name), fmt, args);
@@ -394,15 +393,7 @@ int gk20a_sync_fence_create(struct sync_timeline *obj,
fence = sync_fence_create(name, pt);
if (fence == NULL) {
sync_pt_free(pt);
return -ENOMEM;
return NULL;
}
fd = get_unused_fd();
if (fd < 0) {
sync_fence_put(fence);
return fd;
}
sync_fence_install(fence, fd);
return fd;
return fence;
}

View File

@@ -29,7 +29,7 @@ struct gk20a_semaphore;
struct sync_timeline *gk20a_sync_timeline_create(const char *fmt, ...);
void gk20a_sync_timeline_destroy(struct sync_timeline *);
void gk20a_sync_timeline_signal(struct sync_timeline *);
int gk20a_sync_fence_create(struct sync_timeline *,
struct sync_fence *gk20a_sync_fence_create(struct sync_timeline *,
struct gk20a_semaphore *,
struct sync_fence *dependency,
const char *fmt, ...);