Btrfs: make show_options result match actual option names
[safe/jmp/linux-2.6] / fs / block_dev.c
index 05865b9..f45dbc1 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <linux/blkpg.h>
 #include <linux/buffer_head.h>
+#include <linux/pagevec.h>
 #include <linux/writeback.h>
 #include <linux/mpage.h>
 #include <linux/mount.h>
@@ -174,6 +175,152 @@ blkdev_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
                                iov, offset, nr_segs, blkdev_get_blocks, NULL);
 }
 
+/*
+ * Write out and wait upon all the dirty data associated with a block
+ * device via its mapping.  Does not take the superblock lock.
+ */
+int sync_blockdev(struct block_device *bdev)
+{
+       int ret = 0;
+
+       if (bdev)
+               ret = filemap_write_and_wait(bdev->bd_inode->i_mapping);
+       return ret;
+}
+EXPORT_SYMBOL(sync_blockdev);
+
+/*
+ * Write out and wait upon all dirty data associated with this
+ * device.   Filesystem data as well as the underlying block
+ * device.  Takes the superblock lock.
+ */
+int fsync_bdev(struct block_device *bdev)
+{
+       struct super_block *sb = get_super(bdev);
+       if (sb) {
+               int res = fsync_super(sb);
+               drop_super(sb);
+               return res;
+       }
+       return sync_blockdev(bdev);
+}
+EXPORT_SYMBOL(fsync_bdev);
+
+/**
+ * freeze_bdev  --  lock a filesystem and force it into a consistent state
+ * @bdev:      blockdevice to lock
+ *
+ * This takes the block device bd_mount_sem to make sure no new mounts
+ * happen on bdev until thaw_bdev() is called.
+ * If a superblock is found on this device, we take the s_umount semaphore
+ * on it to make sure nobody unmounts until the snapshot creation is done.
+ * The reference counter (bd_fsfreeze_count) guarantees that only the last
+ * unfreeze process can unfreeze the frozen filesystem actually when multiple
+ * freeze requests arrive simultaneously. It counts up in freeze_bdev() and
+ * count down in thaw_bdev(). When it becomes 0, thaw_bdev() will unfreeze
+ * actually.
+ */
+struct super_block *freeze_bdev(struct block_device *bdev)
+{
+       struct super_block *sb;
+       int error = 0;
+
+       mutex_lock(&bdev->bd_fsfreeze_mutex);
+       if (bdev->bd_fsfreeze_count > 0) {
+               bdev->bd_fsfreeze_count++;
+               sb = get_super(bdev);
+               mutex_unlock(&bdev->bd_fsfreeze_mutex);
+               return sb;
+       }
+       bdev->bd_fsfreeze_count++;
+
+       down(&bdev->bd_mount_sem);
+       sb = get_super(bdev);
+       if (sb && !(sb->s_flags & MS_RDONLY)) {
+               sb->s_frozen = SB_FREEZE_WRITE;
+               smp_wmb();
+
+               __fsync_super(sb);
+
+               sb->s_frozen = SB_FREEZE_TRANS;
+               smp_wmb();
+
+               sync_blockdev(sb->s_bdev);
+
+               if (sb->s_op->freeze_fs) {
+                       error = sb->s_op->freeze_fs(sb);
+                       if (error) {
+                               printk(KERN_ERR
+                                       "VFS:Filesystem freeze failed\n");
+                               sb->s_frozen = SB_UNFROZEN;
+                               drop_super(sb);
+                               up(&bdev->bd_mount_sem);
+                               bdev->bd_fsfreeze_count--;
+                               mutex_unlock(&bdev->bd_fsfreeze_mutex);
+                               return ERR_PTR(error);
+                       }
+               }
+       }
+
+       sync_blockdev(bdev);
+       mutex_unlock(&bdev->bd_fsfreeze_mutex);
+
+       return sb;      /* thaw_bdev releases s->s_umount and bd_mount_sem */
+}
+EXPORT_SYMBOL(freeze_bdev);
+
+/**
+ * thaw_bdev  -- unlock filesystem
+ * @bdev:      blockdevice to unlock
+ * @sb:                associated superblock
+ *
+ * Unlocks the filesystem and marks it writeable again after freeze_bdev().
+ */
+int thaw_bdev(struct block_device *bdev, struct super_block *sb)
+{
+       int error = 0;
+
+       mutex_lock(&bdev->bd_fsfreeze_mutex);
+       if (!bdev->bd_fsfreeze_count) {
+               mutex_unlock(&bdev->bd_fsfreeze_mutex);
+               return -EINVAL;
+       }
+
+       bdev->bd_fsfreeze_count--;
+       if (bdev->bd_fsfreeze_count > 0) {
+               if (sb)
+                       drop_super(sb);
+               mutex_unlock(&bdev->bd_fsfreeze_mutex);
+               return 0;
+       }
+
+       if (sb) {
+               BUG_ON(sb->s_bdev != bdev);
+               if (!(sb->s_flags & MS_RDONLY)) {
+                       if (sb->s_op->unfreeze_fs) {
+                               error = sb->s_op->unfreeze_fs(sb);
+                               if (error) {
+                                       printk(KERN_ERR
+                                               "VFS:Filesystem thaw failed\n");
+                                       sb->s_frozen = SB_FREEZE_TRANS;
+                                       bdev->bd_fsfreeze_count++;
+                                       mutex_unlock(&bdev->bd_fsfreeze_mutex);
+                                       return error;
+                               }
+                       }
+                       sb->s_frozen = SB_UNFROZEN;
+                       smp_wmb();
+                       wake_up(&sb->s_wait_unfrozen);
+               }
+               drop_super(sb);
+       }
+
+       up(&bdev->bd_mount_sem);
+       mutex_unlock(&bdev->bd_fsfreeze_mutex);
+       return 0;
+}
+EXPORT_SYMBOL(thaw_bdev);
+
 static int blkdev_writepage(struct page *page, struct writeback_control *wbc)
 {
        return block_write_full_page(page, blkdev_get_block, wbc);
@@ -285,6 +432,8 @@ static void init_once(void *foo)
        INIT_LIST_HEAD(&bdev->bd_holder_list);
 #endif
        inode_init_once(&ei->vfs_inode);
+       /* Initialize mutex for freeze. */
+       mutex_init(&bdev->bd_fsfreeze_mutex);
 }
 
 static inline void __bd_forget(struct inode *inode)
