Re: [f2fs-dev] [PATCH v6] f2fs: support dynamic reserve/release for device aliasing
Chao Yu <[email protected]> Tue, 4 Aug 2026 08:42:06 +0800
| Newsgroups | gmane.linux.kernel,gmane.linux.file-systems.f2fs |
|---|---|
| Message-ID | <[email protected]> |
On 8/4/26 00:18, Daeho Jeong wrote: > On Sun, Aug 2, 2026 at 7:00 PM Chao Yu <[email protected]> wrote: >> >> On 7/30/26 23:46, Daeho Jeong wrote: >>> From: Daeho Jeong <[email protected]> >>> >>> 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 <[email protected]> >>> --- >>> 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, > > 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; >>