Skip to content

Commit

Permalink
drm: add core support for unplugging a device (v2)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Dave Airlie committed Mar 15, 2012
1 parent cbc7e22 commit 2c07a21
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 0 deletions.
4 changes: 4 additions & 0 deletions drivers/gpu/drm/drm_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions drivers/gpu/drm/drm_fops.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);

Expand Down
3 changes: 3 additions & 0 deletions drivers/gpu/drm/drm_gem.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
23 changes: 23 additions & 0 deletions drivers/gpu/drm/drm_stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
3 changes: 3 additions & 0 deletions drivers/gpu/drm/drm_vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
16 changes: 16 additions & 0 deletions include/drm/drmP.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 */
/*@{*/
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 2c07a21

Please sign in to comment.