If there's a DRM master, return -EBUSY.
Block userspace from becoming master by taking the master lock while
the client is setting the mode.

Suggested-by: Daniel Vetter <daniel.vet...@ffwll.ch>
Signed-off-by: Noralf Trønnes <nor...@tronnes.org>
---
 drivers/gpu/drm/drm_auth.c      | 33 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_client.c    | 34 +++++++++++++++++++++++++++++-----
 drivers/gpu/drm/drm_fb_helper.c |  8 ++++----
 drivers/gpu/drm/drm_internal.h  |  2 ++
 include/drm/drm_client.h        |  4 ++--
 5 files changed, 70 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
index d9c0f7573905..d656d0d93da3 100644
--- a/drivers/gpu/drm/drm_auth.c
+++ b/drivers/gpu/drm/drm_auth.c
@@ -366,3 +366,36 @@ void drm_master_put(struct drm_master **master)
        *master = NULL;
 }
 EXPORT_SYMBOL(drm_master_put);
+
+/**
+ * drm_master_block - Block DRM master operations
+ * @dev: DRM device
+ *
+ * This function checks if there is a master on @dev. If there is no master it
+ * blocks anyone from becoming master. In-kernel clients can use this to know
+ * when they can act as master. Use drm_master_unblock() to unblock.
+ *
+ * Returns:
+ * True if there is no master, false otherwise.
+ */
+bool drm_master_block(struct drm_device *dev)
+{
+       mutex_lock(&dev->master_mutex);
+       if (dev->master) {
+               mutex_unlock(&dev->master_mutex);
+               return false;
+       }
+
+       return true;
+}
+
+/**
+ * drm_master_unblock - Unblock DRM master operations
+ * @dev: DRM device
+ *
+ * Unblock and allow userspace to become master.
+ */
+void drm_master_unblock(struct drm_device *dev)
+{
+       mutex_unlock(&dev->master_mutex);
+}
diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index 27818a467b09..764c556630b8 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -18,6 +18,8 @@
 #include <drm/drm_device.h>
 #include <drm/drm_modes.h>
 
+#include "drm_internal.h"
+
 struct drm_client_display_offset {
        int x, y;
 };
@@ -313,18 +315,30 @@ static int drm_client_display_restore_legacy(struct 
drm_client_display *display)
 /**
  * drm_client_display_restore() - Restore client display
  * @display: Client display
+ * @force: If true, restore even if there's a DRM master
  *
  * Restore client display using the current modeset configuration.
  *
  * Return:
  * Zero on succes or negative error code on failure.
  */
-int drm_client_display_restore(struct drm_client_display *display)
+int drm_client_display_restore(struct drm_client_display *display, bool force)
 {
-       if (drm_drv_uses_atomic_modeset(display->dev))
-               return drm_client_display_restore_atomic(display, true);
+       struct drm_device *dev = display->dev;
+       int ret;
+
+       if (!force && !drm_master_block(dev))
+               return -EBUSY;
+
+       if (drm_drv_uses_atomic_modeset(dev))
+               ret = drm_client_display_restore_atomic(display, true);
        else
-               return drm_client_display_restore_legacy(display);
+               ret = drm_client_display_restore_legacy(display);
+
+       if (!force)
+               drm_master_unblock(dev);
+
+       return ret;
 }
 EXPORT_SYMBOL(drm_client_display_restore);
 
@@ -354,13 +368,23 @@ static void drm_client_display_dpms_legacy(struct 
drm_client_display *display, i
  * drm_client_display_dpms() - Set display DPMS mode
  * @display: Client display
  * @mode: DPMS mode
+ *
+ * Returns:
+ * Zero on success, -EBUSY if there's a DRM master.
  */
-void drm_client_display_dpms(struct drm_client_display *display, int mode)
+int drm_client_display_dpms(struct drm_client_display *display, int mode)
 {
+       if (!drm_master_block(display->dev))
+               return -EBUSY;
+
        if (drm_drv_uses_atomic_modeset(display->dev))
                drm_client_display_restore_atomic(display, mode == 
DRM_MODE_DPMS_ON);
        else
                drm_client_display_dpms_legacy(display, mode);
+
+       drm_master_unblock(display->dev);
+
+       return 0;
 }
 EXPORT_SYMBOL(drm_client_display_dpms);
 
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 01d8840930a3..98e5bc92c9f2 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -181,7 +181,7 @@ int drm_fb_helper_restore_fbdev_mode_unlocked(struct 
drm_fb_helper *fb_helper)
                return 0;
 
        mutex_lock(&fb_helper->lock);
-       ret = drm_client_display_restore(fb_helper->display);
+       ret = drm_client_display_restore(fb_helper->display, false);
 
        do_delayed = fb_helper->delayed_hotplug;
        if (do_delayed)
@@ -243,7 +243,7 @@ static bool drm_fb_helper_force_kernel_mode(void)
                        continue;
 
                mutex_lock(&helper->lock);
-               ret = drm_client_display_restore(helper->display);
+               ret = drm_client_display_restore(helper->display, true);
                if (ret)
                        error = true;
                mutex_unlock(&helper->lock);
@@ -1254,7 +1254,7 @@ static int pan_display_atomic(struct fb_var_screeninfo 
*var,
 
        pan_set(fb_helper, var->xoffset, var->yoffset);
 
-       ret = drm_client_display_restore(fb_helper->display);
+       ret = drm_client_display_restore(fb_helper->display, false);
        if (!ret) {
                info->var.xoffset = var->xoffset;
                info->var.yoffset = var->yoffset;
@@ -1423,7 +1423,7 @@ static int drm_fb_helper_single_fb_probe(struct 
drm_fb_helper *fb_helper,
 
                /* First time: disable all crtc's.. */
                if (!fb_helper->deferred_setup && 
!READ_ONCE(fb_helper->dev->master))
-                       drm_client_display_restore(fb_helper->display);
+                       drm_client_display_restore(fb_helper->display, false);
                return -EAGAIN;
        }
 
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index 3f5d7706bcc9..f38dcaf139d7 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -92,6 +92,8 @@ int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
                         struct drm_file *file_priv);
 int drm_master_open(struct drm_file *file_priv);
 void drm_master_release(struct drm_file *file_priv);
+bool drm_master_block(struct drm_device *dev);
+void drm_master_unblock(struct drm_device *dev);
 
 /* drm_sysfs.c */
 extern struct class *drm_class;
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index 27d2a46cd94a..3befd879a0b0 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -46,8 +46,8 @@ drm_client_display_find_modeset(struct drm_client_display 
*display, struct drm_c
 bool drm_client_display_panel_rotation(struct drm_connector *connector,
                                       struct drm_plane *plane,
                                       unsigned int *rotation);
-int drm_client_display_restore(struct drm_client_display *display);
-void drm_client_display_dpms(struct drm_client_display *display, int mode);
+int drm_client_display_restore(struct drm_client_display *display, bool force);
+int drm_client_display_dpms(struct drm_client_display *display, int mode);
 struct drm_client_display *
 drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned 
int height);
 
-- 
2.15.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to