From: Herman Chen Date: Wed, 27 May 2015 02:26:33 +0000 (+0800) Subject: vcodec: fix enable race condition when power on/off X-Git-Tag: firefly_0821_release~4103 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=41dad664eb222680a2ac16b268596bc1debaf6c3;p=firefly-linux-kernel-4.4.55.git vcodec: fix enable race condition when power on/off When multi-instances power on at same time the condition enabled maybe set mutli-times. Use atomic function to replace orginal read/compare/change operation to avoid multi-instances. Also add power on/off count to debugfs for checking Signed-off-by: Herman Chen --- diff --git a/arch/arm/mach-rockchip/vcodec_service.c b/arch/arm/mach-rockchip/vcodec_service.c index 548f9556c5a9..8e0143547b55 100644 --- a/arch/arm/mach-rockchip/vcodec_service.c +++ b/arch/arm/mach-rockchip/vcodec_service.c @@ -416,7 +416,9 @@ typedef struct vpu_service_info { struct list_head done; /* link to link_reg in struct vpu_reg */ struct list_head session; /* link to list_session in struct vpu_session */ atomic_t total_running; - bool enabled; + atomic_t enabled; + atomic_t power_on_cnt; + atomic_t power_off_cnt; vpu_reg *reg_codec; vpu_reg *reg_pproc; vpu_reg *reg_resev; @@ -522,9 +524,10 @@ static void vcodec_enter_mode(struct vpu_subdev_data *data) #if defined(CONFIG_VCODEC_MMU) if (data->mmu_dev && !test_bit(MMU_ACTIVATED, &data->state)) { set_bit(MMU_ACTIVATED, &data->state); - BUG_ON(!pservice->enabled); - if (pservice->enabled) + if (atomic_read(&pservice->enabled)) rockchip_iovmm_activate(data->dev); + else + BUG_ON(!atomic_read(&pservice->enabled)); } #endif return; @@ -565,9 +568,10 @@ static void vcodec_enter_mode(struct vpu_subdev_data *data) #if defined(CONFIG_VCODEC_MMU) if (data->mmu_dev && !test_bit(MMU_ACTIVATED, &data->state)) { set_bit(MMU_ACTIVATED, &data->state); - BUG_ON(!pservice->enabled); - if (pservice->enabled) + if (atomic_read(&pservice->enabled)) rockchip_iovmm_activate(data->dev); + else + BUG_ON(!atomic_read(&pservice->enabled)); } #endif pservice->prev_mode = pservice->curr_mode; @@ -618,8 +622,10 @@ static int vpu_get_clk(struct vpu_service_info *pservice) } if (pservice->pd_video == NULL) { pservice->pd_video = devm_clk_get(pservice->dev, "pd_video"); - if (IS_ERR(pservice->pd_video)) + if (IS_ERR(pservice->pd_video)) { pservice->pd_video = NULL; + dev_info(pservice->dev, "do not have pd_video\n"); + } } break; default: @@ -683,9 +689,10 @@ static void vpu_reset(struct vpu_subdev_data *data) #if defined(CONFIG_VCODEC_MMU) if (data->mmu_dev && test_bit(MMU_ACTIVATED, &data->state)) { clear_bit(MMU_ACTIVATED, &data->state); - BUG_ON(!pservice->enabled); - if (pservice->enabled) + if (atomic_read(&pservice->enabled)) rockchip_iovmm_deactivate(data->dev); + else + BUG_ON(!atomic_read(&pservice->enabled)); } #endif } @@ -713,10 +720,10 @@ static void vpu_service_power_off(struct vpu_service_info *pservice) { int total_running; struct vpu_subdev_data *data = NULL, *n; - if (!pservice->enabled) + int ret = atomic_add_unless(&pservice->enabled, -1, 0); + if (!ret) return; - pservice->enabled = false; total_running = atomic_read(&pservice->total_running); if (total_running) { pr_alert("alert: power off when %d task running!!\n", total_running); @@ -750,6 +757,7 @@ static void vpu_service_power_off(struct vpu_service_info *pservice) clk_disable_unprepare(pservice->clk_cabac); #endif + atomic_add(1, &pservice->power_off_cnt); wake_unlock(&pservice->wake_lock); pr_info("done\n"); } @@ -775,6 +783,7 @@ static void vpu_power_off_work(struct work_struct *work_s) static void vpu_service_power_on(struct vpu_service_info *pservice) { + int ret; static ktime_t last; ktime_t now = ktime_get(); if (ktime_to_ns(ktime_sub(now, last)) > NSEC_PER_SEC) { @@ -782,7 +791,8 @@ static void vpu_service_power_on(struct vpu_service_info *pservice) vpu_queue_power_off_work(pservice); last = now; } - if (pservice->enabled) + ret = atomic_add_unless(&pservice->enabled, 1, 1); + if (!ret) return ; pr_info("%s: power on\n", dev_name(pservice->dev)); @@ -807,7 +817,7 @@ static void vpu_service_power_on(struct vpu_service_info *pservice) #endif udelay(10); - pservice->enabled = true; + atomic_add(1, &pservice->power_on_cnt); wake_lock(&pservice->wake_lock); } @@ -2163,7 +2173,9 @@ static void vcodec_init_drvdata(struct vpu_service_info *pservice) pservice->reg_pproc = NULL; atomic_set(&pservice->total_running, 0); - pservice->enabled = false; + atomic_set(&pservice->enabled, 0); + atomic_set(&pservice->power_on_cnt, 0); + atomic_set(&pservice->power_off_cnt, 0); INIT_DELAYED_WORK(&pservice->power_off_work, vpu_power_off_work); @@ -2596,7 +2608,12 @@ static int debug_vcodec_show(struct seq_file *s, void *unused) seq_printf(s, "done register set\n"); } } + + seq_printf(s, "\npower counter: on %d off %d\n", + atomic_read(&pservice->power_on_cnt), + atomic_read(&pservice->power_off_cnt)); mutex_unlock(&pservice->lock); + vpu_service_power_off(pservice); return 0; }