static void btrfs_dev_replace_update_device_in_mapping_tree( struct btrfs_fs_info *fs_info, struct btrfs_device *srcdev, struct btrfs_device *tgtdev) { struct extent_map_tree *em_tree = &fs_info->mapping_tree.map_tree; struct extent_map *em; struct map_lookup *map; u64 start = 0; int i; write_lock(&em_tree->lock); do { em = lookup_extent_mapping(em_tree, start, (u64)-1); if (!em) break; map = (struct map_lookup *)em->bdev; for (i = 0; i < map->num_stripes; i++) if (srcdev == map->stripes[i].dev) map->stripes[i].dev = tgtdev; start = em->start + em->len; free_extent_map(em); } while (start); write_unlock(&em_tree->lock); }
int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len) { int ret = 0; struct extent_map *merge = NULL; struct rb_node *rb; struct extent_map *em; write_lock(&tree->lock); em = lookup_extent_mapping(tree, start, len); WARN_ON(em->start != start || !em); if (!em) goto out; clear_bit(EXTENT_FLAG_PINNED, &em->flags); if (em->start != 0) { rb = rb_prev(&em->rb_node); if (rb) merge = rb_entry(rb, struct extent_map, rb_node); if (rb && mergable_maps(merge, em)) { em->start = merge->start; em->len += merge->len; em->block_len += merge->block_len; em->block_start = merge->block_start; merge->in_tree = 0; rb_erase(&merge->rb_node, &tree->map); free_extent_map(merge); } }
/** * add_extent_mapping - add new extent map to the extent tree * @tree: tree to insert new map in * @em: map to insert * * Insert @em into @tree or perform a simple forward/backward merge with * existing mappings. The extent_map struct passed in will be inserted * into the tree directly, with an additional reference taken, or a * reference dropped if the merge attempt was sucessfull. */ int add_extent_mapping(struct extent_map_tree *tree, struct extent_map *em) { int ret = 0; struct extent_map *merge = NULL; struct rb_node *rb; struct extent_map *exist; exist = lookup_extent_mapping(tree, em->start, em->len); if (exist) { free_extent_map(exist); ret = -EEXIST; goto out; } assert_spin_locked(&tree->lock); rb = tree_insert(&tree->map, em->start, &em->rb_node); if (rb) { ret = -EEXIST; free_extent_map(merge); goto out; } atomic_inc(&em->refs); if (em->start != 0) { rb = rb_prev(&em->rb_node); if (rb) merge = rb_entry(rb, struct extent_map, rb_node); if (rb && mergable_maps(merge, em)) { em->start = merge->start; em->len += merge->len; em->block_len += merge->block_len; em->block_start = merge->block_start; merge->in_tree = 0; rb_erase(&merge->rb_node, &tree->map); free_extent_map(merge); } }
static void try_merge_map(struct extent_map_tree *tree, struct extent_map *em) { struct extent_map *merge = NULL; struct rb_node *rb; if (em->start != 0) { rb = rb_prev(&em->rb_node); if (rb) merge = rb_entry(rb, struct extent_map, rb_node); if (rb && mergable_maps(merge, em)) { em->start = merge->start; em->orig_start = merge->orig_start; em->len += merge->len; em->block_len += merge->block_len; em->block_start = merge->block_start; merge->in_tree = 0; em->mod_len = (em->mod_len + em->mod_start) - merge->mod_start; em->mod_start = merge->mod_start; em->generation = max(em->generation, merge->generation); rb_erase(&merge->rb_node, &tree->map); free_extent_map(merge); } }
static int test_hole_first(u32 sectorsize, u32 nodesize) { struct btrfs_fs_info *fs_info = NULL; struct inode *inode = NULL; struct btrfs_root *root = NULL; struct extent_map *em = NULL; int ret = -ENOMEM; inode = btrfs_new_test_inode(); if (!inode) { test_msg("Couldn't allocate inode\n"); return ret; } BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY; BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID; BTRFS_I(inode)->location.offset = 0; fs_info = btrfs_alloc_dummy_fs_info(); if (!fs_info) { test_msg("Couldn't allocate dummy fs info\n"); goto out; } root = btrfs_alloc_dummy_root(fs_info, sectorsize, nodesize); if (IS_ERR(root)) { test_msg("Couldn't allocate root\n"); goto out; } root->node = alloc_dummy_extent_buffer(NULL, nodesize, nodesize); if (!root->node) { test_msg("Couldn't allocate dummy buffer\n"); goto out; } extent_buffer_get(root->node); btrfs_set_header_nritems(root->node, 0); btrfs_set_header_level(root->node, 0); BTRFS_I(inode)->root = root; ret = -EINVAL; /* * Need a blank inode item here just so we don't confuse * btrfs_get_extent. */ insert_inode_item_key(root); insert_extent(root, sectorsize, sectorsize, sectorsize, 0, sectorsize, sectorsize, BTRFS_FILE_EXTENT_REG, 0, 1); em = btrfs_get_extent(inode, NULL, 0, 0, 2 * sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (em->start != 0 || em->len != sectorsize) { test_msg("Unexpected extent wanted start 0 len %u, " "got start %llu len %llu\n", sectorsize, em->start, em->len); goto out; } if (em->flags != vacancy_only) { test_msg("Wrong flags, wanted %lu, have %lu\n", vacancy_only, em->flags); goto out; } free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, sectorsize, 2 * sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != sectorsize) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != sectorsize || em->len != sectorsize) { test_msg("Unexpected extent wanted start %u len %u, " "got start %llu len %llu\n", sectorsize, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, wanted 0 got %lu\n", em->flags); goto out; } ret = 0; out: if (!IS_ERR(em)) free_extent_map(em); iput(inode); btrfs_free_dummy_root(root); btrfs_free_dummy_fs_info(fs_info); return ret; }
static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) { struct btrfs_fs_info *fs_info = NULL; struct inode *inode = NULL; struct btrfs_root *root = NULL; struct extent_map *em = NULL; u64 orig_start; u64 disk_bytenr; u64 offset; int ret = -ENOMEM; inode = btrfs_new_test_inode(); if (!inode) { test_msg("Couldn't allocate inode\n"); return ret; } BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY; BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID; BTRFS_I(inode)->location.offset = 0; fs_info = btrfs_alloc_dummy_fs_info(); if (!fs_info) { test_msg("Couldn't allocate dummy fs info\n"); goto out; } root = btrfs_alloc_dummy_root(fs_info, sectorsize, nodesize); if (IS_ERR(root)) { test_msg("Couldn't allocate root\n"); goto out; } root->node = alloc_dummy_extent_buffer(NULL, nodesize, nodesize); if (!root->node) { test_msg("Couldn't allocate dummy buffer\n"); goto out; } /* * We will just free a dummy node if it's ref count is 2 so we need an * extra ref so our searches don't accidentally release our page. */ extent_buffer_get(root->node); btrfs_set_header_nritems(root->node, 0); btrfs_set_header_level(root->node, 0); ret = -EINVAL; /* First with no extents */ BTRFS_I(inode)->root = root; em = btrfs_get_extent(inode, NULL, 0, 0, sectorsize, 0); if (IS_ERR(em)) { em = NULL; test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (!test_bit(EXTENT_FLAG_VACANCY, &em->flags)) { test_msg("Vacancy flag wasn't set properly\n"); goto out; } free_extent_map(em); btrfs_drop_extent_cache(inode, 0, (u64)-1, 0); /* * All of the magic numbers are based on the mapping setup in * setup_file_extents, so if you change anything there you need to * update the comment and update the expected values below. */ setup_file_extents(root, sectorsize); em = btrfs_get_extent(inode, NULL, 0, 0, (u64)-1, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (em->start != 0 || em->len != 5) { test_msg("Unexpected extent wanted start 0 len 5, got start " "%llu len %llu\n", em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_INLINE) { test_msg("Expected an inline, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != (sectorsize - 5)) { test_msg("Unexpected extent wanted start %llu len 1, got start " "%llu len %llu\n", offset, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } /* * We don't test anything else for inline since it doesn't get set * unless we have a page for it to write into. Maybe we should change * this? */ offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != 4) { test_msg("Unexpected extent wanted start %llu len 4, got start " "%llu len %llu\n", offset, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } offset = em->start + em->len; free_extent_map(em); /* Regular extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize - 1) { test_msg("Unexpected extent wanted start %llu len 4095, got " "start %llu len %llu\n", offset, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); /* The next 3 are split extents */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } disk_bytenr = em->block_start; orig_start = em->start; offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != orig_start) { test_msg("Wrong orig offset, want %llu, have %llu\n", orig_start, em->orig_start); goto out; } disk_bytenr += (em->start - orig_start); if (em->block_start != disk_bytenr) { test_msg("Wrong block start, want %llu, have %llu\n", disk_bytenr, em->block_start); goto out; } offset = em->start + em->len; free_extent_map(em); /* Prealloc extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != prealloc_only) { test_msg("Unexpected flags set, want %lu have %lu\n", prealloc_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); /* The next 3 are a half written prealloc extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != prealloc_only) { test_msg("Unexpected flags set, want %lu have %lu\n", prealloc_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } disk_bytenr = em->block_start; orig_start = em->start; offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_HOLE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != orig_start) { test_msg("Unexpected orig offset, wanted %llu, have %llu\n", orig_start, em->orig_start); goto out; } if (em->block_start != (disk_bytenr + (em->start - em->orig_start))) { test_msg("Unexpected block start, wanted %llu, have %llu\n", disk_bytenr + (em->start - em->orig_start), em->block_start); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != prealloc_only) { test_msg("Unexpected flags set, want %lu have %lu\n", prealloc_only, em->flags); goto out; } if (em->orig_start != orig_start) { test_msg("Wrong orig offset, want %llu, have %llu\n", orig_start, em->orig_start); goto out; } if (em->block_start != (disk_bytenr + (em->start - em->orig_start))) { test_msg("Unexpected block start, wanted %llu, have %llu\n", disk_bytenr + (em->start - em->orig_start), em->block_start); goto out; } offset = em->start + em->len; free_extent_map(em); /* Now for the compressed extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u," "got start %llu len %llu\n", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != compressed_only) { test_msg("Unexpected flags set, want %lu have %lu\n", compressed_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } if (em->compress_type != BTRFS_COMPRESS_ZLIB) { test_msg("Unexpected compress type, wanted %d, got %d\n", BTRFS_COMPRESS_ZLIB, em->compress_type); goto out; } offset = em->start + em->len; free_extent_map(em); /* Split compressed extent */ em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u," "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != compressed_only) { test_msg("Unexpected flags set, want %lu have %lu\n", compressed_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } if (em->compress_type != BTRFS_COMPRESS_ZLIB) { test_msg("Unexpected compress type, wanted %d, got %d\n", BTRFS_COMPRESS_ZLIB, em->compress_type); goto out; } disk_bytenr = em->block_start; orig_start = em->start; offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != disk_bytenr) { test_msg("Block start does not match, want %llu got %llu\n", disk_bytenr, em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != compressed_only) { test_msg("Unexpected flags set, want %lu have %lu\n", compressed_only, em->flags); goto out; } if (em->orig_start != orig_start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, orig_start); goto out; } if (em->compress_type != BTRFS_COMPRESS_ZLIB) { test_msg("Unexpected compress type, wanted %d, got %d\n", BTRFS_COMPRESS_ZLIB, em->compress_type); goto out; } offset = em->start + em->len; free_extent_map(em); /* A hole between regular extents but no hole extent */ em = btrfs_get_extent(inode, NULL, 0, offset + 6, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, 4096 * 1024, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { test_msg("Expected a hole extent, got %llu\n", em->block_start); goto out; } /* * Currently we just return a length that we requested rather than the * length of the actual hole, if this changes we'll have to change this * test. */ if (em->start != offset || em->len != 3 * sectorsize) { test_msg("Unexpected extent wanted start %llu len %u, " "got start %llu len %llu\n", offset, 3 * sectorsize, em->start, em->len); goto out; } if (em->flags != vacancy_only) { test_msg("Unexpected flags set, want %lu have %lu\n", vacancy_only, em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } offset = em->start + em->len; free_extent_map(em); em = btrfs_get_extent(inode, NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { test_msg("Got an error when we shouldn't have\n"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { test_msg("Expected a real extent, got %llu\n", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { test_msg("Unexpected extent wanted start %llu len %u," "got start %llu len %llu\n", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); goto out; } if (em->orig_start != em->start) { test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, em->orig_start); goto out; } ret = 0; out: if (!IS_ERR(em)) free_extent_map(em); iput(inode); btrfs_free_dummy_root(root); btrfs_free_dummy_fs_info(fs_info); return ret; }
static int should_defrag_range(struct inode *inode, u64 start, u64 len, int thresh, u64 *last_len, u64 *skip, u64 *defrag_end) { struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; struct extent_map *em = NULL; struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; int ret = 1; if (thresh == 0) thresh = 256 * 1024; /* * make sure that once we start defragging and extent, we keep on * defragging it */ if (start < *defrag_end) return 1; *skip = 0; /* * hopefully we have this extent in the tree already, try without * the full extent lock */ read_lock(&em_tree->lock); em = lookup_extent_mapping(em_tree, start, len); read_unlock(&em_tree->lock); if (!em) { /* get the big lock and read metadata off disk */ lock_extent(io_tree, start, start + len - 1, GFP_NOFS); em = btrfs_get_extent(inode, NULL, 0, start, len, 0); unlock_extent(io_tree, start, start + len - 1, GFP_NOFS); if (IS_ERR(em)) return 0; } /* this will cover holes, and inline extents */ if (em->block_start >= EXTENT_MAP_LAST_BYTE) ret = 0; /* * we hit a real extent, if it is big don't bother defragging it again */ if ((*last_len == 0 || *last_len >= thresh) && em->len >= thresh) ret = 0; /* * last_len ends up being a counter of how many bytes we've defragged. * every time we choose not to defrag an extent, we reset *last_len * so that the next tiny extent will force a defrag. * * The end result of this is that tiny extents before a single big * extent will force at least part of that big extent to be defragged. */ if (ret) { *last_len += len; *defrag_end = extent_map_end(em); } else { *last_len = 0; *skip = extent_map_end(em); *defrag_end = 0; } free_extent_map(em); return ret; }
/* * this drops all the extents in the cache that intersect the range * [start, end]. Existing extents are split as required. */ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end, int skip_pinned) { struct extent_map *em; struct extent_map *split = NULL; struct extent_map *split2 = NULL; struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; u64 len = end - start + 1; int ret; int testend = 1; unsigned long flags; int compressed = 0; WARN_ON(end < start); if (end == (u64)-1) { len = (u64)-1; testend = 0; } while (1) { if (!split) split = alloc_extent_map(GFP_NOFS); if (!split2) split2 = alloc_extent_map(GFP_NOFS); write_lock(&em_tree->lock); em = lookup_extent_mapping(em_tree, start, len); if (!em) { write_unlock(&em_tree->lock); break; } flags = em->flags; if (skip_pinned && test_bit(EXTENT_FLAG_PINNED, &em->flags)) { if (testend && em->start + em->len >= start + len) { free_extent_map(em); write_unlock(&em_tree->lock); break; } start = em->start + em->len; if (testend) len = start + len - (em->start + em->len); free_extent_map(em); write_unlock(&em_tree->lock); continue; } compressed = test_bit(EXTENT_FLAG_COMPRESSED, &em->flags); clear_bit(EXTENT_FLAG_PINNED, &em->flags); remove_extent_mapping(em_tree, em); if (em->block_start < EXTENT_MAP_LAST_BYTE && em->start < start) { split->start = em->start; split->len = start - em->start; split->orig_start = em->orig_start; split->block_start = em->block_start; if (compressed) split->block_len = em->block_len; else split->block_len = split->len; split->bdev = em->bdev; split->flags = flags; ret = add_extent_mapping(em_tree, split); BUG_ON(ret); free_extent_map(split); split = split2; split2 = NULL; } if (em->block_start < EXTENT_MAP_LAST_BYTE && testend && em->start + em->len > start + len) { u64 diff = start + len - em->start; split->start = start + len; split->len = em->start + em->len - (start + len); split->bdev = em->bdev; split->flags = flags; if (compressed) { split->block_len = em->block_len; split->block_start = em->block_start; split->orig_start = em->orig_start; } else { split->block_len = split->len; split->block_start = em->block_start + diff; split->orig_start = split->start; } ret = add_extent_mapping(em_tree, split); BUG_ON(ret); free_extent_map(split); split = NULL; } write_unlock(&em_tree->lock); /* once for us */ free_extent_map(em); /* once for the tree*/ free_extent_map(em); } if (split) free_extent_map(split); if (split2) free_extent_map(split2); return 0; }