Merge branch 'drm-intel-next' of git://git.kernel.org/pub/scm/linux/kernel/git/anholt...
[pandora-kernel.git] / drivers / block / loop.c
index bf03455..ddae808 100644 (file)
@@ -474,10 +474,35 @@ static int do_bio_filebacked(struct loop_device *lo, struct bio *bio)
        int ret;
 
        pos = ((loff_t) bio->bi_sector << 9) + lo->lo_offset;
-       if (bio_rw(bio) == WRITE)
+
+       if (bio_rw(bio) == WRITE) {
+               int barrier = bio_barrier(bio);
+               struct file *file = lo->lo_backing_file;
+
+               if (barrier) {
+                       if (unlikely(!file->f_op->fsync)) {
+                               ret = -EOPNOTSUPP;
+                               goto out;
+                       }
+
+                       ret = vfs_fsync(file, file->f_path.dentry, 0);
+                       if (unlikely(ret)) {
+                               ret = -EIO;
+                               goto out;
+                       }
+               }
+
                ret = lo_send(lo, bio, pos);
-       else
+
+               if (barrier && !ret) {
+                       ret = vfs_fsync(file, file->f_path.dentry, 0);
+                       if (unlikely(ret))
+                               ret = -EIO;
+               }
+       } else
                ret = lo_receive(lo, bio, lo->lo_blocksize, pos);
+
+out:
        return ret;
 }
 
@@ -826,6 +851,9 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
        lo->lo_queue->queuedata = lo;
        lo->lo_queue->unplug_fn = loop_unplug;
 
+       if (!(lo_flags & LO_FLAGS_READ_ONLY) && file->f_op->fsync)
+               blk_queue_ordered(lo->lo_queue, QUEUE_ORDERED_DRAIN, NULL);
+
        set_capacity(lo->lo_disk, size);
        bd_set_size(bdev, size << 9);
 
@@ -941,11 +969,18 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
                bd_set_size(bdev, 0);
        mapping_set_gfp_mask(filp->f_mapping, gfp);
        lo->lo_state = Lo_unbound;
-       fput(filp);
        /* This is safe: open() is still holding a reference. */
        module_put(THIS_MODULE);
        if (max_part > 0)
                ioctl_by_bdev(bdev, BLKRRPART, 0);
+       mutex_unlock(&lo->lo_ctl_mutex);
+       /*
+        * Need not hold lo_ctl_mutex to fput backing file.
+        * Calling fput holding lo_ctl_mutex triggers a circular
+        * lock dependency possibility warning as fput can take
+        * bd_mutex which is usually taken before lo_ctl_mutex.
+        */
+       fput(filp);
        return 0;
 }
 
@@ -1157,13 +1192,37 @@ loop_get_status64(struct loop_device *lo, struct loop_info64 __user *arg) {
        return err;
 }
 
+static int loop_set_capacity(struct loop_device *lo, struct block_device *bdev)
+{
+       int err;
+       sector_t sec;
+       loff_t sz;
+
+       err = -ENXIO;
+       if (unlikely(lo->lo_state != Lo_bound))
+               goto out;
+       err = figure_loop_size(lo);
+       if (unlikely(err))
+               goto out;
+       sec = get_capacity(lo->lo_disk);
+       /* the width of sector_t may be narrow for bit-shift */
+       sz = sec;
+       sz <<= 9;
+       mutex_lock(&bdev->bd_mutex);
+       bd_set_size(bdev, sz);
+       mutex_unlock(&bdev->bd_mutex);
+
+ out:
+       return err;
+}
+
 static int lo_ioctl(struct block_device *bdev, fmode_t mode,
        unsigned int cmd, unsigned long arg)
 {
        struct loop_device *lo = bdev->bd_disk->private_data;
        int err;
 
-       mutex_lock(&lo->lo_ctl_mutex);
+       mutex_lock_nested(&lo->lo_ctl_mutex, 1);
        switch (cmd) {
        case LOOP_SET_FD:
                err = loop_set_fd(lo, mode, bdev, arg);
@@ -1172,7 +1231,10 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
                err = loop_change_fd(lo, bdev, arg);
                break;
        case LOOP_CLR_FD:
+               /* loop_clr_fd would have unlocked lo_ctl_mutex on success */
                err = loop_clr_fd(lo, bdev);
+               if (!err)
+                       goto out_unlocked;
                break;
        case LOOP_SET_STATUS:
                err = loop_set_status_old(lo, (struct loop_info __user *) arg);
@@ -1186,10 +1248,17 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
        case LOOP_GET_STATUS64:
                err = loop_get_status64(lo, (struct loop_info64 __user *) arg);
                break;
+       case LOOP_SET_CAPACITY:
+               err = -EPERM;
+               if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
+                       err = loop_set_capacity(lo, bdev);
+               break;
        default:
                err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;
        }
        mutex_unlock(&lo->lo_ctl_mutex);
+
+out_unlocked:
        return err;
 }
 
@@ -1331,6 +1400,7 @@ static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode,
                        lo, (struct compat_loop_info __user *) arg);
                mutex_unlock(&lo->lo_ctl_mutex);
                break;
+       case LOOP_SET_CAPACITY:
        case LOOP_CLR_FD:
        case LOOP_GET_STATUS64:
        case LOOP_SET_STATUS64:
@@ -1361,6 +1431,7 @@ static int lo_open(struct block_device *bdev, fmode_t mode)
 static int lo_release(struct gendisk *disk, fmode_t mode)
 {
        struct loop_device *lo = disk->private_data;
+       int err;
 
        mutex_lock(&lo->lo_ctl_mutex);
 
@@ -1372,7 +1443,9 @@ static int lo_release(struct gendisk *disk, fmode_t mode)
                 * In autoclear mode, stop the loop thread
                 * and remove configuration after last close.
                 */
-               loop_clr_fd(lo, NULL);
+               err = loop_clr_fd(lo, NULL);
+               if (!err)
+                       goto out_unlocked;
        } else {
                /*
                 * Otherwise keep thread (if running) and config,
@@ -1383,7 +1456,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode)
 
 out:
        mutex_unlock(&lo->lo_ctl_mutex);
-
+out_unlocked:
        return 0;
 }