f2fs系列文章fill_super(二)

    sb_set_blocksize通过函数set_blocksize对size进行检查并设置块设备的块大小。然后将super block的s_blocksize,s_blocksize_bits设置F2FS_BLKSIZE和F2FS_BLKSIZE相应的bit位数。

int sb_set_blocksize(struct super_block *sb, int size)
{
	if (set_blocksize(sb->s_bdev, size))
		return 0;
	sb->s_blocksize = size;
	sb->s_blocksize_bits = blksize_bits(size);
	return sb->s_blocksize;
}

    set_blocksize对size进行检查,[512,page_size]是文件系统的size范围,并且必须是2的幂,并且不能比设备的块大小小。并把super block的设备的块大小s_bdev设置成F2FS_BLKSIZE。

int set_blocksize(struct block_device *bdev, int size)
{
	if (size > PAGE_SIZE || size < 512 || !is_power_of_2(size))
		return -EINVAL;

	if (size < bdev_logical_block_size(bdev))
		return -EINVAL;

	if (bdev->bd_block_size != size) {
		sync_blockdev(bdev);
		bdev->bd_block_size = size;
		bdev->bd_inode->i_blkbits = blksize_bits(size);
		kill_bdev(bdev);
	}
	return 0;
}

    read_raw_super_block:首先分配一个f2fs_super_block的空间。f2fs文件系统有着两个f2fs_super_block,调用sb_bread对其进行依次读取,直到读取到的f2fs_super_block是没有问题的。读取之后调用sanity_check_raw_super对f2fs_super_block进行一些基本的检查。如果没有问题并且raw_super是NULL(第一次肯定是NULL的,第二次读取如果第一次读取或者检查失败就会是NULL的),那么就将读取的f2fs_super_block赋值给raw_super,并且用valid_super_block记录下有效的block号(假如没有一个是有效的,那么这个是NULL,如果只有一个是有效的,那么记录的就是有效的那个,如果两个都是有效的那么记录的就是第一个)。如果读取或者检查失败过那么用recovery来标记需要对f2fs_super_block进行修复。如果两个都失败了,那么就将分配的f2fs_super_block的空间释放掉。

static int read_raw_super_block(struct f2fs_sb_info *sbi, struct f2fs_super_block **raw_super,
			int *valid_super_block, int *recovery)
{
	struct super_block *sb = sbi->sb;
	int block;
	struct buffer_head *bh;
	struct f2fs_super_block *super;
	int err = 0;

	super = kzalloc(sizeof(struct f2fs_super_block), GFP_KERNEL);
	if (!super)
		return -ENOMEM;

	for (block = 0; block < 2; block++) {
		bh = sb_bread(sb, block);
		if (!bh) {
			f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock", block + 1);
			err = -EIO;
			continue;
		}
		if (sanity_check_raw_super(sbi, bh)) {
			f2fs_msg(sb, KERN_ERR, "Can't find valid F2FS filesystem in %dth superblock",
				block + 1);
			err = -EINVAL;
			brelse(bh);
			continue;
		}

		if (!*raw_super) {
			memcpy(super, bh->b_data + F2FS_SUPER_OFFSET, sizeof(*super));
			*valid_super_block = block;
			*raw_super = super;
		}
		brelse(bh);
	}
	if (err < 0)
		*recovery = 1;
	if (!*raw_super)
		kfree(super);
	else
		err = 0;

	return err;
}

    sanity_check_raw_super:首先检查魔数magic是否为F2FS_SUPER_MAGIC,然后检查文件系统的块大小F2FS_BLKSIZE是否和页大小PAGE_SIZE相等,因为f2fs文件系统的设计上是块大小与页大小是一致的。接下来检查f2fs_super_block的字段log_blocksize经过运算后是否与块大小F2FS_BLKSIZE保持一致。接着检查segment大小是否为512个block。接着是sector的大小必须(512,PAGE_SIZE)且必须是2的幂。然后是node/meta/root inode的ino分别是1/2/3。最后通过sanity_check_area_boundary来检查CP SIT NAT SSA MAIN_AREA的界限。

