summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/ctree.h2
-rw-r--r--fs/btrfs/inode.c23
-rw-r--r--fs/btrfs/ioctl.h6
-rw-r--r--fs/btrfs/super.c1
-rw-r--r--fs/btrfs/volumes.c75
-rw-r--r--fs/btrfs/volumes.h1
6 files changed, 108 insertions, 0 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index ac7106ec535..b9257b37bb9 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -551,6 +551,8 @@ struct btrfs_fs_info {
u64 data_alloc_profile;
u64 metadata_alloc_profile;
u64 system_alloc_profile;
+
+ void *bdev_holder;
};
/*
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index ab707c0930d..1a74b501869 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3070,6 +3070,27 @@ static int btrfs_ioctl_defrag(struct file *file)
return 0;
}
+long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg)
+{
+ struct btrfs_ioctl_vol_args *vol_args;
+ int ret;
+
+ vol_args = kmalloc(sizeof(*vol_args), GFP_NOFS);
+
+ if (!vol_args)
+ return -ENOMEM;
+
+ if (copy_from_user(vol_args, arg, sizeof(*vol_args))) {
+ ret = -EFAULT;
+ goto out;
+ }
+ ret = btrfs_init_new_device(root, vol_args->name);
+
+out:
+ kfree(vol_args);
+ return ret;
+}
+
long btrfs_ioctl(struct file *file, unsigned int
cmd, unsigned long arg)
{
@@ -3082,6 +3103,8 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_defrag(file);
case BTRFS_IOC_RESIZE:
return btrfs_ioctl_resize(root, (void __user *)arg);
+ case BTRFS_IOC_ADD_DEV:
+ return btrfs_ioctl_add_dev(root, (void __user *)arg);
}
return -ENOTTY;
diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h
index 4551e82013c..8ad35fc4ba5 100644
--- a/fs/btrfs/ioctl.h
+++ b/fs/btrfs/ioctl.h
@@ -36,4 +36,10 @@ struct btrfs_ioctl_vol_args {
struct btrfs_ioctl_vol_args)
#define BTRFS_IOC_SCAN_DEV _IOW(BTRFS_IOCTL_MAGIC, 4, \
struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_ADD_DEV _IOW(BTRFS_IOCTL_MAGIC, 10, \
+ struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_RM_DEV _IOW(BTRFS_IOCTL_MAGIC, 11, \
+ struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_BALANCE _IOW(BTRFS_IOCTL_MAGIC, 12, \
+ struct btrfs_ioctl_vol_args)
#endif
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 89286490688..7153dfaa340 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -388,6 +388,7 @@ int btrfs_get_sb_bdev(struct file_system_type *fs_type,
goto error;
}
+ btrfs_sb(s)->fs_info->bdev_holder = fs_type;
s->s_flags |= MS_ACTIVE;
}
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index a2c56de1548..b93c15aa17d 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -19,6 +19,7 @@
#include <linux/bio.h>
#include <linux/buffer_head.h>
#include <linux/blkdev.h>
+#include <linux/random.h>
#include <asm/div64.h>
#include "ctree.h"
#include "extent_map.h"
@@ -592,6 +593,80 @@ out:
return ret;
}
+int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_device *device;
+ struct block_device *bdev;
+ struct list_head *cur;
+ struct list_head *devices;
+ u64 total_bytes;
+ int ret = 0;
+
+
+ bdev = open_bdev_excl(device_path, 0, root->fs_info->bdev_holder);
+ if (!bdev) {
+ return -EIO;
+ }
+ mutex_lock(&root->fs_info->fs_mutex);
+ trans = btrfs_start_transaction(root, 1);
+ devices = &root->fs_info->fs_devices->devices;
+ list_for_each(cur, devices) {
+ device = list_entry(cur, struct btrfs_device, dev_list);
+ if (device->bdev == bdev) {
+ ret = -EEXIST;
+ goto out;
+ }
+ }
+
+ device = kzalloc(sizeof(*device), GFP_NOFS);
+ if (!device) {
+ /* we can safely leave the fs_devices entry around */
+ ret = -ENOMEM;
+ goto out_close_bdev;
+ }
+
+ device->barriers = 1;
+ generate_random_uuid(device->uuid);
+ spin_lock_init(&device->io_lock);
+ device->name = kstrdup(device_path, GFP_NOFS);
+ if (!device->name) {
+ kfree(device);
+ goto out_close_bdev;
+ }
+ device->io_width = root->sectorsize;
+ device->io_align = root->sectorsize;
+ device->sector_size = root->sectorsize;
+ device->total_bytes = i_size_read(bdev->bd_inode);
+ device->dev_root = root->fs_info->dev_root;
+ device->bdev = bdev;
+
+ ret = btrfs_add_device(trans, root, device);
+ if (ret)
+ goto out_close_bdev;
+
+ total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
+ btrfs_set_super_total_bytes(&root->fs_info->super_copy,
+ total_bytes + device->total_bytes);
+
+ total_bytes = btrfs_super_num_devices(&root->fs_info->super_copy);
+ btrfs_set_super_num_devices(&root->fs_info->super_copy,
+ total_bytes + 1);
+
+ list_add(&device->dev_list, &root->fs_info->fs_devices->devices);
+ list_add(&device->dev_alloc_list,
+ &root->fs_info->fs_devices->alloc_list);
+ root->fs_info->fs_devices->num_devices++;
+out:
+ btrfs_end_transaction(trans, root);
+ mutex_unlock(&root->fs_info->fs_mutex);
+ return ret;
+
+out_close_bdev:
+ close_bdev_excl(bdev);
+ goto out;
+}
+
int btrfs_update_device(struct btrfs_trans_handle *trans,
struct btrfs_device *device)
{
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 6fe8440b37a..6f173450378 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -133,4 +133,5 @@ int btrfs_grow_device(struct btrfs_trans_handle *trans,
struct btrfs_device *btrfs_find_device(struct btrfs_root *root, u64 devid,
u8 *uuid);
int btrfs_shrink_device(struct btrfs_device *device, u64 new_size);
+int btrfs_init_new_device(struct btrfs_root *root, char *path);
#endif