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