diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index d166bd08040025d550adc72b65d26204a2bdaf81..0b65fbc8a6308c9b1f7aae823e7d10149d75c380 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -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; diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 6263b0147598de9688fcdfefb8a0dadc841f9ad9..7348a3dab250a47046eb1f025f12fb3365a5df16 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -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); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 3ebe3c8f58b599cb82994369904c81b3180dbf74..0ef358e5324542a6be54751527e889c8eb8f3eca 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -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)) { diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 6d7b083c5b776d482d3b0a21f60a43adebd8f66d..bbd40eaf98216ded6e2bd28e5c24da151342f1a8 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -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); diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index 55cd615678125a2c6f8b3c64fed35b499c0e8192..149561818349e602e39dc469e60a385394c5bdf7 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -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); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index cfd921ff0cc4a11ef63d2f9ccba9a9f4bf9644e3..574bd1c81ebddfa3cb410d17178f48bfc0f00994 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -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;