aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcpaul@redhat.com <cpaul@redhat.com>2016-04-04 19:58:47 -0400
committerDave Airlie <airlied@redhat.com>2016-04-21 19:53:17 -0400
commitdeba0a2af9592b2022a0bce7b085a318b53ce1db (patch)
tree03af9a5bf6f40966c75e47d19a1decc3235eae67
parent3addc4e3a7142fcb9782c1d8ab70c63ebbd6d803 (diff)
drm/dp/mst: Validate port in drm_dp_payload_send_msg()
With the joys of things running concurrently, there's always a chance that the port we get passed in drm_dp_payload_send_msg() isn't actually valid anymore. Because of this, we need to make sure we validate the reference to the port before we use it otherwise we risk running into various race conditions. For instance, on the Dell MST monitor I have here for testing, hotplugging it enough times causes us to kernel panic: [drm:intel_mst_enable_dp] 1 [drm:drm_dp_update_payload_part2] payload 0 1 [drm:intel_get_hpd_pins] hotplug event received, stat 0x00200000, dig 0x10101011, pins 0x00000020 [drm:intel_hpd_irq_handler] digital hpd port B - short [drm:intel_dp_hpd_pulse] got hpd irq on port B - short [drm:intel_dp_check_mst_status] got esi 00 10 00 [drm:drm_dp_update_payload_part2] payload 1 1 general protection fault: 0000 [#1] SMP … Call Trace: [<ffffffffa012b632>] drm_dp_update_payload_part2+0xc2/0x130 [drm_kms_helper] [<ffffffffa032ef08>] intel_mst_enable_dp+0xf8/0x180 [i915] [<ffffffffa0310dbd>] haswell_crtc_enable+0x3ed/0x8c0 [i915] [<ffffffffa030c84d>] intel_atomic_commit+0x5ad/0x1590 [i915] [<ffffffffa01db877>] ? drm_atomic_set_crtc_for_connector+0x57/0xe0 [drm] [<ffffffffa01dc4e7>] drm_atomic_commit+0x37/0x60 [drm] [<ffffffffa0130a3a>] drm_atomic_helper_set_config+0x7a/0xb0 [drm_kms_helper] [<ffffffffa01cc482>] drm_mode_set_config_internal+0x62/0x100 [drm] [<ffffffffa01d02ad>] drm_mode_setcrtc+0x3cd/0x4e0 [drm] [<ffffffffa01c18e3>] drm_ioctl+0x143/0x510 [drm] [<ffffffffa01cfee0>] ? drm_mode_setplane+0x1b0/0x1b0 [drm] [<ffffffff810f79a7>] ? hrtimer_start_range_ns+0x1b7/0x3a0 [<ffffffff81212962>] do_vfs_ioctl+0x92/0x570 [<ffffffff81590852>] ? __sys_recvmsg+0x42/0x80 [<ffffffff81212eb9>] SyS_ioctl+0x79/0x90 [<ffffffff816b4e32>] entry_SYSCALL_64_fastpath+0x1a/0xa4 RIP [<ffffffffa012b026>] drm_dp_payload_send_msg+0x146/0x1f0 [drm_kms_helper] Which occurs because of the hotplug event shown in the log, which ends up causing DRM's dp helpers to drop the port we're updating the payload on and panic. CC: stable@vger.kernel.org Signed-off-by: Lyude <cpaul@redhat.com> Reviewed-by: David Airlie <airlied@linux.ie> Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r--drivers/gpu/drm/drm_dp_mst_topology.c9
1 files changed, 8 insertions, 1 deletions
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 27fbd79d0daf..e17fbdaf874b 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -1672,13 +1672,19 @@ static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
1672 u8 sinks[DRM_DP_MAX_SDP_STREAMS]; 1672 u8 sinks[DRM_DP_MAX_SDP_STREAMS];
1673 int i; 1673 int i;
1674 1674
1675 port = drm_dp_get_validated_port_ref(mgr, port);
1676 if (!port)
1677 return -EINVAL;
1678
1675 port_num = port->port_num; 1679 port_num = port->port_num;
1676 mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent); 1680 mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
1677 if (!mstb) { 1681 if (!mstb) {
1678 mstb = drm_dp_get_last_connected_port_and_mstb(mgr, port->parent, &port_num); 1682 mstb = drm_dp_get_last_connected_port_and_mstb(mgr, port->parent, &port_num);
1679 1683
1680 if (!mstb) 1684 if (!mstb) {
1685 drm_dp_put_port(port);
1681 return -EINVAL; 1686 return -EINVAL;
1687 }
1682 } 1688 }
1683 1689
1684 txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); 1690 txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
@@ -1707,6 +1713,7 @@ static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
1707 kfree(txmsg); 1713 kfree(txmsg);
1708fail_put: 1714fail_put:
1709 drm_dp_put_mst_branch_device(mstb); 1715 drm_dp_put_mst_branch_device(mstb);
1716 drm_dp_put_port(port);
1710 return ret; 1717 return ret;
1711} 1718}
1712 1719