dm snapshot: style cleanups
[linux-2.6.git] / block / ioctl.c
index 9cfa2e1..1124cd2 100644 (file)
@@ -1,10 +1,10 @@
 #include <linux/capability.h>
 #include <linux/blkdev.h>
+#include <linux/gfp.h>
 #include <linux/blkpg.h>
 #include <linux/hdreg.h>
 #include <linux/backing-dev.h>
 #include <linux/buffer_head.h>
-#include <linux/smp_lock.h>
 #include <linux/blktrace_api.h>
 #include <asm/uaccess.h>
 
@@ -12,11 +12,12 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user
 {
        struct block_device *bdevp;
        struct gendisk *disk;
+       struct hd_struct *part;
        struct blkpg_ioctl_arg a;
        struct blkpg_partition p;
+       struct disk_part_iter piter;
        long long start, length;
-       int part;
-       int i;
+       int partno;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
@@ -27,8 +28,8 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user
        disk = bdev->bd_disk;
        if (bdev != bdev->bd_contains)
                return -EINVAL;
-       part = p.pno;
-       if (part <= 0 || part >= disk->minors)
+       partno = p.pno;
+       if (partno <= 0)
                return -EINVAL;
        switch (a.op) {
                case BLKPG_ADD_PARTITION:
@@ -42,36 +43,37 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user
                                    || pstart < 0 || plength < 0)
                                        return -EINVAL;
                        }
-                       /* partition number in use? */
+
                        mutex_lock(&bdev->bd_mutex);
-                       if (disk->part[part - 1]) {
-                               mutex_unlock(&bdev->bd_mutex);
-                               return -EBUSY;
-                       }
-                       /* overlap? */
-                       for (i = 0; i < disk->minors - 1; i++) {
-                               struct hd_struct *s = disk->part[i];
 
-                               if (!s)
-                                       continue;
-                               if (!(start+length <= s->start_sect ||
-                                     start >= s->start_sect + s->nr_sects)) {
+                       /* overlap? */
+                       disk_part_iter_init(&piter, disk,
+                                           DISK_PITER_INCL_EMPTY);
+                       while ((part = disk_part_iter_next(&piter))) {
+                               if (!(start + length <= part->start_sect ||
+                                     start >= part->start_sect + part->nr_sects)) {
+                                       disk_part_iter_exit(&piter);
                                        mutex_unlock(&bdev->bd_mutex);
                                        return -EBUSY;
                                }
                        }
+                       disk_part_iter_exit(&piter);
+
                        /* all seems OK */
-                       add_partition(disk, part, start, length);
+                       part = add_partition(disk, partno, start, length,
+                                            ADDPART_FLAG_NONE, NULL);
                        mutex_unlock(&bdev->bd_mutex);
-                       return 0;
+                       return IS_ERR(part) ? PTR_ERR(part) : 0;
                case BLKPG_DEL_PARTITION:
-                       if (!disk->part[part-1])
-                               return -ENXIO;
-                       if (disk->part[part - 1]->nr_sects == 0)
+                       part = disk_get_part(disk, partno);
+                       if (!part)
                                return -ENXIO;
-                       bdevp = bdget_disk(disk, part);
+
+                       bdevp = bdget(part_devt(part));
+                       disk_put_part(part);
                        if (!bdevp)
                                return -ENOMEM;
+
                        mutex_lock(&bdevp->bd_mutex);
                        if (bdevp->bd_openers) {
                                mutex_unlock(&bdevp->bd_mutex);
@@ -80,10 +82,10 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user
                        }
                        /* all seems OK */
                        fsync_bdev(bdevp);
-                       invalidate_bdev(bdevp, 0);
+                       invalidate_bdev(bdevp);
 
-                       mutex_lock(&bdev->bd_mutex);
-                       delete_partition(disk, part);
+                       mutex_lock_nested(&bdev->bd_mutex, 1);
+                       delete_partition(disk, partno);
                        mutex_unlock(&bdev->bd_mutex);
                        mutex_unlock(&bdevp->bd_mutex);
                        bdput(bdevp);
@@ -99,7 +101,7 @@ static int blkdev_reread_part(struct block_device *bdev)
        struct gendisk *disk = bdev->bd_disk;
        int res;
 
-       if (disk->minors == 1 || bdev != bdev->bd_contains)
+       if (!disk_partitionable(disk) || bdev != bdev->bd_contains)
                return -EINVAL;
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
@@ -110,6 +112,25 @@ static int blkdev_reread_part(struct block_device *bdev)
        return res;
 }
 
+static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,
+                            uint64_t len, int secure)
+{
+       unsigned long flags = 0;
+
+       if (start & 511)
+               return -EINVAL;
+       if (len & 511)
+               return -EINVAL;
+       start >>= 9;
+       len >>= 9;
+
+       if (start + len > (i_size_read(bdev->bd_inode) >> 9))
+               return -EINVAL;
+       if (secure)
+               flags |= BLKDEV_DISCARD_SECURE;
+       return blkdev_issue_discard(bdev, start, len, GFP_KERNEL, flags);
+}
+
 static int put_ushort(unsigned long arg, unsigned short val)
 {
        return put_user(val, (unsigned short __user *)arg);
@@ -120,6 +141,11 @@ static int put_int(unsigned long arg, int val)
        return put_user(val, (int __user *)arg);
 }
 
