Skip to content

Commit e0f04e4

Browse files
author
Thomas Zimmermann
committed
drm/atomic-helpers: Invoke end_fb_access while owning plane state
Invoke drm_plane_helper_funcs.end_fb_access before drm_atomic_helper_commit_hw_done(). The latter function hands over ownership of the plane state to the following commit, which might free it. Releasing resources in end_fb_access then operates on undefined state. This bug has been observed with non-blocking commits when they are being queued up quickly. Here is an example stack trace from the bug report. The plane state has been free'd already, so the pages for drm_gem_fb_vunmap() are gone. Unable to handle kernel paging request at virtual address 0000000100000049 [...] drm_gem_fb_vunmap+0x18/0x74 drm_gem_end_shadow_fb_access+0x1c/0x2c drm_atomic_helper_cleanup_planes+0x58/0xd8 drm_atomic_helper_commit_tail+0x90/0xa0 commit_tail+0x15c/0x188 commit_work+0x14/0x20 Fix this by running end_fb_access immediately after updating all planes in drm_atomic_helper_commit_planes(). The existing clean-up helper drm_atomic_helper_cleanup_planes() now only handles cleanup_fb. For aborted commits, roll back from drm_atomic_helper_prepare_planes() in the new helper drm_atomic_helper_unprepare_planes(). This case is different from regular cleanup, as we have to release the new state; regular cleanup releases the old state. The new helper also invokes cleanup_fb for all planes. The changes mostly involve DRM's atomic helpers. Only two drivers, i915 and nouveau, implement their own commit function. Update them to invoke drm_atomic_helper_unprepare_planes(). Drivers with custom commit_tail function do not require changes. v4: * fix documentation (kernel test robot) v3: * add drm_atomic_helper_unprepare_planes() for rolling back * use correct state for end_fb_access v2: * fix test in drm_atomic_helper_cleanup_planes() Reported-by: Alyssa Ross <[email protected]> Closes: https://lore.kernel.org/dri-devel/[email protected]/ Suggested-by: Daniel Vetter <[email protected]> Fixes: 94d879e ("drm/atomic-helper: Add {begin,end}_fb_access to plane helpers") Tested-by: Alyssa Ross <[email protected]> Reviewed-by: Alyssa Ross <[email protected]> Signed-off-by: Thomas Zimmermann <[email protected]> Cc: <[email protected]> # v6.2+ Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 2651330 commit e0f04e4

File tree

4 files changed

+56
-28
lines changed

4 files changed

+56
-28
lines changed

drivers/gpu/drm/drm_atomic_helper.c

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,7 +2012,7 @@ int drm_atomic_helper_commit(struct drm_device *dev,
20122012
return ret;
20132013

20142014
drm_atomic_helper_async_commit(dev, state);
2015-
drm_atomic_helper_cleanup_planes(dev, state);
2015+
drm_atomic_helper_unprepare_planes(dev, state);
20162016

20172017
return 0;
20182018
}
@@ -2072,7 +2072,7 @@ int drm_atomic_helper_commit(struct drm_device *dev,
20722072
return 0;
20732073

20742074
err:
2075-
drm_atomic_helper_cleanup_planes(dev, state);
2075+
drm_atomic_helper_unprepare_planes(dev, state);
20762076
return ret;
20772077
}
20782078
EXPORT_SYMBOL(drm_atomic_helper_commit);
@@ -2650,6 +2650,39 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev,
26502650
}
26512651
EXPORT_SYMBOL(drm_atomic_helper_prepare_planes);
26522652

2653+
/**
2654+
* drm_atomic_helper_unprepare_planes - release plane resources on aborts
2655+
* @dev: DRM device
2656+
* @state: atomic state object with old state structures
2657+
*
2658+
* This function cleans up plane state, specifically framebuffers, from the
2659+
* atomic state. It undoes the effects of drm_atomic_helper_prepare_planes()
2660+
* when aborting an atomic commit. For cleaning up after a successful commit
2661+
* use drm_atomic_helper_cleanup_planes().
2662+
*/
2663+
void drm_atomic_helper_unprepare_planes(struct drm_device *dev,
2664+
struct drm_atomic_state *state)
2665+
{
2666+
struct drm_plane *plane;
2667+
struct drm_plane_state *new_plane_state;
2668+
int i;
2669+
2670+
for_each_new_plane_in_state(state, plane, new_plane_state, i) {
2671+
const struct drm_plane_helper_funcs *funcs = plane->helper_private;
2672+
2673+
if (funcs->end_fb_access)
2674+
funcs->end_fb_access(plane, new_plane_state);
2675+
}
2676+
2677+
for_each_new_plane_in_state(state, plane, new_plane_state, i) {
2678+
const struct drm_plane_helper_funcs *funcs = plane->helper_private;
2679+
2680+
if (funcs->cleanup_fb)
2681+
funcs->cleanup_fb(plane, new_plane_state);
2682+
}
2683+
}
2684+
EXPORT_SYMBOL(drm_atomic_helper_unprepare_planes);
2685+
26532686
static bool plane_crtc_active(const struct drm_plane_state *state)
26542687
{
26552688
return state->crtc && state->crtc->state->active;
@@ -2784,6 +2817,17 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
27842817

27852818
funcs->atomic_flush(crtc, old_state);
27862819
}
2820+
2821+
/*
2822+
* Signal end of framebuffer access here before hw_done. After hw_done,
2823+
* a later commit might have already released the plane state.
2824+
*/
2825+
for_each_old_plane_in_state(old_state, plane, old_plane_state, i) {
2826+
const struct drm_plane_helper_funcs *funcs = plane->helper_private;
2827+
2828+
if (funcs->end_fb_access)
2829+
funcs->end_fb_access(plane, old_plane_state);
2830+
}
27872831
}
27882832
EXPORT_SYMBOL(drm_atomic_helper_commit_planes);
27892833

@@ -2911,40 +2955,22 @@ EXPORT_SYMBOL(drm_atomic_helper_disable_planes_on_crtc);
29112955
* configuration. Hence the old configuration must be perserved in @old_state to
29122956
* be able to call this function.
29132957
*
2914-
* This function must also be called on the new state when the atomic update
2915-
* fails at any point after calling drm_atomic_helper_prepare_planes().
2958+
* This function may not be called on the new state when the atomic update
2959+
* fails at any point after calling drm_atomic_helper_prepare_planes(). Use
2960+
* drm_atomic_helper_unprepare_planes() in this case.
29162961
*/
29172962
void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
29182963
struct drm_atomic_state *old_state)
29192964
{
29202965
struct drm_plane *plane;
2921-
struct drm_plane_state *old_plane_state, *new_plane_state;
2966+
struct drm_plane_state *old_plane_state;
29222967
int i;
29232968

2924-
for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, new_plane_state, i) {
2969+
for_each_old_plane_in_state(old_state, plane, old_plane_state, i) {
29252970
const struct drm_plane_helper_funcs *funcs = plane->helper_private;
29262971

2927-
if (funcs->end_fb_access)
2928-
funcs->end_fb_access(plane, new_plane_state);
2929-
}
2930-
2931-
for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, new_plane_state, i) {
2932-
const struct drm_plane_helper_funcs *funcs;
2933-
struct drm_plane_state *plane_state;
2934-
2935-
/*
2936-
* This might be called before swapping when commit is aborted,
2937-
* in which case we have to cleanup the new state.
2938-
*/
2939-
if (old_plane_state == plane->state)
2940-
plane_state = new_plane_state;
2941-
else
2942-
plane_state = old_plane_state;
2943-
2944-
funcs = plane->helper_private;
2945-
29462972
if (funcs->cleanup_fb)
2947-
funcs->cleanup_fb(plane, plane_state);
2973+
funcs->cleanup_fb(plane, old_plane_state);
29482974
}
29492975
}
29502976
EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);

drivers/gpu/drm/i915/display/intel_display.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7475,7 +7475,7 @@ int intel_atomic_commit(struct drm_device *dev, struct drm_atomic_state *_state,
74757475
for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i)
74767476
intel_color_cleanup_commit(new_crtc_state);
74777477

7478-
drm_atomic_helper_cleanup_planes(dev, &state->base);
7478+
drm_atomic_helper_unprepare_planes(dev, &state->base);
74797479
intel_runtime_pm_put(&dev_priv->runtime_pm, state->wakeref);
74807480
return ret;
74817481
}

drivers/gpu/drm/nouveau/dispnv50/disp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2474,7 +2474,7 @@ nv50_disp_atomic_commit(struct drm_device *dev,
24742474

24752475
err_cleanup:
24762476
if (ret)
2477-
drm_atomic_helper_cleanup_planes(dev, state);
2477+
drm_atomic_helper_unprepare_planes(dev, state);
24782478
done:
24792479
pm_runtime_put_autosuspend(dev->dev);
24802480
return ret;

include/drm/drm_atomic_helper.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
9797

9898
int drm_atomic_helper_prepare_planes(struct drm_device *dev,
9999
struct drm_atomic_state *state);
100+
void drm_atomic_helper_unprepare_planes(struct drm_device *dev,
101+
struct drm_atomic_state *state);
100102

101103
#define DRM_PLANE_COMMIT_ACTIVE_ONLY BIT(0)
102104
#define DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET BIT(1)

0 commit comments

Comments
 (0)