aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Keeping <john@metanate.com>2016-03-11 12:21:17 -0500
committerMark Yao <mark.yao@rock-chips.com>2016-03-28 02:48:29 -0400
commitf135046e519dc54a701110fa93a6d61c4989ed96 (patch)
treebfd38bcc50a9aeeaa8f188e738c1a7642b5b447a
parent92915da647be831ddbaa7e03e7528d921ec7f8aa (diff)
drm/rockchip: cancel pending vblanks on close
When closing the DRM device while a vblank is pending, we access file_priv after it has been free'd, which gives: Unable to handle kernel NULL pointer dereference at virtual address 00000000 ... PC is at __list_add+0x5c/0xe8 LR is at send_vblank_event+0x54/0x1f0 ... [<c02952e8>] (__list_add) from [<c031a7b4>] (send_vblank_event+0x54/0x1f0) [<c031a760>] (send_vblank_event) from [<c031a9c0>] (drm_send_vblank_event+0x70/0x78) [<c031a950>] (drm_send_vblank_event) from [<c031a9f8>] (drm_crtc_send_vblank_event+0x30/0x34) [<c031a9c8>] (drm_crtc_send_vblank_event) from [<c0339ad8>] (vop_isr+0x224/0x28c) [<c03398b4>] (vop_isr) from [<c0081780>] (handle_irq_event_percpu+0x12c/0x3e4) This can be triggered somewhat reliably with: modetest -M rockchip -v -s ... Add a preclose hook to the driver so that we can discard any pending vblank events when the device is closed. Signed-off-by: John Keeping <john@metanate.com>
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.c22
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.h1
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c20
3 files changed, 43 insertions, 0 deletions
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 896da09e49ee..f556a8f4fde6 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -251,6 +251,27 @@ static int rockchip_drm_unload(struct drm_device *drm_dev)
251 return 0; 251 return 0;
252} 252}
253 253
254static void rockchip_drm_crtc_cancel_pending_vblank(struct drm_crtc *crtc,
255 struct drm_file *file_priv)
256{
257 struct rockchip_drm_private *priv = crtc->dev->dev_private;
258 int pipe = drm_crtc_index(crtc);
259
260 if (pipe < ROCKCHIP_MAX_CRTC &&
261 priv->crtc_funcs[pipe] &&
262 priv->crtc_funcs[pipe]->cancel_pending_vblank)
263 priv->crtc_funcs[pipe]->cancel_pending_vblank(crtc, file_priv);
264}
265
266static void rockchip_drm_preclose(struct drm_device *dev,
267 struct drm_file *file_priv)
268{
269 struct drm_crtc *crtc;
270
271 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
272 rockchip_drm_crtc_cancel_pending_vblank(crtc, file_priv);
273}
274
254void rockchip_drm_lastclose(struct drm_device *dev) 275void rockchip_drm_lastclose(struct drm_device *dev)
255{ 276{
256 struct rockchip_drm_private *priv = dev->dev_private; 277 struct rockchip_drm_private *priv = dev->dev_private;
@@ -281,6 +302,7 @@ static struct drm_driver rockchip_drm_driver = {
281 DRIVER_PRIME | DRIVER_ATOMIC, 302 DRIVER_PRIME | DRIVER_ATOMIC,
282 .load = rockchip_drm_load, 303 .load = rockchip_drm_load,
283 .unload = rockchip_drm_unload, 304 .unload = rockchip_drm_unload,
305 .preclose = rockchip_drm_preclose,
284 .lastclose = rockchip_drm_lastclose, 306 .lastclose = rockchip_drm_lastclose,
285 .get_vblank_counter = drm_vblank_no_hw_counter, 307 .get_vblank_counter = drm_vblank_no_hw_counter,
286 .enable_vblank = rockchip_drm_crtc_enable_vblank, 308 .enable_vblank = rockchip_drm_crtc_enable_vblank,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 3529f692edb8..00d17d71aa4c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -40,6 +40,7 @@ struct rockchip_crtc_funcs {
40 int (*enable_vblank)(struct drm_crtc *crtc); 40 int (*enable_vblank)(struct drm_crtc *crtc);
41 void (*disable_vblank)(struct drm_crtc *crtc); 41 void (*disable_vblank)(struct drm_crtc *crtc);
42 void (*wait_for_update)(struct drm_crtc *crtc); 42 void (*wait_for_update)(struct drm_crtc *crtc);
43 void (*cancel_pending_vblank)(struct drm_crtc *crtc, struct drm_file *file_priv);
43}; 44};
44 45
45struct rockchip_atomic_commit { 46struct rockchip_atomic_commit {
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 82d55bd8deb6..44b2ba7b5cc9 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -875,10 +875,30 @@ static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
875 WARN_ON(!wait_for_completion_timeout(&vop->wait_update_complete, 100)); 875 WARN_ON(!wait_for_completion_timeout(&vop->wait_update_complete, 100));
876} 876}
877 877
878static void vop_crtc_cancel_pending_vblank(struct drm_crtc *crtc,
879 struct drm_file *file_priv)
880{
881 struct drm_device *drm = crtc->dev;
882 struct vop *vop = to_vop(crtc);
883 struct drm_pending_vblank_event *e;
884 unsigned long flags;
885
886 spin_lock_irqsave(&drm->event_lock, flags);
887 e = vop->event;
888 if (e && e->base.file_priv == file_priv) {
889 vop->event = NULL;
890
891 e->base.destroy(&e->base);
892 file_priv->event_space += sizeof(e->event);
893 }
894 spin_unlock_irqrestore(&drm->event_lock, flags);
895}
896
878static const struct rockchip_crtc_funcs private_crtc_funcs = { 897static const struct rockchip_crtc_funcs private_crtc_funcs = {
879 .enable_vblank = vop_crtc_enable_vblank, 898 .enable_vblank = vop_crtc_enable_vblank,
880 .disable_vblank = vop_crtc_disable_vblank, 899 .disable_vblank = vop_crtc_disable_vblank,
881 .wait_for_update = vop_crtc_wait_for_update, 900 .wait_for_update = vop_crtc_wait_for_update,
901 .cancel_pending_vblank = vop_crtc_cancel_pending_vblank,
882}; 902};
883 903
884static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, 904static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,