+static int put_uint(unsigned long arg, unsigned int val)
+{
+       return put_user(val, (unsigned int __user *)arg);
+}
+
 static int put_long(unsigned long arg, long val)
 {
        return put_user(val, (long __user *)arg);
@@ -135,92 +161,32 @@ static int put_u64(unsigned long arg, u64 val)
        return put_user(val, (u64 __user *)arg);
 }
 
-static int blkdev_locked_ioctl(struct file *file, struct block_device *bdev,
-                               unsigned cmd, unsigned long arg)
+int __blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode,
+                       unsigned cmd, unsigned long arg)
 {
-       struct backing_dev_info *bdi;
-       int ret, n;
-
-       switch (cmd) {
-       case BLKRAGET:
-       case BLKFRAGET:
-               if (!arg)
-                       return -EINVAL;
-               bdi = blk_get_backing_dev_info(bdev);
-               if (bdi == NULL)
-                       return -ENOTTY;
-               return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512);
-       case BLKROGET:
-               return put_int(arg, bdev_read_only(bdev) != 0);
-       case BLKBSZGET: /* get the logical block size (cf. BLKSSZGET) */
-               return put_int(arg, block_size(bdev));
-       case BLKSSZGET: /* get block device hardware sector size */
-               return put_int(arg, bdev_hardsect_size(bdev));
-       case BLKSECTGET:
-               return put_ushort(arg, bdev_get_queue(bdev)->max_sectors);
-       case BLKRASET:
-       case BLKFRASET:
-               if(!capable(CAP_SYS_ADMIN))
-                       return -EACCES;
-               bdi = blk_get_backing_dev_info(bdev);
-               if (bdi == NULL)
-                       return -ENOTTY;
-               bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
-               return 0;
-       case BLKBSZSET:
-               /* set the logical block size */
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EACCES;
-               if (!arg)
-                       return -EINVAL;
-               if (get_user(n, (int __user *) arg))
-                       return -EFAULT;
-               if (bd_claim(bdev, file) < 0)
-                       return -EBUSY;
-               ret = set_blocksize(bdev, n);
-               bd_release(bdev);
-               return ret;
-       case BLKPG:
-               return blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg);
-       case BLKRRPART:
-               return blkdev_reread_part(bdev);
-       case BLKGETSIZE:
-               if ((bdev->bd_inode->i_size >> 9) > ~0UL)
-                       return -EFBIG;
-               return put_ulong(arg, bdev->bd_inode->i_size >> 9);
-       case BLKGETSIZE64:
-               return put_u64(arg, bdev->bd_inode->i_size);
-       case BLKTRACESTART:
-       case BLKTRACESTOP:
-       case BLKTRACESETUP:
-       case BLKTRACETEARDOWN:
-               return blk_trace_ioctl(bdev, cmd, (char __user *) arg);
-       }
-       return -ENOIOCTLCMD;
-}
+       struct gendisk *disk = bdev->bd_disk;
 
-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;
-       }
+       if (disk->fops->ioctl)
+               return disk->fops->ioctl(bdev, mode, cmd, arg);
 
        return -ENOTTY;
 }
+/*
+ * For the record: _GPL here is only because somebody decided to slap it
+ * on the previous export.  Sheer idiocy, since it wasn't copyrightable
+ * at all and could be open-coded without any exports by anybody who cares.
+ */
+EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl);
 
