Loading fs/btrfs/dev-replace.c +44 −55 Original line number Diff line number Diff line Loading @@ -44,9 +44,6 @@ static void btrfs_dev_replace_update_device_in_mapping_tree( struct btrfs_fs_info *fs_info, struct btrfs_device *srcdev, struct btrfs_device *tgtdev); static int btrfs_dev_replace_find_srcdev(struct btrfs_root *root, u64 srcdevid, char *srcdev_name, struct btrfs_device **device); static u64 __btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info); static int btrfs_dev_replace_kthread(void *data); static int btrfs_dev_replace_continue_on_mount(struct btrfs_fs_info *fs_info); Loading Loading @@ -305,8 +302,8 @@ void btrfs_after_dev_replace_commit(struct btrfs_fs_info *fs_info) dev_replace->cursor_left_last_write_of_item; } int btrfs_dev_replace_start(struct btrfs_root *root, struct btrfs_ioctl_dev_replace_args *args) 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; Loading @@ -315,29 +312,16 @@ int btrfs_dev_replace_start(struct btrfs_root *root, 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; /* 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); 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, args->start.tgtdev_name, ret = btrfs_init_dev_replace_tgtdev(root, tgtdev_name, src_device, &tgt_device); mutex_unlock(&fs_info->volume_mutex); if (ret) Loading @@ -364,18 +348,17 @@ int btrfs_dev_replace_start(struct btrfs_root *root, break; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED: args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED; ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED; goto leave; } dev_replace->cont_reading_from_srcdev_mode = args->start.cont_reading_from_srcdev_mode; 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(root->fs_info, 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), Loading @@ -396,14 +379,13 @@ int btrfs_dev_replace_start(struct btrfs_root *root, dev_replace->item_needs_writeback = 1; atomic64_set(&dev_replace->num_write_errors, 0); atomic64_set(&dev_replace->num_uncorrectable_read_errors, 0); args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR; btrfs_dev_replace_unlock(dev_replace, 1); ret = btrfs_sysfs_add_device_link(tgt_device->fs_devices, tgt_device); if (ret) btrfs_err(root->fs_info, "kobj add dev failed %d\n", ret); btrfs_err(fs_info, "kobj add dev failed %d\n", ret); btrfs_wait_ordered_roots(root->fs_info, -1); btrfs_wait_ordered_roots(fs_info, -1); /* force writing the updated state information to disk */ trans = btrfs_start_transaction(root, 0); Loading @@ -421,11 +403,9 @@ int btrfs_dev_replace_start(struct btrfs_root *root, 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 */ ret = btrfs_dev_replace_finishing(fs_info, ret); if (ret == -EINPROGRESS) { args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS; ret = 0; ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS; } else { WARN_ON(ret); } Loading @@ -440,6 +420,35 @@ int btrfs_dev_replace_start(struct btrfs_root *root, return ret; } int btrfs_dev_replace_by_ioctl(struct btrfs_root *root, struct btrfs_ioctl_dev_replace_args *args) { int ret; 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; ret = btrfs_dev_replace_start(root, args->start.tgtdev_name, args->start.srcdevid, args->start.srcdev_name, args->start.cont_reading_from_srcdev_mode); args->result = ret; /* don't warn if EINPROGRESS, someone else might be running scrub */ if (ret == BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS) ret = 0; return ret; } /* * blocked until all flighting bios are finished. */ Loading Loading @@ -560,10 +569,9 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, 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; 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; btrfs_assign_next_active_device(fs_info, src_device, tgt_device); list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list); fs_info->fs_devices->rw_devices++; Loading Loading @@ -626,25 +634,6 @@ static void btrfs_dev_replace_update_device_in_mapping_tree( write_unlock(&em_tree->lock); } static int btrfs_dev_replace_find_srcdev(struct btrfs_root *root, u64 srcdevid, char *srcdev_name, struct btrfs_device **device) { int ret; if (srcdevid) { ret = 0; *device = btrfs_find_device(root->fs_info, srcdevid, NULL, NULL); if (!*device) ret = -ENOENT; } else { ret = btrfs_find_device_missing_or_by_path(root, srcdev_name, device); } return ret; } void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info, struct btrfs_ioctl_dev_replace_args *args) { Loading fs/btrfs/dev-replace.h +3 −1 Original line number Diff line number Diff line Loading @@ -25,8 +25,10 @@ int btrfs_init_dev_replace(struct btrfs_fs_info *fs_info); int btrfs_run_dev_replace(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info); void btrfs_after_dev_replace_commit(struct btrfs_fs_info *fs_info); int btrfs_dev_replace_start(struct btrfs_root *root, int btrfs_dev_replace_by_ioctl(struct btrfs_root *root, struct btrfs_ioctl_dev_replace_args *args); int btrfs_dev_replace_start(struct btrfs_root *root, char *tgtdev_name, u64 srcdevid, char *srcdev_name, int read_src); void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info, struct btrfs_ioctl_dev_replace_args *args); int btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info, Loading fs/btrfs/ioctl.c +58 −2 Original line number Diff line number Diff line Loading @@ -2676,6 +2676,60 @@ static long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg) return ret; } static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg) { struct btrfs_root *root = BTRFS_I(file_inode(file))->root; struct btrfs_ioctl_vol_args_v2 *vol_args; int ret; if (!capable(CAP_SYS_ADMIN)) return -EPERM; ret = mnt_want_write_file(file); if (ret) return ret; vol_args = memdup_user(arg, sizeof(*vol_args)); if (IS_ERR(vol_args)) { ret = PTR_ERR(vol_args); goto err_drop; } /* Check for compatibility reject unknown flags */ if (vol_args->flags & ~BTRFS_VOL_ARG_V2_FLAGS_SUPPORTED) return -EOPNOTSUPP; if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running, 1)) { ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; goto out; } mutex_lock(&root->fs_info->volume_mutex); if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) { ret = btrfs_rm_device(root, NULL, vol_args->devid); } else { vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0'; ret = btrfs_rm_device(root, vol_args->name, 0); } mutex_unlock(&root->fs_info->volume_mutex); atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); if (!ret) { if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) btrfs_info(root->fs_info, "device deleted: id %llu", vol_args->devid); else btrfs_info(root->fs_info, "device deleted: %s", vol_args->name); } out: kfree(vol_args); err_drop: mnt_drop_write_file(file); return ret; } static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) { struct btrfs_root *root = BTRFS_I(file_inode(file))->root; Loading Loading @@ -2703,7 +2757,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; mutex_lock(&root->fs_info->volume_mutex); ret = btrfs_rm_device(root, vol_args->name); ret = btrfs_rm_device(root, vol_args->name, 0); mutex_unlock(&root->fs_info->volume_mutex); if (!ret) Loading Loading @@ -4387,7 +4441,7 @@ static long btrfs_ioctl_dev_replace(struct btrfs_root *root, void __user *arg) 1)) { ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; } else { ret = btrfs_dev_replace_start(root, p); ret = btrfs_dev_replace_by_ioctl(root, p); atomic_set( &root->fs_info->mutually_exclusive_operation_running, 0); Loading Loading @@ -5480,6 +5534,8 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_add_dev(root, argp); case BTRFS_IOC_RM_DEV: return btrfs_ioctl_rm_dev(file, argp); case BTRFS_IOC_RM_DEV_V2: return btrfs_ioctl_rm_dev_v2(file, argp); case BTRFS_IOC_FS_INFO: return btrfs_ioctl_fs_info(root, argp); case BTRFS_IOC_DEV_INFO: Loading fs/btrfs/volumes.c +198 −181 Original line number Diff line number Diff line Loading @@ -118,6 +118,21 @@ const u64 btrfs_raid_group[BTRFS_NR_RAID_TYPES] = { [BTRFS_RAID_RAID6] = BTRFS_BLOCK_GROUP_RAID6, }; /* * Table to convert BTRFS_RAID_* to the error code if minimum number of devices * condition is not met. Zero means there's no corresponding * BTRFS_ERROR_DEV_*_NOT_MET value. */ const int btrfs_raid_mindev_error[BTRFS_NR_RAID_TYPES] = { [BTRFS_RAID_RAID10] = BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET, [BTRFS_RAID_RAID1] = BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET, [BTRFS_RAID_DUP] = 0, [BTRFS_RAID_RAID0] = 0, [BTRFS_RAID_SINGLE] = 0, [BTRFS_RAID_RAID5] = BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET, [BTRFS_RAID_RAID6] = BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET, }; static int init_first_rw_device(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_device *device); Loading Loading @@ -699,6 +714,7 @@ static noinline int device_list_add(const char *path, * if there is new btrfs on an already registered device, * then remove the stale device entry. */ if (ret > 0) btrfs_free_stale_device(device); *fs_devices_ret = fs_devices; Loading Loading @@ -988,6 +1004,56 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, return ret; } void btrfs_release_disk_super(struct page *page) { kunmap(page); put_page(page); } int btrfs_read_disk_super(struct block_device *bdev, u64 bytenr, struct page **page, struct btrfs_super_block **disk_super) { void *p; pgoff_t index; /* make sure our super fits in the device */ if (bytenr + PAGE_SIZE >= i_size_read(bdev->bd_inode)) return 1; /* make sure our super fits in the page */ if (sizeof(**disk_super) > PAGE_SIZE) return 1; /* make sure our super doesn't straddle pages on disk */ index = bytenr >> PAGE_SHIFT; if ((bytenr + sizeof(**disk_super) - 1) >> PAGE_SHIFT != index) return 1; /* pull in the page with our super */ *page = read_cache_page_gfp(bdev->bd_inode->i_mapping, index, GFP_KERNEL); if (IS_ERR_OR_NULL(*page)) return 1; p = kmap(*page); /* align our pointer to the offset of the super block */ *disk_super = p + (bytenr & ~PAGE_MASK); if (btrfs_super_bytenr(*disk_super) != bytenr || btrfs_super_magic(*disk_super) != BTRFS_MAGIC) { btrfs_release_disk_super(*page); return 1; } if ((*disk_super)->label[0] && (*disk_super)->label[BTRFS_LABEL_SIZE - 1]) (*disk_super)->label[BTRFS_LABEL_SIZE - 1] = '\0'; return 0; } /* * Look for a btrfs signature on a device. This may be called out of the mount path * and we are not allowed to call set_blocksize during the scan. The superblock Loading @@ -999,13 +1065,11 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, struct btrfs_super_block *disk_super; struct block_device *bdev; struct page *page; void *p; int ret = -EINVAL; u64 devid; u64 transid; u64 total_devices; u64 bytenr; pgoff_t index; /* * we would like to check all the supers, but that would make Loading @@ -1018,41 +1082,14 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, mutex_lock(&uuid_mutex); bdev = blkdev_get_by_path(path, flags, holder); if (IS_ERR(bdev)) { ret = PTR_ERR(bdev); goto error; } /* make sure our super fits in the device */ if (bytenr + PAGE_SIZE >= i_size_read(bdev->bd_inode)) goto error_bdev_put; /* make sure our super fits in the page */ if (sizeof(*disk_super) > PAGE_SIZE) goto error_bdev_put; /* make sure our super doesn't straddle pages on disk */ index = bytenr >> PAGE_SHIFT; if ((bytenr + sizeof(*disk_super) - 1) >> PAGE_SHIFT != index) goto error_bdev_put; /* pull in the page with our super */ page = read_cache_page_gfp(bdev->bd_inode->i_mapping, index, GFP_NOFS); if (IS_ERR_OR_NULL(page)) if (btrfs_read_disk_super(bdev, bytenr, &page, &disk_super)) goto error_bdev_put; p = kmap(page); /* align our pointer to the offset of the super block */ disk_super = p + (bytenr & ~PAGE_MASK); if (btrfs_super_bytenr(disk_super) != bytenr || btrfs_super_magic(disk_super) != BTRFS_MAGIC) goto error_unmap; devid = btrfs_stack_device_id(&disk_super->dev_item); transid = btrfs_super_generation(disk_super); total_devices = btrfs_super_num_devices(disk_super); Loading @@ -1060,8 +1097,6 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, ret = device_list_add(path, disk_super, devid, fs_devices_ret); if (ret > 0) { if (disk_super->label[0]) { if (disk_super->label[BTRFS_LABEL_SIZE - 1]) disk_super->label[BTRFS_LABEL_SIZE - 1] = '\0'; printk(KERN_INFO "BTRFS: device label %s ", disk_super->label); } else { printk(KERN_INFO "BTRFS: device fsid %pU ", disk_super->fsid); Loading @@ -1073,9 +1108,7 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, if (!ret && fs_devices_ret) (*fs_devices_ret)->total_devices = total_devices; error_unmap: kunmap(page); put_page(page); btrfs_release_disk_super(page); error_bdev_put: blkdev_put(bdev, flags); Loading Loading @@ -1688,112 +1721,117 @@ static int btrfs_rm_dev_item(struct btrfs_root *root, return ret; } int btrfs_rm_device(struct btrfs_root *root, char *device_path) /* * Verify that @num_devices satisfies the RAID profile constraints in the whole * filesystem. It's up to the caller to adjust that number regarding eg. device * replace. */ static int btrfs_check_raid_min_devices(struct btrfs_fs_info *fs_info, u64 num_devices) { struct btrfs_device *device; struct btrfs_device *next_device; struct block_device *bdev; struct buffer_head *bh = NULL; struct btrfs_super_block *disk_super; struct btrfs_fs_devices *cur_devices; u64 all_avail; u64 devid; u64 num_devices; u8 *dev_uuid; unsigned seq; int ret = 0; bool clear_super = false; mutex_lock(&uuid_mutex); int i; do { seq = read_seqbegin(&root->fs_info->profiles_lock); seq = read_seqbegin(&fs_info->profiles_lock); all_avail = root->fs_info->avail_data_alloc_bits | root->fs_info->avail_system_alloc_bits | root->fs_info->avail_metadata_alloc_bits; } while (read_seqretry(&root->fs_info->profiles_lock, seq)); all_avail = fs_info->avail_data_alloc_bits | fs_info->avail_system_alloc_bits | fs_info->avail_metadata_alloc_bits; } while (read_seqretry(&fs_info->profiles_lock, seq)); num_devices = root->fs_info->fs_devices->num_devices; btrfs_dev_replace_lock(&root->fs_info->dev_replace, 0); if (btrfs_dev_replace_is_ongoing(&root->fs_info->dev_replace)) { WARN_ON(num_devices < 1); num_devices--; } btrfs_dev_replace_unlock(&root->fs_info->dev_replace, 0); for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) { if (!(all_avail & btrfs_raid_group[i])) continue; if ((all_avail & BTRFS_BLOCK_GROUP_RAID10) && num_devices <= 4) { ret = BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET; goto out; } if (num_devices < btrfs_raid_array[i].devs_min) { int ret = btrfs_raid_mindev_error[i]; if ((all_avail & BTRFS_BLOCK_GROUP_RAID1) && num_devices <= 2) { ret = BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET; goto out; if (ret) return ret; } } if ((all_avail & BTRFS_BLOCK_GROUP_RAID5) && root->fs_info->fs_devices->rw_devices <= 2) { ret = BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET; goto out; return 0; } if ((all_avail & BTRFS_BLOCK_GROUP_RAID6) && root->fs_info->fs_devices->rw_devices <= 3) { ret = BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET; goto out; struct btrfs_device *btrfs_find_next_active_device(struct btrfs_fs_devices *fs_devs, struct btrfs_device *device) { struct btrfs_device *next_device; list_for_each_entry(next_device, &fs_devs->devices, dev_list) { if (next_device != device && !next_device->missing && next_device->bdev) return next_device; } if (strcmp(device_path, "missing") == 0) { struct list_head *devices; struct btrfs_device *tmp; return NULL; } device = NULL; devices = &root->fs_info->fs_devices->devices; /* * It is safe to read the devices since the volume_mutex * is held. * Helper function to check if the given device is part of s_bdev / latest_bdev * and replace it with the provided or the next active device, in the context * where this function called, there should be always be another device (or * this_dev) which is active. */ list_for_each_entry(tmp, devices, dev_list) { if (tmp->in_fs_metadata && !tmp->is_tgtdev_for_dev_replace && !tmp->bdev) { device = tmp; break; void btrfs_assign_next_active_device(struct btrfs_fs_info *fs_info, struct btrfs_device *device, struct btrfs_device *this_dev) { struct btrfs_device *next_device; if (this_dev) next_device = this_dev; else next_device = btrfs_find_next_active_device(fs_info->fs_devices, device); ASSERT(next_device); if (fs_info->sb->s_bdev && (fs_info->sb->s_bdev == device->bdev)) fs_info->sb->s_bdev = next_device->bdev; if (fs_info->fs_devices->latest_bdev == device->bdev) fs_info->fs_devices->latest_bdev = next_device->bdev; } int btrfs_rm_device(struct btrfs_root *root, char *device_path, u64 devid) { struct btrfs_device *device; struct btrfs_fs_devices *cur_devices; u64 num_devices; int ret = 0; bool clear_super = false; char *dev_name = NULL; mutex_lock(&uuid_mutex); num_devices = root->fs_info->fs_devices->num_devices; btrfs_dev_replace_lock(&root->fs_info->dev_replace, 0); if (btrfs_dev_replace_is_ongoing(&root->fs_info->dev_replace)) { WARN_ON(num_devices < 1); num_devices--; } bdev = NULL; bh = NULL; disk_super = NULL; if (!device) { ret = BTRFS_ERROR_DEV_MISSING_NOT_FOUND; btrfs_dev_replace_unlock(&root->fs_info->dev_replace, 0); ret = btrfs_check_raid_min_devices(root->fs_info, num_devices - 1); if (ret) goto out; } } else { ret = btrfs_get_bdev_and_sb(device_path, FMODE_WRITE | FMODE_EXCL, root->fs_info->bdev_holder, 0, &bdev, &bh); ret = btrfs_find_device_by_devspec(root, devid, device_path, &device); if (ret) goto out; disk_super = (struct btrfs_super_block *)bh->b_data; devid = btrfs_stack_device_id(&disk_super->dev_item); dev_uuid = disk_super->dev_item.uuid; device = btrfs_find_device(root->fs_info, devid, dev_uuid, disk_super->fsid); if (!device) { ret = -ENOENT; goto error_brelse; } } if (device->is_tgtdev_for_dev_replace) { ret = BTRFS_ERROR_DEV_TGT_REPLACE; goto error_brelse; goto out; } if (device->writeable && root->fs_info->fs_devices->rw_devices == 1) { ret = BTRFS_ERROR_DEV_ONLY_WRITABLE; goto error_brelse; goto out; } if (device->writeable) { Loading @@ -1801,6 +1839,11 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) list_del_init(&device->dev_alloc_list); device->fs_devices->rw_devices--; unlock_chunks(root); dev_name = kstrdup(device->name->str, GFP_KERNEL); if (!dev_name) { ret = -ENOMEM; goto error_undo; } clear_super = true; } Loading Loading @@ -1842,12 +1885,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) if (device->missing) device->fs_devices->missing_devices--; next_device = list_entry(root->fs_info->fs_devices->devices.next, struct btrfs_device, dev_list); if (device->bdev == root->fs_info->sb->s_bdev) root->fs_info->sb->s_bdev = next_device->bdev; if (device->bdev == root->fs_info->fs_devices->latest_bdev) root->fs_info->fs_devices->latest_bdev = next_device->bdev; btrfs_assign_next_active_device(root->fs_info, device, NULL); if (device->bdev) { device->fs_devices->open_devices--; Loading Loading @@ -1883,63 +1921,23 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) * at this point, the device is zero sized. We want to * remove it from the devices list and zero out the old super */ if (clear_super && disk_super) { u64 bytenr; int i; /* make sure this device isn't detected as part of * the FS anymore */ memset(&disk_super->magic, 0, sizeof(disk_super->magic)); set_buffer_dirty(bh); sync_dirty_buffer(bh); /* clear the mirror copies of super block on the disk * being removed, 0th copy is been taken care above and * the below would take of the rest */ for (i = 1; i < BTRFS_SUPER_MIRROR_MAX; i++) { bytenr = btrfs_sb_offset(i); if (bytenr + BTRFS_SUPER_INFO_SIZE >= i_size_read(bdev->bd_inode)) break; brelse(bh); bh = __bread(bdev, bytenr / 4096, BTRFS_SUPER_INFO_SIZE); if (!bh) continue; disk_super = (struct btrfs_super_block *)bh->b_data; if (clear_super) { struct block_device *bdev; if (btrfs_super_bytenr(disk_super) != bytenr || btrfs_super_magic(disk_super) != BTRFS_MAGIC) { continue; } memset(&disk_super->magic, 0, sizeof(disk_super->magic)); set_buffer_dirty(bh); sync_dirty_buffer(bh); bdev = blkdev_get_by_path(dev_name, FMODE_READ | FMODE_EXCL, root->fs_info->bdev_holder); if (!IS_ERR(bdev)) { btrfs_scratch_superblocks(bdev, dev_name); blkdev_put(bdev, FMODE_READ | FMODE_EXCL); } } ret = 0; if (bdev) { /* Notify udev that device has changed */ btrfs_kobject_uevent(bdev, KOBJ_CHANGE); /* Update ctime/mtime for device path for libblkid */ update_dev_time(device_path); } error_brelse: brelse(bh); if (bdev) blkdev_put(bdev, FMODE_READ | FMODE_EXCL); out: kfree(dev_name); mutex_unlock(&uuid_mutex); return ret; error_undo: if (device->writeable) { lock_chunks(root); Loading @@ -1948,7 +1946,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) device->fs_devices->rw_devices++; unlock_chunks(root); } goto error_brelse; goto out; } void btrfs_rm_dev_replace_remove_srcdev(struct btrfs_fs_info *fs_info, Loading Loading @@ -2017,8 +2015,6 @@ void btrfs_rm_dev_replace_free_srcdev(struct btrfs_fs_info *fs_info, void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, struct btrfs_device *tgtdev) { struct btrfs_device *next_device; mutex_lock(&uuid_mutex); WARN_ON(!tgtdev); mutex_lock(&fs_info->fs_devices->device_list_mutex); Loading @@ -2030,12 +2026,8 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, fs_info->fs_devices->num_devices--; next_device = list_entry(fs_info->fs_devices->devices.next, struct btrfs_device, dev_list); if (tgtdev->bdev == fs_info->sb->s_bdev) fs_info->sb->s_bdev = next_device->bdev; if (tgtdev->bdev == fs_info->fs_devices->latest_bdev) fs_info->fs_devices->latest_bdev = next_device->bdev; btrfs_assign_next_active_device(fs_info, tgtdev, NULL); list_del_rcu(&tgtdev->dev_list); mutex_unlock(&fs_info->fs_devices->device_list_mutex); Loading Loading @@ -2109,6 +2101,31 @@ int btrfs_find_device_missing_or_by_path(struct btrfs_root *root, } } /* * Lookup a device given by device id, or the path if the id is 0. */ int btrfs_find_device_by_devspec(struct btrfs_root *root, u64 devid, char *devpath, struct btrfs_device **device) { int ret; if (devid) { ret = 0; *device = btrfs_find_device(root->fs_info, devid, NULL, NULL); if (!*device) ret = -ENOENT; } else { if (!devpath || !devpath[0]) return -EINVAL; ret = btrfs_find_device_missing_or_by_path(root, devpath, device); } return ret; } /* * does all the dirty work required for changing file system's UUID. */ Loading fs/btrfs/volumes.h +7 −2 Original line number Diff line number Diff line Loading @@ -340,7 +340,7 @@ struct btrfs_raid_attr { }; extern const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES]; extern const int btrfs_raid_mindev_error[BTRFS_NR_RAID_TYPES]; extern const u64 btrfs_raid_group[BTRFS_NR_RAID_TYPES]; struct map_lookup { Loading Loading @@ -445,13 +445,18 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, struct btrfs_fs_devices **fs_devices_ret); int btrfs_close_devices(struct btrfs_fs_devices *fs_devices); void btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices, int step); void btrfs_assign_next_active_device(struct btrfs_fs_info *fs_info, struct btrfs_device *device, struct btrfs_device *this_dev); int btrfs_find_device_missing_or_by_path(struct btrfs_root *root, char *device_path, struct btrfs_device **device); int btrfs_find_device_by_devspec(struct btrfs_root *root, u64 devid, char *devpath, struct btrfs_device **device); struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info, const u64 *devid, const u8 *uuid); int btrfs_rm_device(struct btrfs_root *root, char *device_path); int btrfs_rm_device(struct btrfs_root *root, char *device_path, u64 devid); void btrfs_cleanup_fs_uuids(void); int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len); int btrfs_grow_device(struct btrfs_trans_handle *trans, Loading Loading
fs/btrfs/dev-replace.c +44 −55 Original line number Diff line number Diff line Loading @@ -44,9 +44,6 @@ static void btrfs_dev_replace_update_device_in_mapping_tree( struct btrfs_fs_info *fs_info, struct btrfs_device *srcdev, struct btrfs_device *tgtdev); static int btrfs_dev_replace_find_srcdev(struct btrfs_root *root, u64 srcdevid, char *srcdev_name, struct btrfs_device **device); static u64 __btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info); static int btrfs_dev_replace_kthread(void *data); static int btrfs_dev_replace_continue_on_mount(struct btrfs_fs_info *fs_info); Loading Loading @@ -305,8 +302,8 @@ void btrfs_after_dev_replace_commit(struct btrfs_fs_info *fs_info) dev_replace->cursor_left_last_write_of_item; } int btrfs_dev_replace_start(struct btrfs_root *root, struct btrfs_ioctl_dev_replace_args *args) 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; Loading @@ -315,29 +312,16 @@ int btrfs_dev_replace_start(struct btrfs_root *root, 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; /* 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); 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, args->start.tgtdev_name, ret = btrfs_init_dev_replace_tgtdev(root, tgtdev_name, src_device, &tgt_device); mutex_unlock(&fs_info->volume_mutex); if (ret) Loading @@ -364,18 +348,17 @@ int btrfs_dev_replace_start(struct btrfs_root *root, break; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED: args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED; ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED; goto leave; } dev_replace->cont_reading_from_srcdev_mode = args->start.cont_reading_from_srcdev_mode; 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(root->fs_info, 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), Loading @@ -396,14 +379,13 @@ int btrfs_dev_replace_start(struct btrfs_root *root, dev_replace->item_needs_writeback = 1; atomic64_set(&dev_replace->num_write_errors, 0); atomic64_set(&dev_replace->num_uncorrectable_read_errors, 0); args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR; btrfs_dev_replace_unlock(dev_replace, 1); ret = btrfs_sysfs_add_device_link(tgt_device->fs_devices, tgt_device); if (ret) btrfs_err(root->fs_info, "kobj add dev failed %d\n", ret); btrfs_err(fs_info, "kobj add dev failed %d\n", ret); btrfs_wait_ordered_roots(root->fs_info, -1); btrfs_wait_ordered_roots(fs_info, -1); /* force writing the updated state information to disk */ trans = btrfs_start_transaction(root, 0); Loading @@ -421,11 +403,9 @@ int btrfs_dev_replace_start(struct btrfs_root *root, 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 */ ret = btrfs_dev_replace_finishing(fs_info, ret); if (ret == -EINPROGRESS) { args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS; ret = 0; ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS; } else { WARN_ON(ret); } Loading @@ -440,6 +420,35 @@ int btrfs_dev_replace_start(struct btrfs_root *root, return ret; } int btrfs_dev_replace_by_ioctl(struct btrfs_root *root, struct btrfs_ioctl_dev_replace_args *args) { int ret; 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; ret = btrfs_dev_replace_start(root, args->start.tgtdev_name, args->start.srcdevid, args->start.srcdev_name, args->start.cont_reading_from_srcdev_mode); args->result = ret; /* don't warn if EINPROGRESS, someone else might be running scrub */ if (ret == BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS) ret = 0; return ret; } /* * blocked until all flighting bios are finished. */ Loading Loading @@ -560,10 +569,9 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, 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; 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; btrfs_assign_next_active_device(fs_info, src_device, tgt_device); list_add(&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list); fs_info->fs_devices->rw_devices++; Loading Loading @@ -626,25 +634,6 @@ static void btrfs_dev_replace_update_device_in_mapping_tree( write_unlock(&em_tree->lock); } static int btrfs_dev_replace_find_srcdev(struct btrfs_root *root, u64 srcdevid, char *srcdev_name, struct btrfs_device **device) { int ret; if (srcdevid) { ret = 0; *device = btrfs_find_device(root->fs_info, srcdevid, NULL, NULL); if (!*device) ret = -ENOENT; } else { ret = btrfs_find_device_missing_or_by_path(root, srcdev_name, device); } return ret; } void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info, struct btrfs_ioctl_dev_replace_args *args) { Loading
fs/btrfs/dev-replace.h +3 −1 Original line number Diff line number Diff line Loading @@ -25,8 +25,10 @@ int btrfs_init_dev_replace(struct btrfs_fs_info *fs_info); int btrfs_run_dev_replace(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info); void btrfs_after_dev_replace_commit(struct btrfs_fs_info *fs_info); int btrfs_dev_replace_start(struct btrfs_root *root, int btrfs_dev_replace_by_ioctl(struct btrfs_root *root, struct btrfs_ioctl_dev_replace_args *args); int btrfs_dev_replace_start(struct btrfs_root *root, char *tgtdev_name, u64 srcdevid, char *srcdev_name, int read_src); void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info, struct btrfs_ioctl_dev_replace_args *args); int btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info, Loading
fs/btrfs/ioctl.c +58 −2 Original line number Diff line number Diff line Loading @@ -2676,6 +2676,60 @@ static long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg) return ret; } static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg) { struct btrfs_root *root = BTRFS_I(file_inode(file))->root; struct btrfs_ioctl_vol_args_v2 *vol_args; int ret; if (!capable(CAP_SYS_ADMIN)) return -EPERM; ret = mnt_want_write_file(file); if (ret) return ret; vol_args = memdup_user(arg, sizeof(*vol_args)); if (IS_ERR(vol_args)) { ret = PTR_ERR(vol_args); goto err_drop; } /* Check for compatibility reject unknown flags */ if (vol_args->flags & ~BTRFS_VOL_ARG_V2_FLAGS_SUPPORTED) return -EOPNOTSUPP; if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running, 1)) { ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; goto out; } mutex_lock(&root->fs_info->volume_mutex); if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) { ret = btrfs_rm_device(root, NULL, vol_args->devid); } else { vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0'; ret = btrfs_rm_device(root, vol_args->name, 0); } mutex_unlock(&root->fs_info->volume_mutex); atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); if (!ret) { if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) btrfs_info(root->fs_info, "device deleted: id %llu", vol_args->devid); else btrfs_info(root->fs_info, "device deleted: %s", vol_args->name); } out: kfree(vol_args); err_drop: mnt_drop_write_file(file); return ret; } static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) { struct btrfs_root *root = BTRFS_I(file_inode(file))->root; Loading Loading @@ -2703,7 +2757,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; mutex_lock(&root->fs_info->volume_mutex); ret = btrfs_rm_device(root, vol_args->name); ret = btrfs_rm_device(root, vol_args->name, 0); mutex_unlock(&root->fs_info->volume_mutex); if (!ret) Loading Loading @@ -4387,7 +4441,7 @@ static long btrfs_ioctl_dev_replace(struct btrfs_root *root, void __user *arg) 1)) { ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; } else { ret = btrfs_dev_replace_start(root, p); ret = btrfs_dev_replace_by_ioctl(root, p); atomic_set( &root->fs_info->mutually_exclusive_operation_running, 0); Loading Loading @@ -5480,6 +5534,8 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_add_dev(root, argp); case BTRFS_IOC_RM_DEV: return btrfs_ioctl_rm_dev(file, argp); case BTRFS_IOC_RM_DEV_V2: return btrfs_ioctl_rm_dev_v2(file, argp); case BTRFS_IOC_FS_INFO: return btrfs_ioctl_fs_info(root, argp); case BTRFS_IOC_DEV_INFO: Loading
fs/btrfs/volumes.c +198 −181 Original line number Diff line number Diff line Loading @@ -118,6 +118,21 @@ const u64 btrfs_raid_group[BTRFS_NR_RAID_TYPES] = { [BTRFS_RAID_RAID6] = BTRFS_BLOCK_GROUP_RAID6, }; /* * Table to convert BTRFS_RAID_* to the error code if minimum number of devices * condition is not met. Zero means there's no corresponding * BTRFS_ERROR_DEV_*_NOT_MET value. */ const int btrfs_raid_mindev_error[BTRFS_NR_RAID_TYPES] = { [BTRFS_RAID_RAID10] = BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET, [BTRFS_RAID_RAID1] = BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET, [BTRFS_RAID_DUP] = 0, [BTRFS_RAID_RAID0] = 0, [BTRFS_RAID_SINGLE] = 0, [BTRFS_RAID_RAID5] = BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET, [BTRFS_RAID_RAID6] = BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET, }; static int init_first_rw_device(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_device *device); Loading Loading @@ -699,6 +714,7 @@ static noinline int device_list_add(const char *path, * if there is new btrfs on an already registered device, * then remove the stale device entry. */ if (ret > 0) btrfs_free_stale_device(device); *fs_devices_ret = fs_devices; Loading Loading @@ -988,6 +1004,56 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, return ret; } void btrfs_release_disk_super(struct page *page) { kunmap(page); put_page(page); } int btrfs_read_disk_super(struct block_device *bdev, u64 bytenr, struct page **page, struct btrfs_super_block **disk_super) { void *p; pgoff_t index; /* make sure our super fits in the device */ if (bytenr + PAGE_SIZE >= i_size_read(bdev->bd_inode)) return 1; /* make sure our super fits in the page */ if (sizeof(**disk_super) > PAGE_SIZE) return 1; /* make sure our super doesn't straddle pages on disk */ index = bytenr >> PAGE_SHIFT; if ((bytenr + sizeof(**disk_super) - 1) >> PAGE_SHIFT != index) return 1; /* pull in the page with our super */ *page = read_cache_page_gfp(bdev->bd_inode->i_mapping, index, GFP_KERNEL); if (IS_ERR_OR_NULL(*page)) return 1; p = kmap(*page); /* align our pointer to the offset of the super block */ *disk_super = p + (bytenr & ~PAGE_MASK); if (btrfs_super_bytenr(*disk_super) != bytenr || btrfs_super_magic(*disk_super) != BTRFS_MAGIC) { btrfs_release_disk_super(*page); return 1; } if ((*disk_super)->label[0] && (*disk_super)->label[BTRFS_LABEL_SIZE - 1]) (*disk_super)->label[BTRFS_LABEL_SIZE - 1] = '\0'; return 0; } /* * Look for a btrfs signature on a device. This may be called out of the mount path * and we are not allowed to call set_blocksize during the scan. The superblock Loading @@ -999,13 +1065,11 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, struct btrfs_super_block *disk_super; struct block_device *bdev; struct page *page; void *p; int ret = -EINVAL; u64 devid; u64 transid; u64 total_devices; u64 bytenr; pgoff_t index; /* * we would like to check all the supers, but that would make Loading @@ -1018,41 +1082,14 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, mutex_lock(&uuid_mutex); bdev = blkdev_get_by_path(path, flags, holder); if (IS_ERR(bdev)) { ret = PTR_ERR(bdev); goto error; } /* make sure our super fits in the device */ if (bytenr + PAGE_SIZE >= i_size_read(bdev->bd_inode)) goto error_bdev_put; /* make sure our super fits in the page */ if (sizeof(*disk_super) > PAGE_SIZE) goto error_bdev_put; /* make sure our super doesn't straddle pages on disk */ index = bytenr >> PAGE_SHIFT; if ((bytenr + sizeof(*disk_super) - 1) >> PAGE_SHIFT != index) goto error_bdev_put; /* pull in the page with our super */ page = read_cache_page_gfp(bdev->bd_inode->i_mapping, index, GFP_NOFS); if (IS_ERR_OR_NULL(page)) if (btrfs_read_disk_super(bdev, bytenr, &page, &disk_super)) goto error_bdev_put; p = kmap(page); /* align our pointer to the offset of the super block */ disk_super = p + (bytenr & ~PAGE_MASK); if (btrfs_super_bytenr(disk_super) != bytenr || btrfs_super_magic(disk_super) != BTRFS_MAGIC) goto error_unmap; devid = btrfs_stack_device_id(&disk_super->dev_item); transid = btrfs_super_generation(disk_super); total_devices = btrfs_super_num_devices(disk_super); Loading @@ -1060,8 +1097,6 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, ret = device_list_add(path, disk_super, devid, fs_devices_ret); if (ret > 0) { if (disk_super->label[0]) { if (disk_super->label[BTRFS_LABEL_SIZE - 1]) disk_super->label[BTRFS_LABEL_SIZE - 1] = '\0'; printk(KERN_INFO "BTRFS: device label %s ", disk_super->label); } else { printk(KERN_INFO "BTRFS: device fsid %pU ", disk_super->fsid); Loading @@ -1073,9 +1108,7 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, if (!ret && fs_devices_ret) (*fs_devices_ret)->total_devices = total_devices; error_unmap: kunmap(page); put_page(page); btrfs_release_disk_super(page); error_bdev_put: blkdev_put(bdev, flags); Loading Loading @@ -1688,112 +1721,117 @@ static int btrfs_rm_dev_item(struct btrfs_root *root, return ret; } int btrfs_rm_device(struct btrfs_root *root, char *device_path) /* * Verify that @num_devices satisfies the RAID profile constraints in the whole * filesystem. It's up to the caller to adjust that number regarding eg. device * replace. */ static int btrfs_check_raid_min_devices(struct btrfs_fs_info *fs_info, u64 num_devices) { struct btrfs_device *device; struct btrfs_device *next_device; struct block_device *bdev; struct buffer_head *bh = NULL; struct btrfs_super_block *disk_super; struct btrfs_fs_devices *cur_devices; u64 all_avail; u64 devid; u64 num_devices; u8 *dev_uuid; unsigned seq; int ret = 0; bool clear_super = false; mutex_lock(&uuid_mutex); int i; do { seq = read_seqbegin(&root->fs_info->profiles_lock); seq = read_seqbegin(&fs_info->profiles_lock); all_avail = root->fs_info->avail_data_alloc_bits | root->fs_info->avail_system_alloc_bits | root->fs_info->avail_metadata_alloc_bits; } while (read_seqretry(&root->fs_info->profiles_lock, seq)); all_avail = fs_info->avail_data_alloc_bits | fs_info->avail_system_alloc_bits | fs_info->avail_metadata_alloc_bits; } while (read_seqretry(&fs_info->profiles_lock, seq)); num_devices = root->fs_info->fs_devices->num_devices; btrfs_dev_replace_lock(&root->fs_info->dev_replace, 0); if (btrfs_dev_replace_is_ongoing(&root->fs_info->dev_replace)) { WARN_ON(num_devices < 1); num_devices--; } btrfs_dev_replace_unlock(&root->fs_info->dev_replace, 0); for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) { if (!(all_avail & btrfs_raid_group[i])) continue; if ((all_avail & BTRFS_BLOCK_GROUP_RAID10) && num_devices <= 4) { ret = BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET; goto out; } if (num_devices < btrfs_raid_array[i].devs_min) { int ret = btrfs_raid_mindev_error[i]; if ((all_avail & BTRFS_BLOCK_GROUP_RAID1) && num_devices <= 2) { ret = BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET; goto out; if (ret) return ret; } } if ((all_avail & BTRFS_BLOCK_GROUP_RAID5) && root->fs_info->fs_devices->rw_devices <= 2) { ret = BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET; goto out; return 0; } if ((all_avail & BTRFS_BLOCK_GROUP_RAID6) && root->fs_info->fs_devices->rw_devices <= 3) { ret = BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET; goto out; struct btrfs_device *btrfs_find_next_active_device(struct btrfs_fs_devices *fs_devs, struct btrfs_device *device) { struct btrfs_device *next_device; list_for_each_entry(next_device, &fs_devs->devices, dev_list) { if (next_device != device && !next_device->missing && next_device->bdev) return next_device; } if (strcmp(device_path, "missing") == 0) { struct list_head *devices; struct btrfs_device *tmp; return NULL; } device = NULL; devices = &root->fs_info->fs_devices->devices; /* * It is safe to read the devices since the volume_mutex * is held. * Helper function to check if the given device is part of s_bdev / latest_bdev * and replace it with the provided or the next active device, in the context * where this function called, there should be always be another device (or * this_dev) which is active. */ list_for_each_entry(tmp, devices, dev_list) { if (tmp->in_fs_metadata && !tmp->is_tgtdev_for_dev_replace && !tmp->bdev) { device = tmp; break; void btrfs_assign_next_active_device(struct btrfs_fs_info *fs_info, struct btrfs_device *device, struct btrfs_device *this_dev) { struct btrfs_device *next_device; if (this_dev) next_device = this_dev; else next_device = btrfs_find_next_active_device(fs_info->fs_devices, device); ASSERT(next_device); if (fs_info->sb->s_bdev && (fs_info->sb->s_bdev == device->bdev)) fs_info->sb->s_bdev = next_device->bdev; if (fs_info->fs_devices->latest_bdev == device->bdev) fs_info->fs_devices->latest_bdev = next_device->bdev; } int btrfs_rm_device(struct btrfs_root *root, char *device_path, u64 devid) { struct btrfs_device *device; struct btrfs_fs_devices *cur_devices; u64 num_devices; int ret = 0; bool clear_super = false; char *dev_name = NULL; mutex_lock(&uuid_mutex); num_devices = root->fs_info->fs_devices->num_devices; btrfs_dev_replace_lock(&root->fs_info->dev_replace, 0); if (btrfs_dev_replace_is_ongoing(&root->fs_info->dev_replace)) { WARN_ON(num_devices < 1); num_devices--; } bdev = NULL; bh = NULL; disk_super = NULL; if (!device) { ret = BTRFS_ERROR_DEV_MISSING_NOT_FOUND; btrfs_dev_replace_unlock(&root->fs_info->dev_replace, 0); ret = btrfs_check_raid_min_devices(root->fs_info, num_devices - 1); if (ret) goto out; } } else { ret = btrfs_get_bdev_and_sb(device_path, FMODE_WRITE | FMODE_EXCL, root->fs_info->bdev_holder, 0, &bdev, &bh); ret = btrfs_find_device_by_devspec(root, devid, device_path, &device); if (ret) goto out; disk_super = (struct btrfs_super_block *)bh->b_data; devid = btrfs_stack_device_id(&disk_super->dev_item); dev_uuid = disk_super->dev_item.uuid; device = btrfs_find_device(root->fs_info, devid, dev_uuid, disk_super->fsid); if (!device) { ret = -ENOENT; goto error_brelse; } } if (device->is_tgtdev_for_dev_replace) { ret = BTRFS_ERROR_DEV_TGT_REPLACE; goto error_brelse; goto out; } if (device->writeable && root->fs_info->fs_devices->rw_devices == 1) { ret = BTRFS_ERROR_DEV_ONLY_WRITABLE; goto error_brelse; goto out; } if (device->writeable) { Loading @@ -1801,6 +1839,11 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) list_del_init(&device->dev_alloc_list); device->fs_devices->rw_devices--; unlock_chunks(root); dev_name = kstrdup(device->name->str, GFP_KERNEL); if (!dev_name) { ret = -ENOMEM; goto error_undo; } clear_super = true; } Loading Loading @@ -1842,12 +1885,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) if (device->missing) device->fs_devices->missing_devices--; next_device = list_entry(root->fs_info->fs_devices->devices.next, struct btrfs_device, dev_list); if (device->bdev == root->fs_info->sb->s_bdev) root->fs_info->sb->s_bdev = next_device->bdev; if (device->bdev == root->fs_info->fs_devices->latest_bdev) root->fs_info->fs_devices->latest_bdev = next_device->bdev; btrfs_assign_next_active_device(root->fs_info, device, NULL); if (device->bdev) { device->fs_devices->open_devices--; Loading Loading @@ -1883,63 +1921,23 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) * at this point, the device is zero sized. We want to * remove it from the devices list and zero out the old super */ if (clear_super && disk_super) { u64 bytenr; int i; /* make sure this device isn't detected as part of * the FS anymore */ memset(&disk_super->magic, 0, sizeof(disk_super->magic)); set_buffer_dirty(bh); sync_dirty_buffer(bh); /* clear the mirror copies of super block on the disk * being removed, 0th copy is been taken care above and * the below would take of the rest */ for (i = 1; i < BTRFS_SUPER_MIRROR_MAX; i++) { bytenr = btrfs_sb_offset(i); if (bytenr + BTRFS_SUPER_INFO_SIZE >= i_size_read(bdev->bd_inode)) break; brelse(bh); bh = __bread(bdev, bytenr / 4096, BTRFS_SUPER_INFO_SIZE); if (!bh) continue; disk_super = (struct btrfs_super_block *)bh->b_data; if (clear_super) { struct block_device *bdev; if (btrfs_super_bytenr(disk_super) != bytenr || btrfs_super_magic(disk_super) != BTRFS_MAGIC) { continue; } memset(&disk_super->magic, 0, sizeof(disk_super->magic)); set_buffer_dirty(bh); sync_dirty_buffer(bh); bdev = blkdev_get_by_path(dev_name, FMODE_READ | FMODE_EXCL, root->fs_info->bdev_holder); if (!IS_ERR(bdev)) { btrfs_scratch_superblocks(bdev, dev_name); blkdev_put(bdev, FMODE_READ | FMODE_EXCL); } } ret = 0; if (bdev) { /* Notify udev that device has changed */ btrfs_kobject_uevent(bdev, KOBJ_CHANGE); /* Update ctime/mtime for device path for libblkid */ update_dev_time(device_path); } error_brelse: brelse(bh); if (bdev) blkdev_put(bdev, FMODE_READ | FMODE_EXCL); out: kfree(dev_name); mutex_unlock(&uuid_mutex); return ret; error_undo: if (device->writeable) { lock_chunks(root); Loading @@ -1948,7 +1946,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) device->fs_devices->rw_devices++; unlock_chunks(root); } goto error_brelse; goto out; } void btrfs_rm_dev_replace_remove_srcdev(struct btrfs_fs_info *fs_info, Loading Loading @@ -2017,8 +2015,6 @@ void btrfs_rm_dev_replace_free_srcdev(struct btrfs_fs_info *fs_info, void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, struct btrfs_device *tgtdev) { struct btrfs_device *next_device; mutex_lock(&uuid_mutex); WARN_ON(!tgtdev); mutex_lock(&fs_info->fs_devices->device_list_mutex); Loading @@ -2030,12 +2026,8 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, fs_info->fs_devices->num_devices--; next_device = list_entry(fs_info->fs_devices->devices.next, struct btrfs_device, dev_list); if (tgtdev->bdev == fs_info->sb->s_bdev) fs_info->sb->s_bdev = next_device->bdev; if (tgtdev->bdev == fs_info->fs_devices->latest_bdev) fs_info->fs_devices->latest_bdev = next_device->bdev; btrfs_assign_next_active_device(fs_info, tgtdev, NULL); list_del_rcu(&tgtdev->dev_list); mutex_unlock(&fs_info->fs_devices->device_list_mutex); Loading Loading @@ -2109,6 +2101,31 @@ int btrfs_find_device_missing_or_by_path(struct btrfs_root *root, } } /* * Lookup a device given by device id, or the path if the id is 0. */ int btrfs_find_device_by_devspec(struct btrfs_root *root, u64 devid, char *devpath, struct btrfs_device **device) { int ret; if (devid) { ret = 0; *device = btrfs_find_device(root->fs_info, devid, NULL, NULL); if (!*device) ret = -ENOENT; } else { if (!devpath || !devpath[0]) return -EINVAL; ret = btrfs_find_device_missing_or_by_path(root, devpath, device); } return ret; } /* * does all the dirty work required for changing file system's UUID. */ Loading
fs/btrfs/volumes.h +7 −2 Original line number Diff line number Diff line Loading @@ -340,7 +340,7 @@ struct btrfs_raid_attr { }; extern const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES]; extern const int btrfs_raid_mindev_error[BTRFS_NR_RAID_TYPES]; extern const u64 btrfs_raid_group[BTRFS_NR_RAID_TYPES]; struct map_lookup { Loading Loading @@ -445,13 +445,18 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, struct btrfs_fs_devices **fs_devices_ret); int btrfs_close_devices(struct btrfs_fs_devices *fs_devices); void btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices, int step); void btrfs_assign_next_active_device(struct btrfs_fs_info *fs_info, struct btrfs_device *device, struct btrfs_device *this_dev); int btrfs_find_device_missing_or_by_path(struct btrfs_root *root, char *device_path, struct btrfs_device **device); int btrfs_find_device_by_devspec(struct btrfs_root *root, u64 devid, char *devpath, struct btrfs_device **device); struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info, const u64 *devid, const u8 *uuid); int btrfs_rm_device(struct btrfs_root *root, char *device_path); int btrfs_rm_device(struct btrfs_root *root, char *device_path, u64 devid); void btrfs_cleanup_fs_uuids(void); int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len); int btrfs_grow_device(struct btrfs_trans_handle *trans, Loading