Skip to contentSkip to conversation

btrfs: btrfs_super_block::log_root_transid related enhancement

PATCH In review Qu Wenruo 5 participants 12 messages Oct 4, 2018 – Oct 11, 2018 Btrfs file system

lore
Share & tools
lore

Qu Wenruo

author
#0 lore raw

cover btrfs: btrfs_super_block::log_root_transid related enhancement

Even we have btrfs_super_block::log_root_transid, it's never really populated.

This patchset will populate that member and use it to detect corrupted log tree early.

Qu Wenruo (2):
btrfs: Populate btrfs_super_block::log_root_transid
btrfs: Validate btrfs_super_block::log_root_transid

 fs/btrfs/disk-io.c     | 28 ++++++++++++++++++++++------
 fs/btrfs/transaction.c |  1 +
 fs/btrfs/tree-log.c    |  2 ++
 3 files changed, 25 insertions(+), 6 deletions(-)
Signature · 1 line
2.19.0
author
#1 lore raw

PATCH 1/2 btrfs: Populate btrfs_super_block::log_root_transid

We have btrfs_super_block::log_root_transid to record the transid of log tree root.

However it's never populated and it's always 0, just as the following dump-super:

 log_root                30572544
 log_root_transid        0
 log_root_level          0

This patch will populate it with log tree root correctly, so the result will be:

 log_root                30572544
 log_root_transid        6
 log_root_level          0

This won't affect current kernel behavior or btrfs check result as we already expect log tree root generation always to be super block genration + 1.

But it could be later used to detect log tree corruption early.

---
 fs/btrfs/transaction.c | 1 +
 fs/btrfs/tree-log.c    | 2 ++
 2 files changed, 3 insertions(+)
fs/btrfs/transaction.c +1 -0
index 3b84f5015029..9d4947079aa3 100644
@@ -2211,6 +2211,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
 
 	btrfs_set_super_log_root(fs_info->super_copy, 0);
 	btrfs_set_super_log_root_level(fs_info->super_copy, 0);
+	btrfs_set_super_log_root_transid(fs_info->super_copy, 0);
 	memcpy(fs_info->super_for_commit, fs_info->super_copy,
 	       sizeof(*fs_info->super_copy));
 
fs/btrfs/tree-log.c +2 -0
index 1650dc44a5e3..3e16e7b54922 100644
@@ -3131,6 +3131,8 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
 				 log_root_tree->node->start);
 	btrfs_set_super_log_root_level(fs_info->super_for_commit,
 				       btrfs_header_level(log_root_tree->node));
+	btrfs_set_super_log_root_transid(fs_info->super_for_commit,
+				btrfs_header_generation(log_root_tree->node));
 
 	log_root_tree->log_transid++;
 	mutex_unlock(&log_root_tree->log_mutex);
Signature · 1 line
2.19.0
#2 lore raw

Re: [PATCH 1/2] btrfs: Populate btrfs_super_block::log_root_transid

Quoted 4 lines

we
already expect log tree root generation always to be super block genration + 1.

This is close to what I was looking for by reading the cover-letter, basically what is the impact/bug by not populating it? but can you explain more, I wonder how was check working so long then?

Quoted 1 line

But it could be later used to detect log tree corruption early.

If there is no impact/bug its rather a good idea to change this when the early log tree corruption detection part is ready. So that there is absolute clarity.

The reason why I am grossly wary is - we have incomplete business about how do we handle the write-hole, say raid1 to begin with.

Thanks, Anand

#3 lore raw

Re: [PATCH 1/2] btrfs: Populate btrfs_super_block::log_root_transid

Quoted 7 lines · Anand Jain
Quoted 4 lines

we
already expect log tree root generation always to be super block genration + 1.

 This is close to what I was looking for by reading the cover-letter, basically  what is the impact/bug by not populating it?

Nothing really.

Both kernel and btrfs-progs completely ignore it.

Quoted 2 lines

but can you
 explain more, I wonder how was check working so long then?

For btrfs-progs, the behavior will not change at all.
It still assume log tree root's generation is super geneartion + 1. And if doesn't match reports a transid mismatch error.

For kernel, super validation checker will do such generation check earlier.

So instead of reporting transid mismatch and then btrfs_log_recover error, it will report log tree is bad.

Quoted 9 lines
Quoted 2 lines

