Merge tag 'sched-urgent-2020-12-27' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / fs / btrfs / ioctl.c
index 69a3841..703212f 100644 (file)
@@ -34,7 +34,6 @@
 #include "print-tree.h"
 #include "volumes.h"
 #include "locking.h"
-#include "inode-map.h"
 #include "backref.h"
 #include "rcu-string.h"
 #include "send.h"
@@ -193,6 +192,15 @@ static int check_fsflags(unsigned int old_flags, unsigned int flags)
        return 0;
 }
 
+static int check_fsflags_compatible(struct btrfs_fs_info *fs_info,
+                                   unsigned int flags)
+{
+       if (btrfs_is_zoned(fs_info) && (flags & FS_NOCOW_FL))
+               return -EPERM;
+
+       return 0;
+}
+
 static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
 {
        struct inode *inode = file_inode(file);
@@ -230,6 +238,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        if (ret)
                goto out_unlock;
 
+       ret = check_fsflags_compatible(fs_info, fsflags);
+       if (ret)
+               goto out_unlock;
+
        binode_flags = binode->flags;
        if (fsflags & FS_SYNC_FL)
                binode_flags |= BTRFS_INODE_SYNC;
@@ -336,7 +348,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        btrfs_sync_inode_flags_to_i_flags(inode);
        inode_inc_iversion(inode);
        inode->i_ctime = current_time(inode);
-       ret = btrfs_update_inode(trans, root, inode);
+       ret = btrfs_update_inode(trans, root, BTRFS_I(inode));
 
  out_end_trans:
        btrfs_end_transaction(trans);
@@ -479,7 +491,7 @@ static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg)
        btrfs_sync_inode_flags_to_i_flags(inode);
        inode_inc_iversion(inode);
        inode->i_ctime = current_time(inode);
-       ret = btrfs_update_inode(trans, root, inode);
+       ret = btrfs_update_inode(trans, root, BTRFS_I(inode));
 
        btrfs_end_transaction(trans);
 
@@ -733,7 +745,7 @@ static noinline int create_subvol(struct inode *dir,
        }
 
        btrfs_i_size_write(BTRFS_I(dir), dir->i_size + namelen * 2);
-       ret = btrfs_update_inode(trans, root, dir);
+       ret = btrfs_update_inode(trans, root, BTRFS_I(dir));
        if (ret) {
                btrfs_abort_transaction(trans, ret);
                goto fail;
@@ -1275,6 +1287,7 @@ static int cluster_pages_for_defrag(struct inode *inode,
        u64 page_end;
        u64 page_cnt;
        u64 start = (u64)start_index << PAGE_SHIFT;
+       u64 search_start;
        int ret;
        int i;
        int i_done;
@@ -1371,6 +1384,40 @@ again:
 
        lock_extent_bits(&BTRFS_I(inode)->io_tree,
                         page_start, page_end - 1, &cached_state);
+
+       /*
+        * When defragmenting we skip ranges that have holes or inline extents,
+        * (check should_defrag_range()), to avoid unnecessary IO and wasting
+        * space. At btrfs_defrag_file(), we check if a range should be defragged
+        * before locking the inode and then, if it should, we trigger a sync
+        * page cache readahead - we lock the inode only after that to avoid
+        * blocking for too long other tasks that possibly want to operate on
+        * other file ranges. But before we were able to get the inode lock,
+        * some other task may have punched a hole in the range, or we may have
+        * now an inline extent, in which case we should not defrag. So check
+        * for that here, where we have the inode and the range locked, and bail
+        * out if that happened.
+        */
+       search_start = page_start;
+       while (search_start < page_end) {
+               struct extent_map *em;
+
+               em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, search_start,
+                                     page_end - search_start);
+               if (IS_ERR(em)) {
+                       ret = PTR_ERR(em);
+                       goto out_unlock_range;
+               }
+               if (em->block_start >= EXTENT_MAP_LAST_BYTE) {
+                       free_extent_map(em);
+                       /* Ok, 0 means we did not defrag anything */
+                       ret = 0;
+                       goto out_unlock_range;
+               }
+               search_start = extent_map_end(em);
+               free_extent_map(em);
+       }
+
        clear_extent_bit(&BTRFS_I(inode)->io_tree, page_start,
                          page_end - 1, EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING |
                          EXTENT_DEFRAG, 0, 0, &cached_state);
@@ -1401,6 +1448,10 @@ again:
        btrfs_delalloc_release_extents(BTRFS_I(inode), page_cnt << PAGE_SHIFT);
        extent_changeset_free(data_reserved);
        return i_done;
+
+out_unlock_range:
+       unlock_extent_cached(&BTRFS_I(inode)->io_tree,
+                            page_start, page_end - 1, &cached_state);
 out:
        for (i = 0; i < i_done; i++) {
                unlock_page(pages[i]);
@@ -1678,7 +1729,7 @@ static noinline int btrfs_ioctl_resize(struct file *file,
                btrfs_info(fs_info, "resizing devid %llu", devid);
        }
 
-       device = btrfs_find_device(fs_info->fs_devices, devid, NULL, NULL, true);
+       device = btrfs_find_device(fs_info->fs_devices, devid, NULL, NULL);
        if (!device) {
                btrfs_info(fs_info, "resizer unable to find device %llu",
                           devid);
@@ -3321,7 +3372,7 @@ static long btrfs_ioctl_dev_info(struct btrfs_fs_info *fs_info,
 
        rcu_read_lock();
        dev = btrfs_find_device(fs_info->fs_devices, di_args->devid, s_uuid,
-                               NULL, true);
+                               NULL);
 
        if (!dev) {
                ret = -ENODEV;
@@ -3393,7 +3444,6 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
                ret = -ENOMEM;
                goto out_free;
        }
-       path->leave_spinning = 1;
 
        trans = btrfs_start_transaction(root, 1);
        if (IS_ERR(trans)) {