unplugged-kernel/drivers/misc/mediatek/apusys/midware/1.1/mdw_sched.c

650 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/kref.h>
#include <linux/kthread.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>
#include "mdw_cmn.h"
#include "mdw_queue.h"
#include "mdw_rsc.h"
#include "mdw_cmd.h"
#include "mdw_sched.h"
#include "mdw_dispr.h"
#include "mdw_trace.h"
#include "mnoc_api.h"
#include "reviser_export.h"
#include "mdw_tag.h"
#define CREATE_TRACE_POINTS
#include "mdw_events.h"
struct mdw_sched_mgr {
struct task_struct *task;
struct completion cmplt;
struct list_head ds_list; //done sc list
struct mutex mtx;
bool pause;
bool stop;
};
static struct mdw_cmd_parser *cmd_parser;
static struct mdw_sched_mgr ms_mgr;
#define MDW_EXEC_PRINT " pid(%d/%d) cmd(0x%llx/0x%llx-#%d/%u)"\
" dev(%d/%s-#%d) mp(0x%x/%u/%u/0x%llx) sched(%d/%u/%u/%u/%u/%d)"\
" mem(%lu/%d/0x%x/0x%x) boost(%u) time(%u/%u)"
static void mdw_sched_met_start(struct mdw_apu_sc *sc, struct mdw_dev_info *d)
{
mdw_trace_begin("apusys_scheduler|dev: %d_%d, cmd_id: 0x%08llx",
d->type,
d->idx,
sc->parent->kid);
}
static void mdw_sched_met_end(struct mdw_apu_sc *sc, struct mdw_dev_info *d,
int ret)
{
mdw_trace_end("apusys_scheduler|dev: %d_%d, cmd_id: 0x%08llx, ret:%d",
d->type,
d->idx,
sc->parent->kid, ret);
}
static void mdw_sched_trace(struct mdw_apu_sc *sc,
struct mdw_dev_info *d, struct apusys_cmd_hnd *h, int ret, int done)
{
struct mdw_tag_pack {
union {
uint64_t val;
struct {
uint16_t sc_idx;
uint16_t num_sc;
uint16_t dev_type;
uint16_t dev_idx;
} __packed s;
struct {
uint32_t pack_id;
uint32_t multi_num;
} __packed m;
struct {
uint16_t prio;
uint16_t soft_limit;
uint16_t hard_limit;
uint16_t suggest_time;
} __packed e;
struct {
uint16_t ctx;
uint16_t vlm_ctx;
uint16_t vlm_usage;
uint16_t tcm_real_size;
} __packed t;
};
};
struct mdw_tag_pack sc_info, multi_info, exec_info, tcm_info;
char state[16];
/* prefix */
memset(state, 0, sizeof(state));
if (!done) {
mdw_sched_met_start(sc, d);
if (snprintf(state, sizeof(state)-1, "start :") < 0)
return;
} else {
mdw_sched_met_end(sc, d, ret);
if (ret) {
if (snprintf(state, sizeof(state)-1, "fail :") < 0)
return;
} else {
if (snprintf(state, sizeof(state)-1, "done :") < 0)
return;
}
}
/* if err, use mdw_drv_err */
if (ret) {
mdw_drv_err("%s"MDW_EXEC_PRINT" ret(%d)\n",
state,
sc->parent->pid,
sc->parent->tgid,
sc->parent->hdr->uid,
sc->parent->kid,
sc->idx,
sc->parent->hdr->num_sc,
d->type,
d->name,
d->idx,
sc->hdr->pack_id,
h->multicore_idx,
sc->multi_total,
sc->multi_bmp,
sc->parent->hdr->priority,
sc->parent->hdr->soft_limit,
sc->parent->hdr->hard_limit,
sc->hdr->ip_time,
sc->hdr->suggest_time,
0,//sc->par_cmd->power_save,
sc->ctx,
sc->hdr->tcm_force,
sc->hdr->tcm_usage,
sc->real_tcm_usage,
h->boost_val,
h->ip_time,
sc->driver_time,
ret);
} else {
mdw_drv_debug("%s"MDW_EXEC_PRINT" ret(%d)\n",
state,
sc->parent->pid,
sc->parent->tgid,
sc->parent->hdr->uid,
sc->parent->kid,
sc->idx,
sc->parent->hdr->num_sc,
d->type,
d->name,
d->idx,
sc->hdr->pack_id,
h->multicore_idx,
sc->multi_total,
sc->multi_bmp,
sc->parent->hdr->priority,
sc->parent->hdr->soft_limit,
sc->parent->hdr->hard_limit,
sc->hdr->ip_time,
sc->hdr->suggest_time,
0,//sc->par_cmd->power_save,
sc->ctx,
sc->hdr->tcm_force,
sc->hdr->tcm_usage,
sc->real_tcm_usage,
h->boost_val,
h->ip_time,
sc->driver_time,
ret);
}
/* encode info for 12 args limitation */
sc_info.s.sc_idx = sc->idx;
sc_info.s.num_sc = sc->parent->hdr->num_sc;
sc_info.s.dev_type = d->type;
sc_info.s.dev_idx = d->idx;
multi_info.m.pack_id = sc->hdr->pack_id;
multi_info.m.multi_num = sc->multi_total;
exec_info.e.prio = sc->parent->hdr->priority;
exec_info.e.soft_limit = sc->parent->hdr->soft_limit;
exec_info.e.hard_limit = sc->parent->hdr->hard_limit;
exec_info.e.suggest_time = sc->hdr->suggest_time;
tcm_info.t.ctx = sc->ctx;
tcm_info.t.vlm_ctx = sc->hdr->mem_ctx;
tcm_info.t.vlm_usage = sc->hdr->tcm_usage;
tcm_info.t.tcm_real_size = sc->real_tcm_usage;
/* trace cmd end */
trace_mdw_cmd(done,
sc->parent->pid,
sc->parent->tgid,
sc->parent->kid,
sc_info.val,
d->name,
multi_info.val,
exec_info.val,
tcm_info.val,
h->boost_val,
h->ip_time,
ret);
}
#undef MDW_EXEC_PRINT
static int mdw_sched_sc_done(void)
{
struct mdw_apu_cmd *c = NULL;
struct mdw_apu_sc *sc = NULL, *s = NULL;
int ret = 0;
mdw_flw_debug("\n");
mdw_trace_begin("check done list|%s", __func__);
/* get done sc from done sc list */
mutex_lock(&ms_mgr.mtx);
s = list_first_entry_or_null(&ms_mgr.ds_list,
struct mdw_apu_sc, ds_item);
if (s)
list_del(&s->ds_item);
mutex_unlock(&ms_mgr.mtx);
if (!s) {
ret = -ENODATA;
goto out;
}
/* recv finished subcmd */
while (1) {
c = s->parent;
ret = cmd_parser->end_sc(s, &sc);
mdw_flw_debug("\n");
/* check return value */
if (ret) {
mdw_drv_err("parse done sc fail(%d)\n", ret);
complete(&c->cmplt);
break;
}
/* check parsed sc */
if (sc) {
/*
* finished sc parse ok, should be call
* again because residual sc
*/
mdw_flw_debug("sc(0x%llx-#%d)\n",
sc->parent->kid, sc->idx);
mdw_sched(sc);
} else {
/* finished sc parse done, break loop */
mdw_sched(NULL);
break;
}
};
out:
mdw_trace_end("check done list|%s", __func__);
return ret;
}
static void mdw_sched_enque_done_sc(struct kref *ref)
{
struct mdw_apu_sc *sc =
container_of(ref, struct mdw_apu_sc, multi_ref);
mdw_flw_debug("sc(0x%llx-#%d)\n", sc->parent->kid, sc->idx);
cmd_parser->put_ctx(sc);
mutex_lock(&ms_mgr.mtx);
list_add_tail(&sc->ds_item, &ms_mgr.ds_list);
mutex_unlock(&ms_mgr.mtx);
mdw_sched(NULL);
}
int mdw_sched_dev_routine(void *arg)
{
int ret = 0;
struct mdw_dev_info *d = (struct mdw_dev_info *)arg;
struct apusys_cmd_hnd h;
struct mdw_apu_sc *sc = NULL;
/* execute */
while (!kthread_should_stop() && d->stop == false) {
mdw_flw_debug("\n");
ret = wait_for_completion_interruptible(&d->cmplt);
if (ret)
goto next;
sc = (struct mdw_apu_sc *)d->sc;
if (!sc) {
mdw_drv_warn("no sc to exec\n");
goto next;
}
/* get mem ctx */
if (cmd_parser->get_ctx(sc)) {
mdw_drv_err("cmd(0x%llx-#%d) get ctx fail\n",
sc->parent->kid, sc->idx);
goto next;
}
/* construct cmd hnd */
mdw_queue_boost(sc);
if (cmd_parser->set_hnd(sc, d->idx, &h)) {
mdw_drv_err("cmd(0x%llx-#%d) set hnd fail\n",
sc->parent->kid, sc->idx);
goto next;
}
mdw_trace_begin("dev(%s-%d) exec|sc(0x%llx-%d) boost(%d/%u)",
d->name, d->idx, sc->parent->kid, sc->idx,
h.boost_val, sc->boost);
/*
* Execute reviser to switch VLM:
* Skip set context on preemptive command,
* context should be set by engine driver itself.
* Give engine a callback to set context id.
*/
if (d->type != APUSYS_DEVICE_MDLA &&
d->type != APUSYS_DEVICE_MDLA_RT) {
reviser_set_context(d->type,
d->idx, sc->ctx);
}
/* count qos start */
apu_cmd_qos_start(sc->parent->kid, sc->idx,
sc->type, d->idx, h.boost_val);
/* execute */
mdw_sched_trace(sc, d, &h, ret, 0);
getnstimeofday(&sc->ts_start);
ret = d->dev->send_cmd(APUSYS_CMD_EXECUTE, &h, d->dev);
getnstimeofday(&sc->ts_end);
sc->driver_time = mdw_cmn_get_time_diff(&sc->ts_start,
&sc->ts_end);
mdw_sched_trace(sc, d, &h, ret, 1);
/* clr hnd */
cmd_parser->clr_hnd(sc, &h);
/* count qos end */
mutex_lock(&sc->mtx);
sc->bw += apu_cmd_qos_end(sc->parent->kid, sc->idx,
sc->type, d->idx);
sc->ip_time = sc->ip_time > h.ip_time ? sc->ip_time : h.ip_time;
sc->boost = h.boost_val;
sc->status = ret;
mdw_flw_debug("multi bmp(0x%llx)\n", sc->multi_bmp);
mutex_unlock(&sc->mtx);
/* put device */
if (mdw_rsc_put_dev(d))
mdw_drv_err("put dev(%d-#%d) fail\n",
d->type, d->dev->idx);
mdw_flw_debug("sc(0x%llx-#%d) ref(%d)\n", sc->parent->kid,
sc->idx, kref_read(&sc->multi_ref));
kref_put(&sc->multi_ref, mdw_sched_enque_done_sc);
mdw_trace_end("dev(%s-%d) exec|boost(%d)",
d->name, d->idx, h.boost_val);
next:
mdw_flw_debug("done\n");
continue;
}
complete(&d->thd_done);
return 0;
}
static int mdw_sched_get_type(uint64_t bmp)
{
unsigned long tmp[BITS_TO_LONGS(APUSYS_DEVICE_MAX)];
memset(&tmp, 0, sizeof(tmp));
bitmap_from_arr32(tmp, (const uint32_t *)&bmp, APUSYS_DEVICE_MAX);
return find_last_bit((unsigned long *)&tmp, APUSYS_DEVICE_MAX);
}
static int mdw_sched_dispatch(struct mdw_apu_sc *sc)
{
int ret = 0, dev_num = 0, exec_num = 0;
mdw_trace_begin("mdw dispatch|sc(0x%llx-%d) pack(%d) multi(%d)",
sc->parent->kid, sc->idx, sc->hdr->pack_id, sc->parent->multi);
/* get dev num */
dev_num = mdw_rsc_get_dev_num(sc->type);
/* check exec #dev */
exec_num = cmd_parser->exec_core_num(sc);
exec_num = exec_num < dev_num ? exec_num : dev_num;
sc->multi_total = exec_num;
/* select dispatch policy */
if (sc->hdr->pack_id)
ret = mdw_dispr_pack(sc);
else if (sc->parent->multi == HDR_FLAG_MULTI_MULTI && exec_num >= 2)
ret = mdw_dispr_multi(sc);
else
ret = mdw_dispr_norm(sc);
mdw_trace_end("mdw dispatch|ret(%d)", ret);
return ret;
}
static int mdw_sched_routine(void *arg)
{
int ret = 0;
struct mdw_apu_sc *sc = NULL;
uint64_t bmp = 0;
int t = 0;
mdw_flw_debug("\n");
while (!kthread_should_stop() && !ms_mgr.stop) {
ret = wait_for_completion_interruptible(&ms_mgr.cmplt);
if (ret)
mdw_drv_warn("sched ret(%d)\n", ret);
if (ms_mgr.pause == true)
continue;
if (!mdw_sched_sc_done()) {
mdw_sched(NULL);
goto next;
}
mdw_dispr_check();
bmp = mdw_rsc_get_avl_bmp();
t = mdw_sched_get_type(bmp);
if (t < 0 || t >= APUSYS_DEVICE_MAX) {
mdw_flw_debug("nothing to sched(%d)\n", t);
goto next;
}
/* get queue */
sc = mdw_queue_pop(t);
if (!sc) {
mdw_drv_err("pop sc(%d) fail\n", t);
goto fail_pop_sc;
}
mdw_flw_debug("pop sc(0x%llx-#%d/%d/%llu)\n",
sc->parent->kid, sc->idx, sc->type, sc->period);
/* dispatch cmd */
ret = mdw_sched_dispatch(sc);
if (ret) {
mdw_flw_debug("sc(0x%llx-#%d) dispatch fail",
sc->parent->kid, sc->idx);
goto fail_exec_sc;
}
goto next;
fail_exec_sc:
if (mdw_queue_insert(sc, true)) {
mdw_drv_err("sc(0x%llx-#%d) insert fail\n",
sc->parent->kid, sc->idx);
}
fail_pop_sc:
next:
mdw_flw_debug("\n");
if (mdw_rsc_get_avl_bmp())
mdw_sched(NULL);
}
mdw_drv_warn("schedule thread end\n");
return 0;
}
int mdw_sched(struct mdw_apu_sc *sc)
{
int ret = 0;
/* no input sc, trigger sched thread only */
if (!sc) {
complete(&ms_mgr.cmplt);
return 0;
}
/* insert sc to queue */
ret = mdw_queue_insert(sc, false);
if (ret) {
mdw_drv_err("sc(0x%llx-#%d) enque fail\n",
sc->parent->kid, sc->idx);
return ret;
}
complete(&ms_mgr.cmplt);
mdw_flw_debug("\n");
return 0;
}
int mdw_sched_pause(void)
{
struct mdw_dev_info *d = NULL;
int type = 0, idx = 0, ret = 0, i = 0;
if (ms_mgr.pause == true) {
mdw_drv_warn("pause ready\n");
return 0;
}
ms_mgr.pause = true;
for (type = 0; type < APUSYS_DEVICE_MAX; type++) {
for (idx = 0; idx < mdw_rsc_get_dev_num(type); idx++) {
d = mdw_rsc_get_dinfo(type, idx);
if (!d)
continue;
ret = d->suspend(d);
if (ret) {
mdw_drv_err("dev(%s%d) suspend fail(%d)\n",
d->name, d->idx, ret);
goto fail_sched_pause;
}
}
}
mdw_drv_info("pause\n");
goto out;
fail_sched_pause:
for (idx -= 1; idx >= 0; idx--) {
d = mdw_rsc_get_dinfo(type, idx);
if (!d)
continue;
if (d->resume(d)) {
mdw_drv_err("dev(%s%d) resume fail(%d)\n",
d->name, d->idx, ret);
}
}
for (i = 0; i < type; i++) {
for (idx = 0; idx < mdw_rsc_get_dev_num(type); idx++) {
d = mdw_rsc_get_dinfo(type, idx);
if (!d)
continue;
if (d->resume(d)) {
mdw_drv_err("dev(%s%d) resume fail(%d)\n",
d->name, d->idx, ret);
}
}
}
ms_mgr.pause = false;
mdw_drv_warn("resume\n");
out:
return ret;
}
void mdw_sched_restart(void)
{
struct mdw_dev_info *d = NULL;
int type = 0, idx = 0, ret = 0;
if (ms_mgr.pause == false)
mdw_drv_warn("resume ready\n");
else
mdw_drv_info("resume\n");
for (type = 0; type < APUSYS_DEVICE_MAX; type++) {
for (idx = 0; idx < mdw_rsc_get_dev_num(type); idx++) {
d = mdw_rsc_get_dinfo(type, idx);
if (!d)
continue;
ret = d->resume(d);
if (ret)
mdw_drv_err("dev(%s%d) resume fail(%d)\n",
d->name, d->idx, ret);
}
}
ms_mgr.pause = false;
mdw_sched(NULL);
}
void mdw_sched_set_thd_group(void)
{
struct file *fd;
char buf[8];
mm_segment_t oldfs;
oldfs = get_fs();
set_fs(get_ds());
fd = filp_open(APUSYS_THD_TASK_FILE_PATH, O_WRONLY, 0);
if (IS_ERR(fd)) {
mdw_drv_debug("don't support low latency group\n");
goto out;
}
memset(buf, 0, sizeof(buf));
if (snprintf(buf, sizeof(buf)-1, "%d", ms_mgr.task->pid) < 0)
goto fail_set_name;
vfs_write(fd, (__force const char __user *)buf,
sizeof(buf), &fd->f_pos);
mdw_drv_debug("setup worker(%d/%s) to group\n",
ms_mgr.task->pid, buf);
fail_set_name:
filp_close(fd, NULL);
out:
set_fs(oldfs);
}
int mdw_sched_init(void)
{
memset(&ms_mgr, 0, sizeof(ms_mgr));
ms_mgr.pause = false;
ms_mgr.stop = false;
init_completion(&ms_mgr.cmplt);
INIT_LIST_HEAD(&ms_mgr.ds_list);
mutex_init(&ms_mgr.mtx);
cmd_parser = mdw_cmd_get_parser();
if (!cmd_parser)
return -ENODEV;
ms_mgr.task = kthread_run(mdw_sched_routine,
NULL, "apusys_sched");
if (!ms_mgr.task) {
mdw_drv_err("create kthread(sched) fail\n");
return -ENOMEM;
}
mdw_dispr_init();
return 0;
}
void mdw_sched_exit(void)
{
ms_mgr.stop = true;
mdw_sched(NULL);
mdw_dispr_exit();
}