Skip to contentSkip to conversation

drm/dp_mst: Improve VCPI helpers, use in nouveau

PATCH Superseded Lyude Paul 5 participants 15 messages Oct 26, 2018 – Oct 30, 2018 2 messages not in the archive

Subsystems (4)

v2 of 6 Superseded Latest is v6 →

lore
Share & tools
lore

Lyude Paul

author maintainer
#0 lore raw

v2 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.

Cc Daniel Vetter <[email protected]>

Lyude Paul (4):
drm/dp_mst: Add some atomic state iterator macros drm/dp_mst: Start tracking per-port VCPI allocations drm/dp_mst: Check payload count in drm_dp_mst_atomic_check() drm/nouveau: Use atomic VCPI helpers for MST

 drivers/gpu/drm/drm_dp_mst_topology.c   | 196 ++++++++++++++++++++----
 drivers/gpu/drm/i915/intel_display.c    |   8 +
 drivers/gpu/drm/i915/intel_dp_mst.c     |  31 ++--
 drivers/gpu/drm/nouveau/dispnv50/disp.c |  54 ++++++-
 include/drm/drm_dp_mst_helper.h         |  88 ++++++++++-
 5 files changed, 322 insertions(+), 55 deletions(-)
Signature · 6 lines
2.17.2

_______________________________________________
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): 20181026203549.1796-1-lyude-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org

#2 lore raw

PATCH v2 1/4 drm/dp_mst: Add some atomic state iterator macros

Cc Daniel Vetter <[email protected]>
---
 include/drm/drm_dp_mst_helper.h | 77 +++++++++++++++++++++++++++++++++
 1 file changed, 77 insertions(+)
include/drm/drm_dp_mst_helper.h +77 -0
index 59f005b419cf..3faceb66f5cb 100644
@@ -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.17.2

_______________________________________________
Nouveau mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/nouveau
Reviewed-by on v2 1/4
#3 lore raw

Re: [PATCH v2 1/4] drm/dp_mst: Add some atomic state iterator macros

Quoted 45 lines · Lyude Paul
Cc Daniel Vetter <[email protected]>
---
 include/drm/drm_dp_mst_helper.h | 77 +++++++++++++++++++++++++++++++++
 1 file changed, 77 insertions(+)
include/drm/drm_dp_mst_helper.h +31 -0
index 59f005b419cf..3faceb66f5cb 100644
@@ -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

include/drm/drm_dp_mst_helper.h int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state, · line 671

Quoted 10 lines
include/drm/drm_dp_mst_helper.h +10 -0
+ * 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)))

I just realized that all our iterator macros do not compose at all. Whic is rather disappointing :-/

list_for_each solves that with a list_for_each_member, which you can then easily use to create more specialized macros. I wonder whether we should have a master macros, which also takes the obj type (we have very uniform naming, so a few well placed ## should make this work neatly), and then you could just #define more specialized iterators without having to retype them (or the need for dummy iterator markers and tons of casting).

Anyway, that's a lament for another day&patch. This looks good.

Quoted 40 lines
include/drm/drm_dp_mst_helper.h +36 -0
+
+/**
+ * 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 · 1 line
2.17.2
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
#4 lore raw

PATCH v2 2/4 drm/dp_mst: Start tracking per-port VCPI allocations

In reply to a message not in the archive

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 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 v1:
- Don't use the now-removed ->atomic_check() for private objects hook, just give drivers a function to call themselves

Cc Daniel Vetter <[email protected]>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 190 +++++++++++++++++++++-----
 drivers/gpu/drm/i915/intel_display.c  |   8 ++
 drivers/gpu/drm/i915/intel_dp_mst.c   |  31 +++--
 include/drm/drm_dp_mst_helper.h       |  11 +-
 4 files changed, 192 insertions(+), 48 deletions(-)
drivers/gpu/drm/drm_dp_mst_topology.c +158 -32
index 8c3cfac437f4..dcfab7536914 100644
@@ -2614,21 +2614,33 @@ 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
  *
+ * 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 atomic_check() handlers to change the current VCPI allocation for
+ * the new state. After the ->atomic_check() hooks of the driver and all other
+ * mode objects in the state have been called, DRM will check the final VCPI
+ * allocations to ensure that they will fit into the available bandwidth on
+ * the topology.
+ *
+ * See also: drm_dp_atomic_release_vcpi_slots()
+ *
  * RETURNS:
- * Total slots in the atomic state assigned for this port or error
+ * 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))
@@ -2637,20 +2649,41 @@ 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;
+	/* 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;
+			break;
+		}
 	}
+	if (!vcpi)
+		prev_slots = 0;
+
+	req_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+
+	DRM_DEBUG_KMS("[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;
+		}
 
-	topology_state->avail_slots -= req_slots;
-	DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state->avail_slots);
+		vcpi->port = port;
+		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);
 
@@ -2658,32 +2691,46 @@ 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
+ *
+ * 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 connector's atomic_check() handler when the connector will no longer
+ * have VCPI allocated (e.g. because it's CRTC was removed).
+ *
+ * It is OK to call this even if @port has been removed from the system, in
+ * which case it will just amount to a no-op.
+ *
+ * See also: drm_dp_atomic_find_vcpi_slots()
  *
  * RETURNS:
- * 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or
- * negative error code
+ * 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 *tmp, *pos;
 
 	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_safe(pos, tmp, &topology_state->vcpis, next) {
+		if (pos->port == port) {
+			list_del(&pos->next);
+			DRM_DEBUG_KMS("[MST PORT:%p] vcpi %d -> 0\n",
+				      port, pos->vcpi);
 
+			kfree(pos);
+			return 0;
+		}
+	}
+
+	/* If no allocation was found, all that means is that the port was
+	 * destroyed since the last atomic commit. That's OK!
+	 */
 	return 0;
 }
 EXPORT_SYMBOL(drm_dp_atomic_release_vcpi_slots);
@@ -3112,15 +3159,50 @@ 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_mst_topology_mgr *mgr = old_state->mgr;
+	struct drm_dp_mst_port *port;
+	struct drm_dp_vcpi_allocation *pos, *vcpi;
 
-	state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
 	if (!state)
 		return NULL;
 
 	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
 
+	state->mgr = mgr;
+	INIT_LIST_HEAD(&state->vcpis);
+
+	/* Copy over the VCPI allocations for ports that still exist */
+	list_for_each_entry(pos, &old_state->vcpis, next) {
+		port = drm_dp_get_validated_port_ref(mgr, pos->port);
+		if (!port) {
+			DRM_DEBUG_ATOMIC("[MST PORT:%p] is gone, vcpi %d -> 0\n",
+					 pos->port, pos->vcpi);
+			continue;
+		}
+
+		vcpi = kzalloc(sizeof(*vcpi), GFP_KERNEL);
+		if (!vcpi) {
+			drm_dp_put_port(port);
+			goto fail_alloc;
+		}
+
+		vcpi->port = port;
+		vcpi->vcpi = pos->vcpi;
+		list_add(&vcpi->next, &state->vcpis);
+		drm_dp_put_port(port);
+	}
+
 	return &state->base;
+
+fail_alloc:
+	list_for_each_entry_safe(pos, vcpi, &state->vcpis, next)
+		kfree(pos);
+	kfree(state);
+
+	return NULL;
 }
 
 static void drm_dp_mst_destroy_state(struct drm_private_obj *obj,
@@ -3128,14 +3210,60 @@ 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)
+		kfree(pos);
 
 	kfree(mst_state);
 }
 
