@@ -2243,6 +2243,46 @@ static noinline long btrfs_ioctl_wait_sync(struct file *file, void __user *argp)
return btrfs_wait_for_commit(root, transid);
}
+/* Balance the filesystem unconditionally */
+long btrfs_ioctl_balance(struct btrfs_fs_info *fs_info)
+{
+ return btrfs_balance(fs_info->dev_root, NULL);
+}
+
+/* Balance particular chunks in the filesystem */
+long btrfs_ioctl_balance_filtered(
+ struct btrfs_fs_info *fs_info,
+ struct btrfs_ioctl_balance_start __user *user_filters)
+{
+ int ret = 0;
+ struct btrfs_ioctl_balance_start *dest;
+
+ dest = kmalloc(sizeof(struct btrfs_ioctl_balance_start), GFP_KERNEL);
+ if (!dest)
+ return -ENOMEM;
+
+ if (copy_from_user(dest, user_filters, sizeof(struct btrfs_ioctl_balance_start))) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ printk("Starting balance with filter: %llx %llx %llx\n",
+ dest->flags, dest->chunk_type, dest->chunk_type_mask);
+
+ /* Basic sanity checking */
+ if (dest->flags & ~BTRFS_BALANCE_FILTER_MASK) {
+ ret = -ENOTSUPP;
+ goto error;
+ }
+
+ /* Do the balance */
+ ret = btrfs_balance(fs_info->dev_root, dest);
+
+error:
+ kfree(dest);
+ return ret;
+}
+
/*
* Return the current status of any balance operation
*/
@@ -2335,11 +2375,13 @@ long btrfs_ioctl(struct file *file, unsigned int
case BTRFS_IOC_RM_DEV:
return btrfs_ioctl_rm_dev(root, argp);
case BTRFS_IOC_BALANCE:
- return btrfs_balance(root->fs_info->dev_root);
+ return btrfs_ioctl_balance(root->fs_info);
case BTRFS_IOC_BALANCE_PROGRESS:
return btrfs_ioctl_balance_progress(root->fs_info, argp);
case BTRFS_IOC_BALANCE_CANCEL:
return btrfs_ioctl_balance_cancel(root->fs_info);
+ case BTRFS_IOC_BALANCE_FILTERED:
+ return btrfs_ioctl_balance_filtered(root->fs_info, argp);
case BTRFS_IOC_CLONE:
return btrfs_ioctl_clone(file, arg, 0, 0, 0);
case BTRFS_IOC_CLONE_RANGE:
@@ -154,6 +154,19 @@ struct btrfs_ioctl_balance_progress {
__u64 completed;
};
+/* Types of balance filter */
+#define BTRFS_BALANCE_FILTER_CHUNK_TYPE 0x1
+#define BTRFS_BALANCE_FILTER_MASK 0x1
+
+/* All the possible options for a filter */
+struct btrfs_ioctl_balance_start {
+ __u64 flags; /* Bit field indicating which fields of this struct are filled */
+
+ /* For FILTER_CHUNK_TYPE */
+ __u64 chunk_type; /* Flag bits required */
+ __u64 chunk_type_mask; /* Mask of bits to examine */
+};
+
#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
struct btrfs_ioctl_vol_args)
#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
@@ -201,4 +214,6 @@ struct btrfs_ioctl_balance_progress {
#define BTRFS_IOC_BALANCE_PROGRESS _IOR(BTRFS_IOCTL_MAGIC, 25, \
struct btrfs_ioctl_balance_progress)
#define BTRFS_IOC_BALANCE_CANCEL _IO(BTRFS_IOCTL_MAGIC, 26)
+#define BTRFS_IOC_BALANCE_FILTERED _IOW(BTRFS_IOCTL_MAGIC, 27, \
+ struct btrfs_ioctl_balance_start)
#endif
@@ -1899,7 +1899,38 @@ static u64 div_factor(u64 num, int factor)
return num;
}
-int btrfs_balance(struct btrfs_root *dev_root)
+int __balance_chunk_filters(
+ struct btrfs_ioctl_balance_start *filter,
+ struct extent_buffer *eb,
+ struct btrfs_chunk *chunk,
+ struct btrfs_key *key)
+{
+ if (filter == NULL) {
+ printk("Filter was NULL: pass all chunks\n");
+ return 1;
+ }
+
+ if (filter->flags == 0) {
+ printk("Filter was empty: pass all chunks\n");
+ return 1;
+ }
+
+ if (filter->flags & BTRFS_BALANCE_FILTER_CHUNK_TYPE) {
+ printk(KERN_INFO "btrfs: balance: Filtering chunk at %llu\n", key->offset);
+ printk(KERN_INFO "btrfs: balance: flags=%llx\n", btrfs_chunk_type(eb, chunk));
+ printk(KERN_INFO "btrfs: balance: to match type %llx\n", filter->chunk_type);
+ printk(KERN_INFO "btrfs: balance: mask %llx\n", filter->chunk_type_mask);
+ printk(KERN_INFO "btrfs: balance: not-mask %llx\n", ~filter->chunk_type_mask);
+ printk(KERN_INFO "btrfs: balance: masked flags %llx\n", (btrfs_chunk_type(eb, chunk) & filter->chunk_type_mask));
+ return (btrfs_chunk_type(eb, chunk) & filter->chunk_type_mask)
+ == filter->chunk_type;
+ }
+
+ return 0;
+}
+
+int btrfs_balance(struct btrfs_root *dev_root,
+ struct btrfs_ioctl_balance_start *filter)
{
int ret;
struct list_head *devices = &dev_root->fs_info->fs_devices->devices;
@@ -1912,6 +1943,9 @@ int btrfs_balance(struct btrfs_root *dev_root)
struct btrfs_trans_handle *trans;
struct btrfs_key found_key;
struct btrfs_balance_info *bal_info;
+ struct btrfs_chunk *chunk;
+
+ printk("Balance: filter pointer is %p\n", filter);
if (dev_root->fs_info->sb->s_flags & MS_RDONLY)
return -EROFS;
@@ -1980,6 +2014,15 @@ int btrfs_balance(struct btrfs_root *dev_root)
if (ret)
break;
+ btrfs_item_key_to_cpu(path->nodes[0], &found_key,
+ path->slots[0]);
+ chunk = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_chunk);
+ if (!__balance_chunk_filters(filter, path->nodes[0], chunk, &found_key)) {
+ printk(KERN_INFO "btrfs: balance (count): Filtering out chunk at %llu\n", found_key.offset);
+ continue;
+ }
+
spin_lock(&dev_root->fs_info->balance_info_lock);
bal_info->expected++;
spin_unlock(&dev_root->fs_info->balance_info_lock);
@@ -2023,18 +2066,27 @@ int btrfs_balance(struct btrfs_root *dev_root)
if (found_key.offset == 0)
break;
- btrfs_release_path(chunk_root, path);
- ret = btrfs_relocate_chunk(chunk_root,
- chunk_root->root_key.objectid,
- found_key.objectid,
- found_key.offset);
- BUG_ON(ret && ret != -ENOSPC);
+ chunk = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_chunk);
+ if (__balance_chunk_filters(filter, path->nodes[0], chunk, &found_key)) {
+ btrfs_release_path(chunk_root, path);
+ ret = btrfs_relocate_chunk(chunk_root,
+ chunk_root->root_key.objectid,
+ found_key.objectid,
+ found_key.offset);
+ BUG_ON(ret && ret != -ENOSPC);
+
+ spin_lock(&dev_root->fs_info->balance_info_lock);
+ bal_info->completed++;
+ spin_unlock(&dev_root->fs_info->balance_info_lock);
+ printk(KERN_INFO "btrfs: balance: %llu/%llu block groups completed\n",
+ bal_info->completed, bal_info->expected);
+ } else {
+ btrfs_release_path(chunk_root, path);
+ printk(KERN_INFO "btrfs: balance: Filtering out chunk at %llu\n", found_key.offset);
+ }
+
key.offset = found_key.offset - 1;
- spin_lock(&dev_root->fs_info->balance_info_lock);
- bal_info->completed++;
- spin_unlock(&dev_root->fs_info->balance_info_lock);
- printk(KERN_INFO "btrfs: balance: %llu/%llu block groups completed\n",
- bal_info->completed, bal_info->expected);
}
ret = 0;
if(bal_info->cancel_pending) {
@@ -21,6 +21,7 @@
#include <linux/bio.h>
#include "async-thread.h"
+#include "ioctl.h"
struct buffer_head;
struct btrfs_pending_bios {
@@ -179,7 +180,7 @@ struct btrfs_device *btrfs_find_device(struct btrfs_root *root, u64 devid,
u8 *uuid, u8 *fsid);
int btrfs_shrink_device(struct btrfs_device *device, u64 new_size);
int btrfs_init_new_device(struct btrfs_root *root, char *path);
-int btrfs_balance(struct btrfs_root *dev_root);
+int btrfs_balance(struct btrfs_root *dev_root, struct btrfs_ioctl_balance_start *filters);
void btrfs_unlock_volumes(void);
void btrfs_lock_volumes(void);
int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset);