Commit 58374713 authored by Arnd Bergmann's avatar Arnd Bergmann Committed by Dave Airlie

drm: kill BKL from common code

This restricts the use of the big kernel lock to the i830 and i810
device drivers. The three remaining users in common code (open, ioctl
and release) get converted to a new mutex, the drm_global_mutex,
making the locking stricter than the big kernel lock.

This may have a performance impact, but only in those cases that
currently don't use DRM_UNLOCKED flag in the ioctl list and would
benefit from that anyway.

The reason why i810 and i830 cannot use drm_global_mutex in their
mmap functions is a lock-order inversion problem between the current
use of the BKL and mmap_sem in these drivers. Since the BKL has
release-on-sleep semantics, it's harmless but it would cause trouble
if we replace the BKL with a mutex.

Instead, these drivers get their own ioctl wrappers that take the
BKL around every ioctl call and then set their own handlers as
DRM_UNLOCKED.
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
Cc: David Airlie <airlied@linux.ie>
Cc: dri-devel@lists.freedesktop.org
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent a1e09b62
...@@ -481,9 +481,9 @@ long drm_ioctl(struct file *filp, ...@@ -481,9 +481,9 @@ long drm_ioctl(struct file *filp,
if (ioctl->flags & DRM_UNLOCKED) if (ioctl->flags & DRM_UNLOCKED)
retcode = func(dev, kdata, file_priv); retcode = func(dev, kdata, file_priv);
else { else {
lock_kernel(); mutex_lock(&drm_global_mutex);
retcode = func(dev, kdata, file_priv); retcode = func(dev, kdata, file_priv);
unlock_kernel(); mutex_unlock(&drm_global_mutex);
} }
if (cmd & IOC_OUT) { if (cmd & IOC_OUT) {
......
...@@ -39,6 +39,9 @@ ...@@ -39,6 +39,9 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
/* from BKL pushdown: note that nothing else serializes idr_find() */
DEFINE_MUTEX(drm_global_mutex);
static int drm_open_helper(struct inode *inode, struct file *filp, static int drm_open_helper(struct inode *inode, struct file *filp,
struct drm_device * dev); struct drm_device * dev);
...@@ -175,8 +178,7 @@ int drm_stub_open(struct inode *inode, struct file *filp) ...@@ -175,8 +178,7 @@ int drm_stub_open(struct inode *inode, struct file *filp)
DRM_DEBUG("\n"); DRM_DEBUG("\n");
/* BKL pushdown: note that nothing else serializes idr_find() */ mutex_lock(&drm_global_mutex);
lock_kernel();
minor = idr_find(&drm_minors_idr, minor_id); minor = idr_find(&drm_minors_idr, minor_id);
if (!minor) if (!minor)
goto out; goto out;
...@@ -197,7 +199,7 @@ int drm_stub_open(struct inode *inode, struct file *filp) ...@@ -197,7 +199,7 @@ int drm_stub_open(struct inode *inode, struct file *filp)
fops_put(old_fops); fops_put(old_fops);
out: out:
unlock_kernel(); mutex_unlock(&drm_global_mutex);
return err; return err;
} }
...@@ -472,7 +474,7 @@ int drm_release(struct inode *inode, struct file *filp) ...@@ -472,7 +474,7 @@ int drm_release(struct inode *inode, struct file *filp)
struct drm_device *dev = file_priv->minor->dev; struct drm_device *dev = file_priv->minor->dev;
int retcode = 0; int retcode = 0;
lock_kernel(); mutex_lock(&drm_global_mutex);
DRM_DEBUG("open_count = %d\n", dev->open_count); DRM_DEBUG("open_count = %d\n", dev->open_count);
...@@ -573,17 +575,14 @@ int drm_release(struct inode *inode, struct file *filp) ...@@ -573,17 +575,14 @@ int drm_release(struct inode *inode, struct file *filp)
if (atomic_read(&dev->ioctl_count)) { if (atomic_read(&dev->ioctl_count)) {
DRM_ERROR("Device busy: %d\n", DRM_ERROR("Device busy: %d\n",
atomic_read(&dev->ioctl_count)); atomic_read(&dev->ioctl_count));
spin_unlock(&dev->count_lock); retcode = -EBUSY;
unlock_kernel(); goto out;
return -EBUSY;
} }
spin_unlock(&dev->count_lock); retcode = drm_lastclose(dev);
unlock_kernel();
return drm_lastclose(dev);
} }
out:
spin_unlock(&dev->count_lock); spin_unlock(&dev->count_lock);
mutex_unlock(&drm_global_mutex);
unlock_kernel();
return retcode; return retcode;
} }
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <linux/interrupt.h> /* For task queue support */ #include <linux/interrupt.h> /* For task queue support */
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#define I810_BUF_FREE 2 #define I810_BUF_FREE 2
...@@ -1240,22 +1241,35 @@ int i810_driver_dma_quiescent(struct drm_device *dev) ...@@ -1240,22 +1241,35 @@ int i810_driver_dma_quiescent(struct drm_device *dev)
return 0; return 0;
} }
/*
* call the drm_ioctl under the big kernel lock because
* to lock against the i810_mmap_buffers function.
*/
long i810_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret;
lock_kernel();
ret = drm_ioctl(file, cmd, arg);
unlock_kernel();
return ret;
}
struct drm_ioctl_desc i810_ioctls[] = { struct drm_ioctl_desc i810_ioctls[] = {
DRM_IOCTL_DEF(DRM_I810_INIT, i810_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_I810_INIT, i810_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_VERTEX, i810_dma_vertex, DRM_AUTH), DRM_IOCTL_DEF(DRM_I810_VERTEX, i810_dma_vertex, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_CLEAR, i810_clear_bufs, DRM_AUTH), DRM_IOCTL_DEF(DRM_I810_CLEAR, i810_clear_bufs, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_FLUSH, i810_flush_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_I810_FLUSH, i810_flush_ioctl, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_GETAGE, i810_getage, DRM_AUTH), DRM_IOCTL_DEF(DRM_I810_GETAGE, i810_getage, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_GETBUF, i810_getbuf, DRM_AUTH), DRM_IOCTL_DEF(DRM_I810_GETBUF, i810_getbuf, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_SWAP, i810_swap_bufs, DRM_AUTH), DRM_IOCTL_DEF(DRM_I810_SWAP, i810_swap_bufs, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_COPY, i810_copybuf, DRM_AUTH), DRM_IOCTL_DEF(DRM_I810_COPY, i810_copybuf, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_DOCOPY, i810_docopy, DRM_AUTH), DRM_IOCTL_DEF(DRM_I810_DOCOPY, i810_docopy, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_OV0INFO, i810_ov0_info, DRM_AUTH), DRM_IOCTL_DEF(DRM_I810_OV0INFO, i810_ov0_info, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_FSTATUS, i810_fstatus, DRM_AUTH), DRM_IOCTL_DEF(DRM_I810_FSTATUS, i810_fstatus, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_OV0FLIP, i810_ov0_flip, DRM_AUTH), DRM_IOCTL_DEF(DRM_I810_OV0FLIP, i810_ov0_flip, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_MC, i810_dma_mc, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_I810_MC, i810_dma_mc, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_RSTATUS, i810_rstatus, DRM_AUTH), DRM_IOCTL_DEF(DRM_I810_RSTATUS, i810_rstatus, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I810_FLIP, i810_flip_bufs, DRM_AUTH) DRM_IOCTL_DEF(DRM_I810_FLIP, i810_flip_bufs, DRM_AUTH|DRM_UNLOCKED),
}; };
int i810_max_ioctl = DRM_ARRAY_SIZE(i810_ioctls); int i810_max_ioctl = DRM_ARRAY_SIZE(i810_ioctls);
......
...@@ -59,7 +59,7 @@ static struct drm_driver driver = { ...@@ -59,7 +59,7 @@ static struct drm_driver driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = drm_open, .open = drm_open,
.release = drm_release, .release = drm_release,
.unlocked_ioctl = drm_ioctl, .unlocked_ioctl = i810_ioctl,
.mmap = drm_mmap, .mmap = drm_mmap,
.poll = drm_poll, .poll = drm_poll,
.fasync = drm_fasync, .fasync = drm_fasync,
......
...@@ -126,6 +126,7 @@ extern void i810_driver_reclaim_buffers_locked(struct drm_device *dev, ...@@ -126,6 +126,7 @@ extern void i810_driver_reclaim_buffers_locked(struct drm_device *dev,
struct drm_file *file_priv); struct drm_file *file_priv);
extern int i810_driver_device_is_agp(struct drm_device *dev); extern int i810_driver_device_is_agp(struct drm_device *dev);
extern long i810_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
extern struct drm_ioctl_desc i810_ioctls[]; extern struct drm_ioctl_desc i810_ioctls[];
extern int i810_max_ioctl; extern int i810_max_ioctl;
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "i830_drm.h" #include "i830_drm.h"
#include "i830_drv.h" #include "i830_drv.h"
#include <linux/interrupt.h> /* For task queue support */ #include <linux/interrupt.h> /* For task queue support */
#include <linux/smp_lock.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -1509,21 +1510,34 @@ int i830_driver_dma_quiescent(struct drm_device *dev) ...@@ -1509,21 +1510,34 @@ int i830_driver_dma_quiescent(struct drm_device *dev)
return 0; return 0;
} }
/*
* call the drm_ioctl under the big kernel lock because
* to lock against the i830_mmap_buffers function.
*/
long i830_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret;
lock_kernel();
ret = drm_ioctl(file, cmd, arg);
unlock_kernel();
return ret;
}
struct drm_ioctl_desc i830_ioctls[] = { struct drm_ioctl_desc i830_ioctls[] = {
DRM_IOCTL_DEF(DRM_I830_INIT, i830_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_I830_INIT, i830_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I830_VERTEX, i830_dma_vertex, DRM_AUTH), DRM_IOCTL_DEF(DRM_I830_VERTEX, i830_dma_vertex, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I830_CLEAR, i830_clear_bufs, DRM_AUTH), DRM_IOCTL_DEF(DRM_I830_CLEAR, i830_clear_bufs, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I830_FLUSH, i830_flush_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_I830_FLUSH, i830_flush_ioctl, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I830_GETAGE, i830_getage, DRM_AUTH), DRM_IOCTL_DEF(DRM_I830_GETAGE, i830_getage, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I830_GETBUF, i830_getbuf, DRM_AUTH), DRM_IOCTL_DEF(DRM_I830_GETBUF, i830_getbuf, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I830_SWAP, i830_swap_bufs, DRM_AUTH), DRM_IOCTL_DEF(DRM_I830_SWAP, i830_swap_bufs, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I830_COPY, i830_copybuf, DRM_AUTH), DRM_IOCTL_DEF(DRM_I830_COPY, i830_copybuf, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I830_DOCOPY, i830_docopy, DRM_AUTH), DRM_IOCTL_DEF(DRM_I830_DOCOPY, i830_docopy, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I830_FLIP, i830_flip_bufs, DRM_AUTH), DRM_IOCTL_DEF(DRM_I830_FLIP, i830_flip_bufs, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I830_IRQ_EMIT, i830_irq_emit, DRM_AUTH), DRM_IOCTL_DEF(DRM_I830_IRQ_EMIT, i830_irq_emit, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I830_IRQ_WAIT, i830_irq_wait, DRM_AUTH), DRM_IOCTL_DEF(DRM_I830_IRQ_WAIT, i830_irq_wait, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I830_GETPARAM, i830_getparam, DRM_AUTH), DRM_IOCTL_DEF(DRM_I830_GETPARAM, i830_getparam, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_I830_SETPARAM, i830_setparam, DRM_AUTH) DRM_IOCTL_DEF(DRM_I830_SETPARAM, i830_setparam, DRM_AUTH|DRM_UNLOCKED),
}; };
int i830_max_ioctl = DRM_ARRAY_SIZE(i830_ioctls); int i830_max_ioctl = DRM_ARRAY_SIZE(i830_ioctls);
......
...@@ -70,7 +70,7 @@ static struct drm_driver driver = { ...@@ -70,7 +70,7 @@ static struct drm_driver driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = drm_open, .open = drm_open,
.release = drm_release, .release = drm_release,
.unlocked_ioctl = drm_ioctl, .unlocked_ioctl = i830_ioctl,
.mmap = drm_mmap, .mmap = drm_mmap,
.poll = drm_poll, .poll = drm_poll,
.fasync = drm_fasync, .fasync = drm_fasync,
......
...@@ -122,6 +122,7 @@ typedef struct drm_i830_private { ...@@ -122,6 +122,7 @@ typedef struct drm_i830_private {
} drm_i830_private_t; } drm_i830_private_t;
long i830_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
extern struct drm_ioctl_desc i830_ioctls[]; extern struct drm_ioctl_desc i830_ioctls[];
extern int i830_max_ioctl; extern int i830_max_ioctl;
......
...@@ -52,7 +52,6 @@ ...@@ -52,7 +52,6 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/smp_lock.h> /* For (un)lock_kernel */
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/cdev.h> #include <linux/cdev.h>
...@@ -1152,6 +1151,7 @@ extern long drm_compat_ioctl(struct file *filp, ...@@ -1152,6 +1151,7 @@ extern long drm_compat_ioctl(struct file *filp,
extern int drm_lastclose(struct drm_device *dev); extern int drm_lastclose(struct drm_device *dev);
/* Device support (drm_fops.h) */ /* Device support (drm_fops.h) */
extern struct mutex drm_global_mutex;
extern int drm_open(struct inode *inode, struct file *filp); extern int drm_open(struct inode *inode, struct file *filp);
extern int drm_stub_open(struct inode *inode, struct file *filp); extern int drm_stub_open(struct inode *inode, struct file *filp);
extern int drm_fasync(int fd, struct file *filp, int on); extern int drm_fasync(int fd, struct file *filp, int on);
......
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