[PATCH] block: add unlocked_ioctl support for block devices
authorArnd Bergmann <arnd@arndb.de>
Thu, 23 Jun 2005 07:10:15 +0000 (00:10 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Thu, 23 Jun 2005 16:45:32 +0000 (09:45 -0700)
This patch allows block device drivers to convert their ioctl functions to
unlocked_ioctl() like character devices and other subsystems.  All
functions that were called with the BKL held before are still used that
way, but I would not be surprised if it could be removed from the ioctl
functions in drivers/block/ioctl.c themselves.

As a side note, I found that compat_blkdev_ioctl() acquires the BKL as
well, which looks like a bug.  I have checked that every user of
disk->fops->compat_ioctl() in the current git tree gets the BKL itself, so
it could easily be removed from compat_blkdev_ioctl().

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

drivers/block/ioctl.c
fs/block_dev.c
include/linux/fs.h

index 6d7bcc9..6e27847 100644 (file)
@@ -133,11 +133,9 @@ static int put_u64(unsigned long arg, u64 val)
        return put_user(val, (u64 __user *)arg);
 }
 
-int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
-                       unsigned long arg)
+static int blkdev_locked_ioctl(struct file *file, struct block_device *bdev,
+                               unsigned cmd, unsigned long arg)
 {
-       struct block_device *bdev = inode->i_bdev;
-       struct gendisk *disk = bdev->bd_disk;
        struct backing_dev_info *bdi;
        int ret, n;
 
@@ -190,36 +188,72 @@ int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
                return put_ulong(arg, bdev->bd_inode->i_size >> 9);
        case BLKGETSIZE64:
                return put_u64(arg, bdev->bd_inode->i_size);
+       }
+       return -ENOIOCTLCMD;
+}
+
+static int blkdev_driver_ioctl(struct inode *inode, struct file *file,
+               struct gendisk *disk, unsigned cmd, unsigned long arg)
+{
+       int ret;
+       if (disk->fops->unlocked_ioctl)
+               return disk->fops->unlocked_ioctl(file, cmd, arg);
+
+       if (disk->fops->ioctl) {
+               lock_kernel();
+               ret = disk->fops->ioctl(inode, file, cmd, arg);
+               unlock_kernel();
+               return ret;
+       }
+
+       return -ENOTTY;
+}
+
+int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
+                       unsigned long arg)
+{
+       struct block_device *bdev = inode->i_bdev;
+       struct gendisk *disk = bdev->bd_disk;
+       int ret, n;
+
+       switch(cmd) {
        case BLKFLSBUF:
                if (!capable(CAP_SYS_ADMIN))
                        return -EACCES;
-               if (disk->fops->ioctl) {
-                       ret = disk->fops->ioctl(inode, file, cmd, arg);
-                       /* -EINVAL to handle old uncorrected drivers */
-                       if (ret != -EINVAL && ret != -ENOTTY)
-                               return ret;
-               }
+
+               ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg);
+               /* -EINVAL to handle old uncorrected drivers */
+               if (ret != -EINVAL && ret != -ENOTTY)
+                       return ret;
+
+               lock_kernel();
                fsync_bdev(bdev);
                invalidate_bdev(bdev, 0);
+               unlock_kernel();
                return 0;
+
        case BLKROSET:
-               if (disk->fops->ioctl) {
-                       ret = disk->fops->ioctl(inode, file, cmd, arg);
-                       /* -EINVAL to handle old uncorrected drivers */
-                       if (ret != -EINVAL && ret != -ENOTTY)
-                               return ret;
-               }
+               ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg);
+               /* -EINVAL to handle old uncorrected drivers */
+               if (ret != -EINVAL && ret != -ENOTTY)
+                       return ret;
                if (!capable(CAP_SYS_ADMIN))
                        return -EACCES;
                if (get_user(n, (int __user *)(arg)))
                        return -EFAULT;
+               lock_kernel();
                set_device_ro(bdev, n);
+               unlock_kernel();
                return 0;
-       default:
-               if (disk->fops->ioctl)
-                       return disk->fops->ioctl(inode, file, cmd, arg);
        }
-       return -ENOTTY;
+
+       lock_kernel();
+       ret = blkdev_locked_ioctl(file, bdev, cmd, arg);
+       unlock_kernel();
+       if (ret != -ENOIOCTLCMD)
+               return ret;
+
+       return blkdev_driver_ioctl(inode, file, disk, cmd, arg);
 }
 
 /* Most of the generic ioctls are handled in the normal fallback path.
index c0cbd1b..e0df94c 100644 (file)
@@ -777,8 +777,7 @@ static ssize_t blkdev_file_aio_write(struct kiocb *iocb, const char __user *buf,
        return generic_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos);
 }
 
-static int block_ioctl(struct inode *inode, struct file *file, unsigned cmd,
-                       unsigned long arg)
+static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 {
        return blkdev_ioctl(file->f_mapping->host, file, cmd, arg);
 }
@@ -803,7 +802,7 @@ struct file_operations def_blk_fops = {
        .aio_write      = blkdev_file_aio_write, 
        .mmap           = generic_file_mmap,
        .fsync          = block_fsync,
-       .ioctl          = block_ioctl,
+       .unlocked_ioctl = block_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = compat_blkdev_ioctl,
 #endif
index 3622e95..9b1278e 100644 (file)
@@ -884,6 +884,7 @@ struct block_device_operations {
        int (*open) (struct inode *, struct file *);
        int (*release) (struct inode *, struct file *);
        int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
+       long (*unlocked_ioctl) (struct file *, unsigned, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned, unsigned long);
        int (*media_changed) (struct gendisk *);
        int (*revalidate_disk) (struct gendisk *);