1155 lines
29 KiB
C
1155 lines
29 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2021 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/uaccess.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sync_file.h>
|
|
#include <linux/sched/clock.h>
|
|
|
|
#include "mdw_trace.h"
|
|
#include "mdw_cmn.h"
|
|
#include "mdw_mem.h"
|
|
#include "mdw_mem_pool.h"
|
|
#include "rv/mdw_rv_tag.h"
|
|
|
|
#define mdw_cmd_show(c, f) \
|
|
f("cmd(0x%llx/0x%llx/0x%llx/0x%llx/%d/%u)param(%u/%u/%u/%u/"\
|
|
"%u/%u/%u)subcmds(%u/%p/%u/%u)pid(%d/%d)(%d)\n", \
|
|
(uint64_t) c->mpriv, c->uid, c->kid, c->rvid, c->id, kref_read(&c->ref), \
|
|
c->priority, c->hardlimit, c->softlimit, \
|
|
c->power_save, c->power_plcy, c->power_dtime, \
|
|
c->app_type, c->num_subcmds, c->cmdbufs, \
|
|
c->num_cmdbufs, c->size_cmdbufs, \
|
|
c->pid, c->tgid, task_pid_nr(current))
|
|
|
|
static void mdw_cmd_cmdbuf_out(struct mdw_fpriv *mpriv, struct mdw_cmd *c)
|
|
{
|
|
struct mdw_subcmd_kinfo *ksubcmd = NULL;
|
|
unsigned int i = 0, j = 0;
|
|
|
|
if (!c->cmdbufs)
|
|
return;
|
|
|
|
/* flush cmdbufs and execinfos */
|
|
if (mdw_mem_invalidate(mpriv, c->cmdbufs))
|
|
mdw_drv_warn("s(0x%llx)c(0x%llx) invalidate cmdbufs(%u) fail\n",
|
|
(uint64_t)mpriv, c->kid, c->cmdbufs->size);
|
|
|
|
for (i = 0; i < c->num_subcmds; i++) {
|
|
ksubcmd = &c->ksubcmds[i];
|
|
for (j = 0; j < ksubcmd->info->num_cmdbufs; j++) {
|
|
if (!ksubcmd->ori_cbs[j]) {
|
|
mdw_drv_warn("no ori mems(%d-%d)\n", i, j);
|
|
continue;
|
|
}
|
|
|
|
/* cmdbuf copy out */
|
|
if (ksubcmd->cmdbufs[j].direction != MDW_CB_IN) {
|
|
mdw_trace_begin("apumdw:cbs_copy_out|cb:%u-%u size:%u type:%u",
|
|
i, j, ksubcmd->ori_cbs[j]->size, ksubcmd->info->type);
|
|
memcpy(ksubcmd->ori_cbs[j]->vaddr,
|
|
(void *)ksubcmd->kvaddrs[j],
|
|
ksubcmd->ori_cbs[j]->size);
|
|
mdw_trace_end();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mdw_cmd_put_cmdbufs(struct mdw_fpriv *mpriv, struct mdw_cmd *c)
|
|
{
|
|
struct mdw_subcmd_kinfo *ksubcmd = NULL;
|
|
unsigned int i = 0, j = 0;
|
|
|
|
if (!c->cmdbufs)
|
|
return;
|
|
|
|
mdw_trace_begin("apumdw:cbs_put|c:0x%llx num_subcmds:%u num_cmdbufs:%u",
|
|
c->kid, c->num_subcmds, c->num_cmdbufs);
|
|
|
|
for (i = 0; i < c->num_subcmds; i++) {
|
|
ksubcmd = &c->ksubcmds[i];
|
|
for (j = 0; j < ksubcmd->info->num_cmdbufs; j++) {
|
|
if (!ksubcmd->ori_cbs[j]) {
|
|
mdw_drv_warn("no ori mems(%d-%d)\n", i, j);
|
|
continue;
|
|
}
|
|
|
|
/* put mem */
|
|
mdw_mem_put(mpriv, ksubcmd->ori_cbs[j]);
|
|
ksubcmd->ori_cbs[j] = NULL;
|
|
}
|
|
}
|
|
|
|
mdw_mem_pool_free(c->cmdbufs);
|
|
c->cmdbufs = NULL;
|
|
|
|
mdw_trace_end();
|
|
}
|
|
|
|
static int mdw_cmd_get_cmdbufs(struct mdw_fpriv *mpriv, struct mdw_cmd *c)
|
|
{
|
|
unsigned int i = 0, j = 0, ofs = 0;
|
|
int ret = -EINVAL;
|
|
struct mdw_subcmd_kinfo *ksubcmd = NULL;
|
|
struct mdw_mem *m = NULL;
|
|
struct apusys_cmdbuf *acbs = NULL;
|
|
|
|
mdw_trace_begin("apumdw:cbs_get|c:0x%llx num_subcmds:%u num_cmdbufs:%u",
|
|
c->kid, c->num_subcmds, c->num_cmdbufs);
|
|
|
|
if (!c->size_cmdbufs || c->cmdbufs)
|
|
goto out;
|
|
|
|
c->cmdbufs = mdw_mem_pool_alloc(&mpriv->cmd_buf_pool, c->size_cmdbufs,
|
|
MDW_DEFAULT_ALIGN);
|
|
if (!c->cmdbufs) {
|
|
mdw_drv_err("s(0x%llx)c(0x%llx) alloc buffer for duplicate fail\n",
|
|
(uint64_t) mpriv, c->kid);
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
/* alloc mem for duplicated cmdbuf */
|
|
for (i = 0; i < c->num_subcmds; i++) {
|
|
mdw_cmd_debug("sc(0x%llx-%u) #cmdbufs(%u)\n",
|
|
c->kid, i, c->ksubcmds[i].info->num_cmdbufs);
|
|
|
|
acbs = kcalloc(c->ksubcmds[i].info->num_cmdbufs, sizeof(*acbs), GFP_KERNEL);
|
|
if (!acbs)
|
|
goto free_cmdbufs;
|
|
|
|
ksubcmd = &c->ksubcmds[i];
|
|
for (j = 0; j < ksubcmd->info->num_cmdbufs; j++) {
|
|
/* calc align */
|
|
if (ksubcmd->cmdbufs[j].align)
|
|
ofs = MDW_ALIGN(ofs, ksubcmd->cmdbufs[j].align);
|
|
else
|
|
ofs = MDW_ALIGN(ofs, MDW_DEFAULT_ALIGN);
|
|
|
|
mdw_cmd_debug("sc(0x%llx-%u) cb#%u offset(%u)\n",
|
|
c->kid, i, j, ofs);
|
|
|
|
/* get mem from handle */
|
|
m = mdw_mem_get(mpriv, ksubcmd->cmdbufs[j].handle);
|
|
if (!m) {
|
|
mdw_drv_err("sc(0x%llx-%u) cb#%u(%llu) get fail\n",
|
|
c->kid, i, j,
|
|
ksubcmd->cmdbufs[j].handle);
|
|
ret = -EINVAL;
|
|
goto free_cmdbufs;
|
|
}
|
|
/* check mem boundary */
|
|
if (m->vaddr == NULL ||
|
|
ksubcmd->cmdbufs[j].size != m->size) {
|
|
mdw_drv_err("sc(0x%llx-%u) cb#%u invalid range(%p/%u/%u)\n",
|
|
c->kid, i, j, m->vaddr,
|
|
ksubcmd->cmdbufs[j].size,
|
|
m->size);
|
|
ret = -EINVAL;
|
|
goto free_cmdbufs;
|
|
}
|
|
|
|
/* cmdbuf copy in */
|
|
if (ksubcmd->cmdbufs[j].direction != MDW_CB_OUT) {
|
|
mdw_trace_begin("apumdw:cbs_copy_in|cb:%u-%u size:%u type:%u",
|
|
i, j,
|
|
ksubcmd->cmdbufs[j].size,
|
|
ksubcmd->info->type);
|
|
memcpy(c->cmdbufs->vaddr + ofs,
|
|
m->vaddr,
|
|
ksubcmd->cmdbufs[j].size);
|
|
mdw_trace_end();
|
|
}
|
|
|
|
/* record buffer info */
|
|
ksubcmd->ori_cbs[j] = m;
|
|
ksubcmd->kvaddrs[j] =
|
|
(uint64_t)(c->cmdbufs->vaddr + ofs);
|
|
ksubcmd->daddrs[j] =
|
|
(uint64_t)(c->cmdbufs->device_va + ofs);
|
|
ofs += ksubcmd->cmdbufs[j].size;
|
|
|
|
mdw_cmd_debug("sc(0x%llx-%u) cb#%u (0x%llx/0x%llx/%u)\n",
|
|
c->kid, i, j,
|
|
ksubcmd->kvaddrs[j],
|
|
ksubcmd->daddrs[j],
|
|
ksubcmd->cmdbufs[j].size);
|
|
|
|
acbs[j].kva = (void *)ksubcmd->kvaddrs[j];
|
|
acbs[j].size = ksubcmd->cmdbufs[j].size;
|
|
}
|
|
|
|
mdw_trace_begin("apumdw:dev validation|c:0x%llx type:%u",
|
|
c->kid, ksubcmd->info->type);
|
|
ret = mdw_dev_validation(mpriv, ksubcmd->info->type,
|
|
c, acbs, ksubcmd->info->num_cmdbufs);
|
|
mdw_trace_end();
|
|
kfree(acbs);
|
|
acbs = NULL;
|
|
if (ret) {
|
|
mdw_drv_err("sc(0x%llx-%u) dev(%u) validate cb(%u) fail(%d)\n",
|
|
c->kid, i, ksubcmd->info->type, ksubcmd->info->num_cmdbufs, ret);
|
|
goto free_cmdbufs;
|
|
}
|
|
}
|
|
/* flush cmdbufs */
|
|
if (mdw_mem_flush(mpriv, c->cmdbufs))
|
|
mdw_drv_warn("s(0x%llx) c(0x%llx) flush cmdbufs(%u) fail\n",
|
|
(uint64_t)mpriv, c->kid, c->cmdbufs->size);
|
|
|
|
ret = 0;
|
|
goto out;
|
|
|
|
free_cmdbufs:
|
|
mdw_cmd_put_cmdbufs(mpriv, c);
|
|
kfree(acbs);
|
|
out:
|
|
mdw_cmd_debug("ret(%d)\n", ret);
|
|
mdw_trace_end();
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int mdw_cmd_create_infos(struct mdw_fpriv *mpriv,
|
|
struct mdw_cmd *c)
|
|
{
|
|
unsigned int i = 0, j = 0, total_size = 0, tmp_size = 0;
|
|
struct mdw_subcmd_exec_info *sc_einfo = NULL;
|
|
int ret = -ENOMEM;
|
|
|
|
c->einfos = c->exec_infos->vaddr;
|
|
if (!c->einfos) {
|
|
mdw_drv_err("invalid exec info addr\n");
|
|
return -EINVAL;
|
|
} else {
|
|
/* clear run infos for return */
|
|
memset(c->exec_infos->vaddr, 0, c->exec_infos->size);
|
|
}
|
|
sc_einfo = &c->einfos->sc;
|
|
|
|
for (i = 0; i < c->num_subcmds; i++) {
|
|
c->ksubcmds[i].info = &c->subcmds[i];
|
|
mdw_cmd_debug("subcmd(%u)(%u/%u/%u/%u/%u/%u/%u/%u/0x%llx)\n",
|
|
i, c->subcmds[i].type,
|
|
c->subcmds[i].suggest_time, c->subcmds[i].vlm_usage,
|
|
c->subcmds[i].vlm_ctx_id, c->subcmds[i].vlm_force,
|
|
c->subcmds[i].boost, c->subcmds[i].pack_id,
|
|
c->subcmds[i].num_cmdbufs, c->subcmds[i].cmdbufs);
|
|
|
|
/* kva for oroginal buffer */
|
|
c->ksubcmds[i].ori_cbs = kcalloc(c->subcmds[i].num_cmdbufs,
|
|
sizeof(c->ksubcmds[i].ori_cbs), GFP_KERNEL);
|
|
if (!c->ksubcmds[i].ori_cbs)
|
|
goto free_cmdbufs;
|
|
|
|
/* record kva for duplicate */
|
|
c->ksubcmds[i].kvaddrs = kcalloc(c->subcmds[i].num_cmdbufs,
|
|
sizeof(*c->ksubcmds[i].kvaddrs), GFP_KERNEL);
|
|
if (!c->ksubcmds[i].kvaddrs)
|
|
goto free_cmdbufs;
|
|
|
|
/* record dva for cmdbufs */
|
|
c->ksubcmds[i].daddrs = kcalloc(c->subcmds[i].num_cmdbufs,
|
|
sizeof(*c->ksubcmds[i].daddrs), GFP_KERNEL);
|
|
if (!c->ksubcmds[i].daddrs)
|
|
goto free_cmdbufs;
|
|
|
|
/* allocate for subcmd cmdbuf */
|
|
c->ksubcmds[i].cmdbufs = kcalloc(c->subcmds[i].num_cmdbufs,
|
|
sizeof(*c->ksubcmds[i].cmdbufs), GFP_KERNEL);
|
|
if (!c->ksubcmds[i].cmdbufs)
|
|
goto free_cmdbufs;
|
|
|
|
/* copy cmdbuf info */
|
|
if (copy_from_user(c->ksubcmds[i].cmdbufs,
|
|
(void __user *)c->subcmds[i].cmdbufs,
|
|
c->subcmds[i].num_cmdbufs *
|
|
sizeof(*c->ksubcmds[i].cmdbufs))) {
|
|
goto free_cmdbufs;
|
|
}
|
|
|
|
c->ksubcmds[i].sc_einfo = &sc_einfo[i];
|
|
|
|
/* accumulate cmdbuf size with alignment */
|
|
for (j = 0; j < c->subcmds[i].num_cmdbufs; j++) {
|
|
c->num_cmdbufs++;
|
|
/* alignment */
|
|
if (c->ksubcmds[i].cmdbufs[j].align) {
|
|
tmp_size = MDW_ALIGN(total_size,
|
|
c->ksubcmds[i].cmdbufs[j].align);
|
|
} else {
|
|
tmp_size = MDW_ALIGN(total_size, MDW_DEFAULT_ALIGN);
|
|
}
|
|
if (tmp_size < total_size) {
|
|
mdw_drv_err("cmdbuf(%u,%u) size align overflow(%u/%u/%u)\n",
|
|
i, j, total_size,
|
|
c->ksubcmds[i].cmdbufs[j].align, tmp_size);
|
|
goto free_cmdbufs;
|
|
}
|
|
total_size = tmp_size;
|
|
|
|
/* accumulator */
|
|
tmp_size = total_size + c->ksubcmds[i].cmdbufs[j].size;
|
|
if (tmp_size < total_size) {
|
|
mdw_drv_err("cmdbuf(%u,%u) size overflow(%u/%u/%u)\n",
|
|
i, j, total_size,
|
|
c->ksubcmds[i].cmdbufs[j].size, tmp_size);
|
|
goto free_cmdbufs;
|
|
}
|
|
total_size = tmp_size;
|
|
}
|
|
}
|
|
c->size_cmdbufs = total_size;
|
|
|
|
mdw_cmd_debug("sc(0x%llx) cb_num(%u) total size(%u)\n",
|
|
c->kid, c->num_cmdbufs, c->size_cmdbufs);
|
|
|
|
ret = mdw_cmd_get_cmdbufs(mpriv, c);
|
|
if (ret)
|
|
goto free_cmdbufs;
|
|
|
|
goto out;
|
|
|
|
free_cmdbufs:
|
|
for (i = 0; i < c->num_subcmds; i++) {
|
|
/* free dvaddrs */
|
|
kfree(c->ksubcmds[i].daddrs);
|
|
c->ksubcmds[i].daddrs = NULL;
|
|
|
|
/* free kvaddrs */
|
|
kfree(c->ksubcmds[i].kvaddrs);
|
|
c->ksubcmds[i].kvaddrs = NULL;
|
|
|
|
/* free ori kvas */
|
|
kfree(c->ksubcmds[i].ori_cbs);
|
|
c->ksubcmds[i].ori_cbs = NULL;
|
|
|
|
/* free cmdbufs */
|
|
kfree(c->ksubcmds[i].cmdbufs);
|
|
c->ksubcmds[i].cmdbufs = NULL;
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static void mdw_cmd_delete_infos(struct mdw_fpriv *mpriv, struct mdw_cmd *c)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
mdw_cmd_put_cmdbufs(mpriv, c);
|
|
|
|
for (i = 0; i < c->num_subcmds; i++) {
|
|
/* free dvaddrs */
|
|
kfree(c->ksubcmds[i].daddrs);
|
|
c->ksubcmds[i].daddrs = NULL;
|
|
|
|
/* free kvaddrs */
|
|
kfree(c->ksubcmds[i].kvaddrs);
|
|
c->ksubcmds[i].kvaddrs = NULL;
|
|
|
|
/* free ori kvas */
|
|
kfree(c->ksubcmds[i].ori_cbs);
|
|
c->ksubcmds[i].ori_cbs = NULL;
|
|
|
|
/* free cmdbufs */
|
|
kfree(c->ksubcmds[i].cmdbufs);
|
|
c->ksubcmds[i].cmdbufs = NULL;
|
|
}
|
|
}
|
|
|
|
void mdw_cmd_mpriv_release(struct mdw_fpriv *mpriv)
|
|
{
|
|
struct mdw_cmd *c = NULL;
|
|
uint32_t id = 0;
|
|
|
|
if (!atomic_read(&mpriv->active) && !atomic_read(&mpriv->active_cmds)) {
|
|
mdw_flw_debug("s(0x%llx) release cmd\n", (uint64_t)mpriv);
|
|
idr_for_each_entry(&mpriv->cmds, c, id) {
|
|
idr_remove(&mpriv->cmds, id);
|
|
mdw_cmd_delete(c);
|
|
}
|
|
mdw_flw_debug("s(0x%llx) release mem\n", (uint64_t)mpriv);
|
|
mutex_lock(&mpriv->mdev->mctl_mtx);
|
|
mdw_mem_mpriv_release(mpriv);
|
|
mutex_unlock(&mpriv->mdev->mctl_mtx);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------
|
|
static uint64_t mdw_fence_ctx_alloc(struct mdw_device *mdev)
|
|
{
|
|
uint64_t idx = 0, ctx = 0;
|
|
|
|
mutex_lock(&mdev->f_mtx);
|
|
idx = find_first_zero_bit(mdev->fence_ctx_mask, mdev->num_fence_ctx);
|
|
if (idx >= mdev->num_fence_ctx) {
|
|
ctx = dma_fence_context_alloc(1);
|
|
mdw_drv_warn("no free fence ctx(%llu), alloc ctx(%llu)\n", idx, ctx);
|
|
} else {
|
|
set_bit(idx, mdev->fence_ctx_mask);
|
|
ctx = mdev->base_fence_ctx + idx;
|
|
}
|
|
mutex_unlock(&mdev->f_mtx);
|
|
mdw_cmd_debug("alloc fence ctx(%llu) idx(%d) base(%llu)\n",
|
|
ctx, idx, mdev->base_fence_ctx);
|
|
|
|
return ctx;
|
|
}
|
|
|
|
static void mdw_fence_ctx_free(struct mdw_device *mdev, uint64_t ctx)
|
|
{
|
|
int idx = 0;
|
|
|
|
idx = ctx - mdev->base_fence_ctx;
|
|
if (idx < 0 || idx >= mdev->num_fence_ctx) {
|
|
mdw_cmd_debug("out of range ctx(%llu/%llu)\n", ctx, mdev->base_fence_ctx);
|
|
return;
|
|
}
|
|
|
|
mutex_lock(&mdev->f_mtx);
|
|
if (!test_bit(idx, mdev->fence_ctx_mask))
|
|
mdw_drv_warn("ctx state conflict(%d)\n", idx);
|
|
else
|
|
clear_bit(idx, mdev->fence_ctx_mask);
|
|
mutex_unlock(&mdev->f_mtx);
|
|
}
|
|
|
|
static const char *mdw_fence_get_driver_name(struct dma_fence *fence)
|
|
{
|
|
return "apu_mdw";
|
|
}
|
|
|
|
static const char *mdw_fence_get_timeline_name(struct dma_fence *fence)
|
|
{
|
|
struct mdw_fence *f =
|
|
container_of(fence, struct mdw_fence, base_fence);
|
|
|
|
return f->name;
|
|
}
|
|
|
|
static bool mdw_fence_enable_signaling(struct dma_fence *fence)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void mdw_fence_release(struct dma_fence *fence)
|
|
{
|
|
struct mdw_fence *mf =
|
|
container_of(fence, struct mdw_fence, base_fence);
|
|
|
|
mdw_flw_debug("fence release, fence(%s/%llu-%llu)\n",
|
|
mf->name, mf->base_fence.context, mf->base_fence.seqno);
|
|
mdw_fence_ctx_free(mf->mdev, mf->base_fence.context);
|
|
kfree(mf);
|
|
}
|
|
|
|
static const struct dma_fence_ops mdw_fence_ops = {
|
|
.get_driver_name = mdw_fence_get_driver_name,
|
|
.get_timeline_name = mdw_fence_get_timeline_name,
|
|
.enable_signaling = mdw_fence_enable_signaling,
|
|
.wait = dma_fence_default_wait,
|
|
.release = mdw_fence_release,
|
|
};
|
|
|
|
//--------------------------------------------
|
|
static int mdw_fence_init(struct mdw_cmd *c, int fd)
|
|
{
|
|
int ret = 0;
|
|
struct mdw_device *mdev = c->mpriv->mdev;
|
|
|
|
c->fence = kzalloc(sizeof(*c->fence), GFP_KERNEL);
|
|
if (!c->fence)
|
|
return -ENOMEM;
|
|
|
|
if (snprintf(c->fence->name, sizeof(c->fence->name), "%d:%s", fd, c->comm) <= 0)
|
|
mdw_drv_warn("set fance name fail\n");
|
|
c->fence->mdev = c->mpriv->mdev;
|
|
spin_lock_init(&c->fence->lock);
|
|
dma_fence_init(&c->fence->base_fence, &mdw_fence_ops,
|
|
&c->fence->lock, mdw_fence_ctx_alloc(mdev),
|
|
atomic_add_return(1, &c->mpriv->exec_seqno));
|
|
|
|
mdw_flw_debug("fence init, c(0x%llx) fence(%s/%llu-%llu)\n",
|
|
(uint64_t)c, c->fence->name, c->fence->base_fence.context,
|
|
c->fence->base_fence.seqno);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void mdw_cmd_unvoke_map(struct mdw_cmd *c)
|
|
{
|
|
struct mdw_cmd_map_invoke *cm_invoke = NULL, *tmp = NULL;
|
|
|
|
list_for_each_entry_safe(cm_invoke, tmp, &c->map_invokes, c_node) {
|
|
list_del(&cm_invoke->c_node);
|
|
mdw_cmd_debug("s(0x%llx)c(0x%llx) unvoke m(0x%llx/%u)\n",
|
|
(uint64_t)c->mpriv, (uint64_t)c,
|
|
cm_invoke->map->m->device_va,
|
|
cm_invoke->map->m->dva_size);
|
|
cm_invoke->map->put(cm_invoke->map);
|
|
kfree(cm_invoke);
|
|
}
|
|
}
|
|
|
|
int mdw_cmd_invoke_map(struct mdw_cmd *c, struct mdw_mem_map *map)
|
|
{
|
|
struct mdw_cmd_map_invoke *cm_invoke = NULL;
|
|
|
|
if (map == NULL)
|
|
return -EINVAL;
|
|
|
|
/* query */
|
|
list_for_each_entry(cm_invoke, &c->map_invokes, c_node) {
|
|
/* already invoked */
|
|
if (cm_invoke->map == map)
|
|
return 0;
|
|
}
|
|
|
|
cm_invoke = kzalloc(sizeof(*cm_invoke), GFP_KERNEL);
|
|
if (cm_invoke == NULL)
|
|
return -ENOMEM;
|
|
|
|
map->get(map);
|
|
cm_invoke->map = map;
|
|
list_add_tail(&cm_invoke->c_node, &c->map_invokes);
|
|
mdw_cmd_debug("s(0x%llx)c(0x%llx) invoke m(0x%llx/%u)\n",
|
|
(uint64_t)c->mpriv, (uint64_t)c, map->m->device_va, map->m->dva_size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mdw_cmd_release(struct kref *ref)
|
|
{
|
|
struct mdw_cmd *c =
|
|
container_of(ref, struct mdw_cmd, ref);
|
|
struct mdw_fpriv *mpriv = c->mpriv;
|
|
|
|
mdw_cmd_show(c, mdw_drv_debug);
|
|
mdw_trace_begin("apumdw:cmd_release|c:0x%llx", c->kid);
|
|
if (c->del_internal)
|
|
c->del_internal(c);
|
|
mutex_lock(&mpriv->mdev->mctl_mtx);
|
|
mdw_cmd_unvoke_map(c);
|
|
mutex_unlock(&mpriv->mdev->mctl_mtx);
|
|
mdw_cmd_delete_infos(c->mpriv, c);
|
|
mdw_mem_put(c->mpriv, c->exec_infos);
|
|
kfree(c->adj_matrix);
|
|
kfree(c->ksubcmds);
|
|
kfree(c->subcmds);
|
|
kfree(c);
|
|
|
|
mpriv->put(mpriv);
|
|
mdw_trace_end();
|
|
}
|
|
|
|
void mdw_cmd_put(struct mdw_cmd *c)
|
|
{
|
|
kref_put(&c->ref, mdw_cmd_release);
|
|
}
|
|
|
|
static void mdw_cmd_get(struct mdw_cmd *c)
|
|
{
|
|
kref_get(&c->ref);
|
|
}
|
|
|
|
void mdw_cmd_delete(struct mdw_cmd *c)
|
|
{
|
|
mdw_cmd_show(c, mdw_drv_debug);
|
|
mdw_cmd_put(c);
|
|
}
|
|
|
|
static void mdw_cmd_delete_async(struct mdw_cmd *c)
|
|
{
|
|
struct mdw_device *mdev = c->mpriv->mdev;
|
|
|
|
/* add cmd list to delete */
|
|
mutex_lock(&mdev->c_mtx);
|
|
list_add_tail(&c->d_node, &mdev->d_cmds);
|
|
mutex_unlock(&mdev->c_mtx);
|
|
|
|
schedule_work(&mdev->c_wk);
|
|
}
|
|
|
|
static int mdw_cmd_sanity_check(struct mdw_cmd *c)
|
|
{
|
|
if (c->priority >= MDW_PRIORITY_MAX ||
|
|
c->num_subcmds > MDW_SUBCMD_MAX ||
|
|
c->num_links > c->num_subcmds) {
|
|
mdw_drv_err("s(0x%llx)cmd invalid(0x%llx/0x%llx)(%u/%u/%u)\n",
|
|
(uint64_t)c->mpriv, c->uid, c->kid,
|
|
c->priority, c->num_subcmds, c->num_links);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (c->exec_infos->size != sizeof(struct mdw_cmd_exec_info) +
|
|
c->num_subcmds * sizeof(struct mdw_subcmd_exec_info)) {
|
|
mdw_drv_err("s(0x%llx)cmd invalid(0x%llx/0x%llx) einfo(%u/%u)\n",
|
|
(uint64_t)c->mpriv, c->uid, c->kid,
|
|
c->exec_infos->size,
|
|
sizeof(struct mdw_cmd_exec_info) +
|
|
c->num_subcmds * sizeof(struct mdw_subcmd_exec_info));
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mdw_cmd_adj_check(struct mdw_cmd *c)
|
|
{
|
|
uint32_t i = 0, j = 0;
|
|
|
|
for (i = 0; i < c->num_subcmds; i++) {
|
|
for (j = 0; j < c->num_subcmds; j++) {
|
|
if (i == j) {
|
|
c->adj_matrix[i * c->num_subcmds + j] = 0;
|
|
continue;
|
|
}
|
|
|
|
if (i < j)
|
|
continue;
|
|
|
|
if (!c->adj_matrix[i * c->num_subcmds + j] ||
|
|
!c->adj_matrix[i + j * c->num_subcmds])
|
|
continue;
|
|
|
|
mdw_drv_err("s(0x%llx)c(0x%llx/0x%llx) adj matrix(%u/%u) fail\n",
|
|
(uint64_t)c->mpriv, c->uid, c->kid, i, j);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mdw_cmd_link_check(struct mdw_cmd *c)
|
|
{
|
|
uint32_t i = 0;
|
|
|
|
for (i = 0; i < c->num_links; i++) {
|
|
if (c->links[i].producer_idx > c->num_subcmds ||
|
|
c->links[i].consumer_idx > c->num_subcmds ||
|
|
!c->links[i].x || !c->links[i].y ||
|
|
!c->links[i].va) {
|
|
mdw_drv_err("link(%u) invalid(%u/%u)(%u/%u)(0x%llx)\n",
|
|
c->links[i].producer_idx,
|
|
c->links[i].consumer_idx,
|
|
c->links[i].x,
|
|
c->links[i].y,
|
|
c->links[i].va);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mdw_cmd_sc_sanity_check(struct mdw_cmd *c)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
/* subcmd info */
|
|
for (i = 0; i < c->num_subcmds; i++) {
|
|
if (c->subcmds[i].type >= MDW_DEV_MAX ||
|
|
c->subcmds[i].vlm_ctx_id >= MDW_SUBCMD_MAX ||
|
|
c->subcmds[i].boost > MDW_BOOST_MAX ||
|
|
c->subcmds[i].pack_id >= MDW_SUBCMD_MAX) {
|
|
mdw_drv_err("subcmd(%u) invalid (%u/%u/%u/%u)\n",
|
|
i, c->subcmds[i].type,
|
|
c->subcmds[i].boost,
|
|
c->subcmds[i].pack_id);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mdw_cmd_run(struct mdw_fpriv *mpriv, struct mdw_cmd *c)
|
|
{
|
|
struct mdw_device *mdev = mpriv->mdev;
|
|
struct dma_fence *f = &c->fence->base_fence;
|
|
int ret = 0;
|
|
|
|
mdw_cmd_show(c, mdw_cmd_debug);
|
|
|
|
c->start_ts = sched_clock();
|
|
ret = mdev->dev_funcs->run_cmd(mpriv, c);
|
|
if (ret) {
|
|
mdw_drv_err("s(0x%llx) run cmd(0x%llx) fail(%d)\n",
|
|
(uint64_t) c->mpriv, c->kid, ret);
|
|
dma_fence_set_error(f, ret);
|
|
if (dma_fence_signal(f)) {
|
|
mdw_drv_warn("c(0x%llx) signal fence fail\n", (uint64_t)c);
|
|
if (f->ops->get_timeline_name && f->ops->get_driver_name) {
|
|
mdw_drv_warn(" fence name(%s-%s)\n",
|
|
f->ops->get_driver_name(f), f->ops->get_timeline_name(f));
|
|
}
|
|
}
|
|
dma_fence_put(f);
|
|
} else {
|
|
mdw_flw_debug("s(0x%llx) cmd(0x%llx) run\n",
|
|
(uint64_t)c->mpriv, c->kid);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void mdw_cmd_check_rets(struct mdw_cmd *c, int ret)
|
|
{
|
|
uint32_t idx = 0, is_dma = 0, is_aps = 0;
|
|
DECLARE_BITMAP(tmp, 64);
|
|
|
|
memcpy(&tmp, &c->einfos->c.sc_rets, sizeof(c->einfos->c.sc_rets));
|
|
|
|
/* extract fail subcmd */
|
|
do {
|
|
idx = find_next_bit((unsigned long *)&tmp, c->num_subcmds, idx);
|
|
if (idx >= c->num_subcmds)
|
|
break;
|
|
|
|
mdw_drv_warn("sc(0x%llx-#%u) type(%u) softlimit(%u) boost(%u) fail\n",
|
|
c->kid, idx, c->subcmds[idx].type,
|
|
c->softlimit, c->subcmds[idx].boost);
|
|
switch (c->subcmds[idx].type) {
|
|
case APUSYS_DEVICE_EDMA:
|
|
is_dma++;
|
|
break;
|
|
case APUSYS_DEVICE_APS:
|
|
is_aps++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
idx++;
|
|
} while (idx < c->num_subcmds);
|
|
|
|
/* trigger exception if dma */
|
|
if (is_dma) {
|
|
dma_exception("dma exec fail:%s:ret(%d/0x%llx)pid(%d/%d)c(0x%llx)\n",
|
|
c->comm, ret, c->einfos->c.sc_rets,
|
|
c->pid, c->tgid, c->kid);
|
|
}
|
|
if (is_aps) {
|
|
aps_exception("aps exec fail:%s:ret(%d/0x%llx)pid(%d/%d)c(0x%llx)\n",
|
|
c->comm, ret, c->einfos->c.sc_rets,
|
|
c->pid, c->tgid, c->kid);
|
|
}
|
|
}
|
|
|
|
static int mdw_cmd_complete(struct mdw_cmd *c, int ret)
|
|
{
|
|
struct dma_fence *f = &c->fence->base_fence;
|
|
struct mdw_fpriv *mpriv = c->mpriv;
|
|
|
|
mdw_trace_begin("apumdw:cmd_complete|cmd:0x%llx/0x%llx", c->uid, c->kid);
|
|
mutex_lock(&c->mtx);
|
|
|
|
c->end_ts = sched_clock();
|
|
c->einfos->c.total_us = (c->end_ts - c->start_ts) / 1000;
|
|
mdw_flw_debug("s(0x%llx) c(%s/0x%llx/0x%llx/0x%llx) ret(%d) sc_rets(0x%llx) complete, pid(%d/%d)(%d)\n",
|
|
(uint64_t)mpriv, c->comm, c->uid, c->kid, c->rvid,
|
|
ret, c->einfos->c.sc_rets,
|
|
c->pid, c->tgid, task_pid_nr(current));
|
|
|
|
/* check subcmds return value */
|
|
if (c->einfos->c.sc_rets) {
|
|
if (!ret)
|
|
ret = -EIO;
|
|
|
|
mdw_cmd_check_rets(c, ret);
|
|
}
|
|
c->einfos->c.ret = ret;
|
|
|
|
if (ret) {
|
|
mdw_drv_err("s(0x%llx) c(%s/0x%llx/0x%llx/0x%llx) ret(%d/0x%llx) time(%llu) pid(%d/%d)\n",
|
|
(uint64_t)mpriv, c->comm, c->uid, c->kid, c->rvid,
|
|
ret, c->einfos->c.sc_rets,
|
|
c->einfos->c.total_us, c->pid, c->tgid);
|
|
dma_fence_set_error(f, ret);
|
|
|
|
if (mdw_debug_on(MDW_DBG_EXP))
|
|
mdw_exception("exec fail:%s:ret(%d/0x%llx)pid(%d/%d)\n",
|
|
c->comm, ret, c->einfos->c.sc_rets, c->pid, c->tgid);
|
|
} else {
|
|
mdw_flw_debug("s(0x%llx) c(%s/0x%llx/0x%llx/0x%llx) ret(%d/0x%llx) time(%llu) pid(%d/%d)\n",
|
|
(uint64_t)mpriv, c->comm, c->uid, c->kid, c->rvid,
|
|
ret, c->einfos->c.sc_rets,
|
|
c->einfos->c.total_us, c->pid, c->tgid);
|
|
}
|
|
|
|
mdw_cmd_cmdbuf_out(mpriv, c);
|
|
|
|
/* signal done */
|
|
c->fence = NULL;
|
|
atomic_dec(&c->is_running);
|
|
if (dma_fence_signal(f)) {
|
|
mdw_drv_warn("c(0x%llx) signal fence fail\n", (uint64_t)c);
|
|
if (f->ops->get_timeline_name && f->ops->get_driver_name) {
|
|
mdw_drv_warn(" fence name(%s-%s)\n",
|
|
f->ops->get_driver_name(f), f->ops->get_timeline_name(f));
|
|
}
|
|
}
|
|
dma_fence_put(f);
|
|
atomic_dec(&mpriv->active_cmds);
|
|
mutex_unlock(&c->mtx);
|
|
|
|
/* check mpriv to clean cmd */
|
|
mutex_lock(&mpriv->mtx);
|
|
mdw_cmd_mpriv_release(mpriv);
|
|
mutex_unlock(&mpriv->mtx);
|
|
|
|
/* put cmd execution ref */
|
|
mdw_cmd_put(c);
|
|
mdw_trace_end();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mdw_cmd_trigger_func(struct work_struct *wk)
|
|
{
|
|
struct mdw_cmd *c =
|
|
container_of(wk, struct mdw_cmd, t_wk);
|
|
int ret = 0;
|
|
|
|
if (c->wait_fence) {
|
|
dma_fence_wait(c->wait_fence, false);
|
|
dma_fence_put(c->wait_fence);
|
|
}
|
|
|
|
mdw_flw_debug("s(0x%llx) c(0x%llx) wait fence done, start run\n",
|
|
(uint64_t)c->mpriv, c->kid);
|
|
mutex_lock(&c->mtx);
|
|
ret = mdw_cmd_run(c->mpriv, c);
|
|
mutex_unlock(&c->mtx);
|
|
|
|
/* put cmd execution ref */
|
|
if (ret) {
|
|
atomic_dec(&c->is_running);
|
|
mdw_cmd_put(c);
|
|
}
|
|
}
|
|
|
|
static struct mdw_cmd *mdw_cmd_create(struct mdw_fpriv *mpriv,
|
|
union mdw_cmd_args *args)
|
|
{
|
|
struct mdw_cmd_in *in = (struct mdw_cmd_in *)args;
|
|
struct mdw_cmd *c = NULL;
|
|
|
|
mdw_trace_begin("apumdw:cmd_create|s:0x%llx", (uint64_t)mpriv);
|
|
|
|
/* check num subcmds maximum */
|
|
if (in->exec.num_subcmds > MDW_SUBCMD_MAX) {
|
|
mdw_drv_err("too much subcmds(%u)\n", in->exec.num_subcmds);
|
|
goto out;
|
|
}
|
|
|
|
/* alloc mdw cmd */
|
|
c = kzalloc(sizeof(*c), GFP_KERNEL);
|
|
if (!c)
|
|
goto out;
|
|
|
|
mutex_init(&c->mtx);
|
|
INIT_LIST_HEAD(&c->map_invokes);
|
|
c->mpriv = mpriv;
|
|
atomic_set(&c->is_running, 0);
|
|
|
|
/* setup cmd info */
|
|
c->pid = task_pid_nr(current);
|
|
c->tgid = task_tgid_nr(current);
|
|
c->kid = (uint64_t)c;
|
|
c->uid = in->exec.uid;
|
|
get_task_comm(c->comm, current);
|
|
c->priority = in->exec.priority;
|
|
c->hardlimit = in->exec.hardlimit;
|
|
c->softlimit = in->exec.softlimit;
|
|
c->power_save = in->exec.power_save;
|
|
c->power_plcy = in->exec.power_plcy;
|
|
c->power_dtime = in->exec.power_dtime;
|
|
c->fastmem_ms = in->exec.fastmem_ms;
|
|
c->app_type = in->exec.app_type;
|
|
c->num_subcmds = in->exec.num_subcmds;
|
|
c->num_links = in->exec.num_links;
|
|
c->exec_infos = mdw_mem_get(mpriv, in->exec.exec_infos);
|
|
if (!c->exec_infos) {
|
|
mdw_drv_err("get exec info fail\n");
|
|
goto free_cmd;
|
|
}
|
|
|
|
/* check input params */
|
|
if (mdw_cmd_sanity_check(c)) {
|
|
mdw_drv_err("cmd sanity check fail\n");
|
|
goto put_execinfos;
|
|
}
|
|
|
|
/* subcmds/ksubcmds */
|
|
c->subcmds = kzalloc(c->num_subcmds * sizeof(*c->subcmds), GFP_KERNEL);
|
|
if (!c->subcmds)
|
|
goto put_execinfos;
|
|
if (copy_from_user(c->subcmds, (void __user *)in->exec.subcmd_infos,
|
|
c->num_subcmds * sizeof(*c->subcmds))) {
|
|
mdw_drv_err("copy subcmds fail\n");
|
|
goto free_subcmds;
|
|
}
|
|
if (mdw_cmd_sc_sanity_check(c)) {
|
|
mdw_drv_err("sc sanity check fail\n");
|
|
goto free_subcmds;
|
|
}
|
|
|
|
c->ksubcmds = kzalloc(c->num_subcmds * sizeof(*c->ksubcmds),
|
|
GFP_KERNEL);
|
|
if (!c->ksubcmds)
|
|
goto free_subcmds;
|
|
|
|
/* adj matrix */
|
|
c->adj_matrix = kzalloc(c->num_subcmds *
|
|
c->num_subcmds * sizeof(uint8_t), GFP_KERNEL);
|
|
if (!c->adj_matrix)
|
|
goto free_ksubcmds;
|
|
if (copy_from_user(c->adj_matrix, (void __user *)in->exec.adj_matrix,
|
|
(c->num_subcmds * c->num_subcmds * sizeof(uint8_t)))) {
|
|
mdw_drv_err("copy adj matrix fail\n");
|
|
goto free_adj;
|
|
}
|
|
if (g_mdw_klog & MDW_DBG_CMD) {
|
|
print_hex_dump(KERN_INFO, "[apusys] adj matrix: ",
|
|
DUMP_PREFIX_OFFSET, 16, 1, c->adj_matrix,
|
|
c->num_subcmds * c->num_subcmds, 0);
|
|
}
|
|
if (mdw_cmd_adj_check(c))
|
|
goto free_adj;
|
|
|
|
/* link */
|
|
if (c->num_links) {
|
|
c->links = kcalloc(c->num_links, sizeof(*c->links), GFP_KERNEL);
|
|
if (!c->links)
|
|
goto free_adj;
|
|
if (copy_from_user(c->links, (void __user *)in->exec.links,
|
|
c->num_links * sizeof(*c->links))) {
|
|
mdw_drv_err("copy links fail\n");
|
|
goto free_link;
|
|
}
|
|
}
|
|
if (mdw_cmd_link_check(c))
|
|
goto free_link;
|
|
|
|
/* create infos */
|
|
if (mdw_cmd_create_infos(mpriv, c)) {
|
|
mdw_drv_err("create cmd info fail\n");
|
|
goto free_link;
|
|
}
|
|
|
|
c->mpriv->get(c->mpriv);
|
|
c->complete = mdw_cmd_complete;
|
|
INIT_WORK(&c->t_wk, &mdw_cmd_trigger_func);
|
|
kref_init(&c->ref);
|
|
mdw_cmd_show(c, mdw_drv_debug);
|
|
|
|
goto out;
|
|
|
|
free_link:
|
|
kfree(c->links);
|
|
free_adj:
|
|
kfree(c->adj_matrix);
|
|
free_ksubcmds:
|
|
kfree(c->ksubcmds);
|
|
free_subcmds:
|
|
kfree(c->subcmds);
|
|
put_execinfos:
|
|
mdw_mem_put(mpriv, c->exec_infos);
|
|
free_cmd:
|
|
kfree(c);
|
|
c = NULL;
|
|
out:
|
|
mdw_trace_end();
|
|
return c;
|
|
}
|
|
|
|
static int mdw_cmd_ioctl_del(struct mdw_fpriv *mpriv, union mdw_cmd_args *args)
|
|
{
|
|
struct mdw_cmd_in *in = (struct mdw_cmd_in *)args;
|
|
struct mdw_cmd *c = NULL;
|
|
int ret = 0;
|
|
|
|
mdw_trace_begin("apumdw:user_delete");
|
|
|
|
mutex_lock(&mpriv->mtx);
|
|
c = (struct mdw_cmd *)idr_find(&mpriv->cmds, in->id);
|
|
if (!c) {
|
|
ret = -EINVAL;
|
|
mdw_drv_warn("can't find id(%d)\n", in->id);
|
|
} else {
|
|
if (c != idr_remove(&mpriv->cmds, c->id))
|
|
mdw_drv_warn("remove cmd idr conflict(0x%llx/%d)\n", c->kid, c->id);
|
|
mdw_cmd_delete(c);
|
|
}
|
|
mutex_unlock(&mpriv->mtx);
|
|
|
|
mdw_trace_end();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mdw_cmd_ioctl_run(struct mdw_fpriv *mpriv, union mdw_cmd_args *args)
|
|
{
|
|
struct mdw_cmd_in *in = (struct mdw_cmd_in *)args;
|
|
struct mdw_cmd *c = NULL, *priv_c = NULL;
|
|
struct sync_file *sync_file = NULL;
|
|
int ret = 0, fd = 0, wait_fd = 0, is_running = 0;
|
|
|
|
mdw_trace_begin("apumdw:user_run");
|
|
|
|
/* get wait fd */
|
|
wait_fd = in->exec.fence;
|
|
|
|
mutex_lock(&mpriv->mtx);
|
|
/* get stale cmd */
|
|
c = (struct mdw_cmd *)idr_find(&mpriv->cmds, in->id);
|
|
if (!c) {
|
|
/* no stale cmd, create cmd */
|
|
mdw_cmd_debug("s(0x%llx) create new\n", (uint64_t)mpriv);
|
|
} else if (in->op == MDW_CMD_IOCTL_RUN_STALE) {
|
|
is_running = atomic_read(&c->is_running);
|
|
if (is_running) {
|
|
mdw_drv_err("s(0x%llx) c(0x%llx) is running(%d), can't execute again\n",
|
|
(uint64_t)mpriv, (uint64_t)c, is_running);
|
|
ret = -ETXTBSY;
|
|
goto out;
|
|
}
|
|
/* run stale cmd */
|
|
mdw_cmd_debug("s(0x%llx) run stale(0x%llx)\n",
|
|
(uint64_t)mpriv, (uint64_t)c);
|
|
goto exec;
|
|
} else {
|
|
/* release stale cmd and create new */
|
|
mdw_cmd_debug("s(0x%llx) delete stale(0x%llx) and create new\n",
|
|
(uint64_t)mpriv, (uint64_t)c);
|
|
priv_c = c;
|
|
c = NULL;
|
|
if (priv_c != idr_remove(&mpriv->cmds, priv_c->id)) {
|
|
mdw_drv_warn("remove cmd idr conflict(0x%llx/%d)\n",
|
|
priv_c->kid, priv_c->id);
|
|
}
|
|
}
|
|
|
|
/* create cmd */
|
|
c = mdw_cmd_create(mpriv, args);
|
|
if (!c) {
|
|
mdw_drv_err("create cmd fail\n");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
memset(args, 0, sizeof(*args));
|
|
|
|
/* alloc idr */
|
|
c->id = idr_alloc(&mpriv->cmds, c, MDW_CMD_IDR_MIN, MDW_CMD_IDR_MAX, GFP_KERNEL);
|
|
if (c->id < MDW_CMD_IDR_MIN) {
|
|
mdw_drv_err("alloc idr fail(%d)\n", c->id);
|
|
goto delete_cmd;
|
|
}
|
|
|
|
exec:
|
|
mutex_lock(&c->mtx);
|
|
mdw_cmd_trace(c, MDW_CMD_ENQUE);
|
|
/* get sync_file fd */
|
|
fd = get_unused_fd_flags(O_CLOEXEC);
|
|
if (fd < 0) {
|
|
mdw_drv_err("get unused fd fail\n");
|
|
ret = -EINVAL;
|
|
goto delete_idr;
|
|
}
|
|
if (mdw_fence_init(c, fd)) {
|
|
mdw_drv_err("cmd init fence fail\n");
|
|
goto put_fd;
|
|
}
|
|
sync_file = sync_file_create(&c->fence->base_fence);
|
|
if (!sync_file) {
|
|
mdw_drv_err("create sync file fail\n");
|
|
dma_fence_put(&c->fence->base_fence);
|
|
ret = -ENOMEM;
|
|
goto put_fd;
|
|
}
|
|
|
|
/* get cmd execution ref */
|
|
atomic_inc(&c->is_running);
|
|
mdw_cmd_get(c);
|
|
|
|
/* check wait fence from other module */
|
|
mdw_flw_debug("s(0x%llx)c(0x%llx) wait fence(%d)...\n",
|
|
(uint64_t)c->mpriv, c->kid, wait_fd);
|
|
c->wait_fence = sync_file_get_fence(wait_fd);
|
|
if (!c->wait_fence) {
|
|
mdw_flw_debug("s(0x%llx)c(0x%llx) no wait fence, trigger directly\n",
|
|
(uint64_t)c->mpriv, c->kid);
|
|
ret = mdw_cmd_run(mpriv, c);
|
|
} else {
|
|
/* wait fence from wq */
|
|
schedule_work(&c->t_wk);
|
|
}
|
|
|
|
if (ret) {
|
|
/* put cmd execution ref */
|
|
atomic_dec(&c->is_running);
|
|
mdw_cmd_put(c);
|
|
goto put_file;
|
|
}
|
|
|
|
/* assign fd */
|
|
fd_install(fd, sync_file->file);
|
|
|
|
/* get ref for cmd exec */
|
|
atomic_inc(&mpriv->active_cmds);
|
|
|
|
/* return fd */
|
|
args->out.exec.fence = fd;
|
|
args->out.exec.id = c->id;
|
|
mdw_flw_debug("async fd(%d) id(%d)\n", fd, c->id);
|
|
mutex_unlock(&c->mtx);
|
|
goto out;
|
|
|
|
put_file:
|
|
fput(sync_file->file);
|
|
put_fd:
|
|
put_unused_fd(fd);
|
|
delete_idr:
|
|
if (c != idr_remove(&mpriv->cmds, c->id))
|
|
mdw_drv_warn("remove cmd idr conflict(0x%llx/%d)\n", c->kid, c->id);
|
|
mutex_unlock(&c->mtx);
|
|
delete_cmd:
|
|
mdw_cmd_delete(c);
|
|
out:
|
|
mutex_unlock(&mpriv->mtx);
|
|
if (priv_c)
|
|
mdw_cmd_delete_async(priv_c);
|
|
|
|
mdw_trace_end();
|
|
|
|
return ret;
|
|
}
|
|
|
|
int mdw_cmd_ioctl(struct mdw_fpriv *mpriv, void *data)
|
|
{
|
|
union mdw_cmd_args *args = (union mdw_cmd_args *)data;
|
|
int ret = 0;
|
|
|
|
mdw_flw_debug("s(0x%llx) op::%d\n", (uint64_t)mpriv, args->in.op);
|
|
|
|
switch (args->in.op) {
|
|
case MDW_CMD_IOCTL_RUN:
|
|
case MDW_CMD_IOCTL_RUN_STALE:
|
|
ret = mdw_cmd_ioctl_run(mpriv, args);
|
|
break;
|
|
case MDW_CMD_IOCTL_DEL:
|
|
ret = mdw_cmd_ioctl_del(mpriv, args);
|
|
break;
|
|
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
mdw_flw_debug("done\n");
|
|
|
|
return ret;
|
|
}
|