[PATCH 1/2] drm/amd/display: Use private obj helpers for dm_atomic_state
Li, Sun peng (Leo)
Sunpeng.Li at amd.com
Tue Nov 27 21:20:39 UTC 2018
On 2018-11-22 12:34 p.m., Nicholas Kazlauskas wrote:
> [Why]
> Two non-blocking commits in succession can result in a sequence where
> the same dc->current_state is queried for both commits.
>
> 1. 1st commit -> check -> commit -> swaps atomic state -> queues work
> 2. 2nd commit -> check -> commit -> swaps atomic state -> queues work
> 3. 1st commit work finishes
>
> The issue with this sequence is that the same dc->current_state is
> read in both atomic checks. If the first commit modifies streams or
> planes those will be missing from the dc->current_state for the
> second atomic check. This result in many stream and plane errors in
> atomic commit tail.
>
> [How]
> The driver still needs to track old to new state to determine if the
> commit in its current implementation. Updating the dc_state in
> atomic tail is wrong since the dc_state swap should be happening as
> part of drm_atomic_helper_swap_state *before* the worker queue kicks
> its work off.
>
> The simplest replacement for the subclassing (which doesn't properly
> manage the old to new atomic state swap) is to use the drm private
> object helpers. While some of the dc_state members could be merged
> into dm_crtc_state or dm_plane_state and copied over that way it is
> easier for now to just treat the whole dc_state structure as a single
> private object.
>
> This allows amdgpu_dm to drop the dc->current_state copy from within
> atomic check. It's replaced by a copy from the current atomic state
> which is propagated correctly for the sequence described above.
>
> Since access to the dm_state private object is now locked this should
> also fix issues that could arise if submitting non-blocking commits
> from different threads.
>
> Cc: Harry Wentland <harry.wentland at amd.com>
> Cc: Leo Li <sunpeng.li at amd.com>
> Signed-off-by: Nicholas Kazlauskas <nicholas.kazlauskas at amd.com>
Patch 1/2 is
Reviewed-by: Leo Li <sunpeng.li at amd.com>
Leo
> ---
> .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 290 ++++++++++++++----
> .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 13 +-
> 2 files changed, 234 insertions(+), 69 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> index 8433d31cdea8..3ae438d9849f 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -968,45 +968,6 @@ const struct amdgpu_ip_block_version dm_ip_block =
> };
>
>
> -static struct drm_atomic_state *
> -dm_atomic_state_alloc(struct drm_device *dev)
> -{
> - struct dm_atomic_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
> -
> - if (!state)
> - return NULL;
> -
> - if (drm_atomic_state_init(dev, &state->base) < 0)
> - goto fail;
> -
> - return &state->base;
> -
> -fail:
> - kfree(state);
> - return NULL;
> -}
> -
> -static void
> -dm_atomic_state_clear(struct drm_atomic_state *state)
> -{
> - struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
> -
> - if (dm_state->context) {
> - dc_release_state(dm_state->context);
> - dm_state->context = NULL;
> - }
> -
> - drm_atomic_state_default_clear(state);
> -}
> -
> -static void
> -dm_atomic_state_alloc_free(struct drm_atomic_state *state)
> -{
> - struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
> - drm_atomic_state_default_release(state);
> - kfree(dm_state);
> -}
> -
> /**
> * DOC: atomic
> *
> @@ -1018,9 +979,6 @@ static const struct drm_mode_config_funcs amdgpu_dm_mode_funcs = {
> .output_poll_changed = drm_fb_helper_output_poll_changed,
> .atomic_check = amdgpu_dm_atomic_check,
> .atomic_commit = amdgpu_dm_atomic_commit,
> - .atomic_state_alloc = dm_atomic_state_alloc,
> - .atomic_state_clear = dm_atomic_state_clear,
> - .atomic_state_free = dm_atomic_state_alloc_free
> };
>
> static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = {
> @@ -1542,8 +1500,117 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
> }
> #endif
>
> +/*
> + * Acquires the lock for the atomic state object and returns
> + * the new atomic state.
> + *
> + * This should only be called during atomic check.
> + */
> +static int dm_atomic_get_state(struct drm_atomic_state *state,
> + struct dm_atomic_state **dm_state)
> +{
> + struct drm_device *dev = state->dev;
> + struct amdgpu_device *adev = dev->dev_private;
> + struct amdgpu_display_manager *dm = &adev->dm;
> + struct drm_private_state *priv_state;
> + int ret;
> +
> + if (*dm_state)
> + return 0;
> +
> + ret = drm_modeset_lock(&dm->atomic_obj_lock, state->acquire_ctx);
> + if (ret)
> + return ret;
> +
> + priv_state = drm_atomic_get_private_obj_state(state, &dm->atomic_obj);
> + if (IS_ERR(priv_state))
> + return PTR_ERR(priv_state);
> +
> + *dm_state = to_dm_atomic_state(priv_state);
> +
> + return 0;
> +}
> +
> +struct dm_atomic_state *
> +dm_atomic_get_new_state(struct drm_atomic_state *state)
> +{
> + struct drm_device *dev = state->dev;
> + struct amdgpu_device *adev = dev->dev_private;
> + struct amdgpu_display_manager *dm = &adev->dm;
> + struct drm_private_obj *obj;
> + struct drm_private_state *new_obj_state;
> + int i;
> +
> + for_each_new_private_obj_in_state(state, obj, new_obj_state, i) {
> + if (obj->funcs == dm->atomic_obj.funcs)
> + return to_dm_atomic_state(new_obj_state);
> + }
> +
> + return NULL;
> +}
> +
> +struct dm_atomic_state *
> +dm_atomic_get_old_state(struct drm_atomic_state *state)
> +{
> + struct drm_device *dev = state->dev;
> + struct amdgpu_device *adev = dev->dev_private;
> + struct amdgpu_display_manager *dm = &adev->dm;
> + struct drm_private_obj *obj;
> + struct drm_private_state *old_obj_state;
> + int i;
> +
> + for_each_old_private_obj_in_state(state, obj, old_obj_state, i) {
> + if (obj->funcs == dm->atomic_obj.funcs)
> + return to_dm_atomic_state(old_obj_state);
> + }
> +
> + return NULL;
> +}
> +
> +static struct drm_private_state *
> +dm_atomic_duplicate_state(struct drm_private_obj *obj)
> +{
> + struct dm_atomic_state *old_state, *new_state;
> +
> + new_state = kzalloc(sizeof(*new_state), GFP_KERNEL);
> + if (!new_state)
> + return NULL;
> +
> + __drm_atomic_helper_private_obj_duplicate_state(obj, &new_state->base);
> +
> + new_state->context = dc_create_state();
> + if (!new_state->context) {
> + kfree(new_state);
> + return NULL;
> + }
> +
> + old_state = to_dm_atomic_state(obj->state);
> + if (old_state && old_state->context)
> + dc_resource_state_copy_construct(old_state->context,
> + new_state->context);
> +
> + return &new_state->base;
> +}
> +
> +static void dm_atomic_destroy_state(struct drm_private_obj *obj,
> + struct drm_private_state *state)
> +{
> + struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
> +
> + if (dm_state && dm_state->context)
> + dc_release_state(dm_state->context);
> +
> + kfree(dm_state);
> +}
> +
> +static struct drm_private_state_funcs dm_atomic_state_funcs = {
> + .atomic_duplicate_state = dm_atomic_duplicate_state,
> + .atomic_destroy_state = dm_atomic_destroy_state,
> +};
> +
> static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
> {
> + struct dm_atomic_state *state;
> int r;
>
> adev->mode_info.mode_config_initialized = true;
> @@ -1561,6 +1628,24 @@ static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
>
> adev->ddev->mode_config.fb_base = adev->gmc.aper_base;
>
> + drm_modeset_lock_init(&adev->dm.atomic_obj_lock);
> +
> + state = kzalloc(sizeof(*state), GFP_KERNEL);
> + if (!state)
> + return -ENOMEM;
> +
> + state->context = dc_create_state();
> + if (!state->context) {
> + kfree(state);
> + return -ENOMEM;
> + }
> +
> + dc_resource_state_copy_construct_current(adev->dm.dc, state->context);
> +
> + drm_atomic_private_obj_init(&adev->dm.atomic_obj,
> + &state->base,
> + &dm_atomic_state_funcs);
> +
> r = amdgpu_display_modeset_create_props(adev);
> if (r)
> return r;
> @@ -1849,6 +1934,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
> static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm)
> {
> drm_mode_config_cleanup(dm->ddev);
> + drm_atomic_private_obj_fini(&dm->atomic_obj);
> return;
> }
>
> @@ -4274,6 +4360,20 @@ static void prepare_flip_isr(struct amdgpu_crtc *acrtc)
> acrtc->crtc_id);
> }
>
> +struct dc_stream_status *dc_state_get_stream_status(
> + struct dc_state *state,
> + struct dc_stream_state *stream)
> +{
> + uint8_t i;
> +
> + for (i = 0; i < state->stream_count; i++) {
> + if (stream == state->streams[i])
> + return &state->stream_status[i];
> + }
> +
> + return NULL;
> +}
> +
> /*
> * Executes flip
> *
> @@ -4477,6 +4577,7 @@ static bool commit_planes_to_stream(
> }
>
> static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
> + struct dc_state *dc_state,
> struct drm_device *dev,
> struct amdgpu_display_manager *dm,
> struct drm_crtc *pcrtc,
> @@ -4493,7 +4594,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
> struct dm_crtc_state *acrtc_state = to_dm_crtc_state(new_pcrtc_state);
> struct dm_crtc_state *dm_old_crtc_state =
> to_dm_crtc_state(drm_atomic_get_old_crtc_state(state, pcrtc));
> - struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
> int planes_count = 0;
> unsigned long flags;
>
> @@ -4554,7 +4654,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
> crtc,
> fb,
> (uint32_t)drm_crtc_vblank_count(crtc) + *wait_for_vblank,
> - dm_state->context);
> + dc_state);
> }
>
> }
> @@ -4579,7 +4679,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
> planes_count,
> acrtc_state,
> dm_old_crtc_state,
> - dm_state->context))
> + dc_state))
> dm_error("%s: Failed to attach plane!\n", __func__);
> } else {
> /*TODO BUG Here should go disable planes on CRTC. */
> @@ -4647,6 +4747,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
> struct amdgpu_device *adev = dev->dev_private;
> struct amdgpu_display_manager *dm = &adev->dm;
> struct dm_atomic_state *dm_state;
> + struct dc_state *dc_state = NULL, *dc_state_temp = NULL;
> uint32_t i, j;
> struct drm_crtc *crtc;
> struct drm_crtc_state *old_crtc_state, *new_crtc_state;
> @@ -4659,7 +4760,16 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
>
> drm_atomic_helper_update_legacy_modeset_state(dev, state);
>
> - dm_state = to_dm_atomic_state(state);
> + dm_state = dm_atomic_get_new_state(state);
> + if (dm_state && dm_state->context) {
> + dc_state = dm_state->context;
> + } else {
> + /* No state changes, retain current state. */
> + dc_state_temp = dc_create_state();
> + ASSERT(dc_state_temp);
> + dc_state = dc_state_temp;
> + dc_resource_state_copy_construct_current(dm->dc, dc_state);
> + }
>
> /* update changed items */
> for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
> @@ -4732,9 +4842,9 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
> }
> } /* for_each_crtc_in_state() */
>
> - if (dm_state->context) {
> - dm_enable_per_frame_crtc_master_sync(dm_state->context);
> - WARN_ON(!dc_commit_state(dm->dc, dm_state->context));
> + if (dc_state) {
> + dm_enable_per_frame_crtc_master_sync(dc_state);
> + WARN_ON(!dc_commit_state(dm->dc, dc_state));
> }
>
> for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
> @@ -4746,6 +4856,10 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
> const struct dc_stream_status *status =
> dc_stream_get_status(dm_new_crtc_state->stream);
>
> + if (!status)
> + status = dc_state_get_stream_status(dc_state,
> + dm_new_crtc_state->stream);
> +
> if (!status)
> DC_ERR("got no status for stream %p on acrtc%p\n", dm_new_crtc_state->stream, acrtc);
> else
> @@ -4828,7 +4942,8 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
> dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
>
> if (dm_new_crtc_state->stream)
> - amdgpu_dm_commit_planes(state, dev, dm, crtc, &wait_for_vblank);
> + amdgpu_dm_commit_planes(state, dc_state, dev,
> + dm, crtc, &wait_for_vblank);
> }
>
>
> @@ -4868,6 +4983,9 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
> for (i = 0; i < crtc_disable_count; i++)
> pm_runtime_put_autosuspend(dev->dev);
> pm_runtime_mark_last_busy(dev->dev);
> +
> + if (dc_state_temp)
> + dc_release_state(dc_state_temp);
> }
>
>
> @@ -5054,11 +5172,11 @@ static int dm_update_crtcs_state(struct amdgpu_display_manager *dm,
> bool enable,
> bool *lock_and_validation_needed)
> {
> + struct dm_atomic_state *dm_state = NULL;
> struct drm_crtc *crtc;
> struct drm_crtc_state *old_crtc_state, *new_crtc_state;
> int i;
> struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
> - struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
> struct dc_stream_state *new_stream;
> int ret = 0;
>
> @@ -5156,6 +5274,10 @@ static int dm_update_crtcs_state(struct amdgpu_display_manager *dm,
> if (!dm_old_crtc_state->stream)
> goto next_crtc;
>
> + ret = dm_atomic_get_state(state, &dm_state);
> + if (ret)
> + goto fail;
> +
> DRM_DEBUG_DRIVER("Disabling DRM crtc: %d\n",
> crtc->base.id);
>
> @@ -5190,6 +5312,10 @@ static int dm_update_crtcs_state(struct amdgpu_display_manager *dm,
>
> WARN_ON(dm_new_crtc_state->stream);
>
> + ret = dm_atomic_get_state(state, &dm_state);
> + if (ret)
> + goto fail;
> +
> dm_new_crtc_state->stream = new_stream;
>
> dc_stream_retain(new_stream);
> @@ -5264,12 +5390,13 @@ static int dm_update_planes_state(struct dc *dc,
> bool enable,
> bool *lock_and_validation_needed)
> {
> +
> + struct dm_atomic_state *dm_state = NULL;
> struct drm_crtc *new_plane_crtc, *old_plane_crtc;
> struct drm_crtc_state *old_crtc_state, *new_crtc_state;
> struct drm_plane *plane;
> struct drm_plane_state *old_plane_state, *new_plane_state;
> struct dm_crtc_state *dm_new_crtc_state, *dm_old_crtc_state;
> - struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
> struct dm_plane_state *dm_new_plane_state, *dm_old_plane_state;
> int i ;
> /* TODO return page_flip_needed() function */
> @@ -5307,6 +5434,10 @@ static int dm_update_planes_state(struct dc *dc,
> DRM_DEBUG_ATOMIC("Disabling DRM plane: %d on DRM crtc %d\n",
> plane->base.id, old_plane_crtc->base.id);
>
> + ret = dm_atomic_get_state(state, &dm_state);
> + if (ret)
> + return ret;
> +
> if (!dc_remove_plane_from_context(
> dc,
> dm_old_crtc_state->stream,
> @@ -5361,6 +5492,12 @@ static int dm_update_planes_state(struct dc *dc,
> return ret;
> }
>
> + ret = dm_atomic_get_state(state, &dm_state);
> + if (ret) {
> + dc_plane_state_release(dc_new_plane_state);
> + return ret;
> + }
> +
> /*
> * Any atomic check errors that occur after this will
> * not need a release. The plane state will be attached
> @@ -5392,11 +5529,14 @@ static int dm_update_planes_state(struct dc *dc,
>
> return ret;
> }
> -enum surface_update_type dm_determine_update_type_for_commit(struct dc *dc, struct drm_atomic_state *state)
> -{
> -
>
> - int i, j, num_plane;
> +static int
> +dm_determine_update_type_for_commit(struct dc *dc,
> + struct drm_atomic_state *state,
> + enum surface_update_type *out_type)
> +{
> + struct dm_atomic_state *dm_state = NULL, *old_dm_state = NULL;
> + int i, j, num_plane, ret = 0;
> struct drm_plane_state *old_plane_state, *new_plane_state;
> struct dm_plane_state *new_dm_plane_state, *old_dm_plane_state;
> struct drm_crtc *new_plane_crtc, *old_plane_crtc;
> @@ -5416,7 +5556,7 @@ enum surface_update_type dm_determine_update_type_for_commit(struct dc *dc, stru
> DRM_ERROR("Plane or surface update failed to allocate");
> /* Set type to FULL to avoid crashing in DC*/
> update_type = UPDATE_TYPE_FULL;
> - goto ret;
> + goto cleanup;
> }
>
> for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
> @@ -5470,27 +5610,40 @@ enum surface_update_type dm_determine_update_type_for_commit(struct dc *dc, stru
> }
>
> if (num_plane > 0) {
> - status = dc_stream_get_status(new_dm_crtc_state->stream);
> + ret = dm_atomic_get_state(state, &dm_state);
> + if (ret)
> + goto cleanup;
> +
> + old_dm_state = dm_atomic_get_old_state(state);
> + if (!old_dm_state) {
> + ret = -EINVAL;
> + goto cleanup;
> + }
> +
> + status = dc_state_get_stream_status(old_dm_state->context,
> + new_dm_crtc_state->stream);
> +
> update_type = dc_check_update_surfaces_for_stream(dc, updates, num_plane,
> &stream_update, status);
>
> if (update_type > UPDATE_TYPE_MED) {
> update_type = UPDATE_TYPE_FULL;
> - goto ret;
> + goto cleanup;
> }
> }
>
> } else if (!new_dm_crtc_state->stream && old_dm_crtc_state->stream) {
> update_type = UPDATE_TYPE_FULL;
> - goto ret;
> + goto cleanup;
> }
> }
>
> -ret:
> +cleanup:
> kfree(updates);
> kfree(surface);
>
> - return update_type;
> + *out_type = update_type;
> + return ret;
> }
>
> /**
> @@ -5522,8 +5675,8 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
> struct drm_atomic_state *state)
> {
> struct amdgpu_device *adev = dev->dev_private;
> + struct dm_atomic_state *dm_state = NULL;
> struct dc *dc = adev->dm.dc;
> - struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
> struct drm_connector *connector;
> struct drm_connector_state *old_con_state, *new_con_state;
> struct drm_crtc *crtc;
> @@ -5564,10 +5717,6 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
> goto fail;
> }
>
> - dm_state->context = dc_create_state();
> - ASSERT(dm_state->context);
> - dc_resource_state_copy_construct_current(dc, dm_state->context);
> -
> /* Remove exiting planes if they are modified */
> ret = dm_update_planes_state(dc, state, false, &lock_and_validation_needed);
> if (ret) {
> @@ -5620,7 +5769,9 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
> lock_and_validation_needed = true;
> }
>
> - update_type = dm_determine_update_type_for_commit(dc, state);
> + ret = dm_determine_update_type_for_commit(dc, state, &update_type);
> + if (ret)
> + goto fail;
>
> if (overall_update_type < update_type)
> overall_update_type = update_type;
> @@ -5638,6 +5789,9 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
>
>
> if (overall_update_type > UPDATE_TYPE_FAST) {
> + ret = dm_atomic_get_state(state, &dm_state);
> + if (ret)
> + goto fail;
>
> ret = do_aquire_global_lock(dev, state);
> if (ret)
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
> index 607c3cdd7d0c..c8fd95950d0c 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
> @@ -111,6 +111,17 @@ struct amdgpu_display_manager {
> struct drm_device *ddev;
> u16 display_indexes_num;
>
> + /**
> + * @atomic_obj
> + *
> + * In combination with &dm_atomic_state it helps manage
> + * global atomic state that doesn't map cleanly into existing
> + * drm resources, like &dc_context.
> + */
> + struct drm_private_obj atomic_obj;
> +
> + struct drm_modeset_lock atomic_obj_lock;
> +
> /**
> * @irq_handler_list_low_tab:
> *
> @@ -239,7 +250,7 @@ struct dm_crtc_state {
> #define to_dm_crtc_state(x) container_of(x, struct dm_crtc_state, base)
>
> struct dm_atomic_state {
> - struct drm_atomic_state base;
> + struct drm_private_state base;
>
> struct dc_state *context;
> };
>
More information about the amd-gfx
mailing list