-static const struct drm_private_state_funcs mst_state_funcs = {
+/**
+ * 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 ->atomic_check() callback.
+ *
+ * Returns:
+ *
+ * 0 if the new state is valid, negative error code otherwise.
+ */
+int drm_dp_mst_atomic_check(struct drm_dp_mst_topology_state *state)
+{
+	struct drm_dp_mst_topology_mgr *mgr = state->mgr;
+	struct drm_dp_vcpi_allocation *pos;
+	int avail_slots = 63;
+
+	list_for_each_entry(pos, &state->vcpis, next) {
+		DRM_DEBUG_ATOMIC("[MST PORT:%p] requires %d vcpi slots\n",
+				 pos->port, pos->vcpi);
+
+		avail_slots -= pos->vcpi;
+		if (avail_slots < 0) {
+			DRM_DEBUG_ATOMIC("[MST PORT:%p] not enough vcpi slots in state %p (avail=%d)\n",
+					 pos->port, state,
+					 avail_slots + pos->vcpi);
+			return -ENOSPC;
+		}
+	}
+	DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p vcpi avail=%d used=%d\n",
+			 mgr, state, avail_slots, 63 - avail_slots);
+
+	return 0;
+}
+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
@@ -3213,13 +3341,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 +8 -0
index fe045abb6472..f66af1465686 100644
@@ -12484,6 +12484,8 @@ static int intel_atomic_check(struct drm_device *dev,
 	struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state, *crtc_state;
+	struct drm_dp_mst_topology_mgr *mgr;
+	struct drm_dp_mst_topology_state *mst_state;
 	int ret, i;
 	bool any_ms = false;
 
@@ -12534,6 +12536,12 @@ static int intel_atomic_check(struct drm_device *dev,
 				       "[modeset]" : "[fastset]");
 	}
 
+	for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
+		ret = drm_dp_mst_atomic_check(mst_state);
+		if (ret)
+			return ret;
+	}
+
 	if (any_ms) {
 		ret = intel_modeset_checks(state);
 
drivers/gpu/drm/i915/intel_dp_mst.c +17 -14
index 8b71d64ebd9d..aaf904738b78 100644
@@ -114,28 +114,31 @@ static int intel_dp_mst_atomic_check(struct drm_connector *connector,
 	struct drm_connector_state *old_conn_state;
 	struct drm_crtc *old_crtc;
 	struct drm_crtc_state *crtc_state;
-	int slots, ret = 0;
+	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;
+	int ret = 0;
 
 	old_conn_state = drm_atomic_get_old_connector_state(state, connector);
 	old_crtc = old_conn_state->crtc;
 	if (!old_crtc)
 		return ret;
 
-	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;
+	/* Free VCPI, since compute_config() won't be run */
+	if (!new_conn_state->crtc) {
+		crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
 
-		old_encoder = old_conn_state->best_encoder;
-		mgr = &enc_to_mst(old_encoder)->primary->dp.mst_mgr;
-
-		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;
+		ret = drm_dp_atomic_release_vcpi_slots(state, mgr, port);
+		if (ret) {
+			DRM_DEBUG_KMS("failed releasing vcpi slots: %d\n",
+				      ret);
+			return ret;
+		}
+		to_intel_crtc_state(crtc_state)->dp_m_n.tu = 0;
 	}
+
 	return ret;
 }
 
include/drm/drm_dp_mst_helper.h +9 -2
index 3faceb66f5cb..0aa7d3658013 100644
@@ -406,9 +406,15 @@ 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;
 	struct drm_dp_mst_topology_mgr *mgr;
 };
 
@@ -624,9 +630,10 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
 				  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);
+				     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 drm_dp_mst_atomic_check(struct drm_dp_mst_topology_state *state);
 
 extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs;
 
Signature · 6 lines
2.17.2

_______________________________________________
Nouveau mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/nouveau
Reviewed-by on v2 2/4
#5 lore raw

Re: [PATCH v2 2/4] drm/dp_mst: Start tracking per-port VCPI allocations

Quoted 56 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 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 v1:
- Don't use the now-removed ->atomic_check() for private objects hook, just give drivers a function to call themselves

Cc Daniel Vetter <[email protected]>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 190 +++++++++++++++++++++-----
 drivers/gpu/drm/i915/intel_display.c  |   8 ++
 drivers/gpu/drm/i915/intel_dp_mst.c   |  31 +++--
 include/drm/drm_dp_mst_helper.h       |  11 +-
 4 files changed, 192 insertions(+), 48 deletions(-)
drivers/gpu/drm/drm_dp_mst_topology.c +0 -0
index 8c3cfac437f4..dcfab7536914 100644
@@ -2614,21 +2614,33 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
 }
 
 /**

drivers/gpu/drm/drm_dp_mst_topology.c static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, · line 2625

Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +4 -1
- * 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
  *
+ * 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 atomic_check() handlers to change the current VCPI allocation for

Maybe do a nice kerneldoc reference to the right atomic_check here.

drivers/gpu/drm/drm_dp_mst_topology.c static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, · line 2626

Quoted 1 line
drivers/gpu/drm/drm_dp_mst_topology.c +1 -0
+ * the new state. After the ->atomic_check() hooks of the driver and all other

This will upset the kerneldoc parser I think.

drivers/gpu/drm/drm_dp_mst_topology.c static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, · line 2631

Quoted 5 lines
drivers/gpu/drm/drm_dp_mst_topology.c +5 -0
+ * mode objects in the state have been called, DRM will check the final VCPI
+ * allocations to ensure that they will fit into the available bandwidth on
+ * the topology.
+ *
+ * See also: drm_dp_atomic_release_vcpi_slots()

Also need to reference drm_dp_mst_atomic_check() here and that drivers must call it or nothing happens.

Quoted 28 lines
drivers/gpu/drm/drm_dp_mst_topology.c +5 -8
+ *
  * RETURNS:
- * Total slots in the atomic state assigned for this port or error
+ * 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))
@@ -2637,20 +2649,41 @@ 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;

drivers/gpu/drm/drm_dp_mst_topology.c int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, · line 2662

Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +9 -0
+	/* 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;
+			break;
+		}
 	}
+	if (!vcpi)
+		prev_slots = 0;

For robustness should we warn here, since drivers forgetting to release vcpi slots is kinda a bug? Or do we need to have this to make life easier for driver writers?

Quoted 35 lines
drivers/gpu/drm/drm_dp_mst_topology.c +21 -4
+
+	req_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+
+	DRM_DEBUG_KMS("[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;
+		}
 
-	topology_state->avail_slots -= req_slots;
-	DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state->avail_slots);
+		vcpi->port = port;
+		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);
 
@@ -2658,32 +2691,46 @@ 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

drivers/gpu/drm/drm_dp_mst_topology.c EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots); · line 2703

Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10 -0
+ *
+ * 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 connector's atomic_check() handler when the connector will no longer
+ * have VCPI allocated (e.g. because it's CRTC was removed).
+ *
+ * It is OK to call this even if @port has been removed from the system, in
+ * which case it will just amount to a no-op.
+ *
+ * See also: drm_dp_atomic_find_vcpi_slots()

Same comments as with the _find_vcpi_slots() function.

Quoted 64 lines
drivers/gpu/drm/drm_dp_mst_topology.c +24 -14
  *
  * RETURNS:
- * 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or
- * negative error code
+ * 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 *tmp, *pos;
 
 	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_safe(pos, tmp, &topology_state->vcpis, next) {
+		if (pos->port == port) {
+			list_del(&pos->next);
+			DRM_DEBUG_KMS("[MST PORT:%p] vcpi %d -> 0\n",
+				      port, pos->vcpi);
 
+			kfree(pos);
+			return 0;
+		}
+	}
+
+	/* If no allocation was found, all that means is that the port was
+	 * destroyed since the last atomic commit. That's OK!
+	 */
 	return 0;
 }
 EXPORT_SYMBOL(drm_dp_atomic_release_vcpi_slots);
@@ -3112,15 +3159,50 @@ 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_mst_topology_mgr *mgr = old_state->mgr;
+	struct drm_dp_mst_port *port;
+	struct drm_dp_vcpi_allocation *pos, *vcpi;
 
-	state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
 	if (!state)
 		return NULL;
 
 	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
 
+	state->mgr = mgr;

drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_destroy_connector_work(struct work_struct *work) · line 3184

Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10 -0
+	INIT_LIST_HEAD(&state->vcpis);
+
+	/* Copy over the VCPI allocations for ports that still exist */
+	list_for_each_entry(pos, &old_state->vcpis, next) {
+		port = drm_dp_get_validated_port_ref(mgr, pos->port);
+		if (!port) {
+			DRM_DEBUG_ATOMIC("[MST PORT:%p] is gone, vcpi %d -> 0\n",
+					 pos->port, pos->vcpi);
+			continue;
+		}

Hm, in general we have 0 validation in the duplicate_state functions, Just dump copying, with minimal pointer/refcount updates.

drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_destroy_connector_work(struct work_struct *work) · line 3186

Quoted 2 lines
drivers/gpu/drm/drm_dp_mst_topology.c +2 -0
+
+		vcpi = kzalloc(sizeof(*vcpi), GFP_KERNEL);

kmemdump + updating the list. Just in case we ever add more stuff here. Probably needs an explicit memset to avoid list debugging.

At least that's the style we use everywhere else.

Quoted 23 lines
drivers/gpu/drm/drm_dp_mst_topology.c +18 -0
+		if (!vcpi) {
+			drm_dp_put_port(port);
+			goto fail_alloc;
+		}
+
+		vcpi->port = port;
+		vcpi->vcpi = pos->vcpi;
+		list_add(&vcpi->next, &state->vcpis);
+		drm_dp_put_port(port);
+	}
+
 	return &state->base;
+
+fail_alloc:
+	list_for_each_entry_safe(pos, vcpi, &state->vcpis, next)
+		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 static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, · line 3214

Quoted 5 lines
drivers/gpu/drm/drm_dp_mst_topology.c +2 -0
@@ -3128,14 +3210,60 @@ 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;
+

WARN_ON(!list_empty());

I think this not being empty on mgr teardown would be a driver bug. All the ports/branch devices should be gone by now, and also all the modeset state should have been torn down, using drm_atomic_helper_shutdown() or friends.

Quoted 8 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3 -1
+	list_for_each_entry_safe(pos, tmp, &mst_state->vcpis, next)
+		kfree(pos);
 
 	kfree(mst_state);
 }
 
-static const struct drm_private_state_funcs mst_state_funcs = {
+/**

drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, · line 3231

Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10 -0
+ * 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 ->atomic_check() callback.

&drm_mode_config_funcs.atomic_check

Quoted 19 lines
drivers/gpu/drm/drm_dp_mst_topology.c +19 -0
+ *
+ * Returns:
+ *
+ * 0 if the new state is valid, negative error code otherwise.
+ */
+int drm_dp_mst_atomic_check(struct drm_dp_mst_topology_state *state)
+{
+	struct drm_dp_mst_topology_mgr *mgr = state->mgr;
+	struct drm_dp_vcpi_allocation *pos;
+	int avail_slots = 63;
+
+	list_for_each_entry(pos, &state->vcpis, next) {
+		DRM_DEBUG_ATOMIC("[MST PORT:%p] requires %d vcpi slots\n",
+				 pos->port, pos->vcpi);
+
+		avail_slots -= pos->vcpi;
+		if (avail_slots < 0) {
+			DRM_DEBUG_ATOMIC("[MST PORT:%p] not enough vcpi slots in state %p (avail=%d)\n",
+					 pos->port, state,

drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, · line 3260

Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10 -0
+					 avail_slots + pos->vcpi);
+			return -ENOSPC;
+		}
+	}
+	DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p vcpi avail=%d used=%d\n",
+			 mgr, state, avail_slots, 63 - avail_slots);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_dp_mst_atomic_check);

Commented on patch 4 already, but I think this would look a bit simpler if we (also/instead, up to you really) expose a helper that checks all mgr in a drm_atomic_state. No need to have the exact same loop in each driver.

Quoted 123 lines
drivers/gpu/drm/drm_dp_mst_topology.c +5 -4
+
+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
@@ -3213,13 +3341,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 +8 -0
index fe045abb6472..f66af1465686 100644
@@ -12484,6 +12484,8 @@ static int intel_atomic_check(struct drm_device *dev,
 	struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state, *crtc_state;
+	struct drm_dp_mst_topology_mgr *mgr;
+	struct drm_dp_mst_topology_state *mst_state;
 	int ret, i;
 	bool any_ms = false;
 
@@ -12534,6 +12536,12 @@ static int intel_atomic_check(struct drm_device *dev,
 				       "[modeset]" : "[fastset]");
 	}
 
+	for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
+		ret = drm_dp_mst_atomic_check(mst_state);
+		if (ret)
+			return ret;
+	}
+
 	if (any_ms) {
 		ret = intel_modeset_checks(state);
 
drivers/gpu/drm/i915/intel_dp_mst.c +17 -14
index 8b71d64ebd9d..aaf904738b78 100644
@@ -114,28 +114,31 @@ static int intel_dp_mst_atomic_check(struct drm_connector *connector,
 	struct drm_connector_state *old_conn_state;
 	struct drm_crtc *old_crtc;
 	struct drm_crtc_state *crtc_state;
-	int slots, ret = 0;
+	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;
+	int ret = 0;
 
 	old_conn_state = drm_atomic_get_old_connector_state(state, connector);
 	old_crtc = old_conn_state->crtc;
 	if (!old_crtc)
 		return ret;
 
-	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;
+	/* Free VCPI, since compute_config() won't be run */
+	if (!new_conn_state->crtc) {
+		crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
 
-		old_encoder = old_conn_state->best_encoder;
-		mgr = &enc_to_mst(old_encoder)->primary->dp.mst_mgr;
-
-		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;
+		ret = drm_dp_atomic_release_vcpi_slots(state, mgr, port);
+		if (ret) {
+			DRM_DEBUG_KMS("failed releasing vcpi slots: %d\n",
+				      ret);
+			return ret;
+		}
+		to_intel_crtc_state(crtc_state)->dp_m_n.tu = 0;
 	}
+
 	return ret;
 }
 
include/drm/drm_dp_mst_helper.h +7 -1
index 3faceb66f5cb..0aa7d3658013 100644
@@ -406,9 +406,15 @@ 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;
 	struct drm_dp_mst_topology_mgr *mgr;
 };
 

include/drm/drm_dp_mst_helper.h int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, · line 630

Quoted 1 line
include/drm/drm_dp_mst_helper.h +0 -0
@@ -624,9 +630,10 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
 				  struct drm_dp_mst_port *port, int pbn);

I think annotating the relase/find functions with __must_check would be good.

include/drm/drm_dp_mst_helper.h int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, · line 635

Quoted 6 lines
include/drm/drm_dp_mst_helper.h +1 -1
 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);
 int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr,
 				 struct drm_dp_mst_port *port, bool power_up);

Same here.

include/drm/drm_dp_mst_helper.h int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, · line 636

Quoted 1 line
include/drm/drm_dp_mst_helper.h +1 -0
+int drm_dp_mst_atomic_check(struct drm_dp_mst_topology_state *state);

With the nits addressed:

Quoted 6 lines
include/drm/drm_dp_mst_helper.h +0 -0
 extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs;
 
Signature · 1 line
2.17.2
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
author maintainer
#6 lore raw

Re: [PATCH v2 2/4] drm/dp_mst: Start tracking per-port VCPI allocations

Quoted 126 lines · Daniel Vetter
Quoted 66 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 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 v1:
- Don't use the now-removed ->atomic_check() for private objects hook, just give drivers a function to call themselves

Cc Daniel Vetter <[email protected]>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 190 +++++++++++++++++++++-----
 drivers/gpu/drm/i915/intel_display.c  |   8 ++
 drivers/gpu/drm/i915/intel_dp_mst.c   |  31 +++--
 include/drm/drm_dp_mst_helper.h       |  11 +-
 4 files changed, 192 insertions(+), 48 deletions(-)
drivers/gpu/drm/drm_dp_mst_topology.c +2 -1
index 8c3cfac437f4..dcfab7536914 100644
@@ -2614,21 +2614,33 @@ 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
  *
+ * Allocates VCPI slots to @port, replacing any previous VCPI allocations
Quoted 5 lines

it
+ * may have had. Any atomic drivers which support MST must call this function
+ * in their atomic_check() handlers to change the current VCPI allocation for

Maybe do a nice kerneldoc reference to the right atomic_check here.

Quoted 2 lines

+ * the new state. After the ->atomic_check() hooks of the driver and all other

This will upset the kerneldoc parser I think.

Quoted 7 lines

+ * mode objects in the state have been called, DRM will check the final VCPI
+ * allocations to ensure that they will fit into the available bandwidth on
+ * the topology.
+ *
+ * See also: drm_dp_atomic_release_vcpi_slots()

Also need to reference drm_dp_mst_atomic_check() here and that drivers must call it or nothing happens.

Quoted 36 lines

+ *
* RETURNS:
- * Total slots in the atomic state assigned for this port or error
+ * 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,

drivers/gpu/drm/drm_dp_mst_topology.c +8 -7
 				  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))
@@ -2637,20 +2649,41 @@ 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;
+	/* 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;
+			break;
Quoted 8 lines

drivers/gpu/drm/drm_dp_mst_topology.c int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, · line 2662

Quoted 4 lines
drivers/gpu/drm/drm_dp_mst_topology.c +3 -0
+		}
 	}
+	if (!vcpi)
+		prev_slots = 0;

For robustness should we warn here, since drivers forgetting to release vcpi slots is kinda a bug? Or do we need to have this to make life easier for driver writers?

Latter-mainly because we want to be able to make sure that when we add stuff like fallback retraining, because then if we run into a case where we need to recalculate the mode for a pipe more then once we have to make sure that every driver is actually tracking what VCPI slots it requested, then also make sure that every driver is mindful to release those VCPI slots on subsequent atomic checks. IMO, it'd be redundant bookkeeping.

Quoted 398 lines
Quoted 41 lines
drivers/gpu/drm/drm_dp_mst_topology.c +23 -4
+
+	req_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+
+	DRM_DEBUG_KMS("[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;
+		}
 
-	topology_state->avail_slots -= req_slots;
-	DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state->avail_slots);
+		vcpi->port = port;
+		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);
@@ -2658,32 +2691,46 @@ 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
+ *
+ * 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 connector's atomic_check() handler when the connector will no

Quoted 8 lines

longer
+ * have VCPI allocated (e.g. because it's CRTC was removed).
+ *
+ * It is OK to call this even if @port has been removed from the system, in
+ * which case it will just amount to a no-op.
+ *
+ * See also: drm_dp_atomic_find_vcpi_slots()

Same comments as with the _find_vcpi_slots() function.

Quoted 67 lines

*
* RETURNS:
- * 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or
- * negative error code
+ * 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,

drivers/gpu/drm/drm_dp_mst_topology.c +23 -12
 				     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 *tmp, *pos;
 
 	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_safe(pos, tmp, &topology_state->vcpis, next) {
+		if (pos->port == port) {
+			list_del(&pos->next);
+			DRM_DEBUG_KMS("[MST PORT:%p] vcpi %d -> 0\n",
+				      port, pos->vcpi);
 
+			kfree(pos);
+			return 0;
+		}
+	}
+
+	/* If no allocation was found, all that means is that the port was
+	 * destroyed since the last atomic commit. That's OK!
+	 */
 	return 0;
 }
 EXPORT_SYMBOL(drm_dp_atomic_release_vcpi_slots);
@@ -3112,15 +3159,50 @@ 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_mst_topology_mgr *mgr = old_state->mgr;
+	struct drm_dp_mst_port *port;
+	struct drm_dp_vcpi_allocation *pos, *vcpi;
 
-	state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
 	if (!state)
 		return NULL;
 
 	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
 
+	state->mgr = mgr;
+	INIT_LIST_HEAD(&state->vcpis);

drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_destroy_connector_work(struct · line 3186

Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +9 -0
+
+	/* Copy over the VCPI allocations for ports that still exist */
+	list_for_each_entry(pos, &old_state->vcpis, next) {
+		port = drm_dp_get_validated_port_ref(mgr, pos->port);
+		if (!port) {
+			DRM_DEBUG_ATOMIC("[MST PORT:%p] is gone, vcpi %d ->
 0\n",
+					 pos->port, pos->vcpi);
+			continue;
+		}

Hm, in general we have 0 validation in the duplicate_state functions, Just dump copying, with minimal pointer/refcount updates.

drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_destroy_connector_work(struct · line 3188

Quoted 2 lines
drivers/gpu/drm/drm_dp_mst_topology.c +2 -0
+
+		vcpi = kzalloc(sizeof(*vcpi), GFP_KERNEL);

kmemdump + updating the list. Just in case we ever add more stuff here. Probably needs an explicit memset to avoid list debugging.

At least that's the style we use everywhere else.

Quoted 23 lines
drivers/gpu/drm/drm_dp_mst_topology.c +18 -0
+		if (!vcpi) {
+			drm_dp_put_port(port);
+			goto fail_alloc;
+		}
+
+		vcpi->port = port;
+		vcpi->vcpi = pos->vcpi;
+		list_add(&vcpi->next, &state->vcpis);
+		drm_dp_put_port(port);
+	}
+
 	return &state->base;
+
+fail_alloc:
+	list_for_each_entry_safe(pos, vcpi, &state->vcpis, next)
+		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 static void drm_dp_mst_destroy_state(struct · line 3215

Quoted 6 lines
drivers/gpu/drm/drm_dp_mst_topology.c +2 -0
@@ -3128,14 +3210,60 @@ 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;
+

WARN_ON(!list_empty());

I think this not being empty on mgr teardown would be a driver bug. All the ports/branch devices should be gone by now, and also all the modeset state should have been torn down, using drm_atomic_helper_shutdown() or friends.

Quoted 13 lines
drivers/gpu/drm/drm_dp_mst_topology.c +4 -1
+	list_for_each_entry_safe(pos, tmp, &mst_state->vcpis, next)
+		kfree(pos);
 
 	kfree(mst_state);
 }
 
-static const struct drm_private_state_funcs mst_state_funcs = {
+/**
+ * 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
+ *

Quoted 8 lines

+ * 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 ->atomic_check() callback.

&drm_mode_config_funcs.atomic_check

Quoted 20 lines

+ *
+ * Returns:
+ *
+ * 0 if the new state is valid, negative error code otherwise.
+ */

drivers/gpu/drm/drm_dp_mst_topology.c +13 -0
+int drm_dp_mst_atomic_check(struct drm_dp_mst_topology_state *state)
+{
+	struct drm_dp_mst_topology_mgr *mgr = state->mgr;
+	struct drm_dp_vcpi_allocation *pos;
+	int avail_slots = 63;
+
+	list_for_each_entry(pos, &state->vcpis, next) {
+		DRM_DEBUG_ATOMIC("[MST PORT:%p] requires %d vcpi slots\n",
+				 pos->port, pos->vcpi);
+
+		avail_slots -= pos->vcpi;
+		if (avail_slots < 0) {
+			DRM_DEBUG_ATOMIC("[MST PORT:%p] not enough vcpi slots

in state %p (avail=%d)\n",

drivers/gpu/drm/drm_dp_mst_topology.c +1 -0
+					 pos->port, state,

drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, · line 3260

Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10 -0
+					 avail_slots + pos->vcpi);
+			return -ENOSPC;
+		}
+	}
+	DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p vcpi avail=%d used=%d\n",
+			 mgr, state, avail_slots, 63 - avail_slots);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_dp_mst_atomic_check);

Commented on patch 4 already, but I think this would look a bit simpler if we (also/instead, up to you really) expose a helper that checks all mgr in a drm_atomic_state. No need to have the exact same loop in each driver.

Quoted 132 lines
drivers/gpu/drm/drm_dp_mst_topology.c +5 -4
+
+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
@@ -3213,13 +3341,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 +8 -0
index fe045abb6472..f66af1465686 100644
@@ -12484,6 +12484,8 @@ static int intel_atomic_check(struct drm_device
 *dev,
 	struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state, *crtc_state;
+	struct drm_dp_mst_topology_mgr *mgr;
+	struct drm_dp_mst_topology_state *mst_state;
 	int ret, i;
 	bool any_ms = false;
@@ -12534,6 +12536,12 @@ static int intel_atomic_check(struct drm_device
 *dev,
 				       "[modeset]" : "[fastset]");
 	}
 
+	for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
+		ret = drm_dp_mst_atomic_check(mst_state);
+		if (ret)
+			return ret;
+	}
+
 	if (any_ms) {
 		ret = intel_modeset_checks(state);
drivers/gpu/drm/i915/intel_dp_mst.c +17 -14
index 8b71d64ebd9d..aaf904738b78 100644
@@ -114,28 +114,31 @@ static int intel_dp_mst_atomic_check(struct
 drm_connector *connector,
 	struct drm_connector_state *old_conn_state;
 	struct drm_crtc *old_crtc;
 	struct drm_crtc_state *crtc_state;
-	int slots, ret = 0;
+	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;
+	int ret = 0;
 
 	old_conn_state = drm_atomic_get_old_connector_state(state, connector);
 	old_crtc = old_conn_state->crtc;
 	if (!old_crtc)
 		return ret;
 
-	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;
+	/* Free VCPI, since compute_config() won't be run */
+	if (!new_conn_state->crtc) {
+		crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
 
-		old_encoder = old_conn_state->best_encoder;
-		mgr = &enc_to_mst(old_encoder)->primary->dp.mst_mgr;
-
-		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;
+		ret = drm_dp_atomic_release_vcpi_slots(state, mgr, port);
+		if (ret) {
+			DRM_DEBUG_KMS("failed releasing vcpi slots: %d\n",
+				      ret);
+			return ret;
+		}
+		to_intel_crtc_state(crtc_state)->dp_m_n.tu = 0;
 	}
+
 	return ret;

}

include/drm/drm_dp_mst_helper.h +7 -1
index 3faceb66f5cb..0aa7d3658013 100644
@@ -406,9 +406,15 @@ 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;
 	struct drm_dp_mst_topology_mgr *mgr;
 };

include/drm/drm_dp_mst_helper.h int drm_dp_atomic_find_vcpi_slots(struct · line 631

Quoted 2 lines
include/drm/drm_dp_mst_helper.h +0 -0
@@ -624,9 +630,10 @@ int drm_dp_atomic_find_vcpi_slots(struct
 drm_atomic_state *state,
 				  struct drm_dp_mst_port *port, int pbn);

I think annotating the relase/find functions with __must_check would be good.

include/drm/drm_dp_mst_helper.h int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, · line 635

Quoted 6 lines
include/drm/drm_dp_mst_helper.h +1 -1
 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);
 int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr,
 				 struct drm_dp_mst_port *port, bool power_up);

Same here.

include/drm/drm_dp_mst_helper.h int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, · line 636

Quoted 1 line
include/drm/drm_dp_mst_helper.h +1 -0
+int drm_dp_mst_atomic_check(struct drm_dp_mst_topology_state *state);

With the nits addressed:

Quoted 7 lines
include/drm/drm_dp_mst_helper.h +0 -0
 extern const struct drm_private_state_funcs
 drm_dp_mst_topology_state_funcs;
Signature · 2 lines
-- 
2.17.2
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): 20181029142429.GB21967-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org

#8 lore raw

Re: [PATCH v2 2/4] drm/dp_mst: Start tracking per-port VCPI allocations

Quoted 288 lines · Daniel Vetter
Quoted 56 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 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 v1:
- Don't use the now-removed ->atomic_check() for private objects hook, just give drivers a function to call themselves

Cc Daniel Vetter <[email protected]>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 190 +++++++++++++++++++++-----
 drivers/gpu/drm/i915/intel_display.c  |   8 ++
 drivers/gpu/drm/i915/intel_dp_mst.c   |  31 +++--
 include/drm/drm_dp_mst_helper.h       |  11 +-
 4 files changed, 192 insertions(+), 48 deletions(-)
drivers/gpu/drm/drm_dp_mst_topology.c +0 -0
index 8c3cfac437f4..dcfab7536914 100644
@@ -2614,21 +2614,33 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
 }
 
 /**

drivers/gpu/drm/drm_dp_mst_topology.c static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, · line 2625

Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +4 -1
- * 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
  *
+ * 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 atomic_check() handlers to change the current VCPI allocation for

Maybe do a nice kerneldoc reference to the right atomic_check here.

drivers/gpu/drm/drm_dp_mst_topology.c static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, · line 2626

Quoted 1 line
drivers/gpu/drm/drm_dp_mst_topology.c +1 -0
+ * the new state. After the ->atomic_check() hooks of the driver and all other

This will upset the kerneldoc parser I think.

drivers/gpu/drm/drm_dp_mst_topology.c static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, · line 2631

Quoted 5 lines
drivers/gpu/drm/drm_dp_mst_topology.c +5 -0
+ * mode objects in the state have been called, DRM will check the final VCPI
+ * allocations to ensure that they will fit into the available bandwidth on
+ * the topology.
+ *
+ * See also: drm_dp_atomic_release_vcpi_slots()

Also need to reference drm_dp_mst_atomic_check() here and that drivers must call it or nothing happens.

Quoted 28 lines
drivers/gpu/drm/drm_dp_mst_topology.c +5 -8
+ *
  * RETURNS:
- * Total slots in the atomic state assigned for this port or error
+ * 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))
@@ -2637,20 +2649,41 @@ 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;

drivers/gpu/drm/drm_dp_mst_topology.c int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, · line 2662

Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +9 -0
+	/* 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;
+			break;
+		}
 	}
+	if (!vcpi)
+		prev_slots = 0;

For robustness should we warn here, since drivers forgetting to release vcpi slots is kinda a bug? Or do we need to have this to make life easier for driver writers?

Quoted 35 lines
drivers/gpu/drm/drm_dp_mst_topology.c +21 -4
+
+	req_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+
+	DRM_DEBUG_KMS("[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;
+		}
 
-	topology_state->avail_slots -= req_slots;
-	DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state->avail_slots);
+		vcpi->port = port;
+		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);
 
@@ -2658,32 +2691,46 @@ 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

drivers/gpu/drm/drm_dp_mst_topology.c EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots); · line 2703

Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10 -0
+ *
+ * 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 connector's atomic_check() handler when the connector will no longer
+ * have VCPI allocated (e.g. because it's CRTC was removed).
+ *
+ * It is OK to call this even if @port has been removed from the system, in
+ * which case it will just amount to a no-op.
+ *
+ * See also: drm_dp_atomic_find_vcpi_slots()

Same comments as with the _find_vcpi_slots() function.

Quoted 64 lines
drivers/gpu/drm/drm_dp_mst_topology.c +24 -14
  *
  * RETURNS:
- * 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or
- * negative error code
+ * 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 *tmp, *pos;
 
 	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_safe(pos, tmp, &topology_state->vcpis, next) {
+		if (pos->port == port) {
+			list_del(&pos->next);
+			DRM_DEBUG_KMS("[MST PORT:%p] vcpi %d -> 0\n",
+				      port, pos->vcpi);
 
+			kfree(pos);
+			return 0;
+		}
+	}
+
+	/* If no allocation was found, all that means is that the port was
+	 * destroyed since the last atomic commit. That's OK!
+	 */
 	return 0;
 }
 EXPORT_SYMBOL(drm_dp_atomic_release_vcpi_slots);
@@ -3112,15 +3159,50 @@ 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_mst_topology_mgr *mgr = old_state->mgr;
+	struct drm_dp_mst_port *port;
+	struct drm_dp_vcpi_allocation *pos, *vcpi;
 
-	state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
 	if (!state)
 		return NULL;
 
 	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
 
+	state->mgr = mgr;

drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_destroy_connector_work(struct work_struct *work) · line 3184

Quoted 10 lines
drivers/gpu/drm/drm_dp_mst_topology.c +10 -0
+	INIT_LIST_HEAD(&state->vcpis);
+
+	/* Copy over the VCPI allocations for ports that still exist */
+	list_for_each_entry(pos, &old_state->vcpis, next) {
+		port = drm_dp_get_validated_port_ref(mgr, pos->port);
+		if (!port) {
+			DRM_DEBUG_ATOMIC("[MST PORT:%p] is gone, vcpi %d -> 0\n",
+					 pos->port, pos->vcpi);
+			continue;
+		}

Hm, in general we have 0 validation in the duplicate_state functions, Just dump copying, with minimal pointer/refcount updates.

drivers/gpu/drm/drm_dp_mst_topology.c static void drm_dp_destroy_connector_work(struct work_struct *work) · line 3186

Quoted 2 lines
drivers/gpu/drm/drm_dp_mst_topology.c +2 -0
+
+		vcpi = kzalloc(sizeof(*vcpi), GFP_KERNEL);

kmemdump + updating the list. Just in case we ever add more stuff here. Probably needs an explicit memset to avoid list debugging.

At least that's the style we use everywhere else.

Quoted 23 lines
drivers/gpu/drm/drm_dp_mst_topology.c +18 -0
+		if (!vcpi) {
+			drm_dp_put_port(port);
+			goto fail_alloc;
+		}
+
+		vcpi->port = port;
+		vcpi->vcpi = pos->vcpi;
+		list_add(&vcpi->next, &state->vcpis);
+		drm_dp_put_port(port);
+	}
+
 	return &state->base;
+
+fail_alloc:
+	list_for_each_entry_safe(pos, vcpi, &state->vcpis, next)
+		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 static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, · line 3214

Quoted 5 lines
drivers/gpu/drm/drm_dp_mst_topology.c +2 -0
@@ -3128,14 +3210,60 @@ 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;
+

WARN_ON(!list_empty());

Quoted 4 lines

I think this not being empty on mgr teardown would be a driver bug. All the ports/branch devices should be gone by now, and also all the modeset state should have been torn down, using drm_atomic_helper_shutdown() or friends.

Lyude pointed out on irc that I was not yet sufficiently coffee'd up when making this comment: The destroy_state callback needs to clean up without complaining ofc! I mixed it up somehow with drm_mode_config_cleanup, where the WARN_ON would have been a good idea.
-Daniel

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
author maintainer
#9 lore raw

PATCH v2 3/4 drm/dp_mst: Check payload count in drm_dp_mst_atomic_check()

In reply to Lyude Paul

It occurred to me that we never actually check this! So let's start doing that.

Cc Daniel Vetter <[email protected]>
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/gpu/drm/drm_dp_mst_topology.c +7 -1
index dcfab7536914..8bb03700e199 100644
@@ -3238,7 +3238,7 @@ int drm_dp_mst_atomic_check(struct drm_dp_mst_topology_state *state)
 {
 	struct drm_dp_mst_topology_mgr *mgr = state->mgr;
 	struct drm_dp_vcpi_allocation *pos;
-	int avail_slots = 63;
+	int avail_slots = 63, payload_count = 0;
 
 	list_for_each_entry(pos, &state->vcpis, next) {
 		DRM_DEBUG_ATOMIC("[MST PORT:%p] requires %d vcpi slots\n",
@@ -3251,6 +3251,12 @@ int drm_dp_mst_atomic_check(struct drm_dp_mst_topology_state *state)
 					 avail_slots + pos->vcpi);
 			return -ENOSPC;
 		}
+
+		if (++payload_count > mgr->max_payloads) {
+			DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p has too many payloads (max=%d)\n",
+					 mgr, state, mgr->max_payloads);
+			return -EINVAL;
+		}
 	}
 	DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p vcpi avail=%d used=%d\n",
 			 mgr, state, avail_slots, 63 - avail_slots);
Signature · 6 lines
2.17.2

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
Reviewed-by on v2 3/4
#10 lore raw

Re: [PATCH v2 3/4] drm/dp_mst: Check payload count in drm_dp_mst_atomic_check()

Quoted 5 lines · Lyude Paul

It occurred to me that we never actually check this! So let's start doing that.

Cc Daniel Vetter <[email protected]>

One thought on testing all this: I think long term some unti tests, where we have a fake driver doing fake mst branch/ports and a bunch of allocations and then checking that it all works and validates would be nice. Longer term project ofc, and maybe after Kunit has been merged ... -Daniel

Quoted 33 lines
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/gpu/drm/drm_dp_mst_topology.c +7 -1
index dcfab7536914..8bb03700e199 100644
@@ -3238,7 +3238,7 @@ int drm_dp_mst_atomic_check(struct drm_dp_mst_topology_state *state)
 {
 	struct drm_dp_mst_topology_mgr *mgr = state->mgr;
 	struct drm_dp_vcpi_allocation *pos;
-	int avail_slots = 63;
+	int avail_slots = 63, payload_count = 0;
 
 	list_for_each_entry(pos, &state->vcpis, next) {
 		DRM_DEBUG_ATOMIC("[MST PORT:%p] requires %d vcpi slots\n",
@@ -3251,6 +3251,12 @@ int drm_dp_mst_atomic_check(struct drm_dp_mst_topology_state *state)
 					 avail_slots + pos->vcpi);
 			return -ENOSPC;
 		}
+
+		if (++payload_count > mgr->max_payloads) {
+			DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p has too many payloads (max=%d)\n",
+					 mgr, state, mgr->max_payloads);
+			return -EINVAL;
+		}
 	}
 	DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p vcpi avail=%d used=%d\n",
 			 mgr, state, avail_slots, 63 - avail_slots);
Signature · 1 line
2.17.2
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
author maintainer
#11 lore raw

Re: [PATCH v2 3/4] drm/dp_mst: Check payload count in drm_dp_mst_atomic_check()

Quoted 7 lines · Daniel Vetter
Quoted 5 lines · Lyude Paul

It occurred to me that we never actually check this! So let's start doing that.

Cc Daniel Vetter <[email protected]>
Quoted 6 lines

One thought on testing all this: I think long term some unti tests, where we have a fake driver doing fake mst branch/ports and a bunch of allocations and then checking that it all works and validates would be nice. Longer term project ofc, and maybe after Kunit has been merged ...

mhm-you see why I was considering how we could use vkms with this :), but yeah getting unit tests is one of the reasons I'm hoping to move some more MST logic out into the DRM helpers

Quoted 41 lines

-Daniel

Quoted 37 lines
---
 drivers/gpu/drm/drm_dp_mst_topology.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/gpu/drm/drm_dp_mst_topology.c +1 -1
index dcfab7536914..8bb03700e199 100644
@@ -3238,7 +3238,7 @@ int drm_dp_mst_atomic_check(struct
 drm_dp_mst_topology_state *state)
 {
 	struct drm_dp_mst_topology_mgr *mgr = state->mgr;
 	struct drm_dp_vcpi_allocation *pos;
-	int avail_slots = 63;
+	int avail_slots = 63, payload_count = 0;
 
 	list_for_each_entry(pos, &state->vcpis, next) {
drivers/gpu/drm/drm_dp_mst_topology.c +6 -0
 		DRM_DEBUG_ATOMIC("[MST PORT:%p] requires %d vcpi slots\n",
@@ -3251,6 +3251,12 @@ int drm_dp_mst_atomic_check(struct
 drm_dp_mst_topology_state *state)
 					 avail_slots + pos->vcpi);
 			return -ENOSPC;
 		}
+
+		if (++payload_count > mgr->max_payloads) {
+			DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p has too many
 payloads (max=%d)\n",
+					 mgr, state, mgr->max_payloads);
+			return -EINVAL;
+		}
 	}
drivers/gpu/drm/drm_dp_mst_topology.c +0 -0
 	DRM_DEBUG_ATOMIC("[MST MGR:%p] state %p vcpi avail=%d used=%d\n",
 			 mgr, state, avail_slots, 63 - avail_slots);
Signature · 1 line
2.17.2
Signature · 7 lines
Cheers,
	Lyude Paul

_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
author maintainer
#12 lore raw

PATCH v2 4/4 drm/nouveau: Use atomic VCPI helpers for MST

In reply to Lyude Paul

Currently, 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.

Cc Daniel Vetter <[email protected]>
---
 drivers/gpu/drm/nouveau/dispnv50/disp.c | 54 +++++++++++++++++++++----
 1 file changed, 47 insertions(+), 7 deletions(-)
drivers/gpu/drm/nouveau/dispnv50/disp.c +47 -7
index 6cbbae3f438b..37503319e5b1 100644
@@ -747,16 +747,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);
-
-	slots = drm_dp_find_vcpi_slots(&mstm->mgr, mstc->pbn);
-	if (slots < 0)
-		return slots;
+	mstc->pbn = drm_dp_calc_pbn_mode(crtc_state->adjusted_mode.clock,
+					 bpp);
+	/* Zombies don't need VCPI */
+	if (!drm_connector_is_unregistered(connector)) {
+		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);
@@ -920,12 +926,38 @@ 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;
+	struct drm_crtc *old_crtc;
+
+	old_conn_state = drm_atomic_get_old_connector_state(state, connector);
+	old_crtc = old_conn_state->crtc;
+
+	/* We only need to release VCPI here if we're going from having a CRTC
+	 * on this connector, to not having one
+	 */
+	if (!old_crtc || new_conn_state->crtc)
+		return 0;
+
+	/* Release the previous VCPI allocation since the encoder's
+	 * atomic_check() won't be called
+	 */
+	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
@@ -2084,6 +2116,8 @@ nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
 	struct drm_connector *connector;
 	struct drm_crtc_state *new_crtc_state;
 	struct drm_crtc *crtc;
+	struct drm_dp_mst_topology_mgr *mgr;
+	struct drm_dp_mst_topology_state *mst_state;
 	int ret, i;
 
 	/* We need to handle colour management on a per-plane basis. */
@@ -2109,6 +2143,12 @@ nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
 			return ret;
 	}
 
+	for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
+		ret = drm_dp_mst_atomic_check(mst_state);
+		if (ret)
+			return ret;
+	}
+
 	return 0;
 }
 
Signature · 6 lines
2.17.2

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel
#13 lore raw

Re: [PATCH v2 4/4] drm/nouveau: Use atomic VCPI helpers for MST

Quoted 99 lines · Lyude Paul

Currently, 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.

Cc Daniel Vetter <[email protected]>
---
 drivers/gpu/drm/nouveau/dispnv50/disp.c | 54 +++++++++++++++++++++----
 1 file changed, 47 insertions(+), 7 deletions(-)
drivers/gpu/drm/nouveau/dispnv50/disp.c +41 -7
index 6cbbae3f438b..37503319e5b1 100644
@@ -747,16 +747,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);
-
-	slots = drm_dp_find_vcpi_slots(&mstm->mgr, mstc->pbn);
-	if (slots < 0)
-		return slots;
+	mstc->pbn = drm_dp_calc_pbn_mode(crtc_state->adjusted_mode.clock,
+					 bpp);
+	/* Zombies don't need VCPI */
+	if (!drm_connector_is_unregistered(connector)) {
+		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);
@@ -920,12 +926,38 @@ 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;
+	struct drm_crtc *old_crtc;
+
+	old_conn_state = drm_atomic_get_old_connector_state(state, connector);
+	old_crtc = old_conn_state->crtc;
+
+	/* We only need to release VCPI here if we're going from having a CRTC
+	 * on this connector, to not having one
+	 */
+	if (!old_crtc || new_conn_state->crtc)
+		return 0;
+
+	/* Release the previous VCPI allocation since the encoder's
+	 * atomic_check() won't be called
+	 */
+	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
@@ -2084,6 +2116,8 @@ nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
 	struct drm_connector *connector;
 	struct drm_crtc_state *new_crtc_state;
 	struct drm_crtc *crtc;
+	struct drm_dp_mst_topology_mgr *mgr;
+	struct drm_dp_mst_topology_state *mst_state;
 	int ret, i;
 
 	/* We need to handle colour management on a per-plane basis. */

drivers/gpu/drm/nouveau/dispnv50/disp.c nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) · line 2150

Quoted 8 lines
drivers/gpu/drm/nouveau/dispnv50/disp.c +5 -0
@@ -2109,6 +2143,12 @@ nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
 			return ret;
 	}
 
+	for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
+		ret = drm_dp_mst_atomic_check(mst_state);
+		if (ret)
+			return ret;
+	}

For even less code I think you could also push this loop into the helpers. I can't come up with any scenario where a driver does not want to check all of them at the same time in the atomic_check sequence.

Otherwise lgtm.
-Daniel

Quoted 7 lines
drivers/gpu/drm/nouveau/dispnv50/disp.c +1 -0
+
 	return 0;
 }
 
Signature · 1 line
2.17.2
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
#14 lore raw

✗ Fi.CI.CHECKPATCH: warning for drm/dp_mst: Improve VCPI helpers, use in nouveau (rev2)

In reply to Lyude Paul
== Series Details ==

== Series Details ==

Series: drm/dp_mst: Improve VCPI helpers, use in nouveau (rev2)
URL : https://patchwork.freedesktop.org/series/51412/
State : warning

== Summary ==

$ dim checkpatch origin/drm-tip
4ddbf8c47846 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 checked 333500ef75cc drm/dp_mst: Start tracking per-port VCPI allocations 3e4c64252736 drm/dp_mst: Check payload count in drm_dp_mst_atomic_check() 60727e6f1f01 drm/nouveau: Use atomic VCPI helpers for MST

_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

#15 lore raw

✓ Fi.CI.BAT: success for drm/dp_mst: Improve VCPI helpers, use in nouveau (rev2)

In reply to Lyude Paul
== Series Details ==

== Series Details ==

Series: drm/dp_mst: Improve VCPI helpers, use in nouveau (rev2)
URL : https://patchwork.freedesktop.org/series/51412/
State : success

== Summary ==

= CI Bug Log - changes from CI_DRM_5044 -> Patchwork_10617 =

== Summary - SUCCESS ==

No regressions found.

External URL: https://patchwork.freedesktop.org/api/1.0/series/51412/revisions/2/mbox/

== Known issues ==

Here are the changes found in Patchwork_10617 that come from known issues:

