1039 lines · 31.4 KB · C
RawDownload
/* SPDX-License-Identifier: GPL-2.0 *//* * fs/f2fs/segment.h * * Copyright (c) 2012 Samsung Electronics Co., Ltd. *             http://www.samsung.com/ */#include <linux/blkdev.h>#include <linux/backing-dev.h> /* constant macro */#define NULL_SEGNO			((unsigned int)(~0))#define NULL_SECNO			((unsigned int)(~0)) #define DEF_RECLAIM_PREFREE_SEGMENTS	5	/* 5% over total segments */#define DEF_MAX_RECLAIM_PREFREE_SEGMENTS	4096	/* 8GB in maximum */ #define F2FS_MIN_SEGMENTS	9 /* SB + 2 (CP + SIT + NAT) + SSA + MAIN */#define F2FS_MIN_META_SEGMENTS	8 /* SB + 2 (CP + SIT + NAT) + SSA */ #define INVALID_MTIME ULLONG_MAX /* no valid blocks in a segment/section */ /* L: Logical segment # in volume, R: Relative segment # in main area */#define GET_L2R_SEGNO(free_i, segno)	((segno) - (free_i)->start_segno)#define GET_R2L_SEGNO(free_i, segno)	((segno) + (free_i)->start_segno) #define IS_DATASEG(t)	((t) <= CURSEG_COLD_DATA)#define IS_NODESEG(t)	((t) >= CURSEG_HOT_NODE && (t) <= CURSEG_COLD_NODE)#define SE_PAGETYPE(se)	((IS_NODESEG((se)->type) ? NODE : DATA)) static inline void sanity_check_seg_type(struct f2fs_sb_info *sbi,						unsigned short seg_type){	f2fs_bug_on(sbi, seg_type >= NR_PERSISTENT_LOG);} #define MAIN_BLKADDR(sbi)						\	(SM_I(sbi) ? SM_I(sbi)->main_blkaddr : 				\		le32_to_cpu(F2FS_RAW_SUPER(sbi)->main_blkaddr))#define SEG0_BLKADDR(sbi)						\	(SM_I(sbi) ? SM_I(sbi)->seg0_blkaddr : 				\		le32_to_cpu(F2FS_RAW_SUPER(sbi)->segment0_blkaddr)) #define MAIN_SEGS(sbi)	(SM_I(sbi)->main_segments)#define MAIN_SECS(sbi)	((sbi)->total_sections)#define has_unpinned_area(sbi)	((sbi)->pinned_area_max_secno < MAIN_SECS(sbi)) #define TOTAL_SEGS(sbi)							\	(SM_I(sbi) ? SM_I(sbi)->segment_count : 				\		le32_to_cpu(F2FS_RAW_SUPER(sbi)->segment_count))#define TOTAL_BLKS(sbi)	(SEGS_TO_BLKS(sbi, TOTAL_SEGS(sbi))) #define MAX_BLKADDR(sbi)	(SEG0_BLKADDR(sbi) + TOTAL_BLKS(sbi))#define SEGMENT_SIZE(sbi)	(1ULL << ((sbi)->log_blocksize +	\					(sbi)->log_blocks_per_seg)) #define START_BLOCK(sbi, segno)	(SEG0_BLKADDR(sbi) +			\	 (SEGS_TO_BLKS(sbi, GET_R2L_SEGNO(FREE_I(sbi), segno)))) #define NEXT_FREE_BLKADDR(sbi, curseg)					\	(START_BLOCK(sbi, (curseg)->segno) + (curseg)->next_blkoff) #define GET_SEGOFF_FROM_SEG0(sbi, blk_addr)	((blk_addr) - SEG0_BLKADDR(sbi))#define GET_SEGNO_FROM_SEG0(sbi, blk_addr)				\	(BLKS_TO_SEGS(sbi, GET_SEGOFF_FROM_SEG0(sbi, blk_addr)))#define GET_BLKOFF_FROM_SEG0(sbi, blk_addr)				\	(GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & (BLKS_PER_SEG(sbi) - 1)) #define GET_SEGNO(sbi, blk_addr)					\	((!__is_valid_data_blkaddr(blk_addr)) ?			\	NULL_SEGNO : GET_L2R_SEGNO(FREE_I(sbi),			\		GET_SEGNO_FROM_SEG0(sbi, blk_addr)))#ifdef CONFIG_BLK_DEV_ZONED#define CAP_BLKS_PER_SEC(sbi)					\	(BLKS_PER_SEC(sbi) - (sbi)->unusable_blocks_per_sec)#define CAP_SEGS_PER_SEC(sbi)					\	(SEGS_PER_SEC(sbi) -					\	BLKS_TO_SEGS(sbi, (sbi)->unusable_blocks_per_sec))#else#define CAP_BLKS_PER_SEC(sbi) BLKS_PER_SEC(sbi)#define CAP_SEGS_PER_SEC(sbi) SEGS_PER_SEC(sbi)#endif#define GET_START_SEG_FROM_SEC(sbi, segno)			\	(rounddown(segno, SEGS_PER_SEC(sbi)))#define GET_SEC_FROM_SEG(sbi, segno)				\	(((segno) == -1) ? -1 : (segno) / SEGS_PER_SEC(sbi))#define GET_SEG_FROM_SEC(sbi, secno)				\	((secno) * SEGS_PER_SEC(sbi))#define GET_ZONE_FROM_SEC(sbi, secno)				\	(((secno) == -1) ? -1 : (secno) / (sbi)->secs_per_zone)#define GET_ZONE_FROM_SEG(sbi, segno)				\	GET_ZONE_FROM_SEC(sbi, GET_SEC_FROM_SEG(sbi, segno)) #define GET_SUM_BLOCK(sbi, segno)	\	(SM_I(sbi)->ssa_blkaddr + (segno / (sbi)->sums_per_block))#define GET_SUM_BLKOFF(sbi, segno) (segno % (sbi)->sums_per_block)#define SUM_BLK_PAGE_ADDR(sbi, folio, segno)	\	(folio_address(folio) + GET_SUM_BLKOFF(sbi, segno) * (sbi)->sum_blocksize) #define GET_SUM_TYPE(footer) ((footer)->entry_type)#define SET_SUM_TYPE(footer, type) ((footer)->entry_type = (type)) #define SIT_ENTRY_OFFSET(sit_i, segno)					\	((segno) % (sit_i)->sents_per_block)#define SIT_BLOCK_OFFSET(segno)					\	((segno) / SIT_ENTRY_PER_BLOCK)#define	START_SEGNO(segno)		\	(SIT_BLOCK_OFFSET(segno) * SIT_ENTRY_PER_BLOCK)#define SIT_BLK_CNT(sbi)			\	DIV_ROUND_UP(MAIN_SEGS(sbi), SIT_ENTRY_PER_BLOCK)#define f2fs_bitmap_size(nr)			\	(BITS_TO_LONGS(nr) * sizeof(unsigned long)) #define SECTOR_FROM_BLOCK(blk_addr)					\	(((sector_t)blk_addr) << F2FS_LOG_SECTORS_PER_BLOCK)#define SECTOR_TO_BLOCK(sectors)					\	((sectors) >> F2FS_LOG_SECTORS_PER_BLOCK) /* * In the victim_sel_policy->alloc_mode, there are three block allocation modes. * LFS writes data sequentially with cleaning operations. * SSR (Slack Space Recycle) reuses obsolete space without cleaning operations. * AT_SSR (Age Threshold based Slack Space Recycle) merges fragments into * fragmented segment which has similar aging degree. */enum {	LFS = 0,	SSR,	AT_SSR,}; /* * In the victim_sel_policy->gc_mode, there are three gc, aka cleaning, modes. * GC_CB is based on cost-benefit algorithm. * GC_GREEDY is based on greedy algorithm. * GC_AT is based on age-threshold algorithm. */enum {	GC_CB = 0,	GC_GREEDY,	GC_AT,	ALLOC_NEXT,	FLUSH_DEVICE,	MAX_GC_POLICY,}; /* * BG_GC means the background cleaning job. * FG_GC means the on-demand cleaning job. */enum {	BG_GC = 0,	FG_GC,}; /* for a function parameter to select a victim segment */struct victim_sel_policy {	int alloc_mode;			/* LFS or SSR */	int gc_mode;			/* GC_CB or GC_GREEDY */	unsigned long *dirty_bitmap;	/* dirty segment/section bitmap */	unsigned int max_search;	/*					 * maximum # of segments/sections					 * to search					 */	unsigned int offset;		/* last scanned bitmap offset */	unsigned int ofs_unit;		/* bitmap search unit */	unsigned int min_cost;		/* minimum cost */	unsigned long long oldest_age;	/* oldest age of segments having the same min cost */	unsigned int min_segno;		/* segment # having min. cost */	unsigned long long age;		/* mtime of GCed section*/	unsigned long long age_threshold;/* age threshold */	bool one_time_gc;		/* one time GC */}; struct seg_entry {	unsigned int type:6;		/* segment type like CURSEG_XXX_TYPE */	unsigned int valid_blocks:10;	/* # of valid blocks */	unsigned int ckpt_valid_blocks:10;	/* # of valid blocks last cp */	unsigned int padding:6;		/* padding */	unsigned char *cur_valid_map;	/* validity bitmap of blocks */	/*	 * # of valid blocks and the validity bitmap stored in the last	 * checkpoint pack. This information is used by the SSR mode.	 */	unsigned char *ckpt_valid_map;	/* validity bitmap of blocks last cp */	unsigned char *discard_map;	unsigned long long mtime;	/* modification time of the segment */}; struct sec_entry {	unsigned int valid_blocks;	/* # of valid blocks in a section */	unsigned int ckpt_valid_blocks; /* # of valid blocks last cp in a section */}; #define MAX_SKIP_GC_COUNT			16 struct revoke_entry {	struct list_head list;	block_t old_addr;		/* for revoking when fail to commit */	pgoff_t index;}; struct sit_info {	block_t sit_base_addr;		/* start block address of SIT area */	block_t sit_blocks;		/* # of blocks used by SIT area */	block_t written_valid_blocks;	/* # of valid blocks in main area */	char *bitmap;			/* all bitmaps pointer */	char *sit_bitmap;		/* SIT bitmap pointer */#ifdef CONFIG_F2FS_CHECK_FS	/* bitmap of segments to be ignored by GC in case of errors */	unsigned long *invalid_segmap;#endif	unsigned int bitmap_size;	/* SIT bitmap size */ 	unsigned long *tmp_map;			/* bitmap for temporal use */	unsigned long *dirty_sentries_bitmap;	/* bitmap for dirty sentries */	unsigned int dirty_sentries;		/* # of dirty sentries */	unsigned int sents_per_block;		/* # of SIT entries per block */	struct rw_semaphore sentry_lock;	/* to protect SIT cache */	struct seg_entry *sentries;		/* SIT segment-level cache */	struct sec_entry *sec_entries;		/* SIT section-level cache */ 	/* for cost-benefit algorithm in cleaning procedure */	unsigned long long elapsed_time;	/* elapsed time after mount */	unsigned long long mounted_time;	/* mount time */	unsigned long long min_mtime;		/* min. modification time */	unsigned long long max_mtime;		/* max. modification time */	unsigned long long dirty_min_mtime;	/* rerange candidates in GC_AT */	unsigned long long dirty_max_mtime;	/* rerange candidates in GC_AT */ 	unsigned int last_victim[MAX_GC_POLICY]; /* last victim segment # */}; struct free_segmap_info {	unsigned int start_segno;	/* start segment number logically */	unsigned int free_segments;	/* # of free segments */	unsigned int free_sections;	/* # of free sections */	spinlock_t segmap_lock;		/* free segmap lock */	unsigned long *free_segmap;	/* free segment bitmap */	unsigned long *free_secmap;	/* free section bitmap */}; /* Notice: The order of dirty type is same with CURSEG_XXX in f2fs.h */enum dirty_type {	DIRTY_HOT_DATA,		/* dirty segments assigned as hot data logs */	DIRTY_WARM_DATA,	/* dirty segments assigned as warm data logs */	DIRTY_COLD_DATA,	/* dirty segments assigned as cold data logs */	DIRTY_HOT_NODE,		/* dirty segments assigned as hot node logs */	DIRTY_WARM_NODE,	/* dirty segments assigned as warm node logs */	DIRTY_COLD_NODE,	/* dirty segments assigned as cold node logs */	DIRTY,			/* to count # of dirty segments */	PRE,			/* to count # of entirely obsolete segments */	NR_DIRTY_TYPE}; struct dirty_seglist_info {	unsigned long *dirty_segmap[NR_DIRTY_TYPE];	unsigned long *dirty_secmap;	struct mutex seglist_lock;		/* lock for segment bitmaps */	int nr_dirty[NR_DIRTY_TYPE];		/* # of dirty segments */	unsigned long *victim_secmap;		/* background GC victims */	unsigned long *pinned_secmap;		/* pinned victims from foreground GC */	unsigned int pinned_secmap_cnt;		/* count of victims which has pinned data */	bool enable_pin_section;		/* enable pinning section */}; /* for active log information */struct curseg_info {	struct mutex curseg_mutex;		/* lock for consistency */	struct f2fs_summary_block *sum_blk;	/* cached summary block */	struct rw_semaphore journal_rwsem;	/* protect journal area */	struct f2fs_journal *journal;		/* cached journal info */	unsigned char alloc_type;		/* current allocation type */	unsigned short seg_type;		/* segment type like CURSEG_XXX_TYPE */	unsigned int segno;			/* current segment number */	unsigned short next_blkoff;		/* next block offset to write */	unsigned int zone;			/* current zone number */	unsigned int next_segno;		/* preallocated segment */	int fragment_remained_chunk;		/* remained block size in a chunk for block fragmentation mode */	bool inited;				/* indicate inmem log is inited */}; struct sit_entry_set {	struct list_head set_list;	/* link with all sit sets */	unsigned int start_segno;	/* start segno of sits in set */	unsigned int entry_cnt;		/* the # of sit entries in set */}; /* * inline functions */static inline struct curseg_info *CURSEG_I(struct f2fs_sb_info *sbi, int type){	return (struct curseg_info *)(SM_I(sbi)->curseg_array + type);} static inline bool is_curseg(struct f2fs_sb_info *sbi, unsigned int segno){	int i; 	for (i = CURSEG_HOT_DATA; i < NO_CHECK_TYPE; i++) {		if (segno == CURSEG_I(sbi, i)->segno)			return true;	}	return false;} static inline bool is_cursec(struct f2fs_sb_info *sbi, unsigned int secno){	int i; 	for (i = CURSEG_HOT_DATA; i < NO_CHECK_TYPE; i++) {		if (secno == GET_SEC_FROM_SEG(sbi, CURSEG_I(sbi, i)->segno))			return true;	}	return false;} static inline struct seg_entry *get_seg_entry(struct f2fs_sb_info *sbi,						unsigned int segno){	struct sit_info *sit_i = SIT_I(sbi);	return &sit_i->sentries[segno];} static inline struct sec_entry *get_sec_entry(struct f2fs_sb_info *sbi,						unsigned int segno){	struct sit_info *sit_i = SIT_I(sbi);	return &sit_i->sec_entries[GET_SEC_FROM_SEG(sbi, segno)];} static inline unsigned int get_valid_blocks(struct f2fs_sb_info *sbi,				unsigned int segno, bool use_section){	/*	 * In order to get # of valid blocks in a section instantly from many	 * segments, f2fs manages two counting structures separately.	 */	if (use_section && __is_large_section(sbi))		return get_sec_entry(sbi, segno)->valid_blocks;	else		return get_seg_entry(sbi, segno)->valid_blocks;} static inline unsigned int get_ckpt_valid_blocks(struct f2fs_sb_info *sbi,				unsigned int segno, bool use_section){	if (use_section && __is_large_section(sbi))		return get_sec_entry(sbi, segno)->ckpt_valid_blocks;	else		return get_seg_entry(sbi, segno)->ckpt_valid_blocks;} static inline void set_ckpt_valid_blocks(struct f2fs_sb_info *sbi,		unsigned int segno){	unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);	unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno);	unsigned int blocks = 0;	int i; 	for (i = 0; i < SEGS_PER_SEC(sbi); i++, start_segno++) {		struct seg_entry *se = get_seg_entry(sbi, start_segno); 		blocks += se->ckpt_valid_blocks;	}	get_sec_entry(sbi, segno)->ckpt_valid_blocks = blocks;} #ifdef CONFIG_F2FS_CHECK_FSstatic inline void sanity_check_valid_blocks(struct f2fs_sb_info *sbi,		unsigned int segno){	unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);	unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno);	unsigned int blocks = 0;	int i; 	for (i = 0; i < SEGS_PER_SEC(sbi); i++, start_segno++) {		struct seg_entry *se = get_seg_entry(sbi, start_segno); 		blocks += se->ckpt_valid_blocks;	} 	if (blocks != get_sec_entry(sbi, segno)->ckpt_valid_blocks) {		f2fs_err(sbi,			"Inconsistent ckpt valid blocks: "			"seg entry(%d) vs sec entry(%d) at secno %d",			blocks, get_sec_entry(sbi, segno)->ckpt_valid_blocks, secno);		f2fs_bug_on(sbi, 1);	}}#elsestatic inline void sanity_check_valid_blocks(struct f2fs_sb_info *sbi,			unsigned int segno){}#endifstatic inline void seg_info_from_raw_sit(struct seg_entry *se,					struct f2fs_sit_entry *rs){	se->valid_blocks = GET_SIT_VBLOCKS(rs);	se->ckpt_valid_blocks = GET_SIT_VBLOCKS(rs);	memcpy(se->cur_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE);	memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE);	se->type = GET_SIT_TYPE(rs);	se->mtime = le64_to_cpu(rs->mtime);} static inline void __seg_info_to_raw_sit(struct seg_entry *se,					struct f2fs_sit_entry *rs){	unsigned short raw_vblocks = (se->type << SIT_VBLOCKS_SHIFT) |					se->valid_blocks;	rs->vblocks = cpu_to_le16(raw_vblocks);	memcpy(rs->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE);	rs->mtime = cpu_to_le64(se->mtime);} static inline void seg_info_to_sit_folio(struct f2fs_sb_info *sbi,				struct folio *folio, unsigned int start){	struct f2fs_sit_block *raw_sit;	struct seg_entry *se;	struct f2fs_sit_entry *rs;	unsigned int end = min(start + SIT_ENTRY_PER_BLOCK,					(unsigned long)MAIN_SEGS(sbi));	int i; 	raw_sit = folio_address(folio);	memset(raw_sit, 0, PAGE_SIZE);	for (i = 0; i < end - start; i++) {		rs = &raw_sit->entries[i];		se = get_seg_entry(sbi, start + i);		__seg_info_to_raw_sit(se, rs);	}} static inline void seg_info_to_raw_sit(struct seg_entry *se,					struct f2fs_sit_entry *rs){	__seg_info_to_raw_sit(se, rs); 	memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE);	se->ckpt_valid_blocks = se->valid_blocks;} static inline unsigned int find_next_inuse(struct free_segmap_info *free_i,		unsigned int max, unsigned int segno){	unsigned int ret;	spin_lock(&free_i->segmap_lock);	ret = find_next_bit(free_i->free_segmap, max, segno);	spin_unlock(&free_i->segmap_lock);	return ret;} static inline void __set_free(struct f2fs_sb_info *sbi, unsigned int segno){	struct free_segmap_info *free_i = FREE_I(sbi);	unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);	unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno);	unsigned int next; 	spin_lock(&free_i->segmap_lock);	clear_bit(segno, free_i->free_segmap);	free_i->free_segments++; 	next = find_next_bit(free_i->free_segmap,			start_segno + SEGS_PER_SEC(sbi), start_segno);	if (next >= start_segno + f2fs_usable_segs_in_sec(sbi)) {		clear_bit(secno, free_i->free_secmap);		free_i->free_sections++;	}	spin_unlock(&free_i->segmap_lock);} static inline void __set_inuse(struct f2fs_sb_info *sbi,		unsigned int segno){	struct free_segmap_info *free_i = FREE_I(sbi);	unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); 	set_bit(segno, free_i->free_segmap);	free_i->free_segments--;	if (!test_and_set_bit(secno, free_i->free_secmap))		free_i->free_sections--;} static inline void __set_test_and_free(struct f2fs_sb_info *sbi,		unsigned int segno, bool inmem){	struct free_segmap_info *free_i = FREE_I(sbi);	unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);	unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno);	unsigned int next;	bool ret; 	spin_lock(&free_i->segmap_lock);

Lines 1–500 of 1039.