diff options
author | Daniel Vetter <daniel.vetter@ffwll.ch> | 2016-10-03 04:28:27 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2016-10-03 22:53:12 -0400 |
commit | 2adb29b18ea7dc9503a397eb9995a5f3e6754b9e (patch) | |
tree | c6d5dbe6d2ee16a79df30dc0a2627a057810ca4f | |
parent | e86fa21b77f4ef840213ecd33230c7c16879a4ac (diff) |
drm: Undo damage to page_flip_ioctl
I screwed up rebasing of my patch in
commit 43968d7b806d7a7e021261294c583a216fddf0e5
Author: Daniel Vetter <daniel.vetter@ffwll.ch>
Date: Wed Sep 21 10:59:24 2016 +0200
drm: Extract drm_plane.[hc]
which meant on error paths drm_crtc_vblank_put could be called without
a get, leading to an underrun of the refcount.
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=98020
Reported-and-tested-by: Andy Furniss <adf.lists@gmail.com>
Cc: Sean Paul <seanpaul@chromium.org>
Cc: Michel Dänzer <michel@daenzer.net>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/20161003082827.11586-1-daniel.vetter@ffwll.ch
Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r-- | drivers/gpu/drm/drm_plane.c | 81 |
1 files changed, 39 insertions, 42 deletions
diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 783aef8acab7..249c0ae52c6d 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c | |||
@@ -783,6 +783,45 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, | |||
783 | if (!crtc) | 783 | if (!crtc) |
784 | return -ENOENT; | 784 | return -ENOENT; |
785 | 785 | ||
786 | if (crtc->funcs->page_flip_target) { | ||
787 | u32 current_vblank; | ||
788 | int r; | ||
789 | |||
790 | r = drm_crtc_vblank_get(crtc); | ||
791 | if (r) | ||
792 | return r; | ||
793 | |||
794 | current_vblank = drm_crtc_vblank_count(crtc); | ||
795 | |||
796 | switch (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) { | ||
797 | case DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE: | ||
798 | if ((int)(target_vblank - current_vblank) > 1) { | ||
799 | DRM_DEBUG("Invalid absolute flip target %u, " | ||
800 | "must be <= %u\n", target_vblank, | ||
801 | current_vblank + 1); | ||
802 | drm_crtc_vblank_put(crtc); | ||
803 | return -EINVAL; | ||
804 | } | ||
805 | break; | ||
806 | case DRM_MODE_PAGE_FLIP_TARGET_RELATIVE: | ||
807 | if (target_vblank != 0 && target_vblank != 1) { | ||
808 | DRM_DEBUG("Invalid relative flip target %u, " | ||
809 | "must be 0 or 1\n", target_vblank); | ||
810 | drm_crtc_vblank_put(crtc); | ||
811 | return -EINVAL; | ||
812 | } | ||
813 | target_vblank += current_vblank; | ||
814 | break; | ||
815 | default: | ||
816 | target_vblank = current_vblank + | ||
817 | !(page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC); | ||
818 | break; | ||
819 | } | ||
820 | } else if (crtc->funcs->page_flip == NULL || | ||
821 | (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) { | ||
822 | return -EINVAL; | ||
823 | } | ||
824 | |||
786 | drm_modeset_lock_crtc(crtc, crtc->primary); | 825 | drm_modeset_lock_crtc(crtc, crtc->primary); |
787 | if (crtc->primary->fb == NULL) { | 826 | if (crtc->primary->fb == NULL) { |
788 | /* The framebuffer is currently unbound, presumably | 827 | /* The framebuffer is currently unbound, presumably |
@@ -793,9 +832,6 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, | |||
793 | goto out; | 832 | goto out; |
794 | } | 833 | } |
795 | 834 | ||
796 | if (crtc->funcs->page_flip == NULL) | ||
797 | goto out; | ||
798 | |||
799 | fb = drm_framebuffer_lookup(dev, page_flip->fb_id); | 835 | fb = drm_framebuffer_lookup(dev, page_flip->fb_id); |
800 | if (!fb) { | 836 | if (!fb) { |
801 | ret = -ENOENT; | 837 | ret = -ENOENT; |
@@ -839,45 +875,6 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, | |||
839 | } | 875 | } |
840 | 876 | ||
841 | crtc->primary->old_fb = crtc->primary->fb; | 877 | crtc->primary->old_fb = crtc->primary->fb; |
842 | if (crtc->funcs->page_flip_target) { | ||
843 | u32 current_vblank; | ||
844 | int r; | ||
845 | |||
846 | r = drm_crtc_vblank_get(crtc); | ||
847 | if (r) | ||
848 | return r; | ||
849 | |||
850 | current_vblank = drm_crtc_vblank_count(crtc); | ||
851 | |||
852 | switch (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) { | ||
853 | case DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE: | ||
854 | if ((int)(target_vblank - current_vblank) > 1) { | ||
855 | DRM_DEBUG("Invalid absolute flip target %u, " | ||
856 | "must be <= %u\n", target_vblank, | ||
857 | current_vblank + 1); | ||
858 | drm_crtc_vblank_put(crtc); | ||
859 | return -EINVAL; | ||
860 | } | ||
861 | break; | ||
862 | case DRM_MODE_PAGE_FLIP_TARGET_RELATIVE: | ||
863 | if (target_vblank != 0 && target_vblank != 1) { | ||
864 | DRM_DEBUG("Invalid relative flip target %u, " | ||
865 | "must be 0 or 1\n", target_vblank); | ||
866 | drm_crtc_vblank_put(crtc); | ||
867 | return -EINVAL; | ||
868 | } | ||
869 | target_vblank += current_vblank; | ||
870 | break; | ||
871 | default: | ||
872 | target_vblank = current_vblank + | ||
873 | !(page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC); | ||
874 | break; | ||
875 | } | ||
876 | } else if (crtc->funcs->page_flip == NULL || | ||
877 | (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) { | ||
878 | return -EINVAL; | ||
879 | } | ||
880 | |||
881 | if (crtc->funcs->page_flip_target) | 878 | if (crtc->funcs->page_flip_target) |
882 | ret = crtc->funcs->page_flip_target(crtc, fb, e, | 879 | ret = crtc->funcs->page_flip_target(crtc, fb, e, |
883 | page_flip->flags, | 880 | page_flip->flags, |