Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit df9c33ad authored by Adrian Salido-Moreno's avatar Adrian Salido-Moreno
Browse files

msm: mdss: handle race condition in pingpong done counter



Current mechanism to count number of synchronized ping pong transfers
can have a race condition in case ping pong done happens as in
following simplified code sequence:

    Thread 0             |        Thread 1
-------------------------|----------------------------
1)                       | wait_event(w, !ctx->koff_cnt &&
                         |         !sctx->koff_cnt)
                         | /* ctx->koff_cnt == 1 */
                         | /* sctx->koff_cnt == 1 */
2) ctx->koff_cnt--       |
                         | /* ctx->koff_cnt == 0 */
3) if (!sctx->koff_cnt)  |
4)   pp_done_cnt++       |
5) sctx->koff_cnt--      |
                         | /* sctx->koff_cnt == 0 */
                         | /* wait_event unblocks, transfer done */
6)                       | ctx->koff_cnt++ /* new transfer */
7)                       | sctx->koff_cnt++
8) if (!ctx->koff_cnt)   |
9)   pp_done_cnt++       |

By decrementing koff_cnt at #5, we are signaling that transfer has
completed for both ctx and sctx, unblocking Thread 1 which can go
ahead and trigger next transfer before we increment pp_done_cnt. By
checking the status of ctx->koff_cnt before #5, we can safely allow
Thread 1 to go forward but still maintain proper count of pp_done_cnt.

CRs-Fixed: 966529
Change-Id: I59e7798b5cc5bc28ff56551a0a009503019d32a9
Signed-off-by: default avatarAdrian Salido-Moreno <adrianm@codeaurora.org>
parent 8562c3ab
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -1019,6 +1019,7 @@ static void mdss_mdp_cmd_pingpong_done(void *arg)
	struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX];
	struct mdss_mdp_vsync_handler *tmp;
	ktime_t vsync_time;
	bool sync_ppdone;

	if (!ctx) {
		pr_err("%s: invalid ctx\n", __func__);
@@ -1044,11 +1045,18 @@ static void mdss_mdp_cmd_pingpong_done(void *arg)

	MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt), ctx->current_pp_num);

	/*
	 * check state of sync ctx before decrementing koff_cnt to avoid race
	 * condition. That is, once both koff_cnt have been served and new koff
	 * can be triggered (sctx->koff_cnt could change)
	 */
	sync_ppdone = mdss_mdp_cmd_do_notifier(ctx);

	if (atomic_add_unless(&ctx->koff_cnt, -1, 0)) {
		if (atomic_read(&ctx->koff_cnt))
			pr_err("%s: too many kickoffs=%d!\n", __func__,
			       atomic_read(&ctx->koff_cnt));
		if (mdss_mdp_cmd_do_notifier(ctx)) {
		if (sync_ppdone) {
			atomic_inc(&ctx->pp_done_cnt);
			schedule_work(&ctx->pp_done_work);