@@ -326,12 +475,13 @@ static struct file_system_type bd_type = {
        .kill_sb        = kill_anon_super,
 };
 
-static struct vfsmount *bd_mnt __read_mostly;
-struct super_block *blockdev_superblock;
+struct super_block *blockdev_superblock __read_mostly;
 
 void __init bdev_cache_init(void)
 {
        int err;
+       struct vfsmount *bd_mnt;
+
        bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode),
                        0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
                                SLAB_MEM_SPREAD|SLAB_PANIC),
@@ -373,7 +523,7 @@ struct block_device *bdget(dev_t dev)
        struct block_device *bdev;
        struct inode *inode;
 
-       inode = iget5_locked(bd_mnt->mnt_sb, hash(dev),
+       inode = iget5_locked(blockdev_superblock, hash(dev),
                        bdev_test, bdev_set, &dev);
 
        if (!inode)
@@ -463,7 +613,7 @@ void bd_forget(struct inode *inode)
 
        spin_lock(&bdev_lock);
        if (inode->i_bdev) {
-               if (inode->i_sb != blockdev_superblock)
+               if (!sb_is_blkdev_sb(inode->i_sb))
                        bdev = inode->i_bdev;
                __bd_forget(inode);
        }
@@ -986,7 +1136,6 @@ static int __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part);
 static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
 {
        struct gendisk *disk;
-       struct hd_struct *part = NULL;
        int ret;
        int partno;
        int perm = 0;
@@ -1004,26 +1153,41 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
                return ret;
        }
 
-       ret = -ENXIO;
-
        lock_kernel();
+ restart:
 
+       ret = -ENXIO;
        disk = get_gendisk(bdev->bd_dev, &partno);
        if (!disk)
                goto out_unlock_kernel;
-       part = disk_get_part(disk, partno);
-       if (!part)
-               goto out_unlock_kernel;
 
        mutex_lock_nested(&bdev->bd_mutex, for_part);
        if (!bdev->bd_openers) {
                bdev->bd_disk = disk;
-               bdev->bd_part = part;
                bdev->bd_contains = bdev;
                if (!partno) {
                        struct backing_dev_info *bdi;
+
+                       ret = -ENXIO;
+                       bdev->bd_part = disk_get_part(disk, partno);
+                       if (!bdev->bd_part)
+                               goto out_clear;
+
                        if (disk->fops->open) {
                                ret = disk->fops->open(bdev, mode);
+                               if (ret == -ERESTARTSYS) {
+                                       /* Lost a race with 'disk' being
+                                        * deleted, try again.
+                                        * See md.c
+                                        */
+                                       disk_put_part(bdev->bd_part);
+                                       bdev->bd_part = NULL;
+                                       module_put(disk->fops->owner);
+                                       put_disk(disk);
+                                       bdev->bd_disk = NULL;
+                                       mutex_unlock(&bdev->bd_mutex);
+                                       goto restart;
+                               }
                                if (ret)
                                        goto out_clear;
                        }
@@ -1049,18 +1213,17 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
                        bdev->bd_contains = whole;
                        bdev->bd_inode->i_data.backing_dev_info =
                           whole->bd_inode->i_data.backing_dev_info;
+                       bdev->bd_part = disk_get_part(disk, partno);
                        if (!(disk->flags & GENHD_FL_UP) ||
-                           !part || !part->nr_sects) {
+                           !bdev->bd_part || !bdev->bd_part->nr_sects) {
                                ret = -ENXIO;
                                goto out_clear;
                        }
-                       bd_set_size(bdev, (loff_t)part->nr_sects << 9);
+                       bd_set_size(bdev, (loff_t)bdev->bd_part->nr_sects << 9);
                }
        } else {
-               disk_put_part(part);
                put_disk(disk);
                module_put(disk->fops->owner);
-               part = NULL;
                disk = NULL;
                if (bdev->bd_contains == bdev) {
                        if (bdev->bd_disk->fops->open) {
@@ -1080,6 +1243,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
        return 0;
 
  out_clear:
+       disk_put_part(bdev->bd_part);
        bdev->bd_disk = NULL;
        bdev->bd_part = NULL;
        bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info;
@@ -1091,7 +1255,6 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
  out_unlock_kernel:
        unlock_kernel();
 
-       disk_put_part(part);
        if (disk)
                module_put(disk->fops->owner);
        put_disk(disk);
@@ -1136,12 +1299,15 @@ static int blkdev_open(struct inode * inode, struct file * filp)
        if (res)
                return res;
 
-       if (!(filp->f_mode & FMODE_EXCL))
-               return 0;
+       if (filp->f_mode & FMODE_EXCL) {
+               res = bd_claim(bdev, filp);
+               if (res)
+                       goto out_blkdev_put;
+       }
 
-       if (!(res = bd_claim(bdev, filp)))
-               return 0;
+       return 0;
 
+ out_blkdev_put:
        blkdev_put(bdev, filp->f_mode);
        return res;
 }
@@ -1204,11 +1370,33 @@ static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 {
        struct block_device *bdev = I_BDEV(file->f_mapping->host);
        fmode_t mode = file->f_mode;
+
+       /*
+        * O_NDELAY can be altered using fcntl(.., F_SETFL, ..), so we have
+        * to updated it before every ioctl.
+        */
        if (file->f_flags & O_NDELAY)
-               mode |= FMODE_NDELAY_NOW;
+               mode |= FMODE_NDELAY;
+       else
+               mode &= ~FMODE_NDELAY;
+
        return blkdev_ioctl(bdev, mode, cmd, arg);
 }
 
+/*
+ * Try to release a page associated with block device when the system
+ * is under memory pressure.
+ */
+static int blkdev_releasepage(struct page *page, gfp_t wait)
+{
+       struct super_block *super = BDEV_I(page->mapping->host)->bdev.bd_super;
+
+       if (super && super->s_op->bdev_try_to_free_page)
+               return super->s_op->bdev_try_to_free_page(super, page, wait);
+
+       return try_to_free_buffers(page);
+}
+
 static const struct address_space_operations def_blk_aops = {
        .readpage       = blkdev_readpage,
        .writepage      = blkdev_writepage,
@@ -1216,6 +1404,7 @@ static const struct address_space_operations def_blk_aops = {
        .write_begin    = blkdev_write_begin,
        .write_end      = blkdev_write_end,
        .writepages     = generic_writepages,
+       .releasepage    = blkdev_releasepage,
        .direct_IO      = blkdev_direct_IO,
 };
 
@@ -1251,39 +1440,39 @@ EXPORT_SYMBOL(ioctl_by_bdev);
 
 /**
  * lookup_bdev  - lookup a struct block_device by name
- * @path:      special file representing the block device
+ * @pathname:  special file representing the block device
  *
  * Get a reference to the blockdevice at @pathname in the current
  * namespace if possible and return it.  Return ERR_PTR(error)
  * otherwise.
  */
-struct block_device *lookup_bdev(const char *path)
+struct block_device *lookup_bdev(const char *pathname)
 {
        struct block_device *bdev;
        struct inode *inode;
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       if (!path || !*path)
+       if (!pathname || !*pathname)
                return ERR_PTR(-EINVAL);
 
-       error = path_lookup(path, LOOKUP_FOLLOW, &nd);
+       error = kern_path(pathname, LOOKUP_FOLLOW, &path);
        if (error)
                return ERR_PTR(error);
 
-       inode = nd.path.dentry->d_inode;
+       inode = path.dentry->d_inode;
        error = -ENOTBLK;
        if (!S_ISBLK(inode->i_mode))
                goto fail;
        error = -EACCES;
-       if (nd.path.mnt->mnt_flags & MNT_NODEV)
+       if (path.mnt->mnt_flags & MNT_NODEV)
                goto fail;
        error = -ENOMEM;
        bdev = bd_acquire(inode);
        if (!bdev)
                goto fail;
 out:
-       path_put(&nd.path);
+       path_put(&path);
        return bdev;
 fail:
        bdev = ERR_PTR(error);