1. 程式人生 > >f2fs系列文章fill_super(二)

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;
}