Merge remote-tracking branch 'aosp/upstream-f2fs-stable-linux-5.10.y' into android12-5.10
* aosp/upstream-f2fs-stable-linux-5.10.y:
f2fs: return EINVAL for hole cases in swap file
f2fs: avoid swapon failure by giving a warning first
f2fs: compress: fix to assign cc.cluster_idx correctly
f2fs: compress: fix race condition of overwrite vs truncate
f2fs: compress: fix to free compress page correctly
f2fs: support iflag change given the mask
f2fs: avoid null pointer access when handling IPU error
fsverity: relax build time dependency on CRYPTO_SHA256
fscrypt: relax Kconfig dependencies for crypto API algorithms
f2fs: drop inplace IO if fs status is abnormal
f2fs: compress: remove unneed check condition
f2fs: clean up left deprecated IO trace codes
f2fs: avoid using native allocate_segment_by_default()
f2fs: remove unnecessary struct declaration
f2fs: fix to avoid NULL pointer dereference
f2fs: avoid duplicated codes for cleanup
f2fs: document: add description about compressed space handling
f2fs: clean up build warnings
f2fs: modify open brace '{' following function definitions
f2fs: fix the periodic wakeups of discard thread
f2fs: fix to avoid accessing invalid fio in f2fs_allocate_data_block()
f2fs: fix to avoid GC/mmap race with f2fs_truncate()
f2fs: set checkpoint_merge by default
f2fs: Fix a hungtask problem in atomic write
f2fs: fix to restrict mount condition on readonly block device
f2fs: introduce gc_merge mount option
f2fs: fix to cover __allocate_new_section() with curseg_lock
f2fs: fix wrong alloc_type in f2fs_do_replace_block
f2fs: delete empty compress.h
f2fs: fix a typo in inode.c
f2fs: allow to change discard policy based on cached discard cmds
f2fs: fix to avoid touching checkpointed data in get_victim()
f2fs: fix to update last i_size if fallocate partially succeeds
f2fs: fix error path of f2fs_remount()
f2fs: fix wrong comment of nat_tree_lock
f2fs: fix to avoid out-of-bounds memory access
f2fs: don't start checkpoint thread in readonly mountpoint
f2fs: do not use AT_SSR mode in FG_GC & high urgent BG_GC
f2fs: add sysfs nodes to get runtime compression stat
f2fs: fix to use per-inode maxbytes in f2fs_fiemap
f2fs: fix to align to section for fallocate() on pinned file
f2fs: expose # of overprivision segments
f2fs: fix error handling in f2fs_end_enable_verity()
f2fs: fix a redundant call to f2fs_balance_fs if an error occurs
f2fs: remove unused file_clear_encrypt()
f2fs: check if swapfile is section-alligned
f2fs: fix last_lblock check in check_swap_activate_fast
f2fs: remove unnecessary IS_SWAPFILE check
f2fs: Replace one-element array with flexible-array member
f2fs: compress: Allow modular (de)compression algorithms
f2fs: check discard command number before traversing discard pending list
f2fs: update comments for explicit memory barrier
f2fs: remove unused FORCE_FG_GC macro
f2fs: avoid unused f2fs_show_compress_options()
f2fs: fix panic during f2fs_resize_fs()
f2fs: fix to allow migrating fully valid segment
f2fs: fix a spelling error
f2fs: fix a spacing coding style
Bug: 181986413
Signed-off-by: Jaegeuk Kim <jaegeuk@google.com>
Change-Id: I1266ec84da529fc8a41e69699d4ad185ac74e6c3
This commit is contained in:
commit
5c5381fe0a
26 changed files with 566 additions and 236 deletions
|
|
@ -276,7 +276,7 @@ Date April 2019
|
|||
Contact: "Daniel Rosenberg" <drosen@google.com>
|
||||
Description: If checkpoint=disable, it displays the number of blocks that
|
||||
are unusable.
|
||||
If checkpoint=enable it displays the enumber of blocks that
|
||||
If checkpoint=enable it displays the number of blocks that
|
||||
would be unusable if checkpoint=disable were to be set.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/encoding
|
||||
|
|
@ -410,6 +410,11 @@ Description: Give a way to change checkpoint merge daemon's io priority.
|
|||
and set the I/O priority within valid range of it. "," delimiter
|
||||
is necessary in between I/O class and priority number.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/ovp_segments
|
||||
Date: March 2021
|
||||
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
|
||||
Description: Shows the number of overprovision segments.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/compr_written_block
|
||||
Date: March 2021
|
||||
Contact: "Daeho Jeong" <daehojeong@google.com>
|
||||
|
|
|
|||
|
|
@ -110,6 +110,12 @@ background_gc=%s Turn on/off cleaning operations, namely garbage
|
|||
on synchronous garbage collection running in background.
|
||||
Default value for this option is on. So garbage
|
||||
collection is on by default.
|
||||
gc_merge When background_gc is on, this option can be enabled to
|
||||
let background GC thread to handle foreground GC requests,
|
||||
it can eliminate the sluggish issue caused by slow foreground
|
||||
GC operation when GC is triggered from a process with limited
|
||||
I/O and CPU resources.
|
||||
nogc_merge Disable GC merge feature.
|
||||
disable_roll_forward Disable the roll-forward recovery routine
|
||||
norecovery Disable the roll-forward recovery routine, mounted read-
|
||||
only (i.e., -o ro,disable_roll_forward)
|
||||
|
|
@ -813,6 +819,14 @@ Compression implementation
|
|||
* chattr +c file
|
||||
* chattr +c dir; touch dir/file
|
||||
* mount w/ -o compress_extension=ext; touch file.ext
|
||||
* mount w/ -o compress_extension=*; touch any_file
|
||||
|
||||
- At this point, compression feature doesn't expose compressed space to user
|
||||
directly in order to guarantee potential data updates later to the space.
|
||||
Instead, the main goal is to reduce data writes to flash disk as much as
|
||||
possible, resulting in extending disk life time as well as relaxing IO
|
||||
congestion. Alternatively, we've added ioctl interface to reclaim compressed
|
||||
space and show it to user after putting the immutable bit.
|
||||
|
||||
Compress metadata layout::
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,13 @@ config F2FS_FS
|
|||
select CRYPTO_CRC32
|
||||
select F2FS_FS_XATTR if FS_ENCRYPTION
|
||||
select FS_ENCRYPTION_ALGS if FS_ENCRYPTION
|
||||
select LZ4_COMPRESS if F2FS_FS_LZ4
|
||||
select LZ4_DECOMPRESS if F2FS_FS_LZ4
|
||||
select LZ4HC_COMPRESS if F2FS_FS_LZ4HC
|
||||
select LZO_COMPRESS if F2FS_FS_LZO
|
||||
select LZO_DECOMPRESS if F2FS_FS_LZO
|
||||
select ZSTD_COMPRESS if F2FS_FS_ZSTD
|
||||
select ZSTD_DECOMPRESS if F2FS_FS_ZSTD
|
||||
help
|
||||
F2FS is based on Log-structured File System (LFS), which supports
|
||||
versatile "flash-friendly" features. The design has been focused on
|
||||
|
|
@ -94,8 +101,6 @@ config F2FS_FS_COMPRESSION
|
|||
config F2FS_FS_LZO
|
||||
bool "LZO compression support"
|
||||
depends on F2FS_FS_COMPRESSION
|
||||
select LZO_COMPRESS
|
||||
select LZO_DECOMPRESS
|
||||
default y
|
||||
help
|
||||
Support LZO compress algorithm, if unsure, say Y.
|
||||
|
|
@ -103,8 +108,6 @@ config F2FS_FS_LZO
|
|||
config F2FS_FS_LZ4
|
||||
bool "LZ4 compression support"
|
||||
depends on F2FS_FS_COMPRESSION
|
||||
select LZ4_COMPRESS
|
||||
select LZ4_DECOMPRESS
|
||||
default y
|
||||
help
|
||||
Support LZ4 compress algorithm, if unsure, say Y.
|
||||
|
|
@ -113,7 +116,6 @@ config F2FS_FS_LZ4HC
|
|||
bool "LZ4HC compression support"
|
||||
depends on F2FS_FS_COMPRESSION
|
||||
depends on F2FS_FS_LZ4
|
||||
select LZ4HC_COMPRESS
|
||||
default y
|
||||
help
|
||||
Support LZ4HC compress algorithm, LZ4HC has compatible on-disk
|
||||
|
|
@ -122,8 +124,6 @@ config F2FS_FS_LZ4HC
|
|||
config F2FS_FS_ZSTD
|
||||
bool "ZSTD compression support"
|
||||
depends on F2FS_FS_COMPRESSION
|
||||
select ZSTD_COMPRESS
|
||||
select ZSTD_DECOMPRESS
|
||||
default y
|
||||
help
|
||||
Support ZSTD compress algorithm, if unsure, say Y.
|
||||
|
|
@ -132,8 +132,6 @@ config F2FS_FS_LZORLE
|
|||
bool "LZO-RLE compression support"
|
||||
depends on F2FS_FS_COMPRESSION
|
||||
depends on F2FS_FS_LZO
|
||||
select LZO_COMPRESS
|
||||
select LZO_DECOMPRESS
|
||||
default y
|
||||
help
|
||||
Support LZO-RLE compress algorithm, if unsure, say Y.
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ static inline size_t f2fs_acl_size(int count)
|
|||
static inline int f2fs_acl_count(size_t size)
|
||||
{
|
||||
ssize_t s;
|
||||
|
||||
size -= sizeof(struct f2fs_acl_header);
|
||||
s = size - 4 * sizeof(struct f2fs_acl_entry_short);
|
||||
if (s < 0) {
|
||||
|
|
|
|||
|
|
@ -719,6 +719,7 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
|
|||
orphan_blk = (struct f2fs_orphan_block *)page_address(page);
|
||||
for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) {
|
||||
nid_t ino = le32_to_cpu(orphan_blk->ino[j]);
|
||||
|
||||
err = recover_orphan_inode(sbi, ino);
|
||||
if (err) {
|
||||
f2fs_put_page(page, 1);
|
||||
|
|
@ -1456,7 +1457,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
orphan_blocks);
|
||||
|
||||
if (__remain_node_summaries(cpc->reason))
|
||||
ckpt->cp_pack_total_block_count = cpu_to_le32(F2FS_CP_PACKS+
|
||||
ckpt->cp_pack_total_block_count = cpu_to_le32(F2FS_CP_PACKS +
|
||||
cp_payload_blks + data_sum_blocks +
|
||||
orphan_blocks + NR_CURSEG_NODE_TYPE);
|
||||
else
|
||||
|
|
@ -1818,7 +1819,11 @@ int f2fs_issue_checkpoint(struct f2fs_sb_info *sbi)
|
|||
llist_add(&req.llnode, &cprc->issue_list);
|
||||
atomic_inc(&cprc->queued_ckpt);
|
||||
|
||||
/* update issue_list before we wake up issue_checkpoint thread */
|
||||
/*
|
||||
* update issue_list before we wake up issue_checkpoint thread,
|
||||
* this smp_mb() pairs with another barrier in ___wait_event(),
|
||||
* see more details in comments of waitqueue_active().
|
||||
*/
|
||||
smp_mb();
|
||||
|
||||
if (waitqueue_active(&cprc->ckpt_wait_queue))
|
||||
|
|
|
|||
|
|
@ -76,12 +76,6 @@ bool f2fs_is_compressed_page(struct page *page)
|
|||
return false;
|
||||
if (IS_ATOMIC_WRITTEN_PAGE(page) || IS_DUMMY_WRITTEN_PAGE(page))
|
||||
return false;
|
||||
/*
|
||||
* page->private may be set with pid.
|
||||
* pid_max is enough to check if it is traced.
|
||||
*/
|
||||
if (IS_IO_TRACED_PAGE(page))
|
||||
return false;
|
||||
|
||||
f2fs_bug_on(F2FS_M_SB(page->mapping),
|
||||
*((u32 *)page_private(page)) != F2FS_COMPRESSED_PAGE_MAGIC);
|
||||
|
|
@ -123,19 +117,6 @@ static void f2fs_unlock_rpages(struct compress_ctx *cc, int len)
|
|||
f2fs_drop_rpages(cc, len, true);
|
||||
}
|
||||
|
||||
static void f2fs_put_rpages_mapping(struct address_space *mapping,
|
||||
pgoff_t start, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
struct page *page = find_get_page(mapping, start + i);
|
||||
|
||||
put_page(page);
|
||||
put_page(page);
|
||||
}
|
||||
}
|
||||
|
||||
static void f2fs_put_rpages_wbc(struct compress_ctx *cc,
|
||||
struct writeback_control *wbc, bool redirty, int unlock)
|
||||
{
|
||||
|
|
@ -164,13 +145,14 @@ int f2fs_init_compress_ctx(struct compress_ctx *cc)
|
|||
return cc->rpages ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
void f2fs_destroy_compress_ctx(struct compress_ctx *cc)
|
||||
void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse)
|
||||
{
|
||||
page_array_free(cc->inode, cc->rpages, cc->cluster_size);
|
||||
cc->rpages = NULL;
|
||||
cc->nr_rpages = 0;
|
||||
cc->nr_cpages = 0;
|
||||
cc->cluster_idx = NULL_CLUSTER;
|
||||
if (!reuse)
|
||||
cc->cluster_idx = NULL_CLUSTER;
|
||||
}
|
||||
|
||||
void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page)
|
||||
|
|
@ -896,7 +878,6 @@ bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index)
|
|||
|
||||
static bool __cluster_may_compress(struct compress_ctx *cc)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
|
||||
loff_t i_size = i_size_read(cc->inode);
|
||||
unsigned nr_pages = DIV_ROUND_UP(i_size, PAGE_SIZE);
|
||||
int i;
|
||||
|
|
@ -904,12 +885,7 @@ static bool __cluster_may_compress(struct compress_ctx *cc)
|
|||
for (i = 0; i < cc->cluster_size; i++) {
|
||||
struct page *page = cc->rpages[i];
|
||||
|
||||
f2fs_bug_on(sbi, !page);
|
||||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return false;
|
||||
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
|
||||
return false;
|
||||
f2fs_bug_on(F2FS_I_SB(cc->inode), !page);
|
||||
|
||||
/* beyond EOF */
|
||||
if (page->index >= nr_pages)
|
||||
|
|
@ -1048,7 +1024,7 @@ retry:
|
|||
}
|
||||
|
||||
if (PageUptodate(page))
|
||||
unlock_page(page);
|
||||
f2fs_put_page(page, 1);
|
||||
else
|
||||
f2fs_compress_ctx_add_page(cc, page);
|
||||
}
|
||||
|
|
@ -1058,33 +1034,35 @@ retry:
|
|||
|
||||
ret = f2fs_read_multi_pages(cc, &bio, cc->cluster_size,
|
||||
&last_block_in_bio, false, true);
|
||||
f2fs_destroy_compress_ctx(cc);
|
||||
f2fs_put_rpages(cc);
|
||||
f2fs_destroy_compress_ctx(cc, true);
|
||||
if (ret)
|
||||
goto release_pages;
|
||||
goto out;
|
||||
if (bio)
|
||||
f2fs_submit_bio(sbi, bio, DATA);
|
||||
|
||||
ret = f2fs_init_compress_ctx(cc);
|
||||
if (ret)
|
||||
goto release_pages;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < cc->cluster_size; i++) {
|
||||
f2fs_bug_on(sbi, cc->rpages[i]);
|
||||
|
||||
page = find_lock_page(mapping, start_idx + i);
|
||||
f2fs_bug_on(sbi, !page);
|
||||
if (!page) {
|
||||
/* page can be truncated */
|
||||
goto release_and_retry;
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(page, DATA, true, true);
|
||||
|
||||
f2fs_compress_ctx_add_page(cc, page);
|
||||
f2fs_put_page(page, 0);
|
||||
|
||||
if (!PageUptodate(page)) {
|
||||
release_and_retry:
|
||||
f2fs_put_rpages(cc);
|
||||
f2fs_unlock_rpages(cc, i + 1);
|
||||
f2fs_put_rpages_mapping(mapping, start_idx,
|
||||
cc->cluster_size);
|
||||
f2fs_destroy_compress_ctx(cc);
|
||||
f2fs_destroy_compress_ctx(cc, true);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
|
@ -1115,10 +1093,10 @@ retry:
|
|||
}
|
||||
|
||||
unlock_pages:
|
||||
f2fs_put_rpages(cc);
|
||||
f2fs_unlock_rpages(cc, i);
|
||||
release_pages:
|
||||
f2fs_put_rpages_mapping(mapping, start_idx, i);
|
||||
f2fs_destroy_compress_ctx(cc);
|
||||
f2fs_destroy_compress_ctx(cc, true);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1153,7 +1131,7 @@ bool f2fs_compress_write_end(struct inode *inode, void *fsdata,
|
|||
set_cluster_dirty(&cc);
|
||||
|
||||
f2fs_put_rpages_wbc(&cc, NULL, false, 1);
|
||||
f2fs_destroy_compress_ctx(&cc);
|
||||
f2fs_destroy_compress_ctx(&cc, false);
|
||||
|
||||
return first_index;
|
||||
}
|
||||
|
|
@ -1373,7 +1351,7 @@ unlock_continue:
|
|||
f2fs_put_rpages(cc);
|
||||
page_array_free(cc->inode, cc->cpages, cc->nr_cpages);
|
||||
cc->cpages = NULL;
|
||||
f2fs_destroy_compress_ctx(cc);
|
||||
f2fs_destroy_compress_ctx(cc, false);
|
||||
return 0;
|
||||
|
||||
out_destroy_crypt:
|
||||
|
|
@ -1384,7 +1362,8 @@ out_destroy_crypt:
|
|||
for (i = 0; i < cc->nr_cpages; i++) {
|
||||
if (!cc->cpages[i])
|
||||
continue;
|
||||
f2fs_put_page(cc->cpages[i], 1);
|
||||
f2fs_compress_free_page(cc->cpages[i]);
|
||||
cc->cpages[i] = NULL;
|
||||
}
|
||||
out_put_cic:
|
||||
kmem_cache_free(cic_entry_slab, cic);
|
||||
|
|
@ -1534,7 +1513,7 @@ write:
|
|||
err = f2fs_write_raw_pages(cc, submitted, wbc, io_type);
|
||||
f2fs_put_rpages_wbc(cc, wbc, false, 0);
|
||||
destroy_out:
|
||||
f2fs_destroy_compress_ctx(cc);
|
||||
f2fs_destroy_compress_ctx(cc, false);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
|||
146
fs/f2fs/data.c
146
fs/f2fs/data.c
|
|
@ -1102,6 +1102,7 @@ int f2fs_reserve_new_blocks(struct dnode_of_data *dn, blkcnt_t count)
|
|||
|
||||
for (; count > 0; dn->ofs_in_node++) {
|
||||
block_t blkaddr = f2fs_data_blkaddr(dn);
|
||||
|
||||
if (blkaddr == NULL_ADDR) {
|
||||
dn->data_blkaddr = NEW_ADDR;
|
||||
__set_data_blkaddr(dn);
|
||||
|
|
@ -1738,7 +1739,7 @@ static int get_data_block_dio_write(struct inode *inode, sector_t iblock,
|
|||
return __get_data_block(inode, iblock, bh_result, create,
|
||||
F2FS_GET_BLOCK_DIO, NULL,
|
||||
f2fs_rw_hint_to_seg_type(inode->i_write_hint),
|
||||
IS_SWAPFILE(inode) ? false : true);
|
||||
true);
|
||||
}
|
||||
|
||||
static int get_data_block_dio(struct inode *inode, sector_t iblock,
|
||||
|
|
@ -1853,6 +1854,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
|||
int ret = 0;
|
||||
bool compr_cluster = false;
|
||||
unsigned int cluster_size = F2FS_I(inode)->i_cluster_size;
|
||||
loff_t maxbytes;
|
||||
|
||||
if (fieinfo->fi_flags & FIEMAP_FLAG_CACHE) {
|
||||
ret = f2fs_precache_extents(inode);
|
||||
|
|
@ -1866,6 +1868,15 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
|||
|
||||
inode_lock(inode);
|
||||
|
||||
maxbytes = max_file_blocks(inode) << F2FS_BLKSIZE_BITS;
|
||||
if (start > maxbytes) {
|
||||
ret = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len > maxbytes || (maxbytes - len) < start)
|
||||
len = maxbytes - start;
|
||||
|
||||
if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) {
|
||||
ret = f2fs_xattr_fiemap(inode, fieinfo);
|
||||
goto out;
|
||||
|
|
@ -2292,7 +2303,7 @@ static int f2fs_mpage_readpages(struct inode *inode,
|
|||
max_nr_pages,
|
||||
&last_block_in_bio,
|
||||
rac != NULL, false);
|
||||
f2fs_destroy_compress_ctx(&cc);
|
||||
f2fs_destroy_compress_ctx(&cc, false);
|
||||
if (ret)
|
||||
goto set_error_page;
|
||||
}
|
||||
|
|
@ -2337,7 +2348,7 @@ next_page:
|
|||
max_nr_pages,
|
||||
&last_block_in_bio,
|
||||
rac != NULL, false);
|
||||
f2fs_destroy_compress_ctx(&cc);
|
||||
f2fs_destroy_compress_ctx(&cc, false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -3038,7 +3049,7 @@ next:
|
|||
}
|
||||
}
|
||||
if (f2fs_compressed_file(inode))
|
||||
f2fs_destroy_compress_ctx(&cc);
|
||||
f2fs_destroy_compress_ctx(&cc, false);
|
||||
#endif
|
||||
if (retry) {
|
||||
index = 0;
|
||||
|
|
@ -3812,6 +3823,7 @@ int f2fs_migrate_page(struct address_space *mapping,
|
|||
|
||||
if (atomic_written) {
|
||||
struct inmem_pages *cur;
|
||||
|
||||
list_for_each_entry(cur, &fi->inmem_pages, list)
|
||||
if (cur->page == page) {
|
||||
cur->page = newpage;
|
||||
|
|
@ -3837,11 +3849,72 @@ int f2fs_migrate_page(struct address_space *mapping,
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_SWAP
|
||||
static int f2fs_is_file_aligned(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
block_t main_blkaddr = SM_I(sbi)->main_blkaddr;
|
||||
block_t cur_lblock;
|
||||
block_t last_lblock;
|
||||
block_t pblock;
|
||||
unsigned long nr_pblocks;
|
||||
unsigned int blocks_per_sec = BLKS_PER_SEC(sbi);
|
||||
unsigned int not_aligned = 0;
|
||||
int ret = 0;
|
||||
|
||||
cur_lblock = 0;
|
||||
last_lblock = bytes_to_blks(inode, i_size_read(inode));
|
||||
|
||||
while (cur_lblock < last_lblock) {
|
||||
struct f2fs_map_blocks map;
|
||||
|
||||
memset(&map, 0, sizeof(map));
|
||||
map.m_lblk = cur_lblock;
|
||||
map.m_len = last_lblock - cur_lblock;
|
||||
map.m_next_pgofs = NULL;
|
||||
map.m_next_extent = NULL;
|
||||
map.m_seg_type = NO_CHECK_TYPE;
|
||||
map.m_may_create = false;
|
||||
|
||||
ret = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_FIEMAP);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* hole */
|
||||
if (!(map.m_flags & F2FS_MAP_FLAGS)) {
|
||||
f2fs_err(sbi, "Swapfile has holes\n");
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pblock = map.m_pblk;
|
||||
nr_pblocks = map.m_len;
|
||||
|
||||
if ((pblock - main_blkaddr) & (blocks_per_sec - 1) ||
|
||||
nr_pblocks & (blocks_per_sec - 1)) {
|
||||
if (f2fs_is_pinned_file(inode)) {
|
||||
f2fs_err(sbi, "Swapfile does not align to section");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
not_aligned++;
|
||||
}
|
||||
|
||||
cur_lblock += nr_pblocks;
|
||||
}
|
||||
if (not_aligned)
|
||||
f2fs_warn(sbi, "Swapfile (%u) is not align to section: \n"
|
||||
"\t1) creat(), 2) ioctl(F2FS_IOC_SET_PIN_FILE), 3) fallocate()",
|
||||
not_aligned);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_swap_activate_fast(struct swap_info_struct *sis,
|
||||
struct file *swap_file, sector_t *span)
|
||||
{
|
||||
struct address_space *mapping = swap_file->f_mapping;
|
||||
struct inode *inode = mapping->host;
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
sector_t cur_lblock;
|
||||
sector_t last_lblock;
|
||||
sector_t pblock;
|
||||
|
|
@ -3849,8 +3922,9 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
|
|||
sector_t highest_pblock = 0;
|
||||
int nr_extents = 0;
|
||||
unsigned long nr_pblocks;
|
||||
u64 len;
|
||||
int ret;
|
||||
unsigned int blocks_per_sec = BLKS_PER_SEC(sbi);
|
||||
unsigned int not_aligned = 0;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Map all the blocks into the extent list. This code doesn't try
|
||||
|
|
@ -3858,31 +3932,44 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
|
|||
*/
|
||||
cur_lblock = 0;
|
||||
last_lblock = bytes_to_blks(inode, i_size_read(inode));
|
||||
len = i_size_read(inode);
|
||||
|
||||
while (cur_lblock <= last_lblock && cur_lblock < sis->max) {
|
||||
while (cur_lblock < last_lblock && cur_lblock < sis->max) {
|
||||
struct f2fs_map_blocks map;
|
||||
pgoff_t next_pgofs;
|
||||
|
||||
cond_resched();
|
||||
|
||||
memset(&map, 0, sizeof(map));
|
||||
map.m_lblk = cur_lblock;
|
||||
map.m_len = bytes_to_blks(inode, len) - cur_lblock;
|
||||
map.m_next_pgofs = &next_pgofs;
|
||||
map.m_len = last_lblock - cur_lblock;
|
||||
map.m_next_pgofs = NULL;
|
||||
map.m_next_extent = NULL;
|
||||
map.m_seg_type = NO_CHECK_TYPE;
|
||||
map.m_may_create = false;
|
||||
|
||||
ret = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_FIEMAP);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
goto out;
|
||||
|
||||
/* hole */
|
||||
if (!(map.m_flags & F2FS_MAP_FLAGS))
|
||||
goto err_out;
|
||||
if (!(map.m_flags & F2FS_MAP_FLAGS)) {
|
||||
f2fs_err(sbi, "Swapfile has holes\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pblock = map.m_pblk;
|
||||
nr_pblocks = map.m_len;
|
||||
|
||||
if ((pblock - SM_I(sbi)->main_blkaddr) & (blocks_per_sec - 1) ||
|
||||
nr_pblocks & (blocks_per_sec - 1)) {
|
||||
if (f2fs_is_pinned_file(inode)) {
|
||||
f2fs_err(sbi, "Swapfile does not align to section");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
not_aligned++;
|
||||
}
|
||||
|
||||
if (cur_lblock + nr_pblocks >= sis->max)
|
||||
nr_pblocks = sis->max - cur_lblock;
|
||||
|
||||
|
|
@ -3909,11 +3996,13 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
|
|||
sis->max = cur_lblock;
|
||||
sis->pages = cur_lblock - 1;
|
||||
sis->highest_bit = cur_lblock - 1;
|
||||
|
||||
if (not_aligned)
|
||||
f2fs_warn(sbi, "Swapfile (%u) is not align to section: \n"
|
||||
"\t1) creat(), 2) ioctl(F2FS_IOC_SET_PIN_FILE), 3) fallocate()",
|
||||
not_aligned);
|
||||
out:
|
||||
return ret;
|
||||
err_out:
|
||||
pr_err("swapon: swapfile has holes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Copied from generic_swapfile_activate() to check any holes */
|
||||
|
|
@ -3922,6 +4011,7 @@ static int check_swap_activate(struct swap_info_struct *sis,
|
|||
{
|
||||
struct address_space *mapping = swap_file->f_mapping;
|
||||
struct inode *inode = mapping->host;
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
unsigned blocks_per_page;
|
||||
unsigned long page_no;
|
||||
sector_t probe_block;
|
||||
|
|
@ -3929,11 +4019,15 @@ static int check_swap_activate(struct swap_info_struct *sis,
|
|||
sector_t lowest_block = -1;
|
||||
sector_t highest_block = 0;
|
||||
int nr_extents = 0;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (PAGE_SIZE == F2FS_BLKSIZE)
|
||||
return check_swap_activate_fast(sis, swap_file, span);
|
||||
|
||||
ret = f2fs_is_file_aligned(inode);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
blocks_per_page = bytes_to_blks(inode, PAGE_SIZE);
|
||||
|
||||
/*
|
||||
|
|
@ -3948,13 +4042,14 @@ static int check_swap_activate(struct swap_info_struct *sis,
|
|||
unsigned block_in_page;
|
||||
sector_t first_block;
|
||||
sector_t block = 0;
|
||||
int err = 0;
|
||||
|
||||
cond_resched();
|
||||
|
||||
block = probe_block;
|
||||
err = bmap(inode, &block);
|
||||
if (err || !block)
|
||||
ret = bmap(inode, &block);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (!block)
|
||||
goto bad_bmap;
|
||||
first_block = block;
|
||||
|
||||
|
|
@ -3970,9 +4065,10 @@ static int check_swap_activate(struct swap_info_struct *sis,
|
|||
block_in_page++) {
|
||||
|
||||
block = probe_block + block_in_page;
|
||||
err = bmap(inode, &block);
|
||||
|
||||
if (err || !block)
|
||||
ret = bmap(inode, &block);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (!block)
|
||||
goto bad_bmap;
|
||||
|
||||
if (block != first_block + block_in_page) {
|
||||
|
|
@ -4012,7 +4108,7 @@ reprobe:
|
|||
out:
|
||||
return ret;
|
||||
bad_bmap:
|
||||
pr_err("swapon: swapfile has holes\n");
|
||||
f2fs_err(sbi, "Swapfile has holes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
si->util_invalid = 50 - si->util_free - si->util_valid;
|
||||
for (i = CURSEG_HOT_DATA; i < NO_CHECK_TYPE; i++) {
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, i);
|
||||
|
||||
si->curseg[i] = curseg->segno;
|
||||
si->cursec[i] = GET_SEC_FROM_SEG(sbi, curseg->segno);
|
||||
si->curzone[i] = GET_ZONE_FROM_SEC(sbi, si->cursec[i]);
|
||||
|
|
@ -300,10 +301,12 @@ get_cache:
|
|||
si->page_mem = 0;
|
||||
if (sbi->node_inode) {
|
||||
unsigned npages = NODE_MAPPING(sbi)->nrpages;
|
||||
|
||||
si->page_mem += (unsigned long long)npages << PAGE_SHIFT;
|
||||
}
|
||||
if (sbi->meta_inode) {
|
||||
unsigned npages = META_MAPPING(sbi)->nrpages;
|
||||
|
||||
si->page_mem += (unsigned long long)npages << PAGE_SHIFT;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -473,6 +473,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
|
|||
struct page *page, struct inode *inode)
|
||||
{
|
||||
enum page_type type = f2fs_has_inline_dentry(dir) ? NODE : DATA;
|
||||
|
||||
lock_page(page);
|
||||
f2fs_wait_on_page_writeback(page, type, true, true);
|
||||
de->ino = cpu_to_le32(inode->i_ino);
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ extern const char *f2fs_fault_name[FAULT_MAX];
|
|||
#define F2FS_MOUNT_NORECOVERY 0x04000000
|
||||
#define F2FS_MOUNT_ATGC 0x08000000
|
||||
#define F2FS_MOUNT_MERGE_CHECKPOINT 0x10000000
|
||||
#define F2FS_MOUNT_GC_MERGE 0x20000000
|
||||
|
||||
#define F2FS_OPTION(sbi) ((sbi)->mount_opt)
|
||||
#define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
|
||||
|
|
@ -637,21 +638,26 @@ enum {
|
|||
#define FADVISE_MODIFIABLE_BITS (FADVISE_COLD_BIT | FADVISE_HOT_BIT)
|
||||
|
||||
#define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
#define file_set_cold(inode) set_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_lost_pino(inode) set_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
#define file_clear_cold(inode) clear_file(inode, FADVISE_COLD_BIT)
|
||||
|
||||
#define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
#define file_lost_pino(inode) set_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
#define file_got_pino(inode) clear_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
|
||||
#define file_is_encrypt(inode) is_file(inode, FADVISE_ENCRYPT_BIT)
|
||||
#define file_set_encrypt(inode) set_file(inode, FADVISE_ENCRYPT_BIT)
|
||||
#define file_clear_encrypt(inode) clear_file(inode, FADVISE_ENCRYPT_BIT)
|
||||
|
||||
#define file_enc_name(inode) is_file(inode, FADVISE_ENC_NAME_BIT)
|
||||
#define file_set_enc_name(inode) set_file(inode, FADVISE_ENC_NAME_BIT)
|
||||
|
||||
#define file_keep_isize(inode) is_file(inode, FADVISE_KEEP_SIZE_BIT)
|
||||
#define file_set_keep_isize(inode) set_file(inode, FADVISE_KEEP_SIZE_BIT)
|
||||
|
||||
#define file_is_hot(inode) is_file(inode, FADVISE_HOT_BIT)
|
||||
#define file_set_hot(inode) set_file(inode, FADVISE_HOT_BIT)
|
||||
#define file_clear_hot(inode) clear_file(inode, FADVISE_HOT_BIT)
|
||||
|
||||
#define file_is_verity(inode) is_file(inode, FADVISE_VERITY_BIT)
|
||||
#define file_set_verity(inode) set_file(inode, FADVISE_VERITY_BIT)
|
||||
|
||||
|
|
@ -860,7 +866,7 @@ struct f2fs_nm_info {
|
|||
/* NAT cache management */
|
||||
struct radix_tree_root nat_root;/* root of the nat entry cache */
|
||||
struct radix_tree_root nat_set_root;/* root of the nat set cache */
|
||||
struct rw_semaphore nat_tree_lock; /* protect nat_tree_lock */
|
||||
struct rw_semaphore nat_tree_lock; /* protect nat entry tree */
|
||||
struct list_head nat_entries; /* cached nat entry list (clean) */
|
||||
spinlock_t nat_list_lock; /* protect clean nat entry list */
|
||||
unsigned int nat_cnt[MAX_NAT_STATE]; /* the # of cached nat entries */
|
||||
|
|
@ -1297,14 +1303,6 @@ enum {
|
|||
#define IS_DUMMY_WRITTEN_PAGE(page) \
|
||||
(page_private(page) == DUMMY_WRITTEN_PAGE)
|
||||
|
||||
#ifdef CONFIG_F2FS_IO_TRACE
|
||||
#define IS_IO_TRACED_PAGE(page) \
|
||||
(page_private(page) > 0 && \
|
||||
page_private(page) < (unsigned long)PID_MAX_LIMIT)
|
||||
#else
|
||||
#define IS_IO_TRACED_PAGE(page) (0)
|
||||
#endif
|
||||
|
||||
/* For compression */
|
||||
enum compress_algorithm_type {
|
||||
COMPRESS_LZO,
|
||||
|
|
@ -2220,6 +2218,7 @@ static inline block_t __cp_payload(struct f2fs_sb_info *sbi)
|
|||
static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag)
|
||||
{
|
||||
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
||||
void *tmp_ptr = &ckpt->sit_nat_version_bitmap;
|
||||
int offset;
|
||||
|
||||
if (is_set_ckpt_flags(sbi, CP_LARGE_NAT_BITMAP_FLAG)) {
|
||||
|
|
@ -2229,7 +2228,7 @@ static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag)
|
|||
* if large_nat_bitmap feature is enabled, leave checksum
|
||||
* protection for all nat/sit bitmaps.
|
||||
*/
|
||||
return &ckpt->sit_nat_version_bitmap + offset + sizeof(__le32);
|
||||
return tmp_ptr + offset + sizeof(__le32);
|
||||
}
|
||||
|
||||
if (__cp_payload(sbi) > 0) {
|
||||
|
|
@ -2240,7 +2239,7 @@ static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag)
|
|||
} else {
|
||||
offset = (flag == NAT_BITMAP) ?
|
||||
le32_to_cpu(ckpt->sit_ver_bitmap_bytesize) : 0;
|
||||
return &ckpt->sit_nat_version_bitmap + offset;
|
||||
return tmp_ptr + offset;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3303,7 +3302,6 @@ void f2fs_hash_filename(const struct inode *dir, struct f2fs_filename *fname);
|
|||
/*
|
||||
* node.c
|
||||
*/
|
||||
struct dnode_of_data;
|
||||
struct node_info;
|
||||
|
||||
int f2fs_check_nid_range(struct f2fs_sb_info *sbi, nid_t nid);
|
||||
|
|
@ -3380,6 +3378,7 @@ block_t f2fs_get_unusable_blocks(struct f2fs_sb_info *sbi);
|
|||
int f2fs_disable_cp_again(struct f2fs_sb_info *sbi, block_t unusable);
|
||||
void f2fs_release_discard_addrs(struct f2fs_sb_info *sbi);
|
||||
int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra);
|
||||
bool f2fs_segment_has_free_slot(struct f2fs_sb_info *sbi, int segno);
|
||||
void f2fs_init_inmem_curseg(struct f2fs_sb_info *sbi);
|
||||
void f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi);
|
||||
void f2fs_restore_inmem_curseg(struct f2fs_sb_info *sbi);
|
||||
|
|
@ -3387,7 +3386,7 @@ void f2fs_get_new_segment(struct f2fs_sb_info *sbi,
|
|||
unsigned int *newseg, bool new_sec, int dir);
|
||||
void f2fs_allocate_segment_for_resize(struct f2fs_sb_info *sbi, int type,
|
||||
unsigned int start, unsigned int end);
|
||||
void f2fs_allocate_new_segment(struct f2fs_sb_info *sbi, int type);
|
||||
void f2fs_allocate_new_section(struct f2fs_sb_info *sbi, int type, bool force);
|
||||
void f2fs_allocate_new_segments(struct f2fs_sb_info *sbi);
|
||||
int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range);
|
||||
bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi,
|
||||
|
|
@ -3551,7 +3550,7 @@ void f2fs_destroy_post_read_wq(struct f2fs_sb_info *sbi);
|
|||
int f2fs_start_gc_thread(struct f2fs_sb_info *sbi);
|
||||
void f2fs_stop_gc_thread(struct f2fs_sb_info *sbi);
|
||||
block_t f2fs_start_bidx_of_node(unsigned int node_ofs, struct inode *inode);
|
||||
int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, bool background,
|
||||
int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, bool background, bool force,
|
||||
unsigned int segno);
|
||||
void f2fs_build_gc_manager(struct f2fs_sb_info *sbi);
|
||||
int f2fs_resize_fs(struct f2fs_sb_info *sbi, __u64 block_count);
|
||||
|
|
@ -3953,7 +3952,7 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc);
|
|||
void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed);
|
||||
void f2fs_put_page_dic(struct page *page);
|
||||
int f2fs_init_compress_ctx(struct compress_ctx *cc);
|
||||
void f2fs_destroy_compress_ctx(struct compress_ctx *cc);
|
||||
void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse);
|
||||
void f2fs_init_compress_info(struct f2fs_sb_info *sbi);
|
||||
int f2fs_init_page_array_cache(struct f2fs_sb_info *sbi);
|
||||
void f2fs_destroy_page_array_cache(struct f2fs_sb_info *sbi);
|
||||
|
|
@ -4198,8 +4197,7 @@ static inline bool f2fs_force_buffered_io(struct inode *inode,
|
|||
if (F2FS_IO_ALIGNED(sbi))
|
||||
return true;
|
||||
}
|
||||
if (is_sbi_flag_set(F2FS_I_SB(inode), SBI_CP_DISABLED) &&
|
||||
!IS_SWAPFILE(inode))
|
||||
if (is_sbi_flag_set(F2FS_I_SB(inode), SBI_CP_DISABLED))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1620,9 +1620,10 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
|
|||
struct f2fs_map_blocks map = { .m_next_pgofs = NULL,
|
||||
.m_next_extent = NULL, .m_seg_type = NO_CHECK_TYPE,
|
||||
.m_may_create = true };
|
||||
pgoff_t pg_end;
|
||||
pgoff_t pg_start, pg_end;
|
||||
loff_t new_size = i_size_read(inode);
|
||||
loff_t off_end;
|
||||
block_t expanded = 0;
|
||||
int err;
|
||||
|
||||
err = inode_newsize_ok(inode, (len + offset));
|
||||
|
|
@ -1635,11 +1636,12 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
|
|||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
pg_start = ((unsigned long long)offset) >> PAGE_SHIFT;
|
||||
pg_end = ((unsigned long long)offset + len) >> PAGE_SHIFT;
|
||||
off_end = (offset + len) & (PAGE_SIZE - 1);
|
||||
|
||||
map.m_lblk = ((unsigned long long)offset) >> PAGE_SHIFT;
|
||||
map.m_len = pg_end - map.m_lblk;
|
||||
map.m_lblk = pg_start;
|
||||
map.m_len = pg_end - pg_start;
|
||||
if (off_end)
|
||||
map.m_len++;
|
||||
|
||||
|
|
@ -1647,19 +1649,15 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
|
|||
return 0;
|
||||
|
||||
if (f2fs_is_pinned_file(inode)) {
|
||||
block_t len = (map.m_len >> sbi->log_blocks_per_seg) <<
|
||||
sbi->log_blocks_per_seg;
|
||||
block_t done = 0;
|
||||
block_t sec_blks = BLKS_PER_SEC(sbi);
|
||||
block_t sec_len = roundup(map.m_len, sec_blks);
|
||||
|
||||
if (map.m_len % sbi->blocks_per_seg)
|
||||
len += sbi->blocks_per_seg;
|
||||
|
||||
map.m_len = sbi->blocks_per_seg;
|
||||
map.m_len = sec_blks;
|
||||
next_alloc:
|
||||
if (has_not_enough_free_secs(sbi, 0,
|
||||
GET_SEC_FROM_SEG(sbi, overprovision_segments(sbi)))) {
|
||||
down_write(&sbi->gc_lock);
|
||||
err = f2fs_gc(sbi, true, false, NULL_SEGNO);
|
||||
err = f2fs_gc(sbi, true, false, false, NULL_SEGNO);
|
||||
if (err && err != -ENODATA && err != -EAGAIN)
|
||||
goto out_err;
|
||||
}
|
||||
|
|
@ -1667,7 +1665,7 @@ next_alloc:
|
|||
down_write(&sbi->pin_sem);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
f2fs_allocate_new_segment(sbi, CURSEG_COLD_DATA_PINNED);
|
||||
f2fs_allocate_new_section(sbi, CURSEG_COLD_DATA_PINNED, false);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
map.m_seg_type = CURSEG_COLD_DATA_PINNED;
|
||||
|
|
@ -1675,24 +1673,25 @@ next_alloc:
|
|||
|
||||
up_write(&sbi->pin_sem);
|
||||
|
||||
done += map.m_len;
|
||||
len -= map.m_len;
|
||||
expanded += map.m_len;
|
||||
sec_len -= map.m_len;
|
||||
map.m_lblk += map.m_len;
|
||||
if (!err && len)
|
||||
if (!err && sec_len)
|
||||
goto next_alloc;
|
||||
|
||||
map.m_len = done;
|
||||
map.m_len = expanded;
|
||||
} else {
|
||||
err = f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_AIO);
|
||||
expanded = map.m_len;
|
||||
}
|
||||
out_err:
|
||||
if (err) {
|
||||
pgoff_t last_off;
|
||||
|
||||
if (!map.m_len)
|
||||
if (!expanded)
|
||||
return err;
|
||||
|
||||
last_off = map.m_lblk + map.m_len - 1;
|
||||
last_off = pg_start + expanded - 1;
|
||||
|
||||
/* update new size to the failed position */
|
||||
new_size = (last_off == pg_end) ? offset + len :
|
||||
|
|
@ -1816,7 +1815,8 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
|
|||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
u32 masked_flags = fi->i_flags & mask;
|
||||
|
||||
f2fs_bug_on(F2FS_I_SB(inode), (iflags & ~mask));
|
||||
/* mask can be shrunk by flags_valid selector */
|
||||
iflags &= mask;
|
||||
|
||||
/* Is it quota file? Do not allow user to mess with it */
|
||||
if (IS_NOQUOTA(inode))
|
||||
|
|
@ -2490,7 +2490,7 @@ static int f2fs_ioc_gc(struct file *filp, unsigned long arg)
|
|||
down_write(&sbi->gc_lock);
|
||||
}
|
||||
|
||||
ret = f2fs_gc(sbi, sync, true, NULL_SEGNO);
|
||||
ret = f2fs_gc(sbi, sync, true, false, NULL_SEGNO);
|
||||
out:
|
||||
mnt_drop_write_file(filp);
|
||||
return ret;
|
||||
|
|
@ -2526,7 +2526,8 @@ do_more:
|
|||
down_write(&sbi->gc_lock);
|
||||
}
|
||||
|
||||
ret = f2fs_gc(sbi, range->sync, true, GET_SEGNO(sbi, range->start));
|
||||
ret = f2fs_gc(sbi, range->sync, true, false,
|
||||
GET_SEGNO(sbi, range->start));
|
||||
if (ret) {
|
||||
if (ret == -EBUSY)
|
||||
ret = -EAGAIN;
|
||||
|
|
@ -2583,7 +2584,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
|
|||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct f2fs_map_blocks map = { .m_next_extent = NULL,
|
||||
.m_seg_type = NO_CHECK_TYPE ,
|
||||
.m_seg_type = NO_CHECK_TYPE,
|
||||
.m_may_create = false };
|
||||
struct extent_info ei = {0, 0, 0};
|
||||
pgoff_t pg_start, pg_end, next_pgofs;
|
||||
|
|
@ -2979,7 +2980,7 @@ static int f2fs_ioc_flush_device(struct file *filp, unsigned long arg)
|
|||
sm->last_victim[GC_CB] = end_segno + 1;
|
||||
sm->last_victim[GC_GREEDY] = end_segno + 1;
|
||||
sm->last_victim[ALLOC_NEXT] = end_segno + 1;
|
||||
ret = f2fs_gc(sbi, true, true, start_segno);
|
||||
ret = f2fs_gc(sbi, true, true, true, start_segno);
|
||||
if (ret == -EAGAIN)
|
||||
ret = 0;
|
||||
else if (ret < 0)
|
||||
|
|
@ -4444,8 +4445,13 @@ write:
|
|||
clear_inode_flag(inode, FI_NO_PREALLOC);
|
||||
|
||||
/* if we couldn't write data, we should deallocate blocks. */
|
||||
if (preallocated && i_size_read(inode) < target_size)
|
||||
if (preallocated && i_size_read(inode) < target_size) {
|
||||
down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
|
||||
down_write(&F2FS_I(inode)->i_mmap_sem);
|
||||
f2fs_truncate(inode);
|
||||
up_write(&F2FS_I(inode)->i_mmap_sem);
|
||||
up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
|
||||
}
|
||||
|
||||
if (ret > 0)
|
||||
f2fs_update_iostat(F2FS_I_SB(inode), APP_WRITE_IO, ret);
|
||||
|
|
|
|||
95
fs/f2fs/gc.c
95
fs/f2fs/gc.c
|
|
@ -31,19 +31,24 @@ static int gc_thread_func(void *data)
|
|||
struct f2fs_sb_info *sbi = data;
|
||||
struct f2fs_gc_kthread *gc_th = sbi->gc_thread;
|
||||
wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head;
|
||||
wait_queue_head_t *fggc_wq = &sbi->gc_thread->fggc_wq;
|
||||
unsigned int wait_ms;
|
||||
|
||||
wait_ms = gc_th->min_sleep_time;
|
||||
|
||||
set_freezable();
|
||||
do {
|
||||
bool sync_mode;
|
||||
bool sync_mode, foreground = false;
|
||||
|
||||
wait_event_interruptible_timeout(*wq,
|
||||
kthread_should_stop() || freezing(current) ||
|
||||
waitqueue_active(fggc_wq) ||
|
||||
gc_th->gc_wake,
|
||||
msecs_to_jiffies(wait_ms));
|
||||
|
||||
if (test_opt(sbi, GC_MERGE) && waitqueue_active(fggc_wq))
|
||||
foreground = true;
|
||||
|
||||
/* give it a try one time */
|
||||
if (gc_th->gc_wake)
|
||||
gc_th->gc_wake = 0;
|
||||
|
|
@ -90,7 +95,10 @@ static int gc_thread_func(void *data)
|
|||
goto do_gc;
|
||||
}
|
||||
|
||||
if (!down_write_trylock(&sbi->gc_lock)) {
|
||||
if (foreground) {
|
||||
down_write(&sbi->gc_lock);
|
||||
goto do_gc;
|
||||
} else if (!down_write_trylock(&sbi->gc_lock)) {
|
||||
stat_other_skip_bggc_count(sbi);
|
||||
goto next;
|
||||
}
|
||||
|
|
@ -107,14 +115,22 @@ static int gc_thread_func(void *data)
|
|||
else
|
||||
increase_sleep_time(gc_th, &wait_ms);
|
||||
do_gc:
|
||||
stat_inc_bggc_count(sbi->stat_info);
|
||||
if (!foreground)
|
||||
stat_inc_bggc_count(sbi->stat_info);
|
||||
|
||||
sync_mode = F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_SYNC;
|
||||
|
||||
/* foreground GC was been triggered via f2fs_balance_fs() */
|
||||
if (foreground)
|
||||
sync_mode = false;
|
||||
|
||||
/* if return value is not zero, no victim was selected */
|
||||
if (f2fs_gc(sbi, sync_mode, true, NULL_SEGNO))
|
||||
if (f2fs_gc(sbi, sync_mode, !foreground, false, NULL_SEGNO))
|
||||
wait_ms = gc_th->no_gc_sleep_time;
|
||||
|
||||
if (foreground)
|
||||
wake_up_all(&gc_th->fggc_wq);
|
||||
|
||||
trace_f2fs_background_gc(sbi->sb, wait_ms,
|
||||
prefree_segments(sbi), free_segments(sbi));
|
||||
|
||||
|
|
@ -144,10 +160,11 @@ int f2fs_start_gc_thread(struct f2fs_sb_info *sbi)
|
|||
gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME;
|
||||
gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME;
|
||||
|
||||
gc_th->gc_wake= 0;
|
||||
gc_th->gc_wake = 0;
|
||||
|
||||
sbi->gc_thread = gc_th;
|
||||
init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head);
|
||||
init_waitqueue_head(&sbi->gc_thread->fggc_wq);
|
||||
sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi,
|
||||
"f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev));
|
||||
if (IS_ERR(gc_th->f2fs_gc_task)) {
|
||||
|
|
@ -162,9 +179,11 @@ out:
|
|||
void f2fs_stop_gc_thread(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_gc_kthread *gc_th = sbi->gc_thread;
|
||||
|
||||
if (!gc_th)
|
||||
return;
|
||||
kthread_stop(gc_th->f2fs_gc_task);
|
||||
wake_up_all(&gc_th->fggc_wq);
|
||||
kfree(gc_th);
|
||||
sbi->gc_thread = NULL;
|
||||
}
|
||||
|
|
@ -392,10 +411,6 @@ static void add_victim_entry(struct f2fs_sb_info *sbi,
|
|||
if (p->gc_mode == GC_AT &&
|
||||
get_valid_blocks(sbi, segno, true) == 0)
|
||||
return;
|
||||
|
||||
if (p->alloc_mode == AT_SSR &&
|
||||
get_seg_entry(sbi, segno)->ckpt_valid_blocks == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < sbi->segs_per_sec; i++)
|
||||
|
|
@ -728,11 +743,27 @@ retry:
|
|||
|
||||
if (sec_usage_check(sbi, secno))
|
||||
goto next;
|
||||
|
||||
/* Don't touch checkpointed data */
|
||||
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED) &&
|
||||
get_ckpt_valid_blocks(sbi, segno) &&
|
||||
p.alloc_mode == LFS))
|
||||
goto next;
|
||||
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
|
||||
if (p.alloc_mode == LFS) {
|
||||
/*
|
||||
* LFS is set to find source section during GC.
|
||||
* The victim should have no checkpointed data.
|
||||
*/
|
||||
if (get_ckpt_valid_blocks(sbi, segno, true))
|
||||
goto next;
|
||||
} else {
|
||||
/*
|
||||
* SSR | AT_SSR are set to find target segment
|
||||
* for writes which can be full by checkpointed
|
||||
* and newly written blocks.
|
||||
*/
|
||||
if (!f2fs_segment_has_free_slot(sbi, segno))
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap))
|
||||
goto next;
|
||||
|
||||
|
|
@ -828,6 +859,7 @@ static void add_gc_inode(struct gc_inode_list *gc_list, struct inode *inode)
|
|||
static void put_gc_inode(struct gc_inode_list *gc_list)
|
||||
{
|
||||
struct inode_entry *ie, *next_ie;
|
||||
|
||||
list_for_each_entry_safe(ie, next_ie, &gc_list->ilist, list) {
|
||||
radix_tree_delete(&gc_list->iroot, ie->inode->i_ino);
|
||||
iput(ie->inode);
|
||||
|
|
@ -952,9 +984,11 @@ block_t f2fs_start_bidx_of_node(unsigned int node_ofs, struct inode *inode)
|
|||
bidx = node_ofs - 1;
|
||||
} else if (node_ofs <= indirect_blks) {
|
||||
int dec = (node_ofs - 4) / (NIDS_PER_BLOCK + 1);
|
||||
|
||||
bidx = node_ofs - 2 - dec;
|
||||
} else {
|
||||
int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
|
||||
|
||||
bidx = node_ofs - 5 - dec;
|
||||
}
|
||||
return bidx * ADDRS_PER_BLOCK(inode) + ADDRS_PER_INODE(inode);
|
||||
|
|
@ -1120,7 +1154,8 @@ static int move_data_block(struct inode *inode, block_t bidx,
|
|||
block_t newaddr;
|
||||
int err = 0;
|
||||
bool lfs_mode = f2fs_lfs_mode(fio.sbi);
|
||||
int type = fio.sbi->am.atgc_enabled ?
|
||||
int type = fio.sbi->am.atgc_enabled && (gc_type == BG_GC) &&
|
||||
(fio.sbi->gc_mode != GC_URGENT_HIGH) ?
|
||||
CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA;
|
||||
|
||||
/* do not read out */
|
||||
|
|
@ -1354,7 +1389,8 @@ out:
|
|||
* the victim data block is ignored.
|
||||
*/
|
||||
static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
struct gc_inode_list *gc_list, unsigned int segno, int gc_type)
|
||||
struct gc_inode_list *gc_list, unsigned int segno, int gc_type,
|
||||
bool force_migrate)
|
||||
{
|
||||
struct super_block *sb = sbi->sb;
|
||||
struct f2fs_summary *entry;
|
||||
|
|
@ -1383,8 +1419,8 @@ next_step:
|
|||
* race condition along with SSR block allocation.
|
||||
*/
|
||||
if ((gc_type == BG_GC && has_not_enough_free_secs(sbi, 0, 0)) ||
|
||||
get_valid_blocks(sbi, segno, true) ==
|
||||
BLKS_PER_SEC(sbi))
|
||||
(!force_migrate && get_valid_blocks(sbi, segno, true) ==
|
||||
BLKS_PER_SEC(sbi)))
|
||||
return submitted;
|
||||
|
||||
if (check_valid_map(sbi, segno, off) == 0)
|
||||
|
|
@ -1519,7 +1555,8 @@ static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
|
|||
|
||||
static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
||||
unsigned int start_segno,
|
||||
struct gc_inode_list *gc_list, int gc_type)
|
||||
struct gc_inode_list *gc_list, int gc_type,
|
||||
bool force_migrate)
|
||||
{
|
||||
struct page *sum_page;
|
||||
struct f2fs_summary_block *sum;
|
||||
|
|
@ -1606,7 +1643,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
|||
gc_type);
|
||||
else
|
||||
submitted += gc_data_segment(sbi, sum->entries, gc_list,
|
||||
segno, gc_type);
|
||||
segno, gc_type,
|
||||
force_migrate);
|
||||
|
||||
stat_inc_seg_count(sbi, type, gc_type);
|
||||
migrated++;
|
||||
|
|
@ -1634,7 +1672,7 @@ skip:
|
|||
}
|
||||
|
||||
int f2fs_gc(struct f2fs_sb_info *sbi, bool sync,
|
||||
bool background, unsigned int segno)
|
||||
bool background, bool force, unsigned int segno)
|
||||
{
|
||||
int gc_type = sync ? FG_GC : BG_GC;
|
||||
int sec_freed = 0, seg_freed = 0, total_freed = 0;
|
||||
|
|
@ -1696,7 +1734,7 @@ gc_more:
|
|||
if (ret)
|
||||
goto stop;
|
||||
|
||||
seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type);
|
||||
seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type, force);
|
||||
if (gc_type == FG_GC &&
|
||||
seg_freed == f2fs_usable_segs_in_sec(sbi, segno))
|
||||
sec_freed++;
|
||||
|
|
@ -1835,7 +1873,7 @@ static int free_segment_range(struct f2fs_sb_info *sbi,
|
|||
.iroot = RADIX_TREE_INIT(gc_list.iroot, GFP_NOFS),
|
||||
};
|
||||
|
||||
do_garbage_collect(sbi, segno, &gc_list, FG_GC);
|
||||
do_garbage_collect(sbi, segno, &gc_list, FG_GC, true);
|
||||
put_gc_inode(&gc_list);
|
||||
|
||||
if (!gc_only && get_valid_blocks(sbi, segno, true)) {
|
||||
|
|
@ -1974,7 +2012,20 @@ int f2fs_resize_fs(struct f2fs_sb_info *sbi, __u64 block_count)
|
|||
|
||||
/* stop CP to protect MAIN_SEC in free_segment_range */
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
spin_lock(&sbi->stat_lock);
|
||||
if (shrunk_blocks + valid_user_blocks(sbi) +
|
||||
sbi->current_reserved_blocks + sbi->unusable_block_count +
|
||||
F2FS_OPTION(sbi).root_reserved_blocks > sbi->user_block_count)
|
||||
err = -ENOSPC;
|
||||
spin_unlock(&sbi->stat_lock);
|
||||
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
err = free_segment_range(sbi, secs, true);
|
||||
|
||||
out_unlock:
|
||||
f2fs_unlock_op(sbi);
|
||||
up_write(&sbi->gc_lock);
|
||||
if (err)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,12 @@ struct f2fs_gc_kthread {
|
|||
|
||||
/* for changing gc mode */
|
||||
unsigned int gc_wake;
|
||||
|
||||
/* for GC_MERGE mount option */
|
||||
wait_queue_head_t fggc_wq; /*
|
||||
* caller of f2fs_balance_fs()
|
||||
* will wait on this wait queue.
|
||||
*/
|
||||
};
|
||||
|
||||
struct gc_inode_list {
|
||||
|
|
|
|||
|
|
@ -237,7 +237,8 @@ out:
|
|||
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
f2fs_balance_fs(sbi, dn.node_changed);
|
||||
if (!err)
|
||||
f2fs_balance_fs(sbi, dn.node_changed);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -666,6 +666,7 @@ retry:
|
|||
node_page = f2fs_get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(node_page)) {
|
||||
int err = PTR_ERR(node_page);
|
||||
|
||||
if (err == -ENOMEM) {
|
||||
cond_resched();
|
||||
goto retry;
|
||||
|
|
@ -698,7 +699,7 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
|||
|
||||
/*
|
||||
* We need to balance fs here to prevent from producing dirty node pages
|
||||
* during the urgent cleaning time when runing out of free sections.
|
||||
* during the urgent cleaning time when running out of free sections.
|
||||
*/
|
||||
f2fs_update_inode_page(inode);
|
||||
if (wbc && wbc->nr_to_write)
|
||||
|
|
|
|||
|
|
@ -419,6 +419,7 @@ struct dentry *f2fs_get_parent(struct dentry *child)
|
|||
struct qstr dotdot = QSTR_INIT("..", 2);
|
||||
struct page *page;
|
||||
unsigned long ino = f2fs_inode_by_name(d_inode(child), &dotdot, &page);
|
||||
|
||||
if (!ino) {
|
||||
if (IS_ERR(page))
|
||||
return ERR_CAST(page);
|
||||
|
|
@ -628,6 +629,7 @@ static const char *f2fs_get_link(struct dentry *dentry,
|
|||
struct delayed_call *done)
|
||||
{
|
||||
const char *link = page_get_link(dentry, inode, done);
|
||||
|
||||
if (!IS_ERR(link) && !*link) {
|
||||
/* this is broken symlink case */
|
||||
do_delayed_call(done);
|
||||
|
|
@ -765,6 +767,7 @@ out_fail:
|
|||
static int f2fs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
|
||||
if (f2fs_empty_dir(inode))
|
||||
return f2fs_unlink(dir, dentry);
|
||||
return -ENOTEMPTY;
|
||||
|
|
|
|||
|
|
@ -43,11 +43,15 @@ int f2fs_check_nid_range(struct f2fs_sb_info *sbi, nid_t nid)
|
|||
bool f2fs_available_free_memory(struct f2fs_sb_info *sbi, int type)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
|
||||
struct sysinfo val;
|
||||
unsigned long avail_ram;
|
||||
unsigned long mem_size = 0;
|
||||
bool res = false;
|
||||
|
||||
if (!nm_i)
|
||||
return true;
|
||||
|
||||
si_meminfo(&val);
|
||||
|
||||
/* only uses low memory */
|
||||
|
|
@ -89,6 +93,10 @@ bool f2fs_available_free_memory(struct f2fs_sb_info *sbi, int type)
|
|||
/* it allows 20% / total_ram for inmemory pages */
|
||||
mem_size = get_pages(sbi, F2FS_INMEM_PAGES);
|
||||
res = mem_size < (val.totalram / 5);
|
||||
} else if (type == DISCARD_CACHE) {
|
||||
mem_size = (atomic_read(&dcc->discard_cmd_cnt) *
|
||||
sizeof(struct discard_cmd)) >> PAGE_SHIFT;
|
||||
res = mem_size < (avail_ram * nm_i->ram_thresh / 100);
|
||||
} else {
|
||||
if (!sbi->sb->s_bdi->wb.dirty_exceeded)
|
||||
return true;
|
||||
|
|
@ -462,6 +470,7 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
|
|||
/* increment version no as node is removed */
|
||||
if (nat_get_blkaddr(e) != NEW_ADDR && new_blkaddr == NULL_ADDR) {
|
||||
unsigned char version = nat_get_version(e);
|
||||
|
||||
nat_set_version(e, inc_node_version(version));
|
||||
}
|
||||
|
||||
|
|
@ -1383,7 +1392,7 @@ repeat:
|
|||
goto out_err;
|
||||
}
|
||||
page_hit:
|
||||
if(unlikely(nid != nid_of_node(page))) {
|
||||
if (unlikely(nid != nid_of_node(page))) {
|
||||
f2fs_warn(sbi, "inconsistent node block, nid:%lu, node_footer[nid:%u,ino:%u,ofs:%u,cpver:%llu,blkaddr:%u]",
|
||||
nid, nid_of_node(page), ino_of_node(page),
|
||||
ofs_of_node(page), cpver_of_node(page),
|
||||
|
|
@ -1775,7 +1784,7 @@ continue_unlock:
|
|||
out:
|
||||
if (nwritten)
|
||||
f2fs_submit_merged_write_cond(sbi, NULL, NULL, ino, NODE);
|
||||
return ret ? -EIO: 0;
|
||||
return ret ? -EIO : 0;
|
||||
}
|
||||
|
||||
static int f2fs_match_ino(struct inode *inode, unsigned long ino, void *data)
|
||||
|
|
@ -2117,8 +2126,8 @@ static int __insert_free_nid(struct f2fs_sb_info *sbi,
|
|||
struct free_nid *i)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
|
||||
int err = radix_tree_insert(&nm_i->free_nid_root, i->nid, i);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
@ -2983,6 +2992,7 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
while ((found = __gang_lookup_nat_set(nm_i,
|
||||
set_idx, SETVEC_SIZE, setvec))) {
|
||||
unsigned idx;
|
||||
|
||||
set_idx = setvec[found - 1]->set + 1;
|
||||
for (idx = 0; idx < found; idx++)
|
||||
__adjust_nat_entry_set(setvec[idx], &sets,
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ enum mem_type {
|
|||
INO_ENTRIES, /* indicates inode entries */
|
||||
EXTENT_CACHE, /* indicates extent cache */
|
||||
INMEM_PAGES, /* indicates inmemory pages */
|
||||
DISCARD_CACHE, /* indicates memory of cached discard cmds */
|
||||
BASE_CHECK, /* check kernel status */
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -458,6 +458,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
|
|||
/* Get the previous summary */
|
||||
for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) {
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, i);
|
||||
|
||||
if (curseg->segno == segno) {
|
||||
sum = curseg->sum_blk->entries[blkoff];
|
||||
goto got_it;
|
||||
|
|
@ -875,5 +876,5 @@ out:
|
|||
#endif
|
||||
sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */
|
||||
|
||||
return ret ? ret: err;
|
||||
return ret ? ret : err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,7 +186,10 @@ void f2fs_register_inmem_page(struct inode *inode, struct page *page)
|
|||
{
|
||||
struct inmem_pages *new;
|
||||
|
||||
f2fs_set_page_private(page, ATOMIC_WRITTEN_PAGE);
|
||||
if (PagePrivate(page))
|
||||
set_page_private(page, (unsigned long)ATOMIC_WRITTEN_PAGE);
|
||||
else
|
||||
f2fs_set_page_private(page, ATOMIC_WRITTEN_PAGE);
|
||||
|
||||
new = f2fs_kmem_cache_alloc(inmem_entry_slab, GFP_NOFS);
|
||||
|
||||
|
|
@ -324,23 +327,27 @@ void f2fs_drop_inmem_pages(struct inode *inode)
|
|||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
|
||||
while (!list_empty(&fi->inmem_pages)) {
|
||||
do {
|
||||
mutex_lock(&fi->inmem_lock);
|
||||
if (list_empty(&fi->inmem_pages)) {
|
||||
fi->i_gc_failures[GC_FAILURE_ATOMIC] = 0;
|
||||
|
||||
spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
if (!list_empty(&fi->inmem_ilist))
|
||||
list_del_init(&fi->inmem_ilist);
|
||||
if (f2fs_is_atomic_file(inode)) {
|
||||
clear_inode_flag(inode, FI_ATOMIC_FILE);
|
||||
sbi->atomic_files--;
|
||||
}
|
||||
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
|
||||
mutex_unlock(&fi->inmem_lock);
|
||||
break;
|
||||
}
|
||||
__revoke_inmem_pages(inode, &fi->inmem_pages,
|
||||
true, false, true);
|
||||
mutex_unlock(&fi->inmem_lock);
|
||||
}
|
||||
|
||||
fi->i_gc_failures[GC_FAILURE_ATOMIC] = 0;
|
||||
|
||||
spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
if (!list_empty(&fi->inmem_ilist))
|
||||
list_del_init(&fi->inmem_ilist);
|
||||
if (f2fs_is_atomic_file(inode)) {
|
||||
clear_inode_flag(inode, FI_ATOMIC_FILE);
|
||||
sbi->atomic_files--;
|
||||
}
|
||||
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
|
||||
|
|
@ -503,8 +510,19 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
|
|||
* dir/node pages without enough free segments.
|
||||
*/
|
||||
if (has_not_enough_free_secs(sbi, 0, 0)) {
|
||||
down_write(&sbi->gc_lock);
|
||||
f2fs_gc(sbi, false, false, NULL_SEGNO);
|
||||
if (test_opt(sbi, GC_MERGE) && sbi->gc_thread &&
|
||||
sbi->gc_thread->f2fs_gc_task) {
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
prepare_to_wait(&sbi->gc_thread->fggc_wq, &wait,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
wake_up(&sbi->gc_thread->gc_wait_queue_head);
|
||||
io_schedule();
|
||||
finish_wait(&sbi->gc_thread->fggc_wq, &wait);
|
||||
} else {
|
||||
down_write(&sbi->gc_lock);
|
||||
f2fs_gc(sbi, false, false, false, NULL_SEGNO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -653,7 +671,11 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino)
|
|||
|
||||
llist_add(&cmd.llnode, &fcc->issue_list);
|
||||
|
||||
/* update issue_list before we wake up issue_flush thread */
|
||||
/*
|
||||
* update issue_list before we wake up issue_flush thread, this
|
||||
* smp_mb() pairs with another barrier in ___wait_event(), see
|
||||
* more details in comments of waitqueue_active().
|
||||
*/
|
||||
smp_mb();
|
||||
|
||||
if (waitqueue_active(&fcc->flush_wait_queue))
|
||||
|
|
@ -861,7 +883,7 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
|
|||
mutex_lock(&dirty_i->seglist_lock);
|
||||
|
||||
valid_blocks = get_valid_blocks(sbi, segno, false);
|
||||
ckpt_valid_blocks = get_ckpt_valid_blocks(sbi, segno);
|
||||
ckpt_valid_blocks = get_ckpt_valid_blocks(sbi, segno, false);
|
||||
|
||||
if (valid_blocks == 0 && (!is_sbi_flag_set(sbi, SBI_CP_DISABLED) ||
|
||||
ckpt_valid_blocks == usable_blocks)) {
|
||||
|
|
@ -946,7 +968,7 @@ static unsigned int get_free_segment(struct f2fs_sb_info *sbi)
|
|||
for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) {
|
||||
if (get_valid_blocks(sbi, segno, false))
|
||||
continue;
|
||||
if (get_ckpt_valid_blocks(sbi, segno))
|
||||
if (get_ckpt_valid_blocks(sbi, segno, false))
|
||||
continue;
|
||||
mutex_unlock(&dirty_i->seglist_lock);
|
||||
return segno;
|
||||
|
|
@ -1095,6 +1117,8 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi,
|
|||
struct discard_policy *dpolicy,
|
||||
int discard_type, unsigned int granularity)
|
||||
{
|
||||
struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
|
||||
|
||||
/* common policy */
|
||||
dpolicy->type = discard_type;
|
||||
dpolicy->sync = true;
|
||||
|
|
@ -1114,7 +1138,9 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi,
|
|||
dpolicy->ordered = true;
|
||||
if (utilization(sbi) > DEF_DISCARD_URGENT_UTIL) {
|
||||
dpolicy->granularity = 1;
|
||||
dpolicy->max_interval = DEF_MIN_DISCARD_ISSUE_TIME;
|
||||
if (atomic_read(&dcc->discard_cmd_cnt))
|
||||
dpolicy->max_interval =
|
||||
DEF_MIN_DISCARD_ISSUE_TIME;
|
||||
}
|
||||
} else if (discard_type == DPOLICY_FORCE) {
|
||||
dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME;
|
||||
|
|
@ -1730,8 +1756,15 @@ static int issue_discard_thread(void *data)
|
|||
set_freezable();
|
||||
|
||||
do {
|
||||
__init_discard_policy(sbi, &dpolicy, DPOLICY_BG,
|
||||
dcc->discard_granularity);
|
||||
if (sbi->gc_mode == GC_URGENT_HIGH ||
|
||||
!f2fs_available_free_memory(sbi, DISCARD_CACHE))
|
||||
__init_discard_policy(sbi, &dpolicy, DPOLICY_FORCE, 1);
|
||||
else
|
||||
__init_discard_policy(sbi, &dpolicy, DPOLICY_BG,
|
||||
dcc->discard_granularity);
|
||||
|
||||
if (!atomic_read(&dcc->discard_cmd_cnt))
|
||||
wait_ms = dpolicy.max_interval;
|
||||
|
||||
wait_event_interruptible_timeout(*q,
|
||||
kthread_should_stop() || freezing(current) ||
|
||||
|
|
@ -1755,9 +1788,8 @@ static int issue_discard_thread(void *data)
|
|||
wait_ms = dpolicy.max_interval;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sbi->gc_mode == GC_URGENT_HIGH)
|
||||
__init_discard_policy(sbi, &dpolicy, DPOLICY_FORCE, 1);
|
||||
if (!atomic_read(&dcc->discard_cmd_cnt))
|
||||
continue;
|
||||
|
||||
sb_start_intwrite(sbi->sb);
|
||||
|
||||
|
|
@ -1765,7 +1797,7 @@ static int issue_discard_thread(void *data)
|
|||
if (issued > 0) {
|
||||
__wait_all_discard_cmd(sbi, &dpolicy);
|
||||
wait_ms = dpolicy.min_interval;
|
||||
} else if (issued == -1){
|
||||
} else if (issued == -1) {
|
||||
wait_ms = f2fs_time_to_wait(sbi, DISCARD_TIME);
|
||||
if (!wait_ms)
|
||||
wait_ms = dpolicy.mid_interval;
|
||||
|
|
@ -2142,6 +2174,7 @@ static void __set_sit_entry_type(struct f2fs_sb_info *sbi, int type,
|
|||
unsigned int segno, int modified)
|
||||
{
|
||||
struct seg_entry *se = get_seg_entry(sbi, segno);
|
||||
|
||||
se->type = type;
|
||||
if (modified)
|
||||
__mark_sit_entry_dirty(sbi, segno);
|
||||
|
|
@ -2333,6 +2366,7 @@ static void __add_sum_entry(struct f2fs_sb_info *sbi, int type,
|
|||
{
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, type);
|
||||
void *addr = curseg->sum_blk;
|
||||
|
||||
addr += curseg->next_blkoff * sizeof(struct f2fs_summary);
|
||||
memcpy(addr, sum, sizeof(struct f2fs_summary));
|
||||
}
|
||||
|
|
@ -2604,22 +2638,20 @@ static void new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec)
|
|||
curseg->alloc_type = LFS;
|
||||
}
|
||||
|
||||
static void __next_free_blkoff(struct f2fs_sb_info *sbi,
|
||||
struct curseg_info *seg, block_t start)
|
||||
static int __next_free_blkoff(struct f2fs_sb_info *sbi,
|
||||
int segno, block_t start)
|
||||
{
|
||||
struct seg_entry *se = get_seg_entry(sbi, seg->segno);
|
||||
struct seg_entry *se = get_seg_entry(sbi, segno);
|
||||
int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long);
|
||||
unsigned long *target_map = SIT_I(sbi)->tmp_map;
|
||||
unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map;
|
||||
unsigned long *cur_map = (unsigned long *)se->cur_valid_map;
|
||||
int i, pos;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < entries; i++)
|
||||
target_map[i] = ckpt_map[i] | cur_map[i];
|
||||
|
||||
pos = __find_rev_next_zero_bit(target_map, sbi->blocks_per_seg, start);
|
||||
|
||||
seg->next_blkoff = pos;
|
||||
return __find_rev_next_zero_bit(target_map, sbi->blocks_per_seg, start);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2631,11 +2663,18 @@ static void __refresh_next_blkoff(struct f2fs_sb_info *sbi,
|
|||
struct curseg_info *seg)
|
||||
{
|
||||
if (seg->alloc_type == SSR)
|
||||
__next_free_blkoff(sbi, seg, seg->next_blkoff + 1);
|
||||
seg->next_blkoff =
|
||||
__next_free_blkoff(sbi, seg->segno,
|
||||
seg->next_blkoff + 1);
|
||||
else
|
||||
seg->next_blkoff++;
|
||||
}
|
||||
|
||||
bool f2fs_segment_has_free_slot(struct f2fs_sb_info *sbi, int segno)
|
||||
{
|
||||
return __next_free_blkoff(sbi, segno, 0) < sbi->blocks_per_seg;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function always allocates a used segment(from dirty seglist) by SSR
|
||||
* manner, so it should recover the existing segment information of valid blocks
|
||||
|
|
@ -2661,7 +2700,7 @@ static void change_curseg(struct f2fs_sb_info *sbi, int type, bool flush)
|
|||
|
||||
reset_curseg(sbi, type, 1);
|
||||
curseg->alloc_type = SSR;
|
||||
__next_free_blkoff(sbi, curseg, 0);
|
||||
curseg->next_blkoff = __next_free_blkoff(sbi, curseg->segno, 0);
|
||||
|
||||
sum_page = f2fs_get_sum_page(sbi, new_segno);
|
||||
if (IS_ERR(sum_page)) {
|
||||
|
|
@ -2893,7 +2932,8 @@ unlock:
|
|||
up_read(&SM_I(sbi)->curseg_lock);
|
||||
}
|
||||
|
||||
static void __allocate_new_segment(struct f2fs_sb_info *sbi, int type)
|
||||
static void __allocate_new_segment(struct f2fs_sb_info *sbi, int type,
|
||||
bool new_sec, bool force)
|
||||
{
|
||||
struct curseg_info *curseg = CURSEG_I(sbi, type);
|
||||
unsigned int old_segno;
|
||||
|
|
@ -2901,32 +2941,43 @@ static void __allocate_new_segment(struct f2fs_sb_info *sbi, int type)
|
|||
if (!curseg->inited)
|
||||
goto alloc;
|
||||
|
||||
if (!curseg->next_blkoff &&
|
||||
!get_valid_blocks(sbi, curseg->segno, false) &&
|
||||
!get_ckpt_valid_blocks(sbi, curseg->segno))
|
||||
return;
|
||||
if (force || curseg->next_blkoff ||
|
||||
get_valid_blocks(sbi, curseg->segno, new_sec))
|
||||
goto alloc;
|
||||
|
||||
if (!get_ckpt_valid_blocks(sbi, curseg->segno, new_sec))
|
||||
return;
|
||||
alloc:
|
||||
old_segno = curseg->segno;
|
||||
SIT_I(sbi)->s_ops->allocate_segment(sbi, type, true);
|
||||
locate_dirty_segment(sbi, old_segno);
|
||||
}
|
||||
|
||||
void f2fs_allocate_new_segment(struct f2fs_sb_info *sbi, int type)
|
||||
static void __allocate_new_section(struct f2fs_sb_info *sbi,
|
||||
int type, bool force)
|
||||
{
|
||||
__allocate_new_segment(sbi, type, true, force);
|
||||
}
|
||||
|
||||
void f2fs_allocate_new_section(struct f2fs_sb_info *sbi, int type, bool force)
|
||||
{
|
||||
down_read(&SM_I(sbi)->curseg_lock);
|
||||
down_write(&SIT_I(sbi)->sentry_lock);
|
||||
__allocate_new_segment(sbi, type);
|
||||
__allocate_new_section(sbi, type, force);
|
||||
up_write(&SIT_I(sbi)->sentry_lock);
|
||||
up_read(&SM_I(sbi)->curseg_lock);
|
||||
}
|
||||
|
||||
void f2fs_allocate_new_segments(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
int i;
|
||||
|
||||
down_read(&SM_I(sbi)->curseg_lock);
|
||||
down_write(&SIT_I(sbi)->sentry_lock);
|
||||
for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++)
|
||||
__allocate_new_segment(sbi, i);
|
||||
__allocate_new_segment(sbi, i, false, false);
|
||||
up_write(&SIT_I(sbi)->sentry_lock);
|
||||
up_read(&SM_I(sbi)->curseg_lock);
|
||||
}
|
||||
|
||||
static const struct segment_allocation default_salloc_ops = {
|
||||
|
|
@ -3239,7 +3290,9 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
|
|||
struct inode *inode = fio->page->mapping->host;
|
||||
|
||||
if (is_cold_data(fio->page)) {
|
||||
if (fio->sbi->am.atgc_enabled)
|
||||
if (fio->sbi->am.atgc_enabled &&
|
||||
(fio->io_type == FS_DATA_IO) &&
|
||||
(fio->sbi->gc_mode != GC_URGENT_HIGH))
|
||||
return CURSEG_ALL_DATA_ATGC;
|
||||
else
|
||||
return CURSEG_COLD_DATA;
|
||||
|
|
@ -3365,12 +3418,12 @@ void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
|
|||
f2fs_inode_chksum_set(sbi, page);
|
||||
}
|
||||
|
||||
if (F2FS_IO_ALIGNED(sbi))
|
||||
fio->retry = false;
|
||||
|
||||
if (fio) {
|
||||
struct f2fs_bio_info *io;
|
||||
|
||||
if (F2FS_IO_ALIGNED(sbi))
|
||||
fio->retry = false;
|
||||
|
||||
INIT_LIST_HEAD(&fio->list);
|
||||
fio->in_list = true;
|
||||
io = sbi->write_io[fio->type] + fio->temp;
|
||||
|
|
@ -3499,7 +3552,13 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio)
|
|||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
f2fs_warn(sbi, "%s: incorrect segment(%u) type, run fsck to fix.",
|
||||
__func__, segno);
|
||||
return -EFSCORRUPTED;
|
||||
err = -EFSCORRUPTED;
|
||||
goto drop_bio;
|
||||
}
|
||||
|
||||
if (is_sbi_flag_set(sbi, SBI_NEED_FSCK) || f2fs_cp_error(sbi)) {
|
||||
err = -EIO;
|
||||
goto drop_bio;
|
||||
}
|
||||
|
||||
stat_inc_inplace_blocks(fio->sbi);
|
||||
|
|
@ -3513,6 +3572,15 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio)
|
|||
f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE);
|
||||
}
|
||||
|
||||
return err;
|
||||
drop_bio:
|
||||
if (fio->bio && *(fio->bio)) {
|
||||
struct bio *bio = *(fio->bio);
|
||||
|
||||
bio->bi_status = BLK_STS_IOERR;
|
||||
bio_endio(bio);
|
||||
*(fio->bio) = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -3539,6 +3607,7 @@ void f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
|||
struct seg_entry *se;
|
||||
int type;
|
||||
unsigned short old_blkoff;
|
||||
unsigned char old_alloc_type;
|
||||
|
||||
segno = GET_SEGNO(sbi, new_blkaddr);
|
||||
se = get_seg_entry(sbi, segno);
|
||||
|
|
@ -3572,6 +3641,7 @@ void f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
|||
|
||||
old_cursegno = curseg->segno;
|
||||
old_blkoff = curseg->next_blkoff;
|
||||
old_alloc_type = curseg->alloc_type;
|
||||
|
||||
/* change the current segment */
|
||||
if (segno != curseg->segno) {
|
||||
|
|
@ -3606,6 +3676,7 @@ void f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
|||
change_curseg(sbi, type, true);
|
||||
}
|
||||
curseg->next_blkoff = old_blkoff;
|
||||
curseg->alloc_type = old_alloc_type;
|
||||
}
|
||||
|
||||
up_write(&sit_i->sentry_lock);
|
||||
|
|
@ -3717,6 +3788,7 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi)
|
|||
|
||||
for (j = 0; j < blk_off; j++) {
|
||||
struct f2fs_summary *s;
|
||||
|
||||
s = (struct f2fs_summary *)(kaddr + offset);
|
||||
seg_i->sum_blk->entries[j] = *s;
|
||||
offset += SUMMARY_SIZE;
|
||||
|
|
@ -3779,6 +3851,7 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int 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;
|
||||
|
|
@ -3880,6 +3953,7 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr)
|
|||
/* Step 3: write summary entries */
|
||||
for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) {
|
||||
unsigned short blkoff;
|
||||
|
||||
seg_i = CURSEG_I(sbi, i);
|
||||
if (sbi->ckpt->alloc_type[i] == SSR)
|
||||
blkoff = sbi->blocks_per_seg;
|
||||
|
|
@ -3916,6 +3990,7 @@ static void write_normal_summaries(struct f2fs_sb_info *sbi,
|
|||
block_t blkaddr, int type)
|
||||
{
|
||||
int i, end;
|
||||
|
||||
if (IS_DATASEG(type))
|
||||
end = type + NR_CURSEG_DATA_TYPE;
|
||||
else
|
||||
|
|
@ -4499,6 +4574,7 @@ static void init_free_segmap(struct f2fs_sb_info *sbi)
|
|||
/* set use the current segments */
|
||||
for (type = CURSEG_HOT_DATA; type <= CURSEG_COLD_NODE; type++) {
|
||||
struct curseg_info *curseg_t = CURSEG_I(sbi, type);
|
||||
|
||||
__set_test_and_inuse(sbi, curseg_t->segno);
|
||||
}
|
||||
}
|
||||
|
|
@ -4731,7 +4807,8 @@ static struct f2fs_dev_info *get_target_zoned_dev(struct f2fs_sb_info *sbi,
|
|||
}
|
||||
|
||||
static int report_one_zone_cb(struct blk_zone *zone, unsigned int idx,
|
||||
void *data) {
|
||||
void *data)
|
||||
{
|
||||
memcpy(data, zone, sizeof(struct blk_zone));
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -4783,7 +4860,8 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type)
|
|||
|
||||
f2fs_notice(sbi, "Assign new section to curseg[%d]: "
|
||||
"curseg[0x%x,0x%x]", type, cs->segno, cs->next_blkoff);
|
||||
allocate_segment_by_default(sbi, type, true);
|
||||
|
||||
f2fs_allocate_new_section(sbi, type, true);
|
||||
|
||||
/* check consistency of the zone curseg pointed to */
|
||||
if (check_zone_write_pointer(sbi, zbd, &zone))
|
||||
|
|
@ -4847,8 +4925,10 @@ struct check_zone_write_pointer_args {
|
|||
};
|
||||
|
||||
static int check_zone_write_pointer_cb(struct blk_zone *zone, unsigned int idx,
|
||||
void *data) {
|
||||
void *data)
|
||||
{
|
||||
struct check_zone_write_pointer_args *args;
|
||||
|
||||
args = (struct check_zone_write_pointer_args *)data;
|
||||
|
||||
return check_zone_write_pointer(args->sbi, args->fdev, zone);
|
||||
|
|
@ -5127,6 +5207,7 @@ static void discard_dirty_segmap(struct f2fs_sb_info *sbi,
|
|||
static void destroy_victim_secmap(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
|
||||
|
||||
kvfree(dirty_i->victim_secmap);
|
||||
}
|
||||
|
||||
|
|
@ -5171,6 +5252,7 @@ static void destroy_curseg(struct f2fs_sb_info *sbi)
|
|||
static void destroy_free_segmap(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct free_segmap_info *free_i = SM_I(sbi)->free_info;
|
||||
|
||||
if (!free_i)
|
||||
return;
|
||||
SM_I(sbi)->free_info = NULL;
|
||||
|
|
|
|||
|
|
@ -172,12 +172,10 @@ enum {
|
|||
/*
|
||||
* 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 */
|
||||
|
|
@ -361,8 +359,20 @@ static inline unsigned int get_valid_blocks(struct f2fs_sb_info *sbi,
|
|||
}
|
||||
|
||||
static inline unsigned int get_ckpt_valid_blocks(struct f2fs_sb_info *sbi,
|
||||
unsigned int segno)
|
||||
unsigned int segno, bool use_section)
|
||||
{
|
||||
if (use_section && __is_large_section(sbi)) {
|
||||
unsigned int start_segno = START_SEGNO(segno);
|
||||
unsigned int blocks = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sbi->segs_per_sec; i++, start_segno++) {
|
||||
struct seg_entry *se = get_seg_entry(sbi, start_segno);
|
||||
|
||||
blocks += se->ckpt_valid_blocks;
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
return get_seg_entry(sbi, segno)->ckpt_valid_blocks;
|
||||
}
|
||||
|
||||
|
|
|
|||
102
fs/f2fs/super.c
102
fs/f2fs/super.c
|
|
@ -151,6 +151,8 @@ enum {
|
|||
Opt_compress_chksum,
|
||||
Opt_compress_mode,
|
||||
Opt_atgc,
|
||||
Opt_gc_merge,
|
||||
Opt_nogc_merge,
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
|
|
@ -223,6 +225,8 @@ static match_table_t f2fs_tokens = {
|
|||
{Opt_compress_chksum, "compress_chksum"},
|
||||
{Opt_compress_mode, "compress_mode=%s"},
|
||||
{Opt_atgc, "atgc"},
|
||||
{Opt_gc_merge, "gc_merge"},
|
||||
{Opt_nogc_merge, "nogc_merge"},
|
||||
{Opt_err, NULL},
|
||||
};
|
||||
|
||||
|
|
@ -555,6 +559,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
|
|||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
int token;
|
||||
|
||||
if (!*p)
|
||||
continue;
|
||||
/*
|
||||
|
|
@ -1073,6 +1078,12 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
|
|||
case Opt_atgc:
|
||||
set_opt(sbi, ATGC);
|
||||
break;
|
||||
case Opt_gc_merge:
|
||||
set_opt(sbi, GC_MERGE);
|
||||
break;
|
||||
case Opt_nogc_merge:
|
||||
clear_opt(sbi, GC_MERGE);
|
||||
break;
|
||||
default:
|
||||
f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
|
||||
p);
|
||||
|
|
@ -1619,6 +1630,7 @@ static inline void f2fs_show_quota_options(struct seq_file *seq,
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
static inline void f2fs_show_compress_options(struct seq_file *seq,
|
||||
struct super_block *sb)
|
||||
{
|
||||
|
|
@ -1664,6 +1676,7 @@ static inline void f2fs_show_compress_options(struct seq_file *seq,
|
|||
else if (F2FS_OPTION(sbi).compress_mode == COMPR_MODE_USER)
|
||||
seq_printf(seq, ",compress_mode=%s", "user");
|
||||
}
|
||||
#endif
|
||||
|
||||
static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
|
||||
{
|
||||
|
|
@ -1676,6 +1689,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
|
|||
else if (F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_OFF)
|
||||
seq_printf(seq, ",background_gc=%s", "off");
|
||||
|
||||
if (test_opt(sbi, GC_MERGE))
|
||||
seq_puts(seq, ",gc_merge");
|
||||
|
||||
if (test_opt(sbi, DISABLE_ROLL_FORWARD))
|
||||
seq_puts(seq, ",disable_roll_forward");
|
||||
if (test_opt(sbi, NORECOVERY))
|
||||
|
|
@ -1827,6 +1843,7 @@ static void default_options(struct f2fs_sb_info *sbi)
|
|||
set_opt(sbi, EXTENT_CACHE);
|
||||
set_opt(sbi, NOHEAP);
|
||||
clear_opt(sbi, DISABLE_CHECKPOINT);
|
||||
set_opt(sbi, MERGE_CHECKPOINT);
|
||||
F2FS_OPTION(sbi).unusable_cap = 0;
|
||||
sbi->sb->s_flags |= SB_LAZYTIME;
|
||||
set_opt(sbi, FLUSH_MERGE);
|
||||
|
|
@ -1868,7 +1885,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
|
|||
|
||||
while (!f2fs_time_over(sbi, DISABLE_TIME)) {
|
||||
down_write(&sbi->gc_lock);
|
||||
err = f2fs_gc(sbi, true, false, NULL_SEGNO);
|
||||
err = f2fs_gc(sbi, true, false, false, NULL_SEGNO);
|
||||
if (err == -ENODATA) {
|
||||
err = 0;
|
||||
break;
|
||||
|
|
@ -1879,7 +1896,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
|
|||
|
||||
ret = sync_filesystem(sbi->sb);
|
||||
if (ret || err) {
|
||||
err = ret ? ret: err;
|
||||
err = ret ? ret : err;
|
||||
goto restore_flag;
|
||||
}
|
||||
|
||||
|
|
@ -1928,8 +1945,9 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|||
struct f2fs_mount_info org_mount_opt;
|
||||
unsigned long old_sb_flags;
|
||||
int err;
|
||||
bool need_restart_gc = false;
|
||||
bool need_stop_gc = false;
|
||||
bool need_restart_gc = false, need_stop_gc = false;
|
||||
bool need_restart_ckpt = false, need_stop_ckpt = false;
|
||||
bool need_restart_flush = false, need_stop_flush = false;
|
||||
bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE);
|
||||
bool disable_checkpoint = test_opt(sbi, DISABLE_CHECKPOINT);
|
||||
bool no_io_align = !F2FS_IO_ALIGNED(sbi);
|
||||
|
|
@ -2038,7 +2056,8 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|||
* option. Also sync the filesystem.
|
||||
*/
|
||||
if ((*flags & SB_RDONLY) ||
|
||||
F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_OFF) {
|
||||
(F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_OFF &&
|
||||
!test_opt(sbi, GC_MERGE))) {
|
||||
if (sbi->gc_thread) {
|
||||
f2fs_stop_gc_thread(sbi);
|
||||
need_restart_gc = true;
|
||||
|
|
@ -2060,18 +2079,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|||
clear_sbi_flag(sbi, SBI_IS_CLOSE);
|
||||
}
|
||||
|
||||
if (checkpoint_changed) {
|
||||
if (test_opt(sbi, DISABLE_CHECKPOINT)) {
|
||||
err = f2fs_disable_checkpoint(sbi);
|
||||
if (err)
|
||||
goto restore_gc;
|
||||
} else {
|
||||
f2fs_enable_checkpoint(sbi);
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_opt(sbi, DISABLE_CHECKPOINT) &&
|
||||
test_opt(sbi, MERGE_CHECKPOINT)) {
|
||||
if ((*flags & SB_RDONLY) || test_opt(sbi, DISABLE_CHECKPOINT) ||
|
||||
!test_opt(sbi, MERGE_CHECKPOINT)) {
|
||||
f2fs_stop_ckpt_thread(sbi);
|
||||
need_restart_ckpt = true;
|
||||
} else {
|
||||
err = f2fs_start_ckpt_thread(sbi);
|
||||
if (err) {
|
||||
f2fs_err(sbi,
|
||||
|
|
@ -2079,8 +2091,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|||
err);
|
||||
goto restore_gc;
|
||||
}
|
||||
} else {
|
||||
f2fs_stop_ckpt_thread(sbi);
|
||||
need_stop_ckpt = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2090,11 +2101,24 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|||
if ((*flags & SB_RDONLY) || !test_opt(sbi, FLUSH_MERGE)) {
|
||||
clear_opt(sbi, FLUSH_MERGE);
|
||||
f2fs_destroy_flush_cmd_control(sbi, false);
|
||||
need_restart_flush = true;
|
||||
} else {
|
||||
err = f2fs_create_flush_cmd_control(sbi);
|
||||
if (err)
|
||||
goto restore_gc;
|
||||
goto restore_ckpt;
|
||||
need_stop_flush = true;
|
||||
}
|
||||
|
||||
if (checkpoint_changed) {
|
||||
if (test_opt(sbi, DISABLE_CHECKPOINT)) {
|
||||
err = f2fs_disable_checkpoint(sbi);
|
||||
if (err)
|
||||
goto restore_flush;
|
||||
} else {
|
||||
f2fs_enable_checkpoint(sbi);
|
||||
}
|
||||
}
|
||||
|
||||
skip:
|
||||
#ifdef CONFIG_QUOTA
|
||||
/* Release old quota file names */
|
||||
|
|
@ -2109,6 +2133,21 @@ skip:
|
|||
adjust_unusable_cap_perc(sbi);
|
||||
*flags = (*flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME);
|
||||
return 0;
|
||||
restore_flush:
|
||||
if (need_restart_flush) {
|
||||
if (f2fs_create_flush_cmd_control(sbi))
|
||||
f2fs_warn(sbi, "background flush thread has stopped");
|
||||
} else if (need_stop_flush) {
|
||||
clear_opt(sbi, FLUSH_MERGE);
|
||||
f2fs_destroy_flush_cmd_control(sbi, false);
|
||||
}
|
||||
restore_ckpt:
|
||||
if (need_restart_ckpt) {
|
||||
if (f2fs_start_ckpt_thread(sbi))
|
||||
f2fs_warn(sbi, "background ckpt thread has stopped");
|
||||
} else if (need_stop_ckpt) {
|
||||
f2fs_stop_ckpt_thread(sbi);
|
||||
}
|
||||
restore_gc:
|
||||
if (need_restart_gc) {
|
||||
if (f2fs_start_gc_thread(sbi))
|
||||
|
|
@ -3722,7 +3761,7 @@ try_onemore:
|
|||
sbi->iostat_period_ms = DEFAULT_IOSTAT_PERIOD_MS;
|
||||
|
||||
for (i = 0; i < NR_PAGE_TYPE; i++) {
|
||||
int n = (i == META) ? 1: NR_TEMP_TYPE;
|
||||
int n = (i == META) ? 1 : NR_TEMP_TYPE;
|
||||
int j;
|
||||
|
||||
sbi->write_io[i] =
|
||||
|
|
@ -3836,7 +3875,7 @@ try_onemore:
|
|||
|
||||
/* setup checkpoint request control and start checkpoint issue thread */
|
||||
f2fs_init_ckpt_req_control(sbi);
|
||||
if (!test_opt(sbi, DISABLE_CHECKPOINT) &&
|
||||
if (!f2fs_readonly(sb) && !test_opt(sbi, DISABLE_CHECKPOINT) &&
|
||||
test_opt(sbi, MERGE_CHECKPOINT)) {
|
||||
err = f2fs_start_ckpt_thread(sbi);
|
||||
if (err) {
|
||||
|
|
@ -3932,10 +3971,18 @@ try_onemore:
|
|||
* previous checkpoint was not done by clean system shutdown.
|
||||
*/
|
||||
if (f2fs_hw_is_readonly(sbi)) {
|
||||
if (!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG))
|
||||
f2fs_err(sbi, "Need to recover fsync data, but write access unavailable");
|
||||
else
|
||||
f2fs_info(sbi, "write access unavailable, skipping recovery");
|
||||
if (!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
|
||||
err = f2fs_recover_fsync_data(sbi, true);
|
||||
if (err > 0) {
|
||||
err = -EROFS;
|
||||
f2fs_err(sbi, "Need to recover fsync data, but "
|
||||
"write access unavailable, please try "
|
||||
"mount w/ disable_roll_forward or norecovery");
|
||||
}
|
||||
if (err < 0)
|
||||
goto free_meta;
|
||||
}
|
||||
f2fs_info(sbi, "write access unavailable, skipping recovery");
|
||||
goto reset_checkpoint;
|
||||
}
|
||||
|
||||
|
|
@ -3992,7 +4039,8 @@ reset_checkpoint:
|
|||
* If filesystem is not mounted as read-only then
|
||||
* do start the gc_thread.
|
||||
*/
|
||||
if (F2FS_OPTION(sbi).bggc_mode != BGGC_MODE_OFF && !f2fs_readonly(sb)) {
|
||||
if ((F2FS_OPTION(sbi).bggc_mode != BGGC_MODE_OFF ||
|
||||
test_opt(sbi, GC_MERGE)) && !f2fs_readonly(sb)) {
|
||||
/* After POR, we can run background GC thread.*/
|
||||
err = f2fs_start_gc_thread(sbi);
|
||||
if (err)
|
||||
|
|
|
|||
|
|
@ -92,6 +92,13 @@ static ssize_t free_segments_show(struct f2fs_attr *a,
|
|||
(unsigned long long)(free_segments(sbi)));
|
||||
}
|
||||
|
||||
static ssize_t ovp_segments_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%llu\n",
|
||||
(unsigned long long)(overprovision_segments(sbi)));
|
||||
}
|
||||
|
||||
static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
|
|
@ -659,6 +666,7 @@ F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, node_io_flag, node_io_flag);
|
|||
F2FS_RW_ATTR(CPRC_INFO, ckpt_req_control, ckpt_thread_ioprio, ckpt_thread_ioprio);
|
||||
F2FS_GENERAL_RO_ATTR(dirty_segments);
|
||||
F2FS_GENERAL_RO_ATTR(free_segments);
|
||||
F2FS_GENERAL_RO_ATTR(ovp_segments);
|
||||
F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes);
|
||||
F2FS_GENERAL_RO_ATTR(features);
|
||||
F2FS_GENERAL_RO_ATTR(current_reserved_blocks);
|
||||
|
|
@ -748,6 +756,7 @@ static struct attribute *f2fs_attrs[] = {
|
|||
ATTR_LIST(ckpt_thread_ioprio),
|
||||
ATTR_LIST(dirty_segments),
|
||||
ATTR_LIST(free_segments),
|
||||
ATTR_LIST(ovp_segments),
|
||||
ATTR_LIST(unusable),
|
||||
ATTR_LIST(lifetime_write_kbytes),
|
||||
ATTR_LIST(features),
|
||||
|
|
|
|||
|
|
@ -486,6 +486,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
|||
f2fs_wait_on_page_writeback(xpage, NODE, true, true);
|
||||
} else {
|
||||
struct dnode_of_data dn;
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, new_nid);
|
||||
xpage = f2fs_new_node_page(&dn, XATTR_NODE_OFFSET);
|
||||
if (IS_ERR(xpage)) {
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ struct f2fs_checkpoint {
|
|||
unsigned char alloc_type[MAX_ACTIVE_LOGS];
|
||||
|
||||
/* SIT and NAT version bitmap */
|
||||
unsigned char sit_nat_version_bitmap[1];
|
||||
unsigned char sit_nat_version_bitmap[];
|
||||
} __packed;
|
||||
|
||||
#define CP_CHKSUM_OFFSET 4092 /* default chksum offset in checkpoint */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue