mirror of
https://github.com/hardkernel/linux.git
synced 2026-04-10 23:18:10 +09:00
Pull f2fs updates from Jaegeuk Kim:
"In this round, we've followed up to support some generic features such
as cgroup, block reservation, linking fscrypt_ops, delivering
write_hints, and some ioctls. And, we could fix some corner cases in
terms of power-cut recovery and subtle deadlocks.
Enhancements:
- bitmap operations to handle NAT blocks
- readahead to improve readdir speed
- switch to use fscrypt_*
- apply write hints for direct IO
- add reserve_root=%u,resuid=%u,resgid=%u to reserve blocks for root/uid/gid
- modify b_avail and b_free to consider root reserved blocks
- support cgroup writeback
- support FIEMAP_FLAG_XATTR for fibmap
- add F2FS_IOC_PRECACHE_EXTENTS to pre-cache extents
- add F2FS_IOC_{GET/SET}_PIN_FILE to pin LBAs for data blocks
- support inode creation time
Bug fixs:
- sysfile-based quota operations
- memory footprint accounting
- allow to write data on partial preallocation case
- fix deadlock case on fallocate
- fix to handle fill_super errors
- fix missing inode updates of fsync'ed file
- recover renamed file which was fsycn'ed before
- drop inmemory pages in corner error case
- keep last_disk_size correctly
- recover missing i_inline flags during roll-forward
Various clean-up patches were added as well"
Cherry-pick from origin/upstream-f2fs-stable-linux-4.4.y:
5f9b3abb91 f2fs: support inode creation time
9fb0de1751 f2fs: rebuild sit page from sit info in mem
1062a0c018 f2fs: stop issuing discard if fs is readonly
fa043fae90 f2fs: clean up duplicated assignment in init_discard_policy
b007190234 f2fs: use GFP_F2FS_ZERO for cleanup
35b11839a1 f2fs: allow to recover node blocks given updated checkpoint
e56500860b f2fs: recover some i_inline flags
64aa9569a1 f2fs: correct removexattr behavior for null valued extended attribute
70b3a923da f2fs: drop page cache after fs shutdown
8069a0e983 f2fs: stop gc/discard thread after fs shutdown
bb924f7777 f2fs: hanlde error case in f2fs_ioc_shutdown
700b53f21e f2fs: split need_inplace_update
f31d52811c f2fs: fix to update last_disk_size correctly
eeb0118b83 f2fs: kill F2FS_INLINE_XATTR_ADDRS for cleanup
c1b74c9670 f2fs: clean up error path of fill_super
d5efd57e01 f2fs: avoid hungtask when GC encrypted block if io_bits is set
c4027d0843 f2fs: allow quota to use reserved blocks
18d267c273 f2fs: fix to drop all inmem pages correctly
4dca47531e f2fs: speed up defragment on sparse file
999f806a7c f2fs: support F2FS_IOC_PRECACHE_EXTENTS
84960fca96 f2fs: add an ioctl to disable GC for specific file
292c8e1cfd f2fs: prevent newly created inode from being dirtied incorrectly
58b1f5b0fc f2fs: support FIEMAP_FLAG_XATTR
6afa9a94d0 f2fs: fix to cover f2fs_inline_data_fiemap with inode_lock
10f4a4140b f2fs: check node page again in write end io
b203c58dfd f2fs: fix to caclulate required free section correctly
d49132d45c f2fs: handle newly created page when revoking inmem pages
2ce6b9d816 f2fs: add resgid and resuid to reserve root blocks
f53dcf6799 f2fs: implement cgroup writeback support
1338f376d5 f2fs: remove unused pend_list_tag
d4f19f6266 f2fs: avoid high cpu usage in discard thread
b78e9302e2 f2fs: make local functions static
62438ba87b f2fs: add reserved blocks for root user
06a366757f f2fs: check segment type in __f2fs_replace_block
4c6bc4be37 f2fs: update inode info to inode page for new file
591b336387 f2fs: show precise # of blocks that user/root can use
b242d7edc5 f2fs: clean up unneeded declaration
87b8168e9e f2fs: continue to do direct IO if we only preallocate partial blocks
2b4d859bd9 f2fs: enable quota at remount from r to w
54bf13a0ad f2fs: skip stop_checkpoint for user data writes
25ef3006ba f2fs: fix missing error number for xattr operation
cff2c7fe41 f2fs: recover directory operations by fsync
e2bb618a0a f2fs: return error during fill_super
8a2c11d865 f2fs: fix an error case of missing update inode page
cd38d5ada5 f2fs: fix potential hangtask in f2fs_trace_pid
e81cafbeba f2fs: no need return value in restore summary process
04d44000d6 f2fs: use unlikely for release case
925d0933d8 f2fs: don't return value in truncate_data_blocks_range
f7986c416d f2fs: clean up f2fs_map_blocks
e4f5e26cda f2fs: clean up hash codes
1f994d4708 f2fs: fix error handling in fill_super
e7db649b5f f2fs: spread f2fs_k{m,z}alloc
5d4e487b99 f2fs: inject fault to kvmalloc
8b33886c37 f2fs: inject fault to kzalloc
d946807987 f2fs: remove a redundant conditional expression
3bc01114a3 f2fs: apply write hints to select the type of segment for direct write
c80f019591 f2fs: switch to fscrypt_prepare_setattr()
bb8b850365 f2fs: switch to fscrypt_prepare_lookup()
9ab470eaf8 f2fs: switch to fscrypt_prepare_rename()
aeaac517a1 f2fs: switch to fscrypt_prepare_link()
101c6a96ad f2fs: switch to fscrypt_file_open()
6d025237a1 f2fs: remove repeated f2fs_bug_on
b01e03d724 f2fs: remove an excess variable
e1f9be2f7c f2fs: fix lock dependency in between dio_rwsem & i_mmap_sem
e5c7c86010 f2fs: remove unused parameter
f130dbb98a f2fs: still write data if preallocate only partial blocks
47ee9b2598 f2fs: introduce sysfs readdir_ra to readahead inode block in readdir
55e2f89181 f2fs: fix concurrent problem for updating free bitmap
e1398f6554 f2fs: remove unneeded memory footprint accounting
2d69561135 f2fs: no need to read nat block if nat_block_bitmap is set
4dd2d07338 f2fs: reserve nid resource for quota sysfile
Signed-off-by: Jaegeuk Kim <jaegeuk@google.com>
841 lines
25 KiB
C
841 lines
25 KiB
C
/*
|
|
* fs/f2fs/segment.h
|
|
*
|
|
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
|
|
* http://www.samsung.com/
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
#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 */
|
|
|
|
/* 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)
|
|
|
|
#define IS_HOT(t) ((t) == CURSEG_HOT_NODE || (t) == CURSEG_HOT_DATA)
|
|
#define IS_WARM(t) ((t) == CURSEG_WARM_NODE || (t) == CURSEG_WARM_DATA)
|
|
#define IS_COLD(t) ((t) == CURSEG_COLD_NODE || (t) == CURSEG_COLD_DATA)
|
|
|
|
#define IS_CURSEG(sbi, seg) \
|
|
(((seg) == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno) || \
|
|
((seg) == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno) || \
|
|
((seg) == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno) || \
|
|
((seg) == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno) || \
|
|
((seg) == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno) || \
|
|
((seg) == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno))
|
|
|
|
#define IS_CURSEC(sbi, secno) \
|
|
(((secno) == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno / \
|
|
(sbi)->segs_per_sec) || \
|
|
((secno) == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno / \
|
|
(sbi)->segs_per_sec) || \
|
|
((secno) == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno / \
|
|
(sbi)->segs_per_sec) || \
|
|
((secno) == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno / \
|
|
(sbi)->segs_per_sec) || \
|
|
((secno) == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno / \
|
|
(sbi)->segs_per_sec) || \
|
|
((secno) == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno / \
|
|
(sbi)->segs_per_sec)) \
|
|
|
|
#define MAIN_BLKADDR(sbi) (SM_I(sbi)->main_blkaddr)
|
|
#define SEG0_BLKADDR(sbi) (SM_I(sbi)->seg0_blkaddr)
|
|
|
|
#define MAIN_SEGS(sbi) (SM_I(sbi)->main_segments)
|
|
#define MAIN_SECS(sbi) ((sbi)->total_sections)
|
|
|
|
#define TOTAL_SEGS(sbi) (SM_I(sbi)->segment_count)
|
|
#define TOTAL_BLKS(sbi) (TOTAL_SEGS(sbi) << (sbi)->log_blocks_per_seg)
|
|
|
|
#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) + \
|
|
(GET_R2L_SEGNO(FREE_I(sbi), segno) << (sbi)->log_blocks_per_seg))
|
|
|
|
#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) \
|
|
(GET_SEGOFF_FROM_SEG0(sbi, blk_addr) >> (sbi)->log_blocks_per_seg)
|
|
#define GET_BLKOFF_FROM_SEG0(sbi, blk_addr) \
|
|
(GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & ((sbi)->blocks_per_seg - 1))
|
|
|
|
#define GET_SEGNO(sbi, blk_addr) \
|
|
((((blk_addr) == NULL_ADDR) || ((blk_addr) == NEW_ADDR)) ? \
|
|
NULL_SEGNO : GET_L2R_SEGNO(FREE_I(sbi), \
|
|
GET_SEGNO_FROM_SEG0(sbi, blk_addr)))
|
|
#define BLKS_PER_SEC(sbi) \
|
|
((sbi)->segs_per_sec * (sbi)->blocks_per_seg)
|
|
#define GET_SEC_FROM_SEG(sbi, segno) \
|
|
((segno) / (sbi)->segs_per_sec)
|
|
#define GET_SEG_FROM_SEC(sbi, secno) \
|
|
((secno) * (sbi)->segs_per_sec)
|
|
#define GET_ZONE_FROM_SEC(sbi, secno) \
|
|
((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) \
|
|
((sbi)->sm_info->ssa_blkaddr + (segno))
|
|
|
|
#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) \
|
|
((MAIN_SEGS(sbi) + SIT_ENTRY_PER_BLOCK - 1) / 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)
|
|
|
|
/*
|
|
* indicate a block allocation direction: RIGHT and LEFT.
|
|
* RIGHT means allocating new sections towards the end of volume.
|
|
* LEFT means the opposite direction.
|
|
*/
|
|
enum {
|
|
ALLOC_RIGHT = 0,
|
|
ALLOC_LEFT
|
|
};
|
|
|
|
/*
|
|
* In the victim_sel_policy->alloc_mode, there are two block allocation modes.
|
|
* LFS writes data sequentially with cleaning operations.
|
|
* SSR (Slack Space Recycle) reuses obsolete space without cleaning operations.
|
|
*/
|
|
enum {
|
|
LFS = 0,
|
|
SSR
|
|
};
|
|
|
|
/*
|
|
* In the victim_sel_policy->gc_mode, there are two gc, aka cleaning, modes.
|
|
* GC_CB is based on cost-benefit algorithm.
|
|
* GC_GREEDY is based on greedy algorithm.
|
|
*/
|
|
enum {
|
|
GC_CB = 0,
|
|
GC_GREEDY,
|
|
ALLOC_NEXT,
|
|
FLUSH_DEVICE,
|
|
MAX_GC_POLICY,
|
|
};
|
|
|
|
/*
|
|
* BG_GC means the background cleaning job.
|
|
* FG_GC means the on-demand cleaning job.
|
|
* FORCE_FG_GC means on-demand cleaning job in background.
|
|
*/
|
|
enum {
|
|
BG_GC = 0,
|
|
FG_GC,
|
|
FORCE_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_segmap; /* dirty segment bitmap */
|
|
unsigned int max_search; /* maximum # of segments to search */
|
|
unsigned int offset; /* last scanned bitmap offset */
|
|
unsigned int ofs_unit; /* bitmap search unit */
|
|
unsigned int min_cost; /* minimum cost */
|
|
unsigned int min_segno; /* segment # having min. cost */
|
|
};
|
|
|
|
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 */
|
|
#ifdef CONFIG_F2FS_CHECK_FS
|
|
unsigned char *cur_valid_map_mir; /* mirror of current valid bitmap */
|
|
#endif
|
|
/*
|
|
* # of valid blocks and the validity bitmap stored in the 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 */
|
|
};
|
|
|
|
struct segment_allocation {
|
|
void (*allocate_segment)(struct f2fs_sb_info *, int, bool);
|
|
};
|
|
|
|
/*
|
|
* this value is set in page as a private data which indicate that
|
|
* the page is atomically written, and it is in inmem_pages list.
|
|
*/
|
|
#define ATOMIC_WRITTEN_PAGE ((unsigned long)-1)
|
|
#define DUMMY_WRITTEN_PAGE ((unsigned long)-2)
|
|
|
|
#define IS_ATOMIC_WRITTEN_PAGE(page) \
|
|
(page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE)
|
|
#define IS_DUMMY_WRITTEN_PAGE(page) \
|
|
(page_private(page) == (unsigned long)DUMMY_WRITTEN_PAGE)
|
|
|
|
struct inmem_pages {
|
|
struct list_head list;
|
|
struct page *page;
|
|
block_t old_addr; /* for revoking when fail to commit */
|
|
};
|
|
|
|
struct sit_info {
|
|
const struct segment_allocation *s_ops;
|
|
|
|
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 *sit_bitmap; /* SIT bitmap pointer */
|
|
#ifdef CONFIG_F2FS_CHECK_FS
|
|
char *sit_bitmap_mir; /* SIT bitmap mirror */
|
|
#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 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 {
|
|
const struct victim_selection *v_ops; /* victim selction operation */
|
|
unsigned long *dirty_segmap[NR_DIRTY_TYPE];
|
|
struct mutex seglist_lock; /* lock for segment bitmaps */
|
|
int nr_dirty[NR_DIRTY_TYPE]; /* # of dirty segments */
|
|
unsigned long *victim_secmap; /* background GC victims */
|
|
};
|
|
|
|
/* victim selection function for cleaning and SSR */
|
|
struct victim_selection {
|
|
int (*get_victim)(struct f2fs_sb_info *, unsigned int *,
|
|
int, int, char);
|
|
};
|
|
|
|
/* 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 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 */
|
|
};
|
|
|
|
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 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 && sbi->segs_per_sec > 1)
|
|
return get_sec_entry(sbi, segno)->valid_blocks;
|
|
else
|
|
return get_seg_entry(sbi, segno)->valid_blocks;
|
|
}
|
|
|
|
static 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);
|
|
#ifdef CONFIG_F2FS_CHECK_FS
|
|
memcpy(se->cur_valid_map_mir, rs->valid_map, SIT_VBLOCK_MAP_SIZE);
|
|
#endif
|
|
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_page(struct f2fs_sb_info *sbi,
|
|
struct page *page, 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 = (struct f2fs_sit_block *)page_address(page);
|
|
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 + sbi->segs_per_sec, start_segno);
|
|
if (next >= start_segno + sbi->segs_per_sec) {
|
|
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)
|
|
{
|
|
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);
|
|
if (test_and_clear_bit(segno, free_i->free_segmap)) {
|
|
free_i->free_segments++;
|
|
|
|
next = find_next_bit(free_i->free_segmap,
|
|
start_segno + sbi->segs_per_sec, start_segno);
|
|
if (next >= start_segno + sbi->segs_per_sec) {
|
|
if (test_and_clear_bit(secno, free_i->free_secmap))
|
|
free_i->free_sections++;
|
|
}
|
|
}
|
|
spin_unlock(&free_i->segmap_lock);
|
|
}
|
|
|
|
static inline void __set_test_and_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);
|
|
|
|
spin_lock(&free_i->segmap_lock);
|
|
if (!test_and_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--;
|
|
}
|
|
spin_unlock(&free_i->segmap_lock);
|
|
}
|
|
|
|
static inline void get_sit_bitmap(struct f2fs_sb_info *sbi,
|
|
void *dst_addr)
|
|
{
|
|
struct sit_info *sit_i = SIT_I(sbi);
|
|
|
|
#ifdef CONFIG_F2FS_CHECK_FS
|
|
if (memcmp(sit_i->sit_bitmap, sit_i->sit_bitmap_mir,
|
|
sit_i->bitmap_size))
|
|
f2fs_bug_on(sbi, 1);
|
|
#endif
|
|
memcpy(dst_addr, sit_i->sit_bitmap, sit_i->bitmap_size);
|
|
}
|
|
|
|
static inline block_t written_block_count(struct f2fs_sb_info *sbi)
|
|
{
|
|
return SIT_I(sbi)->written_valid_blocks;
|
|
}
|
|
|
|
static inline unsigned int free_segments(struct f2fs_sb_info *sbi)
|
|
{
|
|
return FREE_I(sbi)->free_segments;
|
|
}
|
|
|
|
static inline int reserved_segments(struct f2fs_sb_info *sbi)
|
|
{
|
|
return SM_I(sbi)->reserved_segments;
|
|
}
|
|
|
|
static inline unsigned int free_sections(struct f2fs_sb_info *sbi)
|
|
{
|
|
return FREE_I(sbi)->free_sections;
|
|
}
|
|
|
|
static inline unsigned int prefree_segments(struct f2fs_sb_info *sbi)
|
|
{
|
|
return DIRTY_I(sbi)->nr_dirty[PRE];
|
|
}
|
|
|
|
static inline unsigned int dirty_segments(struct f2fs_sb_info *sbi)
|
|
{
|
|
return DIRTY_I(sbi)->nr_dirty[DIRTY_HOT_DATA] +
|
|
DIRTY_I(sbi)->nr_dirty[DIRTY_WARM_DATA] +
|
|
DIRTY_I(sbi)->nr_dirty[DIRTY_COLD_DATA] +
|
|
DIRTY_I(sbi)->nr_dirty[DIRTY_HOT_NODE] +
|
|
DIRTY_I(sbi)->nr_dirty[DIRTY_WARM_NODE] +
|
|
DIRTY_I(sbi)->nr_dirty[DIRTY_COLD_NODE];
|
|
}
|
|
|
|
static inline int overprovision_segments(struct f2fs_sb_info *sbi)
|
|
{
|
|
return SM_I(sbi)->ovp_segments;
|
|
}
|
|
|
|
static inline int reserved_sections(struct f2fs_sb_info *sbi)
|
|
{
|
|
return GET_SEC_FROM_SEG(sbi, (unsigned int)reserved_segments(sbi));
|
|
}
|
|
|
|
static inline bool has_curseg_enough_space(struct f2fs_sb_info *sbi)
|
|
{
|
|
unsigned int node_blocks = get_pages(sbi, F2FS_DIRTY_NODES) +
|
|
get_pages(sbi, F2FS_DIRTY_DENTS);
|
|
unsigned int dent_blocks = get_pages(sbi, F2FS_DIRTY_DENTS);
|
|
unsigned int segno, left_blocks;
|
|
int i;
|
|
|
|
/* check current node segment */
|
|
for (i = CURSEG_HOT_NODE; i <= CURSEG_COLD_NODE; i++) {
|
|
segno = CURSEG_I(sbi, i)->segno;
|
|
left_blocks = sbi->blocks_per_seg -
|
|
get_seg_entry(sbi, segno)->ckpt_valid_blocks;
|
|
|
|
if (node_blocks > left_blocks)
|
|
return false;
|
|
}
|
|
|
|
/* check current data segment */
|
|
segno = CURSEG_I(sbi, CURSEG_HOT_DATA)->segno;
|
|
left_blocks = sbi->blocks_per_seg -
|
|
get_seg_entry(sbi, segno)->ckpt_valid_blocks;
|
|
if (dent_blocks > left_blocks)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi,
|
|
int freed, int needed)
|
|
{
|
|
int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES);
|
|
int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS);
|
|
int imeta_secs = get_blocktype_secs(sbi, F2FS_DIRTY_IMETA);
|
|
|
|
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
|
|
return false;
|
|
|
|
if (free_sections(sbi) + freed == reserved_sections(sbi) + needed &&
|
|
has_curseg_enough_space(sbi))
|
|
return false;
|
|
return (free_sections(sbi) + freed) <=
|
|
(node_secs + 2 * dent_secs + imeta_secs +
|
|
reserved_sections(sbi) + needed);
|
|
}
|
|
|
|
static inline bool excess_prefree_segs(struct f2fs_sb_info *sbi)
|
|
{
|
|
return prefree_segments(sbi) > SM_I(sbi)->rec_prefree_segments;
|
|
}
|
|
|
|
static inline int utilization(struct f2fs_sb_info *sbi)
|
|
{
|
|
return div_u64((u64)valid_user_blocks(sbi) * 100,
|
|
sbi->user_block_count);
|
|
}
|
|
|
|
/*
|
|
* Sometimes f2fs may be better to drop out-of-place update policy.
|
|
* And, users can control the policy through sysfs entries.
|
|
* There are five policies with triggering conditions as follows.
|
|
* F2FS_IPU_FORCE - all the time,
|
|
* F2FS_IPU_SSR - if SSR mode is activated,
|
|
* F2FS_IPU_UTIL - if FS utilization is over threashold,
|
|
* F2FS_IPU_SSR_UTIL - if SSR mode is activated and FS utilization is over
|
|
* threashold,
|
|
* F2FS_IPU_FSYNC - activated in fsync path only for high performance flash
|
|
* storages. IPU will be triggered only if the # of dirty
|
|
* pages over min_fsync_blocks.
|
|
* F2FS_IPUT_DISABLE - disable IPU. (=default option)
|
|
*/
|
|
#define DEF_MIN_IPU_UTIL 70
|
|
#define DEF_MIN_FSYNC_BLOCKS 8
|
|
#define DEF_MIN_HOT_BLOCKS 16
|
|
|
|
enum {
|
|
F2FS_IPU_FORCE,
|
|
F2FS_IPU_SSR,
|
|
F2FS_IPU_UTIL,
|
|
F2FS_IPU_SSR_UTIL,
|
|
F2FS_IPU_FSYNC,
|
|
F2FS_IPU_ASYNC,
|
|
};
|
|
|
|
static inline unsigned int curseg_segno(struct f2fs_sb_info *sbi,
|
|
int type)
|
|
{
|
|
struct curseg_info *curseg = CURSEG_I(sbi, type);
|
|
return curseg->segno;
|
|
}
|
|
|
|
static inline unsigned char curseg_alloc_type(struct f2fs_sb_info *sbi,
|
|
int type)
|
|
{
|
|
struct curseg_info *curseg = CURSEG_I(sbi, type);
|
|
return curseg->alloc_type;
|
|
}
|
|
|
|
static inline unsigned short curseg_blkoff(struct f2fs_sb_info *sbi, int type)
|
|
{
|
|
struct curseg_info *curseg = CURSEG_I(sbi, type);
|
|
return curseg->next_blkoff;
|
|
}
|
|
|
|
static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno)
|
|
{
|
|
f2fs_bug_on(sbi, segno > TOTAL_SEGS(sbi) - 1);
|
|
}
|
|
|
|
static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr)
|
|
{
|
|
BUG_ON(blk_addr < SEG0_BLKADDR(sbi)
|
|
|| blk_addr >= MAX_BLKADDR(sbi));
|
|
}
|
|
|
|
/*
|
|
* Summary block is always treated as an invalid block
|
|
*/
|
|
static inline int check_block_count(struct f2fs_sb_info *sbi,
|
|
int segno, struct f2fs_sit_entry *raw_sit)
|
|
{
|
|
#ifdef CONFIG_F2FS_CHECK_FS
|
|
bool is_valid = test_bit_le(0, raw_sit->valid_map) ? true : false;
|
|
int valid_blocks = 0;
|
|
int cur_pos = 0, next_pos;
|
|
|
|
/* check bitmap with valid block count */
|
|
do {
|
|
if (is_valid) {
|
|
next_pos = find_next_zero_bit_le(&raw_sit->valid_map,
|
|
sbi->blocks_per_seg,
|
|
cur_pos);
|
|
valid_blocks += next_pos - cur_pos;
|
|
} else
|
|
next_pos = find_next_bit_le(&raw_sit->valid_map,
|
|
sbi->blocks_per_seg,
|
|
cur_pos);
|
|
cur_pos = next_pos;
|
|
is_valid = !is_valid;
|
|
} while (cur_pos < sbi->blocks_per_seg);
|
|
|
|
if (unlikely(GET_SIT_VBLOCKS(raw_sit) != valid_blocks)) {
|
|
f2fs_msg(sbi->sb, KERN_ERR,
|
|
"Mismatch valid blocks %d vs. %d",
|
|
GET_SIT_VBLOCKS(raw_sit), valid_blocks);
|
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
/* check segment usage, and check boundary of a given segment number */
|
|
if (unlikely(GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg
|
|
|| segno > TOTAL_SEGS(sbi) - 1)) {
|
|
f2fs_msg(sbi->sb, KERN_ERR,
|
|
"Wrong valid blocks %d or segno %u",
|
|
GET_SIT_VBLOCKS(raw_sit), segno);
|
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi,
|
|
unsigned int start)
|
|
{
|
|
struct sit_info *sit_i = SIT_I(sbi);
|
|
unsigned int offset = SIT_BLOCK_OFFSET(start);
|
|
block_t blk_addr = sit_i->sit_base_addr + offset;
|
|
|
|
check_seg_range(sbi, start);
|
|
|
|
#ifdef CONFIG_F2FS_CHECK_FS
|
|
if (f2fs_test_bit(offset, sit_i->sit_bitmap) !=
|
|
f2fs_test_bit(offset, sit_i->sit_bitmap_mir))
|
|
f2fs_bug_on(sbi, 1);
|
|
#endif
|
|
|
|
/* calculate sit block address */
|
|
if (f2fs_test_bit(offset, sit_i->sit_bitmap))
|
|
blk_addr += sit_i->sit_blocks;
|
|
|
|
return blk_addr;
|
|
}
|
|
|
|
static inline pgoff_t next_sit_addr(struct f2fs_sb_info *sbi,
|
|
pgoff_t block_addr)
|
|
{
|
|
struct sit_info *sit_i = SIT_I(sbi);
|
|
block_addr -= sit_i->sit_base_addr;
|
|
if (block_addr < sit_i->sit_blocks)
|
|
block_addr += sit_i->sit_blocks;
|
|
else
|
|
block_addr -= sit_i->sit_blocks;
|
|
|
|
return block_addr + sit_i->sit_base_addr;
|
|
}
|
|
|
|
static inline void set_to_next_sit(struct sit_info *sit_i, unsigned int start)
|
|
{
|
|
unsigned int block_off = SIT_BLOCK_OFFSET(start);
|
|
|
|
f2fs_change_bit(block_off, sit_i->sit_bitmap);
|
|
#ifdef CONFIG_F2FS_CHECK_FS
|
|
f2fs_change_bit(block_off, sit_i->sit_bitmap_mir);
|
|
#endif
|
|
}
|
|
|
|
static inline unsigned long long get_mtime(struct f2fs_sb_info *sbi)
|
|
{
|
|
struct sit_info *sit_i = SIT_I(sbi);
|
|
return sit_i->elapsed_time + CURRENT_TIME_SEC.tv_sec -
|
|
sit_i->mounted_time;
|
|
}
|
|
|
|
static inline void set_summary(struct f2fs_summary *sum, nid_t nid,
|
|
unsigned int ofs_in_node, unsigned char version)
|
|
{
|
|
sum->nid = cpu_to_le32(nid);
|
|
sum->ofs_in_node = cpu_to_le16(ofs_in_node);
|
|
sum->version = version;
|
|
}
|
|
|
|
static inline block_t start_sum_block(struct f2fs_sb_info *sbi)
|
|
{
|
|
return __start_cp_addr(sbi) +
|
|
le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum);
|
|
}
|
|
|
|
static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type)
|
|
{
|
|
return __start_cp_addr(sbi) +
|
|
le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_total_block_count)
|
|
- (base + 1) + type;
|
|
}
|
|
|
|
static inline bool no_fggc_candidate(struct f2fs_sb_info *sbi,
|
|
unsigned int secno)
|
|
{
|
|
if (get_valid_blocks(sbi, GET_SEG_FROM_SEC(sbi, secno), true) >
|
|
sbi->fggc_threshold)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static inline bool sec_usage_check(struct f2fs_sb_info *sbi, unsigned int secno)
|
|
{
|
|
if (IS_CURSEC(sbi, secno) || (sbi->cur_victim_sec == secno))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* It is very important to gather dirty pages and write at once, so that we can
|
|
* submit a big bio without interfering other data writes.
|
|
* By default, 512 pages for directory data,
|
|
* 512 pages (2MB) * 8 for nodes, and
|
|
* 256 pages * 8 for meta are set.
|
|
*/
|
|
static inline int nr_pages_to_skip(struct f2fs_sb_info *sbi, int type)
|
|
{
|
|
if (sbi->sb->s_bdi->wb.dirty_exceeded)
|
|
return 0;
|
|
|
|
if (type == DATA)
|
|
return sbi->blocks_per_seg;
|
|
else if (type == NODE)
|
|
return 8 * sbi->blocks_per_seg;
|
|
else if (type == META)
|
|
return 8 * BIO_MAX_PAGES;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* When writing pages, it'd better align nr_to_write for segment size.
|
|
*/
|
|
static inline long nr_pages_to_write(struct f2fs_sb_info *sbi, int type,
|
|
struct writeback_control *wbc)
|
|
{
|
|
long nr_to_write, desired;
|
|
|
|
if (wbc->sync_mode != WB_SYNC_NONE)
|
|
return 0;
|
|
|
|
nr_to_write = wbc->nr_to_write;
|
|
desired = BIO_MAX_PAGES;
|
|
if (type == NODE)
|
|
desired <<= 1;
|
|
|
|
wbc->nr_to_write = desired;
|
|
return desired - nr_to_write;
|
|
}
|
|
|
|
static inline void wake_up_discard_thread(struct f2fs_sb_info *sbi, bool force)
|
|
{
|
|
struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
|
|
bool wakeup = false;
|
|
int i;
|
|
|
|
if (force)
|
|
goto wake_up;
|
|
|
|
mutex_lock(&dcc->cmd_lock);
|
|
for (i = MAX_PLIST_NUM - 1; i >= 0; i--) {
|
|
if (i + 1 < dcc->discard_granularity)
|
|
break;
|
|
if (!list_empty(&dcc->pend_list[i])) {
|
|
wakeup = true;
|
|
break;
|
|
}
|
|
}
|
|
mutex_unlock(&dcc->cmd_lock);
|
|
if (!wakeup)
|
|
return;
|
|
wake_up:
|
|
dcc->discard_wake = 1;
|
|
wake_up_interruptible_all(&dcc->discard_wait_queue);
|
|
}
|