[Freedreno] [v1] drm/msm: Fix race condition in msm driver with async layer updates
mkrishn at codeaurora.org
mkrishn at codeaurora.org
Thu Oct 15 09:18:00 UTC 2020
On 2020-10-14 20:55, Rob Clark wrote:
> On Wed, Oct 14, 2020 at 5:58 AM Krishna Manikandan
> <mkrishn at codeaurora.org> wrote:
>>
>> When there are back to back commits with async cursor update,
>> there is a case where second commit can program the DPU hw
>> blocks while first didn't complete flushing config to HW.
>>
>> Synchronize the compositions such that second commit waits
>> until first commit flushes the composition.
>>
>> This change also introduces per crtc commit lock, such that
>> commits on different crtcs are not blocked by each other.
>>
>> Signed-off-by: Krishna Manikandan <mkrishn at codeaurora.org>
>> ---
>> drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 1 +
>> drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h | 1 +
>> drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 26
>> ++++++++++++++++++++++++
>> drivers/gpu/drm/msm/msm_atomic.c | 35
>> ++++++++++++++++++++++----------
>> drivers/gpu/drm/msm/msm_kms.h | 5 +++++
>> 5 files changed, 57 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
>> index c2729f7..9024719 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
>> @@ -1383,6 +1383,7 @@ struct drm_crtc *dpu_crtc_init(struct drm_device
>> *dev, struct drm_plane *plane,
>>
>> /* initialize event handling */
>> spin_lock_init(&dpu_crtc->event_lock);
>> + mutex_init(&dpu_crtc->commit_lock);
>>
>> DPU_DEBUG("%s: successfully initialized crtc\n",
>> dpu_crtc->name);
>> return crtc;
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
>> index cec3474..1eeb73d 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
>> @@ -169,6 +169,7 @@ struct dpu_crtc {
>>
>> /* for handling internal event thread */
>> spinlock_t event_lock;
>> + struct mutex commit_lock;
>>
>> struct dpu_core_perf_params cur_perf;
>>
>> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
>> b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
>> index c0a4d4e..f99ae7a 100644
>> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
>> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
>> @@ -445,6 +445,30 @@ static void dpu_kms_wait_flush(struct msm_kms
>> *kms, unsigned crtc_mask)
>> dpu_kms_wait_for_commit_done(kms, crtc);
>> }
>>
>> +static void dpu_kms_commit_lock(struct msm_kms *kms, unsigned int
>> crtc_mask)
>> +{
>> + struct dpu_kms *dpu_kms = to_dpu_kms(kms);
>> + struct drm_crtc *crtc;
>> + struct dpu_crtc *dpu_crtc;
>> +
>> + for_each_crtc_mask(dpu_kms->dev, crtc, crtc_mask) {
>> + dpu_crtc = to_dpu_crtc(crtc);
>> + mutex_lock(&dpu_crtc->commit_lock);
>> + }
>> +}
>> +
>> +static void dpu_kms_commit_unlock(struct msm_kms *kms, unsigned int
>> crtc_mask)
>> +{
>> + struct dpu_kms *dpu_kms = to_dpu_kms(kms);
>> + struct drm_crtc *crtc;
>> + struct dpu_crtc *dpu_crtc;
>> +
>> + for_each_crtc_mask(dpu_kms->dev, crtc, crtc_mask) {
>> + dpu_crtc = to_dpu_crtc(crtc);
>> + mutex_unlock(&dpu_crtc->commit_lock);
>> + }
>> +}
>> +
>> static int _dpu_kms_initialize_dsi(struct drm_device *dev,
>> struct msm_drm_private *priv,
>> struct dpu_kms *dpu_kms)
>> @@ -738,6 +762,8 @@ static const struct msm_kms_funcs kms_funcs = {
>> #ifdef CONFIG_DEBUG_FS
>> .debugfs_init = dpu_kms_debugfs_init,
>> #endif
>> + .commit_lock = dpu_kms_commit_lock,
>> + .commit_unlock = dpu_kms_commit_unlock,
>> };
>>
>> static void _dpu_kms_mmu_destroy(struct dpu_kms *dpu_kms)
>> diff --git a/drivers/gpu/drm/msm/msm_atomic.c
>> b/drivers/gpu/drm/msm/msm_atomic.c
>> index 561bfa4..d33253f 100644
>> --- a/drivers/gpu/drm/msm/msm_atomic.c
>> +++ b/drivers/gpu/drm/msm/msm_atomic.c
>> @@ -55,16 +55,32 @@ static void vblank_put(struct msm_kms *kms,
>> unsigned crtc_mask)
>> }
>> }
>>
>> +static void msm_commit_lock(struct msm_kms *kms, unsigned int
>> crtc_mask)
>> +{
>> + if (kms->funcs->commit_lock)
>> + kms->funcs->commit_lock(kms, crtc_mask);
>> + else
>> + mutex_lock(&kms->commit_lock);
>> +}
>> +
>> +static void msm_commit_unlock(struct msm_kms *kms, unsigned int
>> crtc_mask)
>> +{
>> + if (kms->funcs->commit_unlock)
>> + kms->funcs->commit_unlock(kms, crtc_mask);
>> + else
>> + mutex_unlock(&kms->commit_lock);
>> +}
>
> Hi, I think the per-crtc commit-lock, and the updated
> locking/unlocking points are the right thing to do, but I don't think
> we need to touch dpu for this. Just change kms->commit_lock to an
> array of mutexes, and drop the vfunc indirection. All the same
> locking logic applies to mdp4/mdp5 as well (ie. don't touch the hw
> until it has flushed)
>
> BR,
> -R
>
Thanks for the comments Rob. I have addressed them in v2 patch.
Regards,
Krishna
>> +
>> static void msm_atomic_async_commit(struct msm_kms *kms, int
>> crtc_idx)
>> {
>> unsigned crtc_mask = BIT(crtc_idx);
>>
>> trace_msm_atomic_async_commit_start(crtc_mask);
>>
>> - mutex_lock(&kms->commit_lock);
>> + msm_commit_lock(kms, crtc_mask);
>>
>> if (!(kms->pending_crtc_mask & crtc_mask)) {
>> - mutex_unlock(&kms->commit_lock);
>> + msm_commit_unlock(kms, crtc_mask);
>> goto out;
>> }
>>
>> @@ -79,7 +95,6 @@ static void msm_atomic_async_commit(struct msm_kms
>> *kms, int crtc_idx)
>> */
>> trace_msm_atomic_flush_commit(crtc_mask);
>> kms->funcs->flush_commit(kms, crtc_mask);
>> - mutex_unlock(&kms->commit_lock);
>>
>> /*
>> * Wait for flush to complete:
>> @@ -90,9 +105,8 @@ static void msm_atomic_async_commit(struct msm_kms
>> *kms, int crtc_idx)
>>
>> vblank_put(kms, crtc_mask);
>>
>> - mutex_lock(&kms->commit_lock);
>> kms->funcs->complete_commit(kms, crtc_mask);
>> - mutex_unlock(&kms->commit_lock);
>> + msm_commit_unlock(kms, crtc_mask);
>> kms->funcs->disable_commit(kms);
>>
>> out:
>> @@ -189,12 +203,11 @@ void msm_atomic_commit_tail(struct
>> drm_atomic_state *state)
>> * Ensure any previous (potentially async) commit has
>> * completed:
>> */
>> + msm_commit_lock(kms, crtc_mask);
>> trace_msm_atomic_wait_flush_start(crtc_mask);
>> kms->funcs->wait_flush(kms, crtc_mask);
>> trace_msm_atomic_wait_flush_finish(crtc_mask);
>>
>> - mutex_lock(&kms->commit_lock);
>> -
>> /*
>> * Now that there is no in-progress flush, prepare the
>> * current update:
>> @@ -232,7 +245,7 @@ void msm_atomic_commit_tail(struct
>> drm_atomic_state *state)
>> }
>>
>> kms->funcs->disable_commit(kms);
>> - mutex_unlock(&kms->commit_lock);
>> + msm_commit_unlock(kms, crtc_mask);
>>
>> /*
>> * At this point, from drm core's perspective, we
>> @@ -260,7 +273,7 @@ void msm_atomic_commit_tail(struct
>> drm_atomic_state *state)
>> */
>> trace_msm_atomic_flush_commit(crtc_mask);
>> kms->funcs->flush_commit(kms, crtc_mask);
>> - mutex_unlock(&kms->commit_lock);
>> + msm_commit_unlock(kms, crtc_mask);
>>
>> /*
>> * Wait for flush to complete:
>> @@ -271,9 +284,9 @@ void msm_atomic_commit_tail(struct
>> drm_atomic_state *state)
>>
>> vblank_put(kms, crtc_mask);
>>
>> - mutex_lock(&kms->commit_lock);
>> + msm_commit_lock(kms, crtc_mask);
>> kms->funcs->complete_commit(kms, crtc_mask);
>> - mutex_unlock(&kms->commit_lock);
>> + msm_commit_unlock(kms, crtc_mask);
>> kms->funcs->disable_commit(kms);
>>
>> drm_atomic_helper_commit_hw_done(state);
>> diff --git a/drivers/gpu/drm/msm/msm_kms.h
>> b/drivers/gpu/drm/msm/msm_kms.h
>> index 1cbef6b..f02e73e 100644
>> --- a/drivers/gpu/drm/msm/msm_kms.h
>> +++ b/drivers/gpu/drm/msm/msm_kms.h
>> @@ -126,6 +126,11 @@ struct msm_kms_funcs {
>> /* debugfs: */
>> int (*debugfs_init)(struct msm_kms *kms, struct drm_minor
>> *minor);
>> #endif
>> + /* commit lock for crtc */
>> + void (*commit_lock)(struct msm_kms *kms, unsigned int
>> crtc_mask);
>> +
>> + /* commit unlock for crtc */
>> + void (*commit_unlock)(struct msm_kms *kms, unsigned int
>> crtc_mask);
>> };
>>
>> struct msm_kms;
>> --
>> 2.7.4
>>
More information about the Freedreno
mailing list