drm/dp_mst: Improve VCPI helpers, use in nouveau
Subsystems (4)
Lyude Paul
maintainerv6 cover drm/dp_mst: Improve VCPI helpers, use in nouveau
This patchset does some cleaning up of the atomic VCPI helpers for MST, and converts nouveau over to using them. I would have included amdgpu in this patch as well, but at the moment moving them over to the atomic helpers is nontrivial.
[sorry for still not adding your R-Bs! I ended up needing to make a lot of changes to make it so we do actually kref() each port in the atomic state]
Cc: Daniel Vetter <[email protected]>
Lyude Paul (6):
drm/dp_mst: Add some atomic state iterator macros drm/dp_mst: Return kref_put() result from drm_dp_put_port() drm/dp_mst: Start tracking per-port VCPI allocations drm/dp_mst: Check payload count in drm_dp_mst_atomic_check() drm/nouveau: Stop unsetting mstc->port, check connector registration drm/nouveau: Use atomic VCPI helpers for MST
drivers/gpu/drm/drm_dp_mst_topology.c | 307 +++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 4 + drivers/gpu/drm/i915/intel_dp_mst.c | 64 +++-- drivers/gpu/drm/nouveau/dispnv50/disp.c | 67 ++++-- include/drm/drm_dp_mst_helper.h | 101 +++++++- 5 files changed, 459 insertions(+), 84 deletions(-)
Signature · 6 lines
2.19.1 _______________________________________________ Intel-gfx mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/intel-gfx
A message that is not in the archive (sent off-list or not yet received): 20181116005008.7243-1-lyude-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org
PATCH v6 1/6 drm/dp_mst: Add some atomic state iterator macros
--- include/drm/drm_dp_mst_helper.h | 77 +++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+)
include/drm/drm_dp_mst_helper.h +77 -0
@@ -628,4 +628,81 @@ int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,int drm_dp_send_power_updown_phy (struct drm_dp_mst_topology_mgr *mgr,struct drm_dp_mst_port *port,bool power_up); +extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs; + +static inline bool +__drm_dp_mst_state_iter_get (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr **mgr, +struct drm_dp_mst_topology_state **old_state, +struct drm_dp_mst_topology_state **new_state, +int i) +{ +struct __drm_private_objs_state *objs_state = &state->private_objs[i]; + +if (objs_state->ptr->funcs != &drm_dp_mst_topology_state_funcs) +return false; + + *mgr =to_dp_mst_topology_mgr (objs_state->ptr); +if (old_state) + *old_state =to_dp_mst_topology_state (objs_state->old_state); +if (new_state) + *new_state =to_dp_mst_topology_state (objs_state->new_state); + +return true; +} + +/** +* for_each_oldnew_mst_mgr_in_state - iterate over all DP MST topology +* managers in an atomic update +* @__state: &struct drm_atomic_state pointer +* @mgr: &struct drm_dp_mst_topology_mgr iteration cursor +* @old_state: &struct drm_dp_mst_topology_state iteration cursor for the old +* state +* @new_state: &struct drm_dp_mst_topology_state iteration cursor for the new +* state +* @__i: int iteration cursor, for macro-internal use +* +* This iterates over all DRM DP MST topology managers in an atomic update, +* tracking both old and new state. This is useful in places where the state +* delta needs to be considered, for example in atomic check functions. +*/ +#define for_each_oldnew_mst_mgr_in_state (__state, mgr, old_state, new_state, __i) \ + for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \ + for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), &(new_state), (__i))) + +/** +* for_each_old_mst_mgr_in_state - iterate over all DP MST topology managers +* in an atomic update +* @__state: &struct drm_atomic_state pointer +* @mgr: &struct drm_dp_mst_topology_mgr iteration cursor +* @old_state: &struct drm_dp_mst_topology_state iteration cursor for the old +* state +* @__i: int iteration cursor, for macro-internal use +* +* This iterates over all DRM DP MST topology managers in an atomic update, +* tracking only the old state. This is useful in disable functions, where we +* need the old state the hardware is still in. +*/ +#define for_each_old_mst_mgr_in_state (__state, mgr, old_state, __i) \ + for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \ + for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), NULL, (__i))) + +/** +* for_each_new_mst_mgr_in_state - iterate over all DP MST topology managers +* in an atomic update +* @__state: &struct drm_atomic_state pointer +* @mgr: &struct drm_dp_mst_topology_mgr iteration cursor +* @new_state: &struct drm_dp_mst_topology_state iteration cursor for the new +* state +* @__i: int iteration cursor, for macro-internal use +* +* This iterates over all DRM DP MST topology managers in an atomic update, +* tracking only the new state. This is useful in enable functions, where we +* need the new state the hardware should be in when the atomic commit +* operation has completed. +*/ +#define for_each_new_mst_mgr_in_state (__state, mgr, new_state, __i) \ + for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \ + for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), NULL, &(new_state), (__i))) +#endif
Signature · 6 lines
2.19.1 _______________________________________________ Nouveau mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/nouveau
Re: [Nouveau] [PATCH v6 1/6] drm/dp_mst: Add some atomic state iterator macros
Quoted 16 lines · Lyude Paul
Signed-off-by Lyude Paul <[email protected]>Reviewed-by Daniel Vetter <[email protected]>--- include/drm/drm_dp_mst_helper.h | 77 +++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+)include/drm/drm_dp_mst_helper.h +2
-0@@ -628,4 +628,81 @@ int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,int drm_dp_send_power_updown_phy (struct drm_dp_mst_topology_mgr *mgr,struct drm_dp_mst_port *port,bool power_up); +extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs; +
include/drm/drm_dp_mst_helper.h int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state, · line 642
Quoted 10 lines
include/drm/drm_dp_mst_helper.h +10
-0+static inline bool +__drm_dp_mst_state_iter_get (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr **mgr, +struct drm_dp_mst_topology_state **old_state, +struct drm_dp_mst_topology_state **new_state, +int i) +{ +struct __drm_private_objs_state *objs_state = &state->private_objs[i]; + +if (objs_state->ptr->funcs != &drm_dp_mst_topology_state_funcs)
Just noticed that this patch is missing the EXPORT_SYMBOL, which is burried in patch 3. Needs to be moved. A tiny bit of kerneldoc along the lines of "never use this directly" would be good too :-) -Daniel
Quoted 73 lines
include/drm/drm_dp_mst_helper.h +65
-0+return false; + + *mgr =to_dp_mst_topology_mgr (objs_state->ptr); +if (old_state) + *old_state =to_dp_mst_topology_state (objs_state->old_state); +if (new_state) + *new_state =to_dp_mst_topology_state (objs_state->new_state); + + return true; +} + +/** +* for_each_oldnew_mst_mgr_in_state - iterate over all DP MST topology +* managers in an atomic update +* @__state: &struct drm_atomic_state pointer +* @mgr: &struct drm_dp_mst_topology_mgr iteration cursor +* @old_state: &struct drm_dp_mst_topology_state iteration cursor for the old +* state +* @new_state: &struct drm_dp_mst_topology_state iteration cursor for the new +* state +* @__i: int iteration cursor, for macro-internal use +* +* This iterates over all DRM DP MST topology managers in an atomic update, +* tracking both old and new state. This is useful in places where the state +* delta needs to be considered, for example in atomic check functions. +*/ +#define for_each_oldnew_mst_mgr_in_state (__state, mgr, old_state, new_state, __i) \ + for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \ + for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), &(new_state), (__i))) + +/** +* for_each_old_mst_mgr_in_state - iterate over all DP MST topology managers +* in an atomic update +* @__state: &struct drm_atomic_state pointer +* @mgr: &struct drm_dp_mst_topology_mgr iteration cursor +* @old_state: &struct drm_dp_mst_topology_state iteration cursor for the old +* state +* @__i: int iteration cursor, for macro-internal use +* +* This iterates over all DRM DP MST topology managers in an atomic update, +* tracking only the old state. This is useful in disable functions, where we +* need the old state the hardware is still in. +*/ +#define for_each_old_mst_mgr_in_state (__state, mgr, old_state, __i) \ + for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \ + for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), NULL, (__i))) + +/** +* for_each_new_mst_mgr_in_state - iterate over all DP MST topology managers +* in an atomic update +* @__state: &struct drm_atomic_state pointer +* @mgr: &struct drm_dp_mst_topology_mgr iteration cursor +* @new_state: &struct drm_dp_mst_topology_state iteration cursor for the new +* state +* @__i: int iteration cursor, for macro-internal use +* +* This iterates over all DRM DP MST topology managers in an atomic update, +* tracking only the new state. This is useful in enable functions, where we +* need the new state the hardware should be in when the atomic commit +* operation has completed. +*/ +#define for_each_new_mst_mgr_in_state (__state, mgr, new_state, __i) \ + for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \ + for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), NULL, &(new_state), (__i))) +#endif Signature · 6 lines
2.19.1 _______________________________________________ Nouveau mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/nouveau
Signature · 7 lines
Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ Intel-gfx mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/intel-gfx
PATCH v6 2/6 drm/dp_mst: Return kref_put() result from drm_dp_put_port()
In reply to a message not in the archiveThis will let us warn if we end up freeing a port when we're not expecting to, which will make reporting erroneous usage of the new MST VCPI helpers a little easier.
--- drivers/gpu/drm/drm_dp_mst_topology.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/gpu/drm/drm_dp_mst_topology.c +3 -3
@@ -45,7 +45,7 @@ static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr, char *buf);static int test_calc_pbn_mode (void ); -static void drm_dp_put_port (struct drm_dp_mst_port *port); +static int drm_dp_put_port (struct drm_dp_mst_port *port); static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, int id,
@@ -969,9 +969,9 @@ static void drm_dp_destroy_port(struct kref *kref)kfree (port); } -static void drm_dp_put_port (struct drm_dp_mst_port *port) +static int drm_dp_put_port (struct drm_dp_mst_port *port) { -kref_put (&port->kref, drm_dp_destroy_port); +return kref_put (&port->kref, drm_dp_destroy_port); }static struct drm_dp_mst_branch *drm_dp_mst_get_validated_mstb_ref_locked (struct drm_dp_mst_branch *mstb,struct drm_dp_mst_branch *to_find)
Signature · 6 lines
2.19.1 _______________________________________________ Nouveau mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/nouveau
PATCH v6 3/6 drm/dp_mst: Start tracking per-port VCPI allocations
In reply to a message not in the archiveThere has been a TODO waiting for quite a long time in drm_dp_mst_topology.c:
/* We cannot rely on port->vcpi.num_slots to update
* topology_state->avail_slots as the port may not exist if the parent
* branch device was unplugged. This should be fixed by tracking
* per-port slot allocation in drm_dp_mst_topology_state instead of
* depending on the caller to tell us how many slots to release. */
That's not the only reason we should fix this: forcing the driver to track the VCPI allocations throughout a state's atomic check is error prone, because it means that extra care has to be taken with the order that drm_dp_atomic_find_vcpi_slots() and drm_dp_atomic_release_vcpi_slots() are called in in order to ensure idempotency. Currently the only driver actually using these helpers, i915, doesn't even do this correctly: multiple ->best_encoder() checks with i915's current implementation would not be idempotent and would over-allocate VCPI slots, something I learned trying to implement fallback retraining in MST.
So: simplify this whole mess, and teach drm_dp_atomic_find_vcpi_slots() and drm_dp_atomic_release_vcpi_slots() to track the VCPI allocations for each port. This allows us to ensure idempotency without having to rely on the driver as much. Additionally: the driver doesn't need to do any kind of VCPI slot tracking anymore if it doesn't need it for it's own internal state.
Additionally; this adds a new drm_dp_mst_atomic_check() helper which must be used by atomic drivers to perform validity checks for the new VCPI allocations incurred by a state.
Also: update the documentation and make it more obvious that these /must/ be called by /all/ atomic drivers supporting MST.
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This required a good bit of changing to when we call drm_dp_find_vcpi_slots(), mainly that we need to ensure that we only redo VCPI allocations on actual mode or CRTC changes, not crtc_state->active changes. Additionally, we no longer take the registration of the DRM connector for each port into account because so long as we have a kref to the port in the new or previous atomic state, the connector will stay registered.
- Use the small changes to drm_dp_put_port() to add even more error checking to make misusage of the helpers more obvious. I added this after having to chase down various use-after-free conditions that started popping up from the new helpers so no one else has to troubleshoot that.
- Move some accidental DRM_DEBUG_KMS() calls to DRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both not be called on the same port in a single atomic check phase (but multiple calls to one or the other is OK)
Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI allocations happen in a state. This makes the next change I'm about to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure that when ports are destroyed and still have VCPI allocations in the topology state, the only state changes allowed are releasing said ports' VCPI. This prevents a state with a mix of VCPI allocations from destroyed ports, and allocations from valid ports.
Changes since v3:
- Don't release VCPI allocations in the topology state immediately in drm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and skip over them in drm_dp_mst_duplicate_state(). This makes it so drm_dp_atomic_release_vcpi_slots() is still idempotent while also throwing warnings if the driver messes up it's book keeping and tries to release VCPI slots on a port that doesn't have any pre-existing VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"
Changes since v2:
- Use kmemdup() for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states in drm_dp_mst_atomic_check() so the driver doesn't have to do it
- Fix documentation in drm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into it's own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth requirements. This is needed because originally we relied on the state duplication functions to prune any stale ports from the new state, which would prevent us from incorrectly considering their bandwidth requirements alongside legitimate new payloads.
- Add function references in drm_dp_atomic_release_vcpi_slots() - danvet
- Annotate atomic VCPI and atomic check functions with __must_check
- danvet
Changes since v1:
- Don't use the now-removed ->atomic_check() for private objects hook, just give drivers a function to call themselves
--- drivers/gpu/drm/drm_dp_mst_topology.c | 294 +++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 4 + drivers/gpu/drm/i915/intel_dp_mst.c | 64 +++--- include/drm/drm_dp_mst_helper.h | 24 ++- 4 files changed, 319 insertions(+), 67 deletions(-)
drivers/gpu/drm/drm_dp_mst_topology.c +260 -34
@@ -2617,21 +2617,42 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, }/** -* drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state +* drm_dp_atomic_find_vcpi_slots() - Find and add VCPI slots to the state * @state: global atomic state * @mgr: MST topology manager for the port * @port: port to find vcpi slots for * @pbn: bandwidth required for the mode in PBN * -* RETURNS: -* Total slots in the atomic state assigned for this port or error +* Allocates VCPI slots to @port, replacing any previous VCPI allocations it +* may have had. Any atomic drivers which support MST must call this function +* in their &drm_encoder_helper_funcs.atomic_check() callback to change the +* current VCPI allocation for the new state, but only when +* &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed is set +* to ensure compatibility with userspace applications that still use the +* legacy modesetting UAPI. +* +* Allocations set by this function are not checked against the bandwidth +* restraints of @mgr until the driver calls drm_dp_mst_atomic_check(). +* +* Additionally, it is OK to call this function multiple times on the same +* @port as needed. It is not OK however, to call this function and +* drm_dp_atomic_release_vcpi_slots() in the same atomic check phase. +* +* See also: +* drm_dp_atomic_release_vcpi_slots() +* drm_dp_mst_atomic_check() +* +* Returns: +* Total slots in the atomic state assigned for this port, or a negative error +* code if the port no longer exists */ int drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state,struct drm_dp_mst_topology_mgr *mgr,struct drm_dp_mst_port *port,int pbn) {struct drm_dp_mst_topology_state *topology_state; -int req_slots; +struct drm_dp_vcpi_allocation *pos, *vcpi =NULL ; +int prev_slots, req_slots, ret; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))
@@ -2640,20 +2661,60 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, port =drm_dp_get_validated_port_ref (mgr, port);if (port ==NULL )return -EINVAL ; - req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); -DRM_DEBUG_KMS ("vcpi slots req=%d, avail=%d\n" , - req_slots, topology_state->avail_slots); -if (req_slots > topology_state->avail_slots) { -drm_dp_put_port (port); -return -ENOSPC ; + topology_state->vcpi_allocated = true; + +/* Find the current allocation for this port, if any */ +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + vcpi = pos; + prev_slots = vcpi->vcpi; + +/* +* This should never happen, unless the driver tries +* releasing and allocating the same VCPI allocation, +* which is an error +*/ +if (WARN_ON (!prev_slots)) { +DRM_ERROR ("cannot allocate and release VCPI on [MST PORT:%p] in the same state\n" , + port); +return -EINVAL ; + } + +break ; + } } +if (!vcpi) + prev_slots =0 ; - topology_state->avail_slots -= req_slots; -DRM_DEBUG_KMS ("vcpi slots avail=%d" , topology_state->avail_slots); + req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); + +DRM_DEBUG_ATOMIC ("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n" , + port->connector->base.id, port->connector->name, + port, prev_slots, req_slots); + +/* Add the new allocation to the state */ +if (!vcpi) { + vcpi =kzalloc (sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) { + ret = -ENOMEM ; + goto out; + } + vcpi->port =drm_dp_get_validated_port_ref (mgr, port); +if (!vcpi->port) { + ret = -EINVAL ; +kfree (vcpi); + goto out; + } +list_add (&vcpi->next, &topology_state->vcpis); + } + vcpi->vcpi = req_slots; + + ret = req_slots; +out:drm_dp_put_port (port); -return req_slots; +return ret; }EXPORT_SYMBOL (drm_dp_atomic_find_vcpi_slots);
@@ -2661,31 +2722,66 @@ EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);* drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots * @state: global atomic state * @mgr: MST topology manager for the port -* @slots: number of vcpi slots to release +* @port: The port to release the VCPI slots from * -* RETURNS: -* 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or -* negative error code +* Releases any VCPI slots that have been allocated to a port in the atomic +* state. Any atomic drivers which support MST must call this function in +* their &drm_connector_helper_funcs.atomic_check() callback when the +* connector will no longer have VCPI allocated (e.g. because it's CRTC was +* removed) when it had VCPI allocated in the previous atomic state. +* +* It is OK to call this even if @port has been removed from the system. +* Additionally, it is OK to call this function multiple times on the same +* @port as needed. It is not OK however, to call this function and +* drm_dp_atomic_find_vcpi_slots() on the same @port in a single atomic check +* phase. +* +* See also: +* drm_dp_atomic_find_vcpi_slots() +* drm_dp_mst_atomic_check() +* +* Returns: +* 0 if all slots for this port were added back to +* &drm_dp_mst_topology_state.avail_slots or negative error code */ int drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state,struct drm_dp_mst_topology_mgr *mgr, -int slots) +struct drm_dp_mst_port *port) {struct drm_dp_mst_topology_state *topology_state; +struct drm_dp_vcpi_allocation *pos; +bool found = false; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))return PTR_ERR (topology_state); -/* We cannot rely on port->vcpi.num_slots to update -* topology_state->avail_slots as the port may not exist if the parent -* branch device was unplugged. This should be fixed by tracking -* per-port slot allocation in drm_dp_mst_topology_state instead of -* depending on the caller to tell us how many slots to release. -*/ - topology_state->avail_slots += slots; -DRM_DEBUG_KMS ("vcpi slots released=%d, avail=%d\n" , - slots, topology_state->avail_slots); +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + found = true; +break ; + } + } +if (WARN_ON (!found)) { +DRM_ERROR ("no VCPI for [MST PORT:%p] found in mst state %p\n" , + port, &topology_state->base); +return -EINVAL ; + } + +DRM_DEBUG_ATOMIC ("[MST PORT:%p] VCPI %d -> 0\n" , port, pos->vcpi); +if (pos->vcpi) { +/* +* This should be safe-as the previous state will still hold a +* reference to this port until we swap states. If it's not, +* the driver has a bookkeeping error +*/ +if (WARN_ON (drm_dp_put_port (port))) { +DRM_ERROR ("cannot allocate and release VCPI on [MST PORT:%p] in the same state\n" , + port); +return -EINVAL ; + } + pos->vcpi =0 ; + }return 0 ; }
@@ -3115,15 +3211,45 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)static struct drm_private_state *drm_dp_mst_duplicate_state (struct drm_private_obj *obj) { -struct drm_dp_mst_topology_state *state; +struct drm_dp_mst_topology_state *state, *old_state = +to_dp_mst_topology_state (obj->state); +struct drm_dp_vcpi_allocation *pos, *vcpi; - state =kmemdup (obj->state,sizeof (*state),GFP_KERNEL ); + state =kmemdup (old_state,sizeof (*state),GFP_KERNEL );if (!state)return NULL ;__drm_atomic_helper_private_obj_duplicate_state (obj, &state->base); +INIT_LIST_HEAD (&state->vcpis); + state->vcpi_allocated = false; + +list_for_each_entry (pos, &old_state->vcpis, next) { +/* Prune leftover freed VCPI allocations */ +if (!pos->vcpi) +continue ; + + vcpi =kmemdup (pos,sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) + goto fail; + +/* Take a plain old kref here and don't validate the port, as +* it's topology might not even exist anymore +*/ +kref_get (&vcpi->port->kref); +list_add (&vcpi->next, &state->vcpis); + } +return &state->base; + +fail: +list_for_each_entry_safe (pos, vcpi, &state->vcpis, next) { +drm_dp_put_port (pos->port); +kfree (pos); + } +kfree (state); + +return NULL ; } static void drm_dp_mst_destroy_state(struct drm_private_obj *obj,
@@ -3131,14 +3257,116 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, {struct drm_dp_mst_topology_state *mst_state =to_dp_mst_topology_state (state); +struct drm_dp_vcpi_allocation *pos, *tmp; + +list_for_each_entry_safe (pos, tmp, &mst_state->vcpis, next) { +/* We only keep references to ports with non-zero VCPIs */ +if (pos->vcpi) +drm_dp_put_port (pos->port); +kfree (pos); + }kfree (mst_state); } -static const struct drm_private_state_funcs mst_state_funcs = { +static inline int +drm_dp_mst_atomic_check_topology_state (struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_topology_state *mst_state) +{ +struct drm_dp_vcpi_allocation *vcpi; +struct drm_dp_mst_port *port; +int avail_slots =63 , ret; + +/* There's no possible scenario where releasing VCPI or keeping it the +* same would make the state invalid +*/ +if (!mst_state->vcpi_allocated) { +DRM_DEBUG_ATOMIC ("[MST MGR:%p] state %p allocates no VCPI, check skipped\n" , + mgr, &mst_state->base); +return 0 ; + } + +list_for_each_entry (vcpi, &mst_state->vcpis, next) { +/* Releasing VCPI is always OK-even if the port is gone */ +if (!vcpi->vcpi) { +DRM_DEBUG_ATOMIC ("[MST PORT:%p] releases all VCPI slots\n" , + vcpi->port); +continue ; + } + + port =drm_dp_get_validated_port_ref (mgr, vcpi->port); +if (!port) { +DRM_DEBUG_ATOMIC ("[MST PORT:%p] is gone but still has VCPI, cannot add new VCPI\n" , + vcpi->port); +return -EINVAL ; + } + +DRM_DEBUG_ATOMIC ("[MST PORT:%p] requires %d vcpi slots\n" , + vcpi->port, vcpi->vcpi); + + avail_slots -= vcpi->vcpi; +if (avail_slots <0 ) { +DRM_DEBUG_ATOMIC ("[MST PORT:%p] not enough VCPI slots in mst state %p (avail=%d)\n" , + vcpi->port, mst_state, + avail_slots + vcpi->vcpi); + ret = -ENOSPC ; + goto port_fail; + } + +drm_dp_put_port (port); + } +DRM_DEBUG_ATOMIC ("[MST MGR:%p] mst state %p VCPI avail=%d used=%d\n" , + mgr, mst_state, avail_slots, +63 - avail_slots); + +return 0 ; +port_fail: +drm_dp_put_port (port); +return ret; +} + +/** +* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an +* atomic update is valid +* @state: Pointer to the new &struct drm_dp_mst_topology_state +* +* Checks the given topology state for an atomic update to ensure that it's +* valid. This includes checking whether there's enough bandwidth to support +* the new VCPI allocations in the atomic update. +* +* Any atomic drivers supporting DP MST must make sure to call this after +* checking the rest of their state in their +* &drm_mode_config_funcs.atomic_check() callback. +* +* See also: +* drm_dp_atomic_find_vcpi_slots() +* drm_dp_atomic_release_vcpi_slots() +* +* Returns: +* +* 0 if the new state is valid, negative error code otherwise. +*/ +int drm_dp_mst_atomic_check (struct drm_atomic_state *state) +{ +struct drm_dp_mst_topology_mgr *mgr; +struct drm_dp_mst_topology_state *mst_state; +int i, ret =0 ; + +for_each_new_mst_mgr_in_state (state, mgr, mst_state, i) { + ret =drm_dp_mst_atomic_check_topology_state (mgr, mst_state); +if (ret) +break ; + } + +return ret; +} +EXPORT_SYMBOL (drm_dp_mst_atomic_check); + +const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs = { .atomic_duplicate_state = drm_dp_mst_duplicate_state, .atomic_destroy_state = drm_dp_mst_destroy_state, }; +EXPORT_SYMBOL (drm_dp_mst_topology_state_funcs);/** * drm_atomic_get_mst_topology_state: get MST topology state
@@ -3216,13 +3444,11 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,return -ENOMEM ; mst_state->mgr = mgr; - -/* max. time slots - one slot for MTP header */ - mst_state->avail_slots =63 ; +INIT_LIST_HEAD (&mst_state->vcpis);drm_atomic_private_obj_init (&mgr->base, &mst_state->base, - &mst_state_funcs); + &drm_dp_mst_topology_state_funcs);return 0 ; }
drivers/gpu/drm/i915/intel_display.c +4 -0
@@ -12592,6 +12592,10 @@ static int intel_atomic_check(struct drm_device *dev, "[modeset]" : "[fastset]"); } + ret =drm_dp_mst_atomic_check (state); +if (ret) +return ret; +if (any_ms) { ret =intel_modeset_checks (state);
drivers/gpu/drm/i915/intel_dp_mst.c +38 -26
90 lines not on this page · raw message
include/drm/drm_dp_mst_helper.h +17 -7
@@ -406,9 +406,16 @@ struct drm_dp_payload {#define to_dp_mst_topology_state (x) container_of(x, struct drm_dp_mst_topology_state, base) +struct drm_dp_vcpi_allocation { +struct drm_dp_mst_port *port; +int vcpi; +struct list_head next; +}; +struct drm_dp_mst_topology_state {struct drm_private_state base; -int avail_slots; +struct list_head vcpis; +u8 vcpi_allocated;struct drm_dp_mst_topology_mgr *mgr; };
@@ -619,14 +626,17 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);int drm_dp_mst_topology_mgr_resume (struct drm_dp_mst_topology_mgr *mgr);struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state (struct drm_atomic_state *state,struct drm_dp_mst_topology_mgr *mgr); -int drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -struct drm_dp_mst_port *port,int pbn); -int drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -int slots); +int __must_check +drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port,int pbn); +int __must_check +drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port);int drm_dp_send_power_updown_phy (struct drm_dp_mst_topology_mgr *mgr,struct drm_dp_mst_port *port,bool power_up); +int __must_check drm_dp_mst_atomic_check (struct drm_atomic_state *state);extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs;
Signature · 6 lines
2.19.1 _______________________________________________ Nouveau mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/nouveau
A message that is not in the archive (sent off-list or not yet received): 20181116005008.7243-4-lyude-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org
Re: [PATCH v6 3/6] drm/dp_mst: Start tracking per-port VCPI allocations
Quoted 35 lines · Lyude Paul
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:/* We cannot rely on
port->vcpi.num_slotsto update
*topology_state->avail_slotsas the port may not exist if the parent
* branch device was unplugged. This should be fixed by tracking
* per-port slot allocation indrm_dp_mst_topology_stateinstead of
* depending on the caller to tell us how many slots to release. */That's not the only reason we should fix this: forcing the driver to track the VCPI allocations throughout a state's atomic check is error prone, because it means that extra care has to be taken with the order that
drm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()are called in in order to ensure idempotency. Currently the only driver actually using these helpers, i915, doesn't even do this correctly: multiple ->best_encoder() checks with i915's current implementation would not be idempotent and would over-allocate VCPI slots, something I learned trying to implement fallback retraining in MST.So: simplify this whole mess, and teach
drm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()to track the VCPI allocations for each port. This allows us to ensure idempotency without having to rely on the driver as much. Additionally: the driver doesn't need to do any kind of VCPI slot tracking anymore if it doesn't need it for it's own internal state.Additionally; this adds a new
drm_dp_mst_atomic_check()helper which must be used by atomic drivers to perform validity checks for the new VCPI allocations incurred by a state.Also: update the documentation and make it more obvious that these /must/ be called by /all/ atomic drivers supporting MST.
Quoted 9 lines
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This required a good bit of changing to when we calldrm_dp_find_vcpi_slots(), mainly that we need to ensure that we only redo VCPI allocations on actual mode or CRTC changes, notcrtc_state->activechanges. Additionally, we no longer take the registration of the DRM connector for each port into account because so long as we have a kref to the port in the new or previous atomic state, the connector will stay registered.
I write an entire pile of small nitpits (still included most of them below), until I realized this here wont work. Delaying the call to destroy the connector (well, unregister it really) wreaks the design we've come up with thus far, resulting in most of my comments here.
Instead, all we need to do is delay the kfree(port) at the bottom of drm_dp_destroy_port(). The vcpi allocation structure _only_ needs the pointer value to stay valid, as a lookup key. It doesn't care at all about anything actually stored in there. So the only thing we need to delay is the kfree. I think the simplest way to achieve this is to add a 2nd kref (port->kfree_ref or something like that), with on reference held by the port itself (released in drm_dp_destroy_port), and the other one held as-needed by the vcpi allocation lists.
I think if we go with this design instead of retrofitting a semantic change of the port lifetime itself, all the complications I complain about below should disappear.
Piles of comments below.
Cheers, Daniel
Quoted 132 lines
- Use the small changes to
drm_dp_put_port()to add even more error checking to make misusage of the helpers more obvious. I added this after having to chase down various use-after-free conditions that started popping up from the new helpers so no one else has to troubleshoot that.
- Move some accidentalDRM_DEBUG_KMS()calls toDRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both not be called on the same port in a single atomic check phase (but multiple calls to one or the other is OK)Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI allocations happen in a state. This makes the next change I'm about to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure that when ports are destroyed and still have VCPI allocations in the topology state, the only state changes allowed are releasing said ports' VCPI. This prevents a state with a mix of VCPI allocations from destroyed ports, and allocations from valid ports.Changes since v3:
- Don't release VCPI allocations in the topology state immediately indrm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and skip over them indrm_dp_mst_duplicate_state(). This makes it sodrm_dp_atomic_release_vcpi_slots()is still idempotent while also throwing warnings if the driver messes up it's book keeping and tries to release VCPI slots on a port that doesn't have any pre-existing VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"Changes since v2:
- Usekmemdup()for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states indrm_dp_mst_atomic_check()so the driver doesn't have to do it
- Fix documentation indrm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into it's own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth requirements. This is needed because originally we relied on the state duplication functions to prune any stale ports from the new state, which would prevent us from incorrectly considering their bandwidth requirements alongside legitimate new payloads.
- Add function references indrm_dp_atomic_release_vcpi_slots()- danvet
- Annotate atomic VCPI and atomic check functions with__must_check
- danvetChanges since v1:
- Don't use the now-removed ->atomic_check() for private objects hook, just give drivers a function to call themselvesSigned-off-by Lyude Paul <[email protected]>Cc Daniel Vetter <[email protected]>--- drivers/gpu/drm/drm_dp_mst_topology.c | 294 +++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 4 + drivers/gpu/drm/i915/intel_dp_mst.c | 64 +++--- include/drm/drm_dp_mst_helper.h | 24 ++- 4 files changed, 319 insertions(+), 67 deletions(-)drivers/gpu/drm/drm_dp_mst_topology.c +35
-10@@ -2617,21 +2617,42 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, }/** -* drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state +* drm_dp_atomic_find_vcpi_slots() - Find and add VCPI slots to the state * @state: global atomic state * @mgr: MST topology manager for the port * @port: port to find vcpi slots for * @pbn: bandwidth required for the mode in PBN * -* RETURNS: -* Total slots in the atomic state assigned for this port or error +* Allocates VCPI slots to @port, replacing any previous VCPI allocations it +* may have had. Any atomic drivers which support MST must call this function +* in their &drm_encoder_helper_funcs.atomic_check() callback to change the +* current VCPI allocation for the new state, but only when +* &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed is set +* to ensure compatibility with userspace applications that still use the +* legacy modesetting UAPI. +* +* Allocations set by this function are not checked against the bandwidth +* restraints of @mgr until the driver calls drm_dp_mst_atomic_check(). +* +* Additionally, it is OK to call this function multiple times on the same +* @port as needed. It is not OK however, to call this function and +* drm_dp_atomic_release_vcpi_slots() in the same atomic check phase. +* +* See also: +* drm_dp_atomic_release_vcpi_slots() +* drm_dp_mst_atomic_check() +* +* Returns: +* Total slots in the atomic state assigned for this port, or a negative error +* code if the port no longer exists */ int drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state,struct drm_dp_mst_topology_mgr *mgr,struct drm_dp_mst_port *port,int pbn) {struct drm_dp_mst_topology_state *topology_state; -int req_slots; +struct drm_dp_vcpi_allocation *pos, *vcpi =NULL ; +int prev_slots, req_slots, ret; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))@@ -2640,20 +2661,60 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, port =drm_dp_get_validated_port_ref (mgr, port);if (port ==NULL )return -EINVAL ; - req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); -DRM_DEBUG_KMS ("vcpi slots req=%d, avail=%d\n" , - req_slots, topology_state->avail_slots); -if (req_slots > topology_state->avail_slots) { -drm_dp_put_port (port); -return -ENOSPC ; + topology_state->vcpi_allocated = true; + +/* Find the current allocation for this port, if any */ +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + vcpi = pos; + prev_slots = vcpi->vcpi; + +/* +* This should never happen, unless the driver tries
drivers/gpu/drm/drm_dp_mst_topology.c int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, · line 2684
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +8
-0+* releasing and allocating the same VCPI allocation, +* which is an error +*/ +if (WARN_ON (!prev_slots)) { +DRM_ERROR ("cannot allocate and release VCPI on [MST PORT:%p] in the same state\n" , + port); +return -EINVAL ; + }
Allowing atomic changes to output state (like disabling it on one crtc, and then enabling on the other, thus both releasing and re-allocating the vcpi in one go) is kinda the point of atomic. So definitely can't EINVAL here.
Sounds like we need more testcases in igt?
Quoted 104 lines
drivers/gpu/drm/drm_dp_mst_topology.c +31
-3+ +break ; + } } +if (!vcpi) + prev_slots =0 ; - topology_state->avail_slots -= req_slots; -DRM_DEBUG_KMS ("vcpi slots avail=%d" , topology_state->avail_slots); + req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); + +DRM_DEBUG_ATOMIC ("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n" , + port->connector->base.id, port->connector->name, + port, prev_slots, req_slots); + +/* Add the new allocation to the state */ +if (!vcpi) { + vcpi =kzalloc (sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) { + ret = -ENOMEM ; + goto out; + } + vcpi->port =drm_dp_get_validated_port_ref (mgr, port); +if (!vcpi->port) { + ret = -EINVAL ; +kfree (vcpi); + goto out; + } +list_add (&vcpi->next, &topology_state->vcpis); + } + vcpi->vcpi = req_slots; + + ret = req_slots; +out:drm_dp_put_port (port); -return req_slots; +return ret; }
EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);drivers/gpu/drm/drm_dp_mst_topology.c +33
-14@@ -2661,31 +2722,66 @@ EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);* drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots * @state: global atomic state * @mgr: MST topology manager for the port -* @slots: number of vcpi slots to release +* @port: The port to release the VCPI slots from * -* RETURNS: -* 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or -* negative error code +* Releases any VCPI slots that have been allocated to a port in the atomic +* state. Any atomic drivers which support MST must call this function in +* their &drm_connector_helper_funcs.atomic_check() callback when the +* connector will no longer have VCPI allocated (e.g. because it's CRTC was +* removed) when it had VCPI allocated in the previous atomic state. +* +* It is OK to call this even if @port has been removed from the system. +* Additionally, it is OK to call this function multiple times on the same +* @port as needed. It is not OK however, to call this function and +* drm_dp_atomic_find_vcpi_slots() on the same @port in a single atomic check +* phase. +* +* See also: +* drm_dp_atomic_find_vcpi_slots() +* drm_dp_mst_atomic_check() +* +* Returns: +* 0 if all slots for this port were added back to +* &drm_dp_mst_topology_state.avail_slots or negative error code */ int drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state,struct drm_dp_mst_topology_mgr *mgr, -int slots) +struct drm_dp_mst_port *port) {struct drm_dp_mst_topology_state *topology_state; +struct drm_dp_vcpi_allocation *pos; +bool found = false; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))return PTR_ERR (topology_state); -/* We cannot rely on port->vcpi.num_slots to update -* topology_state->avail_slots as the port may not exist if the parent -* branch device was unplugged. This should be fixed by tracking -* per-port slot allocation in drm_dp_mst_topology_state instead of -* depending on the caller to tell us how many slots to release. -*/ - topology_state->avail_slots += slots; -DRM_DEBUG_KMS ("vcpi slots released=%d, avail=%d\n" , - slots, topology_state->avail_slots); +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + found = true; +break ; + } + } +if (WARN_ON (!found)) { +DRM_ERROR ("no VCPI for [MST PORT:%p] found in mst state %p\n" , + port, &topology_state->base); +return -EINVAL ;
drivers/gpu/drm/drm_dp_mst_topology.c EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots); · line 2779
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10
-0+ } + +DRM_DEBUG_ATOMIC ("[MST PORT:%p] VCPI %d -> 0\n" , port, pos->vcpi); +if (pos->vcpi) { +/* +* This should be safe-as the previous state will still hold a +* reference to this port until we swap states. If it's not, +* the driver has a bookkeeping error +*/ +if (WARN_ON (drm_dp_put_port (port))) {
The usual way to do this (instead of adding a return value to the normal _put function) is something like
kref_put(port, function_that_with_just_a_WARNING_DRM_ERROR)
avoids the need for patch 2.
Quoted 76 lines
drivers/gpu/drm/drm_dp_mst_topology.c +6
-0+DRM_ERROR ("cannot allocate and release VCPI on [MST PORT:%p] in the same state\n" , + port); +return -EINVAL ; + } + pos->vcpi =0 ; + }return 0 ;}
drivers/gpu/drm/drm_dp_mst_topology.c +42
-3@@ -3115,15 +3211,45 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)static struct drm_private_state *drm_dp_mst_duplicate_state (struct drm_private_obj *obj) { -struct drm_dp_mst_topology_state *state; +struct drm_dp_mst_topology_state *state, *old_state = +to_dp_mst_topology_state (obj->state); +struct drm_dp_vcpi_allocation *pos, *vcpi; - state =kmemdup (obj->state,sizeof (*state),GFP_KERNEL ); + state =kmemdup (old_state,sizeof (*state),GFP_KERNEL );if (!state)return NULL ;__drm_atomic_helper_private_obj_duplicate_state (obj, &state->base); +INIT_LIST_HEAD (&state->vcpis); + state->vcpi_allocated = false; + +list_for_each_entry (pos, &old_state->vcpis, next) { +/* Prune leftover freed VCPI allocations */ +if (!pos->vcpi) +continue ; + + vcpi =kmemdup (pos,sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) + goto fail; + +/* Take a plain old kref here and don't validate the port, as +* it's topology might not even exist anymore +*/ +kref_get (&vcpi->port->kref); +list_add (&vcpi->next, &state->vcpis); + } +return &state->base; + +fail: +list_for_each_entry_safe (pos, vcpi, &state->vcpis, next) { +drm_dp_put_port (pos->port); +kfree (pos); + } +kfree (state); + +return NULL ; } static void drm_dp_mst_destroy_state(struct drm_private_obj *obj,@@ -3131,14 +3257,116 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, {struct drm_dp_mst_topology_state *mst_state =to_dp_mst_topology_state (state); +struct drm_dp_vcpi_allocation *pos, *tmp; + +list_for_each_entry_safe (pos, tmp, &mst_state->vcpis, next) { +/* We only keep references to ports with non-zero VCPIs */ +if (pos->vcpi) +drm_dp_put_port (pos->port); +kfree (pos); + }kfree (mst_state); } -static const struct drm_private_state_funcs mst_state_funcs = { +static inline int +drm_dp_mst_atomic_check_topology_state(struct drm_dp_mst_topology_mgr *mgr,
drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, · line 3283
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10
-0+ struct drm_dp_mst_topology_state *mst_state) +{ +struct drm_dp_vcpi_allocation *vcpi; +struct drm_dp_mst_port *port; +int avail_slots =63 , ret; + +/* There's no possible scenario where releasing VCPI or keeping it the +* same would make the state invalid +*/ +if (!mst_state->vcpi_allocated) {
Do we need this? If not I'm always in favour of removing code if it's not needed :-)
Quoted 9 lines
drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+DRM_DEBUG_ATOMIC ("[MST MGR:%p] state %p allocates no VCPI, check skipped\n" , + mgr, &mst_state->base); +return 0 ; + } + +list_for_each_entry (vcpi, &mst_state->vcpis, next) { +/* Releasing VCPI is always OK-even if the port is gone */ +if (!vcpi->vcpi) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] releases all VCPI slots\n",
drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, · line 3302
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10
-0+ vcpi->port); +continue ; + } + + port =drm_dp_get_validated_port_ref (mgr, vcpi->port); +if (!port) { +DRM_DEBUG_ATOMIC ("[MST PORT:%p] is gone but still has VCPI, cannot add new VCPI\n" , + vcpi->port); +return -EINVAL ; + }
For find_vcpi we've already done this. For unplugged vcpi, we can't do this here I think - otherwise if you enable 2 mst outputs on the same branch, and unplug both, old non-atomic userspace can never ever again disable them because it can't disable both. The check (to skip already deallocated ports) above isn't enough to handle that.
Quoted 122 lines
drivers/gpu/drm/drm_dp_mst_topology.c +66
-4+ +DRM_DEBUG_ATOMIC ("[MST PORT:%p] requires %d vcpi slots\n" , + vcpi->port, vcpi->vcpi); + + avail_slots -= vcpi->vcpi; +if (avail_slots <0 ) { +DRM_DEBUG_ATOMIC ("[MST PORT:%p] not enough VCPI slots in mst state %p (avail=%d)\n" , + vcpi->port, mst_state, + avail_slots + vcpi->vcpi); + ret = -ENOSPC ; + goto port_fail; + } + +drm_dp_put_port (port); + } +DRM_DEBUG_ATOMIC ("[MST MGR:%p] mst state %p VCPI avail=%d used=%d\n" , + mgr, mst_state, avail_slots, +63 - avail_slots); + +return 0 ; +port_fail: +drm_dp_put_port (port); +return ret; +} + +/** +* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an +* atomic update is valid +* @state: Pointer to the new &struct drm_dp_mst_topology_state +* +* Checks the given topology state for an atomic update to ensure that it's +* valid. This includes checking whether there's enough bandwidth to support +* the new VCPI allocations in the atomic update. +* +* Any atomic drivers supporting DP MST must make sure to call this after +* checking the rest of their state in their +* &drm_mode_config_funcs.atomic_check() callback. +* +* See also: +* drm_dp_atomic_find_vcpi_slots() +* drm_dp_atomic_release_vcpi_slots() +* +* Returns: +* +* 0 if the new state is valid, negative error code otherwise. +*/ +int drm_dp_mst_atomic_check (struct drm_atomic_state *state) +{ +struct drm_dp_mst_topology_mgr *mgr; +struct drm_dp_mst_topology_state *mst_state; +int i, ret =0 ; + +for_each_new_mst_mgr_in_state (state, mgr, mst_state, i) { + ret =drm_dp_mst_atomic_check_topology_state (mgr, mst_state); +if (ret) +break ; + } + +return ret; +} +EXPORT_SYMBOL (drm_dp_mst_atomic_check); + +const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs = { .atomic_duplicate_state = drm_dp_mst_duplicate_state, .atomic_destroy_state = drm_dp_mst_destroy_state, }; +EXPORT_SYMBOL (drm_dp_mst_topology_state_funcs);/** * drm_atomic_get_mst_topology_state: get MST topology state @@ -3216,13 +3444,11 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,return -ENOMEM ; mst_state->mgr = mgr; - -/* max. time slots - one slot for MTP header */ - mst_state->avail_slots =63 ; +INIT_LIST_HEAD (&mst_state->vcpis);drm_atomic_private_obj_init (&mgr->base, &mst_state->base, - &mst_state_funcs); + &drm_dp_mst_topology_state_funcs);return 0 ; }drivers/gpu/drm/i915/intel_display.c +4
-0@@ -12592,6 +12592,10 @@ static int intel_atomic_check(struct drm_device *dev, "[modeset]" : "[fastset]"); } + ret =drm_dp_mst_atomic_check (state); +if (ret) +return ret; +if (any_ms) { ret =intel_modeset_checks (state);drivers/gpu/drm/i915/intel_dp_mst.c +6
-1@@ -41,8 +41,13 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,struct drm_connector *connector = conn_state->connector;void *port =to_intel_connector (connector)->port;struct drm_atomic_state *state = pipe_config->base.state; +struct drm_crtc *crtc = pipe_config->base.crtc; +struct drm_crtc_state *old_crtc_state = +drm_atomic_get_old_crtc_state (state, crtc); +struct drm_crtc_state *new_crtc_state = &pipe_config->base;int bpp; -int lane_count, slots =0 ; +int lane_count, slots = +to_intel_crtc_state (old_crtc_state)->dp_m_n.tu;const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;int mst_pbn; bool constant_n = drm_dp_has_quirk(&intel_dp->desc,@@ -77,8 +82,12 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, mst_pbn =drm_dp_calc_pbn_mode (adjusted_mode->crtc_clock, bpp);
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 90
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +6
-2pipe_config->pbn = mst_pbn; -/* Zombie connectors can't have VCPI slots */ -if (!drm_connector_is_unregistered (connector)) { +/* Only change VCPI allocation on actual mode changes, to prevent us +* from trying to allocate VCPI to ports that no longer exist when we +* may just be trying to disable DPMS on them +*/ +if (new_crtc_state->mode_changed || + new_crtc_state->connectors_changed) {
Why exactly is this needed? We only call compute_config on the enocoder when enabling an output. Not when disabling one. Similar for the atomic helpers and the various encoder callbacks (you have the same change in the nouveau code). Did this actually blow up while testing?
Quoted 21 lines
drivers/gpu/drm/i915/intel_dp_mst.c +8
-4slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr, port,@@ -107,36 +116,39 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,return true; } -static int intel_dp_mst_atomic_check (struct drm_connector *connector, -struct drm_connector_state *new_conn_state) +static int +intel_dp_mst_atomic_check (struct drm_connector *connector, +struct drm_connector_state *new_conn_state) { +struct intel_connector *intel_connector = +to_intel_connector (connector); +struct drm_dp_mst_topology_mgr *mgr = + &intel_connector->mst_port->mst_mgr; +struct drm_dp_mst_port *port = intel_connector->port;struct drm_atomic_state *state = new_conn_state->state; -struct drm_connector_state *old_conn_state; -struct drm_crtc *old_crtc;
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 120
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +5
-4-struct drm_crtc_state *crtc_state; -int slots, ret =0 ; +struct drm_connector_state *old_conn_state = +drm_atomic_get_old_connector_state (state, connector); +struct drm_crtc *old_crtc = old_conn_state->crtc, + *new_crtc = new_conn_state->crtc; +struct drm_crtc_state *old_crtc_state, *new_crtc_state; - old_conn_state =drm_atomic_get_old_connector_state (state, connector); - old_crtc = old_conn_state->crtc;
Bikeshed: Reworking all the above hides the real changes quite a bit.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 139
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +3
-6if (!old_crtc) -return ret; +return 0 ; - crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); - slots =to_intel_crtc_state (crtc_state)->dp_m_n.tu; -if (drm_atomic_crtc_needs_modeset (crtc_state) && slots >0 ) { -struct drm_dp_mst_topology_mgr *mgr; -struct drm_encoder *old_encoder; + old_crtc_state =drm_atomic_get_old_crtc_state (state, old_crtc); + if (!old_crtc_state ||
!old_crtc_state would be a framework bug. Just remove that check.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 141
Quoted 2 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ !to_intel_crtc_state(old_crtc_state)->dp_m_n.tu) +return 0 ;
Hm, why do we need this? Having a DP port previously enabled with a 0 tu sounds like a pretty big driver bug.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 146
Quoted 7 lines
drivers/gpu/drm/i915/intel_dp_mst.c +4
-2- old_encoder = old_conn_state->best_encoder; - mgr = &enc_to_mst (old_encoder)->primary->dp.mst_mgr; +/* If we switched CRTCs, clear the previous one's allocation */ + new_crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); +if (new_crtc_state->connectors_changed && !new_crtc_state->enable) +to_intel_crtc_state (new_crtc_state)->dp_m_n.tu =0 ;
No idea why we need to clear this here? Shouldn't we just clear this unconditionally if we release the allocation.
Also naming convention, I'd call this old_crtc_new_state, and maybe old_crtc_old_state. To make it clear these are the old/new states for old_crtc. Instead of old/new state for the new crtc.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 149
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-7- ret =drm_dp_atomic_release_vcpi_slots (state, mgr, slots); -if (ret) -DRM_DEBUG_KMS ("failed releasing %d vcpi slots:%d\n" , slots, ret); -else -to_intel_crtc_state (crtc_state)->dp_m_n.tu =0 ; - } -return ret; +if (new_crtc) +return 0 ;
Not sure why we shouldn't release this? If we handle this in helpers, it would remove piles of special casing code here I think. Plus remove special code in the hlpers.
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ +return drm_dp_atomic_release_vcpi_slots (state, mgr, port); } static void intel_mst_disable_dp(struct intel_encoder *encoder,include/drm/drm_dp_mst_helper.h +0
-0
include/drm/drm_dp_mst_helper.h struct drm_dp_payload { · line 409
Quoted 4 lines
include/drm/drm_dp_mst_helper.h +1
-0@@ -406,9 +406,16 @@ struct drm_dp_payload {#define to_dp_mst_topology_state (x) container_of(x, struct drm_dp_mst_topology_state, base) +struct drm_dp_vcpi_allocation {
I think a comment here that this pointer holds a full reference (even past when the connector is unplugged) would be good. Otherwise the loop in release_vcpi would be buggy.
Quoted 40 lines
include/drm/drm_dp_mst_helper.h +16
-7+struct drm_dp_mst_port *port; +int vcpi; +struct list_head next; +}; +struct drm_dp_mst_topology_state {struct drm_private_state base; -int avail_slots; +struct list_head vcpis; +u8 vcpi_allocated;struct drm_dp_mst_topology_mgr *mgr; };@@ -619,14 +626,17 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);int drm_dp_mst_topology_mgr_resume (struct drm_dp_mst_topology_mgr *mgr);struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state (struct drm_atomic_state *state,struct drm_dp_mst_topology_mgr *mgr); -int drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -struct drm_dp_mst_port *port,int pbn); -int drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -int slots); +int __must_check +drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port,int pbn); +int __must_check +drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port);int drm_dp_send_power_updown_phy (struct drm_dp_mst_topology_mgr *mgr,struct drm_dp_mst_port *port,bool power_up); +int __must_check drm_dp_mst_atomic_check (struct drm_atomic_state *state);extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs;Signature · 1 line
2.19.1
Signature · 7 lines
Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ Nouveau mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/nouveau
Re: [PATCH v6 3/6] drm/dp_mst: Start tracking per-port VCPI allocations
Quoted 60 lines · Daniel Vetter
Quoted 35 lines · Lyude Paul
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:/* We cannot rely on
port->vcpi.num_slotsto update
*topology_state->avail_slotsas the port may not exist if the parent
* branch device was unplugged. This should be fixed by tracking
* per-port slot allocation indrm_dp_mst_topology_stateinstead of
* depending on the caller to tell us how many slots to release. */That's not the only reason we should fix this: forcing the driver to track the VCPI allocations throughout a state's atomic check is error prone, because it means that extra care has to be taken with the order that
drm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()are called in in order to ensure idempotency. Currently the only driver actually using these helpers, i915, doesn't even do this correctly: multiple ->best_encoder() checks with i915's current implementation would not be idempotent and would over-allocate VCPI slots, something I learned trying to implement fallback retraining in MST.So: simplify this whole mess, and teach
drm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()to track the VCPI allocations for each port. This allows us to ensure idempotency without having to rely on the driver as much. Additionally: the driver doesn't need to do any kind of VCPI slot tracking anymore if it doesn't need it for it's own internal state.Additionally; this adds a new
drm_dp_mst_atomic_check()helper which must be used by atomic drivers to perform validity checks for the new VCPI allocations incurred by a state.Also: update the documentation and make it more obvious that these /must/ be called by /all/ atomic drivers supporting MST.
Quoted 9 lines
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This required a good bit of changing to when we calldrm_dp_find_vcpi_slots(), mainly that we need to ensure that we only redo VCPI allocations on actual mode or CRTC changes, notcrtc_state->activechanges. Additionally, we no longer take the registration of the DRM connector for each port into account because so long as we have a kref to the port in the new or previous atomic state, the connector will stay registered.I write an entire pile of small nitpits (still included most of them below), until I realized this here wont work. Delaying the call to destroy the connector (well, unregister it really) wreaks the design we've come up with thus far, resulting in most of my comments here.
Instead, all we need to do is delay the kfree(port) at the bottom of
drm_dp_destroy_port(). The vcpi allocation structure _only_ needs the pointer value to stay valid, as a lookup key. It doesn't care at all about anything actually stored in there. So the only thing we need to delay is the kfree. I think the simplest way to achieve this is to add a 2nd kref (port->kfree_refor something like that), with on reference held by the port itself (released indrm_dp_destroy_port), and the other one held as-needed by the vcpi allocation lists.
Quoted 3 lines
I think if we go with this design instead of retrofitting a semantic change of the port lifetime itself, all the complications I complain about below should disappear.
In the above I meant drm_dp_destroy_port or drm_dp_destroy_connector_work.
Aside: I think creating a kref for the final kfree would also solve a bunch of other issues in a much neater way: In
commit f038c5b99fc1332f558b495d136d4f433ee65caa
Author: Lyude Paul <[email protected]> Date: Tue Nov 13 17:46:14 2018 -0500
drm/dp_mst: Skip validating ports during destruction, just ref
we could use that kfree reference to make sure the port pointer is alive. This of course means that drm_dp_update_payload_part1() would also need to use the kfree reference for the vcpi allocations. And probably everywhere else (e.g. in your nouveau series for payload information).
That would give us a very clear separation overall between "port can still be used because it's not yet unplugged" vs. "port only hangs around because a bunch of vcpi allocations and other payload things upstream of the port might still need the port lookup key to free resources". Instead of again sprinkling complicated conditions and magic tricks all over the code to figure out whethe we should allow the get_validate_port to succeed or not, or the modeset to succeed or not.
-Daniel
Quoted 679 lines
Piles of comments below.
Cheers, Daniel
Quoted 132 lines
- Use the small changes to
drm_dp_put_port()to add even more error checking to make misusage of the helpers more obvious. I added this after having to chase down various use-after-free conditions that started popping up from the new helpers so no one else has to troubleshoot that.
- Move some accidentalDRM_DEBUG_KMS()calls toDRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both not be called on the same port in a single atomic check phase (but multiple calls to one or the other is OK)Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI allocations happen in a state. This makes the next change I'm about to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure that when ports are destroyed and still have VCPI allocations in the topology state, the only state changes allowed are releasing said ports' VCPI. This prevents a state with a mix of VCPI allocations from destroyed ports, and allocations from valid ports.Changes since v3:
- Don't release VCPI allocations in the topology state immediately indrm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and skip over them indrm_dp_mst_duplicate_state(). This makes it sodrm_dp_atomic_release_vcpi_slots()is still idempotent while also throwing warnings if the driver messes up it's book keeping and tries to release VCPI slots on a port that doesn't have any pre-existing VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"Changes since v2:
- Usekmemdup()for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states indrm_dp_mst_atomic_check()so the driver doesn't have to do it
- Fix documentation indrm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into it's own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth requirements. This is needed because originally we relied on the state duplication functions to prune any stale ports from the new state, which would prevent us from incorrectly considering their bandwidth requirements alongside legitimate new payloads.
- Add function references indrm_dp_atomic_release_vcpi_slots()- danvet
- Annotate atomic VCPI and atomic check functions with__must_check
- danvetChanges since v1:
- Don't use the now-removed ->atomic_check() for private objects hook, just give drivers a function to call themselvesSigned-off-by Lyude Paul <[email protected]>Cc Daniel Vetter <[email protected]>--- drivers/gpu/drm/drm_dp_mst_topology.c | 294 +++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 4 + drivers/gpu/drm/i915/intel_dp_mst.c | 64 +++--- include/drm/drm_dp_mst_helper.h | 24 ++- 4 files changed, 319 insertions(+), 67 deletions(-)drivers/gpu/drm/drm_dp_mst_topology.c +35
-10@@ -2617,21 +2617,42 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, }/** -* drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state +* drm_dp_atomic_find_vcpi_slots() - Find and add VCPI slots to the state * @state: global atomic state * @mgr: MST topology manager for the port * @port: port to find vcpi slots for * @pbn: bandwidth required for the mode in PBN * -* RETURNS: -* Total slots in the atomic state assigned for this port or error +* Allocates VCPI slots to @port, replacing any previous VCPI allocations it +* may have had. Any atomic drivers which support MST must call this function +* in their &drm_encoder_helper_funcs.atomic_check() callback to change the +* current VCPI allocation for the new state, but only when +* &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed is set +* to ensure compatibility with userspace applications that still use the +* legacy modesetting UAPI. +* +* Allocations set by this function are not checked against the bandwidth +* restraints of @mgr until the driver calls drm_dp_mst_atomic_check(). +* +* Additionally, it is OK to call this function multiple times on the same +* @port as needed. It is not OK however, to call this function and +* drm_dp_atomic_release_vcpi_slots() in the same atomic check phase. +* +* See also: +* drm_dp_atomic_release_vcpi_slots() +* drm_dp_mst_atomic_check() +* +* Returns: +* Total slots in the atomic state assigned for this port, or a negative error +* code if the port no longer exists */ int drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state,struct drm_dp_mst_topology_mgr *mgr,struct drm_dp_mst_port *port,int pbn) {struct drm_dp_mst_topology_state *topology_state; -int req_slots; +struct drm_dp_vcpi_allocation *pos, *vcpi =NULL ; +int prev_slots, req_slots, ret; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))@@ -2640,20 +2661,60 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, port =drm_dp_get_validated_port_ref (mgr, port);if (port ==NULL )return -EINVAL ; - req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); -DRM_DEBUG_KMS ("vcpi slots req=%d, avail=%d\n" , - req_slots, topology_state->avail_slots); -if (req_slots > topology_state->avail_slots) { -drm_dp_put_port (port); -return -ENOSPC ; + topology_state->vcpi_allocated = true; + +/* Find the current allocation for this port, if any */ +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + vcpi = pos; + prev_slots = vcpi->vcpi; + +/* +* This should never happen, unless the driver tries drivers/gpu/drm/drm_dp_mst_topology.c int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, · line 2684
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +8
-0+* releasing and allocating the same VCPI allocation, +* which is an error +*/ +if (WARN_ON (!prev_slots)) { +DRM_ERROR ("cannot allocate and release VCPI on [MST PORT:%p] in the same state\n" , + port); +return -EINVAL ; + }Allowing atomic changes to output state (like disabling it on one crtc, and then enabling on the other, thus both releasing and re-allocating the vcpi in one go) is kinda the point of atomic. So definitely can't EINVAL here.
Sounds like we need more testcases in igt?
Quoted 104 lines
drivers/gpu/drm/drm_dp_mst_topology.c +31
-3+ +break ; + } } +if (!vcpi) + prev_slots =0 ; - topology_state->avail_slots -= req_slots; -DRM_DEBUG_KMS ("vcpi slots avail=%d" , topology_state->avail_slots); + req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); + +DRM_DEBUG_ATOMIC ("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n" , + port->connector->base.id, port->connector->name, + port, prev_slots, req_slots); + +/* Add the new allocation to the state */ +if (!vcpi) { + vcpi =kzalloc (sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) { + ret = -ENOMEM ; + goto out; + } + vcpi->port =drm_dp_get_validated_port_ref (mgr, port); +if (!vcpi->port) { + ret = -EINVAL ; +kfree (vcpi); + goto out; + } +list_add (&vcpi->next, &topology_state->vcpis); + } + vcpi->vcpi = req_slots; + + ret = req_slots; +out:drm_dp_put_port (port); -return req_slots; +return ret; }
EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);drivers/gpu/drm/drm_dp_mst_topology.c +33
-14@@ -2661,31 +2722,66 @@ EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);* drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots * @state: global atomic state * @mgr: MST topology manager for the port -* @slots: number of vcpi slots to release +* @port: The port to release the VCPI slots from * -* RETURNS: -* 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or -* negative error code +* Releases any VCPI slots that have been allocated to a port in the atomic +* state. Any atomic drivers which support MST must call this function in +* their &drm_connector_helper_funcs.atomic_check() callback when the +* connector will no longer have VCPI allocated (e.g. because it's CRTC was +* removed) when it had VCPI allocated in the previous atomic state. +* +* It is OK to call this even if @port has been removed from the system. +* Additionally, it is OK to call this function multiple times on the same +* @port as needed. It is not OK however, to call this function and +* drm_dp_atomic_find_vcpi_slots() on the same @port in a single atomic check +* phase. +* +* See also: +* drm_dp_atomic_find_vcpi_slots() +* drm_dp_mst_atomic_check() +* +* Returns: +* 0 if all slots for this port were added back to +* &drm_dp_mst_topology_state.avail_slots or negative error code */ int drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state,struct drm_dp_mst_topology_mgr *mgr, -int slots) +struct drm_dp_mst_port *port) {struct drm_dp_mst_topology_state *topology_state; +struct drm_dp_vcpi_allocation *pos; +bool found = false; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))return PTR_ERR (topology_state); -/* We cannot rely on port->vcpi.num_slots to update -* topology_state->avail_slots as the port may not exist if the parent -* branch device was unplugged. This should be fixed by tracking -* per-port slot allocation in drm_dp_mst_topology_state instead of -* depending on the caller to tell us how many slots to release. -*/ - topology_state->avail_slots += slots; -DRM_DEBUG_KMS ("vcpi slots released=%d, avail=%d\n" , - slots, topology_state->avail_slots); +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + found = true; +break ; + } + } +if (WARN_ON (!found)) { +DRM_ERROR ("no VCPI for [MST PORT:%p] found in mst state %p\n" , + port, &topology_state->base); +return -EINVAL ;drivers/gpu/drm/drm_dp_mst_topology.c EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots); · line 2779
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10
-0+ } + +DRM_DEBUG_ATOMIC ("[MST PORT:%p] VCPI %d -> 0\n" , port, pos->vcpi); +if (pos->vcpi) { +/* +* This should be safe-as the previous state will still hold a +* reference to this port until we swap states. If it's not, +* the driver has a bookkeeping error +*/ +if (WARN_ON (drm_dp_put_port (port))) {The usual way to do this (instead of adding a return value to the normal
_putfunction) is something like
kref_put(port, function_that_with_just_a_WARNING_DRM_ERROR)avoids the need for patch 2.
Quoted 76 lines
drivers/gpu/drm/drm_dp_mst_topology.c +6
-0+DRM_ERROR ("cannot allocate and release VCPI on [MST PORT:%p] in the same state\n" , + port); +return -EINVAL ; + } + pos->vcpi =0 ; + }return 0 ;}
drivers/gpu/drm/drm_dp_mst_topology.c +42
-3@@ -3115,15 +3211,45 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)static struct drm_private_state *drm_dp_mst_duplicate_state (struct drm_private_obj *obj) { -struct drm_dp_mst_topology_state *state; +struct drm_dp_mst_topology_state *state, *old_state = +to_dp_mst_topology_state (obj->state); +struct drm_dp_vcpi_allocation *pos, *vcpi; - state =kmemdup (obj->state,sizeof (*state),GFP_KERNEL ); + state =kmemdup (old_state,sizeof (*state),GFP_KERNEL );if (!state)return NULL ;__drm_atomic_helper_private_obj_duplicate_state (obj, &state->base); +INIT_LIST_HEAD (&state->vcpis); + state->vcpi_allocated = false; + +list_for_each_entry (pos, &old_state->vcpis, next) { +/* Prune leftover freed VCPI allocations */ +if (!pos->vcpi) +continue ; + + vcpi =kmemdup (pos,sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) + goto fail; + +/* Take a plain old kref here and don't validate the port, as +* it's topology might not even exist anymore +*/ +kref_get (&vcpi->port->kref); +list_add (&vcpi->next, &state->vcpis); + } +return &state->base; + +fail: +list_for_each_entry_safe (pos, vcpi, &state->vcpis, next) { +drm_dp_put_port (pos->port); +kfree (pos); + } +kfree (state); + +return NULL ; } static void drm_dp_mst_destroy_state(struct drm_private_obj *obj,@@ -3131,14 +3257,116 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, {struct drm_dp_mst_topology_state *mst_state =to_dp_mst_topology_state (state); +struct drm_dp_vcpi_allocation *pos, *tmp; + +list_for_each_entry_safe (pos, tmp, &mst_state->vcpis, next) { +/* We only keep references to ports with non-zero VCPIs */ +if (pos->vcpi) +drm_dp_put_port (pos->port); +kfree (pos); + }kfree (mst_state); } -static const struct drm_private_state_funcs mst_state_funcs = { +static inline int +drm_dp_mst_atomic_check_topology_state(struct drm_dp_mst_topology_mgr *mgr,drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, · line 3283
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10
-0+ struct drm_dp_mst_topology_state *mst_state) +{ +struct drm_dp_vcpi_allocation *vcpi; +struct drm_dp_mst_port *port; +int avail_slots =63 , ret; + +/* There's no possible scenario where releasing VCPI or keeping it the +* same would make the state invalid +*/ +if (!mst_state->vcpi_allocated) {Do we need this? If not I'm always in favour of removing code if it's not needed :-)
Quoted 9 lines
drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+DRM_DEBUG_ATOMIC ("[MST MGR:%p] state %p allocates no VCPI, check skipped\n" , + mgr, &mst_state->base); +return 0 ; + } + +list_for_each_entry (vcpi, &mst_state->vcpis, next) { +/* Releasing VCPI is always OK-even if the port is gone */ +if (!vcpi->vcpi) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] releases all VCPI slots\n",drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, · line 3302
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10
-0+ vcpi->port); +continue ; + } + + port =drm_dp_get_validated_port_ref (mgr, vcpi->port); +if (!port) { +DRM_DEBUG_ATOMIC ("[MST PORT:%p] is gone but still has VCPI, cannot add new VCPI\n" , + vcpi->port); +return -EINVAL ; + }For
find_vcpiwe've already done this. For unplugged vcpi, we can't do this here I think - otherwise if you enable 2 mst outputs on the same branch, and unplug both, old non-atomic userspace can never ever again disable them because it can't disable both. The check (to skip already deallocated ports) above isn't enough to handle that.Quoted 122 lines
drivers/gpu/drm/drm_dp_mst_topology.c +66
-4+ +DRM_DEBUG_ATOMIC ("[MST PORT:%p] requires %d vcpi slots\n" , + vcpi->port, vcpi->vcpi); + + avail_slots -= vcpi->vcpi; +if (avail_slots <0 ) { +DRM_DEBUG_ATOMIC ("[MST PORT:%p] not enough VCPI slots in mst state %p (avail=%d)\n" , + vcpi->port, mst_state, + avail_slots + vcpi->vcpi); + ret = -ENOSPC ; + goto port_fail; + } + +drm_dp_put_port (port); + } +DRM_DEBUG_ATOMIC ("[MST MGR:%p] mst state %p VCPI avail=%d used=%d\n" , + mgr, mst_state, avail_slots, +63 - avail_slots); + +return 0 ; +port_fail: +drm_dp_put_port (port); +return ret; +} + +/** +* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an +* atomic update is valid +* @state: Pointer to the new &struct drm_dp_mst_topology_state +* +* Checks the given topology state for an atomic update to ensure that it's +* valid. This includes checking whether there's enough bandwidth to support +* the new VCPI allocations in the atomic update. +* +* Any atomic drivers supporting DP MST must make sure to call this after +* checking the rest of their state in their +* &drm_mode_config_funcs.atomic_check() callback. +* +* See also: +* drm_dp_atomic_find_vcpi_slots() +* drm_dp_atomic_release_vcpi_slots() +* +* Returns: +* +* 0 if the new state is valid, negative error code otherwise. +*/ +int drm_dp_mst_atomic_check (struct drm_atomic_state *state) +{ +struct drm_dp_mst_topology_mgr *mgr; +struct drm_dp_mst_topology_state *mst_state; +int i, ret =0 ; + +for_each_new_mst_mgr_in_state (state, mgr, mst_state, i) { + ret =drm_dp_mst_atomic_check_topology_state (mgr, mst_state); +if (ret) +break ; + } + +return ret; +} +EXPORT_SYMBOL (drm_dp_mst_atomic_check); + +const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs = { .atomic_duplicate_state = drm_dp_mst_duplicate_state, .atomic_destroy_state = drm_dp_mst_destroy_state, }; +EXPORT_SYMBOL (drm_dp_mst_topology_state_funcs);/** * drm_atomic_get_mst_topology_state: get MST topology state @@ -3216,13 +3444,11 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,return -ENOMEM ; mst_state->mgr = mgr; - -/* max. time slots - one slot for MTP header */ - mst_state->avail_slots =63 ; +INIT_LIST_HEAD (&mst_state->vcpis);drm_atomic_private_obj_init (&mgr->base, &mst_state->base, - &mst_state_funcs); + &drm_dp_mst_topology_state_funcs);return 0 ; }drivers/gpu/drm/i915/intel_display.c +4
-0@@ -12592,6 +12592,10 @@ static int intel_atomic_check(struct drm_device *dev, "[modeset]" : "[fastset]"); } + ret =drm_dp_mst_atomic_check (state); +if (ret) +return ret; +if (any_ms) { ret =intel_modeset_checks (state);drivers/gpu/drm/i915/intel_dp_mst.c +6
-1@@ -41,8 +41,13 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,struct drm_connector *connector = conn_state->connector;void *port =to_intel_connector (connector)->port;struct drm_atomic_state *state = pipe_config->base.state; +struct drm_crtc *crtc = pipe_config->base.crtc; +struct drm_crtc_state *old_crtc_state = +drm_atomic_get_old_crtc_state (state, crtc); +struct drm_crtc_state *new_crtc_state = &pipe_config->base;int bpp; -int lane_count, slots =0 ; +int lane_count, slots = +to_intel_crtc_state (old_crtc_state)->dp_m_n.tu;const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;int mst_pbn; bool constant_n = drm_dp_has_quirk(&intel_dp->desc,@@ -77,8 +82,12 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, mst_pbn =drm_dp_calc_pbn_mode (adjusted_mode->crtc_clock, bpp);drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 90
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +6
-2pipe_config->pbn = mst_pbn; -/* Zombie connectors can't have VCPI slots */ -if (!drm_connector_is_unregistered (connector)) { +/* Only change VCPI allocation on actual mode changes, to prevent us +* from trying to allocate VCPI to ports that no longer exist when we +* may just be trying to disable DPMS on them +*/ +if (new_crtc_state->mode_changed || + new_crtc_state->connectors_changed) {Why exactly is this needed? We only call
compute_configon the enocoder when enabling an output. Not when disabling one. Similar for the atomic helpers and the various encoder callbacks (you have the same change in the nouveau code). Did this actually blow up while testing?Quoted 21 lines
drivers/gpu/drm/i915/intel_dp_mst.c +8
-4slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr, port,@@ -107,36 +116,39 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,return true; } -static int intel_dp_mst_atomic_check (struct drm_connector *connector, -struct drm_connector_state *new_conn_state) +static int +intel_dp_mst_atomic_check (struct drm_connector *connector, +struct drm_connector_state *new_conn_state) { +struct intel_connector *intel_connector = +to_intel_connector (connector); +struct drm_dp_mst_topology_mgr *mgr = + &intel_connector->mst_port->mst_mgr; +struct drm_dp_mst_port *port = intel_connector->port;struct drm_atomic_state *state = new_conn_state->state; -struct drm_connector_state *old_conn_state; -struct drm_crtc *old_crtc;drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 120
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +5
-4-struct drm_crtc_state *crtc_state; -int slots, ret =0 ; +struct drm_connector_state *old_conn_state = +drm_atomic_get_old_connector_state (state, connector); +struct drm_crtc *old_crtc = old_conn_state->crtc, + *new_crtc = new_conn_state->crtc; +struct drm_crtc_state *old_crtc_state, *new_crtc_state; - old_conn_state =drm_atomic_get_old_connector_state (state, connector); - old_crtc = old_conn_state->crtc;Bikeshed: Reworking all the above hides the real changes quite a bit.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 139
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +3
-6if (!old_crtc) -return ret; +return 0 ; - crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); - slots =to_intel_crtc_state (crtc_state)->dp_m_n.tu; -if (drm_atomic_crtc_needs_modeset (crtc_state) && slots >0 ) { -struct drm_dp_mst_topology_mgr *mgr; -struct drm_encoder *old_encoder; + old_crtc_state =drm_atomic_get_old_crtc_state (state, old_crtc); + if (!old_crtc_state ||!old_crtc_state would be a framework bug. Just remove that check.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 141
Quoted 2 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ !to_intel_crtc_state(old_crtc_state)->dp_m_n.tu) +return 0 ;Hm, why do we need this? Having a DP port previously enabled with a 0 tu sounds like a pretty big driver bug.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 146
Quoted 7 lines
drivers/gpu/drm/i915/intel_dp_mst.c +4
-2- old_encoder = old_conn_state->best_encoder; - mgr = &enc_to_mst (old_encoder)->primary->dp.mst_mgr; +/* If we switched CRTCs, clear the previous one's allocation */ + new_crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); +if (new_crtc_state->connectors_changed && !new_crtc_state->enable) +to_intel_crtc_state (new_crtc_state)->dp_m_n.tu =0 ;No idea why we need to clear this here? Shouldn't we just clear this unconditionally if we release the allocation.
Also naming convention, I'd call this
old_crtc_new_state, and maybeold_crtc_old_state. To make it clear these are the old/new states forold_crtc. Instead of old/new state for the new crtc.drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 149
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-7- ret =drm_dp_atomic_release_vcpi_slots (state, mgr, slots); -if (ret) -DRM_DEBUG_KMS ("failed releasing %d vcpi slots:%d\n" , slots, ret); -else -to_intel_crtc_state (crtc_state)->dp_m_n.tu =0 ; - } -return ret; +if (new_crtc) +return 0 ;Not sure why we shouldn't release this? If we handle this in helpers, it would remove piles of special casing code here I think. Plus remove special code in the hlpers.
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ +return drm_dp_atomic_release_vcpi_slots (state, mgr, port); } static void intel_mst_disable_dp(struct intel_encoder *encoder,include/drm/drm_dp_mst_helper.h +0
-0include/drm/drm_dp_mst_helper.h struct drm_dp_payload { · line 409
Quoted 4 lines
include/drm/drm_dp_mst_helper.h +1
-0@@ -406,9 +406,16 @@ struct drm_dp_payload {#define to_dp_mst_topology_state (x) container_of(x, struct drm_dp_mst_topology_state, base) +struct drm_dp_vcpi_allocation {I think a comment here that this pointer holds a full reference (even past when the connector is unplugged) would be good. Otherwise the loop in
release_vcpiwould be buggy.Quoted 40 lines
include/drm/drm_dp_mst_helper.h +16
-7+struct drm_dp_mst_port *port; +int vcpi; +struct list_head next; +}; +struct drm_dp_mst_topology_state {struct drm_private_state base; -int avail_slots; +struct list_head vcpis; +u8 vcpi_allocated;struct drm_dp_mst_topology_mgr *mgr; };@@ -619,14 +626,17 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);int drm_dp_mst_topology_mgr_resume (struct drm_dp_mst_topology_mgr *mgr);struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state (struct drm_atomic_state *state,struct drm_dp_mst_topology_mgr *mgr); -int drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -struct drm_dp_mst_port *port,int pbn); -int drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -int slots); +int __must_check +drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port,int pbn); +int __must_check +drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port);int drm_dp_send_power_updown_phy (struct drm_dp_mst_topology_mgr *mgr,struct drm_dp_mst_port *port,bool power_up); +int __must_check drm_dp_mst_atomic_check (struct drm_atomic_state *state);extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs;Signature · 1 line
2.19.1Signature · 3 lines
Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
Signature · 7 lines
Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
A message that is not in the archive (sent off-list or not yet received): 20181126212250.GS4266-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org
Re: [PATCH v6 3/6] drm/dp_mst: Start tracking per-port VCPI allocations
Quoted 81 lines · Daniel Vetter
Quoted 60 lines · Daniel Vetter
Quoted 35 lines · Lyude Paul
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:/* We cannot rely on
port->vcpi.num_slotsto update
*topology_state->avail_slotsas the port may not exist if the parent
* branch device was unplugged. This should be fixed by tracking
* per-port slot allocation indrm_dp_mst_topology_stateinstead of
* depending on the caller to tell us how many slots to release. */That's not the only reason we should fix this: forcing the driver to track the VCPI allocations throughout a state's atomic check is error prone, because it means that extra care has to be taken with the order that
drm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()are called in in order to ensure idempotency. Currently the only driver actually using these helpers, i915, doesn't even do this correctly: multiple ->best_encoder() checks with i915's current implementation would not be idempotent and would over-allocate VCPI slots, something I learned trying to implement fallback retraining in MST.So: simplify this whole mess, and teach
drm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()to track the VCPI allocations for each port. This allows us to ensure idempotency without having to rely on the driver as much. Additionally: the driver doesn't need to do any kind of VCPI slot tracking anymore if it doesn't need it for it's own internal state.Additionally; this adds a new
drm_dp_mst_atomic_check()helper which must be used by atomic drivers to perform validity checks for the new VCPI allocations incurred by a state.Also: update the documentation and make it more obvious that these /must/ be called by /all/ atomic drivers supporting MST.
Quoted 9 lines
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This required a good bit of changing to when we calldrm_dp_find_vcpi_slots(), mainly that we need to ensure that we only redo VCPI allocations on actual mode or CRTC changes, notcrtc_state->activechanges. Additionally, we no longer take the registration of the DRM connector for each port into account because so long as we have a kref to the port in the new or previous atomic state, the connector will stay registered.I write an entire pile of small nitpits (still included most of them below), until I realized this here wont work. Delaying the call to destroy the connector (well, unregister it really) wreaks the design we've come up with thus far, resulting in most of my comments here.
Instead, all we need to do is delay the kfree(port) at the bottom of
drm_dp_destroy_port(). The vcpi allocation structure _only_ needs the pointer value to stay valid, as a lookup key. It doesn't care at all about anything actually stored in there. So the only thing we need to delay is the kfree. I think the simplest way to achieve this is to add a 2nd kref (port->kfree_refor something like that), with on reference held by the port itself (released indrm_dp_destroy_port), and the other one held as-needed by the vcpi allocation lists.Quoted 3 lines
I think if we go with this design instead of retrofitting a semantic change of the port lifetime itself, all the complications I complain about below should disappear.
In the above I meant
drm_dp_destroy_portordrm_dp_destroy_connector_work.Aside: I think creating a kref for the final kfree would also solve a bunch of other issues in a much neater way: In
commit
f038c5b99fc1332f558b495d136d4f433ee65caaAuthor: Lyude Paul <[email protected]> Date: Tue Nov 13 17:46:14 2018 -0500drm/dp_mst: Skip validating ports during destruction, just ref
we could use that kfree reference to make sure the port pointer is alive. This of course means that
drm_dp_update_payload_part1()would also need to use the kfree reference for the vcpi allocations. And probably everywhere else (e.g. in your nouveau series for payload information).
Quoted 7 lines
That would give us a very clear separation overall between "port can still be used because it's not yet unplugged" vs. "port only hangs around because a bunch of vcpi allocations and other payload things upstream of the port might still need the port lookup key to free resources". Instead of again sprinkling complicated conditions and magic tricks all over the code to figure out whethe we should allow the
get_validate_portto succeed or not, or the modeset to succeed or not.
mhm, I've been experimenting with this and I think I agree this is definitely the right solution to go with. It'll end up decomplicating a lot of this :)
Quoted 731 lines
-Daniel
Quoted 729 lines
Piles of comments below.
Cheers, Daniel
Quoted 146 lines
- Use the small changes to
drm_dp_put_port()to add even more error checking to make misusage of the helpers more obvious. I added this after having to chase down various use-after-free conditions that started popping up from the new helpers so no one else has to troubleshoot that.
- Move some accidentalDRM_DEBUG_KMS()calls toDRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both not
be
called on the same port in a single atomic check phase (but multiple calls to one or the other is OK)Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI allocations happen in a state. This makes the next change I'm about to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure that when ports are destroyed and still have VCPI allocations in the topology state, the only state changes allowed are releasing said ports' VCPI. This prevents a state with a mix of VCPI allocations from destroyed ports, and allocations from valid ports.Changes since v3:
- Don't release VCPI allocations in the topology state immediately indrm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and skip over them indrm_dp_mst_duplicate_state(). This makes it sodrm_dp_atomic_release_vcpi_slots()is still idempotent while also throwing warnings if the driver messes up it's book keeping and tries to release VCPI slots on a port that doesn't have any pre-existing VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"Changes since v2:
- Usekmemdup()for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states indrm_dp_mst_atomic_check()so the driver doesn't have to do it
- Fix documentation indrm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into it's own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth requirements. This is needed because originally we relied on the state duplication functions to prune any stale ports from the new state, which would prevent us from incorrectly considering their bandwidth requirements alongside legitimate new payloads.
- Add function references indrm_dp_atomic_release_vcpi_slots()-
danvet
- Annotate atomic VCPI and atomic check functions with__must_check
- danvetChanges since v1:
- Don't use the now-removed ->atomic_check() for private objects hook, just give drivers a function to call themselvesSigned-off-by Lyude Paul <[email protected]>Cc Daniel Vetter <[email protected]>--- drivers/gpu/drm/drm_dp_mst_topology.c | 294 +++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 4 + drivers/gpu/drm/i915/intel_dp_mst.c | 64 +++--- include/drm/drm_dp_mst_helper.h | 24 ++- 4 files changed, 319 insertions(+), 67 deletions(-)drivers/gpu/drm/drm_dp_mst_topology.c +0
-1@@ -2617,21 +2617,42 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, }/** -* drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state
+ *drm_dp_atomic_find_vcpi_slots()- Find and add VCPI slots to the state
* @state: global atomic state
* @mgr: MST topology manager for the port
* @port: port to find vcpi slots for
* @pbn: bandwidth required for the mode in PBN
*
- * RETURNS:
- * Total slots in the atomic state assigned for this port or error
+ * Allocates VCPI slots to @port, replacing any previous VCPI allocations it
+ * may have had. Any atomic drivers which support MST must call this function
+ * in their&drm_encoder_helper_funcs.atomic_check()callback to change the
+ * current VCPI allocation for the new state, but only when
+ * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed is set
+ * to ensure compatibility with userspace applications that still use the
+ * legacy modesetting UAPI.
+ *
+ * Allocations set by this function are not checked against the bandwidth
+ * restraints of @mgr until the driver callsdrm_dp_mst_atomic_check().
+ *
+ * Additionally, it is OK to call this function multiple times on the same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_release_vcpi_slots()in the same atomic check phase.
+ *
+ * See also:
+ *drm_dp_atomic_release_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * Total slots in the atomic state assigned for this port, or a negative error
+ * code if the port no longer exists
*/
int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state*state,drivers/gpu/drm/drm_dp_mst_topology.c +11
-7struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn) { struct drm_dp_mst_topology_state *topology_state; -int req_slots; +struct drm_dp_vcpi_allocation *pos, *vcpi =NULL ; +int prev_slots, req_slots, ret; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))@@ -2640,20 +2661,60 @@ int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state *state, port =drm_dp_get_validated_port_ref (mgr, port);if (port ==NULL )return -EINVAL ; - req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); -DRM_DEBUG_KMS ("vcpi slots req=%d, avail=%d\n" , - req_slots, topology_state->avail_slots); -if (req_slots > topology_state->avail_slots) { -drm_dp_put_port (port); -return -ENOSPC ; + topology_state->vcpi_allocated = true; + +/* Find the current allocation for this port, if any */ +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + vcpi = pos; + prev_slots = vcpi->vcpi; + +/* drivers/gpu/drm/drm_dp_mst_topology.c int drm_dp_atomic_find_vcpi_slots(struct · line 2684
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+* This should never happen, unless the driver tries +* releasing and allocating the same VCPI allocation, +* which is an error +*/ +if (WARN_ON (!prev_slots)) { + DRM_ERROR("cannot allocate and release VCPI on [MST PORT:%p] in the same state\n", + port); +return -EINVAL ; + }Allowing atomic changes to output state (like disabling it on one crtc, and then enabling on the other, thus both releasing and re-allocating the vcpi in one go) is kinda the point of atomic. So definitely can't EINVAL here.
Sounds like we need more testcases in igt?
Quoted 110 lines
drivers/gpu/drm/drm_dp_mst_topology.c +31
-3+ +break ; + } } +if (!vcpi) + prev_slots =0 ; - topology_state->avail_slots -= req_slots; -DRM_DEBUG_KMS ("vcpi slots avail=%d" , topology_state->avail_slots); + req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); + +DRM_DEBUG_ATOMIC ("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n" , + port->connector->base.id, port->connector->name, + port, prev_slots, req_slots); + +/* Add the new allocation to the state */ +if (!vcpi) { + vcpi =kzalloc (sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) { + ret = -ENOMEM ; + goto out; + } + vcpi->port =drm_dp_get_validated_port_ref (mgr, port); +if (!vcpi->port) { + ret = -EINVAL ; +kfree (vcpi); + goto out; + } +list_add (&vcpi->next, &topology_state->vcpis); + } + vcpi->vcpi = req_slots; + + ret = req_slots; +out:drm_dp_put_port (port); -return req_slots; +return ret; }
EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);drivers/gpu/drm/drm_dp_mst_topology.c +1
-3@@ -2661,31 +2722,66 @@ EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);* drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots * @state: global atomic state * @mgr: MST topology manager for the port -* @slots: number of vcpi slots to release +* @port: The port to release the VCPI slots from * -* RETURNS: -* 0 if @slots were added back to &drm_dp_mst_topology_state- Quoted 1 line
avail_slotsor- * negative error code
+ * Releases any VCPI slots that have been allocated to a port in the atomic
+ * state. Any atomic drivers which support MST must call this function in
+ * their&drm_connector_helper_funcs.atomic_check()callback when the
+ * connector will no longer have VCPI allocated (e.g. because it's CRTC was
+ * removed) when it had VCPI allocated in the previous atomic state.
+ *
+ * It is OK to call this even if @port has been removed from the system.
+ * Additionally, it is OK to call this function multiple times on the same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_find_vcpi_slots()on the same @port in a single atomic check
+ * phase.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * 0 if all slots for this port were added back to
+ * &drm_dp_mst_topology_state.avail_slots or negative error code */
int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state*state,drivers/gpu/drm/drm_dp_mst_topology.c +13
-10struct drm_dp_mst_topology_mgr *mgr, - int slots) + struct drm_dp_mst_port *port) { struct drm_dp_mst_topology_state *topology_state; +struct drm_dp_vcpi_allocation *pos; +bool found = false; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))return PTR_ERR (topology_state); -/* We cannot rely on port->vcpi.num_slots to update -* topology_state->avail_slots as the port may not exist if the parent -* branch device was unplugged. This should be fixed by tracking -* per-port slot allocation in drm_dp_mst_topology_state instead of -* depending on the caller to tell us how many slots to release. -*/ - topology_state->avail_slots += slots; -DRM_DEBUG_KMS ("vcpi slots released=%d, avail=%d\n" , - slots, topology_state->avail_slots); +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + found = true; +break ; + } + } +if (WARN_ON (!found)) { +DRM_ERROR ("no VCPI for [MST PORT:%p] found in mst state %p\n" , + port, &topology_state->base); +return -EINVAL ;drivers/gpu/drm/drm_dp_mst_topology.c EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots); · line 2778
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10
-0+ } + +DRM_DEBUG_ATOMIC ("[MST PORT:%p] VCPI %d -> 0\n" , port, pos->vcpi); +if (pos->vcpi) { +/* +* This should be safe-as the previous state will still hold a +* reference to this port until we swap states. If it's not, +* the driver has a bookkeeping error +*/ +if (WARN_ON (drm_dp_put_port (port))) {The usual way to do this (instead of adding a return value to the normal
_putfunction) is something like
kref_put(port, function_that_with_just_a_WARNING_DRM_ERROR)avoids the need for patch 2.
Quoted 81 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_ERROR("cannot allocate and release VCPI on [MSTPORT:%p] in the same state\n",
drivers/gpu/drm/drm_dp_mst_topology.c +37
-2+ port); +return -EINVAL ; + } + pos->vcpi =0 ; + }return 0 ; }@@ -3115,15 +3211,45 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)static struct drm_private_state *drm_dp_mst_duplicate_state (struct drm_private_obj *obj) { -struct drm_dp_mst_topology_state *state; +struct drm_dp_mst_topology_state *state, *old_state = +to_dp_mst_topology_state (obj->state); +struct drm_dp_vcpi_allocation *pos, *vcpi; - state =kmemdup (obj->state,sizeof (*state),GFP_KERNEL ); + state =kmemdup (old_state,sizeof (*state),GFP_KERNEL );if (!state)return NULL ;__drm_atomic_helper_private_obj_duplicate_state (obj, &state->base); +INIT_LIST_HEAD (&state->vcpis); + state->vcpi_allocated = false; + +list_for_each_entry (pos, &old_state->vcpis, next) { +/* Prune leftover freed VCPI allocations */ +if (!pos->vcpi) +continue ; + + vcpi =kmemdup (pos,sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) + goto fail; + +/* Take a plain old kref here and don't validate the port, as +* it's topology might not even exist anymore +*/ +kref_get (&vcpi->port->kref); +list_add (&vcpi->next, &state->vcpis); + } +return &state->base; + +fail: +list_for_each_entry_safe (pos, vcpi, &state->vcpis, next) { +drm_dp_put_port (pos->port); +kfree (pos); + } +kfree (state); + +return NULL ; }static void drm_dp_mst_destroy_state(struct
drm_private_obj*obj,drivers/gpu/drm/drm_dp_mst_topology.c +11
-1@@ -3131,14 +3257,116 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, {struct drm_dp_mst_topology_state *mst_state =to_dp_mst_topology_state (state); +struct drm_dp_vcpi_allocation *pos, *tmp; + +list_for_each_entry_safe (pos, tmp, &mst_state->vcpis, next) { +/* We only keep references to ports with non-zero VCPIs */ +if (pos->vcpi) +drm_dp_put_port (pos->port); +kfree (pos); + }kfree (mst_state); } -static const struct drm_private_state_funcs mst_state_funcs = { +static inline int +drm_dp_mst_atomic_check_topology_state (struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_statedrivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct · line 3286
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +9
-0*mst_state) +{ +struct drm_dp_vcpi_allocation *vcpi; +struct drm_dp_mst_port *port; +int avail_slots =63 , ret; + +/* There's no possible scenario where releasing VCPI or keeping it the +* same would make the state invalid +*/ +if (!mst_state->vcpi_allocated) {Do we need this? If not I'm always in favour of removing code if it's not needed :-)
Quoted 12 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10
-0+ DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p allocates no VCPI, check skipped\n", + mgr, &mst_state->base); +return 0 ; + } + +list_for_each_entry (vcpi, &mst_state->vcpis, next) { +/* Releasing VCPI is always OK-even if the port is gone */ +if (!vcpi->vcpi) { +DRM_DEBUG_ATOMIC ("[MST PORT:%p] releases all VCPI slots\n", + vcpi->port);drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, · line 3302
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+continue ; + } + + port =drm_dp_get_validated_port_ref (mgr, vcpi->port); +if (!port) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] is gone but still has VCPI, cannot add new VCPI\n", + vcpi->port); +return -EINVAL ; + }For
find_vcpiwe've already done this. For unplugged vcpi, we can't do this here I think - otherwise if you enable 2 mst outputs on the same branch, and unplug both, old non-atomic userspace can never ever again disable them because it can't disable both. The check (to skip already deallocated ports) above isn't enough to handle that.Quoted 135 lines
drivers/gpu/drm/drm_dp_mst_topology.c +7
-0+ +DRM_DEBUG_ATOMIC ("[MST PORT:%p] requires %d vcpi slots\n" , + vcpi->port, vcpi->vcpi); + + avail_slots -= vcpi->vcpi; +if (avail_slots <0 ) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] not enough VCPI slotsin mst state %p (avail=%d)\n",
drivers/gpu/drm/drm_dp_mst_topology.c +20
-0+ vcpi->port, mst_state, + avail_slots + vcpi->vcpi); + ret = -ENOSPC ; + goto port_fail; + } + +drm_dp_put_port (port); + } +DRM_DEBUG_ATOMIC ("[MST MGR:%p] mst state %p VCPI avail=%d used=%d\n" , + mgr, mst_state, avail_slots, +63 - avail_slots); + +return 0 ; +port_fail: +drm_dp_put_port (port); +return ret; +} + +/** +* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
+ * atomic update is valid
+ * @state: Pointer to the new &structdrm_dp_mst_topology_state
+ *
+ * Checks the given topology state for an atomic update to ensure that it's
+ * valid. This includes checking whether there's enough bandwidth to support
+ * the new VCPI allocations in the atomic update.
+ *
+ * Any atomic drivers supporting DP MST must make sure to call this after
+ * checking the rest of their state in their
+ *&drm_mode_config_funcs.atomic_check()callback.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_atomic_release_vcpi_slots()
+ *
+ * Returns:
+ *
+ * 0 if the new state is valid, negative error code otherwise.
+ */drivers/gpu/drm/drm_dp_mst_topology.c +17
-0+int drm_dp_mst_atomic_check (struct drm_atomic_state *state) +{ +struct drm_dp_mst_topology_mgr *mgr; +struct drm_dp_mst_topology_state *mst_state; +int i, ret =0 ; + +for_each_new_mst_mgr_in_state (state, mgr, mst_state, i) { + ret =drm_dp_mst_atomic_check_topology_state (mgr, mst_state); +if (ret) +break ; + } + +return ret; +} +EXPORT_SYMBOL (drm_dp_mst_atomic_check); + +const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs ={
drivers/gpu/drm/drm_dp_mst_topology.c +3
-4.atomic_duplicate_state = drm_dp_mst_duplicate_state, .atomic_destroy_state = drm_dp_mst_destroy_state, }; +EXPORT_SYMBOL (drm_dp_mst_topology_state_funcs);/** * drm_atomic_get_mst_topology_state: get MST topology state @@ -3216,13 +3444,11 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, return -ENOMEM ; mst_state->mgr = mgr; - -/* max. time slots - one slot for MTP header */ - mst_state->avail_slots =63 ; +INIT_LIST_HEAD (&mst_state->vcpis);drm_atomic_private_obj_init (&mgr->base, &mst_state->base, - &mst_state_funcs); + &drm_dp_mst_topology_state_funcs);return 0 ;}
drivers/gpu/drm/i915/intel_display.c +4
-0@@ -12592,6 +12592,10 @@ static int intel_atomic_check(struct drm_device *dev, "[modeset]" : "[fastset]"); } + ret =drm_dp_mst_atomic_check (state); +if (ret) +return ret; +if (any_ms) { ret =intel_modeset_checks (state);drivers/gpu/drm/i915/intel_dp_mst.c +6
-1@@ -41,8 +41,13 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, struct drm_connector *connector = conn_state->connector;void *port =to_intel_connector (connector)->port;struct drm_atomic_state *state = pipe_config->base.state; +struct drm_crtc *crtc = pipe_config->base.crtc; +struct drm_crtc_state *old_crtc_state = +drm_atomic_get_old_crtc_state (state, crtc); +struct drm_crtc_state *new_crtc_state = &pipe_config->base;int bpp; -int lane_count, slots =0 ; +int lane_count, slots = +to_intel_crtc_state (old_crtc_state)->dp_m_n.tu;const struct drm_display_mode *adjusted_mode = &pipe_config- >base.adjusted_mode;drivers/gpu/drm/i915/intel_dp_mst.c +0
-0int mst_pbn; bool constant_n = drm_dp_has_quirk(&intel_dp->desc,@@ -77,8 +82,12 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, mst_pbn =drm_dp_calc_pbn_mode (adjusted_mode->crtc_clock, bpp);drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 91
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +6
-2pipe_config->pbn = mst_pbn; -/* Zombie connectors can't have VCPI slots */ -if (!drm_connector_is_unregistered (connector)) { +/* Only change VCPI allocation on actual mode changes, to prevent us +* from trying to allocate VCPI to ports that no longer exist when we +* may just be trying to disable DPMS on them +*/ +if (new_crtc_state->mode_changed || + new_crtc_state->connectors_changed) {Why exactly is this needed? We only call
compute_configon the enocoder when enabling an output. Not when disabling one. Similar for the atomic helpers and the various encoder callbacks (you have the same change in the nouveau code). Did this actually blow up while testing?Quoted 22 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-0slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr,drivers/gpu/drm/i915/intel_dp_mst.c +8
-4port,@@ -107,36 +116,39 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, return true; } -static int intel_dp_mst_atomic_check (struct drm_connector *connector, -struct drm_connector_state *new_conn_state) +static int +intel_dp_mst_atomic_check (struct drm_connector *connector, +struct drm_connector_state *new_conn_state) { +struct intel_connector *intel_connector = +to_intel_connector (connector); +struct drm_dp_mst_topology_mgr *mgr = + &intel_connector->mst_port->mst_mgr; +struct drm_dp_mst_port *port = intel_connector->port;struct drm_atomic_state *state = new_conn_state->state; -struct drm_connector_state *old_conn_state; -struct drm_crtc *old_crtc;drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 121
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +5
-4-struct drm_crtc_state *crtc_state; -int slots, ret =0 ; +struct drm_connector_state *old_conn_state = +drm_atomic_get_old_connector_state (state, connector); +struct drm_crtc *old_crtc = old_conn_state->crtc, + *new_crtc = new_conn_state->crtc; +struct drm_crtc_state *old_crtc_state, *new_crtc_state; - old_conn_state =drm_atomic_get_old_connector_state (state, connector); - old_crtc = old_conn_state->crtc;Bikeshed: Reworking all the above hides the real changes quite a bit.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 139
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +3
-6if (!old_crtc) -return ret; +return 0 ; - crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); - slots =to_intel_crtc_state (crtc_state)->dp_m_n.tu; -if (drm_atomic_crtc_needs_modeset (crtc_state) && slots >0 ) { -struct drm_dp_mst_topology_mgr *mgr; -struct drm_encoder *old_encoder; + old_crtc_state =drm_atomic_get_old_crtc_state (state, old_crtc); + if (!old_crtc_state ||!old_crtc_state would be a framework bug. Just remove that check.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 142
Quoted 2 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ !to_intel_crtc_state(old_crtc_state)->dp_m_n.tu) +return 0 ;Hm, why do we need this? Having a DP port previously enabled with a 0 tu sounds like a pretty big driver bug.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 146
Quoted 7 lines
drivers/gpu/drm/i915/intel_dp_mst.c +4
-2- old_encoder = old_conn_state->best_encoder; - mgr = &enc_to_mst (old_encoder)->primary->dp.mst_mgr; +/* If we switched CRTCs, clear the previous one's allocation */ + new_crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); +if (new_crtc_state->connectors_changed && !new_crtc_state->enable) +to_intel_crtc_state (new_crtc_state)->dp_m_n.tu =0 ;No idea why we need to clear this here? Shouldn't we just clear this unconditionally if we release the allocation.
Also naming convention, I'd call this
old_crtc_new_state, and maybeold_crtc_old_state. To make it clear these are the old/new states forold_crtc. Instead of old/new state for the new crtc.drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 149
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-7- ret =drm_dp_atomic_release_vcpi_slots (state, mgr, slots); -if (ret) -DRM_DEBUG_KMS ("failed releasing %d vcpi slots:%d\n" , slots, ret); -else -to_intel_crtc_state (crtc_state)->dp_m_n.tu =0 ; - } -return ret; +if (new_crtc) +return 0 ;Not sure why we shouldn't release this? If we handle this in helpers, it would remove piles of special casing code here I think. Plus remove special code in the hlpers.
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ +return drm_dp_atomic_release_vcpi_slots (state, mgr, port); } static void intel_mst_disable_dp(struct intel_encoder *encoder,include/drm/drm_dp_mst_helper.h +0
-0include/drm/drm_dp_mst_helper.h struct drm_dp_payload { · line 410
Quoted 5 lines
include/drm/drm_dp_mst_helper.h +1
-0@@ -406,9 +406,16 @@ struct drm_dp_payload {#define to_dp_mst_topology_state (x) container_of(x, struct drm_dp_mst_topology_state, base) +struct drm_dp_vcpi_allocation {I think a comment here that this pointer holds a full reference (even past when the connector is unplugged) would be good. Otherwise the loop in
release_vcpiwould be buggy.Quoted 46 lines
include/drm/drm_dp_mst_helper.h +16
-7+struct drm_dp_mst_port *port; +int vcpi; +struct list_head next; +}; +struct drm_dp_mst_topology_state {struct drm_private_state base; -int avail_slots; +struct list_head vcpis; +u8 vcpi_allocated;struct drm_dp_mst_topology_mgr *mgr; };@@ -619,14 +626,17 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);int drm_dp_mst_topology_mgr_resume (struct drm_dp_mst_topology_mgr *mgr);struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state (struct drm_atomic_state *state,struct drm_dp_mst_topology_mgr *mgr); -int drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -struct drm_dp_mst_port *port,int pbn); -int drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -int slots); +int __must_check +drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port,int pbn); +int __must_check +drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port);int drm_dp_send_power_updown_phy (struct drm_dp_mst_topology_mgr *mgr,struct drm_dp_mst_port *port,bool power_up); +int __must_check drm_dp_mst_atomic_check(struct drm_atomic_state*state);
extern const struct
drm_private_state_funcsdrm_dp_mst_topology_state_funcs;Signature · 1 line
2.19.1Signature · 3 lines
Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
Signature · 7 lines
Cheers, Lyude Paul _______________________________________________ Nouveau mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/nouveau
Re: [PATCH v6 3/6] drm/dp_mst: Start tracking per-port VCPI allocations
Quoted 90 lines · Lyude Paul
Quoted 81 lines · Daniel Vetter
Quoted 60 lines · Daniel Vetter
Quoted 35 lines · Lyude Paul
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:/* We cannot rely on
port->vcpi.num_slotsto update
*topology_state->avail_slotsas the port may not exist if the parent
* branch device was unplugged. This should be fixed by tracking
* per-port slot allocation indrm_dp_mst_topology_stateinstead of
* depending on the caller to tell us how many slots to release. */That's not the only reason we should fix this: forcing the driver to track the VCPI allocations throughout a state's atomic check is error prone, because it means that extra care has to be taken with the order that
drm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()are called in in order to ensure idempotency. Currently the only driver actually using these helpers, i915, doesn't even do this correctly: multiple ->best_encoder() checks with i915's current implementation would not be idempotent and would over-allocate VCPI slots, something I learned trying to implement fallback retraining in MST.So: simplify this whole mess, and teach
drm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()to track the VCPI allocations for each port. This allows us to ensure idempotency without having to rely on the driver as much. Additionally: the driver doesn't need to do any kind of VCPI slot tracking anymore if it doesn't need it for it's own internal state.Additionally; this adds a new
drm_dp_mst_atomic_check()helper which must be used by atomic drivers to perform validity checks for the new VCPI allocations incurred by a state.Also: update the documentation and make it more obvious that these /must/ be called by /all/ atomic drivers supporting MST.
Quoted 9 lines
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This required a good bit of changing to when we calldrm_dp_find_vcpi_slots(), mainly that we need to ensure that we only redo VCPI allocations on actual mode or CRTC changes, notcrtc_state->activechanges. Additionally, we no longer take the registration of the DRM connector for each port into account because so long as we have a kref to the port in the new or previous atomic state, the connector will stay registered.I write an entire pile of small nitpits (still included most of them below), until I realized this here wont work. Delaying the call to destroy the connector (well, unregister it really) wreaks the design we've come up with thus far, resulting in most of my comments here.
Instead, all we need to do is delay the kfree(port) at the bottom of
drm_dp_destroy_port(). The vcpi allocation structure _only_ needs the pointer value to stay valid, as a lookup key. It doesn't care at all about anything actually stored in there. So the only thing we need to delay is the kfree. I think the simplest way to achieve this is to add a 2nd kref (port->kfree_refor something like that), with on reference held by the port itself (released indrm_dp_destroy_port), and the other one held as-needed by the vcpi allocation lists.Quoted 3 lines
I think if we go with this design instead of retrofitting a semantic change of the port lifetime itself, all the complications I complain about below should disappear.
In the above I meant
drm_dp_destroy_portordrm_dp_destroy_connector_work.Aside: I think creating a kref for the final kfree would also solve a bunch of other issues in a much neater way: In
commit
f038c5b99fc1332f558b495d136d4f433ee65caaAuthor: Lyude Paul <[email protected]> Date: Tue Nov 13 17:46:14 2018 -0500drm/dp_mst: Skip validating ports during destruction, just ref
we could use that kfree reference to make sure the port pointer is alive. This of course means that
drm_dp_update_payload_part1()would also need to use the kfree reference for the vcpi allocations. And probably everywhere else (e.g. in your nouveau series for payload information).Quoted 7 lines
That would give us a very clear separation overall between "port can still be used because it's not yet unplugged" vs. "port only hangs around because a bunch of vcpi allocations and other payload things upstream of the port might still need the port lookup key to free resources". Instead of again sprinkling complicated conditions and magic tricks all over the code to figure out whethe we should allow the
get_validate_portto succeed or not, or the modeset to succeed or not.
Quoted 2 lines
mhm, I've been experimenting with this and I think I agree this is definitely the right solution to go with. It'll end up decomplicating a lot of this :)
Since it looks like we'll use the kfree_ref quite a bit, wrappers for that would be useful. Still not sure what to call those, best I could come up with is dp_port_malloc_get/put. Plus a comment explaining what it does. It's going to be a bit of wtf-is-this no matter what :-/ -Daniel
Quoted 735 lines
Quoted 731 lines
-Daniel
Quoted 729 lines
Piles of comments below.
Cheers, Daniel
Quoted 146 lines
- Use the small changes to
drm_dp_put_port()to add even more error checking to make misusage of the helpers more obvious. I added this after having to chase down various use-after-free conditions that started popping up from the new helpers so no one else has to troubleshoot that.
- Move some accidentalDRM_DEBUG_KMS()calls toDRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both not
be
called on the same port in a single atomic check phase (but multiple calls to one or the other is OK)Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI allocations happen in a state. This makes the next change I'm about to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure that when ports are destroyed and still have VCPI allocations in the topology state, the only state changes allowed are releasing said ports' VCPI. This prevents a state with a mix of VCPI allocations from destroyed ports, and allocations from valid ports.Changes since v3:
- Don't release VCPI allocations in the topology state immediately indrm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and skip over them indrm_dp_mst_duplicate_state(). This makes it sodrm_dp_atomic_release_vcpi_slots()is still idempotent while also throwing warnings if the driver messes up it's book keeping and tries to release VCPI slots on a port that doesn't have any pre-existing VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"Changes since v2:
- Usekmemdup()for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states indrm_dp_mst_atomic_check()so the driver doesn't have to do it
- Fix documentation indrm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into it's own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth requirements. This is needed because originally we relied on the state duplication functions to prune any stale ports from the new state, which would prevent us from incorrectly considering their bandwidth requirements alongside legitimate new payloads.
- Add function references indrm_dp_atomic_release_vcpi_slots()-
danvet
- Annotate atomic VCPI and atomic check functions with__must_check
- danvetChanges since v1:
- Don't use the now-removed ->atomic_check() for private objects hook, just give drivers a function to call themselvesSigned-off-by Lyude Paul <[email protected]>Cc Daniel Vetter <[email protected]>--- drivers/gpu/drm/drm_dp_mst_topology.c | 294 +++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 4 + drivers/gpu/drm/i915/intel_dp_mst.c | 64 +++--- include/drm/drm_dp_mst_helper.h | 24 ++- 4 files changed, 319 insertions(+), 67 deletions(-)drivers/gpu/drm/drm_dp_mst_topology.c +0
-1@@ -2617,21 +2617,42 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, }/** -* drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state
+ *drm_dp_atomic_find_vcpi_slots()- Find and add VCPI slots to the state
* @state: global atomic state
* @mgr: MST topology manager for the port
* @port: port to find vcpi slots for
* @pbn: bandwidth required for the mode in PBN
*
- * RETURNS:
- * Total slots in the atomic state assigned for this port or error
+ * Allocates VCPI slots to @port, replacing any previous VCPI allocations it
+ * may have had. Any atomic drivers which support MST must call this function
+ * in their&drm_encoder_helper_funcs.atomic_check()callback to change the
+ * current VCPI allocation for the new state, but only when
+ * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed is set
+ * to ensure compatibility with userspace applications that still use the
+ * legacy modesetting UAPI.
+ *
+ * Allocations set by this function are not checked against the bandwidth
+ * restraints of @mgr until the driver callsdrm_dp_mst_atomic_check().
+ *
+ * Additionally, it is OK to call this function multiple times on the same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_release_vcpi_slots()in the same atomic check phase.
+ *
+ * See also:
+ *drm_dp_atomic_release_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * Total slots in the atomic state assigned for this port, or a negative error
+ * code if the port no longer exists
*/
int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state*state,drivers/gpu/drm/drm_dp_mst_topology.c +11
-7struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn) { struct drm_dp_mst_topology_state *topology_state; -int req_slots; +struct drm_dp_vcpi_allocation *pos, *vcpi =NULL ; +int prev_slots, req_slots, ret; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))@@ -2640,20 +2661,60 @@ int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state *state, port =drm_dp_get_validated_port_ref (mgr, port);if (port ==NULL )return -EINVAL ; - req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); -DRM_DEBUG_KMS ("vcpi slots req=%d, avail=%d\n" , - req_slots, topology_state->avail_slots); -if (req_slots > topology_state->avail_slots) { -drm_dp_put_port (port); -return -ENOSPC ; + topology_state->vcpi_allocated = true; + +/* Find the current allocation for this port, if any */ +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + vcpi = pos; + prev_slots = vcpi->vcpi; + +/* drivers/gpu/drm/drm_dp_mst_topology.c int drm_dp_atomic_find_vcpi_slots(struct · line 2684
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+* This should never happen, unless the driver tries +* releasing and allocating the same VCPI allocation, +* which is an error +*/ +if (WARN_ON (!prev_slots)) { + DRM_ERROR("cannot allocate and release VCPI on [MST PORT:%p] in the same state\n", + port); +return -EINVAL ; + }Allowing atomic changes to output state (like disabling it on one crtc, and then enabling on the other, thus both releasing and re-allocating the vcpi in one go) is kinda the point of atomic. So definitely can't EINVAL here.
Sounds like we need more testcases in igt?
Quoted 110 lines
drivers/gpu/drm/drm_dp_mst_topology.c +31
-3+ +break ; + } } +if (!vcpi) + prev_slots =0 ; - topology_state->avail_slots -= req_slots; -DRM_DEBUG_KMS ("vcpi slots avail=%d" , topology_state->avail_slots); + req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); + +DRM_DEBUG_ATOMIC ("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n" , + port->connector->base.id, port->connector->name, + port, prev_slots, req_slots); + +/* Add the new allocation to the state */ +if (!vcpi) { + vcpi =kzalloc (sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) { + ret = -ENOMEM ; + goto out; + } + vcpi->port =drm_dp_get_validated_port_ref (mgr, port); +if (!vcpi->port) { + ret = -EINVAL ; +kfree (vcpi); + goto out; + } +list_add (&vcpi->next, &topology_state->vcpis); + } + vcpi->vcpi = req_slots; + + ret = req_slots; +out:drm_dp_put_port (port); -return req_slots; +return ret; }
EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);drivers/gpu/drm/drm_dp_mst_topology.c +1
-3@@ -2661,31 +2722,66 @@ EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);* drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots * @state: global atomic state * @mgr: MST topology manager for the port -* @slots: number of vcpi slots to release +* @port: The port to release the VCPI slots from * -* RETURNS: -* 0 if @slots were added back to &drm_dp_mst_topology_state- Quoted 1 line
avail_slotsor- * negative error code
+ * Releases any VCPI slots that have been allocated to a port in the atomic
+ * state. Any atomic drivers which support MST must call this function in
+ * their&drm_connector_helper_funcs.atomic_check()callback when the
+ * connector will no longer have VCPI allocated (e.g. because it's CRTC was
+ * removed) when it had VCPI allocated in the previous atomic state.
+ *
+ * It is OK to call this even if @port has been removed from the system.
+ * Additionally, it is OK to call this function multiple times on the same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_find_vcpi_slots()on the same @port in a single atomic check
+ * phase.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * 0 if all slots for this port were added back to
+ * &drm_dp_mst_topology_state.avail_slots or negative error code */
int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state*state,drivers/gpu/drm/drm_dp_mst_topology.c +13
-10struct drm_dp_mst_topology_mgr *mgr, - int slots) + struct drm_dp_mst_port *port) { struct drm_dp_mst_topology_state *topology_state; +struct drm_dp_vcpi_allocation *pos; +bool found = false; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))return PTR_ERR (topology_state); -/* We cannot rely on port->vcpi.num_slots to update -* topology_state->avail_slots as the port may not exist if the parent -* branch device was unplugged. This should be fixed by tracking -* per-port slot allocation in drm_dp_mst_topology_state instead of -* depending on the caller to tell us how many slots to release. -*/ - topology_state->avail_slots += slots; -DRM_DEBUG_KMS ("vcpi slots released=%d, avail=%d\n" , - slots, topology_state->avail_slots); +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + found = true; +break ; + } + } +if (WARN_ON (!found)) { +DRM_ERROR ("no VCPI for [MST PORT:%p] found in mst state %p\n" , + port, &topology_state->base); +return -EINVAL ;drivers/gpu/drm/drm_dp_mst_topology.c EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots); · line 2778
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10
-0+ } + +DRM_DEBUG_ATOMIC ("[MST PORT:%p] VCPI %d -> 0\n" , port, pos->vcpi); +if (pos->vcpi) { +/* +* This should be safe-as the previous state will still hold a +* reference to this port until we swap states. If it's not, +* the driver has a bookkeeping error +*/ +if (WARN_ON (drm_dp_put_port (port))) {The usual way to do this (instead of adding a return value to the normal
_putfunction) is something like
kref_put(port, function_that_with_just_a_WARNING_DRM_ERROR)avoids the need for patch 2.
Quoted 81 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_ERROR("cannot allocate and release VCPI on [MSTPORT:%p] in the same state\n",
drivers/gpu/drm/drm_dp_mst_topology.c +37
-2+ port); +return -EINVAL ; + } + pos->vcpi =0 ; + }return 0 ; }@@ -3115,15 +3211,45 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)static struct drm_private_state *drm_dp_mst_duplicate_state (struct drm_private_obj *obj) { -struct drm_dp_mst_topology_state *state; +struct drm_dp_mst_topology_state *state, *old_state = +to_dp_mst_topology_state (obj->state); +struct drm_dp_vcpi_allocation *pos, *vcpi; - state =kmemdup (obj->state,sizeof (*state),GFP_KERNEL ); + state =kmemdup (old_state,sizeof (*state),GFP_KERNEL );if (!state)return NULL ;__drm_atomic_helper_private_obj_duplicate_state (obj, &state->base); +INIT_LIST_HEAD (&state->vcpis); + state->vcpi_allocated = false; + +list_for_each_entry (pos, &old_state->vcpis, next) { +/* Prune leftover freed VCPI allocations */ +if (!pos->vcpi) +continue ; + + vcpi =kmemdup (pos,sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) + goto fail; + +/* Take a plain old kref here and don't validate the port, as +* it's topology might not even exist anymore +*/ +kref_get (&vcpi->port->kref); +list_add (&vcpi->next, &state->vcpis); + } +return &state->base; + +fail: +list_for_each_entry_safe (pos, vcpi, &state->vcpis, next) { +drm_dp_put_port (pos->port); +kfree (pos); + } +kfree (state); + +return NULL ; }static void drm_dp_mst_destroy_state(struct
drm_private_obj*obj,drivers/gpu/drm/drm_dp_mst_topology.c +11
-1@@ -3131,14 +3257,116 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, {struct drm_dp_mst_topology_state *mst_state =to_dp_mst_topology_state (state); +struct drm_dp_vcpi_allocation *pos, *tmp; + +list_for_each_entry_safe (pos, tmp, &mst_state->vcpis, next) { +/* We only keep references to ports with non-zero VCPIs */ +if (pos->vcpi) +drm_dp_put_port (pos->port); +kfree (pos); + }kfree (mst_state); } -static const struct drm_private_state_funcs mst_state_funcs = { +static inline int +drm_dp_mst_atomic_check_topology_state (struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_statedrivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct · line 3286
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +9
-0*mst_state) +{ +struct drm_dp_vcpi_allocation *vcpi; +struct drm_dp_mst_port *port; +int avail_slots =63 , ret; + +/* There's no possible scenario where releasing VCPI or keeping it the +* same would make the state invalid +*/ +if (!mst_state->vcpi_allocated) {Do we need this? If not I'm always in favour of removing code if it's not needed :-)
Quoted 12 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10
-0+ DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p allocates no VCPI, check skipped\n", + mgr, &mst_state->base); +return 0 ; + } + +list_for_each_entry (vcpi, &mst_state->vcpis, next) { +/* Releasing VCPI is always OK-even if the port is gone */ +if (!vcpi->vcpi) { +DRM_DEBUG_ATOMIC ("[MST PORT:%p] releases all VCPI slots\n", + vcpi->port);drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, · line 3302
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+continue ; + } + + port =drm_dp_get_validated_port_ref (mgr, vcpi->port); +if (!port) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] is gone but still has VCPI, cannot add new VCPI\n", + vcpi->port); +return -EINVAL ; + }For
find_vcpiwe've already done this. For unplugged vcpi, we can't do this here I think - otherwise if you enable 2 mst outputs on the same branch, and unplug both, old non-atomic userspace can never ever again disable them because it can't disable both. The check (to skip already deallocated ports) above isn't enough to handle that.Quoted 135 lines
drivers/gpu/drm/drm_dp_mst_topology.c +7
-0+ +DRM_DEBUG_ATOMIC ("[MST PORT:%p] requires %d vcpi slots\n" , + vcpi->port, vcpi->vcpi); + + avail_slots -= vcpi->vcpi; +if (avail_slots <0 ) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] not enough VCPI slotsin mst state %p (avail=%d)\n",
drivers/gpu/drm/drm_dp_mst_topology.c +20
-0+ vcpi->port, mst_state, + avail_slots + vcpi->vcpi); + ret = -ENOSPC ; + goto port_fail; + } + +drm_dp_put_port (port); + } +DRM_DEBUG_ATOMIC ("[MST MGR:%p] mst state %p VCPI avail=%d used=%d\n" , + mgr, mst_state, avail_slots, +63 - avail_slots); + +return 0 ; +port_fail: +drm_dp_put_port (port); +return ret; +} + +/** +* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
+ * atomic update is valid
+ * @state: Pointer to the new &structdrm_dp_mst_topology_state
+ *
+ * Checks the given topology state for an atomic update to ensure that it's
+ * valid. This includes checking whether there's enough bandwidth to support
+ * the new VCPI allocations in the atomic update.
+ *
+ * Any atomic drivers supporting DP MST must make sure to call this after
+ * checking the rest of their state in their
+ *&drm_mode_config_funcs.atomic_check()callback.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_atomic_release_vcpi_slots()
+ *
+ * Returns:
+ *
+ * 0 if the new state is valid, negative error code otherwise.
+ */drivers/gpu/drm/drm_dp_mst_topology.c +17
-0+int drm_dp_mst_atomic_check (struct drm_atomic_state *state) +{ +struct drm_dp_mst_topology_mgr *mgr; +struct drm_dp_mst_topology_state *mst_state; +int i, ret =0 ; + +for_each_new_mst_mgr_in_state (state, mgr, mst_state, i) { + ret =drm_dp_mst_atomic_check_topology_state (mgr, mst_state); +if (ret) +break ; + } + +return ret; +} +EXPORT_SYMBOL (drm_dp_mst_atomic_check); + +const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs ={
drivers/gpu/drm/drm_dp_mst_topology.c +3
-4.atomic_duplicate_state = drm_dp_mst_duplicate_state, .atomic_destroy_state = drm_dp_mst_destroy_state, }; +EXPORT_SYMBOL (drm_dp_mst_topology_state_funcs);/** * drm_atomic_get_mst_topology_state: get MST topology state @@ -3216,13 +3444,11 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, return -ENOMEM ; mst_state->mgr = mgr; - -/* max. time slots - one slot for MTP header */ - mst_state->avail_slots =63 ; +INIT_LIST_HEAD (&mst_state->vcpis);drm_atomic_private_obj_init (&mgr->base, &mst_state->base, - &mst_state_funcs); + &drm_dp_mst_topology_state_funcs);return 0 ;}
drivers/gpu/drm/i915/intel_display.c +4
-0@@ -12592,6 +12592,10 @@ static int intel_atomic_check(struct drm_device *dev, "[modeset]" : "[fastset]"); } + ret =drm_dp_mst_atomic_check (state); +if (ret) +return ret; +if (any_ms) { ret =intel_modeset_checks (state);drivers/gpu/drm/i915/intel_dp_mst.c +6
-1@@ -41,8 +41,13 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, struct drm_connector *connector = conn_state->connector;void *port =to_intel_connector (connector)->port;struct drm_atomic_state *state = pipe_config->base.state; +struct drm_crtc *crtc = pipe_config->base.crtc; +struct drm_crtc_state *old_crtc_state = +drm_atomic_get_old_crtc_state (state, crtc); +struct drm_crtc_state *new_crtc_state = &pipe_config->base;int bpp; -int lane_count, slots =0 ; +int lane_count, slots = +to_intel_crtc_state (old_crtc_state)->dp_m_n.tu;const struct drm_display_mode *adjusted_mode = &pipe_config- >base.adjusted_mode;drivers/gpu/drm/i915/intel_dp_mst.c +0
-0int mst_pbn; bool constant_n = drm_dp_has_quirk(&intel_dp->desc,@@ -77,8 +82,12 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, mst_pbn =drm_dp_calc_pbn_mode (adjusted_mode->crtc_clock, bpp);drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 91
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +6
-2pipe_config->pbn = mst_pbn; -/* Zombie connectors can't have VCPI slots */ -if (!drm_connector_is_unregistered (connector)) { +/* Only change VCPI allocation on actual mode changes, to prevent us +* from trying to allocate VCPI to ports that no longer exist when we +* may just be trying to disable DPMS on them +*/ +if (new_crtc_state->mode_changed || + new_crtc_state->connectors_changed) {Why exactly is this needed? We only call
compute_configon the enocoder when enabling an output. Not when disabling one. Similar for the atomic helpers and the various encoder callbacks (you have the same change in the nouveau code). Did this actually blow up while testing?Quoted 22 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-0slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr,drivers/gpu/drm/i915/intel_dp_mst.c +8
-4port,@@ -107,36 +116,39 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, return true; } -static int intel_dp_mst_atomic_check (struct drm_connector *connector, -struct drm_connector_state *new_conn_state) +static int +intel_dp_mst_atomic_check (struct drm_connector *connector, +struct drm_connector_state *new_conn_state) { +struct intel_connector *intel_connector = +to_intel_connector (connector); +struct drm_dp_mst_topology_mgr *mgr = + &intel_connector->mst_port->mst_mgr; +struct drm_dp_mst_port *port = intel_connector->port;struct drm_atomic_state *state = new_conn_state->state; -struct drm_connector_state *old_conn_state; -struct drm_crtc *old_crtc;drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 121
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +5
-4-struct drm_crtc_state *crtc_state; -int slots, ret =0 ; +struct drm_connector_state *old_conn_state = +drm_atomic_get_old_connector_state (state, connector); +struct drm_crtc *old_crtc = old_conn_state->crtc, + *new_crtc = new_conn_state->crtc; +struct drm_crtc_state *old_crtc_state, *new_crtc_state; - old_conn_state =drm_atomic_get_old_connector_state (state, connector); - old_crtc = old_conn_state->crtc;Bikeshed: Reworking all the above hides the real changes quite a bit.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 139
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +3
-6if (!old_crtc) -return ret; +return 0 ; - crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); - slots =to_intel_crtc_state (crtc_state)->dp_m_n.tu; -if (drm_atomic_crtc_needs_modeset (crtc_state) && slots >0 ) { -struct drm_dp_mst_topology_mgr *mgr; -struct drm_encoder *old_encoder; + old_crtc_state =drm_atomic_get_old_crtc_state (state, old_crtc); + if (!old_crtc_state ||!old_crtc_state would be a framework bug. Just remove that check.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 142
Quoted 2 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ !to_intel_crtc_state(old_crtc_state)->dp_m_n.tu) +return 0 ;Hm, why do we need this? Having a DP port previously enabled with a 0 tu sounds like a pretty big driver bug.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 146
Quoted 7 lines
drivers/gpu/drm/i915/intel_dp_mst.c +4
-2- old_encoder = old_conn_state->best_encoder; - mgr = &enc_to_mst (old_encoder)->primary->dp.mst_mgr; +/* If we switched CRTCs, clear the previous one's allocation */ + new_crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); +if (new_crtc_state->connectors_changed && !new_crtc_state->enable) +to_intel_crtc_state (new_crtc_state)->dp_m_n.tu =0 ;No idea why we need to clear this here? Shouldn't we just clear this unconditionally if we release the allocation.
Also naming convention, I'd call this
old_crtc_new_state, and maybeold_crtc_old_state. To make it clear these are the old/new states forold_crtc. Instead of old/new state for the new crtc.drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 149
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-7- ret =drm_dp_atomic_release_vcpi_slots (state, mgr, slots); -if (ret) -DRM_DEBUG_KMS ("failed releasing %d vcpi slots:%d\n" , slots, ret); -else -to_intel_crtc_state (crtc_state)->dp_m_n.tu =0 ; - } -return ret; +if (new_crtc) +return 0 ;Not sure why we shouldn't release this? If we handle this in helpers, it would remove piles of special casing code here I think. Plus remove special code in the hlpers.
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ +return drm_dp_atomic_release_vcpi_slots (state, mgr, port); } static void intel_mst_disable_dp(struct intel_encoder *encoder,include/drm/drm_dp_mst_helper.h +0
-0include/drm/drm_dp_mst_helper.h struct drm_dp_payload { · line 410
Quoted 5 lines
include/drm/drm_dp_mst_helper.h +1
-0@@ -406,9 +406,16 @@ struct drm_dp_payload {#define to_dp_mst_topology_state (x) container_of(x, struct drm_dp_mst_topology_state, base) +struct drm_dp_vcpi_allocation {I think a comment here that this pointer holds a full reference (even past when the connector is unplugged) would be good. Otherwise the loop in
release_vcpiwould be buggy.Quoted 46 lines
include/drm/drm_dp_mst_helper.h +16
-7+struct drm_dp_mst_port *port; +int vcpi; +struct list_head next; +}; +struct drm_dp_mst_topology_state {struct drm_private_state base; -int avail_slots; +struct list_head vcpis; +u8 vcpi_allocated;struct drm_dp_mst_topology_mgr *mgr; };@@ -619,14 +626,17 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);int drm_dp_mst_topology_mgr_resume (struct drm_dp_mst_topology_mgr *mgr);struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state (struct drm_atomic_state *state,struct drm_dp_mst_topology_mgr *mgr); -int drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -struct drm_dp_mst_port *port,int pbn); -int drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -int slots); +int __must_check +drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port,int pbn); +int __must_check +drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port);int drm_dp_send_power_updown_phy (struct drm_dp_mst_topology_mgr *mgr,struct drm_dp_mst_port *port,bool power_up); +int __must_check drm_dp_mst_atomic_check(struct drm_atomic_state*state);
extern const struct
drm_private_state_funcsdrm_dp_mst_topology_state_funcs;Signature · 1 line
2.19.1Signature · 3 lines
Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.chSignature · 2 lines
Cheers, Lyude Paul
Signature · 7 lines
Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ Intel-gfx mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Lyude Paul
maintainerRe: [PATCH v6 3/6] drm/dp_mst: Start tracking per-port VCPI allocations
Quoted 126 lines · Daniel Vetter
Quoted 120 lines · Lyude Paul
Quoted 108 lines · Daniel Vetter
Quoted 82 lines · Daniel Vetter
Quoted 47 lines · Lyude Paul
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:/* We cannot rely on port->vcpi.num_slots to update * topology_state->avail_slots as the port may not exist ifthe parent
* branch device was unplugged. This should be fixed by
tracking
* per-port slot allocation indrm_dp_mst_topology_state
instead of
* depending on the caller to tell us how many slots to
release.*/That's not the only reason we should fix this: forcing the driver to track the VCPI allocations throughout a state's atomic check is error prone, because it means that extra care has to be taken with the
order thatdrm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()are called in in order to ensure idempotency. Currently the only driver actually using these helpers, i915, doesn't even do this correctly: multiple ->best_encoder() checks
with i915's current implementation would not be idempotent and would over-allocate VCPI slots, something I learned trying to implement fallback retraining in MST.So: simplify this whole mess, and teach
drm_dp_atomic_find_vcpi_slots()
anddrm_dp_atomic_release_vcpi_slots()to track the VCPI allocations for
each port. This allows us to ensure idempotency without having to rely
on the driver as much. Additionally: the driver doesn't need to do any
kind of VCPI slot tracking anymore if it doesn't need it for it's own
internal state.Additionally; this adds a new
drm_dp_mst_atomic_check()helper which must be used by atomic drivers to perform validity checks for the new
VCPI allocations incurred by a state.Also: update the documentation and make it more obvious that these /must/ be called by /all/ atomic drivers supporting MST.
Quoted 13 lines
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This
required
a good bit of changing to when we calldrm_dp_find_vcpi_slots(), mainly that we need to ensure that we only redo VCPI allocations
on
actual mode or CRTC changes, notcrtc_state->activechanges. Additionally, we no longer take the registration of the DRM
connector
for each port into account because so long as we have a kref to
the
port in the new or previous atomic state, the connector will stay registered.I write an entire pile of small nitpits (still included most of them below), until I realized this here wont work. Delaying the call to destroy
the connector (well, unregister it really) wreaks the design we've come up
with thus far, resulting in most of my comments here.Instead, all we need to do is delay the kfree(port) at the bottom of
drm_dp_destroy_port(). The vcpi allocation structure _only_ needs the pointer value to stay valid, as a lookup key. It doesn't care at all about
anything actually stored in there. So the only thing we need to delay is
the kfree. I think the simplest way to achieve this is to add a 2nd kref
(port->kfree_refor something like that), with on reference held by the
port itself (released indrm_dp_destroy_port), and the other one held as-needed by the vcpi allocation lists.Quoted 4 lines
I think if we go with this design instead of retrofitting a semantic change of the port lifetime itself, all the complications I complain about
below should disappear.In the above I meant
drm_dp_destroy_portordrm_dp_destroy_connector_work.Aside: I think creating a kref for the final kfree would also solve a bunch of other issues in a much neater way: In
commit
f038c5b99fc1332f558b495d136d4f433ee65caaAuthor: Lyude Paul <[email protected]> Date: Tue Nov 13 17:46:14 2018 -0500drm/dp_mst: Skip validating ports during destruction, just ref
we could use that kfree reference to make sure the port pointer is alive.
This of course means thatdrm_dp_update_payload_part1()would also need to
use the kfree reference for the vcpi allocations. And probably everywhere
else (e.g. in your nouveau series for payload information).Quoted 10 lines
That would give us a very clear separation overall between "port can still
be used because it's not yet unplugged" vs. "port only hangs around because a bunch of vcpi allocations and other payload things upstream of the port might still need the port lookup key to free resources". Instead
of again sprinkling complicated conditions and magic tricks all over the code to figure out whethe we should allow theget_validate_portto succeed
or not, or the modeset to succeed or not.Quoted 4 lines
mhm, I've been experimenting with this and I think I agree this is definitely
the right solution to go with. It'll end up decomplicating a lot of this :)
Quoted 5 lines
Since it looks like we'll use the
kfree_refquite a bit, wrappers for that would be useful. Still not sure what to call those, best I could come up with is dp_port_malloc_get/put. Plus a comment explaining what it does. It's going to be a bit of wtf-is-this no matter what :-/ -Daniel
We could do this the other way around so it looks like this maybe
struct kref; /* manages kfree() */
struct topology_kref; /* corresonds to lifetime in topology */
Then only expose functions for the normal kref to drivers, so that any possible confusion is still limited to drm_dp_mst_topology.c
Quoted 829 lines
Quoted 829 lines
Quoted 829 lines
-Daniel
Quoted 826 lines
Piles of comments below.
Cheers, Daniel
Quoted 177 lines
- Use the small changes to
drm_dp_put_port()to add even more error checking to make misusage of the helpers more obvious. I added
this
after having to chase down various use-after-free conditions that started popping up from the new helpers so no one else has to troubleshoot that.
- Move some accidentalDRM_DEBUG_KMS()calls toDRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both
not
be
called on the same port in a single atomic check phase (but
multiple
calls to one or the other is OK)Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI allocations happen in a state. This makes the next change I'm
about
to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure
that
when ports are destroyed and still have VCPI allocations in the topology state, the only state changes allowed are releasing said ports' VCPI. This prevents a state with a mix of VCPI allocations from destroyed ports, and allocations from valid ports.Changes since v3:
- Don't release VCPI allocations in the topology state immediately
in
drm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and
skip
over them indrm_dp_mst_duplicate_state(). This makes it sodrm_dp_atomic_release_vcpi_slots()is still idempotent while also throwing warnings if the driver messes up it's book keeping and
tries
to release VCPI slots on a port that doesn't have any pre-
existing
VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"Changes since v2:
- Usekmemdup()for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states indrm_dp_mst_atomic_check()so the driver doesn't have to do it
- Fix documentation indrm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into
it's
own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth requirements. This is needed because originally we relied on the state duplication functions to prune any stale ports from the new state, which would prevent us from incorrectly considering their bandwidth requirements alongside legitimate new payloads.
- Add function references indrm_dp_atomic_release_vcpi_slots()-
danvet
- Annotate atomic VCPI and atomic check functions with__must_check
- danvetChanges since v1:
- Don't use the now-removed ->atomic_check() for private objects
hook,
just give drivers a function to call themselvesSigned-off-by Lyude Paul <[email protected]>Cc Daniel Vetter <[email protected]>--- drivers/gpu/drm/drm_dp_mst_topology.c | 294 +++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 4 + drivers/gpu/drm/i915/intel_dp_mst.c | 64 +++--- include/drm/drm_dp_mst_helper.h | 24 ++- 4 files changed, 319 insertions(+), 67 deletions(-)drivers/gpu/drm/drm_dp_mst_topology.c +0
-1@@ -2617,21 +2617,42 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, }/** -* drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state
+ *drm_dp_atomic_find_vcpi_slots()- Find and add VCPI slots to the state
* @state: global atomic state
* @mgr: MST topology manager for the port
* @port: port to find vcpi slots for
* @pbn: bandwidth required for the mode in PBN
*
- * RETURNS:
- * Total slots in the atomic state assigned for this port or error
+ * Allocates VCPI slots to @port, replacing any previous VCPI allocations it
+ * may have had. Any atomic drivers which support MST must call this
function
+ * in their&drm_encoder_helper_funcs.atomic_check()callback to change
the
+ * current VCPI allocation for the new state, but only when
+ * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed
is set
+ * to ensure compatibility with userspace applications that still use
the
+ * legacy modesetting UAPI.
+ *
+ * Allocations set by this function are not checked against the bandwidth
+ * restraints of @mgr until the driver callsdrm_dp_mst_atomic_check().
+ *
+ * Additionally, it is OK to call this function multiple times on the
same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_release_vcpi_slots()in the same atomic check phase.
+ *
+ * See also:
+ *drm_dp_atomic_release_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * Total slots in the atomic state assigned for this port, or a negative error
+ * code if the port no longer exists
*/
int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state*state,drivers/gpu/drm/drm_dp_mst_topology.c +0
-0struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, intpbn)
{drivers/gpu/drm/drm_dp_mst_topology.c +2
-1struct drm_dp_mst_topology_state *topology_state; -int req_slots; +struct drm_dp_vcpi_allocation *pos, *vcpi =NULL ; +int prev_slots, req_slots, ret; topology_state = drm_atomic_get_mst_topology_state(state,mgr);
drivers/gpu/drm/drm_dp_mst_topology.c +10
-6if (IS_ERR (topology_state))@@ -2640,20 +2661,60 @@ int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state *state, port =drm_dp_get_validated_port_ref (mgr, port);if (port ==NULL )return -EINVAL ; - req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); -DRM_DEBUG_KMS ("vcpi slots req=%d, avail=%d\n" , - req_slots, topology_state->avail_slots); -if (req_slots > topology_state->avail_slots) { -drm_dp_put_port (port); -return -ENOSPC ; + topology_state->vcpi_allocated = true; + +/* Find the current allocation for this port, if any */ +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + vcpi = pos; + prev_slots = vcpi->vcpi; + +/* +* This should never happen, unless the driver tries
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+* releasing and allocating the same VCPI allocation,
drivers/gpu/drm/drm_dp_mst_topology.c +4
-0+* which is an error +*/ +if (WARN_ON (!prev_slots)) { + DRM_ERROR("cannot allocate and releaseVCPI on
[MST PORT:%p] in the same state\n",drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ port); +return -EINVAL ; + }Allowing atomic changes to output state (like disabling it on one crtc,
and then enabling on the other, thus both releasing and re-allocating the
vcpi in one go) is kinda the point of atomic. So definitely can't EINVAL
here.Sounds like we need more testcases in igt?
Quoted 140 lines
drivers/gpu/drm/drm_dp_mst_topology.c +5
-2+ +break ; + } } +if (!vcpi) + prev_slots =0 ; - topology_state->avail_slots -= req_slots; - DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state-Quoted 1 line
avail_slots);drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); + + DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d ->%d\n",
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ port->connector->base.id, port->connector-Quoted 1 line
name,
drivers/gpu/drm/drm_dp_mst_topology.c +22
-1+ port, prev_slots, req_slots); + +/* Add the new allocation to the state */ +if (!vcpi) { + vcpi =kzalloc (sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) { + ret = -ENOMEM ; + goto out; + } + vcpi->port =drm_dp_get_validated_port_ref (mgr, port); +if (!vcpi->port) { + ret = -EINVAL ; +kfree (vcpi); + goto out; + } +list_add (&vcpi->next, &topology_state->vcpis); + } + vcpi->vcpi = req_slots; + + ret = req_slots; +out:drm_dp_put_port (port); -return req_slots; +return ret; }EXPORT_SYMBOL (drm_dp_atomic_find_vcpi_slots);@@ -2661,31 +2722,66 @@EXPORT_SYMBOL (drm_dp_atomic_find_vcpi_slots); * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpislots
* @state: global atomic state
* @mgr: MST topology manager for the port
- * @slots: number of vcpi slots to release
+ * @port: The port to release the VCPI slots from
*
- * RETURNS:
- * 0 if @slots were added back to &drm_dp_mst_topology_state-Quoted 1 line
avail_slotsor- * negative error code
+ * Releases any VCPI slots that have been allocated to a port in the
atomic
+ * state. Any atomic drivers which support MST must call this function
in
+ * their&drm_connector_helper_funcs.atomic_check()callback when the
+ * connector will no longer have VCPI allocated (e.g. because it's CRTC
was
+ * removed) when it had VCPI allocated in the previous atomic state.
+ *
+ * It is OK to call this even if @port has been removed from the system.
+ * Additionally, it is OK to call this function multiple times on the
same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_find_vcpi_slots()on the same @port in a single atomic
check
+ * phase.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * 0 if all slots for this port were added back to
+ * &drm_dp_mst_topology_state.avail_slots or negative error code */
int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state
*state,
structdrm_dp_mst_topology_mgr
*mgr,drivers/gpu/drm/drm_dp_mst_topology.c +3
-1- int slots) + struct drm_dp_mst_port *port) {struct drm_dp_mst_topology_state *topology_state; +struct drm_dp_vcpi_allocation *pos; +bool found = false; topology_state = drm_atomic_get_mst_topology_state(state,mgr);
drivers/gpu/drm/drm_dp_mst_topology.c +0
-2if (IS_ERR (topology_state))return PTR_ERR (topology_state); -/* We cannot rely on port->vcpi.num_slots to update -* topology_state->avail_slots as the port may not exist if the parent
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* branch device was unplugged. This should be fixed by tracking
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* per-port slot allocation in drm_dp_mst_topology_state instead of
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* depending on the caller to tell us how many slots to release.
drivers/gpu/drm/drm_dp_mst_topology.c +8
-4-*/ - topology_state->avail_slots += slots; -DRM_DEBUG_KMS ("vcpi slots released=%d, avail=%d\n" , - slots, topology_state->avail_slots); +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + found = true; +break ; + } + } +if (WARN_ON (!found)) { + DRM_ERROR("no VCPI for [MST PORT:%p] found in mststate %p\n",
drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ port, &topology_state->base); +return -EINVAL ; + } + + DRM_DEBUG_ATOMIC("[MST PORT:%p] VCPI %d -> 0\n", port, pos-Quoted 1 line
vcpi);
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+if (pos->vcpi) { +/* +* This should be safe-as the previous state will still hold a
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+* reference to this port until we swap states. If it's not,
drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+* the driver has a bookkeeping error +*/ +if (WARN_ON (drm_dp_put_port (port))) {The usual way to do this (instead of adding a return value to the normal
_putfunction) is something like
kref_put(port, function_that_with_just_a_WARNING_DRM_ERROR)avoids the need for patch 2.
Quoted 96 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_ERROR("cannot allocate and release VCPI on[MST
PORT:%p] in the same state\n",drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ port); +return -EINVAL ; + } + pos->vcpi =0 ; + }return 0 ; }@@ -3115,15 +3211,45 @@ static void
drm_dp_destroy_connector_work(struct work_struct *work)
static structdrm_private_state*drm_dp_mst_duplicate_state(struct drm_private_obj *obj){drivers/gpu/drm/drm_dp_mst_topology.c +4
-2-struct drm_dp_mst_topology_state *state; +struct drm_dp_mst_topology_state *state, *old_state = +to_dp_mst_topology_state (obj->state); +struct drm_dp_vcpi_allocation *pos, *vcpi; - state =kmemdup (obj->state,sizeof (*state),GFP_KERNEL ); + state =kmemdup (old_state,sizeof (*state),GFP_KERNEL );if (!state)return NULL ; __drm_atomic_helper_private_obj_duplicate_state(obj, &state-Quoted 1 line
base);
drivers/gpu/drm/drm_dp_mst_topology.c +13
-0+INIT_LIST_HEAD (&state->vcpis); + state->vcpi_allocated = false; + +list_for_each_entry (pos, &old_state->vcpis, next) { +/* Prune leftover freed VCPI allocations */ +if (!pos->vcpi) +continue ; + + vcpi =kmemdup (pos,sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) + goto fail; + +/* Take a plain old kref here and don't validate the port, as
drivers/gpu/drm/drm_dp_mst_topology.c +19
-0+* it's topology might not even exist anymore +*/ +kref_get (&vcpi->port->kref); +list_add (&vcpi->next, &state->vcpis); + } +return &state->base; + +fail: +list_for_each_entry_safe (pos, vcpi, &state->vcpis, next) { +drm_dp_put_port (pos->port); +kfree (pos); + } +kfree (state); + +return NULL ; } static void drm_dp_mst_destroy_state(struct drm_private_obj *obj,@@ -3131,14 +3257,116 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, {struct drm_dp_mst_topology_state *mst_state =to_dp_mst_topology_state (state); +struct drm_dp_vcpi_allocation *pos, *tmp; + +list_for_each_entry_safe (pos, tmp, &mst_state->vcpis, next) { +/* We only keep references to ports with non-zero VCPIs */
drivers/gpu/drm/drm_dp_mst_topology.c +6
-1+if (pos->vcpi) +drm_dp_put_port (pos->port); +kfree (pos); + }kfree (mst_state); } -static const struct drm_private_state_funcs mst_state_funcs = { +static inline int +drm_dp_mst_atomic_check_topology_state(struct
drm_dp_mst_topology_mgr
*mgr,drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ struct
drm_dp_mst_topology_state*mst_state)drivers/gpu/drm/drm_dp_mst_topology.c +6
-0+{ +struct drm_dp_vcpi_allocation *vcpi; +struct drm_dp_mst_port *port; +int avail_slots =63 , ret; + +/* There's no possible scenario where releasing VCPI or keeping it the
drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+* same would make the state invalid +*/ +if (!mst_state->vcpi_allocated) {Do we need this? If not I'm always in favour of removing code if it's not
needed :-)Quoted 23 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p allocates noVCPI,
check skipped\n",drivers/gpu/drm/drm_dp_mst_topology.c +6
-0+ mgr, &mst_state->base); +return 0 ; + } + +list_for_each_entry (vcpi, &mst_state->vcpis, next) { +/* Releasing VCPI is always OK-even if the port is gone */
drivers/gpu/drm/drm_dp_mst_topology.c +2
-0+if (!vcpi->vcpi) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] releases allVCPI
slots\n",drivers/gpu/drm/drm_dp_mst_topology.c +7
-0+ vcpi->port); +continue ; + } + + port =drm_dp_get_validated_port_ref (mgr, vcpi->port); +if (!port) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] is gone butstill has
VCPI, cannot add new VCPI\n",drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ vcpi->port); +return -EINVAL ; + }For
find_vcpiwe've already done this. For unplugged vcpi, we can't do this here I think - otherwise if you enable 2 mst outputs on the same branch, and unplug both, old non-atomic userspace can never ever again disable them because it can't disable both. The check (to skip already deallocated ports) above isn't enough to handle that.Quoted 152 lines
drivers/gpu/drm/drm_dp_mst_topology.c +2
-0+ + DRM_DEBUG_ATOMIC("[MST PORT:%p] requires %d vcpislots\n",
drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ vcpi->port, vcpi->vcpi); + + avail_slots -= vcpi->vcpi; +if (avail_slots <0 ) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] not enoughVCPI slots
in mst state %p (avail=%d)\n",drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+ vcpi->port, mst_state, + avail_slots + vcpi->vcpi); + ret = -ENOSPC ; + goto port_fail; + } + +drm_dp_put_port (port); + } + DRM_DEBUG_ATOMIC("[MST MGR:%p] mst state %p VCPI avail=%dused=%d\n",
drivers/gpu/drm/drm_dp_mst_topology.c +11
-0+ mgr, mst_state, avail_slots, + 63 - avail_slots); + +return 0 ; +port_fail: +drm_dp_put_port (port); +return ret; +} + +/** +* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
+ * atomic update is valid
+ * @state: Pointer to the new &structdrm_dp_mst_topology_state
+ *
+ * Checks the given topology state for an atomic update to ensure that
it's
+ * valid. This includes checking whether there's enough bandwidth to
support
+ * the new VCPI allocations in the atomic update.
+ *
+ * Any atomic drivers supporting DP MST must make sure to call this after
+ * checking the rest of their state in their
+ *&drm_mode_config_funcs.atomic_check()callback.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_atomic_release_vcpi_slots()
+ *
+ * Returns:
+ *
+ * 0 if the new state is valid, negative error code otherwise.
+ */drivers/gpu/drm/drm_dp_mst_topology.c +8
-0+int drm_dp_mst_atomic_check (struct drm_atomic_state *state) +{ +struct drm_dp_mst_topology_mgr *mgr; +struct drm_dp_mst_topology_state *mst_state; +int i, ret =0 ; + +for_each_new_mst_mgr_in_state (state, mgr, mst_state, i) { + ret = drm_dp_mst_atomic_check_topology_state(mgr,
mst_state);drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+if (ret) +break ; + } + +return ret; +} +EXPORT_SYMBOL (drm_dp_mst_atomic_check); + +const struct drm_private_state_funcs
drm_dp_mst_topology_state_funcs=
{drivers/gpu/drm/drm_dp_mst_topology.c +3
-4.atomic_duplicate_state = drm_dp_mst_duplicate_state, .atomic_destroy_state = drm_dp_mst_destroy_state, }; +EXPORT_SYMBOL (drm_dp_mst_topology_state_funcs);/** * drm_atomic_get_mst_topology_state: get MST topology state @@ -3216,13 +3444,11 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, return -ENOMEM ; mst_state->mgr = mgr; - -/* max. time slots - one slot for MTP header */ - mst_state->avail_slots =63 ; +INIT_LIST_HEAD (&mst_state->vcpis);drm_atomic_private_obj_init (&mgr->base, &mst_state->base, - &mst_state_funcs); + &drm_dp_mst_topology_state_funcs);return 0 ;}
drivers/gpu/drm/i915/intel_display.c +0
-0@@ -12592,6 +12592,10 @@ static int intel_atomic_check(struct
drm_device
*dev,drivers/gpu/drm/i915/intel_display.c +4
-0"[modeset]" : "[fastset]"); } + ret =drm_dp_mst_atomic_check (state); +if (ret) +return ret; +if (any_ms) { ret =intel_modeset_checks (state);drivers/gpu/drm/i915/intel_dp_mst.c +6
-1@@ -41,8 +41,13 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, struct drm_connector *connector = conn_state->connector;void *port =to_intel_connector (connector)->port;struct drm_atomic_state *state = pipe_config->base.state; +struct drm_crtc *crtc = pipe_config->base.crtc; +struct drm_crtc_state *old_crtc_state = +drm_atomic_get_old_crtc_state (state, crtc); +struct drm_crtc_state *new_crtc_state = &pipe_config->base;int bpp; -int lane_count, slots =0 ; +int lane_count, slots = +to_intel_crtc_state (old_crtc_state)->dp_m_n.tu;const struct drm_display_mode *adjusted_mode = &pipe_config- > base.adjusted_mode;drivers/gpu/drm/i915/intel_dp_mst.c +1
-2int mst_pbn; bool constant_n = drm_dp_has_quirk(&intel_dp->desc,@@ -77,8 +82,12 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, mst_pbn =drm_dp_calc_pbn_mode (adjusted_mode->crtc_clock, bpp); pipe_config->pbn = mst_pbn; -/* Zombie connectors can't have VCPI slots */ -if (!drm_connector_is_unregistered (connector)) { +/* Only change VCPI allocation on actual mode changes, to prevent us
drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+* from trying to allocate VCPI to ports that no longer exist when we
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 4 lines
drivers/gpu/drm/i915/intel_dp_mst.c +4
-0+* may just be trying to disable DPMS on them +*/ +if (new_crtc_state->mode_changed || + new_crtc_state->connectors_changed) {Why exactly is this needed? We only call
compute_configon the enocoder
when enabling an output. Not when disabling one. Similar for the atomic
helpers and the various encoder callbacks (you have the same change in the
nouveau code). Did this actually blow up while testing?Quoted 25 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-0slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp-Quoted 1 line
mst_mgr,drivers/gpu/drm/i915/intel_dp_mst.c +8
-5port,@@ -107,36 +116,39 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, return true; } -static int intel_dp_mst_atomic_check (struct drm_connector *connector, -struct drm_connector_state *new_conn_state) +static int +intel_dp_mst_atomic_check (struct drm_connector *connector, +struct drm_connector_state *new_conn_state) { +struct intel_connector *intel_connector = +to_intel_connector (connector); +struct drm_dp_mst_topology_mgr *mgr = + &intel_connector->mst_port->mst_mgr; +struct drm_dp_mst_port *port = intel_connector->port;struct drm_atomic_state *state = new_conn_state->state; -struct drm_connector_state *old_conn_state; -struct drm_crtc *old_crtc; -struct drm_crtc_state *crtc_state;drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 123
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +5
-3-int slots, ret =0 ; +struct drm_connector_state *old_conn_state = +drm_atomic_get_old_connector_state (state, connector); +struct drm_crtc *old_crtc = old_conn_state->crtc, + *new_crtc = new_conn_state->crtc; +struct drm_crtc_state *old_crtc_state, *new_crtc_state; - old_conn_state =drm_atomic_get_old_connector_state (state, connector); - old_crtc = old_conn_state->crtc;Bikeshed: Reworking all the above hides the real changes quite a bit.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 143
Quoted 12 lines
drivers/gpu/drm/i915/intel_dp_mst.c +3
-6if (!old_crtc) -return ret; +return 0 ; - crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); - slots =to_intel_crtc_state (crtc_state)->dp_m_n.tu; -if (drm_atomic_crtc_needs_modeset (crtc_state) && slots >0 ) { -struct drm_dp_mst_topology_mgr *mgr; -struct drm_encoder *old_encoder; + old_crtc_state =drm_atomic_get_old_crtc_state (state, old_crtc); + if (!old_crtc_state ||!old_crtc_state would be a framework bug. Just remove that check.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 145
Quoted 2 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ !to_intel_crtc_state(old_crtc_state)->dp_m_n.tu) +return 0 ;Hm, why do we need this? Having a DP port previously enabled with a 0 tu
sounds like a pretty big driver bug.drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 152
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +4
-2-old_encoder = old_conn_state->best_encoder; -mgr = &enc_to_mst(old_encoder)->primary->dp.mst_mgr; +/* If we switched CRTCs, clear the previous one's allocation */ + new_crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); +if (new_crtc_state->connectors_changed && !new_crtc_state- >enable) +to_intel_crtc_state (new_crtc_state)->dp_m_n.tu =0 ;No idea why we need to clear this here? Shouldn't we just clear this unconditionally if we release the allocation.
Also naming convention, I'd call this
old_crtc_new_state, and maybeold_crtc_old_state. To make it clear these are the old/new states forold_crtc. Instead of old/new state for the new crtc.Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-3- ret =drm_dp_atomic_release_vcpi_slots (state, mgr, slots); -if (ret) - DRM_DEBUG_KMS("failed releasing %d vcpi slots:%d\n",slots, ret);
drivers/gpu/drm/i915/intel_dp_mst.c +0
-2-else - to_intel_crtc_state(crtc_state)->dp_m_n.tu =0;
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 4 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-2- } -return ret; +if (new_crtc) +return 0 ;Not sure why we shouldn't release this? If we handle this in helpers, it
would remove piles of special casing code here I think. Plus remove special code in the hlpers.Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ +return drm_dp_atomic_release_vcpi_slots (state, mgr, port); } static void intel_mst_disable_dp(struct intel_encoder *encoder,include/drm/drm_dp_mst_helper.h +0
-0include/drm/drm_dp_mst_helper.h struct drm_dp_payload { · line 410
Quoted 5 lines
include/drm/drm_dp_mst_helper.h +1
-0@@ -406,9 +406,16 @@ struct drm_dp_payload {#define to_dp_mst_topology_state (x) container_of(x, struct drm_dp_mst_topology_state, base) +struct drm_dp_vcpi_allocation {I think a comment here that this pointer holds a full reference (even past
when the connector is unplugged) would be good. Otherwise the loop inrelease_vcpiwould be buggy.Quoted 46 lines
include/drm/drm_dp_mst_helper.h +7
-1+struct drm_dp_mst_port *port; +int vcpi; +struct list_head next; +}; +struct drm_dp_mst_topology_state {struct drm_private_state base; -int avail_slots; +struct list_head vcpis; +u8 vcpi_allocated;struct drm_dp_mst_topology_mgr *mgr; };@@ -619,14 +626,17 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);int drm_dp_mst_topology_mgr_resume (struct drm_dp_mst_topology_mgr *mgr);struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state (struct drm_atomic_state *state, struct
drm_dp_mst_topology_mgr*mgr);include/drm/drm_dp_mst_helper.h +0
-3-int drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, intpbn);
-int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state*state,include/drm/drm_dp_mst_helper.h +0
-1-struct drm_dp_mst_topology_mgr *mgr,
include/drm/drm_dp_mst_helper.h +8
-1- int slots); +int __must_check +drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port,int pbn); +int __must_check +drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port); int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr*mgr,
structdrm_dp_mst_port*port, boolpower_up);
+int__must_checkdrm_dp_mst_atomic_check(struct drm_atomic_state *state);Quoted 6 lines
extern const struct
drm_private_state_funcsdrm_dp_mst_topology_state_funcs;--
2.19.1--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Quoted 4 lines
Quoted 4 lines
--
Cheers,
Lyude Paul
--
Cheers,
Lyude Paul
_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH v6 3/6] drm/dp_mst: Start tracking per-port VCPI allocations
Quoted 133 lines · Lyude Paul
Quoted 126 lines · Daniel Vetter
Quoted 120 lines · Lyude Paul
Quoted 108 lines · Daniel Vetter
Quoted 82 lines · Daniel Vetter
Quoted 47 lines · Lyude Paul
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:/* We cannot rely on port->vcpi.num_slots to update * topology_state->avail_slots as the port may not exist ifthe parent
* branch device was unplugged. This should be fixed by
tracking
* per-port slot allocation indrm_dp_mst_topology_state
instead of
* depending on the caller to tell us how many slots to
release.*/That's not the only reason we should fix this: forcing the driver to track the VCPI allocations throughout a state's atomic check is error prone, because it means that extra care has to be taken with the
order thatdrm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()are called in in order to ensure idempotency. Currently the only driver actually using these helpers, i915, doesn't even do this correctly: multiple ->best_encoder() checks
with i915's current implementation would not be idempotent and would over-allocate VCPI slots, something I learned trying to implement fallback retraining in MST.So: simplify this whole mess, and teach
drm_dp_atomic_find_vcpi_slots()
anddrm_dp_atomic_release_vcpi_slots()to track the VCPI allocations for
each port. This allows us to ensure idempotency without having to rely
on the driver as much. Additionally: the driver doesn't need to do any
kind of VCPI slot tracking anymore if it doesn't need it for it's own
internal state.Additionally; this adds a new
drm_dp_mst_atomic_check()helper which must be used by atomic drivers to perform validity checks for the new
VCPI allocations incurred by a state.Also: update the documentation and make it more obvious that these /must/ be called by /all/ atomic drivers supporting MST.
Quoted 13 lines
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This
required
a good bit of changing to when we calldrm_dp_find_vcpi_slots(), mainly that we need to ensure that we only redo VCPI allocations
on
actual mode or CRTC changes, notcrtc_state->activechanges. Additionally, we no longer take the registration of the DRM
connector
for each port into account because so long as we have a kref to
the
port in the new or previous atomic state, the connector will stay registered.I write an entire pile of small nitpits (still included most of them below), until I realized this here wont work. Delaying the call to destroy
the connector (well, unregister it really) wreaks the design we've come up
with thus far, resulting in most of my comments here.Instead, all we need to do is delay the kfree(port) at the bottom of
drm_dp_destroy_port(). The vcpi allocation structure _only_ needs the pointer value to stay valid, as a lookup key. It doesn't care at all about
anything actually stored in there. So the only thing we need to delay is
the kfree. I think the simplest way to achieve this is to add a 2nd kref
(port->kfree_refor something like that), with on reference held by the
port itself (released indrm_dp_destroy_port), and the other one held as-needed by the vcpi allocation lists.Quoted 4 lines
I think if we go with this design instead of retrofitting a semantic change of the port lifetime itself, all the complications I complain about
below should disappear.In the above I meant
drm_dp_destroy_portordrm_dp_destroy_connector_work.Aside: I think creating a kref for the final kfree would also solve a bunch of other issues in a much neater way: In
commit
f038c5b99fc1332f558b495d136d4f433ee65caaAuthor: Lyude Paul <[email protected]> Date: Tue Nov 13 17:46:14 2018 -0500drm/dp_mst: Skip validating ports during destruction, just ref
we could use that kfree reference to make sure the port pointer is alive.
This of course means thatdrm_dp_update_payload_part1()would also need to
use the kfree reference for the vcpi allocations. And probably everywhere
else (e.g. in your nouveau series for payload information).Quoted 10 lines
That would give us a very clear separation overall between "port can still
be used because it's not yet unplugged" vs. "port only hangs around because a bunch of vcpi allocations and other payload things upstream of the port might still need the port lookup key to free resources". Instead
of again sprinkling complicated conditions and magic tricks all over the code to figure out whethe we should allow theget_validate_portto succeed
or not, or the modeset to succeed or not.Quoted 4 lines
mhm, I've been experimenting with this and I think I agree this is definitely
the right solution to go with. It'll end up decomplicating a lot of this :)Quoted 5 lines
Since it looks like we'll use the
kfree_refquite a bit, wrappers for that would be useful. Still not sure what to call those, best I could come up with is dp_port_malloc_get/put. Plus a comment explaining what it does. It's going to be a bit of wtf-is-this no matter what :-/ -Daniel
Quoted 7 lines
We could do this the other way around so it looks like this maybe
struct kref; /* manages
kfree()*/
structtopology_kref; /* corresonds to lifetime in topology */Then only expose functions for the normal kref to drivers, so that any possible confusion is still limited to
drm_dp_mst_topology.c
I like this bikeshed a lot. Since we need a bunch of the plain kref_get/put internally, probably still good to have a wrapper. For that the port_malloc_get/put() still sounds good to me - port_kfree_get/put sounds confusing, since it's not the kfree we're refcounting, but the memory allocation.
Another option would be to add _topology to the public get/put functions, but that makes for a fairly long function name :-/
-Daniel
Quoted 838 lines
Quoted 830 lines
Quoted 830 lines
Quoted 830 lines
-Daniel
Quoted 826 lines
Piles of comments below.
Cheers, Daniel
Quoted 177 lines
- Use the small changes to
drm_dp_put_port()to add even more error checking to make misusage of the helpers more obvious. I added
this
after having to chase down various use-after-free conditions that started popping up from the new helpers so no one else has to troubleshoot that.
- Move some accidentalDRM_DEBUG_KMS()calls toDRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both
not
be
called on the same port in a single atomic check phase (but
multiple
calls to one or the other is OK)Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI allocations happen in a state. This makes the next change I'm
about
to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure
that
when ports are destroyed and still have VCPI allocations in the topology state, the only state changes allowed are releasing said ports' VCPI. This prevents a state with a mix of VCPI allocations from destroyed ports, and allocations from valid ports.Changes since v3:
- Don't release VCPI allocations in the topology state immediately
in
drm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and
skip
over them indrm_dp_mst_duplicate_state(). This makes it sodrm_dp_atomic_release_vcpi_slots()is still idempotent while also throwing warnings if the driver messes up it's book keeping and
tries
to release VCPI slots on a port that doesn't have any pre-
existing
VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"Changes since v2:
- Usekmemdup()for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states indrm_dp_mst_atomic_check()so the driver doesn't have to do it
- Fix documentation indrm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into
it's
own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth requirements. This is needed because originally we relied on the state duplication functions to prune any stale ports from the new state, which would prevent us from incorrectly considering their bandwidth requirements alongside legitimate new payloads.
- Add function references indrm_dp_atomic_release_vcpi_slots()-
danvet
- Annotate atomic VCPI and atomic check functions with__must_check
- danvetChanges since v1:
- Don't use the now-removed ->atomic_check() for private objects
hook,
just give drivers a function to call themselvesSigned-off-by Lyude Paul <[email protected]>Cc Daniel Vetter <[email protected]>--- drivers/gpu/drm/drm_dp_mst_topology.c | 294 +++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 4 + drivers/gpu/drm/i915/intel_dp_mst.c | 64 +++--- include/drm/drm_dp_mst_helper.h | 24 ++- 4 files changed, 319 insertions(+), 67 deletions(-)drivers/gpu/drm/drm_dp_mst_topology.c +0
-1@@ -2617,21 +2617,42 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, }/** -* drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state
+ *drm_dp_atomic_find_vcpi_slots()- Find and add VCPI slots to the state
* @state: global atomic state
* @mgr: MST topology manager for the port
* @port: port to find vcpi slots for
* @pbn: bandwidth required for the mode in PBN
*
- * RETURNS:
- * Total slots in the atomic state assigned for this port or error
+ * Allocates VCPI slots to @port, replacing any previous VCPI allocations it
+ * may have had. Any atomic drivers which support MST must call this
function
+ * in their&drm_encoder_helper_funcs.atomic_check()callback to change
the
+ * current VCPI allocation for the new state, but only when
+ * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed
is set
+ * to ensure compatibility with userspace applications that still use
the
+ * legacy modesetting UAPI.
+ *
+ * Allocations set by this function are not checked against the bandwidth
+ * restraints of @mgr until the driver callsdrm_dp_mst_atomic_check().
+ *
+ * Additionally, it is OK to call this function multiple times on the
same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_release_vcpi_slots()in the same atomic check phase.
+ *
+ * See also:
+ *drm_dp_atomic_release_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * Total slots in the atomic state assigned for this port, or a negative error
+ * code if the port no longer exists
*/
int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state*state,drivers/gpu/drm/drm_dp_mst_topology.c +0
-0struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, intpbn)
{drivers/gpu/drm/drm_dp_mst_topology.c +2
-1struct drm_dp_mst_topology_state *topology_state; -int req_slots; +struct drm_dp_vcpi_allocation *pos, *vcpi =NULL ; +int prev_slots, req_slots, ret; topology_state = drm_atomic_get_mst_topology_state(state,mgr);
drivers/gpu/drm/drm_dp_mst_topology.c +10
-6if (IS_ERR (topology_state))@@ -2640,20 +2661,60 @@ int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state *state, port =drm_dp_get_validated_port_ref (mgr, port);if (port ==NULL )return -EINVAL ; - req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); -DRM_DEBUG_KMS ("vcpi slots req=%d, avail=%d\n" , - req_slots, topology_state->avail_slots); -if (req_slots > topology_state->avail_slots) { -drm_dp_put_port (port); -return -ENOSPC ; + topology_state->vcpi_allocated = true; + +/* Find the current allocation for this port, if any */ +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + vcpi = pos; + prev_slots = vcpi->vcpi; + +/* +* This should never happen, unless the driver tries
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+* releasing and allocating the same VCPI allocation,
drivers/gpu/drm/drm_dp_mst_topology.c +4
-0+* which is an error +*/ +if (WARN_ON (!prev_slots)) { + DRM_ERROR("cannot allocate and releaseVCPI on
[MST PORT:%p] in the same state\n",drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ port); +return -EINVAL ; + }Allowing atomic changes to output state (like disabling it on one crtc,
and then enabling on the other, thus both releasing and re-allocating the
vcpi in one go) is kinda the point of atomic. So definitely can't EINVAL
here.Sounds like we need more testcases in igt?
Quoted 140 lines
drivers/gpu/drm/drm_dp_mst_topology.c +5
-2+ +break ; + } } +if (!vcpi) + prev_slots =0 ; - topology_state->avail_slots -= req_slots; - DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state-Quoted 1 line
avail_slots);drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); + + DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d ->%d\n",
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ port->connector->base.id, port->connector-Quoted 1 line
name,
drivers/gpu/drm/drm_dp_mst_topology.c +22
-1+ port, prev_slots, req_slots); + +/* Add the new allocation to the state */ +if (!vcpi) { + vcpi =kzalloc (sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) { + ret = -ENOMEM ; + goto out; + } + vcpi->port =drm_dp_get_validated_port_ref (mgr, port); +if (!vcpi->port) { + ret = -EINVAL ; +kfree (vcpi); + goto out; + } +list_add (&vcpi->next, &topology_state->vcpis); + } + vcpi->vcpi = req_slots; + + ret = req_slots; +out:drm_dp_put_port (port); -return req_slots; +return ret; }EXPORT_SYMBOL (drm_dp_atomic_find_vcpi_slots);@@ -2661,31 +2722,66 @@EXPORT_SYMBOL (drm_dp_atomic_find_vcpi_slots); * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpislots
* @state: global atomic state
* @mgr: MST topology manager for the port
- * @slots: number of vcpi slots to release
+ * @port: The port to release the VCPI slots from
*
- * RETURNS:
- * 0 if @slots were added back to &drm_dp_mst_topology_state-Quoted 1 line
avail_slotsor- * negative error code
+ * Releases any VCPI slots that have been allocated to a port in the
atomic
+ * state. Any atomic drivers which support MST must call this function
in
+ * their&drm_connector_helper_funcs.atomic_check()callback when the
+ * connector will no longer have VCPI allocated (e.g. because it's CRTC
was
+ * removed) when it had VCPI allocated in the previous atomic state.
+ *
+ * It is OK to call this even if @port has been removed from the system.
+ * Additionally, it is OK to call this function multiple times on the
same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_find_vcpi_slots()on the same @port in a single atomic
check
+ * phase.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * 0 if all slots for this port were added back to
+ * &drm_dp_mst_topology_state.avail_slots or negative error code */
int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state
*state,
structdrm_dp_mst_topology_mgr
*mgr,drivers/gpu/drm/drm_dp_mst_topology.c +3
-1- int slots) + struct drm_dp_mst_port *port) {struct drm_dp_mst_topology_state *topology_state; +struct drm_dp_vcpi_allocation *pos; +bool found = false; topology_state = drm_atomic_get_mst_topology_state(state,mgr);
drivers/gpu/drm/drm_dp_mst_topology.c +0
-2if (IS_ERR (topology_state))return PTR_ERR (topology_state); -/* We cannot rely on port->vcpi.num_slots to update -* topology_state->avail_slots as the port may not exist if the parent
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* branch device was unplugged. This should be fixed by tracking
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* per-port slot allocation in drm_dp_mst_topology_state instead of
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* depending on the caller to tell us how many slots to release.
drivers/gpu/drm/drm_dp_mst_topology.c +8
-4-*/ - topology_state->avail_slots += slots; -DRM_DEBUG_KMS ("vcpi slots released=%d, avail=%d\n" , - slots, topology_state->avail_slots); +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + found = true; +break ; + } + } +if (WARN_ON (!found)) { + DRM_ERROR("no VCPI for [MST PORT:%p] found in mststate %p\n",
drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ port, &topology_state->base); +return -EINVAL ; + } + + DRM_DEBUG_ATOMIC("[MST PORT:%p] VCPI %d -> 0\n", port, pos-Quoted 1 line
vcpi);
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+if (pos->vcpi) { +/* +* This should be safe-as the previous state will still hold a
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+* reference to this port until we swap states. If it's not,
drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+* the driver has a bookkeeping error +*/ +if (WARN_ON (drm_dp_put_port (port))) {The usual way to do this (instead of adding a return value to the normal
_putfunction) is something like
kref_put(port, function_that_with_just_a_WARNING_DRM_ERROR)avoids the need for patch 2.
Quoted 96 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_ERROR("cannot allocate and release VCPI on[MST
PORT:%p] in the same state\n",drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ port); +return -EINVAL ; + } + pos->vcpi =0 ; + }return 0 ; }@@ -3115,15 +3211,45 @@ static void
drm_dp_destroy_connector_work(struct work_struct *work)
static structdrm_private_state*drm_dp_mst_duplicate_state(struct drm_private_obj *obj){drivers/gpu/drm/drm_dp_mst_topology.c +4
-2-struct drm_dp_mst_topology_state *state; +struct drm_dp_mst_topology_state *state, *old_state = +to_dp_mst_topology_state (obj->state); +struct drm_dp_vcpi_allocation *pos, *vcpi; - state =kmemdup (obj->state,sizeof (*state),GFP_KERNEL ); + state =kmemdup (old_state,sizeof (*state),GFP_KERNEL );if (!state)return NULL ; __drm_atomic_helper_private_obj_duplicate_state(obj, &state-Quoted 1 line
base);
drivers/gpu/drm/drm_dp_mst_topology.c +13
-0+INIT_LIST_HEAD (&state->vcpis); + state->vcpi_allocated = false; + +list_for_each_entry (pos, &old_state->vcpis, next) { +/* Prune leftover freed VCPI allocations */ +if (!pos->vcpi) +continue ; + + vcpi =kmemdup (pos,sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) + goto fail; + +/* Take a plain old kref here and don't validate the port, as
drivers/gpu/drm/drm_dp_mst_topology.c +19
-0+* it's topology might not even exist anymore +*/ +kref_get (&vcpi->port->kref); +list_add (&vcpi->next, &state->vcpis); + } +return &state->base; + +fail: +list_for_each_entry_safe (pos, vcpi, &state->vcpis, next) { +drm_dp_put_port (pos->port); +kfree (pos); + } +kfree (state); + +return NULL ; } static void drm_dp_mst_destroy_state(struct drm_private_obj *obj,@@ -3131,14 +3257,116 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, {struct drm_dp_mst_topology_state *mst_state =to_dp_mst_topology_state (state); +struct drm_dp_vcpi_allocation *pos, *tmp; + +list_for_each_entry_safe (pos, tmp, &mst_state->vcpis, next) { +/* We only keep references to ports with non-zero VCPIs */
drivers/gpu/drm/drm_dp_mst_topology.c +6
-1+if (pos->vcpi) +drm_dp_put_port (pos->port); +kfree (pos); + }kfree (mst_state); } -static const struct drm_private_state_funcs mst_state_funcs = { +static inline int +drm_dp_mst_atomic_check_topology_state(struct
drm_dp_mst_topology_mgr
*mgr,drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ struct
drm_dp_mst_topology_state*mst_state)drivers/gpu/drm/drm_dp_mst_topology.c +6
-0+{ +struct drm_dp_vcpi_allocation *vcpi; +struct drm_dp_mst_port *port; +int avail_slots =63 , ret; + +/* There's no possible scenario where releasing VCPI or keeping it the
drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+* same would make the state invalid +*/ +if (!mst_state->vcpi_allocated) {Do we need this? If not I'm always in favour of removing code if it's not
needed :-)Quoted 23 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p allocates noVCPI,
check skipped\n",drivers/gpu/drm/drm_dp_mst_topology.c +6
-0+ mgr, &mst_state->base); +return 0 ; + } + +list_for_each_entry (vcpi, &mst_state->vcpis, next) { +/* Releasing VCPI is always OK-even if the port is gone */
drivers/gpu/drm/drm_dp_mst_topology.c +2
-0+if (!vcpi->vcpi) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] releases allVCPI
slots\n",drivers/gpu/drm/drm_dp_mst_topology.c +7
-0+ vcpi->port); +continue ; + } + + port =drm_dp_get_validated_port_ref (mgr, vcpi->port); +if (!port) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] is gone butstill has
VCPI, cannot add new VCPI\n",drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ vcpi->port); +return -EINVAL ; + }For
find_vcpiwe've already done this. For unplugged vcpi, we can't do this here I think - otherwise if you enable 2 mst outputs on the same branch, and unplug both, old non-atomic userspace can never ever again disable them because it can't disable both. The check (to skip already deallocated ports) above isn't enough to handle that.Quoted 152 lines
drivers/gpu/drm/drm_dp_mst_topology.c +2
-0+ + DRM_DEBUG_ATOMIC("[MST PORT:%p] requires %d vcpislots\n",
drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ vcpi->port, vcpi->vcpi); + + avail_slots -= vcpi->vcpi; +if (avail_slots <0 ) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] not enoughVCPI slots
in mst state %p (avail=%d)\n",drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+ vcpi->port, mst_state, + avail_slots + vcpi->vcpi); + ret = -ENOSPC ; + goto port_fail; + } + +drm_dp_put_port (port); + } + DRM_DEBUG_ATOMIC("[MST MGR:%p] mst state %p VCPI avail=%dused=%d\n",
drivers/gpu/drm/drm_dp_mst_topology.c +11
-0+ mgr, mst_state, avail_slots, + 63 - avail_slots); + +return 0 ; +port_fail: +drm_dp_put_port (port); +return ret; +} + +/** +* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
+ * atomic update is valid
+ * @state: Pointer to the new &structdrm_dp_mst_topology_state
+ *
+ * Checks the given topology state for an atomic update to ensure that
it's
+ * valid. This includes checking whether there's enough bandwidth to
support
+ * the new VCPI allocations in the atomic update.
+ *
+ * Any atomic drivers supporting DP MST must make sure to call this after
+ * checking the rest of their state in their
+ *&drm_mode_config_funcs.atomic_check()callback.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_atomic_release_vcpi_slots()
+ *
+ * Returns:
+ *
+ * 0 if the new state is valid, negative error code otherwise.
+ */drivers/gpu/drm/drm_dp_mst_topology.c +8
-0+int drm_dp_mst_atomic_check (struct drm_atomic_state *state) +{ +struct drm_dp_mst_topology_mgr *mgr; +struct drm_dp_mst_topology_state *mst_state; +int i, ret =0 ; + +for_each_new_mst_mgr_in_state (state, mgr, mst_state, i) { + ret = drm_dp_mst_atomic_check_topology_state(mgr,
mst_state);drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+if (ret) +break ; + } + +return ret; +} +EXPORT_SYMBOL (drm_dp_mst_atomic_check); + +const struct drm_private_state_funcs
drm_dp_mst_topology_state_funcs=
{drivers/gpu/drm/drm_dp_mst_topology.c +3
-4.atomic_duplicate_state = drm_dp_mst_duplicate_state, .atomic_destroy_state = drm_dp_mst_destroy_state, }; +EXPORT_SYMBOL (drm_dp_mst_topology_state_funcs);/** * drm_atomic_get_mst_topology_state: get MST topology state @@ -3216,13 +3444,11 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, return -ENOMEM ; mst_state->mgr = mgr; - -/* max. time slots - one slot for MTP header */ - mst_state->avail_slots =63 ; +INIT_LIST_HEAD (&mst_state->vcpis);drm_atomic_private_obj_init (&mgr->base, &mst_state->base, - &mst_state_funcs); + &drm_dp_mst_topology_state_funcs);return 0 ;}
drivers/gpu/drm/i915/intel_display.c +0
-0@@ -12592,6 +12592,10 @@ static int intel_atomic_check(struct
drm_device
*dev,drivers/gpu/drm/i915/intel_display.c +4
-0"[modeset]" : "[fastset]"); } + ret =drm_dp_mst_atomic_check (state); +if (ret) +return ret; +if (any_ms) { ret =intel_modeset_checks (state);drivers/gpu/drm/i915/intel_dp_mst.c +6
-1@@ -41,8 +41,13 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, struct drm_connector *connector = conn_state->connector;void *port =to_intel_connector (connector)->port;struct drm_atomic_state *state = pipe_config->base.state; +struct drm_crtc *crtc = pipe_config->base.crtc; +struct drm_crtc_state *old_crtc_state = +drm_atomic_get_old_crtc_state (state, crtc); +struct drm_crtc_state *new_crtc_state = &pipe_config->base;int bpp; -int lane_count, slots =0 ; +int lane_count, slots = +to_intel_crtc_state (old_crtc_state)->dp_m_n.tu;const struct drm_display_mode *adjusted_mode = &pipe_config- > base.adjusted_mode;drivers/gpu/drm/i915/intel_dp_mst.c +1
-2int mst_pbn; bool constant_n = drm_dp_has_quirk(&intel_dp->desc,@@ -77,8 +82,12 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, mst_pbn =drm_dp_calc_pbn_mode (adjusted_mode->crtc_clock, bpp); pipe_config->pbn = mst_pbn; -/* Zombie connectors can't have VCPI slots */ -if (!drm_connector_is_unregistered (connector)) { +/* Only change VCPI allocation on actual mode changes, to prevent us
drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+* from trying to allocate VCPI to ports that no longer exist when we
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 4 lines
drivers/gpu/drm/i915/intel_dp_mst.c +4
-0+* may just be trying to disable DPMS on them +*/ +if (new_crtc_state->mode_changed || + new_crtc_state->connectors_changed) {Why exactly is this needed? We only call
compute_configon the enocoder
when enabling an output. Not when disabling one. Similar for the atomic
helpers and the various encoder callbacks (you have the same change in the
nouveau code). Did this actually blow up while testing?Quoted 25 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-0slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp-Quoted 1 line
mst_mgr,drivers/gpu/drm/i915/intel_dp_mst.c +8
-5port,@@ -107,36 +116,39 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, return true; } -static int intel_dp_mst_atomic_check (struct drm_connector *connector, -struct drm_connector_state *new_conn_state) +static int +intel_dp_mst_atomic_check (struct drm_connector *connector, +struct drm_connector_state *new_conn_state) { +struct intel_connector *intel_connector = +to_intel_connector (connector); +struct drm_dp_mst_topology_mgr *mgr = + &intel_connector->mst_port->mst_mgr; +struct drm_dp_mst_port *port = intel_connector->port;struct drm_atomic_state *state = new_conn_state->state; -struct drm_connector_state *old_conn_state; -struct drm_crtc *old_crtc; -struct drm_crtc_state *crtc_state;drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 123
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +5
-3-int slots, ret =0 ; +struct drm_connector_state *old_conn_state = +drm_atomic_get_old_connector_state (state, connector); +struct drm_crtc *old_crtc = old_conn_state->crtc, + *new_crtc = new_conn_state->crtc; +struct drm_crtc_state *old_crtc_state, *new_crtc_state; - old_conn_state =drm_atomic_get_old_connector_state (state, connector); - old_crtc = old_conn_state->crtc;Bikeshed: Reworking all the above hides the real changes quite a bit.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 143
Quoted 12 lines
drivers/gpu/drm/i915/intel_dp_mst.c +3
-6if (!old_crtc) -return ret; +return 0 ; - crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); - slots =to_intel_crtc_state (crtc_state)->dp_m_n.tu; -if (drm_atomic_crtc_needs_modeset (crtc_state) && slots >0 ) { -struct drm_dp_mst_topology_mgr *mgr; -struct drm_encoder *old_encoder; + old_crtc_state =drm_atomic_get_old_crtc_state (state, old_crtc); + if (!old_crtc_state ||!old_crtc_state would be a framework bug. Just remove that check.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 145
Quoted 2 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ !to_intel_crtc_state(old_crtc_state)->dp_m_n.tu) +return 0 ;Hm, why do we need this? Having a DP port previously enabled with a 0 tu
sounds like a pretty big driver bug.drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 152
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +4
-2-old_encoder = old_conn_state->best_encoder; -mgr = &enc_to_mst(old_encoder)->primary->dp.mst_mgr; +/* If we switched CRTCs, clear the previous one's allocation */ + new_crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); +if (new_crtc_state->connectors_changed && !new_crtc_state- >enable) +to_intel_crtc_state (new_crtc_state)->dp_m_n.tu =0 ;No idea why we need to clear this here? Shouldn't we just clear this unconditionally if we release the allocation.
Also naming convention, I'd call this
old_crtc_new_state, and maybeold_crtc_old_state. To make it clear these are the old/new states forold_crtc. Instead of old/new state for the new crtc.Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-3- ret =drm_dp_atomic_release_vcpi_slots (state, mgr, slots); -if (ret) - DRM_DEBUG_KMS("failed releasing %d vcpi slots:%d\n",slots, ret);
drivers/gpu/drm/i915/intel_dp_mst.c +0
-2-else - to_intel_crtc_state(crtc_state)->dp_m_n.tu =0;
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 4 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-2- } -return ret; +if (new_crtc) +return 0 ;Not sure why we shouldn't release this? If we handle this in helpers, it
would remove piles of special casing code here I think. Plus remove special code in the hlpers.Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ +return drm_dp_atomic_release_vcpi_slots (state, mgr, port); } static void intel_mst_disable_dp(struct intel_encoder *encoder,include/drm/drm_dp_mst_helper.h +0
-0include/drm/drm_dp_mst_helper.h struct drm_dp_payload { · line 410
Quoted 5 lines
include/drm/drm_dp_mst_helper.h +1
-0@@ -406,9 +406,16 @@ struct drm_dp_payload {#define to_dp_mst_topology_state (x) container_of(x, struct drm_dp_mst_topology_state, base) +struct drm_dp_vcpi_allocation {I think a comment here that this pointer holds a full reference (even past
when the connector is unplugged) would be good. Otherwise the loop inrelease_vcpiwould be buggy.Quoted 46 lines
include/drm/drm_dp_mst_helper.h +7
-1+struct drm_dp_mst_port *port; +int vcpi; +struct list_head next; +}; +struct drm_dp_mst_topology_state {struct drm_private_state base; -int avail_slots; +struct list_head vcpis; +u8 vcpi_allocated;struct drm_dp_mst_topology_mgr *mgr; };@@ -619,14 +626,17 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);int drm_dp_mst_topology_mgr_resume (struct drm_dp_mst_topology_mgr *mgr);struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state (struct drm_atomic_state *state, struct
drm_dp_mst_topology_mgr*mgr);include/drm/drm_dp_mst_helper.h +0
-3-int drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, intpbn);
-int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state*state,include/drm/drm_dp_mst_helper.h +0
-1-struct drm_dp_mst_topology_mgr *mgr,
include/drm/drm_dp_mst_helper.h +8
-1- int slots); +int __must_check +drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port,int pbn); +int __must_check +drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port); int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr*mgr,
structdrm_dp_mst_port*port, boolpower_up);
+int__must_checkdrm_dp_mst_atomic_check(struct drm_atomic_state *state);Quoted 6 lines
extern const struct
drm_private_state_funcsdrm_dp_mst_topology_state_funcs;--
2.19.1--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.chQuoted 4 lines
Quoted 4 lines
--
Cheers,
Lyude Paul--
Cheers,
Lyude Paul
Signature · 7 lines
Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ dri-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/dri-devel
A message that is not in the archive (sent off-list or not yet received): 20181128081708.GG4266-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org
Re: [PATCH v6 3/6] drm/dp_mst: Start tracking per-port VCPI allocations
Quoted 175 lines · Daniel Vetter
Quoted 166 lines · Lyude Paul
Quoted 158 lines · Daniel Vetter
Quoted 151 lines · Lyude Paul
Quoted 137 lines · Daniel Vetter
Quoted 107 lines · Daniel Vetter
Quoted 69 lines · Lyude Paul
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:/* We cannot rely on port->vcpi.num_slots to update * topology_state->avail_slots as the port may not exist ifthe parent
* branch device was unplugged. This should be fixed by
tracking
* per-port slot allocation indrm_dp_mst_topology_state
instead of
* depending on the caller to tell us how many slots to
release.*/That's not the only reason we should fix this: forcing the driver to
track the VCPI allocations throughout a state's atomic check is error prone, because it means that extra care has to be taken with
the
order thatdrm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()are called in in order to ensure
idempotency. Currently the only driver actually using these helpers,
i915, doesn't even do this correctly: multiple ->best_encoder() checks
with i915's current implementation would not be idempotent and would
over-allocate VCPI slots, something I learned trying to implement
fallback retraining in MST.So: simplify this whole mess, and teach
drm_dp_atomic_find_vcpi_slots()
anddrm_dp_atomic_release_vcpi_slots()to track the VCPI allocations
for
each port. This allows us to ensure idempotency without having to
rely
on the driver as much. Additionally: the driver doesn't need to do
any
kind of VCPI slot tracking anymore if it doesn't need it for it's
own
internal state.Additionally; this adds a new
drm_dp_mst_atomic_check()helper which
must be used by atomic drivers to perform validity checks for the
new
VCPI allocations incurred by a state.Also: update the documentation and make it more obvious that these
/must/ be called by /all/ atomic drivers supporting MST.Changes since v6:
- Keep a kref to all of the ports we have allocations on. This
required
a good bit of changing to when we calldrm_dp_find_vcpi_slots(),
mainly that we need to ensure that we only redo VCPI
allocations
on
actual mode or CRTC changes, notcrtc_state->activechanges.Quoted 8 lines
Additionally, we no longer take the registration of the DRM
connector
for each port into account because so long as we have a kref
to
the
port in the new or previous atomic state, the connector will
stay
registered.I write an entire pile of small nitpits (still included most of them
below), until I realized this here wont work. Delaying the call to destroy
the connector (well, unregister it really) wreaks the design we've come up
with thus far, resulting in most of my comments here.Instead, all we need to do is delay the kfree(port) at the bottom of
drm_dp_destroy_port(). The vcpi allocation structure _only_ needs the
pointer value to stay valid, as a lookup key. It doesn't care at all
about
anything actually stored in there. So the only thing we need to delay
is
the kfree. I think the simplest way to achieve this is to add a 2nd
kref
(port->kfree_refor something like that), with on reference held by
the
port itself (released indrm_dp_destroy_port), and the other one held
as-needed by the vcpi allocation lists.Quoted 6 lines
I think if we go with this design instead of retrofitting a semantic
change of the port lifetime itself, all the complications I complain
about
below should disappear.In the above I meant
drm_dp_destroy_portordrm_dp_destroy_connector_work.Aside: I think creating a kref for the final kfree would also solve a
bunch of other issues in a much neater way: Incommit
f038c5b99fc1332f558b495d136d4f433ee65caaAuthor: Lyude Paul <[email protected]> Date: Tue Nov 13 17:46:14 2018 -0500drm/dp_mst: Skip validating ports during destruction, just ref
we could use that kfree reference to make sure the port pointer is alive.
This of course means thatdrm_dp_update_payload_part1()would also need
to
use the kfree reference for the vcpi allocations. And probably everywhere
else (e.g. in your nouveau series for payload information).Quoted 12 lines
That would give us a very clear separation overall between "port can still
be used because it's not yet unplugged" vs. "port only hangs around because a bunch of vcpi allocations and other payload things upstream of
the port might still need the port lookup key to free resources". Instead
of again sprinkling complicated conditions and magic tricks all over the
code to figure out whethe we should allow theget_validate_portto succeed
or not, or the modeset to succeed or not.Quoted 5 lines
mhm, I've been experimenting with this and I think I agree this is definitely
the right solution to go with. It'll end up decomplicating a lot of this
:)Quoted 6 lines
Since it looks like we'll use the
kfree_refquite a bit, wrappers for that
would be useful. Still not sure what to call those, best I could come up with is dp_port_malloc_get/put. Plus a comment explaining what it does. It's going to be a bit of wtf-is-this no matter what :-/ -DanielQuoted 7 lines
We could do this the other way around so it looks like this maybe
struct kref; /* manages
kfree()*/
structtopology_kref; /* corresonds to lifetime in topology */Then only expose functions for the normal kref to drivers, so that any possible confusion is still limited to
drm_dp_mst_topology.c
Quoted 8 lines
I like this bikeshed a lot. Since we need a bunch of the plain kref_get/put internally, probably still good to have a wrapper. For that the port_malloc_get/put() still sounds good to me - port_kfree_get/put sounds confusing, since it's not the kfree we're refcounting, but the memory allocation.
Another option would be to add
_topologyto the public get/put functions, but that makes for a fairly long function name :-/
Well if we exposed get/put functions for this publically, I'd probably just call them drm_dp_get_port() and drm_dp_put_port() since it wouldn't make sense to have differentiated naming if we're only exposing one of the krefs publically (e.g. the malloc kref).
Also: I've been thinking about shortening some of the names of the MST helper functions for a while. I don't think we need to say drm_dp_mst_topology_mgr everywhere :)
Quoted 904 lines
-Daniel
Quoted 902 lines
Quoted 894 lines
Quoted 894 lines
Quoted 894 lines
-Daniel
Quoted 892 lines
Piles of comments below.
Cheers, Daniel
Quoted 207 lines
- Use the small changes to
drm_dp_put_port()to add even more
error
checking to make misusage of the helpers more obvious. I
added
this
after having to chase down various use-after-free conditions
that
started popping up from the new helpers so no one else has to troubleshoot that.
- Move some accidentalDRM_DEBUG_KMS()calls toDRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should
both
not
be
called on the same port in a single atomic check phase (but
multiple
calls to one or the other is OK)Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new
VCPI
allocations happen in a state. This makes the next change I'm
about
to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead
ensure
that
when ports are destroyed and still have VCPI allocations in
the
topology state, the only state changes allowed are releasing
said
ports' VCPI. This prevents a state with a mix of VCPI
allocations
from destroyed ports, and allocations from valid ports.Changes since v3:
- Don't release VCPI allocations in the topology state
immediately
in
drm_dp_atomic_release_vcpi_slots(), instead mark them as 0
and
skip
over them indrm_dp_mst_duplicate_state(). This makes it sodrm_dp_atomic_release_vcpi_slots()is still idempotent while
also
throwing warnings if the driver messes up it's book keeping
and
tries
to release VCPI slots on a port that doesn't have any pre-
existing
VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst
state"Changes since v2:
- Usekmemdup()for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states indrm_dp_mst_atomic_check()so the driver doesn't have to do it
- Fix documentation indrm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into
it's
own function, reduces indenting
- Don't consider "stale" MST ports when calculating the
bandwidth
requirements. This is needed because originally we relied on
the
state duplication functions to prune any stale ports from the
new
state, which would prevent us from incorrectly considering
their
bandwidth requirements alongside legitimate new payloads.
- Add function references indrm_dp_atomic_release_vcpi_slots()
-
danvet
- Annotate atomic VCPI and atomic check functions with__must_check
- danvetChanges since v1:
- Don't use the now-removed ->atomic_check() for private
objects
hook,
just give drivers a function to call themselvesSigned-off-by Lyude Paul <[email protected]>Cc Daniel Vetter <[email protected]>--- drivers/gpu/drm/drm_dp_mst_topology.c | 294 +++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 4 + drivers/gpu/drm/i915/intel_dp_mst.c | 64 +++--- include/drm/drm_dp_mst_helper.h | 24 ++- 4 files changed, 319 insertions(+), 67 deletions(-)drivers/gpu/drm/drm_dp_mst_topology.c +0
-1@@ -2617,21 +2617,42 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, }/** -* drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the
state
+ *drm_dp_atomic_find_vcpi_slots()- Find and add VCPI slots to the
state
* @state: global atomic state
* @mgr: MST topology manager for the port
* @port: port to find vcpi slots for
* @pbn: bandwidth required for the mode in PBN
*
- * RETURNS:
- * Total slots in the atomic state assigned for this port or error
+ * Allocates VCPI slots to @port, replacing any previous VCPI allocations it
+ * may have had. Any atomic drivers which support MST must call this
function
+ * in their&drm_encoder_helper_funcs.atomic_check()callback to
change
the
+ * current VCPI allocation for the new state, but only when
+ * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed
is set
+ * to ensure compatibility with userspace applications that still
use
the
+ * legacy modesetting UAPI.
+ *
+ * Allocations set by this function are not checked against the bandwidth
+ * restraints of @mgr until the driver callsdrm_dp_mst_atomic_check().
+ *
+ * Additionally, it is OK to call this function multiple times on
the
same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_release_vcpi_slots()in the same atomic check phase.
+ *
+ * See also:
+ *drm_dp_atomic_release_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * Total slots in the atomic state assigned for this port, or a negative error
+ * code if the port no longer exists
*/
int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state
*state,drivers/gpu/drm/drm_dp_mst_topology.c +0
-0struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, intpbn)
{drivers/gpu/drm/drm_dp_mst_topology.c +2
-1struct drm_dp_mst_topology_state *topology_state; -int req_slots; +struct drm_dp_vcpi_allocation *pos, *vcpi =NULL ; +int prev_slots, req_slots, ret; topology_state = drm_atomic_get_mst_topology_state(state,mgr);
drivers/gpu/drm/drm_dp_mst_topology.c +10
-6if (IS_ERR (topology_state))@@ -2640,20 +2661,60 @@ int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state *state, port =drm_dp_get_validated_port_ref (mgr, port);if (port ==NULL )return -EINVAL ; - req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); -DRM_DEBUG_KMS ("vcpi slots req=%d, avail=%d\n" , - req_slots, topology_state->avail_slots); -if (req_slots > topology_state->avail_slots) { -drm_dp_put_port (port); -return -ENOSPC ; + topology_state->vcpi_allocated = true; + +/* Find the current allocation for this port, if any */ +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + vcpi = pos; + prev_slots = vcpi->vcpi; + +/* +* This should never happen, unless the driver tries
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+* releasing and allocating the same VCPI allocation,
drivers/gpu/drm/drm_dp_mst_topology.c +4
-0+* which is an error +*/ +if (WARN_ON (!prev_slots)) { + DRM_ERROR("cannot allocate and releaseVCPI on
[MST PORT:%p] in the same state\n",drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ port); +return -EINVAL ; + }Allowing atomic changes to output state (like disabling it on one crtc,
and then enabling on the other, thus both releasing and re- allocating
the
vcpi in one go) is kinda the point of atomic. So definitely can't EINVAL
here.Sounds like we need more testcases in igt?
Quoted 148 lines
drivers/gpu/drm/drm_dp_mst_topology.c +5
-2+ +break ; + } } +if (!vcpi) + prev_slots =0 ; - topology_state->avail_slots -= req_slots; - DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state-Quoted 1 line
avail_slots);drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); + + DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d ->%d\n",
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ port->connector->base.id, port->connector-Quoted 1 line
name,
drivers/gpu/drm/drm_dp_mst_topology.c +22
-1+ port, prev_slots, req_slots); + +/* Add the new allocation to the state */ +if (!vcpi) { + vcpi =kzalloc (sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) { + ret = -ENOMEM ; + goto out; + } + vcpi->port =drm_dp_get_validated_port_ref (mgr, port); +if (!vcpi->port) { + ret = -EINVAL ; +kfree (vcpi); + goto out; + } +list_add (&vcpi->next, &topology_state->vcpis); + } + vcpi->vcpi = req_slots; + + ret = req_slots; +out:drm_dp_put_port (port); -return req_slots; +return ret; }EXPORT_SYMBOL (drm_dp_atomic_find_vcpi_slots);@@ -2661,31 +2722,66 @@EXPORT_SYMBOL (drm_dp_atomic_find_vcpi_slots); * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpislots
* @state: global atomic state
* @mgr: MST topology manager for the port
- * @slots: number of vcpi slots to release
+ * @port: The port to release the VCPI slots from
*
- * RETURNS:
- * 0 if @slots were added back to &drm_dp_mst_topology_state-Quoted 1 line
avail_slotsor- * negative error code
+ * Releases any VCPI slots that have been allocated to a port in
the
atomic
+ * state. Any atomic drivers which support MST must call this function
in
+ * their&drm_connector_helper_funcs.atomic_check()callback when
the
+ * connector will no longer have VCPI allocated (e.g. because it's
CRTC
was
+ * removed) when it had VCPI allocated in the previous atomic state.
+ *
+ * It is OK to call this even if @port has been removed from the
system.
+ * Additionally, it is OK to call this function multiple times on
the
same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_find_vcpi_slots()on the same @port in a single
atomic
check
+ * phase.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * 0 if all slots for this port were added back to
+ * &drm_dp_mst_topology_state.avail_slots or negative error code
*/
int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state
*state,
structdrm_dp_mst_topology_mgr
*mgr,drivers/gpu/drm/drm_dp_mst_topology.c +3
-1- int slots) + struct drm_dp_mst_port *port) {struct drm_dp_mst_topology_state *topology_state; +struct drm_dp_vcpi_allocation *pos; +bool found = false; topology_state = drm_atomic_get_mst_topology_state(state,mgr);
drivers/gpu/drm/drm_dp_mst_topology.c +0
-2if (IS_ERR (topology_state))return PTR_ERR (topology_state); -/* We cannot rely on port->vcpi.num_slots to update -* topology_state->avail_slots as the port may not exist if the parent
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* branch device was unplugged. This should be fixed by tracking
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* per-port slot allocation in drm_dp_mst_topology_state instead of
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* depending on the caller to tell us how many slots to release.
drivers/gpu/drm/drm_dp_mst_topology.c +8
-4-*/ - topology_state->avail_slots += slots; -DRM_DEBUG_KMS ("vcpi slots released=%d, avail=%d\n" , - slots, topology_state->avail_slots); +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + found = true; +break ; + } + } +if (WARN_ON (!found)) { + DRM_ERROR("no VCPI for [MST PORT:%p] found in mststate %p\n",
drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ port, &topology_state->base); +return -EINVAL ; + } + + DRM_DEBUG_ATOMIC("[MST PORT:%p] VCPI %d -> 0\n", port, pos-Quoted 1 line
vcpi);
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+if (pos->vcpi) { +/* +* This should be safe-as the previous state will still hold a
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+* reference to this port until we swap states. If it's not,
drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+* the driver has a bookkeeping error +*/ +if (WARN_ON (drm_dp_put_port (port))) {The usual way to do this (instead of adding a return value to the normal
_putfunction) is something like
kref_put(port, function_that_with_just_a_WARNING_DRM_ERROR)avoids the need for patch 2.
Quoted 98 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_ERROR("cannot allocate and release VCPI on[MST
PORT:%p] in the same state\n",drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ port); +return -EINVAL ; + } + pos->vcpi =0 ; + }return 0 ; }@@ -3115,15 +3211,45 @@ static void
drm_dp_destroy_connector_work(struct work_struct *work)
static structdrm_private_state*drm_dp_mst_duplicate_state(struct drm_private_obj *obj){drivers/gpu/drm/drm_dp_mst_topology.c +4
-2-struct drm_dp_mst_topology_state *state; +struct drm_dp_mst_topology_state *state, *old_state = +to_dp_mst_topology_state (obj->state); +struct drm_dp_vcpi_allocation *pos, *vcpi; - state =kmemdup (obj->state,sizeof (*state),GFP_KERNEL ); + state =kmemdup (old_state,sizeof (*state),GFP_KERNEL );if (!state)return NULL ; __drm_atomic_helper_private_obj_duplicate_state(obj, &state-Quoted 1 line
base);
drivers/gpu/drm/drm_dp_mst_topology.c +13
-0+INIT_LIST_HEAD (&state->vcpis); + state->vcpi_allocated = false; + +list_for_each_entry (pos, &old_state->vcpis, next) { +/* Prune leftover freed VCPI allocations */ +if (!pos->vcpi) +continue ; + + vcpi =kmemdup (pos,sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) + goto fail; + +/* Take a plain old kref here and don't validate the port, as
drivers/gpu/drm/drm_dp_mst_topology.c +15
-0+* it's topology might not even exist anymore +*/ +kref_get (&vcpi->port->kref); +list_add (&vcpi->next, &state->vcpis); + } +return &state->base; + +fail: +list_for_each_entry_safe (pos, vcpi, &state->vcpis, next) { +drm_dp_put_port (pos->port); +kfree (pos); + } +kfree (state); + +return NULL ; } static void drm_dp_mst_destroy_state(struct drm_private_obj*obj,
drivers/gpu/drm/drm_dp_mst_topology.c +0
-0@@ -3131,14 +3257,116 @@ static voiddrm_dp_mst_destroy_state(struct
drm_private_obj*obj,
{drivers/gpu/drm/drm_dp_mst_topology.c +4
-0struct drm_dp_mst_topology_state *mst_state =to_dp_mst_topology_state (state); +struct drm_dp_vcpi_allocation *pos, *tmp; + +list_for_each_entry_safe (pos, tmp, &mst_state->vcpis, next) { +/* We only keep references to ports with non-zero VCPIs */
drivers/gpu/drm/drm_dp_mst_topology.c +6
-1+if (pos->vcpi) +drm_dp_put_port (pos->port); +kfree (pos); + }kfree (mst_state); } -static const struct drm_private_state_funcs mst_state_funcs = { +static inline int +drm_dp_mst_atomic_check_topology_state(struct
drm_dp_mst_topology_mgr
*mgr,drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ struct
drm_dp_mst_topology_state*mst_state)drivers/gpu/drm/drm_dp_mst_topology.c +6
-0+{ +struct drm_dp_vcpi_allocation *vcpi; +struct drm_dp_mst_port *port; +int avail_slots =63 , ret; + +/* There's no possible scenario where releasing VCPI or keeping it the
drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+* same would make the state invalid +*/ +if (!mst_state->vcpi_allocated) {Do we need this? If not I'm always in favour of removing code if it's
not
needed :-)Quoted 23 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p allocates noVCPI,
check skipped\n",drivers/gpu/drm/drm_dp_mst_topology.c +6
-0+ mgr, &mst_state->base); +return 0 ; + } + +list_for_each_entry (vcpi, &mst_state->vcpis, next) { +/* Releasing VCPI is always OK-even if the port is gone */
drivers/gpu/drm/drm_dp_mst_topology.c +2
-0+if (!vcpi->vcpi) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] releases allVCPI
slots\n",drivers/gpu/drm/drm_dp_mst_topology.c +7
-0+ vcpi->port); +continue ; + } + + port =drm_dp_get_validated_port_ref (mgr, vcpi->port); +if (!port) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] is gone butstill has
VCPI, cannot add new VCPI\n",drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ vcpi->port); +return -EINVAL ; + }For
find_vcpiwe've already done this. For unplugged vcpi, we can't do
this here I think - otherwise if you enable 2 mst outputs on the same
branch, and unplug both, old non-atomic userspace can never ever again
disable them because it can't disable both. The check (to skip already
deallocated ports) above isn't enough to handle that.Quoted 157 lines
drivers/gpu/drm/drm_dp_mst_topology.c +2
-0+ + DRM_DEBUG_ATOMIC("[MST PORT:%p] requires %d vcpislots\n",
drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ vcpi->port, vcpi->vcpi); + + avail_slots -= vcpi->vcpi; +if (avail_slots <0 ) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] not enoughVCPI slots
in mst state %p (avail=%d)\n",drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+ vcpi->port, mst_state, + avail_slots + vcpi->vcpi); + ret = -ENOSPC ; + goto port_fail; + } + +drm_dp_put_port (port); + } + DRM_DEBUG_ATOMIC("[MST MGR:%p] mst state %p VCPI avail=%dused=%d\n",
drivers/gpu/drm/drm_dp_mst_topology.c +11
-0+ mgr, mst_state, avail_slots, + 63 - avail_slots); + +return 0 ; +port_fail: +drm_dp_put_port (port); +return ret; +} + +/** +* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
+ * atomic update is valid
+ * @state: Pointer to the new &structdrm_dp_mst_topology_state
+ *
+ * Checks the given topology state for an atomic update to ensure
that
it's
+ * valid. This includes checking whether there's enough bandwidth
to
support
+ * the new VCPI allocations in the atomic update.
+ *
+ * Any atomic drivers supporting DP MST must make sure to call this
after
+ * checking the rest of their state in their
+ *&drm_mode_config_funcs.atomic_check()callback.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_atomic_release_vcpi_slots()
+ *
+ * Returns:
+ *
+ * 0 if the new state is valid, negative error code otherwise.
+ */drivers/gpu/drm/drm_dp_mst_topology.c +8
-0+int drm_dp_mst_atomic_check (struct drm_atomic_state *state) +{ +struct drm_dp_mst_topology_mgr *mgr; +struct drm_dp_mst_topology_state *mst_state; +int i, ret =0 ; + +for_each_new_mst_mgr_in_state (state, mgr, mst_state, i) { + ret = drm_dp_mst_atomic_check_topology_state(mgr,
mst_state);drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+if (ret) +break ; + } + +return ret; +} +EXPORT_SYMBOL (drm_dp_mst_atomic_check); + +const struct drm_private_state_funcs
drm_dp_mst_topology_state_funcs=
{drivers/gpu/drm/drm_dp_mst_topology.c +3
-4.atomic_duplicate_state = drm_dp_mst_duplicate_state, .atomic_destroy_state = drm_dp_mst_destroy_state, }; +EXPORT_SYMBOL (drm_dp_mst_topology_state_funcs);/** * drm_atomic_get_mst_topology_state: get MST topology state @@ -3216,13 +3444,11 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, return -ENOMEM ; mst_state->mgr = mgr; - -/* max. time slots - one slot for MTP header */ - mst_state->avail_slots =63 ; +INIT_LIST_HEAD (&mst_state->vcpis);drm_atomic_private_obj_init (&mgr->base, &mst_state->base, - &mst_state_funcs); + &drm_dp_mst_topology_state_funcs);return 0 ;}
drivers/gpu/drm/i915/intel_display.c +0
-0@@ -12592,6 +12592,10 @@ static int intel_atomic_check(struct
drm_device
*dev,drivers/gpu/drm/i915/intel_display.c +4
-0"[modeset]" : "[fastset]"); } + ret =drm_dp_mst_atomic_check (state); +if (ret) +return ret; +if (any_ms) { ret =intel_modeset_checks (state);drivers/gpu/drm/i915/intel_dp_mst.c +0
-0@@ -41,8 +41,13 @@ static boolintel_dp_mst_compute_config(struct
intel_encoder*encoder,drivers/gpu/drm/i915/intel_dp_mst.c +6
-1struct drm_connector *connector = conn_state->connector;void *port =to_intel_connector (connector)->port;struct drm_atomic_state *state = pipe_config->base.state; +struct drm_crtc *crtc = pipe_config->base.crtc; +struct drm_crtc_state *old_crtc_state = +drm_atomic_get_old_crtc_state (state, crtc); +struct drm_crtc_state *new_crtc_state = &pipe_config->base;int bpp; -int lane_count, slots =0 ; +int lane_count, slots = +to_intel_crtc_state (old_crtc_state)->dp_m_n.tu; const struct drm_display_mode *adjusted_mode = &pipe_config-Quoted 1 line
base.adjusted_mode;
drivers/gpu/drm/i915/intel_dp_mst.c +0
-0int mst_pbn; bool constant_n = drm_dp_has_quirk(&intel_dp->desc,@@ -77,8 +82,12 @@ static boolintel_dp_mst_compute_config(struct
intel_encoder*encoder,
mst_pbn= drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock,
bpp);drivers/gpu/drm/i915/intel_dp_mst.c +1
-2pipe_config->pbn = mst_pbn; -/* Zombie connectors can't have VCPI slots */ -if (!drm_connector_is_unregistered (connector)) { +/* Only change VCPI allocation on actual mode changes, to prevent us
drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+* from trying to allocate VCPI to ports that no longer exist when we
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 4 lines
drivers/gpu/drm/i915/intel_dp_mst.c +4
-0+* may just be trying to disable DPMS on them +*/ +if (new_crtc_state->mode_changed || + new_crtc_state->connectors_changed) {Why exactly is this needed? We only call
compute_configon the enocoder
when enabling an output. Not when disabling one. Similar for the atomic
helpers and the various encoder callbacks (you have the same change in
the
nouveau code). Did this actually blow up while testing?Quoted 35 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-0slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp-Quoted 1 line
mst_mgr,drivers/gpu/drm/i915/intel_dp_mst.c +0
-0port,@@ -107,36 +116,39 @@ static boolintel_dp_mst_compute_config(struct
intel_encoder*encoder,drivers/gpu/drm/i915/intel_dp_mst.c +0
-1return true; } -static int intel_dp_mst_atomic_check(struct drm_connector*connector,
drivers/gpu/drm/i915/intel_dp_mst.c +13
-6- struct drm_connector_state *new_conn_state) +static int +intel_dp_mst_atomic_check (struct drm_connector *connector, +struct drm_connector_state *new_conn_state) { +struct intel_connector *intel_connector = +to_intel_connector (connector); +struct drm_dp_mst_topology_mgr *mgr = + &intel_connector->mst_port->mst_mgr; +struct drm_dp_mst_port *port = intel_connector->port;struct drm_atomic_state *state = new_conn_state->state; -struct drm_connector_state *old_conn_state; -struct drm_crtc *old_crtc; -struct drm_crtc_state *crtc_state; -int slots, ret =0 ; +struct drm_connector_state *old_conn_state = +drm_atomic_get_old_connector_state (state, connector); +struct drm_crtc *old_crtc = old_conn_state->crtc, + *new_crtc = new_conn_state->crtc; +struct drm_crtc_state *old_crtc_state, *new_crtc_state; - old_conn_state = drm_atomic_get_old_connector_state(state,connector);
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 1 line
drivers/gpu/drm/i915/intel_dp_mst.c +0
-1- old_crtc = old_conn_state->crtc;Bikeshed: Reworking all the above hides the real changes quite a bit.
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 12 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-6if (!old_crtc) -return ret; +return 0 ; - crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); - slots =to_intel_crtc_state (crtc_state)->dp_m_n.tu; -if (drm_atomic_crtc_needs_modeset (crtc_state) && slots >0 ) { -struct drm_dp_mst_topology_mgr *mgr; -struct drm_encoder *old_encoder; + old_crtc_state = drm_atomic_get_old_crtc_state(state,
old_crtc);drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+ if (!old_crtc_state ||!old_crtc_state would be a framework bug. Just remove that check.
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 2 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ !to_intel_crtc_state(old_crtc_state)->dp_m_n.tu) +return 0 ;Hm, why do we need this? Having a DP port previously enabled with a 0
tu
sounds like a pretty big driver bug.drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 9 lines
drivers/gpu/drm/i915/intel_dp_mst.c +1
-2- old_encoder = old_conn_state->best_encoder; - mgr = &enc_to_mst (old_encoder)->primary->dp.mst_mgr; +/* If we switched CRTCs, clear the previous one's allocation */
drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+ new_crtc_state = drm_atomic_get_new_crtc_state(state,
old_crtc);drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+ if (new_crtc_state->connectors_changed && !new_crtc_state-Quoted 1 line
enable)
drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+to_intel_crtc_state (new_crtc_state)->dp_m_n.tu =0 ;No idea why we need to clear this here? Shouldn't we just clear this
unconditionally if we release the allocation.Also naming convention, I'd call this
old_crtc_new_state, and maybeold_crtc_old_state. To make it clear these are the old/new states forold_crtc. Instead of old/new state for the new crtc.drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 13 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-1- ret = drm_dp_atomic_release_vcpi_slots(state, mgr,slots);
drivers/gpu/drm/i915/intel_dp_mst.c +0
-2-if (ret) - DRM_DEBUG_KMS("failed releasing %d vcpislots:%d\n",
slots, ret);drivers/gpu/drm/i915/intel_dp_mst.c +0
-2-else - to_intel_crtc_state(crtc_state)->dp_m_n.tu =0;
drivers/gpu/drm/i915/intel_dp_mst.c +2
-2- } -return ret; +if (new_crtc) +return 0 ;Not sure why we shouldn't release this? If we handle this in helpers,
it
would remove piles of special casing code here I think. Plus remove
special code in the hlpers.Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ +return drm_dp_atomic_release_vcpi_slots (state, mgr, port); } static void intel_mst_disable_dp(struct intel_encoder *encoder,include/drm/drm_dp_mst_helper.h +0
-0include/drm/drm_dp_mst_helper.h struct drm_dp_payload { · line 410
Quoted 5 lines
include/drm/drm_dp_mst_helper.h +1
-0@@ -406,9 +406,16 @@ struct drm_dp_payload {#define to_dp_mst_topology_state (x) container_of(x, struct drm_dp_mst_topology_state, base) +struct drm_dp_vcpi_allocation {I think a comment here that this pointer holds a full reference (even
past
when the connector is unplugged) would be good. Otherwise the loop inrelease_vcpiwould be buggy.Quoted 52 lines
include/drm/drm_dp_mst_helper.h +7
-1+struct drm_dp_mst_port *port; +int vcpi; +struct list_head next; +}; +struct drm_dp_mst_topology_state {struct drm_private_state base; -int avail_slots; +struct list_head vcpis; +u8 vcpi_allocated;struct drm_dp_mst_topology_mgr *mgr; };@@ -619,14 +626,17 @@ void
drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);
int drm_dp_mst_topology_mgr_resume(structdrm_dp_mst_topology_mgr
*mgr);
structdrm_dp_mst_topology_state
*drm_atomic_get_mst_topology_state(structdrm_atomic_state*state,
st
ructdrm_dp_mst_topology_mgr*mgr);
-int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state*state,include/drm/drm_dp_mst_helper.h +0
-2-struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, intpbn);
-int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state*state,include/drm/drm_dp_mst_helper.h +0
-1-struct drm_dp_mst_topology_mgr *mgr,
include/drm/drm_dp_mst_helper.h +6
-1- int slots); +int __must_check +drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port,int pbn); +int __must_check +drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state*state,
include/drm/drm_dp_mst_helper.h +2
-0+struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port); int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr*mgr,
structdrm_dp_mst_port*port, boolpower_up);
+int__must_checkdrm_dp_mst_atomic_check(structdrm_atomic_state
*state);Quoted 6 lines
extern const struct
drm_private_state_funcsdrm_dp_mst_topology_state_funcs;--
2.19.1--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.chQuoted 4 lines
Quoted 4 lines
--
Cheers,
Lyude Paul--
Cheers,
Lyude Paul
Signature · 7 lines
Cheers, Lyude Paul _______________________________________________ Nouveau mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/nouveau
Lyude Paul
maintainerRe: [PATCH v6 3/6] drm/dp_mst: Start tracking per-port VCPI allocations
In reply to Daniel VetterQuoted 11 lines · Daniel Vetter
Quoted 9 lines · Lyude Paul
On Tue, 2018-11-27 at 20:44 +0100, Daniel Vetter wrote:
We could do this the other way around so it looks like this maybe
struct kref; /* manages
kfree()*/
structtopology_kref; /* corresonds to lifetime in topology */Then only expose functions for the normal kref to drivers, so that any possible confusion is still limited to
drm_dp_mst_topology.c
Quoted 8 lines
I like this bikeshed a lot. Since we need a bunch of the plain kref_get/put internally, probably still good to have a wrapper. For that the port_malloc_get/put() still sounds good to me - port_kfree_get/put sounds confusing, since it's not the kfree we're refcounting, but the memory allocation.
Another option would be to add
_topologyto the public get/put functions, but that makes for a fairly long function name :-/
bleh. Looked at not using a malloc() prefix in the naming but it's definitely still more confusing without one.
I really do question if we really want the canonical naming prefix for the MST helpers to be drm_dp_mst_topology. It would be very nice to have this freed so we could do something like this:
drm_dp_mst_topology_get_mstb() /* &topology_kref */ drm_dp_mst_topology_put_mstb() /* &topology_kref */drm_dp_mst_get_mstb_malloc() /* &kref */drm_dp_mst_put_mstb_malloc() /* &kref */
drm_dp_mst_topology_get_port() /* &topology_kref */ drm_dp_mst_topology_put_port() /* &topology_kref */drm_dp_mst_get_port_malloc() /* &kref */drm_dp_mst_put_port_malloc() /* &kref */
Additionally, I had an epiphany and figured out a seriously dead-simple rule that answers "what ref to use where" in the MST helper code:
Let's start with a topology like this:
|- mst_primary
|- port #1
|- port #2
|- port #3
|- mstb #1
|- port #4
|- mstb #2
|- port #5
|- port #6
|- mstb #3
|- port #7
|- mstb #4
|- port #8
|- port #9If mstb #1 was removed from the topology, ports 4-9 and mstbs 2-4 must lose their topology references. Simply put: each port holds a topology reference to it's mstb (if there is one), and each mstb holds a topology reference to it's ports.
Now, let's allocate some payloads on the topology:
|- mst_primary
|- port #1
|- port #2
|- port #3
|- mstb #1
|- port #4
|- mstb #2 (has payload)
|- port #5
|- port #6
|- mstb #3
|- port #7
|- mstb #4 (has payload)
|- port #8
|- port #9So, now if we remove mstb #1 any payloads under it need to be freed. Since mstb #4's and #2's parents no longer exist in the topology however, we must go back up the tree to find our last living relative.
So:
mstb #4 → port #7 → mstb #3 → port #6 → mstb #1 → port #3 for payload 1
mstb #4 → port #4 → mstb #1 → port #3 for payload 2
Going off this we can come up with the rule for malloc refs: each port holds a malloc ref to it's parent mstb, and each mstb holds a malloc ref to it's parent port (if there is one). So, it's just the reverse of the topology ref rule. Now just add refs for payloads and other stuff, and we're good.
Hooray! Now I can also use this in any docs I write too :)
Quoted 904 lines
-Daniel
Quoted 902 lines
Quoted 894 lines
Quoted 894 lines
Quoted 894 lines
-Daniel
Quoted 892 lines
Piles of comments below.
Cheers, Daniel
Quoted 207 lines
- Use the small changes to
drm_dp_put_port()to add even more
error
checking to make misusage of the helpers more obvious. I
added
this
after having to chase down various use-after-free conditions
that
started popping up from the new helpers so no one else has to troubleshoot that.
- Move some accidentalDRM_DEBUG_KMS()calls toDRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should
both
not
be
called on the same port in a single atomic check phase (but
multiple
calls to one or the other is OK)Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new
VCPI
allocations happen in a state. This makes the next change I'm
about
to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead
ensure
that
when ports are destroyed and still have VCPI allocations in
the
topology state, the only state changes allowed are releasing
said
ports' VCPI. This prevents a state with a mix of VCPI
allocations
from destroyed ports, and allocations from valid ports.Changes since v3:
- Don't release VCPI allocations in the topology state
immediately
in
drm_dp_atomic_release_vcpi_slots(), instead mark them as 0
and
skip
over them indrm_dp_mst_duplicate_state(). This makes it sodrm_dp_atomic_release_vcpi_slots()is still idempotent while
also
throwing warnings if the driver messes up it's book keeping
and
tries
to release VCPI slots on a port that doesn't have any pre-
existing
VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst
state"Changes since v2:
- Usekmemdup()for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states indrm_dp_mst_atomic_check()so the driver doesn't have to do it
- Fix documentation indrm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into
it's
own function, reduces indenting
- Don't consider "stale" MST ports when calculating the
bandwidth
requirements. This is needed because originally we relied on
the
state duplication functions to prune any stale ports from the
new
state, which would prevent us from incorrectly considering
their
bandwidth requirements alongside legitimate new payloads.
- Add function references indrm_dp_atomic_release_vcpi_slots()
-
danvet
- Annotate atomic VCPI and atomic check functions with__must_check
- danvetChanges since v1:
- Don't use the now-removed ->atomic_check() for private
objects
hook,
just give drivers a function to call themselvesSigned-off-by Lyude Paul <[email protected]>Cc Daniel Vetter <[email protected]>--- drivers/gpu/drm/drm_dp_mst_topology.c | 294 +++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 4 + drivers/gpu/drm/i915/intel_dp_mst.c | 64 +++--- include/drm/drm_dp_mst_helper.h | 24 ++- 4 files changed, 319 insertions(+), 67 deletions(-)drivers/gpu/drm/drm_dp_mst_topology.c +0
-1@@ -2617,21 +2617,42 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, }/** -* drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the
state
+ *drm_dp_atomic_find_vcpi_slots()- Find and add VCPI slots to the
state
* @state: global atomic state
* @mgr: MST topology manager for the port
* @port: port to find vcpi slots for
* @pbn: bandwidth required for the mode in PBN
*
- * RETURNS:
- * Total slots in the atomic state assigned for this port or error
+ * Allocates VCPI slots to @port, replacing any previous VCPI allocations it
+ * may have had. Any atomic drivers which support MST must call this
function
+ * in their&drm_encoder_helper_funcs.atomic_check()callback to
change
the
+ * current VCPI allocation for the new state, but only when
+ * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed
is set
+ * to ensure compatibility with userspace applications that still
use
the
+ * legacy modesetting UAPI.
+ *
+ * Allocations set by this function are not checked against the bandwidth
+ * restraints of @mgr until the driver callsdrm_dp_mst_atomic_check().
+ *
+ * Additionally, it is OK to call this function multiple times on
the
same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_release_vcpi_slots()in the same atomic check phase.
+ *
+ * See also:
+ *drm_dp_atomic_release_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * Total slots in the atomic state assigned for this port, or a negative error
+ * code if the port no longer exists
*/
int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state
*state,drivers/gpu/drm/drm_dp_mst_topology.c +0
-0struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, intpbn)
{drivers/gpu/drm/drm_dp_mst_topology.c +2
-1struct drm_dp_mst_topology_state *topology_state; -int req_slots; +struct drm_dp_vcpi_allocation *pos, *vcpi =NULL ; +int prev_slots, req_slots, ret; topology_state = drm_atomic_get_mst_topology_state(state,mgr);
drivers/gpu/drm/drm_dp_mst_topology.c +10
-6if (IS_ERR (topology_state))@@ -2640,20 +2661,60 @@ int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state *state, port =drm_dp_get_validated_port_ref (mgr, port);if (port ==NULL )return -EINVAL ; - req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); -DRM_DEBUG_KMS ("vcpi slots req=%d, avail=%d\n" , - req_slots, topology_state->avail_slots); -if (req_slots > topology_state->avail_slots) { -drm_dp_put_port (port); -return -ENOSPC ; + topology_state->vcpi_allocated = true; + +/* Find the current allocation for this port, if any */ +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + vcpi = pos; + prev_slots = vcpi->vcpi; + +/* +* This should never happen, unless the driver tries
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+* releasing and allocating the same VCPI allocation,
drivers/gpu/drm/drm_dp_mst_topology.c +4
-0+* which is an error +*/ +if (WARN_ON (!prev_slots)) { + DRM_ERROR("cannot allocate and releaseVCPI on
[MST PORT:%p] in the same state\n",drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ port); +return -EINVAL ; + }Allowing atomic changes to output state (like disabling it on one crtc,
and then enabling on the other, thus both releasing and re- allocating
the
vcpi in one go) is kinda the point of atomic. So definitely can't EINVAL
here.Sounds like we need more testcases in igt?
Quoted 148 lines
drivers/gpu/drm/drm_dp_mst_topology.c +5
-2+ +break ; + } } +if (!vcpi) + prev_slots =0 ; - topology_state->avail_slots -= req_slots; - DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state-Quoted 1 line
avail_slots);drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); + + DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d ->%d\n",
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ port->connector->base.id, port->connector-Quoted 1 line
name,
drivers/gpu/drm/drm_dp_mst_topology.c +22
-1+ port, prev_slots, req_slots); + +/* Add the new allocation to the state */ +if (!vcpi) { + vcpi =kzalloc (sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) { + ret = -ENOMEM ; + goto out; + } + vcpi->port =drm_dp_get_validated_port_ref (mgr, port); +if (!vcpi->port) { + ret = -EINVAL ; +kfree (vcpi); + goto out; + } +list_add (&vcpi->next, &topology_state->vcpis); + } + vcpi->vcpi = req_slots; + + ret = req_slots; +out:drm_dp_put_port (port); -return req_slots; +return ret; }EXPORT_SYMBOL (drm_dp_atomic_find_vcpi_slots);@@ -2661,31 +2722,66 @@EXPORT_SYMBOL (drm_dp_atomic_find_vcpi_slots); * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpislots
* @state: global atomic state
* @mgr: MST topology manager for the port
- * @slots: number of vcpi slots to release
+ * @port: The port to release the VCPI slots from
*
- * RETURNS:
- * 0 if @slots were added back to &drm_dp_mst_topology_state-Quoted 1 line
avail_slotsor- * negative error code
+ * Releases any VCPI slots that have been allocated to a port in
the
atomic
+ * state. Any atomic drivers which support MST must call this function
in
+ * their&drm_connector_helper_funcs.atomic_check()callback when
the
+ * connector will no longer have VCPI allocated (e.g. because it's
CRTC
was
+ * removed) when it had VCPI allocated in the previous atomic state.
+ *
+ * It is OK to call this even if @port has been removed from the
system.
+ * Additionally, it is OK to call this function multiple times on
the
same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_find_vcpi_slots()on the same @port in a single
atomic
check
+ * phase.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * 0 if all slots for this port were added back to
+ * &drm_dp_mst_topology_state.avail_slots or negative error code
*/
int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state
*state,
structdrm_dp_mst_topology_mgr
*mgr,drivers/gpu/drm/drm_dp_mst_topology.c +3
-1- int slots) + struct drm_dp_mst_port *port) {struct drm_dp_mst_topology_state *topology_state; +struct drm_dp_vcpi_allocation *pos; +bool found = false; topology_state = drm_atomic_get_mst_topology_state(state,mgr);
drivers/gpu/drm/drm_dp_mst_topology.c +0
-2if (IS_ERR (topology_state))return PTR_ERR (topology_state); -/* We cannot rely on port->vcpi.num_slots to update -* topology_state->avail_slots as the port may not exist if the parent
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* branch device was unplugged. This should be fixed by tracking
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* per-port slot allocation in drm_dp_mst_topology_state instead of
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* depending on the caller to tell us how many slots to release.
drivers/gpu/drm/drm_dp_mst_topology.c +8
-4-*/ - topology_state->avail_slots += slots; -DRM_DEBUG_KMS ("vcpi slots released=%d, avail=%d\n" , - slots, topology_state->avail_slots); +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + found = true; +break ; + } + } +if (WARN_ON (!found)) { + DRM_ERROR("no VCPI for [MST PORT:%p] found in mststate %p\n",
drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ port, &topology_state->base); +return -EINVAL ; + } + + DRM_DEBUG_ATOMIC("[MST PORT:%p] VCPI %d -> 0\n", port, pos-Quoted 1 line
vcpi);
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+if (pos->vcpi) { +/* +* This should be safe-as the previous state will still hold a
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+* reference to this port until we swap states. If it's not,
drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+* the driver has a bookkeeping error +*/ +if (WARN_ON (drm_dp_put_port (port))) {The usual way to do this (instead of adding a return value to the normal
_putfunction) is something like
kref_put(port, function_that_with_just_a_WARNING_DRM_ERROR)avoids the need for patch 2.
Quoted 98 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_ERROR("cannot allocate and release VCPI on[MST
PORT:%p] in the same state\n",drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ port); +return -EINVAL ; + } + pos->vcpi =0 ; + }return 0 ; }@@ -3115,15 +3211,45 @@ static void
drm_dp_destroy_connector_work(struct work_struct *work)
static structdrm_private_state*drm_dp_mst_duplicate_state(struct drm_private_obj *obj){drivers/gpu/drm/drm_dp_mst_topology.c +4
-2-struct drm_dp_mst_topology_state *state; +struct drm_dp_mst_topology_state *state, *old_state = +to_dp_mst_topology_state (obj->state); +struct drm_dp_vcpi_allocation *pos, *vcpi; - state =kmemdup (obj->state,sizeof (*state),GFP_KERNEL ); + state =kmemdup (old_state,sizeof (*state),GFP_KERNEL );if (!state)return NULL ; __drm_atomic_helper_private_obj_duplicate_state(obj, &state-Quoted 1 line
base);
drivers/gpu/drm/drm_dp_mst_topology.c +13
-0+INIT_LIST_HEAD (&state->vcpis); + state->vcpi_allocated = false; + +list_for_each_entry (pos, &old_state->vcpis, next) { +/* Prune leftover freed VCPI allocations */ +if (!pos->vcpi) +continue ; + + vcpi =kmemdup (pos,sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) + goto fail; + +/* Take a plain old kref here and don't validate the port, as
drivers/gpu/drm/drm_dp_mst_topology.c +15
-0+* it's topology might not even exist anymore +*/ +kref_get (&vcpi->port->kref); +list_add (&vcpi->next, &state->vcpis); + } +return &state->base; + +fail: +list_for_each_entry_safe (pos, vcpi, &state->vcpis, next) { +drm_dp_put_port (pos->port); +kfree (pos); + } +kfree (state); + +return NULL ; } static void drm_dp_mst_destroy_state(struct drm_private_obj*obj,
drivers/gpu/drm/drm_dp_mst_topology.c +0
-0@@ -3131,14 +3257,116 @@ static voiddrm_dp_mst_destroy_state(struct
drm_private_obj*obj,
{drivers/gpu/drm/drm_dp_mst_topology.c +4
-0struct drm_dp_mst_topology_state *mst_state =to_dp_mst_topology_state (state); +struct drm_dp_vcpi_allocation *pos, *tmp; + +list_for_each_entry_safe (pos, tmp, &mst_state->vcpis, next) { +/* We only keep references to ports with non-zero VCPIs */
drivers/gpu/drm/drm_dp_mst_topology.c +6
-1+if (pos->vcpi) +drm_dp_put_port (pos->port); +kfree (pos); + }kfree (mst_state); } -static const struct drm_private_state_funcs mst_state_funcs = { +static inline int +drm_dp_mst_atomic_check_topology_state(struct
drm_dp_mst_topology_mgr
*mgr,drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ struct
drm_dp_mst_topology_state*mst_state)drivers/gpu/drm/drm_dp_mst_topology.c +6
-0+{ +struct drm_dp_vcpi_allocation *vcpi; +struct drm_dp_mst_port *port; +int avail_slots =63 , ret; + +/* There's no possible scenario where releasing VCPI or keeping it the
drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+* same would make the state invalid +*/ +if (!mst_state->vcpi_allocated) {Do we need this? If not I'm always in favour of removing code if it's
not
needed :-)Quoted 23 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p allocates noVCPI,
check skipped\n",drivers/gpu/drm/drm_dp_mst_topology.c +6
-0+ mgr, &mst_state->base); +return 0 ; + } + +list_for_each_entry (vcpi, &mst_state->vcpis, next) { +/* Releasing VCPI is always OK-even if the port is gone */
drivers/gpu/drm/drm_dp_mst_topology.c +2
-0+if (!vcpi->vcpi) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] releases allVCPI
slots\n",drivers/gpu/drm/drm_dp_mst_topology.c +7
-0+ vcpi->port); +continue ; + } + + port =drm_dp_get_validated_port_ref (mgr, vcpi->port); +if (!port) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] is gone butstill has
VCPI, cannot add new VCPI\n",drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ vcpi->port); +return -EINVAL ; + }For
find_vcpiwe've already done this. For unplugged vcpi, we can't do
this here I think - otherwise if you enable 2 mst outputs on the same
branch, and unplug both, old non-atomic userspace can never ever again
disable them because it can't disable both. The check (to skip already
deallocated ports) above isn't enough to handle that.Quoted 157 lines
drivers/gpu/drm/drm_dp_mst_topology.c +2
-0+ + DRM_DEBUG_ATOMIC("[MST PORT:%p] requires %d vcpislots\n",
drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ vcpi->port, vcpi->vcpi); + + avail_slots -= vcpi->vcpi; +if (avail_slots <0 ) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] not enoughVCPI slots
in mst state %p (avail=%d)\n",drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+ vcpi->port, mst_state, + avail_slots + vcpi->vcpi); + ret = -ENOSPC ; + goto port_fail; + } + +drm_dp_put_port (port); + } + DRM_DEBUG_ATOMIC("[MST MGR:%p] mst state %p VCPI avail=%dused=%d\n",
drivers/gpu/drm/drm_dp_mst_topology.c +11
-0+ mgr, mst_state, avail_slots, + 63 - avail_slots); + +return 0 ; +port_fail: +drm_dp_put_port (port); +return ret; +} + +/** +* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
+ * atomic update is valid
+ * @state: Pointer to the new &structdrm_dp_mst_topology_state
+ *
+ * Checks the given topology state for an atomic update to ensure
that
it's
+ * valid. This includes checking whether there's enough bandwidth
to
support
+ * the new VCPI allocations in the atomic update.
+ *
+ * Any atomic drivers supporting DP MST must make sure to call this
after
+ * checking the rest of their state in their
+ *&drm_mode_config_funcs.atomic_check()callback.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_atomic_release_vcpi_slots()
+ *
+ * Returns:
+ *
+ * 0 if the new state is valid, negative error code otherwise.
+ */drivers/gpu/drm/drm_dp_mst_topology.c +8
-0+int drm_dp_mst_atomic_check (struct drm_atomic_state *state) +{ +struct drm_dp_mst_topology_mgr *mgr; +struct drm_dp_mst_topology_state *mst_state; +int i, ret =0 ; + +for_each_new_mst_mgr_in_state (state, mgr, mst_state, i) { + ret = drm_dp_mst_atomic_check_topology_state(mgr,
mst_state);drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+if (ret) +break ; + } + +return ret; +} +EXPORT_SYMBOL (drm_dp_mst_atomic_check); + +const struct drm_private_state_funcs
drm_dp_mst_topology_state_funcs=
{drivers/gpu/drm/drm_dp_mst_topology.c +3
-4.atomic_duplicate_state = drm_dp_mst_duplicate_state, .atomic_destroy_state = drm_dp_mst_destroy_state, }; +EXPORT_SYMBOL (drm_dp_mst_topology_state_funcs);/** * drm_atomic_get_mst_topology_state: get MST topology state @@ -3216,13 +3444,11 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, return -ENOMEM ; mst_state->mgr = mgr; - -/* max. time slots - one slot for MTP header */ - mst_state->avail_slots =63 ; +INIT_LIST_HEAD (&mst_state->vcpis);drm_atomic_private_obj_init (&mgr->base, &mst_state->base, - &mst_state_funcs); + &drm_dp_mst_topology_state_funcs);return 0 ;}
drivers/gpu/drm/i915/intel_display.c +0
-0@@ -12592,6 +12592,10 @@ static int intel_atomic_check(struct
drm_device
*dev,drivers/gpu/drm/i915/intel_display.c +4
-0"[modeset]" : "[fastset]"); } + ret =drm_dp_mst_atomic_check (state); +if (ret) +return ret; +if (any_ms) { ret =intel_modeset_checks (state);drivers/gpu/drm/i915/intel_dp_mst.c +0
-0@@ -41,8 +41,13 @@ static boolintel_dp_mst_compute_config(struct
intel_encoder*encoder,drivers/gpu/drm/i915/intel_dp_mst.c +6
-1struct drm_connector *connector = conn_state->connector;void *port =to_intel_connector (connector)->port;struct drm_atomic_state *state = pipe_config->base.state; +struct drm_crtc *crtc = pipe_config->base.crtc; +struct drm_crtc_state *old_crtc_state = +drm_atomic_get_old_crtc_state (state, crtc); +struct drm_crtc_state *new_crtc_state = &pipe_config->base;int bpp; -int lane_count, slots =0 ; +int lane_count, slots = +to_intel_crtc_state (old_crtc_state)->dp_m_n.tu; const struct drm_display_mode *adjusted_mode = &pipe_config-Quoted 1 line
base.adjusted_mode;
drivers/gpu/drm/i915/intel_dp_mst.c +0
-0int mst_pbn; bool constant_n = drm_dp_has_quirk(&intel_dp->desc,@@ -77,8 +82,12 @@ static boolintel_dp_mst_compute_config(struct
intel_encoder*encoder,
mst_pbn= drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock,
bpp);drivers/gpu/drm/i915/intel_dp_mst.c +1
-2pipe_config->pbn = mst_pbn; -/* Zombie connectors can't have VCPI slots */ -if (!drm_connector_is_unregistered (connector)) { +/* Only change VCPI allocation on actual mode changes, to prevent us
drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+* from trying to allocate VCPI to ports that no longer exist when we
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 4 lines
drivers/gpu/drm/i915/intel_dp_mst.c +4
-0+* may just be trying to disable DPMS on them +*/ +if (new_crtc_state->mode_changed || + new_crtc_state->connectors_changed) {Why exactly is this needed? We only call
compute_configon the enocoder
when enabling an output. Not when disabling one. Similar for the atomic
helpers and the various encoder callbacks (you have the same change in
the
nouveau code). Did this actually blow up while testing?Quoted 35 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-0slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp-Quoted 1 line
mst_mgr,drivers/gpu/drm/i915/intel_dp_mst.c +0
-0port,@@ -107,36 +116,39 @@ static boolintel_dp_mst_compute_config(struct
intel_encoder*encoder,drivers/gpu/drm/i915/intel_dp_mst.c +0
-1return true; } -static int intel_dp_mst_atomic_check(struct drm_connector*connector,
drivers/gpu/drm/i915/intel_dp_mst.c +13
-6- struct drm_connector_state *new_conn_state) +static int +intel_dp_mst_atomic_check (struct drm_connector *connector, +struct drm_connector_state *new_conn_state) { +struct intel_connector *intel_connector = +to_intel_connector (connector); +struct drm_dp_mst_topology_mgr *mgr = + &intel_connector->mst_port->mst_mgr; +struct drm_dp_mst_port *port = intel_connector->port;struct drm_atomic_state *state = new_conn_state->state; -struct drm_connector_state *old_conn_state; -struct drm_crtc *old_crtc; -struct drm_crtc_state *crtc_state; -int slots, ret =0 ; +struct drm_connector_state *old_conn_state = +drm_atomic_get_old_connector_state (state, connector); +struct drm_crtc *old_crtc = old_conn_state->crtc, + *new_crtc = new_conn_state->crtc; +struct drm_crtc_state *old_crtc_state, *new_crtc_state; - old_conn_state = drm_atomic_get_old_connector_state(state,connector);
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 1 line
drivers/gpu/drm/i915/intel_dp_mst.c +0
-1- old_crtc = old_conn_state->crtc;Bikeshed: Reworking all the above hides the real changes quite a bit.
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 12 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-6if (!old_crtc) -return ret; +return 0 ; - crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); - slots =to_intel_crtc_state (crtc_state)->dp_m_n.tu; -if (drm_atomic_crtc_needs_modeset (crtc_state) && slots >0 ) { -struct drm_dp_mst_topology_mgr *mgr; -struct drm_encoder *old_encoder; + old_crtc_state = drm_atomic_get_old_crtc_state(state,
old_crtc);drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+ if (!old_crtc_state ||!old_crtc_state would be a framework bug. Just remove that check.
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 2 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ !to_intel_crtc_state(old_crtc_state)->dp_m_n.tu) +return 0 ;Hm, why do we need this? Having a DP port previously enabled with a 0
tu
sounds like a pretty big driver bug.drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 9 lines
drivers/gpu/drm/i915/intel_dp_mst.c +1
-2- old_encoder = old_conn_state->best_encoder; - mgr = &enc_to_mst (old_encoder)->primary->dp.mst_mgr; +/* If we switched CRTCs, clear the previous one's allocation */
drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+ new_crtc_state = drm_atomic_get_new_crtc_state(state,
old_crtc);drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+ if (new_crtc_state->connectors_changed && !new_crtc_state-Quoted 1 line
enable)
drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+to_intel_crtc_state (new_crtc_state)->dp_m_n.tu =0 ;No idea why we need to clear this here? Shouldn't we just clear this
unconditionally if we release the allocation.Also naming convention, I'd call this
old_crtc_new_state, and maybeold_crtc_old_state. To make it clear these are the old/new states forold_crtc. Instead of old/new state for the new crtc.drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 13 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-1- ret = drm_dp_atomic_release_vcpi_slots(state, mgr,slots);
drivers/gpu/drm/i915/intel_dp_mst.c +0
-2-if (ret) - DRM_DEBUG_KMS("failed releasing %d vcpislots:%d\n",
slots, ret);drivers/gpu/drm/i915/intel_dp_mst.c +0
-2-else - to_intel_crtc_state(crtc_state)->dp_m_n.tu =0;
drivers/gpu/drm/i915/intel_dp_mst.c +2
-2- } -return ret; +if (new_crtc) +return 0 ;Not sure why we shouldn't release this? If we handle this in helpers,
it
would remove piles of special casing code here I think. Plus remove
special code in the hlpers.Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ +return drm_dp_atomic_release_vcpi_slots (state, mgr, port); } static void intel_mst_disable_dp(struct intel_encoder *encoder,include/drm/drm_dp_mst_helper.h +0
-0include/drm/drm_dp_mst_helper.h struct drm_dp_payload { · line 410
Quoted 5 lines
include/drm/drm_dp_mst_helper.h +1
-0@@ -406,9 +406,16 @@ struct drm_dp_payload {#define to_dp_mst_topology_state (x) container_of(x, struct drm_dp_mst_topology_state, base) +struct drm_dp_vcpi_allocation {I think a comment here that this pointer holds a full reference (even
past
when the connector is unplugged) would be good. Otherwise the loop inrelease_vcpiwould be buggy.Quoted 52 lines
include/drm/drm_dp_mst_helper.h +7
-1+struct drm_dp_mst_port *port; +int vcpi; +struct list_head next; +}; +struct drm_dp_mst_topology_state {struct drm_private_state base; -int avail_slots; +struct list_head vcpis; +u8 vcpi_allocated;struct drm_dp_mst_topology_mgr *mgr; };@@ -619,14 +626,17 @@ void
drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);
int drm_dp_mst_topology_mgr_resume(structdrm_dp_mst_topology_mgr
*mgr);
structdrm_dp_mst_topology_state
*drm_atomic_get_mst_topology_state(structdrm_atomic_state*state,
st
ructdrm_dp_mst_topology_mgr*mgr);
-int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state*state,include/drm/drm_dp_mst_helper.h +0
-2-struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, intpbn);
-int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state*state,include/drm/drm_dp_mst_helper.h +0
-1-struct drm_dp_mst_topology_mgr *mgr,
include/drm/drm_dp_mst_helper.h +6
-1- int slots); +int __must_check +drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port,int pbn); +int __must_check +drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state*state,
include/drm/drm_dp_mst_helper.h +2
-0+struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port); int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr*mgr,
structdrm_dp_mst_port*port, boolpower_up);
+int__must_checkdrm_dp_mst_atomic_check(structdrm_atomic_state
*state);Quoted 6 lines
extern const struct
drm_private_state_funcsdrm_dp_mst_topology_state_funcs;--
2.19.1--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.chQuoted 4 lines
Quoted 4 lines
--
Cheers,
Lyude Paul--
Cheers,
Lyude Paul
Signature · 7 lines
Cheers, Lyude Paul _______________________________________________ Intel-gfx mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/intel-gfx
A message that is not in the archive (sent off-list or not yet received): 2743da9300e7805fd144995d33b9249aefc2857a.camel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org
Re: [PATCH v6 3/6] drm/dp_mst: Start tracking per-port VCPI allocations
Quoted 34 lines · Lyude Paul
Quoted 11 lines · Daniel Vetter
Quoted 9 lines · Lyude Paul
On Tue, 2018-11-27 at 20:44 +0100, Daniel Vetter wrote:
We could do this the other way around so it looks like this maybe
struct kref; /* manages
kfree()*/
structtopology_kref; /* corresonds to lifetime in topology */Then only expose functions for the normal kref to drivers, so that any possible confusion is still limited to
drm_dp_mst_topology.cQuoted 8 lines
I like this bikeshed a lot. Since we need a bunch of the plain kref_get/put internally, probably still good to have a wrapper. For that the port_malloc_get/put() still sounds good to me - port_kfree_get/put sounds confusing, since it's not the kfree we're refcounting, but the memory allocation.
Another option would be to add
_topologyto the public get/put functions, but that makes for a fairly long function name :-/bleh. Looked at not using a
malloc()prefix in the naming but it's definitely still more confusing without one.I really do question if we really want the canonical naming prefix for the MST helpers to be
drm_dp_mst_topology. It would be very nice to have this freed so we could do something like this:
drm_dp_mst_topology_get_mstb()/*&topology_kref*/drm_dp_mst_topology_put_mstb()/*&topology_kref*/drm_dp_mst_get_mstb_malloc()/* &kref */drm_dp_mst_put_mstb_malloc()/* &kref */
Quoted 4 lines
drm_dp_mst_topology_get_port()/*&topology_kref*/drm_dp_mst_topology_put_port()/*&topology_kref*/drm_dp_mst_get_port_malloc()/* &kref */drm_dp_mst_put_port_malloc()/* &kref */
Ack on this bikeshed, looks good to me.
Quoted 51 lines
Additionally, I had an epiphany and figured out a seriously dead-simple rule that answers "what ref to use where" in the MST helper code:
Let's start with a topology like this:
|-
mst_primary|- port #1 |- port #2 |- port #3 |- mstb #1 |- port #4 |- mstb #2 |- port #5 |- port #6 |- mstb #3 |- port #7 |- mstb #4 |- port #8 |- port #9If mstb #1 was removed from the topology, ports 4-9 and mstbs 2-4 must lose their topology references. Simply put: each port holds a topology reference to it's mstb (if there is one), and each mstb holds a topology reference to it's ports.
Now, let's allocate some payloads on the topology:
|-
mst_primary|- port #1 |- port #2 |- port #3 |- mstb #1 |- port #4 |- mstb #2 (has payload) |- port #5 |- port #6 |- mstb #3 |- port #7 |- mstb #4 (has payload) |- port #8 |- port #9So, now if we remove mstb #1 any payloads under it need to be freed. Since mstb #4's and #2's parents no longer exist in the topology however, we must go back up the tree to find our last living relative.
So:
mstb #4 → port #7 → mstb #3 → port #6 → mstb #1 → port #3 for payload 1
mstb #4 → port #4 → mstb #1 → port #3 for payload 2
Quoted 7 lines
Going off this we can come up with the rule for malloc refs: each port holds a malloc ref to it's parent mstb, and each mstb holds a malloc ref to it's parent port (if there is one). So, it's just the reverse of the topology ref rule. Now just add refs for payloads and other stuff, and we're good.
Hooray! Now I can also use this in any docs I write too :)
This flew right over my head (but coffee isn't working yet I guess), so definitely good to put this into the kerneldoc comments :-) -Daniel
Quoted 909 lines
Quoted 905 lines
-Daniel
Quoted 902 lines
Quoted 894 lines
Quoted 894 lines
Quoted 894 lines
-Daniel
Quoted 892 lines
Piles of comments below.
Cheers, Daniel
Quoted 207 lines
- Use the small changes to
drm_dp_put_port()to add even more
error
checking to make misusage of the helpers more obvious. I
added
this
after having to chase down various use-after-free conditions
that
started popping up from the new helpers so no one else has to troubleshoot that.
- Move some accidentalDRM_DEBUG_KMS()calls toDRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should
both
not
be
called on the same port in a single atomic check phase (but
multiple
calls to one or the other is OK)Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new
VCPI
allocations happen in a state. This makes the next change I'm
about
to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead
ensure
that
when ports are destroyed and still have VCPI allocations in
the
topology state, the only state changes allowed are releasing
said
ports' VCPI. This prevents a state with a mix of VCPI
allocations
from destroyed ports, and allocations from valid ports.Changes since v3:
- Don't release VCPI allocations in the topology state
immediately
in
drm_dp_atomic_release_vcpi_slots(), instead mark them as 0
and
skip
over them indrm_dp_mst_duplicate_state(). This makes it sodrm_dp_atomic_release_vcpi_slots()is still idempotent while
also
throwing warnings if the driver messes up it's book keeping
and
tries
to release VCPI slots on a port that doesn't have any pre-
existing
VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst
state"Changes since v2:
- Usekmemdup()for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states indrm_dp_mst_atomic_check()so the driver doesn't have to do it
- Fix documentation indrm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into
it's
own function, reduces indenting
- Don't consider "stale" MST ports when calculating the
bandwidth
requirements. This is needed because originally we relied on
the
state duplication functions to prune any stale ports from the
new
state, which would prevent us from incorrectly considering
their
bandwidth requirements alongside legitimate new payloads.
- Add function references indrm_dp_atomic_release_vcpi_slots()
-
danvet
- Annotate atomic VCPI and atomic check functions with__must_check
- danvetChanges since v1:
- Don't use the now-removed ->atomic_check() for private
objects
hook,
just give drivers a function to call themselvesSigned-off-by Lyude Paul <[email protected]>Cc Daniel Vetter <[email protected]>--- drivers/gpu/drm/drm_dp_mst_topology.c | 294 +++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 4 + drivers/gpu/drm/i915/intel_dp_mst.c | 64 +++--- include/drm/drm_dp_mst_helper.h | 24 ++- 4 files changed, 319 insertions(+), 67 deletions(-)drivers/gpu/drm/drm_dp_mst_topology.c +0
-1@@ -2617,21 +2617,42 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, }/** -* drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the
state
+ *drm_dp_atomic_find_vcpi_slots()- Find and add VCPI slots to the
state
* @state: global atomic state
* @mgr: MST topology manager for the port
* @port: port to find vcpi slots for
* @pbn: bandwidth required for the mode in PBN
*
- * RETURNS:
- * Total slots in the atomic state assigned for this port or error
+ * Allocates VCPI slots to @port, replacing any previous VCPI allocations it
+ * may have had. Any atomic drivers which support MST must call this
function
+ * in their&drm_encoder_helper_funcs.atomic_check()callback to
change
the
+ * current VCPI allocation for the new state, but only when
+ * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed
is set
+ * to ensure compatibility with userspace applications that still
use
the
+ * legacy modesetting UAPI.
+ *
+ * Allocations set by this function are not checked against the bandwidth
+ * restraints of @mgr until the driver callsdrm_dp_mst_atomic_check().
+ *
+ * Additionally, it is OK to call this function multiple times on
the
same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_release_vcpi_slots()in the same atomic check phase.
+ *
+ * See also:
+ *drm_dp_atomic_release_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * Total slots in the atomic state assigned for this port, or a negative error
+ * code if the port no longer exists
*/
int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state
*state,drivers/gpu/drm/drm_dp_mst_topology.c +0
-0struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, intpbn)
{drivers/gpu/drm/drm_dp_mst_topology.c +2
-1struct drm_dp_mst_topology_state *topology_state; -int req_slots; +struct drm_dp_vcpi_allocation *pos, *vcpi =NULL ; +int prev_slots, req_slots, ret; topology_state = drm_atomic_get_mst_topology_state(state,mgr);
drivers/gpu/drm/drm_dp_mst_topology.c +10
-6if (IS_ERR (topology_state))@@ -2640,20 +2661,60 @@ int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state *state, port =drm_dp_get_validated_port_ref (mgr, port);if (port ==NULL )return -EINVAL ; - req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); -DRM_DEBUG_KMS ("vcpi slots req=%d, avail=%d\n" , - req_slots, topology_state->avail_slots); -if (req_slots > topology_state->avail_slots) { -drm_dp_put_port (port); -return -ENOSPC ; + topology_state->vcpi_allocated = true; + +/* Find the current allocation for this port, if any */ +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + vcpi = pos; + prev_slots = vcpi->vcpi; + +/* +* This should never happen, unless the driver tries
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+* releasing and allocating the same VCPI allocation,
drivers/gpu/drm/drm_dp_mst_topology.c +4
-0+* which is an error +*/ +if (WARN_ON (!prev_slots)) { + DRM_ERROR("cannot allocate and releaseVCPI on
[MST PORT:%p] in the same state\n",drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ port); +return -EINVAL ; + }Allowing atomic changes to output state (like disabling it on one crtc,
and then enabling on the other, thus both releasing and re- allocating
the
vcpi in one go) is kinda the point of atomic. So definitely can't EINVAL
here.Sounds like we need more testcases in igt?
Quoted 148 lines
drivers/gpu/drm/drm_dp_mst_topology.c +5
-2+ +break ; + } } +if (!vcpi) + prev_slots =0 ; - topology_state->avail_slots -= req_slots; - DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state-Quoted 1 line
avail_slots);drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); + + DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d ->%d\n",
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ port->connector->base.id, port->connector-Quoted 1 line
name,
drivers/gpu/drm/drm_dp_mst_topology.c +22
-1+ port, prev_slots, req_slots); + +/* Add the new allocation to the state */ +if (!vcpi) { + vcpi =kzalloc (sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) { + ret = -ENOMEM ; + goto out; + } + vcpi->port =drm_dp_get_validated_port_ref (mgr, port); +if (!vcpi->port) { + ret = -EINVAL ; +kfree (vcpi); + goto out; + } +list_add (&vcpi->next, &topology_state->vcpis); + } + vcpi->vcpi = req_slots; + + ret = req_slots; +out:drm_dp_put_port (port); -return req_slots; +return ret; }EXPORT_SYMBOL (drm_dp_atomic_find_vcpi_slots);@@ -2661,31 +2722,66 @@EXPORT_SYMBOL (drm_dp_atomic_find_vcpi_slots); * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpislots
* @state: global atomic state
* @mgr: MST topology manager for the port
- * @slots: number of vcpi slots to release
+ * @port: The port to release the VCPI slots from
*
- * RETURNS:
- * 0 if @slots were added back to &drm_dp_mst_topology_state-Quoted 1 line
avail_slotsor- * negative error code
+ * Releases any VCPI slots that have been allocated to a port in
the
atomic
+ * state. Any atomic drivers which support MST must call this function
in
+ * their&drm_connector_helper_funcs.atomic_check()callback when
the
+ * connector will no longer have VCPI allocated (e.g. because it's
CRTC
was
+ * removed) when it had VCPI allocated in the previous atomic state.
+ *
+ * It is OK to call this even if @port has been removed from the
system.
+ * Additionally, it is OK to call this function multiple times on
the
same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_find_vcpi_slots()on the same @port in a single
atomic
check
+ * phase.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * 0 if all slots for this port were added back to
+ * &drm_dp_mst_topology_state.avail_slots or negative error code
*/
int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state
*state,
structdrm_dp_mst_topology_mgr
*mgr,drivers/gpu/drm/drm_dp_mst_topology.c +3
-1- int slots) + struct drm_dp_mst_port *port) {struct drm_dp_mst_topology_state *topology_state; +struct drm_dp_vcpi_allocation *pos; +bool found = false; topology_state = drm_atomic_get_mst_topology_state(state,mgr);
drivers/gpu/drm/drm_dp_mst_topology.c +0
-2if (IS_ERR (topology_state))return PTR_ERR (topology_state); -/* We cannot rely on port->vcpi.num_slots to update -* topology_state->avail_slots as the port may not exist if the parent
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* branch device was unplugged. This should be fixed by tracking
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* per-port slot allocation in drm_dp_mst_topology_state instead of
drivers/gpu/drm/drm_dp_mst_topology.c +0
-1-* depending on the caller to tell us how many slots to release.
drivers/gpu/drm/drm_dp_mst_topology.c +8
-4-*/ - topology_state->avail_slots += slots; -DRM_DEBUG_KMS ("vcpi slots released=%d, avail=%d\n" , - slots, topology_state->avail_slots); +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + found = true; +break ; + } + } +if (WARN_ON (!found)) { + DRM_ERROR("no VCPI for [MST PORT:%p] found in mststate %p\n",
drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ port, &topology_state->base); +return -EINVAL ; + } + + DRM_DEBUG_ATOMIC("[MST PORT:%p] VCPI %d -> 0\n", port, pos-Quoted 1 line
vcpi);
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+if (pos->vcpi) { +/* +* This should be safe-as the previous state will still hold a
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+* reference to this port until we swap states. If it's not,
drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+* the driver has a bookkeeping error +*/ +if (WARN_ON (drm_dp_put_port (port))) {The usual way to do this (instead of adding a return value to the normal
_putfunction) is something like
kref_put(port, function_that_with_just_a_WARNING_DRM_ERROR)avoids the need for patch 2.
Quoted 98 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_ERROR("cannot allocate and release VCPI on[MST
PORT:%p] in the same state\n",drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ port); +return -EINVAL ; + } + pos->vcpi =0 ; + }return 0 ; }@@ -3115,15 +3211,45 @@ static void
drm_dp_destroy_connector_work(struct work_struct *work)
static structdrm_private_state*drm_dp_mst_duplicate_state(struct drm_private_obj *obj){drivers/gpu/drm/drm_dp_mst_topology.c +4
-2-struct drm_dp_mst_topology_state *state; +struct drm_dp_mst_topology_state *state, *old_state = +to_dp_mst_topology_state (obj->state); +struct drm_dp_vcpi_allocation *pos, *vcpi; - state =kmemdup (obj->state,sizeof (*state),GFP_KERNEL ); + state =kmemdup (old_state,sizeof (*state),GFP_KERNEL );if (!state)return NULL ; __drm_atomic_helper_private_obj_duplicate_state(obj, &state-Quoted 1 line
base);
drivers/gpu/drm/drm_dp_mst_topology.c +13
-0+INIT_LIST_HEAD (&state->vcpis); + state->vcpi_allocated = false; + +list_for_each_entry (pos, &old_state->vcpis, next) { +/* Prune leftover freed VCPI allocations */ +if (!pos->vcpi) +continue ; + + vcpi =kmemdup (pos,sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) + goto fail; + +/* Take a plain old kref here and don't validate the port, as
drivers/gpu/drm/drm_dp_mst_topology.c +15
-0+* it's topology might not even exist anymore +*/ +kref_get (&vcpi->port->kref); +list_add (&vcpi->next, &state->vcpis); + } +return &state->base; + +fail: +list_for_each_entry_safe (pos, vcpi, &state->vcpis, next) { +drm_dp_put_port (pos->port); +kfree (pos); + } +kfree (state); + +return NULL ; } static void drm_dp_mst_destroy_state(struct drm_private_obj*obj,
drivers/gpu/drm/drm_dp_mst_topology.c +0
-0@@ -3131,14 +3257,116 @@ static voiddrm_dp_mst_destroy_state(struct
drm_private_obj*obj,
{drivers/gpu/drm/drm_dp_mst_topology.c +4
-0struct drm_dp_mst_topology_state *mst_state =to_dp_mst_topology_state (state); +struct drm_dp_vcpi_allocation *pos, *tmp; + +list_for_each_entry_safe (pos, tmp, &mst_state->vcpis, next) { +/* We only keep references to ports with non-zero VCPIs */
drivers/gpu/drm/drm_dp_mst_topology.c +6
-1+if (pos->vcpi) +drm_dp_put_port (pos->port); +kfree (pos); + }kfree (mst_state); } -static const struct drm_private_state_funcs mst_state_funcs = { +static inline int +drm_dp_mst_atomic_check_topology_state(struct
drm_dp_mst_topology_mgr
*mgr,drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ struct
drm_dp_mst_topology_state*mst_state)drivers/gpu/drm/drm_dp_mst_topology.c +6
-0+{ +struct drm_dp_vcpi_allocation *vcpi; +struct drm_dp_mst_port *port; +int avail_slots =63 , ret; + +/* There's no possible scenario where releasing VCPI or keeping it the
drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+* same would make the state invalid +*/ +if (!mst_state->vcpi_allocated) {Do we need this? If not I'm always in favour of removing code if it's
not
needed :-)Quoted 23 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p allocates noVCPI,
check skipped\n",drivers/gpu/drm/drm_dp_mst_topology.c +6
-0+ mgr, &mst_state->base); +return 0 ; + } + +list_for_each_entry (vcpi, &mst_state->vcpis, next) { +/* Releasing VCPI is always OK-even if the port is gone */
drivers/gpu/drm/drm_dp_mst_topology.c +2
-0+if (!vcpi->vcpi) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] releases allVCPI
slots\n",drivers/gpu/drm/drm_dp_mst_topology.c +7
-0+ vcpi->port); +continue ; + } + + port =drm_dp_get_validated_port_ref (mgr, vcpi->port); +if (!port) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] is gone butstill has
VCPI, cannot add new VCPI\n",drivers/gpu/drm/drm_dp_mst_topology.c
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ vcpi->port); +return -EINVAL ; + }For
find_vcpiwe've already done this. For unplugged vcpi, we can't do
this here I think - otherwise if you enable 2 mst outputs on the same
branch, and unplug both, old non-atomic userspace can never ever again
disable them because it can't disable both. The check (to skip already
deallocated ports) above isn't enough to handle that.Quoted 157 lines
drivers/gpu/drm/drm_dp_mst_topology.c +2
-0+ + DRM_DEBUG_ATOMIC("[MST PORT:%p] requires %d vcpislots\n",
drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ vcpi->port, vcpi->vcpi); + + avail_slots -= vcpi->vcpi; +if (avail_slots <0 ) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] not enoughVCPI slots
in mst state %p (avail=%d)\n",drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+ vcpi->port, mst_state, + avail_slots + vcpi->vcpi); + ret = -ENOSPC ; + goto port_fail; + } + +drm_dp_put_port (port); + } + DRM_DEBUG_ATOMIC("[MST MGR:%p] mst state %p VCPI avail=%dused=%d\n",
drivers/gpu/drm/drm_dp_mst_topology.c +11
-0+ mgr, mst_state, avail_slots, + 63 - avail_slots); + +return 0 ; +port_fail: +drm_dp_put_port (port); +return ret; +} + +/** +* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
+ * atomic update is valid
+ * @state: Pointer to the new &structdrm_dp_mst_topology_state
+ *
+ * Checks the given topology state for an atomic update to ensure
that
it's
+ * valid. This includes checking whether there's enough bandwidth
to
support
+ * the new VCPI allocations in the atomic update.
+ *
+ * Any atomic drivers supporting DP MST must make sure to call this
after
+ * checking the rest of their state in their
+ *&drm_mode_config_funcs.atomic_check()callback.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_atomic_release_vcpi_slots()
+ *
+ * Returns:
+ *
+ * 0 if the new state is valid, negative error code otherwise.
+ */drivers/gpu/drm/drm_dp_mst_topology.c +8
-0+int drm_dp_mst_atomic_check (struct drm_atomic_state *state) +{ +struct drm_dp_mst_topology_mgr *mgr; +struct drm_dp_mst_topology_state *mst_state; +int i, ret =0 ; + +for_each_new_mst_mgr_in_state (state, mgr, mst_state, i) { + ret = drm_dp_mst_atomic_check_topology_state(mgr,
mst_state);drivers/gpu/drm/drm_dp_mst_topology.c +9
-0+if (ret) +break ; + } + +return ret; +} +EXPORT_SYMBOL (drm_dp_mst_atomic_check); + +const struct drm_private_state_funcs
drm_dp_mst_topology_state_funcs=
{drivers/gpu/drm/drm_dp_mst_topology.c +3
-4.atomic_duplicate_state = drm_dp_mst_duplicate_state, .atomic_destroy_state = drm_dp_mst_destroy_state, }; +EXPORT_SYMBOL (drm_dp_mst_topology_state_funcs);/** * drm_atomic_get_mst_topology_state: get MST topology state @@ -3216,13 +3444,11 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, return -ENOMEM ; mst_state->mgr = mgr; - -/* max. time slots - one slot for MTP header */ - mst_state->avail_slots =63 ; +INIT_LIST_HEAD (&mst_state->vcpis);drm_atomic_private_obj_init (&mgr->base, &mst_state->base, - &mst_state_funcs); + &drm_dp_mst_topology_state_funcs);return 0 ;}
drivers/gpu/drm/i915/intel_display.c +0
-0@@ -12592,6 +12592,10 @@ static int intel_atomic_check(struct
drm_device
*dev,drivers/gpu/drm/i915/intel_display.c +4
-0"[modeset]" : "[fastset]"); } + ret =drm_dp_mst_atomic_check (state); +if (ret) +return ret; +if (any_ms) { ret =intel_modeset_checks (state);drivers/gpu/drm/i915/intel_dp_mst.c +0
-0@@ -41,8 +41,13 @@ static boolintel_dp_mst_compute_config(struct
intel_encoder*encoder,drivers/gpu/drm/i915/intel_dp_mst.c +6
-1struct drm_connector *connector = conn_state->connector;void *port =to_intel_connector (connector)->port;struct drm_atomic_state *state = pipe_config->base.state; +struct drm_crtc *crtc = pipe_config->base.crtc; +struct drm_crtc_state *old_crtc_state = +drm_atomic_get_old_crtc_state (state, crtc); +struct drm_crtc_state *new_crtc_state = &pipe_config->base;int bpp; -int lane_count, slots =0 ; +int lane_count, slots = +to_intel_crtc_state (old_crtc_state)->dp_m_n.tu; const struct drm_display_mode *adjusted_mode = &pipe_config-Quoted 1 line
base.adjusted_mode;
drivers/gpu/drm/i915/intel_dp_mst.c +0
-0int mst_pbn; bool constant_n = drm_dp_has_quirk(&intel_dp->desc,@@ -77,8 +82,12 @@ static boolintel_dp_mst_compute_config(struct
intel_encoder*encoder,
mst_pbn= drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock,
bpp);drivers/gpu/drm/i915/intel_dp_mst.c +1
-2pipe_config->pbn = mst_pbn; -/* Zombie connectors can't have VCPI slots */ -if (!drm_connector_is_unregistered (connector)) { +/* Only change VCPI allocation on actual mode changes, to prevent us
drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+* from trying to allocate VCPI to ports that no longer exist when we
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 4 lines
drivers/gpu/drm/i915/intel_dp_mst.c +4
-0+* may just be trying to disable DPMS on them +*/ +if (new_crtc_state->mode_changed || + new_crtc_state->connectors_changed) {Why exactly is this needed? We only call
compute_configon the enocoder
when enabling an output. Not when disabling one. Similar for the atomic
helpers and the various encoder callbacks (you have the same change in
the
nouveau code). Did this actually blow up while testing?Quoted 35 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-0slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp-Quoted 1 line
mst_mgr,drivers/gpu/drm/i915/intel_dp_mst.c +0
-0port,@@ -107,36 +116,39 @@ static boolintel_dp_mst_compute_config(struct
intel_encoder*encoder,drivers/gpu/drm/i915/intel_dp_mst.c +0
-1return true; } -static int intel_dp_mst_atomic_check(struct drm_connector*connector,
drivers/gpu/drm/i915/intel_dp_mst.c +13
-6- struct drm_connector_state *new_conn_state) +static int +intel_dp_mst_atomic_check (struct drm_connector *connector, +struct drm_connector_state *new_conn_state) { +struct intel_connector *intel_connector = +to_intel_connector (connector); +struct drm_dp_mst_topology_mgr *mgr = + &intel_connector->mst_port->mst_mgr; +struct drm_dp_mst_port *port = intel_connector->port;struct drm_atomic_state *state = new_conn_state->state; -struct drm_connector_state *old_conn_state; -struct drm_crtc *old_crtc; -struct drm_crtc_state *crtc_state; -int slots, ret =0 ; +struct drm_connector_state *old_conn_state = +drm_atomic_get_old_connector_state (state, connector); +struct drm_crtc *old_crtc = old_conn_state->crtc, + *new_crtc = new_conn_state->crtc; +struct drm_crtc_state *old_crtc_state, *new_crtc_state; - old_conn_state = drm_atomic_get_old_connector_state(state,connector);
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 1 line
drivers/gpu/drm/i915/intel_dp_mst.c +0
-1- old_crtc = old_conn_state->crtc;Bikeshed: Reworking all the above hides the real changes quite a bit.
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 12 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-6if (!old_crtc) -return ret; +return 0 ; - crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); - slots =to_intel_crtc_state (crtc_state)->dp_m_n.tu; -if (drm_atomic_crtc_needs_modeset (crtc_state) && slots >0 ) { -struct drm_dp_mst_topology_mgr *mgr; -struct drm_encoder *old_encoder; + old_crtc_state = drm_atomic_get_old_crtc_state(state,
old_crtc);drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+ if (!old_crtc_state ||!old_crtc_state would be a framework bug. Just remove that check.
drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 2 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ !to_intel_crtc_state(old_crtc_state)->dp_m_n.tu) +return 0 ;Hm, why do we need this? Having a DP port previously enabled with a 0
tu
sounds like a pretty big driver bug.drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 9 lines
drivers/gpu/drm/i915/intel_dp_mst.c +1
-2- old_encoder = old_conn_state->best_encoder; - mgr = &enc_to_mst (old_encoder)->primary->dp.mst_mgr; +/* If we switched CRTCs, clear the previous one's allocation */
drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+ new_crtc_state = drm_atomic_get_new_crtc_state(state,
old_crtc);drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+ if (new_crtc_state->connectors_changed && !new_crtc_state-Quoted 1 line
enable)
drivers/gpu/drm/i915/intel_dp_mst.c +1
-0+to_intel_crtc_state (new_crtc_state)->dp_m_n.tu =0 ;No idea why we need to clear this here? Shouldn't we just clear this
unconditionally if we release the allocation.Also naming convention, I'd call this
old_crtc_new_state, and maybeold_crtc_old_state. To make it clear these are the old/new states forold_crtc. Instead of old/new state for the new crtc.drivers/gpu/drm/i915/intel_dp_mst.c
Quoted 13 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-1- ret = drm_dp_atomic_release_vcpi_slots(state, mgr,slots);
drivers/gpu/drm/i915/intel_dp_mst.c +0
-2-if (ret) - DRM_DEBUG_KMS("failed releasing %d vcpislots:%d\n",
slots, ret);drivers/gpu/drm/i915/intel_dp_mst.c +0
-2-else - to_intel_crtc_state(crtc_state)->dp_m_n.tu =0;
drivers/gpu/drm/i915/intel_dp_mst.c +2
-2- } -return ret; +if (new_crtc) +return 0 ;Not sure why we shouldn't release this? If we handle this in helpers,
it
would remove piles of special casing code here I think. Plus remove
special code in the hlpers.Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ +return drm_dp_atomic_release_vcpi_slots (state, mgr, port); } static void intel_mst_disable_dp(struct intel_encoder *encoder,include/drm/drm_dp_mst_helper.h +0
-0include/drm/drm_dp_mst_helper.h struct drm_dp_payload { · line 410
Quoted 5 lines
include/drm/drm_dp_mst_helper.h +1
-0@@ -406,9 +406,16 @@ struct drm_dp_payload {#define to_dp_mst_topology_state (x) container_of(x, struct drm_dp_mst_topology_state, base) +struct drm_dp_vcpi_allocation {I think a comment here that this pointer holds a full reference (even
past
when the connector is unplugged) would be good. Otherwise the loop inrelease_vcpiwould be buggy.Quoted 52 lines
include/drm/drm_dp_mst_helper.h +7
-1+struct drm_dp_mst_port *port; +int vcpi; +struct list_head next; +}; +struct drm_dp_mst_topology_state {struct drm_private_state base; -int avail_slots; +struct list_head vcpis; +u8 vcpi_allocated;struct drm_dp_mst_topology_mgr *mgr; };@@ -619,14 +626,17 @@ void
drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);
int drm_dp_mst_topology_mgr_resume(structdrm_dp_mst_topology_mgr
*mgr);
structdrm_dp_mst_topology_state
*drm_atomic_get_mst_topology_state(structdrm_atomic_state*state,
st
ructdrm_dp_mst_topology_mgr*mgr);
-int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state*state,include/drm/drm_dp_mst_helper.h +0
-2-struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, intpbn);
-int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state*state,include/drm/drm_dp_mst_helper.h +0
-1-struct drm_dp_mst_topology_mgr *mgr,
include/drm/drm_dp_mst_helper.h +6
-1- int slots); +int __must_check +drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port,int pbn); +int __must_check +drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state*state,
include/drm/drm_dp_mst_helper.h +2
-0+struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port); int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr*mgr,
structdrm_dp_mst_port*port, boolpower_up);
+int__must_checkdrm_dp_mst_atomic_check(structdrm_atomic_state
*state);Quoted 6 lines
extern const struct
drm_private_state_funcsdrm_dp_mst_topology_state_funcs;--
2.19.1--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.chQuoted 4 lines
Quoted 4 lines
--
Cheers,
Lyude Paul--
Cheers,
Lyude PaulSignature · 2 lines
Cheers, Lyude Paul
Signature · 7 lines
Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ Nouveau mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/nouveau
Lyude Paul
maintainerRe: [PATCH v6 3/6] drm/dp_mst: Start tracking per-port VCPI allocations
In reply to Daniel VetterQuoted 215 lines · Daniel Vetter
Quoted 35 lines · Lyude Paul
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:/* We cannot rely on
port->vcpi.num_slotsto update
*topology_state->avail_slotsas the port may not exist if the parent
* branch device was unplugged. This should be fixed by tracking
* per-port slot allocation indrm_dp_mst_topology_stateinstead of
* depending on the caller to tell us how many slots to release. */That's not the only reason we should fix this: forcing the driver to track the VCPI allocations throughout a state's atomic check is error prone, because it means that extra care has to be taken with the order that
drm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()are called in in order to ensure idempotency. Currently the only driver actually using these helpers, i915, doesn't even do this correctly: multiple ->best_encoder() checks with i915's current implementation would not be idempotent and would over-allocate VCPI slots, something I learned trying to implement fallback retraining in MST.So: simplify this whole mess, and teach
drm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()to track the VCPI allocations for each port. This allows us to ensure idempotency without having to rely on the driver as much. Additionally: the driver doesn't need to do any kind of VCPI slot tracking anymore if it doesn't need it for it's own internal state.Additionally; this adds a new
drm_dp_mst_atomic_check()helper which must be used by atomic drivers to perform validity checks for the new VCPI allocations incurred by a state.Also: update the documentation and make it more obvious that these /must/ be called by /all/ atomic drivers supporting MST.
Quoted 9 lines
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This required a good bit of changing to when we calldrm_dp_find_vcpi_slots(), mainly that we need to ensure that we only redo VCPI allocations on actual mode or CRTC changes, notcrtc_state->activechanges. Additionally, we no longer take the registration of the DRM connector for each port into account because so long as we have a kref to the port in the new or previous atomic state, the connector will stay registered.I write an entire pile of small nitpits (still included most of them below), until I realized this here wont work. Delaying the call to destroy the connector (well, unregister it really) wreaks the design we've come up with thus far, resulting in most of my comments here.
Instead, all we need to do is delay the kfree(port) at the bottom of
drm_dp_destroy_port(). The vcpi allocation structure _only_ needs the pointer value to stay valid, as a lookup key. It doesn't care at all about anything actually stored in there. So the only thing we need to delay is the kfree. I think the simplest way to achieve this is to add a 2nd kref (port->kfree_refor something like that), with on reference held by the port itself (released indrm_dp_destroy_port), and the other one held as-needed by the vcpi allocation lists.I think if we go with this design instead of retrofitting a semantic change of the port lifetime itself, all the complications I complain about below should disappear.
Piles of comments below.
Cheers, Daniel
Quoted 147 lines
- Use the small changes to
drm_dp_put_port()to add even more error checking to make misusage of the helpers more obvious. I added this after having to chase down various use-after-free conditions that started popping up from the new helpers so no one else has to troubleshoot that.
- Move some accidentalDRM_DEBUG_KMS()calls toDRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both not be called on the same port in a single atomic check phase (but multiple calls to one or the other is OK)Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI allocations happen in a state. This makes the next change I'm about to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure that when ports are destroyed and still have VCPI allocations in the topology state, the only state changes allowed are releasing said ports' VCPI. This prevents a state with a mix of VCPI allocations from destroyed ports, and allocations from valid ports.Changes since v3:
- Don't release VCPI allocations in the topology state immediately indrm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and skip over them indrm_dp_mst_duplicate_state(). This makes it sodrm_dp_atomic_release_vcpi_slots()is still idempotent while also throwing warnings if the driver messes up it's book keeping and tries to release VCPI slots on a port that doesn't have any pre-existing VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"Changes since v2:
- Usekmemdup()for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states indrm_dp_mst_atomic_check()so the driver doesn't have to do it
- Fix documentation indrm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into it's own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth requirements. This is needed because originally we relied on the state duplication functions to prune any stale ports from the new state, which would prevent us from incorrectly considering their bandwidth requirements alongside legitimate new payloads.
- Add function references indrm_dp_atomic_release_vcpi_slots()- danvet
- Annotate atomic VCPI and atomic check functions with__must_check
- danvetChanges since v1:
- Don't use the now-removed ->atomic_check() for private objects hook, just give drivers a function to call themselvesSigned-off-by Lyude Paul <[email protected]>Cc Daniel Vetter <[email protected]>--- drivers/gpu/drm/drm_dp_mst_topology.c | 294 +++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 4 + drivers/gpu/drm/i915/intel_dp_mst.c | 64 +++--- include/drm/drm_dp_mst_helper.h | 24 ++- 4 files changed, 319 insertions(+), 67 deletions(-)drivers/gpu/drm/drm_dp_mst_topology.c +2
-3@@ -2617,21 +2617,42 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, }/** -* drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state +* drm_dp_atomic_find_vcpi_slots() - Find and add VCPI slots to the state * @state: global atomic state * @mgr: MST topology manager for the port * @port: port to find vcpi slots for * @pbn: bandwidth required for the mode in PBN * -* RETURNS: -* Total slots in the atomic state assigned for this port or error +* Allocates VCPI slots to @port, replacing any previous VCPI allocations it
+ * may have had. Any atomic drivers which support MST must call this function
+ * in their&drm_encoder_helper_funcs.atomic_check()callback to change the
+ * current VCPI allocation for the new state, but only when
+ * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed is set
+ * to ensure compatibility with userspace applications that still use the
+ * legacy modesetting UAPI.
+ *
+ * Allocations set by this function are not checked against the bandwidth
+ * restraints of @mgr until the driver callsdrm_dp_mst_atomic_check().
+ *
+ * Additionally, it is OK to call this function multiple times on the same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_release_vcpi_slots()in the same atomic check phase.
+ *
+ * See also:
+ *drm_dp_atomic_release_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * Total slots in the atomic state assigned for this port, or a negative error
+ * code if the port no longer exists
*/
int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state*state,drivers/gpu/drm/drm_dp_mst_topology.c +17
-7struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn) { struct drm_dp_mst_topology_state *topology_state; -int req_slots; +struct drm_dp_vcpi_allocation *pos, *vcpi =NULL ; +int prev_slots, req_slots, ret; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))@@ -2640,20 +2661,60 @@ int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state *state, port =drm_dp_get_validated_port_ref (mgr, port);if (port ==NULL )return -EINVAL ; - req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); -DRM_DEBUG_KMS ("vcpi slots req=%d, avail=%d\n" , - req_slots, topology_state->avail_slots); -if (req_slots > topology_state->avail_slots) { -drm_dp_put_port (port); -return -ENOSPC ; + topology_state->vcpi_allocated = true; + +/* Find the current allocation for this port, if any */ + list_for_each_entry(pos, &topology_state->vcpis, next) { + if (pos->port == port) { + vcpi = pos; + prev_slots = vcpi->vcpi; + + /* + * This should never happen, unless the driver tries + * releasing and allocating the same VCPI allocation, + * which is an error + */ + if (WARN_ON(!prev_slots)) { + DRM_ERROR("cannot allocate and release VCPI on [MST PORT:%p] in the same state\n",
Quoted 8 lines
drivers/gpu/drm/drm_dp_mst_topology.c int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, · line 2682
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ port); +return -EINVAL ; + }Allowing atomic changes to output state (like disabling it on one crtc, and then enabling on the other, thus both releasing and re-allocating the vcpi in one go) is kinda the point of atomic. So definitely can't EINVAL here.
Actually-so long the original assertion we had of not allowing "stale" (e.g. VCPIs from now-destroyed topologies) VCPI allocations and real VCPI allocations in the same atomic state is held true, this should be fine.
So: (and this answers the question below as well), the way this is supposed to work is that we just skip the whole VCPI validation sequence unless part of the state actually tried to add VCPI. This means userland can still disable displays one-by-one or set them as inactive, so long as it doesn't try to enable new MST displays until the point at which it's disabled everything leftover from the previous topology - since disabling displays won't allocate VCPI and thus skips the port validation checks here.
Quoted 215 lines
Sounds like we need more testcases in igt?
Quoted 108 lines
drivers/gpu/drm/drm_dp_mst_topology.c +31
-3+ +break ; + } } +if (!vcpi) + prev_slots =0 ; - topology_state->avail_slots -= req_slots; -DRM_DEBUG_KMS ("vcpi slots avail=%d" , topology_state->avail_slots); + req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); + +DRM_DEBUG_ATOMIC ("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n" , + port->connector->base.id, port->connector->name, + port, prev_slots, req_slots); + +/* Add the new allocation to the state */ +if (!vcpi) { + vcpi =kzalloc (sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) { + ret = -ENOMEM ; + goto out; + } + vcpi->port =drm_dp_get_validated_port_ref (mgr, port); +if (!vcpi->port) { + ret = -EINVAL ; +kfree (vcpi); + goto out; + } +list_add (&vcpi->next, &topology_state->vcpis); + } + vcpi->vcpi = req_slots; + + ret = req_slots; +out:drm_dp_put_port (port); -return req_slots; +return ret; }
EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);drivers/gpu/drm/drm_dp_mst_topology.c +1
-3@@ -2661,31 +2722,66 @@ EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);* drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots * @state: global atomic state * @mgr: MST topology manager for the port -* @slots: number of vcpi slots to release +* @port: The port to release the VCPI slots from * -* RETURNS: -* 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or
- * negative error code
+ * Releases any VCPI slots that have been allocated to a port in the atomic
+ * state. Any atomic drivers which support MST must call this function in
+ * their&drm_connector_helper_funcs.atomic_check()callback when the
+ * connector will no longer have VCPI allocated (e.g. because it's CRTC was
+ * removed) when it had VCPI allocated in the previous atomic state.
+ *
+ * It is OK to call this even if @port has been removed from the system.
+ * Additionally, it is OK to call this function multiple times on the same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_find_vcpi_slots()on the same @port in a single atomic check
+ * phase.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * 0 if all slots for this port were added back to
+ * &drm_dp_mst_topology_state.avail_slots or negative error code */
int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state*state,drivers/gpu/drm/drm_dp_mst_topology.c +13
-10struct drm_dp_mst_topology_mgr *mgr, - int slots) + struct drm_dp_mst_port *port) { struct drm_dp_mst_topology_state *topology_state; +struct drm_dp_vcpi_allocation *pos; +bool found = false; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))return PTR_ERR (topology_state); -/* We cannot rely on port->vcpi.num_slots to update -* topology_state->avail_slots as the port may not exist if the parent -* branch device was unplugged. This should be fixed by tracking -* per-port slot allocation in drm_dp_mst_topology_state instead of -* depending on the caller to tell us how many slots to release. -*/ - topology_state->avail_slots += slots; -DRM_DEBUG_KMS ("vcpi slots released=%d, avail=%d\n" , - slots, topology_state->avail_slots); +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + found = true; +break ; + } + } +if (WARN_ON (!found)) { +DRM_ERROR ("no VCPI for [MST PORT:%p] found in mst state %p\n" , + port, &topology_state->base); +return -EINVAL ;drivers/gpu/drm/drm_dp_mst_topology.c EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots); · line 2778
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10
-0+ } + +DRM_DEBUG_ATOMIC ("[MST PORT:%p] VCPI %d -> 0\n" , port, pos->vcpi); +if (pos->vcpi) { +/* +* This should be safe-as the previous state will still hold a +* reference to this port until we swap states. If it's not, +* the driver has a bookkeeping error +*/ +if (WARN_ON (drm_dp_put_port (port))) {The usual way to do this (instead of adding a return value to the normal
_putfunction) is something like
kref_put(port, function_that_with_just_a_WARNING_DRM_ERROR)avoids the need for patch 2.
Quoted 86 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_ERROR("cannot allocate and release VCPI on [MSTPORT:%p] in the same state\n",
drivers/gpu/drm/drm_dp_mst_topology.c +37
-2+ port); +return -EINVAL ; + } + pos->vcpi =0 ; + }return 0 ; }@@ -3115,15 +3211,45 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)static struct drm_private_state *drm_dp_mst_duplicate_state (struct drm_private_obj *obj) { -struct drm_dp_mst_topology_state *state; +struct drm_dp_mst_topology_state *state, *old_state = +to_dp_mst_topology_state (obj->state); +struct drm_dp_vcpi_allocation *pos, *vcpi; - state =kmemdup (obj->state,sizeof (*state),GFP_KERNEL ); + state =kmemdup (old_state,sizeof (*state),GFP_KERNEL );if (!state)return NULL ;__drm_atomic_helper_private_obj_duplicate_state (obj, &state->base); +INIT_LIST_HEAD (&state->vcpis); + state->vcpi_allocated = false; + +list_for_each_entry (pos, &old_state->vcpis, next) { +/* Prune leftover freed VCPI allocations */ +if (!pos->vcpi) +continue ; + + vcpi =kmemdup (pos,sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) + goto fail; + +/* Take a plain old kref here and don't validate the port, as +* it's topology might not even exist anymore +*/ +kref_get (&vcpi->port->kref); +list_add (&vcpi->next, &state->vcpis); + } +return &state->base; + +fail: +list_for_each_entry_safe (pos, vcpi, &state->vcpis, next) { +drm_dp_put_port (pos->port); +kfree (pos); + } +kfree (state); + +return NULL ; }static void drm_dp_mst_destroy_state(struct
drm_private_obj*obj,drivers/gpu/drm/drm_dp_mst_topology.c +15
-1@@ -3131,14 +3257,116 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, {struct drm_dp_mst_topology_state *mst_state =to_dp_mst_topology_state (state); +struct drm_dp_vcpi_allocation *pos, *tmp; + +list_for_each_entry_safe (pos, tmp, &mst_state->vcpis, next) { +/* We only keep references to ports with non-zero VCPIs */ +if (pos->vcpi) +drm_dp_put_port (pos->port); +kfree (pos); + }kfree (mst_state); } -static const struct drm_private_state_funcs mst_state_funcs = { +static inline int +drm_dp_mst_atomic_check_topology_state (struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_topology_state *mst_state) +{ +struct drm_dp_vcpi_allocation *vcpi; +struct drm_dp_mst_port *port; +int avail_slots =63 , ret;
Quoted 8 lines
drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, · line 3283
Quoted 5 lines
drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ +/* There's no possible scenario where releasing VCPI or keeping it the +* same would make the state invalid +*/ +if (!mst_state->vcpi_allocated) {Do we need this? If not I'm always in favour of removing code if it's not needed :-)
answered by my explanation above ^
Quoted 21 lines
Quoted 21 lines
drivers/gpu/drm/drm_dp_mst_topology.c +17
-0+ DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p allocates no VCPI, check skipped\n", + mgr, &mst_state->base); +return 0 ; + } + +list_for_each_entry (vcpi, &mst_state->vcpis, next) { +/* Releasing VCPI is always OK-even if the port is gone */ +if (!vcpi->vcpi) { +DRM_DEBUG_ATOMIC ("[MST PORT:%p] releases all VCPI slots\n", + vcpi->port); +continue ; + } + + port =drm_dp_get_validated_port_ref (mgr, vcpi->port); +if (!port) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] is gone but still has VCPI, cannot add new VCPI\n", + vcpi->port);
Quoted 8 lines
drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct · line 3308
Quoted 2 lines
drivers/gpu/drm/drm_dp_mst_topology.c +2
-0+return -EINVAL ; + }For
find_vcpiwe've already done this. For unplugged vcpi, we can't do this here I think - otherwise if you enable 2 mst outputs on the same branch, and unplug both, old non-atomic userspace can never ever again disable them because it can't disable both. The check (to skip already deallocated ports) above isn't enough to handle that.
answered by my explanation above ^
Quoted 136 lines
Quoted 136 lines
drivers/gpu/drm/drm_dp_mst_topology.c +7
-0+ +DRM_DEBUG_ATOMIC ("[MST PORT:%p] requires %d vcpi slots\n" , + vcpi->port, vcpi->vcpi); + + avail_slots -= vcpi->vcpi; +if (avail_slots <0 ) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] not enough VCPI slotsin mst state %p (avail=%d)\n",
drivers/gpu/drm/drm_dp_mst_topology.c +20
-0+ vcpi->port, mst_state, + avail_slots + vcpi->vcpi); + ret = -ENOSPC ; + goto port_fail; + } + +drm_dp_put_port (port); + } +DRM_DEBUG_ATOMIC ("[MST MGR:%p] mst state %p VCPI avail=%d used=%d\n" , + mgr, mst_state, avail_slots, +63 - avail_slots); + +return 0 ; +port_fail: +drm_dp_put_port (port); +return ret; +} + +/** +* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
+ * atomic update is valid
+ * @state: Pointer to the new &structdrm_dp_mst_topology_state
+ *
+ * Checks the given topology state for an atomic update to ensure that it's
+ * valid. This includes checking whether there's enough bandwidth to support
+ * the new VCPI allocations in the atomic update.
+ *
+ * Any atomic drivers supporting DP MST must make sure to call this after
+ * checking the rest of their state in their
+ *&drm_mode_config_funcs.atomic_check()callback.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_atomic_release_vcpi_slots()
+ *
+ * Returns:
+ *
+ * 0 if the new state is valid, negative error code otherwise.
+ */drivers/gpu/drm/drm_dp_mst_topology.c +20
-4+int drm_dp_mst_atomic_check (struct drm_atomic_state *state) +{ +struct drm_dp_mst_topology_mgr *mgr; +struct drm_dp_mst_topology_state *mst_state; +int i, ret =0 ; + +for_each_new_mst_mgr_in_state (state, mgr, mst_state, i) { + ret =drm_dp_mst_atomic_check_topology_state (mgr, mst_state); +if (ret) +break ; + } + +return ret; +} +EXPORT_SYMBOL (drm_dp_mst_atomic_check); + +const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs = { .atomic_duplicate_state = drm_dp_mst_duplicate_state, .atomic_destroy_state = drm_dp_mst_destroy_state, }; +EXPORT_SYMBOL (drm_dp_mst_topology_state_funcs);/** * drm_atomic_get_mst_topology_state: get MST topology state @@ -3216,13 +3444,11 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, return -ENOMEM ; mst_state->mgr = mgr; - -/* max. time slots - one slot for MTP header */ - mst_state->avail_slots =63 ; +INIT_LIST_HEAD (&mst_state->vcpis);drm_atomic_private_obj_init (&mgr->base, &mst_state->base, - &mst_state_funcs); + &drm_dp_mst_topology_state_funcs);return 0 ;}
drivers/gpu/drm/i915/intel_display.c +4
-0@@ -12592,6 +12592,10 @@ static int intel_atomic_check(struct drm_device *dev, "[modeset]" : "[fastset]"); } + ret =drm_dp_mst_atomic_check (state); +if (ret) +return ret; +if (any_ms) { ret =intel_modeset_checks (state);drivers/gpu/drm/i915/intel_dp_mst.c +6
-1@@ -41,8 +41,13 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, struct drm_connector *connector = conn_state->connector;void *port =to_intel_connector (connector)->port;struct drm_atomic_state *state = pipe_config->base.state; +struct drm_crtc *crtc = pipe_config->base.crtc; +struct drm_crtc_state *old_crtc_state = +drm_atomic_get_old_crtc_state (state, crtc); +struct drm_crtc_state *new_crtc_state = &pipe_config->base;int bpp; -int lane_count, slots =0 ; +int lane_count, slots = +to_intel_crtc_state (old_crtc_state)->dp_m_n.tu;const struct drm_display_mode *adjusted_mode = &pipe_config- >base.adjusted_mode;drivers/gpu/drm/i915/intel_dp_mst.c +0
-0int mst_pbn; bool constant_n = drm_dp_has_quirk(&intel_dp->desc,@@ -77,8 +82,12 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, mst_pbn =drm_dp_calc_pbn_mode (adjusted_mode->crtc_clock, bpp); pipe_config->pbn = mst_pbn;
Quoted 13 lines
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 90
Quoted 8 lines
drivers/gpu/drm/i915/intel_dp_mst.c +6
-2-/* Zombie connectors can't have VCPI slots */ -if (!drm_connector_is_unregistered (connector)) { +/* Only change VCPI allocation on actual mode changes, to prevent us +* from trying to allocate VCPI to ports that no longer exist when we +* may just be trying to disable DPMS on them +*/ +if (new_crtc_state->mode_changed || + new_crtc_state->connectors_changed) {Why exactly is this needed? We only call
compute_configon the enocoder when enabling an output. Not when disabling one. Similar for the atomic helpers and the various encoder callbacks (you have the same change in the nouveau code). Did this actually blow up while testing?
yes-it looked like we were calling compute_config on simple DPMS on->off chnges as well, this ended up making it so we wouldn't try changing the VCPI allocation on such commits.
ACK on all of the other comments here.
Quoted 152 lines
Quoted 23 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-0slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr,drivers/gpu/drm/i915/intel_dp_mst.c +8
-4port,@@ -107,36 +116,39 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, return true; } -static int intel_dp_mst_atomic_check (struct drm_connector *connector, -struct drm_connector_state *new_conn_state) +static int +intel_dp_mst_atomic_check (struct drm_connector *connector, +struct drm_connector_state *new_conn_state) { +struct intel_connector *intel_connector = +to_intel_connector (connector); +struct drm_dp_mst_topology_mgr *mgr = + &intel_connector->mst_port->mst_mgr; +struct drm_dp_mst_port *port = intel_connector->port;struct drm_atomic_state *state = new_conn_state->state; -struct drm_connector_state *old_conn_state; -struct drm_crtc *old_crtc;drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 121
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +5
-4-struct drm_crtc_state *crtc_state; -int slots, ret =0 ; +struct drm_connector_state *old_conn_state = +drm_atomic_get_old_connector_state (state, connector); +struct drm_crtc *old_crtc = old_conn_state->crtc, + *new_crtc = new_conn_state->crtc; +struct drm_crtc_state *old_crtc_state, *new_crtc_state; - old_conn_state =drm_atomic_get_old_connector_state (state, connector); - old_crtc = old_conn_state->crtc;Bikeshed: Reworking all the above hides the real changes quite a bit.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 139
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +3
-6if (!old_crtc) -return ret; +return 0 ; - crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); - slots =to_intel_crtc_state (crtc_state)->dp_m_n.tu; -if (drm_atomic_crtc_needs_modeset (crtc_state) && slots >0 ) { -struct drm_dp_mst_topology_mgr *mgr; -struct drm_encoder *old_encoder; + old_crtc_state =drm_atomic_get_old_crtc_state (state, old_crtc); + if (!old_crtc_state ||!old_crtc_state would be a framework bug. Just remove that check.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 142
Quoted 2 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ !to_intel_crtc_state(old_crtc_state)->dp_m_n.tu) +return 0 ;Hm, why do we need this? Having a DP port previously enabled with a 0 tu sounds like a pretty big driver bug.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 146
Quoted 7 lines
drivers/gpu/drm/i915/intel_dp_mst.c +4
-2- old_encoder = old_conn_state->best_encoder; - mgr = &enc_to_mst (old_encoder)->primary->dp.mst_mgr; +/* If we switched CRTCs, clear the previous one's allocation */ + new_crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); +if (new_crtc_state->connectors_changed && !new_crtc_state->enable) +to_intel_crtc_state (new_crtc_state)->dp_m_n.tu =0 ;No idea why we need to clear this here? Shouldn't we just clear this unconditionally if we release the allocation.
Also naming convention, I'd call this
old_crtc_new_state, and maybeold_crtc_old_state. To make it clear these are the old/new states forold_crtc. Instead of old/new state for the new crtc.drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 149
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-7- ret =drm_dp_atomic_release_vcpi_slots (state, mgr, slots); -if (ret) -DRM_DEBUG_KMS ("failed releasing %d vcpi slots:%d\n" , slots, ret); -else -to_intel_crtc_state (crtc_state)->dp_m_n.tu =0 ; - } -return ret; +if (new_crtc) +return 0 ;Not sure why we shouldn't release this? If we handle this in helpers, it would remove piles of special casing code here I think. Plus remove special code in the hlpers.
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ +return drm_dp_atomic_release_vcpi_slots (state, mgr, port); } static void intel_mst_disable_dp(struct intel_encoder *encoder,include/drm/drm_dp_mst_helper.h +0
-0include/drm/drm_dp_mst_helper.h struct drm_dp_payload { · line 410
Quoted 5 lines
include/drm/drm_dp_mst_helper.h +1
-0@@ -406,9 +406,16 @@ struct drm_dp_payload {#define to_dp_mst_topology_state (x) container_of(x, struct drm_dp_mst_topology_state, base) +struct drm_dp_vcpi_allocation {I think a comment here that this pointer holds a full reference (even past when the connector is unplugged) would be good. Otherwise the loop in
release_vcpiwould be buggy.Quoted 44 lines
include/drm/drm_dp_mst_helper.h +16
-7+struct drm_dp_mst_port *port; +int vcpi; +struct list_head next; +}; +struct drm_dp_mst_topology_state {struct drm_private_state base; -int avail_slots; +struct list_head vcpis; +u8 vcpi_allocated;struct drm_dp_mst_topology_mgr *mgr; };@@ -619,14 +626,17 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);int drm_dp_mst_topology_mgr_resume (struct drm_dp_mst_topology_mgr *mgr);struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state (struct drm_atomic_state *state,struct drm_dp_mst_topology_mgr *mgr); -int drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -struct drm_dp_mst_port *port,int pbn); -int drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -int slots); +int __must_check +drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port,int pbn); +int __must_check +drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port);int drm_dp_send_power_updown_phy (struct drm_dp_mst_topology_mgr *mgr,struct drm_dp_mst_port *port,bool power_up); +int __must_check drm_dp_mst_atomic_check (struct drm_atomic_state *state);extern const struct
drm_private_state_funcsdrm_dp_mst_topology_state_funcs;Signature · 1 line
2.19.1
Signature · 7 lines
Cheers, Lyude Paul _______________________________________________ Intel-gfx mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/intel-gfx
A message that is not in the archive (sent off-list or not yet received): 62577f81fbccbbc1f2c06183abdb490e35a5b0d5.camel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org
Re: [PATCH v6 3/6] drm/dp_mst: Start tracking per-port VCPI allocations
Quoted 228 lines · Lyude Paul
Quoted 215 lines · Daniel Vetter
Quoted 35 lines · Lyude Paul
There has been a TODO waiting for quite a long time in
drm_dp_mst_topology.c:/* We cannot rely on
port->vcpi.num_slotsto update
*topology_state->avail_slotsas the port may not exist if the parent
* branch device was unplugged. This should be fixed by tracking
* per-port slot allocation indrm_dp_mst_topology_stateinstead of
* depending on the caller to tell us how many slots to release. */That's not the only reason we should fix this: forcing the driver to track the VCPI allocations throughout a state's atomic check is error prone, because it means that extra care has to be taken with the order that
drm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()are called in in order to ensure idempotency. Currently the only driver actually using these helpers, i915, doesn't even do this correctly: multiple ->best_encoder() checks with i915's current implementation would not be idempotent and would over-allocate VCPI slots, something I learned trying to implement fallback retraining in MST.So: simplify this whole mess, and teach
drm_dp_atomic_find_vcpi_slots()anddrm_dp_atomic_release_vcpi_slots()to track the VCPI allocations for each port. This allows us to ensure idempotency without having to rely on the driver as much. Additionally: the driver doesn't need to do any kind of VCPI slot tracking anymore if it doesn't need it for it's own internal state.Additionally; this adds a new
drm_dp_mst_atomic_check()helper which must be used by atomic drivers to perform validity checks for the new VCPI allocations incurred by a state.Also: update the documentation and make it more obvious that these /must/ be called by /all/ atomic drivers supporting MST.
Quoted 9 lines
Changes since v6:
- Keep a kref to all of the ports we have allocations on. This required a good bit of changing to when we calldrm_dp_find_vcpi_slots(), mainly that we need to ensure that we only redo VCPI allocations on actual mode or CRTC changes, notcrtc_state->activechanges. Additionally, we no longer take the registration of the DRM connector for each port into account because so long as we have a kref to the port in the new or previous atomic state, the connector will stay registered.I write an entire pile of small nitpits (still included most of them below), until I realized this here wont work. Delaying the call to destroy the connector (well, unregister it really) wreaks the design we've come up with thus far, resulting in most of my comments here.
Instead, all we need to do is delay the kfree(port) at the bottom of
drm_dp_destroy_port(). The vcpi allocation structure _only_ needs the pointer value to stay valid, as a lookup key. It doesn't care at all about anything actually stored in there. So the only thing we need to delay is the kfree. I think the simplest way to achieve this is to add a 2nd kref (port->kfree_refor something like that), with on reference held by the port itself (released indrm_dp_destroy_port), and the other one held as-needed by the vcpi allocation lists.I think if we go with this design instead of retrofitting a semantic change of the port lifetime itself, all the complications I complain about below should disappear.
Piles of comments below.
Cheers, Daniel
Quoted 147 lines
- Use the small changes to
drm_dp_put_port()to add even more error checking to make misusage of the helpers more obvious. I added this after having to chase down various use-after-free conditions that started popping up from the new helpers so no one else has to troubleshoot that.
- Move some accidentalDRM_DEBUG_KMS()calls toDRM_DEBUG_ATOMIC()
- Update documentation again, note that find/release() should both not be called on the same port in a single atomic check phase (but multiple calls to one or the other is OK)Changes since v4:
- Don't skip the atomic checks for VCPI allocations if no new VCPI allocations happen in a state. This makes the next change I'm about to list here a lot easier to implement.
- Don't ignore VCPI allocations on destroyed ports, instead ensure that when ports are destroyed and still have VCPI allocations in the topology state, the only state changes allowed are releasing said ports' VCPI. This prevents a state with a mix of VCPI allocations from destroyed ports, and allocations from valid ports.Changes since v3:
- Don't release VCPI allocations in the topology state immediately indrm_dp_atomic_release_vcpi_slots(), instead mark them as 0 and skip over them indrm_dp_mst_duplicate_state(). This makes it sodrm_dp_atomic_release_vcpi_slots()is still idempotent while also throwing warnings if the driver messes up it's book keeping and tries to release VCPI slots on a port that doesn't have any pre-existing VCPI allocation - danvet
- Change mst_state/state in some debugging messages to "mst state"Changes since v2:
- Usekmemdup()for duplicating MST state - danvet
- Move port validation out of duplicate state callback - danvet
- Handle looping through MST topology states indrm_dp_mst_atomic_check()so the driver doesn't have to do it
- Fix documentation indrm_dp_atomic_find_vcpi_slots()
- Move the atomic check for each individual topology state into it's own function, reduces indenting
- Don't consider "stale" MST ports when calculating the bandwidth requirements. This is needed because originally we relied on the state duplication functions to prune any stale ports from the new state, which would prevent us from incorrectly considering their bandwidth requirements alongside legitimate new payloads.
- Add function references indrm_dp_atomic_release_vcpi_slots()- danvet
- Annotate atomic VCPI and atomic check functions with__must_check
- danvetChanges since v1:
- Don't use the now-removed ->atomic_check() for private objects hook, just give drivers a function to call themselvesSigned-off-by Lyude Paul <[email protected]>Cc Daniel Vetter <[email protected]>--- drivers/gpu/drm/drm_dp_mst_topology.c | 294 +++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_display.c | 4 + drivers/gpu/drm/i915/intel_dp_mst.c | 64 +++--- include/drm/drm_dp_mst_helper.h | 24 ++- 4 files changed, 319 insertions(+), 67 deletions(-)drivers/gpu/drm/drm_dp_mst_topology.c +2
-3@@ -2617,21 +2617,42 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, }/** -* drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state +* drm_dp_atomic_find_vcpi_slots() - Find and add VCPI slots to the state * @state: global atomic state * @mgr: MST topology manager for the port * @port: port to find vcpi slots for * @pbn: bandwidth required for the mode in PBN * -* RETURNS: -* Total slots in the atomic state assigned for this port or error +* Allocates VCPI slots to @port, replacing any previous VCPI allocations it
+ * may have had. Any atomic drivers which support MST must call this function
+ * in their&drm_encoder_helper_funcs.atomic_check()callback to change the
+ * current VCPI allocation for the new state, but only when
+ * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed is set
+ * to ensure compatibility with userspace applications that still use the
+ * legacy modesetting UAPI.
+ *
+ * Allocations set by this function are not checked against the bandwidth
+ * restraints of @mgr until the driver callsdrm_dp_mst_atomic_check().
+ *
+ * Additionally, it is OK to call this function multiple times on the same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_release_vcpi_slots()in the same atomic check phase.
+ *
+ * See also:
+ *drm_dp_atomic_release_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * Total slots in the atomic state assigned for this port, or a negative error
+ * code if the port no longer exists
*/
int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state*state,drivers/gpu/drm/drm_dp_mst_topology.c +17
-7struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn) { struct drm_dp_mst_topology_state *topology_state; -int req_slots; +struct drm_dp_vcpi_allocation *pos, *vcpi =NULL ; +int prev_slots, req_slots, ret; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))@@ -2640,20 +2661,60 @@ int drm_dp_atomic_find_vcpi_slots(structdrm_atomic_state *state, port =drm_dp_get_validated_port_ref (mgr, port);if (port ==NULL )return -EINVAL ; - req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); -DRM_DEBUG_KMS ("vcpi slots req=%d, avail=%d\n" , - req_slots, topology_state->avail_slots); -if (req_slots > topology_state->avail_slots) { -drm_dp_put_port (port); -return -ENOSPC ; + topology_state->vcpi_allocated = true; + +/* Find the current allocation for this port, if any */ + list_for_each_entry(pos, &topology_state->vcpis, next) { + if (pos->port == port) { + vcpi = pos; + prev_slots = vcpi->vcpi; + + /* + * This should never happen, unless the driver tries + * releasing and allocating the same VCPI allocation, + * which is an error + */ + if (WARN_ON(!prev_slots)) { + DRM_ERROR("cannot allocate and release VCPI on [MST PORT:%p] in the same state\n",Quoted 8 lines
drivers/gpu/drm/drm_dp_mst_topology.c int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, · line 2682
Quoted 3 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3
-0+ port); +return -EINVAL ; + }Allowing atomic changes to output state (like disabling it on one crtc, and then enabling on the other, thus both releasing and re-allocating the vcpi in one go) is kinda the point of atomic. So definitely can't EINVAL here.
Actually-so long the original assertion we had of not allowing "stale" (e.g. VCPIs from now-destroyed topologies) VCPI allocations and real VCPI allocations in the same atomic state is held true, this should be fine.
Quoted 7 lines
So: (and this answers the question below as well), the way this is supposed to work is that we just skip the whole VCPI validation sequence unless part of the state actually tried to add VCPI. This means userland can still disable displays one-by-one or set them as inactive, so long as it doesn't try to enable new MST displays until the point at which it's disabled everything leftover from the previous topology - since disabling displays won't allocate VCPI and thus skips the port validation checks here.
This doesn't sound robust if there's a race, e.g. userspace tries to light up a display right at the time when we unplug a different (already enabled output). I think to fix this properly we need to replace all the dp_get_validated_port calls in all the vcpi/payload places with the lower-level kref that only prevents the kfree.
Fundamentally hotunplug is still racy, but this way I think there's at least no surprises. And we could probably remove a bunch of the hacks around cleanup paths.
Quoted 222 lines
Quoted 214 lines
Sounds like we need more testcases in igt?
Quoted 108 lines
drivers/gpu/drm/drm_dp_mst_topology.c +31
-3+ +break ; + } } +if (!vcpi) + prev_slots =0 ; - topology_state->avail_slots -= req_slots; -DRM_DEBUG_KMS ("vcpi slots avail=%d" , topology_state->avail_slots); + req_slots =DIV_ROUND_UP (pbn, mgr->pbn_div); + +DRM_DEBUG_ATOMIC ("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n" , + port->connector->base.id, port->connector->name, + port, prev_slots, req_slots); + +/* Add the new allocation to the state */ +if (!vcpi) { + vcpi =kzalloc (sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) { + ret = -ENOMEM ; + goto out; + } + vcpi->port =drm_dp_get_validated_port_ref (mgr, port); +if (!vcpi->port) { + ret = -EINVAL ; +kfree (vcpi); + goto out; + } +list_add (&vcpi->next, &topology_state->vcpis); + } + vcpi->vcpi = req_slots; + + ret = req_slots; +out:drm_dp_put_port (port); -return req_slots; +return ret; }
EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);drivers/gpu/drm/drm_dp_mst_topology.c +1
-3@@ -2661,31 +2722,66 @@ EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);* drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots * @state: global atomic state * @mgr: MST topology manager for the port -* @slots: number of vcpi slots to release +* @port: The port to release the VCPI slots from * -* RETURNS: -* 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or
- * negative error code
+ * Releases any VCPI slots that have been allocated to a port in the atomic
+ * state. Any atomic drivers which support MST must call this function in
+ * their&drm_connector_helper_funcs.atomic_check()callback when the
+ * connector will no longer have VCPI allocated (e.g. because it's CRTC was
+ * removed) when it had VCPI allocated in the previous atomic state.
+ *
+ * It is OK to call this even if @port has been removed from the system.
+ * Additionally, it is OK to call this function multiple times on the same
+ * @port as needed. It is not OK however, to call this function and
+ *drm_dp_atomic_find_vcpi_slots()on the same @port in a single atomic check
+ * phase.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_mst_atomic_check()
+ *
+ * Returns:
+ * 0 if all slots for this port were added back to
+ * &drm_dp_mst_topology_state.avail_slots or negative error code */
int drm_dp_atomic_release_vcpi_slots(structdrm_atomic_state*state,drivers/gpu/drm/drm_dp_mst_topology.c +13
-10struct drm_dp_mst_topology_mgr *mgr, - int slots) + struct drm_dp_mst_port *port) { struct drm_dp_mst_topology_state *topology_state; +struct drm_dp_vcpi_allocation *pos; +bool found = false; topology_state =drm_atomic_get_mst_topology_state (state, mgr);if (IS_ERR (topology_state))return PTR_ERR (topology_state); -/* We cannot rely on port->vcpi.num_slots to update -* topology_state->avail_slots as the port may not exist if the parent -* branch device was unplugged. This should be fixed by tracking -* per-port slot allocation in drm_dp_mst_topology_state instead of -* depending on the caller to tell us how many slots to release. -*/ - topology_state->avail_slots += slots; -DRM_DEBUG_KMS ("vcpi slots released=%d, avail=%d\n" , - slots, topology_state->avail_slots); +list_for_each_entry (pos, &topology_state->vcpis, next) { +if (pos->port == port) { + found = true; +break ; + } + } +if (WARN_ON (!found)) { +DRM_ERROR ("no VCPI for [MST PORT:%p] found in mst state %p\n" , + port, &topology_state->base); +return -EINVAL ;drivers/gpu/drm/drm_dp_mst_topology.c EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots); · line 2778
Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10
-0+ } + +DRM_DEBUG_ATOMIC ("[MST PORT:%p] VCPI %d -> 0\n" , port, pos->vcpi); +if (pos->vcpi) { +/* +* This should be safe-as the previous state will still hold a +* reference to this port until we swap states. If it's not, +* the driver has a bookkeeping error +*/ +if (WARN_ON (drm_dp_put_port (port))) {The usual way to do this (instead of adding a return value to the normal
_putfunction) is something like
kref_put(port, function_that_with_just_a_WARNING_DRM_ERROR)avoids the need for patch 2.
Quoted 86 lines
drivers/gpu/drm/drm_dp_mst_topology.c +1
-0+ DRM_ERROR("cannot allocate and release VCPI on [MSTPORT:%p] in the same state\n",
drivers/gpu/drm/drm_dp_mst_topology.c +37
-2+ port); +return -EINVAL ; + } + pos->vcpi =0 ; + }return 0 ; }@@ -3115,15 +3211,45 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)static struct drm_private_state *drm_dp_mst_duplicate_state (struct drm_private_obj *obj) { -struct drm_dp_mst_topology_state *state; +struct drm_dp_mst_topology_state *state, *old_state = +to_dp_mst_topology_state (obj->state); +struct drm_dp_vcpi_allocation *pos, *vcpi; - state =kmemdup (obj->state,sizeof (*state),GFP_KERNEL ); + state =kmemdup (old_state,sizeof (*state),GFP_KERNEL );if (!state)return NULL ;__drm_atomic_helper_private_obj_duplicate_state (obj, &state->base); +INIT_LIST_HEAD (&state->vcpis); + state->vcpi_allocated = false; + +list_for_each_entry (pos, &old_state->vcpis, next) { +/* Prune leftover freed VCPI allocations */ +if (!pos->vcpi) +continue ; + + vcpi =kmemdup (pos,sizeof (*vcpi),GFP_KERNEL ); +if (!vcpi) + goto fail; + +/* Take a plain old kref here and don't validate the port, as +* it's topology might not even exist anymore +*/ +kref_get (&vcpi->port->kref); +list_add (&vcpi->next, &state->vcpis); + } +return &state->base; + +fail: +list_for_each_entry_safe (pos, vcpi, &state->vcpis, next) { +drm_dp_put_port (pos->port); +kfree (pos); + } +kfree (state); + +return NULL ; }static void drm_dp_mst_destroy_state(struct
drm_private_obj*obj,drivers/gpu/drm/drm_dp_mst_topology.c +15
-1@@ -3131,14 +3257,116 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, {struct drm_dp_mst_topology_state *mst_state =to_dp_mst_topology_state (state); +struct drm_dp_vcpi_allocation *pos, *tmp; + +list_for_each_entry_safe (pos, tmp, &mst_state->vcpis, next) { +/* We only keep references to ports with non-zero VCPIs */ +if (pos->vcpi) +drm_dp_put_port (pos->port); +kfree (pos); + }kfree (mst_state); } -static const struct drm_private_state_funcs mst_state_funcs = { +static inline int +drm_dp_mst_atomic_check_topology_state (struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_topology_state *mst_state) +{ +struct drm_dp_vcpi_allocation *vcpi; +struct drm_dp_mst_port *port; +int avail_slots =63 , ret;Quoted 8 lines
drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, · line 3283
Quoted 5 lines
drivers/gpu/drm/drm_dp_mst_topology.c +5
-0+ +/* There's no possible scenario where releasing VCPI or keeping it the +* same would make the state invalid +*/ +if (!mst_state->vcpi_allocated) {Do we need this? If not I'm always in favour of removing code if it's not needed :-)
Quoted 1 line
answered by my explanation above ^
If removing this breaks the code then I think it's a bit fragile. And see above, open to races (yes unlikely ones, but then all of mst is full of unlikely races it that still seem to happen eventually).
I think it's better if we can remove all these special cases (which should be possible if we drop the get_validated_port below).
Quoted 181 lines
Quoted 21 lines
Quoted 21 lines
drivers/gpu/drm/drm_dp_mst_topology.c +17
-0+ DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p allocates no VCPI, check skipped\n", + mgr, &mst_state->base); +return 0 ; + } + +list_for_each_entry (vcpi, &mst_state->vcpis, next) { +/* Releasing VCPI is always OK-even if the port is gone */ +if (!vcpi->vcpi) { +DRM_DEBUG_ATOMIC ("[MST PORT:%p] releases all VCPI slots\n", + vcpi->port); +continue ; + } + + port =drm_dp_get_validated_port_ref (mgr, vcpi->port); +if (!port) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] is gone but still has VCPI, cannot add new VCPI\n", + vcpi->port);Quoted 8 lines
drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct · line 3308
Quoted 2 lines
drivers/gpu/drm/drm_dp_mst_topology.c +2
-0+return -EINVAL ; + }For
find_vcpiwe've already done this. For unplugged vcpi, we can't do this here I think - otherwise if you enable 2 mst outputs on the same branch, and unplug both, old non-atomic userspace can never ever again disable them because it can't disable both. The check (to skip already deallocated ports) above isn't enough to handle that.answered by my explanation above ^
Quoted 136 lines
Quoted 136 lines
drivers/gpu/drm/drm_dp_mst_topology.c +7
-0+ +DRM_DEBUG_ATOMIC ("[MST PORT:%p] requires %d vcpi slots\n" , + vcpi->port, vcpi->vcpi); + + avail_slots -= vcpi->vcpi; +if (avail_slots <0 ) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] not enough VCPI slotsin mst state %p (avail=%d)\n",
drivers/gpu/drm/drm_dp_mst_topology.c +20
-0+ vcpi->port, mst_state, + avail_slots + vcpi->vcpi); + ret = -ENOSPC ; + goto port_fail; + } + +drm_dp_put_port (port); + } +DRM_DEBUG_ATOMIC ("[MST MGR:%p] mst state %p VCPI avail=%d used=%d\n" , + mgr, mst_state, avail_slots, +63 - avail_slots); + +return 0 ; +port_fail: +drm_dp_put_port (port); +return ret; +} + +/** +* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
+ * atomic update is valid
+ * @state: Pointer to the new &structdrm_dp_mst_topology_state
+ *
+ * Checks the given topology state for an atomic update to ensure that it's
+ * valid. This includes checking whether there's enough bandwidth to support
+ * the new VCPI allocations in the atomic update.
+ *
+ * Any atomic drivers supporting DP MST must make sure to call this after
+ * checking the rest of their state in their
+ *&drm_mode_config_funcs.atomic_check()callback.
+ *
+ * See also:
+ *drm_dp_atomic_find_vcpi_slots()
+ *drm_dp_atomic_release_vcpi_slots()
+ *
+ * Returns:
+ *
+ * 0 if the new state is valid, negative error code otherwise.
+ */drivers/gpu/drm/drm_dp_mst_topology.c +20
-4+int drm_dp_mst_atomic_check (struct drm_atomic_state *state) +{ +struct drm_dp_mst_topology_mgr *mgr; +struct drm_dp_mst_topology_state *mst_state; +int i, ret =0 ; + +for_each_new_mst_mgr_in_state (state, mgr, mst_state, i) { + ret =drm_dp_mst_atomic_check_topology_state (mgr, mst_state); +if (ret) +break ; + } + +return ret; +} +EXPORT_SYMBOL (drm_dp_mst_atomic_check); + +const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs = { .atomic_duplicate_state = drm_dp_mst_duplicate_state, .atomic_destroy_state = drm_dp_mst_destroy_state, }; +EXPORT_SYMBOL (drm_dp_mst_topology_state_funcs);/** * drm_atomic_get_mst_topology_state: get MST topology state @@ -3216,13 +3444,11 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, return -ENOMEM ; mst_state->mgr = mgr; - -/* max. time slots - one slot for MTP header */ - mst_state->avail_slots =63 ; +INIT_LIST_HEAD (&mst_state->vcpis);drm_atomic_private_obj_init (&mgr->base, &mst_state->base, - &mst_state_funcs); + &drm_dp_mst_topology_state_funcs);return 0 ;}
drivers/gpu/drm/i915/intel_display.c +4
-0@@ -12592,6 +12592,10 @@ static int intel_atomic_check(struct drm_device *dev, "[modeset]" : "[fastset]"); } + ret =drm_dp_mst_atomic_check (state); +if (ret) +return ret; +if (any_ms) { ret =intel_modeset_checks (state);drivers/gpu/drm/i915/intel_dp_mst.c +6
-1@@ -41,8 +41,13 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, struct drm_connector *connector = conn_state->connector;void *port =to_intel_connector (connector)->port;struct drm_atomic_state *state = pipe_config->base.state; +struct drm_crtc *crtc = pipe_config->base.crtc; +struct drm_crtc_state *old_crtc_state = +drm_atomic_get_old_crtc_state (state, crtc); +struct drm_crtc_state *new_crtc_state = &pipe_config->base;int bpp; -int lane_count, slots =0 ; +int lane_count, slots = +to_intel_crtc_state (old_crtc_state)->dp_m_n.tu;const struct drm_display_mode *adjusted_mode = &pipe_config- >base.adjusted_mode;drivers/gpu/drm/i915/intel_dp_mst.c +0
-0int mst_pbn; bool constant_n = drm_dp_has_quirk(&intel_dp->desc,@@ -77,8 +82,12 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, mst_pbn =drm_dp_calc_pbn_mode (adjusted_mode->crtc_clock, bpp); pipe_config->pbn = mst_pbn;Quoted 13 lines
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 90
Quoted 8 lines
drivers/gpu/drm/i915/intel_dp_mst.c +6
-2-/* Zombie connectors can't have VCPI slots */ -if (!drm_connector_is_unregistered (connector)) { +/* Only change VCPI allocation on actual mode changes, to prevent us +* from trying to allocate VCPI to ports that no longer exist when we +* may just be trying to disable DPMS on them +*/ +if (new_crtc_state->mode_changed || + new_crtc_state->connectors_changed) {Why exactly is this needed? We only call
compute_configon the enocoder when enabling an output. Not when disabling one. Similar for the atomic helpers and the various encoder callbacks (you have the same change in the nouveau code). Did this actually blow up while testing?
Quoted 3 lines
yes-it looked like we were calling
compute_configon simple DPMSon->offchnges as well, this ended up making it so we wouldn't try changing the VCPI allocation on such commits.
Ah right, we discussed all this when adding the is_unregistered() check. If we don't change the port lifetime (but only when kfree is called) then there's no need to change anything here. Allows us to keep the nice consistency of the is_unregistered() check in driver code.
I should have rewritten this when I realized that you've changed the lifetime rules in a different way than what I had in mind, as-is my comment didn't make much sense.
-Daniel
Quoted 157 lines
ACK on all of the other comments here.
Quoted 152 lines
Quoted 23 lines
drivers/gpu/drm/i915/intel_dp_mst.c +0
-0slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr,drivers/gpu/drm/i915/intel_dp_mst.c +8
-4port,@@ -107,36 +116,39 @@ static bool intel_dp_mst_compute_config(structintel_encoder *encoder, return true; } -static int intel_dp_mst_atomic_check (struct drm_connector *connector, -struct drm_connector_state *new_conn_state) +static int +intel_dp_mst_atomic_check (struct drm_connector *connector, +struct drm_connector_state *new_conn_state) { +struct intel_connector *intel_connector = +to_intel_connector (connector); +struct drm_dp_mst_topology_mgr *mgr = + &intel_connector->mst_port->mst_mgr; +struct drm_dp_mst_port *port = intel_connector->port;struct drm_atomic_state *state = new_conn_state->state; -struct drm_connector_state *old_conn_state; -struct drm_crtc *old_crtc;drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 121
Quoted 10 lines
drivers/gpu/drm/i915/intel_dp_mst.c +5
-4-struct drm_crtc_state *crtc_state; -int slots, ret =0 ; +struct drm_connector_state *old_conn_state = +drm_atomic_get_old_connector_state (state, connector); +struct drm_crtc *old_crtc = old_conn_state->crtc, + *new_crtc = new_conn_state->crtc; +struct drm_crtc_state *old_crtc_state, *new_crtc_state; - old_conn_state =drm_atomic_get_old_connector_state (state, connector); - old_crtc = old_conn_state->crtc;Bikeshed: Reworking all the above hides the real changes quite a bit.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 139
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +3
-6if (!old_crtc) -return ret; +return 0 ; - crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); - slots =to_intel_crtc_state (crtc_state)->dp_m_n.tu; -if (drm_atomic_crtc_needs_modeset (crtc_state) && slots >0 ) { -struct drm_dp_mst_topology_mgr *mgr; -struct drm_encoder *old_encoder; + old_crtc_state =drm_atomic_get_old_crtc_state (state, old_crtc); + if (!old_crtc_state ||!old_crtc_state would be a framework bug. Just remove that check.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct · line 142
Quoted 2 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ !to_intel_crtc_state(old_crtc_state)->dp_m_n.tu) +return 0 ;Hm, why do we need this? Having a DP port previously enabled with a 0 tu sounds like a pretty big driver bug.
drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 146
Quoted 7 lines
drivers/gpu/drm/i915/intel_dp_mst.c +4
-2- old_encoder = old_conn_state->best_encoder; - mgr = &enc_to_mst (old_encoder)->primary->dp.mst_mgr; +/* If we switched CRTCs, clear the previous one's allocation */ + new_crtc_state =drm_atomic_get_new_crtc_state (state, old_crtc); +if (new_crtc_state->connectors_changed && !new_crtc_state->enable) +to_intel_crtc_state (new_crtc_state)->dp_m_n.tu =0 ;No idea why we need to clear this here? Shouldn't we just clear this unconditionally if we release the allocation.
Also naming convention, I'd call this
old_crtc_new_state, and maybeold_crtc_old_state. To make it clear these are the old/new states forold_crtc. Instead of old/new state for the new crtc.drivers/gpu/drm/i915/intel_dp_mst.c static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, · line 149
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-7- ret =drm_dp_atomic_release_vcpi_slots (state, mgr, slots); -if (ret) -DRM_DEBUG_KMS ("failed releasing %d vcpi slots:%d\n" , slots, ret); -else -to_intel_crtc_state (crtc_state)->dp_m_n.tu =0 ; - } -return ret; +if (new_crtc) +return 0 ;Not sure why we shouldn't release this? If we handle this in helpers, it would remove piles of special casing code here I think. Plus remove special code in the hlpers.
Quoted 11 lines
drivers/gpu/drm/i915/intel_dp_mst.c +2
-0+ +return drm_dp_atomic_release_vcpi_slots (state, mgr, port); } static void intel_mst_disable_dp(struct intel_encoder *encoder,include/drm/drm_dp_mst_helper.h +0
-0include/drm/drm_dp_mst_helper.h struct drm_dp_payload { · line 410
Quoted 5 lines
include/drm/drm_dp_mst_helper.h +1
-0@@ -406,9 +406,16 @@ struct drm_dp_payload {#define to_dp_mst_topology_state (x) container_of(x, struct drm_dp_mst_topology_state, base) +struct drm_dp_vcpi_allocation {I think a comment here that this pointer holds a full reference (even past when the connector is unplugged) would be good. Otherwise the loop in
release_vcpiwould be buggy.Quoted 44 lines
include/drm/drm_dp_mst_helper.h +16
-7+struct drm_dp_mst_port *port; +int vcpi; +struct list_head next; +}; +struct drm_dp_mst_topology_state {struct drm_private_state base; -int avail_slots; +struct list_head vcpis; +u8 vcpi_allocated;struct drm_dp_mst_topology_mgr *mgr; };@@ -619,14 +626,17 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);int drm_dp_mst_topology_mgr_resume (struct drm_dp_mst_topology_mgr *mgr);struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state (struct drm_atomic_state *state,struct drm_dp_mst_topology_mgr *mgr); -int drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -struct drm_dp_mst_port *port,int pbn); -int drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, -struct drm_dp_mst_topology_mgr *mgr, -int slots); +int __must_check +drm_dp_atomic_find_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port,int pbn); +int __must_check +drm_dp_atomic_release_vcpi_slots (struct drm_atomic_state *state, +struct drm_dp_mst_topology_mgr *mgr, +struct drm_dp_mst_port *port);int drm_dp_send_power_updown_phy (struct drm_dp_mst_topology_mgr *mgr,struct drm_dp_mst_port *port,bool power_up); +int __must_check drm_dp_mst_atomic_check (struct drm_atomic_state *state);extern const struct
drm_private_state_funcsdrm_dp_mst_topology_state_funcs;Signature · 1 line
2.19.1Signature · 2 lines
Cheers, Lyude Paul
Signature · 7 lines
Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ Nouveau mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/nouveau
PATCH v6 5/6 drm/nouveau: Stop unsetting mstc->port, check connector registration
In reply to a message not in the archiveSame thing we did in i915, but for nouveau now.
--- drivers/gpu/drm/nouveau/dispnv50/disp.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-)
drivers/gpu/drm/nouveau/dispnv50/disp.c +7 -8
@@ -698,8 +698,10 @@ nv50_msto_cleanup(struct nv50_msto *msto)struct nv50_mstm *mstm = mstc->mstm;NV_ATOMIC (drm,"%s: msto cleanup\n" , msto->encoder.name); -if (mstc->port && mstc->port->vcpi.vcpi >0 && !nv50_msto_payload (msto)) +if (!drm_connector_is_unregistered (&mstc->connector) && + mstc->port->vcpi.vcpi >0 && !nv50_msto_payload (msto))drm_dp_mst_deallocate_vcpi (&mstm->mgr, mstc->port); +if (msto->disabled) { msto->mstc =NULL ; msto->head =NULL ;
@@ -725,7 +727,8 @@ nv50_msto_prepare(struct nv50_msto *msto) };NV_ATOMIC (drm,"%s: msto prepare\n" , msto->encoder.name); -if (mstc->port && mstc->port->vcpi.vcpi >0 ) { +if (!drm_connector_is_unregistered (&mstc->connector) && + mstc->port->vcpi.vcpi >0 ) {struct drm_dp_payload *payload =nv50_msto_payload (msto);if (payload) { args.vcpi.start_slot = payload->start_slot;
@@ -822,7 +825,7 @@ nv50_msto_disable(struct drm_encoder *encoder)struct nv50_mstc *mstc = msto->mstc;struct nv50_mstm *mstm = mstc->mstm; -if (mstc->port) +if (!drm_connector_is_unregistered (&mstc->connector))drm_dp_mst_reset_vcpi_slots (&mstm->mgr, mstc->port); mstm->outp->update (mstm->outp, msto->head->base.index,NULL ,0 ,0 );
@@ -935,7 +938,7 @@ nv50_mstc_detect(struct drm_connector *connector, bool force)enum drm_connector_status conn_status;int ret; -if (!mstc->port) +if (drm_connector_is_unregistered (&mstc->connector))return connector_status_disconnected; ret =pm_runtime_get_sync (connector->dev->dev);
@@ -1073,10 +1076,6 @@ nv50_mstm_destroy_connector(struct drm_dp_mst_topology_mgr *mgr,drm_fb_helper_remove_one_connector (&drm->fbcon->helper, &mstc->connector); -drm_modeset_lock (&drm->dev->mode_config.connection_mutex,NULL ); - mstc->port =NULL ; -drm_modeset_unlock (&drm->dev->mode_config.connection_mutex); -drm_connector_put (&mstc->connector); }
Signature · 6 lines
2.19.1 _______________________________________________ Nouveau mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/nouveau
Lyude Paul
maintainerPATCH v6 4/6 drm/dp_mst: Check payload count in drm_dp_mst_atomic_check()
In reply to Lyude PaulIt occurred to me that we never actually check this! So let's start doing that.
--- drivers/gpu/drm/drm_dp_mst_topology.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/gpu/drm/drm_dp_mst_topology.c +8 -1
@@ -3275,7 +3275,7 @@ drm_dp_mst_atomic_check_topology_state(struct drm_dp_mst_topology_mgr *mgr, {struct drm_dp_vcpi_allocation *vcpi;struct drm_dp_mst_port *port; -int avail_slots =63 , ret; +int avail_slots =63 , payload_count =0 , ret;/* There's no possible scenario where releasing VCPI or keeping it the * same would make the state invalid
@@ -3313,6 +3313,13 @@ drm_dp_mst_atomic_check_topology_state(struct drm_dp_mst_topology_mgr *mgr, goto port_fail; } +if (++payload_count > mgr->max_payloads) { +DRM_DEBUG_ATOMIC ("[MST MGR:%p] state %p has too many payloads (max=%d)\n" , + mgr, mst_state, mgr->max_payloads); + ret = -EINVAL ; + goto port_fail; + } +drm_dp_put_port (port); } DRM_DEBUG_ATOMIC("[MST MGR:%p] mst state %p VCPI avail=%d used=%d\n",
Signature · 6 lines
2.19.1 _______________________________________________ Intel-gfx mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Lyude Paul
maintainerPATCH v6 6/6 drm/nouveau: Use atomic VCPI helpers for MST
In reply to Lyude PaulCurrently, nouveau uses the yolo method of setting up MST displays: it uses the old VCPI helpers (drm_dp_find_vcpi_slots()) for computing the display configuration. These helpers don't take care to make sure they take a reference to the mstb port that they're checking, and additionally don't actually check whether or not the topology still has enough bandwidth to provide the VCPI tokens required.
So, drop usage of the old helpers and move entirely over to the atomic helpers.
Changes since v5:
- Update nv50_msto_atomic_check() and nv50_mstc_atomic_check() to the new requirements for drm_dp_atomic_find_vcpi_slots() and drm_dp_atomic_release_vcpi_slots()
--- drivers/gpu/drm/nouveau/dispnv50/disp.c | 52 ++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 6 deletions(-)
drivers/gpu/drm/nouveau/dispnv50/disp.c +46 -6
@@ -750,16 +750,22 @@ nv50_msto_atomic_check(struct drm_encoder *encoder,struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { - struct nv50_mstc *mstc = nv50_mstc(conn_state->connector); + struct drm_atomic_state *state = crtc_state->state; +struct drm_connector *connector = conn_state->connector; +struct nv50_mstc *mstc =nv50_mstc (connector);struct nv50_mstm *mstm = mstc->mstm; -int bpp = conn_state->connector->display_info.bpc *3 ; +int bpp = connector->display_info.bpc *3 ;int slots; - mstc->pbn =drm_dp_calc_pbn_mode (crtc_state->adjusted_mode.clock, bpp); + mstc->pbn =drm_dp_calc_pbn_mode (crtc_state->adjusted_mode.clock, + bpp); - slots =drm_dp_find_vcpi_slots (&mstm->mgr, mstc->pbn); -if (slots <0 ) -return slots; +if (crtc_state->connectors_changed || crtc_state->mode_changed) { + slots =drm_dp_atomic_find_vcpi_slots (state, &mstm->mgr, + mstc->port, mstc->pbn); +if (slots <0 ) +return slots; + }return nv50_outp_atomic_check_view (encoder, crtc_state, conn_state, mstc->native);
@@ -923,12 +929,42 @@ nv50_mstc_get_modes(struct drm_connector *connector)return ret; } +static int +nv50_mstc_atomic_check (struct drm_connector *connector, +struct drm_connector_state *new_conn_state) +{ +struct drm_atomic_state *state = new_conn_state->state; +struct nv50_mstc *mstc =nv50_mstc (connector); +struct drm_dp_mst_topology_mgr *mgr = &mstc->mstm->mgr; +struct drm_connector_state *old_conn_state = +drm_atomic_get_old_connector_state (state, connector); +struct drm_crtc_state *old_crtc_state; +struct drm_crtc *new_crtc = new_conn_state->crtc, + *old_crtc = old_conn_state->crtc; + +if (!old_crtc) +return 0 ; + + old_crtc_state =drm_atomic_get_old_crtc_state (state, old_crtc); +if (!old_crtc_state || !old_crtc_state->enable) +return 0 ; + +if (new_crtc) +return 0 ; + +/* This connector will be left without an enabled CRTC, so its VCPI +* must be released here +*/ +return drm_dp_atomic_release_vcpi_slots (state, mgr, mstc->port); +} +static const struct drm_connector_helper_funcs nv50_mstc_help = { .get_modes = nv50_mstc_get_modes, .mode_valid = nv50_mstc_mode_valid, .best_encoder = nv50_mstc_best_encoder, .atomic_best_encoder = nv50_mstc_atomic_best_encoder, + .atomic_check = nv50_mstc_atomic_check, }; static enum drm_connector_status
@@ -2108,6 +2144,10 @@ nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)return ret; } + ret =drm_dp_mst_atomic_check (state); +if (ret) +return ret; +return 0 ; }
Signature · 6 lines
2.19.1 _______________________________________________ Intel-gfx mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Patchwork
CI✗ Fi.CI.CHECKPATCH: warning for drm/dp_mst: Improve VCPI helpers, use in nouveau (rev6)
In reply to Lyude Paul== Series Details ==
== Series Details ==
Series: drm/dp_mst: Improve VCPI helpers, use in nouveau (rev6)
URL : https://patchwork.freedesktop.org/series/51412/
State : warning
== Summary ==
$ dim checkpatch origin/drm-tip 446143844eea drm/dp_mst: Add some atomic state iterator macros -:7: WARNING:COMMIT_MESSAGE: Missing commit description - Add an appropriate one
-:55: CHECK:MACRO_ARG_REUSE: Macro argument reuse '__state' - possible side-effects? #55: FILE: include/drm/drm_dp_mst_helper.h:669:
+#define for_each_oldnew_mst_mgr_in_state(__state, mgr, old_state, new_state, __i) \
+ for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \
+ for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), &(new_state), (__i)))
-:55: CHECK:MACRO_ARG_REUSE: Macro argument reuse '__i' - possible side-effects? #55: FILE: include/drm/drm_dp_mst_helper.h:669:
+#define for_each_oldnew_mst_mgr_in_state(__state, mgr, old_state, new_state, __i) \
+ for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \
+ for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), &(new_state), (__i)))
-:57: WARNING:LONG_LINE: line over 100 characters
#57: FILE: include/drm/drm_dp_mst_helper.h:671:
+ for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), &(new_state), (__i)))
-:72: CHECK:MACRO_ARG_REUSE: Macro argument reuse '__state' - possible side-effects? #72: FILE: include/drm/drm_dp_mst_helper.h:686:
+#define for_each_old_mst_mgr_in_state(__state, mgr, old_state, __i) \
+ for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \
+ for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), NULL, (__i)))
-:72: CHECK:MACRO_ARG_REUSE: Macro argument reuse '__i' - possible side-effects? #72: FILE: include/drm/drm_dp_mst_helper.h:686:
+#define for_each_old_mst_mgr_in_state(__state, mgr, old_state, __i) \
+ for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \
+ for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), NULL, (__i)))
-:74: WARNING:LONG_LINE: line over 100 characters
#74: FILE: include/drm/drm_dp_mst_helper.h:688:
+ for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), &(old_state), NULL, (__i)))
-:90: CHECK:MACRO_ARG_REUSE: Macro argument reuse '__state' - possible side-effects? #90: FILE: include/drm/drm_dp_mst_helper.h:704:
+#define for_each_new_mst_mgr_in_state(__state, mgr, new_state, __i) \
+ for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \
+ for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), NULL, &(new_state), (__i)))
-:90: CHECK:MACRO_ARG_REUSE: Macro argument reuse '__i' - possible side-effects? #90: FILE: include/drm/drm_dp_mst_helper.h:704:
+#define for_each_new_mst_mgr_in_state(__state, mgr, new_state, __i) \
+ for ((__i) = 0; (__i) < (__state)->num_private_objs; (__i)++) \
+ for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), NULL, &(new_state), (__i)))
-:92: WARNING:LONG_LINE: line over 100 characters
#92: FILE: include/drm/drm_dp_mst_helper.h:706:
+ for_each_if(__drm_dp_mst_state_iter_get((__state), &(mgr), NULL, &(new_state), (__i)))
total: 0 errors, 4 warnings, 6 checks, 81 lines checked69ff1fdbfe4e drm/dp_mst: Return kref_put() result from drm_dp_put_port() 783d19c2f54c drm/dp_mst: Start tracking per-port VCPI allocations de21a67c03da drm/dp_mst: Check payload count in drm_dp_mst_atomic_check() e0be5ec00684 drm/nouveau: Stop unsetting mstc->port, check connector registration 70fc28c8d19f drm/nouveau: Use atomic VCPI helpers for MST
_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Patchwork
CI✗ Fi.CI.SPARSE: warning for drm/dp_mst: Improve VCPI helpers, use in nouveau (rev6)
In reply to Lyude Paul== Series Details ==
== Series Details ==
Series: drm/dp_mst: Improve VCPI helpers, use in nouveau (rev6)
URL : https://patchwork.freedesktop.org/series/51412/
State : warning
== Summary ==
$ dim sparse origin/drm-tip Sparse version: v0.5.2 Commit: drm/dp_mst: Add some atomic state iterator macros Okay!
Commit: drm/dp_mst: Return kref_put() result from drm_dp_put_port() Okay!
Commit: drm/dp_mst: Start tracking per-port VCPI allocations +./include/linux/slab.h:332:43: warning: dubious: x & !y
Commit: drm/dp_mst: Check payload count in drm_dp_mst_atomic_check() Okay!
Commit: drm/nouveau: Stop unsetting mstc->port, check connector registration Okay!
Commit: drm/nouveau: Use atomic VCPI helpers for MST Okay!
_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Patchwork
CI✓ Fi.CI.BAT: success for drm/dp_mst: Improve VCPI helpers, use in nouveau (rev6)
In reply to Lyude Paul== Series Details ==
== Series Details ==
Series: drm/dp_mst: Improve VCPI helpers, use in nouveau (rev6)
URL : https://patchwork.freedesktop.org/series/51412/
State : success
== Summary ==
= CI Bug Log - changes from CI_DRM_5149 -> Patchwork_10835 =
== Summary - SUCCESS ==
No regressions found.
External URL: https://patchwork.freedesktop.org/api/1.0/series/51412/revisions/6/mbox/
== Known issues ==
Here are the changes found in Patchwork_10835 that come from known issues:
=== IGT changes ===
==== Issues hit ====
igt@gem_ctx_create@basic-files:
fi-bsw-kefka: PASS -> FAIL (fdo#108656)
igt@kms_frontbuffer_tracking@basic:
fi-icl-u: PASS -> FAIL (fdo#103167)
igt@kms_pipe_crc_basic@nonblocking-crc-pipe-a-frame-sequence:
fi-byt-clapper: PASS -> FAIL (fdo#103191, fdo#107362)
==== Possible fixes ====
igt@gem_ctx_create@basic-files:
fi-bsw-n3050: FAIL (fdo#108656) -> PASS
igt@gem_ctx_switch@basic-default:
fi-icl-u2: DMESG-WARN (fdo#107724) -> PASS
igt@kms_chamelium@common-hpd-after-suspend:
fi-skl-6700k2: WARN (fdo#108680) -> PASS
igt@kms_pipe_crc_basic@read-crc-pipe-a:
fi-byt-clapper: FAIL (fdo#107362) -> PASS +1
igt@kms_pipe_crc_basic@suspend-read-crc-pipe-a:
fi-blb-e6850: INCOMPLETE (fdo#107718) -> PASS
igt@prime_vgem@basic-fence-flip:
fi-gdg-551: FAIL (fdo#103182) -> PASS
==== Warnings ====
igt@drv_selftest@live_contexts:
fi-icl-u: INCOMPLETE (fdo#108315) -> DMESG-FAIL (fdo#108569) fdo#103167 https://bugs.freedesktop.org/show_bug.cgi?id=103167
fdo#103182 https://bugs.freedesktop.org/show_bug.cgi?id=103182
fdo#103191 https://bugs.freedesktop.org/show_bug.cgi?id=103191
fdo#107362 https://bugs.freedesktop.org/show_bug.cgi?id=107362
fdo#107718 https://bugs.freedesktop.org/show_bug.cgi?id=107718
fdo#107724 https://bugs.freedesktop.org/show_bug.cgi?id=107724
fdo#108315 https://bugs.freedesktop.org/show_bug.cgi?id=108315
fdo#108569 https://bugs.freedesktop.org/show_bug.cgi?id=108569
fdo#108656 https://bugs.freedesktop.org/show_bug.cgi?id=108656
fdo#108680 https://bugs.freedesktop.org/show_bug.cgi?id=108680
== Participating hosts (51 -> 45) ==
Missing (6): fi-ilk-m540 fi-hsw-4200u fi-byt-squawks fi-bsw-cyan fi-snb-2520m fi-ctg-p8600
== Build changes ==
* Linux: CI_DRM_5149 -> Patchwork_10835
CI_DRM_5149: c9e35242429a47b1c550dce738933bbd1739d897 @ git://anongit.freedesktop.org/gfx-ci/linux
IGT_4715: 111593c49d812a4f4ff9ab0ef053a3ab88a6f73f @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
Patchwork_10835: 70fc28c8d19f2f366bb26f0a89b3dc42dafb25d5 @ git://anongit.freedesktop.org/gfx-ci/linux
== Linux commits ==
70fc28c8d19f drm/nouveau: Use atomic VCPI helpers for MST e0be5ec00684 drm/nouveau: Stop unsetting mstc->port, check connector registration de21a67c03da drm/dp_mst: Check payload count in drm_dp_mst_atomic_check() 783d19c2f54c drm/dp_mst: Start tracking per-port VCPI allocations 69ff1fdbfe4e drm/dp_mst: Return kref_put() result from drm_dp_put_port() 446143844eea drm/dp_mst: Add some atomic state iterator macros
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_10835/issues.html
_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Patchwork
CI✗ Fi.CI.IGT: failure for drm/dp_mst: Improve VCPI helpers, use in nouveau (rev6)
In reply to Lyude Paul== Series Details ==
== Series Details ==
Series: drm/dp_mst: Improve VCPI helpers, use in nouveau (rev6)
URL : https://patchwork.freedesktop.org/series/51412/
State : failure
== Summary ==
= CI Bug Log - changes from CI_DRM_5149_full -> Patchwork_10835_full =
== Summary - FAILURE ==
Serious unknown changes coming with Patchwork_10835_full absolutely need to be verified manually.
If you think the reported changes have nothing to do with the changes introduced in Patchwork_10835_full, please notify your bug team to allow them to document this new failure mode, which will reduce false positives in CI.
== Possible new issues ==
Here are the unknown changes that may have been introduced in Patchwork_10835_full:
=== IGT changes ===
==== Possible regressions ====
igt@kms_flip_tiling@flip-changes-tiling:
shard-skl: NOTRUN -> FAIL
==== Warnings ====
igt@pm_rc6_residency@rc6-accuracy:
shard-snb: PASS -> SKIP== Known issues ==
Here are the changes found in Patchwork_10835_full that come from known issues:
=== IGT changes ===
==== Issues hit ====
igt@gem_eio@in-flight-suspend:
shard-kbl: PASS -> INCOMPLETE (fdo#106702, fdo#103665)
igt@gem_ppgtt@blt-vs-render-ctx0:
shard-kbl: PASS -> INCOMPLETE (fdo#106023, fdo#103665, fdo#106887)
igt@kms_busy@extended-modeset-hang-newfb-render-a:
shard-skl: NOTRUN -> DMESG-WARN (fdo#107956)
igt@kms_busy@extended-pageflip-modeset-hang-oldfb-render-c:
shard-skl: PASS -> DMESG-WARN (fdo#107956)
igt@kms_color@pipe-a-degamma:
shard-skl: NOTRUN -> FAIL (fdo#104782, fdo#108145)
igt@kms_color@pipe-b-legacy-gamma:
shard-apl: PASS -> FAIL (fdo#104782)
igt@kms_cursor_crc@cursor-64x21-sliding:
shard-apl: PASS -> FAIL (fdo#103232) +3
igt@kms_fbcon_fbt@psr:
shard-skl: NOTRUN -> FAIL (fdo#107882)
igt@kms_flip@flip-vs-expired-vblank-interruptible:
shard-glk: PASS -> FAIL (fdo#102887, fdo#105363)
igt@kms_flip_tiling@flip-to-x-tiled:
shard-skl: NOTRUN -> FAIL (fdo#108134)
igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-pwrite:
shard-apl: PASS -> FAIL (fdo#103167)
igt@kms_frontbuffer_tracking@psr-1p-primscrn-pri-shrfb-draw-blt:
shard-skl: NOTRUN -> FAIL (fdo#103167) +2
igt@kms_plane_alpha_blend@pipe-a-alpha-basic:
shard-skl: NOTRUN -> FAIL (fdo#107815, fdo#108145) +1
igt@kms_plane_alpha_blend@pipe-a-alpha-transparant-fb:
shard-skl: NOTRUN -> FAIL (fdo#108145) +1
igt@kms_plane_multiple@atomic-pipe-a-tiling-y:
shard-apl: PASS -> FAIL (fdo#103166)
igt@kms_plane_multiple@atomic-pipe-b-tiling-yf:
shard-glk: PASS -> FAIL (fdo#103166) +1
igt@kms_setmode@basic:
shard-apl: PASS -> FAIL (fdo#99912)
igt@pm_rpm@modeset-lpsp-stress:
shard-skl: NOTRUN -> INCOMPLETE (fdo#107807) +2
igt@pm_rpm@sysfs-read:
shard-skl: PASS -> INCOMPLETE (fdo#107807) +2
==== Possible fixes ====
igt@kms_busy@extended-modeset-hang-newfb-with-reset-render-c:
shard-hsw: DMESG-WARN (fdo#107956) -> PASS
igt@kms_chv_cursor_fail@pipe-a-128x128-top-edge:
shard-kbl: DMESG-FAIL (fdo#105602, fdo#104671, fdo#103558) -> PASS
igt@kms_color@pipe-a-legacy-gamma:
shard-kbl: DMESG-WARN (fdo#105602, fdo#103558) -> PASS +1
shard-apl: DMESG-WARN (fdo#105602, fdo#103558) -> PASS +1
igt@kms_color@pipe-b-degamma:
shard-apl: FAIL (fdo#104782) -> PASS
igt@kms_cursor_crc@cursor-256x256-onscreen:
shard-apl: FAIL (fdo#103232) -> PASS
igt@kms_cursor_crc@cursor-256x256-random:
shard-glk: FAIL (fdo#103232) -> PASS
igt@kms_flip@flip-vs-expired-vblank-interruptible:
shard-kbl: FAIL (fdo#102887, fdo#105363) -> PASS
igt@kms_plane@plane-position-covered-pipe-c-planes:
shard-apl: FAIL (fdo#103166) -> PASS
igt@kms_setmode@basic:
shard-glk: FAIL (fdo#99912) -> PASS
shard-hsw: FAIL (fdo#99912) -> PASS fdo#102887 https://bugs.freedesktop.org/show_bug.cgi?id=102887
fdo#103166 https://bugs.freedesktop.org/show_bug.cgi?id=103166
fdo#103167 https://bugs.freedesktop.org/show_bug.cgi?id=103167
fdo#103232 https://bugs.freedesktop.org/show_bug.cgi?id=103232
fdo#103558 https://bugs.freedesktop.org/show_bug.cgi?id=103558
fdo#103665 https://bugs.freedesktop.org/show_bug.cgi?id=103665
fdo#104671 https://bugs.freedesktop.org/show_bug.cgi?id=104671
fdo#104782 https://bugs.freedesktop.org/show_bug.cgi?id=104782
fdo#105363 https://bugs.freedesktop.org/show_bug.cgi?id=105363
fdo#105602 https://bugs.freedesktop.org/show_bug.cgi?id=105602
fdo#106023 https://bugs.freedesktop.org/show_bug.cgi?id=106023
fdo#106702 https://bugs.freedesktop.org/show_bug.cgi?id=106702
fdo#106887 https://bugs.freedesktop.org/show_bug.cgi?id=106887
fdo#107807 https://bugs.freedesktop.org/show_bug.cgi?id=107807
fdo#107815 https://bugs.freedesktop.org/show_bug.cgi?id=107815
fdo#107882 https://bugs.freedesktop.org/show_bug.cgi?id=107882
fdo#107956 https://bugs.freedesktop.org/show_bug.cgi?id=107956
fdo#108134 https://bugs.freedesktop.org/show_bug.cgi?id=108134
fdo#108145 https://bugs.freedesktop.org/show_bug.cgi?id=108145
fdo#99912 https://bugs.freedesktop.org/show_bug.cgi?id=99912
== Participating hosts (7 -> 6) ==
Missing (1): shard-iclb
== Build changes ==
* Linux: CI_DRM_5149 -> Patchwork_10835
CI_DRM_5149: c9e35242429a47b1c550dce738933bbd1739d897 @ git://anongit.freedesktop.org/gfx-ci/linux
IGT_4715: 111593c49d812a4f4ff9ab0ef053a3ab88a6f73f @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
Patchwork_10835: 70fc28c8d19f2f366bb26f0a89b3dc42dafb25d5 @ git://anongit.freedesktop.org/gfx-ci/linux
piglit_4509: fdc5a4ca11124ab8413c7988896eec4c97336694 @ git://anongit.freedesktop.org/piglit
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_10835/shards.html
_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Patchwork
CI✓ Fi.CI.BAT: success for drm/dp_mst: Improve VCPI helpers, use in nouveau (rev6)
In reply to Lyude Paul== Series Details ==
== Series Details ==
Series: drm/dp_mst: Improve VCPI helpers, use in nouveau (rev6)
URL : https://patchwork.freedesktop.org/series/51412/
State : success
== Summary ==
= CI Bug Log - changes from CI_DRM_5152 -> Patchwork_10843 =
== Summary - SUCCESS ==
No regressions found.
External URL: https://patchwork.freedesktop.org/api/1.0/series/51412/revisions/6/mbox/
== Known issues ==
Here are the changes found in Patchwork_10843 that come from known issues:
=== IGT changes ===
==== Issues hit ====
igt@gem_ctx_create@basic-files:
fi-icl-u2: PASS -> DMESG-WARN (fdo#107724)
igt@i915_selftest@live_contexts:
fi-bsw-kefka: PASS -> DMESG-FAIL (fdo#108626, fdo#108656)
igt@kms_pipe_crc_basic@nonblocking-crc-pipe-a-frame-sequence:
fi-byt-clapper: PASS -> FAIL (fdo#107362, fdo#103191) +1
==== Possible fixes ====
igt@kms_frontbuffer_tracking@basic:
fi-hsw-peppy: DMESG-WARN (fdo#102614) -> PASS
==== Warnings ====
igt@i915_selftest@live_contexts:
fi-icl-u: DMESG-FAIL (fdo#108569) -> INCOMPLETE (fdo#108315) fdo#102614 https://bugs.freedesktop.org/show_bug.cgi?id=102614
fdo#103191 https://bugs.freedesktop.org/show_bug.cgi?id=103191
fdo#107362 https://bugs.freedesktop.org/show_bug.cgi?id=107362
fdo#107724 https://bugs.freedesktop.org/show_bug.cgi?id=107724
fdo#108315 https://bugs.freedesktop.org/show_bug.cgi?id=108315
fdo#108569 https://bugs.freedesktop.org/show_bug.cgi?id=108569
fdo#108626 https://bugs.freedesktop.org/show_bug.cgi?id=108626
fdo#108656 https://bugs.freedesktop.org/show_bug.cgi?id=108656
== Participating hosts (51 -> 47) ==
Missing (4): fi-ctg-p8600 fi-ilk-m540 fi-byt-squawks fi-hsw-4200u
== Build changes ==
* Linux: CI_DRM_5152 -> Patchwork_10843
CI_DRM_5152: 458538284af8fa513c9d1404a8651931d6c79ddf @ git://anongit.freedesktop.org/gfx-ci/linux
IGT_4718: 8ac5cfb4db9c7bc593beec18a6be1e2ff163106c @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
Patchwork_10843: 06eec138d7d531a074fb79592bb9cacbe2302bb7 @ git://anongit.freedesktop.org/gfx-ci/linux
== Linux commits ==
06eec138d7d5 drm/nouveau: Use atomic VCPI helpers for MST 92a67542920b drm/nouveau: Stop unsetting mstc->port, check connector registration b8566f9fea49 drm/dp_mst: Check payload count in drm_dp_mst_atomic_check() 29194b8b74f3 drm/dp_mst: Start tracking per-port VCPI allocations 849758e12551 drm/dp_mst: Return kref_put() result from drm_dp_put_port() cec677f01b2c drm/dp_mst: Add some atomic state iterator macros
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_10843/issues.html
_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Patchwork
CI✓ Fi.CI.IGT: success for drm/dp_mst: Improve VCPI helpers, use in nouveau (rev6)
In reply to Lyude Paul== Series Details ==
== Series Details ==
Series: drm/dp_mst: Improve VCPI helpers, use in nouveau (rev6)
URL : https://patchwork.freedesktop.org/series/51412/
State : success
== Summary ==
= CI Bug Log - changes from CI_DRM_5152_full -> Patchwork_10843_full =
== Summary - SUCCESS ==
No regressions found.
== Known issues ==
Here are the changes found in Patchwork_10843_full that come from known issues:
=== IGT changes ===
==== Issues hit ====
igt@gem_eio@in-flight-suspend:
shard-apl: PASS -> INCOMPLETE (fdo#103927)
igt@gem_exec_schedule@pi-ringfull-bsd:
shard-skl: NOTRUN -> FAIL (fdo#103158) +1
igt@kms_atomic_transition@1x-modeset-transitions-fencing:
shard-kbl: PASS -> DMESG-WARN (fdo#105602, fdo#103558) +4
igt@kms_atomic_transition@plane-toggle-modeset-transition:
shard-apl: PASS -> DMESG-WARN (fdo#105602, fdo#103558) +10
igt@kms_busy@extended-modeset-hang-newfb-with-reset-render-a:
shard-kbl: PASS -> DMESG-WARN (fdo#107956)
igt@kms_chv_cursor_fail@pipe-a-128x128-left-edge:
shard-skl: NOTRUN -> FAIL (fdo#104671)
igt@kms_cursor_crc@cursor-128x128-random:
shard-glk: PASS -> FAIL (fdo#103232)
igt@kms_cursor_crc@cursor-128x128-suspend:
shard-skl: PASS -> FAIL (fdo#103191, fdo#103232)
igt@kms_cursor_crc@cursor-256x256-offscreen:
shard-skl: NOTRUN -> FAIL (fdo#103232)
igt@kms_cursor_crc@cursor-64x64-onscreen:
shard-apl: PASS -> FAIL (fdo#103232) +1
igt@kms_draw_crc@draw-method-rgb565-mmap-wc-xtiled:
shard-kbl: PASS -> DMESG-FAIL (fdo#105602, fdo#103558)
shard-apl: PASS -> DMESG-FAIL (fdo#105602, fdo#103558)
igt@kms_flip@plain-flip-fb-recreate:
shard-skl: PASS -> FAIL (fdo#100368)
igt@kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-render:
shard-glk: PASS -> FAIL (fdo#103167)
igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-indfb-draw-mmap-wc:
shard-skl: NOTRUN -> FAIL (fdo#103167)
igt@kms_frontbuffer_tracking@fbcpsr-modesetfrombusy:
shard-skl: NOTRUN -> FAIL (fdo#105682) +1
igt@kms_plane@plane-position-covered-pipe-b-planes:
shard-glk: PASS -> FAIL (fdo#103166)
igt@kms_plane_alpha_blend@pipe-a-alpha-opaque-fb:
shard-glk: PASS -> FAIL (fdo#108145)
igt@kms_plane_alpha_blend@pipe-a-constant-alpha-min:
shard-skl: NOTRUN -> FAIL (fdo#108145)
igt@kms_plane_alpha_blend@pipe-b-coverage-7efc:
shard-skl: PASS -> FAIL (fdo#107815)
igt@kms_plane_alpha_blend@pipe-c-alpha-basic:
shard-skl: NOTRUN -> FAIL (fdo#108145, fdo#107815)
igt@kms_plane_multiple@atomic-pipe-b-tiling-yf:
shard-apl: PASS -> DMESG-FAIL (fdo#105602, fdo#103166, fdo#103558)
igt@pm_rpm@system-suspend-execbuf:
shard-kbl: PASS -> INCOMPLETE (fdo#103665, fdo#107807)
==== Possible fixes ====
igt@gem_pwrite_pread@uncached-copy-performance:
shard-skl: INCOMPLETE -> PASS
igt@kms_atomic_transition@1x-modeset-transitions-fencing:
shard-skl: FAIL (fdo#108470, fdo#107815) -> PASS
igt@kms_busy@extended-pageflip-modeset-hang-oldfb-render-a:
shard-glk: DMESG-WARN (fdo#107956) -> PASS
igt@kms_cursor_crc@cursor-256x256-suspend:
shard-skl: INCOMPLETE (fdo#104108) -> PASS
igt@kms_cursor_crc@cursor-64x21-random:
shard-apl: FAIL (fdo#103232) -> PASS
igt@kms_cursor_legacy@flip-vs-cursor-legacy:
shard-skl: FAIL (fdo#102670) -> PASS
igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-render:
shard-apl: FAIL (fdo#103167) -> PASS
igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-onoff:
shard-glk: FAIL (fdo#103167) -> PASS +1
igt@kms_pipe_crc_basic@nonblocking-crc-pipe-c-frame-sequence:
shard-skl: FAIL (fdo#107362, fdo#103191) -> PASS
igt@kms_plane@plane-position-covered-pipe-c-planes:
shard-apl: FAIL (fdo#103166) -> PASS +1
igt@kms_plane_multiple@atomic-pipe-a-tiling-x:
shard-glk: FAIL (fdo#103166) -> PASS
igt@kms_setmode@basic:
shard-apl: FAIL (fdo#99912) -> PASS
shard-kbl: FAIL (fdo#99912) -> PASS
igt@perf@oa-exponents:
shard-glk: FAIL (fdo#105483) -> PASS
igt@pm_rpm@gem-pread:
shard-skl: INCOMPLETE (fdo#107807) -> PASS
igt@pm_rpm@modeset-non-lpsp-stress-no-wait:
shard-skl: INCOMPLETE (fdo#107807) -> SKIP
igt@pm_rpm@modeset-stress-extra-wait:
shard-apl: INCOMPLETE (fdo#103927) -> PASS
==== Warnings ====
igt@kms_content_protection@atomic:
shard-apl: FAIL (fdo#108597) -> DMESG-FAIL (fdo#105602, fdo#103558)
igt@kms_cursor_crc@cursor-256x85-onscreen:
shard-apl: FAIL (fdo#103232) -> DMESG-FAIL (fdo#105602, fdo#103558, fdo#103232) +1
igt@kms_plane@pixel-format-pipe-c-planes:
shard-apl: FAIL (fdo#103166) -> DMESG-FAIL (fdo#105602, fdo#103166, fdo#103558)
shard-kbl: FAIL (fdo#103166) -> DMESG-FAIL (fdo#105602, fdo#103166, fdo#103558)
shard-skl: FAIL (fdo#103166) -> DMESG-FAIL (fdo#103166, fdo#106885)
igt@kms_plane_alpha_blend@pipe-c-alpha-7efc:
shard-kbl: FAIL (fdo#108145, fdo#108590) -> DMESG-FAIL (fdo#105602, fdo#108145, fdo#103558)
shard-apl: FAIL (fdo#108145) -> DMESG-FAIL (fdo#105602, fdo#108145, fdo#103558) fdo#100368 https://bugs.freedesktop.org/show_bug.cgi?id=100368
fdo#102670 https://bugs.freedesktop.org/show_bug.cgi?id=102670
fdo#103158 https://bugs.freedesktop.org/show_bug.cgi?id=103158
fdo#103166 https://bugs.freedesktop.org/show_bug.cgi?id=103166
fdo#103167 https://bugs.freedesktop.org/show_bug.cgi?id=103167
fdo#103191 https://bugs.freedesktop.org/show_bug.cgi?id=103191
fdo#103232 https://bugs.freedesktop.org/show_bug.cgi?id=103232
fdo#103558 https://bugs.freedesktop.org/show_bug.cgi?id=103558
fdo#103665 https://bugs.freedesktop.org/show_bug.cgi?id=103665
fdo#103927 https://bugs.freedesktop.org/show_bug.cgi?id=103927
fdo#104108 https://bugs.freedesktop.org/show_bug.cgi?id=104108
fdo#104671 https://bugs.freedesktop.org/show_bug.cgi?id=104671
fdo#105483 https://bugs.freedesktop.org/show_bug.cgi?id=105483
fdo#105602 https://bugs.freedesktop.org/show_bug.cgi?id=105602
fdo#105682 https://bugs.freedesktop.org/show_bug.cgi?id=105682
fdo#106885 https://bugs.freedesktop.org/show_bug.cgi?id=106885
fdo#107362 https://bugs.freedesktop.org/show_bug.cgi?id=107362
fdo#107807 https://bugs.freedesktop.org/show_bug.cgi?id=107807
fdo#107815 https://bugs.freedesktop.org/show_bug.cgi?id=107815
fdo#107956 https://bugs.freedesktop.org/show_bug.cgi?id=107956
fdo#108145 https://bugs.freedesktop.org/show_bug.cgi?id=108145
fdo#108470 https://bugs.freedesktop.org/show_bug.cgi?id=108470
fdo#108590 https://bugs.freedesktop.org/show_bug.cgi?id=108590
fdo#108597 https://bugs.freedesktop.org/show_bug.cgi?id=108597
fdo#99912 https://bugs.freedesktop.org/show_bug.cgi?id=99912
== Participating hosts (7 -> 6) ==
Missing (1): shard-iclb
== Build changes ==
* Linux: CI_DRM_5152 -> Patchwork_10843
CI_DRM_5152: 458538284af8fa513c9d1404a8651931d6c79ddf @ git://anongit.freedesktop.org/gfx-ci/linux
IGT_4718: 8ac5cfb4db9c7bc593beec18a6be1e2ff163106c @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
Patchwork_10843: 06eec138d7d531a074fb79592bb9cacbe2302bb7 @ git://anongit.freedesktop.org/gfx-ci/linux
piglit_4509: fdc5a4ca11124ab8413c7988896eec4c97336694 @ git://anongit.freedesktop.org/piglit
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_10843/shards.html
_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx