Commit 70d201a4 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull f2fs update from Jaegeuk Kim:
 "In this series, we've some progress to support Zoned block device
  regarding to the power-cut recovery flow and enabling
  checkpoint=disable feature which is essential for Android OTA.

  Other than that, some patches touched sysfs entries and tracepoints
  which are minor, while several bug fixes on error handlers and
  compression flows are good to improve the overall stability.

  Enhancements:
   - enable checkpoint=disable for zoned block device
   - sysfs entries such as discard status, discard_io_aware, dir_level
   - tracepoints such as f2fs_vm_page_mkwrite(), f2fs_rename(),
     f2fs_new_inode()
   - use shared inode lock during f2fs_fiemap() and f2fs_seek_block()

  Bug fixes:
   - address some power-cut recovery issues on zoned block device
   - handle errors and logics on do_garbage_collect(),
     f2fs_reserve_new_block(), f2fs_move_file_range(),
     f2fs_recover_xattr_data()
   - don't set FI_PREALLOCATED_ALL for partial write
   - fix to update iostat correctly in f2fs_filemap_fault()
   - fix to wait on block writeback for post_read case
   - fix to tag gcing flag on page during block migration
   - restrict max filesize for 16K f2fs
   - fix to avoid dirent corruption
   - explicitly null-terminate the xattr list

  There are also several clean-up patches to remove dead codes and
  better readability"

* tag 'f2fs-for-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (33 commits)
  f2fs: show more discard status by sysfs
  f2fs: Add error handling for negative returns from do_garbage_collect
  f2fs: Constrain the modification range of dir_level in the sysfs
  f2fs: Use wait_event_freezable_timeout() for freezable kthread
  f2fs: fix to check return value of f2fs_recover_xattr_data
  f2fs: don't set FI_PREALLOCATED_ALL for partial write
  f2fs: fix to update iostat correctly in f2fs_filemap_fault()
  f2fs: fix to check compress file in f2fs_move_file_range()
  f2fs: fix to wait on block writeback for post_read case
  f2fs: fix to tag gcing flag on page during block migration
  f2fs: add tracepoint for f2fs_vm_page_mkwrite()
  f2fs: introduce f2fs_invalidate_internal_cache() for cleanup
  f2fs: update blkaddr in __set_data_blkaddr() for cleanup
  f2fs: introduce get_dnode_addr() to clean up codes
  f2fs: delete obsolete FI_DROP_CACHE
  f2fs: delete obsolete FI_FIRST_BLOCK_WRITTEN
  f2fs: Restrict max filesize for 16K f2fs
  f2fs: let's finish or reset zones all the time
  f2fs: check write pointers when checkpoint=disable
  f2fs: fix write pointers on zoned device after roll forward
  ...
parents 6a31658a c3c2d45b
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -498,6 +498,21 @@ Description: Show status of f2fs checkpoint in real time.
		CP_RESIZEFS_FLAG		0x00004000
		=============================== ==============================

What:		/sys/fs/f2fs/<disk>/stat/issued_discard
Date:		December 2023
Contact:	"Zhiguo Niu" <zhiguo.niu@unisoc.com>
Description:	Shows the number of issued discard.

What:		/sys/fs/f2fs/<disk>/stat/queued_discard
Date:		December 2023
Contact:	"Zhiguo Niu" <zhiguo.niu@unisoc.com>
Description:	Shows the number of queued discard.

What:		/sys/fs/f2fs/<disk>/stat/undiscard_blks
Date:		December 2023
Contact:	"Zhiguo Niu" <zhiguo.niu@unisoc.com>
Description:	Shows the total number of undiscard blocks.

What:		/sys/fs/f2fs/<disk>/ckpt_thread_ioprio
Date:		January 2021
Contact:	"Daeho Jeong" <daehojeong@google.com>
@@ -740,3 +755,9 @@ Description: When compress cache is on, it controls cached page
		If cached page percent exceed threshold, then deny caching compress page.
		The value should be in range of (0, 100], by default it was initialized
		as 20(%).

What:		/sys/fs/f2fs/<disk>/discard_io_aware
Date:		November 2023
Contact:	"Chao Yu" <chao@kernel.org>
Description:	It controls to enable/disable IO aware feature for background discard.
		By default, the value is 1 which indicates IO aware is on.
+3 −3
Original line number Diff line number Diff line
@@ -1036,8 +1036,10 @@ static void set_cluster_dirty(struct compress_ctx *cc)
	int i;

	for (i = 0; i < cc->cluster_size; i++)
		if (cc->rpages[i])
		if (cc->rpages[i]) {
			set_page_dirty(cc->rpages[i]);
			set_page_private_gcing(cc->rpages[i]);
		}
}

static int prepare_compress_overwrite(struct compress_ctx *cc,
@@ -1369,8 +1371,6 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
	add_compr_block_stat(inode, cc->valid_nr_cpages);

	set_inode_flag(cc->inode, FI_APPEND_WRITE);
	if (cc->cluster_idx == 0)
		set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN);

	f2fs_put_dnode(&dn);
	if (quota_inode)
+17 −31
Original line number Diff line number Diff line
@@ -1179,18 +1179,12 @@ static int f2fs_submit_page_read(struct inode *inode, struct page *page,
	return 0;
}

static void __set_data_blkaddr(struct dnode_of_data *dn)
static void __set_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr)
{
	struct f2fs_node *rn = F2FS_NODE(dn->node_page);
	__le32 *addr_array;
	int base = 0;
	__le32 *addr = get_dnode_addr(dn->inode, dn->node_page);

	if (IS_INODE(dn->node_page) && f2fs_has_extra_attr(dn->inode))
		base = get_extra_isize(dn->inode);

	/* Get physical address of data block */
	addr_array = blkaddr_in_node(rn);
	addr_array[base + dn->ofs_in_node] = cpu_to_le32(dn->data_blkaddr);
	dn->data_blkaddr = blkaddr;
	addr[dn->ofs_in_node] = cpu_to_le32(dn->data_blkaddr);
}

/*
@@ -1199,18 +1193,17 @@ static void __set_data_blkaddr(struct dnode_of_data *dn)
 *  ->node_page
 *    update block addresses in the node page
 */
void f2fs_set_data_blkaddr(struct dnode_of_data *dn)
void f2fs_set_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr)
{
	f2fs_wait_on_page_writeback(dn->node_page, NODE, true, true);
	__set_data_blkaddr(dn);
	__set_data_blkaddr(dn, blkaddr);
	if (set_page_dirty(dn->node_page))
		dn->node_changed = true;
}

void f2fs_update_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr)
{
	dn->data_blkaddr = blkaddr;
	f2fs_set_data_blkaddr(dn);
	f2fs_set_data_blkaddr(dn, blkaddr);
	f2fs_update_read_extent_cache(dn);
}

@@ -1237,8 +1230,7 @@ int f2fs_reserve_new_blocks(struct dnode_of_data *dn, blkcnt_t count)
		block_t blkaddr = f2fs_data_blkaddr(dn);

		if (blkaddr == NULL_ADDR) {
			dn->data_blkaddr = NEW_ADDR;
			__set_data_blkaddr(dn);
			__set_data_blkaddr(dn, NEW_ADDR);
			count--;
		}
	}
@@ -1492,11 +1484,9 @@ static int __allocate_data_block(struct dnode_of_data *dn, int seg_type)
	old_blkaddr = dn->data_blkaddr;
	f2fs_allocate_data_block(sbi, NULL, old_blkaddr, &dn->data_blkaddr,
				&sum, seg_type, NULL);
	if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) {
		invalidate_mapping_pages(META_MAPPING(sbi),
					old_blkaddr, old_blkaddr);
		f2fs_invalidate_compress_page(sbi, old_blkaddr);
	}
	if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
		f2fs_invalidate_internal_cache(sbi, old_blkaddr);

	f2fs_update_data_blkaddr(dn, dn->data_blkaddr);
	return 0;
}
@@ -1992,7 +1982,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
	if (ret)
		return ret;

	inode_lock(inode);
	inode_lock_shared(inode);

	maxbytes = max_file_blocks(inode) << F2FS_BLKSIZE_BITS;
	if (start > maxbytes) {
@@ -2112,7 +2102,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
	if (ret == 1)
		ret = 0;

	inode_unlock(inode);
	inode_unlock_shared(inode);
	return ret;
}

@@ -2566,9 +2556,6 @@ int f2fs_encrypt_one_page(struct f2fs_io_info *fio)

	page = fio->compressed_page ? fio->compressed_page : fio->page;

	/* wait for GCed page writeback via META_MAPPING */
	f2fs_wait_on_block_writeback(inode, fio->old_blkaddr);

	if (fscrypt_inode_uses_inline_crypto(inode))
		return 0;

@@ -2755,6 +2742,10 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
		goto out_writepage;
	}

	/* wait for GCed page writeback via META_MAPPING */
	if (fio->post_read)
		f2fs_wait_on_block_writeback(inode, fio->old_blkaddr);

	/*
	 * If current allocation needs SSR,
	 * it had better in-place writes for updated data.
@@ -2810,8 +2801,6 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
	f2fs_outplace_write_data(&dn, fio);
	trace_f2fs_do_write_data_page(page, OPU);
	set_inode_flag(inode, FI_APPEND_WRITE);
	if (page->index == 0)
		set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN);
out_writepage:
	f2fs_put_dnode(&dn);
out:
@@ -2894,9 +2883,6 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,

	zero_user_segment(page, offset, PAGE_SIZE);
write:
	if (f2fs_is_drop_cache(inode))
		goto out;

	/* Dentry/quota blocks are controlled by checkpoint */
	if (S_ISDIR(inode->i_mode) || quota_inode) {
		/*
+30 −16
Original line number Diff line number Diff line
@@ -374,6 +374,12 @@ enum {
	MAX_DPOLICY,
};

enum {
	DPOLICY_IO_AWARE_DISABLE,	/* force to not be aware of IO */
	DPOLICY_IO_AWARE_ENABLE,	/* force to be aware of IO */
	DPOLICY_IO_AWARE_MAX,
};

struct discard_policy {
	int type;			/* type of discard */
	unsigned int min_interval;	/* used for candidates exist */
@@ -406,6 +412,7 @@ struct discard_cmd_control {
	unsigned int discard_urgent_util;	/* utilization which issue discard proactively */
	unsigned int discard_granularity;	/* discard granularity */
	unsigned int max_ordered_discard;	/* maximum discard granularity issued by lba order */
	unsigned int discard_io_aware;		/* io_aware policy */
	unsigned int undiscard_blks;		/* # of undiscard blocks */
	unsigned int next_pos;			/* next discard position */
	atomic_t issued_discard;		/* # of issued discard */
@@ -774,8 +781,6 @@ enum {
	FI_UPDATE_WRITE,	/* inode has in-place-update data */
	FI_NEED_IPU,		/* used for ipu per file */
	FI_ATOMIC_FILE,		/* indicate atomic file */
	FI_FIRST_BLOCK_WRITTEN,	/* indicate #0 data block was written */
	FI_DROP_CACHE,		/* drop dirty page cache */
	FI_DATA_EXIST,		/* indicate data exists */
	FI_INLINE_DOTS,		/* indicate inline dot dentries */
	FI_SKIP_WRITES,		/* should skip data page writeback */
@@ -3272,22 +3277,13 @@ static inline bool f2fs_is_cow_file(struct inode *inode)
	return is_inode_flag_set(inode, FI_COW_FILE);
}

static inline bool f2fs_is_first_block_written(struct inode *inode)
{
	return is_inode_flag_set(inode, FI_FIRST_BLOCK_WRITTEN);
}

static inline bool f2fs_is_drop_cache(struct inode *inode)
{
	return is_inode_flag_set(inode, FI_DROP_CACHE);
}

static inline __le32 *get_dnode_addr(struct inode *inode,
					struct page *node_page);
static inline void *inline_data_addr(struct inode *inode, struct page *page)
{
	struct f2fs_inode *ri = F2FS_INODE(page);
	int extra_size = get_extra_isize(inode);
	__le32 *addr = get_dnode_addr(inode, page);

	return (void *)&(ri->i_addr[extra_size + DEF_INLINE_RESERVED_SIZE]);
	return (void *)(addr + DEF_INLINE_RESERVED_SIZE);
}

static inline int f2fs_has_inline_dentry(struct inode *inode)
@@ -3432,6 +3428,17 @@ static inline int get_inline_xattr_addrs(struct inode *inode)
	return F2FS_I(inode)->i_inline_xattr_size;
}

static inline __le32 *get_dnode_addr(struct inode *inode,
					struct page *node_page)
{
	int base = 0;

	if (IS_INODE(node_page) && f2fs_has_extra_attr(inode))
		base = get_extra_isize(inode);

	return blkaddr_in_node(F2FS_NODE(node_page)) + base;
}

#define f2fs_get_inode_mode(i) \
	((is_inode_flag_set(i, FI_ACL_MODE)) ? \
	 (F2FS_I(i)->i_acl_mode) : ((i)->i_mode))
@@ -3815,7 +3822,7 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio);
struct block_device *f2fs_target_device(struct f2fs_sb_info *sbi,
		block_t blk_addr, sector_t *sector);
