Re: [f2fs-dev] [PATCH v5] f2fs: support dynamic reserve/release for device aliasing

From: Daeho Jeong

Date: Thu Jul 30 2026 - 11:25:25 EST


On Wed, Jul 29, 2026 at 11:32 PM Chao Yu <chao@xxxxxxxxxx> wrote:
>
> On 7/30/26 00:43, Daeho Jeong wrote:
> > On Wed, Jul 29, 2026 at 2:09 AM Chao Yu <chao@xxxxxxxxxx> wrote:
> >>
> >> On 7/24/26 04:20, Daeho Jeong wrote:
> >>> From: Daeho Jeong <daehojeong@xxxxxxxxxx>
> >>>
> >>> This patch adds a dynamic management feature to the existing device
> >>> aliasing functionality. It allows users to dynamically reserve or
> >>> release specific devices from the filesystem's free pool at runtime
> >>> through new ioctls.
> >>>
> >>> To support this, three new ioctls are introduced:
> >>> - F2FS_IOC_RESERVE_DEV_ALIAS: This reclaims the space occupied by a
> >>> device aliasing file. It first performs a capacity check, resets GC
> >>> victim information for the target range, marks the segments as in-use
> >>> to prevent new allocations, and then triggers GC to migrate existing
> >>> valid data out of the range. Finally, it reserves these blocks in the
> >>> SIT to effectively exclude the device from the usable capacity.
> >>>
> >>> - F2FS_IOC_RELEASE_DEV_ALIAS: This releases the reserved space of a
> >>> previously reserved device aliasing file. It truncates the blocks
> >>> associated with the file, which makes them available for general
> >>> filesystem allocation again.
> >>>
> >>> - F2FS_IOC_GET_DEV_ALIAS_STATUS: This retrieves the current aliasing
> >>> status of a device aliasing file, returning whether the file is
> >>> released (inactive alias) or reserved (active alias, with blocks
> >>> fully allocated on the device).
> >>>
> >>> Signed-off-by: Daeho Jeong <daehojeong@xxxxxxxxxx>
> >>> ---
> >>> v5: prevented new segment allocation on devices undergoing reservation
> >>> or with active aliases.
> >>> used in-memory struct to reserve blocks for device aliasing.
> >>> cleared prefree (PRE) dirty segment bitmap.
> >>> v4: renamed interfaces.
> >>> fixed race conditions between checkpoint=disable mount and ioctls.
> >>> refactored segment reservation part.
> >>> modified lock usage.
> >>> v3: add CAP_SYS_ADMIN and checkpoint=disabled check.
> >>> remove a f2fs specific flag exposed with getflags.
> >>> v2: prevent operations during checkpoint=disabled.
> >>> ---
> >>> ---
> >>> Documentation/filesystems/f2fs.rst | 35 ++++
> >>> fs/f2fs/f2fs.h | 10 +-
> >>> fs/f2fs/file.c | 278 ++++++++++++++++++++++++++++-
> >>> fs/f2fs/gc.c | 30 ++--
> >>> fs/f2fs/namei.c | 14 ++
> >>> fs/f2fs/segment.c | 174 +++++++++++++-----
> >>> fs/f2fs/segment.h | 22 +++
> >>> fs/f2fs/super.c | 35 ++++
> >>> include/uapi/linux/f2fs.h | 7 +
> >>> 9 files changed, 542 insertions(+), 63 deletions(-)
> >>>
> >>> diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst
> >>> index 8c4a14ae444f..1a5fd4afe609 100644
> >>> --- a/Documentation/filesystems/f2fs.rst
> >>> +++ b/Documentation/filesystems/f2fs.rst
> >>> @@ -1045,6 +1045,41 @@ So, the key idea is, user can do any file operations on /dev/vdc, and
> >>> reclaim the space after the use, while the space is counted as /data.
> >>> That doesn't require modifying partition size and filesystem format.
> >>>
> >>> +Dynamic Device Aliasing Management
> >>> +----------------------------------
> >>> +
> >>> +In addition to static device aliasing by deleting the aliasing file, F2FS
> >>> +supports dynamic management of device aliasing. This mechanism allows the system
> >>> +to dynamically transition partition ownership between F2FS userdata and external
> >>> +entities (e.g., zRAM, raw partition) based on system requirements without
> >>> +deleting the master aliasing file or requiring unmount/remount.
> >>> +
> >>> +The master aliasing file is created during the initial format of the file system
> >>> +and remains as a persistent control entity (ioctl gateway) in the root directory.
> >>> +
> >>> +- Partition Reservation (In-service to Aliased)
> >>> + When a specific partition needs to be dedicated to external services (e.g., zRAM),
> >>> + a user can reserve the device alias range via ioctl. The kernel resets GC victim
> >>> + information for the target range, marks segments as in-use to prevent new
> >>> + allocations, and triggers forced GC to migrate existing valid data out of the
> >>> + range. Finally, it reserves these blocks in the SIT to effectively exclude the
> >>> + device from the usable capacity.
> >>> +
> >>> +- Partition Release (Aliased to In-service)
> >>> + When external usage concludes, the space is reclaimed not by deleting the file,
> >>> + but through the release ioctl. The kernel truncates blocks associated with
> >>> + the file, releasing them back to general filesystem allocation.
> >>> +
> >>> +.. code-block::
> >>> +
> >>> + # f2fs_io dev_alias release /mnt/f2fs/vdc.file
> >>> + # df -h
> >>> + /dev/vdb 64G 753M 64G 2% /mnt/f2fs
> >>> +
> >>> + # f2fs_io dev_alias reserve /mnt/f2fs/vdc.file
> >>> + # df -h
> >>> + /dev/vdb 64G 33G 32G 52% /mnt/f2fs
> >>> +
> >>> Per-file Read-Only Large Folio Support
> >>> --------------------------------------
> >>>
> >>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> >>> index f1774d4e18d2..74c2d58ce271 100644
> >>> --- a/fs/f2fs/f2fs.h
> >>> +++ b/fs/f2fs/f2fs.h
> >>> @@ -1404,6 +1404,8 @@ struct f2fs_dev_info {
> >>> unsigned int total_segments;
> >>> block_t start_blk;
> >>> block_t end_blk;
> >>> + bool has_alias;
> >>> + bool is_reserving;
> >>> #ifdef CONFIG_BLK_DEV_ZONED
> >>> unsigned int nr_blkz; /* Total number of zones */
> >>> unsigned long *blkz_seq; /* Bitmap indicating sequential zones */
> >>> @@ -1857,6 +1859,7 @@ struct f2fs_sb_info {
> >>> block_t last_valid_block_count; /* for recovery */
> >>> block_t reserved_blocks; /* configurable reserved blocks */
> >>> block_t current_reserved_blocks; /* current reserved blocks */
> >>> + block_t alias_reserved_blocks; /* reserved blocks for device alias */
> >>>
> >>> /* Additional tracking for no checkpoint mode */
> >>> block_t unusable_block_count; /* # of blocks saved by last cp */
> >>> @@ -2559,7 +2562,8 @@ static inline unsigned int get_available_block_count(struct f2fs_sb_info *sbi,
> >>> block_t avail_user_block_count;
> >>>
> >>> avail_user_block_count = sbi->user_block_count -
> >>> - sbi->current_reserved_blocks;
> >>> + sbi->current_reserved_blocks -
> >>> + sbi->alias_reserved_blocks;
> >>>
> >>> if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_root(sbi, inode, cap))
> >>> avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
> >>> @@ -4010,6 +4014,8 @@ int f2fs_flush_device_cache(struct f2fs_sb_info *sbi);
> >>> void f2fs_destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free);
> >>> void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr,
> >>> unsigned int len);
> >>> +void f2fs_reserve_device_alias(struct f2fs_sb_info *sbi, block_t addr,
> >>> + unsigned int len);
> >>> bool f2fs_is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr);
> >>> int f2fs_start_discard_thread(struct f2fs_sb_info *sbi);
> >>> void f2fs_drop_discard_cmd(struct f2fs_sb_info *sbi);
> >>> @@ -4231,6 +4237,8 @@ void f2fs_build_gc_manager(struct f2fs_sb_info *sbi);
> >>> int f2fs_gc_range(struct f2fs_sb_info *sbi,
> >>> unsigned int start_seg, unsigned int end_seg,
> >>> bool dry_run, unsigned int dry_run_sections);
> >>> +void f2fs_reset_gc_victim_resource(struct f2fs_sb_info *sbi,
> >>> + unsigned int start, unsigned int end);
> >>> int f2fs_resize_fs(struct file *filp, __u64 block_count);
> >>> int __init f2fs_create_garbage_collection_cache(void);
> >>> void f2fs_destroy_garbage_collection_cache(void);
> >>> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> >>> index 4b52c56d71f0..642f06f2faea 100644
> >>> --- a/fs/f2fs/file.c
> >>> +++ b/fs/f2fs/file.c
> >>> @@ -813,13 +813,19 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock)
> >>>
> >>> if (IS_DEVICE_ALIASING(inode)) {
> >>> struct extent_tree *et = F2FS_I(inode)->extent_tree[EX_READ];
> >>> - struct extent_info ei = et->largest;
> >>> + struct extent_info ei;
> >>> +
> >>> + read_lock(&et->lock);
> >>> + ei = et->largest;
> >>> + read_unlock(&et->lock);
> >>>
> >>> f2fs_invalidate_blocks(sbi, ei.blk, ei.len);
> >>>
> >>> dec_valid_block_count(sbi, inode, ei.len);
> >>> f2fs_update_time(sbi, REQ_TIME);
> >>>
> >>> + f2fs_drop_extent_tree(inode);
> >>> +
> >>> f2fs_folio_put(ifolio, true);
> >>> goto out;
> >>> }
> >>> @@ -1100,8 +1106,9 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> >>> if ((attr->ia_valid & ATTR_SIZE)) {
> >>> if (mapping_large_folio_support(inode->i_mapping))
> >>> return -EOPNOTSUPP;
> >>> - if (!f2fs_is_compress_backend_ready(inode) ||
> >>> - IS_DEVICE_ALIASING(inode))
> >>> + if (IS_DEVICE_ALIASING(inode))
> >>> + return -EPERM;
> >>> + if (!f2fs_is_compress_backend_ready(inode))
> >>> return -EOPNOTSUPP;
> >>> if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED) &&
> >>> !IS_ALIGNED(attr->ia_size,
> >>> @@ -2130,6 +2137,9 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
> >>> if (IS_NOQUOTA(inode))
> >>> return -EPERM;
> >>>
> >>> + if (IS_DEVICE_ALIASING(inode))
> >>> + return -EPERM;
> >>> +
> >>> if ((iflags ^ masked_flags) & F2FS_CASEFOLD_FL) {
> >>> if (!f2fs_sb_has_casefold(F2FS_I_SB(inode)))
> >>> return -EOPNOTSUPP;
> >>> @@ -2678,6 +2688,17 @@ static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg)
> >>> return fscrypt_ioctl_get_policy(filp, (void __user *)arg);
> >>> }
> >>>
> >>> +static int f2fs_ioc_get_dev_alias_status(struct file *filp, unsigned long arg)
> >>> +{
> >>> + struct inode *inode = file_inode(filp);
> >>> +
> >>> + if (!IS_DEVICE_ALIASING(inode))
> >>> + return -EINVAL;
> >>> +
> >>> + return put_user(F2FS_HAS_BLOCKS(inode) ? F2FS_DEV_ALIAS_STATUS_RESERVED :
> >>> + F2FS_DEV_ALIAS_STATUS_RELEASED, (u32 __user *)arg);
> >>> +}
> >>> +
> >>> static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg)
> >>> {
> >>> struct inode *inode = file_inode(filp);
> >>> @@ -3616,6 +3637,248 @@ static int f2fs_ioc_get_dev_alias_file(struct file *filp, unsigned long arg)
> >>> (u32 __user *)arg);
> >>> }
> >>>
> >>> +static bool f2fs_get_dev_alias_extent(struct f2fs_sb_info *sbi,
> >>> + struct dentry *dentry,
> >>> + struct extent_info *ei)
> >>> +{
> >>> + int i;
> >>> +
> >>> + for (i = 1; i < sbi->s_ndevs; i++) {
> >>> + char *name = strrchr(FDEV(i).path, '/');
> >>> +
> >>> + name = name ? name + 1 : FDEV(i).path;
> >>> + if (!strcmp(name, dentry->d_name.name)) {
> >>> + ei->blk = FDEV(i).start_blk;
> >>> + ei->len = FDEV(i).total_segments << sbi->log_blocks_per_seg;
> >>> + ei->fofs = 0;
> >>> + return true;
> >>> + }
> >>
> >> Trivial cleanup to avoid long line:
> >>
> >> if (strcmp(name, dentry->d_name.name))
> >> continue;
> >>
> >> ei->blk = FDEV(i).start_blk;
> >> ei->len = FDEV(i).total_segments << sbi->log_blocks_per_seg;
> >> ei->fofs = 0;
> >> return true;
> >>
> >>> + }
> >>> + return false;
> >>> +}
> >>> +
> >>> +static int f2fs_ioc_reserve_dev_alias(struct file *filp)
> >>> +{
> >>> + struct inode *inode = file_inode(filp);
> >>> + struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> >>> + struct extent_tree *et = F2FS_I(inode)->extent_tree[EX_READ];
> >>> + struct extent_info ei;
> >>> + struct cp_control cpc = { CP_SYNC, 0, 0, 0 };
> >>> + struct f2fs_lock_context lc, glc;
> >>> + blkcnt_t count;
> >>> + unsigned int start, end;
> >>> + int type, err;
> >>> +
> >>> + if (!capable(CAP_SYS_ADMIN))
> >>> + return -EPERM;
> >>> +
> >>> + if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
> >>> + return -EINVAL;
> >>> +
> >>> + err = mnt_want_write_file(filp);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + inode_lock(inode);
> >>> +
> >>> + if (!IS_DEVICE_ALIASING(inode)) {
> >>> + err = -EINVAL;
> >>> + goto out_inode_unlock;
> >>> + }
> >>> +
> >>> + if (F2FS_HAS_BLOCKS(inode)) {
> >>> + err = 0;
> >>> + goto out_inode_unlock;
> >>> + }
> >>> +
> >>> + if (!f2fs_get_dev_alias_extent(sbi, filp->f_path.dentry, &ei)) {
> >>> + f2fs_warn(sbi, "device alias file (%s, ino=%llu) has no matching device",
> >>> + filp->f_path.dentry->d_name.name,
> >>> + (unsigned long long)inode->i_ino);
> >>> + set_sbi_flag(sbi, SBI_NEED_FSCK);
> >>> + f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE);
> >>> + err = -EFSCORRUPTED;
> >>> + goto out_inode_unlock;
> >>> + }
> >>> +
> >>> + spin_lock(&sbi->stat_lock);
> >>> + if (sbi->total_valid_block_count + ei.len >
> >>> + get_available_block_count(sbi, inode, true)) {
> >>> + spin_unlock(&sbi->stat_lock);
> >>> + err = -ENOSPC;
> >>> + goto out_inode_unlock;
> >>> + }
> >>> + sbi->alias_reserved_blocks += ei.len;
> >>> + spin_unlock(&sbi->stat_lock);
> >>> +
> >>> + spin_lock(&FREE_I(sbi)->segmap_lock);
> >>> + FDEV(f2fs_target_device_index(sbi, ei.blk)).is_reserving = true;
> >>> + spin_unlock(&FREE_I(sbi)->segmap_lock);
> >>> +
> >>> + start = GET_SEGNO(sbi, ei.blk);
> >>> + end = GET_SEGNO(sbi, ei.blk + ei.len - 1);
> >>> +
> >>> + /* Acquire gc_lock for victim reset, curseg resize, and range GC */
> >>> + f2fs_down_write_trace(&sbi->gc_lock, &glc);
> >>> +
> >>> + /* Reset the victim information to prevent GC from targeting the range */
> >>> + f2fs_reset_gc_victim_resource(sbi, start, end);
> >>> +
> >>> + /* Move out cursegs from the target range */
> >>> + for (type = CURSEG_HOT_DATA; type < NR_CURSEG_PERSIST_TYPE; type++) {
> >>> + err = f2fs_allocate_segment_for_resize(sbi, type, start, end);
> >>> + if (err)
> >>> + goto out_gc_unlock;
> >>> + }
> >>> +
> >>> + f2fs_lock_op(sbi, &lc);
> >>> +
> >>> + if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
> >>> + err = -EINVAL;
> >>> + f2fs_unlock_op(sbi, &lc);
> >>> + goto out_gc_unlock;
> >>> + }
> >>> +
> >>> + /* do GC to move out valid blocks in the range all at once! */
> >>> + err = f2fs_gc_range(sbi, start, end, false, 0);
> >>> + if (err) {
> >>> + f2fs_unlock_op(sbi, &lc);
> >>> + goto out_gc_unlock;
> >>> + }
> >>> +
> >>> + count = ei.len;
> >>> + err = inc_valid_block_count(sbi, inode, &count, false);
> >>
> >> Hmm.. seems if device alias file is 4GB, we have reserved 4GB by set
> >> alias_reserved_blocks to 4GB, here we want to allocate another 4GB?
> >> It will fail if the left space is less than 4GB, seems not make sense.
> >>
> >> Maybe we can add another parameter to let inc_valid_block_count to 1)
> >> increase total_valid_block_count and 2) decrease alias_reserved_blocks
> >> atomically under protetion of stat_lock? Thoughts?
> >>
> >>
> >>> + if (err) {
> >>> + f2fs_unlock_op(sbi, &lc);
> >>> + goto out_gc_unlock;
> >>> + }
> >>> +
> >>> + spin_lock(&sbi->stat_lock);
> >>> + sbi->alias_reserved_blocks -= ei.len;
> >>> + spin_unlock(&sbi->stat_lock);
> >>> +
> >>> + if (et) {
> >>
> >> Any case that et is NULL?
> >>
> >>> + write_lock(&et->lock);
> >>> + et->largest = ei;
> >>> + write_unlock(&et->lock);
> >>> + }
> >>> + clear_inode_flag(inode, FI_NO_EXTENT);
> >>> +
> >>> + f2fs_reserve_device_alias(sbi, ei.blk, ei.len);
> >>> +
> >>> + i_size_write(inode, (loff_t)ei.len << PAGE_SHIFT);
> >>
> >> << sbi->blocksize? in case page_size is not equals to blocksize.
> >>
> >>> + f2fs_update_inode_page(inode);
> >>> +
> >>> + spin_lock(&FREE_I(sbi)->segmap_lock);
> >>> + FDEV(f2fs_target_device_index(sbi, ei.blk)).is_reserving = false;
> >>> + spin_unlock(&FREE_I(sbi)->segmap_lock);
> >>> +
> >>> + f2fs_unlock_op(sbi, &lc);
> >>> + f2fs_up_write_trace(&sbi->gc_lock, &glc);
> >>> +
> >>> + inode_unlock(inode);
> >>> + mnt_drop_write_file(filp);
> >>> +
> >>> + err = f2fs_write_checkpoint(sbi, &cpc);
> >>> + return err;
> >>> +
> >>> +out_gc_unlock:
> >>> + spin_lock(&sbi->stat_lock);
> >>> + sbi->alias_reserved_blocks -= ei.len;
> >>> + spin_unlock(&sbi->stat_lock);
> >>> +
> >>> + spin_lock(&FREE_I(sbi)->segmap_lock);
> >>> + FDEV(f2fs_target_device_index(sbi, ei.blk)).is_reserving = false;
> >>> + spin_unlock(&FREE_I(sbi)->segmap_lock);
> >>> + f2fs_up_write_trace(&sbi->gc_lock, &glc);
> >>> +
> >>
> >> out_inode_unlock:
> >>
> >>> + inode_unlock(inode);
> >>> + mnt_drop_write_file(filp);
> >>> + return err;
> >>> +
> >>> +out_inode_unlock:
> >>> + inode_unlock(inode);
> >>> + mnt_drop_write_file(filp);
> >>> + return err;
> >>
> >> Can be removed.
> >>
> >>> +}
> >>> +
> >>> +static int f2fs_ioc_release_dev_alias(struct file *filp)
> >>> +{
> >>> + struct inode *inode = file_inode(filp);
> >>> + struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> >>> + struct extent_tree *et = F2FS_I(inode)->extent_tree[EX_READ];
> >>> + struct extent_info ei = {0, };
> >>> + struct cp_control cpc = { CP_SYNC, 0, 0, 0 };
> >>> + struct f2fs_lock_context lc, glc;
> >>> + int err;
> >>> +
> >>> + if (!capable(CAP_SYS_ADMIN))
> >>> + return -EPERM;
> >>> +
> >>> + if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
> >>> + return -EINVAL;
> >>> +
> >>> + err = mnt_want_write_file(filp);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + inode_lock(inode);
> >>> +
> >>> + if (!IS_DEVICE_ALIASING(inode)) {
> >>> + err = -EINVAL;
> >>> + goto out_inode_unlock;
> >>> + }
> >>> +
> >>> + if (!F2FS_HAS_BLOCKS(inode)) {
> >>> + err = 0;
> >>> + goto out_inode_unlock;
> >>> + }
> >>> +
> >>> + err = filemap_write_and_wait(inode->i_mapping);
> >>> + if (err)
> >>> + goto out_inode_unlock;
> >>> +
> >>> + read_lock(&et->lock);
> >>> + ei = et->largest;
> >>> + read_unlock(&et->lock);
> >>
> >> Do we need to do sanity check on whether start/end of device alias file
> >> are aligned to section-size? may be in sanity_check_extent_cache()?
> >
> > Thanks for all the comments. I accepted all other comments.
> >
> > In sanity_check_extent_cache(), we already verify that the extent
> > range of a device alias inode (ei.blk and ei.len) matches the exact
> > start and end block of a secondary device (FDEV(devi).start_blk and
> > FDEV(devi).end_blk):
> > Since every secondary block device in F2FS is already guaranteed to be
> > aligned to section boundaries, matching the extent against FDEV(devi)
> > inherently guarantees section alignment for the device alias file as
> > well.
>
> IIUC, for the worst case, we may suffer issue (potentially GC on unaligned
> pinfiles) when mounting an image containing unaligned device alias file which
> is fuzzed by syzkaller?
>
> Also, I'm worry about whether there is any code flaw can cause to miss to keep
> the alignment, since recently I found a bug that pinfile may not be alinged
> to section, see details in commit d0a481fad5c7 ("f2fs: fix to avoid potential
> section-unaligned pinfile"), so I proposed to add such sanity check to detect
> it. :)

Got it. Let me cover the case.

Thanks,

>
> Thoughts?
>
> Thanks,
>
> >
> > Therefore, adding an explicit section alignment check in
> > sanity_check_extent_cache() seems redundant.
> >
> >>
> >>> +
> >>> + f2fs_down_write_trace(&sbi->gc_lock, &glc);
> >>> + f2fs_lock_op(sbi, &lc);
> >>> +
> >>> + if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
> >>> + err = -EINVAL;
> >>> + f2fs_unlock_op(sbi, &lc);
> >>> + f2fs_up_write_trace(&sbi->gc_lock, &glc);
> >>> + goto out_inode_unlock;
> >>> + }
> >>> +
> >>> + truncate_setsize(inode, 0);
> >>> +
> >>
> >> To avoid racing w/ mmap?
> >>
> >> filemap_invalidate_lock(inode->i_mapping);
> >>
> >>> + err = f2fs_truncate_blocks(inode, 0, false);
> >>
> >> filemap_invalidate_unlock(inode->i_mapping);
> >>
> >>> + if (err) {
> >>> + i_size_write(inode, (loff_t)ei.len << PAGE_SHIFT);
> >>
> >> << sbi->blocksize? in case page_size is not equals to blocksize.
> >>
> >>> + f2fs_unlock_op(sbi, &lc);
> >>> + f2fs_up_write_trace(&sbi->gc_lock, &glc);
> >>> + goto out_inode_unlock;
> >>> + }
> >>> +
> >>> + f2fs_update_inode_page(inode);
> >>> +
> >>> + f2fs_unlock_op(sbi, &lc);
> >>> + f2fs_up_write_trace(&sbi->gc_lock, &glc);
> >>> +
> >>> + inode_unlock(inode);
> >>> + mnt_drop_write_file(filp);
> >>> +
> >>> + err = f2fs_write_checkpoint(sbi, &cpc);
> >>
> >> return f2fs_write_checkpoint(sbi, &cpc);
> >>
> >>> + return err;
> >>> +
> >>> +out_inode_unlock:
> >>> + inode_unlock(inode);
> >>> + mnt_drop_write_file(filp);
> >>> + return err;
> >>> +}
> >>> +
> >>> static int f2fs_ioc_io_prio(struct file *filp, unsigned long arg)
> >>> {
> >>> struct inode *inode = file_inode(filp);
> >>> @@ -4742,8 +5005,14 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> >>> return f2fs_ioc_compress_file(filp);
> >>> case F2FS_IOC_GET_DEV_ALIAS_FILE:
> >>> return f2fs_ioc_get_dev_alias_file(filp, arg);
> >>> + case F2FS_IOC_GET_DEV_ALIAS_STATUS:
> >>> + return f2fs_ioc_get_dev_alias_status(filp, arg);
> >>> case F2FS_IOC_IO_PRIO:
> >>> return f2fs_ioc_io_prio(filp, arg);
> >>> + case F2FS_IOC_RESERVE_DEV_ALIAS:
> >>> + return f2fs_ioc_reserve_dev_alias(filp);
> >>> + case F2FS_IOC_RELEASE_DEV_ALIAS:
> >>> + return f2fs_ioc_release_dev_alias(filp);
> >>> default:
> >>> return -ENOTTY;
> >>> }
> >>> @@ -5530,7 +5799,10 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> >>> case F2FS_IOC_DECOMPRESS_FILE:
> >>> case F2FS_IOC_COMPRESS_FILE:
> >>> case F2FS_IOC_GET_DEV_ALIAS_FILE:
> >>> + case F2FS_IOC_GET_DEV_ALIAS_STATUS:
> >>> case F2FS_IOC_IO_PRIO:
> >>> + case F2FS_IOC_RESERVE_DEV_ALIAS:
> >>> + case F2FS_IOC_RELEASE_DEV_ALIAS:
> >>> break;
> >>> default:
> >>> return -ENOIOCTLCMD;
> >>> diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
> >>> index ffaa7ba76a1b..93bcb35a5b5d 100644
> >>> --- a/fs/f2fs/gc.c
> >>> +++ b/fs/f2fs/gc.c
> >>> @@ -2197,29 +2197,37 @@ int f2fs_gc_range(struct f2fs_sb_info *sbi,
> >>> return 0;
> >>> }
> >>>
> >>> +void f2fs_reset_gc_victim_resource(struct f2fs_sb_info *sbi,
> >>> + unsigned int start, unsigned int end)
> >>> +{
> >>> + int i;
> >>> +
> >>> + mutex_lock(&DIRTY_I(sbi)->seglist_lock);
> >>> + for (i = 0; i < MAX_GC_POLICY; i++)
> >>> + if (SIT_I(sbi)->last_victim[i] >= start &&
> >>> + SIT_I(sbi)->last_victim[i] <= end)
> >>> + SIT_I(sbi)->last_victim[i] = 0;
> >>> +
> >>> + for (i = BG_GC; i <= FG_GC; i++)
> >>> + if (sbi->next_victim_seg[i] >= start &&
> >>> + sbi->next_victim_seg[i] <= end)
> >>> + sbi->next_victim_seg[i] = NULL_SEGNO;
> >>> + mutex_unlock(&DIRTY_I(sbi)->seglist_lock);
> >>> +}
> >>> +
> >>> static int free_segment_range(struct f2fs_sb_info *sbi,
> >>> unsigned int secs, bool dry_run)
> >>> {
> >>> unsigned int next_inuse, start, end;
> >>> struct cp_control cpc = { CP_RESIZE, 0, 0, 0 };
> >>> - int gc_mode, gc_type;
> >>> int err = 0;
> >>> int type;
> >>>
> >>> - /* Force block allocation for GC */
> >>> MAIN_SECS(sbi) -= secs;
> >>> start = MAIN_SECS(sbi) * SEGS_PER_SEC(sbi);
> >>> end = MAIN_SEGS(sbi) - 1;
> >>>
> >>> - mutex_lock(&DIRTY_I(sbi)->seglist_lock);
> >>> - for (gc_mode = 0; gc_mode < MAX_GC_POLICY; gc_mode++)
> >>> - if (SIT_I(sbi)->last_victim[gc_mode] >= start)
> >>> - SIT_I(sbi)->last_victim[gc_mode] = 0;
> >>> -
> >>> - for (gc_type = BG_GC; gc_type <= FG_GC; gc_type++)
> >>> - if (sbi->next_victim_seg[gc_type] >= start)
> >>> - sbi->next_victim_seg[gc_type] = NULL_SEGNO;
> >>> - mutex_unlock(&DIRTY_I(sbi)->seglist_lock);
> >>> + f2fs_reset_gc_victim_resource(sbi, start, end);
> >>>
> >>> /* Move out cursegs from the target range */
> >>> for (type = CURSEG_HOT_DATA; type < NR_CURSEG_PERSIST_TYPE; type++) {
> >>> diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
> >>> index cac03b8e91a1..8c3b57987f6c 100644
> >>> --- a/fs/f2fs/namei.c
> >>> +++ b/fs/f2fs/namei.c
> >>> @@ -425,6 +425,9 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
> >>> if (!f2fs_is_checkpoint_ready(sbi))
> >>> return -ENOSPC;
> >>>
> >>> + if (IS_DEVICE_ALIASING(inode))
> >>> + return -EPERM;
> >>> +
> >>> err = fscrypt_prepare_link(old_dentry, dir, dentry);
> >>> if (err)
> >>> return err;
> >>> @@ -568,6 +571,9 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
> >>>
> >>> trace_f2fs_unlink_enter(dir, dentry);
> >>>
> >>> + if (IS_DEVICE_ALIASING(inode))
> >>> + return -EPERM;
> >>> +
> >>> if (unlikely(f2fs_cp_error(sbi))) {
> >>> err = -EIO;
> >>> goto out;
> >>> @@ -946,6 +952,9 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
> >>> bool old_is_dir = S_ISDIR(old_inode->i_mode);
> >>> int err;
> >>>
> >>> + if (IS_DEVICE_ALIASING(old_inode))
> >>> + return -EPERM;
> >>> +
> >>> if (unlikely(f2fs_cp_error(sbi)))
> >>> return -EIO;
> >>> if (!f2fs_is_checkpoint_ready(sbi))
> >>> @@ -1016,6 +1025,8 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
> >>> }
> >>>
> >>> if (new_inode) {
> >>> + if (IS_DEVICE_ALIASING(new_inode))
> >>> + return -EPERM;
> >>>
> >>> err = -ENOTEMPTY;
> >>> if (old_is_dir && !f2fs_empty_dir(new_inode))
> >>> @@ -1143,6 +1154,9 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
> >>> int old_nlink = 0, new_nlink = 0;
> >>> int err;
> >>>
> >>> + if (IS_DEVICE_ALIASING(old_inode) || IS_DEVICE_ALIASING(new_inode))
> >>> + return -EPERM;
> >>> +
> >>> if (unlikely(f2fs_cp_error(sbi)))
> >>> return -EIO;
> >>> if (!f2fs_is_checkpoint_ready(sbi))
> >>> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
> >>> index d71ddb3ee918..1202bde3390a 100644
> >>> --- a/fs/f2fs/segment.c
> >>> +++ b/fs/f2fs/segment.c
> >>> @@ -2502,35 +2502,42 @@ static int update_sit_entry_for_alloc(struct f2fs_sb_info *sbi, struct seg_entry
> >>> unsigned int segno, block_t blkaddr, unsigned int offset, int del)
> >>> {
> >>> bool exist;
> >>> + int del_count = del;
> >>> + int i;
> >>>
> >>> - exist = f2fs_test_and_set_bit(offset, se->cur_valid_map);
> >>> - if (unlikely(exist)) {
> >>> - f2fs_err(sbi, "Bitmap was wrongly set, blk:%u", blkaddr);
> >>> - f2fs_bug_on(sbi, 1);
> >>> - se->valid_blocks--;
> >>> - del = 0;
> >>> - }
> >>> + f2fs_bug_on(sbi, GET_SEGNO(sbi, blkaddr) != GET_SEGNO(sbi, blkaddr + del_count - 1));
> >>>
> >>> - if (f2fs_block_unit_discard(sbi) &&
> >>> - !f2fs_test_and_set_bit(offset, se->discard_map))
> >>> - sbi->discard_blks--;
> >>> + for (i = 0; i < del_count; i++) {
> >>> + exist = f2fs_test_and_set_bit(offset + i, se->cur_valid_map);
> >>> + if (unlikely(exist)) {
> >>> + f2fs_err(sbi, "Bitmap was wrongly set, blk:%u", blkaddr + i);
> >>> + f2fs_bug_on(sbi, 1);
> >>> + se->valid_blocks--;
> >>> + del -= 1;
> >>> + continue;
> >>> + }
> >>>
> >>> - /*
> >>> - * SSR should never reuse block which is checkpointed
> >>> - * or newly invalidated.
> >>> - */
> >>> - if (!is_sbi_flag_set(sbi, SBI_CP_DISABLED)) {
> >>> - if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map)) {
> >>> - se->ckpt_valid_blocks++;
> >>> - if (__is_large_section(sbi))
> >>> - get_sec_entry(sbi, segno)->ckpt_valid_blocks++;
> >>> + if (f2fs_block_unit_discard(sbi) &&
> >>> + !f2fs_test_and_set_bit(offset + i, se->discard_map))
> >>> + sbi->discard_blks--;
> >>> +
> >>> + /*
> >>> + * SSR should never reuse block which is checkpointed
> >>> + * or newly invalidated.
> >>> + */
> >>> + if (!is_sbi_flag_set(sbi, SBI_CP_DISABLED)) {
> >>> + if (!f2fs_test_and_set_bit(offset + i, se->ckpt_valid_map)) {
> >>> + se->ckpt_valid_blocks++;
> >>> + if (__is_large_section(sbi))
> >>> + get_sec_entry(sbi, segno)->ckpt_valid_blocks++;
> >>> + }
> >>> }
> >>> - }
> >>>
> >>> - if (!f2fs_test_bit(offset, se->ckpt_valid_map)) {
> >>> - se->ckpt_valid_blocks += del;
> >>> - if (__is_large_section(sbi))
> >>> - get_sec_entry(sbi, segno)->ckpt_valid_blocks += del;
> >>> + if (!f2fs_test_bit(offset + i, se->ckpt_valid_map)) {
> >>> + se->ckpt_valid_blocks += 1;
> >>> + if (__is_large_section(sbi))
> >>> + get_sec_entry(sbi, segno)->ckpt_valid_blocks += 1;
> >>> + }
> >>> }
> >>>
> >>> if (__is_large_section(sbi))
> >>> @@ -2585,9 +2592,14 @@ void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr,
> >>> unsigned int segno = GET_SEGNO(sbi, addr);
> >>> struct sit_info *sit_i = SIT_I(sbi);
> >>> block_t addr_start = addr, addr_end = addr + len - 1;
> >>> - unsigned int seg_num = GET_SEGNO(sbi, addr_end) - segno + 1;
> >>> + unsigned int seg_num;
> >>> unsigned int i = 1, max_blocks = sbi->blocks_per_seg, cnt;
> >>>
> >>> + if (len == 0)
> >>> + return;
> >>> +
> >>> + seg_num = GET_SEGNO(sbi, addr_end) - segno + 1;
> >>> +
> >>> f2fs_bug_on(sbi, addr == NULL_ADDR);
> >>> if (addr == NEW_ADDR || addr == COMPRESS_ADDR)
> >>> return;
> >>> @@ -2620,6 +2632,52 @@ void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr,
> >>> up_write(&sit_i->sentry_lock);
> >>> }
> >>>
> >>> +void f2fs_reserve_device_alias(struct f2fs_sb_info *sbi, block_t addr,
> >>> + unsigned int len)
> >>> +{
> >>> + unsigned int segno = GET_SEGNO(sbi, addr);
> >>> + struct sit_info *sit_i = SIT_I(sbi);
> >>> + block_t addr_start = addr, addr_end = addr + len - 1;
> >>> + unsigned int seg_num;
> >>> + unsigned int i = 1, max_blocks = sbi->blocks_per_seg, cnt;
> >>> +
> >>> + if (len == 0)
> >>> + return;
> >>> +
> >>> + seg_num = GET_SEGNO(sbi, addr_end) - segno + 1;
> >>> +
> >>> + down_write(&sit_i->sentry_lock);
> >>> +
> >>> + if (seg_num == 1)
> >>> + cnt = len;
> >>> + else
> >>> + cnt = max_blocks - GET_BLKOFF_FROM_SEG0(sbi, addr);
> >>> +
> >>> + do {
> >>> + update_segment_mtime(sbi, addr_start, 0);
> >>> + update_sit_entry(sbi, addr_start, cnt);
> >>> + __set_test_and_inuse(sbi, segno);
> >>> +
> >>> + /* Remove the segment from PRE (prefree) to prevent checkpoint from freeing it! */
> >>> + mutex_lock(&DIRTY_I(sbi)->seglist_lock);
> >>> + if (test_and_clear_bit(segno, DIRTY_I(sbi)->dirty_segmap[PRE]))
> >>> + DIRTY_I(sbi)->nr_dirty[PRE]--;
> >>> + mutex_unlock(&DIRTY_I(sbi)->seglist_lock);
> >>> +
> >>> + /* add it into dirty seglist */
> >>> + locate_dirty_segment(sbi, segno);
> >>> +
> >>> + /* update @addr_start and @cnt and @segno */
> >>> + addr_start = START_BLOCK(sbi, ++segno);
> >>> + if (++i == seg_num)
> >>> + cnt = GET_BLKOFF_FROM_SEG0(sbi, addr_end) + 1;
> >>> + else
> >>> + cnt = max_blocks;
> >>> + } while (i <= seg_num);
> >>> +
> >>> + up_write(&sit_i->sentry_lock);
> >>> +}
> >>> +
> >>> bool f2fs_is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr)
> >>> {
> >>> struct sit_info *sit_i = SIT_I(sbi);
> >>> @@ -2758,8 +2816,13 @@ static int is_next_segment_free(struct f2fs_sb_info *sbi,
> >>> unsigned int segno = curseg->segno + 1;
> >>> struct free_segmap_info *free_i = FREE_I(sbi);
> >>>
> >>> - if (segno < MAIN_SEGS(sbi) && segno % SEGS_PER_SEC(sbi))
> >>> + if (segno < MAIN_SEGS(sbi) && segno % SEGS_PER_SEC(sbi)) {
> >>> + int devi = f2fs_target_device_index(sbi, START_BLOCK(sbi, segno));
> >>> +
> >>> + if (f2fs_dev_is_reserving(sbi, devi))
> >>> + return 0;
> >>> return !test_bit(segno, free_i->free_segmap);
> >>> + }
> >>> return 0;
> >>> }
> >>>
> >>> @@ -2778,7 +2841,8 @@ static int get_new_segment(struct f2fs_sb_info *sbi,
> >>> unsigned int alloc_policy = sbi->allocate_section_policy;
> >>> unsigned int alloc_hint = sbi->allocate_section_hint;
> >>> bool init = true;
> >>> - int i;
> >>> + bool looped = false;
> >>> + int i, devi;
> >>> int ret = 0;
> >>>
> >>> spin_lock(&free_i->segmap_lock);
> >>> @@ -2791,8 +2855,13 @@ static int get_new_segment(struct f2fs_sb_info *sbi,
> >>> if (!new_sec && ((*newseg + 1) % SEGS_PER_SEC(sbi))) {
> >>> segno = find_next_zero_bit(free_i->free_segmap,
> >>> GET_SEG_FROM_SEC(sbi, hint + 1), *newseg + 1);
> >>> - if (segno < GET_SEG_FROM_SEC(sbi, hint + 1))
> >>> + if (segno < GET_SEG_FROM_SEC(sbi, hint + 1)) {
> >>> + devi = f2fs_target_device_index(sbi, START_BLOCK(sbi, segno));
> >>> +
> >>> + if (f2fs_dev_is_alloc_blocked(sbi, devi, pinning))
> >>> + goto find_other_zone;
> >>> goto got_it;
> >>> + }
> >>> }
> >>>
> >>> #ifdef CONFIG_BLK_DEV_ZONED
> >>> @@ -2828,33 +2897,42 @@ static int get_new_segment(struct f2fs_sb_info *sbi,
> >>> find_other_zone:
> >>> secno = find_next_zero_bit(free_i->free_secmap, MAIN_SECS(sbi), hint);
> >>>
> >>> -#ifdef CONFIG_BLK_DEV_ZONED
> >>> - if (secno >= MAIN_SECS(sbi) && f2fs_sb_has_blkzoned(sbi)) {
> >>> - /* Write only to sequential zones */
> >>> - if (sbi->blkzone_alloc_policy == BLKZONE_ALLOC_ONLY_SEQ) {
> >>> - hint = GET_SEC_FROM_SEG(sbi, sbi->first_seq_zone_segno);
> >>> - secno = find_next_zero_bit(free_i->free_secmap, MAIN_SECS(sbi), hint);
> >>> - } else
> >>> - secno = find_first_zero_bit(free_i->free_secmap,
> >>> - MAIN_SECS(sbi));
> >>> - if (secno >= MAIN_SECS(sbi)) {
> >>> - ret = -ENOSPC;
> >>> - f2fs_bug_on(sbi, 1);
> >>> - goto out_unlock;
> >>> - }
> >>> - }
> >>> -#endif
> >>> -
> >>> if (secno >= MAIN_SECS(sbi)) {
> >>> - secno = find_first_zero_bit(free_i->free_secmap,
> >>> - MAIN_SECS(sbi));
> >>> - if (secno >= MAIN_SECS(sbi)) {
> >>> + if (looped) {
> >>> ret = -ENOSPC;
> >>> f2fs_bug_on(sbi, !pinning);
> >>> goto out_unlock;
> >>> }
> >>> + hint = 0;
> >>> +#ifdef CONFIG_BLK_DEV_ZONED
> >>> + /* Write only to sequential zones */
> >>> + if (f2fs_sb_has_blkzoned(sbi) &&
> >>> + sbi->blkzone_alloc_policy == BLKZONE_ALLOC_ONLY_SEQ)
> >>> + hint = GET_SEC_FROM_SEG(sbi, sbi->first_seq_zone_segno);
> >>> +#endif
> >>> + looped = true;
> >>> + goto find_other_zone;
> >>> }
> >>> +
> >>> segno = GET_SEG_FROM_SEC(sbi, secno);
> >>> +
> >>> + devi = f2fs_target_device_index(sbi, START_BLOCK(sbi, segno));
> >>> +
> >>> + if (f2fs_dev_is_alloc_blocked(sbi, devi, pinning)) {
> >>> + while (devi < sbi->s_ndevs &&
> >>> + f2fs_dev_is_alloc_blocked(sbi, devi, pinning)) {
> >>> + unsigned int end_segno = GET_SEGNO(sbi, FDEV(devi).end_blk);
> >>> +
> >>> + hint = GET_SEC_FROM_SEG(sbi, end_segno) + 1;
> >>> + devi++;
> >>> + }
> >>> + goto find_other_zone;
> >>> + }
> >>> +
> >>> + if (sec_usage_check(sbi, secno)) {
> >>> + hint = secno + 1;
> >>> + goto find_other_zone;
> >>> + }
> >>> zoneno = GET_ZONE_FROM_SEC(sbi, secno);
> >>>
> >>> /* give up on finding another zone */
> >>> diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h
> >>> index b0c06b3580b4..107508ae7eb3 100644
> >>> --- a/fs/f2fs/segment.h
> >>> +++ b/fs/f2fs/segment.h
> >>> @@ -954,10 +954,32 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type)
> >>> - (base + 1) + type;
> >>> }
> >>>
> >>> +static inline bool f2fs_dev_is_reserving(struct f2fs_sb_info *sbi, int devi)
> >>> +{
> >>> + if (!f2fs_sb_has_device_alias(sbi))
> >>> + return false;
> >>> + return FDEV(devi).is_reserving;
> >>> +}
> >>> +
> >>> +static inline bool f2fs_dev_is_alloc_blocked(struct f2fs_sb_info *sbi,
> >>> + int devi, bool pinning)
> >>> +{
> >>> + if (!f2fs_sb_has_device_alias(sbi))
> >>> + return false;
> >>> + return (pinning && FDEV(devi).has_alias) || FDEV(devi).is_reserving;
> >>> +}
> >>> +
> >>> static inline bool sec_usage_check(struct f2fs_sb_info *sbi, unsigned int secno)
> >>> {
> >>> if (is_cursec(sbi, secno) || (sbi->cur_victim_sec == secno))
> >>> return true;
> >>> + if (f2fs_sb_has_device_alias(sbi)) {
> >>> + block_t start_blk = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, secno));
> >>> + int devi = f2fs_target_device_index(sbi, start_blk);
> >>> +
> >>> + if (f2fs_dev_is_reserving(sbi, devi))
> >>> + return true;
> >>> + }
> >>> return false;
> >>> }
> >>>
> >>> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> >>> index c448d992ff2a..bca1475cb573 100644
> >>> --- a/fs/f2fs/super.c
> >>> +++ b/fs/f2fs/super.c
> >>> @@ -5001,6 +5001,38 @@ static void f2fs_tuning_parameters(struct f2fs_sb_info *sbi)
> >>> sbi->readdir_ra = true;
> >>> }
> >>>
> >>> +static void f2fs_restore_device_alias(struct f2fs_sb_info *sbi)
> >>> +{
> >>> + struct inode *root = d_inode(sbi->sb->s_root);
> >>> + struct f2fs_dir_entry *de;
> >>> + struct folio *folio;
> >>> + int i;
> >>> +
> >>> + if (!f2fs_sb_has_device_alias(sbi))
> >>> + return;
> >>> +
> >>> + for (i = 1; i < sbi->s_ndevs; i++) {
> >>> + char *name = strrchr(FDEV(i).path, '/');
> >>> + struct qstr qstr;
> >>> +
> >>> + name = name ? name + 1 : FDEV(i).path;
> >>> + qstr.name = name;
> >>> + qstr.len = strlen(name);
> >>> +
> >>> + de = f2fs_find_entry(root, &qstr, &folio);
> >>> + if (de) {
> >>
> >> Trivial cleaup to avoid long line.
> >>
> >> if (!de)
> >> continue;
> >>
> >> Thanks,
> >>
> >>> + struct inode *inode = f2fs_iget(sbi->sb, le32_to_cpu(de->ino));
> >>> +
> >>> + if (!IS_ERR(inode)) {
> >>> + if (IS_DEVICE_ALIASING(inode))
> >>> + FDEV(i).has_alias = true;
> >>> + iput(inode);
> >>> + }
> >>> + f2fs_folio_put(folio, 0);
> >>> + }
> >>> + }
> >>> +}
> >>> +
> >>> static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
> >>> {
> >>> struct f2fs_fs_context *ctx = fc->fs_private;
> >>> @@ -5209,6 +5241,7 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
> >>> sbi->last_valid_block_count = sbi->total_valid_block_count;
> >>> sbi->reserved_blocks = 0;
> >>> sbi->current_reserved_blocks = 0;
> >>> + sbi->alias_reserved_blocks = 0;
> >>> limit_reserve_root(sbi);
> >>> adjust_unusable_cap_perc(sbi);
> >>>
> >>> @@ -5436,6 +5469,8 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
> >>> f2fs_update_time(sbi, REQ_TIME);
> >>> clear_sbi_flag(sbi, SBI_CP_DISABLED_QUICK);
> >>>
> >>> + f2fs_restore_device_alias(sbi);
> >>> +
> >>> sbi->umount_lock_holder = NULL;
> >>> return 0;
> >>>
> >>> diff --git a/include/uapi/linux/f2fs.h b/include/uapi/linux/f2fs.h
> >>> index 795e26258355..4409ada2fecb 100644
> >>> --- a/include/uapi/linux/f2fs.h
> >>> +++ b/include/uapi/linux/f2fs.h
> >>> @@ -45,6 +45,9 @@
> >>> #define F2FS_IOC_START_ATOMIC_REPLACE _IO(F2FS_IOCTL_MAGIC, 25)
> >>> #define F2FS_IOC_GET_DEV_ALIAS_FILE _IOR(F2FS_IOCTL_MAGIC, 26, __u32)
> >>> #define F2FS_IOC_IO_PRIO _IOW(F2FS_IOCTL_MAGIC, 27, __u32)
> >>> +#define F2FS_IOC_RESERVE_DEV_ALIAS _IO(F2FS_IOCTL_MAGIC, 28)
> >>> +#define F2FS_IOC_RELEASE_DEV_ALIAS _IO(F2FS_IOCTL_MAGIC, 29)
> >>> +#define F2FS_IOC_GET_DEV_ALIAS_STATUS _IOR(F2FS_IOCTL_MAGIC, 30, __u32)
> >>>
> >>> /*
> >>> * should be same as XFS_IOC_GOINGDOWN.
> >>> @@ -70,6 +73,10 @@ enum {
> >>> F2FS_IOPRIO_MAX,
> >>> };
> >>>
> >>> +/* for F2FS_IOC_GET_DEV_ALIAS_STATUS */
> >>> +#define F2FS_DEV_ALIAS_STATUS_RELEASED 0
> >>> +#define F2FS_DEV_ALIAS_STATUS_RESERVED 1
> >>> +
> >>> struct f2fs_gc_range {
> >>> __u32 sync;
> >>> __u64 start;
> >>
>