On 2019/1/2 下午4:57, Nikolay Borisov wrote:
>
>
> On 25.12.18 г. 7:28 ч., Qu Wenruo wrote:
>> The code is mostly ported from kernel with minimal change.
>>
>> Since btrfs-progs doesn't support replaying log, there is some code
>> unnecessary for btrfs-progs, but to keep the code the same, that
>> unnecessary code is kept as it.
>>
>> Now "btrfs check --repair" will update backup roots correctly.
>>
>> Signed-off-by: Qu Wenruo <w...@suse.com>
>> ---
>> disk-io.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 78 insertions(+)
>>
>> diff --git a/disk-io.c b/disk-io.c
>> index 133835a4d063..f28a6b526391 100644
>> --- a/disk-io.c
>> +++ b/disk-io.c
>> @@ -1621,6 +1621,83 @@ write_err:
>> return ret;
>> }
>>
>> +/*
>> + * copy all the root pointers into the super backup array.
>> + * this will bump the backup pointer by one when it is
>> + * done
>> + */
>> +static void backup_super_roots(struct btrfs_fs_info *info)
>> +{
>> + struct btrfs_root_backup *root_backup;
>> + int next_backup;
>> + int last_backup;
>> +
>> + last_backup = find_best_backup_root(info->super_copy);
>
> Slightly offtopic: but find_best_backup_root only partially resembles
> it's kernel counterpart - find_newest_super_backup. And it's missing the
> code for handling wraparound. So in kernel space if the newest root is
> at index 3 and index 0 backup also has the same gen then we switch the
> index to 0, why is this needed?
I'm not sure either.
IIRC kernel shouldn't create two backup roots with the same gen.
> In any case I think
> 'find_best_backup_root' in progs should be renamed to
> 'find_newest_super_backup' and copied from kernel space.
>
>> + next_backup = (last_backup + 1) % BTRFS_NUM_BACKUP_ROOTS;
>
> This logic is different than the kernel one which does:
> last_backup = (next_backup + BTRFS_NUM_BACKUP_ROOTS - 1) %
> BTRFS_NUM_BACKUP_ROOTS;
>
> So in the kernel when info->backup_root_index == 0 then the newest
> backup root is going to be written at position 3, the next one in pos 1,
> the next in pos 2 and so on. OTOH in userspace we might potentially
> start writing at 1 (which doesn't seem problematic per se). So why the
> difference in logic?
The logic is the same, for kernel it's 'last_backup' which should, and
is, going backward one slot.
And the next one is still written to next slot.
Just as the dump super shows:
$ btrfs ins dump-super -f /dev/data/btrfs | grep -A1 "backup "
backup 0:
backup_tree_root: 30818304 gen: 10 level: 0
--
backup 1:
backup_tree_root: 30621696 gen: 7 level: 0
--
backup 2:
backup_tree_root: 30703616 gen: 8 level: 0
--
backup 3:
backup_tree_root: 30785536 gen: 9 level: 0
So the logic should be the same.
Just simpler implementation in btrfs-progs, without
fs_info->backup_root_index.
Thanks,
Qu
>
>> +
>> + /* just overwrite the last backup if we're at the same generation */
>> + root_backup = info->super_copy->super_roots + last_backup;
>> + if (btrfs_backup_tree_root_gen(root_backup) ==
>> + btrfs_header_generation(info->tree_root->node))
>> + next_backup = last_backup;
>> +
>> + root_backup = info->super_copy->super_roots + next_backup;
>> +
>> + /*
>> + * make sure all of our padding and empty slots get zero filled
>> + * regardless of which ones we use today
>> + */
>> + memset(root_backup, 0, sizeof(*root_backup));
>> + btrfs_set_backup_tree_root(root_backup, info->tree_root->node->start);
>> + btrfs_set_backup_tree_root_gen(root_backup,
>> + btrfs_header_generation(info->tree_root->node));
>> + btrfs_set_backup_tree_root_level(root_backup,
>> + btrfs_header_level(info->tree_root->node));
>> +
>> + btrfs_set_backup_chunk_root(root_backup, info->chunk_root->node->start);
>> + btrfs_set_backup_chunk_root_gen(root_backup,
>> + btrfs_header_generation(info->chunk_root->node));
>> + btrfs_set_backup_chunk_root_level(root_backup,
>> + btrfs_header_level(info->chunk_root->node));
>> +
>> + btrfs_set_backup_extent_root(root_backup,
>> info->extent_root->node->start);
>> + btrfs_set_backup_extent_root_gen(root_backup,
>> +
>> btrfs_header_generation(info->extent_root->node));
>> + btrfs_set_backup_extent_root_level(root_backup,
>> + btrfs_header_level(info->extent_root->node));
>> + /*
>> + * we might commit during log recovery, which happens before we set
>> + * the fs_root. Make sure it is valid before we fill it in.
>> + */
>> + if (info->fs_root && info->fs_root->node) {
>> + btrfs_set_backup_fs_root(root_backup,
>> + info->fs_root->node->start);
>> + btrfs_set_backup_fs_root_gen(root_backup,
>> + btrfs_header_generation(info->fs_root->node));
>> + btrfs_set_backup_fs_root_level(root_backup,
>> + btrfs_header_level(info->fs_root->node));
>> + }
>> +
>> + btrfs_set_backup_dev_root(root_backup, info->dev_root->node->start);
>> + btrfs_set_backup_dev_root_gen(root_backup,
>> + btrfs_header_generation(info->dev_root->node));
>> + btrfs_set_backup_dev_root_level(root_backup,
>> +
>> btrfs_header_level(info->dev_root->node));
>> +
>> + btrfs_set_backup_csum_root(root_backup, info->csum_root->node->start);
>> + btrfs_set_backup_csum_root_gen(root_backup,
>> + btrfs_header_generation(info->csum_root->node));
>> + btrfs_set_backup_csum_root_level(root_backup,
>> + btrfs_header_level(info->csum_root->node));
>> +
>> + btrfs_set_backup_total_bytes(root_backup,
>> + btrfs_super_total_bytes(info->super_copy));
>> + btrfs_set_backup_bytes_used(root_backup,
>> + btrfs_super_bytes_used(info->super_copy));
>> + btrfs_set_backup_num_devices(root_backup,
>> + btrfs_super_num_devices(info->super_copy));
>> +};
>> +
>> int write_all_supers(struct btrfs_fs_info *fs_info)
>> {
>> struct list_head *head = &fs_info->fs_devices->devices;
>> @@ -1630,6 +1707,7 @@ int write_all_supers(struct btrfs_fs_info *fs_info)
>> int ret;
>> u64 flags;
>>
>> + backup_super_roots(fs_info);
>> sb = fs_info->super_copy;
>> dev_item = &sb->dev_item;
>> list_for_each_entry(dev, head, dev_list) {
>>