1029 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1029 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * Copyright (C) Rockchip Electronics Co., Ltd.
 | |
|  *
 | |
|  * Author: Huang Lee <Putin.li@rock-chips.com>
 | |
|  */
 | |
| 
 | |
| #define pr_fmt(fmt) "rve_job: " fmt
 | |
| 
 | |
| #include "rve_job.h"
 | |
| #include "rve_fence.h"
 | |
| #include "rve_reg.h"
 | |
| 
 | |
| struct rve_job *
 | |
| rve_scheduler_get_pending_job_list(struct rve_scheduler_t *scheduler)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 	struct rve_job *job;
 | |
| 
 | |
| 	spin_lock_irqsave(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 	job = list_first_entry_or_null(&scheduler->todo_list,
 | |
| 		struct rve_job, head);
 | |
| 
 | |
| 	spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 	return job;
 | |
| }
 | |
| 
 | |
| struct rve_job *
 | |
| rve_scheduler_get_running_job(struct rve_scheduler_t *scheduler)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 	struct rve_job *job;
 | |
| 
 | |
| 	spin_lock_irqsave(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 	job = scheduler->running_job;
 | |
| 
 | |
| 	spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 	return job;
 | |
| }
 | |
| 
 | |
| static void rve_scheduler_set_pid_info(struct rve_job *job, ktime_t now)
 | |
| {
 | |
| 	struct rve_scheduler_t *scheduler;
 | |
| 	bool pid_match_flag = false;
 | |
| 	ktime_t tmp = 0;
 | |
| 	int pid_mark = 0, i;
 | |
| 
 | |
| 	scheduler = rve_job_get_scheduler(job);
 | |
| 
 | |
| 	for (i = 0; i < RVE_MAX_PID_INFO; i++) {
 | |
| 		if (scheduler->session.pid_info[i].pid == 0)
 | |
| 			scheduler->session.pid_info[i].pid = job->pid;
 | |
| 
 | |
| 		if (scheduler->session.pid_info[i].pid == job->pid) {
 | |
| 			pid_match_flag = true;
 | |
| 			scheduler->session.pid_info[i].hw_time_total +=
 | |
| 				(job->hw_running_time - now);
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (!pid_match_flag) {
 | |
| 		for (i = 0; i < RVE_MAX_PID_INFO; i++) {
 | |
| 			if (i == 0) {
 | |
| 				tmp = scheduler->session.pid_info[i].hw_time_total;
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			if (tmp > scheduler->session.pid_info[i].hw_time_total)
 | |
| 				pid_mark = i;
 | |
| 		}
 | |
| 
 | |
| 		scheduler->session.pid_info[pid_mark].pid = job->pid;
 | |
| 		scheduler->session.pid_info[pid_mark].hw_time_total +=
 | |
| 					ktime_us_delta(now, job->hw_running_time);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| struct rve_scheduler_t *rve_job_get_scheduler(struct rve_job *job)
 | |
| {
 | |
| 	return job->scheduler;
 | |
| }
 | |
| 
 | |
| struct rve_internal_ctx_t *rve_job_get_internal_ctx(struct rve_job *job)
 | |
| {
 | |
| 	return job->ctx;
 | |
| }
 | |
| 
 | |
| static void rve_job_free(struct rve_job *job)
 | |
| {
 | |
| #ifdef CONFIG_SYNC_FILE
 | |
| 	if (job->out_fence)
 | |
| 		dma_fence_put(job->out_fence);
 | |
| #endif
 | |
| 
 | |
| 	free_page((unsigned long)job);
 | |
| }
 | |
| 
 | |
| static int rve_job_cleanup(struct rve_job *job)
 | |
| {
 | |
| 	ktime_t now = ktime_get();
 | |
| 
 | |
| 	if (DEBUGGER_EN(TIME)) {
 | |
| 		pr_info("(pid:%d) job clean use time = %lld\n", job->pid,
 | |
| 			ktime_us_delta(now, job->timestamp));
 | |
| 	}
 | |
| 	rve_job_free(job);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void rve_job_session_destroy(struct rve_session *session)
 | |
| {
 | |
| 	struct rve_scheduler_t *scheduler = NULL;
 | |
| 	struct rve_job *job_pos, *job_q;
 | |
| 	int i;
 | |
| 
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	for (i = 0; i < rve_drvdata->num_of_scheduler; i++) {
 | |
| 		scheduler = rve_drvdata->scheduler[i];
 | |
| 
 | |
| 		spin_lock_irqsave(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 		list_for_each_entry_safe(job_pos, job_q, &scheduler->todo_list, head) {
 | |
| 			if (session == job_pos->session) {
 | |
| 				list_del(&job_pos->head);
 | |
| 
 | |
| 				spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 				rve_job_free(job_pos);
 | |
| 
 | |
| 				spin_lock_irqsave(&scheduler->irq_lock, flags);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static struct rve_job *rve_job_alloc(struct rve_internal_ctx_t *ctx)
 | |
| {
 | |
| 	struct rve_job *job = NULL;
 | |
| 
 | |
| 	job = (struct rve_job *)get_zeroed_page(GFP_KERNEL | GFP_DMA32);
 | |
| 	if (!job)
 | |
| 		return NULL;
 | |
| 
 | |
| #ifdef CONFIG_SYNC_FILE
 | |
| 	spin_lock_init(&job->fence_lock);
 | |
| #endif
 | |
| 	INIT_LIST_HEAD(&job->head);
 | |
| 
 | |
| 	job->timestamp = ktime_get();
 | |
| 	job->pid = current->pid;
 | |
| 	job->regcmd_data = &ctx->regcmd_data[ctx->running_job_count];
 | |
| 
 | |
| 	job->scheduler = rve_drvdata->scheduler[0];
 | |
| 	job->core = rve_drvdata->scheduler[0]->core;
 | |
| 	job->ctx = ctx;
 | |
| 	ctx->scheduler = job->scheduler;
 | |
| 	job->session = ctx->session;
 | |
| 
 | |
| 	if (ctx->priority > 0) {
 | |
| 		if (ctx->priority > RVE_SCHED_PRIORITY_MAX)
 | |
| 			job->priority = RVE_SCHED_PRIORITY_MAX;
 | |
| 		else
 | |
| 			job->priority = ctx->priority;
 | |
| 	}
 | |
| 
 | |
| 	return job;
 | |
| }
 | |
| 
 | |
| static void rve_job_dump_info(struct rve_job *job)
 | |
| {
 | |
| 	pr_info("job: priority = %d, core = %d\n",
 | |
| 		job->priority, job->core);
 | |
| }
 | |
| 
 | |
| static int rve_job_run(struct rve_job *job)
 | |
| {
 | |
| 	struct rve_scheduler_t *scheduler;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	scheduler = rve_job_get_scheduler(job);
 | |
| 
 | |
| #ifndef RVE_PD_AWAYS_ON
 | |
| 	/* enable power */
 | |
| 	ret = rve_power_enable(scheduler);
 | |
| 	if (ret < 0) {
 | |
| 		pr_err("power enable failed");
 | |
| 		return ret;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	ret = scheduler->ops->init_reg(job);
 | |
| 	if (ret < 0) {
 | |
| 		pr_err("init reg failed");
 | |
| 		goto failed;
 | |
| 	}
 | |
| 
 | |
| 	ret = scheduler->ops->set_reg(job, scheduler);
 | |
| 	if (ret < 0) {
 | |
| 		pr_err("set reg failed");
 | |
| 		goto failed;
 | |
| 	}
 | |
| 
 | |
| 	/* for debug */
 | |
| 	if (DEBUGGER_EN(MSG))
 | |
| 		rve_job_dump_info(job);
 | |
| 
 | |
| 	return ret;
 | |
| 
 | |
| failed:
 | |
| #ifndef RVE_PD_AWAYS_ON
 | |
| 	rve_power_disable(scheduler);
 | |
| #endif
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void rve_job_next(struct rve_scheduler_t *scheduler)
 | |
| {
 | |
| 	struct rve_job *job = NULL;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| next_job:
 | |
| 	spin_lock_irqsave(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 	if (scheduler->running_job ||
 | |
| 		list_empty(&scheduler->todo_list)) {
 | |
| 		spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	job = list_first_entry(&scheduler->todo_list, struct rve_job, head);
 | |
| 
 | |
| 	list_del_init(&job->head);
 | |
| 
 | |
| 	scheduler->job_count--;
 | |
| 
 | |
| 	scheduler->running_job = job;
 | |
| 
 | |
| 	spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 	job->ret = rve_job_run(job);
 | |
| 
 | |
| 	/* If some error before hw run */
 | |
| 	if (job->ret < 0) {
 | |
| 		pr_err("some error on rve_job_run before hw start, %s(%d)\n",
 | |
| 			__func__, __LINE__);
 | |
| 
 | |
| 		spin_lock_irqsave(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 		scheduler->running_job = NULL;
 | |
| 
 | |
| 		spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 		rve_internal_ctx_signal(job);
 | |
| 
 | |
| 		goto next_job;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void rve_job_finish_and_next(struct rve_job *job, int ret)
 | |
| {
 | |
| 	ktime_t now = ktime_get();
 | |
| 	struct rve_scheduler_t *scheduler;
 | |
| 
 | |
| 	job->ret = ret;
 | |
| 
 | |
| 	scheduler = rve_job_get_scheduler(job);
 | |
| 
 | |
| 	if (DEBUGGER_EN(TIME)) {
 | |
| 		pr_info("hw use time = %lld\n", ktime_us_delta(now, job->hw_running_time));
 | |
| 		pr_info("(pid:%d) job done use time = %lld\n", job->pid,
 | |
| 			ktime_us_delta(now, job->timestamp));
 | |
| 	}
 | |
| 
 | |
| 	rve_internal_ctx_signal(job);
 | |
| 
 | |
| 	rve_job_next(scheduler);
 | |
| 
 | |
| #ifndef RVE_PD_AWAYS_ON
 | |
| 	rve_power_disable(scheduler);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| void rve_job_done(struct rve_scheduler_t *scheduler, int ret)
 | |
| {
 | |
| 	struct rve_job *job;
 | |
| 	unsigned long flags;
 | |
| 	u32 error_flag;
 | |
| 	uint32_t *cmd_reg;
 | |
| 	int i;
 | |
| 
 | |
| 	ktime_t now = ktime_get();
 | |
| 
 | |
| 	spin_lock_irqsave(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 	job = scheduler->running_job;
 | |
| 	scheduler->running_job = NULL;
 | |
| 
 | |
| 	scheduler->timer.busy_time += ktime_us_delta(now, job->hw_recoder_time);
 | |
| 
 | |
| 	rve_scheduler_set_pid_info(job, now);
 | |
| 
 | |
| 	spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 	spin_lock_irqsave(&job->ctx->lock, flags);
 | |
| 
 | |
| 	job->ctx->debug_info.max_cost_time_per_sec =
 | |
| 		max(job->ctx->debug_info.last_job_hw_use_time,
 | |
| 			job->ctx->debug_info.max_cost_time_per_sec);
 | |
| 	job->ctx->debug_info.last_job_hw_use_time = ktime_us_delta(now, job->hw_running_time);
 | |
| 	job->ctx->debug_info.hw_time_total += job->ctx->debug_info.last_job_hw_use_time;
 | |
| 	job->ctx->debug_info.last_job_use_time = ktime_us_delta(now, job->timestamp);
 | |
| 
 | |
| 	spin_unlock_irqrestore(&job->ctx->lock, flags);
 | |
| 
 | |
| 	/* record CFG REG copy to user */
 | |
| 	cmd_reg = job->regcmd_data->cmd_reg;
 | |
| 	for (i = 0; i < 40; i++)
 | |
| 		cmd_reg[18 + i] = rve_read(RVE_CFG_REG + i * 4, scheduler);
 | |
| 
 | |
| 	error_flag = rve_read(RVE_SWREG6_IVE_WORK_STA, scheduler);
 | |
| 
 | |
| 	rve_get_monitor_info(job);
 | |
| 
 | |
| 	if (DEBUGGER_EN(MSG))
 | |
| 		pr_info("irq thread work_status[%.8x]\n", error_flag);
 | |
| 
 | |
| 	/* disable llp enable, TODO: support pause mode */
 | |
| 	rve_write(0, RVE_SWLTB3_ENABLE, scheduler);
 | |
| 
 | |
| 	rve_job_finish_and_next(job, ret);
 | |
| }
 | |
| 
 | |
| static void rve_job_timeout_clean(struct rve_scheduler_t *scheduler)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 	struct rve_job *job = NULL;
 | |
| 	ktime_t now = ktime_get();
 | |
| 
 | |
| 	spin_lock_irqsave(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 	job = scheduler->running_job;
 | |
| 	if (job && (job->flags & RVE_ASYNC) &&
 | |
| 	   (ktime_to_ms(ktime_sub(now, job->hw_running_time)) >= RVE_ASYNC_TIMEOUT_DELAY)) {
 | |
| 		scheduler->running_job = NULL;
 | |
| 
 | |
| 		spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 		scheduler->ops->soft_reset(scheduler);
 | |
| 
 | |
| 		rve_internal_ctx_signal(job);
 | |
| 
 | |
| #ifndef RVE_PD_AWAYS_ON
 | |
| 		rve_power_disable(scheduler);
 | |
| #endif
 | |
| 	} else {
 | |
| 		spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static struct rve_scheduler_t *rve_job_schedule(struct rve_job *job)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 	struct rve_scheduler_t *scheduler = NULL;
 | |
| 	struct rve_job *job_pos;
 | |
| 	bool first_match = 0;
 | |
| 
 | |
| 	scheduler = rve_job_get_scheduler(job);
 | |
| 	if (scheduler == NULL) {
 | |
| 		pr_err("failed to get scheduler, %s(%d)\n", __func__, __LINE__);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	/* Only async will timeout clean */
 | |
| 	rve_job_timeout_clean(scheduler);
 | |
| 
 | |
| 	spin_lock_irqsave(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 	/* priority policy set by userspace */
 | |
| 	if (list_empty(&scheduler->todo_list)
 | |
| 		|| (job->priority == RVE_SCHED_PRIORITY_DEFAULT)) {
 | |
| 		list_add_tail(&job->head, &scheduler->todo_list);
 | |
| 	} else {
 | |
| 		list_for_each_entry(job_pos, &scheduler->todo_list, head) {
 | |
| 			if (job->priority > job_pos->priority &&
 | |
| 					(!first_match)) {
 | |
| 				list_add(&job->head, &job_pos->head);
 | |
| 				first_match = true;
 | |
| 			}
 | |
| 
 | |
| 			/*
 | |
| 			 * Increase the priority of subsequent tasks
 | |
| 			 * after inserting into the list
 | |
| 			 */
 | |
| 			if (first_match)
 | |
| 				job_pos->priority++;
 | |
| 		}
 | |
| 
 | |
| 		if (!first_match)
 | |
| 			list_add_tail(&job->head, &scheduler->todo_list);
 | |
| 	}
 | |
| 
 | |
| 	scheduler->job_count++;
 | |
| 
 | |
| 	spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 	rve_job_next(scheduler);
 | |
| 
 | |
| 	return scheduler;
 | |
| }
 | |
| 
 | |
| static void rve_job_abort_running(struct rve_job *job)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 	struct rve_scheduler_t *scheduler;
 | |
| 
 | |
| 	scheduler = rve_job_get_scheduler(job);
 | |
| 
 | |
| 	spin_lock_irqsave(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 	/* invalid job */
 | |
| 	if (job == scheduler->running_job)
 | |
| 		scheduler->running_job = NULL;
 | |
| 
 | |
| 	spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 	rve_job_cleanup(job);
 | |
| }
 | |
| 
 | |
| static void rve_job_abort_invalid(struct rve_job *job)
 | |
| {
 | |
| 	rve_job_cleanup(job);
 | |
| }
 | |
| 
 | |
| static inline int rve_job_wait(struct rve_job *job)
 | |
| {
 | |
| 	struct rve_scheduler_t *scheduler;
 | |
| 
 | |
| 	int left_time;
 | |
| 	ktime_t now;
 | |
| 	int ret;
 | |
| 
 | |
| 	scheduler = rve_job_get_scheduler(job);
 | |
| 
 | |
| 	left_time = wait_event_timeout(scheduler->job_done_wq,
 | |
| 		job->ctx->finished_job_count == job->ctx->cmd_num,
 | |
| 		RVE_SYNC_TIMEOUT_DELAY * job->ctx->cmd_num);
 | |
| 
 | |
| 	switch (left_time) {
 | |
| 	case 0:
 | |
| 		pr_err("%s timeout", __func__);
 | |
| 		scheduler->ops->soft_reset(scheduler);
 | |
| 		ret = -EBUSY;
 | |
| 		break;
 | |
| 	case -ERESTARTSYS:
 | |
| 		ret = -ERESTARTSYS;
 | |
| 		break;
 | |
| 	default:
 | |
| 		ret = 0;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	now = ktime_get();
 | |
| 
 | |
| 	if (DEBUGGER_EN(TIME))
 | |
| 		pr_info("%s use time = %lld\n", __func__,
 | |
| 			 ktime_to_us(ktime_sub(now, job->hw_running_time)));
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| #ifdef CONFIG_SYNC_FILE
 | |
| static void rve_job_input_fence_signaled(struct dma_fence *fence,
 | |
| 					 struct dma_fence_cb *_waiter)
 | |
| {
 | |
| 	struct rve_fence_waiter *waiter = (struct rve_fence_waiter *)_waiter;
 | |
| 	struct rve_scheduler_t *scheduler = NULL;
 | |
| 
 | |
| 	ktime_t now;
 | |
| 
 | |
| 	now = ktime_get();
 | |
| 
 | |
| 	if (DEBUGGER_EN(TIME))
 | |
| 		pr_err("rve job wait in_fence signal use time = %lld\n",
 | |
| 			ktime_to_us(ktime_sub(now, waiter->job->timestamp)));
 | |
| 
 | |
| 	scheduler = rve_job_schedule(waiter->job);
 | |
| 
 | |
| 	if (scheduler == NULL)
 | |
| 		pr_err("failed to get scheduler, %s(%d)\n", __func__, __LINE__);
 | |
| 
 | |
| 	kfree(waiter);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int rve_job_config_by_user_ctx(struct rve_user_ctx_t *user_ctx)
 | |
| {
 | |
| 	struct rve_pending_ctx_manager *ctx_manager;
 | |
| 	struct rve_internal_ctx_t *ctx;
 | |
| 	int ret = 0;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	ctx_manager = rve_drvdata->pend_ctx_manager;
 | |
| 
 | |
| 	ctx = rve_internal_ctx_lookup(ctx_manager, user_ctx->id);
 | |
| 	if (IS_ERR_OR_NULL(ctx)) {
 | |
| 		pr_err("can not find internal ctx from id[%d]", user_ctx->id);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	spin_lock_irqsave(&ctx->lock, flags);
 | |
| 
 | |
| 	if (ctx->is_running) {
 | |
| 		pr_err("can not re-config when ctx is running");
 | |
| 		spin_unlock_irqrestore(&ctx->lock, flags);
 | |
| 		return -EFAULT;
 | |
| 	}
 | |
| 
 | |
| 	spin_unlock_irqrestore(&ctx->lock, flags);
 | |
| 
 | |
| 	/* TODO: user cmd_num */
 | |
| 	user_ctx->cmd_num = 1;
 | |
| 
 | |
| 	if (ctx->regcmd_data == NULL) {
 | |
| 		ctx->regcmd_data = kmalloc_array(user_ctx->cmd_num,
 | |
| 			sizeof(struct rve_cmd_reg_array_t), GFP_KERNEL);
 | |
| 		if (ctx->regcmd_data == NULL) {
 | |
| 			pr_err("regcmd_data alloc error!\n");
 | |
| 			return -ENOMEM;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (unlikely(copy_from_user(ctx->regcmd_data,
 | |
| 					u64_to_user_ptr(user_ctx->regcmd_data),
 | |
| 				    sizeof(struct rve_cmd_reg_array_t) * user_ctx->cmd_num))) {
 | |
| 		pr_err("regcmd_data copy_from_user failed\n");
 | |
| 		ret = -EFAULT;
 | |
| 
 | |
| 		goto err_free_regcmd_data;
 | |
| 	}
 | |
| 
 | |
| 	ctx->sync_mode = user_ctx->sync_mode;
 | |
| 	ctx->cmd_num = user_ctx->cmd_num;
 | |
| 	ctx->priority = user_ctx->priority;
 | |
| 	ctx->in_fence_fd = user_ctx->in_fence_fd;
 | |
| 
 | |
| 	/* TODO: cmd addr */
 | |
| 
 | |
| 	return ret;
 | |
| 
 | |
| err_free_regcmd_data:
 | |
| 	kfree(ctx->regcmd_data);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int rve_job_commit_by_user_ctx(struct rve_user_ctx_t *user_ctx)
 | |
| {
 | |
| 	struct rve_pending_ctx_manager *ctx_manager;
 | |
| 	struct rve_internal_ctx_t *ctx;
 | |
| 	int ret = 0;
 | |
| 	unsigned long flags;
 | |
| 	int i;
 | |
| 
 | |
| 	ctx_manager = rve_drvdata->pend_ctx_manager;
 | |
| 
 | |
| 	ctx = rve_internal_ctx_lookup(ctx_manager, user_ctx->id);
 | |
| 	if (IS_ERR_OR_NULL(ctx)) {
 | |
| 		pr_err("can not find internal ctx from id[%d]", user_ctx->id);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	spin_lock_irqsave(&ctx->lock, flags);
 | |
| 
 | |
| 	if (ctx->is_running) {
 | |
| 		pr_err("can not re-config when ctx is running");
 | |
| 		spin_unlock_irqrestore(&ctx->lock, flags);
 | |
| 		return -EFAULT;
 | |
| 	}
 | |
| 
 | |
| 	/* Reset */
 | |
| 	ctx->finished_job_count = 0;
 | |
| 	ctx->running_job_count = 0;
 | |
| 	ctx->is_running = true;
 | |
| 	ctx->disable_auto_cancel = user_ctx->disable_auto_cancel;
 | |
| 
 | |
| 	ctx->sync_mode = user_ctx->sync_mode;
 | |
| 	if (ctx->sync_mode == 0)
 | |
| 		ctx->sync_mode = RVE_SYNC;
 | |
| 
 | |
| 	spin_unlock_irqrestore(&ctx->lock, flags);
 | |
| 
 | |
| 	for (i = 0; i < ctx->cmd_num; i++) {
 | |
| 		ret = rve_job_commit(ctx);
 | |
| 		if (ret < 0) {
 | |
| 			pr_err("rve_job_commit failed, i = %d\n", i);
 | |
| 			return -EFAULT;
 | |
| 		}
 | |
| 
 | |
| 		ctx->running_job_count++;
 | |
| 	}
 | |
| 
 | |
| 	user_ctx->out_fence_fd = ctx->out_fence_fd;
 | |
| 
 | |
| 	if (unlikely(copy_to_user(u64_to_user_ptr(user_ctx->regcmd_data),
 | |
| 				  ctx->regcmd_data,
 | |
| 				  sizeof(struct rve_cmd_reg_array_t) * ctx->cmd_num))) {
 | |
| 		pr_err("ctx->regcmd_data copy_to_user failed\n");
 | |
| 		return -EFAULT;
 | |
| 	}
 | |
| 
 | |
| 	if (!ctx->disable_auto_cancel && ctx->sync_mode == RVE_SYNC)
 | |
| 		kref_put(&ctx->refcount, rve_internal_ctx_kref_release);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int rve_job_cancel_by_user_ctx(uint32_t ctx_id)
 | |
| {
 | |
| 	struct rve_pending_ctx_manager *ctx_manager;
 | |
| 	struct rve_internal_ctx_t *ctx;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	ctx_manager = rve_drvdata->pend_ctx_manager;
 | |
| 
 | |
| 	ctx = rve_internal_ctx_lookup(ctx_manager, ctx_id);
 | |
| 	if (IS_ERR_OR_NULL(ctx)) {
 | |
| 		pr_err("can not find internal ctx from id[%d]", ctx_id);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	kref_put(&ctx->refcount, rve_internal_ctx_kref_release);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int rve_job_commit(struct rve_internal_ctx_t *ctx)
 | |
| {
 | |
| 	struct rve_job *job = NULL;
 | |
| 	struct rve_scheduler_t *scheduler = NULL;
 | |
| #ifdef CONFIG_SYNC_FILE
 | |
| 	struct dma_fence *in_fence;
 | |
| #endif
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	job = rve_job_alloc(ctx);
 | |
| 	if (!job) {
 | |
| 		pr_err("failed to alloc rve job!\n");
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 
 | |
| 	if (ctx->sync_mode == RVE_ASYNC) {
 | |
| #ifdef CONFIG_SYNC_FILE
 | |
| 		job->flags |= RVE_ASYNC;
 | |
| 
 | |
| 		if (!ctx->out_fence) {
 | |
| 			ret = rve_out_fence_alloc(job);
 | |
| 			if (ret) {
 | |
| 				rve_job_free(job);
 | |
| 				return ret;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		ctx->out_fence = job->out_fence;
 | |
| 
 | |
| 		ctx->out_fence_fd = rve_out_fence_get_fd(job);
 | |
| 
 | |
| 		if (ctx->out_fence_fd < 0)
 | |
| 			pr_err("out fence get fd failed");
 | |
| 
 | |
| 		if (DEBUGGER_EN(MSG))
 | |
| 			pr_info("in_fence_fd = %d", ctx->in_fence_fd);
 | |
| 
 | |
| 		/* if input fence is valiable */
 | |
| 		if (ctx->in_fence_fd > 0) {
 | |
| 			in_fence = rve_get_input_fence(
 | |
| 				ctx->in_fence_fd);
 | |
| 			if (!in_fence) {
 | |
| 				pr_err("%s: failed to get input dma_fence\n",
 | |
| 					 __func__);
 | |
| 				rve_job_free(job);
 | |
| 				return ret;
 | |
| 			}
 | |
| 
 | |
| 			/* close input fence fd */
 | |
| 			close_fd(ctx->in_fence_fd);
 | |
| 
 | |
| 			ret = dma_fence_get_status(in_fence);
 | |
| 			/* ret = 1: fence has been signaled */
 | |
| 			if (ret == 1) {
 | |
| 				scheduler = rve_job_schedule(job);
 | |
| 
 | |
| 				if (scheduler == NULL) {
 | |
| 					pr_err("failed to get scheduler, %s(%d)\n",
 | |
| 						 __func__, __LINE__);
 | |
| 					goto invalid_job;
 | |
| 				}
 | |
| 				/* if input fence is valid */
 | |
| 			} else if (ret == 0) {
 | |
| 				ret = rve_add_dma_fence_callback(job,
 | |
| 					in_fence, rve_job_input_fence_signaled);
 | |
| 				if (ret < 0) {
 | |
| 					pr_err("%s: failed to add fence callback\n",
 | |
| 						 __func__);
 | |
| 					rve_job_free(job);
 | |
| 					return ret;
 | |
| 				}
 | |
| 			} else {
 | |
| 				pr_err("%s: fence status error\n", __func__);
 | |
| 				rve_job_free(job);
 | |
| 				return ret;
 | |
| 			}
 | |
| 		} else {
 | |
| 			scheduler = rve_job_schedule(job);
 | |
| 
 | |
| 			if (scheduler == NULL) {
 | |
| 				pr_err("failed to get scheduler, %s(%d)\n",
 | |
| 					 __func__, __LINE__);
 | |
| 				goto invalid_job;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return ret;
 | |
| #else
 | |
| 		pr_err("can not support ASYNC mode, please enable CONFIG_SYNC_FILE");
 | |
| 		return -EFAULT;
 | |
| #endif
 | |
| 
 | |
| 	/* RVE_SYNC: wait until job finish */
 | |
| 	} else if (ctx->sync_mode == RVE_SYNC) {
 | |
| 		scheduler = rve_job_schedule(job);
 | |
| 
 | |
| 		if (scheduler == NULL) {
 | |
| 			pr_err("failed to get scheduler, %s(%d)\n", __func__,
 | |
| 				 __LINE__);
 | |
| 			goto invalid_job;
 | |
| 		}
 | |
| 
 | |
| 		ret = job->ret;
 | |
| 		if (ret < 0) {
 | |
| 			pr_err("some error on job, %s(%d)\n", __func__,
 | |
| 				 __LINE__);
 | |
| 			goto running_job_abort;
 | |
| 		}
 | |
| 
 | |
| 		ret = rve_job_wait(job);
 | |
| 		if (ret < 0)
 | |
| 			goto running_job_abort;
 | |
| 
 | |
| 		rve_job_cleanup(job);
 | |
| 	}
 | |
| 	return ret;
 | |
| 
 | |
| invalid_job:
 | |
| 	rve_job_abort_invalid(job);
 | |
| 	return ret;
 | |
| 
 | |
| /* only used by SYNC mode */
 | |
| running_job_abort:
 | |
| 	rve_job_abort_running(job);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| struct rve_internal_ctx_t *
 | |
| rve_internal_ctx_lookup(struct rve_pending_ctx_manager *ctx_manager, uint32_t id)
 | |
| {
 | |
| 	struct rve_internal_ctx_t *ctx = NULL;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	spin_lock_irqsave(&ctx_manager->lock, flags);
 | |
| 
 | |
| 	ctx = idr_find(&ctx_manager->ctx_id_idr, id);
 | |
| 
 | |
| 	spin_unlock_irqrestore(&ctx_manager->lock, flags);
 | |
| 
 | |
| 	if (ctx == NULL)
 | |
| 		pr_err("can not find internal ctx from id[%d]", id);
 | |
| 
 | |
| 	return ctx;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Called at driver close to release the internal ctx's id references.
 | |
|  */
 | |
| static int rve_internal_ctx_free_remove_idr_cb(int id, void *ptr, void *data)
 | |
| {
 | |
| 	struct rve_internal_ctx_t *ctx = ptr;
 | |
| 
 | |
| 	idr_remove(&rve_drvdata->pend_ctx_manager->ctx_id_idr, ctx->id);
 | |
| 	kfree(ctx);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int rve_internal_ctx_free_remove_idr(struct rve_internal_ctx_t *ctx)
 | |
| {
 | |
| 	struct rve_pending_ctx_manager *ctx_manager;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	ctx_manager = rve_drvdata->pend_ctx_manager;
 | |
| 
 | |
| 	spin_lock_irqsave(&ctx_manager->lock, flags);
 | |
| 
 | |
| 	ctx_manager->ctx_count--;
 | |
| 	idr_remove(&ctx_manager->ctx_id_idr, ctx->id);
 | |
| 
 | |
| 	spin_unlock_irqrestore(&ctx_manager->lock, flags);
 | |
| 
 | |
| 	kfree(ctx);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int rve_internal_ctx_signal(struct rve_job *job)
 | |
| {
 | |
| 	struct rve_internal_ctx_t *ctx;
 | |
| 	struct rve_scheduler_t *scheduler;
 | |
| 	int finished_job_count;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	scheduler = rve_job_get_scheduler(job);
 | |
| 	if (scheduler == NULL) {
 | |
| 		pr_err("failed to get scheduler, %s(%d)\n", __func__, __LINE__);
 | |
| 		return -EFAULT;
 | |
| 	}
 | |
| 
 | |
| 	ctx = rve_job_get_internal_ctx(job);
 | |
| 	if (IS_ERR_OR_NULL(ctx)) {
 | |
| 		pr_err("can not find internal ctx");
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	ctx->regcmd_data = job->regcmd_data;
 | |
| 
 | |
| 	spin_lock_irqsave(&ctx->lock, flags);
 | |
| 
 | |
| 	finished_job_count = ++ctx->finished_job_count;
 | |
| 
 | |
| 	spin_unlock_irqrestore(&ctx->lock, flags);
 | |
| 
 | |
| 	if (finished_job_count >= ctx->cmd_num) {
 | |
| #ifdef CONFIG_SYNC_FILE
 | |
| 		if (ctx->out_fence)
 | |
| 			dma_fence_signal(ctx->out_fence);
 | |
| #endif
 | |
| 
 | |
| 		job->flags |= RVE_JOB_DONE;
 | |
| 
 | |
| 		wake_up(&scheduler->job_done_wq);
 | |
| 
 | |
| 		spin_lock_irqsave(&ctx->lock, flags);
 | |
| 
 | |
| 		ctx->is_running = false;
 | |
| 		ctx->out_fence = NULL;
 | |
| 
 | |
| 		spin_unlock_irqrestore(&ctx->lock, flags);
 | |
| 
 | |
| 		if (job->flags & RVE_ASYNC) {
 | |
| 			rve_job_cleanup(job);
 | |
| 			if (!ctx->disable_auto_cancel)
 | |
| 				kref_put(&ctx->refcount, rve_internal_ctx_kref_release);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int rve_internal_ctx_alloc_to_get_idr_id(struct rve_session *session)
 | |
| {
 | |
| 	struct rve_pending_ctx_manager *ctx_manager;
 | |
| 	struct rve_internal_ctx_t *ctx;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
 | |
| 	if (ctx == NULL) {
 | |
| 		pr_err("can not kzalloc for rve_pending_ctx_manager\n");
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 
 | |
| 	ctx_manager = rve_drvdata->pend_ctx_manager;
 | |
| 	if (ctx_manager == NULL) {
 | |
| 		pr_err("rve_pending_ctx_manager is null!\n");
 | |
| 		goto failed;
 | |
| 	}
 | |
| 
 | |
| 	spin_lock_init(&ctx->lock);
 | |
| 
 | |
| 	/*
 | |
| 	 * Get the user-visible handle using idr. Preload and perform
 | |
| 	 * allocation under our spinlock.
 | |
| 	 */
 | |
| 
 | |
| 	idr_preload(GFP_KERNEL);
 | |
| 
 | |
| 	spin_lock_irqsave(&ctx_manager->lock, flags);
 | |
| 
 | |
| 	ctx->id = idr_alloc(&ctx_manager->ctx_id_idr, ctx, 1, 0, GFP_ATOMIC);
 | |
| 	if (ctx->id < 0) {
 | |
| 		pr_err("idr_alloc failed");
 | |
| 		spin_unlock_irqrestore(&ctx_manager->lock, flags);
 | |
| 		goto failed;
 | |
| 	}
 | |
| 
 | |
| 	ctx_manager->ctx_count++;
 | |
| 
 | |
| 	ctx->debug_info.pid = current->pid;
 | |
| 	ctx->debug_info.timestamp = ktime_get();
 | |
| 	ctx->session = session;
 | |
| 
 | |
| 	spin_unlock_irqrestore(&ctx_manager->lock, flags);
 | |
| 
 | |
| 	idr_preload_end();
 | |
| 
 | |
| 	ctx->regcmd_data = NULL;
 | |
| 
 | |
| 	kref_init(&ctx->refcount);
 | |
| 
 | |
| 	return ctx->id;
 | |
| 
 | |
| failed:
 | |
| 	kfree(ctx);
 | |
| 	return -EFAULT;
 | |
| }
 | |
| 
 | |
| void rve_internal_ctx_kref_release(struct kref *ref)
 | |
| {
 | |
| 	struct rve_internal_ctx_t *ctx;
 | |
| 	struct rve_scheduler_t *scheduler = NULL;
 | |
| 	struct rve_job *job_pos, *job_q, *job;
 | |
| 	int i;
 | |
| 	bool need_reset = false;
 | |
| 	unsigned long flags;
 | |
| 	ktime_t now = ktime_get();
 | |
| 
 | |
| 	ctx = container_of(ref, struct rve_internal_ctx_t, refcount);
 | |
| 
 | |
| 	spin_lock_irqsave(&ctx->lock, flags);
 | |
| 	if (!ctx->is_running || ctx->finished_job_count >= ctx->cmd_num) {
 | |
| 		spin_unlock_irqrestore(&ctx->lock, flags);
 | |
| 		goto free_ctx;
 | |
| 	}
 | |
| 	spin_unlock_irqrestore(&ctx->lock, flags);
 | |
| 
 | |
| 	for (i = 0; i < rve_drvdata->num_of_scheduler; i++) {
 | |
| 		scheduler = rve_drvdata->scheduler[i];
 | |
| 
 | |
| 		spin_lock_irqsave(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 		list_for_each_entry_safe(job_pos, job_q, &scheduler->todo_list, head) {
 | |
| 			if (ctx->id == job_pos->ctx->id) {
 | |
| 				job = job_pos;
 | |
| 				list_del_init(&job_pos->head);
 | |
| 
 | |
| 				scheduler->job_count--;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/* for load */
 | |
| 		if (scheduler->running_job) {
 | |
| 			job = scheduler->running_job;
 | |
| 
 | |
| 			if (job->ctx->id == ctx->id) {
 | |
| 				scheduler->running_job = NULL;
 | |
| 				scheduler->timer.busy_time += ktime_us_delta(now, job->hw_recoder_time);
 | |
| 				need_reset = true;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 		if (need_reset) {
 | |
| 			pr_err("reset core[%d] by user cancel", scheduler->core);
 | |
| 			scheduler->ops->soft_reset(scheduler);
 | |
| 
 | |
| 			rve_job_finish_and_next(job, 0);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| free_ctx:
 | |
| 	kfree(ctx->regcmd_data);
 | |
| 	rve_internal_ctx_free_remove_idr(ctx);
 | |
| }
 | |
| 
 | |
| int rve_ctx_manager_init(struct rve_pending_ctx_manager **ctx_manager_session)
 | |
| {
 | |
| 	struct rve_pending_ctx_manager *ctx_manager = NULL;
 | |
| 
 | |
| 	*ctx_manager_session = kzalloc(sizeof(struct rve_pending_ctx_manager), GFP_KERNEL);
 | |
| 	if (*ctx_manager_session == NULL) {
 | |
| 		pr_err("can not kzalloc for rve_pending_ctx_manager\n");
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 
 | |
| 	ctx_manager = *ctx_manager_session;
 | |
| 
 | |
| 	spin_lock_init(&ctx_manager->lock);
 | |
| 
 | |
| 	idr_init_base(&ctx_manager->ctx_id_idr, 1);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int rve_ctx_manager_remove(struct rve_pending_ctx_manager **ctx_manager_session)
 | |
| {
 | |
| 	struct rve_pending_ctx_manager *ctx_manager = *ctx_manager_session;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	spin_lock_irqsave(&ctx_manager->lock, flags);
 | |
| 
 | |
| 	idr_for_each(&ctx_manager->ctx_id_idr, &rve_internal_ctx_free_remove_idr_cb, ctx_manager);
 | |
| 	idr_destroy(&ctx_manager->ctx_id_idr);
 | |
| 
 | |
| 	spin_unlock_irqrestore(&ctx_manager->lock, flags);
 | |
| 
 | |
| 	kfree(*ctx_manager_session);
 | |
| 
 | |
| 	*ctx_manager_session = NULL;
 | |
| 
 | |
| 	return 0;
 | |
| }
 |