drm/atomic: Update docs around locking and commit sequencing
Both locking and especially sequencing of nonblocking commits have evolved a lot. The details are all there, but I noticed that the big picture and connections have fallen behind a bit. Apply polish. Motivated by some review discussions with Thierry. v2: Review from Thierry Reviewed-by: Thierry Reding <treding@nvidia.com> Cc: Thierry Reding <treding@nvidia.com> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20191204100011.859468-1-daniel.vetter@ffwll.ch
This commit is contained in:
@@ -260,7 +260,8 @@ Taken all together there's two consequences for the atomic design:
|
|||||||
drm_connector_state <drm_connector_state>` for connectors. These are the only
|
drm_connector_state <drm_connector_state>` for connectors. These are the only
|
||||||
objects with userspace-visible and settable state. For internal state drivers
|
objects with userspace-visible and settable state. For internal state drivers
|
||||||
can subclass these structures through embeddeding, or add entirely new state
|
can subclass these structures through embeddeding, or add entirely new state
|
||||||
structures for their globally shared hardware functions.
|
structures for their globally shared hardware functions, see :c:type:`struct
|
||||||
|
drm_private_state<drm_private_state>`.
|
||||||
|
|
||||||
- An atomic update is assembled and validated as an entirely free-standing pile
|
- An atomic update is assembled and validated as an entirely free-standing pile
|
||||||
of structures within the :c:type:`drm_atomic_state <drm_atomic_state>`
|
of structures within the :c:type:`drm_atomic_state <drm_atomic_state>`
|
||||||
@@ -269,6 +270,14 @@ Taken all together there's two consequences for the atomic design:
|
|||||||
to the driver and modeset objects. This way rolling back an update boils down
|
to the driver and modeset objects. This way rolling back an update boils down
|
||||||
to releasing memory and unreferencing objects like framebuffers.
|
to releasing memory and unreferencing objects like framebuffers.
|
||||||
|
|
||||||
|
Locking of atomic state structures is internally using :c:type:`struct
|
||||||
|
drm_modeset_lock <drm_modeset_lock>`. As a general rule the locking shouldn't be
|
||||||
|
exposed to drivers, instead the right locks should be automatically acquired by
|
||||||
|
any function that duplicates or peeks into a state, like e.g.
|
||||||
|
:c:func:`drm_atomic_get_crtc_state()`. Locking only protects the software data
|
||||||
|
structure, ordering of committing state changes to hardware is sequenced using
|
||||||
|
:c:type:`struct drm_crtc_commit <drm_crtc_commit>`.
|
||||||
|
|
||||||
Read on in this chapter, and also in :ref:`drm_atomic_helper` for more detailed
|
Read on in this chapter, and also in :ref:`drm_atomic_helper` for more detailed
|
||||||
coverage of specific topics.
|
coverage of specific topics.
|
||||||
|
|
||||||
|
|||||||
@@ -688,10 +688,12 @@ static void drm_atomic_plane_print_state(struct drm_printer *p,
|
|||||||
* associated state struct &drm_private_state.
|
* associated state struct &drm_private_state.
|
||||||
*
|
*
|
||||||
* Similar to userspace-exposed objects, private state structures can be
|
* Similar to userspace-exposed objects, private state structures can be
|
||||||
* acquired by calling drm_atomic_get_private_obj_state(). Since this function
|
* acquired by calling drm_atomic_get_private_obj_state(). This also takes care
|
||||||
* does not take care of locking, drivers should wrap it for each type of
|
* of locking, hence drivers should not have a need to call drm_modeset_lock()
|
||||||
* private state object they have with the required call to drm_modeset_lock()
|
* directly. Sequence of the actual hardware state commit is not handled,
|
||||||
* for the corresponding &drm_modeset_lock.
|
* drivers might need to keep track of struct drm_crtc_commit within subclassed
|
||||||
|
* structure of &drm_private_state as necessary, e.g. similar to
|
||||||
|
* &drm_plane_state.commit. See also &drm_atomic_state.fake_commit.
|
||||||
*
|
*
|
||||||
* All private state structures contained in a &drm_atomic_state update can be
|
* All private state structures contained in a &drm_atomic_state update can be
|
||||||
* iterated using for_each_oldnew_private_obj_in_state(),
|
* iterated using for_each_oldnew_private_obj_in_state(),
|
||||||
|
|||||||
@@ -1832,17 +1832,21 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
|
|||||||
/**
|
/**
|
||||||
* DOC: implementing nonblocking commit
|
* DOC: implementing nonblocking commit
|
||||||
*
|
*
|
||||||
* Nonblocking atomic commits have to be implemented in the following sequence:
|
* Nonblocking atomic commits should use struct &drm_crtc_commit to sequence
|
||||||
|
* different operations against each another. Locks, especially struct
|
||||||
|
* &drm_modeset_lock, should not be held in worker threads or any other
|
||||||
|
* asynchronous context used to commit the hardware state.
|
||||||
*
|
*
|
||||||
* 1. Run drm_atomic_helper_prepare_planes() first. This is the only function
|
* drm_atomic_helper_commit() implements the recommended sequence for
|
||||||
* which commit needs to call which can fail, so we want to run it first and
|
* nonblocking commits, using drm_atomic_helper_setup_commit() internally:
|
||||||
|
*
|
||||||
|
* 1. Run drm_atomic_helper_prepare_planes(). Since this can fail and we
|
||||||
|
* need to propagate out of memory/VRAM errors to userspace, it must be called
|
||||||
* synchronously.
|
* synchronously.
|
||||||
*
|
*
|
||||||
* 2. Synchronize with any outstanding nonblocking commit worker threads which
|
* 2. Synchronize with any outstanding nonblocking commit worker threads which
|
||||||
* might be affected the new state update. This can be done by either cancelling
|
* might be affected by the new state update. This is handled by
|
||||||
* or flushing the work items, depending upon whether the driver can deal with
|
* drm_atomic_helper_setup_commit().
|
||||||
* cancelled updates. Note that it is important to ensure that the framebuffer
|
|
||||||
* cleanup is still done when cancelling.
|
|
||||||
*
|
*
|
||||||
* Asynchronous workers need to have sufficient parallelism to be able to run
|
* Asynchronous workers need to have sufficient parallelism to be able to run
|
||||||
* different atomic commits on different CRTCs in parallel. The simplest way to
|
* different atomic commits on different CRTCs in parallel. The simplest way to
|
||||||
@@ -1853,21 +1857,29 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
|
|||||||
* must be done as one global operation, and enabling or disabling a CRTC can
|
* must be done as one global operation, and enabling or disabling a CRTC can
|
||||||
* take a long time. But even that is not required.
|
* take a long time. But even that is not required.
|
||||||
*
|
*
|
||||||
|
* IMPORTANT: A &drm_atomic_state update for multiple CRTCs is sequenced
|
||||||
|
* against all CRTCs therein. Therefore for atomic state updates which only flip
|
||||||
|
* planes the driver must not get the struct &drm_crtc_state of unrelated CRTCs
|
||||||
|
* in its atomic check code: This would prevent committing of atomic updates to
|
||||||
|
* multiple CRTCs in parallel. In general, adding additional state structures
|
||||||
|
* should be avoided as much as possible, because this reduces parallelism in
|
||||||
|
* (nonblocking) commits, both due to locking and due to commit sequencing
|
||||||
|
* requirements.
|
||||||
|
*
|
||||||
* 3. The software state is updated synchronously with
|
* 3. The software state is updated synchronously with
|
||||||
* drm_atomic_helper_swap_state(). Doing this under the protection of all modeset
|
* drm_atomic_helper_swap_state(). Doing this under the protection of all modeset
|
||||||
* locks means concurrent callers never see inconsistent state. And doing this
|
* locks means concurrent callers never see inconsistent state. Note that commit
|
||||||
* while it's guaranteed that no relevant nonblocking worker runs means that
|
* workers do not hold any locks; their access is only coordinated through
|
||||||
* nonblocking workers do not need grab any locks. Actually they must not grab
|
* ordering. If workers would access state only through the pointers in the
|
||||||
* locks, for otherwise the work flushing will deadlock.
|
* free-standing state objects (currently not the case for any driver) then even
|
||||||
|
* multiple pending commits could be in-flight at the same time.
|
||||||
*
|
*
|
||||||
* 4. Schedule a work item to do all subsequent steps, using the split-out
|
* 4. Schedule a work item to do all subsequent steps, using the split-out
|
||||||
* commit helpers: a) pre-plane commit b) plane commit c) post-plane commit and
|
* commit helpers: a) pre-plane commit b) plane commit c) post-plane commit and
|
||||||
* then cleaning up the framebuffers after the old framebuffer is no longer
|
* then cleaning up the framebuffers after the old framebuffer is no longer
|
||||||
* being displayed.
|
* being displayed. The scheduled work should synchronize against other workers
|
||||||
*
|
* using the &drm_crtc_commit infrastructure as needed. See
|
||||||
* The above scheme is implemented in the atomic helper libraries in
|
* drm_atomic_helper_setup_commit() for more details.
|
||||||
* drm_atomic_helper_commit() using a bunch of helper functions. See
|
|
||||||
* drm_atomic_helper_setup_commit() for a starting point.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int stall_checks(struct drm_crtc *crtc, bool nonblock)
|
static int stall_checks(struct drm_crtc *crtc, bool nonblock)
|
||||||
@@ -2096,7 +2108,7 @@ EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
|
|||||||
*
|
*
|
||||||
* This function waits for all preceeding commits that touch the same CRTC as
|
* This function waits for all preceeding commits that touch the same CRTC as
|
||||||
* @old_state to both be committed to the hardware (as signalled by
|
* @old_state to both be committed to the hardware (as signalled by
|
||||||
* drm_atomic_helper_commit_hw_done) and executed by the hardware (as signalled
|
* drm_atomic_helper_commit_hw_done()) and executed by the hardware (as signalled
|
||||||
* by calling drm_crtc_send_vblank_event() on the &drm_crtc_state.event).
|
* by calling drm_crtc_send_vblank_event() on the &drm_crtc_state.event).
|
||||||
*
|
*
|
||||||
* This is part of the atomic helper support for nonblocking commits, see
|
* This is part of the atomic helper support for nonblocking commits, see
|
||||||
|
|||||||
@@ -60,8 +60,8 @@
|
|||||||
* wait for flip_done <----
|
* wait for flip_done <----
|
||||||
* clean up atomic state
|
* clean up atomic state
|
||||||
*
|
*
|
||||||
* The important bit to know is that cleanup_done is the terminal event, but the
|
* The important bit to know is that &cleanup_done is the terminal event, but the
|
||||||
* ordering between flip_done and hw_done is entirely up to the specific driver
|
* ordering between &flip_done and &hw_done is entirely up to the specific driver
|
||||||
* and modeset state change.
|
* and modeset state change.
|
||||||
*
|
*
|
||||||
* For an implementation of how to use this look at
|
* For an implementation of how to use this look at
|
||||||
@@ -92,6 +92,9 @@ struct drm_crtc_commit {
|
|||||||
* commit is sent to userspace, or when an out-fence is singalled. Note
|
* commit is sent to userspace, or when an out-fence is singalled. Note
|
||||||
* that for most hardware, in most cases this happens after @hw_done is
|
* that for most hardware, in most cases this happens after @hw_done is
|
||||||
* signalled.
|
* signalled.
|
||||||
|
*
|
||||||
|
* Completion of this stage is signalled implicitly by calling
|
||||||
|
* drm_crtc_send_vblank_event() on &drm_crtc_state.event.
|
||||||
*/
|
*/
|
||||||
struct completion flip_done;
|
struct completion flip_done;
|
||||||
|
|
||||||
@@ -107,6 +110,9 @@ struct drm_crtc_commit {
|
|||||||
* Note that this does not need to include separately reference-counted
|
* Note that this does not need to include separately reference-counted
|
||||||
* resources like backing storage buffer pinning, or runtime pm
|
* resources like backing storage buffer pinning, or runtime pm
|
||||||
* management.
|
* management.
|
||||||
|
*
|
||||||
|
* Drivers should call drm_atomic_helper_commit_hw_done() to signal
|
||||||
|
* completion of this stage.
|
||||||
*/
|
*/
|
||||||
struct completion hw_done;
|
struct completion hw_done;
|
||||||
|
|
||||||
@@ -118,6 +124,9 @@ struct drm_crtc_commit {
|
|||||||
* a vblank wait completed it might be a bit later. This completion is
|
* a vblank wait completed it might be a bit later. This completion is
|
||||||
* useful to throttle updates and avoid hardware updates getting ahead
|
* useful to throttle updates and avoid hardware updates getting ahead
|
||||||
* of the buffer cleanup too much.
|
* of the buffer cleanup too much.
|
||||||
|
*
|
||||||
|
* Drivers should call drm_atomic_helper_commit_cleanup_done() to signal
|
||||||
|
* completion of this stage.
|
||||||
*/
|
*/
|
||||||
struct completion cleanup_done;
|
struct completion cleanup_done;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user