-int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
+/*
+ * always keep this in sync with compat_blkdev_ioctl()
+ */
+int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
                        unsigned long arg)
 {
-       struct block_device *bdev = inode->i_bdev;
        struct gendisk *disk = bdev->bd_disk;
+       struct backing_dev_info *bdi;
+       loff_t size;
        int ret, n;
 
        switch(cmd) {
@@ -228,19 +194,17 @@ int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
                if (!capable(CAP_SYS_ADMIN))
                        return -EACCES;
 
-               ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg);
+               ret = __blkdev_driver_ioctl(bdev, mode, 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();
+               invalidate_bdev(bdev);
                return 0;
 
        case BLKROSET:
-               ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg);
+               ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
                /* -EINVAL to handle old uncorrected drivers */
                if (ret != -EINVAL && ret != -ENOTTY)
                        return ret;
@@ -248,10 +212,23 @@ int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
                        return -EACCES;
                if (get_user(n, (int __user *)(arg)))
                        return -EFAULT;
-               lock_kernel();
                set_device_ro(bdev, n);
-               unlock_kernel();
                return 0;
+
+       case BLKDISCARD:
+       case BLKSECDISCARD: {
+               uint64_t range[2];
+
+               if (!(mode & FMODE_WRITE))
+                       return -EBADF;
+
+               if (copy_from_user(range, (void __user *)arg, sizeof(range)))
+                       return -EFAULT;
+
+               return blk_ioctl_discard(bdev, range[0], range[1],
+                                        cmd == BLKSECDISCARD);
+       }
+
        case HDIO_GETGEO: {
                struct hd_geometry geo;
 
@@ -264,6 +241,7 @@ int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
                 * We need to set the startsect first, the driver may
                 * want to override it.
                 */
+               memset(&geo, 0, sizeof(geo));
                geo.start = get_start_sect(bdev);
                ret = disk->fops->getgeo(bdev, &geo);
                if (ret)
@@ -273,31 +251,80 @@ int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
                        return -EFAULT;
                return 0;
        }
-       }
-
-       lock_kernel();
-       ret = blkdev_locked_ioctl(file, bdev, cmd, arg);
-       unlock_kernel();
-       if (ret != -ENOIOCTLCMD)
+       case BLKRAGET:
+       case BLKFRAGET:
+               if (!arg)
+                       return -EINVAL;
+               bdi = blk_get_backing_dev_info(bdev);
+               if (bdi == NULL)
+                       return -ENOTTY;
+               return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512);
+       case BLKROGET:
+               return put_int(arg, bdev_read_only(bdev) != 0);
+       case BLKBSZGET: /* get block device soft block size (cf. BLKSSZGET) */
+               return put_int(arg, block_size(bdev));
+       case BLKSSZGET: /* get block device logical block size */
+               return put_int(arg, bdev_logical_block_size(bdev));
+       case BLKPBSZGET: /* get block device physical block size */
+               return put_uint(arg, bdev_physical_block_size(bdev));
+       case BLKIOMIN:
+               return put_uint(arg, bdev_io_min(bdev));
+       case BLKIOOPT:
+               return put_uint(arg, bdev_io_opt(bdev));
+       case BLKALIGNOFF:
+               return put_int(arg, bdev_alignment_offset(bdev));
+       case BLKDISCARDZEROES:
+               return put_uint(arg, bdev_discard_zeroes_data(bdev));
+       case BLKSECTGET:
+               return put_ushort(arg, queue_max_sectors(bdev_get_queue(bdev)));
+       case BLKRASET:
+       case BLKFRASET:
+               if(!capable(CAP_SYS_ADMIN))
+                       return -EACCES;
+               bdi = blk_get_backing_dev_info(bdev);
+               if (bdi == NULL)
+                       return -ENOTTY;
+               bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
+               return 0;
+       case BLKBSZSET:
+               /* set the logical block size */
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EACCES;
+               if (!arg)
+                       return -EINVAL;
+               if (get_user(n, (int __user *) arg))
+                       return -EFAULT;
+               if (!(mode & FMODE_EXCL)) {
+                       bdgrab(bdev);
+                       if (blkdev_get(bdev, mode | FMODE_EXCL, &bdev) < 0)
+                               return -EBUSY;
+               }
+               ret = set_blocksize(bdev, n);
+               if (!(mode & FMODE_EXCL))
+                       blkdev_put(bdev, mode | FMODE_EXCL);
                return ret;
-
-       return blkdev_driver_ioctl(inode, file, disk, cmd, arg);
-}
-
-/* Most of the generic ioctls are handled in the normal fallback path.
-   This assumes the blkdev's low level compat_ioctl always returns
-   ENOIOCTLCMD for unknown ioctls. */
-long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
-{
-       struct block_device *bdev = file->f_dentry->d_inode->i_bdev;
-       struct gendisk *disk = bdev->bd_disk;
-       int ret = -ENOIOCTLCMD;
-       if (disk->fops->compat_ioctl) {
-               lock_kernel();
-               ret = disk->fops->compat_ioctl(file, cmd, arg);
-               unlock_kernel();
+       case BLKPG:
+               ret = blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg);
+               break;
+       case BLKRRPART:
+               ret = blkdev_reread_part(bdev);
+               break;
+       case BLKGETSIZE:
+               size = i_size_read(bdev->bd_inode);
+               if ((size >> 9) > ~0UL)
+                       return -EFBIG;
+               return put_ulong(arg, size >> 9);
+       case BLKGETSIZE64:
+               return put_u64(arg, i_size_read(bdev->bd_inode));
+       case BLKTRACESTART:
+       case BLKTRACESTOP:
+       case BLKTRACESETUP:
+       case BLKTRACETEARDOWN:
+               ret = blk_trace_ioctl(bdev, cmd, (char __user *) arg);
+               break;
+       default:
+               ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
        }
        return ret;
 }
-
 EXPORT_SYMBOL_GPL(blkdev_ioctl);