[Public]


> -----Original Message-----
> From: Lyude Paul <ly...@redhat.com>
> Sent: Wednesday, June 8, 2022 3:30 AM
> To: dri-devel@lists.freedesktop.org; nouv...@lists.freedesktop.org; amd-
> g...@lists.freedesktop.org
> Cc: Lin, Wayne <wayne....@amd.com>; Ville Syrjälä
> <ville.syrj...@linux.intel.com>; Zuo, Jerry <jerry....@amd.com>; Jani Nikula
> <jani.nik...@intel.com>; Imre Deak <imre.d...@intel.com>; Daniel Vetter
> <daniel.vet...@ffwll.ch>; Sean Paul <s...@poorly.run>; David Airlie
> <airl...@linux.ie>; Daniel Vetter <dan...@ffwll.ch>; Thomas Zimmermann
> <tzimmerm...@suse.de>; Lakha, Bhawanpreet
> <bhawanpreet.la...@amd.com>; open list <linux-ker...@vger.kernel.org>
> Subject: [RESEND RFC 15/18] drm/display/dp_mst: Skip releasing payloads if
> last connected port isn't connected
> 
> In the past, we've ran into strange issues regarding errors in response to
> trying to destroy payloads after a port has been unplugged. We fixed this
> back in:
> 
> This is intended to replace the workaround that was added here:
> 
> commit 3769e4c0af5b ("drm/dp_mst: Avoid to mess up payload table by
> ports in stale topology")
> 
> which was intended fix to some of the payload leaks that were observed
> before, where we would attempt to determine if the port was still
> connected to the topology before updating payloads using
> drm_dp_mst_port_downstream_of_branch. This wasn't a particularly good
> solution, since one of the points of still having port and mstb validation is 
> to
> avoid sending messages to newly disconnected branches wherever possible
> - thus the required use of drm_dp_mst_port_downstream_of_branch
> would indicate something may be wrong with said validation.
> 
> It seems like it may have just been races and luck that made
> drm_dp_mst_port_downstream_of_branch work however, as while I was
> trying to figure out the true cause of this issue when removing the legacy
> MST code - I discovered an important excerpt in section 2.14.2.3.3.6 of the DP
> 2.0
> specs:
> 
> "BAD_PARAM - This reply is transmitted when a Message Transaction
> parameter is in error; for example, the next port number is invalid or /no
> device is connected/ to the port associated with the port number."
> 
> Sure enough - removing the calls to
> drm_dp_mst_port_downstream_of_branch()
> and instead checking the ->ddps field of the parent port to see whether we
> should release a given payload or not seems to totally fix the issue. This 
> does
> actually make sense to me, as it seems the implication is that given a
> topology where an MSTB is removed, the payload for the MST parent's port
> will be released automatically if that port is also marked as disconnected.
> However, if there's another parent in the chain after that which is connected
> - payloads must be released there with an ALLOCATE_PAYLOAD message.
> 
> So, let's do that!
> 
> Signed-off-by: Lyude Paul <ly...@redhat.com>
> Cc: Wayne Lin <wayne....@amd.com>
> Cc: Ville Syrjälä <ville.syrj...@linux.intel.com>
> Cc: Fangzhi Zuo <jerry....@amd.com>
> Cc: Jani Nikula <jani.nik...@intel.com>
> Cc: Imre Deak <imre.d...@intel.com>
> Cc: Daniel Vetter <daniel.vet...@ffwll.ch>
> Cc: Sean Paul <s...@poorly.run>
> ---
>  drivers/gpu/drm/display/drm_dp_mst_topology.c | 51 +++++++------------
>  1 file changed, 17 insertions(+), 34 deletions(-)
> 
> diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c
> b/drivers/gpu/drm/display/drm_dp_mst_topology.c
> index dd314586bac3..70adb8db4335 100644
> --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c
> @@ -3137,7 +3137,7 @@ static struct drm_dp_mst_port
> *drm_dp_get_last_connected_port_to_mstb(struct drm  static struct
> drm_dp_mst_branch *  drm_dp_get_last_connected_port_and_mstb(struct
> drm_dp_mst_topology_mgr *mgr,
>                                       struct drm_dp_mst_branch *mstb,
> -                                     int *port_num)
> +                                     struct drm_dp_mst_port **last_port)
>  {
>       struct drm_dp_mst_branch *rmstb = NULL;
>       struct drm_dp_mst_port *found_port;
> @@ -3153,7 +3153,8 @@
> drm_dp_get_last_connected_port_and_mstb(struct
> drm_dp_mst_topology_mgr *mgr,
> 
>               if (drm_dp_mst_topology_try_get_mstb(found_port-
> >parent)) {
>                       rmstb = found_port->parent;
> -                     *port_num = found_port->port_num;
> +                     *last_port = found_port;
> +                     drm_dp_mst_get_port_malloc(found_port);
>               } else {
>                       /* Search again, starting from this parent */
>                       mstb = found_port->parent;
> @@ -3170,7 +3171,7 @@ static int drm_dp_payload_send_msg(struct
> drm_dp_mst_topology_mgr *mgr,
>                                  int pbn)
>  {
>       struct drm_dp_sideband_msg_tx *txmsg;
> -     struct drm_dp_mst_branch *mstb;
> +     struct drm_dp_mst_branch *mstb = NULL;
>       int ret, port_num;
>       u8 sinks[DRM_DP_MAX_SDP_STREAMS];
>       int i;
> @@ -3178,12 +3179,22 @@ static int drm_dp_payload_send_msg(struct
> drm_dp_mst_topology_mgr *mgr,
>       port_num = port->port_num;
>       mstb = drm_dp_mst_topology_get_mstb_validated(mgr, port-
> >parent);
>       if (!mstb) {
> -             mstb = drm_dp_get_last_connected_port_and_mstb(mgr,
> -                                                            port->parent,
> -                                                            &port_num);
> +             struct drm_dp_mst_port *rport = NULL;
> +             bool ddps;
> 
> +             mstb = drm_dp_get_last_connected_port_and_mstb(mgr,
> port->parent,
> +&rport);
>               if (!mstb)
>                       return -EINVAL;
> +
> +             ddps = rport->ddps;
> +             port_num = rport->port_num;
> +             drm_dp_mst_put_port_malloc(rport);
> +
> +             /* If the port is currently marked as disconnected, don't send
> a payload message */
> +             if (!ddps) {
Hi Lyude,

Thanks for driving this!
Shouldn't we still send ALLOCATE_PAYLOAD with PBN 0 to the last connected 
Port even its peer device is disconnected? We rely on this "path msg" to update
all payload ID tables along the virtual payload channel.

commit 3769e4c0af5b ("drm/dp_mst: Avoid to mess up payload table by
ports in stale topology") was trying to skip updating payload for a target 
which is
no longer existing in the current topology rooted at mgr->mst_primary. I passed
"mgr->mst_primary" to drm_dp_mst_port_downstream_of_branch() previously.
Sorry, I might not fully understand the issue you've seen. Could you elaborate 
on
this more please?

Thanks!
> +                     ret = -EINVAL;
> +                     goto fail_put;
> +             }
>       }
> 
>       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); @@ -3384,7 +3395,6
> @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr
> *mgr, int start_s
>       struct drm_dp_mst_port *port;
>       int i, j;
>       int cur_slots = start_slot;
> -     bool skip;
> 
>       mutex_lock(&mgr->payload_lock);
>       for (i = 0; i < mgr->max_payloads; i++) { @@ -3399,16 +3409,6 @@ int
> drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr,
> int start_s
>                       port = container_of(vcpi, struct drm_dp_mst_port,
>                                           vcpi);
> 
> -                     mutex_lock(&mgr->lock);
> -                     skip
> = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary);
> -                     mutex_unlock(&mgr->lock);
> -
> -                     if (skip) {
> -                             drm_dbg_kms(mgr->dev,
> -                                         "Virtual channel %d is not in 
> current
> topology\n",
> -                                         i);
> -                             continue;
> -                     }
>                       /* Validated ports don't matter if we're releasing
>                        * VCPI
>                        */
> @@ -3509,7 +3509,6 @@ int drm_dp_update_payload_part2(struct
> drm_dp_mst_topology_mgr *mgr)
>       struct drm_dp_mst_port *port;
>       int i;
>       int ret = 0;
> -     bool skip;
> 
>       mutex_lock(&mgr->payload_lock);
>       for (i = 0; i < mgr->max_payloads; i++) { @@ -3519,13 +3518,6 @@ int
> drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr)
> 
>               port = container_of(mgr->proposed_vcpis[i], struct
> drm_dp_mst_port, vcpi);
> 
> -             mutex_lock(&mgr->lock);
> -             skip = !drm_dp_mst_port_downstream_of_branch(port,
> mgr->mst_primary);
> -             mutex_unlock(&mgr->lock);
> -
> -             if (skip)
> -                     continue;
> -
>               drm_dbg_kms(mgr->dev, "payload %d %d\n", i, mgr-
> >payloads[i].payload_state);
>               if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL)
> {
>                       ret = drm_dp_create_payload_step2(mgr, port, mgr-
> >proposed_vcpis[i]->vcpi, &mgr->payloads[i]); @@ -4780,18 +4772,9 @@
> EXPORT_SYMBOL(drm_dp_mst_reset_vcpi_slots);
>  void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
>                               struct drm_dp_mst_port *port)
>  {
> -     bool skip;
> -
>       if (!port->vcpi.vcpi)
>               return;
> 
> -     mutex_lock(&mgr->lock);
> -     skip = !drm_dp_mst_port_downstream_of_branch(port, mgr-
> >mst_primary);
> -     mutex_unlock(&mgr->lock);
> -
> -     if (skip)
> -             return;
> -
>       drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
>       port->vcpi.num_slots = 0;
>       port->vcpi.pbn = 0;
> --
> 2.35.3
--
Wayne Lin

Reply via email to