Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/dp_mst: Avoid to mess up payload table by ports in stale topology

[Why]
After unplug/hotplug hub from the system, userspace might start to
clear stale payloads gradually. If we call drm_dp_mst_deallocate_vcpi()
to release stale VCPI of those ports which are not relating to current
topology, we have chane to wrongly clear active payload table entry for
current topology.

E.g.
We have allocated VCPI 1 in current payload table and we call
drm_dp_mst_deallocate_vcpi() to clean VCPI 1 in stale topology. In
drm_dp_mst_deallocate_vcpi(), it will call drm_dp_mst_put_payload_id()
tp put VCPI 1 and which means ID 1 is available again. Thereafter, if we
want to allocate a new payload stream, it will find ID 1 is available by
drm_dp_mst_assign_payload_id(). However, ID 1 is being used

[How]
Check target sink is relating to current topology or not before doing
any payload table update.
Searching upward to find the target sink's relevant root branch device.
If the found root branch device is not the same root of current
topology, don't update payload table.

Changes since v1:
* Change debug macro to use drm_dbg_kms() instead
* Amend the commit message to add Cc tag.

Signed-off-by: Wayne Lin <Wayne.Lin@amd.com>
Cc: stable@vger.kernel.org
Signed-off-by: Lyude Paul <lyude@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210616035501.3776-3-Wayne.Lin@amd.com
Reviewed-by: Lyude Paul <lyude@redhat.com>

authored by

Wayne Lin and committed by
Lyude Paul
3769e4c0 35d3e8cb

+29
+29
drivers/gpu/drm/drm_dp_mst_topology.c
··· 94 94 static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_mst_port *port); 95 95 static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr); 96 96 97 + static bool drm_dp_mst_port_downstream_of_branch(struct drm_dp_mst_port *port, 98 + struct drm_dp_mst_branch *branch); 99 + 97 100 #define DBG_PREFIX "[dp_mst]" 98 101 99 102 #define DP_STR(x) [DP_ ## x] = #x ··· 3369 3366 struct drm_dp_mst_port *port; 3370 3367 int i, j; 3371 3368 int cur_slots = 1; 3369 + bool skip; 3372 3370 3373 3371 mutex_lock(&mgr->payload_lock); 3374 3372 for (i = 0; i < mgr->max_payloads; i++) { ··· 3384 3380 port = container_of(vcpi, struct drm_dp_mst_port, 3385 3381 vcpi); 3386 3382 3383 + mutex_lock(&mgr->lock); 3384 + skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary); 3385 + mutex_unlock(&mgr->lock); 3386 + 3387 + if (skip) { 3388 + drm_dbg_kms("Virtual channel %d is not in current topology\n", i); 3389 + continue; 3390 + } 3387 3391 /* Validated ports don't matter if we're releasing 3388 3392 * VCPI 3389 3393 */ ··· 3491 3479 struct drm_dp_mst_port *port; 3492 3480 int i; 3493 3481 int ret = 0; 3482 + bool skip; 3494 3483 3495 3484 mutex_lock(&mgr->payload_lock); 3496 3485 for (i = 0; i < mgr->max_payloads; i++) { ··· 3500 3487 continue; 3501 3488 3502 3489 port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); 3490 + 3491 + mutex_lock(&mgr->lock); 3492 + skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary); 3493 + mutex_unlock(&mgr->lock); 3494 + 3495 + if (skip) 3496 + continue; 3503 3497 3504 3498 drm_dbg_kms(mgr->dev, "payload %d %d\n", i, mgr->payloads[i].payload_state); 3505 3499 if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) { ··· 4594 4574 void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, 4595 4575 struct drm_dp_mst_port *port) 4596 4576 { 4577 + bool skip; 4578 + 4597 4579 if (!port->vcpi.vcpi) 4580 + return; 4581 + 4582 + mutex_lock(&mgr->lock); 4583 + skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary); 4584 + mutex_unlock(&mgr->lock); 4585 + 4586 + if (skip) 4598 4587 return; 4599 4588 4600 4589 drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);