]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
Merge branch 'for-chris-4.11-part2' of git://git.kernel.org/pub/scm/linux/kernel...
authorChris Mason <clm@fb.com>
Tue, 28 Feb 2017 22:35:09 +0000 (14:35 -0800)
committerChris Mason <clm@fb.com>
Tue, 28 Feb 2017 22:35:09 +0000 (14:35 -0800)
fs/btrfs/ctree.c
fs/btrfs/disk-io.c
fs/btrfs/extent-tree.c
fs/btrfs/file-item.c
fs/btrfs/inode.c
fs/btrfs/send.c
fs/btrfs/tree-log.c

index 2c3c943bfcdc8d24b63be190f33d8abf9f800f75..7dc8844037e03b7cfb738d81af6c22b33ee2b31d 100644 (file)
@@ -4157,6 +4157,9 @@ static noinline int push_for_double_split(struct btrfs_trans_handle *trans,
 
        /* try to push all the items before our slot into the next leaf */
        slot = path->slots[0];
+       space_needed = data_size;
+       if (slot > 0)
+               space_needed -= btrfs_leaf_free_space(fs_info, path->nodes[0]);
        ret = push_leaf_left(trans, root, path, 1, space_needed, 0, slot);
        if (ret < 0)
                return ret;
@@ -4212,6 +4215,10 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans,
                if (wret < 0)
                        return wret;
                if (wret) {
+                       space_needed = data_size;
+                       if (slot > 0)
+                               space_needed -= btrfs_leaf_free_space(fs_info,
+                                                                     l);
                        wret = push_leaf_left(trans, root, path, space_needed,
                                              space_needed, 0, (u32)-1);
                        if (wret < 0)
index cad47ece2fdfbf4b738c0cb2391bd95bb4411a93..73fdc6bdaea98322d2d8fa602d33e4b8c1f5ecc1 100644 (file)
@@ -2205,11 +2205,9 @@ static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info)
        btrfs_destroy_workqueue(fs_info->delalloc_workers);
        btrfs_destroy_workqueue(fs_info->workers);
        btrfs_destroy_workqueue(fs_info->endio_workers);
-       btrfs_destroy_workqueue(fs_info->endio_meta_workers);
        btrfs_destroy_workqueue(fs_info->endio_raid56_workers);
        btrfs_destroy_workqueue(fs_info->endio_repair_workers);
        btrfs_destroy_workqueue(fs_info->rmw_workers);
-       btrfs_destroy_workqueue(fs_info->endio_meta_write_workers);
        btrfs_destroy_workqueue(fs_info->endio_write_workers);
        btrfs_destroy_workqueue(fs_info->endio_freespace_worker);
        btrfs_destroy_workqueue(fs_info->submit_workers);
@@ -2219,6 +2217,13 @@ static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info)
        btrfs_destroy_workqueue(fs_info->flush_workers);
        btrfs_destroy_workqueue(fs_info->qgroup_rescan_workers);
        btrfs_destroy_workqueue(fs_info->extent_workers);
+       /*
+        * Now that all other work queues are destroyed, we can safely destroy
+        * the queues used for metadata I/O, since tasks from those other work
+        * queues can do metadata I/O operations.
+        */
+       btrfs_destroy_workqueue(fs_info->endio_meta_workers);
+       btrfs_destroy_workqueue(fs_info->endio_meta_write_workers);
 }
 
 static void free_root_extent_buffers(struct btrfs_root *root)
@@ -3261,7 +3266,6 @@ fail_fsdev_sysfs:
 
 fail_block_groups:
        btrfs_put_block_group_cache(fs_info);
-       btrfs_free_block_groups(fs_info);
 
 fail_tree_roots:
        free_root_pointers(fs_info, 1);
@@ -3269,6 +3273,7 @@ fail_tree_roots:
 
 fail_sb_buffer:
        btrfs_stop_all_workers(fs_info);
+       btrfs_free_block_groups(fs_info);
 fail_alloc:
 fail_iput:
        btrfs_mapping_tree_free(&fs_info->mapping_tree);
@@ -3977,8 +3982,6 @@ void close_ctree(struct btrfs_fs_info *fs_info)
 
        btrfs_put_block_group_cache(fs_info);
 