But it could be later used to detect log tree corruption early.

 If there is no impact/bug its rather a good idea to change this when the early log tree corruption detection part is ready. So that there is absolute clarity.

 The reason why I am grossly wary is - we have incomplete business about how do we handle the write-hole, say raid1 to begin with.

If the solution is going to rely on log tree, then the patch shouldn't affect anyone.

The main purpose here is to make the log_root_transid to follow all other generation behavior (root, chunk).
Nothing to really affect users.

Thanks,
Qu

Quoted 2 lines

Thanks, Anand

#4 lore raw

Re: [PATCH 1/2] btrfs: Populate btrfs_super_block::log_root_transid

In reply to Qu Wenruo
Quoted 15 lines · Qu Wenruo

We have btrfs_super_block::log_root_transid to record the transid of log tree root.

However it's never populated and it's always 0, just as the following dump-super:

 log_root                30572544
 log_root_transid        0
 log_root_level          0

This patch will populate it with log tree root correctly, so the result will be:

 log_root                30572544
 log_root_transid        6
 log_root_level          0
Quoted 5 lines

This won't affect current kernel behavior or btrfs check result as we already expect log tree root generation always to be super block genration + 1.

But it could be later used to detect log tree corruption early.

The backward compatibility seems to be ok in general, I found one scenario where the check will fail:

* mount with unpatched kernel, log_root_transid = 0
* mount with patched kernel, log_root_transid 100, generation 101
* mount with unpatched kernel, log_root_transid 100 (unchanged), generation 201
* mount with patched kernel -> check fails

So the problem is when log_root_transid is not 0 and changes out of sync with the generation. The above sequence of kernels can simply happen when switching between old stable and current releases.

#5 lore raw

Re: [PATCH 1/2] btrfs: Populate btrfs_super_block::log_root_transid

Quoted 22 lines · David Sterba
Quoted 15 lines · Qu Wenruo

We have btrfs_super_block::log_root_transid to record the transid of log tree root.

However it's never populated and it's always 0, just as the following dump-super:

 log_root                30572544
 log_root_transid        0
 log_root_level          0

This patch will populate it with log tree root correctly, so the result will be:

 log_root                30572544
 log_root_transid        6
 log_root_level          0
Quoted 5 lines

This won't affect current kernel behavior or btrfs check result as we already expect log tree root generation always to be super block genration + 1.

But it could be later used to detect log tree corruption early.

Quoted 7 lines

The backward compatibility seems to be ok in general, I found one scenario where the check will fail:

* mount with unpatched kernel, log_root_transid = 0
* mount with patched kernel, log_root_transid 100, generation 101
* mount with unpatched kernel, log_root_transid 100 (unchanged), generation 201
* mount with patched kernel -> check fails

Indeed, this is a problem I missed.

It provides the old principle, if we're going to change how kernel use/interprete a on-disk memeber, we have to introduce a new incompatible flag.

Please just drop the series of patch.

Thanks,
Qu

Quoted 5 lines

So the problem is when log_root_transid is not 0 and changes out of sync with the generation. The above sequence of kernels can simply happen when switching between old stable and current releases.

#6 lore raw

Re: [PATCH 1/2] btrfs: Populate btrfs_super_block::log_root_transid

Quoted 33 lines · Qu Wenruo
Quoted 22 lines · David Sterba
Quoted 15 lines · Qu Wenruo

We have btrfs_super_block::log_root_transid to record the transid of log tree root.

However it's never populated and it's always 0, just as the following dump-super:

 log_root                30572544
 log_root_transid        0
 log_root_level          0

This patch will populate it with log tree root correctly, so the result will be:

 log_root                30572544
 log_root_transid        6
 log_root_level          0
Quoted 5 lines

This won't affect current kernel behavior or btrfs check result as we already expect log tree root generation always to be super block genration + 1.

But it could be later used to detect log tree corruption early.

Quoted 7 lines

The backward compatibility seems to be ok in general, I found one scenario where the check will fail:

* mount with unpatched kernel, log_root_transid = 0
* mount with patched kernel, log_root_transid 100, generation 101
* mount with unpatched kernel, log_root_transid 100 (unchanged), generation 201
* mount with patched kernel -> check fails

Quoted 7 lines

Indeed, this is a problem I missed.

It provides the old principle, if we're going to change how kernel use/interprete a on-disk memeber, we have to introduce a new incompatible flag.

Please just drop the series of patch.

Since this member really doesn't have any purpose now I think the prudent thing to do is to rename it to "reserved1" or "padding" or whatever but basically signal that it's no longer in used. In the future when a new incompaat bit will be required for some feature it can be re-used.

Quoted 9 lines

Thanks,
Qu

Quoted 5 lines

So the problem is when log_root_transid is not 0 and changes out of sync with the generation. The above sequence of kernels can simply happen when switching between old stable and current releases.

#7 lore raw

Re: [PATCH 1/2] btrfs: Populate btrfs_super_block::log_root_transid

Quoted 42 lines · Nikolay Borisov
Quoted 39 lines · Qu Wenruo
Quoted 22 lines · David Sterba
Quoted 15 lines · Qu Wenruo

We have btrfs_super_block::log_root_transid to record the transid of log tree root.

However it's never populated and it's always 0, just as the following dump-super:

 log_root                30572544
 log_root_transid        0
 log_root_level          0

This patch will populate it with log tree root correctly, so the result will be:

 log_root                30572544
 log_root_transid        6
 log_root_level          0
Quoted 5 lines

This won't affect current kernel behavior or btrfs check result as we already expect log tree root generation always to be super block genration + 1.

But it could be later used to detect log tree corruption early.

Quoted 7 lines

The backward compatibility seems to be ok in general, I found one scenario where the check will fail:

* mount with unpatched kernel, log_root_transid = 0
* mount with patched kernel, log_root_transid 100, generation 101
* mount with unpatched kernel, log_root_transid 100 (unchanged), generation 201
* mount with patched kernel -> check fails

Indeed, this is a problem I missed.

It provides the old principle, if we're going to change how kernel use/interprete a on-disk memeber, we have to introduce a new incompatible flag.

Quoted 7 lines
Quoted 1 line

Please just drop the series of patch.

Since this member really doesn't have any purpose now I think the prudent thing to do is to rename it to "reserved1" or "padding" or whatever but basically signal that it's no longer in used. In the future when a new incompaat bit will be required for some feature it can be re-used.

Makes sense.

I'll craft a patch for that.

Thanks,
Qu

Quoted 10 lines
Quoted 10 lines

Thanks,
Qu

Quoted 5 lines

So the problem is when log_root_transid is not 0 and changes out of sync with the generation. The above sequence of kernels can simply happen when switching between old stable and current releases.

author
#8 lore raw

PATCH 2/2 btrfs: Validate btrfs_super_block::log_root_transid

In reply to Qu Wenruo

So we can detect log root related problem early and report the error more meaningfully other than hitting it log root read time.

---
 fs/btrfs/disk-io.c | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)
fs/btrfs/disk-io.c +22 -6
index 5124c15705ce..34c68401c6bc 100644
@@ -2408,6 +2408,7 @@ static int validate_super(struct btrfs_fs_info *fs_info,
 {
 	u64 nodesize = btrfs_super_nodesize(sb);
 	u64 sectorsize = btrfs_super_sectorsize(sb);
+	u64 generation;
 	int ret = 0;
 
 	if (btrfs_super_magic(sb) != BTRFS_MAGIC) {
@@ -2534,21 +2535,36 @@ static int validate_super(struct btrfs_fs_info *fs_info,
 		ret = -EINVAL;
 	}
 
+	generation = btrfs_super_generation(sb);
 	/*
 	 * The generation is a global counter, we'll trust it more than the others
 	 * but it's still possible that it's the one that's wrong.
 	 */
-	if (btrfs_super_generation(sb) < btrfs_super_chunk_root_generation(sb))
+	if (generation < btrfs_super_chunk_root_generation(sb))
 		btrfs_warn(fs_info,
 			"suspicious: generation < chunk_root_generation: %llu < %llu",
-			btrfs_super_generation(sb),
-			btrfs_super_chunk_root_generation(sb));
-	if (btrfs_super_generation(sb) < btrfs_super_cache_generation(sb)
+			generation, btrfs_super_chunk_root_generation(sb));
+	if (generation < btrfs_super_cache_generation(sb)
 	    && btrfs_super_cache_generation(sb) != (u64)-1)
 		btrfs_warn(fs_info,
 			"suspicious: generation < cache_generation: %llu < %llu",
-			btrfs_super_generation(sb),
-			btrfs_super_cache_generation(sb));
+			generation, btrfs_super_cache_generation(sb));
+
+	/*
+	 * Check log root transid against super block generation.
+	 *
+	 * Older kernel doesn't populate log_root_transid, only need to check
+	 * it when it's not zero.
+	 * Since replaying suspicious log tree can cause serious problem, refuse
+	 * to mount if it doesn't match.
+	 */
+	if (btrfs_super_log_root_transid(sb) &&
+	    btrfs_super_log_root_transid(sb) != generation + 1) {
+		btrfs_err(fs_info,
+	"bad log tree, log_root_transid != generation + 1: %llu != %llu + 1",
+			  btrfs_super_log_root_transid(sb), generation);
+		ret = -EINVAL;
+	}
 
 	return ret;
 }
Signature · 1 line
2.19.0
#9 lore raw

Re: [PATCH 2/2] btrfs: Validate btrfs_super_block::log_root_transid

Quoted 45 lines · Qu Wenruo

So we can detect log root related problem early and report the error more meaningfully other than hitting it log root read time.

---
 fs/btrfs/disk-io.c | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)