=== IGT changes ===

    ==== Issues hit ====

    igt@drv_selftest@live_contexts:
      fi-icl-u:           PASS -> DMESG-FAIL (fdo#108569)

    igt@drv_selftest@live_evict:
      fi-bsw-kefka:       PASS -> DMESG-WARN (fdo#107709)

    igt@gem_exec_suspend@basic-s4-devices:
      fi-blb-e6850:       NOTRUN -> INCOMPLETE (fdo#107718)

    igt@kms_chamelium@dp-edid-read:
      fi-kbl-7500u:       PASS -> WARN (fdo#102672)

    igt@kms_flip@basic-flip-vs-dpms:
      fi-glk-j4005:       PASS -> DMESG-WARN (fdo#106097)

    igt@kms_flip@basic-flip-vs-wf_vblank:
      fi-glk-j4005:       PASS -> FAIL (fdo#100368)

    igt@kms_pipe_crc_basic@suspend-read-crc-pipe-a:
      fi-byt-clapper:     PASS -> FAIL (fdo#107362, fdo#103191) +1

    
    ==== Possible fixes ====

    igt@gem_exec_suspend@basic-s3:
      fi-blb-e6850:       INCOMPLETE (fdo#107718) -> PASS

    igt@gem_sync@basic-each:
      fi-glk-j4005:       DMESG-WARN (fdo#105719) -> PASS

    igt@kms_pipe_crc_basic@read-crc-pipe-b-frame-sequence:
      fi-byt-clapper:     FAIL (fdo#107362, fdo#103191) -> PASS

    igt@kms_pipe_crc_basic@read-crc-pipe-c-frame-sequence:
      fi-glk-j4005:       DMESG-FAIL (fdo#106000) -> PASS

fdo#100368 https://bugs.freedesktop.org/show_bug.cgi?id=100368
fdo#102672 https://bugs.freedesktop.org/show_bug.cgi?id=102672
fdo#103191 https://bugs.freedesktop.org/show_bug.cgi?id=103191
fdo#105719 https://bugs.freedesktop.org/show_bug.cgi?id=105719
fdo#106000 https://bugs.freedesktop.org/show_bug.cgi?id=106000
fdo#106097 https://bugs.freedesktop.org/show_bug.cgi?id=106097
fdo#107362 https://bugs.freedesktop.org/show_bug.cgi?id=107362
fdo#107709 https://bugs.freedesktop.org/show_bug.cgi?id=107709
fdo#107718 https://bugs.freedesktop.org/show_bug.cgi?id=107718
fdo#108569 https://bugs.freedesktop.org/show_bug.cgi?id=108569

== Participating hosts (47 -> 43) ==

Additional (1): fi-gdg-551
Missing (5): fi-kbl-soraka fi-ilk-m540 fi-hsw-4200u fi-bsw-cyan fi-ctg-p8600

== Build changes ==

* Linux: CI_DRM_5044 -> Patchwork_10617

CI_DRM_5044: c4487dca27970879bf67f331614142c749984d65 @ git://anongit.freedesktop.org/gfx-ci/linux
IGT_4698: af57164fcb16950187ad402ed31f565e88c42a78 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
Patchwork_10617: 60727e6f1f0124ec01f0f04dac6a34e932bb2e65 @ git://anongit.freedesktop.org/gfx-ci/linux

== Linux commits ==

60727e6f1f01 drm/nouveau: Use atomic VCPI helpers for MST 3e4c64252736 drm/dp_mst: Check payload count in drm_dp_mst_atomic_check() 333500ef75cc drm/dp_mst: Start tracking per-port VCPI allocations 4ddbf8c47846 drm/dp_mst: Add some atomic state iterator macros

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_10617/issues.html
_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

#16 lore raw

✓ Fi.CI.IGT: success for drm/dp_mst: Improve VCPI helpers, use in nouveau (rev2)

In reply to Lyude Paul
== Series Details ==

== Series Details ==

Series: drm/dp_mst: Improve VCPI helpers, use in nouveau (rev2)
URL : https://patchwork.freedesktop.org/series/51412/
State : success

== Summary ==

= CI Bug Log - changes from CI_DRM_5044_full -> Patchwork_10617_full =

== Summary - SUCCESS ==

No regressions found.

== Known issues ==

Here are the changes found in Patchwork_10617_full that come from known issues:

=== IGT changes ===

    ==== Issues hit ====

    igt@drv_suspend@shrink:
      shard-kbl:          PASS -> INCOMPLETE (fdo#106886, fdo#103665)

    igt@gem_ctx_isolation@bcs0-s3:
      shard-kbl:          PASS -> INCOMPLETE (fdo#103665)

    igt@gem_exec_schedule@pi-ringfull-render:
      shard-skl:          NOTRUN -> FAIL (fdo#103158)

    igt@gem_pwrite@huge-gtt-forwards:
      shard-glk:          NOTRUN -> INCOMPLETE (k.org#198133, fdo#103359)

    igt@kms_busy@extended-modeset-hang-newfb-render-a:
      shard-skl:          NOTRUN -> DMESG-WARN (fdo#107956) +1

    igt@kms_ccs@pipe-a-crc-primary-basic:
      shard-skl:          NOTRUN -> FAIL (fdo#107725)

    igt@kms_cursor_crc@cursor-128x128-onscreen:
      shard-glk:          PASS -> FAIL (fdo#103232)

    igt@kms_flip@dpms-vs-vblank-race-interruptible:
      shard-apl:          PASS -> DMESG-WARN (fdo#108549) +11

    igt@kms_flip@flip-vs-expired-vblank:
      shard-skl:          PASS -> FAIL (fdo#105363)

    igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-fullscreen:
      shard-apl:          PASS -> FAIL (fdo#103167)

    igt@kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-pwrite:
      shard-glk:          PASS -> FAIL (fdo#103167) +1

    igt@kms_frontbuffer_tracking@fbcpsr-rgb565-draw-pwrite:
      shard-snb:          SKIP -> INCOMPLETE (fdo#105411)

    igt@kms_plane@pixel-format-pipe-c-planes:
      shard-kbl:          NOTRUN -> FAIL (fdo#103166)

    igt@kms_plane@plane-position-covered-pipe-a-planes:
      shard-apl:          PASS -> FAIL (fdo#103166)

    igt@kms_plane_alpha_blend@pipe-a-alpha-7efc:
      shard-skl:          NOTRUN -> FAIL (fdo#107815, fdo#108145) +1

    igt@kms_plane_alpha_blend@pipe-c-alpha-transparant-fb:
      shard-skl:          NOTRUN -> FAIL (fdo#108145) +2

    igt@kms_universal_plane@universal-plane-pipe-a-functional:
      shard-glk:          PASS -> FAIL (fdo#103166)

    igt@kms_vblank@pipe-c-ts-continuation-dpms-suspend:
      shard-apl:          PASS -> DMESG-FAIL (fdo#108549)

    igt@prime_vgem@wait-blt:
      shard-glk:          PASS -> DMESG-WARN (fdo#105763, fdo#106538) +1

    
    ==== Possible fixes ====

    igt@drv_suspend@debugfs-reader:
      shard-skl:          INCOMPLETE (fdo#104108) -> PASS

    igt@gem_workarounds@suspend-resume-context:
      shard-kbl:          INCOMPLETE (fdo#103665) -> PASS

    igt@kms_ccs@pipe-b-crc-sprite-planes-basic:
      shard-glk:          FAIL (fdo#108145) -> PASS

    igt@kms_color@pipe-b-ctm-max:
      shard-apl:          DMESG-WARN (fdo#108549) -> PASS +13

    igt@kms_cursor_crc@cursor-64x64-onscreen:
      shard-apl:          FAIL (fdo#103232) -> PASS +1

    igt@kms_cursor_crc@cursor-64x64-suspend:
      shard-apl:          FAIL (fdo#103232, fdo#103191) -> PASS

    igt@kms_cursor_legacy@2x-nonblocking-modeset-vs-cursor-atomic:
      shard-glk:          FAIL (fdo#105454, fdo#106509) -> PASS

    igt@kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-blt:
      shard-glk:          FAIL (fdo#103167) -> PASS

    igt@kms_plane@plane-panning-top-left-pipe-b-planes:
      shard-apl:          INCOMPLETE (fdo#103927) -> PASS

    igt@kms_plane_multiple@atomic-pipe-a-tiling-x:
      shard-apl:          FAIL (fdo#103166) -> PASS +1

    igt@kms_vblank@pipe-b-ts-continuation-dpms-suspend:
      shard-snb:          DMESG-WARN (fdo#102365) -> PASS

    igt@kms_vblank@pipe-c-ts-continuation-dpms-rpm:
      shard-apl:          DMESG-FAIL (fdo#108549) -> PASS

    igt@pm_rpm@dpms-lpsp:
      shard-skl:          INCOMPLETE (fdo#107807) -> PASS

    igt@prime_busy@hang-bsd:
      shard-glk:          INCOMPLETE (k.org#198133, fdo#103359) -> PASS

    
    ==== Warnings ====

    igt@kms_cursor_crc@cursor-128x42-sliding:
      shard-apl:          DMESG-WARN (fdo#108549) -> FAIL (fdo#103232)

    igt@kms_cursor_crc@cursor-64x21-sliding:
      shard-apl:          FAIL (fdo#103232) -> DMESG-WARN (fdo#108549)

    igt@kms_cursor_crc@cursor-64x64-sliding:
      shard-apl:          DMESG-FAIL (fdo#108549, fdo#103232) -> DMESG-WARN (fdo#108549)

    igt@kms_frontbuffer_tracking@fbc-1p-primscrn-cur-indfb-draw-mmap-cpu:
      shard-glk:          DMESG-WARN (fdo#105763, fdo#106538) -> INCOMPLETE (k.org#198133, fdo#103359)

fdo#102365 https://bugs.freedesktop.org/show_bug.cgi?id=102365
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#103359 https://bugs.freedesktop.org/show_bug.cgi?id=103359
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#105363 https://bugs.freedesktop.org/show_bug.cgi?id=105363
fdo#105411 https://bugs.freedesktop.org/show_bug.cgi?id=105411
fdo#105454 https://bugs.freedesktop.org/show_bug.cgi?id=105454
fdo#105763 https://bugs.freedesktop.org/show_bug.cgi?id=105763
fdo#106509 https://bugs.freedesktop.org/show_bug.cgi?id=106509
fdo#106538 https://bugs.freedesktop.org/show_bug.cgi?id=106538
fdo#106886 https://bugs.freedesktop.org/show_bug.cgi?id=106886
fdo#107725 https://bugs.freedesktop.org/show_bug.cgi?id=107725
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#108549 https://bugs.freedesktop.org/show_bug.cgi?id=108549
k.org#198133 https://bugzilla.kernel.org/show_bug.cgi?id=198133

== Participating hosts (6 -> 6) ==

No changes in participating hosts

== Build changes ==

* Linux: CI_DRM_5044 -> Patchwork_10617

CI_DRM_5044: c4487dca27970879bf67f331614142c749984d65 @ git://anongit.freedesktop.org/gfx-ci/linux
IGT_4698: af57164fcb16950187ad402ed31f565e88c42a78 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
Patchwork_10617: 60727e6f1f0124ec01f0f04dac6a34e932bb2e65 @ 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_10617/shards.html
_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx