Merge tag 'f2fs-for-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs

Pull f2fs updates from Jaegeuk Kim:
 "In this round, key enhancements focus on reducing inode management
  memory overhead, introducing resizable tail sections with unified
  pinned allocation, and boosting I/O throughput via parallel
  multi-device flushes and asynchronous f2fs_write_end_io() execution.
  We also add dynamic device alias reservations to allow on-the-fly
  space donation from user partitions.

  Alongside these features, critical bug fixes resolve folio race
  conditions, lingering dirty flags, dentry and block counter leaks, and
  potential deadloops in f2fs_fsync_node_pages(). Additional stability
  patches address error-path handling across symlink, sync, and
  rename/unlink operations, prevent pinned file fragmentation, and
  correct segment migration and free section accounting in
  free_segment_range.

  Enhancements:
   - reduce memory footprint of ino management
   - support dynamic reserve/release for device aliasing
   - issue multi-device flushes in parallel
   - add a way to run f2fs_write_end_io() asynchronously
   - support resizable tail section and unify pinned allocation

  Bug fixes:
   - fix to pass folio->index to f2fs_sanity_check_node_footer()
   - fix folio_nr_pages() race after put in large folio invalidate
   - fix to clear dirty flag on folio in error path
   - accurately adjust free_sections during free_segment_range
   - fix to avoid potential deadloop in f2fs_fsync_node_pages()
   - fix the error path in symlink, device alias in rename/unlink,
     f2fs_sync_fs
   - fix to migrate all curseg types during free_segment_range
   - fix to avoid pinfile fragment on fragment:{block, segment} mode
   - fix valid block count leak on data block allocation failure
   - fix dentry folio leak in find_in_level
   - reject overlapping move range after len expansion
   - fix some bugs related to file pinning, GC functions, i_size

  And, the series includes a number of minor bug fixes"

* tag 'f2fs-for-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (51 commits)
  f2fs: support resizable tail section and unify pinned allocation
  f2fs: don't leave the hashed inode while it's unlinked
  f2fs: accurately adjust free_sections during free_segment_range
  f2fs: fix to avoid potential deadloop in f2fs_fsync_node_pages()
  f2fs: use adjusted write range after f2fs_write_checks()
  f2fs: fix to propagate error from f2fs_sync_fs()
  f2fs: return symlink writeback errors
  f2fs: fix error handling on device alias check in rename and unlink
  f2fs: fix to reset all pinned status during fggc
  f2fs: use f2fs_{down, up}_(read, write}_trace() for nat_tree_lock
  f2fs: reduce memory footprint of ino management
  f2fs: fix i_size when pinned fallocate partially fails
  f2fs: fix to migrate all curseg types during free_segment_range
  f2fs: avoid setting SBI_NEED_FSCK on transient resize failure
  f2fs: fix to avoid pinfile fragment on fragment:{block, segment} mode
  f2fs: cleanup w/ f2fs_need_rand_{blk, seg, seg_blk}
  f2fs: fix to shrink gc_lock coverage in f2fs_gc_range()
  f2fs: fix to reclaim space in f2fs_allocate_pinning_section()
  f2fs: unify add/remove ino entry API for all ino types
  f2fs: fix to zero post-EOF data when extending file size
  ...
This commit is contained in:
Linus Torvalds
2026-08-28 10:48:48 -07:00
25 changed files with 1535 additions and 459 deletions

View File

@@ -986,6 +986,7 @@ Description: This sysfs entry can be used to enable/disable to adjust priority f
0x00000008 gc_lock
0x00000010 cp_global
0x00000020 io_rwsem
0x00000040 nat_tree_lock
========== ==================
What: /sys/fs/f2fs/<disk>/lock_duration_priority
@@ -1002,3 +1003,20 @@ Description: It can be used to tune priority of f2fs critical task, e.g. f2fs_ck
threads, limitation as below:
- it requires user has CAP_SYS_NICE capability.
- the range is [100, 139], by default the value is 120.
What: /sys/fs/f2fs/<disk>/max_atc_write_bio_size
Date: June 2026
Contact: Bart Van Assche <bvanassche@acm.org>
Description: Every time a write operation completes f2fs_write_end_io() is
called. This function may be called from an atomic context,
e.g. from inside an interrupt handler. This attribute controls
the maximum size of a write bio that is completed in atomic
(atc) context. The default value for this attribute is UINT_MAX
which means that this functionality is disabled by default.
What: /sys/fs/f2fs/<disk>/pinned_area_max_secno
Date: August 2026
Contact: "Daeho Jeong" <daehojeong@google.com>
Description: This is a read-only entry to show the upper bound section number
for pinned files. Pinned files will only be allocated within
sections 0 to pinned_area_max_secno - 1.

View File

@@ -415,7 +415,13 @@ lookup_mode=%s Control the directory lookup behavior for casefolded
auto F2FS determines the mode based on the
on-disk `SB_ENC_NO_COMPAT_FALLBACK_FL`
flag.
================== ========================================
resizable_tail_secno=%u Control the number of sections at the tail of the
filesystem reserved for online resizing. Pinned files
will only be allocated within sections 0 to
(MAIN_SECS - resizable_tail_secno) - 1. If set to 0
(default), there is no tail restriction unless running
on a zoned block device where conventional zones are
used.
======================== ============================================================
Debugfs Entries
@@ -1043,6 +1049,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
--------------------------------------

View File

@@ -107,6 +107,7 @@ static bool need_uplift_priority(struct f2fs_rwsem *sem, bool is_write)
case LOCK_NAME_GC_LOCK:
case LOCK_NAME_CP_GLOBAL:
case LOCK_NAME_IO_RWSEM:
case LOCK_NAME_NAT_TREE_LOCK:
return true;
default:
f2fs_bug_on(sem->sbi, 1);
@@ -766,28 +767,116 @@ static void __remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
spin_unlock(&im->ino_lock);
}
static void __set_ino_bitmap(struct f2fs_sb_info *sbi, nid_t ino, int type)
{
struct inode_management *im = &sbi->im[type];
unsigned long index = INO_SLOT_INDEX(ino);
unsigned int ofs = INO_BIT_OFFSET(ino);
void **slot, *entry;
unsigned long bitmap = 0;
int ret;
ret = radix_tree_preload(GFP_NOFS | __GFP_NOFAIL);
f2fs_bug_on(sbi, ret);
spin_lock(&im->ino_lock);
slot = radix_tree_lookup_slot(&im->ino_root, index);
if (slot) {
entry = radix_tree_deref_slot_protected(slot, &im->ino_lock);
bitmap = xa_to_value(entry);
if (!(bitmap & (1UL << ofs))) {
bitmap |= (1UL << ofs);
entry = xa_mk_value(bitmap);
radix_tree_replace_slot(&im->ino_root, slot, entry);
}
} else {
bitmap |= (1UL << ofs);
entry = xa_mk_value(bitmap);
if (unlikely(radix_tree_insert(&im->ino_root, index, entry)))
f2fs_bug_on(sbi, 1);
}
spin_unlock(&im->ino_lock);
radix_tree_preload_end();
}
static void __clear_ino_bitmap(struct f2fs_sb_info *sbi, nid_t ino, int type)
{
struct inode_management *im = &sbi->im[type];
unsigned long index = INO_SLOT_INDEX(ino);
unsigned int ofs = INO_BIT_OFFSET(ino);
void **slot, *entry;
unsigned long bitmap;
spin_lock(&im->ino_lock);
slot = radix_tree_lookup_slot(&im->ino_root, index);
if (slot) {
entry = radix_tree_deref_slot_protected(slot, &im->ino_lock);
bitmap = xa_to_value(entry);
if (bitmap & (1UL << ofs))
bitmap &= ~(1UL << ofs);
if (bitmap) {
entry = xa_mk_value(bitmap);
radix_tree_replace_slot(&im->ino_root, slot, entry);
} else {
radix_tree_delete(&im->ino_root, index);
}
}
spin_unlock(&im->ino_lock);
}
static void f2fs_wait_for_inode_record(struct f2fs_sb_info *sbi, int mode)
{
if (mode != APPEND_INO && mode != UPDATE_INO)
return;
/* Let's wait for some pending updates for APPEND_INO and UPDATE_INO. */
flush_workqueue(sbi->evict_wq);
}
static void __f2fs_add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino,
unsigned int devidx, int type)
{
if (type <= FLUSH_INO)
__add_ino_entry(sbi, ino, devidx, type);
else
__set_ino_bitmap(sbi, ino, type);
}
void f2fs_add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
{
/* add new dirty ino entry into list */
__add_ino_entry(sbi, ino, 0, type);
__f2fs_add_ino_entry(sbi, ino, 0, type);
}
void f2fs_remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
{
/* remove dirty ino entry from list */
__remove_ino_entry(sbi, ino, type);
if (type <= FLUSH_INO)
__remove_ino_entry(sbi, ino, type);
else
__clear_ino_bitmap(sbi, ino, type);
}
/* mode should be APPEND_INO, UPDATE_INO or TRANS_DIR_INO */
/* mode should be APPEND_INO, UPDATE_INO, TRANS_DIR_INO and XATTR_DIR_INO */
bool f2fs_exist_written_data(struct f2fs_sb_info *sbi, nid_t ino, int mode)
{
struct inode_management *im = &sbi->im[mode];
struct ino_entry *e;
unsigned long index = INO_SLOT_INDEX(ino);
unsigned int ofs = INO_BIT_OFFSET(ino);
void *entry;
unsigned long bitmap;
f2fs_bug_on(sbi, mode <= FLUSH_INO);
spin_lock(&im->ino_lock);
e = radix_tree_lookup(&im->ino_root, ino);
entry = radix_tree_lookup(&im->ino_root, index);
if (!entry) {
spin_unlock(&im->ino_lock);
return false;
}
bitmap = xa_to_value(entry);
spin_unlock(&im->ino_lock);
return e ? true : false;
return bitmap & (1UL << ofs);
}
void f2fs_release_ino_entry(struct f2fs_sb_info *sbi, bool all)
@@ -795,9 +884,11 @@ void f2fs_release_ino_entry(struct f2fs_sb_info *sbi, bool all)
struct ino_entry *e, *tmp;
int i;
for (i = all ? ORPHAN_INO : APPEND_INO; i < MAX_INO_ENTRY; i++) {
for (i = all ? ORPHAN_INO : FLUSH_INO; i <= FLUSH_INO; i++) {
struct inode_management *im = &sbi->im[i];
f2fs_wait_for_inode_record(sbi, i);
spin_lock(&im->ino_lock);
list_for_each_entry_safe(e, tmp, &im->ino_list, list) {
list_del(&e->list);
@@ -807,12 +898,20 @@ void f2fs_release_ino_entry(struct f2fs_sb_info *sbi, bool all)
}
spin_unlock(&im->ino_lock);
}
for (i = APPEND_INO; i < MAX_INO_ENTRY; i++) {
struct inode_management *im = &sbi->im[i];
spin_lock(&im->ino_lock);
xa_destroy(&im->ino_root);
spin_unlock(&im->ino_lock);
}
}
void f2fs_set_dirty_device(struct f2fs_sb_info *sbi, nid_t ino,
unsigned int devidx, int type)
{
__add_ino_entry(sbi, ino, devidx, type);
__f2fs_add_ino_entry(sbi, ino, devidx, type);
}
bool f2fs_is_dirty_device(struct f2fs_sb_info *sbi, nid_t ino,
@@ -864,14 +963,14 @@ void f2fs_release_orphan_inode(struct f2fs_sb_info *sbi)
void f2fs_add_orphan_inode(struct inode *inode)
{
/* add new orphan ino entry into list */
__add_ino_entry(F2FS_I_SB(inode), inode->i_ino, 0, ORPHAN_INO);
f2fs_add_ino_entry(F2FS_I_SB(inode), inode->i_ino, ORPHAN_INO);
f2fs_update_inode_page(inode);
}
void f2fs_remove_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
{
/* remove orphan entry from orphan list */
__remove_ino_entry(sbi, ino, ORPHAN_INO);
f2fs_remove_ino_entry(sbi, ino, ORPHAN_INO);
}
static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)

View File

@@ -384,13 +384,32 @@ static void f2fs_write_end_bio(struct bio *bio)
bio_put(bio);
}
static void f2fs_write_end_io(struct bio *bio)
static void f2fs_write_end_io_work(struct work_struct *work)
{
iostat_update_and_unbind_ctx(bio);
struct bio *bio = &container_of(work, struct f2fs_bio, work)->bio;
f2fs_write_end_bio(bio);
}
static void f2fs_write_end_io(struct bio *bio)
{
struct f2fs_sb_info *sbi;
iostat_update_and_unbind_ctx(bio);
sbi = bio->bi_private;
if (in_atomic() && bio->bi_iter.bi_size > sbi->max_atc_write_bio_size) {
struct work_struct *w;
w = &container_of(bio, struct f2fs_bio, bio)->work;
INIT_WORK(w, f2fs_write_end_io_work);
queue_work(sbi->wq, w);
} else {
f2fs_write_end_bio(bio);
}
}
#ifdef CONFIG_BLK_DEV_ZONED
static void f2fs_zone_write_end_io(struct bio *bio)
{
@@ -1216,7 +1235,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;
@@ -1288,10 +1307,11 @@ struct folio *f2fs_get_read_data_folio(struct inode *inode, pgoff_t index,
if (folio_test_large(folio)) {
pgoff_t folio_index = mapping_align_index(mapping, index);
unsigned long nr_pages = folio_nr_pages(folio);
f2fs_folio_put(folio, true);
invalidate_inode_pages2_range(mapping, folio_index,
folio_index + folio_nr_pages(folio) - 1);
folio_index + nr_pages - 1);
f2fs_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT);
goto retry;
}
@@ -1488,7 +1508,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;
}
@@ -1497,8 +1517,11 @@ static int __allocate_data_block(struct dnode_of_data *dn, int seg_type)
old_blkaddr = dn->data_blkaddr;
err = f2fs_allocate_data_block(sbi, NULL, old_blkaddr,
&dn->data_blkaddr, &sum, seg_type, NULL);
if (err)
if (err) {
if (old_blkaddr == NULL_ADDR)
dec_valid_block_count(sbi, dn->inode, count);
return err;
}
if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
f2fs_invalidate_internal_cache(sbi, old_blkaddr, 1);
@@ -3900,7 +3923,7 @@ static int f2fs_write_begin(const struct kiocb *iocb,
* Will wait that below with our IO control.
*/
folio = f2fs_filemap_get_folio(mapping, index,
FGP_LOCK | FGP_WRITE | FGP_CREAT | FGP_NOFS,
FGP_LOCK | FGP_WRITE | FGP_CREAT,
mapping_gfp_mask(mapping));
if (IS_ERR(folio)) {
err = PTR_ERR(folio);
@@ -4460,13 +4483,24 @@ int f2fs_init_wq(struct f2fs_sb_info *sbi)
{
sbi->wq = alloc_workqueue("f2fs_wq", WQ_UNBOUND | WQ_HIGHPRI,
num_online_cpus());
return sbi->wq ? 0 : -ENOMEM;
if (!sbi->wq)
return -ENOMEM;
sbi->evict_wq = alloc_workqueue("f2fs_evict_wq",
WQ_UNBOUND | WQ_HIGHPRI, num_online_cpus());
if (!sbi->evict_wq) {
destroy_workqueue(sbi->wq);
return -ENOMEM;
}
return 0;
}
void f2fs_destroy_wq(struct f2fs_sb_info *sbi)
{
if (sbi->wq)
destroy_workqueue(sbi->wq);
if (sbi->evict_wq)
destroy_workqueue(sbi->evict_wq);
}
int __init f2fs_init_bio_entry_cache(void)

View File

@@ -352,10 +352,6 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
get_cache:
si->cache_mem = 0;
/* build gc */
if (sbi->gc_thread)
si->cache_mem += sizeof(struct f2fs_gc_kthread);
/* build merge flush thread */
if (SM_I(sbi)->fcc_info)
si->cache_mem += sizeof(struct flush_cmd_control);

View File

@@ -320,6 +320,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
de = find_in_block(dir, dentry_folio, fname, &max_slots, use_hash);
if (IS_ERR(de)) {
f2fs_folio_put(dentry_folio, false);
*res_folio = ERR_CAST(de);
de = NULL;
break;
@@ -460,7 +461,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
folio_mark_dirty(folio);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
f2fs_mark_inode_dirty_sync(dir, false);
f2fs_mark_inode_dirty_sync(dir, true);
f2fs_folio_put(folio, true);
}
@@ -615,7 +616,7 @@ void f2fs_update_parent_metadata(struct inode *dir, struct inode *inode,
clear_inode_flag(inode, FI_NEW_INODE);
}
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
f2fs_mark_inode_dirty_sync(dir, false);
f2fs_mark_inode_dirty_sync(dir, true);
if (F2FS_I(dir)->i_current_depth != current_depth)
f2fs_i_depth_write(dir, current_depth);
@@ -927,7 +928,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct folio *folio,
f2fs_folio_put(folio, true);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
f2fs_mark_inode_dirty_sync(dir, false);
f2fs_mark_inode_dirty_sync(dir, true);
if (inode)
f2fs_drop_nlink(dir, inode);

View File

@@ -17,6 +17,7 @@
#include "f2fs.h"
#include "node.h"
#include "segment.h"
#include <trace/events/f2fs.h>
bool sanity_check_extent_cache(struct inode *inode, struct folio *ifolio)
@@ -62,6 +63,14 @@ bool sanity_check_extent_cache(struct inode *inode, struct folio *ifolio)
__func__, inode->i_ino, ei.blk, ei.fofs, ei.len);
return false;
}
if ((GET_SEGOFF_FROM_SEG0(sbi, 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;
}

View File

@@ -193,6 +193,7 @@ enum f2fs_lock_name {
LOCK_NAME_GC_LOCK,
LOCK_NAME_CP_GLOBAL,
LOCK_NAME_IO_RWSEM,
LOCK_NAME_NAT_TREE_LOCK,
LOCK_NAME_MAX,
};
@@ -254,6 +255,7 @@ struct f2fs_mount_info {
block_t unusable_cap; /* Amount of space allowed to be
* unusable when disabling checkpoint
*/
unsigned int resizable_tail_secno; /* number of resizable tail sections */
/* For compression */
unsigned char compress_algorithm; /* algorithm type */
@@ -388,14 +390,18 @@ enum {
/* for the list of ino */
enum {
ORPHAN_INO, /* for orphan ino list */
FLUSH_INO, /* for multiple device flushing */
APPEND_INO, /* for append ino list */
UPDATE_INO, /* for update ino list */
TRANS_DIR_INO, /* for transactions dir ino list */
XATTR_DIR_INO, /* for xattr updated dir ino list */
FLUSH_INO, /* for multiple device flushing */
MAX_INO_ENTRY, /* max. list */
};
#define INO_BITS_PER_SLOT BITS_PER_XA_VALUE
#define INO_SLOT_INDEX(ino) ((ino) / INO_BITS_PER_SLOT)
#define INO_BIT_OFFSET(ino) ((ino) % INO_BITS_PER_SLOT)
struct ino_entry {
struct list_head list; /* list head */
nid_t ino; /* inode number */
@@ -1403,6 +1409,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 */
@@ -1747,6 +1755,33 @@ struct decompress_io_ctx {
#define MAX_COMPRESS_LOG_SIZE 8
#define MAX_COMPRESS_WINDOW_SIZE(log_size) ((PAGE_SIZE) << (log_size))
struct f2fs_gc_kthread {
struct task_struct *f2fs_gc_task;
wait_queue_head_t gc_wait_queue_head;
/* for gc sleep time */
unsigned int urgent_sleep_time;
unsigned int min_sleep_time;
unsigned int max_sleep_time;
unsigned int no_gc_sleep_time;
/* for changing gc mode */
bool gc_wake;
/* for GC_MERGE mount option */
wait_queue_head_t fggc_wq; /*
* caller of f2fs_balance_fs()
* will wait on this wait queue.
*/
/* for gc control for zoned devices */
unsigned int no_zoned_gc_percent;
unsigned int boost_zoned_gc_percent;
unsigned int valid_thresh_ratio;
unsigned int boost_gc_multiple;
unsigned int boost_gc_greedy;
};
struct f2fs_sb_info {
struct super_block *sb; /* pointer to VFS super block */
struct proc_dir_entry *s_proc; /* proc entry */
@@ -1772,6 +1807,8 @@ struct f2fs_sb_info {
struct f2fs_sm_info *sm_info; /* segment manager */
/* for bio operations */
/* Largest write bio size completed in atomic context (atc). */
u32 max_atc_write_bio_size;
struct f2fs_bio_info *write_io[NR_PAGE_TYPE]; /* for write bios */
/* keep migration IO order for LFS mode */
struct f2fs_rwsem io_order_lock;
@@ -1854,6 +1891,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 */
@@ -1882,7 +1920,7 @@ struct f2fs_sb_info {
* semaphore for GC, avoid
* race between GC and GC or CP
*/
struct f2fs_gc_kthread *gc_thread; /* GC thread */
struct f2fs_gc_kthread gc_thread; /* GC thread */
struct atgc_management am; /* atgc management */
unsigned int cur_victim_sec; /* current victim section num */
unsigned int gc_mode; /* current GC state */
@@ -1967,6 +2005,7 @@ struct f2fs_sb_info {
spinlock_t dev_lock; /* protect dirty_device */
bool aligned_blksize; /* all devices has the same logical blksize */
unsigned int first_seq_zone_segno; /* first segno in sequential zone */
unsigned int pinned_area_max_secno; /* upper bound section for pinned files */
unsigned int bggc_io_aware; /* For adjust the BG_GC priority when pending IO */
unsigned int allocate_section_hint; /* the boundary position between devices */
unsigned int allocate_section_policy; /* determine the section writing priority */
@@ -1980,6 +2019,8 @@ struct f2fs_sb_info {
struct workqueue_struct *wq; /* bio completion workqueue */
struct workqueue_struct *evict_wq; /* inode eviction workqueue */
/*
* If we are in irq context, let's update error information into
* on-disk superblock in the work.
@@ -2556,7 +2597,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;
@@ -2573,7 +2615,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;
@@ -2596,10 +2639,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;
@@ -3830,7 +3879,10 @@ void f2fs_update_inode_page(struct inode *inode);
int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc);
void f2fs_remove_donate_inode(struct inode *inode);
void f2fs_evict_inode(struct inode *inode);
void f2fs_handle_failed_inode(struct inode *inode, struct f2fs_lock_context *lc);
void f2fs_handle_failed_inode(struct inode *inode,
struct f2fs_lock_context *lc, bool add_orphan);
int f2fs_init_evict_inode_work(void);
void f2fs_destroy_evict_inode_work(void);
/*
* namei.c
@@ -4007,6 +4059,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);
@@ -4095,10 +4149,26 @@ static inline struct inode *fio_inode(struct f2fs_io_info *fio)
#define MIN_FRAGMENT_SIZE 1
#define MAX_FRAGMENT_SIZE 512
static inline bool f2fs_need_rand_seg(struct f2fs_sb_info *sbi)
static inline bool f2fs_need_rand_blk(struct f2fs_sb_info *sbi,
enum log_type type)
{
return F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_SEG ||
F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK;
if (type == CURSEG_COLD_DATA_PINNED)
return false;
return F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK;
}
static inline bool f2fs_need_rand_seg(struct f2fs_sb_info *sbi,
enum log_type type)
{
if (type == CURSEG_COLD_DATA_PINNED)
return false;
return F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_SEG;
}
static inline bool f2fs_need_rand_seg_blk(struct f2fs_sb_info *sbi,
enum log_type type)
{
return f2fs_need_rand_blk(sbi, type) || f2fs_need_rand_seg(sbi, type);
}
/*
@@ -4226,7 +4296,9 @@ int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control);
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);
bool dry_run, unsigned int dry_run_sections, bool lock);
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);

View File

@@ -36,23 +36,69 @@
#include <trace/events/f2fs.h>
#include <uapi/linux/f2fs.h>
static void f2fs_zero_post_eof_page(struct inode *inode,
loff_t new_size, bool lock)
static int fill_zero(struct inode *inode, pgoff_t index,
loff_t start, loff_t len);
static int do_zero_post_eof_page(struct inode *inode, loff_t new_size)
{
loff_t old_size = i_size_read(inode);
unsigned int offset, len;
pgoff_t index;
int err;
offset = old_size & (PAGE_SIZE - 1);
if (!offset)
return 0;
len = min_t(loff_t, PAGE_SIZE - offset, new_size - old_size);
index = old_size >> PAGE_SHIFT;
if (f2fs_has_inline_data(inode)) {
/* data post eof should be always zero */
if (new_size <= MAX_INLINE_DATA(inode))
return 0;
err = f2fs_convert_inline_inode(inode);
if (err)
return err;
}
err = fill_zero(inode, index, offset, len);
if (err)
return err;
return filemap_write_and_wait_range(inode->i_mapping,
old_size, old_size + len - 1);
}
static int f2fs_zero_post_eof_page(struct inode *inode,
loff_t new_size, bool lock, bool writeback)
{
loff_t old_size = i_size_read(inode);
bool strict =
F2FS_OPTION(F2FS_I_SB(inode)).fsync_mode == FSYNC_MODE_STRICT;
if (old_size >= new_size)
return;
return 0;
if (mapping_empty(inode->i_mapping))
return;
if (!strict && mapping_empty(inode->i_mapping))
return 0;
if (lock)
filemap_invalidate_lock(inode->i_mapping);
/* zero or drop pages only in range of [old_size, new_size] */
truncate_inode_pages_range(inode->i_mapping, old_size, new_size);
truncate_inode_pages_range(inode->i_mapping, old_size, new_size - 1);
if (lock)
filemap_invalidate_unlock(inode->i_mapping);
if (!writeback || !strict)
return 0;
/*
* In fsync_mode=strict, when we expand an unaligned EOF size, we
* should zero post EOF data and writeback the data immediately,
* so that it can avoid exposing stale data after metadata flush
* and POR.
*/
return do_zero_post_eof_page(inode, new_size);
}
static vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf)
@@ -132,7 +178,10 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
f2fs_bug_on(sbi, f2fs_has_inline_data(inode));
f2fs_zero_post_eof_page(inode, (folio->index + 1) << PAGE_SHIFT, true);
err = f2fs_zero_post_eof_page(inode,
(folio->index + 1) << PAGE_SHIFT, true, false);
if (err)
goto out_pagefault;
file_update_time(vmf->vma->vm_file);
filemap_invalidate_lock_shared(inode->i_mapping);
@@ -189,7 +238,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
out_sem:
filemap_invalidate_unlock_shared(inode->i_mapping);
out_pagefault:
sb_end_pagefault(inode->i_sb);
out:
ret = vmf_fs_error(err);
@@ -813,13 +862,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;
}
@@ -1096,24 +1151,31 @@ 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,
F2FS_BLK_TO_BYTES(fi->i_cluster_size)))
return -EINVAL;
/*
* To prevent scattered pin block generation, we don't allow
* smaller/equal size unaligned truncation for pinned file.
* We only support overwrite IO to pinned file, so don't
* care about larger size truncation.
*/
if (f2fs_is_pinned_file(inode) &&
attr->ia_size <= i_size_read(inode) &&
!IS_ALIGNED(attr->ia_size,
F2FS_BLK_TO_BYTES(CAP_BLKS_PER_SEC(sbi))))
return -EINVAL;
if (f2fs_is_pinned_file(inode)) {
/*
* It may break section-aligned fallocate recovery
* mechanism, so do not allow larger size truncation.
*/
if (attr->ia_size > i_size_read(inode))
return -EINVAL;
/*
* To prevent scattered pin block generation, we don't
* allow smaller/equal size unaligned truncation for
* pinned file.
*/
else if (!IS_ALIGNED(attr->ia_size,
F2FS_BLK_TO_BYTES(CAP_BLKS_PER_SEC(sbi))))
return -EINVAL;
}
}
if (is_quota_modification(idmap, inode, attr)) {
@@ -1165,8 +1227,12 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
f2fs_down_write(&fi->i_gc_rwsem[WRITE]);
filemap_invalidate_lock(inode->i_mapping);
if (attr->ia_size > old_size)
f2fs_zero_post_eof_page(inode, attr->ia_size, false);
if (attr->ia_size > old_size) {
err = f2fs_zero_post_eof_page(inode,
attr->ia_size, false, true);
if (err)
goto err_out;
}
truncate_setsize(inode, attr->ia_size);
if (attr->ia_size <= old_size)
@@ -1175,6 +1241,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
* do not trim all blocks after i_size if target size is
* larger than i_size.
*/
err_out:
filemap_invalidate_unlock(inode->i_mapping);
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
if (err)
@@ -1286,7 +1353,9 @@ static int f2fs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
if (ret)
return ret;
f2fs_zero_post_eof_page(inode, offset + len, true);
ret = f2fs_zero_post_eof_page(inode, offset + len, true, false);
if (ret)
return ret;
pg_start = ((unsigned long long) offset) >> PAGE_SHIFT;
pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT;
@@ -1573,7 +1642,9 @@ static int f2fs_do_collapse(struct inode *inode, loff_t offset, loff_t len)
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
filemap_invalidate_lock(inode->i_mapping);
f2fs_zero_post_eof_page(inode, offset + len, false);
ret = f2fs_zero_post_eof_page(inode, offset + len, false, false);
if (ret)
goto out_unlock;
f2fs_lock_op(sbi, &lc);
f2fs_drop_extent_tree(inode);
@@ -1581,6 +1652,7 @@ static int f2fs_do_collapse(struct inode *inode, loff_t offset, loff_t len)
ret = __exchange_data_block(inode, inode, end, start, nrpages - end, true);
f2fs_unlock_op(sbi, &lc);
out_unlock:
filemap_invalidate_unlock(inode->i_mapping);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
return ret;
@@ -1613,11 +1685,14 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len)
/* write out all moved pages, if possible */
filemap_invalidate_lock(inode->i_mapping);
filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX);
if (ret)
goto out_unlock;
truncate_pagecache(inode, offset);
new_size = i_size_read(inode) - len;
ret = f2fs_truncate_blocks(inode, new_size, true);
out_unlock:
filemap_invalidate_unlock(inode->i_mapping);
if (!ret)
f2fs_i_size_write(inode, new_size);
@@ -1699,7 +1774,9 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len,
if (ret)
return ret;
f2fs_zero_post_eof_page(inode, offset + len, true);
ret = f2fs_zero_post_eof_page(inode, offset + len, true, false);
if (ret)
return ret;
pg_start = ((unsigned long long) offset) >> PAGE_SHIFT;
pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT;
@@ -1834,7 +1911,9 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len)
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
filemap_invalidate_lock(mapping);
f2fs_zero_post_eof_page(inode, offset + len, false);
ret = f2fs_zero_post_eof_page(inode, offset + len, false, false);
if (ret)
goto out_unlock;
truncate_pagecache(inode, offset);
while (!ret && idx > pg_start) {
@@ -1852,6 +1931,7 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len)
idx + delta, nr, false);
f2fs_unlock_op(sbi, &lc);
}
out_unlock:
filemap_invalidate_unlock(mapping);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
if (ret)
@@ -1894,7 +1974,9 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset,
if (err)
return err;
f2fs_zero_post_eof_page(inode, offset + len, true);
err = f2fs_zero_post_eof_page(inode, offset + len, true, true);
if (err)
return err;
f2fs_balance_fs(sbi, true);
@@ -1915,8 +1997,9 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset,
block_t sec_len;
if (map.m_lblk % sec_blks) {
map.m_lblk = rounddown(map.m_lblk, sec_blks);
map.m_len = pg_end - map.m_lblk;
pg_start = rounddown(map.m_lblk, sec_blks);
map.m_lblk = pg_start;
map.m_len = pg_end - pg_start;
if (off_end)
map.m_len++;
}
@@ -2126,6 +2209,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;
@@ -2674,6 +2760,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);
@@ -3079,6 +3176,9 @@ static int f2fs_ioc_defragment(struct file *filp, unsigned long arg)
if (f2fs_readonly(sbi->sb))
return -EROFS;
if (IS_DEVICE_ALIASING(inode))
return -EOPNOTSUPP;
if (copy_from_user(&range, (struct f2fs_defragment __user *)arg,
sizeof(range)))
return -EFAULT;
@@ -3117,8 +3217,9 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
struct inode *dst = file_inode(file_out);
struct f2fs_sb_info *sbi = F2FS_I_SB(src);
struct f2fs_lock_context lc;
size_t olen = len, dst_max_i_size = 0;
size_t dst_osize;
size_t olen = len;
loff_t dst_max_i_size = 0;
loff_t dst_osize, dst_end;
int ret;
if (file_in->f_path.mnt != file_out->f_path.mnt ||
@@ -3131,7 +3232,8 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
if (!S_ISREG(src->i_mode) || !S_ISREG(dst->i_mode))
return -EINVAL;
if (IS_ENCRYPTED(src) || IS_ENCRYPTED(dst))
if (IS_ENCRYPTED(src) || IS_ENCRYPTED(dst) ||
IS_DEVICE_ALIASING(src) || IS_DEVICE_ALIASING(dst))
return -EOPNOTSUPP;
if (pos_out < 0 || pos_in < 0)
@@ -3140,8 +3242,6 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
if (src == dst) {
if (pos_in == pos_out)
return 0;
if (pos_out > pos_in && pos_out < pos_in + len)
return -EINVAL;
}
inode_lock(src);
@@ -3167,6 +3267,8 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
goto out_unlock;
if (len == 0)
olen = len = src->i_size - pos_in;
if (src == dst && pos_out > pos_in && pos_out < pos_in + len)
goto out_unlock;
if (pos_in + len == src->i_size)
len = ALIGN(src->i_size, F2FS_BLKSIZE) - pos_in;
if (len == 0) {
@@ -3175,8 +3277,15 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
}
dst_osize = dst->i_size;
if (pos_out + olen > dst->i_size)
dst_max_i_size = pos_out + olen;
if (olen > LLONG_MAX - pos_out)
goto out_unlock;
dst_end = pos_out + olen;
if (dst_end > dst->i_size) {
ret = inode_newsize_ok(dst, dst_end);
if (ret)
goto out_unlock;
dst_max_i_size = dst_end;
}
/* verify the end result is block aligned */
if (!IS_ALIGNED(pos_in, F2FS_BLKSIZE) ||
@@ -3612,6 +3721,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, false);
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);
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);
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);
@@ -4037,7 +4381,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;
@@ -4469,7 +4813,7 @@ static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
page_idx = folio_next_index(folio);
} while (page_len < len);
do {
while (redirty_idx < page_idx) {
folio = filemap_lock_folio(mapping, redirty_idx);
/* It will never fail, when folio has pinned above */
@@ -4482,7 +4826,7 @@ static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
redirty_idx = folio_next_index(folio);
folio_unlock(folio);
folio_put_refs(folio, 2);
} while (redirty_idx < page_idx);
}
return ret;
}
@@ -4738,8 +5082,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;
}
@@ -4998,8 +5348,10 @@ static ssize_t f2fs_write_checks(struct kiocb *iocb, struct iov_iter *from)
if (err)
return err;
f2fs_zero_post_eof_page(inode,
iocb->ki_pos + iov_iter_count(from), true);
err = f2fs_zero_post_eof_page(inode,
iocb->ki_pos + iov_iter_count(from), true, true);
if (err)
return err;
return count;
}
@@ -5285,9 +5637,8 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
bool dio;
bool may_need_sync = true;
int preallocated;
const loff_t pos = iocb->ki_pos;
const ssize_t count = iov_iter_count(from);
ssize_t ret;
loff_t bufio_start_pos;
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) {
ret = -EIO;
@@ -5308,15 +5659,17 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
inode_lock(inode);
}
ret = f2fs_write_checks(iocb, from);
if (ret <= 0)
goto out_unlock;
if (f2fs_is_pinned_file(inode) &&
!f2fs_overwrite_io(inode, pos, count)) {
!f2fs_overwrite_io(inode, iocb->ki_pos, iov_iter_count(from))) {
ret = -EIO;
goto out_unlock;
}
ret = f2fs_write_checks(iocb, from);
if (ret <= 0)
goto out_unlock;
bufio_start_pos = iocb->ki_pos;
/* Determine whether we will do a direct write or a buffered write. */
dio = f2fs_should_use_dio(inode, iocb, from);
@@ -5371,8 +5724,8 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
*/
if (ret > 0 && !dio && (iocb->ki_flags & IOCB_DIRECT))
f2fs_flush_buffered_write(iocb->ki_filp->f_mapping,
orig_pos,
orig_pos + ret - 1);
bufio_start_pos,
bufio_start_pos + ret - 1);
return ret;
}
@@ -5526,7 +5879,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;

