static struct btrfs_trans_handle *start_transaction(struct btrfs_root *root, u64 num_items, int type) { struct btrfs_trans_handle *h; struct btrfs_transaction *cur_trans; int retries = 0; int ret; again: h = kmem_cache_alloc(btrfs_trans_handle_cachep, GFP_NOFS); if (!h) return ERR_PTR(-ENOMEM); mutex_lock(&root->fs_info->trans_mutex); if (may_wait_transaction(root, type)) wait_current_trans(root); ret = join_transaction(root); BUG_ON(ret); cur_trans = root->fs_info->running_transaction; cur_trans->use_count++; mutex_unlock(&root->fs_info->trans_mutex); h->transid = cur_trans->transid; h->transaction = cur_trans; h->blocks_used = 0; h->block_group = 0; h->bytes_reserved = 0; h->delayed_ref_updates = 0; h->block_rsv = NULL; smp_mb(); if (cur_trans->blocked && may_wait_transaction(root, type)) { btrfs_commit_transaction(h, root); goto again; } if (num_items > 0) { ret = btrfs_trans_reserve_metadata(h, root, num_items, &retries); if (ret == -EAGAIN) { btrfs_commit_transaction(h, root); goto again; } if (ret < 0) { btrfs_end_transaction(h, root); return ERR_PTR(ret); } } mutex_lock(&root->fs_info->trans_mutex); record_root_in_trans(h, root); mutex_unlock(&root->fs_info->trans_mutex); if (!current->journal_info && type != TRANS_USERSPACE) current->journal_info = h; return h; }
static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, struct btrfs_root *root, int throttle) { struct btrfs_transaction *cur_trans = trans->transaction; struct btrfs_fs_info *info = root->fs_info; int count = 0; while (count < 4) { unsigned long cur = trans->delayed_ref_updates; trans->delayed_ref_updates = 0; if (cur && trans->transaction->delayed_refs.num_heads_ready > 64) { trans->delayed_ref_updates = 0; /* * do a full flush if the transaction is trying * to close */ if (trans->transaction->delayed_refs.flushing) cur = 0; btrfs_run_delayed_refs(trans, root, cur); } else { break; } count++; } btrfs_trans_release_metadata(trans, root); if (!root->fs_info->open_ioctl_trans && should_end_transaction(trans, root)) trans->transaction->blocked = 1; if (cur_trans->blocked && !cur_trans->in_commit) { if (throttle) return btrfs_commit_transaction(trans, root); else wake_up_process(info->transaction_kthread); } mutex_lock(&info->trans_mutex); WARN_ON(cur_trans != info->running_transaction); WARN_ON(cur_trans->num_writers < 1); cur_trans->num_writers--; if (waitqueue_active(&cur_trans->writer_wait)) wake_up(&cur_trans->writer_wait); put_transaction(cur_trans); mutex_unlock(&info->trans_mutex); if (current->journal_info == trans) current->journal_info = NULL; memset(trans, 0, sizeof(*trans)); kmem_cache_free(btrfs_trans_handle_cachep, trans); if (throttle) btrfs_run_delayed_iputs(root); return 0; }
static int update_seeding_flag(struct btrfs_root *root, int set_flag) { struct btrfs_trans_handle *trans; struct btrfs_super_block *disk_super; u64 super_flags; int ret; disk_super = root->fs_info->super_copy; super_flags = btrfs_super_flags(disk_super); if (set_flag) { if (super_flags & BTRFS_SUPER_FLAG_SEEDING) { if (force) return 0; else warning("seeding flag is already set on %s", device); return 1; } super_flags |= BTRFS_SUPER_FLAG_SEEDING; } else { if (!(super_flags & BTRFS_SUPER_FLAG_SEEDING)) { warning("seeding flag is not set on %s", device); return 1; } super_flags &= ~BTRFS_SUPER_FLAG_SEEDING; warning("seeding flag cleared on %s", device); } trans = btrfs_start_transaction(root, 1); btrfs_set_super_flags(disk_super, super_flags); ret = btrfs_commit_transaction(trans, root); return ret; }
int update_seeding_flag(struct btrfs_root *root, int set_flag) { struct btrfs_trans_handle *trans; struct btrfs_super_block *disk_super; u64 super_flags; disk_super = root->fs_info->super_copy; super_flags = btrfs_super_flags(disk_super); if (set_flag) { if (super_flags & BTRFS_SUPER_FLAG_SEEDING) { fprintf(stderr, "seeding flag is already set on %s\n", device); return 1; } super_flags |= BTRFS_SUPER_FLAG_SEEDING; } else { if (!(super_flags & BTRFS_SUPER_FLAG_SEEDING)) { fprintf(stderr, "seeding flag is not set on %s\n", device); return 1; } super_flags &= ~BTRFS_SUPER_FLAG_SEEDING; } trans = btrfs_start_transaction(root, 1); btrfs_set_super_flags(disk_super, super_flags); btrfs_commit_transaction(trans, root); return 0; }
int main(int ac, char **av) { struct btrfs_root *root; struct btrfs_trans_handle *trans; int ret; if (ac != 2) print_usage(); radix_tree_init(); if((ret = check_mounted(av[1])) < 0) { fprintf(stderr, "Could not check mount status: %s\n", strerror(-ret)); goto out; } else if(ret) { fprintf(stderr, "%s is currently mounted. Aborting.\n", av[1]); ret = -EBUSY; goto out; } root = open_ctree(av[1], 0, OPEN_CTREE_WRITES); if (root == NULL) return 1; trans = btrfs_start_transaction(root, 1); btrfs_set_super_log_root(root->fs_info->super_copy, 0); btrfs_set_super_log_root_level(root->fs_info->super_copy, 0); btrfs_commit_transaction(trans, root); close_ctree(root); out: return !!ret; }
static int create_snapshot(struct btrfs_root *root, struct dentry *dentry, char *name, int namelen) { struct inode *inode; struct btrfs_pending_snapshot *pending_snapshot; struct btrfs_trans_handle *trans; int ret; if (!root->ref_cows) return -EINVAL; /* * 1 - inode item * 2 - refs * 1 - root item * 2 - dir items */ ret = btrfs_reserve_metadata_space(root, 6); if (ret) goto fail; pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_NOFS); if (!pending_snapshot) { ret = -ENOMEM; btrfs_unreserve_metadata_space(root, 6); goto fail; } pending_snapshot->name = kmalloc(namelen + 1, GFP_NOFS); if (!pending_snapshot->name) { ret = -ENOMEM; kfree(pending_snapshot); btrfs_unreserve_metadata_space(root, 6); goto fail; } memcpy(pending_snapshot->name, name, namelen); pending_snapshot->name[namelen] = '\0'; pending_snapshot->dentry = dentry; trans = btrfs_start_transaction(root, 1); BUG_ON(!trans); pending_snapshot->root = root; list_add(&pending_snapshot->list, &trans->transaction->pending_snapshots); ret = btrfs_commit_transaction(trans, root); BUG_ON(ret); btrfs_unreserve_metadata_space(root, 6); inode = btrfs_lookup_dentry(dentry->d_parent->d_inode, dentry); if (IS_ERR(inode)) { ret = PTR_ERR(inode); goto fail; } BUG_ON(!inode); d_instantiate(dentry, inode); ret = 0; fail: return ret; }
static int create_snapshot(struct btrfs_root *root, struct dentry *dentry) { struct inode *inode; struct btrfs_pending_snapshot *pending_snapshot; struct btrfs_trans_handle *trans; int ret; if (!root->ref_cows) return -EINVAL; pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_NOFS); if (!pending_snapshot) return -ENOMEM; btrfs_init_block_rsv(&pending_snapshot->block_rsv); pending_snapshot->dentry = dentry; pending_snapshot->root = root; trans = btrfs_start_transaction(root->fs_info->extent_root, 5); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto fail; } ret = btrfs_snap_reserve_metadata(trans, pending_snapshot); BUG_ON(ret); list_add(&pending_snapshot->list, &trans->transaction->pending_snapshots); ret = btrfs_commit_transaction(trans, root->fs_info->extent_root); BUG_ON(ret); ret = pending_snapshot->error; if (ret) goto fail; btrfs_orphan_cleanup(pending_snapshot->snap); inode = btrfs_lookup_dentry(dentry->d_parent->d_inode, dentry); if (IS_ERR(inode)) { ret = PTR_ERR(inode); goto fail; } BUG_ON(!inode); d_instantiate(dentry, inode); ret = 0; fail: kfree(pending_snapshot); return ret; }
static u64 __btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info) { struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; struct btrfs_device *tgt_device = NULL; struct btrfs_trans_handle *trans; struct btrfs_root *root = fs_info->tree_root; u64 result; int ret; if (fs_info->sb->s_flags & MS_RDONLY) return -EROFS; mutex_lock(&dev_replace->lock_finishing_cancel_unmount); btrfs_dev_replace_lock(dev_replace); switch (dev_replace->replace_state) { case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED: case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED: result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED; btrfs_dev_replace_unlock(dev_replace); goto leave; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED: result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR; tgt_device = dev_replace->tgtdev; dev_replace->tgtdev = NULL; dev_replace->srcdev = NULL; break; } dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED; dev_replace->time_stopped = get_seconds(); dev_replace->item_needs_writeback = 1; btrfs_dev_replace_unlock(dev_replace); btrfs_scrub_cancel(fs_info); trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return PTR_ERR(trans); } ret = btrfs_commit_transaction(trans, root); WARN_ON(ret); if (tgt_device) btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device); leave: mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return result; }
int enable_extrefs_flag(struct btrfs_root *root) { struct btrfs_trans_handle *trans; struct btrfs_super_block *disk_super; u64 super_flags; disk_super = root->fs_info->super_copy; super_flags = btrfs_super_incompat_flags(disk_super); super_flags |= BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF; trans = btrfs_start_transaction(root, 1); btrfs_set_super_incompat_flags(disk_super, super_flags); btrfs_commit_transaction(trans, root); return 0; }
static int set_super_incompat_flags(struct btrfs_root *root, u64 flags) { struct btrfs_trans_handle *trans; struct btrfs_super_block *disk_super; u64 super_flags; int ret; disk_super = root->fs_info->super_copy; super_flags = btrfs_super_incompat_flags(disk_super); super_flags |= flags; trans = btrfs_start_transaction(root, 1); btrfs_set_super_incompat_flags(disk_super, super_flags); ret = btrfs_commit_transaction(trans, root); return ret; }
int btrfs_create_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; struct btrfs_block_group_cache *block_group; struct rb_node *node; int ret; trans = btrfs_start_transaction(tree_root, 0); if (IS_ERR(trans)) return PTR_ERR(trans); fs_info->creating_free_space_tree = 1; free_space_root = btrfs_create_tree(trans, fs_info, BTRFS_FREE_SPACE_TREE_OBJECTID); if (IS_ERR(free_space_root)) { ret = PTR_ERR(free_space_root); goto abort; } fs_info->free_space_root = free_space_root; node = rb_first(&fs_info->block_group_cache_tree); while (node) { block_group = rb_entry(node, struct btrfs_block_group_cache, cache_node); ret = populate_free_space_tree(trans, fs_info, block_group); if (ret) goto abort; node = rb_next(node); } btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE); fs_info->creating_free_space_tree = 0; ret = btrfs_commit_transaction(trans, tree_root); if (ret) return ret; return 0; abort: fs_info->creating_free_space_tree = 0; btrfs_abort_transaction(trans, ret); btrfs_end_transaction(trans, tree_root); return ret; }
static int cmd_rescue_zero_log(int argc, char **argv) { struct btrfs_root *root; struct btrfs_trans_handle *trans; struct btrfs_super_block *sb; char *devname; int ret; clean_args_no_options(argc, argv, cmd_rescue_zero_log_usage); if (check_argc_exact(argc, 2)) usage(cmd_rescue_zero_log_usage); devname = argv[optind]; ret = check_mounted(devname); if (ret < 0) { errno = -ret; error("could not check mount status: %m"); goto out; } else if (ret) { error("%s is currently mounted", devname); ret = -EBUSY; goto out; } root = open_ctree(devname, 0, OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL); if (!root) { error("could not open ctree"); return 1; } sb = root->fs_info->super_copy; printf("Clearing log on %s, previous log_root %llu, level %u\n", devname, (unsigned long long)btrfs_super_log_root(sb), (unsigned)btrfs_super_log_root_level(sb)); trans = btrfs_start_transaction(root, 1); BUG_ON(IS_ERR(trans)); btrfs_set_super_log_root(sb, 0); btrfs_set_super_log_root_level(sb, 0); btrfs_commit_transaction(trans, root); close_ctree(root); out: return !!ret; }
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; }
int main(int ac, char **av) { struct btrfs_root *root; struct btrfs_trans_handle *trans; struct btrfs_super_block *sb; int ret; set_argv0(av); if (check_argc_exact(ac, 2)) print_usage(); radix_tree_init(); printf("WARNING: this utility is deprecated, please use 'btrfs rescue zero-log'\n\n"); if ((ret = check_mounted(av[1])) < 0) { fprintf(stderr, "ERROR: could not check mount status: %s\n", strerror(-ret)); goto out; } else if (ret) { fprintf(stderr, "ERROR: %s is currently mounted\n", av[1]); ret = -EBUSY; goto out; } root = open_ctree(av[1], 0, OPEN_CTREE_WRITES | OPEN_CTREE_PARTIAL); if (!root) { fprintf(stderr, "ERROR: cannot open ctree\n"); return 1; } sb = root->fs_info->super_copy; printf("Clearing log on %s, previous log_root %llu, level %u\n", av[1], (unsigned long long)btrfs_super_log_root(sb), (unsigned)btrfs_super_log_root_level(sb)); trans = btrfs_start_transaction(root, 1); btrfs_set_super_log_root(root->fs_info->super_copy, 0); btrfs_set_super_log_root_level(root->fs_info->super_copy, 0); btrfs_commit_transaction(trans, root); close_ctree(root); out: return !!ret; }
static void change_label_unmounted(char *dev, char *nLabel) { struct btrfs_root *root; struct btrfs_trans_handle *trans; /* Open the super_block at the default location * and as read-write. */ root = open_ctree(dev, 0, 1); if (!root) /* errors are printed by open_ctree() */ return; trans = btrfs_start_transaction(root, 1); strncpy(root->fs_info->super_copy.label, nLabel, BTRFS_LABEL_SIZE); root->fs_info->super_copy.label[BTRFS_LABEL_SIZE-1] = 0; btrfs_commit_transaction(trans, root); /* Now we close it since we are done. */ close_ctree(root); }
static int make_image(char *source_dir, struct btrfs_root *root, int out_fd) { int ret; struct btrfs_trans_handle *trans; struct stat root_st; struct directory_name_entry dir_head; struct directory_name_entry *dir_entry = NULL; ret = lstat(source_dir, &root_st); if (ret) { fprintf(stderr, "unable to lstat the %s\n", source_dir); goto fail; } INIT_LIST_HEAD(&dir_head.list); trans = btrfs_start_transaction(root, 1); ret = traverse_directory(trans, root, source_dir, &dir_head, out_fd); if (ret) { fprintf(stderr, "unable to traverse_directory\n"); goto fail; } btrfs_commit_transaction(trans, root); printf("Making image is completed.\n"); return 0; fail: while (!list_empty(&dir_head.list)) { dir_entry = list_entry(dir_head.list.next, struct directory_name_entry, list); list_del(&dir_entry->list); free(dir_entry); } fprintf(stderr, "Making image is aborted.\n"); return -1; }
int btrfs_sync_fs(struct super_block *sb, int wait) { struct btrfs_trans_handle *trans; struct btrfs_root *root; int ret; root = btrfs_sb(sb); if (sb->s_flags & MS_RDONLY) return 0; sb->s_dirt = 0; if (!wait) { filemap_flush(root->fs_info->btree_inode->i_mapping); return 0; } btrfs_start_delalloc_inodes(root); btrfs_wait_ordered_extents(root, 0); trans = btrfs_start_transaction(root, 1); ret = btrfs_commit_transaction(trans, root); sb->s_dirt = 0; return ret; }
static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, int scrub_ret) { struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; struct btrfs_device *tgt_device; struct btrfs_device *src_device; struct btrfs_root *root = fs_info->tree_root; u8 uuid_tmp[BTRFS_UUID_SIZE]; struct btrfs_trans_handle *trans; int ret = 0; /* don't allow cancel or unmount to disturb the finishing procedure */ mutex_lock(&dev_replace->lock_finishing_cancel_unmount); down_read(&dev_replace->rwsem); /* was the operation canceled, or is it finished? */ if (dev_replace->replace_state != BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) { up_read(&dev_replace->rwsem); mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return 0; } tgt_device = dev_replace->tgtdev; src_device = dev_replace->srcdev; up_read(&dev_replace->rwsem); /* * flush all outstanding I/O and inode extent mappings before the * copy operation is declared as being finished */ ret = btrfs_start_delalloc_roots(fs_info, -1); if (ret) { mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return ret; } btrfs_wait_ordered_roots(fs_info, U64_MAX, 0, (u64)-1); trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return PTR_ERR(trans); } ret = btrfs_commit_transaction(trans); WARN_ON(ret); /* keep away write_all_supers() during the finishing procedure */ mutex_lock(&fs_info->fs_devices->device_list_mutex); mutex_lock(&fs_info->chunk_mutex); down_write(&dev_replace->rwsem); dev_replace->replace_state = scrub_ret ? BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED : BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED; dev_replace->tgtdev = NULL; dev_replace->srcdev = NULL; dev_replace->time_stopped = ktime_get_real_seconds(); dev_replace->item_needs_writeback = 1; /* replace old device with new one in mapping tree */ if (!scrub_ret) { btrfs_dev_replace_update_device_in_mapping_tree(fs_info, src_device, tgt_device); } else { if (scrub_ret != -ECANCELED) btrfs_err_in_rcu(fs_info, "btrfs_scrub_dev(%s, %llu, %s) failed %d", btrfs_dev_name(src_device), src_device->devid, rcu_str_deref(tgt_device->name), scrub_ret); up_write(&dev_replace->rwsem); mutex_unlock(&fs_info->chunk_mutex); mutex_unlock(&fs_info->fs_devices->device_list_mutex); btrfs_rm_dev_replace_blocked(fs_info); if (tgt_device) btrfs_destroy_dev_replace_tgtdev(tgt_device); btrfs_rm_dev_replace_unblocked(fs_info); mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return scrub_ret; } btrfs_info_in_rcu(fs_info, "dev_replace from %s (devid %llu) to %s finished", btrfs_dev_name(src_device), src_device->devid, rcu_str_deref(tgt_device->name)); clear_bit(BTRFS_DEV_STATE_REPLACE_TGT, &tgt_device->dev_state); tgt_device->devid = src_device->devid; src_device->devid = BTRFS_DEV_REPLACE_DEVID; memcpy(uuid_tmp, tgt_device->uuid, sizeof(uuid_tmp)); memcpy(tgt_device->uuid, src_device->uuid, sizeof(tgt_device->uuid)); memcpy(src_device->uuid, uuid_tmp, sizeof(src_device->uuid)); btrfs_device_set_total_bytes(tgt_device, src_device->total_bytes); btrfs_device_set_disk_total_bytes(tgt_device, src_device->disk_total_bytes); btrfs_device_set_bytes_used(tgt_device, src_device->bytes_used); ASSERT(list_empty(&src_device->resized_list)); tgt_device->commit_total_bytes = src_device->commit_total_bytes; tgt_device->commit_bytes_used = src_device->bytes_used; btrfs_assign_next_active_device(src_device, tgt_device); list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list); fs_info->fs_devices->rw_devices++; up_write(&dev_replace->rwsem); btrfs_rm_dev_replace_blocked(fs_info); btrfs_rm_dev_replace_remove_srcdev(src_device); btrfs_rm_dev_replace_unblocked(fs_info); /* * Increment dev_stats_ccnt so that btrfs_run_dev_stats() will * update on-disk dev stats value during commit transaction */ atomic_inc(&tgt_device->dev_stats_ccnt); /* * this is again a consistent state where no dev_replace procedure * is running, the target device is part of the filesystem, the * source device is not part of the filesystem anymore and its 1st * superblock is scratched out so that it is no longer marked to * belong to this filesystem. */ mutex_unlock(&fs_info->chunk_mutex); mutex_unlock(&fs_info->fs_devices->device_list_mutex); /* replace the sysfs entry */ btrfs_sysfs_rm_device_link(fs_info->fs_devices, src_device); btrfs_rm_dev_replace_free_srcdev(fs_info, src_device); /* write back the superblocks */ trans = btrfs_start_transaction(root, 0); if (!IS_ERR(trans)) btrfs_commit_transaction(trans); mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return 0; }
int btrfs_dev_replace_start(struct btrfs_root *root, char *tgtdev_name, u64 srcdevid, char *srcdev_name, int read_src) { struct btrfs_trans_handle *trans; struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; int ret; struct btrfs_device *tgt_device = NULL; struct btrfs_device *src_device = NULL; /* the disk copy procedure reuses the scrub code */ mutex_lock(&fs_info->volume_mutex); ret = btrfs_find_device_by_devspec(root, srcdevid, srcdev_name, &src_device); if (ret) { mutex_unlock(&fs_info->volume_mutex); return ret; } ret = btrfs_init_dev_replace_tgtdev(root, tgtdev_name, src_device, &tgt_device); mutex_unlock(&fs_info->volume_mutex); if (ret) return ret; /* * Here we commit the transaction to make sure commit_total_bytes * of all the devices are updated. */ trans = btrfs_attach_transaction(root); if (!IS_ERR(trans)) { ret = btrfs_commit_transaction(trans, root); if (ret) return ret; } else if (PTR_ERR(trans) != -ENOENT) { return PTR_ERR(trans); } btrfs_dev_replace_lock(dev_replace, 1); switch (dev_replace->replace_state) { case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED: case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED: break; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED: ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED; goto leave; } dev_replace->cont_reading_from_srcdev_mode = read_src; WARN_ON(!src_device); dev_replace->srcdev = src_device; WARN_ON(!tgt_device); dev_replace->tgtdev = tgt_device; btrfs_info_in_rcu(fs_info, "dev_replace from %s (devid %llu) to %s started", src_device->missing ? "<missing disk>" : rcu_str_deref(src_device->name), src_device->devid, rcu_str_deref(tgt_device->name)); /* * from now on, the writes to the srcdev are all duplicated to * go to the tgtdev as well (refer to btrfs_map_block()). */ dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED; dev_replace->time_started = get_seconds(); dev_replace->cursor_left = 0; dev_replace->committed_cursor_left = 0; dev_replace->cursor_left_last_write_of_item = 0; dev_replace->cursor_right = 0; dev_replace->is_valid = 1; dev_replace->item_needs_writeback = 1; atomic64_set(&dev_replace->num_write_errors, 0); atomic64_set(&dev_replace->num_uncorrectable_read_errors, 0); btrfs_dev_replace_unlock(dev_replace, 1); ret = btrfs_sysfs_add_device_link(tgt_device->fs_devices, tgt_device); if (ret) btrfs_err(fs_info, "kobj add dev failed %d\n", ret); btrfs_wait_ordered_roots(root->fs_info, -1, 0, (u64)-1); /* force writing the updated state information to disk */ trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { ret = PTR_ERR(trans); btrfs_dev_replace_lock(dev_replace, 1); goto leave; } ret = btrfs_commit_transaction(trans, root); WARN_ON(ret); /* the disk copy procedure reuses the scrub code */ ret = btrfs_scrub_dev(fs_info, src_device->devid, 0, btrfs_device_get_total_bytes(src_device), &dev_replace->scrub_progress, 0, 1); ret = btrfs_dev_replace_finishing(fs_info, ret); if (ret == -EINPROGRESS) { ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS; } else { WARN_ON(ret); } return ret; leave: dev_replace->srcdev = NULL; dev_replace->tgtdev = NULL; btrfs_dev_replace_unlock(dev_replace, 1); btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device); 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 btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, int scrub_ret) { struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; struct btrfs_device *tgt_device; struct btrfs_device *src_device; struct btrfs_root *root = fs_info->tree_root; u8 uuid_tmp[BTRFS_UUID_SIZE]; struct btrfs_trans_handle *trans; int ret = 0; /* don't allow cancel or unmount to disturb the finishing procedure */ mutex_lock(&dev_replace->lock_finishing_cancel_unmount); btrfs_dev_replace_lock(dev_replace); /* was the operation canceled, or is it finished? */ if (dev_replace->replace_state != BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) { btrfs_dev_replace_unlock(dev_replace); mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return 0; } tgt_device = dev_replace->tgtdev; src_device = dev_replace->srcdev; btrfs_dev_replace_unlock(dev_replace); /* replace old device with new one in mapping tree */ if (!scrub_ret) btrfs_dev_replace_update_device_in_mapping_tree(fs_info, src_device, tgt_device); /* * flush all outstanding I/O and inode extent mappings before the * copy operation is declared as being finished */ ret = btrfs_start_all_delalloc_inodes(root->fs_info, 0); if (ret) { mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return ret; } btrfs_wait_all_ordered_extents(root->fs_info, 0); trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return PTR_ERR(trans); } ret = btrfs_commit_transaction(trans, root); WARN_ON(ret); /* keep away write_all_supers() during the finishing procedure */ mutex_lock(&root->fs_info->fs_devices->device_list_mutex); btrfs_dev_replace_lock(dev_replace); dev_replace->replace_state = scrub_ret ? BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED : BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED; dev_replace->tgtdev = NULL; dev_replace->srcdev = NULL; dev_replace->time_stopped = btrfs_get_seconds_since_1970(); dev_replace->item_needs_writeback = 1; if (scrub_ret) { printk_in_rcu(KERN_ERR "btrfs: btrfs_scrub_dev(%s, %llu, %s) failed %d\n", src_device->missing ? "<missing disk>" : rcu_str_deref(src_device->name), src_device->devid, rcu_str_deref(tgt_device->name), scrub_ret); btrfs_dev_replace_unlock(dev_replace); mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); if (tgt_device) btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device); mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return 0; } printk_in_rcu(KERN_INFO "btrfs: dev_replace from %s (devid %llu) to %s) finished\n", src_device->missing ? "<missing disk>" : rcu_str_deref(src_device->name), src_device->devid, rcu_str_deref(tgt_device->name)); tgt_device->is_tgtdev_for_dev_replace = 0; tgt_device->devid = src_device->devid; src_device->devid = BTRFS_DEV_REPLACE_DEVID; tgt_device->bytes_used = src_device->bytes_used; memcpy(uuid_tmp, tgt_device->uuid, sizeof(uuid_tmp)); memcpy(tgt_device->uuid, src_device->uuid, sizeof(tgt_device->uuid)); memcpy(src_device->uuid, uuid_tmp, sizeof(src_device->uuid)); tgt_device->total_bytes = src_device->total_bytes; tgt_device->disk_total_bytes = src_device->disk_total_bytes; tgt_device->bytes_used = src_device->bytes_used; if (fs_info->sb->s_bdev == src_device->bdev) fs_info->sb->s_bdev = tgt_device->bdev; if (fs_info->fs_devices->latest_bdev == src_device->bdev) fs_info->fs_devices->latest_bdev = tgt_device->bdev; list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list); btrfs_rm_dev_replace_srcdev(fs_info, src_device); if (src_device->bdev) { /* zero out the old super */ btrfs_scratch_superblock(src_device); } /* * this is again a consistent state where no dev_replace procedure * is running, the target device is part of the filesystem, the * source device is not part of the filesystem anymore and its 1st * superblock is scratched out so that it is no longer marked to * belong to this filesystem. */ btrfs_dev_replace_unlock(dev_replace); mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); /* write back the superblocks */ trans = btrfs_start_transaction(root, 0); if (!IS_ERR(trans)) btrfs_commit_transaction(trans, root); mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return 0; }
int btrfs_dev_replace_start(struct btrfs_root *root, struct btrfs_ioctl_dev_replace_args *args) { struct btrfs_trans_handle *trans; struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; int ret; struct btrfs_device *tgt_device = NULL; struct btrfs_device *src_device = NULL; if (btrfs_fs_incompat(fs_info, RAID56)) { pr_warn("btrfs: dev_replace cannot yet handle RAID5/RAID6\n"); return -EINVAL; } switch (args->start.cont_reading_from_srcdev_mode) { case BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS: case BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID: break; default: return -EINVAL; } if ((args->start.srcdevid == 0 && args->start.srcdev_name[0] == '\0') || args->start.tgtdev_name[0] == '\0') return -EINVAL; mutex_lock(&fs_info->volume_mutex); ret = btrfs_init_dev_replace_tgtdev(root, args->start.tgtdev_name, &tgt_device); if (ret) { pr_err("btrfs: target device %s is invalid!\n", args->start.tgtdev_name); mutex_unlock(&fs_info->volume_mutex); return -EINVAL; } ret = btrfs_dev_replace_find_srcdev(root, args->start.srcdevid, args->start.srcdev_name, &src_device); mutex_unlock(&fs_info->volume_mutex); if (ret) { ret = -EINVAL; goto leave_no_lock; } if (tgt_device->total_bytes < src_device->total_bytes) { pr_err("btrfs: target device is smaller than source device!\n"); ret = -EINVAL; goto leave_no_lock; } btrfs_dev_replace_lock(dev_replace); switch (dev_replace->replace_state) { case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED: case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED: break; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED: args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED; goto leave; } dev_replace->cont_reading_from_srcdev_mode = args->start.cont_reading_from_srcdev_mode; WARN_ON(!src_device); dev_replace->srcdev = src_device; WARN_ON(!tgt_device); dev_replace->tgtdev = tgt_device; printk_in_rcu(KERN_INFO "btrfs: dev_replace from %s (devid %llu) to %s) started\n", src_device->missing ? "<missing disk>" : rcu_str_deref(src_device->name), src_device->devid, rcu_str_deref(tgt_device->name)); tgt_device->total_bytes = src_device->total_bytes; tgt_device->disk_total_bytes = src_device->disk_total_bytes; tgt_device->bytes_used = src_device->bytes_used; /* * from now on, the writes to the srcdev are all duplicated to * go to the tgtdev as well (refer to btrfs_map_block()). */ dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED; dev_replace->time_started = btrfs_get_seconds_since_1970(); dev_replace->cursor_left = 0; dev_replace->committed_cursor_left = 0; dev_replace->cursor_left_last_write_of_item = 0; dev_replace->cursor_right = 0; dev_replace->is_valid = 1; dev_replace->item_needs_writeback = 1; args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR; btrfs_dev_replace_unlock(dev_replace); btrfs_wait_all_ordered_extents(root->fs_info, 0); /* force writing the updated state information to disk */ trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { ret = PTR_ERR(trans); btrfs_dev_replace_lock(dev_replace); goto leave; } ret = btrfs_commit_transaction(trans, root); WARN_ON(ret); /* the disk copy procedure reuses the scrub code */ ret = btrfs_scrub_dev(fs_info, src_device->devid, 0, src_device->total_bytes, &dev_replace->scrub_progress, 0, 1); ret = btrfs_dev_replace_finishing(root->fs_info, ret); WARN_ON(ret); return 0; leave: dev_replace->srcdev = NULL; dev_replace->tgtdev = NULL; btrfs_dev_replace_unlock(dev_replace); leave_no_lock: if (tgt_device) btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device); return ret; }
int btrfs_dedup_enable(struct btrfs_fs_info *fs_info, u16 type, u16 backend, u64 blocksize, u64 limit) { struct btrfs_dedup_info *dedup_info; struct btrfs_root *dedup_root; struct btrfs_key key; struct btrfs_trans_handle *trans; struct btrfs_path *path; struct btrfs_dedup_status_item *status; int create_tree; u64 compat_ro_flag = btrfs_super_compat_ro_flags(fs_info->super_copy); int ret = 0; /* Sanity check */ if (blocksize > BTRFS_DEDUP_BLOCKSIZE_MAX || blocksize < BTRFS_DEDUP_BLOCKSIZE_MIN || blocksize < fs_info->tree_root->sectorsize || !is_power_of_2(blocksize)) return -EINVAL; if (type > ARRAY_SIZE(btrfs_dedup_sizes)) return -EINVAL; if (backend >= BTRFS_DEDUP_BACKEND_LAST) return -EINVAL; if (backend == BTRFS_DEDUP_BACKEND_INMEMORY && limit == 0) limit = 4096; /* default value */ if (backend == BTRFS_DEDUP_BACKEND_ONDISK && limit != 0) limit = 0; /* * If current fs doesn't support DEDUP feature, don't enable * on-disk dedup. */ if (!(compat_ro_flag & BTRFS_FEATURE_COMPAT_RO_DEDUP) && backend == BTRFS_DEDUP_BACKEND_ONDISK) return -EINVAL; /* Meaningless and unable to enable dedup for RO fs */ if (fs_info->sb->s_flags & MS_RDONLY) return -EINVAL; if (fs_info->dedup_info) { dedup_info = fs_info->dedup_info; /* Check if we are re-enable for different dedup config */ if (dedup_info->blocksize != blocksize || dedup_info->hash_type != type || dedup_info->backend != backend) { btrfs_dedup_disable(fs_info); goto enable; } /* On-fly limit change is OK */ mutex_lock(&dedup_info->lock); fs_info->dedup_info->limit_nr = limit; mutex_unlock(&dedup_info->lock); return 0; } enable: create_tree = compat_ro_flag & BTRFS_FEATURE_COMPAT_RO_DEDUP; ret = init_dedup_info(fs_info, type, backend, blocksize, limit); dedup_info = fs_info->dedup_info; if (ret < 0) goto out; if (!create_tree) goto out; /* Create dedup tree for status at least */ path = btrfs_alloc_path(); if (!path) { ret = -ENOMEM; goto out; } trans = btrfs_start_transaction(fs_info->tree_root, 2); if (IS_ERR(trans)) { ret = PTR_ERR(trans); btrfs_free_path(path); goto out; } dedup_root = btrfs_create_tree(trans, fs_info, BTRFS_DEDUP_TREE_OBJECTID); if (IS_ERR(dedup_root)) { ret = PTR_ERR(dedup_root); btrfs_abort_transaction(trans, fs_info->tree_root, ret); btrfs_free_path(path); goto out; } dedup_info->dedup_root = dedup_root; key.objectid = 0; key.type = BTRFS_DEDUP_STATUS_ITEM_KEY; key.offset = 0; ret = btrfs_insert_empty_item(trans, dedup_root, path, &key, sizeof(*status)); if (ret < 0) { btrfs_abort_transaction(trans, fs_info->tree_root, ret); btrfs_free_path(path); goto out; } status = btrfs_item_ptr(path->nodes[0], path->slots[0], struct btrfs_dedup_status_item); btrfs_set_dedup_status_blocksize(path->nodes[0], status, blocksize); btrfs_set_dedup_status_limit(path->nodes[0], status, limit); btrfs_set_dedup_status_hash_type(path->nodes[0], status, type); btrfs_set_dedup_status_backend(path->nodes[0], status, backend); btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_free_path(path); ret = btrfs_commit_transaction(trans, fs_info->tree_root); out: if (ret < 0) { kfree(dedup_info); fs_info->dedup_info = NULL; } return ret; }
int btrfs_dev_replace_start(struct btrfs_root *root, struct btrfs_ioctl_dev_replace_args *args) { struct btrfs_trans_handle *trans; struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; int ret; struct btrfs_device *tgt_device = NULL; struct btrfs_device *src_device = NULL; switch (args->start.cont_reading_from_srcdev_mode) { case BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS: case BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID: break; default: return -EINVAL; } if ((args->start.srcdevid == 0 && args->start.srcdev_name[0] == '\0') || args->start.tgtdev_name[0] == '\0') return -EINVAL; /* * Here we commit the transaction to make sure commit_total_bytes * of all the devices are updated. */ trans = btrfs_attach_transaction(root); if (!IS_ERR(trans)) { ret = btrfs_commit_transaction(trans, root); if (ret) return ret; } else if (PTR_ERR(trans) != -ENOENT) { return PTR_ERR(trans); } /* the disk copy procedure reuses the scrub code */ mutex_lock(&fs_info->volume_mutex); ret = btrfs_dev_replace_find_srcdev(root, args->start.srcdevid, args->start.srcdev_name, &src_device); if (ret) { mutex_unlock(&fs_info->volume_mutex); return ret; } ret = btrfs_init_dev_replace_tgtdev(root, args->start.tgtdev_name, src_device, &tgt_device); mutex_unlock(&fs_info->volume_mutex); if (ret) return ret; btrfs_dev_replace_lock(dev_replace); switch (dev_replace->replace_state) { case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED: case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED: break; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED: args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED; goto leave; } dev_replace->cont_reading_from_srcdev_mode = args->start.cont_reading_from_srcdev_mode; WARN_ON(!src_device); dev_replace->srcdev = src_device; WARN_ON(!tgt_device); dev_replace->tgtdev = tgt_device; printk_in_rcu(KERN_INFO "BTRFS: dev_replace from %s (devid %llu) to %s started\n", src_device->missing ? "<missing disk>" : rcu_str_deref(src_device->name), src_device->devid, rcu_str_deref(tgt_device->name)); /* * from now on, the writes to the srcdev are all duplicated to * go to the tgtdev as well (refer to btrfs_map_block()). */ dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED; dev_replace->time_started = get_seconds(); dev_replace->cursor_left = 0; dev_replace->committed_cursor_left = 0; dev_replace->cursor_left_last_write_of_item = 0; dev_replace->cursor_right = 0; dev_replace->is_valid = 1; dev_replace->item_needs_writeback = 1; args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR; btrfs_dev_replace_unlock(dev_replace); btrfs_wait_ordered_roots(root->fs_info, -1); /* force writing the updated state information to disk */ trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { ret = PTR_ERR(trans); btrfs_dev_replace_lock(dev_replace); goto leave; } ret = btrfs_commit_transaction(trans, root); WARN_ON(ret); /* the disk copy procedure reuses the scrub code */ ret = btrfs_scrub_dev(fs_info, src_device->devid, 0, btrfs_device_get_total_bytes(src_device), &dev_replace->scrub_progress, 0, 1); ret = btrfs_dev_replace_finishing(root->fs_info, ret); /* don't warn if EINPROGRESS, someone else might be running scrub */ if (ret == -EINPROGRESS) { args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS; ret = 0; } else { WARN_ON(ret); } return ret; leave: dev_replace->srcdev = NULL; dev_replace->tgtdev = NULL; btrfs_dev_replace_unlock(dev_replace); btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device); return ret; }
static noinline int create_subvol(struct btrfs_root *root, struct dentry *dentry, char *name, int namelen) { struct btrfs_trans_handle *trans; struct btrfs_key key; struct btrfs_root_item root_item; struct btrfs_inode_item *inode_item; struct extent_buffer *leaf; struct btrfs_root *new_root; struct inode *dir = dentry->d_parent->d_inode; int ret; int err; u64 objectid; u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID; u64 index = 0; ret = btrfs_find_free_objectid(NULL, root->fs_info->tree_root, 0, &objectid); if (ret) return ret; /* * 1 - inode item * 2 - refs * 1 - root item * 2 - dir items */ trans = btrfs_start_transaction(root, 6); if (IS_ERR(trans)) return PTR_ERR(trans); leaf = btrfs_alloc_free_block(trans, root, root->leafsize, 0, objectid, NULL, 0, 0, 0); if (IS_ERR(leaf)) { ret = PTR_ERR(leaf); goto fail; } memset_extent_buffer(leaf, 0, 0, sizeof(struct btrfs_header)); btrfs_set_header_bytenr(leaf, leaf->start); btrfs_set_header_generation(leaf, trans->transid); btrfs_set_header_backref_rev(leaf, BTRFS_MIXED_BACKREF_REV); btrfs_set_header_owner(leaf, objectid); write_extent_buffer(leaf, root->fs_info->fsid, (unsigned long)btrfs_header_fsid(leaf), BTRFS_FSID_SIZE); write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid, (unsigned long)btrfs_header_chunk_tree_uuid(leaf), BTRFS_UUID_SIZE); btrfs_mark_buffer_dirty(leaf); inode_item = &root_item.inode; memset(inode_item, 0, sizeof(*inode_item)); inode_item->generation = cpu_to_le64(1); inode_item->size = cpu_to_le64(3); inode_item->nlink = cpu_to_le32(1); inode_item->nbytes = cpu_to_le64(root->leafsize); inode_item->mode = cpu_to_le32(S_IFDIR | 0755); root_item.flags = 0; root_item.byte_limit = 0; inode_item->flags = cpu_to_le64(BTRFS_INODE_ROOT_ITEM_INIT); btrfs_set_root_bytenr(&root_item, leaf->start); btrfs_set_root_generation(&root_item, trans->transid); btrfs_set_root_level(&root_item, 0); btrfs_set_root_refs(&root_item, 1); btrfs_set_root_used(&root_item, leaf->len); btrfs_set_root_last_snapshot(&root_item, 0); memset(&root_item.drop_progress, 0, sizeof(root_item.drop_progress)); root_item.drop_level = 0; btrfs_tree_unlock(leaf); free_extent_buffer(leaf); leaf = NULL; btrfs_set_root_dirid(&root_item, new_dirid); key.objectid = objectid; key.offset = 0; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key, &root_item); if (ret) goto fail; key.offset = (u64)-1; new_root = btrfs_read_fs_root_no_name(root->fs_info, &key); BUG_ON(IS_ERR(new_root)); btrfs_record_root_in_trans(trans, new_root); ret = btrfs_create_subvol_root(trans, new_root, new_dirid, BTRFS_I(dir)->block_group); /* * insert the directory item */ ret = btrfs_set_inode_index(dir, &index); BUG_ON(ret); ret = btrfs_insert_dir_item(trans, root, name, namelen, dir->i_ino, &key, BTRFS_FT_DIR, index); if (ret) goto fail; btrfs_i_size_write(dir, dir->i_size + namelen * 2); ret = btrfs_update_inode(trans, root, dir); BUG_ON(ret); ret = btrfs_add_root_ref(trans, root->fs_info->tree_root, objectid, root->root_key.objectid, dir->i_ino, index, name, namelen); BUG_ON(ret); d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry)); fail: err = btrfs_commit_transaction(trans, root); if (err && !ret) ret = err; return ret; }
static int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info, const char *tgtdev_name, u64 srcdevid, const char *srcdev_name, int read_src) { struct btrfs_root *root = fs_info->dev_root; struct btrfs_trans_handle *trans; struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; int ret; struct btrfs_device *tgt_device = NULL; struct btrfs_device *src_device = NULL; bool need_unlock; src_device = btrfs_find_device_by_devspec(fs_info, srcdevid, srcdev_name); if (IS_ERR(src_device)) return PTR_ERR(src_device); if (btrfs_pinned_by_swapfile(fs_info, src_device)) { btrfs_warn_in_rcu(fs_info, "cannot replace device %s (devid %llu) due to active swapfile", btrfs_dev_name(src_device), src_device->devid); return -ETXTBSY; } ret = btrfs_init_dev_replace_tgtdev(fs_info, tgtdev_name, src_device, &tgt_device); if (ret) return ret; /* * Here we commit the transaction to make sure commit_total_bytes * of all the devices are updated. */ trans = btrfs_attach_transaction(root); if (!IS_ERR(trans)) { ret = btrfs_commit_transaction(trans); if (ret) return ret; } else if (PTR_ERR(trans) != -ENOENT) { return PTR_ERR(trans); } need_unlock = true; down_write(&dev_replace->rwsem); switch (dev_replace->replace_state) { case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED: case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED: break; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED: ASSERT(0); ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED; goto leave; } dev_replace->cont_reading_from_srcdev_mode = read_src; WARN_ON(!src_device); dev_replace->srcdev = src_device; dev_replace->tgtdev = tgt_device; btrfs_info_in_rcu(fs_info, "dev_replace from %s (devid %llu) to %s started", btrfs_dev_name(src_device), src_device->devid, rcu_str_deref(tgt_device->name)); /* * from now on, the writes to the srcdev are all duplicated to * go to the tgtdev as well (refer to btrfs_map_block()). */ dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED; dev_replace->time_started = ktime_get_real_seconds(); dev_replace->cursor_left = 0; dev_replace->committed_cursor_left = 0; dev_replace->cursor_left_last_write_of_item = 0; dev_replace->cursor_right = 0; dev_replace->is_valid = 1; dev_replace->item_needs_writeback = 1; atomic64_set(&dev_replace->num_write_errors, 0); atomic64_set(&dev_replace->num_uncorrectable_read_errors, 0); up_write(&dev_replace->rwsem); need_unlock = false; ret = btrfs_sysfs_add_device_link(tgt_device->fs_devices, tgt_device); if (ret) btrfs_err(fs_info, "kobj add dev failed %d", ret); btrfs_wait_ordered_roots(fs_info, U64_MAX, 0, (u64)-1); /* force writing the updated state information to disk */ trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { ret = PTR_ERR(trans); need_unlock = true; down_write(&dev_replace->rwsem); dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED; dev_replace->srcdev = NULL; dev_replace->tgtdev = NULL; goto leave; } ret = btrfs_commit_transaction(trans); WARN_ON(ret); /* the disk copy procedure reuses the scrub code */ ret = btrfs_scrub_dev(fs_info, src_device->devid, 0, btrfs_device_get_total_bytes(src_device), &dev_replace->scrub_progress, 0, 1); ret = btrfs_dev_replace_finishing(fs_info, ret); if (ret == -EINPROGRESS) { ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS; } else if (ret != -ECANCELED) { WARN_ON(ret); } return ret; leave: if (need_unlock) up_write(&dev_replace->rwsem); btrfs_destroy_dev_replace_tgtdev(tgt_device); return ret; }
int main(int ac, char **av) { char *file; struct btrfs_root *root; struct btrfs_trans_handle *trans; char *label = NULL; char *first_file; u64 block_count = 0; u64 dev_block_count = 0; u64 blocks[7]; u64 alloc_start = 0; u64 metadata_profile = 0; u64 data_profile = 0; u32 leafsize = sysconf(_SC_PAGESIZE); u32 sectorsize = 4096; u32 nodesize = leafsize; u32 stripesize = 4096; int zero_end = 1; int option_index = 0; int fd; int ret; int i; int mixed = 0; int data_profile_opt = 0; int metadata_profile_opt = 0; int discard = 1; int ssd = 0; int force_overwrite = 0; char *source_dir = NULL; int source_dir_set = 0; u64 num_of_meta_chunks = 0; u64 size_of_data = 0; u64 source_dir_size = 0; int dev_cnt = 0; int saved_optind; char estr[100]; u64 features = 0; while(1) { int c; c = getopt_long(ac, av, "A:b:fl:n:s:m:d:L:O:r:VMK", long_options, &option_index); if (c < 0) break; switch(c) { case 'A': alloc_start = parse_size(optarg); break; case 'f': force_overwrite = 1; break; case 'd': data_profile = parse_profile(optarg); data_profile_opt = 1; break; case 'l': case 'n': nodesize = parse_size(optarg); leafsize = parse_size(optarg); break; case 'L': label = parse_label(optarg); break; case 'm': metadata_profile = parse_profile(optarg); metadata_profile_opt = 1; break; case 'M': mixed = 1; break; case 'O': { char *orig = strdup(optarg); char *tmp = orig; tmp = parse_fs_features(tmp, &features); if (tmp) { fprintf(stderr, "Unrecognized filesystem feature '%s'\n", tmp); free(orig); exit(1); } free(orig); if (features & BTRFS_FEATURE_LIST_ALL) { list_all_fs_features(); exit(0); } break; } case 's': sectorsize = parse_size(optarg); break; case 'b': block_count = parse_size(optarg); if (block_count <= 1024*1024*1024) { printf("SMALL VOLUME: forcing mixed " "metadata/data groups\n"); mixed = 1; } zero_end = 0; break; case 'V': print_version(); break; case 'r': source_dir = optarg; source_dir_set = 1; break; case 'K': discard = 0; break; default: print_usage(); } } sectorsize = max(sectorsize, (u32)sysconf(_SC_PAGESIZE)); if (check_leaf_or_node_size(leafsize, sectorsize)) exit(1); if (check_leaf_or_node_size(nodesize, sectorsize)) exit(1); saved_optind = optind; dev_cnt = ac - optind; if (dev_cnt == 0) print_usage(); if (source_dir_set && dev_cnt > 1) { fprintf(stderr, "The -r option is limited to a single device\n"); exit(1); } while (dev_cnt-- > 0) { file = av[optind++]; if (is_block_device(file)) if (test_dev_for_mkfs(file, force_overwrite, estr)) { fprintf(stderr, "Error: %s", estr); exit(1); } } optind = saved_optind; dev_cnt = ac - optind; file = av[optind++]; ssd = is_ssd(file); if (is_vol_small(file)) { printf("SMALL VOLUME: forcing mixed metadata/data groups\n"); mixed = 1; if (metadata_profile != data_profile) { if (metadata_profile_opt || data_profile_opt) { fprintf(stderr, "With mixed block groups data and metadata profiles must be the same\n"); exit(1); } } } /* * Set default profiles according to number of added devices. * For mixed groups defaults are single/single. */ if (!mixed) { if (!metadata_profile_opt) { if (dev_cnt == 1 && ssd) printf("Detected a SSD, turning off metadata " "duplication. Mkfs with -m dup if you want to " "force metadata duplication.\n"); metadata_profile = (dev_cnt > 1) ? BTRFS_BLOCK_GROUP_RAID1 : (ssd) ? 0: BTRFS_BLOCK_GROUP_DUP; } if (!data_profile_opt) { data_profile = (dev_cnt > 1) ? BTRFS_BLOCK_GROUP_RAID0 : 0; /* raid0 or single */ } } else { metadata_profile = 0; data_profile = 0; } ret = test_num_disk_vs_raid(metadata_profile, data_profile, dev_cnt, mixed, estr); if (ret) { fprintf(stderr, "Error: %s\n", estr); exit(1); } /* if we are here that means all devs are good to btrfsify */ printf("\nWARNING! - %s IS EXPERIMENTAL\n", BTRFS_BUILD_VERSION); printf("WARNING! - see http://btrfs.wiki.kernel.org before using\n\n"); dev_cnt--; if (!source_dir_set) { /* * open without O_EXCL so that the problem should not * occur by the following processing. * (btrfs_register_one_device() fails if O_EXCL is on) */ fd = open(file, O_RDWR); if (fd < 0) { fprintf(stderr, "unable to open %s: %s\n", file, strerror(errno)); exit(1); } first_file = file; ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count, block_count, &mixed, discard); if (block_count && block_count > dev_block_count) { fprintf(stderr, "%s is smaller than requested size\n", file); exit(1); } } else { fd = open_target(file); if (fd < 0) { fprintf(stderr, "unable to open the %s\n", file); exit(1); } first_file = file; source_dir_size = size_sourcedir(source_dir, sectorsize, &num_of_meta_chunks, &size_of_data); if(block_count < source_dir_size) block_count = source_dir_size; ret = zero_output_file(fd, block_count, sectorsize); if (ret) { fprintf(stderr, "unable to zero the output file\n"); exit(1); } /* our "device" is the new image file */ dev_block_count = block_count; } /* To create the first block group and chunk 0 in make_btrfs */ if (dev_block_count < BTRFS_MKFS_SYSTEM_GROUP_SIZE) { fprintf(stderr, "device is too small to make filesystem\n"); exit(1); } blocks[0] = BTRFS_SUPER_INFO_OFFSET; for (i = 1; i < 7; i++) { blocks[i] = BTRFS_SUPER_INFO_OFFSET + 1024 * 1024 + leafsize * i; } /* * FS features that can be set by other means than -O * just set the bit here */ if (mixed) features |= BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS; if ((data_profile | metadata_profile) & (BTRFS_BLOCK_GROUP_RAID5 | BTRFS_BLOCK_GROUP_RAID6)) { features |= BTRFS_FEATURE_INCOMPAT_RAID56; } process_fs_features(features); ret = make_btrfs(fd, file, label, blocks, dev_block_count, nodesize, leafsize, sectorsize, stripesize, features); if (ret) { fprintf(stderr, "error during mkfs: %s\n", strerror(-ret)); exit(1); } root = open_ctree(file, 0, OPEN_CTREE_WRITES); if (!root) { fprintf(stderr, "Open ctree failed\n"); close(fd); exit(1); } root->fs_info->alloc_start = alloc_start; ret = make_root_dir(root, mixed); if (ret) { fprintf(stderr, "failed to setup the root directory\n"); exit(1); } trans = btrfs_start_transaction(root, 1); if (dev_cnt == 0) goto raid_groups; btrfs_register_one_device(file); zero_end = 1; while (dev_cnt-- > 0) { int old_mixed = mixed; file = av[optind++]; /* * open without O_EXCL so that the problem should not * occur by the following processing. * (btrfs_register_one_device() fails if O_EXCL is on) */ fd = open(file, O_RDWR); if (fd < 0) { fprintf(stderr, "unable to open %s: %s\n", file, strerror(errno)); exit(1); } ret = btrfs_device_already_in_root(root, fd, BTRFS_SUPER_INFO_OFFSET); if (ret) { fprintf(stderr, "skipping duplicate device %s in FS\n", file); close(fd); continue; } ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count, block_count, &mixed, discard); mixed = old_mixed; BUG_ON(ret); ret = btrfs_add_to_fsid(trans, root, fd, file, dev_block_count, sectorsize, sectorsize, sectorsize); BUG_ON(ret); btrfs_register_one_device(file); } raid_groups: if (!source_dir_set) { ret = create_raid_groups(trans, root, data_profile, data_profile_opt, metadata_profile, metadata_profile_opt, mixed, ssd); BUG_ON(ret); } ret = create_data_reloc_tree(trans, root); BUG_ON(ret); printf("fs created label %s on %s\n\tnodesize %u leafsize %u " "sectorsize %u size %s\n", label, first_file, nodesize, leafsize, sectorsize, pretty_size(btrfs_super_total_bytes(root->fs_info->super_copy))); printf("%s\n", BTRFS_BUILD_VERSION); btrfs_commit_transaction(trans, root); if (source_dir_set) { trans = btrfs_start_transaction(root, 1); ret = create_chunks(trans, root, num_of_meta_chunks, size_of_data); BUG_ON(ret); btrfs_commit_transaction(trans, root); ret = make_image(source_dir, root, fd); BUG_ON(ret); } ret = close_ctree(root); BUG_ON(ret); free(label); return 0; }
int btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info) { struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; struct btrfs_device *tgt_device = NULL; struct btrfs_device *src_device = NULL; struct btrfs_trans_handle *trans; struct btrfs_root *root = fs_info->tree_root; int result; int ret; if (sb_rdonly(fs_info->sb)) return -EROFS; mutex_lock(&dev_replace->lock_finishing_cancel_unmount); down_write(&dev_replace->rwsem); switch (dev_replace->replace_state) { case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED: case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED: result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED; up_write(&dev_replace->rwsem); break; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: tgt_device = dev_replace->tgtdev; src_device = dev_replace->srcdev; up_write(&dev_replace->rwsem); ret = btrfs_scrub_cancel(fs_info); if (ret < 0) { result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED; } else { result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR; /* * btrfs_dev_replace_finishing() will handle the * cleanup part */ btrfs_info_in_rcu(fs_info, "dev_replace from %s (devid %llu) to %s canceled", btrfs_dev_name(src_device), src_device->devid, btrfs_dev_name(tgt_device)); } break; case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED: /* * Scrub doing the replace isn't running so we need to do the * cleanup step of btrfs_dev_replace_finishing() here */ result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR; tgt_device = dev_replace->tgtdev; src_device = dev_replace->srcdev; dev_replace->tgtdev = NULL; dev_replace->srcdev = NULL; dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED; dev_replace->time_stopped = ktime_get_real_seconds(); dev_replace->item_needs_writeback = 1; up_write(&dev_replace->rwsem); /* Scrub for replace must not be running in suspended state */ ret = btrfs_scrub_cancel(fs_info); ASSERT(ret != -ENOTCONN); trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return PTR_ERR(trans); } ret = btrfs_commit_transaction(trans); WARN_ON(ret); btrfs_info_in_rcu(fs_info, "suspended dev_replace from %s (devid %llu) to %s canceled", btrfs_dev_name(src_device), src_device->devid, btrfs_dev_name(tgt_device)); if (tgt_device) btrfs_destroy_dev_replace_tgtdev(tgt_device); break; default: up_write(&dev_replace->rwsem); result = -EINVAL; } mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return result; }
static int make_root_dir(struct btrfs_root *root, int mixed) { struct btrfs_trans_handle *trans; struct btrfs_key location; u64 bytes_used; u64 chunk_start = 0; u64 chunk_size = 0; int ret; trans = btrfs_start_transaction(root, 1); bytes_used = btrfs_super_bytes_used(root->fs_info->super_copy); root->fs_info->system_allocs = 1; ret = btrfs_make_block_group(trans, root, bytes_used, BTRFS_BLOCK_GROUP_SYSTEM, BTRFS_FIRST_CHUNK_TREE_OBJECTID, 0, BTRFS_MKFS_SYSTEM_GROUP_SIZE); BUG_ON(ret); if (mixed) { ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, &chunk_start, &chunk_size, BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA); if (ret == -ENOSPC) { fprintf(stderr, "no space to alloc data/metadata chunk\n"); goto err; } BUG_ON(ret); ret = btrfs_make_block_group(trans, root, 0, BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA, BTRFS_FIRST_CHUNK_TREE_OBJECTID, chunk_start, chunk_size); BUG_ON(ret); printf("Created a data/metadata chunk of size %llu\n", chunk_size); } else { ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, &chunk_start, &chunk_size, BTRFS_BLOCK_GROUP_METADATA); if (ret == -ENOSPC) { fprintf(stderr, "no space to alloc metadata chunk\n"); goto err; } BUG_ON(ret); ret = btrfs_make_block_group(trans, root, 0, BTRFS_BLOCK_GROUP_METADATA, BTRFS_FIRST_CHUNK_TREE_OBJECTID, chunk_start, chunk_size); BUG_ON(ret); } root->fs_info->system_allocs = 0; btrfs_commit_transaction(trans, root); trans = btrfs_start_transaction(root, 1); BUG_ON(!trans); if (!mixed) { ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, &chunk_start, &chunk_size, BTRFS_BLOCK_GROUP_DATA); if (ret == -ENOSPC) { fprintf(stderr, "no space to alloc data chunk\n"); goto err; } BUG_ON(ret); ret = btrfs_make_block_group(trans, root, 0, BTRFS_BLOCK_GROUP_DATA, BTRFS_FIRST_CHUNK_TREE_OBJECTID, chunk_start, chunk_size); BUG_ON(ret); } ret = btrfs_make_root_dir(trans, root->fs_info->tree_root, BTRFS_ROOT_TREE_DIR_OBJECTID); if (ret) goto err; ret = btrfs_make_root_dir(trans, root, BTRFS_FIRST_FREE_OBJECTID); if (ret) goto err; memcpy(&location, &root->fs_info->fs_root->root_key, sizeof(location)); location.offset = (u64)-1; ret = btrfs_insert_dir_item(trans, root->fs_info->tree_root, "default", 7, btrfs_super_root_dir(root->fs_info->super_copy), &location, BTRFS_FT_DIR, 0); if (ret) goto err; ret = btrfs_insert_inode_ref(trans, root->fs_info->tree_root, "default", 7, location.objectid, BTRFS_ROOT_TREE_DIR_OBJECTID, 0); if (ret) goto err; btrfs_commit_transaction(trans, root); err: return ret; }