The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Chao Yu <chao@kernel.org>
To: Daeho Jeong <daeho43@gmail.com>
Cc: chao@kernel.org, linux-kernel@vger.kernel.org,
	linux-f2fs-devel@lists.sourceforge.net, kernel-team@android.com,
	Daeho Jeong <daehojeong@google.com>
Subject: Re: [f2fs-dev] [PATCH v6] f2fs: support dynamic reserve/release for device aliasing
Date: Wed, 5 Aug 2026 09:19:46 +0800	[thread overview]
Message-ID: <a325deb7-5371-46e7-a185-e6a781371142@kernel.org> (raw)
In-Reply-To: <CACOAw_w1fcBgGkW3MT=NgjWp1B5-1FSgMe2Ofm0AKcQU4OsBWA@mail.gmail.com>

On 8/5/26 01:08, Daeho Jeong wrote:
> On Mon, Aug 3, 2026 at 5:42 PM Chao Yu <chao@kernel.org> wrote:
>>
>> On 8/4/26 00:18, Daeho Jeong wrote:
>>> On Sun, Aug 2, 2026 at 7:00 PM Chao Yu <chao@kernel.org> wrote:
>>>>
>>>> On 7/30/26 23:46, Daeho Jeong wrote:
>>>>> From: Daeho Jeong <daehojeong@google.com>
>>>>>
>>>>> 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@google.com>
>>>>> ---
>>>>> v6: fixed duplicated block reservations and racing w/ mmap.
>>>>>       added the extent alignment check for device aliasing areas.
>>>>> 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/data.c                     |   4 +-
>>>>>    fs/f2fs/extent_cache.c             |   7 +
>>>>>    fs/f2fs/f2fs.h                     |  19 +-
>>>>>    fs/f2fs/file.c                     | 273 ++++++++++++++++++++++++++++-
>>>>>    fs/f2fs/gc.c                       |  30 ++--
>>>>>    fs/f2fs/namei.c                    |  14 ++
>>>>>    fs/f2fs/segment.c                  | 176 +++++++++++++------
>>>>>    fs/f2fs/segment.h                  |  22 +++
>>>>>    fs/f2fs/super.c                    |  36 ++++
>>>>>    include/uapi/linux/f2fs.h          |   7 +
>>>>>    11 files changed, 555 insertions(+), 68 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/data.c b/fs/f2fs/data.c
>>>>> index ac1cf4de3d62..892d594048b7 100644
>>>>> --- a/fs/f2fs/data.c
>>>>> +++ b/fs/f2fs/data.c
>>>>> @@ -1270,7 +1270,7 @@ int f2fs_reserve_new_blocks(struct dnode_of_data *dn, blkcnt_t count)
>>>>>
>>>>>        if (unlikely(is_inode_flag_set(dn->inode, FI_NO_ALLOC)))
>>>>>                return -EPERM;
>>>>> -     err = inc_valid_block_count(sbi, dn->inode, &count, true);
>>>>> +     err = inc_valid_block_count(sbi, dn->inode, &count, true, false);
>>>>>        if (unlikely(err))
>>>>>                return err;
>>>>>
>>>>> @@ -1542,7 +1542,7 @@ static int __allocate_data_block(struct dnode_of_data *dn, int seg_type)
>>>>>
>>>>>        dn->data_blkaddr = f2fs_data_blkaddr(dn);
>>>>>        if (dn->data_blkaddr == NULL_ADDR) {
>>>>> -             err = inc_valid_block_count(sbi, dn->inode, &count, true);
>>>>> +             err = inc_valid_block_count(sbi, dn->inode, &count, true, false);
>>>>>                if (unlikely(err))
>>>>>                        return err;
>>>>>        }
>>>>> diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
>>>>> index 61f6b9714366..3a737db0c9e1 100644
>>>>> --- a/fs/f2fs/extent_cache.c
>>>>> +++ b/fs/f2fs/extent_cache.c
>>>>> @@ -62,6 +62,13 @@ bool sanity_check_extent_cache(struct inode *inode, struct folio *ifolio)
>>>>>                            __func__, inode->i_ino, ei.blk, ei.fofs, ei.len);
>>>>>                        return false;
>>>>>                }
>>>>> +
>>>>> +             if ((ei.blk % BLKS_PER_SEC(sbi)) || (ei.len % BLKS_PER_SEC(sbi))) {
>>>>> +                     f2fs_warn(sbi, "%s: device alias inode (ino=%llx)'s extent info [%u, %u, %u] is not aligned to section size %u",
>>>>> +                               __func__, inode->i_ino, ei.blk, ei.fofs, ei.len,
>>>>> +                               BLKS_PER_SEC(sbi));
>>>>> +                     return false;
>>>>> +             }
>>>>>                return true;
>>>>>        }
>>>>>
>>>>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>>>>> index f1774d4e18d2..f24e30bb5c3d 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;
>>>>> @@ -2576,7 +2580,8 @@ static inline unsigned int get_available_block_count(struct f2fs_sb_info *sbi,
>>>>>
>>>>>    static inline void f2fs_i_blocks_write(struct inode *, block_t, bool, bool);
>>>>>    static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,
>>>>> -                              struct inode *inode, blkcnt_t *count, bool partial)
>>>>> +                              struct inode *inode, blkcnt_t *count,
>>>>> +                              bool partial, bool alias_reserved)
>>>>>    {
>>>>>        long long diff = 0, release = 0;
>>>>>        block_t avail_user_block_count;
>>>>> @@ -2599,10 +2604,16 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,
>>>>>
>>>>>        spin_lock(&sbi->stat_lock);
>>>>>
>>>>> +     if (alias_reserved)
>>>>> +             sbi->alias_reserved_blocks -= *count;
>>>>> +
>>>>>        avail_user_block_count = get_available_block_count(sbi, inode, true);
>>>>>        diff = (long long)sbi->total_valid_block_count + *count -
>>>>>                                                avail_user_block_count;
>>>>>        if (unlikely(diff > 0)) {
>>>>> +             if (alias_reserved)
>>>>> +                     sbi->alias_reserved_blocks += *count;
>>>>> +
>>>>>                if (!partial) {
>>>>>                        spin_unlock(&sbi->stat_lock);
>>>>>                        release = *count;
>>>>> @@ -4010,6 +4021,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 +4244,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..835f10bc59b5 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,241 @@ 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))
>>>>> +                     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, true);
>>>>> +     if (err) {
>>>>> +             f2fs_unlock_op(sbi, &lc);
>>>>> +             goto out_gc_unlock;
>>>>> +     }
>>>>> +
>>>>> +     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 << sbi->log_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);
>>>>
>>>> Do we need to call f2fs_write_checkpoint(sbi, &cpc) here to make sure persist
>>>> device alias file first? to avoid following f2fs_ioc_release_dev_alias() racin
>>>>    w/ f2fs_ioc_reserve_dev_alias()?
>>>>
>>>> - f2fs_ioc_reserve_dev_alias            - f2fs_ioc_release_dev_alias
>>>> ...
>>>>    - f2fs_unlock_op
>>>>    - f2fs_up_write_trace(gc_lock)
>>>>    - inode_unlock
>>>>                                           - inode_lock
>>>>                                           ...
>>>>    - f2fs_write_checkpoint
>>>
>>> Hi Chao,
>>>
>>> I couldn't get your point. Could you elaborate more, if I missed it?
>>
>> Hi Daeho,
>>
>> Actually, what I mean is if f2fs_write_checkpoint() is not covered by
>> inode lock, in race condition there will be below invoking serial:
>>
>> Assume:
>> f2fs_ioc_reserve_dev_alias := reserve_dev_alias + f2fs_write_checkpoint
>> f2fs_ioc_release_dev_alias := release_dev_alias + f2fs_write_checkpoint
>>
>> 1) Thread A: reserve_dev_alias
>> 2) Thread B: release_dev_alias
>> 3) Thread C: reserve_dev_alias
>> 4) Thread B: f2fs_write_checkpoint
>> 5) Thread A: f2fs_write_checkpoint
>> 6) Thread C: f2fs_write_checkpoint
>>
>> After 1) all data after reserve_dev_alias() are not checkpointed, then the
>> following release_dev_alias in 2) won't encounter any issue, right? and the
>> same quetions for 3).
>>
>> IOW, reserve_dev_alias/release_dev_alias doesn't need to rely on checkpointed
>> state of each other, am I understand it correctly? if so there won't be any
>> issue.
>>
>>>
>>> 1. In-memory state is already atomic (No Race):
>>>      Before inode_unlock(), all in-memory metadata (extent_tree, SIT
>>> segment bitmaps, i_size, and dirty inode page) are fully updated under
>>> inode_lock + f2fs_lock_op. A subsequent f2fs_ioc_release_dev_alias()
>>> will observe a complete, consistent state.
>>>
>>> 2. On-disk consistency & crash safety:
>>>      Checkpoints are serialized internally via cp_mutex /
>>> f2fs_lock_all(). Regardless of the checkpoint execution timing or any
>>> sudden power-off (SPO) between the two ioctls, the filesystem always
>>> recovers to the last committed atomic checkpoint without metadata
>>> corruption.
>>
>> Thanks for the explanation.
>>
>> Thanks,
> 
> 
> Yes, your understanding is completely correct!
> 
> Neither reserve_dev_alias() nor release_dev_alias() relies on the
> checkpointed state of previous operations.
> They only depend on the in-memory metadata (extent tree, SIT bitmaps,
> i_size, etc.), which is already atomically updated and fully
> synchronized by inode_lock and f2fs_lock_op.
> Therefore, the interleaved checkpoint scenario you described won't
> cause any issue at all.

It make sense, thanks a lot for the explanation!

Thanks,

> 
> Thanks,
> 
>>
>>>
>>> Thanks,
>>>
>>>>
>>>>> +
>>>>> +     inode_unlock(inode);
>>>>> +     mnt_drop_write_file(filp);
>>>>> +
>>>>> +     return f2fs_write_checkpoint(sbi, &cpc);
>>>>> +
>>>>> +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;
>>>>> +}
>>>>> +
>>>>> +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);
>>>>> +
>>>>> +     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;
>>>>> +     }
>>>>> +
>>>>> +     filemap_invalidate_lock(inode->i_mapping);
>>>>> +     truncate_setsize(inode, 0);
>>>>> +
>>>>> +     err = f2fs_truncate_blocks(inode, 0, false);
>>>>> +     if (err)
>>>>> +             i_size_write(inode, (loff_t)ei.len << sbi->log_blocksize);
>>>>> +     filemap_invalidate_unlock(inode->i_mapping);
>>>>> +
>>>>> +     if (err) {
>>>>> +             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);
>>>>
>>>>          err = f2fs_write_checkpoint(sbi, &cpc);
>>>>
>>>> Thanks,
>>>>
>>>>> +
>>>>> +     inode_unlock(inode);
>>>>> +     mnt_drop_write_file(filp);
>>>>> +
>>>>> +     return f2fs_write_checkpoint(sbi, &cpc);
>>>>> +
>>>>> +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);
>>>>> @@ -4041,7 +4297,7 @@ static int reserve_compress_blocks(struct dnode_of_data *dn, pgoff_t count,
>>>>>                }
>>>>>
>>>>>                ret = inc_valid_block_count(sbi, dn->inode,
>>>>> -                                             &to_reserved, false);
>>>>> +                                             &to_reserved, false, false);
>>>>>                if (unlikely(ret))
>>>>>                        return ret;
>>>>>
>>>>> @@ -4742,8 +4998,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 +5792,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..d70dc5ef3de4 100644
>>>>> --- a/fs/f2fs/segment.c
>>>>> +++ b/fs/f2fs/segment.c
>>>>> @@ -261,7 +261,7 @@ static int __replace_atomic_write_block(struct inode *inode, pgoff_t index,
>>>>>        } else {
>>>>>                blkcnt_t count = 1;
>>>>>
>>>>> -             err = inc_valid_block_count(sbi, inode, &count, true);
>>>>> +             err = inc_valid_block_count(sbi, inode, &count, true, false);
>>>>>                if (err) {
>>>>>                        f2fs_put_dnode(&dn);
>>>>>                        return err;
>>>>> @@ -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..d5dc83e613e2 100644
>>>>> --- a/fs/f2fs/super.c
>>>>> +++ b/fs/f2fs/super.c
>>>>> @@ -5001,6 +5001,39 @@ 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 inode *inode;
>>>>> +             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)
>>>>> +                     continue;
>>>>> +
>>>>> +             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 +5242,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 +5470,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;
>>>>
>>


  reply	other threads:[~2026-08-05  1:19 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-30 15:46 [PATCH v6] f2fs: support dynamic reserve/release for device aliasing Daeho Jeong
2026-08-03  2:00 ` [f2fs-dev] " Chao Yu
2026-08-03 16:18   ` Daeho Jeong
2026-08-04  0:42     ` Chao Yu
2026-08-04 17:08       ` Daeho Jeong
2026-08-05  1:19         ` Chao Yu [this message]
2026-08-05  1:20 ` Chao Yu
2026-08-05 16:43   ` Daeho Jeong
2026-08-06  2:42     ` Chao Yu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a325deb7-5371-46e7-a185-e6a781371142@kernel.org \
    --to=chao@kernel.org \
    --cc=daeho43@gmail.com \
    --cc=daehojeong@google.com \
    --cc=kernel-team@android.com \
    --cc=linux-f2fs-devel@lists.sourceforge.net \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox