drm/exynos: fix kernel panic issue at drm releasing
authorInki Dae <inki.dae@samsung.com>
Tue, 5 Jan 2016 10:50:31 +0000 (19:50 +0900)
committerInki Dae <daeinki@gmail.com>
Tue, 12 Jan 2016 15:16:39 +0000 (00:16 +0900)
This patch fixes a kernel panic issue which happened
when drm driver is closed while modetest.

This issue could be reproduced easily by launching modetest
with page flip repeatedly.

The reason is that invalid drm_file object could be accessed by
send_vblank_event function when finishing page flip if the drm_file
object was removed by drm_release and there was a pended page
flip event which was already committed to hardware.

So this patch makes the pended page flip event to be cancelled by
preclose callback which is called at front of drm_release function.

Changelog v2:
- free vblank event objects belonging to the request process,
  increment event space and decrease pending_update when cancelling
  the event

Signed-off-by: Inki Dae <inki.dae@samsung.com>
Reviewed-by: Daniel Stone <daniels@collabora.com>
Acked-by: Daniel Vetter <daniel@ffwll.ch>
drivers/gpu/drm/exynos/exynos_drm_crtc.c
drivers/gpu/drm/exynos/exynos_drm_crtc.h
drivers/gpu/drm/exynos/exynos_drm_drv.c

index cd9498164dc1bd2b3836562acd14bd1bd5191429..e36579c1c025bc97b2ae2c7b59e2cd46931a54ee 100644 (file)
@@ -226,3 +226,29 @@ void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
        if (exynos_crtc->ops->te_handler)
                exynos_crtc->ops->te_handler(exynos_crtc);
 }
+
+void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc,
+                                       struct drm_file *file)
+{
+       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+       struct drm_pending_vblank_event *e;
+       unsigned long flags;
+
+       spin_lock_irqsave(&crtc->dev->event_lock, flags);
+       e = exynos_crtc->event;
+       if (e && e->base.file_priv == file) {
+               exynos_crtc->event = NULL;
+               /*
+                * event will be destroyed by core part
+                * so below line should be removed later with core changes
+                */
+               e->base.destroy(&e->base);
+               /*
+                * event_space will be increased by core part
+                * so below line should be removed later with core changes.
+                */
+               file->event_space += sizeof(e->event);
+               atomic_dec(&exynos_crtc->pending_update);
+       }
+       spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+}
index 6a581a8af4650fcf5f07ea3fa84c6b1dd5e81ad4..cfdcf3e4eb1bc36847ceb83d709065874aaacb0c 100644 (file)
@@ -40,4 +40,8 @@ int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
  */
 void exynos_drm_crtc_te_handler(struct drm_crtc *crtc);
 
+/* This function cancels a page flip request. */
+void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc,
+                                       struct drm_file *file);
+
 #endif
index 9756797a15a5c2081b3a0a43e8a4dd7659243961..68f0f36f6e7e073af4d88da75a7a84f0734576f2 100644 (file)
@@ -330,7 +330,12 @@ err_file_priv_free:
 static void exynos_drm_preclose(struct drm_device *dev,
                                        struct drm_file *file)
 {
+       struct drm_crtc *crtc;
+
        exynos_drm_subdrv_close(dev, file);
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+               exynos_drm_crtc_cancel_page_flip(crtc, file);
 }
 
 static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)