msm: mdss: fix race condition between commit thread and power off

There is a deadlock in use cases where the power off
is called concurrently with the overlay kickoff from
different threads. To avoid this condition, during
power off sequence we first make sure to stop the
fb display thread before the power off sequence is
handled.

Change-Id: I708143206b914f6f72c440c4dc3a1c73fceea6b2
Signed-off-by: Ingrid Gallardo <ingridg@codeaurora.org>
This commit is contained in:
Ingrid Gallardo 2014-07-31 19:42:10 -07:00 committed by David Keitel
parent 71c3d682f9
commit 3ee962653b

View file

@ -1152,6 +1152,38 @@ void mdss_fb_update_backlight(struct msm_fb_data_type *mfd)
mutex_unlock(&mfd->bl_lock);
}
static int mdss_fb_start_disp_thread(struct msm_fb_data_type *mfd)
{
int ret = 0;
pr_debug("%pS: start display thread fb%d\n",
__builtin_return_address(0), mfd->index);
mdss_fb_get_split(mfd);
atomic_set(&mfd->commits_pending, 0);
mfd->disp_thread = kthread_run(__mdss_fb_display_thread,
mfd, "mdss_fb%d", mfd->index);
if (IS_ERR(mfd->disp_thread)) {
pr_err("ERROR: unable to start display thread %d\n",
mfd->index);
ret = PTR_ERR(mfd->disp_thread);
mfd->disp_thread = NULL;
}
return ret;
}
static void mdss_fb_stop_disp_thread(struct msm_fb_data_type *mfd)
{
pr_debug("%pS: stop display thread fb%d\n",
__builtin_return_address(0), mfd->index);
kthread_stop(mfd->disp_thread);
mfd->disp_thread = NULL;
}
static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info,
int op_enable)
{
@ -1165,16 +1197,28 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info,
if (mfd->dcm_state == DCM_ENTER)
return -EPERM;
pr_debug("%pS mode:%d\n", __builtin_return_address(0),
blank_mode);
cur_power_state = mfd->panel_power_state;
switch (blank_mode) {
case FB_BLANK_UNBLANK:
pr_debug("unblank called. cur pwr state=%d\n", cur_power_state);
/* Start Display thread */
if (mfd->disp_thread == NULL) {
ret = mdss_fb_start_disp_thread(mfd);
if (ret < 0)
return ret;
}
if (!mdss_panel_is_power_on_interactive(cur_power_state) &&
mfd->mdp.on_fnc) {
ret = mfd->mdp.on_fnc(mfd);
if (ret == 0) {
mfd->panel_power_state = MDSS_PANEL_POWER_ON;
mfd->panel_info->panel_dead = false;
} else if (mfd->disp_thread) {
mdss_fb_stop_disp_thread(mfd);
}
mutex_lock(&mfd->update.lock);
mfd->update.type = NOTIFY_TYPE_UPDATE;
@ -1233,6 +1277,9 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info,
mfd->op_enable = false;
mutex_lock(&mfd->bl_lock);
if (mdss_panel_is_power_off(req_power_state)) {
/* Stop Display thread */
if (mfd->disp_thread)
mdss_fb_stop_disp_thread(mfd);
mdss_fb_set_backlight(mfd, 0);
mfd->bl_updated = 0;
}
@ -2037,17 +2084,6 @@ static int mdss_fb_open(struct fb_info *info, int user)
}
if (!mfd->ref_cnt) {
mdss_fb_get_split(mfd);
mfd->disp_thread = kthread_run(__mdss_fb_display_thread, mfd,
"mdss_fb%d", mfd->index);
if (IS_ERR(mfd->disp_thread)) {
pr_err("unable to start display thread %d\n",
mfd->index);
result = PTR_ERR(mfd->disp_thread);
mfd->disp_thread = NULL;
goto thread_error;
}
result = mdss_fb_blank_sub(FB_BLANK_UNBLANK, info,
mfd->op_enable);
if (result) {
@ -2063,10 +2099,6 @@ static int mdss_fb_open(struct fb_info *info, int user)
return 0;
blank_error:
kthread_stop(mfd->disp_thread);
mfd->disp_thread = NULL;
thread_error:
pm_runtime_put(info->dev);
pm_error:
@ -2137,11 +2169,6 @@ static int mdss_fb_release_all(struct fb_info *info, bool release_all)
pm_runtime_put(info->dev);
} while (release_all && pinfo->ref_cnt);
if (release_all && mfd->disp_thread) {
kthread_stop(mfd->disp_thread);
mfd->disp_thread = NULL;
}
if (pinfo->ref_cnt == 0) {
list_del(&pinfo->list);
kfree(pinfo);
@ -2187,11 +2214,6 @@ static int mdss_fb_release_all(struct fb_info *info, bool release_all)
}
if (!mfd->ref_cnt) {
if (mfd->disp_thread) {
kthread_stop(mfd->disp_thread);
mfd->disp_thread = NULL;
}
if (mfd->mdp.release_fnc) {
ret = mfd->mdp.release_fnc(mfd, true, pid);
if (ret)