diff mbox

[RFC,v4,17/25] drm/client: Bail out if there's a DRM master

Message ID 20180413165354.8331-18-noralf@tronnes.org (mailing list archive)
State New, archived
Headers show

Commit Message

Noralf Trønnes April 13, 2018, 4:53 p.m. UTC
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.vetter@ffwll.ch>
Signed-off-by: Noralf Trønnes <noralf@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 mbox

Patch

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);