Commit 1a75a222 authored by Daniel Vetter's avatar Daniel Vetter

drm: Hide hw.lock cleanup in filp->release better

A few things:
- Rename the cleanup function from drm_master_release to
  drm_legacy_lock_release. It doesn't relase any master stuff, but
  just the legacy hw lock.
- Hide it in drm_lock.c, which allows us to make a few more functions
  static in there. To avoid forward decl we need to shuffle the code a
  bit though.
- Push the check for ->master into the function itself.
- Only call this for !DRIVER_MODESET.

End result: Another place that takes struct_mutex gone for good for
modern drivers.

v2: Remove leftover comment.
Reviewed-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: default avatarEmil Velikov <emil.l.velikov@gmail.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1465930269-7883-3-git-send-email-daniel.vetter@ffwll.ch
parent e1b1cf2f
...@@ -338,18 +338,6 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) ...@@ -338,18 +338,6 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
return ret; return ret;
} }
static void drm_master_release(struct drm_device *dev, struct file *filp)
{
struct drm_file *file_priv = filp->private_data;
if (drm_legacy_i_have_hw_lock(dev, file_priv)) {
DRM_DEBUG("File %p released, freeing lock for context %d\n",
filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
drm_legacy_lock_free(&file_priv->master->lock,
_DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
}
}
static void drm_events_release(struct drm_file *file_priv) static void drm_events_release(struct drm_file *file_priv)
{ {
struct drm_device *dev = file_priv->minor->dev; struct drm_device *dev = file_priv->minor->dev;
...@@ -468,9 +456,8 @@ int drm_release(struct inode *inode, struct file *filp) ...@@ -468,9 +456,8 @@ int drm_release(struct inode *inode, struct file *filp)
(long)old_encode_dev(file_priv->minor->kdev->devt), (long)old_encode_dev(file_priv->minor->kdev->devt),
dev->open_count); dev->open_count);
/* if the master has gone away we can't do anything with the lock */ if (!drm_core_check_feature(dev, DRIVER_MODESET))
if (file_priv->minor->master) drm_legacy_lock_release(dev, filp);
drm_master_release(dev, filp);
if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
drm_legacy_reclaim_buffers(dev, file_priv); drm_legacy_reclaim_buffers(dev, file_priv);
......
...@@ -88,14 +88,10 @@ struct drm_agp_mem { ...@@ -88,14 +88,10 @@ struct drm_agp_mem {
struct list_head head; struct list_head head;
}; };
/* /* drm_lock.c */
* Generic Userspace Locking-API
*/
int drm_legacy_i_have_hw_lock(struct drm_device *d, struct drm_file *f);
int drm_legacy_lock(struct drm_device *d, void *v, struct drm_file *f); int drm_legacy_lock(struct drm_device *d, void *v, struct drm_file *f);
int drm_legacy_unlock(struct drm_device *d, void *v, struct drm_file *f); int drm_legacy_unlock(struct drm_device *d, void *v, struct drm_file *f);
int drm_legacy_lock_free(struct drm_lock_data *lock, unsigned int ctx); void drm_legacy_lock_release(struct drm_device *dev, struct file *filp);
/* DMA support */ /* DMA support */
int drm_legacy_dma_setup(struct drm_device *dev); int drm_legacy_dma_setup(struct drm_device *dev);
......
...@@ -40,6 +40,110 @@ ...@@ -40,6 +40,110 @@
static int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context); static int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context);
/**
* Take the heavyweight lock.
*
* \param lock lock pointer.
* \param context locking context.
* \return one if the lock is held, or zero otherwise.
*
* Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction.
*/
static
int drm_lock_take(struct drm_lock_data *lock_data,
unsigned int context)
{
unsigned int old, new, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
spin_lock_bh(&lock_data->spinlock);
do {
old = *lock;
if (old & _DRM_LOCK_HELD)
new = old | _DRM_LOCK_CONT;
else {
new = context | _DRM_LOCK_HELD |
((lock_data->user_waiters + lock_data->kernel_waiters > 1) ?
_DRM_LOCK_CONT : 0);
}
prev = cmpxchg(lock, old, new);
} while (prev != old);
spin_unlock_bh(&lock_data->spinlock);
if (_DRM_LOCKING_CONTEXT(old) == context) {
if (old & _DRM_LOCK_HELD) {
if (context != DRM_KERNEL_CONTEXT) {
DRM_ERROR("%d holds heavyweight lock\n",
context);
}
return 0;
}
}
if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) {
/* Have lock */
return 1;
}
return 0;
}
/**
* This takes a lock forcibly and hands it to context. Should ONLY be used
* inside *_unlock to give lock to kernel before calling *_dma_schedule.
*
* \param dev DRM device.
* \param lock lock pointer.
* \param context locking context.
* \return always one.
*
* Resets the lock file pointer.
* Marks the lock as held by the given context, via the \p cmpxchg instruction.
*/
static int drm_lock_transfer(struct drm_lock_data *lock_data,
unsigned int context)
{
unsigned int old, new, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
lock_data->file_priv = NULL;
do {
old = *lock;
new = context | _DRM_LOCK_HELD;
prev = cmpxchg(lock, old, new);
} while (prev != old);
return 1;
}
static int drm_legacy_lock_free(struct drm_lock_data *lock_data,
unsigned int context)
{
unsigned int old, new, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
spin_lock_bh(&lock_data->spinlock);
if (lock_data->kernel_waiters != 0) {
drm_lock_transfer(lock_data, 0);
lock_data->idle_has_lock = 1;
spin_unlock_bh(&lock_data->spinlock);
return 1;
}
spin_unlock_bh(&lock_data->spinlock);
do {
old = *lock;
new = _DRM_LOCKING_CONTEXT(old);
prev = cmpxchg(lock, old, new);
} while (prev != old);
if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
DRM_ERROR("%d freed heavyweight lock held by %d\n",
context, _DRM_LOCKING_CONTEXT(old));
return 1;
}
wake_up_interruptible(&lock_data->lock_queue);
return 0;
}
/** /**
* Lock ioctl. * Lock ioctl.
* *
...@@ -164,120 +268,6 @@ int drm_legacy_unlock(struct drm_device *dev, void *data, struct drm_file *file_ ...@@ -164,120 +268,6 @@ int drm_legacy_unlock(struct drm_device *dev, void *data, struct drm_file *file_
return 0; return 0;
} }
/**
* Take the heavyweight lock.
*
* \param lock lock pointer.
* \param context locking context.
* \return one if the lock is held, or zero otherwise.
*
* Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction.
*/
static
int drm_lock_take(struct drm_lock_data *lock_data,
unsigned int context)
{
unsigned int old, new, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
spin_lock_bh(&lock_data->spinlock);
do {
old = *lock;
if (old & _DRM_LOCK_HELD)
new = old | _DRM_LOCK_CONT;
else {
new = context | _DRM_LOCK_HELD |
((lock_data->user_waiters + lock_data->kernel_waiters > 1) ?
_DRM_LOCK_CONT : 0);
}
prev = cmpxchg(lock, old, new);
} while (prev != old);
spin_unlock_bh(&lock_data->spinlock);
if (_DRM_LOCKING_CONTEXT(old) == context) {
if (old & _DRM_LOCK_HELD) {
if (context != DRM_KERNEL_CONTEXT) {
DRM_ERROR("%d holds heavyweight lock\n",
context);
}
return 0;
}
}
if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) {
/* Have lock */
return 1;
}
return 0;
}
/**
* This takes a lock forcibly and hands it to context. Should ONLY be used
* inside *_unlock to give lock to kernel before calling *_dma_schedule.
*
* \param dev DRM device.
* \param lock lock pointer.
* \param context locking context.
* \return always one.
*
* Resets the lock file pointer.
* Marks the lock as held by the given context, via the \p cmpxchg instruction.
*/
static int drm_lock_transfer(struct drm_lock_data *lock_data,
unsigned int context)
{
unsigned int old, new, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
lock_data->file_priv = NULL;
do {
old = *lock;
new = context | _DRM_LOCK_HELD;
prev = cmpxchg(lock, old, new);
} while (prev != old);
return 1;
}
/**
* Free lock.
*
* \param dev DRM device.
* \param lock lock.
* \param context context.
*
* Resets the lock file pointer.
* Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task
* waiting on the lock queue.
*/
int drm_legacy_lock_free(struct drm_lock_data *lock_data, unsigned int context)
{
unsigned int old, new, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
spin_lock_bh(&lock_data->spinlock);
if (lock_data->kernel_waiters != 0) {
drm_lock_transfer(lock_data, 0);
lock_data->idle_has_lock = 1;
spin_unlock_bh(&lock_data->spinlock);
return 1;
}
spin_unlock_bh(&lock_data->spinlock);
do {
old = *lock;
new = _DRM_LOCKING_CONTEXT(old);
prev = cmpxchg(lock, old, new);
} while (prev != old);
if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
DRM_ERROR("%d freed heavyweight lock held by %d\n",
context, _DRM_LOCKING_CONTEXT(old));
return 1;
}
wake_up_interruptible(&lock_data->lock_queue);
return 0;
}
/** /**
* This function returns immediately and takes the hw lock * This function returns immediately and takes the hw lock
* with the kernel context if it is free, otherwise it gets the highest priority when and if * with the kernel context if it is free, otherwise it gets the highest priority when and if
...@@ -330,11 +320,27 @@ void drm_legacy_idlelock_release(struct drm_lock_data *lock_data) ...@@ -330,11 +320,27 @@ void drm_legacy_idlelock_release(struct drm_lock_data *lock_data)
} }
EXPORT_SYMBOL(drm_legacy_idlelock_release); EXPORT_SYMBOL(drm_legacy_idlelock_release);
int drm_legacy_i_have_hw_lock(struct drm_device *dev, static int drm_legacy_i_have_hw_lock(struct drm_device *dev,
struct drm_file *file_priv) struct drm_file *file_priv)
{ {
struct drm_master *master = file_priv->master; struct drm_master *master = file_priv->master;
return (file_priv->lock_count && master->lock.hw_lock && return (file_priv->lock_count && master->lock.hw_lock &&
_DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) && _DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) &&
master->lock.file_priv == file_priv); master->lock.file_priv == file_priv);
} }
void drm_legacy_lock_release(struct drm_device *dev, struct file *filp)
{
struct drm_file *file_priv = filp->private_data;
/* if the master has gone away we can't do anything with the lock */
if (!file_priv->minor->master)
return;
if (drm_legacy_i_have_hw_lock(dev, file_priv)) {
DRM_DEBUG("File %p released, freeing lock for context %d\n",
filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
drm_legacy_lock_free(&file_priv->master->lock,
_DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment