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

Commit 42743be1 authored by Xiaowen Wu's avatar Xiaowen Wu
Browse files

drm/msm: fix re-entry problem for msm_atomic_commit



When plane is detached from crtc in one atomic commit and then
attached to another crtc in the next atomic commit, the second
plane state swap will happen when the first commit is still running
in the previous crtc's worker thread, which will result in two
threads programming the same plane and run into undeterministic
state.

Fix is to add plane_mask check before state swap, together with
crtc_mask check. This will make sure there is no pending worker
thread working on the same plane.

Change-Id: I64cd4b68fa9746e988d933069ac0f8083d974c79
Signed-off-by: default avatarXiaowen Wu <wxiaowen@codeaurora.org>
parent 40d4e973
Loading
Loading
Loading
Loading
+12 −5
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ struct msm_commit {
	struct drm_device *dev;
	struct drm_atomic_state *state;
	uint32_t crtc_mask;
	uint32_t plane_mask;
	bool nonblock;
	struct kthread_work commit_work;
};
@@ -81,16 +82,19 @@ static int msm_drm_notifier_call_chain(unsigned long val, void *v)
/* block until specified crtcs are no longer pending update, and
 * atomically mark them as pending update
 */
static int start_atomic(struct msm_drm_private *priv, uint32_t crtc_mask)
static int start_atomic(struct msm_drm_private *priv, uint32_t crtc_mask,
			uint32_t plane_mask)
{
	int ret;

	spin_lock(&priv->pending_crtcs_event.lock);
	ret = wait_event_interruptible_locked(priv->pending_crtcs_event,
			!(priv->pending_crtcs & crtc_mask));
			!(priv->pending_crtcs & crtc_mask) &&
			!(priv->pending_planes & plane_mask));
	if (ret == 0) {
		DBG("start: %08x", crtc_mask);
		priv->pending_crtcs |= crtc_mask;
		priv->pending_planes |= plane_mask;
	}
	spin_unlock(&priv->pending_crtcs_event.lock);

@@ -99,18 +103,20 @@ static int start_atomic(struct msm_drm_private *priv, uint32_t crtc_mask)

/* clear specified crtcs (no longer pending update)
 */
static void end_atomic(struct msm_drm_private *priv, uint32_t crtc_mask)
static void end_atomic(struct msm_drm_private *priv, uint32_t crtc_mask,
			uint32_t plane_mask)
{
	spin_lock(&priv->pending_crtcs_event.lock);
	DBG("end: %08x", crtc_mask);
	priv->pending_crtcs &= ~crtc_mask;
	priv->pending_planes &= ~plane_mask;
	wake_up_all_locked(&priv->pending_crtcs_event);
	spin_unlock(&priv->pending_crtcs_event.lock);
}

static void commit_destroy(struct msm_commit *c)
{
	end_atomic(c->dev->dev_private, c->crtc_mask);
	end_atomic(c->dev->dev_private, c->crtc_mask, c->plane_mask);
	if (c->nonblock)
		kfree(c);
}
@@ -752,13 +758,14 @@ int msm_atomic_commit(struct drm_device *dev,

			drm_atomic_set_fence_for_plane(new_plane_state, fence);
		}
		c->plane_mask |= (1 << drm_plane_index(plane));
	}

	/*
	 * Wait for pending updates on any of the same crtc's and then
	 * mark our set of crtc's as busy:
	 */
	ret = start_atomic(dev->dev_private, c->crtc_mask);
	ret = start_atomic(dev->dev_private, c->crtc_mask, c->plane_mask);
	if (ret)
		goto err_free;

+1 −0
Original line number Diff line number Diff line
@@ -603,6 +603,7 @@ struct msm_drm_private {

	/* crtcs pending async atomic updates: */
	uint32_t pending_crtcs;
	uint32_t pending_planes;
	wait_queue_head_t pending_crtcs_event;

	unsigned int num_planes;