/* * deletes the csum items from the csum tree for a given * range of bytes. */ int btrfs_del_csums(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 bytenr, u64 len) { struct btrfs_path *path; struct btrfs_key key; u64 end_byte = bytenr + len; u64 csum_end; struct extent_buffer *leaf; int ret; u16 csum_size = btrfs_super_csum_size(&root->fs_info->super_copy); int blocksize = root->sectorsize; root = root->fs_info->csum_root; path = btrfs_alloc_path(); while (1) { key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; key.offset = end_byte - 1; key.type = BTRFS_EXTENT_CSUM_KEY; ret = btrfs_search_slot(trans, root, &key, path, -1, 1); if (ret > 0) { if (path->slots[0] == 0) goto out; path->slots[0]--; } leaf = path->nodes[0]; btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID || key.type != BTRFS_EXTENT_CSUM_KEY) { break; } if (key.offset >= end_byte) break; csum_end = btrfs_item_size_nr(leaf, path->slots[0]) / csum_size; csum_end *= blocksize; csum_end += key.offset; /* this csum ends before we start, we're done */ if (csum_end <= bytenr) break; /* delete the entire item, it is inside our range */ if (key.offset >= bytenr && csum_end <= end_byte) { ret = btrfs_del_item(trans, root, path); BUG_ON(ret); } else if (key.offset < bytenr && csum_end > end_byte) { unsigned long offset; unsigned long shift_len; unsigned long item_offset; /* * [ bytenr - len ] * [csum ] * * Our bytes are in the middle of the csum, * we need to split this item and insert a new one. * * But we can't drop the path because the * csum could change, get removed, extended etc. * * The trick here is the max size of a csum item leaves * enough room in the tree block for a single * item header. So, we split the item in place, * adding a new header pointing to the existing * bytes. Then we loop around again and we have * a nicely formed csum item that we can neatly * truncate. */ offset = (bytenr - key.offset) / blocksize; offset *= csum_size; shift_len = (len / blocksize) * csum_size; item_offset = btrfs_item_ptr_offset(leaf, path->slots[0]); memset_extent_buffer(leaf, 0, item_offset + offset, shift_len); key.offset = bytenr; /* * btrfs_split_item returns -EAGAIN when the * item changed size or key */ ret = btrfs_split_item(trans, root, path, &key, offset); BUG_ON(ret && ret != -EAGAIN); key.offset = end_byte - 1; } else { ret = truncate_one_csum(trans, root, path, &key, bytenr, len); BUG_ON(ret); } btrfs_release_path(root, path); } out: btrfs_free_path(path); return 0; }
/* * called from commit_transaction. Writes changed device replace state to * disk. */ int btrfs_run_dev_replace(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info) { int ret; struct btrfs_root *dev_root = fs_info->dev_root; struct btrfs_path *path; struct btrfs_key key; struct extent_buffer *eb; struct btrfs_dev_replace_item *ptr; struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; btrfs_dev_replace_lock(dev_replace); if (!dev_replace->is_valid || !dev_replace->item_needs_writeback) { btrfs_dev_replace_unlock(dev_replace); return 0; } btrfs_dev_replace_unlock(dev_replace); key.objectid = 0; key.type = BTRFS_DEV_REPLACE_KEY; key.offset = 0; path = btrfs_alloc_path(); if (!path) { ret = -ENOMEM; goto out; } ret = btrfs_search_slot(trans, dev_root, &key, path, -1, 1); if (ret < 0) { btrfs_warn(fs_info, "error %d while searching for dev_replace item!", ret); goto out; } if (ret == 0 && btrfs_item_size_nr(path->nodes[0], path->slots[0]) < sizeof(*ptr)) { /* * need to delete old one and insert a new one. * Since no attempt is made to recover any old state, if the * dev_replace state is 'running', the data on the target * drive is lost. * It would be possible to recover the state: just make sure * that the beginning of the item is never changed and always * contains all the essential information. Then read this * minimal set of information and use it as a base for the * new state. */ ret = btrfs_del_item(trans, dev_root, path); if (ret != 0) { btrfs_warn(fs_info, "delete too small dev_replace item failed %d!", ret); goto out; } ret = 1; } if (ret == 1) { /* need to insert a new item */ btrfs_release_path(path); ret = btrfs_insert_empty_item(trans, dev_root, path, &key, sizeof(*ptr)); if (ret < 0) { btrfs_warn(fs_info, "insert dev_replace item failed %d!", ret); goto out; } } eb = path->nodes[0]; ptr = btrfs_item_ptr(eb, path->slots[0], struct btrfs_dev_replace_item); btrfs_dev_replace_lock(dev_replace); if (dev_replace->srcdev) btrfs_set_dev_replace_src_devid(eb, ptr, dev_replace->srcdev->devid); else btrfs_set_dev_replace_src_devid(eb, ptr, (u64)-1); btrfs_set_dev_replace_cont_reading_from_srcdev_mode(eb, ptr, dev_replace->cont_reading_from_srcdev_mode); btrfs_set_dev_replace_replace_state(eb, ptr, dev_replace->replace_state); btrfs_set_dev_replace_time_started(eb, ptr, dev_replace->time_started); btrfs_set_dev_replace_time_stopped(eb, ptr, dev_replace->time_stopped); btrfs_set_dev_replace_num_write_errors(eb, ptr, atomic64_read(&dev_replace->num_write_errors)); btrfs_set_dev_replace_num_uncorrectable_read_errors(eb, ptr, atomic64_read(&dev_replace->num_uncorrectable_read_errors)); dev_replace->cursor_left_last_write_of_item = dev_replace->cursor_left; btrfs_set_dev_replace_cursor_left(eb, ptr, dev_replace->cursor_left_last_write_of_item); btrfs_set_dev_replace_cursor_right(eb, ptr, dev_replace->cursor_right); dev_replace->item_needs_writeback = 0; btrfs_dev_replace_unlock(dev_replace); btrfs_mark_buffer_dirty(eb); out: btrfs_free_path(path); return ret; }
static int add_free_space_extent(struct btrfs_trans_handle *trans, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, u64 start, u64 size) { struct btrfs_root *root = trans->fs_info->free_space_root; struct btrfs_key key, new_key; u64 found_start, found_end; u64 end = start + size; int new_extents = 1; int ret; /* * We are adding a new extent of free space, but we need to merge * extents. There are four cases here: * * 1. The new extent does not have any immediate neighbors to merge * with: add the new key and increment the free space extent count. We * may need to convert the block group to bitmaps as a result. * 2. The new extent has an immediate neighbor before it: remove the * previous key and insert a new key combining both of them. There is no * net change in the number of extents. * 3. The new extent has an immediate neighbor after it: remove the next * key and insert a new key combining both of them. There is no net * change in the number of extents. * 4. The new extent has immediate neighbors on both sides: remove both * of the keys and insert a new key combining all of them. Where we used * to have two extents, we now have one, so decrement the extent count. */ new_key.objectid = start; new_key.type = BTRFS_FREE_SPACE_EXTENT_KEY; new_key.offset = size; /* Search for a neighbor on the left. */ if (start == block_group->key.objectid) goto right; key.objectid = start - 1; key.type = (u8)-1; key.offset = (u64)-1; ret = btrfs_search_prev_slot(trans, root, &key, path, -1, 1); if (ret) goto out; btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); if (key.type != BTRFS_FREE_SPACE_EXTENT_KEY) { ASSERT(key.type == BTRFS_FREE_SPACE_INFO_KEY); btrfs_release_path(path); goto right; } found_start = key.objectid; found_end = key.objectid + key.offset; ASSERT(found_start >= block_group->key.objectid && found_end > block_group->key.objectid); ASSERT(found_start < start && found_end <= start); /* * Delete the neighbor on the left and absorb it into the new key (cases * 2 and 4). */ if (found_end == start) { ret = btrfs_del_item(trans, root, path); if (ret) goto out; new_key.objectid = found_start; new_key.offset += key.offset; new_extents--; } btrfs_release_path(path); right: /* Search for a neighbor on the right. */ if (end == block_group->key.objectid + block_group->key.offset) goto insert; key.objectid = end; key.type = (u8)-1; key.offset = (u64)-1; ret = btrfs_search_prev_slot(trans, root, &key, path, -1, 1); if (ret) goto out; btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); if (key.type != BTRFS_FREE_SPACE_EXTENT_KEY) { ASSERT(key.type == BTRFS_FREE_SPACE_INFO_KEY); btrfs_release_path(path); goto insert; } found_start = key.objectid; found_end = key.objectid + key.offset; ASSERT(found_start >= block_group->key.objectid && found_end > block_group->key.objectid); ASSERT((found_start < start && found_end <= start) || (found_start >= end && found_end > end)); /* * Delete the neighbor on the right and absorb it into the new key * (cases 3 and 4). */ if (found_start == end) { ret = btrfs_del_item(trans, root, path); if (ret) goto out; new_key.offset += key.offset; new_extents--; } btrfs_release_path(path); insert: /* Insert the new key (cases 1-4). */ ret = btrfs_insert_empty_item(trans, root, path, &new_key, 0); if (ret) goto out; btrfs_release_path(path); ret = update_free_space_extent_count(trans, block_group, path, new_extents); out: return ret; }
int main(int ac, char **av) { struct btrfs_key ins; struct btrfs_key last = { (u64)-1, 0, 0}; char *buf; int i; int num; int ret; int run_size = 300000; int max_key = 100000000; int tree_size = 2; struct btrfs_path path; struct btrfs_root *root; struct btrfs_trans_handle *trans; buf = malloc(512); memset(buf, 0, 512); radix_tree_init(); root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, OPEN_CTREE_WRITES); if (!root) { fprintf(stderr, "Open ctree failed\n"); exit(1); } trans = btrfs_start_transaction(root, 1); srand(55); btrfs_set_key_type(&ins, BTRFS_STRING_ITEM_KEY); for (i = 0; i < run_size; i++) { num = next_key(i, max_key); // num = i; sprintf(buf, "string-%d", num); if (i % 10000 == 0) fprintf(stderr, "insert %d:%d\n", num, i); ins.objectid = num; ins.offset = 0; ret = btrfs_insert_item(trans, root, &ins, buf, 512); if (!ret) tree_size++; if (i == run_size - 5) { btrfs_commit_transaction(trans, root); trans = btrfs_start_transaction(root, 1); } } btrfs_commit_transaction(trans, root); close_ctree(root); exit(1); root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, OPEN_CTREE_WRITES); if (!root) { fprintf(stderr, "Open ctree failed\n"); exit(1); } printf("starting search\n"); srand(55); for (i = 0; i < run_size; i++) { num = next_key(i, max_key); ins.objectid = num; btrfs_init_path(&path); if (i % 10000 == 0) fprintf(stderr, "search %d:%d\n", num, i); ret = btrfs_search_slot(NULL, root, &ins, &path, 0, 0); if (ret) { btrfs_print_tree(root, root->node, 1); printf("unable to find %d\n", num); exit(1); } btrfs_release_path(&path); } close_ctree(root); root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, OPEN_CTREE_WRITES); if (!root) { fprintf(stderr, "Open ctree failed\n"); exit(1); } printf("node %p level %d total ptrs %d free spc %lu\n", root->node, btrfs_header_level(root->node), btrfs_header_nritems(root->node), (unsigned long)BTRFS_NODEPTRS_PER_BLOCK(root) - btrfs_header_nritems(root->node)); printf("all searches good, deleting some items\n"); i = 0; srand(55); trans = btrfs_start_transaction(root, 1); for (i = 0 ; i < run_size/4; i++) { num = next_key(i, max_key); ins.objectid = num; btrfs_init_path(&path); ret = btrfs_search_slot(trans, root, &ins, &path, -1, 1); if (!ret) { if (i % 10000 == 0) fprintf(stderr, "del %d:%d\n", num, i); ret = btrfs_del_item(trans, root, &path); if (ret != 0) BUG(); tree_size--; } btrfs_release_path(&path); } btrfs_commit_transaction(trans, root); close_ctree(root); root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, OPEN_CTREE_WRITES); if (!root) { fprintf(stderr, "Open ctree failed\n"); exit(1); } trans = btrfs_start_transaction(root, 1); srand(128); for (i = 0; i < run_size; i++) { num = next_key(i, max_key); sprintf(buf, "string-%d", num); ins.objectid = num; if (i % 10000 == 0) fprintf(stderr, "insert %d:%d\n", num, i); ret = btrfs_insert_item(trans, root, &ins, buf, 512); if (!ret) tree_size++; } btrfs_commit_transaction(trans, root); close_ctree(root); root = open_ctree(av[1], BTRFS_SUPER_INFO_OFFSET, OPEN_CTREE_WRITES); if (!root) { fprintf(stderr, "Open ctree failed\n"); exit(1); } srand(128); printf("starting search2\n"); for (i = 0; i < run_size; i++) { num = next_key(i, max_key); ins.objectid = num; btrfs_init_path(&path); if (i % 10000 == 0) fprintf(stderr, "search %d:%d\n", num, i); ret = btrfs_search_slot(NULL, root, &ins, &path, 0, 0); if (ret) { btrfs_print_tree(root, root->node, 1); printf("unable to find %d\n", num); exit(1); } btrfs_release_path(&path); } printf("starting big long delete run\n"); trans = btrfs_start_transaction(root, 1); while(root->node && btrfs_header_nritems(root->node) > 0) { struct extent_buffer *leaf; int slot; ins.objectid = (u64)-1; btrfs_init_path(&path); ret = btrfs_search_slot(trans, root, &ins, &path, -1, 1); if (ret == 0) BUG(); leaf = path.nodes[0]; slot = path.slots[0]; if (slot != btrfs_header_nritems(leaf)) BUG(); while(path.slots[0] > 0) { path.slots[0] -= 1; slot = path.slots[0]; leaf = path.nodes[0]; btrfs_item_key_to_cpu(leaf, &last, slot); if (tree_size % 10000 == 0) printf("big del %d:%d\n", tree_size, i); ret = btrfs_del_item(trans, root, &path); if (ret != 0) { printf("del_item returned %d\n", ret); BUG(); } tree_size--; } btrfs_release_path(&path); } /* printf("previous tree:\n"); btrfs_print_tree(root, root->commit_root); printf("map before commit\n"); btrfs_print_tree(root->extent_root, root->extent_root->node); */ btrfs_commit_transaction(trans, root); printf("tree size is now %d\n", tree_size); printf("root %p commit root %p\n", root->node, root->commit_root); btrfs_print_tree(root, root->node, 1); close_ctree(root); return 0; }
static int remove_free_space_extent(struct btrfs_trans_handle *trans, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, u64 start, u64 size) { struct btrfs_root *root = trans->fs_info->free_space_root; struct btrfs_key key; u64 found_start, found_end; u64 end = start + size; int new_extents = -1; int ret; key.objectid = start; key.type = (u8)-1; key.offset = (u64)-1; ret = btrfs_search_prev_slot(trans, root, &key, path, -1, 1); if (ret) goto out; btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); ASSERT(key.type == BTRFS_FREE_SPACE_EXTENT_KEY); found_start = key.objectid; found_end = key.objectid + key.offset; ASSERT(start >= found_start && end <= found_end); /* * Okay, now that we've found the free space extent which contains the * free space that we are removing, there are four cases: * * 1. We're using the whole extent: delete the key we found and * decrement the free space extent count. * 2. We are using part of the extent starting at the beginning: delete * the key we found and insert a new key representing the leftover at * the end. There is no net change in the number of extents. * 3. We are using part of the extent ending at the end: delete the key * we found and insert a new key representing the leftover at the * beginning. There is no net change in the number of extents. * 4. We are using part of the extent in the middle: delete the key we * found and insert two new keys representing the leftovers on each * side. Where we used to have one extent, we now have two, so increment * the extent count. We may need to convert the block group to bitmaps * as a result. */ /* Delete the existing key (cases 1-4). */ ret = btrfs_del_item(trans, root, path); if (ret) goto out; /* Add a key for leftovers at the beginning (cases 3 and 4). */ if (start > found_start) { key.objectid = found_start; key.type = BTRFS_FREE_SPACE_EXTENT_KEY; key.offset = start - found_start; btrfs_release_path(path); ret = btrfs_insert_empty_item(trans, root, path, &key, 0); if (ret) goto out; new_extents++; } /* Add a key for leftovers at the end (cases 2 and 4). */ if (end < found_end) { key.objectid = end; key.type = BTRFS_FREE_SPACE_EXTENT_KEY; key.offset = found_end - end; btrfs_release_path(path); ret = btrfs_insert_empty_item(trans, root, path, &key, 0); if (ret) goto out; new_extents++; } btrfs_release_path(path); ret = update_free_space_extent_count(trans, block_group, path, new_extents); out: return ret; }