aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Mahoney <jeffm@suse.com>2013-11-15 15:33:55 -0500
committerChris Mason <clm@fb.com>2014-01-28 16:19:23 -0500
commit2eaa055fab4e3127c9f572fda1b710cbb2acdf1c (patch)
tree43ec0254e30315288d03196054ff3fff534ed06c
parent9e5ac13acbb9e806a54f131432501bf462248c35 (diff)
btrfs: add ioctls to query/change feature bits online
There are some feature bits that require no offline setup and can be enabled online. I've only reviewed extended irefs, but there will probably be more. We introduce three new ioctls: - BTRFS_IOC_GET_SUPPORTED_FEATURES: query the kernel for supported features. - BTRFS_IOC_GET_FEATURES: query the kernel for enabled features on a per-fs basis, as well as querying for which features are changeable with mounted. - BTRFS_IOC_SET_FEATURES: change features on a per-fs basis. We introduce two new masks per feature set (_SAFE_SET and _SAFE_CLEAR) that allow us to define which features are safe to change at runtime. The failure modes for BTRFS_IOC_SET_FEATURES are as follows: - Enabling a completely unsupported feature: warns and returns -ENOTSUPP - Enabling a feature that can only be done offline: warns and returns -EPERM Signed-off-by: Jeff Mahoney <jeffm@suse.com> Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Chris Mason <clm@fb.com>
-rw-r--r--fs/btrfs/ctree.h9
-rw-r--r--fs/btrfs/ioctl.c142
-rw-r--r--include/uapi/linux/btrfs.h12
3 files changed, 163 insertions, 0 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 1aafccda05d1..498452ebfd37 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -524,7 +524,12 @@ struct btrfs_super_block {
524#define BTRFS_FEATURE_INCOMPAT_NO_HOLES (1ULL << 9) 524#define BTRFS_FEATURE_INCOMPAT_NO_HOLES (1ULL << 9)
525 525
526#define BTRFS_FEATURE_COMPAT_SUPP 0ULL 526#define BTRFS_FEATURE_COMPAT_SUPP 0ULL
527#define BTRFS_FEATURE_COMPAT_SAFE_SET 0ULL
528#define BTRFS_FEATURE_COMPAT_SAFE_CLEAR 0ULL
527#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL 529#define BTRFS_FEATURE_COMPAT_RO_SUPP 0ULL
530#define BTRFS_FEATURE_COMPAT_RO_SAFE_SET 0ULL
531#define BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR 0ULL
532
528#define BTRFS_FEATURE_INCOMPAT_SUPP \ 533#define BTRFS_FEATURE_INCOMPAT_SUPP \
529 (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \ 534 (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \
530 BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \ 535 BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \
@@ -536,6 +541,10 @@ struct btrfs_super_block {
536 BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA | \ 541 BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA | \
537 BTRFS_FEATURE_INCOMPAT_NO_HOLES) 542 BTRFS_FEATURE_INCOMPAT_NO_HOLES)
538 543
544#define BTRFS_FEATURE_INCOMPAT_SAFE_SET \
545 (BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF)
546#define BTRFS_FEATURE_INCOMPAT_SAFE_CLEAR 0ULL
547
539/* 548/*
540 * A leaf is full of items. offset and size tell us where to find 549 * A leaf is full of items. offset and size tell us where to find
541 * the item in the leaf (relative to the start of the data area) 550 * the item in the leaf (relative to the start of the data area)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 21da5762b0b1..d4e105b54091 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -4480,6 +4480,142 @@ out_unlock:
4480 return ret; 4480 return ret;
4481} 4481}
4482 4482
4483#define INIT_FEATURE_FLAGS(suffix) \
4484 { .compat_flags = BTRFS_FEATURE_COMPAT_##suffix, \
4485 .compat_ro_flags = BTRFS_FEATURE_COMPAT_RO_##suffix, \
4486 .incompat_flags = BTRFS_FEATURE_INCOMPAT_##suffix }
4487
4488static int btrfs_ioctl_get_supported_features(struct file *file,
4489 void __user *arg)
4490{
4491 static struct btrfs_ioctl_feature_flags features[3] = {
4492 INIT_FEATURE_FLAGS(SUPP),
4493 INIT_FEATURE_FLAGS(SAFE_SET),
4494 INIT_FEATURE_FLAGS(SAFE_CLEAR)
4495 };
4496
4497 if (copy_to_user(arg, &features, sizeof(features)))
4498 return -EFAULT;
4499
4500 return 0;
4501}
4502
4503static int btrfs_ioctl_get_features(struct file *file, void __user *arg)
4504{
4505 struct btrfs_root *root = BTRFS_I(file_inode(file))->root;
4506 struct btrfs_super_block *super_block = root->fs_info->super_copy;
4507 struct btrfs_ioctl_feature_flags features;
4508
4509 features.compat_flags = btrfs_super_compat_flags(super_block);
4510 features.compat_ro_flags = btrfs_super_compat_ro_flags(super_block);
4511 features.incompat_flags = btrfs_super_incompat_flags(super_block);
4512
4513 if (copy_to_user(arg, &features, sizeof(features)))
4514 return -EFAULT;
4515
4516 return 0;
4517}
4518
4519static int check_feature_bits(struct btrfs_root *root, const char *type,
4520 u64 change_mask, u64 flags, u64 supported_flags,
4521 u64 safe_set, u64 safe_clear)
4522{
4523 u64 disallowed, unsupported;
4524 u64 set_mask = flags & change_mask;
4525 u64 clear_mask = ~flags & change_mask;
4526
4527 unsupported = set_mask & ~supported_flags;
4528 if (unsupported) {
4529 btrfs_warn(root->fs_info,
4530 "this kernel does not support %s bits 0x%llx",
4531 type, unsupported);
4532 return -EOPNOTSUPP;
4533 }
4534
4535 disallowed = set_mask & ~safe_set;
4536 if (disallowed) {
4537 btrfs_warn(root->fs_info,
4538 "can't set %s bits 0x%llx while mounted",
4539 type, disallowed);
4540 return -EPERM;
4541 }
4542
4543 disallowed = clear_mask & ~safe_clear;
4544 if (disallowed) {
4545 btrfs_warn(root->fs_info,
4546 "can't clear %s bits 0x%llx while mounted",
4547 type, disallowed);
4548 return -EPERM;
4549 }
4550
4551 return 0;
4552}
4553
4554#define check_feature(root, change_mask, flags, mask_base) \
4555check_feature_bits(root, # mask_base, change_mask, flags, \
4556 BTRFS_FEATURE_ ## mask_base ## _SUPP, \
4557 BTRFS_FEATURE_ ## mask_base ## _SAFE_SET, \
4558 BTRFS_FEATURE_ ## mask_base ## _SAFE_CLEAR)
4559
4560static int btrfs_ioctl_set_features(struct file *file, void __user *arg)
4561{
4562 struct btrfs_root *root = BTRFS_I(file_inode(file))->root;
4563 struct btrfs_super_block *super_block = root->fs_info->super_copy;
4564 struct btrfs_ioctl_feature_flags flags[2];
4565 struct btrfs_trans_handle *trans;
4566 u64 newflags;
4567 int ret;
4568
4569 if (!capable(CAP_SYS_ADMIN))
4570 return -EPERM;
4571
4572 if (copy_from_user(flags, arg, sizeof(flags)))
4573 return -EFAULT;
4574
4575 /* Nothing to do */
4576 if (!flags[0].compat_flags && !flags[0].compat_ro_flags &&
4577 !flags[0].incompat_flags)
4578 return 0;
4579
4580 ret = check_feature(root, flags[0].compat_flags,
4581 flags[1].compat_flags, COMPAT);
4582 if (ret)
4583 return ret;
4584
4585 ret = check_feature(root, flags[0].compat_ro_flags,
4586 flags[1].compat_ro_flags, COMPAT_RO);
4587 if (ret)
4588 return ret;
4589
4590 ret = check_feature(root, flags[0].incompat_flags,
4591 flags[1].incompat_flags, INCOMPAT);
4592 if (ret)
4593 return ret;
4594
4595 trans = btrfs_start_transaction(root, 1);
4596 if (IS_ERR(trans))
4597 return PTR_ERR(trans);
4598
4599 spin_lock(&root->fs_info->super_lock);
4600 newflags = btrfs_super_compat_flags(super_block);
4601 newflags |= flags[0].compat_flags & flags[1].compat_flags;
4602 newflags &= ~(flags[0].compat_flags & ~flags[1].compat_flags);
4603 btrfs_set_super_compat_flags(super_block, newflags);
4604
4605 newflags = btrfs_super_compat_ro_flags(super_block);
4606 newflags |= flags[0].compat_ro_flags & flags[1].compat_ro_flags;
4607 newflags &= ~(flags[0].compat_ro_flags & ~flags[1].compat_ro_flags);
4608 btrfs_set_super_compat_ro_flags(super_block, newflags);
4609
4610 newflags = btrfs_super_incompat_flags(super_block);
4611 newflags |= flags[0].incompat_flags & flags[1].incompat_flags;
4612 newflags &= ~(flags[0].incompat_flags & ~flags[1].incompat_flags);
4613 btrfs_set_super_incompat_flags(super_block, newflags);
4614 spin_unlock(&root->fs_info->super_lock);
4615
4616 return btrfs_end_transaction(trans, root);
4617}
4618
4483long btrfs_ioctl(struct file *file, unsigned int 4619long btrfs_ioctl(struct file *file, unsigned int
4484 cmd, unsigned long arg) 4620 cmd, unsigned long arg)
4485{ 4621{
@@ -4598,6 +4734,12 @@ long btrfs_ioctl(struct file *file, unsigned int
4598 return btrfs_ioctl_set_fslabel(file, argp); 4734 return btrfs_ioctl_set_fslabel(file, argp);
4599 case BTRFS_IOC_FILE_EXTENT_SAME: 4735 case BTRFS_IOC_FILE_EXTENT_SAME:
4600 return btrfs_ioctl_file_extent_same(file, argp); 4736 return btrfs_ioctl_file_extent_same(file, argp);
4737 case BTRFS_IOC_GET_SUPPORTED_FEATURES:
4738 return btrfs_ioctl_get_supported_features(file, argp);
4739 case BTRFS_IOC_GET_FEATURES:
4740 return btrfs_ioctl_get_features(file, argp);
4741 case BTRFS_IOC_SET_FEATURES:
4742 return btrfs_ioctl_set_features(file, argp);
4601 } 4743 }
4602 4744
4603 return -ENOTTY; 4745 return -ENOTTY;
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index 45e618921c61..b4d69092fbdb 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -184,6 +184,12 @@ struct btrfs_ioctl_fs_info_args {
184 __u64 reserved[124]; /* pad to 1k */ 184 __u64 reserved[124]; /* pad to 1k */
185}; 185};
186 186
187struct btrfs_ioctl_feature_flags {
188 __u64 compat_flags;
189 __u64 compat_ro_flags;
190 __u64 incompat_flags;
191};
192
187/* balance control ioctl modes */ 193/* balance control ioctl modes */
188#define BTRFS_BALANCE_CTL_PAUSE 1 194#define BTRFS_BALANCE_CTL_PAUSE 1
189#define BTRFS_BALANCE_CTL_CANCEL 2 195#define BTRFS_BALANCE_CTL_CANCEL 2
@@ -606,5 +612,11 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code)
606 struct btrfs_ioctl_dev_replace_args) 612 struct btrfs_ioctl_dev_replace_args)
607#define BTRFS_IOC_FILE_EXTENT_SAME _IOWR(BTRFS_IOCTL_MAGIC, 54, \ 613#define BTRFS_IOC_FILE_EXTENT_SAME _IOWR(BTRFS_IOCTL_MAGIC, 54, \
608 struct btrfs_ioctl_same_args) 614 struct btrfs_ioctl_same_args)
615#define BTRFS_IOC_GET_FEATURES _IOR(BTRFS_IOCTL_MAGIC, 57, \
616 struct btrfs_ioctl_feature_flags)
617#define BTRFS_IOC_SET_FEATURES _IOW(BTRFS_IOCTL_MAGIC, 57, \
618 struct btrfs_ioctl_feature_flags[2])
619#define BTRFS_IOC_GET_SUPPORTED_FEATURES _IOR(BTRFS_IOCTL_MAGIC, 57, \
620 struct btrfs_ioctl_feature_flags[3])
609 621
610#endif /* _UAPI_LINUX_BTRFS_H */ 622#endif /* _UAPI_LINUX_BTRFS_H */