View File

@@ -31,9 +31,9 @@ static unsigned int count_bits(const unsigned long *addr,
static int gc_thread_func(void *data)
{
struct f2fs_sb_info *sbi = data;
struct f2fs_gc_kthread *gc_th = sbi->gc_thread;
wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head;
wait_queue_head_t *fggc_wq = &sbi->gc_thread->fggc_wq;
struct f2fs_gc_kthread *gc_th = &sbi->gc_thread;
wait_queue_head_t *wq = &sbi->gc_thread.gc_wait_queue_head;
wait_queue_head_t *fggc_wq = &sbi->gc_thread.fggc_wq;
unsigned int wait_ms;
struct f2fs_gc_control gc_control = {
.victim_segno = NULL_SEGNO,
@@ -193,13 +193,9 @@ static int gc_thread_func(void *data)
int f2fs_start_gc_thread(struct f2fs_sb_info *sbi)
{
struct f2fs_gc_kthread *gc_th;
struct f2fs_gc_kthread *gc_th = &sbi->gc_thread;
dev_t dev = sbi->sb->s_bdev->bd_dev;
gc_th = f2fs_kmalloc(sbi, sizeof(struct f2fs_gc_kthread), GFP_KERNEL);
if (!gc_th)
return -ENOMEM;
gc_th->urgent_sleep_time = DEF_GC_THREAD_URGENT_SLEEP_TIME;
gc_th->valid_thresh_ratio = DEF_GC_THREAD_VALID_THRESH_RATIO;
gc_th->boost_gc_multiple = BOOST_GC_MULTIPLE;
@@ -221,16 +217,14 @@ int f2fs_start_gc_thread(struct f2fs_sb_info *sbi)
gc_th->gc_wake = false;
sbi->gc_thread = gc_th;
init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head);
init_waitqueue_head(&sbi->gc_thread->fggc_wq);
sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi,
init_waitqueue_head(&gc_th->gc_wait_queue_head);
init_waitqueue_head(&gc_th->fggc_wq);
gc_th->f2fs_gc_task = kthread_run(gc_thread_func, sbi,
"f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev));
if (IS_ERR(gc_th->f2fs_gc_task)) {
int err = PTR_ERR(gc_th->f2fs_gc_task);
kfree(gc_th);
sbi->gc_thread = NULL;
gc_th->f2fs_gc_task = NULL;
return err;
}
@@ -241,14 +235,14 @@ int f2fs_start_gc_thread(struct f2fs_sb_info *sbi)
void f2fs_stop_gc_thread(struct f2fs_sb_info *sbi)
{
struct f2fs_gc_kthread *gc_th = sbi->gc_thread;
struct f2fs_gc_kthread *gc_th = &sbi->gc_thread;
if (!gc_th)
if (!gc_th->f2fs_gc_task)
return;
kthread_stop(gc_th->f2fs_gc_task);
gc_th->f2fs_gc_task = NULL;
wake_up_all(&gc_th->fggc_wq);
kfree(gc_th);
sbi->gc_thread = NULL;
}
static int select_gc_type(struct f2fs_sb_info *sbi, int gc_type)
@@ -316,7 +310,7 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type,
p->max_search = sbi->max_victim_search;
/* let's select beginning hot/small space first. */
if (f2fs_need_rand_seg(sbi)) {
if (f2fs_need_rand_seg_blk(sbi, type)) {
p->offset = get_random_u32_below(MAIN_SECS(sbi) *
SEGS_PER_SEC(sbi));
SIT_I(sbi)->last_victim[p->gc_mode] = p->offset;
@@ -796,7 +790,7 @@ int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result,
if (one_time) {
p.one_time_gc = one_time;
if (has_enough_free_secs(sbi, 0, NR_PERSISTENT_LOG))
valid_thresh_ratio = sbi->gc_thread->valid_thresh_ratio;
valid_thresh_ratio = sbi->gc_thread.valid_thresh_ratio;
}
retry:
@@ -1807,9 +1801,9 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
if (f2fs_sb_has_blkzoned(sbi) &&
!has_enough_free_blocks(sbi,
sbi->gc_thread->boost_zoned_gc_percent))
sbi->gc_thread.boost_zoned_gc_percent))
window_granularity *=
sbi->gc_thread->boost_gc_multiple;
sbi->gc_thread.boost_gc_multiple;
end_segno = start_segno + window_granularity;
}
@@ -2156,8 +2150,9 @@ 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)
bool dry_run, unsigned int dry_run_sections, bool lock)
{
struct f2fs_lock_context lc;
unsigned int segno;
unsigned int gc_secs = dry_run_sections;
@@ -2170,66 +2165,102 @@ int f2fs_gc_range(struct f2fs_sb_info *sbi,
.ilist = LIST_HEAD_INIT(gc_list.ilist),
.iroot = RADIX_TREE_INIT(gc_list.iroot, GFP_NOFS),
};
int err = 0;
if (lock)
f2fs_down_write_trace(&sbi->gc_lock, &lc);
/*
* avoid migrating empty section, as it can be allocated by
* log in parallel.
*/
if (!get_valid_blocks(sbi, segno, true))
continue;
goto next;
if (is_cursec(sbi, GET_SEC_FROM_SEG(sbi, segno)))
continue;
goto next;
do_garbage_collect(sbi, segno, &gc_list, FG_GC, true, false);
put_gc_inode(&gc_list);
if (!dry_run && get_valid_blocks(sbi, segno, true))
return -EAGAIN;
/* reset all pinned status during fggc */
f2fs_unpin_all_sections(sbi, true);
if (!dry_run && get_valid_blocks(sbi, segno, true)) {
err = -EAGAIN;
goto next;
}
if (dry_run && dry_run_sections &&
!get_valid_blocks(sbi, segno, true) && --gc_secs == 0)
break;
!get_valid_blocks(sbi, segno, true)) {
--gc_secs;
goto next;
}
if (fatal_signal_pending(current))
return -ERESTARTSYS;
err = -ERESTARTSYS;
next:
if (lock)
f2fs_up_write_trace(&sbi->gc_lock, &lc);
if (err)
return err;
if (dry_run && dry_run_sections && !gc_secs)
return 0;
}
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;
unsigned int secno, next_inuse, start, end, end_secno;
struct cp_control cpc = { CP_RESIZE, 0, 0, 0 };
int gc_mode, gc_type;
unsigned int freed_secs = 0;
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;
end_secno = GET_SEC_FROM_SEG(sbi, end);
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;
f2fs_reset_gc_victim_resource(sbi, start, end);
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);
spin_lock(&FREE_I(sbi)->segmap_lock);
for (secno = MAIN_SECS(sbi); secno <= end_secno; secno++) {
if (!test_bit(secno, FREE_I(sbi)->free_secmap))
freed_secs++;
}
FREE_I(sbi)->free_sections -= freed_secs;
spin_unlock(&FREE_I(sbi)->segmap_lock);
/* Move out cursegs from the target range */
for (type = CURSEG_HOT_DATA; type < NR_CURSEG_PERSIST_TYPE; type++) {
for (type = CURSEG_HOT_DATA; type < NR_CURSEG_TYPE; type++) {
err = f2fs_allocate_segment_for_resize(sbi, type, start, end);
if (err)
goto out;
}
/* do GC to move out valid blocks in the range */
err = f2fs_gc_range(sbi, start, end, dry_run, 0);
err = f2fs_gc_range(sbi, start, end, dry_run, 0, false);
if (err || dry_run)
goto out;
@@ -2245,6 +2276,9 @@ static int free_segment_range(struct f2fs_sb_info *sbi,
f2fs_bug_on(sbi, 1);
}
out:
spin_lock(&FREE_I(sbi)->segmap_lock);
FREE_I(sbi)->free_sections += freed_secs;
spin_unlock(&FREE_I(sbi)->segmap_lock);
MAIN_SECS(sbi) += secs;
return err;
}
@@ -2422,7 +2456,7 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count)
set_sbi_flag(sbi, SBI_IS_RESIZEFS);
err = free_segment_range(sbi, secs, false);
if (err)
goto recover_out;
goto recover_user_blocks;
update_sb_metadata(sbi, -secs);
@@ -2444,11 +2478,14 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count)
f2fs_commit_super(sbi, false);
}
recover_out:
clear_sbi_flag(sbi, SBI_IS_RESIZEFS);
if (err) {
f2fs_bug_on(sbi, err == -EAGAIN);
set_sbi_flag(sbi, SBI_NEED_FSCK);
f2fs_err(sbi, "resize_fs failed, should run fsck to repair!");
}
recover_user_blocks:
clear_sbi_flag(sbi, SBI_IS_RESIZEFS);
if (err) {
spin_lock(&sbi->stat_lock);
sbi->user_block_count += shrunk_blocks;
spin_unlock(&sbi->stat_lock);

View File

@@ -45,32 +45,7 @@
#define NR_GC_CHECKPOINT_SECS (3) /* data/node/dentry sections */
struct f2fs_gc_kthread {
struct task_struct *f2fs_gc_task;
wait_queue_head_t gc_wait_queue_head;
/* for gc sleep time */
unsigned int urgent_sleep_time;
unsigned int min_sleep_time;
unsigned int max_sleep_time;
unsigned int no_gc_sleep_time;
/* for changing gc mode */
bool gc_wake;
/* for GC_MERGE mount option */
wait_queue_head_t fggc_wq; /*
* caller of f2fs_balance_fs()
* will wait on this wait queue.
*/
/* for gc control for zoned devices */
unsigned int no_zoned_gc_percent;
unsigned int boost_zoned_gc_percent;
unsigned int valid_thresh_ratio;
unsigned int boost_gc_multiple;
unsigned int boost_gc_greedy;
};
struct gc_inode_list {
struct list_head ilist;
@@ -197,6 +172,6 @@ static inline bool need_to_boost_gc(struct f2fs_sb_info *sbi)
{
if (f2fs_sb_has_blkzoned(sbi))
return !has_enough_free_blocks(sbi,
sbi->gc_thread->boost_zoned_gc_percent);
sbi->gc_thread.boost_zoned_gc_percent);
return has_enough_invalid_blocks(sbi);
}

View File

@@ -732,7 +732,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry,
f2fs_folio_put(folio, true);
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
f2fs_mark_inode_dirty_sync(dir, false);
f2fs_mark_inode_dirty_sync(dir, true);
if (inode)
f2fs_drop_nlink(dir, inode);

View File

@@ -9,6 +9,7 @@
#include <linux/f2fs_fs.h>
#include <linux/writeback.h>
#include <linux/sched/mm.h>
#include <linux/swap.h>
#include <linux/lz4.h>
#include <linux/zstd.h>
#include <linux/fserror.h>
@@ -24,6 +25,18 @@
extern const struct address_space_operations f2fs_compress_aops;
#endif
#define NUM_PREALLOC_EVICT_INODE_WORK 8
static struct kmem_cache *evict_inode_work_cache;
static mempool_t *evict_inode_work_pool;
struct evict_inode_work {
struct work_struct work;
struct f2fs_sb_info *sbi;
nid_t ino;
unsigned int add_ino_entry_bits;
};
void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync)
{
if (is_inode_flag_set(inode, FI_NEW_INODE))
@@ -637,6 +650,9 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
inode->i_fop = &f2fs_dir_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops;
mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS);
/* Let's prepare APPEND/UPDATE_INO before future access. */
flush_workqueue(sbi->evict_wq);
} else if (S_ISLNK(inode->i_mode)) {
if (file_is_encrypt(inode))
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
@@ -854,16 +870,32 @@ void f2fs_remove_donate_inode(struct inode *inode)
spin_unlock(&sbi->inode_lock[DONATE_INODE]);
}
static void f2fs_record_inode_state(struct f2fs_sb_info *sbi, nid_t ino,
unsigned int bits)
{
if (bits & BIT(APPEND_INO))
f2fs_add_ino_entry(sbi, ino, APPEND_INO);
if (bits & BIT(UPDATE_INO))
f2fs_add_ino_entry(sbi, ino, UPDATE_INO);
}
static void f2fs_evict_inode_work(struct work_struct *work)
{
struct evict_inode_work *ew =
container_of(work, struct evict_inode_work, work);
f2fs_record_inode_state(ew->sbi, ew->ino, ew->add_ino_entry_bits);
mempool_free(ew, evict_inode_work_pool);
}
/*
* Called at the last iput() if i_nlink is zero
* Return true, if we shouldn't go through post_evict_inode.
*/
void f2fs_evict_inode(struct inode *inode)
static bool f2fs_pre_evict_inode(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode);
nid_t xnid = fi->i_xattr_nid;
int err = 0;
bool freeze_protected = false;
f2fs_abort_atomic_write(inode, true);
@@ -883,13 +915,13 @@ void f2fs_evict_inode(struct inode *inode)
truncate_inode_pages_final(&inode->i_data);
if ((inode->i_nlink || is_bad_inode(inode)) &&
test_opt(sbi, COMPRESS_CACHE) && f2fs_compressed_file(inode))
test_opt(sbi, COMPRESS_CACHE) && f2fs_compressed_file(inode))
f2fs_invalidate_compress_pages(sbi, inode->i_ino);
if (inode->i_ino == F2FS_NODE_INO(sbi) ||
inode->i_ino == F2FS_META_INO(sbi) ||
inode->i_ino == F2FS_COMPRESS_INO(sbi))
goto out_clear;
inode->i_ino == F2FS_META_INO(sbi) ||
inode->i_ino == F2FS_COMPRESS_INO(sbi))
return true;
f2fs_bug_on(sbi, get_dirty_pages(inode));
f2fs_remove_dirty_inode(inode);
@@ -898,14 +930,18 @@ void f2fs_evict_inode(struct inode *inode)
if (!IS_DEVICE_ALIASING(inode))
f2fs_destroy_extent_tree(inode);
if (inode->i_nlink || is_bad_inode(inode))
goto no_delete;
return false;
}
err = f2fs_dquot_initialize(inode);
if (err) {
err = 0;
static void f2fs_delete_inode(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
bool freeze_protected = false;
struct f2fs_lock_context lc;
int err = 0;
if (f2fs_dquot_initialize(inode))
set_sbi_flag(sbi, SBI_QUOTA_NEED_REPAIR);
}
f2fs_remove_ino_entry(sbi, inode->i_ino, APPEND_INO);
f2fs_remove_ino_entry(sbi, inode->i_ino, UPDATE_INO);
@@ -924,30 +960,30 @@ void f2fs_evict_inode(struct inode *inode)
if (time_to_inject(sbi, FAULT_EVICT_INODE))
err = -EIO;
if (!err) {
struct f2fs_lock_context lc;
if (err)
goto error_check;
f2fs_lock_op(sbi, &lc);
err = f2fs_remove_inode_page(inode);
f2fs_unlock_op(sbi, &lc);
if (err == -ENOENT) {
err = 0;
f2fs_lock_op(sbi, &lc);
err = f2fs_remove_inode_page(inode);
f2fs_unlock_op(sbi, &lc);
/*
* in fuzzed image, another node may has the same
* block address as inode's, if it was truncated
* previously, truncation of inode node will fail.
*/
if (is_inode_flag_set(inode, FI_DIRTY_INODE)) {
f2fs_warn(F2FS_I_SB(inode),
"f2fs_evict_inode: inconsistent node id, ino:%llu",
inode->i_ino);
f2fs_inode_synced(inode);
set_sbi_flag(sbi, SBI_NEED_FSCK);
}
if (err == -ENOENT) {
err = 0;
/*
* in fuzzed image, another node may has the same
* block address as inode's, if it was truncated
* previously, truncation of inode node will fail.
*/
if (is_inode_flag_set(inode, FI_DIRTY_INODE)) {
f2fs_warn(F2FS_I_SB(inode),
"f2fs_evict_inode: inconsistent node id, ino:%llu",
inode->i_ino);
f2fs_inode_synced(inode);
set_sbi_flag(sbi, SBI_NEED_FSCK);
}
}
error_check:
/* give more chances, if ENOMEM case */
if (err == -ENOMEM) {
err = 0;
@@ -957,27 +993,38 @@ void f2fs_evict_inode(struct inode *inode)
if (IS_DEVICE_ALIASING(inode))
f2fs_destroy_extent_tree(inode);
if (err) {
f2fs_update_inode_page(inode);
if (dquot_initialize_needed(inode))
set_sbi_flag(sbi, SBI_QUOTA_NEED_REPAIR);
if (!err)
goto unfreeze_out;
/*
* If both f2fs_truncate() and f2fs_update_inode_page() failed
* due to fuzzed corrupted inode, call f2fs_inode_synced() to
* avoid triggering later f2fs_bug_on().
*/
if (is_inode_flag_set(inode, FI_DIRTY_INODE)) {
f2fs_warn(sbi,
"f2fs_evict_inode: inode is dirty, ino:%llu",
inode->i_ino);
f2fs_inode_synced(inode);
set_sbi_flag(sbi, SBI_NEED_FSCK);
}
f2fs_update_inode_page(inode);
if (dquot_initialize_needed(inode))
set_sbi_flag(sbi, SBI_QUOTA_NEED_REPAIR);
/*
* If both f2fs_truncate() and f2fs_update_inode_page() failed
* due to fuzzed corrupted inode, call f2fs_inode_synced() to
* avoid triggering later f2fs_bug_on().
*/
if (is_inode_flag_set(inode, FI_DIRTY_INODE)) {
f2fs_warn(sbi,
"f2fs_evict_inode: inode is dirty, ino:%llu",
inode->i_ino);
f2fs_inode_synced(inode);
set_sbi_flag(sbi, SBI_NEED_FSCK);
}
unfreeze_out:
if (freeze_protected)
sb_end_intwrite(inode->i_sb);
no_delete:
}
static void f2fs_post_evict_inode(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode);
nid_t xnid = fi->i_xattr_nid;
unsigned int record_bits = 0;
dquot_drop(inode);
stat_dec_inline_xattr(inode);
@@ -1003,12 +1050,32 @@ void f2fs_evict_inode(struct inode *inode)
inode->i_ino);
if (xnid)
invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid);
if (inode->i_nlink) {
if (is_inode_flag_set(inode, FI_APPEND_WRITE))
f2fs_add_ino_entry(sbi, inode->i_ino, APPEND_INO);
if (is_inode_flag_set(inode, FI_UPDATE_WRITE))
f2fs_add_ino_entry(sbi, inode->i_ino, UPDATE_INO);
if (!inode->i_nlink)
goto skip_record;
if (is_inode_flag_set(inode, FI_APPEND_WRITE))
record_bits = BIT(APPEND_INO);
if (is_inode_flag_set(inode, FI_UPDATE_WRITE))
record_bits = BIT(UPDATE_INO);
if (!record_bits)
goto skip_record;
/* Let's do this in workqueue out of the direct reclaim path. */
if (current_is_kswapd()) {
f2fs_record_inode_state(sbi, inode->i_ino, record_bits);
} else {
struct evict_inode_work *ew =
mempool_alloc(evict_inode_work_pool, GFP_NOFS);
ew->sbi = sbi;
ew->ino = inode->i_ino;
ew->add_ino_entry_bits = record_bits;
INIT_WORK(&ew->work, f2fs_evict_inode_work);
queue_work(sbi->evict_wq, &ew->work);
}
skip_record:
if (is_inode_flag_set(inode, FI_FREE_NID)) {
f2fs_alloc_nid_failed(sbi, inode->i_ino);
clear_inode_flag(inode, FI_FREE_NID);
@@ -1019,13 +1086,29 @@ void f2fs_evict_inode(struct inode *inode)
* In that case, f2fs_check_nid_range() is enough to give a clue.
*/
}
out_clear:
}
/*
* Called at the last iput() if i_nlink is zero
*/
void f2fs_evict_inode(struct inode *inode)
{
if (f2fs_pre_evict_inode(inode))
goto clear_out;
if (!inode->i_nlink && !is_bad_inode(inode))
f2fs_delete_inode(inode);
f2fs_post_evict_inode(inode);
clear_out:
fscrypt_put_encryption_info(inode);
clear_inode(inode);
}
/* caller should call f2fs_lock_op() */
void f2fs_handle_failed_inode(struct inode *inode, struct f2fs_lock_context *lc)
void f2fs_handle_failed_inode(struct inode *inode,
struct f2fs_lock_context *lc, bool orphan_free)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct node_info ni;
@@ -1047,6 +1130,9 @@ void f2fs_handle_failed_inode(struct inode *inode, struct f2fs_lock_context *lc)
/* don't make bad inode, since it becomes a regular file. */
unlock_new_inode(inode);
if (!orphan_free)
goto out;
/*
* Note: we should add inode to orphan list before f2fs_unlock_op()
* so we can prevent losing this orphan when encoutering checkpoint
@@ -1079,3 +1165,29 @@ void f2fs_handle_failed_inode(struct inode *inode, struct f2fs_lock_context *lc)
/* iput will drop the inode object */
iput(inode);
}
int __init f2fs_init_evict_inode_work(void)
{
evict_inode_work_cache =
kmem_cache_create("f2fs_evict_inode_work",
sizeof(struct evict_inode_work), 0, 0, NULL);
if (!evict_inode_work_cache)
goto fail;
evict_inode_work_pool =
mempool_create_slab_pool(NUM_PREALLOC_EVICT_INODE_WORK,
evict_inode_work_cache);
if (!evict_inode_work_pool)
goto fail_free_cache;
return 0;
fail_free_cache:
kmem_cache_destroy(evict_inode_work_cache);
fail:
return -ENOMEM;
}
void f2fs_destroy_evict_inode_work(void)
{
mempool_destroy(evict_inode_work_pool);
kmem_cache_destroy(evict_inode_work_cache);
}

View File

@@ -332,6 +332,12 @@ void f2fs_destroy_iostat_processing(void)
int f2fs_init_iostat(struct f2fs_sb_info *sbi)
{
/*
* The f2fs_iostat tracepoint emits a fixed number of read folio order
* buckets; make sure every order fits so none is silently dropped.
*/
BUILD_BUG_ON(NR_PAGE_ORDERS > F2FS_IOSTAT_RD_FOLIO_ORDERS);
/* init iostat info */
spin_lock_init(&sbi->iostat_lock);
spin_lock_init(&sbi->iostat_lat_lock);

View File

@@ -402,13 +402,16 @@ static int f2fs_create(struct mnt_idmap *idmap, struct inode *dir,
d_instantiate_new(dentry, inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
if (IS_DIRSYNC(dir)) {
err = f2fs_sync_fs(sbi->sb, 1);
if (err)
return err;
}
f2fs_balance_fs(sbi, true);
return 0;
out:
f2fs_handle_failed_inode(inode, &lc);
f2fs_handle_failed_inode(inode, &lc, true);
return err;
}
@@ -425,6 +428,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;
@@ -452,8 +458,11 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
d_instantiate(dentry, inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
if (IS_DIRSYNC(dir)) {
err = f2fs_sync_fs(sbi->sb, 1);
if (err)
return err;
}
return 0;
out:
clear_inode_flag(inode, FI_INC_LINK);
@@ -557,35 +566,31 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
return ERR_PTR(err);
}
static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
static int __do_unlink(struct inode *dir, struct inode *inode,
const struct qstr *name)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
struct inode *inode = d_inode(dentry);
struct f2fs_dir_entry *de;
struct f2fs_lock_context lc;
struct folio *folio;
int err;
trace_f2fs_unlink_enter(dir, dentry);
if (IS_DEVICE_ALIASING(inode))
return -EPERM;
if (unlikely(f2fs_cp_error(sbi))) {
err = -EIO;
goto out;
}
if (unlikely(f2fs_cp_error(sbi)))
return -EIO;
err = f2fs_dquot_initialize(dir);
if (err)
goto out;
return err;
err = f2fs_dquot_initialize(inode);
if (err)
goto out;
return err;
de = f2fs_find_entry(dir, &dentry->d_name, &folio);
if (!de) {
if (IS_ERR(folio))
err = PTR_ERR(folio);
goto out;
}
de = f2fs_find_entry(dir, name, &folio);
if (!de)
return IS_ERR(folio) ? PTR_ERR(folio) : 0;
if (unlikely(inode->i_nlink == 0)) {
f2fs_warn(sbi, "%s: inode (ino=%llx) has zero i_nlink",
@@ -603,11 +608,28 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
err = f2fs_acquire_orphan_inode(sbi);
if (err) {
f2fs_unlock_op(sbi, &lc);
f2fs_folio_put(folio, false);
goto out;
goto err_out;
}
f2fs_delete_entry(de, folio, dir, inode);
f2fs_unlock_op(sbi, &lc);
return 0;
corrupted:
err = -EFSCORRUPTED;
set_sbi_flag(sbi, SBI_NEED_FSCK);
err_out:
f2fs_folio_put(folio, false);
return err;
}
static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
{
int err;
trace_f2fs_unlink_enter(dir, dentry);
err = __do_unlink(dir, d_inode(dentry), &dentry->d_name);
if (err)
goto out;
/* VFS negative dentries are incompatible with Encoding and
* Case-insensitiveness. Eventually we'll want avoid
@@ -619,15 +641,9 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
d_invalidate(dentry);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
goto out;
corrupted:
err = -EFSCORRUPTED;
set_sbi_flag(sbi, SBI_NEED_FSCK);
f2fs_folio_put(folio, false);
err = f2fs_sync_fs(F2FS_I_SB(dir)->sb, 1);
out:
trace_f2fs_unlink_exit(inode, err);
trace_f2fs_unlink_exit(d_inode(dentry), err);
return err;
}
@@ -654,6 +670,8 @@ static int f2fs_symlink(struct mnt_idmap *idmap, struct inode *dir,
struct inode *inode;
size_t len = strlen(symname);
struct fscrypt_str disk_link;
bool orphan_free = true;
int ret = -EAGAIN;
int err;
if (unlikely(f2fs_cp_error(sbi)))
@@ -684,17 +702,19 @@ static int f2fs_symlink(struct mnt_idmap *idmap, struct inode *dir,
f2fs_lock_op(sbi, &lc);
err = f2fs_add_link(dentry, inode);
if (err)
goto out_f2fs_handle_failed_inode;
goto free_inode;
f2fs_unlock_op(sbi, &lc);
f2fs_alloc_nid_done(sbi, inode->i_ino);
/* Write the symlink path to the new inode. */
err = fscrypt_encrypt_symlink(inode, symname, len, &disk_link);
if (err)
goto err_out;
goto unlink_free_inode;
err = page_symlink(inode, disk_link.name, disk_link.len);
if (err)
goto unlink_free_inode;
err_out:
d_instantiate_new(dentry, inode);
/*
@@ -706,25 +726,29 @@ static int f2fs_symlink(struct mnt_idmap *idmap, struct inode *dir,
* If the symlink path is stored into inline_data, there is no
* performance regression.
*/
if (!err) {
filemap_write_and_wait_range(inode->i_mapping, 0,
disk_link.len - 1);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
} else {
f2fs_unlink(dir, dentry);
}
ret = filemap_write_and_wait_range(inode->i_mapping, 0,
disk_link.len - 1);
if (!ret && IS_DIRSYNC(dir))
err = f2fs_sync_fs(sbi->sb, 1);
f2fs_balance_fs(sbi, true);
goto out_free_encrypted_link;
out_f2fs_handle_failed_inode:
f2fs_handle_failed_inode(inode, &lc);
out_free_encrypted_link:
out:
if (disk_link.name != (unsigned char *)symname)
kfree(disk_link.name);
return err;
unlink_free_inode:
ret = __do_unlink(dir, inode, &dentry->d_name);
if (ret) {
/* Give up and leave a broken symlink. */
d_instantiate_new(dentry, inode);
goto out;
}
orphan_free = false;
f2fs_lock_op(sbi, &lc);
free_inode:
f2fs_handle_failed_inode(inode, &lc, orphan_free);
goto out;
}
static struct dentry *f2fs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
@@ -762,15 +786,18 @@ static struct dentry *f2fs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
d_instantiate_new(dentry, inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
if (IS_DIRSYNC(dir)) {
err = f2fs_sync_fs(sbi->sb, 1);
if (err)
return ERR_PTR(err);
}
f2fs_balance_fs(sbi, true);
return NULL;
out_fail:
clear_inode_flag(inode, FI_INC_LINK);
f2fs_handle_failed_inode(inode, &lc);
f2fs_handle_failed_inode(inode, &lc, true);
return ERR_PTR(err);
}
@@ -817,13 +844,16 @@ static int f2fs_mknod(struct mnt_idmap *idmap, struct inode *dir,
d_instantiate_new(dentry, inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
if (IS_DIRSYNC(dir)) {
err = f2fs_sync_fs(sbi->sb, 1);
if (err)
return err;
}
f2fs_balance_fs(sbi, true);
return 0;
out:
f2fs_handle_failed_inode(inode, &lc);
f2fs_handle_failed_inode(inode, &lc, true);
return err;
}
@@ -894,7 +924,7 @@ static int __f2fs_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
release_out:
f2fs_release_orphan_inode(sbi);
out:
f2fs_handle_failed_inode(inode, &lc);
f2fs_handle_failed_inode(inode, &lc, true);
return err;
}
@@ -946,6 +976,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 +1049,10 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
}
if (new_inode) {
if (IS_DEVICE_ALIASING(new_inode)) {
err = -EPERM;
goto out_dir;
}
err = -ENOTEMPTY;
if (old_is_dir && !f2fs_empty_dir(new_inode))
@@ -1076,7 +1113,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
f2fs_up_write(&F2FS_I(old_inode)->i_sem);
inode_set_ctime_current(old_inode);
f2fs_mark_inode_dirty_sync(old_inode, false);
f2fs_mark_inode_dirty_sync(old_inode, true);
f2fs_delete_entry(old_entry, old_folio, old_dir, NULL);
old_folio = NULL;
@@ -1110,8 +1147,11 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
f2fs_unlock_op(sbi, &lc);
if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
f2fs_sync_fs(sbi->sb, 1);
if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) {
err = f2fs_sync_fs(sbi->sb, 1);
if (err)
return err;
}
f2fs_update_time(sbi, REQ_TIME);
return 0;
@@ -1143,6 +1183,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))
@@ -1246,7 +1289,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
f2fs_i_links_write(old_dir, old_nlink > 0);
f2fs_up_write(&F2FS_I(old_dir)->i_sem);
}
f2fs_mark_inode_dirty_sync(old_dir, false);
f2fs_mark_inode_dirty_sync(old_dir, true);
/* update directory entry info of new dir inode */
f2fs_set_link(new_dir, new_entry, new_folio, old_inode);
@@ -1265,7 +1308,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
f2fs_i_links_write(new_dir, new_nlink > 0);
f2fs_up_write(&F2FS_I(new_dir)->i_sem);
}
f2fs_mark_inode_dirty_sync(new_dir, false);
f2fs_mark_inode_dirty_sync(new_dir, true);
if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT) {
f2fs_add_ino_entry(sbi, old_dir->i_ino, TRANS_DIR_INO);
@@ -1274,8 +1317,11 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
f2fs_unlock_op(sbi, &lc);
if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
f2fs_sync_fs(sbi->sb, 1);
if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) {
err = f2fs_sync_fs(sbi->sb, 1);
if (err)
return err;
}
f2fs_update_time(sbi, REQ_TIME);
return 0;

