Commit aea6bf90 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull f2fs updates from Jaegeuk Kim:
 "In this cycle, we introduce a bigger page size support by changing the
  internal f2fs's block size aligned to the page size. We also continue
  to improve zoned block device support regarding the power off
  recovery. As usual, there are some bug fixes regarding the error
  handling routines in compression and ioctl.

  Enhancements:
   - Support Block Size == Page Size
   - let f2fs_precache_extents() traverses in file range
   - stop iterating f2fs_map_block if hole exists
   - preload extent_cache for POSIX_FADV_WILLNEED
   - compress: fix to avoid fragment w/ OPU during f2fs_ioc_compress_file()

  Bug fixes:
   - do not return EFSCORRUPTED, but try to run online repair
   - finish previous checkpoints before returning from remount
   - fix error handling of __get_node_page and __f2fs_build_free_nids
   - clean up zones when not successfully unmounted
   - fix to initialize map.m_pblk in f2fs_precache_extents()
   - fix to drop meta_inode's page cache in f2fs_put_super()
   - set the default compress_level on ioctl
   - fix to avoid use-after-free on dic
   - fix to avoid redundant compress extension
   - do sanity check on cluster when CONFIG_F2FS_CHECK_FS is on
   - fix deadloop in f2fs_write_cache_pages()"

* tag 'f2fs-for-6.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs:
  f2fs: finish previous checkpoints before returning from remount
  f2fs: fix error handling of __get_node_page
  f2fs: do not return EFSCORRUPTED, but try to run online repair
  f2fs: fix error path of __f2fs_build_free_nids
  f2fs: Clean up errors in segment.h
  f2fs: clean up zones when not successfully unmounted
  f2fs: let f2fs_precache_extents() traverses in file range
  f2fs: avoid format-overflow warning
  f2fs: fix to initialize map.m_pblk in f2fs_precache_extents()
  f2fs: Support Block Size == Page Size
  f2fs: stop iterating f2fs_map_block if hole exists
  f2fs: preload extent_cache for POSIX_FADV_WILLNEED
  f2fs: set the default compress_level on ioctl
  f2fs: compress: fix to avoid fragment w/ OPU during f2fs_ioc_compress_file()
  f2fs: fix to drop meta_inode's page cache in f2fs_put_super()
  f2fs: split initial and dynamic conditions for extent_cache
  f2fs: compress: fix to avoid redundant compress extension
  f2fs: compress: do sanity check on cluster when CONFIG_F2FS_CHECK_FS is on
  f2fs: compress: fix to avoid use-after-free on dic
  f2fs: compress: fix deadloop in f2fs_write_cache_pages()
parents c9b93caf 1e7bef5f
Loading
Loading
Loading
Loading
+35 −28
Original line number Diff line number Diff line
@@ -893,14 +893,15 @@ static bool cluster_has_invalid_data(struct compress_ctx *cc)

bool f2fs_sanity_check_cluster(struct dnode_of_data *dn)
{
#ifdef CONFIG_F2FS_CHECK_FS
	struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
	unsigned int cluster_size = F2FS_I(dn->inode)->i_cluster_size;
	bool compressed = dn->data_blkaddr == COMPRESS_ADDR;
	int cluster_end = 0;
	unsigned int count;
	int i;
	char *reason = "";

	if (!compressed)
	if (dn->data_blkaddr != COMPRESS_ADDR)
		return false;

	/* [..., COMPR_ADDR, ...] */
@@ -909,7 +910,7 @@ bool f2fs_sanity_check_cluster(struct dnode_of_data *dn)
		goto out;
	}

	for (i = 1; i < cluster_size; i++) {
	for (i = 1, count = 1; i < cluster_size; i++, count++) {
		block_t blkaddr = data_blkaddr(dn->inode, dn->node_page,
							dn->ofs_in_node + i);

@@ -929,19 +930,42 @@ bool f2fs_sanity_check_cluster(struct dnode_of_data *dn)
			goto out;
		}
	}

	f2fs_bug_on(F2FS_I_SB(dn->inode), count != cluster_size &&
		!is_inode_flag_set(dn->inode, FI_COMPRESS_RELEASED));

	return false;
out:
	f2fs_warn(sbi, "access invalid cluster, ino:%lu, nid:%u, ofs_in_node:%u, reason:%s",
			dn->inode->i_ino, dn->nid, dn->ofs_in_node, reason);
	set_sbi_flag(sbi, SBI_NEED_FSCK);
	return true;
#else
	return false;
#endif
}

static int __f2fs_get_cluster_blocks(struct inode *inode,
					struct dnode_of_data *dn)
{
	unsigned int cluster_size = F2FS_I(inode)->i_cluster_size;
	int count, i;

	for (i = 1, count = 1; i < cluster_size; i++) {
		block_t blkaddr = data_blkaddr(dn->inode, dn->node_page,
							dn->ofs_in_node + i);

		if (__is_valid_data_blkaddr(blkaddr))
			count++;
	}

	return count;
}

static int __f2fs_cluster_blocks(struct inode *inode,
				unsigned int cluster_idx, bool compr)
				unsigned int cluster_idx, bool compr_blks)
{
	struct dnode_of_data dn;
	unsigned int cluster_size = F2FS_I(inode)->i_cluster_size;
	unsigned int start_idx = cluster_idx <<
				F2FS_I(inode)->i_log_cluster_size;
	int ret;
@@ -956,31 +980,14 @@ static int __f2fs_cluster_blocks(struct inode *inode,

	if (f2fs_sanity_check_cluster(&dn)) {
		ret = -EFSCORRUPTED;
		f2fs_handle_error(F2FS_I_SB(inode), ERROR_CORRUPTED_CLUSTER);
		goto fail;
	}

	if (dn.data_blkaddr == COMPRESS_ADDR) {
		int i;

		if (compr_blks)
			ret = __f2fs_get_cluster_blocks(inode, &dn);
		else
			ret = 1;
		for (i = 1; i < cluster_size; i++) {
			block_t blkaddr;

			blkaddr = data_blkaddr(dn.inode,
					dn.node_page, dn.ofs_in_node + i);
			if (compr) {
				if (__is_valid_data_blkaddr(blkaddr))
					ret++;
			} else {
				if (blkaddr != NULL_ADDR)
					ret++;
			}
		}

		f2fs_bug_on(F2FS_I_SB(inode),
			!compr && ret != cluster_size &&
			!is_inode_flag_set(inode, FI_COMPRESS_RELEASED));
	}
fail:
	f2fs_put_dnode(&dn);
@@ -993,7 +1000,7 @@ static int f2fs_compressed_blocks(struct compress_ctx *cc)
	return __f2fs_cluster_blocks(cc->inode, cc->cluster_idx, true);
}

/* return # of valid blocks in compressed cluster */
/* return whether cluster is compressed one or not */
int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index)
{
	return __f2fs_cluster_blocks(inode,
@@ -1976,7 +1983,7 @@ void f2fs_destroy_compress_inode(struct f2fs_sb_info *sbi)
int f2fs_init_page_array_cache(struct f2fs_sb_info *sbi)
{
	dev_t dev = sbi->sb->s_bdev->bd_dev;
	char slab_name[32];
	char slab_name[35];

	if (!f2fs_sb_has_compression(sbi))
		return 0;
+28 −7
Original line number Diff line number Diff line
@@ -1690,9 +1690,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag)
			map->m_flags |= F2FS_MAP_NEW;
	} else if (is_hole) {
		if (f2fs_compressed_file(inode) &&
		    f2fs_sanity_check_cluster(&dn) &&
		    (flag != F2FS_GET_BLOCK_FIEMAP ||
		     IS_ENABLED(CONFIG_F2FS_CHECK_FS))) {
		    f2fs_sanity_check_cluster(&dn)) {
			err = -EFSCORRUPTED;
			f2fs_handle_error(sbi,
					ERROR_CORRUPTED_CLUSTER);
@@ -2344,8 +2342,10 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
		f2fs_wait_on_block_writeback(inode, blkaddr);

		if (f2fs_load_compressed_page(sbi, page, blkaddr)) {
			if (atomic_dec_and_test(&dic->remaining_pages))
			if (atomic_dec_and_test(&dic->remaining_pages)) {
				f2fs_decompress_cluster(dic, true);
				break;
			}
			continue;
		}

@@ -2665,6 +2665,11 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio)
		return true;
	if (f2fs_is_atomic_file(inode))
		return true;
	/* rewrite low ratio compress data w/ OPU mode to avoid fragmentation */
	if (f2fs_compressed_file(inode) &&
		F2FS_OPTION(sbi).compress_mode == COMPR_MODE_USER &&
		is_inode_flag_set(inode, FI_ENABLE_COMPRESS))
		return true;

	/* swap file is migrating in aligned write mode */
	if (is_inode_flag_set(inode, FI_ALIGNED_WRITE))
@@ -3023,7 +3028,8 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
{
	int ret = 0;
	int done = 0, retry = 0;
	struct page *pages[F2FS_ONSTACK_PAGES];
	struct page *pages_local[F2FS_ONSTACK_PAGES];
	struct page **pages = pages_local;
	struct folio_batch fbatch;
	struct f2fs_sb_info *sbi = F2FS_M_SB(mapping);
	struct bio *bio = NULL;
@@ -3047,6 +3053,7 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
#endif
	int nr_folios, p, idx;
	int nr_pages;
	unsigned int max_pages = F2FS_ONSTACK_PAGES;
	pgoff_t index;
	pgoff_t end;		/* Inclusive */
	pgoff_t done_index;
@@ -3056,6 +3063,15 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
	int submitted = 0;
	int i;

#ifdef CONFIG_F2FS_FS_COMPRESSION
	if (f2fs_compressed_file(inode) &&
		1 << cc.log_cluster_size > F2FS_ONSTACK_PAGES) {
		pages = f2fs_kzalloc(sbi, sizeof(struct page *) <<
				cc.log_cluster_size, GFP_NOFS | __GFP_NOFAIL);
		max_pages = 1 << cc.log_cluster_size;
	}
#endif

	folio_batch_init(&fbatch);

	if (get_dirty_pages(mapping->host) <=
@@ -3101,7 +3117,7 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
add_more:
			pages[nr_pages] = folio_page(folio, idx);
			folio_get(folio);
			if (++nr_pages == F2FS_ONSTACK_PAGES) {
			if (++nr_pages == max_pages) {
				index = folio->index + idx + 1;
				folio_batch_release(&fbatch);
				goto write;
@@ -3283,6 +3299,11 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
	if (bio)
		f2fs_submit_merged_ipu_write(sbi, &bio, NULL);

#ifdef CONFIG_F2FS_FS_COMPRESSION
	if (pages != pages_local)
		kfree(pages);
#endif

	return ret;
}

@@ -4055,7 +4076,7 @@ static int check_swap_activate(struct swap_info_struct *sis,
	sis->highest_bit = cur_lblock - 1;
out:
	if (not_aligned)
		f2fs_warn(sbi, "Swapfile (%u) is not align to section: 1) creat(), 2) ioctl(F2FS_IOC_SET_PIN_FILE), 3) fallocate(%u * N)",
		f2fs_warn(sbi, "Swapfile (%u) is not align to section: 1) creat(), 2) ioctl(F2FS_IOC_SET_PIN_FILE), 3) fallocate(%lu * N)",
			  not_aligned, blks_per_sec * F2FS_BLKSIZE);
	return ret;
}
+21 −32
Original line number Diff line number Diff line
@@ -74,40 +74,14 @@ static void __set_extent_info(struct extent_info *ei,
	}
}

static bool __may_read_extent_tree(struct inode *inode)
{
	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);

	if (!test_opt(sbi, READ_EXTENT_CACHE))
		return false;
	if (is_inode_flag_set(inode, FI_NO_EXTENT))
		return false;
	if (is_inode_flag_set(inode, FI_COMPRESSED_FILE) &&
			 !f2fs_sb_has_readonly(sbi))
		return false;
	return S_ISREG(inode->i_mode);
}

static bool __may_age_extent_tree(struct inode *inode)
{
	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);

	if (!test_opt(sbi, AGE_EXTENT_CACHE))
		return false;
	if (is_inode_flag_set(inode, FI_COMPRESSED_FILE))
		return false;
	if (file_is_cold(inode))
		return false;

	return S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode);
}

static bool __init_may_extent_tree(struct inode *inode, enum extent_type type)
{
	if (type == EX_READ)
		return __may_read_extent_tree(inode);
	else if (type == EX_BLOCK_AGE)
		return __may_age_extent_tree(inode);
		return test_opt(F2FS_I_SB(inode), READ_EXTENT_CACHE) &&
			S_ISREG(inode->i_mode);
	if (type == EX_BLOCK_AGE)
		return test_opt(F2FS_I_SB(inode), AGE_EXTENT_CACHE) &&
			(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode));
	return false;
}

@@ -120,7 +94,22 @@ static bool __may_extent_tree(struct inode *inode, enum extent_type type)
	if (list_empty(&F2FS_I_SB(inode)->s_list))
		return false;

	return __init_may_extent_tree(inode, type);
	if (!__init_may_extent_tree(inode, type))
		return false;

	if (type == EX_READ) {
		if (is_inode_flag_set(inode, FI_NO_EXTENT))
			return false;
		if (is_inode_flag_set(inode, FI_COMPRESSED_FILE) &&
				 !f2fs_sb_has_readonly(F2FS_I_SB(inode)))
			return false;
	} else if (type == EX_BLOCK_AGE) {
		if (is_inode_flag_set(inode, FI_COMPRESSED_FILE))
			return false;
		if (file_is_cold(inode))
			return false;
	}
	return true;
}

