Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit 4996d55

Browse files
Daeho JeongTreehugger Robot
authored andcommitted
FROMGIT: f2fs: introduce device aliasing file
F2FS should understand how the device aliasing file works and support deleting the file after use. A device aliasing file can be created by mkfs.f2fs tool and it can map the whole device with an extent, not using node blocks. The file space should be pinned and normally used for read-only usages. Bug: 336319772 Change-Id: Ifbac959ef42e5c8af790bb1e0f9f4da9f5e1b592 Link: https://lore.kernel.org/lkml/[email protected]/ Signed-off-by: Daeho Jeong <[email protected]> Signed-off-by: Chao Yu <[email protected]> Reviewed-by: Chao Yu <[email protected]> Signed-off-by: Jaegeuk Kim <[email protected]> (cherry picked from commit 495494c3037e439c5bdb7b3514924bc35a47494e https: //git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git/ dev)
1 parent bd8c55b commit 4996d55

File tree

9 files changed

+164
-6
lines changed

9 files changed

+164
-6
lines changed

Documentation/filesystems/f2fs.rst

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,3 +943,47 @@ NVMe Zoned Namespace devices
943943
can start before the zone-capacity and span across zone-capacity boundary.
944944
Such spanning segments are also considered as usable segments. All blocks
945945
past the zone-capacity are considered unusable in these segments.
946+
947+
Device aliasing feature
948+
-----------------------
949+
950+
f2fs can utilize a special file called a "device aliasing file." This file allows
951+
the entire storage device to be mapped with a single, large extent, not using
952+
the usual f2fs node structures. This mapped area is pinned and primarily intended
953+
for holding the space.
954+
955+
Essentially, this mechanism allows a portion of the f2fs area to be temporarily
956+
reserved and used by another filesystem or for different purposes. Once that
957+
external usage is complete, the device aliasing file can be deleted, releasing
958+
the reserved space back to F2FS for its own use.
959+
960+
<use-case>
961+
962+
# ls /dev/vd*
963+
/dev/vdb (32GB) /dev/vdc (32GB)
964+
# mkfs.ext4 /dev/vdc
965+
# mkfs.f2fs -c /dev/[email protected] /dev/vdb
966+
# mount /dev/vdb /mnt/f2fs
967+
# ls -l /mnt/f2fs
968+
vdc.file
969+
# df -h
970+
/dev/vdb 64G 33G 32G 52% /mnt/f2fs
971+
972+
# mount -o loop /dev/vdc /mnt/ext4
973+
# df -h
974+
/dev/vdb 64G 33G 32G 52% /mnt/f2fs
975+
/dev/loop7 32G 24K 30G 1% /mnt/ext4
976+
# umount /mnt/ext4
977+
978+
# f2fs_io getflags /mnt/f2fs/vdc.file
979+
get a flag on /mnt/f2fs/vdc.file ret=0, flags=nocow(pinned),immutable
980+
# f2fs_io setflags noimmutable /mnt/f2fs/vdc.file
981+
get a flag on noimmutable ret=0, flags=800010
982+
set a flag on /mnt/f2fs/vdc.file ret=0, flags=noimmutable
983+
# rm /mnt/f2fs/vdc.file
984+
# df -h
985+
/dev/vdb 64G 753M 64G 2% /mnt/f2fs
986+
987+
So, the key idea is, user can do any file operations on /dev/vdc, and
988+
reclaim the space after the use, while the space is counted as /data.
989+
That doesn't require modifying partition size and filesystem format.

fs/f2fs/data.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3463,6 +3463,11 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
34633463

34643464
if (!f2fs_lookup_read_extent_cache_block(inode, index,
34653465
&dn.data_blkaddr)) {
3466+
if (IS_DEVICE_ALIASING(inode)) {
3467+
err = -ENODATA;
3468+
goto out;
3469+
}
3470+
34663471
if (locked) {
34673472
err = f2fs_reserve_block(&dn, index);
34683473
goto out;

fs/f2fs/extent_cache.c

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ bool sanity_check_extent_cache(struct inode *inode, struct page *ipage)
2424
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
2525
struct f2fs_extent *i_ext = &F2FS_INODE(ipage)->i_ext;
2626
struct extent_info ei;
27+
int devi;
2728

2829
get_read_extent_info(&ei, i_ext);
2930

@@ -38,7 +39,36 @@ bool sanity_check_extent_cache(struct inode *inode, struct page *ipage)
3839
ei.blk, ei.fofs, ei.len);
3940
return false;
4041
}
41-
return true;
42+
43+
if (!IS_DEVICE_ALIASING(inode))
44+
return true;
45+
46+
for (devi = 0; devi < sbi->s_ndevs; devi++) {
47+
if (FDEV(devi).start_blk != ei.blk ||
48+
FDEV(devi).end_blk != ei.blk + ei.len - 1)
49+
continue;
50+
51+
if (devi == 0) {
52+
f2fs_warn(sbi,
53+
"%s: inode (ino=%lx) is an alias of meta device",
54+
__func__, inode->i_ino);
55+
return false;
56+
}
57+
58+
if (bdev_is_zoned(FDEV(devi).bdev)) {
59+
f2fs_warn(sbi,
60+
"%s: device alias inode (ino=%lx)'s extent info "
61+
"[%u, %u, %u] maps to zoned block device",
62+
__func__, inode->i_ino, ei.blk, ei.fofs, ei.len);
63+
return false;
64+
}
65+
return true;
66+
}
67+
68+
f2fs_warn(sbi, "%s: device alias inode (ino=%lx)'s extent info "
69+
"[%u, %u, %u] is inconsistent w/ any devices",
70+
__func__, inode->i_ino, ei.blk, ei.fofs, ei.len);
71+
return false;
4272
}
4373

4474
static void __set_extent_info(struct extent_info *ei,
@@ -76,6 +106,9 @@ static bool __init_may_extent_tree(struct inode *inode, enum extent_type type)
76106

77107
static bool __may_extent_tree(struct inode *inode, enum extent_type type)
78108
{
109+
if (IS_DEVICE_ALIASING(inode) && type == EX_READ)
110+
return true;
111+
79112
/*
80113
* for recovered files during mount do not create extents
81114
* if shrinker is not registered.
@@ -401,6 +434,11 @@ void f2fs_init_read_extent_tree(struct inode *inode, struct page *ipage)
401434
if (atomic_read(&et->node_cnt) || !ei.len)
402435
goto skip;
403436

437+
if (IS_DEVICE_ALIASING(inode)) {
438+
et->largest = ei;
439+
goto skip;
440+
}
441+
404442
en = __attach_extent_node(sbi, et, &ei, NULL,
405443
&et->root.rb_root.rb_node, true);
406444
if (en) {
@@ -463,6 +501,11 @@ static bool __lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
463501
goto out;
464502
}
465503

504+
if (IS_DEVICE_ALIASING(inode)) {
505+
ret = false;
506+
goto out;
507+
}
508+
466509
en = __lookup_extent_node(&et->root, et->cached_en, pgofs);
467510
if (!en)
468511
goto out;

fs/f2fs/f2fs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ struct f2fs_mount_info {
213213
#define F2FS_FEATURE_CASEFOLD 0x00001000
214214
#define F2FS_FEATURE_COMPRESSION 0x00002000
215215
#define F2FS_FEATURE_RO 0x00004000
216+
#define F2FS_FEATURE_DEVICE_ALIAS 0x00008000
216217

217218
#define __F2FS_HAS_FEATURE(raw_super, mask) \
218219
((raw_super->feature & cpu_to_le32(mask)) != 0)
@@ -3052,6 +3053,7 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
30523053
#define F2FS_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */
30533054
#define F2FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
30543055
#define F2FS_CASEFOLD_FL 0x40000000 /* Casefolded file */
3056+
#define F2FS_DEVICE_ALIAS_FL 0x80000000 /* File for aliasing a device */
30553057

30563058
#define F2FS_QUOTA_DEFAULT_FL (F2FS_NOATIME_FL | F2FS_IMMUTABLE_FL)
30573059

@@ -3067,6 +3069,8 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
30673069
/* Flags that are appropriate for non-directories/regular files. */
30683070
#define F2FS_OTHER_FLMASK (F2FS_NODUMP_FL | F2FS_NOATIME_FL)
30693071

3072+
#define IS_DEVICE_ALIASING(inode) (F2FS_I(inode)->i_flags & F2FS_DEVICE_ALIAS_FL)
3073+
30703074
static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags)
30713075
{
30723076
if (S_ISDIR(mode))
@@ -4500,6 +4504,7 @@ F2FS_FEATURE_FUNCS(sb_chksum, SB_CHKSUM);
45004504
F2FS_FEATURE_FUNCS(casefold, CASEFOLD);
45014505
F2FS_FEATURE_FUNCS(compression, COMPRESSION);
45024506
F2FS_FEATURE_FUNCS(readonly, RO);
4507+
F2FS_FEATURE_FUNCS(device_alias, DEVICE_ALIAS);
45034508

45044509
#ifdef CONFIG_BLK_DEV_ZONED
45054510
static inline bool f2fs_blkz_is_seq(struct f2fs_sb_info *sbi, int devi,

fs/f2fs/file.c

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,11 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock)
731731

732732
trace_f2fs_truncate_blocks_enter(inode, from);
733733

734+
if (IS_DEVICE_ALIASING(inode) && from) {
735+
err = -EINVAL;
736+
goto out_err;
737+
}
738+
734739
free_from = (pgoff_t)F2FS_BLK_ALIGN(from);
735740

736741
if (free_from >= max_file_blocks(inode))
@@ -745,6 +750,21 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock)
745750
goto out;
746751
}
747752

753+
if (IS_DEVICE_ALIASING(inode)) {
754+
struct extent_tree *et = F2FS_I(inode)->extent_tree[EX_READ];
755+
struct extent_info ei = et->largest;
756+
unsigned int i;
757+
758+
for (i = 0; i < ei.len; i++)
759+
f2fs_invalidate_blocks(sbi, ei.blk + i);
760+
761+
dec_valid_block_count(sbi, inode, ei.len);
762+
f2fs_update_time(sbi, REQ_TIME);
763+
764+
f2fs_put_page(ipage, 1);
765+
goto out;
766+
}
767+
748768
if (f2fs_has_inline_data(inode)) {
749769
f2fs_truncate_inline_inode(inode, ipage, from);
750770
f2fs_put_page(ipage, 1);
@@ -780,7 +800,7 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock)
780800
/* lastly zero out the first data page */
781801
if (!err)
782802
err = truncate_partial_data_page(inode, from, truncate_page);
783-
803+
out_err:
784804
trace_f2fs_truncate_blocks_exit(inode, err);
785805
return err;
786806
}
@@ -1000,7 +1020,8 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
10001020
return -EPERM;
10011021

10021022
if ((attr->ia_valid & ATTR_SIZE)) {
1003-
if (!f2fs_is_compress_backend_ready(inode))
1023+
if (!f2fs_is_compress_backend_ready(inode) ||
1024+
IS_DEVICE_ALIASING(inode))
10041025
return -EOPNOTSUPP;
10051026
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED) &&
10061027
!IS_ALIGNED(attr->ia_size,
@@ -1868,7 +1889,7 @@ static long f2fs_fallocate(struct file *file, int mode,
18681889
return -EIO;
18691890
if (!f2fs_is_checkpoint_ready(F2FS_I_SB(inode)))
18701891
return -ENOSPC;
1871-
if (!f2fs_is_compress_backend_ready(inode))
1892+
if (!f2fs_is_compress_backend_ready(inode) || IS_DEVICE_ALIASING(inode))
18721893
return -EOPNOTSUPP;
18731894

18741895
/* f2fs only support ->fallocate for regular file */
@@ -3304,6 +3325,9 @@ int f2fs_pin_file_control(struct inode *inode, bool inc)
33043325
struct f2fs_inode_info *fi = F2FS_I(inode);
33053326
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
33063327

3328+
if (IS_DEVICE_ALIASING(inode))
3329+
return -EINVAL;
3330+
33073331
if (fi->i_gc_failures >= sbi->gc_pin_file_threshold) {
33083332
f2fs_warn(sbi, "%s: Enable GC = ino %lx after %x GC trials",
33093333
__func__, inode->i_ino, fi->i_gc_failures);
@@ -3334,6 +3358,9 @@ static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg)
33343358
if (f2fs_readonly(sbi->sb))
33353359
return -EROFS;
33363360

3361+
if (!pin && IS_DEVICE_ALIASING(inode))
3362+
return -EOPNOTSUPP;
3363+
33373364
ret = mnt_want_write_file(filp);
33383365
if (ret)
33393366
return ret;
@@ -3399,6 +3426,12 @@ static int f2fs_ioc_get_pin_file(struct file *filp, unsigned long arg)
33993426
return put_user(pin, (u32 __user *)arg);
34003427
}
34013428

3429+
static int f2fs_ioc_get_dev_alias_file(struct file *filp, unsigned long arg)
3430+
{
3431+
return put_user(IS_DEVICE_ALIASING(file_inode(filp)) ? 1 : 0,
3432+
(u32 __user *)arg);
3433+
}
3434+
34023435
int f2fs_precache_extents(struct inode *inode)
34033436
{
34043437
struct f2fs_inode_info *fi = F2FS_I(inode);
@@ -4498,6 +4531,8 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
44984531
return f2fs_ioc_decompress_file(filp);
44994532
case F2FS_IOC_COMPRESS_FILE:
45004533
return f2fs_ioc_compress_file(filp);
4534+
case F2FS_IOC_GET_DEV_ALIAS_FILE:
4535+
return f2fs_ioc_get_dev_alias_file(filp, arg);
45014536
default:
45024537
return -ENOTTY;
45034538
}
@@ -4773,7 +4808,8 @@ static int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *iter,
47734808
else
47744809
return 0;
47754810

4776-
map.m_may_create = true;
4811+
if (!IS_DEVICE_ALIASING(inode))
4812+
map.m_may_create = true;
47774813
if (dio) {
47784814
map.m_seg_type = f2fs_rw_hint_to_seg_type(sbi,
47794815
inode->i_write_hint);
@@ -5210,6 +5246,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
52105246
case F2FS_IOC_SET_COMPRESS_OPTION:
52115247
case F2FS_IOC_DECOMPRESS_FILE:
52125248
case F2FS_IOC_COMPRESS_FILE:
5249+
case F2FS_IOC_GET_DEV_ALIAS_FILE:
52135250
break;
52145251
default:
52155252
return -ENOIOCTLCMD;

fs/f2fs/inode.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,19 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page)
372372
return false;
373373
}
374374

375+
if (IS_DEVICE_ALIASING(inode)) {
376+
if (!f2fs_sb_has_device_alias(sbi)) {
377+
f2fs_warn(sbi, "%s: inode (ino=%lx) has device alias flag, but the feature is off",
378+
__func__, inode->i_ino);
379+
return false;
380+
}
381+
if (!f2fs_is_pinned_file(inode)) {
382+
f2fs_warn(sbi, "%s: inode (ino=%lx) has device alias flag, but is not pinned",
383+
__func__, inode->i_ino);
384+
return false;
385+
}
386+
}
387+
375388
return true;
376389
}
377390

@@ -823,7 +836,8 @@ void f2fs_evict_inode(struct inode *inode)
823836
f2fs_bug_on(sbi, get_dirty_pages(inode));
824837
f2fs_remove_dirty_inode(inode);
825838

826-
f2fs_destroy_extent_tree(inode);
839+
if (!IS_DEVICE_ALIASING(inode))
840+
f2fs_destroy_extent_tree(inode);
827841

828842
if (inode->i_nlink || is_bad_inode(inode))
829843
goto no_delete;
@@ -879,6 +893,9 @@ void f2fs_evict_inode(struct inode *inode)
879893
goto retry;
880894
}
881895

896+
if (IS_DEVICE_ALIASING(inode))
897+
f2fs_destroy_extent_tree(inode);
898+
882899
if (err) {
883900
f2fs_update_inode_page(inode);
884901
if (dquot_initialize_needed(inode))

fs/f2fs/super.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,10 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
824824
set_opt(sbi, READ_EXTENT_CACHE);
825825
break;
826826
case Opt_noextent_cache:
827+
if (F2FS_HAS_FEATURE(sbi, F2FS_FEATURE_DEVICE_ALIAS)) {
828+
f2fs_err(sbi, "device aliasing requires extent cache");
829+
return -EINVAL;
830+
}
827831
clear_opt(sbi, READ_EXTENT_CACHE);
828832
break;
829833
case Opt_noinline_data:

fs/f2fs/sysfs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,7 @@ F2FS_SB_FEATURE_RO_ATTR(sb_checksum, SB_CHKSUM);
13131313
F2FS_SB_FEATURE_RO_ATTR(casefold, CASEFOLD);
13141314
F2FS_SB_FEATURE_RO_ATTR(compression, COMPRESSION);
13151315
F2FS_SB_FEATURE_RO_ATTR(readonly, RO);
1316+
F2FS_SB_FEATURE_RO_ATTR(device_alias, DEVICE_ALIAS);
13161317

13171318
static struct attribute *f2fs_sb_feat_attrs[] = {
13181319
ATTR_LIST(sb_encryption),
@@ -1329,6 +1330,7 @@ static struct attribute *f2fs_sb_feat_attrs[] = {
13291330
ATTR_LIST(sb_casefold),
13301331
ATTR_LIST(sb_compression),
13311332
ATTR_LIST(sb_readonly),
1333+
ATTR_LIST(sb_device_alias),
13321334
NULL,
13331335
};
13341336
ATTRIBUTE_GROUPS(f2fs_sb_feat);

include/uapi/linux/f2fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#define F2FS_IOC_DECOMPRESS_FILE _IO(F2FS_IOCTL_MAGIC, 23)
4444
#define F2FS_IOC_COMPRESS_FILE _IO(F2FS_IOCTL_MAGIC, 24)
4545
#define F2FS_IOC_START_ATOMIC_REPLACE _IO(F2FS_IOCTL_MAGIC, 25)
46+
#define F2FS_IOC_GET_DEV_ALIAS_FILE _IOR(F2FS_IOCTL_MAGIC, 26, __u32)
4647

4748
/*
4849
* should be same as XFS_IOC_GOINGDOWN.

0 commit comments

Comments
 (0)