int f2fs_target_device_index(struct f2fs_sb_info *sbi, block_t blkaddr);
void f2fs_set_data_blkaddr(struct dnode_of_data *dn);
void f2fs_set_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr);
void f2fs_update_data_blkaddr(struct dnode_of_data *dn, block_t blkaddr);
int f2fs_reserve_new_blocks(struct dnode_of_data *dn, blkcnt_t count);
int f2fs_reserve_new_block(struct dnode_of_data *dn);
@@ -4606,6 +4613,13 @@ static inline bool f2fs_is_readonly(struct f2fs_sb_info *sbi)
	return f2fs_sb_has_readonly(sbi) || f2fs_readonly(sbi->sb);
}

static inline void f2fs_invalidate_internal_cache(struct f2fs_sb_info *sbi,
								block_t blkaddr)
{
	invalidate_mapping_pages(META_MAPPING(sbi), blkaddr, blkaddr);
	f2fs_invalidate_compress_page(sbi, blkaddr);
}

#define EFSBADCRC	EBADMSG		/* Bad CRC detected */
#define EFSCORRUPTED	EUCLEAN		/* Filesystem is corrupted */

+33 −33
Original line number Diff line number Diff line
@@ -42,11 +42,11 @@ static vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf)
	vm_fault_t ret;

	ret = filemap_fault(vmf);
	if (!ret)
	if (ret & VM_FAULT_LOCKED)
		f2fs_update_iostat(F2FS_I_SB(inode), inode,
					APP_MAPPED_READ_IO, F2FS_BLKSIZE);

	trace_f2fs_filemap_fault(inode, vmf->pgoff, (unsigned long)ret);
	trace_f2fs_filemap_fault(inode, vmf->pgoff, vmf->vma->vm_flags, ret);

	return ret;
}
@@ -59,26 +59,29 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
	struct dnode_of_data dn;
	bool need_alloc = true;
	int err = 0;
	vm_fault_t ret;

	if (unlikely(IS_IMMUTABLE(inode)))
		return VM_FAULT_SIGBUS;

	if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED))
		return VM_FAULT_SIGBUS;
	if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) {
		err = -EIO;
		goto out;
	}

	if (unlikely(f2fs_cp_error(sbi))) {
		err = -EIO;
		goto err;
		goto out;
	}

	if (!f2fs_is_checkpoint_ready(sbi)) {
		err = -ENOSPC;
		goto err;
		goto out;
	}

	err = f2fs_convert_inline_inode(inode);
	if (err)
		goto err;
		goto out;

#ifdef CONFIG_F2FS_FS_COMPRESSION
	if (f2fs_compressed_file(inode)) {
@@ -86,7 +89,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)

		if (ret < 0) {
			err = ret;
			goto err;
			goto out;
		} else if (ret) {
			need_alloc = false;
		}
@@ -153,13 +156,15 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
	f2fs_update_iostat(sbi, inode, APP_MAPPED_IO, F2FS_BLKSIZE);
	f2fs_update_time(sbi, REQ_TIME);

	trace_f2fs_vm_page_mkwrite(page, DATA);
out_sem:
	filemap_invalidate_unlock_shared(inode->i_mapping);

	sb_end_pagefault(inode->i_sb);
err:
	return vmf_fs_error(err);
out:
	ret = vmf_fs_error(err);

	trace_f2fs_vm_page_mkwrite(inode, page->index, vmf->vma->vm_flags, ret);
	return ret;
}

static const struct vm_operations_struct f2fs_file_vm_ops = {
@@ -418,7 +423,7 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence)
	loff_t isize;
	int err = 0;

	inode_lock(inode);
	inode_lock_shared(inode);

	isize = i_size_read(inode);
	if (offset >= isize)
@@ -483,10 +488,10 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence)
found:
	if (whence == SEEK_HOLE && data_ofs > isize)
		data_ofs = isize;
	inode_unlock(inode);
	inode_unlock_shared(inode);
	return vfs_setpos(file, data_ofs, maxbytes);
fail:
	inode_unlock(inode);
	inode_unlock_shared(inode);
	return -ENXIO;
}