View File

@@ -401,15 +401,16 @@ bool f2fs_need_dentry_mark(struct f2fs_sb_info *sbi, nid_t nid)
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct nat_entry *e;
bool need = false;
struct f2fs_lock_context lc;
f2fs_down_read(&nm_i->nat_tree_lock);
f2fs_down_read_trace(&nm_i->nat_tree_lock, &lc);
e = __lookup_nat_cache(nm_i, nid, false);
if (e) {
if (!get_nat_flag(e, IS_CHECKPOINTED) &&
!get_nat_flag(e, HAS_FSYNCED_INODE))
need = true;
}
f2fs_up_read(&nm_i->nat_tree_lock);
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
return need;
}
@@ -418,12 +419,13 @@ bool f2fs_is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid)
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct nat_entry *e;
bool is_cp = true;
struct f2fs_lock_context lc;
f2fs_down_read(&nm_i->nat_tree_lock);
f2fs_down_read_trace(&nm_i->nat_tree_lock, &lc);
e = __lookup_nat_cache(nm_i, nid, false);
if (e && !get_nat_flag(e, IS_CHECKPOINTED))
is_cp = false;
f2fs_up_read(&nm_i->nat_tree_lock);
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
return is_cp;
}
@@ -432,16 +434,16 @@ bool f2fs_need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino)
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct nat_entry *e;
bool need_update = true;
struct f2fs_lock_context lc;
struct f2fs_lock_context lc, nlc;
f2fs_down_read_trace(&sbi->node_write, &lc);
f2fs_down_read(&nm_i->nat_tree_lock);
f2fs_down_read_trace(&nm_i->nat_tree_lock, &nlc);
e = __lookup_nat_cache(nm_i, ino, false);
if (e && get_nat_flag(e, HAS_LAST_FSYNC) &&
(get_nat_flag(e, IS_CHECKPOINTED) ||
get_nat_flag(e, HAS_FSYNCED_INODE)))
need_update = false;
f2fs_up_read(&nm_i->nat_tree_lock);
f2fs_up_read_trace(&nm_i->nat_tree_lock, &nlc);
f2fs_up_read_trace(&sbi->node_write, &lc);
return need_update;
}
@@ -452,6 +454,7 @@ static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid,
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
struct nat_entry *new, *e;
struct f2fs_lock_context lc;
/* Let's mitigate lock contention of nat_tree_lock during checkpoint */
if (f2fs_rwsem_is_locked(&sbi->cp_global_sem))
@@ -461,7 +464,7 @@ static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid,
if (!new)
return;
f2fs_down_write(&nm_i->nat_tree_lock);
f2fs_down_write_trace(&nm_i->nat_tree_lock, &lc);
e = __lookup_nat_cache(nm_i, nid, false);
if (!e)
e = __init_nat_entry(nm_i, new, ne, false, false);
@@ -470,7 +473,7 @@ static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid,
nat_get_blkaddr(e) !=
le32_to_cpu(ne->block_addr) ||
nat_get_version(e) != ne->version);
f2fs_up_write(&nm_i->nat_tree_lock);
f2fs_up_write_trace(&nm_i->nat_tree_lock, &lc);
if (e != new)
__free_nat_entry(new);
}
@@ -482,8 +485,9 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
struct nat_entry *e;
struct nat_entry *new = __alloc_nat_entry(sbi, ni->nid, true);
bool init_dirty = false;
struct f2fs_lock_context lc;
f2fs_down_write(&nm_i->nat_tree_lock);
f2fs_down_write_trace(&nm_i->nat_tree_lock, &lc);
e = __lookup_nat_cache(nm_i, ni->nid, true);
if (!e) {
init_dirty = true;
@@ -533,15 +537,16 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
set_nat_flag(e, HAS_FSYNCED_INODE, true);
set_nat_flag(e, HAS_LAST_FSYNC, fsync_done);
}
f2fs_up_write(&nm_i->nat_tree_lock);
f2fs_up_write_trace(&nm_i->nat_tree_lock, &lc);
}
int f2fs_try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
int nr = nr_shrink;
struct f2fs_lock_context lc;
if (!f2fs_down_write_trylock(&nm_i->nat_tree_lock))
if (!f2fs_down_write_trylock_trace(&nm_i->nat_tree_lock, &lc))
return 0;
spin_lock(&nm_i->nat_list_lock);
@@ -563,7 +568,7 @@ int f2fs_try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)
}
spin_unlock(&nm_i->nat_list_lock);
f2fs_up_write(&nm_i->nat_tree_lock);
f2fs_up_write_trace(&nm_i->nat_tree_lock, &lc);
return nr - nr_shrink;
}
@@ -581,18 +586,19 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid,
pgoff_t index;
int i;
bool need_cache = true;
struct f2fs_lock_context lc;
ni->flag = 0;
ni->nid = nid;
retry:
/* Check nat cache */
f2fs_down_read(&nm_i->nat_tree_lock);
f2fs_down_read_trace(&nm_i->nat_tree_lock, &lc);
e = __lookup_nat_cache(nm_i, nid, false);
if (e) {
ni->ino = nat_get_ino(e);
ni->blk_addr = nat_get_blkaddr(e);
ni->version = nat_get_version(e);
f2fs_up_read(&nm_i->nat_tree_lock);
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
if (IS_ENABLED(CONFIG_F2FS_CHECK_FS)) {
need_cache = false;
goto sanity_check;
@@ -610,7 +616,7 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid,
down_read(&curseg->journal_rwsem);
} else if (f2fs_rwsem_is_contended(&nm_i->nat_tree_lock) ||
!down_read_trylock(&curseg->journal_rwsem)) {
f2fs_up_read(&nm_i->nat_tree_lock);
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
goto retry;
}
@@ -621,13 +627,13 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid,
}
up_read(&curseg->journal_rwsem);
if (i >= 0) {
f2fs_up_read(&nm_i->nat_tree_lock);
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
goto sanity_check;
}
/* Fill node_info from nat page */
index = current_nat_addr(sbi, nid);
f2fs_up_read(&nm_i->nat_tree_lock);
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
folio = f2fs_get_meta_folio(sbi, index);
if (IS_ERR(folio))
@@ -660,6 +666,7 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid,
__builtin_return_address(0),
ni->ino, ni->nid, ni->blk_addr, ni->version, ni->flag);
f2fs_handle_error(sbi, ERROR_INCONSISTENT_NAT);
return -EFSCORRUPTED;
}
/* cache nat entry */
@@ -1617,7 +1624,7 @@ static struct folio *__get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid,
if (!err)
return folio;
out_err:
folio_clear_uptodate(folio);
clear_node_folio_dirty(folio);
out_put_err:
/* ENOENT comes from read_node_folio which is not an error. */
if (err != -ENOENT)
@@ -1789,7 +1796,7 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
/* get old block addr of this node page */
nid = nid_of_node(folio);
if (f2fs_sanity_check_node_footer(sbi, folio, nid,
if (f2fs_sanity_check_node_footer(sbi, folio, folio->index,
NODE_TYPE_REGULAR, false)) {
fserror_report_metadata(sbi->sb, -EFSCORRUPTED, GFP_NOFS);
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_CORRUPTED_NID);
@@ -2009,6 +2016,11 @@ int f2fs_fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode,
f2fs_debug(sbi, "Retry to write fsync mark: ino=%u, idx=%lx",
ino, last_folio->index);
folio_lock(last_folio);
if (unlikely(!is_node_folio(last_folio))) {
f2fs_folio_put(last_folio, true);
ret = -EAGAIN;
goto out;
}
f2fs_folio_wait_writeback(last_folio, NODE, true, true);
folio_mark_dirty(last_folio);
folio_unlock(last_folio);
@@ -2566,8 +2578,9 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi)
struct f2fs_nm_info *nm_i = NM_I(sbi);
unsigned int i, idx;
nid_t nid;
struct f2fs_lock_context lc;
f2fs_down_read(&nm_i->nat_tree_lock);
f2fs_down_read_trace(&nm_i->nat_tree_lock, &lc);
for (i = 0; i < nm_i->nat_blocks; i++) {
if (!test_bit_le(i, nm_i->nat_block_bitmap))
@@ -2590,7 +2603,7 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi)
out:
scan_curseg_cache(sbi);
f2fs_up_read(&nm_i->nat_tree_lock);
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
}
static int __f2fs_build_free_nids(struct f2fs_sb_info *sbi,
@@ -2599,6 +2612,7 @@ static int __f2fs_build_free_nids(struct f2fs_sb_info *sbi,
struct f2fs_nm_info *nm_i = NM_I(sbi);
int i = 0, ret;
nid_t nid = nm_i->next_scan_nid;
struct f2fs_lock_context lc;
if (unlikely(nid >= nm_i->max_nid))
nid = 0;
@@ -2625,7 +2639,7 @@ static int __f2fs_build_free_nids(struct f2fs_sb_info *sbi,
f2fs_ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), FREE_NID_PAGES,
META_NAT, true);
f2fs_down_read(&nm_i->nat_tree_lock);
f2fs_down_read_trace(&nm_i->nat_tree_lock, &lc);
while (1) {
if (!test_bit_le(NAT_BLOCK_OFFSET(nid),
@@ -2641,7 +2655,7 @@ static int __f2fs_build_free_nids(struct f2fs_sb_info *sbi,
}
if (ret) {
f2fs_up_read(&nm_i->nat_tree_lock);
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
if (ret == -EFSCORRUPTED) {
f2fs_err(sbi, "NAT is corrupt, run fsck to fix it");
@@ -2668,7 +2682,7 @@ static int __f2fs_build_free_nids(struct f2fs_sb_info *sbi,
/* find free nids from current sum_pages */
scan_curseg_cache(sbi);
f2fs_up_read(&nm_i->nat_tree_lock);
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
f2fs_ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nm_i->next_scan_nid),
nm_i->ra_nid_pages, META_NAT, false);
@@ -3205,21 +3219,22 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
nid_t set_idx = 0;
LIST_HEAD(sets);
int err = 0;
struct f2fs_lock_context lc;
/*
* during unmount, let's flush nat_bits before checking
* nat_cnt[DIRTY_NAT].
*/
if (enabled_nat_bits(sbi, cpc)) {
f2fs_down_write(&nm_i->nat_tree_lock);
f2fs_down_write_trace(&nm_i->nat_tree_lock, &lc);
remove_nats_in_journal(sbi);
f2fs_up_write(&nm_i->nat_tree_lock);
f2fs_up_write_trace(&nm_i->nat_tree_lock, &lc);
}
if (!nm_i->nat_cnt[DIRTY_NAT])
return 0;
f2fs_down_write(&nm_i->nat_tree_lock);
f2fs_down_write_trace(&nm_i->nat_tree_lock, &lc);
/*
* if there are no enough space in journal to store dirty nat
@@ -3260,7 +3275,7 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
break;
}
f2fs_up_write(&nm_i->nat_tree_lock);
f2fs_up_write_trace(&nm_i->nat_tree_lock, &lc);
/* Allow dirty nats by node block allocation in write_begin */
return err;
@@ -3379,7 +3394,8 @@ static int init_node_manager(struct f2fs_sb_info *sbi)
mutex_init(&nm_i->build_lock);
spin_lock_init(&nm_i->nid_list_lock);
init_f2fs_rwsem(&nm_i->nat_tree_lock);
init_f2fs_rwsem_trace(&nm_i->nat_tree_lock, sbi,
LOCK_NAME_NAT_TREE_LOCK);
nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid);
nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP);
@@ -3471,6 +3487,7 @@ void f2fs_destroy_node_manager(struct f2fs_sb_info *sbi)
struct nat_entry_set **setvec = (struct nat_entry_set **)vec;
nid_t nid = 0;
unsigned int found;
struct f2fs_lock_context lc;
if (!nm_i)
return;
@@ -3489,7 +3506,7 @@ void f2fs_destroy_node_manager(struct f2fs_sb_info *sbi)
spin_unlock(&nm_i->nid_list_lock);
/* destroy nat cache */
f2fs_down_write(&nm_i->nat_tree_lock);
f2fs_down_write_trace(&nm_i->nat_tree_lock, &lc);
while ((found = __gang_lookup_nat_cache(nm_i,
nid, NAT_VEC_SIZE, natvec))) {
unsigned idx;
@@ -3520,7 +3537,7 @@ void f2fs_destroy_node_manager(struct f2fs_sb_info *sbi)
kmem_cache_free(nat_entry_set_slab, setvec[idx]);
}
}
f2fs_up_write(&nm_i->nat_tree_lock);
f2fs_up_write_trace(&nm_i->nat_tree_lock, &lc);
kvfree(nm_i->nat_block_bitmap);
if (nm_i->free_nid_bitmap) {

View File

@@ -116,18 +116,26 @@ static void del_fsync_inode(struct fsync_inode_entry *entry, int drop)
}
static int init_recovered_filename(const struct inode *dir,
struct inode *inode,
struct f2fs_inode *raw_inode,
struct f2fs_filename *fname,
struct qstr *usr_fname)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
int err;
memset(fname, 0, sizeof(*fname));
fname->disk_name.len = le32_to_cpu(raw_inode->i_namelen);
fname->disk_name.name = raw_inode->i_name;
if (WARN_ON(fname->disk_name.len > F2FS_NAME_LEN))
return -ENAMETOOLONG;
if (unlikely(!fname->disk_name.len ||
fname->disk_name.len > F2FS_NAME_LEN)) {
f2fs_err(sbi, "invalid recovered filename length %u for ino %llu",
fname->disk_name.len, inode->i_ino);
set_sbi_flag(sbi, SBI_NEED_FSCK);
f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE);
return -EFSCORRUPTED;
}
if (!IS_ENCRYPTED(dir)) {
usr_fname->name = fname->disk_name.name;
@@ -158,6 +166,22 @@ static int init_recovered_filename(const struct inode *dir,
return 0;
}
static const char *recover_printable_name(struct inode *inode,
struct f2fs_inode *raw,
int *name_len)
{
static const char encrypted_name[] = "<encrypted>";
if (file_enc_name(inode)) {
*name_len = sizeof(encrypted_name) - 1;
return encrypted_name;
}
*name_len = min_t(unsigned int, le32_to_cpu(raw->i_namelen),
F2FS_NAME_LEN);
return raw->i_name;
}
static int recover_dentry(struct inode *inode, struct folio *ifolio,
struct list_head *dir_list)
{
@@ -170,7 +194,8 @@ static int recover_dentry(struct inode *inode, struct folio *ifolio,
struct inode *dir, *einode;
struct fsync_inode_entry *entry;
int err = 0;
char *name;
const char *name;
int name_len;
entry = get_fsync_inode(dir_list, pino);
if (!entry) {
@@ -184,7 +209,7 @@ static int recover_dentry(struct inode *inode, struct folio *ifolio,
}
dir = entry->inode;
err = init_recovered_filename(dir, raw_inode, &fname, &usr_fname);
err = init_recovered_filename(dir, inode, raw_inode, &fname, &usr_fname);
if (err)
goto out;
retry:
@@ -229,12 +254,9 @@ static int recover_dentry(struct inode *inode, struct folio *ifolio,
out_put:
f2fs_folio_put(folio, false);
out:
if (file_enc_name(inode))
name = "<encrypted>";
else
name = raw_inode->i_name;
f2fs_notice(F2FS_I_SB(inode), "%s: ino = %x, name = %s, dir = %llu, err = %d",
__func__, ino_of_node(ifolio), name,
name = recover_printable_name(inode, raw_inode, &name_len);
f2fs_notice(F2FS_I_SB(inode), "%s: ino = %x, name = %.*s, dir = %llu, err = %d",
__func__, ino_of_node(ifolio), name_len, name,
IS_ERR(dir) ? 0 : dir->i_ino, err);
return err;
}
@@ -282,7 +304,8 @@ static int recover_inode(struct inode *inode, struct folio *folio)
{
struct f2fs_inode *raw = F2FS_INODE(folio);
struct f2fs_inode_info *fi = F2FS_I(inode);
char *name;
const char *name;
int name_len;
int err;
inode->i_mode = le16_to_cpu(raw->i_mode);
@@ -331,13 +354,11 @@ static int recover_inode(struct inode *inode, struct folio *folio)
f2fs_mark_inode_dirty_sync(inode, true);
if (file_enc_name(inode))
name = "<encrypted>";
else
name = F2FS_INODE(folio)->i_name;
name = recover_printable_name(inode, raw, &name_len);
f2fs_notice(F2FS_I_SB(inode), "recover_inode: ino = %x, name = %s, inline = %x",
ino_of_node(folio), name, raw->i_inline);
f2fs_notice(F2FS_I_SB(inode), "%s: ino = %x, name = %.*s, inline = %x",
__func__, ino_of_node(folio), name_len, name,
raw->i_inline);
return 0;
}

View File

@@ -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;
@@ -452,15 +452,14 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
f2fs_submit_merged_write(sbi, DATA);
f2fs_submit_all_merged_ipu_writes(sbi);
if (test_opt(sbi, GC_MERGE) && sbi->gc_thread &&
sbi->gc_thread->f2fs_gc_task) {
if (test_opt(sbi, GC_MERGE) && sbi->gc_thread.f2fs_gc_task) {
DEFINE_WAIT(wait);
prepare_to_wait(&sbi->gc_thread->fggc_wq, &wait,
prepare_to_wait(&sbi->gc_thread.fggc_wq, &wait,
TASK_UNINTERRUPTIBLE);
wake_up(&sbi->gc_thread->gc_wait_queue_head);
wake_up(&sbi->gc_thread.gc_wait_queue_head);
io_schedule();
finish_wait(&sbi->gc_thread->fggc_wq, &wait);
finish_wait(&sbi->gc_thread.fggc_wq, &wait);
} else {
struct f2fs_gc_control gc_control = {
.victim_segno = NULL_SEGNO,
@@ -566,21 +565,59 @@ static int __submit_flush_wait(struct f2fs_sb_info *sbi,
return ret;
}
static void f2fs_flush_end_io(struct bio *bio)
{
complete(bio->bi_private);
}
struct f2fs_flush_bio {
struct bio bio;
struct completion wait;
};
static int submit_flush_wait(struct f2fs_sb_info *sbi, nid_t ino)
{
struct f2fs_flush_bio *flush_bio;
unsigned long devices = 0;
int ret = 0;
int i;
if (!f2fs_is_multi_device(sbi))
return __submit_flush_wait(sbi, sbi->sb->s_bdev);
flush_bio = kmalloc(array_size(sbi->s_ndevs, sizeof(*flush_bio)),
GFP_NOFS | __GFP_NOFAIL);
for (i = 0; i < sbi->s_ndevs; i++) {
if (!f2fs_is_dirty_device(sbi, ino, i, FLUSH_INO))
continue;
ret = __submit_flush_wait(sbi, FDEV(i).bdev);
if (ret)
break;
bio_init(&flush_bio[i].bio, FDEV(i).bdev, NULL, 0,
REQ_OP_WRITE | REQ_SYNC | REQ_PREFLUSH);
init_completion(&flush_bio[i].wait);
flush_bio[i].bio.bi_private = &flush_bio[i].wait;
flush_bio[i].bio.bi_end_io = f2fs_flush_end_io;
devices |= BIT(i);
submit_bio(&flush_bio[i].bio);
}
for (i = 0; i < sbi->s_ndevs; i++) {
int err;
if (!(devices & BIT(i)))
continue;
wait_for_completion(&flush_bio[i].wait);
err = blk_status_to_errno(flush_bio[i].bio.bi_status);
trace_f2fs_issue_flush(FDEV(i).bdev, test_opt(sbi, NOBARRIER),
test_opt(sbi, FLUSH_MERGE), err);
if (!err)
f2fs_update_iostat(sbi, NULL, FS_FLUSH_IO, 0);
else if (!ret)
ret = err;
bio_uninit(&flush_bio[i].bio);
}
kfree(flush_bio);
return ret;
}
@@ -2502,35 +2539,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--;
/*
* 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++;
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;
}
}
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_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 + 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 +2629,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 +2669,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 +2853,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;
}
@@ -2777,8 +2877,10 @@ static int get_new_segment(struct f2fs_sb_info *sbi,
unsigned int old_zoneno = GET_ZONE_FROM_SEG(sbi, *newseg);
unsigned int alloc_policy = sbi->allocate_section_policy;
unsigned int alloc_hint = sbi->allocate_section_hint;
unsigned int max_secno = MAIN_SECS(sbi);
bool init = true;
int i;
bool looped = false;
int i, devi;
int ret = 0;
spin_lock(&free_i->segmap_lock);
@@ -2791,8 +2893,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
@@ -2802,7 +2909,7 @@ static int get_new_segment(struct f2fs_sb_info *sbi,
*/
if (f2fs_sb_has_blkzoned(sbi)) {
/* Prioritize writing to conventional zones */
if (sbi->blkzone_alloc_policy == BLKZONE_ALLOC_PRIOR_CONV || pinning)
if (sbi->blkzone_alloc_policy == BLKZONE_ALLOC_PRIOR_CONV)
segno = 0;
else
segno = max(sbi->first_seq_zone_segno, *newseg);
@@ -2818,43 +2925,57 @@ static int get_new_segment(struct f2fs_sb_info *sbi,
alloc_hint > MAIN_SECS(sbi))
alloc_hint = MAIN_SECS(sbi);
if (alloc_policy == ALLOCATE_FORWARD_FROM_HINT &&
hint < alloc_hint)
hint = alloc_hint;
else if (alloc_policy == ALLOCATE_FORWARD_WITHIN_HINT &&
hint >= alloc_hint)
if (pinning) {
max_secno = sbi->pinned_area_max_secno;
hint = 0;
} else if (alloc_policy == ALLOCATE_FORWARD_FROM_HINT &&
hint < alloc_hint) {
hint = alloc_hint;
} else if (alloc_policy == ALLOCATE_FORWARD_WITHIN_HINT &&
hint >= alloc_hint) {
hint = 0;
}
find_other_zone:
secno = find_next_zero_bit(free_i->free_secmap, MAIN_SECS(sbi), hint);
secno = find_next_zero_bit(free_i->free_secmap, max_secno, 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)) {
ret = -ENOSPC;
if (secno >= max_secno) {
if (looped) {
ret = (pinning && has_unpinned_area(sbi)) ?
-EAGAIN : -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 */
@@ -2886,12 +3007,6 @@ static int get_new_segment(struct f2fs_sb_info *sbi,
goto out_unlock;
}
/* no free section in conventional device or conventional zone */
if (new_sec && pinning &&
f2fs_is_sequential_zone_area(sbi, START_BLOCK(sbi, segno))) {
ret = -EAGAIN;
goto out_unlock;
}
__set_inuse(sbi, segno);
*newseg = segno;
out_unlock:
@@ -2937,7 +3052,7 @@ static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type)
sanity_check_seg_type(sbi, seg_type);
if (__is_large_section(sbi)) {
if (f2fs_need_rand_seg(sbi)) {
if (f2fs_need_rand_seg_blk(sbi, type)) {
unsigned int hint = GET_SEC_FROM_SEG(sbi, curseg->segno);
if (GET_SEC_FROM_SEG(sbi, curseg->segno + 1) != hint)
@@ -2946,7 +3061,7 @@ static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type)
GET_SEG_FROM_SEC(sbi, hint + 1) - 1);
}
return curseg->segno;
} else if (f2fs_need_rand_seg(sbi)) {
} else if (f2fs_need_rand_seg_blk(sbi, type)) {
return get_random_u32_below(MAIN_SECS(sbi) * SEGS_PER_SEC(sbi));
}
@@ -3002,7 +3117,7 @@ static int new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec)
curseg->next_segno = segno;
reset_curseg(sbi, type, 1);
curseg->alloc_type = LFS;
if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK)
if (f2fs_need_rand_blk(sbi, type))
curseg->fragment_remained_chunk =
get_random_u32_inclusive(1, sbi->max_fragment_chunk);
return 0;
@@ -3357,15 +3472,17 @@ int f2fs_allocate_pinning_section(struct f2fs_sb_info *sbi)
err = f2fs_allocate_new_section(sbi, CURSEG_COLD_DATA_PINNED, false);
f2fs_unlock_op(sbi, &lc);
if (f2fs_sb_has_blkzoned(sbi) && err == -EAGAIN && gc_required) {
f2fs_down_write_trace(&sbi->gc_lock, &lc);
err = f2fs_gc_range(sbi, 0, sbi->first_seq_zone_segno - 1,
true, ZONED_PIN_SEC_REQUIRED_COUNT);
f2fs_up_write_trace(&sbi->gc_lock, &lc);
gc_required = false;
if (!err)
if (has_unpinned_area(sbi) && err == -EAGAIN && gc_required) {
err = f2fs_gc_range(sbi, 0,
sbi->pinned_area_max_secno * SEGS_PER_SEC(sbi) - 1,
true, ZONED_PIN_SEC_REQUIRED_COUNT, true);
if (err)
return err;
err = f2fs_sync_fs(sbi->sb, 1);
if (!err) {
gc_required = false;
goto retry;
}
}
return err;
@@ -3820,7 +3937,7 @@ int f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct folio *folio,
curseg->next_blkoff = f2fs_find_next_ssr_block(sbi, curseg);
} else {
curseg->next_blkoff++;
if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK)
if (f2fs_need_rand_blk(sbi, type))
f2fs_randomize_chunk(sbi, curseg);
}
if (curseg->next_blkoff >= f2fs_usable_blks_in_seg(sbi, curseg->segno))

View File

@@ -43,6 +43,7 @@ static inline void sanity_check_seg_type(struct f2fs_sb_info *sbi,
#define MAIN_SEGS(sbi) (SM_I(sbi)->main_segments)
#define MAIN_SECS(sbi) ((sbi)->total_sections)
#define has_unpinned_area(sbi) ((sbi)->pinned_area_max_secno < MAIN_SECS(sbi))
#define TOTAL_SEGS(sbi) \
(SM_I(sbi) ? SM_I(sbi)->segment_count : \
@@ -796,20 +797,6 @@ F2FS_IPU_POLICY(F2FS_IPU_ASYNC);
F2FS_IPU_POLICY(F2FS_IPU_NOCACHE);
F2FS_IPU_POLICY(F2FS_IPU_HONOR_OPU_WRITE);
static inline unsigned int curseg_segno(struct f2fs_sb_info *sbi,
int type)
{
struct curseg_info *curseg = CURSEG_I(sbi, type);
return curseg->segno;
}
static inline unsigned char curseg_alloc_type(struct f2fs_sb_info *sbi,
int type)
{
struct curseg_info *curseg = CURSEG_I(sbi, type);
return curseg->alloc_type;
}
static inline bool valid_main_segno(struct f2fs_sb_info *sbi,
unsigned int segno)
{
@@ -954,10 +941,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;
}

View File

@@ -109,7 +109,8 @@ unsigned long f2fs_shrink_scan(struct shrinker *shrink,
freed += f2fs_shrink_age_extent_tree(sbi, nr >> 2);
/* shrink read extent cache entries */
freed += f2fs_shrink_read_extent_tree(sbi, nr >> 2);
if (freed < nr)
freed += f2fs_shrink_read_extent_tree(sbi, nr >> 2);
/* shrink clean nat cache entries */
if (freed < nr)

View File

@@ -235,6 +235,7 @@ enum {
Opt_jqfmt,
Opt_checkpoint,
Opt_lookup_mode,
Opt_resizable_tail_secno,
Opt_err,
};
@@ -366,6 +367,7 @@ static const struct fs_parameter_spec f2fs_param_specs[] = {
fsparam_flag("age_extent_cache", Opt_age_extent_cache),
fsparam_enum("errors", Opt_errors, f2fs_param_errors),
fsparam_enum("lookup_mode", Opt_lookup_mode, f2fs_param_lookup_mode),
fsparam_u32("resizable_tail_secno", Opt_resizable_tail_secno),
{}
};
@@ -404,6 +406,7 @@ static match_table_t f2fs_checkpoint_tokens = {
#define F2FS_SPEC_errors (1 << 23)
#define F2FS_SPEC_lookup_mode (1 << 24)
#define F2FS_SPEC_reserve_node (1 << 25)
#define F2FS_SPEC_resizable_tail_secno (1 << 26)
struct f2fs_fs_context {
struct f2fs_mount_info info;
@@ -551,6 +554,17 @@ static inline void adjust_unusable_cap_perc(struct f2fs_sb_info *sbi)
F2FS_OPTION(sbi).unusable_cap_perc);
}
static inline void adjust_pinned_area_boundary(struct f2fs_sb_info *sbi)
{
sbi->pinned_area_max_secno = MAIN_SECS(sbi);
if (f2fs_sb_has_blkzoned(sbi) && sbi->first_seq_zone_segno != NULL_SEGNO)
sbi->pinned_area_max_secno = min(sbi->pinned_area_max_secno,
GET_SEC_FROM_SEG(sbi, sbi->first_seq_zone_segno));
if (F2FS_OPTION(sbi).resizable_tail_secno)
sbi->pinned_area_max_secno = min(sbi->pinned_area_max_secno,
MAIN_SECS(sbi) - F2FS_OPTION(sbi).resizable_tail_secno);
}
static void init_once(void *foo)
{
struct f2fs_inode_info *fi = (struct f2fs_inode_info *) foo;
@@ -1235,6 +1249,10 @@ static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
F2FS_CTX_INFO(ctx).lookup_mode = result.uint_32;
ctx->spec_mask |= F2FS_SPEC_lookup_mode;
break;
case Opt_resizable_tail_secno:
F2FS_CTX_INFO(ctx).resizable_tail_secno = result.uint_32;
ctx->spec_mask |= F2FS_SPEC_resizable_tail_secno;
break;
}
return 0;
}
@@ -1763,6 +1781,9 @@ static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
F2FS_OPTION(sbi).errors = F2FS_CTX_INFO(ctx).errors;
if (ctx->spec_mask & F2FS_SPEC_lookup_mode)
F2FS_OPTION(sbi).lookup_mode = F2FS_CTX_INFO(ctx).lookup_mode;
if (ctx->spec_mask & F2FS_SPEC_resizable_tail_secno)
F2FS_OPTION(sbi).resizable_tail_secno =
F2FS_CTX_INFO(ctx).resizable_tail_secno;
f2fs_apply_compression(fc, sb);
f2fs_apply_test_dummy_encryption(fc, sb);
@@ -1771,6 +1792,13 @@ static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
static int f2fs_sanity_check_options(struct f2fs_sb_info *sbi, bool remount)
{
unsigned int total_sections = le32_to_cpu(sbi->raw_super->section_count);
if (F2FS_OPTION(sbi).resizable_tail_secno >= total_sections) {
f2fs_err(sbi, "Option resizable_tail_secno is larger than or equal to total sections (%u >= %u)",
F2FS_OPTION(sbi).resizable_tail_secno, total_sections);
return -EINVAL;
}
if (f2fs_sb_has_device_alias(sbi) &&
!test_opt(sbi, READ_EXTENT_CACHE)) {
f2fs_err(sbi, "device aliasing requires extent cache");
@@ -2058,7 +2086,8 @@ static void f2fs_put_super(struct super_block *sb)
if (!get_pages(sbi, i))
continue;
f2fs_err(sbi, "detect filesystem reference count leak during "
"umount, type: %d, count: %lld", i, get_pages(sbi, i));
"umount, type: %d, count: %lld, err: %d, cp_err: %d",
i, get_pages(sbi, i), err, f2fs_cp_error(sbi));
f2fs_bug_on(sbi, 1);
}
@@ -2456,9 +2485,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
seq_puts(seq, "adaptive");
else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_LFS)
seq_puts(seq, "lfs");
else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_SEG)
else if (f2fs_need_rand_seg(sbi, NO_CHECK_TYPE))
seq_puts(seq, "fragment:segment");
else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK)
else if (f2fs_need_rand_blk(sbi, NO_CHECK_TYPE))
seq_puts(seq, "fragment:block");
seq_printf(seq, ",active_logs=%u", F2FS_OPTION(sbi).active_logs);
if (test_opt(sbi, RESERVE_ROOT) || test_opt(sbi, RESERVE_NODE))
@@ -2543,6 +2572,10 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
else if (F2FS_OPTION(sbi).lookup_mode == LOOKUP_AUTO)
seq_show_option(seq, "lookup_mode", "auto");
if (F2FS_OPTION(sbi).resizable_tail_secno)
seq_printf(seq, ",resizable_tail_secno=%u",
F2FS_OPTION(sbi).resizable_tail_secno);
return 0;
}
@@ -2585,6 +2618,7 @@ static void default_options(struct f2fs_sb_info *sbi, bool remount)
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
F2FS_OPTION(sbi).memory_mode = MEMORY_MODE_NORMAL;
F2FS_OPTION(sbi).errors = MOUNT_ERRORS_CONTINUE;
F2FS_OPTION(sbi).resizable_tail_secno = 0;
set_opt(sbi, INLINE_XATTR);
set_opt(sbi, INLINE_DATA);
@@ -2943,11 +2977,11 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb)
if ((flags & SB_RDONLY) ||
(F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_OFF &&
!test_opt(sbi, GC_MERGE))) {
if (sbi->gc_thread) {
if (sbi->gc_thread.f2fs_gc_task) {
f2fs_stop_gc_thread(sbi);
need_restart_gc = true;
}
} else if (!sbi->gc_thread) {
} else if (!sbi->gc_thread.f2fs_gc_task) {
err = f2fs_start_gc_thread(sbi);
if (err)
goto restore_opts;
@@ -2959,7 +2993,9 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb)
set_sbi_flag(sbi, SBI_IS_DIRTY);
set_sbi_flag(sbi, SBI_IS_CLOSE);
f2fs_sync_fs(sb, 1);
err = f2fs_sync_fs(sb, 1);
if (err)
goto restore_gc;
clear_sbi_flag(sbi, SBI_IS_CLOSE);
}
@@ -3042,6 +3078,7 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb)
sb->s_flags = (sb->s_flags & ~SB_POSIXACL) |
(test_opt(sbi, POSIX_ACL) ? SB_POSIXACL : 0);
adjust_pinned_area_boundary(sbi);
limit_reserve_root(sbi);
fc->sb_flags = (flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME);
@@ -3168,7 +3205,7 @@ static ssize_t f2fs_quota_read(struct super_block *sb, int type, char *data,
repeat:
folio = mapping_read_folio_gfp(mapping, off >> PAGE_SHIFT,
GFP_NOFS);
GFP_KERNEL);
if (IS_ERR(folio)) {
if (PTR_ERR(folio) == -ENOMEM) {
memalloc_retry_wait(GFP_NOFS);
@@ -5004,6 +5041,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;
@@ -5068,6 +5138,7 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_fs_info = sbi;
sbi->raw_super = raw_super;
sbi->max_atc_write_bio_size = UINT_MAX;
INIT_WORK(&sbi->s_error_work, f2fs_record_error_work);
memcpy(sbi->errors, raw_super->s_errors, MAX_F2FS_ERRORS);
@@ -5207,6 +5278,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);
@@ -5249,6 +5321,8 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
/* get segno of first zoned block device */
sbi->first_seq_zone_segno = get_first_seq_zone_segno(sbi);
adjust_pinned_area_boundary(sbi);
sbi->reserved_pin_section = f2fs_sb_has_blkzoned(sbi) ?
ZONED_PIN_SEC_REQUIRED_COUNT :
GET_SEC_FROM_SEG(sbi, overprovision_segments(sbi));
@@ -5434,6 +5508,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;
@@ -5701,10 +5777,16 @@ static int __init init_f2fs_fs(void)
err = f2fs_init_xattr_cache();
if (err)
goto free_casefold_cache;
err = register_filesystem(&f2fs_fs_type);
err = f2fs_init_evict_inode_work();
if (err)
goto free_xattr_cache;
err = register_filesystem(&f2fs_fs_type);
if (err)
goto free_evict_inode_cache;
return 0;
free_evict_inode_cache:
f2fs_destroy_evict_inode_work();
free_xattr_cache:
f2fs_destroy_xattr_cache();
free_casefold_cache:
@@ -5747,6 +5829,7 @@ static int __init init_f2fs_fs(void)
static void __exit exit_f2fs_fs(void)
{
unregister_filesystem(&f2fs_fs_type);
f2fs_destroy_evict_inode_work();
f2fs_destroy_xattr_cache();
f2fs_destroy_casefold_cache();
f2fs_destroy_compress_cache();

View File

@@ -75,7 +75,7 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type)
{
if (struct_type == GC_THREAD)
return (unsigned char *)sbi->gc_thread;
return (unsigned char *)&sbi->gc_thread;
else if (struct_type == SM_INFO)
return (unsigned char *)SM_I(sbi);
else if (struct_type == DCC_INFO)
@@ -557,7 +557,7 @@ static ssize_t __sbi_store(struct f2fs_attr *a,
return -EINVAL;
cprc->ckpt_thread_ioprio = IOPRIO_PRIO_VALUE(class, level);
if (test_opt(sbi, MERGE_CHECKPOINT)) {
if (cprc->f2fs_issue_ckpt) {
ret = set_task_ioprio(cprc->f2fs_issue_ckpt,
cprc->ckpt_thread_ioprio);
if (ret)
@@ -664,20 +664,20 @@ static ssize_t __sbi_store(struct f2fs_attr *a,
sbi->gc_mode = GC_NORMAL;
} else if (t == 1) {
sbi->gc_mode = GC_URGENT_HIGH;
if (sbi->gc_thread) {
sbi->gc_thread->gc_wake = true;
if (sbi->gc_thread.f2fs_gc_task) {
sbi->gc_thread.gc_wake = true;
wake_up_interruptible_all(
&sbi->gc_thread->gc_wait_queue_head);
&sbi->gc_thread.gc_wait_queue_head);
wake_up_discard_thread(sbi, true);
}
} else if (t == 2) {
sbi->gc_mode = GC_URGENT_LOW;
} else if (t == 3) {
sbi->gc_mode = GC_URGENT_MID;
if (sbi->gc_thread) {
sbi->gc_thread->gc_wake = true;
if (sbi->gc_thread.f2fs_gc_task) {
sbi->gc_thread.gc_wake = true;
wake_up_interruptible_all(
&sbi->gc_thread->gc_wait_queue_head);
&sbi->gc_thread.gc_wait_queue_head);
}
} else {
return -EINVAL;
@@ -934,14 +934,14 @@ static ssize_t __sbi_store(struct f2fs_attr *a,
if (!strcmp(a->attr.name, "gc_boost_gc_multiple")) {
if (t < 1 || t > SEGS_PER_SEC(sbi))
return -EINVAL;
sbi->gc_thread->boost_gc_multiple = (unsigned int)t;
sbi->gc_thread.boost_gc_multiple = (unsigned int)t;
return count;
}
if (!strcmp(a->attr.name, "gc_boost_gc_greedy")) {
if (t > GC_GREEDY)
return -EINVAL;
sbi->gc_thread->boost_gc_greedy = (unsigned int)t;
sbi->gc_thread.boost_gc_greedy = (unsigned int)t;
return count;
}
@@ -989,8 +989,8 @@ static ssize_t __sbi_store(struct f2fs_attr *a,
if (sbi->cprc_info.f2fs_issue_ckpt)
set_user_nice(sbi->cprc_info.f2fs_issue_ckpt,
PRIO_TO_NICE(sbi->critical_task_priority));
if (sbi->gc_thread && sbi->gc_thread->f2fs_gc_task)
set_user_nice(sbi->gc_thread->f2fs_gc_task,
if (sbi->gc_thread.f2fs_gc_task)
set_user_nice(sbi->gc_thread.f2fs_gc_task,
PRIO_TO_NICE(sbi->critical_task_priority));
return count;
}
@@ -1007,13 +1007,15 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a,
ssize_t ret;
bool gc_entry = (!strcmp(a->attr.name, "gc_urgent") ||
a->struct_type == GC_THREAD);
bool thread_entry = !strcmp(a->attr.name, "ckpt_thread_ioprio") ||
!strcmp(a->attr.name, "critical_task_priority");
if (gc_entry) {
if (gc_entry || thread_entry) {
if (!down_read_trylock(&sbi->sb->s_umount))
return -EAGAIN;
}
ret = __sbi_store(a, sbi, buf, count);
if (gc_entry)
if (gc_entry || thread_entry)
up_read(&sbi->sb->s_umount);
return ret;
@@ -1266,6 +1268,7 @@ F2FS_SBI_RW_ATTR(gc_idle_interval, interval_time[GC_TIME]);
F2FS_SBI_RW_ATTR(umount_discard_timeout, interval_time[UMOUNT_DISCARD_TIMEOUT]);
F2FS_SBI_RW_ATTR(gc_pin_file_thresh, gc_pin_file_threshold);
F2FS_SBI_RW_ATTR(gc_reclaimed_segments, gc_reclaimed_segs);
F2FS_SBI_RW_ATTR(max_atc_write_bio_size, max_atc_write_bio_size);
F2FS_SBI_GENERAL_RW_ATTR(max_victim_search);
F2FS_SBI_GENERAL_RW_ATTR(migration_granularity);
F2FS_SBI_GENERAL_RW_ATTR(migration_window_granularity);
@@ -1310,6 +1313,7 @@ F2FS_SBI_GENERAL_RW_ATTR(blkzone_alloc_policy);
#endif
F2FS_SBI_GENERAL_RW_ATTR(carve_out);
F2FS_SBI_GENERAL_RW_ATTR(reserved_pin_section);
F2FS_SBI_GENERAL_RO_ATTR(pinned_area_max_secno);
F2FS_SBI_GENERAL_RW_ATTR(bggc_io_aware);
F2FS_SBI_GENERAL_RW_ATTR(max_lock_elapsed_time);
F2FS_SBI_GENERAL_RW_ATTR(lock_duration_priority);
@@ -1509,6 +1513,7 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(seq_file_ra_mul),
ATTR_LIST(gc_segment_mode),
ATTR_LIST(gc_reclaimed_segments),
ATTR_LIST(max_atc_write_bio_size),
ATTR_LIST(max_fragment_chunk),
ATTR_LIST(max_fragment_hole),
ATTR_LIST(current_atomic_write),
@@ -1521,6 +1526,7 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(max_read_extent_count),
ATTR_LIST(carve_out),
ATTR_LIST(reserved_pin_section),
ATTR_LIST(pinned_area_max_secno),
ATTR_LIST(allocate_section_hint),
ATTR_LIST(allocate_section_policy),
ATTR_LIST(max_lock_elapsed_time),

View File

@@ -119,7 +119,7 @@ static int f2fs_xattr_advise_set(const struct xattr_handler *handler,
unsigned char old_advise = F2FS_I(inode)->i_advise;
unsigned char new_advise;
if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
if (!inode_owner_or_capable(idmap, inode))
return -EPERM;
if (value == NULL)
return -EINVAL;

View File

@@ -191,7 +191,8 @@ TRACE_DEFINE_ENUM(CP_PHASE_FINISH_CHECKPOINT);
{ LOCK_NAME_NODE_WRITE, "node_write" }, \
{ LOCK_NAME_GC_LOCK, "gc_lock" }, \
{ LOCK_NAME_CP_GLOBAL, "cp_global" }, \
{ LOCK_NAME_IO_RWSEM, "io_rwsem" })
{ LOCK_NAME_IO_RWSEM, "io_rwsem" }, \
{ LOCK_NAME_NAT_TREE_LOCK, "nat_tree_lock" })
struct f2fs_sb_info;
struct f2fs_io_info;
@@ -2114,6 +2115,14 @@ DEFINE_EVENT(f2fs_zip_end, f2fs_decompress_pages_end,
);
#ifdef CONFIG_F2FS_IOSTAT
/*
* Number of read folio order buckets emitted by the f2fs_iostat tracepoint.
* TP_printk() cannot loop, so the field count is fixed here and must be >=
* the largest possible NR_PAGE_ORDERS (14 on arm64 with 64K pages). The
* BUILD_BUG_ON() in f2fs_update_read_folio_count() enforces this.
*/
#define F2FS_IOSTAT_RD_FOLIO_ORDERS 16
TRACE_EVENT(f2fs_iostat,
TP_PROTO(struct f2fs_sb_info *sbi, unsigned long long *iostat,
@@ -2151,7 +2160,7 @@ TRACE_EVENT(f2fs_iostat,
__field(unsigned long long, fs_mrio)
__field(unsigned long long, fs_discard)
__field(unsigned long long, fs_reset_zone)
__array(unsigned long long, read_folio_count, 11)
__array(unsigned long long, read_folio_count, F2FS_IOSTAT_RD_FOLIO_ORDERS)
),
TP_fast_assign(
@@ -2186,7 +2195,8 @@ TRACE_EVENT(f2fs_iostat,
__entry->fs_reset_zone = iostat[FS_ZONE_RESET_IO];
memset(__entry->read_folio_count, 0, sizeof(__entry->read_folio_count));
memcpy(__entry->read_folio_count, read_folio_count,
sizeof(unsigned long long) * min_t(int, NR_PAGE_ORDERS, 11));
sizeof(unsigned long long) *
min_t(int, NR_PAGE_ORDERS, F2FS_IOSTAT_RD_FOLIO_ORDERS));
),
TP_printk("dev = (%d,%d), "
@@ -2201,7 +2211,8 @@ TRACE_EVENT(f2fs_iostat,
"fs [data=%llu, (gc_data=%llu, cdata=%llu), "
"node=%llu, meta=%llu], "
"read_folio_count [0=%llu, 1=%llu, 2=%llu, 3=%llu, 4=%llu, "
"5=%llu, 6=%llu, 7=%llu, 8=%llu, 9=%llu, 10=%llu]",
"5=%llu, 6=%llu, 7=%llu, 8=%llu, 9=%llu, 10=%llu, 11=%llu, "
"12=%llu, 13=%llu, 14=%llu, 15=%llu]",
show_dev(__entry->dev), __entry->app_wio, __entry->app_dio,
__entry->app_bio, __entry->app_mio, __entry->app_bcdio,
__entry->app_mcdio, __entry->fs_dio, __entry->fs_cdio,
@@ -2218,7 +2229,9 @@ TRACE_EVENT(f2fs_iostat,
__entry->read_folio_count[4], __entry->read_folio_count[5],
__entry->read_folio_count[6], __entry->read_folio_count[7],
__entry->read_folio_count[8], __entry->read_folio_count[9],
__entry->read_folio_count[10])
__entry->read_folio_count[10], __entry->read_folio_count[11],
__entry->read_folio_count[12], __entry->read_folio_count[13],
__entry->read_folio_count[14], __entry->read_folio_count[15])
);
#ifndef __F2FS_IOSTAT_LATENCY_TYPE

View File

@@ -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;