Skip to content

Commit 128d333

Browse files
Daeho JeongJaegeuk Kim
authored andcommitted
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. 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]>
1 parent fa08972 commit 128d333

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
@@ -3441,6 +3441,11 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
34413441

34423442
if (!f2fs_lookup_read_extent_cache_block(inode, index,
34433443
&dn.data_blkaddr)) {
3444+
if (IS_DEVICE_ALIASING(inode)) {
3445+
err = -ENODATA;
3446+
goto out;
3447+
}
3448+
34443449
if (locked) {
34453450
err = f2fs_reserve_block(&dn, index);
34463451
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)
@@ -3046,6 +3047,7 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
30463047
#define F2FS_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */
30473048
#define F2FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
30483049
#define F2FS_CASEFOLD_FL 0x40000000 /* Casefolded file */
3050+
#define F2FS_DEVICE_ALIAS_FL 0x80000000 /* File for aliasing a device */
30493051

30503052
#define F2FS_QUOTA_DEFAULT_FL (F2FS_NOATIME_FL | F2FS_IMMUTABLE_FL)
30513053

@@ -3061,6 +3063,8 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
30613063
/* Flags that are appropriate for non-directories/regular files. */
30623064
#define F2FS_OTHER_FLMASK (F2FS_NODUMP_FL | F2FS_NOATIME_FL)
30633065

3066+
#define IS_DEVICE_ALIASING(inode) (F2FS_I(inode)->i_flags & F2FS_DEVICE_ALIAS_FL)
3067+
30643068
static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags)
30653069
{
30663070
if (S_ISDIR(mode))
@@ -4526,6 +4530,7 @@ F2FS_FEATURE_FUNCS(sb_chksum, SB_CHKSUM);
45264530
F2FS_FEATURE_FUNCS(casefold, CASEFOLD);
45274531
F2FS_FEATURE_FUNCS(compression, COMPRESSION);
45284532
F2FS_FEATURE_FUNCS(readonly, RO);
4533+
F2FS_FEATURE_FUNCS(device_alias, DEVICE_ALIAS);
45294534

45304535
#ifdef CONFIG_BLK_DEV_ZONED
45314536
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
@@ -725,6 +725,11 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock)
725725

726726
trace_f2fs_truncate_blocks_enter(inode, from);
727727

728+
if (IS_DEVICE_ALIASING(inode) && from) {
729+
err = -EINVAL;
730+
goto out_err;
731+
}
732+
728733
free_from = (pgoff_t)F2FS_BLK_ALIGN(from);
729734

730735
if (free_from >= max_file_blocks(inode))
@@ -739,6 +744,21 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock)
739744
goto out;
740745
}
741746

747+
if (IS_DEVICE_ALIASING(inode)) {
748+
struct extent_tree *et = F2FS_I(inode)->extent_tree[EX_READ];
749+
struct extent_info ei = et->largest;
750+
unsigned int i;
751+
752+
for (i = 0; i < ei.len; i++)
753+
f2fs_invalidate_blocks(sbi, ei.blk + i);
754+
755+
dec_valid_block_count(sbi, inode, ei.len);
756+
f2fs_update_time(sbi, REQ_TIME);
757+
758+
f2fs_put_page(ipage, 1);
759+
goto out;
760+
}
761+
742762
if (f2fs_has_inline_data(inode)) {
743763
f2fs_truncate_inline_inode(inode, ipage, from);
744764
f2fs_put_page(ipage, 1);
@@ -774,7 +794,7 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock)
774794
/* lastly zero out the first data page */
775795
if (!err)
776796
err = truncate_partial_data_page(inode, from, truncate_page);
777-
797+
out_err:
778798
trace_f2fs_truncate_blocks_exit(inode, err);
779799
return err;
780800
}
@@ -992,7 +1012,8 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
9921012
return -EPERM;
9931013

9941014
if ((attr->ia_valid & ATTR_SIZE)) {
995-
if (!f2fs_is_compress_backend_ready(inode))
1015+
if (!f2fs_is_compress_backend_ready(inode) ||
1016+
IS_DEVICE_ALIASING(inode))
9961017
return -EOPNOTSUPP;
9971018
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED) &&
9981019
!IS_ALIGNED(attr->ia_size,
@@ -1861,7 +1882,7 @@ static long f2fs_fallocate(struct file *file, int mode,
18611882
return -EIO;
18621883
if (!f2fs_is_checkpoint_ready(F2FS_I_SB(inode)))
18631884
return -ENOSPC;
1864-
if (!f2fs_is_compress_backend_ready(inode))
1885+
if (!f2fs_is_compress_backend_ready(inode) || IS_DEVICE_ALIASING(inode))
18651886
return -EOPNOTSUPP;
18661887

18671888
/* f2fs only support ->fallocate for regular file */
@@ -3297,6 +3318,9 @@ int f2fs_pin_file_control(struct inode *inode, bool inc)
32973318
struct f2fs_inode_info *fi = F2FS_I(inode);
32983319
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
32993320

3321+
if (IS_DEVICE_ALIASING(inode))
3322+
return -EINVAL;
3323+
33003324
if (fi->i_gc_failures >= sbi->gc_pin_file_threshold) {
33013325
f2fs_warn(sbi, "%s: Enable GC = ino %lx after %x GC trials",
33023326
__func__, inode->i_ino, fi->i_gc_failures);
@@ -3327,6 +3351,9 @@ static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg)
33273351
if (f2fs_readonly(sbi->sb))
33283352
return -EROFS;
33293353

3354+
if (!pin && IS_DEVICE_ALIASING(inode))
3355+
return -EOPNOTSUPP;
3356+
33303357
ret = mnt_want_write_file(filp);
33313358
if (ret)
33323359
return ret;
@@ -3392,6 +3419,12 @@ static int f2fs_ioc_get_pin_file(struct file *filp, unsigned long arg)
33923419
return put_user(pin, (u32 __user *)arg);
33933420
}
33943421

3422+
static int f2fs_ioc_get_dev_alias_file(struct file *filp, unsigned long arg)
3423+
{
3424+
return put_user(IS_DEVICE_ALIASING(file_inode(filp)) ? 1 : 0,
3425+
(u32 __user *)arg);
3426+
}
3427+
33953428
int f2fs_precache_extents(struct inode *inode)
33963429
{
33973430
struct f2fs_inode_info *fi = F2FS_I(inode);
@@ -4491,6 +4524,8 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
44914524
return f2fs_ioc_decompress_file(filp);
44924525
case F2FS_IOC_COMPRESS_FILE:
44934526
return f2fs_ioc_compress_file(filp);
4527+
case F2FS_IOC_GET_DEV_ALIAS_FILE:
4528+
return f2fs_ioc_get_dev_alias_file(filp, arg);
44944529
default:
44954530
return -ENOTTY;
44964531
}
@@ -4766,7 +4801,8 @@ static int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *iter,
47664801
else
47674802
return 0;
47684803

4769-
map.m_may_create = true;
4804+
if (!IS_DEVICE_ALIASING(inode))
4805+
map.m_may_create = true;
47704806
if (dio) {
47714807
map.m_seg_type = f2fs_rw_hint_to_seg_type(sbi,
47724808
inode->i_write_hint);
@@ -5203,6 +5239,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
52035239
case F2FS_IOC_SET_COMPRESS_OPTION:
52045240
case F2FS_IOC_DECOMPRESS_FILE:
52055241
case F2FS_IOC_COMPRESS_FILE:
5242+
case F2FS_IOC_GET_DEV_ALIAS_FILE:
52065243
break;
52075244
default:
52085245
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

@@ -825,7 +838,8 @@ void f2fs_evict_inode(struct inode *inode)
825838
f2fs_bug_on(sbi, get_dirty_pages(inode));
826839
f2fs_remove_dirty_inode(inode);
827840

828-
f2fs_destroy_extent_tree(inode);
841+
if (!IS_DEVICE_ALIASING(inode))
842+
f2fs_destroy_extent_tree(inode);
829843

830844
if (inode->i_nlink || is_bad_inode(inode))
831845
goto no_delete;
@@ -881,6 +895,9 @@ void f2fs_evict_inode(struct inode *inode)
881895
goto retry;
882896
}
883897

898+
if (IS_DEVICE_ALIASING(inode))
899+
f2fs_destroy_extent_tree(inode);
900+
884901
if (err) {
885902
f2fs_update_inode_page(inode);
886903
if (dquot_initialize_needed(inode))

fs/f2fs/super.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,10 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
834834
set_opt(sbi, READ_EXTENT_CACHE);
835835
break;
836836
case Opt_noextent_cache:
837+
if (F2FS_HAS_FEATURE(sbi, F2FS_FEATURE_DEVICE_ALIAS)) {
838+
f2fs_err(sbi, "device aliasing requires extent cache");
839+
return -EINVAL;
840+
}
837841
clear_opt(sbi, READ_EXTENT_CACHE);
838842
break;
839843
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)