@@ -557,20 +562,14 @@ static int f2fs_file_open(struct inode *inode, struct file *filp)
void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
{
	struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
	struct f2fs_node *raw_node;
	int nr_free = 0, ofs = dn->ofs_in_node, len = count;
	__le32 *addr;
	int base = 0;
	bool compressed_cluster = false;
	int cluster_index = 0, valid_blocks = 0;
	int cluster_size = F2FS_I(dn->inode)->i_cluster_size;
	bool released = !atomic_read(&F2FS_I(dn->inode)->i_compr_blocks);

	if (IS_INODE(dn->node_page) && f2fs_has_extra_attr(dn->inode))
		base = get_extra_isize(dn->inode);

	raw_node = F2FS_NODE(dn->node_page);
	addr = blkaddr_in_node(raw_node) + base + ofs;
	addr = get_dnode_addr(dn->inode, dn->node_page) + ofs;

	/* Assumption: truncation starts with cluster */
	for (; count > 0; count--, addr++, dn->ofs_in_node++, cluster_index++) {
@@ -588,8 +587,7 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
		if (blkaddr == NULL_ADDR)
			continue;

		dn->data_blkaddr = NULL_ADDR;
		f2fs_set_data_blkaddr(dn);
		f2fs_set_data_blkaddr(dn, NULL_ADDR);

		if (__is_valid_data_blkaddr(blkaddr)) {
			if (!f2fs_is_valid_blkaddr(sbi, blkaddr,
@@ -599,9 +597,6 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
				valid_blocks++;
		}

		if (dn->ofs_in_node == 0 && IS_INODE(dn->node_page))
			clear_inode_flag(dn->inode, FI_FIRST_BLOCK_WRITTEN);

		f2fs_invalidate_blocks(sbi, blkaddr);

		if (!released || blkaddr != COMPRESS_ADDR)
@@ -1317,6 +1312,7 @@ static int __clone_blkaddrs(struct inode *src_inode, struct inode *dst_inode,
			}
			memcpy_page(pdst, 0, psrc, 0, PAGE_SIZE);
			set_page_dirty(pdst);
			set_page_private_gcing(pdst);
			f2fs_put_page(pdst, 1);
			f2fs_put_page(psrc, 1);

@@ -1487,8 +1483,7 @@ static int f2fs_do_zero_range(struct dnode_of_data *dn, pgoff_t start,
		}

		f2fs_invalidate_blocks(sbi, dn->data_blkaddr);
		dn->data_blkaddr = NEW_ADDR;
		f2fs_set_data_blkaddr(dn);
		f2fs_set_data_blkaddr(dn, NEW_ADDR);
	}

	f2fs_update_read_extent_cache_range(dn, start, 0, index - start);
@@ -2818,6 +2813,11 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
			goto out;
	}

	if (f2fs_compressed_file(src) || f2fs_compressed_file(dst)) {
		ret = -EOPNOTSUPP;
		goto out_unlock;
	}

	ret = -EINVAL;
	if (pos_in + len > src->i_size || pos_in + len < pos_in)
		goto out_unlock;
@@ -3463,8 +3463,7 @@ static int release_compress_blocks(struct dnode_of_data *dn, pgoff_t count)
			if (blkaddr != NEW_ADDR)
				continue;

			dn->data_blkaddr = NULL_ADDR;
			f2fs_set_data_blkaddr(dn);
			f2fs_set_data_blkaddr(dn, NULL_ADDR);
		}

		f2fs_i_compr_blocks_update(dn->inode, compr_blocks, false);
@@ -3630,8 +3629,7 @@ static int reserve_compress_blocks(struct dnode_of_data *dn, pgoff_t count)
				continue;
			}

			dn->data_blkaddr = NEW_ADDR;
			f2fs_set_data_blkaddr(dn);
			f2fs_set_data_blkaddr(dn, NEW_ADDR);
		}

		reserved = cluster_size - compr_blocks;
@@ -4054,6 +4052,7 @@ static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
		f2fs_bug_on(F2FS_I_SB(inode), !page);

		set_page_dirty(page);
		set_page_private_gcing(page);
		f2fs_put_page(page, 1);
		f2fs_put_page(page, 0);
	}
@@ -4568,7 +4567,8 @@ static int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *iter,
	if (map.m_len > map.m_lblk)
		map.m_len -= map.m_lblk;
	else
		map.m_len = 0;
		return 0;

	map.m_may_create = true;
	if (dio) {
		map.m_seg_type = f2fs_rw_hint_to_seg_type(inode->i_write_hint);
Loading