]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
drm: Use srcu to protect drm_device.unplugged
authorNoralf Trønnes <noralf@tronnes.org>
Wed, 28 Mar 2018 07:38:35 +0000 (10:38 +0300)
committerOleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
Thu, 29 Mar 2018 10:19:20 +0000 (13:19 +0300)
Use srcu to protect drm_device.unplugged in a race free manner.
Drivers can use drm_dev_enter()/drm_dev_exit() to protect and mark
sections preventing access to device resources that are not available
after the device is gone.

Suggested-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
Reviewed-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
Tested-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
Cc: intel-gfx@lists.freedesktop.org
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/1522222715-11814-1-git-send-email-andr2000@gmail.com
drivers/gpu/drm/drm_drv.c
include/drm/drm_device.h
include/drm/drm_drv.h

index a1b9338736e3be4aafc5522c217f75ce3dcea832..32a83b41ab61ea00a014d8abc5624c55a817094f 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/moduleparam.h>
 #include <linux/mount.h>
 #include <linux/slab.h>
+#include <linux/srcu.h>
 
 #include <drm/drm_drv.h>
 #include <drm/drmP.h>
@@ -75,6 +76,8 @@ static bool drm_core_init_complete = false;
 
 static struct dentry *drm_debugfs_root;
 
+DEFINE_STATIC_SRCU(drm_unplug_srcu);
+
 /*
  * DRM Minors
  * A DRM device can provide several char-dev interfaces on the DRM-Major. Each
@@ -318,18 +321,51 @@ void drm_put_dev(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_put_dev);
 
-static void drm_device_set_unplugged(struct drm_device *dev)
+/**
+ * drm_dev_enter - Enter device critical section
+ * @dev: DRM device
+ * @idx: Pointer to index that will be passed to the matching drm_dev_exit()
+ *
+ * This function marks and protects the beginning of a section that should not
+ * be entered after the device has been unplugged. The section end is marked
+ * with drm_dev_exit(). Calls to this function can be nested.
+ *
+ * Returns:
+ * True if it is OK to enter the section, false otherwise.
+ */
+bool drm_dev_enter(struct drm_device *dev, int *idx)
+{
+       *idx = srcu_read_lock(&drm_unplug_srcu);
+
+       if (dev->unplugged) {
+               srcu_read_unlock(&drm_unplug_srcu, *idx);
+               return false;
+       }
+
+       return true;
+}
+EXPORT_SYMBOL(drm_dev_enter);
+
+/**
+ * drm_dev_exit - Exit device critical section
+ * @idx: index returned from drm_dev_enter()
+ *
+ * This function marks the end of a section that should not be entered after
+ * the device has been unplugged.
+ */
+void drm_dev_exit(int idx)
 {
-       smp_wmb();
-       atomic_set(&dev->unplugged, 1);
+       srcu_read_unlock(&drm_unplug_srcu, idx);
 }
+EXPORT_SYMBOL(drm_dev_exit);
 
 /**
  * drm_dev_unplug - unplug a DRM device
  * @dev: DRM device
  *
  * This unplugs a hotpluggable DRM device, which makes it inaccessible to
- * userspace operations. Entry-points can use drm_dev_is_unplugged(). This
+ * userspace operations. Entry-points can use drm_dev_enter() and
+ * drm_dev_exit() to protect device resources in a race free manner. This
  * essentially unregisters the device like drm_dev_unregister(), but can be
  * called while there are still open users of @dev.
  */
@@ -338,10 +374,18 @@ void drm_dev_unplug(struct drm_device *dev)
        drm_dev_unregister(dev);
 
        mutex_lock(&drm_global_mutex);
-       drm_device_set_unplugged(dev);
        if (dev->open_count == 0)
                drm_dev_put(dev);
        mutex_unlock(&drm_global_mutex);
+
+       /*
+        * After synchronizing any critical read section is guaranteed to see
+        * the new value of ->unplugged, and any critical section which might
+        * still have seen the old value of ->unplugged is guaranteed to have
+        * finished.
+        */
+       dev->unplugged = true;
+       synchronize_srcu(&drm_unplug_srcu);
 }
 EXPORT_SYMBOL(drm_dev_unplug);
 
index 7c4fa32f3fc6fb151c25a3f44d96f04ac71a86fc..3a0eac2885b795f9bcad8c6cd90ddd087b573a91 100644 (file)
@@ -46,7 +46,14 @@ struct drm_device {
        /* currently active master for this device. Protected by master_mutex */
        struct drm_master *master;
 
-       atomic_t unplugged;                     /**< Flag whether dev is dead */
+       /**
+        * @unplugged:
+        *
+        * Flag to tell if the device has been unplugged.
+        * See drm_dev_enter() and drm_dev_is_unplugged().
+        */
+       bool unplugged;
+
        struct inode *anon_inode;               /**< inode for private address-space */
        char *unique;                           /**< unique name of the device */
        /*@} */
index d32b688eb346c9252eb66d73be49fdd477e290de..ff7312c40cd8f68e898fbd29671b809cf05add47 100644 (file)
@@ -623,6 +623,8 @@ void drm_dev_get(struct drm_device *dev);
 void drm_dev_put(struct drm_device *dev);
 void drm_dev_unref(struct drm_device *dev);
 void drm_put_dev(struct drm_device *dev);
+bool drm_dev_enter(struct drm_device *dev, int *idx);
+void drm_dev_exit(int idx);
 void drm_dev_unplug(struct drm_device *dev);
 
 /**
@@ -634,11 +636,16 @@ void drm_dev_unplug(struct drm_device *dev);
  * unplugged, these two functions guarantee that any store before calling
  * drm_dev_unplug() is visible to callers of this function after it completes
  */
-static inline int drm_dev_is_unplugged(struct drm_device *dev)
+static inline bool drm_dev_is_unplugged(struct drm_device *dev)
 {
-       int ret = atomic_read(&dev->unplugged);
-       smp_rmb();
-       return ret;
+       int idx;
+
+       if (drm_dev_enter(dev, &idx)) {
+               drm_dev_exit(idx);
+               return false;
+       }
+
+       return true;
 }