static int sanity_check_raw_super(struct f2fs_sb_info *sbi, struct buffer_head *bh)
{
	struct f2fs_super_block *raw_super = (struct f2fs_super_block *)
					(bh->b_data + F2FS_SUPER_OFFSET);
	struct super_block *sb = sbi->sb;
	unsigned int blocksize;

	if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic)) {
		f2fs_msg(sb, KERN_INFO, "Magic Mismatch, valid(0x%x) - read(0x%x)",
			F2FS_SUPER_MAGIC, le32_to_cpu(raw_super->magic));
		return 1;
	}

	if (F2FS_BLKSIZE != PAGE_SIZE) {
		f2fs_msg(sb, KERN_INFO, "Invalid page_cache_size (%lu), supports only 4KB\n",
			PAGE_SIZE);
		return 1;
	}

	blocksize = 1 << le32_to_cpu(raw_super->log_blocksize);
	if (blocksize != F2FS_BLKSIZE) {
		f2fs_msg(sb, KERN_INFO, "Invalid blocksize (%u), supports only 4KB\n", blocksize);
		return 1;
	}

	if (le32_to_cpu(raw_super->log_blocks_per_seg) != 9) {
		f2fs_msg(sb, KERN_INFO, "Invalid log blocks per segment (%u)\n",
			le32_to_cpu(raw_super->log_blocks_per_seg));
		return 1;
	}

	if (le32_to_cpu(raw_super->log_sectorsize) > F2FS_MAX_LOG_SECTOR_SIZE ||
		le32_to_cpu(raw_super->log_sectorsize) < F2FS_MIN_LOG_SECTOR_SIZE) {
		f2fs_msg(sb, KERN_INFO, "Invalid log sectorsize (%u)",
			le32_to_cpu(raw_super->log_sectorsize));
		return 1;
	}
	if (le32_to_cpu(raw_super->log_sectors_per_block) + le32_to_cpu(raw_super->log_sectorsize) !=
			F2FS_MAX_LOG_SECTOR_SIZE) {
		f2fs_msg(sb, KERN_INFO, "Invalid log sectors per block(%u) log sectorsize(%u)",
			le32_to_cpu(raw_super->log_sectors_per_block), le32_to_cpu(raw_super->log_sectorsize));
		return 1;
	}

	if (le32_to_cpu(raw_super->node_ino) != 1 ||
		le32_to_cpu(raw_super->meta_ino) != 2 ||
		le32_to_cpu(raw_super->root_ino) != 3) {
		f2fs_msg(sb, KERN_INFO, "Invalid Fs Meta Ino: node(%u) meta(%u) root(%u)",
			le32_to_cpu(raw_super->node_ino),
			le32_to_cpu(raw_super->meta_ino),
			le32_to_cpu(raw_super->root_ino));
		return 1;
	}
	if (sanity_check_area_boundary(sbi, bh))
		return 1;

	return 0;
}

    get_valid_checkpoint:先分配一段盛装f2fs_checkpoint和sit/nat bitmap的空间。然后调用validate_checkpoin对第一个cp pack的两个f2fs_checkpoint进行读取比对,获取稳定版本的cp pack。接着再次调用validate_checkpoin对第二个cp pack的两个f2fs_checkpoint进行读取比对,获取稳定版本的cp pack。如果两个都是稳定版本的cp pack,那么就将最新的version比较大的cp pack作为当前cp pack。如果只有一个是稳定的,那么就将这个稳定的版本作为当前cp pack。如果两个都不是稳定的,那就返回错误信息。有了稳定版本之后,首先将读取的f2fs_checkpoint拷贝到开始分配的空间,再调用函数sanity_check_ckpt对f2fs_checkpoint做一下基本的检查。然后读取sit/nat bitmap到分配的空间中。

int get_valid_checkpoint(struct f2fs_sb_info *sbi)
{
	struct f2fs_checkpoint *cp_block;
	struct f2fs_super_block *fsb = sbi->raw_super;
	struct page *cp1, *cp2, *cur_page;
	unsigned long blk_size = sbi->blocksize;
	unsigned long long cp1_version = 0, cp2_version = 0;
	unsigned long long cp_start_blk_no;
	unsigned int cp_blks = 1 + __cp_payload(sbi);
	block_t cp_blk_no;
	int i;

	sbi->ckpt = kzalloc(cp_blks * blk_size, GFP_KERNEL);
	if (!sbi->ckpt)
		return -ENOMEM;
	
	cp_start_blk_no = le32_to_cpu(fsb->cp_blkaddr);
	cp1 = validate_checkpoint(sbi, cp_start_blk_no, &cp1_version);
	cp_start_blk_no += ((unsigned long long)1) << le32_to_cpu(fsb->log_blocks_per_seg);
	cp2 = validate_checkpoint(sbi, cp_start_blk_no, &cp2_version);

	if (cp1 && cp2) {
		if (ver_after(cp2_version, cp1_version))
			cur_page = cp2;
		else
			cur_page = cp1;
	} else if (cp1) {
		cur_page = cp1;
	} else if (cp2) {
		cur_page = cp2;
	} else {
		goto fail_no_cp;
	}

	cp_block = (struct f2fs_checkpoint *)page_address(cur_page);
	memcpy(sbi->ckpt, cp_block, blk_size);

	if (sanity_check_ckpt(sbi))
		goto fail_no_cp;

	if (cp_blks <= 1)
		goto done;

	cp_blk_no = le32_to_cpu(fsb->cp_blkaddr);
	if (cur_page == cp2)
		cp_blk_no += 1 << le32_to_cpu(fsb->log_blocks_per_seg);

	for (i = 1; i < cp_blks; i++) {
		void *sit_bitmap_ptr;
		unsigned char *ckpt = (unsigned char *)sbi->ckpt;
		cur_page = get_meta_page(sbi, cp_blk_no + i);
		sit_bitmap_ptr = page_address(cur_page);
		memcpy(ckpt + i * blk_size, sit_bitmap_ptr, blk_size);
		f2fs_put_page(cur_page, 1);
	}
done:
	f2fs_put_page(cp1, 1);
	f2fs_put_page(cp2, 1);
	return 0;

fail_no_cp:
	kfree(sbi->ckpt);
	return -EINVAL;
}

       validate_checkpoint:首先调用get_checkpoint_version读取cp pack中的第一个f2fs_checkpoint,获取其版本号。然后再调用get_checkpoint_version读取cp pack中的第二个f2fs_checkpoint,获取其版本号。如果读取校验失败则返回NULL。接下来比对cp pack中的两个f2fs_checkpoint的version,如果是一致的,那么说明这个cp pack是个稳定的版本,返回读取的f2fs_checkpoint。否则这个cp pack是在check_point的过程中出现宕机了,返回错误信息。

