From c002048f4f184d2910ee75884c0ea06993255a4e Mon Sep 17 00:00:00 2001 From: Li Huang Date: Thu, 31 Mar 2022 16:23:00 +0800 Subject: [PATCH] video: rockchip: rga3: fixup crash on rga_job_next 1. Free job resource when user exit to call release 2. Support rga_seesion. Signed-off-by: Li Huang Change-Id: I87d01ce24d80769bb379c6117798063133e922b3 --- drivers/video/rockchip/rga3/include/rga_drv.h | 17 ++ drivers/video/rockchip/rga3/include/rga_job.h | 6 +- drivers/video/rockchip/rga3/rga_drv.c | 187 +++++++++++++++--- drivers/video/rockchip/rga3/rga_job.c | 33 +++- 4 files changed, 210 insertions(+), 33 deletions(-) diff --git a/drivers/video/rockchip/rga3/include/rga_drv.h b/drivers/video/rockchip/rga3/include/rga_drv.h index 3bddad5666f9..6e2799ffd222 100644 --- a/drivers/video/rockchip/rga3/include/rga_drv.h +++ b/drivers/video/rockchip/rga3/include/rga_drv.h @@ -248,10 +248,15 @@ struct rga2_mmu_other_t { struct rga_scheduler_t; +struct rga_session { + int id; +}; + struct rga_job { struct list_head head; struct rga_scheduler_t *scheduler; + struct rga_session *session; struct rga_req rga_command_base; uint32_t cmd_reg[32 * 8]; @@ -334,6 +339,8 @@ struct rga_scheduler_t { struct rga_internal_ctx_t { struct rga_req *cached_cmd; + struct rga_session *session; + int cmd_num; int flags; int id; @@ -370,6 +377,14 @@ struct rga_pending_ctx_manager { int ctx_count; }; +struct rga_session_manager { + struct mutex lock; + + struct idr ctx_id_idr; + + int session_cnt; +}; + struct rga_drvdata_t { struct miscdevice miscdev; @@ -386,6 +401,8 @@ struct rga_drvdata_t { /* rga_job pending manager, import by RGA_START_CONFIG */ struct rga_pending_ctx_manager *pend_ctx_manager; + struct rga_session_manager *session_manager; + #ifdef CONFIG_ROCKCHIP_RGA_ASYNC struct rga_fence_context *fence_ctx; #endif diff --git a/drivers/video/rockchip/rga3/include/rga_job.h b/drivers/video/rockchip/rga3/include/rga_job.h index 34ed2ea0f1dc..ac12af4e4c40 100644 --- a/drivers/video/rockchip/rga3/include/rga_job.h +++ b/drivers/video/rockchip/rga3/include/rga_job.h @@ -23,6 +23,8 @@ enum job_flags { struct rga_scheduler_t *rga_job_get_scheduler(struct rga_job *job); +void rga_job_session_destroy(struct rga_session *session); + void rga_job_done(struct rga_scheduler_t *scheduler, int ret); int rga_job_commit(struct rga_req *rga_command_base, struct rga_internal_ctx_t *ctx); @@ -36,7 +38,9 @@ int rga_ctx_manager_remove(struct rga_pending_ctx_manager **ctx_manager_session) struct rga_internal_ctx_t * rga_internal_ctx_lookup(struct rga_pending_ctx_manager *ctx_manager, uint32_t id); -uint32_t rga_internal_ctx_alloc_to_get_idr_id(uint32_t flags); + +uint32_t rga_internal_ctx_alloc_to_get_idr_id(uint32_t flags, struct rga_session *session); + void rga_internal_ctx_kref_release(struct kref *ref); int rga_internal_ctx_config_by_user_ctx(struct rga_user_ctx_t *user_ctx); int rga_internal_ctx_commit_by_user_ctx(struct rga_user_ctx_t *user_ctx); diff --git a/drivers/video/rockchip/rga3/rga_drv.c b/drivers/video/rockchip/rga3/rga_drv.c index 019e9ac284f3..4cfb97162655 100644 --- a/drivers/video/rockchip/rga3/rga_drv.c +++ b/drivers/video/rockchip/rga3/rga_drv.c @@ -394,6 +394,131 @@ static void rga_power_disable_all(void) #endif //CONFIG_ROCKCHIP_FPGA +static int rga_session_manager_init(struct rga_session_manager **session_manager_ptr) +{ + struct rga_session_manager *session_manager = NULL; + + *session_manager_ptr = kzalloc(sizeof(struct rga_session_manager), GFP_KERNEL); + if (*session_manager_ptr == NULL) { + pr_err("can not kzalloc for rga_session_manager\n"); + return -ENOMEM; + } + + session_manager = *session_manager_ptr; + + mutex_init(&session_manager->lock); + + idr_init_base(&session_manager->ctx_id_idr, 1); + + return 0; +} + +/* + * Called at driver close to release the rga session's id references. + */ +static int rga_session_free_remove_idr_cb(int id, void *ptr, void *data) +{ + struct rga_session *session = ptr; + + idr_remove(&rga_drvdata->session_manager->ctx_id_idr, session->id); + kfree(session); + + return 0; +} + +static int rga_session_free_remove_idr(struct rga_session *session) +{ + struct rga_session_manager *session_manager; + + session_manager = rga_drvdata->session_manager; + + mutex_lock(&session_manager->lock); + + session_manager->session_cnt--; + idr_remove(&session_manager->ctx_id_idr, session->id); + + mutex_unlock(&session_manager->lock); + + return 0; +} + +static int rga_session_manager_remove(struct rga_session_manager **session_manager_ptr) +{ + struct rga_session_manager *session_manager = *session_manager_ptr; + + mutex_lock(&session_manager->lock); + + idr_for_each(&session_manager->ctx_id_idr, &rga_session_free_remove_idr_cb, session_manager); + idr_destroy(&session_manager->ctx_id_idr); + + mutex_unlock(&session_manager->lock); + + kfree(*session_manager_ptr); + + *session_manager_ptr = NULL; + + return 0; +} + +static struct rga_session *rga_session_init(void) +{ + struct rga_session_manager *session_manager = NULL; + struct rga_session *session = kzalloc(sizeof(*session), GFP_KERNEL); + + session_manager = rga_drvdata->session_manager; + if (session_manager == NULL) { + pr_err("rga_session_manager is null!\n"); + kfree(session); + return NULL; + } + + mutex_lock(&session_manager->lock); + + idr_preload(GFP_KERNEL); + session->id = idr_alloc(&session_manager->ctx_id_idr, session, 1, 0, GFP_ATOMIC); + session_manager->session_cnt++; + idr_preload_end(); + + mutex_unlock(&session_manager->lock); + + return session; +} + +static int rga_session_deinit(struct rga_session *session) +{ + pid_t pid; + int ctx_id; + struct rga_pending_ctx_manager *ctx_manager; + struct rga_internal_ctx_t *ctx; + + pid = current->pid; + + ctx_manager = rga_drvdata->pend_ctx_manager; + + mutex_lock(&ctx_manager->lock); + + idr_for_each_entry(&ctx_manager->ctx_id_idr, ctx, ctx_id) { + + mutex_unlock(&ctx_manager->lock); + + if (session == ctx->session) { + pr_err("[pid:%d] destroy ctx[%d] when the user exits", pid, ctx->id); + kref_put(&ctx->refcount, rga_internal_ctx_kref_release); + } + + mutex_lock(&ctx_manager->lock); + } + + mutex_unlock(&ctx_manager->lock); + + rga_session_free_remove_idr(session); + rga_job_session_destroy(session); + + kfree(session); + + return 0; +} + static long rga_ioctl_import_buffer(unsigned long arg) { int i; @@ -517,7 +642,7 @@ err_free_external_buffer: return ret; } -static long rga_ioctl_cmd_start(unsigned long arg) +static long rga_ioctl_cmd_start(unsigned long arg, struct rga_session *session) { uint32_t rga_user_ctx_id; uint32_t flags; @@ -526,7 +651,7 @@ static long rga_ioctl_cmd_start(unsigned long arg) if (copy_from_user(&flags, (void *)arg, sizeof(uint32_t))) ret = -EFAULT; - rga_user_ctx_id = rga_internal_ctx_alloc_to_get_idr_id(flags); + rga_user_ctx_id = rga_internal_ctx_alloc_to_get_idr_id(flags, session); if (copy_to_user((void *)arg, &rga_user_ctx_id, sizeof(uint32_t))) ret = -EFAULT; @@ -638,9 +763,9 @@ static long rga_ioctl(struct file *file, uint32_t cmd, unsigned long arg) char version[16] = { 0 }; struct rga_version_t driver_version; struct rga_hw_versions_t hw_versions; - struct rga_internal_ctx_t ctx; + struct rga_internal_ctx_t *ctx; - memset(&ctx, 0x0, sizeof(ctx)); + struct rga_session *session = file->private_data; if (!rga) { pr_err("rga_drvdata is null, rga is not init\n"); @@ -663,10 +788,17 @@ static long rga_ioctl(struct file *file, uint32_t cmd, unsigned long arg) if (DEBUGGER_EN(MSG)) rga_cmd_print_debug_info(&req_rga); - ctx.sync_mode = cmd; - ctx.use_batch_mode = false; + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + pr_err("can not kzalloc for ctx!\n"); + return -ENOMEM; + } - ret = rga_job_commit(&req_rga, &ctx); + ctx->sync_mode = cmd; + ctx->use_batch_mode = false; + ctx->session = session; + + ret = rga_job_commit(&req_rga, ctx); if (ret < 0) { if (ret == -ERESTARTSYS) { if (DEBUGGER_EN(MSG)) @@ -685,6 +817,8 @@ static long rga_ioctl(struct file *file, uint32_t cmd, unsigned long arg) break; } + kfree(ctx); + break; case RGA_CACHE_FLUSH: case RGA_FLUSH: @@ -779,7 +913,7 @@ static long rga_ioctl(struct file *file, uint32_t cmd, unsigned long arg) break; case RGA_START_CONFIG: - ret = rga_ioctl_cmd_start(arg); + ret = rga_ioctl_cmd_start(arg, session); break; @@ -852,35 +986,22 @@ static int rga_debugger_remove(struct rga_debugger **debugger_p) static int rga_open(struct inode *inode, struct file *file) { + struct rga_session *session = NULL; + + session = rga_session_init(); + if (!session) + return -ENOMEM; + + file->private_data = (void *)session; + return nonseekable_open(inode, file); } static int rga_release(struct inode *inode, struct file *file) { - pid_t pid; - int ctx_id; - struct rga_pending_ctx_manager *ctx_manager; - struct rga_internal_ctx_t *ctx; + struct rga_session *session = file->private_data; - pid = current->pid; - - ctx_manager = rga_drvdata->pend_ctx_manager; - - mutex_lock(&ctx_manager->lock); - - idr_for_each_entry(&ctx_manager->ctx_id_idr, ctx, ctx_id) { - - mutex_unlock(&ctx_manager->lock); - - if (pid == ctx->pid) { - pr_err("[pid:%d] destroy ctx[%d] when the user exits", pid, ctx->id); - kref_put(&ctx->refcount, rga_internal_ctx_kref_release); - } - - mutex_lock(&ctx_manager->lock); - } - - mutex_unlock(&ctx_manager->lock); + rga_session_deinit(session); return 0; } @@ -1377,6 +1498,8 @@ static int __init rga_init(void) rga_ctx_manager_init(&rga_drvdata->pend_ctx_manager); + rga_session_manager_init(&rga_drvdata->session_manager); + #ifdef CONFIG_ROCKCHIP_RGA_ASYNC rga_fence_context_init(&rga_drvdata->fence_ctx); #endif @@ -1408,6 +1531,8 @@ static void __exit rga_exit(void) rga_ctx_manager_remove(&rga_drvdata->pend_ctx_manager); + rga_session_manager_remove(&rga_drvdata->session_manager); + rga_cancel_timer(); platform_driver_unregister(&rga3_core0_driver); diff --git a/drivers/video/rockchip/rga3/rga_job.c b/drivers/video/rockchip/rga3/rga_job.c index cf657ab9fc3b..9655dea34b37 100644 --- a/drivers/video/rockchip/rga3/rga_job.c +++ b/drivers/video/rockchip/rga3/rga_job.c @@ -119,6 +119,35 @@ static void rga_job_free(struct rga_job *job) free_page((unsigned long)job); } +void rga_job_session_destroy(struct rga_session *session) +{ + struct rga_scheduler_t *scheduler = NULL; + struct rga_job *job_pos, *job_q; + int i; + + unsigned long flags; + + for (i = 0; i < rga_drvdata->num_of_scheduler; i++) { + scheduler = rga_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); + + rga_job_free(job_pos); + + spin_lock_irqsave(&scheduler->irq_lock, flags); + } + } + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + } +} + static int rga_job_cleanup(struct rga_job *job) { ktime_t now = ktime_get(); @@ -638,6 +667,7 @@ int rga_job_commit(struct rga_req *rga_command_base, struct rga_internal_ctx_t * job->use_batch_mode = ctx->use_batch_mode; job->ctx_id = ctx->id; + job->session = ctx->session; /* * because fd can not pass on other thread, @@ -908,7 +938,7 @@ int rga_internal_ctx_signal(struct rga_scheduler_t *scheduler, struct rga_job *j return 0; } -uint32_t rga_internal_ctx_alloc_to_get_idr_id(uint32_t flags) +uint32_t rga_internal_ctx_alloc_to_get_idr_id(uint32_t flags, struct rga_session *session) { struct rga_pending_ctx_manager *ctx_manager; struct rga_internal_ctx_t *ctx; @@ -944,6 +974,7 @@ uint32_t rga_internal_ctx_alloc_to_get_idr_id(uint32_t flags) kref_init(&ctx->refcount); ctx->pid = current->pid; ctx->flags = flags; + ctx->session = session; mutex_unlock(&ctx_manager->lock);