diff options
author | Owain Gordon Ainsworth <oga@openbsd.org> | 2008-07-07 17:23:48 +0100 |
---|---|---|
committer | Robert Noland <rnoland@2hip.net> | 2008-07-16 21:37:39 -0400 |
commit | 74cf1f91be7f4139601624af0343e3d411190dec (patch) | |
tree | 05b856fe8ee275c8c153f89c5a85ef56d7ea580e /bsd-core/drm_irq.c | |
parent | 96580f660e5509dcf6c34de5630e3d36b156bcd5 (diff) |
BSD: change drm_locked_task*() to use the same scheme as linux.
The current code can sleep in an interrupt handler, that is bad. So
instead if we can't grab the lock, flag it and run the tasklet on
unlock.
Signed-off-by: Robert Noland <rnoland@2hip.net>
Diffstat (limited to 'bsd-core/drm_irq.c')
-rw-r--r-- | bsd-core/drm_irq.c | 45 |
1 files changed, 23 insertions, 22 deletions
diff --git a/bsd-core/drm_irq.c b/bsd-core/drm_irq.c index c3ecd28b..a066cfc9 100644 --- a/bsd-core/drm_irq.c +++ b/bsd-core/drm_irq.c @@ -578,41 +578,42 @@ static void drm_locked_task(void *context, int pending __unused) { struct drm_device *dev = context; - DRM_LOCK(); - for (;;) { - int ret; - - if (drm_lock_take(&dev->lock.hw_lock->lock, - DRM_KERNEL_CONTEXT)) - { - dev->lock.file_priv = NULL; /* kernel owned */ - dev->lock.lock_time = jiffies; - atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); - break; /* Got lock */ - } + DRM_SPINLOCK(&dev->tsk_lock); - /* Contention */ -#if defined(__FreeBSD__) && __FreeBSD_version > 500000 - ret = mtx_sleep((void *)&dev->lock.lock_queue, &dev->dev_lock, - PZERO | PCATCH, "drmlk2", 0); -#else - ret = tsleep((void *)&dev->lock.lock_queue, PZERO | PCATCH, - "drmlk2", 0); -#endif - if (ret != 0) - return; + DRM_LOCK(); /* XXX drm_lock_take() should do it's own locking */ + if (dev->locked_task_call == NULL || + drm_lock_take(&dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT) == 0) { + DRM_UNLOCK(); + DRM_SPINUNLOCK(&dev->tsk_lock); + return; } + + dev->lock.file_priv = NULL; /* kernel owned */ + dev->lock.lock_time = jiffies; + atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); + DRM_UNLOCK(); dev->locked_task_call(dev); drm_lock_free(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + + dev->locked_task_call = NULL; + + DRM_SPINUNLOCK(&dev->tsk_lock); } void drm_locked_tasklet(struct drm_device *dev, void (*tasklet)(struct drm_device *dev)) { + DRM_SPINLOCK(&dev->tsk_lock); + if (dev->locked_task_call != NULL) { + DRM_SPINUNLOCK(&dev->tsk_lock); + return; + } + dev->locked_task_call = tasklet; + DRM_SPINUNLOCK(&dev->tsk_lock); taskqueue_enqueue(taskqueue_swi, &dev->locked_task); } |