Merge tag 'write-page-prefaulting' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / fs / btrfs / ioctl.c
index 927771d..238cee5 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/iversion.h>
 #include <linux/fileattr.h>
 #include <linux/fsverity.h>
+#include <linux/sched/xacct.h>
 #include "ctree.h"
 #include "disk-io.h"
 #include "export.h"
@@ -88,6 +89,24 @@ struct btrfs_ioctl_send_args_32 {
 
 #define BTRFS_IOC_SEND_32 _IOW(BTRFS_IOCTL_MAGIC, 38, \
                               struct btrfs_ioctl_send_args_32)
+
+struct btrfs_ioctl_encoded_io_args_32 {
+       compat_uptr_t iov;
+       compat_ulong_t iovcnt;
+       __s64 offset;
+       __u64 flags;
+       __u64 len;
+       __u64 unencoded_len;
+       __u64 unencoded_offset;
+       __u32 compression;
+       __u32 encryption;
+       __u8 reserved[64];
+};
+
+#define BTRFS_IOC_ENCODED_READ_32 _IOR(BTRFS_IOCTL_MAGIC, 64, \
+                                      struct btrfs_ioctl_encoded_io_args_32)
+#define BTRFS_IOC_ENCODED_WRITE_32 _IOW(BTRFS_IOCTL_MAGIC, 64, \
+                                       struct btrfs_ioctl_encoded_io_args_32)
 #endif
 
 /* Mask out flags that are inappropriate for the given type of inode. */
@@ -440,10 +459,8 @@ void btrfs_exclop_balance(struct btrfs_fs_info *fs_info,
        }
 }
 
-static int btrfs_ioctl_getversion(struct file *file, int __user *arg)
+static int btrfs_ioctl_getversion(struct inode *inode, int __user *arg)
 {
-       struct inode *inode = file_inode(file);
-
        return put_user(inode->i_generation, arg);
 }
 
@@ -753,6 +770,13 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
        struct btrfs_trans_handle *trans;
        int ret;
 
+       /* We do not support snapshotting right now. */
+       if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) {
+               btrfs_warn(fs_info,
+                          "extent tree v2 doesn't support snapshotting yet");
+               return -EOPNOTSUPP;
+       }
+
        if (!test_bit(BTRFS_ROOT_SHAREABLE, &root->state))
                return -EINVAL;
 
@@ -1012,8 +1036,155 @@ out:
        return ret;
 }
 
+/*
+ * Defrag specific helper to get an extent map.
+ *
+ * Differences between this and btrfs_get_extent() are:
+ *
+ * - No extent_map will be added to inode->extent_tree
+ *   To reduce memory usage in the long run.
+ *
+ * - Extra optimization to skip file extents older than @newer_than
+ *   By using btrfs_search_forward() we can skip entire file ranges that
+ *   have extents created in past transactions, because btrfs_search_forward()
+ *   will not visit leaves and nodes with a generation smaller than given
+ *   minimal generation threshold (@newer_than).
+ *
+ * Return valid em if we find a file extent matching the requirement.
+ * Return NULL if we can not find a file extent matching the requirement.
+ *
+ * Return ERR_PTR() for error.
+ */
+static struct extent_map *defrag_get_extent(struct btrfs_inode *inode,
+                                           u64 start, u64 newer_than)
+{
+       struct btrfs_root *root = inode->root;
+       struct btrfs_file_extent_item *fi;
+       struct btrfs_path path = { 0 };
+       struct extent_map *em;
+       struct btrfs_key key;
+       u64 ino = btrfs_ino(inode);
+       int ret;
+
+       em = alloc_extent_map();
+       if (!em) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       key.objectid = ino;
+       key.type = BTRFS_EXTENT_DATA_KEY;
+       key.offset = start;
+
+       if (newer_than) {
+               ret = btrfs_search_forward(root, &key, &path, newer_than);
+               if (ret < 0)
+                       goto err;
+               /* Can't find anything newer */
+               if (ret > 0)
+                       goto not_found;
+       } else {
+               ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+               if (ret < 0)
+                       goto err;
+       }
+       if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
+               /*
+                * If btrfs_search_slot() makes path to point beyond nritems,
+                * we should not have an empty leaf, as this inode must at
+                * least have its INODE_ITEM.
+                */
+               ASSERT(btrfs_header_nritems(path.nodes[0]));
+               path.slots[0] = btrfs_header_nritems(path.nodes[0]) - 1;
+       }
+       btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+       /* Perfect match, no need to go one slot back */
+       if (key.objectid == ino && key.type == BTRFS_EXTENT_DATA_KEY &&
+           key.offset == start)
+               goto iterate;
+
+       /* We didn't find a perfect match, needs to go one slot back */
+       if (path.slots[0] > 0) {
+               btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+               if (key.objectid == ino && key.type == BTRFS_EXTENT_DATA_KEY)
+                       path.slots[0]--;
+       }
+
+iterate:
+       /* Iterate through the path to find a file extent covering @start */
+       while (true) {
+               u64 extent_end;
+
+               if (path.slots[0] >= btrfs_header_nritems(path.nodes[0]))
+                       goto next;
+
+               btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+
+               /*
+                * We may go one slot back to INODE_REF/XATTR item, then
+                * need to go forward until we reach an EXTENT_DATA.
+                * But we should still has the correct ino as key.objectid.
+                */
+               if (WARN_ON(key.objectid < ino) || key.type < BTRFS_EXTENT_DATA_KEY)
+                       goto next;
+
+               /* It's beyond our target range, definitely not extent found */
+               if (key.objectid > ino || key.type > BTRFS_EXTENT_DATA_KEY)
+                       goto not_found;
+
+               /*
+                *      |       |<- File extent ->|
+                *      \- start
+                *
+                * This means there is a hole between start and key.offset.
+                */
+               if (key.offset > start) {
+                       em->start = start;
+                       em->orig_start = start;
+                       em->block_start = EXTENT_MAP_HOLE;
+                       em->len = key.offset - start;
+                       break;
+               }
+
+               fi = btrfs_item_ptr(path.nodes[0], path.slots[0],
+                                   struct btrfs_file_extent_item);
+               extent_end = btrfs_file_extent_end(&path);
+
+               /*
+                *      |<- file extent ->|     |
+                *                              \- start
+                *
+                * We haven't reached start, search next slot.
+                */
+               if (extent_end <= start)
+                       goto next;
+
+               /* Now this extent covers @start, convert it to em */
+               btrfs_extent_item_to_extent_map(inode, &path, fi, false, em);
+               break;
+next:
+               ret = btrfs_next_item(root, &path);
+               if (ret < 0)
+                       goto err;
+               if (ret > 0)
+                       goto not_found;
+       }
+       btrfs_release_path(&path);
+       return em;
+
+not_found:
+       btrfs_release_path(&path);
+       free_extent_map(em);
+       return NULL;
+
+err:
+       btrfs_release_path(&path);
+       free_extent_map(em);
+       return ERR_PTR(ret);
+}
+
 static struct extent_map *defrag_lookup_extent(struct inode *inode, u64 start,
-                                              bool locked)
+                                              u64 newer_than, bool locked)
 {
        struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
        struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
@@ -1028,6 +1199,20 @@ static struct extent_map *defrag_lookup_extent(struct inode *inode, u64 start,
        em = lookup_extent_mapping(em_tree, start, sectorsize);
        read_unlock(&em_tree->lock);
 
+       /*
+        * We can get a merged extent, in that case, we need to re-search
+        * tree to get the original em for defrag.
+        *
+        * If @newer_than is 0 or em::generation < newer_than, we can trust
+        * this em, as either we don't care about the generation, or the
+        * merged extent map will be rejected anyway.
+        */
+       if (em && test_bit(EXTENT_FLAG_MERGED, &em->flags) &&
+           newer_than && em->generation >= newer_than) {
+               free_extent_map(em);
+               em = NULL;
+       }
+
        if (!em) {
                struct extent_state *cached = NULL;
                u64 end = start + sectorsize - 1;
@@ -1035,7 +1220,7 @@ static struct extent_map *defrag_lookup_extent(struct inode *inode, u64 start,
                /* get the big lock and read metadata off disk */
                if (!locked)
                        lock_extent_bits(io_tree, start, end, &cached);
-               em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, start, sectorsize);
+               em = defrag_get_extent(BTRFS_I(inode), start, newer_than);
                if (!locked)
                        unlock_extent_cached(io_tree, start, end, &cached);
 
@@ -1046,23 +1231,42 @@ static struct extent_map *defrag_lookup_extent(struct inode *inode, u64 start,
        return em;
 }
 
+static u32 get_extent_max_capacity(const struct extent_map *em)
+{
+       if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags))
+               return BTRFS_MAX_COMPRESSED;
+       return BTRFS_MAX_EXTENT_SIZE;
+}
+
 static bool defrag_check_next_extent(struct inode *inode, struct extent_map *em,
                                     bool locked)
 {
        struct extent_map *next;
-       bool ret = true;
+       bool ret = false;
 
        /* this is the last extent */
        if (em->start + em->len >= i_size_read(inode))
                return false;
 
-       next = defrag_lookup_extent(inode, em->start + em->len, locked);
+       /*
+        * We want to check if the next extent can be merged with the current
+        * one, which can be an extent created in a past generation, so we pass
+        * a minimum generation of 0 to defrag_lookup_extent().
+        */
+       next = defrag_lookup_extent(inode, em->start + em->len, 0, locked);
+       /* No more em or hole */
        if (!next || next->block_start >= EXTENT_MAP_LAST_BYTE)
-               ret = false;
-       else if ((em->block_start + em->block_len == next->block_start) &&
-                (em->block_len > SZ_128K && next->block_len > SZ_128K))
-               ret = false;
-
+               goto out;
+       if (test_bit(EXTENT_FLAG_PREALLOC, &next->flags))
+               goto out;
+       /*
+        * If the next extent is at its max capacity, defragging current extent
+        * makes no sense, as the total number of extents won't change.
+        */
+       if (next->len >= get_extent_max_capacity(em))
+               goto out;
+       ret = true;
+out:
        free_extent_map(next);
        return ret;
 }
@@ -1186,8 +1390,10 @@ struct defrag_target_range {
 static int defrag_collect_targets(struct btrfs_inode *inode,
                                  u64 start, u64 len, u32 extent_thresh,
                                  u64 newer_than, bool do_compress,
-                                 bool locked, struct list_head *target_list)
+                                 bool locked, struct list_head *target_list,
+                                 u64 *last_scanned_ret)
 {
+       bool last_is_target = false;
        u64 cur = start;
        int ret = 0;
 
@@ -1197,7 +1403,9 @@ static int defrag_collect_targets(struct btrfs_inode *inode,
                bool next_mergeable = true;
                u64 range_len;
 
-               em = defrag_lookup_extent(&inode->vfs_inode, cur, locked);
+               last_is_target = false;
+               em = defrag_lookup_extent(&inode->vfs_inode, cur,
+                                         newer_than, locked);
                if (!em)
                        break;
 
@@ -1254,6 +1462,13 @@ static int defrag_collect_targets(struct btrfs_inode *inode,
                if (range_len >= extent_thresh)
                        goto next;
 
+               /*
+                * Skip extents already at its max capacity, this is mostly for
+                * compressed extents, which max cap is only 128K.
+                */
+               if (em->len >= get_extent_max_capacity(em))
+                       goto next;
+
                next_mergeable = defrag_check_next_extent(&inode->vfs_inode, em,
                                                          locked);
                if (!next_mergeable) {
@@ -1272,6 +1487,7 @@ static int defrag_collect_targets(struct btrfs_inode *inode,
                }
 
 add:
+               last_is_target = true;
                range_len = min(extent_map_end(em), start + len) - cur;
                /*
                 * This one is a good target, check if it can be merged into
@@ -1315,10 +1531,22 @@ next:
                        kfree(entry);
                }
        }
+       if (!ret && last_scanned_ret) {
+               /*
+                * If the last extent is not a target, the caller can skip to
+                * the end of that extent.
+                * Otherwise, we can only go the end of the specified range.
+                */
+               if (!last_is_target)
+                       *last_scanned_ret = max(cur, *last_scanned_ret);
+               else
+                       *last_scanned_ret = max(start + len, *last_scanned_ret);
+       }
        return ret;
 }
 
 #define CLUSTER_SIZE   (SZ_256K)
+static_assert(IS_ALIGNED(CLUSTER_SIZE, PAGE_SIZE));
 
 /*
  * Defrag one contiguous target range.
@@ -1373,7 +1601,8 @@ static int defrag_one_locked_target(struct btrfs_inode *inode,
 }
 
 static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,
-                           u32 extent_thresh, u64 newer_than, bool do_compress)
+                           u32 extent_thresh, u64 newer_than, bool do_compress,
+                           u64 *last_scanned_ret)
 {
        struct extent_state *cached_state = NULL;
        struct defrag_target_range *entry;
@@ -1419,7 +1648,7 @@ static int defrag_one_range(struct btrfs_inode *inode, u64 start, u32 len,
         */
        ret = defrag_collect_targets(inode, start, len, extent_thresh,
                                     newer_than, do_compress, true,
-                                    &target_list);
+                                    &target_list, last_scanned_ret);
        if (ret < 0)
                goto unlock_extent;
 
@@ -1454,7 +1683,8 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
                              u64 start, u32 len, u32 extent_thresh,
                              u64 newer_than, bool do_compress,
                              unsigned long *sectors_defragged,
-                             unsigned long max_sectors)
+                             unsigned long max_sectors,
+                             u64 *last_scanned_ret)
 {
        const u32 sectorsize = inode->root->fs_info->sectorsize;
        struct defrag_target_range *entry;
@@ -1462,10 +1692,9 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
        LIST_HEAD(target_list);
        int ret;
 
-       BUILD_BUG_ON(!IS_ALIGNED(CLUSTER_SIZE, PAGE_SIZE));
        ret = defrag_collect_targets(inode, start, len, extent_thresh,
                                     newer_than, do_compress, false,
-                                    &target_list);
+                                    &target_list, NULL);
        if (ret < 0)
                goto out;
 
@@ -1482,6 +1711,15 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
                        range_len = min_t(u32, range_len,
                                (max_sectors - *sectors_defragged) * sectorsize);
 
+               /*
+                * If defrag_one_range() has updated last_scanned_ret,
+                * our range may already be invalid (e.g. hole punched).
+                * Skip if our range is before last_scanned_ret, as there is
+                * no need to defrag the range anymore.
+                */
+               if (entry->start + range_len <= *last_scanned_ret)
+                       continue;
+
                if (ra)
                        page_cache_sync_readahead(inode->vfs_inode.i_mapping,
                                ra, NULL, entry->start >> PAGE_SHIFT,
@@ -1494,7 +1732,8 @@ static int defrag_one_cluster(struct btrfs_inode *inode,
                 * accounting.
                 */
                ret = defrag_one_range(inode, entry->start, range_len,
-                                      extent_thresh, newer_than, do_compress);
+                                      extent_thresh, newer_than, do_compress,
+                                      last_scanned_ret);
                if (ret < 0)
                        break;
                *sectors_defragged += range_len >>
@@ -1505,6 +1744,8 @@ out:
                list_del_init(&entry->list);
                kfree(entry);
        }
+       if (ret >= 0)
+               *last_scanned_ret = max(*last_scanned_ret, start + len);
        return ret;
 }
 
@@ -1590,11 +1831,9 @@ int btrfs_defrag_file(struct inode *inode, struct file_ra_state *ra,
 
        while (cur < last_byte) {
                const unsigned long prev_sectors_defragged = sectors_defragged;
+               u64 last_scanned = cur;
                u64 cluster_end;
 
-               /* The cluster size 256K should always be page aligned */
-               BUILD_BUG_ON(!IS_ALIGNED(CLUSTER_SIZE, PAGE_SIZE));
-
                if (btrfs_defrag_cancelled(fs_info)) {
                        ret = -EAGAIN;
                        break;
@@ -1619,8 +1858,8 @@ int btrfs_defrag_file(struct inode *inode, struct file_ra_state *ra,
                        BTRFS_I(inode)->defrag_compress = compress_type;
                ret = defrag_one_cluster(BTRFS_I(inode), ra, cur,
                                cluster_end + 1 - cur, extent_thresh,
-                               newer_than, do_compress,
-                               &sectors_defragged, max_to_defrag);
+                               newer_than, do_compress, &sectors_defragged,
+                               max_to_defrag, &last_scanned);
 
                if (sectors_defragged > prev_sectors_defragged)
                        balance_dirty_pages_ratelimited(inode->i_mapping);
@@ -1628,7 +1867,7 @@ int btrfs_defrag_file(struct inode *inode, struct file_ra_state *ra,
                btrfs_inode_unlock(inode, 0);
                if (ret < 0)
                        break;
-               cur = cluster_end + 1;
+               cur = max(cluster_end + 1, last_scanned);
                if (ret > 0) {
                        ret = 0;
                        break;
@@ -2011,10 +2250,9 @@ free_args:
        return ret;
 }
 
-static noinline int btrfs_ioctl_subvol_getflags(struct file *file,
+static noinline int btrfs_ioctl_subvol_getflags(struct inode *inode,
                                                void __user *arg)
 {
-       struct inode *inode = file_inode(file);
        struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
        struct btrfs_root *root = BTRFS_I(inode)->root;
        int ret = 0;
@@ -2344,12 +2582,11 @@ err:
        return ret;
 }
 
-static noinline int btrfs_ioctl_tree_search(struct file *file,
-                                          void __user *argp)
+static noinline int btrfs_ioctl_tree_search(struct inode *inode,
+                                           void __user *argp)
 {
        struct btrfs_ioctl_search_args __user *uargs;
        struct btrfs_ioctl_search_key sk;
-       struct inode *inode;
        int ret;
        size_t buf_size;
 
@@ -2363,7 +2600,6 @@ static noinline int btrfs_ioctl_tree_search(struct file *file,
 
        buf_size = sizeof(uargs->buf);
 
-       inode = file_inode(file);
        ret = search_ioctl(inode, &sk, &buf_size, uargs->buf);
 
        /*
@@ -2378,12 +2614,11 @@ static noinline int btrfs_ioctl_tree_search(struct file *file,
        return ret;
 }
 
-static noinline int btrfs_ioctl_tree_search_v2(struct file *file,
+static noinline int btrfs_ioctl_tree_search_v2(struct inode *inode,
                                               void __user *argp)
 {
        struct btrfs_ioctl_search_args_v2 __user *uarg;
        struct btrfs_ioctl_search_args_v2 args;
-       struct inode *inode;
        int ret;
        size_t buf_size;
        const size_t buf_limit = SZ_16M;
@@ -2402,7 +2637,6 @@ static noinline int btrfs_ioctl_tree_search_v2(struct file *file,
        if (buf_size > buf_limit)
                buf_size = buf_limit;
 
-       inode = file_inode(file);
        ret = search_ioctl(inode, &args.key, &buf_size,
                           (char __user *)(&uarg->buf[0]));
        if (ret == 0 && copy_to_user(&uarg->key, &args.key, sizeof(args.key)))
@@ -2653,25 +2887,22 @@ out:
        return ret;
 }
 
-static noinline int btrfs_ioctl_ino_lookup(struct file *file,
+static noinline int btrfs_ioctl_ino_lookup(struct btrfs_root *root,
                                           void __user *argp)
 {
        struct btrfs_ioctl_ino_lookup_args *args;
-       struct inode *inode;
        int ret = 0;
 
        args = memdup_user(argp, sizeof(*args));
        if (IS_ERR(args))
                return PTR_ERR(args);
 
-       inode = file_inode(file);
-
        /*
         * Unprivileged query to obtain the containing subvolume root id. The
         * path is reset so it's consistent with btrfs_search_path_in_tree.
         */
        if (args->treeid == 0)
-               args->treeid = BTRFS_I(inode)->root->root_key.objectid;
+               args->treeid = root->root_key.objectid;
 
        if (args->objectid == BTRFS_FIRST_FREE_OBJECTID) {
                args->name[0] = 0;
@@ -2683,7 +2914,7 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file,
                goto out;
        }
 
-       ret = btrfs_search_path_in_tree(BTRFS_I(inode)->root->fs_info,
+       ret = btrfs_search_path_in_tree(root->fs_info,
                                        args->treeid, args->objectid,
                                        args->name);
 
@@ -2739,7 +2970,7 @@ static int btrfs_ioctl_ino_lookup_user(struct file *file, void __user *argp)
 }
 
 /* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */
-static int btrfs_ioctl_get_subvol_info(struct file *file, void __user *argp)
+static int btrfs_ioctl_get_subvol_info(struct inode *inode, void __user *argp)
 {
        struct btrfs_ioctl_get_subvol_info_args *subvol_info;
        struct btrfs_fs_info *fs_info;
@@ -2751,7 +2982,6 @@ static int btrfs_ioctl_get_subvol_info(struct file *file, void __user *argp)
        struct extent_buffer *leaf;
        unsigned long item_off;
        unsigned long item_len;
-       struct inode *inode;
        int slot;
        int ret = 0;
 
@@ -2765,7 +2995,6 @@ static int btrfs_ioctl_get_subvol_info(struct file *file, void __user *argp)
                return -ENOMEM;
        }
 
-       inode = file_inode(file);
        fs_info = BTRFS_I(inode)->root->fs_info;
 
        /* Get root_item of inode's subvolume */
@@ -2859,15 +3088,14 @@ out_free:
  * Return ROOT_REF information of the subvolume containing this inode
  * except the subvolume name.
  */
-static int btrfs_ioctl_get_subvol_rootref(struct file *file, void __user *argp)
+static int btrfs_ioctl_get_subvol_rootref(struct btrfs_root *root,
+                                         void __user *argp)
 {
        struct btrfs_ioctl_get_subvol_rootref_args *rootrefs;
        struct btrfs_root_ref *rref;
-       struct btrfs_root *root;
        struct btrfs_path *path;
        struct btrfs_key key;
        struct extent_buffer *leaf;
-       struct inode *inode;
        u64 objectid;
        int slot;
        int ret;
@@ -2883,15 +3111,13 @@ static int btrfs_ioctl_get_subvol_rootref(struct file *file, void __user *argp)
                return PTR_ERR(rootrefs);
        }
 
-       inode = file_inode(file);
-       root = BTRFS_I(inode)->root->fs_info->tree_root;
-       objectid = BTRFS_I(inode)->root->root_key.objectid;
-
+       objectid = root->root_key.objectid;
        key.objectid = objectid;
        key.type = BTRFS_ROOT_REF_KEY;
        key.offset = rootrefs->min_treeid;
        found = 0;
 
+       root = root->fs_info->tree_root;
        ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
        if (ret < 0) {
                goto out;
@@ -2971,6 +3197,13 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
        int err = 0;
        bool destroy_parent = false;
 
+       /* We don't support snapshots with extent tree v2 yet. */
+       if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) {
+               btrfs_err(fs_info,
+                         "extent tree v2 doesn't support snapshot deletion yet");
+               return -EOPNOTSUPP;
+       }
+
        if (destroy_v2) {
                vol_args2 = memdup_user(arg, sizeof(*vol_args2));
                if (IS_ERR(vol_args2))
@@ -3246,6 +3479,11 @@ static long btrfs_ioctl_add_dev(struct btrfs_fs_info *fs_info, void __user *arg)
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
+       if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) {
+               btrfs_err(fs_info, "device add not supported on extent tree v2 yet");
+               return -EINVAL;
+       }
+
        if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_DEV_ADD)) {
                if (!btrfs_exclop_start_try_lock(fs_info, BTRFS_EXCLOP_DEV_ADD))
                        return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS;
@@ -3771,6 +4009,11 @@ static long btrfs_ioctl_scrub(struct file *file, void __user *arg)
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
+       if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) {
+               btrfs_err(fs_info, "scrub is not supported on extent tree v2 yet");
+               return -EINVAL;
+       }
+
        sa = memdup_user(arg, sizeof(*sa));
        if (IS_ERR(sa))
                return PTR_ERR(sa);
@@ -3870,6 +4113,11 @@ static long btrfs_ioctl_dev_replace(struct btrfs_fs_info *fs_info,
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
+       if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) {
+               btrfs_err(fs_info, "device replace not supported on extent tree v2 yet");
+               return -EINVAL;
+       }
+
        p = memdup_user(arg, sizeof(*p));
        if (IS_ERR(p))
                return PTR_ERR(p);
@@ -4931,7 +5179,7 @@ out_drop_write:
        return ret;
 }
 
-static int _btrfs_ioctl_send(struct file *file, void __user *argp, bool compat)
+static int _btrfs_ioctl_send(struct inode *inode, void __user *argp, bool compat)
 {
        struct btrfs_ioctl_send_args *arg;
        int ret;
@@ -4961,11 +5209,194 @@ static int _btrfs_ioctl_send(struct file *file, void __user *argp, bool compat)
                if (IS_ERR(arg))
                        return PTR_ERR(arg);
        }
-       ret = btrfs_ioctl_send(file, arg);
+       ret = btrfs_ioctl_send(inode, arg);
        kfree(arg);
        return ret;
 }
 
+static int btrfs_ioctl_encoded_read(struct file *file, void __user *argp,
+                                   bool compat)
+{
+       struct btrfs_ioctl_encoded_io_args args = { 0 };
+       size_t copy_end_kernel = offsetofend(struct btrfs_ioctl_encoded_io_args,
+                                            flags);
+       size_t copy_end;
+       struct iovec iovstack[UIO_FASTIOV];
+       struct iovec *iov = iovstack;
+       struct iov_iter iter;
+       loff_t pos;
+       struct kiocb kiocb;
+       ssize_t ret;
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               ret = -EPERM;
+               goto out_acct;
+       }
+
+       if (compat) {
+#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
+               struct btrfs_ioctl_encoded_io_args_32 args32;
+
+               copy_end = offsetofend(struct btrfs_ioctl_encoded_io_args_32,
+                                      flags);
+               if (copy_from_user(&args32, argp, copy_end)) {
+                       ret = -EFAULT;
+                       goto out_acct;
+               }
+               args.iov = compat_ptr(args32.iov);
+               args.iovcnt = args32.iovcnt;
+               args.offset = args32.offset;
+               args.flags = args32.flags;
+#else
+               return -ENOTTY;
+#endif
+       } else {
+               copy_end = copy_end_kernel;
+               if (copy_from_user(&args, argp, copy_end)) {
+                       ret = -EFAULT;
+                       goto out_acct;
+               }
+       }
+       if (args.flags != 0) {
+               ret = -EINVAL;
+               goto out_acct;
+       }
+
+       ret = import_iovec(READ, args.iov, args.iovcnt, ARRAY_SIZE(iovstack),
+                          &iov, &iter);
+       if (ret < 0)
+               goto out_acct;
+
+       if (iov_iter_count(&iter) == 0) {
+               ret = 0;
+               goto out_iov;
+       }
+       pos = args.offset;
+       ret = rw_verify_area(READ, file, &pos, args.len);
+       if (ret < 0)
+               goto out_iov;
+
+       init_sync_kiocb(&kiocb, file);
+       kiocb.ki_pos = pos;
+
+       ret = btrfs_encoded_read(&kiocb, &iter, &args);
+       if (ret >= 0) {
+               fsnotify_access(file);
+               if (copy_to_user(argp + copy_end,
+                                (char *)&args + copy_end_kernel,
+                                sizeof(args) - copy_end_kernel))
+                       ret = -EFAULT;
+       }
+
+out_iov:
+       kfree(iov);
+out_acct:
+       if (ret > 0)
+               add_rchar(current, ret);
+       inc_syscr(current);
+       return ret;
+}
+
+static int btrfs_ioctl_encoded_write(struct file *file, void __user *argp, bool compat)
+{
+       struct btrfs_ioctl_encoded_io_args args;
+       struct iovec iovstack[UIO_FASTIOV];
+       struct iovec *iov = iovstack;
+       struct iov_iter iter;
+       loff_t pos;
+       struct kiocb kiocb;
+       ssize_t ret;
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               ret = -EPERM;
+               goto out_acct;
+       }
+
+       if (!(file->f_mode & FMODE_WRITE)) {
+               ret = -EBADF;
+               goto out_acct;
+       }
+
+       if (compat) {
+#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
+               struct btrfs_ioctl_encoded_io_args_32 args32;
+
+               if (copy_from_user(&args32, argp, sizeof(args32))) {
+                       ret = -EFAULT;
+                       goto out_acct;
+               }
+               args.iov = compat_ptr(args32.iov);
+               args.iovcnt = args32.iovcnt;
+               args.offset = args32.offset;
+               args.flags = args32.flags;
+               args.len = args32.len;
+               args.unencoded_len = args32.unencoded_len;
+               args.unencoded_offset = args32.unencoded_offset;
+               args.compression = args32.compression;
+               args.encryption = args32.encryption;
+               memcpy(args.reserved, args32.reserved, sizeof(args.reserved));
+#else
+               return -ENOTTY;
+#endif
+       } else {
+               if (copy_from_user(&args, argp, sizeof(args))) {
+                       ret = -EFAULT;
+                       goto out_acct;
+               }
+       }
+
+       ret = -EINVAL;
+       if (args.flags != 0)
+               goto out_acct;
+       if (memchr_inv(args.reserved, 0, sizeof(args.reserved)))
+               goto out_acct;
+       if (args.compression == BTRFS_ENCODED_IO_COMPRESSION_NONE &&
+           args.encryption == BTRFS_ENCODED_IO_ENCRYPTION_NONE)
+               goto out_acct;
+       if (args.compression >= BTRFS_ENCODED_IO_COMPRESSION_TYPES ||
+           args.encryption >= BTRFS_ENCODED_IO_ENCRYPTION_TYPES)
+               goto out_acct;
+       if (args.unencoded_offset > args.unencoded_len)
+               goto out_acct;
+       if (args.len > args.unencoded_len - args.unencoded_offset)
+               goto out_acct;
+
+       ret = import_iovec(WRITE, args.iov, args.iovcnt, ARRAY_SIZE(iovstack),
+                          &iov, &iter);
+       if (ret < 0)
+               goto out_acct;
+
+       file_start_write(file);
+
+       if (iov_iter_count(&iter) == 0) {
+               ret = 0;
+               goto out_end_write;
+       }
+       pos = args.offset;
+       ret = rw_verify_area(WRITE, file, &pos, args.len);
+       if (ret < 0)
+               goto out_end_write;
+
+       init_sync_kiocb(&kiocb, file);
+       ret = kiocb_set_rw_flags(&kiocb, 0);
+       if (ret)
+               goto out_end_write;
+       kiocb.ki_pos = pos;
+
+       ret = btrfs_do_write_iter(&kiocb, &iter, &args);
+       if (ret > 0)
+               fsnotify_modify(file);
+
+out_end_write:
+       file_end_write(file);
+       kfree(iov);
+out_acct:
+       if (ret > 0)
+               add_wchar(current, ret);
+       inc_syscw(current);
+       return ret;
+}
+
 long btrfs_ioctl(struct file *file, unsigned int
                cmd, unsigned long arg)
 {
@@ -4976,7 +5407,7 @@ long btrfs_ioctl(struct file *file, unsigned int
 
        switch (cmd) {
        case FS_IOC_GETVERSION:
-               return btrfs_ioctl_getversion(file, argp);
+               return btrfs_ioctl_getversion(inode, argp);
        case FS_IOC_GETFSLABEL:
                return btrfs_ioctl_get_fslabel(fs_info, argp);
        case FS_IOC_SETFSLABEL:
@@ -4996,7 +5427,7 @@ long btrfs_ioctl(struct file *file, unsigned int
        case BTRFS_IOC_SNAP_DESTROY_V2:
                return btrfs_ioctl_snap_destroy(file, argp, true);
        case BTRFS_IOC_SUBVOL_GETFLAGS:
-               return btrfs_ioctl_subvol_getflags(file, argp);
+               return btrfs_ioctl_subvol_getflags(inode, argp);
        case BTRFS_IOC_SUBVOL_SETFLAGS:
                return btrfs_ioctl_subvol_setflags(file, argp);
        case BTRFS_IOC_DEFAULT_SUBVOL:
@@ -5020,11 +5451,11 @@ long btrfs_ioctl(struct file *file, unsigned int
        case BTRFS_IOC_BALANCE:
                return btrfs_ioctl_balance(file, NULL);
        case BTRFS_IOC_TREE_SEARCH:
-               return btrfs_ioctl_tree_search(file, argp);
+               return btrfs_ioctl_tree_search(inode, argp);
        case BTRFS_IOC_TREE_SEARCH_V2:
-               return btrfs_ioctl_tree_search_v2(file, argp);
+               return btrfs_ioctl_tree_search_v2(inode, argp);
        case BTRFS_IOC_INO_LOOKUP:
-               return btrfs_ioctl_ino_lookup(file, argp);
+               return btrfs_ioctl_ino_lookup(root, argp);
        case BTRFS_IOC_INO_PATHS:
                return btrfs_ioctl_ino_to_path(root, argp);
        case BTRFS_IOC_LOGICAL_INO:
@@ -5071,10 +5502,10 @@ long btrfs_ioctl(struct file *file, unsigned int
                return btrfs_ioctl_set_received_subvol_32(file, argp);
 #endif
        case BTRFS_IOC_SEND:
-               return _btrfs_ioctl_send(file, argp, false);
+               return _btrfs_ioctl_send(inode, argp, false);
 #if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
        case BTRFS_IOC_SEND_32:
-               return _btrfs_ioctl_send(file, argp, true);
+               return _btrfs_ioctl_send(inode, argp, true);
 #endif
        case BTRFS_IOC_GET_DEV_STATS:
                return btrfs_ioctl_get_dev_stats(fs_info, argp);
@@ -5101,15 +5532,25 @@ long btrfs_ioctl(struct file *file, unsigned int
        case BTRFS_IOC_SET_FEATURES:
                return btrfs_ioctl_set_features(file, argp);
        case BTRFS_IOC_GET_SUBVOL_INFO:
-               return btrfs_ioctl_get_subvol_info(file, argp);
+               return btrfs_ioctl_get_subvol_info(inode, argp);
        case BTRFS_IOC_GET_SUBVOL_ROOTREF:
-               return btrfs_ioctl_get_subvol_rootref(file, argp);
+               return btrfs_ioctl_get_subvol_rootref(root, argp);
        case BTRFS_IOC_INO_LOOKUP_USER:
                return btrfs_ioctl_ino_lookup_user(file, argp);
        case FS_IOC_ENABLE_VERITY:
                return fsverity_ioctl_enable(file, (const void __user *)argp);
        case FS_IOC_MEASURE_VERITY:
                return fsverity_ioctl_measure(file, argp);
+       case BTRFS_IOC_ENCODED_READ:
+               return btrfs_ioctl_encoded_read(file, argp, false);
+       case BTRFS_IOC_ENCODED_WRITE:
+               return btrfs_ioctl_encoded_write(file, argp, false);
+#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
+       case BTRFS_IOC_ENCODED_READ_32:
+               return btrfs_ioctl_encoded_read(file, argp, true);
+       case BTRFS_IOC_ENCODED_WRITE_32:
+               return btrfs_ioctl_encoded_write(file, argp, true);
+#endif
        }
 
        return -ENOTTY;