drm: add core support for unplugging a device (v2)
authorDave Airlie <airlied@redhat.com>
Mon, 20 Feb 2012 14:18:07 +0000 (14:18 +0000)
committerDave Airlie <airlied@redhat.com>
Thu, 15 Mar 2012 13:35:33 +0000 (13:35 +0000)
Two parts to this, one is simple unplug from sysfs for the device node.

The second adds an unplugged state, if we have device opens, we
just set the unplugged state and return, if we have no device
opens we drop the drm device.

If after a lastclose we discover we are unplugged we then
drop the drm device.

v2: use an atomic for unplugged and wrap it for users,
add checks on open + mmap + ioctl entry points.

Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_fops.c
drivers/gpu/drm/drm_gem.c
drivers/gpu/drm/drm_stub.c
drivers/gpu/drm/drm_vm.c
include/drm/drmP.h

index d166bd08040025d550adc72b65d26204a2bdaf81..0b65fbc8a6308c9b1f7aae823e7d10149d75c380 100644 (file)
@@ -390,6 +390,10 @@ long drm_ioctl(struct file *filp,
        unsigned int usize, asize;
 
        dev = file_priv->minor->dev;
+
+       if (drm_device_is_unplugged(dev))
+               return -ENODEV;
+
        atomic_inc(&dev->ioctl_count);
        atomic_inc(&dev->counts[_DRM_STAT_IOCTLS]);
        ++file_priv->ioctl_count;
index 6263b0147598de9688fcdfefb8a0dadc841f9ad9..7348a3dab250a47046eb1f025f12fb3365a5df16 100644 (file)
@@ -133,6 +133,9 @@ int drm_open(struct inode *inode, struct file *filp)
        if (!(dev = minor->dev))
                return -ENODEV;
 
+       if (drm_device_is_unplugged(dev))
+               return -ENODEV;
+
        retcode = drm_open_helper(inode, filp, dev);
        if (!retcode) {
                atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
@@ -181,6 +184,9 @@ int drm_stub_open(struct inode *inode, struct file *filp)
        if (!(dev = minor->dev))
                goto out;
 
+       if (drm_device_is_unplugged(dev))
+               goto out;
+
        old_fops = filp->f_op;
        filp->f_op = fops_get(dev->driver->fops);
        if (filp->f_op == NULL) {
@@ -579,6 +585,8 @@ int drm_release(struct inode *inode, struct file *filp)
                        retcode = -EBUSY;
                } else
                        retcode = drm_lastclose(dev);
+               if (drm_device_is_unplugged(dev))
+                       drm_put_dev(dev);
        }
        mutex_unlock(&drm_global_mutex);
 
index 3ebe3c8f58b599cb82994369904c81b3180dbf74..0ef358e5324542a6be54751527e889c8eb8f3eca 100644 (file)
@@ -661,6 +661,9 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
        struct drm_hash_item *hash;
        int ret = 0;
 
+       if (drm_device_is_unplugged(dev))
+               return -ENODEV;
+
        mutex_lock(&dev->struct_mutex);
 
        if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
index 6d7b083c5b776d482d3b0a21f60a43adebd8f66d..bbd40eaf98216ded6e2bd28e5c24da151342f1a8 100644 (file)
@@ -429,6 +429,11 @@ int drm_put_minor(struct drm_minor **minor_p)
        return 0;
 }
 
+static void drm_unplug_minor(struct drm_minor *minor)
+{
+       drm_sysfs_device_remove(minor);
+}
+
 /**
  * Called via drm_exit() at module unload time or when pci device is
  * unplugged.
@@ -492,3 +497,21 @@ void drm_put_dev(struct drm_device *dev)
        kfree(dev);
 }
 EXPORT_SYMBOL(drm_put_dev);
+
+void drm_unplug_dev(struct drm_device *dev)
+{
+       /* for a USB device */
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               drm_unplug_minor(dev->control);
+       drm_unplug_minor(dev->primary);
+
+       mutex_lock(&drm_global_mutex);
+
+       drm_device_set_unplugged(dev);
+
+       if (dev->open_count == 0) {
+               drm_put_dev(dev);
+       }
+       mutex_unlock(&drm_global_mutex);
+}
+EXPORT_SYMBOL(drm_unplug_dev);
index 55cd615678125a2c6f8b3c64fed35b499c0e8192..149561818349e602e39dc469e60a385394c5bdf7 100644 (file)
@@ -680,6 +680,9 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
        struct drm_device *dev = priv->minor->dev;
        int ret;
 
+       if (drm_device_is_unplugged(dev))
+               return -ENODEV;
+
        mutex_lock(&dev->struct_mutex);
        ret = drm_mmap_locked(filp, vma);
        mutex_unlock(&dev->struct_mutex);
index cfd921ff0cc4a11ef63d2f9ccba9a9f4bf9644e3..574bd1c81ebddfa3cb410d17178f48bfc0f00994 100644 (file)
@@ -1170,6 +1170,8 @@ struct drm_device {
        struct idr object_name_idr;
        /*@} */
        int switch_power_state;
+
+       atomic_t unplugged; /* device has been unplugged or gone away */
 };
 
 #define DRM_SWITCH_POWER_ON 0
@@ -1235,6 +1237,19 @@ static inline int drm_mtrr_del(int handle, unsigned long offset,
 }
 #endif
 
+static inline void drm_device_set_unplugged(struct drm_device *dev)
+{
+       smp_wmb();
+       atomic_set(&dev->unplugged, 1);
+}
+
+static inline int drm_device_is_unplugged(struct drm_device *dev)
+{
+       int ret = atomic_read(&dev->unplugged);
+       smp_rmb();
+       return ret;
+}
+
 /******************************************************************/
 /** \name Internal function definitions */
 /*@{*/
@@ -1455,6 +1470,7 @@ extern void drm_master_put(struct drm_master **master);
 
 extern void drm_put_dev(struct drm_device *dev);
 extern int drm_put_minor(struct drm_minor **minor);
+extern void drm_unplug_dev(struct drm_device *dev);
 extern unsigned int drm_debug;
 
 extern unsigned int drm_vblank_offdelay;