1. 程式人生 > >f2fs系列文章——快取summary寫入磁碟的問題

f2fs系列文章——快取summary寫入磁碟的問題

    這篇文章講述一下f2fs檔案系統中快取在記憶體中的f2fs_summary寫入磁碟的問題,這個涉及到f2fs_summary寫入磁碟的時機、位置以及curseg在do_checkpoint中的寫入問題和mount的時候的恢復curseg的問題。

    首先,對於curseg_info中的f2fs_summary_block有兩種方式同步到page cache中。一種是在curseg_info進行替換時呼叫change_curseg或者new_curseg時呼叫write_sum_page將curseg_info中的f2fs_summary_block同步到page cache中。第二種是在do_checkpoint的時候呼叫write_data_summaries或者write_node_summaries將其同步到page cache中。

static void new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec)
{
	struct curseg_info *curseg = CURSEG_I(sbi, type);
	unsigned int segno = curseg->segno;
	int dir = ALLOC_LEFT;

	write_sum_page(sbi, curseg->sum_blk, GET_SUM_BLOCK(sbi, segno));
	if (type == CURSEG_WARM_DATA || type == CURSEG_COLD_DATA)
		dir = ALLOC_RIGHT;

	if (test_opt(sbi, NOHEAP))
		dir = ALLOC_RIGHT;

	get_new_segment(sbi, &segno, new_sec, dir);
	curseg->next_segno = segno;
	reset_curseg(sbi, type, 1);
	curseg->alloc_type = LFS;
}
static void change_curseg(struct f2fs_sb_info *sbi, int type, bool reuse)
{
	struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
	struct curseg_info *curseg = CURSEG_I(sbi, type);
	unsigned int new_segno = curseg->next_segno;
	struct f2fs_summary_block *sum_node;
	struct page *sum_page;

	write_sum_page(sbi, curseg->sum_blk, GET_SUM_BLOCK(sbi, curseg->segno));
	__set_test_and_inuse(sbi, new_segno);

	mutex_lock(&dirty_i->seglist_lock);
	__remove_dirty_segment(sbi, new_segno, PRE);
	__remove_dirty_segment(sbi, new_segno, DIRTY);
	mutex_unlock(&dirty_i->seglist_lock);

	reset_curseg(sbi, type, 1);
	curseg->alloc_type = SSR;
	__next_free_blkoff(sbi, curseg, 0);

	if (reuse) {
		sum_page = get_sum_page(sbi, new_segno);
		sum_node = (struct f2fs_summary_block *)page_address(sum_page);
		memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE);
		f2fs_put_page(sum_page, 1);
	}
}
static void write_sum_page(struct f2fs_sb_info *sbi, 
				struct f2fs_summary_block *sum_blk, block_t blk_addr)  
{  
    update_meta_page(sbi, (void *)sum_blk, blk_addr);  
}  
  
void update_meta_page(struct f2fs_sb_info *sbi, void *src, block_t blk_addr)  
{  
    struct page *page = grab_meta_page(sbi, blk_addr);  
    void *dst = page_address(page);  
  
    if (src)  
        memcpy(dst, src, PAGE_SIZE);  
    else  
        memset(dst, 0, PAGE_SIZE);  
    set_page_dirty(page);  
    f2fs_put_page(page, 1);  
} 
static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
{

	...

	write_data_summaries(sbi, start_blk);
	
	...

	if (__remain_node_summaries(cpc->reason)) {
		write_node_summaries(sbi, start_blk);
		start_blk += NR_CURSEG_NODE_TYPE;
	}
	update_meta_page(sbi, ckpt, start_blk);

	...

}

    這裡兩種方式的page cache對應的是不同的位置,第一種最後呼叫的update_meta_page中的grab_meta_page的blk_addr對應的是該f2fs_summary_block在SSA區域對應的位置;第二種呼叫的update_meta_page中的start_blk對應的是cp pack區域對應的位置,而且對於node的summary只有在umount和fastboot兩種情況才會寫入cp pack區域。

static inline bool __remain_node_summaries(int reason)
{
	return (reason == CP_UMOUNT || reason == CP_FASTBOOT);
}

    對於第一種情況落到SSA,由於已經換了curseg,所以之後訪問這個f2fs_summary_block就是訪問SSA區域,所以不會出現任何問題。但是第二種情況,訪問這個f2fs_summary_block是通過curseg_info來訪問的,而curseg_info的summary涉及到了宕機問題。對於data的summary,這個由於在do_checkpoint的時候會將這些寫進cp pack區域,恢復的時候可以恢復到上一個checkpoint的資料。但是對於node的summary在do_checkpoint的時候沒有寫入,那麼宕機之後就沒有可恢復的資料。但是實際上在mount的時候node的curseg_info的恢復時在基於segno和next_blk在上次do_checkpoint儲存的情況下,對這個segno對應的segment的[0,next_blk]的node進行讀取,用其中的footer裡面的資料來恢復node對應的curseg_info的f2fs_summary_block。

static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
{

	...

	if (IS_NODESEG(type)) {
		if (__exist_node_summaries(sbi)) {
			struct f2fs_summary *ns = &sum->entries[0];
			int i;
			for (i = 0; i < sbi->blocks_per_seg; i++, ns++) {
				ns->version = 0;
				ns->ofs_in_node = 0;
			}
		} else {
			int err;

			err = restore_node_summary(sbi, segno, sum);
			if (err) {
				f2fs_put_page(new, 1);
				return err;
			}
		}
	}

	...

}

 

int restore_node_summary(struct f2fs_sb_info *sbi, unsigned int segno, struct f2fs_summary_block *sum)
{
	struct f2fs_node *rn;
	struct f2fs_summary *sum_entry;
	block_t addr;
	int bio_blocks = MAX_BIO_BLOCKS(sbi);
	int i, idx, last_offset, nrpages;

	last_offset = sbi->blocks_per_seg;
	addr = START_BLOCK(sbi, segno);
	sum_entry = &sum->entries[0];

	for (i = 0; i < last_offset; i += nrpages, addr += nrpages) {
		nrpages = min(last_offset - i, bio_blocks);

		ra_meta_pages(sbi, addr, nrpages, META_POR, true);
		for (idx = addr; idx < addr + nrpages; idx++) {
			struct page *page = get_tmp_page(sbi, idx);

			rn = F2FS_NODE(page);
			sum_entry->nid = rn->footer.nid;
			sum_entry->version = 0;
			sum_entry->ofs_in_node = 0;
			sum_entry++;
			f2fs_put_page(page, 1);
		}

		invalidate_mapping_pages(META_MAPPING(sbi), addr, addr + nrpages);
	}
	return 0;
}

    關於node的curseg_info的這種機制可能是為了減少check_point的開銷(也不是很大啊),但是重新啟動的開銷就可能很大(讀取了大量的node)。在恢復的時候,由於對於node的f2fs_summary,其reserved[3]或者version和ofs_in_node對應的3個位元組全部都置為了零。