diff options
| author | John Keeping <john@metanate.com> | 2016-03-11 12:21:17 -0500 |
|---|---|---|
| committer | Mark Yao <mark.yao@rock-chips.com> | 2016-03-28 02:48:29 -0400 |
| commit | f135046e519dc54a701110fa93a6d61c4989ed96 (patch) | |
| tree | bfd38bcc50a9aeeaa8f188e738c1a7642b5b447a | |
| parent | 92915da647be831ddbaa7e03e7528d921ec7f8aa (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.c | 22 | ||||
| -rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 20 |
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 | ||
| 254 | static 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 | |||
| 266 | static 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 | |||
| 254 | void rockchip_drm_lastclose(struct drm_device *dev) | 275 | void 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 | ||
| 45 | struct rockchip_atomic_commit { | 46 | struct 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 | ||
| 878 | static 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 | |||
| 878 | static const struct rockchip_crtc_funcs private_crtc_funcs = { | 897 | static 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 | ||
| 884 | static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, | 904 | static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, |
