// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. */ #include #include #include #include #include #include "apusys_device.h" #include "mdw_cmn.h" #include "mdw_cmd.h" #include "apu_cmd.h" #include "mdw_mem.h" #include "mdw_dbg.h" #include "mdw_sched.h" #include "mdw_rsc.h" #include "mdw_trace.h" #include "reviser_export.h" #include "mdw_fence.h" #define MDW_CMD_PSR_NUM_ERR 0xffffffff #define MDW_CMD_SCR_BMP_ERR 0xffffffffffffffff #define MDW_CMD_EMPTY_NUM 0xff /* parse apu cmd related functions */ static void mdw_cmd_show_cmd(struct mdw_apu_cmd *c) { struct apu_cmd_hdr *h = c->hdr; mdw_cmd_debug("-------------------------\n"); mdw_cmd_debug(" apusys cmd(0x%llx)\n", c->kid); mdw_cmd_debug(" magic = 0x%llx\n", h->magic); mdw_cmd_debug(" uid = 0x%llx\n", h->uid); mdw_cmd_debug(" version = %u\n", h->version); mdw_cmd_debug(" priority = %u\n", h->priority); mdw_cmd_debug(" hard_limit = %u\n", h->hard_limit); mdw_cmd_debug(" soft_limit = %u\n", h->soft_limit); mdw_cmd_debug(" pid = %u\n", h->pid); mdw_cmd_debug(" flags = 0x%llx\n", h->flags); mdw_cmd_debug(" num_sc = %u\n", h->num_sc); mdw_cmd_debug(" ofs_scr_list = %u\n", h->ofs_scr_list); mdw_cmd_debug(" ofs_pdr_cnt_list = %u\n", h->ofs_pdr_cnt_list); mdw_cmd_debug(" scofs_list_entry = %u\n", h->scofs_list_entry); mdw_cmd_debug("-------------------------\n"); } static void mdw_cmd_show_sc(struct mdw_apu_sc *sc) { struct apu_sc_hdr_cmn *h = sc->hdr; mdw_cmd_debug("-------------------------\n"); mdw_cmd_debug(" apusys sc(0x%llx-#%d)\n", sc->parent->kid, sc->idx); mdw_cmd_debug(" type = %u\n", h->type); mdw_cmd_debug(" driver_time = %u\n", h->driver_time); mdw_cmd_debug(" ip_time = %u\n", h->ip_time); mdw_cmd_debug(" suggest_time = %u\n", h->suggest_time); mdw_cmd_debug(" bandwidth = %u\n", h->bandwidth); mdw_cmd_debug(" tcm_usage = %u\n", h->tcm_usage); mdw_cmd_debug(" tcm_force = %d\n", h->tcm_force); mdw_cmd_debug(" boost_val = %d\n", h->boost_val); mdw_cmd_debug(" pack_id = %d\n", h->pack_id); mdw_cmd_debug(" reserved = %d\n", h->reserved); mdw_cmd_debug(" mem_ctx = %u\n", h->mem_ctx); mdw_cmd_debug(" cb_info_size = %u\n", h->cb_info_size); mdw_cmd_debug(" ofs_cb_info = %u\n", h->ofs_cb_info); mdw_cmd_debug(" kva = 0x%llx\n", sc->kva); mdw_cmd_debug(" size = %u\n", sc->size); mdw_cmd_debug("-------------------------\n"); } static void mdw_cmd_show_sc_perf(struct mdw_apu_sc *sc) { mdw_pef_debug("-------------------------\n"); mdw_pef_debug(" apusys sc(0x%llx-#%d)\n", sc->parent->kid, sc->idx); mdw_pef_debug(" parsing time = %u\n", mdw_cmn_get_time_diff(&sc->parent->ts_create, &sc->ts_create)); mdw_pef_debug(" wait dependency = %u\n", mdw_cmn_get_time_diff(&sc->ts_create, &sc->ts_enque)); mdw_pef_debug(" sched time = %u\n", mdw_cmn_get_time_diff(&sc->ts_enque, &sc->ts_deque)); mdw_pef_debug(" preset time = %u\n", mdw_cmn_get_time_diff(&sc->ts_deque, &sc->ts_start)); mdw_pef_debug(" exec time = %u\n", mdw_cmn_get_time_diff(&sc->ts_start, &sc->ts_end)); mdw_pef_debug(" post time = %u\n", mdw_cmn_get_time_diff(&sc->ts_end, &sc->ts_delete)); mdw_pef_debug(" life time = %u\n", mdw_cmn_get_time_diff(&sc->ts_create, &sc->ts_delete)); mdw_pef_debug("-------------------------\n"); } static void mdw_cmd_show_cmd_perf(struct mdw_apu_cmd *c) { mdw_pef_debug("-------------------------\n"); mdw_pef_debug(" apusys cmd(0x%llx)\n", c->kid); mdw_pef_debug(" life time = %u\n", mdw_cmn_get_time_diff(&c->ts_create, &c->ts_delete)); mdw_pef_debug("-------------------------\n"); } static void mdw_cmd_show_hnd(struct apusys_cmd_hnd *h) { mdw_cmd_debug("-------------------------\n"); mdw_cmd_debug(" kva = 0x%llx\n", h->kva); mdw_cmd_debug(" iova = 0x%x\n", h->iova); mdw_cmd_debug(" size = %u\n", h->size); mdw_cmd_debug(" cmd_id = 0x%llx\n", h->cmd_id); mdw_cmd_debug(" subcmd_idx = %u\n", h->subcmd_idx); mdw_cmd_debug(" priority = %u\n", h->priority); mdw_cmd_debug(" ip_time = %u\n", h->ip_time); mdw_cmd_debug(" boost_val = %d\n", h->boost_val); mdw_cmd_debug(" cluster_size = %d\n", h->cluster_size); mdw_cmd_debug(" multicore_total = %u\n", h->multicore_total); mdw_cmd_debug(" multicore_idx = %u\n", h->multicore_idx); mdw_cmd_debug(" pmu_kva = 0x%llx\n", h->pmu_kva); mdw_cmd_debug(" cmd_entry = 0x%llx\n", h->cmd_entry); mdw_cmd_debug(" ctx_id = %d\n", h->ctx_id); mdw_cmd_debug("-------------------------\n"); } static uint32_t mdw_cmd_get_pdr_num(struct mdw_apu_sc *sc) { struct mdw_apu_cmd *cmd = sc->parent; uint32_t pdr_cnt = 0; uint32_t *pdr_cnt_list; pdr_cnt_list = (uint32_t *)( (uint64_t)cmd->u_hdr + cmd->hdr->ofs_pdr_cnt_list); pdr_cnt = pdr_cnt_list[sc->idx]; mdw_cmd_debug("0x%llx/0x%llx/0x%x/%d -> %d\n", (uint64_t)pdr_cnt_list, (uint64_t)cmd->u_hdr, cmd->hdr->ofs_pdr_cnt_list, sc->idx, pdr_cnt_list[sc->idx]); if (pdr_cnt > cmd->hdr->num_sc) return MDW_CMD_PSR_NUM_ERR; return pdr_cnt; } static struct apu_sc_hdr_cmn *mdw_cmd_get_sc_hdr(struct mdw_apu_cmd *cmd, int idx) { uint32_t ofs = 0; struct apu_cmd_hdr *cmd_hdr = cmd->u_hdr; struct apu_sc_hdr_cmn *sh = NULL; if ((uint32_t)idx >= cmd_hdr->num_sc) return NULL; ofs = *(uint32_t *)((uint64_t)&cmd_hdr->scofs_list_entry + SIZE_SUBGRAPH_SCOFS_ELEMENT * idx); if (ofs + sizeof(struct apu_sc_hdr_cmn) > cmd->size) goto fail_size; sh = (struct apu_sc_hdr_cmn *)((uint64_t)cmd_hdr + ofs); if (sh->type == APUSYS_DEVICE_MDLA && ofs + sizeof(struct apu_sc_hdr_cmn) + sizeof(struct apu_mdla_hdr) > cmd->size) goto fail_size; return (struct apu_sc_hdr_cmn *)((uint64_t)cmd_hdr + ofs); fail_size: mdw_drv_err("sc(0x%llx-#%d) ofs(%u) over size(%d)\n", cmd_hdr->uid, idx, ofs, cmd->size); return NULL; } static int mdw_cmd_parse_flags(struct mdw_apu_cmd *c) { c->multi = (uint8_t)((c->hdr->flags & HDR_FLAG_MASK_MULTI) >> 62); if (c->multi > HDR_FLAG_MULTI_MULTI) return -EINVAL; /* Create Fence FD */ if (c->hdr->flags & HDR_FLAG_MASK_FENCE_EXEC) { c->uf_hdr = (struct apu_fence_hdr *)( (uint64_t)c->u_hdr + sizeof(struct apu_cmd_hdr) + (c->hdr->num_sc - 1) * sizeof(uint32_t)); c->file = NULL; if (apu_sync_file_create(c) < 0) return -EINVAL; } return 0; } static uint64_t mdw_cmd_get_scr(struct mdw_apu_sc *sc) { struct mdw_apu_cmd *cmd = sc->parent; uint32_t *scr_list; int i = 0, idx = 0, j = 0, num_scr = 0; uint64_t scr_bmp = 0; scr_list = (uint32_t *)((uint64_t)cmd->u_hdr + cmd->hdr->ofs_scr_list); for (i = 0; i < (int)cmd->hdr->num_sc; i++) { if (i != sc->idx) { idx = idx + scr_list[idx] + 1; continue; } num_scr = (int)scr_list[idx]; idx++; /* * idx start with 1, because 0=num * refer to apusys cmd definition */ for (j = 0; j < num_scr; j++) { mdw_cmd_debug("sc(0x%llx-#%d) add scr(%d/%p), num_scr = %d\n", cmd->hdr->uid, sc->idx, scr_list[idx], &scr_list[idx], num_scr); if (scr_list[idx] >= cmd->hdr->num_sc) { mdw_drv_err("sc(0x%llx-#%d) scr idx(%d/%d) invalid\n", cmd->hdr->uid, sc->idx, scr_list[idx], cmd->hdr->num_sc); return MDW_CMD_SCR_BMP_ERR; } scr_bmp |= (1ULL << scr_list[idx]); idx++; } mdw_cmd_debug("sc(#%d) scr_bmp = 0x%llx\n", sc->idx, scr_bmp); break; } return scr_bmp; } static int mdw_cmd_hdr_get_status(struct mdw_apu_cmd *c) { return ((int)(c->u_hdr->flags & HDR_FlAG_MASK_STATUS_BMP) >> HDR_FLAG_MASK_STATUS_OFS); } static void mdw_cmd_hdr_set_status(struct mdw_apu_cmd *c, int status) { c->u_hdr->flags = (c->u_hdr->flags & ~(HDR_FlAG_MASK_STATUS_BMP)) | status << HDR_FLAG_MASK_STATUS_OFS; } static void mdw_cmd_hdr_update_time(struct mdw_apu_cmd *c) { uint64_t us = 0; if (c->uf_hdr) { us = (c->end_ts.tv_sec - c->start_ts.tv_sec) * 1000000; us += ((c->end_ts.tv_nsec - c->start_ts.tv_nsec) / 1000); c->uf_hdr->total_time = us; } } static void mdw_cmd_set_sc_hdr(struct mdw_apu_sc *sc) { /* execution time */ sc->u_hdr->driver_time = sc->driver_time; /* ip time */ sc->u_hdr->ip_time = sc->ip_time; /* bandwidth */ sc->u_hdr->bandwidth = sc->bw; /* boost val */ sc->u_hdr->boost_val = sc->boost; /* cmd status */ if (sc->status) mdw_cmd_hdr_set_status(sc->parent, HDR_FLAG_EXEC_STATUS_HWERROR); } static void *mdw_cmd_get_dev_hdr(struct mdw_apu_sc *sc) { return (void *)((uint64_t)sc->u_hdr + sizeof(struct apu_sc_hdr_cmn)); } static inline int mdw_cmd_valid(struct mdw_apu_cmd *c) { struct apu_cmd_hdr *h = c->hdr; if (h->magic != APUSYS_MAGIC_NUMBER || h->version != APUSYS_CMD_VERSION || h->num_sc == 0 || h->num_sc > MDW_CMD_SC_MAX || h->ofs_scr_list > c->size || h->ofs_pdr_cnt_list > c->size || h->priority >= MDW_CMD_PRIO_MAX) return -EINVAL; return 0; } static inline int mdw_cmd_sc_valid(struct mdw_apu_sc *sc) { struct mdw_apu_cmd *c = sc->parent; /* check type max */ if (sc->hdr->type >= APUSYS_DEVICE_RT) { mdw_drv_err("sc(0x%llx-#%d) invalid type(%u)\n", c->kid, sc->idx, sc->hdr->type); return -ENODEV; } /* check memory context range */ if (sc->hdr->mem_ctx >= MDW_CMD_SC_MAX) { mdw_drv_err("sc(0x%llx-#%d) invalid ctx(%u)\n", c->kid, sc->idx, sc->hdr->mem_ctx); return -EINVAL; } /* check pack id */ if (sc->hdr->pack_id >= MDW_CMD_SC_MAX) { mdw_drv_err("sc(0x%llx-#%d) invalid pack(%u)\n", c->kid, sc->idx, sc->hdr->pack_id); return -EINVAL; } /* check successor bitmap */ if (sc->scr_bmp == MDW_CMD_SCR_BMP_ERR) { mdw_drv_err("sc(0x%llx-#%d) invalid scr bmp\n", c->kid, sc->idx); return -EINVAL; } /* check presuccessor number */ if (sc->pdr_num == MDW_CMD_PSR_NUM_ERR) { mdw_drv_err("sc(0x%llx-#%d) invalid pdr num\n", c->kid, sc->idx); return -EINVAL; } /* limit boost value */ sc->boost = sc->hdr->boost_val < 100 ? sc->hdr->boost_val : 100; return 0; } static int mdw_cmd_check_sc_ready(struct mdw_apu_sc *sc) { if (sc->idx < 0 || sc->idx >= MDW_CMD_SC_MAX) return -EINVAL; mdw_cmd_debug("sc(0x%llx-#%d) pdr_num = %u/%u\n", sc->parent->kid, sc->idx, sc->pdr_num, sc->parent->pdr_cnt[sc->idx]); return sc->pdr_num - sc->parent->pdr_cnt[sc->idx] == 0 ? 0 : -EBADR; } static int mdw_cmd_get_pack_ctx(struct mdw_apu_cmd *c) { int i = 0; struct apu_sc_hdr_cmn *h = NULL; memset(c->ctx_repo, MDW_CMD_EMPTY_NUM, sizeof(c->ctx_repo)); for (i = 0; i < c->hdr->num_sc; i++) { h = mdw_cmd_get_sc_hdr(c, i); if (!h) continue; if (h->mem_ctx >= MDW_CMD_SC_MAX || h->pack_id >= MDW_CMD_SC_MAX) { mdw_drv_err("sc(0x%llx-#%d) invalid pack(%u) ctx(%u)\n", c->kid, i, h->pack_id, h->mem_ctx); return -EINVAL; } c->ctx_cnt[h->mem_ctx]++; c->pack_cnt[h->pack_id]++; } return 0; } static bool mdw_cmd_is_deadline(struct mdw_apu_sc *sc) { if (sc->period) return true; return false; } static struct mdw_apu_cmd *mdw_cmd_create_cmd(int fd, uint32_t size, uint32_t ofs, struct mdw_usr *u) { struct mdw_apu_cmd *c; if (ofs + sizeof(struct apu_cmd_hdr) > size) { mdw_drv_err("hdr ofs overflow(%u/%lu/%u)\n", ofs, sizeof(struct apu_cmd_hdr), size); goto out; } /* create cmd */ c = vzalloc(sizeof(struct mdw_apu_cmd)); if (!c) goto fail_alloc_cmd; /* mapping */ c->cmdbuf = vzalloc(sizeof(struct apusys_kmem)); if (!c->cmdbuf) goto fail_alloc_km; c->cmdbuf->fd = fd; c->cmdbuf->size = size; if (mdw_mem_map_kva(c->cmdbuf)) goto fail_map_kva; /* setup hdr */ c->hdr = vzalloc(sizeof(struct apu_cmd_hdr)); if (!c->hdr) goto fail_alloc_hdr; c->u_hdr = (struct apu_cmd_hdr *)(c->cmdbuf->kva + ofs); memcpy(c->hdr, c->u_hdr, sizeof(struct apu_cmd_hdr)); c->size = size; c->kid = (uint64_t)c; refcount_set(&c->ref.refcount, c->hdr->num_sc); c->usr = u; /* init cmd completion */ init_completion(&c->cmplt); /* check basic information */ if (mdw_cmd_valid(c)) goto fail_cmd_invalid; if (mdw_cmd_parse_flags(c)) goto fail_parse_flags; if (mdw_cmd_get_pack_ctx(c)) goto fail_get_pack_ctx; /* init sc list */ INIT_LIST_HEAD(&c->sc_list); INIT_LIST_HEAD(&c->di_list); /* init mutex*/ mutex_init(&c->mtx); getnstimeofday(&c->ts_create); ktime_get_ts64(&c->start_ts); /* init sc state bmp */ c->sc_status_bmp = (1ULL << c->hdr->num_sc) - 1; mdw_drv_debug("cmd(0x%llx/0x%llx) create\n", c->hdr->uid, c->kid); mdw_cmd_debug("cmd sc status bitmap = 0x%llx\n", c->sc_status_bmp); mdw_cmd_show_cmd(c); return c; fail_get_pack_ctx: fail_parse_flags: fail_cmd_invalid: vfree(c->hdr); fail_alloc_hdr: mdw_mem_unmap_kva(c->cmdbuf); fail_map_kva: vfree(c->cmdbuf); fail_alloc_km: vfree(c); fail_alloc_cmd: out: return NULL; } static int mdw_cmd_delete_cmd(struct mdw_apu_cmd *c) { if (kref_read(&c->ref) != 0) { mdw_drv_err("cmd(0x%llx/0x%llx) can't destroy\n", c->hdr->uid, c->kid); return -EBUSY; } mdw_drv_debug("cmd(0x%llx/0x%llx) destroy\n", c->hdr->uid, c->kid); getnstimeofday(&c->ts_delete); ktime_get_ts64(&c->end_ts); mdw_cmd_hdr_update_time(c); mdw_cmd_show_cmd_perf(c); mdw_mem_unmap_kva(c->cmdbuf); vfree(c->cmdbuf); vfree(c->hdr); vfree(c); return 0; } static int mdw_cmd_get_codebuf_info(struct mdw_apu_sc *sc) { int fd = 0, ret = 0; struct mdw_apu_cmd *c = sc->parent; if (sc->hdr->ofs_cb_info & FLAG_SUBGRAPH_FD_MAP) { fd = sc->hdr->ofs_cb_info & ~FLAG_SUBGRAPH_FD_MAP; mdw_cmd_debug("sc(0x%llx-#%d) map from fd(%d)", c->kid, sc->idx, fd); sc->buf.fd = fd; sc->buf.size = sc->hdr->cb_info_size; ret = mdw_mem_map_kva(&sc->buf); if (ret) goto out; sc->kva = sc->buf.kva; } else { sc->kva = (uint64_t)c->u_hdr + sc->hdr->ofs_cb_info; mdw_cmd_debug("sc(0x%llx-#%d) form ofs(0x%x)", c->kid, sc->idx, sc->hdr->ofs_cb_info); /* check sc codebuf overflow */ if (sc->kva + sc->size > ((uint64_t)c->u_hdr + c->size)) { mdw_drv_err("sc(0x%llx-#%d) codebuf overflow(0x%llx/%u/0x%llx/%u)\n", c->kid, sc->idx, sc->kva, sc->size, (uint64_t)c->u_hdr, c->size); return -EINVAL; } } out: return ret; } static void mdw_cmd_release_codebuf_info(struct mdw_apu_sc *sc) { if (sc->hdr->ofs_cb_info & FLAG_SUBGRAPH_FD_MAP) mdw_mem_unmap_kva(&sc->buf); } static void mdw_cmd_done(struct kref *ref) { struct mdw_apu_cmd *c = container_of(ref, struct mdw_apu_cmd, ref); /* abort, delete cmd direct */ if (c->sc_status_bmp || c->state == MDW_CMD_STATE_ABORT) { mdw_drv_warn("abort, delete c(0x%llx) directly\n", c->kid); mdw_cmd_delete_cmd(c); } else { complete(&c->cmplt); } } static void mdw_cmd_delete_sc(struct mdw_apu_sc *sc) { struct mdw_queue *mq = NULL; mdw_drv_debug("sc(0x%llx-#%d) destroy\n", sc->parent->kid, sc->idx); mq = mdw_rsc_get_queue(sc->type); if (!mq) { mdw_drv_err("can't find mq(%d)\n", sc->type); return; } mdw_queue_task_end(sc); getnstimeofday(&sc->ts_delete); mdw_cmd_show_sc_perf(sc); mutex_lock(&sc->mtx); mdw_cmd_set_sc_hdr(sc); mdw_cmd_release_codebuf_info(sc); mutex_unlock(&sc->mtx); vfree(sc->hdr); vfree(sc); } static struct mdw_apu_sc *mdw_cmd_create_sc(struct mdw_apu_cmd *c) { struct mdw_apu_sc *sc = NULL; struct mdw_queue *mq = NULL; sc = vzalloc(sizeof(struct mdw_apu_sc)); if (!sc) return NULL; /* init sc's list item and insert to cmd's list */ sc->u_hdr = mdw_cmd_get_sc_hdr(c, c->parsed_sc_num); if (!sc->u_hdr) goto fail_get_sc_hdr; sc->hdr = vzalloc(sizeof(struct apu_sc_hdr_cmn)); if (!sc->hdr) goto fail_alloc_hdr; memcpy(sc->hdr, sc->u_hdr, sizeof(struct apu_sc_hdr_cmn)); mutex_init(&sc->mtx); sc->parent = c; sc->type = sc->hdr->type; sc->size = sc->hdr->cb_info_size; sc->idx = c->parsed_sc_num; sc->pdr_num = mdw_cmd_get_pdr_num(sc); sc->scr_bmp = mdw_cmd_get_scr(sc); sc->runtime = sc->hdr->ip_time; kref_init(&sc->multi_ref); sc->d_hdr = (void *)((uint64_t)(sc->u_hdr) + sizeof(struct apu_sc_hdr_cmn)); if (mdw_cmd_get_codebuf_info(sc)) goto fail_get_codebuf_info; if (preemption_support && mdw_rsc_get_dev_num(sc->type + APUSYS_DEVICE_RT) && c->hdr->soft_limit) { sc->type += APUSYS_DEVICE_RT; sc->period = c->hdr->soft_limit * 1000; sc->deadline = jiffies + usecs_to_jiffies(sc->period); } mdw_cmd_debug("sc(0x%llx-#%d) ctx(%d) pack(%d)\n", c->kid, sc->idx, sc->hdr->mem_ctx, sc->hdr->pack_id); /* check sc valid */ if (mdw_cmd_sc_valid(sc)) goto fail_sc_invalid; /* task start */ mq = mdw_rsc_get_queue(sc->type); if (!mq) { mdw_drv_err("can't find mq(%d)\n", sc->type); goto fail_get_mq; } mdw_queue_task_start(sc); getnstimeofday(&sc->ts_create); mdw_drv_debug("sc(0x%llx-#%d) create\n", c->kid, sc->idx); mdw_cmd_show_sc(sc); return sc; fail_sc_invalid: fail_get_mq: fail_get_codebuf_info: vfree(sc->hdr); fail_alloc_hdr: fail_get_sc_hdr: mdw_flw_debug("\n"); vfree(sc); return NULL; } static int mdw_cmd_abort_cmd(struct mdw_apu_cmd *c) { struct list_head *tmp = NULL, *list_ptr = NULL; struct mdw_apu_sc *sc = NULL; int i = 0, cnt = 0; mutex_lock(&c->mtx); c->state = MDW_CMD_STATE_ABORT; if (!mdw_cmd_hdr_get_status(c)) mdw_cmd_hdr_set_status(c, HDR_FLAG_EXEC_STATUS_ABORT); mdw_flw_debug("exec status in flag(%d)\n", mdw_cmd_hdr_get_status(c)); list_for_each_safe(list_ptr, tmp, &c->sc_list) { sc = list_entry(list_ptr, struct mdw_apu_sc, cmd_item); list_del(&sc->cmd_item); mdw_cmd_delete_sc(sc); cnt++; } mutex_unlock(&c->mtx); for (i = 0; i < cnt; i++) kref_put(&c->ref, mdw_cmd_done); return 0; } static int mdw_cmd_parse_cmd(struct mdw_apu_cmd *c, struct mdw_apu_sc **out) { int ret = 0; struct mdw_apu_sc *sc; *out = NULL; if (c->parsed_sc_num >= c->hdr->num_sc) return 0; sc = mdw_cmd_create_sc(c); if (!sc) return -EINVAL; mutex_lock(&c->mtx); if (mdw_cmd_check_sc_ready(sc)) list_add_tail(&sc->cmd_item, &c->sc_list); else *out = sc; c->parsed_sc_num++; ret = c->hdr->num_sc - c->parsed_sc_num; mutex_unlock(&c->mtx); return ret; } static void mdw_cmd_update_scr(struct mdw_apu_sc *sc) { struct mdw_apu_cmd *c = sc->parent; int idx = 0; while (sc->scr_bmp != 0) { if (!(sc->scr_bmp & (1ULL << idx))) goto next; mdw_flw_debug("sc(0x%llx-#%d) update scr(%d)\n", c->kid, sc->idx, idx); c->pdr_cnt[idx]++; sc->scr_bmp = sc->scr_bmp & ~(1ULL << idx); next: idx++; if (idx >= MDW_CMD_SC_MAX) break; } } static int mdw_cmd_end_sc(struct mdw_apu_sc *in, struct mdw_apu_sc **out) { int ret = 0; struct mdw_apu_cmd *c; struct mdw_apu_sc *sc = NULL; struct list_head *tmp = NULL, *list_ptr = NULL; *out = NULL; c = in->parent; mutex_lock(&c->mtx); if (c->sc_status_bmp & (1ULL << in->idx)) { c->sc_status_bmp &= ~(1ULL << in->idx); mdw_cmd_update_scr(in); /* update subcmd return value */ if (in->status) c->sc_rets |= (1ULL << in->idx); } mdw_flw_debug("cmd status = 0x%llx after #%d sc done\n", c->sc_status_bmp, in->idx); /* check sc list */ if (list_empty(&c->sc_list)) { mdw_flw_debug("cmd(0x%llx) empty\n", c->kid); goto out; } list_for_each_safe(list_ptr, tmp, &c->sc_list) { sc = list_entry(list_ptr, struct mdw_apu_sc, cmd_item); mdw_flw_debug("sc(0x%llx-#%d) bmp(0x%llx/0x%llx)\n", c->kid, sc->idx, sc->scr_bmp, c->sc_status_bmp); if (!mdw_cmd_check_sc_ready(sc)) { list_del(&sc->cmd_item); *out = sc; mdw_cmd_debug("sc(0x%llx-#%d) ready(%p)\n", c->kid, sc->idx, sc); break; } } out: mutex_unlock(&c->mtx); /* if no out, delete sc */ if (*out == NULL) { mdw_flw_debug("sc(0x%llx-#%d) done ref(%d)\n", c->kid, in->idx, kref_read(&c->ref)); mdw_cmd_delete_sc(in); kref_put(&c->ref, mdw_cmd_done); } return ret; } int mdw_cmd_get_ctx(struct mdw_apu_sc *sc) { int ret = 0; uint32_t tcm_usage = 0; struct mdw_apu_cmd *c = sc->parent; mdw_trace_begin("get ctx|sc(0x%llx-%d)", sc->parent->kid, sc->idx); mutex_lock(&c->mtx); // if tcm usage from user == 0, set by debug prop tcm_usage = sc->hdr->tcm_usage == 0 ? mdw_dbg_get_prop(MDW_DBG_PROP_TCM_DEFAULT) : sc->hdr->tcm_usage; /* if indicated ctx == NONE, get vlm directly */ if (sc->hdr->mem_ctx == VALUE_SUBGRAPH_CTX_ID_NONE && sc->multi_total <= 1) { ret = reviser_get_vlm(tcm_usage, sc->hdr->tcm_force, &sc->ctx, &sc->real_tcm_usage); mdw_flw_debug("sc(0x%llx-#%d) get ctx(%lu/%u/%u)\n", c->kid, sc->idx, sc->ctx, sc->hdr->mem_ctx, tcm_usage); goto out; } if (c->ctx_repo[sc->hdr->mem_ctx] != MDW_CMD_EMPTY_NUM) { sc->ctx = (uint32_t)c->ctx_repo[sc->hdr->mem_ctx]; goto out; } ret = reviser_get_vlm(tcm_usage, sc->hdr->tcm_force, &sc->ctx, &sc->real_tcm_usage); c->ctx_repo[sc->hdr->mem_ctx] = sc->ctx; mdw_flw_debug("sc(0x%llx-#%d) get ctx(%lu/%u/%u)\n", c->kid, sc->idx, sc->ctx, sc->hdr->mem_ctx, tcm_usage); out: mutex_unlock(&c->mtx); mdw_trace_end("get ctx|sc(0x%llx-%d) ctx(%lu)", sc->parent->kid, sc->idx, sc->ctx); return ret; } void mdw_cmd_put_ctx(struct mdw_apu_sc *sc) { struct mdw_apu_cmd *c = sc->parent; mdw_trace_begin("put ctx|sc(0x%llx-%d) ctx(%lu)", sc->parent->kid, sc->idx, sc->ctx); mutex_lock(&c->mtx); if (sc->hdr->mem_ctx == VALUE_SUBGRAPH_CTX_ID_NONE && sc->multi_total <= 1) { reviser_free_vlm(sc->ctx); mdw_flw_debug("sc(0x%llx-#%d) put ctx(%lu/%u)\n", c->kid, sc->idx, sc->ctx, sc->hdr->mem_ctx); goto out; } c->ctx_cnt[sc->hdr->mem_ctx]--; if (!c->ctx_cnt[sc->hdr->mem_ctx]) { reviser_free_vlm(sc->ctx); c->ctx_repo[sc->hdr->mem_ctx] = MDW_CMD_EMPTY_NUM; mdw_flw_debug("sc(0x%llx-#%d) put ctx(%lu/%u)\n", c->kid, sc->idx, sc->ctx, sc->hdr->mem_ctx); } out: mutex_unlock(&c->mtx); mdw_trace_end("put ctx|sc(0x%llx-%d) ctx(%lu)", sc->parent->kid, sc->idx, sc->ctx); } static int mdw_cmd_sc_exec_num(struct mdw_apu_sc *sc) { struct apu_mdla_hdr *h = NULL; int exec_num = 1; int dbg_multi = 0; dbg_multi = mdw_dbg_get_prop(MDW_DBG_PROP_MULTICORE); if (dbg_multi == HDR_FLAG_MULTI_SINGLE || sc->parent->multi == HDR_FLAG_MULTI_SINGLE) exec_num = 1; else if (sc->type == APUSYS_DEVICE_MDLA || sc->type == APUSYS_DEVICE_MDLA_RT) { h = (struct apu_mdla_hdr *)mdw_cmd_get_dev_hdr(sc); if (h->ofs_codebuf_info_dual0 && h->ofs_codebuf_info_dual1) exec_num = 2; } return exec_num; } static void mdw_cmd_sc_clr_hnd(struct mdw_apu_sc *sc, void *hnd) { struct apusys_cmd_hnd *h = (struct apusys_cmd_hnd *)hnd; if (!h->kva) return; memcpy((void *)h->m_kva, (void *)h->kva, sc->size); kfree((void *)h->kva); h->kva = 0; h->m_kva = 0; } static int mdw_cmd_sc_set_hnd(struct mdw_apu_sc *sc, int d_idx, void *hnd) { struct apusys_cmd_hnd *h = (struct apusys_cmd_hnd *)hnd; struct apu_mdla_hdr *m_hdr = NULL; struct mdw_apu_cmd *c = sc->parent; int ret = 0; /* contruct cmd hnd */ mutex_lock(&sc->mtx); memset(h, 0, sizeof(struct apusys_cmd_hnd)); h->size = sc->size; h->cmdbuf = c->cmdbuf; h->cmd_id = c->kid; h->subcmd_idx = sc->idx; h->priority = c->hdr->priority; h->ip_time = 0; h->boost_val = sc->boost; h->multicore_total = sc->multi_total; h->multicore_idx = 0; h->cmd_entry = c->cmdbuf->kva; h->cmd_size = c->size; h->ctx_id = sc->ctx; h->context_callback = reviser_set_context; h->cluster_size = sc->cluster_size; switch (sc->type) { case APUSYS_DEVICE_MDLA: case APUSYS_DEVICE_MDLA_RT: /* for mdla pmu */ m_hdr = (struct apu_mdla_hdr *)sc->d_hdr; if (m_hdr->ofs_pmu_info > c->size) h->pmu_kva = h->cmd_entry; else h->pmu_kva = c->cmdbuf->kva + m_hdr->ofs_pmu_info; /* multicore */ if (sc->multi_total <= 1) { h->m_kva = sc->kva; } else { m_hdr = mdw_cmd_get_dev_hdr(sc); if (d_idx == 0) { h->m_kva = c->cmdbuf->kva + m_hdr->ofs_codebuf_info_dual0; } else { h->m_kva = c->cmdbuf->kva + m_hdr->ofs_codebuf_info_dual1; } h->multicore_idx = d_idx; if (h->m_kva + sc->size > c->cmdbuf->kva + c->size) { mdw_drv_err("sc over size(0x%llx/%u)(0x%llx/%u)\n", h->m_kva, sc->size, c->cmdbuf->kva, c->size); ret = -EINVAL; goto out; } mdw_flw_debug("multi(%d/%d) kva(0x%llx), offset(%u/%u)\n", d_idx, sc->multi_total, h->kva, m_hdr->ofs_codebuf_info_dual0, m_hdr->ofs_codebuf_info_dual1); } break; default: h->m_kva = sc->kva; break; } /* duplicate cmdbuf */ h->kva = (uint64_t)kzalloc(sc->size, GFP_KERNEL); if (!h->kva) { ret = -ENOMEM; goto out; } memcpy((void *)h->kva, (void *)h->m_kva, sc->size); mdw_cmd_debug("cmd(0x%llx-#%d) duplicate (0x%llx/%u)\n", c->kid, sc->idx, h->kva, sc->size); out: mdw_cmd_show_hnd(h); mutex_unlock(&sc->mtx); return ret; } struct mdw_cmd_parser mdw_cmd_parser = { .create_cmd = mdw_cmd_create_cmd, .delete_cmd = mdw_cmd_delete_cmd, .abort_cmd = mdw_cmd_abort_cmd, .parse_cmd = mdw_cmd_parse_cmd, .end_sc = mdw_cmd_end_sc, .get_ctx = mdw_cmd_get_ctx, .put_ctx = mdw_cmd_put_ctx, .exec_core_num = mdw_cmd_sc_exec_num, .set_hnd = mdw_cmd_sc_set_hnd, .clr_hnd = mdw_cmd_sc_clr_hnd, .is_deadline = mdw_cmd_is_deadline, }; struct mdw_cmd_parser *mdw_cmd_get_parser(void) { return &mdw_cmd_parser; } uint64_t mdw_cmd_get_magic(void) { return APUSYS_MAGIC_NUMBER; } uint32_t mdw_cmd_get_ver(void) { return APUSYS_CMD_VERSION; }