Skip to content

Commit 047d4cd

Browse files
committed
drm/dp_mst: Rewrite and fix bandwidth limit checks
Sigh, this is mostly my fault for not giving commit cd82d82 ("drm/dp_mst: Add branch bandwidth validation to MST atomic check") enough scrutiny during review. The way we're checking bandwidth limitations here is mostly wrong: For starters, drm_dp_mst_atomic_check_bw_limit() determines the pbn_limit of a branch by simply scanning each port on the current branch device, then uses the last non-zero full_pbn value that it finds. It then counts the sum of the PBN used on each branch device for that level, and compares against the full_pbn value it found before. This is wrong because ports can and will have different PBN limitations on many hubs, especially since a number of DisplayPort hubs out there will be clever and only use the smallest link rate required for each downstream sink - potentially giving every port a different full_pbn value depending on what link rate it's trained at. This means with our current code, which max PBN value we end up with is not well defined. Additionally, we also need to remember when checking bandwidth limitations that the top-most device in any MST topology is a branch device, not a port. This means that the first level of a topology doesn't technically have a full_pbn value that needs to be checked. Instead, we should assume that so long as our VCPI allocations fit we're within the bandwidth limitations of the primary MSTB. We do however, want to check full_pbn on every port including those of the primary MSTB. However, it's important to keep in mind that this value represents the minimum link rate /between a port's sink or mstb, and the mstb itself/. A quick diagram to explain: MSTB #1 / \ / \ Port #1 Port #2 full_pbn for Port #1 → | | ← full_pbn for Port #2 Sink #1 MSTB #2 | etc... Note that in the above diagram, the combined PBN from all VCPI allocations on said hub should not exceed the full_pbn value of port #2, and the display configuration on sink #1 should not exceed the full_pbn value of port #1. However, port #1 and port #2 can otherwise consume as much bandwidth as they want so long as their VCPI allocations still fit. And finally - our current bandwidth checking code also makes the mistake of not checking whether something is an end device or not before trying to traverse down it. So, let's fix it by rewriting our bandwidth checking helpers. We split the function into one part for handling branches which simply adds up the total PBN on each branch and returns it, and one for checking each port to ensure we're not going over its PBN limit. Phew. This should fix regressions seen, where we erroneously reject display configurations due to thinking they're going over our bandwidth limits when they're not. Changes since v1: * Took an even closer look at how PBN limitations are supposed to be handled, and did some experimenting with Sean Paul. Ended up rewriting these helpers again, but this time they should actually be correct! Changes since v2: * Small indenting fix * Fix pbn_used check in drm_dp_mst_atomic_check_port_bw_limit() Signed-off-by: Lyude Paul <[email protected]> Fixes: cd82d82 ("drm/dp_mst: Add branch bandwidth validation to MST atomic check") Cc: Sean Paul <[email protected]> Acked-by: Alex Deucher <[email protected]> Reviewed-by: Mikita Lipski <[email protected]> Tested-by: Hans de Goede <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 87212b5 commit 047d4cd

File tree

1 file changed

+93
-26
lines changed

1 file changed

+93
-26
lines changed

drivers/gpu/drm/drm_dp_mst_topology.c

Lines changed: 93 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4830,41 +4830,102 @@ static bool drm_dp_mst_port_downstream_of_branch(struct drm_dp_mst_port *port,
48304830
return false;
48314831
}
48324832

4833-
static inline
4834-
int drm_dp_mst_atomic_check_bw_limit(struct drm_dp_mst_branch *branch,
4835-
struct drm_dp_mst_topology_state *mst_state)
4833+
static int
4834+
drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port,
4835+
struct drm_dp_mst_topology_state *state);
4836+
4837+
static int
4838+
drm_dp_mst_atomic_check_mstb_bw_limit(struct drm_dp_mst_branch *mstb,
4839+
struct drm_dp_mst_topology_state *state)
48364840
{
4837-
struct drm_dp_mst_port *port;
48384841
struct drm_dp_vcpi_allocation *vcpi;
4839-
int pbn_limit = 0, pbn_used = 0;
4842+
struct drm_dp_mst_port *port;
4843+
int pbn_used = 0, ret;
4844+
bool found = false;
48404845

4841-
list_for_each_entry(port, &branch->ports, next) {
4842-
if (port->mstb)
4843-
if (drm_dp_mst_atomic_check_bw_limit(port->mstb, mst_state))
4844-
return -ENOSPC;
4846+
/* Check that we have at least one port in our state that's downstream
4847+
* of this branch, otherwise we can skip this branch
4848+
*/
4849+
list_for_each_entry(vcpi, &state->vcpis, next) {
4850+
if (!vcpi->pbn ||
4851+
!drm_dp_mst_port_downstream_of_branch(vcpi->port, mstb))
4852+
continue;
48454853

4846-
if (port->full_pbn > 0)
4847-
pbn_limit = port->full_pbn;
4854+
found = true;
4855+
break;
48484856
}
4849-
DRM_DEBUG_ATOMIC("[MST BRANCH:%p] branch has %d PBN available\n",
4850-
branch, pbn_limit);
4857+
if (!found)
4858+
return 0;
48514859

4852-
list_for_each_entry(vcpi, &mst_state->vcpis, next) {
4853-
if (!vcpi->pbn)
4854-
continue;
4860+
if (mstb->port_parent)
4861+
DRM_DEBUG_ATOMIC("[MSTB:%p] [MST PORT:%p] Checking bandwidth limits on [MSTB:%p]\n",
4862+
mstb->port_parent->parent, mstb->port_parent,
4863+
mstb);
4864+
else
4865+
DRM_DEBUG_ATOMIC("[MSTB:%p] Checking bandwidth limits\n",
4866+
mstb);
4867+
4868+
list_for_each_entry(port, &mstb->ports, next) {
4869+
ret = drm_dp_mst_atomic_check_port_bw_limit(port, state);
4870+
if (ret < 0)
4871+
return ret;
48554872

4856-
if (drm_dp_mst_port_downstream_of_branch(vcpi->port, branch))
4857-
pbn_used += vcpi->pbn;
4873+
pbn_used += ret;
48584874
}
4859-
DRM_DEBUG_ATOMIC("[MST BRANCH:%p] branch used %d PBN\n",
4860-
branch, pbn_used);
48614875

4862-
if (pbn_used > pbn_limit) {
4863-
DRM_DEBUG_ATOMIC("[MST BRANCH:%p] No available bandwidth\n",
4864-
branch);
4876+
return pbn_used;
4877+
}
4878+
4879+
static int
4880+
drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port,
4881+
struct drm_dp_mst_topology_state *state)
4882+
{
4883+
struct drm_dp_vcpi_allocation *vcpi;
4884+
int pbn_used = 0;
4885+
4886+
if (port->pdt == DP_PEER_DEVICE_NONE)
4887+
return 0;
4888+
4889+
if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) {
4890+
bool found = false;
4891+
4892+
list_for_each_entry(vcpi, &state->vcpis, next) {
4893+
if (vcpi->port != port)
4894+
continue;
4895+
if (!vcpi->pbn)
4896+
return 0;
4897+
4898+
found = true;
4899+
break;
4900+
}
4901+
if (!found)
4902+
return 0;
4903+
4904+
/* This should never happen, as it means we tried to
4905+
* set a mode before querying the full_pbn
4906+
*/
4907+
if (WARN_ON(!port->full_pbn))
4908+
return -EINVAL;
4909+
4910+
pbn_used = vcpi->pbn;
4911+
} else {
4912+
pbn_used = drm_dp_mst_atomic_check_mstb_bw_limit(port->mstb,
4913+
state);
4914+
if (pbn_used <= 0)
4915+
return pbn_used;
4916+
}
4917+
4918+
if (pbn_used > port->full_pbn) {
4919+
DRM_DEBUG_ATOMIC("[MSTB:%p] [MST PORT:%p] required PBN of %d exceeds port limit of %d\n",
4920+
port->parent, port, pbn_used,
4921+
port->full_pbn);
48654922
return -ENOSPC;
48664923
}
4867-
return 0;
4924+
4925+
DRM_DEBUG_ATOMIC("[MSTB:%p] [MST PORT:%p] uses %d out of %d PBN\n",
4926+
port->parent, port, pbn_used, port->full_pbn);
4927+
4928+
return pbn_used;
48684929
}
48694930

48704931
static inline int
@@ -5062,9 +5123,15 @@ int drm_dp_mst_atomic_check(struct drm_atomic_state *state)
50625123
ret = drm_dp_mst_atomic_check_vcpi_alloc_limit(mgr, mst_state);
50635124
if (ret)
50645125
break;
5065-
ret = drm_dp_mst_atomic_check_bw_limit(mgr->mst_primary, mst_state);
5066-
if (ret)
5126+
5127+
mutex_lock(&mgr->lock);
5128+
ret = drm_dp_mst_atomic_check_mstb_bw_limit(mgr->mst_primary,
5129+
mst_state);
5130+
mutex_unlock(&mgr->lock);
5131+
if (ret < 0)
50675132
break;
5133+
else
5134+
ret = 0;
50685135
}
50695136

50705137
return ret;

0 commit comments

Comments
 (0)