int btrfs_commit_transaction(struct btrfs_trans_handle *trans, struct btrfs_root *root) { u64 transid = trans->transid; int ret = 0; struct btrfs_fs_info *fs_info = root->fs_info; if (root->commit_root == root->node) goto commit_tree; free_extent_buffer(root->commit_root); root->commit_root = NULL; btrfs_set_root_bytenr(&root->root_item, root->node->start); btrfs_set_root_generation(&root->root_item, trans->transid); root->root_item.level = btrfs_header_level(root->node); ret = btrfs_update_root(trans, root->fs_info->tree_root, &root->root_key, &root->root_item); BUG_ON(ret); commit_tree: ret = commit_tree_roots(trans, fs_info); BUG_ON(ret); ret = __commit_transaction(trans, root); BUG_ON(ret); write_ctree_super(trans, root); btrfs_finish_extent_commit(trans, fs_info->extent_root, &fs_info->pinned_extents); btrfs_free_transaction(root, trans); free_extent_buffer(root->commit_root); root->commit_root = NULL; fs_info->running_transaction = NULL; fs_info->last_trans_committed = transid; return 0; }
static int commit_tree_roots(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info) { struct btrfs_root *root; struct list_head *next; struct extent_buffer *eb; int ret; if (fs_info->readonly) return 0; eb = fs_info->tree_root->node; extent_buffer_get(eb); ret = btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb); free_extent_buffer(eb); if (ret) return ret; while(!list_empty(&fs_info->dirty_cowonly_roots)) { next = fs_info->dirty_cowonly_roots.next; list_del_init(next); root = list_entry(next, struct btrfs_root, dirty_list); update_cowonly_root(trans, root); free_extent_buffer(root->commit_root); root->commit_root = NULL; } return 0; }
/* * read tree blocks and add keys where required. */ static int __add_missing_keys(struct btrfs_fs_info *fs_info, struct list_head *head) { struct list_head *pos; struct extent_buffer *eb; list_for_each(pos, head) { struct __prelim_ref *ref; ref = list_entry(pos, struct __prelim_ref, list); if (ref->parent) continue; if (ref->key_for_search.type) continue; BUG_ON(!ref->wanted_disk_byte); eb = read_tree_block(fs_info->tree_root, ref->wanted_disk_byte, fs_info->tree_root->leafsize, 0); if (!eb || !extent_buffer_uptodate(eb)) { free_extent_buffer(eb); return -EIO; } btrfs_tree_read_lock(eb); if (btrfs_header_level(eb) == 0) btrfs_item_key_to_cpu(eb, &ref->key_for_search, 0); else btrfs_node_key_to_cpu(eb, &ref->key_for_search, 0); btrfs_tree_read_unlock(eb); free_extent_buffer(eb); } return 0; }
/* * Given a list of roots that need to be deleted, call btrfs_drop_snapshot on * all of them */ int btrfs_drop_dead_root(struct btrfs_root *root) { struct btrfs_trans_handle *trans; struct btrfs_root *tree_root = root->fs_info->tree_root; unsigned long nr; int ret; while (1) { /* * we don't want to jump in and create a bunch of * delayed refs if the transaction is starting to close */ wait_transaction_pre_flush(tree_root->fs_info); trans = btrfs_start_transaction(tree_root, 1); /* * we've joined a transaction, make sure it isn't * closing right now */ if (trans->transaction->delayed_refs.flushing) { btrfs_end_transaction(trans, tree_root); continue; } ret = btrfs_drop_snapshot(trans, root); if (ret != -EAGAIN) break; ret = btrfs_update_root(trans, tree_root, &root->root_key, &root->root_item); if (ret) break; nr = trans->blocks_used; ret = btrfs_end_transaction(trans, tree_root); BUG_ON(ret); btrfs_btree_balance_dirty(tree_root, nr); cond_resched(); } BUG_ON(ret); ret = btrfs_del_root(trans, tree_root, &root->root_key); BUG_ON(ret); nr = trans->blocks_used; ret = btrfs_end_transaction(trans, tree_root); BUG_ON(ret); free_extent_buffer(root->node); free_extent_buffer(root->commit_root); kfree(root); btrfs_btree_balance_dirty(tree_root, nr); return ret; }
int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root) { if (root->node) free_extent_buffer(root->node); if (root->commit_root) free_extent_buffer(root->commit_root); kfree(root); return 0; }
int next_leaf(struct btrfs_root *root, struct btrfs_path *path) { int slot; int level = 1; struct extent_buffer *c; struct extent_buffer *next = NULL; for (; level < BTRFS_MAX_LEVEL; level++) { if (path->nodes[level]) break; } if (level == BTRFS_MAX_LEVEL) return 1; slot = path->slots[level] + 1; while(level < BTRFS_MAX_LEVEL) { if (!path->nodes[level]) return 1; slot = path->slots[level] + 1; c = path->nodes[level]; if (slot >= btrfs_header_nritems(c)) { level++; if (level == BTRFS_MAX_LEVEL) return 1; continue; } if (next) free_extent_buffer(next); if (path->reada) reada_for_search(root, path, level, slot, 0); next = read_node_slot(root, c, slot); break; } path->slots[level] = slot; while(1) { level--; c = path->nodes[level]; free_extent_buffer(c); path->nodes[level] = next; path->slots[level] = 0; if (!level) break; if (path->reada) reada_for_search(root, path, level, 0, 0); next = read_node_slot(root, next, 0); } return 0; }
/* * new snapshots need to be created at a very specific time in the * transaction commit. This does the actual creation */ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_pending_snapshot *pending) { struct btrfs_key key; struct btrfs_root_item *new_root_item; struct btrfs_root *tree_root = fs_info->tree_root; struct btrfs_root *root = pending->root; struct extent_buffer *tmp; struct extent_buffer *old; int ret; u64 objectid; new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS); if (!new_root_item) { ret = -ENOMEM; goto fail; } ret = btrfs_find_free_objectid(trans, tree_root, 0, &objectid); if (ret) goto fail; record_root_in_trans(trans, root); btrfs_set_root_last_snapshot(&root->root_item, trans->transid); memcpy(new_root_item, &root->root_item, sizeof(*new_root_item)); key.objectid = objectid; /* record when the snapshot was created in key.offset */ key.offset = trans->transid; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); old = btrfs_lock_root_node(root); btrfs_cow_block(trans, root, old, NULL, 0, &old); btrfs_set_lock_blocking(old); btrfs_copy_root(trans, root, old, &tmp, objectid); btrfs_tree_unlock(old); free_extent_buffer(old); btrfs_set_root_node(new_root_item, tmp); ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key, new_root_item); btrfs_tree_unlock(tmp); free_extent_buffer(tmp); if (ret) goto fail; key.offset = (u64)-1; memcpy(&pending->root_key, &key, sizeof(key)); fail: kfree(new_root_item); btrfs_unreserve_metadata_space(root, 6); return ret; }
static void print_extents(struct btrfs_root *root, struct extent_buffer *eb) { int i; u32 nr; u32 size; if (!eb) return; if (btrfs_is_leaf(eb)) { btrfs_print_leaf(root, eb); return; } size = btrfs_level_size(root, btrfs_header_level(eb) - 1); nr = btrfs_header_nritems(eb); for (i = 0; i < nr; i++) { struct extent_buffer *next = read_tree_block(root, btrfs_node_blockptr(eb, i), size, btrfs_node_ptr_generation(eb, i)); if (!extent_buffer_uptodate(next)) continue; if (btrfs_is_leaf(next) && btrfs_header_level(eb) != 1) BUG(); if (btrfs_header_level(next) != btrfs_header_level(eb) - 1) BUG(); print_extents(root, next); free_extent_buffer(next); } }
void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *eb, int follow) { int i; u32 nr; u32 size; struct btrfs_disk_key disk_key; struct btrfs_key key; if (!eb) return; nr = btrfs_header_nritems(eb); if (btrfs_is_leaf(eb)) { btrfs_print_leaf(root, eb); return; } printf("node %llu level %d items %d free %u generation %llu owner %llu\n", (unsigned long long)eb->start, btrfs_header_level(eb), nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr, (unsigned long long)btrfs_header_generation(eb), (unsigned long long)btrfs_header_owner(eb)); print_uuids(eb); fflush(stdout); size = btrfs_level_size(root, btrfs_header_level(eb) - 1); for (i = 0; i < nr; i++) { u64 blocknr = btrfs_node_blockptr(eb, i); btrfs_node_key(eb, &disk_key, i); btrfs_disk_key_to_cpu(&key, &disk_key); printf("\t"); btrfs_print_key(&disk_key); printf(" block %llu (%llu) gen %llu\n", (unsigned long long)blocknr, (unsigned long long)blocknr / size, (unsigned long long)btrfs_node_ptr_generation(eb, i)); fflush(stdout); } if (!follow) return; for (i = 0; i < nr; i++) { struct extent_buffer *next = read_tree_block(root, btrfs_node_blockptr(eb, i), size, btrfs_node_ptr_generation(eb, i)); if (!next) { fprintf(stderr, "failed to read %llu in tree %llu\n", (unsigned long long)btrfs_node_blockptr(eb, i), (unsigned long long)btrfs_header_owner(eb)); continue; } if (btrfs_is_leaf(next) && btrfs_header_level(eb) != 1) BUG(); if (btrfs_header_level(next) != btrfs_header_level(eb) - 1) BUG(); btrfs_print_tree(root, next, 1); free_extent_buffer(next); } }
static int change_extents_uuid(struct btrfs_fs_info *fs_info) { struct btrfs_root *root = fs_info->extent_root; struct btrfs_path path; struct btrfs_key key = {0, 0, 0}; int ret = 0; btrfs_init_path(&path); /* * Here we don't use transaction as it will takes a lot of reserve * space, and that will make a near-full btrfs unable to change uuid */ ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); if (ret < 0) goto out; while (1) { struct btrfs_extent_item *ei; struct extent_buffer *eb; u64 flags; u64 bytenr; btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); if (key.type != BTRFS_EXTENT_ITEM_KEY && key.type != BTRFS_METADATA_ITEM_KEY) goto next; ei = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_extent_item); flags = btrfs_extent_flags(path.nodes[0], ei); if (!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)) goto next; bytenr = key.objectid; eb = read_tree_block(root, bytenr, root->nodesize, 0); if (IS_ERR(eb)) { error("failed to read tree block: %llu", bytenr); ret = PTR_ERR(eb); goto out; } ret = change_header_uuid(root, eb); free_extent_buffer(eb); if (ret < 0) { error("failed to change uuid of tree block: %llu", bytenr); goto out; } next: ret = btrfs_next_item(root, &path); if (ret < 0) goto out; if (ret > 0) { ret = 0; goto out; } } out: btrfs_release_path(&path); return ret; }
/* * update all the cowonly tree roots on disk */ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans, struct btrfs_root *root) { struct btrfs_fs_info *fs_info = root->fs_info; struct list_head *next; struct extent_buffer *eb; int ret; ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1); BUG_ON(ret); eb = btrfs_lock_root_node(fs_info->tree_root); btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb); btrfs_tree_unlock(eb); free_extent_buffer(eb); ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1); BUG_ON(ret); while (!list_empty(&fs_info->dirty_cowonly_roots)) { next = fs_info->dirty_cowonly_roots.next; list_del_init(next); root = list_entry(next, struct btrfs_root, dirty_list); update_cowonly_root(trans, root); } down_write(&fs_info->extent_commit_sem); switch_commit_root(fs_info->extent_root); up_write(&fs_info->extent_commit_sem); return 0; }
static int create_data_reloc_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root) { struct btrfs_key location; struct btrfs_root_item root_item; struct extent_buffer *tmp; u64 objectid = BTRFS_DATA_RELOC_TREE_OBJECTID; int ret; ret = btrfs_copy_root(trans, root, root->node, &tmp, objectid); BUG_ON(ret); memcpy(&root_item, &root->root_item, sizeof(root_item)); btrfs_set_root_bytenr(&root_item, tmp->start); btrfs_set_root_level(&root_item, btrfs_header_level(tmp)); btrfs_set_root_generation(&root_item, trans->transid); free_extent_buffer(tmp); location.objectid = objectid; location.type = BTRFS_ROOT_ITEM_KEY; location.offset = 0; ret = btrfs_insert_root(trans, root->fs_info->tree_root, &location, &root_item); BUG_ON(ret); return 0; }
int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize, u64 parent_transid) { int ret; struct extent_buffer *eb; u64 length; struct btrfs_multi_bio *multi = NULL; struct btrfs_device *device; eb = btrfs_find_tree_block(root, bytenr, blocksize); if (eb && btrfs_buffer_uptodate(eb, parent_transid)) { free_extent_buffer(eb); return 0; } length = blocksize; ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, bytenr, &length, &multi, 0, NULL); BUG_ON(ret); device = multi->stripes[0].dev; device->total_ios++; blocksize = min(blocksize, (u32)(64 * 1024)); readahead(device->fd, multi->stripes[0].physical, blocksize); kfree(multi); return 0; }
static int iterate_inode_refs(u64 inum, struct btrfs_root *fs_root, struct btrfs_path *path, iterate_irefs_t *iterate, void *ctx) { int ret = 0; int slot; u32 cur; u32 len; u32 name_len; u64 parent = 0; int found = 0; struct extent_buffer *eb; struct btrfs_item *item; struct btrfs_inode_ref *iref; struct btrfs_key found_key; while (!ret) { ret = inode_ref_info(inum, parent ? parent+1 : 0, fs_root, path, &found_key); if (ret < 0) break; if (ret) { ret = found ? 0 : -ENOENT; break; } ++found; parent = found_key.offset; slot = path->slots[0]; eb = btrfs_clone_extent_buffer(path->nodes[0]); if (!eb) { ret = -ENOMEM; break; } extent_buffer_get(eb); btrfs_release_path(path); item = btrfs_item_nr(slot); iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); for (cur = 0; cur < btrfs_item_size(eb, item); cur += len) { name_len = btrfs_inode_ref_name_len(eb, iref); /* path must be released before calling iterate()! */ pr_debug("following ref at offset %u for inode %llu in " "tree %llu\n", cur, found_key.objectid, fs_root->objectid); ret = iterate(parent, name_len, (unsigned long)(iref + 1), eb, ctx); if (ret) break; len = sizeof(*iref) + name_len; iref = (struct btrfs_inode_ref *)((char *)iref + len); } free_extent_buffer(eb); } btrfs_release_path(path); return ret; }
static int __commit_transaction(struct btrfs_trans_handle *trans, struct btrfs_root *root) { u64 start; u64 end; struct extent_buffer *eb; struct extent_io_tree *tree = &root->fs_info->extent_cache; int ret; while(1) { ret = find_first_extent_bit(tree, 0, &start, &end, EXTENT_DIRTY); if (ret) break; while(start <= end) { eb = find_first_extent_buffer(tree, start); BUG_ON(!eb || eb->start != start); ret = write_tree_block(trans, root, eb); BUG_ON(ret); start += eb->len; clear_extent_buffer_dirty(eb); free_extent_buffer(eb); } } return 0; }
void extent_io_tree_cleanup(struct extent_io_tree *tree) { struct extent_state *es; struct extent_buffer *eb; struct cache_extent *cache; while(!list_empty(&tree->lru)) { eb = list_entry(tree->lru.next, struct extent_buffer, lru); if (eb->refs != 1) { fprintf(stderr, "extent buffer leak: " "start %llu len %u\n", (unsigned long long)eb->start, eb->len); eb->refs = 1; } free_extent_buffer(eb); } while (1) { cache = find_first_cache_extent(&tree->state, 0); if (!cache) break; es = container_of(cache, struct extent_state, cache_node); remove_cache_extent(&tree->state, &es->cache_node); free_extent_state(es); } }
static int next_leaf(struct btrfs_root *root, struct btrfs_path *path) { int slot; int level = 1; int offset = 1; struct extent_buffer *c; struct extent_buffer *next = NULL; struct btrfs_fs_info *fs_info = root->fs_info; again: for (; level < BTRFS_MAX_LEVEL; level++) { if (path->nodes[level]) break; } if (level >= BTRFS_MAX_LEVEL) return 1; slot = path->slots[level] + 1; while(level < BTRFS_MAX_LEVEL) { if (!path->nodes[level]) return 1; slot = path->slots[level] + offset; c = path->nodes[level]; if (slot >= btrfs_header_nritems(c)) { level++; if (level == BTRFS_MAX_LEVEL) return 1; offset = 1; continue; } if (path->reada) reada_for_search(fs_info, path, level, slot, 0); next = read_node_slot(fs_info, c, slot); if (extent_buffer_uptodate(next)) break; offset++; } path->slots[level] = slot; while(1) { level--; c = path->nodes[level]; free_extent_buffer(c); path->nodes[level] = next; path->slots[level] = 0; if (!level) break; if (path->reada) reada_for_search(fs_info, path, level, 0, 0); next = read_node_slot(fs_info, next, 0); if (!extent_buffer_uptodate(next)) goto again; } return 0; }
/* * this iterates to turn a name (from iref/extref) into a full filesystem path. * Elements of the path are separated by '/' and the path is guaranteed to be * 0-terminated. the path is only given within the current file system. * Therefore, it never starts with a '/'. the caller is responsible to provide * "size" bytes in "dest". the dest buffer will be filled backwards. finally, * the start point of the resulting string is returned. this pointer is within * dest, normally. * in case the path buffer would overflow, the pointer is decremented further * as if output was written to the buffer, though no more output is actually * generated. that way, the caller can determine how much space would be * required for the path to fit into the buffer. in that case, the returned * value will be smaller than dest. callers must check this! */ char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, u32 name_len, unsigned long name_off, struct extent_buffer *eb_in, u64 parent, char *dest, u32 size) { int slot; u64 next_inum; int ret; s64 bytes_left = ((s64)size) - 1; struct extent_buffer *eb = eb_in; struct btrfs_key found_key; struct btrfs_inode_ref *iref; if (bytes_left >= 0) dest[bytes_left] = '\0'; while (1) { bytes_left -= name_len; if (bytes_left >= 0) read_extent_buffer(eb, dest + bytes_left, name_off, name_len); if (eb != eb_in) free_extent_buffer(eb); ret = inode_ref_info(parent, 0, fs_root, path, &found_key); if (ret > 0) ret = -ENOENT; if (ret) break; next_inum = found_key.offset; /* regular exit ahead */ if (parent == next_inum) break; slot = path->slots[0]; eb = path->nodes[0]; /* make sure we can use eb after releasing the path */ if (eb != eb_in) eb->refs++; btrfs_release_path(path); iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); name_len = btrfs_inode_ref_name_len(eb, iref); name_off = (unsigned long)(iref + 1); parent = next_inum; --bytes_left; if (bytes_left >= 0) dest[bytes_left] = '/'; } btrfs_release_path(path); if (ret) return ERR_PTR(ret); return dest + bytes_left; }
int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info) { struct btrfs_trans_handle *trans; struct btrfs_root *tree_root = fs_info->tree_root; struct btrfs_root *free_space_root = fs_info->free_space_root; int ret; trans = btrfs_start_transaction(tree_root, 0); if (IS_ERR(trans)) return PTR_ERR(trans); btrfs_clear_fs_compat_ro(fs_info, FREE_SPACE_TREE); fs_info->free_space_root = NULL; ret = clear_free_space_tree(trans, free_space_root); if (ret) goto abort; ret = btrfs_del_root(trans, tree_root, &free_space_root->root_key); if (ret) goto abort; list_del(&free_space_root->dirty_list); btrfs_tree_lock(free_space_root->node); clean_tree_block(trans, tree_root->fs_info, free_space_root->node); btrfs_tree_unlock(free_space_root->node); btrfs_free_tree_block(trans, free_space_root, free_space_root->node, 0, 1); free_extent_buffer(free_space_root->node); free_extent_buffer(free_space_root->commit_root); kfree(free_space_root); ret = btrfs_commit_transaction(trans, tree_root); if (ret) return ret; return 0; abort: btrfs_abort_transaction(trans, ret); btrfs_end_transaction(trans, tree_root); return ret; }
void btrfs_print_tree(struct btrfs_fs_info *fs_info, struct extent_buffer *c) { int i; u32 nr; struct btrfs_key key; int level; if (!c) return; nr = btrfs_header_nritems(c); level = btrfs_header_level(c); if (level == 0) { btrfs_print_leaf(fs_info, c); return; } btrfs_info(fs_info, "node %llu level %d total ptrs %d free spc %u", btrfs_header_bytenr(c), level, nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(fs_info) - nr); for (i = 0; i < nr; i++) { btrfs_node_key_to_cpu(c, &key, i); pr_info("\tkey %d (%llu %u %llu) block %llu\n", i, key.objectid, key.type, key.offset, btrfs_node_blockptr(c, i)); } for (i = 0; i < nr; i++) { struct extent_buffer *next = read_tree_block(fs_info, btrfs_node_blockptr(c, i), btrfs_node_ptr_generation(c, i)); if (IS_ERR(next)) { continue; } else if (!extent_buffer_uptodate(next)) { free_extent_buffer(next); continue; } if (btrfs_is_leaf(next) && level != 1) BUG(); if (btrfs_header_level(next) != level - 1) BUG(); btrfs_print_tree(fs_info, next); free_extent_buffer(next); } }
static struct btrfs_root *open_ctree_broken(int fd, const char *device) { struct btrfs_fs_info *fs_info; struct btrfs_super_block *disk_super; struct btrfs_fs_devices *fs_devices = NULL; struct extent_buffer *eb; int ret; fs_info = btrfs_new_fs_info(0, BTRFS_SUPER_INFO_OFFSET); if (!fs_info) { fprintf(stderr, "Failed to allocate memory for fs_info\n"); return NULL; } ret = btrfs_scan_fs_devices(fd, device, &fs_devices, 0, 1); if (ret) goto out; fs_info->fs_devices = fs_devices; ret = btrfs_open_devices(fs_devices, O_RDONLY); if (ret) goto out_devices; disk_super = fs_info->super_copy; ret = btrfs_read_dev_super(fs_devices->latest_bdev, disk_super, fs_info->super_bytenr, 1); if (ret) { printk("No valid btrfs found\n"); goto out_devices; } memcpy(fs_info->fsid, &disk_super->fsid, BTRFS_FSID_SIZE); ret = btrfs_check_fs_compatibility(disk_super, 0); if (ret) goto out_devices; ret = btrfs_setup_chunk_tree_and_device_map(fs_info); if (ret) goto out_chunk; eb = fs_info->chunk_root->node; read_extent_buffer(eb, fs_info->chunk_tree_uuid, btrfs_header_chunk_tree_uuid(eb), BTRFS_UUID_SIZE); return fs_info->chunk_root; out_chunk: free_extent_buffer(fs_info->chunk_root->node); btrfs_cleanup_all_caches(fs_info); out_devices: btrfs_close_devices(fs_info->fs_devices); out: btrfs_free_fs_info(fs_info); return NULL; }
int commit_tree_roots(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info) { struct btrfs_root *root; struct list_head *next; struct extent_buffer *eb; int ret; if (fs_info->readonly) return 0; eb = fs_info->tree_root->node; extent_buffer_get(eb); ret = btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb); free_extent_buffer(eb); if (ret) return ret; /* * If the above CoW is the first one to dirty the current tree_root, * delayed refs for it won't be run until after this function has * finished executing, meaning we won't process the extent tree root, * which will have been added to ->dirty_cowonly_roots. So run * delayed refs here as well. */ ret = btrfs_run_delayed_refs(trans, -1); if (ret) return ret; while(!list_empty(&fs_info->dirty_cowonly_roots)) { next = fs_info->dirty_cowonly_roots.next; list_del_init(next); root = list_entry(next, struct btrfs_root, dirty_list); ret = update_cowonly_root(trans, root); free_extent_buffer(root->commit_root); root->commit_root = NULL; if (ret < 0) return ret; } return 0; }
struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize, u64 parent_transid) { int ret; struct extent_buffer *eb; u64 best_transid = 0; int mirror_num = 0; int good_mirror = 0; int num_copies; int ignore = 0; eb = btrfs_find_create_tree_block(root, bytenr, blocksize); if (!eb) return NULL; if (btrfs_buffer_uptodate(eb, parent_transid)) return eb; while (1) { ret = read_whole_eb(root->fs_info, eb, mirror_num); if (ret == 0 && check_tree_block(root, eb) == 0 && csum_tree_block(root, eb, 1) == 0 && verify_parent_transid(eb->tree, eb, parent_transid, ignore) == 0) { btrfs_set_buffer_uptodate(eb); return eb; } if (ignore) { if (check_tree_block(root, eb)) printk("read block failed check_tree_block\n"); else printk("Csum didn't match\n"); break; } num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, eb->start, eb->len); if (num_copies == 1) { ignore = 1; continue; } if (btrfs_header_generation(eb) > best_transid) { best_transid = btrfs_header_generation(eb); good_mirror = mirror_num; } mirror_num++; if (mirror_num > num_copies) { mirror_num = good_mirror; ignore = 1; continue; } } free_extent_buffer(eb); return NULL; }
static void __recow_root(struct btrfs_trans_handle *trans, struct btrfs_root *root) { int ret; struct extent_buffer *tmp; if (trans->transid != btrfs_root_generation(&root->root_item)) { ret = __btrfs_cow_block(trans, root, root->node, NULL, 0, &tmp, 0, 0); BUG_ON(ret); free_extent_buffer(tmp); } }
void extent_io_tree_cleanup(struct extent_io_tree *tree) { struct extent_buffer *eb; while(!list_empty(&tree->lru)) { eb = list_entry(tree->lru.next, struct extent_buffer, lru); fprintf(stderr, "extent buffer leak: " "start %llu len %u\n", (unsigned long long)eb->start, eb->len); free_extent_buffer(eb); } cache_tree_free_extents(&tree->state, free_extent_state_func); }
struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize, u64 parent_transid) { int ret; int dev_nr; struct extent_buffer *eb; u64 length; struct btrfs_multi_bio *multi = NULL; struct btrfs_device *device; int mirror_num = 0; int num_copies; eb = btrfs_find_create_tree_block(root, bytenr, blocksize); if (!eb) return NULL; if (btrfs_buffer_uptodate(eb, parent_transid)) return eb; dev_nr = 0; length = blocksize; while (1) { ret = btrfs_map_block(&root->fs_info->mapping_tree, READ, eb->start, &length, &multi, mirror_num); BUG_ON(ret); device = multi->stripes[0].dev; eb->fd = device->fd; device->total_ios++; eb->dev_bytenr = multi->stripes[0].physical; kfree(multi); ret = read_extent_from_disk(eb); if (ret == 0 && check_tree_block(root, eb) == 0 && csum_tree_block(root, eb, 1) == 0 && verify_parent_transid(eb->tree, eb, parent_transid) == 0) { btrfs_set_buffer_uptodate(eb); return eb; } num_copies = btrfs_num_copies(&root->fs_info->mapping_tree, eb->start, eb->len); if (num_copies == 1) { break; } mirror_num++; if (mirror_num > num_copies) { break; } } free_extent_buffer(eb); return NULL; }
void btrfs_print_tree(struct btrfs_root *root, struct extent_buffer *c) { int i; u32 nr; struct btrfs_key key; int level; if (!c) return; nr = btrfs_header_nritems(c); level = btrfs_header_level(c); if (level == 0) { btrfs_print_leaf(root, c); return; } printk(KERN_INFO "node %llu level %d total ptrs %d free spc %u\n", (unsigned long long)btrfs_header_bytenr(c), level, nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(root) - nr); for (i = 0; i < nr; i++) { btrfs_node_key_to_cpu(c, &key, i); printk(KERN_INFO "\tkey %d (%llu %u %llu) block %llu\n", i, (unsigned long long)key.objectid, key.type, (unsigned long long)key.offset, (unsigned long long)btrfs_node_blockptr(c, i)); } for (i = 0; i < nr; i++) { struct extent_buffer *next = read_tree_block(root, btrfs_node_blockptr(c, i), btrfs_level_size(root, level - 1), btrfs_node_ptr_generation(c, i)); if (btrfs_is_leaf(next) && level != 1) BUG(); if (btrfs_header_level(next) != level - 1) BUG(); btrfs_print_tree(root, next); free_extent_buffer(next); } }
static int find_and_setup_log_root(struct btrfs_root *tree_root, struct btrfs_fs_info *fs_info, struct btrfs_super_block *disk_super) { u32 blocksize; u64 blocknr = btrfs_super_log_root(disk_super); struct btrfs_root *log_root = malloc(sizeof(struct btrfs_root)); if (!log_root) return -ENOMEM; if (blocknr == 0) { free(log_root); return 0; } blocksize = btrfs_level_size(tree_root, btrfs_super_log_root_level(disk_super)); __setup_root(tree_root->nodesize, tree_root->leafsize, tree_root->sectorsize, tree_root->stripesize, log_root, fs_info, BTRFS_TREE_LOG_OBJECTID); log_root->node = read_tree_block(tree_root, blocknr, blocksize, btrfs_super_generation(disk_super) + 1); fs_info->log_root_tree = log_root; if (!extent_buffer_uptodate(log_root->node)) { free_extent_buffer(log_root->node); free(log_root); fs_info->log_root_tree = NULL; return -EIO; } return 0; }
static int recow_roots(struct btrfs_trans_handle *trans, struct btrfs_root *root) { int ret; struct extent_buffer *tmp; struct btrfs_fs_info *info = root->fs_info; ret = __btrfs_cow_block(trans, info->fs_root, info->fs_root->node, NULL, 0, &tmp, 0, 0); BUG_ON(ret); free_extent_buffer(tmp); ret = __btrfs_cow_block(trans, info->tree_root, info->tree_root->node, NULL, 0, &tmp, 0, 0); BUG_ON(ret); free_extent_buffer(tmp); ret = __btrfs_cow_block(trans, info->extent_root, info->extent_root->node, NULL, 0, &tmp, 0, 0); BUG_ON(ret); free_extent_buffer(tmp); ret = __btrfs_cow_block(trans, info->chunk_root, info->chunk_root->node, NULL, 0, &tmp, 0, 0); BUG_ON(ret); free_extent_buffer(tmp); ret = __btrfs_cow_block(trans, info->dev_root, info->dev_root->node, NULL, 0, &tmp, 0, 0); BUG_ON(ret); free_extent_buffer(tmp); ret = __btrfs_cow_block(trans, info->csum_root, info->csum_root->node, NULL, 0, &tmp, 0, 0); BUG_ON(ret); free_extent_buffer(tmp); return 0; }
static noinline void switch_commit_root(struct btrfs_root *root) { free_extent_buffer(root->commit_root); root->commit_root = btrfs_root_node(root); }