Skip to contentSkip to conversation

Btrfs: fix full backref problem when inserting shared block reference

RFC PATCH Superseded Miao Xie 6 participants 16 messages Aug 9, 2012 – Jan 31, 2013 Btrfs file system

lore
Share & tools
lore

Miao Xie

author
#0 lore raw

RFC PATCH 1/1 Btrfs: fix full backref problem when inserting shared block reference

If we create several snapshots at the same time, the following BUG_ON() will be triggered.

kernel BUG at fs/btrfs/extent-tree.c:6047!

Steps to reproduce:

 # mkfs.btrfs <partition>
 # mount <partition> <mnt>
 # cd <mnt>
 # for ((i=0;i<2400;i++)); do touch long_name_to_make_tree_more_deep$i; done
 # for ((i=0; i<4; i++))
Quoted 7 lines

do
mkdir $i
for ((j=0; j<200; j++))
do
btrfs sub snap . $i/$j
done &
done

The reason is:
Before transaction commit, some operations changed the fs tree and new tree blocks were allocated because of COW. We used the implicit non-shared back reference for those newly allocated tree blocks because they were not shared by two or more trees.

And then we created the first snapshot for the fs tree, according to the back reference rules, we also used implicit back refs for the child tree blocks of the root node of the fs tree, now those child nodes/leaves were shared by two trees.

Then We didn't deal with the delayed references, and continued to change the fs tree(created the second snapshot and inserted the dir item of the new snapshot into the fs tree). According to the rules of the back reference, we added full back refs for those tree blocks whose parents have be shared by two trees. Now some newly allocated tree blocks had two types of the references.

As we know, the delayed reference system handles these delayed references from back to front, and the full delayed reference is inserted after the implicit ones. So when we dealt with the back references of those newly allocated tree blocks, the full references was dealt with at first. And if the first reference is a shared back reference and the tree block that the reference points to is newly allocated, It would be considered as a tree block which is shared by two or more trees when it is allocated and should be a full back reference not a implicit one, the flag of its reference also should be set to FULL_BACKREF. But in fact, it was a non-shared tree block with a implicit reference at beginning, so it was not compulsory to set the flags to FULL_BACKREF. So BUG_ON was triggered.

We have several methods to fix this bug:
1. deal with delayed references after the snapshot is created and before we change the source tree of the snapshot. This is the easiest and safest way.
2. modify the sort method of the delayed reference tree, make the full delayed references be inserted before the implicit ones. It is also very easy, but I don't know if it will introduce some problems or not.
3. modify select_delayed_ref() and make it select the implicit delayed reference at first. This way is not so good because it may wastes CPU time if we have lots of delayed references.
4. set the flags to FULL_BACKREF, this method is a little complex comparing with the 1st way.

I chose the 1st way to fix it.

---
 fs/btrfs/transaction.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)
fs/btrfs/transaction.c +5 -0
index 7ac7cdc..2eafbd2 100644
@@ -1119,6 +1119,11 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
 	ret = btrfs_reloc_post_snapshot(trans, pending);
 	if (ret)
 		goto abort_trans;
+
+	ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
+	if (ret)
+		goto abort_trans;
+
 	ret = 0;
 fail:
 	kfree(new_root_item);
Signature · 1 line
1.7.6.5
#1 lore raw
Quoted 1 line · Miao Xie

I chose the 1st way to fix it.

The least I can say now is that it fixed the crash! The approach is minimalistic and I think we can take it for now. I didn't review it, only tested with reproducer you described plus modified the numbers up and down, no problems so far, and the "wikipedia" test-subvol stresstest that caused trouble to one of your patches is also ok. I'll do some more testing on other machines and will report problems eventually.

Thanks!

david

#2 lore raw
Quoted 3 lines · David Sterba

and down, no problems so far, and the "wikipedia" test-subvol stresstest that caused trouble to one of your patches is also ok. I'll do some more testing on other machines and will report problems eventually.

So it won't be so easy :)

The test generated 15+ G of data, ~500 snapshots, then umount and fsck:

lots of

ref mismatch on [9655283712 4096] extent item 1, found 0
Incorrect local backref count on 9655283712 root 5 owner 589776 offset 110592 found 0 wanted 1 back 0x86badf0 backpointer mismatch on [9655283712 4096]
owner ref check failed [9655283712 4096]

and then

checking fs roots
root 2854 inode 233882 errors 2500
root 2880 inode 271639 errors 2200

and it's not finished yet, other types of error may pop up.

david

author
#3 lore raw
Quoted 16 lines · David Sterba
Quoted 3 lines · David Sterba

and down, no problems so far, and the "wikipedia" test-subvol stresstest that caused trouble to one of your patches is also ok. I'll do some more testing on other machines and will report problems eventually.

So it won't be so easy :)

The test generated 15+ G of data, ~500 snapshots, then umount and fsck:

lots of

ref mismatch on [9655283712 4096] extent item 1, found 0
Incorrect local backref count on 9655283712 root 5 owner 589776 offset 110592 found 0 wanted 1 back 0x86badf0 backpointer mismatch on [9655283712 4096]
owner ref check failed [9655283712 4096]

Quoted 7 lines

and then

checking fs roots
root 2854 inode 233882 errors 2500
root 2880 inode 271639 errors 2200

and it's not finished yet, other types of error may pop up.

Thanks for your test, I'll look into it.

Regards
Miao

author
#4 lore raw
In reply to David Sterba
Quoted 16 lines · David Sterba
Quoted 3 lines · David Sterba

and down, no problems so far, and the "wikipedia" test-subvol stresstest that caused trouble to one of your patches is also ok. I'll do some more testing on other machines and will report problems eventually.

So it won't be so easy :)

The test generated 15+ G of data, ~500 snapshots, then umount and fsck:

lots of

ref mismatch on [9655283712 4096] extent item 1, found 0
Incorrect local backref count on 9655283712 root 5 owner 589776 offset 110592 found 0 wanted 1 back 0x86badf0 backpointer mismatch on [9655283712 4096]
owner ref check failed [9655283712 4096]

Quoted 7 lines

and then

checking fs roots
root 2854 inode 233882 errors 2500
root 2880 inode 271639 errors 2200

and it's not finished yet, other types of error may pop up.

Could you try Arne's patch?

[PATCH v2] Btrfs: fix race in run_clustered_refs
http://marc.info/?l=linux-btrfs&m=134449329717830&w=2

I run test for several times with this patch, and the problem didn't happen. So it seems this patch can fix the above problem.

Regards
Miao

author
#5 lore raw
In reply to David Sterba
Quoted 7 lines · David Sterba
Quoted 3 lines · David Sterba

and down, no problems so far, and the "wikipedia" test-subvol stresstest that caused trouble to one of your patches is also ok. I'll do some more testing on other machines and will report problems eventually.

So it won't be so easy :)

Quoted 8 lines

The test generated 15+ G of data, ~500 snapshots, then umount and fsck:

lots of

ref mismatch on [9655283712 4096] extent item 1, found 0
Incorrect local backref count on 9655283712 root 5 owner 589776 offset 110592 found 0 wanted 1 back 0x86badf0 backpointer mismatch on [9655283712 4096]
owner ref check failed [9655283712 4096]

By debuging, I found it should be a bug of btrfsck. Could you try this patch?

Thanks
Miao

Quoted 1 line

From 77e9bcaae464354c0b0176631ba51374e3d31cfc Mon Sep 17 00:00:00 2001

From: Miao Xie <[email protected]>
Date: Tue, 21 Aug 2012 14:16:27 +0800
Subject: [PATCH] Btrfs-progs: fix wrong return value of check_owner_ref()

If we find the block by seach corresponding fs tree, we should return 0, and tell the caller we pass the check.

---
 btrfsck.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
btrfsck.c +1 -1
index 4e91769..57e7b57 100644
@@ -1954,7 +1954,7 @@ static int check_owner_ref(struct btrfs_root *root,
 
 	if (buf->start == btrfs_node_blockptr(path.nodes[level + 1],
 					      path.slots[level + 1]))
-		rec->owner_ref_checked = 1;
+		found = 1;
 
 	btrfs_release_path(ref_root, &path);
 	return found ? 0 : 1;
Signature · 1 line
1.7.6.5
Quoted 7 lines

and then

checking fs roots
root 2854 inode 233882 errors 2500
root 2880 inode 271639 errors 2200

and it's not finished yet, other types of error may pop up.

#6 lore raw
In reply to Miao Xie
Quoted 48 lines · Miao Xie

If we create several snapshots at the same time, the following BUG_ON() will be triggered.

kernel BUG at fs/btrfs/extent-tree.c:6047!

Steps to reproduce:

 # mkfs.btrfs <partition>
 # mount <partition> <mnt>
 # cd <mnt>
 # for ((i=0;i<2400;i++)); do touch long_name_to_make_tree_more_deep$i; done
 # for ((i=0; i<4; i++))
Quoted 7 lines

do
mkdir $i
for ((j=0; j<200; j++))
do
btrfs sub snap . $i/$j
done &
done

The reason is:
Before transaction commit, some operations changed the fs tree and new tree blocks were allocated because of COW. We used the implicit non-shared back reference for those newly allocated tree blocks because they were not shared by two or more trees.

And then we created the first snapshot for the fs tree, according to the back reference rules, we also used implicit back refs for the child tree blocks of the root node of the fs tree, now those child nodes/leaves were shared by two trees.

Then We didn't deal with the delayed references, and continued to change the fs tree(created the second snapshot and inserted the dir item of the new snapshot into the fs tree). According to the rules of the back reference, we added full back refs for those tree blocks whose parents have be shared by two trees. Now some newly allocated tree blocks had two types of the references.

As we know, the delayed reference system handles these delayed references from back to front, and the full delayed reference is inserted after the implicit ones. So when we dealt with the back references of those newly allocated tree blocks, the full references was dealt with at first. And if the first reference is a shared back reference and the tree block that the reference points to is newly allocated, It would be considered as a tree block which is shared by two or more trees when it is allocated and should be a full back reference not a implicit one, the flag of its reference also should be set to FULL_BACKREF. But in fact, it was a non-shared tree block with a implicit reference at beginning, so it was not compulsory to set the flags to FULL_BACKREF. So BUG_ON was triggered.

Quoted 6 lines

We have several methods to fix this bug:
1. deal with delayed references after the snapshot is created and before we change the source tree of the snapshot. This is the easiest and safest way.
2. modify the sort method of the delayed reference tree, make the full delayed references be inserted before the implicit ones. It is also very easy, but I don't know if it will introduce some problems or not.

Thanks for tracking this down, FWIW I like option 2 the most, it would be intereseting to see if it does actually introduce new issues. Thanks,

Josef

#7 lore raw
Quoted 56 lines · Josef Bacik
Quoted 48 lines · Miao Xie

If we create several snapshots at the same time, the following BUG_ON() will be triggered.

kernel BUG at fs/btrfs/extent-tree.c:6047!

Steps to reproduce:

 # mkfs.btrfs <partition>
 # mount <partition> <mnt>
 # cd <mnt>
 # for ((i=0;i<2400;i++)); do touch long_name_to_make_tree_more_deep$i; done
 # for ((i=0; i<4; i++))
Quoted 7 lines

do
mkdir $i
for ((j=0; j<200; j++))
do
btrfs sub snap . $i/$j
done &
done

The reason is:
Before transaction commit, some operations changed the fs tree and new tree blocks were allocated because of COW. We used the implicit non-shared back reference for those newly allocated tree blocks because they were not shared by two or more trees.

And then we created the first snapshot for the fs tree, according to the back reference rules, we also used implicit back refs for the child tree blocks of the root node of the fs tree, now those child nodes/leaves were shared by two trees.

Then We didn't deal with the delayed references, and continued to change the fs tree(created the second snapshot and inserted the dir item of the new snapshot into the fs tree). According to the rules of the back reference, we added full back refs for those tree blocks whose parents have be shared by two trees. Now some newly allocated tree blocks had two types of the references.

As we know, the delayed reference system handles these delayed references from back to front, and the full delayed reference is inserted after the implicit ones. So when we dealt with the back references of those newly allocated tree blocks, the full references was dealt with at first. And if the first reference is a shared back reference and the tree block that the reference points to is newly allocated, It would be considered as a tree block which is shared by two or more trees when it is allocated and should be a full back reference not a implicit one, the flag of its reference also should be set to FULL_BACKREF. But in fact, it was a non-shared tree block with a implicit reference at beginning, so it was not compulsory to set the flags to FULL_BACKREF. So BUG_ON was triggered.

Quoted 6 lines

We have several methods to fix this bug:
1. deal with delayed references after the snapshot is created and before we change the source tree of the snapshot. This is the easiest and safest way.
2. modify the sort method of the delayed reference tree, make the full delayed references be inserted before the implicit ones. It is also very easy, but I don't know if it will introduce some problems or not.

Quoted 2 lines

Thanks for tracking this down, FWIW I like option 2 the most, it would be intereseting to see if it does actually introduce new issues. Thanks,

For this release, I like the current patch ;) Great job tracking it down Miao.

-chris

#8 lore raw
Quoted 57 lines · Chris L. Mason
Quoted 56 lines · Josef Bacik
Quoted 54 lines · Miao Xie

If we create several snapshots at the same time, the following BUG_ON() will be triggered.

kernel BUG at fs/btrfs/extent-tree.c:6047!

Steps to reproduce:

 # mkfs.btrfs <partition>
 # mount <partition> <mnt>
 # cd <mnt>
 # for ((i=0;i<2400;i++)); do touch long_name_to_make_tree_more_deep$i; done
 # for ((i=0; i<4; i++))
Quoted 7 lines

do
mkdir $i
for ((j=0; j<200; j++))
do
btrfs sub snap . $i/$j
done &
done

The reason is:
Before transaction commit, some operations changed the fs tree and new tree blocks were allocated because of COW. We used the implicit non-shared back reference for those newly allocated tree blocks because they were not shared by two or more trees.

And then we created the first snapshot for the fs tree, according to the back reference rules, we also used implicit back refs for the child tree blocks of the root node of the fs tree, now those child nodes/leaves were shared by two trees.

Then We didn't deal with the delayed references, and continued to change the fs tree(created the second snapshot and inserted the dir item of the new snapshot into the fs tree). According to the rules of the back reference, we added full back refs for those tree blocks whose parents have be shared by two trees. Now some newly allocated tree blocks had two types of the references.

As we know, the delayed reference system handles these delayed references from back to front, and the full delayed reference is inserted after the implicit ones. So when we dealt with the back references of those newly allocated tree blocks, the full references was dealt with at first. And if the first reference is a shared back reference and the tree block that the reference points to is newly allocated, It would be considered as a tree block which is shared by two or more trees when it is allocated and should be a full back reference not a implicit one, the flag of its reference also should be set to FULL_BACKREF. But in fact, it was a non-shared tree block with a implicit reference at beginning, so it was not compulsory to set the flags to FULL_BACKREF. So BUG_ON was triggered.

We have several methods to fix this bug:
1. deal with delayed references after the snapshot is created and before we change the source tree of the snapshot. This is the easiest and safest way.
2. modify the sort method of the delayed reference tree, make the full delayed references be inserted before the implicit ones. It is also very easy, but I don't know if it will introduce some problems or not.

Quoted 6 lines
Quoted 2 lines

Thanks for tracking this down, FWIW I like option 2 the most, it would be intereseting to see if it does actually introduce new issues. Thanks,

For this release, I like the current patch ;) Great job tracking it down Miao.

Well sure it's much cleaner, but it appears to not work right?

Josef

#9 lore raw
Quoted 58 lines · Josef Bacik
Quoted 57 lines · Chris L. Mason
Quoted 56 lines · Josef Bacik
Quoted 54 lines · Miao Xie

If we create several snapshots at the same time, the following BUG_ON() will be triggered.

kernel BUG at fs/btrfs/extent-tree.c:6047!

Steps to reproduce:

 # mkfs.btrfs <partition>
 # mount <partition> <mnt>
 # cd <mnt>
 # for ((i=0;i<2400;i++)); do touch long_name_to_make_tree_more_deep$i; done
 # for ((i=0; i<4; i++))
Quoted 7 lines

do
mkdir $i
for ((j=0; j<200; j++))
do
btrfs sub snap . $i/$j
done &
done

The reason is:
Before transaction commit, some operations changed the fs tree and new tree blocks were allocated because of COW. We used the implicit non-shared back reference for those newly allocated tree blocks because they were not shared by two or more trees.

And then we created the first snapshot for the fs tree, according to the back reference rules, we also used implicit back refs for the child tree blocks of the root node of the fs tree, now those child nodes/leaves were shared by two trees.

Then We didn't deal with the delayed references, and continued to change the fs tree(created the second snapshot and inserted the dir item of the new snapshot into the fs tree). According to the rules of the back reference, we added full back refs for those tree blocks whose parents have be shared by two trees. Now some newly allocated tree blocks had two types of the references.

As we know, the delayed reference system handles these delayed references from back to front, and the full delayed reference is inserted after the implicit ones. So when we dealt with the back references of those newly allocated tree blocks, the full references was dealt with at first. And if the first reference is a shared back reference and the tree block that the reference points to is newly allocated, It would be considered as a tree block which is shared by two or more trees when it is allocated and should be a full back reference not a implicit one, the flag of its reference also should be set to FULL_BACKREF. But in fact, it was a non-shared tree block with a implicit reference at beginning, so it was not compulsory to set the flags to FULL_BACKREF. So BUG_ON was triggered.

We have several methods to fix this bug:
1. deal with delayed references after the snapshot is created and before we change the source tree of the snapshot. This is the easiest and safest way.
2. modify the sort method of the delayed reference tree, make the full delayed references be inserted before the implicit ones. It is also very easy, but I don't know if it will introduce some problems or not.

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

Thanks for tracking this down, FWIW I like option 2 the most, it would be intereseting to see if it does actually introduce new issues. Thanks,

For this release, I like the current patch ;) Great job tracking it down Miao.

Well sure it's much cleaner, but it appears to not work right?

I'm not sure if those are from a different bug. I'll try to reproduce as well.

-chris

#10 lore raw
In reply to Miao Xie
Quoted 18 lines · Miao Xie

If we create several snapshots at the same time, the following BUG_ON() will be triggered.

kernel BUG at fs/btrfs/extent-tree.c:6047!

Steps to reproduce:

 # mkfs.btrfs <partition>
 # mount <partition> <mnt>
 # cd <mnt>
 # for ((i=0;i<2400;i++)); do touch long_name_to_make_tree_more_deep$i; done
 # for ((i=0; i<4; i++))
Quoted 7 lines

do
mkdir $i
for ((j=0; j<200; j++))
do
btrfs sub snap . $i/$j
done &
done

snapshot creation has a critical section. Once we copy a given root to its snapshot, we're not allowed to change it until the transaction is fully committed.

This means that if you're taking a snapshot of root A and storing the directory item of the snapshot in root A, you can only do it once per transaction with getting into trouble.

Looks like the code doesn't enforce this though. Dave, could you please give this a try:

fs/btrfs/ctree.c +2 -0
index fcc8c21..9e7c621 100644
@@ -1324,6 +1324,8 @@ noinline int btrfs_cow_block(struct btrfs_trans_handle *trans,
 	u64 search_start;
 	int ret;
 
+	WARN_ON(root->danger_transid == trans->transid);
+
 	if (trans->transaction != root->fs_info->running_transaction) {
 		printk(KERN_CRIT "trans %llu running %llu\n",
 		       (unsigned long long)trans->transid,
fs/btrfs/ctree.h +13 -0
index 0d195b5..35b5603 100644
@@ -1490,6 +1490,12 @@ struct btrfs_root {
 	u64 objectid;
 	u64 last_trans;
 
+	/*
+	 * the last transaction that took a snapshot of this
+	 * root.  We're only allowed one snapshot per root per transaction
+	 */
+	u64 snapshot_trans;
+
 	/* data allocations are done in sectorsize units */
 	u32 sectorsize;
 
@@ -1550,6 +1556,13 @@ struct btrfs_root {
 
 	int force_cow;
 
+	/*
+	 * this marks the critical section of snapshot creation.  If we
+	 * make any changes to a root after this critical section starts,
+	 * we corrupt the FS.  It is checked by btrfs_cow_block
+	 */
+	u64 danger_transid;
+
 	spinlock_t root_times_lock;
 };
 
fs/btrfs/ioctl.c +16 -1
index 9df50fa..b20d835 100644
@@ -524,13 +524,28 @@ static int create_snapshot(struct btrfs_root *root, struct dentry *dentry,
 		pending_snapshot->inherit = *inherit;
 		*inherit = NULL;	/* take responsibility to free it */
 	}
-
+again:
 	trans = btrfs_start_transaction(root->fs_info->extent_root, 5);
 	if (IS_ERR(trans)) {
 		ret = PTR_ERR(trans);
 		goto fail;
 	}
 
+	/* we're only allowed to snapshot a given root once per transaction */
+	spin_lock(&root->fs_info->trans_lock);
+	if (root->snapshot_trans == trans->transid) {
+		spin_unlock(&root->fs_info->trans_lock);
+		ret = btrfs_commit_transaction(trans, root->fs_info->extent_root);
+		if (ret)
+			goto fail;
+		goto again;
+	}
+
+	root->snapshot_trans = trans->transid;
+
+	spin_unlock(&root->fs_info->trans_lock);
+
+
 	ret = btrfs_snap_reserve_metadata(trans, pending_snapshot);
 	BUG_ON(ret);
 
fs/btrfs/transaction.c +1 -0
index 27c2600..4507421 100644
@@ -1093,6 +1093,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
 
 	/* see comments in should_cow_block() */
 	root->force_cow = 1;
+	root->danger_transid = trans->transid;
 	smp_wmb();
 
 	btrfs_set_root_node(new_root_item, tmp);
author
#11 lore raw
Quoted 20 lines · Chris Mason
Quoted 18 lines · Miao Xie

If we create several snapshots at the same time, the following BUG_ON() will be triggered.

kernel BUG at fs/btrfs/extent-tree.c:6047!

Steps to reproduce:

 # mkfs.btrfs <partition>
 # mount <partition> <mnt>
 # cd <mnt>
 # for ((i=0;i<2400;i++)); do touch long_name_to_make_tree_more_deep$i; done
 # for ((i=0; i<4; i++))
Quoted 7 lines

do
mkdir $i
for ((j=0; j<200; j++))
do
btrfs sub snap . $i/$j
done &
done

Quoted 3 lines

snapshot creation has a critical section. Once we copy a given root to its snapshot, we're not allowed to change it until the transaction is fully committed.

I knew this critical section. But I think we can kick it away by forcing the snapshoted tree to do COW.

BTW, I will take a vacation next week, so I can not reply it until the week after next. If it is not urgent, I will continue looking into this problem after I come back.

Thanks
Miao

Quoted 103 lines

This means that if you're taking a snapshot of root A and storing the directory item of the snapshot in root A, you can only do it once per transaction with getting into trouble.

Looks like the code doesn't enforce this though. Dave, could you please give this a try:

fs/btrfs/ctree.c +2 -0
index fcc8c21..9e7c621 100644
@@ -1324,6 +1324,8 @@ noinline int btrfs_cow_block(struct btrfs_trans_handle *trans,
 	u64 search_start;
 	int ret;
 
+	WARN_ON(root->danger_transid == trans->transid);
+
 	if (trans->transaction != root->fs_info->running_transaction) {
 		printk(KERN_CRIT "trans %llu running %llu\n",
 		       (unsigned long long)trans->transid,
fs/btrfs/ctree.h +13 -0
index 0d195b5..35b5603 100644
@@ -1490,6 +1490,12 @@ struct btrfs_root {
 	u64 objectid;
 	u64 last_trans;
 
+	/*
+	 * the last transaction that took a snapshot of this
+	 * root.  We're only allowed one snapshot per root per transaction
+	 */
+	u64 snapshot_trans;
+
 	/* data allocations are done in sectorsize units */
 	u32 sectorsize;
 
@@ -1550,6 +1556,13 @@ struct btrfs_root {
 
 	int force_cow;
 
+	/*
+	 * this marks the critical section of snapshot creation.  If we
+	 * make any changes to a root after this critical section starts,
+	 * we corrupt the FS.  It is checked by btrfs_cow_block
+	 */
+	u64 danger_transid;
+
 	spinlock_t root_times_lock;
 };
 
fs/btrfs/ioctl.c +16 -1
index 9df50fa..b20d835 100644
@@ -524,13 +524,28 @@ static int create_snapshot(struct btrfs_root *root, struct dentry *dentry,
 		pending_snapshot->inherit = *inherit;
 		*inherit = NULL;	/* take responsibility to free it */
 	}
-
+again:
 	trans = btrfs_start_transaction(root->fs_info->extent_root, 5);
 	if (IS_ERR(trans)) {
 		ret = PTR_ERR(trans);
 		goto fail;
 	}
 
+	/* we're only allowed to snapshot a given root once per transaction */
+	spin_lock(&root->fs_info->trans_lock);
+	if (root->snapshot_trans == trans->transid) {
+		spin_unlock(&root->fs_info->trans_lock);
+		ret = btrfs_commit_transaction(trans, root->fs_info->extent_root);
+		if (ret)
+			goto fail;
+		goto again;
+	}
+
+	root->snapshot_trans = trans->transid;
+
+	spin_unlock(&root->fs_info->trans_lock);
+
+
 	ret = btrfs_snap_reserve_metadata(trans, pending_snapshot);
 	BUG_ON(ret);
 
fs/btrfs/transaction.c +1 -1
index 27c2600..4507421 100644
@@ -1093,6 +1093,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
 
 	/* see comments in should_cow_block() */
 	root->force_cow = 1;
+	root->danger_transid = trans->transid;
 	smp_wmb();
 
 	btrfs_set_root_node(new_root_item, tmp);
--

To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#12 lore raw
Quoted 21 lines · Miao Xie
Quoted 20 lines · Chris Mason
Quoted 18 lines · Miao Xie

If we create several snapshots at the same time, the following BUG_ON() will be triggered.

kernel BUG at fs/btrfs/extent-tree.c:6047!

Steps to reproduce:

 # mkfs.btrfs <partition>
 # mount <partition> <mnt>
 # cd <mnt>
 # for ((i=0;i<2400;i++)); do touch long_name_to_make_tree_more_deep$i; done
 # for ((i=0; i<4; i++))
Quoted 7 lines

do
mkdir $i
for ((j=0; j<200; j++))
do
btrfs sub snap . $i/$j
done &
done

Quoted 6 lines
Quoted 3 lines

snapshot creation has a critical section. Once we copy a given root to its snapshot, we're not allowed to change it until the transaction is fully committed.

I knew this critical section. But I think we can kick it away by forcing the snapshoted tree to do COW.

Yes, it should be possible.

Quoted 3 lines

BTW, I will take a vacation next week, so I can not reply it until the week after next. If it is not urgent, I will continue looking into this problem after I come back.

No problem, we can take the current patch for now and improve add back the ability to do multiple snapshots per root later.

-chris

#13 lore raw

Hi Miao,
I was following this thread in the past, but I did not understand it fully, maybe you can explain?

Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 12 lines
 # mkfs.btrfs <partition>
 # mount <partition> <mnt>
 # cd <mnt>
 # for ((i=0;i<2400;i++)); do touch long_name_to_make_tree_more_deep$i; done
 # for ((i=0; i<4; i++))
Quoted 7 lines

do
mkdir $i
for ((j=0; j<200; j++))
do
btrfs sub snap . $i/$j
done &
done

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

snapshot creation has a critical section. Once we copy a given root to its snapshot, we're not allowed to change it until the transaction is fully committed.

Is the limitation that if we are creating a snap B of root A, and placing the root of B somewhere into the tree of A, then we can do this only once per transaction? Does this limitation still exist or your fix fixes it?

Also, according to your reproducer, each "btrfs sub snap" will start/join a transaction, but then it will call btrfs_commit_transaction() and not btrfs_commit_transaction_async(), so it will wait until the transaction commits. So how it may happen that you create more than one snap in the same transaction with your reproducer?

The reason I am asking, is that I want to try to write code that creates several snaps in one transaction and only then commits. Should this be possible or there is some limitation, like I mentioned above?

Thanks for your help,
Alex.

author
#14 lore raw
Quoted 17 lines · Alex Lyakas

Hi Miao,
I was following this thread in the past, but I did not understand it fully, maybe you can explain?

Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 12 lines
 # mkfs.btrfs <partition>
 # mount <partition> <mnt>
 # cd <mnt>
 # for ((i=0;i<2400;i++)); do touch long_name_to_make_tree_more_deep$i; done
 # for ((i=0; i<4; i++))
Quoted 7 lines

do
mkdir $i
for ((j=0; j<200; j++))
do
btrfs sub snap . $i/$j
done &
done

Quoted 8 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

snapshot creation has a critical section. Once we copy a given root to its snapshot, we're not allowed to change it until the transaction is fully committed.

Is the limitation that if we are creating a snap B of root A, and placing the root of B somewhere into the tree of A, then we can do this only once per transaction? Does this limitation still exist or your fix fixes it?

The limitation is the snapshoted subvolume can not be changed until the transaction is committed. That is we can not insert anything(including the root of B and the directory item/index of B) into the tree of A after snap B is created.

This limitation was fixed.

Quoted 6 lines

Also, according to your reproducer, each "btrfs sub snap" will start/join a transaction, but then it will call btrfs_commit_transaction() and not btrfs_commit_transaction_async(), so it will wait until the transaction commits. So how it may happen that you create more than one snap in the same transaction with your reproducer?

run several tasks, and each task create snapshots repeatedly in its own directory.
(If we create snapshots in the same directory, the i_mutex of the directory
will make the process serialized)

Quoted 3 lines

The reason I am asking, is that I want to try to write code that creates several snaps in one transaction and only then commits. Should this be possible or there is some limitation, like I mentioned above?

As far as I know, it is possible, there is no limitation now.

Thanks
Miao

#15 lore raw

Thanks for your comments, Miao.

Quoted 40 lines · Miao Xie
Quoted 17 lines · Alex Lyakas

Hi Miao,
I was following this thread in the past, but I did not understand it fully, maybe you can explain?

Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 12 lines
 # mkfs.btrfs <partition>
 # mount <partition> <mnt>
 # cd <mnt>
 # for ((i=0;i<2400;i++)); do touch long_name_to_make_tree_more_deep$i; done
 # for ((i=0; i<4; i++))
Quoted 7 lines

do
mkdir $i
for ((j=0; j<200; j++))
do
btrfs sub snap . $i/$j
done &
done

Quoted 8 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

snapshot creation has a critical section. Once we copy a given root to its snapshot, we're not allowed to change it until the transaction is fully committed.

Is the limitation that if we are creating a snap B of root A, and placing the root of B somewhere into the tree of A, then we can do this only once per transaction? Does this limitation still exist or your fix fixes it?

The limitation is the snapshoted subvolume can not be changed until the transaction is committed. That is we can not insert anything(including the root of B and the directory item/index of B) into the tree of A after snap B is created.

This limitation was fixed.

Quoted 6 lines

Also, according to your reproducer, each "btrfs sub snap" will start/join a transaction, but then it will call btrfs_commit_transaction() and not btrfs_commit_transaction_async(), so it will wait until the transaction commits. So how it may happen that you create more than one snap in the same transaction with your reproducer?

Quoted 4 lines

run several tasks, and each task create snapshots repeatedly in its own directory.
(If we create snapshots in the same directory, the i_mutex of the directory
will make the process serialized)

So I missed the fact that btrfs_start_transaction() can actually join an existing transaction. So if several threads ask to create snaps in parallel, we may hit this.

Quoted 9 lines
Quoted 4 lines

The reason I am asking, is that I want to try to write code that creates several snaps in one transaction and only then commits. Should this be possible or there is some limitation, like I mentioned above?

As far as I know, it is possible, there is no limitation now.

Thanks
Miao