static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
				block_t cp_addr, unsigned long long *version)
{
	struct page *cp_page_1 = NULL, *cp_page_2 = NULL;
	struct f2fs_checkpoint *cp_block = NULL;
	unsigned long long cur_version = 0, pre_version = 0;
	int err;

	err = get_checkpoint_version(sbi, cp_addr, &cp_block, &cp_page_1, version);
	if (err)
		goto invalid_cp1;
	pre_version = *version;

	cp_addr += le32_to_cpu(cp_block->cp_pack_total_block_count) - 1;
	err = get_checkpoint_version(sbi, cp_addr, &cp_block, &cp_page_2, version);
	if (err)
		goto invalid_cp2;
	cur_version = *version;

	if (cur_version == pre_version) {
		*version = cur_version;
		f2fs_put_page(cp_page_2, 1);
		return cp_page_1;
	}
invalid_cp2:
	f2fs_put_page(cp_page_2, 1);
invalid_cp1:
	f2fs_put_page(cp_page_1, 1);
	return NULL;
}

       get_checkpoint_version:读取f2fs_checkpoint,然后对其进行crc校验,如果校验成功就返回这个f2fs_checkpoint的version,否则返回错误信息。 

static int get_checkpoint_version(struct f2fs_sb_info *sbi, block_t cp_addr,
		struct f2fs_checkpoint **cp_block, struct page **cp_page, unsigned long long *version)
{
	unsigned long blk_size = sbi->blocksize;
	size_t crc_offset = 0;
	__u32 crc = 0;

	*cp_page = get_meta_page(sbi, cp_addr);
	*cp_block = (struct f2fs_checkpoint *)page_address(*cp_page);

	crc_offset = le32_to_cpu((*cp_block)->checksum_offset);
	if (crc_offset >= blk_size) {
		f2fs_msg(sbi->sb, KERN_WARNING,
			"invalid crc_offset: %zu", crc_offset);
		return -EINVAL;
	}

	crc = le32_to_cpu(*((__le32 *)((unsigned char *)*cp_block + crc_offset)));
	if (!f2fs_crc_valid(sbi, crc, *cp_block, crc_offset)) {
		f2fs_msg(sbi->sb, KERN_WARNING, "invalid crc value");
		return -EINVAL;
	}

	*version = cur_cp_version(*cp_block);
	return 0;
}

       sanity_check_ckpt:统计f2fs_super_block中的segment_count_ckpt、segment_count_sit、segment_count_nat、segment_count_ssa加上f2fs_checkpoint中的rsvd_segment_count不能大于f2fs_super_block中的总的segment的数量segment_count,也就是main area不能是没有空间的。然后f2fs_checkpoint中不能设置CP_ERROR_FLAG。

int sanity_check_ckpt(struct f2fs_sb_info *sbi)
{
	unsigned int total, fsmeta;
	struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);

	total = le32_to_cpu(raw_super->segment_count);
	fsmeta = le32_to_cpu(raw_super->segment_count_ckpt);
	fsmeta += le32_to_cpu(raw_super->segment_count_sit);
	fsmeta += le32_to_cpu(raw_super->segment_count_nat);
	fsmeta += le32_to_cpu(ckpt->rsvd_segment_count);
	fsmeta += le32_to_cpu(raw_super->segment_count_ssa);

	if (unlikely(fsmeta >= total))
		return 1;

	if (unlikely(f2fs_cp_error(sbi))) {
		f2fs_msg(sbi->sb, KERN_ERR, "A bug case: need to run fsck");
		return 1;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/WaterWin/article/details/79807635