diff options
| author | Ujwal Patel <ujwalp@codeaurora.org> | 2013-10-22 23:24:46 -0700 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:22:48 -0700 |
| commit | a681b26df005032f14688c3693e9f3fb73bb6ff7 (patch) | |
| tree | ad47c0f7fb7dc8ef2bbc0eb6923d5bfd98c30313 | |
| parent | c84875ea810b3eb44fbe2646c84763b2235293ef (diff) | |
msm: mdss: Fix race condition during device shutdown
During device shutdown or restart, two separate contexts can call
mdss_fb_release. One system shutdown/restart thread which calls
shutdown routine registered through device driver. Other closing
of frame buffer devices from user processes. Fix race condition
between these two contexts by using proper locking mechanism and
releasing all the resources.
Change-Id: I583b0f1273d984215e276daf3b6307f3b6e9e8c6
Signed-off-by: Ujwal Patel <ujwalp@codeaurora.org>
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_fb.c | 58 |
1 files changed, 31 insertions, 27 deletions
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 20b838780b62..f6c5074e87ec 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -77,6 +77,7 @@ static struct msm_mdp_interface *mdp_instance; static int mdss_fb_register(struct msm_fb_data_type *mfd); static int mdss_fb_open(struct fb_info *info, int user); static int mdss_fb_release(struct fb_info *info, int user); +static int mdss_fb_release_all(struct fb_info *info, bool release_all); static int mdss_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info); static int mdss_fb_check_var(struct fb_var_screeninfo *var, @@ -297,10 +298,9 @@ static void mdss_fb_shutdown(struct platform_device *pdev) { struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); - for (; mfd->ref_cnt > 1; mfd->ref_cnt--) - pm_runtime_put(mfd->fbi->dev); - - mdss_fb_release(mfd->fbi, 0); + lock_fb_info(mfd->fbi); + mdss_fb_release_all(mfd->fbi, true); + unlock_fb_info(mfd->fbi); } static int mdss_fb_probe(struct platform_device *pdev) @@ -1196,10 +1196,10 @@ static int mdss_fb_open(struct fb_info *info, int user) return 0; } -static int mdss_fb_release(struct fb_info *info, int user) +static int mdss_fb_release_all(struct fb_info *info, bool release_all) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; - struct mdss_fb_proc_info *pinfo = NULL; + struct mdss_fb_proc_info *pinfo = NULL, *temp_pinfo = NULL; int ret = 0; int pid = current->tgid; @@ -1209,27 +1209,26 @@ static int mdss_fb_release(struct fb_info *info, int user) } mdss_fb_pan_idle(mfd); - mfd->ref_cnt--; - list_for_each_entry(pinfo, &mfd->proc_list, list) { - if (pinfo->pid == pid) - break; - } + pr_debug("release_all = %s\n", release_all ? "true" : "false"); - if (!pinfo || (pinfo->pid != pid)) { - pr_warn("unable to find process info for fb%d pid=%d\n", - mfd->index, pid); - if (mfd->mdp.release_fnc) { - ret = mfd->mdp.release_fnc(mfd); - if (ret) - pr_err("error releasing fb%d resources\n", - mfd->index); - } - } else { - pr_debug("found process entry pid=%d ref=%d\n", - pinfo->pid, pinfo->ref_cnt); + list_for_each_entry_safe(pinfo, temp_pinfo, &mfd->proc_list, list) { + if (!release_all && (pinfo->pid != pid)) + continue; + + pr_debug("found process entry pid=%d ref=%d\n", pinfo->pid, + pinfo->ref_cnt); + + do { + if (mfd->ref_cnt < pinfo->ref_cnt) + pr_warn("WARN:mfd->ref_cnt < pinfo->ref_cnt\n"); + else + mfd->ref_cnt--; + + pinfo->ref_cnt--; + pm_runtime_put(info->dev); + } while (release_all && pinfo->ref_cnt); - pinfo->ref_cnt--; if (pinfo->ref_cnt == 0) { if (mfd->mdp.release_fnc) { ret = mfd->mdp.release_fnc(mfd); @@ -1244,17 +1243,22 @@ static int mdss_fb_release(struct fb_info *info, int user) if (!mfd->ref_cnt) { ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info, - mfd->op_enable); + mfd->op_enable); if (ret) { - pr_err("can't turn off fb%d! rc=%d\n", mfd->index, ret); + pr_err("can't turn off fb%d! rc=%d\n", + mfd->index, ret); return ret; } } - pm_runtime_put(info->dev); return ret; } +static int mdss_fb_release(struct fb_info *info, int user) +{ + return mdss_fb_release_all(info, false); +} + static void mdss_fb_power_setting_idle(struct msm_fb_data_type *mfd) { int ret; |