static void __try_update_largest_extent(struct extent_tree *et,
+15 −2
Original line number Diff line number Diff line
@@ -3258,11 +3258,12 @@ int f2fs_precache_extents(struct inode *inode)
		return -EOPNOTSUPP;

	map.m_lblk = 0;
	map.m_pblk = 0;
	map.m_next_pgofs = NULL;
	map.m_next_extent = &m_next_extent;
	map.m_seg_type = NO_CHECK_TYPE;
	map.m_may_create = false;
	end = max_file_blocks(inode);
	end = F2FS_BLK_ALIGN(i_size_read(inode));

	while (map.m_lblk < end) {
		map.m_len = end - map.m_lblk;
@@ -3270,7 +3271,7 @@ int f2fs_precache_extents(struct inode *inode)
		f2fs_down_write(&fi->i_gc_rwsem[WRITE]);
		err = f2fs_map_blocks(inode, &map, F2FS_GET_BLOCK_PRECACHE);
		f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
		if (err)
		if (err || !map.m_len)
			return err;

		map.m_lblk = m_next_extent;
@@ -4005,6 +4006,15 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
	F2FS_I(inode)->i_compress_algorithm = option.algorithm;
	F2FS_I(inode)->i_log_cluster_size = option.log_cluster_size;
	F2FS_I(inode)->i_cluster_size = BIT(option.log_cluster_size);
	/* Set default level */
	if (F2FS_I(inode)->i_compress_algorithm == COMPRESS_ZSTD)
		F2FS_I(inode)->i_compress_level = F2FS_ZSTD_DEFAULT_CLEVEL;
	else
		F2FS_I(inode)->i_compress_level = 0;
	/* Adjust mount option level */
	if (option.algorithm == F2FS_OPTION(sbi).compress_algorithm &&
	    F2FS_OPTION(sbi).compress_level)
		F2FS_I(inode)->i_compress_level = F2FS_OPTION(sbi).compress_level;
	f2fs_mark_inode_dirty_sync(inode, true);

	if (!f2fs_is_compress_backend_ready(inode))
@@ -4849,6 +4859,9 @@ static int f2fs_file_fadvise(struct file *filp, loff_t offset, loff_t len,
		filp->f_mode &= ~FMODE_RANDOM;
		spin_unlock(&filp->f_lock);
		return 0;
	} else if (advice == POSIX_FADV_WILLNEED && offset == 0) {
		/* Load extent cache at the first readahead. */
		f2fs_precache_extents(inode);
	}

	err = generic_fadvise(filp, offset, len, advice);
+1 −1
Original line number Diff line number Diff line
@@ -315,7 +315,7 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page)
			f2fs_has_inline_xattr(inode) &&
			(!fi->i_inline_xattr_size ||
			fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) {
			f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, max: %zu",
			f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, max: %lu",
				  __func__, inode->i_ino, fi->i_inline_xattr_size,
				  MAX_INLINE_XATTR_SIZE);
			return false;
Loading