fs/btrfs/disk-io.c +7 -6
index 5124c15705ce..34c68401c6bc 100644
@@ -2408,6 +2408,7 @@ static int validate_super(struct btrfs_fs_info *fs_info,
 {
 	u64 nodesize = btrfs_super_nodesize(sb);
 	u64 sectorsize = btrfs_super_sectorsize(sb);
+	u64 generation;
 	int ret = 0;
 
 	if (btrfs_super_magic(sb) != BTRFS_MAGIC) {
@@ -2534,21 +2535,36 @@ static int validate_super(struct btrfs_fs_info *fs_info,
 		ret = -EINVAL;
 	}
 
+	generation = btrfs_super_generation(sb);
 	/*
 	 * The generation is a global counter, we'll trust it more than the others
 	 * but it's still possible that it's the one that's wrong.
 	 */
-	if (btrfs_super_generation(sb) < btrfs_super_chunk_root_generation(sb))
+	if (generation < btrfs_super_chunk_root_generation(sb))
 		btrfs_warn(fs_info,
 			"suspicious: generation < chunk_root_generation: %llu < %llu",
-			btrfs_super_generation(sb),
-			btrfs_super_chunk_root_generation(sb));
-	if (btrfs_super_generation(sb) < btrfs_super_cache_generation(sb)
+			generation, btrfs_super_chunk_root_generation(sb));
+	if (generation < btrfs_super_cache_generation(sb)
 	    && btrfs_super_cache_generation(sb) != (u64)-1)
 		btrfs_warn(fs_info,
 			"suspicious: generation < cache_generation: %llu < %llu",
-			btrfs_super_generation(sb),
-			btrfs_super_cache_generation(sb));
+			generation, btrfs_super_cache_generation(sb));
+

fs/btrfs/disk-io.c static int validate_super(struct btrfs_fs_info *fs_info, · line 2562

Quoted 10 lines
fs/btrfs/disk-io.c +10 -0
+	/*
+	 * Check log root transid against super block generation.
+	 *
+	 * Older kernel doesn't populate log_root_transid, only need to check
+	 * it when it's not zero.
+	 * Since replaying suspicious log tree can cause serious problem, refuse
+	 * to mount if it doesn't match.
+	 */
+	if (btrfs_super_log_root_transid(sb) &&
+	    btrfs_super_log_root_transid(sb) != generation + 1) {

nit: in the same vein as you do for btrfs_super_generation just assign super_log_root_Transid to a local var and use that. IMO it's more consistent with what's happening in this function.

Quoted 9 lines
fs/btrfs/disk-io.c +5 -0
+		btrfs_err(fs_info,
+	"bad log tree, log_root_transid != generation + 1: %llu != %llu + 1",
+			  btrfs_super_log_root_transid(sb), generation);
+		ret = -EINVAL;
+	}
 
 	return ret;
 }
#10 lore raw

Re: [PATCH 2/2] btrfs: Validate btrfs_super_block::log_root_transid

Quoted 54 lines · Nikolay Borisov
Quoted 51 lines · Qu Wenruo

So we can detect log root related problem early and report the error more meaningfully other than hitting it log root read time.

---
 fs/btrfs/disk-io.c | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)
fs/btrfs/disk-io.c +13 -6
index 5124c15705ce..34c68401c6bc 100644
@@ -2408,6 +2408,7 @@ static int validate_super(struct btrfs_fs_info *fs_info,
 {
 	u64 nodesize = btrfs_super_nodesize(sb);
 	u64 sectorsize = btrfs_super_sectorsize(sb);
+	u64 generation;
 	int ret = 0;
 
 	if (btrfs_super_magic(sb) != BTRFS_MAGIC) {
@@ -2534,21 +2535,36 @@ static int validate_super(struct btrfs_fs_info *fs_info,
 		ret = -EINVAL;
 	}
 
+	generation = btrfs_super_generation(sb);
 	/*
 	 * The generation is a global counter, we'll trust it more than the others
 	 * but it's still possible that it's the one that's wrong.
 	 */
-	if (btrfs_super_generation(sb) < btrfs_super_chunk_root_generation(sb))
+	if (generation < btrfs_super_chunk_root_generation(sb))
 		btrfs_warn(fs_info,
 			"suspicious: generation < chunk_root_generation: %llu < %llu",
-			btrfs_super_generation(sb),
-			btrfs_super_chunk_root_generation(sb));
-	if (btrfs_super_generation(sb) < btrfs_super_cache_generation(sb)
+			generation, btrfs_super_chunk_root_generation(sb));
+	if (generation < btrfs_super_cache_generation(sb)
 	    && btrfs_super_cache_generation(sb) != (u64)-1)
 		btrfs_warn(fs_info,
 			"suspicious: generation < cache_generation: %llu < %llu",
-			btrfs_super_generation(sb),
-			btrfs_super_cache_generation(sb));
+			generation, btrfs_super_cache_generation(sb));
+
+	/*
+	 * Check log root transid against super block generation.
+	 *
+	 * Older kernel doesn't populate log_root_transid, only need to check
+	 * it when it's not zero.
+	 * Since replaying suspicious log tree can cause serious problem, refuse
Quoted 8 lines

fs/btrfs/disk-io.c static int validate_super(struct btrfs_fs_info *fs_info, · line 2562

Quoted 4 lines
fs/btrfs/disk-io.c +4 -0
+	 * to mount if it doesn't match.
+	 */
+	if (btrfs_super_log_root_transid(sb) &&
+	    btrfs_super_log_root_transid(sb) != generation + 1) {

nit: in the same vein as you do for btrfs_super_generation just assign super_log_root_Transid to a local var and use that. IMO it's more consistent with what's happening in this function.

@generation is used for more than one location (chunk generation and cache generation), while log root transid (along with chunk generation and cache generation) isn't used more than twice.

Thus I only assign generation to @generation.

Thanks,
Qu

Quoted 10 lines
Quoted 10 lines
fs/btrfs/disk-io.c +5 -0
+		btrfs_err(fs_info,
+	"bad log tree, log_root_transid != generation + 1: %llu != %llu + 1",
+			  btrfs_super_log_root_transid(sb), generation);
+		ret = -EINVAL;
+	}
 
 	return ret;
 }
#11 lore raw

Re: [PATCH 2/2] btrfs: Validate btrfs_super_block::log_root_transid

In reply to Nikolay Borisov
Quoted 6 lines · Nikolay Borisov
Quoted 6 lines
fs/btrfs/disk-io.c +6 -0
+	/*
+	 * Check log root transid against super block generation.
+	 *
+	 * Older kernel doesn't populate log_root_transid, only need to check
+	 * it when it's not zero.
+	 * Since replaying suspicious log tree can cause serious problem, refuse
Quoted 8 lines

fs/btrfs/disk-io.c static int validate_super(struct btrfs_fs_info *fs_info, · line 2562

Quoted 4 lines
fs/btrfs/disk-io.c +4 -0
+	 * to mount if it doesn't match.
+	 */
+	if (btrfs_super_log_root_transid(sb) &&
+	    btrfs_super_log_root_transid(sb) != generation + 1) {

nit: in the same vein as you do for btrfs_super_generation just assign super_log_root_Transid to a local var and use that. IMO it's more consistent with what's happening in this function.

While I see your point I think the way Qu did it is also fine, there's not much duplication and I find it readable.