Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ceda0864 authored by Miao Xie's avatar Miao Xie Committed by Josef Bacik
Browse files

Btrfs: use a lock to protect incompat/compat flag of the super block



The following case will make the incompat/compat flag of the super block
be recovered.
 Task1					|Task2
 flags = btrfs_super_incompat_flags();	|
					|flags = btrfs_super_incompat_flags();
 flags |= new_flag1;			|
					|flags |= new_flag2;
 btrfs_set_super_incompat_flags(flags);	|
					|btrfs_set_super_incompat_flags(flags);
the new_flag1 is recovered.

In order to avoid this problem, we introduce a lock named super_lock into
the btrfs_fs_info structure. If we want to update incompat/compat flags
of the super block, we must hold it.

Signed-off-by: default avatarMiao Xie <miaox@cn.fujitsu.com>
Signed-off-by: default avatarJosef Bacik <jbacik@fusionio.com>
parent f42a34b2
Loading
Loading
Loading
Loading
+20 −2
Original line number Original line Diff line number Diff line
@@ -1362,6 +1362,17 @@ struct btrfs_fs_info {
	wait_queue_head_t transaction_blocked_wait;
	wait_queue_head_t transaction_blocked_wait;
	wait_queue_head_t async_submit_wait;
	wait_queue_head_t async_submit_wait;


	/*
	 * Used to protect the incompat_flags, compat_flags, compat_ro_flags
	 * when they are updated.
	 *
	 * Because we do not clear the flags for ever, so we needn't use
	 * the lock on the read side.
	 *
	 * We also needn't use the lock when we mount the fs, because
	 * there is no other task which will update the flag.
	 */
	spinlock_t super_lock;
	struct btrfs_super_block *super_copy;
	struct btrfs_super_block *super_copy;
	struct btrfs_super_block *super_for_commit;
	struct btrfs_super_block *super_for_commit;
	struct block_device *__bdev;
	struct block_device *__bdev;
@@ -3687,9 +3698,16 @@ static inline void __btrfs_set_fs_incompat(struct btrfs_fs_info *fs_info,


	disk_super = fs_info->super_copy;
	disk_super = fs_info->super_copy;
	features = btrfs_super_incompat_flags(disk_super);
	features = btrfs_super_incompat_flags(disk_super);
	if (!(features & flag)) {
		spin_lock(&fs_info->super_lock);
		features = btrfs_super_incompat_flags(disk_super);
		if (!(features & flag)) {
		if (!(features & flag)) {
			features |= flag;
			features |= flag;
			btrfs_set_super_incompat_flags(disk_super, features);
			btrfs_set_super_incompat_flags(disk_super, features);
			printk(KERN_INFO "btrfs: setting %llu feature flag\n",
					 flag);
		}
		spin_unlock(&fs_info->super_lock);
	}
	}
}
}


+5 −0
Original line number Original line Diff line number Diff line
@@ -2086,6 +2086,7 @@ int open_ctree(struct super_block *sb,
	spin_lock_init(&fs_info->defrag_inodes_lock);
	spin_lock_init(&fs_info->defrag_inodes_lock);
	spin_lock_init(&fs_info->free_chunk_lock);
	spin_lock_init(&fs_info->free_chunk_lock);
	spin_lock_init(&fs_info->tree_mod_seq_lock);
	spin_lock_init(&fs_info->tree_mod_seq_lock);
	spin_lock_init(&fs_info->super_lock);
	rwlock_init(&fs_info->tree_mod_log_lock);
	rwlock_init(&fs_info->tree_mod_log_lock);
	mutex_init(&fs_info->reloc_mutex);
	mutex_init(&fs_info->reloc_mutex);
	seqlock_init(&fs_info->profiles_lock);
	seqlock_init(&fs_info->profiles_lock);
@@ -2349,6 +2350,10 @@ int open_ctree(struct super_block *sb,
		goto fail_alloc;
		goto fail_alloc;
	}
	}


	/*
	 * Needn't use the lock because there is no other task which will
	 * update the flag.
	 */
	btrfs_set_super_incompat_flags(disk_super, features);
	btrfs_set_super_incompat_flags(disk_super, features);


	features = btrfs_super_compat_ro_flags(disk_super) &
	features = btrfs_super_compat_ro_flags(disk_super) &
+1 −9
Original line number Original line Diff line number Diff line
@@ -3674,18 +3674,10 @@ static u32 find_raid56_stripe_len(u32 data_devices, u32 dev_stripe_target)


static void check_raid56_incompat_flag(struct btrfs_fs_info *info, u64 type)
static void check_raid56_incompat_flag(struct btrfs_fs_info *info, u64 type)
{
{
	u64 features;

	if (!(type & (BTRFS_BLOCK_GROUP_RAID5 | BTRFS_BLOCK_GROUP_RAID6)))
	if (!(type & (BTRFS_BLOCK_GROUP_RAID5 | BTRFS_BLOCK_GROUP_RAID6)))
		return;
		return;


	features = btrfs_super_incompat_flags(info->super_copy);
	btrfs_set_fs_incompat(info, RAID56);
	if (features & BTRFS_FEATURE_INCOMPAT_RAID56)
		return;

	features |= BTRFS_FEATURE_INCOMPAT_RAID56;
	btrfs_set_super_incompat_flags(info->super_copy, features);
	printk(KERN_INFO "btrfs: setting RAID5/6 feature flag\n");
}
}


static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,