-       btrfs_free_block_groups(fs_info);
-
        /*
         * we must make sure there is not any read request to
         * submit after we stopping all workers.
@@ -3986,6 +3989,8 @@ void close_ctree(struct btrfs_fs_info *fs_info)
        invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
        btrfs_stop_all_workers(fs_info);
 
+       btrfs_free_block_groups(fs_info);
+
        clear_bit(BTRFS_FS_OPEN, &fs_info->flags);
        free_root_pointers(fs_info, 1);
 
index 3853fab2f49f5f6a8eb015c37b9afa39bc0f400f..60794658ffd880d32f8921ba1286fa3e717c3be6 100644 (file)
@@ -9735,6 +9735,11 @@ void btrfs_put_block_group_cache(struct btrfs_fs_info *info)
        }
 }
 
+/*
+ * Must be called only after stopping all workers, since we could have block
+ * group caching kthreads running, and therefore they could race with us if we
+ * freed the block groups before stopping them.
+ */
 int btrfs_free_block_groups(struct btrfs_fs_info *info)
 {
        struct btrfs_block_group_cache *block_group;
@@ -9774,9 +9779,6 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
                list_del(&block_group->list);
                up_write(&block_group->space_info->groups_sem);
 
-               if (block_group->cached == BTRFS_CACHE_STARTED)
-                       wait_block_group_cache_done(block_group);
-
                /*
                 * We haven't cached this block group, which means we could
                 * possibly have excluded extents on this block group.
@@ -9786,6 +9788,7 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
                        free_excluded_extents(info, block_group);
 
                btrfs_remove_free_space_cache(block_group);
+               ASSERT(block_group->cached != BTRFS_CACHE_STARTED);
                ASSERT(list_empty(&block_group->dirty_list));
                ASSERT(list_empty(&block_group->io_list));
                ASSERT(list_empty(&block_group->bg_list));
index 54ec6d6ef0163fb51aa9ae242a423e8dd3d51474..64fcb31d71633c2731d6241b1236f7c57b1f5b6f 100644 (file)
@@ -643,7 +643,33 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans,
 
                /* delete the entire item, it is inside our range */
                if (key.offset >= bytenr && csum_end <= end_byte) {
-                       ret = btrfs_del_item(trans, root, path);
+                       int del_nr = 1;
+
+                       /*
+                        * Check how many csum items preceding this one in this
+                        * leaf correspond to our range and then delete them all
+                        * at once.
+                        */
+                       if (key.offset > bytenr && path->slots[0] > 0) {
+                               int slot = path->slots[0] - 1;
+
+                               while (slot >= 0) {
+                                       struct btrfs_key pk;
+
+                                       btrfs_item_key_to_cpu(leaf, &pk, slot);
+                                       if (pk.offset < bytenr ||
+                                           pk.type != BTRFS_EXTENT_CSUM_KEY ||
+                                           pk.objectid !=
+                                           BTRFS_EXTENT_CSUM_OBJECTID)
+                                               break;
+                                       path->slots[0] = slot;
+                                       del_nr++;
+                                       key.offset = pk.offset;
+                                       slot--;
+                               }
+                       }
+                       ret = btrfs_del_items(trans, root, path,
+                                             path->slots[0], del_nr);
                        if (ret)
                                goto out;
                        if (key.offset == bytenr)
index ca1995cfd8e99631acb97bfa9a4ec51e5e602dd1..b2bc07aad1ae98cac403ed54c839350f8a46b84a 100644 (file)
@@ -1323,10 +1323,16 @@ next_slot:
                         * either valid or do not exist.
                         */
                        if (csum_exist_in_range(fs_info, disk_bytenr,
-                                               num_bytes))
+                                               num_bytes)) {
+                               if (!nolock)
+                                       btrfs_end_write_no_snapshoting(root);
                                goto out_check;
-                       if (!btrfs_inc_nocow_writers(fs_info, disk_bytenr))
+                       }
+                       if (!btrfs_inc_nocow_writers(fs_info, disk_bytenr)) {
+                               if (!nolock)
+                                       btrfs_end_write_no_snapshoting(root);
                                goto out_check;
+                       }
                        nocow = 1;
                } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
                        extent_end = found_key.offset +
@@ -4404,19 +4410,8 @@ search_again:
                if (found_type > min_type) {
                        del_item = 1;
                } else {
-                       if (item_end < new_size) {
-                               /*
-                                * With NO_HOLES mode, for the following mapping
-                                *
-                                * [0-4k][hole][8k-12k]
-                                *
-                                * if truncating isize down to 6k, it ends up
-                                * isize being 8k.
-                                */
-                               if (btrfs_fs_incompat(root->fs_info, NO_HOLES))
-                                       last_size = new_size;
+                       if (item_end < new_size)
                                break;
-                       }
                        if (found_key.offset >= new_size)
                                del_item = 1;
                        else
@@ -4599,8 +4594,12 @@ out:
                        btrfs_abort_transaction(trans, ret);
        }
 error:
-       if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID)
+       if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) {
+               ASSERT(last_size >= new_size);
+               if (!err && last_size > new_size)
+                       last_size = new_size;
                btrfs_ordered_update_i_size(inode, last_size, NULL);
+       }
 
        btrfs_free_path(path);
 
index d145ce804620219820b276e627b034c35f18550f..456c8901489b6c6b468901854bcdcbc53cb5cf13 100644 (file)
@@ -1681,6 +1681,9 @@ static int is_inode_existent(struct send_ctx *sctx, u64 ino, u64 gen)
 {
        int ret;
 
+       if (ino == BTRFS_FIRST_FREE_OBJECTID)
+               return 1;
+
        ret = get_cur_inode_state(sctx, ino, gen);
        if (ret < 0)
                goto out;
@@ -1866,7 +1869,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
         * not deleted and then re-created, if it was then we have no overwrite
         * and we can just unlink this entry.
         */
-       if (sctx->parent_root) {
+       if (sctx->parent_root && dir != BTRFS_FIRST_FREE_OBJECTID) {
                ret = get_inode_info(sctx->parent_root, dir, NULL, &gen, NULL,
                                     NULL, NULL, NULL);
                if (ret < 0 && ret != -ENOENT)
@@ -1934,6 +1937,19 @@ static int did_overwrite_ref(struct send_ctx *sctx,
        if (ret <= 0)
                goto out;
 
+       if (dir != BTRFS_FIRST_FREE_OBJECTID) {
+               ret = get_inode_info(sctx->send_root, dir, NULL, &gen, NULL,
+                                    NULL, NULL, NULL);
+               if (ret < 0 && ret != -ENOENT)
+                       goto out;
+               if (ret) {
+                       ret = 0;
+                       goto out;
+               }
+               if (gen != dir_gen)
+                       goto out;
+       }
+
        /* check if the ref was overwritten by another ref */
        ret = lookup_dir_item_inode(sctx->send_root, dir, name, name_len,
                        &ow_inode, &other_type);
@@ -3556,6 +3572,7 @@ static int wait_for_parent_move(struct send_ctx *sctx,
 {
        int ret = 0;
        u64 ino = parent_ref->dir;
+       u64 ino_gen = parent_ref->dir_gen;
        u64 parent_ino_before, parent_ino_after;
        struct fs_path *path_before = NULL;
        struct fs_path *path_after = NULL;
@@ -3576,6 +3593,8 @@ static int wait_for_parent_move(struct send_ctx *sctx,
         * at get_cur_path()).
         */
        while (ino > BTRFS_FIRST_FREE_OBJECTID) {
+               u64 parent_ino_after_gen;
+
                if (is_waiting_for_move(sctx, ino)) {
                        /*
                         * If the current inode is an ancestor of ino in the
@@ -3598,7 +3617,7 @@ static int wait_for_parent_move(struct send_ctx *sctx,
                fs_path_reset(path_after);
 
                ret = get_first_ref(sctx->send_root, ino, &parent_ino_after,
-                                   NULL, path_after);
+                                   &parent_ino_after_gen, path_after);
                if (ret < 0)
                        goto out;
                ret = get_first_ref(sctx->parent_root, ino, &parent_ino_before,
@@ -3615,10 +3634,20 @@ static int wait_for_parent_move(struct send_ctx *sctx,
                if (ino > sctx->cur_ino &&
                    (parent_ino_before != parent_ino_after || len1 != len2 ||
                     memcmp(path_before->start, path_after->start, len1))) {
-                       ret = 1;
-                       break;
+                       u64 parent_ino_gen;
+
+                       ret = get_inode_info(sctx->parent_root, ino, NULL,
+                                            &parent_ino_gen, NULL, NULL, NULL,
+                                            NULL);
+                       if (ret < 0)
+                               goto out;
+                       if (ino_gen == parent_ino_gen) {
+                               ret = 1;
+                               break;
+                       }
                }
                ino = parent_ino_after;
+               ino_gen = parent_ino_after_gen;
        }
 
 out:
@@ -5277,6 +5306,81 @@ out:
        return ret;
 }
 
+static int range_is_hole_in_parent(struct send_ctx *sctx,
+                                  const u64 start,
+                                  const u64 end)
+{
+       struct btrfs_path *path;
+       struct btrfs_key key;
+       struct btrfs_root *root = sctx->parent_root;
+       u64 search_start = start;
+       int ret;
+
+       path = alloc_path_for_send();
+       if (!path)
+               return -ENOMEM;
+
+       key.objectid = sctx->cur_ino;
+       key.type = BTRFS_EXTENT_DATA_KEY;
+       key.offset = search_start;
+       ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+       if (ret < 0)
+               goto out;
+       if (ret > 0 && path->slots[0] > 0)
+               path->slots[0]--;
+
+       while (search_start < end) {
+               struct extent_buffer *leaf = path->nodes[0];
+               int slot = path->slots[0];
+               struct btrfs_file_extent_item *fi;
+               u64 extent_end;
+
+               if (slot >= btrfs_header_nritems(leaf)) {
+                       ret = btrfs_next_leaf(root, path);
+                       if (ret < 0)
+                               goto out;
+                       else if (ret > 0)
+                               break;
+                       continue;
+               }
+
+               btrfs_item_key_to_cpu(leaf, &key, slot);
+               if (key.objectid < sctx->cur_ino ||
+                   key.type < BTRFS_EXTENT_DATA_KEY)
+                       goto next;
+               if (key.objectid > sctx->cur_ino ||
+                   key.type > BTRFS_EXTENT_DATA_KEY ||
+                   key.offset >= end)
+                       break;
+
+               fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
+               if (btrfs_file_extent_type(leaf, fi) ==
+                   BTRFS_FILE_EXTENT_INLINE) {
+                       u64 size = btrfs_file_extent_inline_len(leaf, slot, fi);
+
+                       extent_end = ALIGN(key.offset + size,
+                                          root->fs_info->sectorsize);
+               } else {
+                       extent_end = key.offset +
+                               btrfs_file_extent_num_bytes(leaf, fi);
+               }
+               if (extent_end <= start)
+                       goto next;
+               if (btrfs_file_extent_disk_bytenr(leaf, fi) == 0) {
+                       search_start = extent_end;
+                       goto next;
+               }
+               ret = 0;
+               goto out;
+next:
+               path->slots[0]++;
+       }
+       ret = 1;
+out:
+       btrfs_free_path(path);
+       return ret;
+}
+
 static int maybe_send_hole(struct send_ctx *sctx, struct btrfs_path *path,
                           struct btrfs_key *key)
 {
@@ -5321,8 +5425,17 @@ static int maybe_send_hole(struct send_ctx *sctx, struct btrfs_path *path,
                        return ret;
        }
 
-       if (sctx->cur_inode_last_extent < key->offset)
-               ret = send_hole(sctx, key->offset);
+       if (sctx->cur_inode_last_extent < key->offset) {
+               ret = range_is_hole_in_parent(sctx,
+                                             sctx->cur_inode_last_extent,
+                                             key->offset);
+               if (ret < 0)
+                       return ret;
+               else if (ret == 0)
+                       ret = send_hole(sctx, key->offset);
+               else
+                       ret = 0;
+       }
        sctx->cur_inode_last_extent = extent_end;
        return ret;
 }
index c9ada4b90004125615238dd0de6845fee37c4954..a59674c3e69efb76d27d6705b41ca76d94e82e15 100644 (file)
@@ -673,6 +673,10 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
                unsigned long dest_offset;
                struct btrfs_key ins;
 
+               if (btrfs_file_extent_disk_bytenr(eb, item) == 0 &&
+                   btrfs_fs_incompat(fs_info, NO_HOLES))
+                       goto update_inode;
+
                ret = btrfs_insert_empty_item(trans, root, path, key,
                                              sizeof(*item));
                if (ret)
@@ -825,6 +829,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
        }
 
        inode_add_bytes(inode, nbytes);
+update_inode:
        ret = btrfs_update_inode(trans, root, inode);
 out:
        if (inode)