// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. */ #include #include #include #include #include #include #include #ifdef CONFIG_PM_SLEEP #include #include #endif #include "apusys_drv.h" #include "mdw_dbg.h" #include "mdw_cmd.h" #include "mdw_cmn.h" #include "mdw_mem.h" #include "mdw_usr.h" #include "mdw_rsc.h" #include "mdw_sched.h" #include "mdw_trace.h" #include "mdw_mem_aee.h" #include "mdw_fence.h" #define MDW_CMD_DEFAULT_TIMEOUT (30*1000) //30s #define MDW_CMD_MAX (32) struct mdw_usr_stat { struct list_head list; struct mutex mtx; }; #ifdef CONFIG_PM_SLEEP static struct wakeup_source *mdw_usr_ws; static uint32_t ws_cnt; static struct mutex ws_mtx; #endif struct mdw_usr_mgr u_mgr; static struct mdw_cmd_parser *cmd_parser; static struct mdw_usr_stat u_stat; static struct mdw_usr_mem_aee u_mem_aee; #define LINEBAR \ "|--------------------------------------------------------"\ "---------------------------------------------------------|\n" static int mdw_usr_pid_cmp(void *priv, struct list_head *a, struct list_head *b) { struct mdw_usr *ua = NULL; struct mdw_usr *ub = NULL; ua = list_entry(a, struct mdw_usr, m_item); ub = list_entry(b, struct mdw_usr, m_item); /** * List_sort is a stable sort, so it is not necessary to distinguish * the @a < @b and @a == @b cases. */ if (ua->pid > ub->pid) return 1; return -1; } static int mdw_usr_mem_size_cmp(void *priv, struct list_head *a, struct list_head *b) { struct mdw_usr *ua = NULL; struct mdw_usr *ub = NULL; ua = list_entry(a, struct mdw_usr, m_item); ub = list_entry(b, struct mdw_usr, m_item); /** * List_sort is a stable sort, so it is not necessary to distinguish * the @a < @b and @a == @b cases. */ if (ua->iova_size > ub->iova_size) return 1; return -1; } void mdw_usr_print_mem_usage(void) { struct list_head *tmp = NULL, *list_ptr = NULL; struct mdw_usr *user = NULL; struct mdw_usr *u = NULL; struct mdw_usr u_tmp; unsigned int total_size = 0; unsigned int percentage = 0; char *cur, *end; memset(&u_tmp, 0, sizeof(struct mdw_usr)); mutex_lock(&u_mem_aee.mtx); cur = u_mem_aee.log_buf; end = u_mem_aee.log_buf + DUMP_LOG_SIZE; mdw_drv_err("name, id, pid, tgid, iova_size, iova_size_max\n"); DUMP_LOG(cur, end, "name, id, pid, tgid, iova_size, iova_size_max\n"); mutex_lock(&u_mgr.mtx); mutex_lock(&u_stat.mtx); /* Sort by PID*/ list_sort(NULL, &u_mgr.list, mdw_usr_pid_cmp); mdw_drv_err("----- APUSYS user -----\n"); DUMP_LOG(cur, end, "----- APUSYS user -----\n"); list_for_each_safe(list_ptr, tmp, &u_mgr.list) { user = list_entry(list_ptr, struct mdw_usr, m_item); total_size = total_size + user->iova_size; mdw_drv_err("%s, %llx, %d, %d, %u, %u\n", user->comm, user->id, user->pid, user->tgid, user->iova_size, user->iova_size_max); DUMP_LOG(cur, end, "%s, %llx, %d, %d, %u, %u\n", user->comm, user->id, user->pid, user->tgid, user->iova_size, user->iova_size_max); if (u_tmp.pid != user->pid) { u = kzalloc(sizeof(struct mdw_usr), GFP_KERNEL); if (u == NULL) goto free_mutex; memcpy(u, user, sizeof(struct mdw_usr)); //Force string end u->comm[TASK_COMM_LEN-1] = '\0'; list_add_tail(&u->m_item, &u_stat.list); u_tmp.pid = user->pid; } else { if (u == NULL) goto free_mutex; u->iova_size = u->iova_size + user->iova_size; u->iova_size_max = u->iova_size_max + user->iova_size_max; } } if (!list_empty(&u_stat.list)) { /* Sort by PID*/ list_sort(NULL, &u_stat.list, mdw_usr_mem_size_cmp); mdw_drv_err("----- APUSYS statistics -----\n"); DUMP_LOG(cur, end, "----- APUSYS statistics -----\n"); list_for_each_safe(list_ptr, tmp, &u_stat.list) { u = list_entry(list_ptr, struct mdw_usr, m_item); if (total_size != 0) { percentage = (uint64_t) u->iova_size * 100 / (uint64_t)total_size; } else { percentage = 0; } mdw_drv_err("%s, %llx, %d, %d, %u, %u, %u, %u%%\n", u->comm, u->id, u->pid, u->tgid, u->iova_size, u->iova_size_max, total_size, percentage); DUMP_LOG(cur, end, "%s, %llx, %d, %d, %u, %u, %u, %u%%\n", u->comm, u->id, u->pid, u->tgid, u->iova_size, u->iova_size_max, total_size, percentage); } /* The last one is the top memory user*/ mdw_drv_err("----- APUSYS top user -----\n"); DUMP_LOG(cur, end, "----- APUSYS top user -----\n"); mdw_drv_err("%s, %llx, %d, %d, %u, %u, %u, %u%%\n", u->comm, u->id, u->pid, u->tgid, u->iova_size, u->iova_size_max, total_size, percentage); DUMP_LOG(cur, end, "%s, %llx, %d, %d, %u, %u, %u, %u%%\n", u->comm, u->id, u->pid, u->tgid, u->iova_size, u->iova_size_max, total_size, percentage); /*delete statistics list*/ list_for_each_safe(list_ptr, tmp, &u_stat.list) { u = list_entry(list_ptr, struct mdw_usr, m_item); list_del(list_ptr); kfree(u); } } free_mutex: mutex_unlock(&u_stat.mtx); mutex_unlock(&u_mgr.mtx); mutex_unlock(&u_mem_aee.mtx); } void mdw_usr_sys_aee_mem(char *buf, int *n) { mutex_lock(&u_mem_aee.mtx); if (snprintf(buf, DUMP_LOG_SIZE, "%s", u_mem_aee.log_buf) < 0) mdw_drv_warn("dump mem info fail\n"); mutex_unlock(&u_mem_aee.mtx); *n = DUMP_LOG_SIZE; } static void mdw_usr_dump_sdev(struct seq_file *s, struct mdw_usr *u) { int cnt = 0; struct mdw_dev_info *d = NULL; struct list_head *tmp = NULL, *list_ptr = NULL; /* device */ list_for_each_safe(list_ptr, tmp, &u->sdev_list) { d = list_entry(list_ptr, struct mdw_dev_info, u_item); mdw_con_info(s, "| %-9d| %-5d| %-5d| %-19s| %-66d|\n", cnt, d->type, d->idx, d->name, d->state); cnt++; } mdw_con_info(s, LINEBAR); } static void mdw_usr_dump_mem(struct seq_file *s, struct mdw_usr *u) { int cnt = 0; struct mdw_mem *mm = NULL; struct list_head *tmp = NULL, *list_ptr = NULL; mdw_con_info(s, "|%-10s|%-6s|%-6s|%-20s|%-12s|%-20s|%-12s|%-20s|\n", " mem", " type", " fd", " uva", " size", " iova", " iova size", " kva"); mdw_con_info(s, LINEBAR); list_for_each_safe(list_ptr, tmp, &u->mem_list) { mm = list_entry(list_ptr, struct mdw_mem, u_item); mdw_con_info(s, "| #%-8d| %-5u| %-5u| 0x%-17llx| %-11d| 0x%-17x| 0x%-9x| 0x%-17llx|\n", cnt, mm->kmem.mem_type, mm->kmem.fd, mm->kmem.uva, mm->kmem.size, mm->kmem.iova, mm->kmem.iova_size, mm->kmem.kva); cnt++; } mdw_con_info(s, LINEBAR); } static void mdw_usr_dump_cmd(struct seq_file *s, struct mdw_usr *u) { int cnt = 0, id = 0; struct mdw_apu_cmd *c; mdw_con_info(s, "|%-10s|%-13s|%-33s|%-33s|%-20s|\n", " cmd", " priority", " uid", " id", " sc num"); mdw_con_info(s, LINEBAR); idr_for_each_entry(&u->cmds_idr, c, id) { mutex_lock(&c->mtx); mdw_con_info(s, "| #%-8d| %-12d| 0x%-30llx| 0x%-30llx| %-19u|\n", cnt, c->hdr->priority, c->hdr->uid, c->kid, c->hdr->num_sc); mutex_unlock(&c->mtx); cnt++; } mdw_con_info(s, LINEBAR); } void mdw_usr_dump(struct seq_file *s) { struct list_head *tmp = NULL, *list_ptr = NULL; struct mdw_usr *u = NULL; int u_cnt = 0; mdw_con_info(s, LINEBAR); mdw_con_info(s, "|%-113s|\n", " apusys user table"); mdw_con_info(s, LINEBAR); mutex_lock(&u_mgr.mtx); list_for_each_safe(list_ptr, tmp, &u_mgr.list) { u = list_entry(list_ptr, struct mdw_usr, m_item); mutex_lock(&u->mtx); mdw_con_info(s, "| user (#%-3d)(%-16s)%83s|\n", u_cnt, u->comm, ""); mdw_con_info(s, "| pid = %-96d|\n", u->pid); mdw_con_info(s, "| tgid = %-96d|\n", u->tgid); mdw_con_info(s, "| iova size max = %-96u|\n", u->iova_size_max); mdw_con_info(s, "| iova size = %-96u|\n", u->iova_size); mdw_con_info(s, LINEBAR); mdw_usr_dump_cmd(s, u); mdw_usr_dump_mem(s, u); mdw_usr_dump_sdev(s, u); mdw_con_info(s, LINEBAR); u_cnt++; mutex_unlock(&u->mtx); } mutex_unlock(&u_mgr.mtx); } #undef LINEBAR //---------------------------------------- static void mdw_usr_ws_init(void) { #if defined CONFIG_PM_SLEEP char ws_name[16]; if (snprintf(ws_name, sizeof(ws_name)-1, "apusys_usr") < 0) { mdw_drv_err("init rsc wakeup source fail\n"); return; } ws_cnt = 0; mutex_init(&ws_mtx); mdw_usr_ws = wakeup_source_register(NULL, (const char *)ws_name); if (!mdw_usr_ws) mdw_drv_err("register ws lock fail!\n"); #else mdw_drv_debug("not support pm wakelock\n"); #endif } static void mdw_usr_ws_destroy(void) { #if defined CONFIG_PM_SLEEP ws_cnt = 0; wakeup_source_unregister(mdw_usr_ws); #else mdw_drv_debug("not support pm wakelock\n"); #endif } void mdw_usr_ws_lock(void) { #ifdef CONFIG_PM_SLEEP mutex_lock(&ws_mtx); if (mdw_usr_ws && !ws_cnt) { mdw_flw_debug("lock\n"); __pm_stay_awake(mdw_usr_ws); } ws_cnt++; mutex_unlock(&ws_mtx); #else mdw_flw_debug("not support pm wakelock\n"); #endif } void mdw_usr_ws_unlock(void) { #ifdef CONFIG_PM_SLEEP mutex_lock(&ws_mtx); ws_cnt--; if (mdw_usr_ws && !ws_cnt) { mdw_flw_debug("unlock\n"); __pm_relax(mdw_usr_ws); } mutex_unlock(&ws_mtx); #else mdw_flw_debug("not support pm wakelock\n"); #endif } static struct mdw_mem *mdw_usr_mem_create(struct apusys_mem *um, int mem_op) { int ret = 0; struct mdw_mem *mm = NULL; mm = vzalloc(sizeof(struct mdw_mem)); if (!mm) return NULL; /* alloc mem */ mdw_mem_u2k(um, &mm->kmem); switch (mem_op) { case APUSYS_MEM_PROP_ALLOC: ret = mdw_mem_alloc(mm); break; case APUSYS_MEM_PROP_IMPORT: ret = mdw_mem_import(mm); break; case APUSYS_MEM_PROP_MAP: ret = mdw_mem_map(mm); break; default: ret = -EINVAL; break; } if (ret) { if (ret == -ENOMEM) { mdw_usr_print_mem_usage(); //TODO Change to Tag apusys_aee_print("mem fail"); } vfree(mm); mm = NULL; } else { mdw_mem_k2u(&mm->kmem, um); } return mm; } static int mdw_usr_mem_delete(struct mdw_mem *mm) { int ret = 0; if (!mm) return -ENODATA; switch (mm->kmem.property) { case APUSYS_MEM_PROP_ALLOC: ret = mdw_mem_free(mm); break; case APUSYS_MEM_PROP_IMPORT: ret = mdw_mem_unimport(mm); break; case APUSYS_MEM_PROP_MAP: ret = mdw_mem_unmap(mm); break; default: ret = -EINVAL; break; } vfree(mm); return ret; } int mdw_usr_mem_alloc(struct apusys_mem *um, struct mdw_usr *u) { struct mdw_mem *mm = NULL; mm = mdw_usr_mem_create(um, APUSYS_MEM_PROP_ALLOC); if (!mm) return -ENOMEM; /* add to user list */ mutex_lock(&u->mtx); list_add_tail(&mm->u_item, &u->mem_list); u->iova_size = u->iova_size + mm->kmem.iova_size; if (u->iova_size_max < u->iova_size) u->iova_size_max = u->iova_size; mutex_unlock(&u->mtx); return 0; } int mdw_usr_mem_free(struct apusys_mem *um, struct mdw_usr *u) { struct list_head *tmp = NULL, *list_ptr = NULL; struct mdw_mem *mm = NULL; int ret = 0; /* get mem from usr list */ mutex_lock(&u->mtx); list_for_each_safe(list_ptr, tmp, &u->mem_list) { mm = list_entry(list_ptr, struct mdw_mem, u_item); if (mm->kmem.fd == um->fd && mm->kmem.mem_type == um->mem_type && mm->kmem.uidr == um->khandle) { mdw_mem_debug("get mem fd(%d) type(%d) kd(%x) idr(%u)\n", mm->kmem.fd, mm->kmem.mem_type, mm->kmem.khandle, mm->kmem.uidr); ret = mdw_mem_u2k_handle(&mm->kmem, um); if (ret) break; list_del(&mm->u_item); mdw_mem_delete_idr(um->khandle); break; } mm = NULL; } if (mm) u->iova_size = u->iova_size - mm->kmem.iova_size; else mdw_mem_debug("Not found fd(%d) type(%d) khandle(%d)\n", um->fd, um->mem_type, um->khandle); mutex_unlock(&u->mtx); return mdw_usr_mem_delete(mm); } int mdw_usr_mem_import(struct apusys_mem *um, struct mdw_usr *u) { struct mdw_mem *mm = NULL; mm = mdw_usr_mem_create(um, APUSYS_MEM_PROP_IMPORT); if (!mm) return -ENOMEM; /* add to user list */ mutex_lock(&u->mtx); list_add_tail(&mm->u_item, &u->mem_list); u->iova_size = u->iova_size + mm->kmem.iova_size; if (u->iova_size_max < u->iova_size) u->iova_size_max = u->iova_size; mutex_unlock(&u->mtx); return 0; } int mdw_usr_mem_map(struct apusys_mem *um, struct mdw_usr *u) { struct mdw_mem *mm = NULL; mm = mdw_usr_mem_create(um, APUSYS_MEM_PROP_MAP); if (!mm) return -ENOMEM; /* add to user list */ mutex_lock(&u->mtx); list_add_tail(&mm->u_item, &u->mem_list); u->iova_size = u->iova_size + mm->kmem.iova_size; if (u->iova_size_max < u->iova_size) u->iova_size_max = u->iova_size; mutex_unlock(&u->mtx); return 0; } int mdw_usr_dev_sec_alloc(int type, struct mdw_usr *u) { struct mdw_rsc_req req; struct mdw_dev_info *d = NULL; struct list_head *tmp = NULL, *list_ptr = NULL; int ret = 0; if (type >= APUSYS_DEVICE_RT || type < 0) return -ENODEV; mutex_lock(&u->mtx); mdw_drv_info("alloc dev(%d)\n", type); /* alloc rt device */ memset(&req, 0, sizeof(req)); req.num[type] = mdw_rsc_get_dev_num(type); if (req.num[type]) req.acq_bmp |= (1ULL << type); req.num[type + APUSYS_DEVICE_RT] = mdw_rsc_get_dev_num(type + APUSYS_DEVICE_RT); if (req.num[type + APUSYS_DEVICE_RT]) req.acq_bmp |= (1ULL << (type + APUSYS_DEVICE_RT)); req.total_num = req.num[type] + req.num[type + APUSYS_DEVICE_RT]; req.mode = MDW_DEV_INFO_GET_MODE_SYNC; req.policy = MDW_DEV_INFO_GET_POLICY_RR; ret = mdw_rsc_get_dev(&req); if (ret) { mdw_drv_err("alloc dev(%d/0x%llx)fail(%d)\n", type, req.acq_bmp, ret); goto out; } /* power off & on device */ mutex_lock(&req.mtx); list_for_each_safe(list_ptr, tmp, &req.d_list) { d = list_entry(list_ptr, struct mdw_dev_info, r_item); mdw_flw_debug("pwn on dev(%s%d)\n", d->name, d->idx); /* don't control power of rt device */ if (d->type >= APUSYS_DEVICE_RT) goto next; ret = d->pwr_off(d); if (ret) { mdw_drv_warn("pwr down dev(%s%d) fail(%d)\n", d->name, d->idx, ret); goto fail_pwr_down; } ret = d->pwr_on(d, 100, MDW_RSC_SET_PWR_ALLON); if (ret) { mdw_drv_warn("pwr on dev(%s%d) fail(%d)\n", d->name, d->idx, ret); goto fail_pwr_on; } next: list_del(&d->r_item); list_add_tail(&d->u_item, &u->sdev_list); } /* secure on */ d = mdw_rsc_get_dinfo(type, 0); if (!d) { mdw_drv_warn("no dev(%d/%d)\n", type, 0); ret = -ENODEV; goto fail_sec_on; } ret = d->sec_on(d); if (ret) { mdw_drv_warn("sec dev(%s%d) fail(%d)\n", d->name, d->idx, ret); goto fail_sec_on; } mutex_unlock(&req.mtx); goto out; fail_sec_on: fail_pwr_on: fail_pwr_down: list_for_each_safe(list_ptr, tmp, &req.d_list) { d = list_entry(list_ptr, struct mdw_dev_info, r_item); if (d->type < APUSYS_DEVICE_RT) d->pwr_off(d); list_del(&d->r_item); mdw_rsc_put_dev(d); } list_for_each_safe(list_ptr, tmp, &u->sdev_list) { d = list_entry(list_ptr, struct mdw_dev_info, u_item); if (d->type != type && d->type != type + APUSYS_DEVICE_RT) continue; if (d->type < APUSYS_DEVICE_RT) d->pwr_off(d); list_del(&d->u_item); mdw_rsc_put_dev(d); } mutex_unlock(&req.mtx); out: mutex_unlock(&u->mtx); mdw_drv_info("alloc dev(%d) done(%d)\n", type, ret); return ret; } int mdw_usr_dev_sec_free(int type, struct mdw_usr *u) { struct mdw_dev_info *d = NULL; struct list_head *tmp = NULL, *list_ptr = NULL; int ret = 0; d = mdw_rsc_get_dinfo(type, 0); if (!d) return -ENODEV; mutex_lock(&u->mtx); mdw_drv_info("free dev(%d)\n", type); ret = d->sec_off(d); if (ret) { mdw_drv_warn("sec off dev(%s%d) fail(%d)\n", d->name, d->idx, ret); goto out; } list_for_each_safe(list_ptr, tmp, &u->sdev_list) { d = list_entry(list_ptr, struct mdw_dev_info, u_item); if (d->type != type && d->type != type + APUSYS_DEVICE_RT) continue; if (d->type < APUSYS_DEVICE_RT) ret = d->pwr_off(d); if (ret) mdw_drv_warn("pwr down dev(%s%d) fail(%d)\n", d->name, d->idx, ret); list_del(&d->u_item); mdw_rsc_put_dev(d); } out: mutex_unlock(&u->mtx); mdw_drv_info("free dev(%d) done\n", type); return ret; } int mdw_usr_fw(struct apusys_ioctl_fw *f, int op) { struct apusys_kmem km; struct mdw_dev_info *d = NULL; int ret = 0; memset(&km, 0, sizeof(km)); km.fd = f->mem_fd; km.size = f->size; ret = mdw_mem_map_kva(&km); if (ret) goto out; ret = mdw_mem_map_iova(&km); if (ret) goto fail_map_iova; if (f->size + f->offset > km.iova_size) { ret = -EINVAL; goto fail_check_size; } d = mdw_rsc_get_dinfo(f->dev_type, f->idx); if (!d) { ret = -ENODEV; goto fail_get_dev; } ret = d->fw(d, f->magic, f->name, km.kva + f->offset, km.iova + f->offset, f->size, op); fail_get_dev: fail_check_size: mdw_mem_unmap_iova(&km); fail_map_iova: mdw_mem_unmap_kva(&km); out: return ret; } int mdw_usr_ucmd(struct apusys_ioctl_ucmd *uc) { struct apusys_kmem km; struct mdw_dev_info *d = NULL; int ret = 0; /* check offset to avoid oob */ if (uc->offset) { mdw_drv_err("don't support offset(%u)\n", uc->offset); return -EINVAL; } memset(&km, 0, sizeof(km)); km.fd = uc->mem_fd; km.size = uc->size; ret = mdw_mem_map_kva(&km); if (ret) goto out; ret = mdw_mem_map_iova(&km); if (ret) goto fail_map_iova; if (uc->size + uc->offset > km.iova_size) { ret = -EINVAL; goto fail_check_size; } d = mdw_rsc_get_dinfo(uc->dev_type, uc->idx); if (!d) { ret = -ENODEV; goto fail_get_dev; } ret = d->ucmd(d, km.kva, km.iova, uc->size); fail_get_dev: fail_check_size: mdw_mem_unmap_iova(&km); fail_map_iova: mdw_mem_unmap_kva(&km); out: return ret; } int mdw_usr_set_pwr(struct apusys_ioctl_power *pwr) { struct mdw_dev_info *d = NULL; d = mdw_rsc_get_dinfo(pwr->dev_type, pwr->idx); if (!d) return -ENODEV; return d->pwr_on(d, pwr->boost_val, MDW_RSC_SET_PWR_TIMEOUT); } static int mdw_usr_par_apu_cmd(struct mdw_apu_cmd *c) { struct mdw_apu_sc *sc; int ret = 0; mdw_trace_begin("cmd parse|cmd(0x%llx)", c->kid); while (1) { ret = cmd_parser->parse_cmd(c, &sc); /* check return value */ if (ret < 0) { /* get sc fail */ mdw_drv_err("parse cmd fail(%d)\n", ret); break; } else if (ret > 0) { if (sc) mdw_sched(sc); } else { if (sc) mdw_sched(sc); mdw_flw_debug("apu cmd(0x%llx) parse done(%d)\n", c->hdr->uid, ret); break; } } mdw_trace_end("cmd parse|cmd(0x%llx)", c->kid); return ret; } int mdw_usr_run_cmd_async(struct mdw_usr *u, struct apusys_ioctl_cmd *in) { int ret = 0; struct mdw_apu_cmd *c = NULL; /* check offset to avoid oob */ if (in->offset) { mdw_drv_err("don't support offset(%u)\n", in->offset); return -EINVAL; } c = cmd_parser->create_cmd(in->mem_fd, in->size, in->offset, u); if (!c) { ret = -EINVAL; goto create_cmd_fail; } c->pid = u->pid; c->tgid = u->tgid; c->usr = u; ret = mdw_usr_par_apu_cmd(c); if (ret) goto parse_cmd_fail; mutex_lock(&u->mtx); c->id = idr_alloc(&u->cmds_idr, c, 1, MDW_CMD_MAX, GFP_KERNEL); if (c->id <= 0) { mdw_drv_err("alloc cmd idr fail\n"); ret = -EFAULT; mutex_unlock(&u->mtx); goto parse_cmd_fail; } mutex_unlock(&u->mtx); mdw_flw_debug("cmd id(0x%llx/0x%llx/%d)\n", c->hdr->uid, c->kid, c->id); in->cmd_id = (unsigned long long)c->id; goto out; parse_cmd_fail: cmd_parser->abort_cmd(c); create_cmd_fail: out: return ret; } int mdw_wait_cmd(struct mdw_usr *u, struct mdw_apu_cmd *c) { int ret = 0, retry = 100, retry_time = 50; unsigned long timeout = 0; mdw_usr_ws_lock(); /* setup timeout */ if (c->hdr->hard_limit) timeout = msecs_to_jiffies(c->hdr->hard_limit); else timeout = msecs_to_jiffies(MDW_CMD_DEFAULT_TIMEOUT); rewait: ret = wait_for_completion_interruptible_timeout(&c->cmplt, timeout); if (ret == -ERESTARTSYS) { //restart if (retry) { if (!(retry % 20)) mdw_drv_warn("cmd(0x%llx) rewait(%d)\n", c->kid, retry); retry--; msleep(retry_time); goto rewait; } else { mdw_drv_warn("cmd(0x%llx) leak\n", c->kid); } } else if (ret == 0) { //timeout mdw_drv_err("cmd(0x%llx) timeout ms(%u)\n", c->kid, jiffies_to_msecs(timeout)); ret = -ETIME; } else if (ret < 0) { //error mdw_drv_err("cmd(0x%llx) fail(%d)\n", c->kid, ret); } /* Remove u_item anyway */ mutex_lock(&c->usr->mtx); if (c->file && c->file->private_data) { kfree(c->file->private_data); c->file->private_data = NULL; } mutex_unlock(&c->usr->mtx); if (ret < 0) { /* Wait fail handle */ if (mdw_dbg_get_prop(MDW_DBG_PROP_CMD_TIMEOUT_AEE)) mdw_dbg_aee("apusys midware wait timeout"); cmd_parser->abort_cmd(c); } else { /* Wait done, delete cmd */ if (c->sc_rets) { mdw_drv_err("cmd(0x%llx) sc(0x%llx) fail\n", c->kid, c->sc_rets); ret = -EIO; } else { ret = 0; } if (cmd_parser->delete_cmd(c)) mdw_drv_err("delete cmd fail\n"); } mdw_usr_ws_unlock(); return ret; } int mdw_usr_wait_cmd(struct mdw_usr *u, struct apusys_ioctl_cmd *in) { struct mdw_apu_cmd *c = NULL; /* get mem from usr list */ mutex_lock(&u->mtx); c = idr_find(&u->cmds_idr, in->cmd_id); if (c) idr_remove(&u->cmds_idr, c->id); mutex_unlock(&u->mtx); if (!c) { mdw_drv_err("no cmd(0x%llx) to wait\n", in->cmd_id); return -EINVAL; } mdw_flw_debug("wait cmd(0x%llx/0x%llx/%d)\n", c->hdr->uid, c->kid, c->id); return mdw_wait_cmd(u, c); } int mdw_usr_run_cmd_sync(struct mdw_usr *u, struct apusys_ioctl_cmd *in) { int ret = 0; ret = mdw_usr_run_cmd_async(u, in); if (ret) return ret; return mdw_usr_wait_cmd(u, in); } struct mdw_usr *mdw_usr_create(void) { struct mdw_usr *u = NULL; /* setup thread group */ mdw_rsc_set_thd_group(); u = vzalloc(sizeof(struct mdw_usr)); if (!u) return NULL; INIT_LIST_HEAD(&u->sdev_list); INIT_LIST_HEAD(&u->mem_list); mutex_init(&u->mtx); idr_init(&u->cmds_idr); get_task_comm(u->comm, current); u->pid = current->pid; u->tgid = current->tgid; u->id = (uint64_t)u; kref_init(&u->kref); mutex_lock(&u_mgr.mtx); list_add_tail(&u->m_item, &u_mgr.list); mutex_unlock(&u_mgr.mtx); return u; } bool mdw_user_check(struct mdw_usr *u) { struct mdw_usr *c; struct list_head *tmp = NULL, *list_ptr = NULL; /* Check user */ list_for_each_safe(list_ptr, tmp, &u_mgr.list) { c = list_entry(list_ptr, struct mdw_usr, m_item); if (c == u) break; c = NULL; } return c == NULL ? false:true; } void mdw_usr_get(struct mdw_usr *u) { kref_get(&u->kref); } int mdw_usr_put(struct mdw_usr *u) { return kref_put(&u->kref, mdw_usr_destroy); } void mdw_usr_destroy(struct kref *kref) { struct mdw_usr *u = container_of(kref, struct mdw_usr, kref); struct list_head *tmp = NULL, *list_ptr = NULL; struct mdw_mem *mm = NULL; struct mdw_apu_cmd *c = NULL; struct mdw_dev_info *d = NULL; uint64_t sbmp = 0; int nd_type = 0, id = 0; mutex_lock(&u_mgr.mtx); mutex_lock(&u->mtx); idr_for_each_entry(&u->cmds_idr, c, id) { idr_remove(&u->cmds_idr, id); mdw_drv_warn("residual cmd(0x%llx)\n", c->kid); cmd_parser->abort_cmd(c); } list_for_each_safe(list_ptr, tmp, &u->mem_list) { mm = list_entry(list_ptr, struct mdw_mem, u_item); list_del(&mm->u_item); mdw_drv_warn("residual mem(0x%x)\n", mm->kmem.iova); mdw_mem_delete_idr(mm->kmem.uidr); mdw_usr_mem_delete(mm); } list_for_each_safe(list_ptr, tmp, &u->sdev_list) { d = list_entry(list_ptr, struct mdw_dev_info, u_item); if (d->type >= APUSYS_DEVICE_MAX) { mdw_drv_err("known device type(%d)\n", d->type); continue; } /* disable secure mode */ nd_type = d->type % APUSYS_DEVICE_RT; if (!((1ULL << nd_type) & sbmp)) { sbmp |= (1ULL << nd_type); d->sec_off(d); mdw_drv_warn("dev(%d/%d) sec off\n", nd_type, d->type); } list_del(&d->u_item); mdw_drv_warn("residual dev(%s%d)\n", d->name, d->idx); d->pwr_off(d); mdw_rsc_put_dev(d); } mutex_unlock(&u->mtx); list_del(&u->m_item); mutex_unlock(&u_mgr.mtx); vfree(u); } int mdw_usr_init(void) { /*mem aee init*/ memset(&u_mem_aee, 0, sizeof(struct mdw_usr_mem_aee)); mutex_init(&u_mem_aee.mtx); /*stat init*/ memset(&u_stat, 0, sizeof(u_stat)); INIT_LIST_HEAD(&u_stat.list); mutex_init(&u_stat.mtx); /*mgr init*/ memset(&u_mgr, 0, sizeof(u_mgr)); INIT_LIST_HEAD(&u_mgr.list); mutex_init(&u_mgr.mtx); mdw_usr_ws_init(); cmd_parser = mdw_cmd_get_parser(); if (!cmd_parser) return -ENODEV; return 0; } void mdw_usr_exit(void) { mdw_usr_ws_destroy(); }