// SPDX-License-Identifier: GPL-2.0
/*
* f2fs extent cache support
*
* Copyright (c) 2015 Motorola Mobility
* Copyright (c) 2015 Samsung Electronics
* Authors: Jaegeuk Kim <jaegeuk@kernel.org>
* Chao Yu <chao2.yu@samsung.com>
*
* block_age-based extent cache added by:
* Copyright (c) 2022 xiaomi Co., Ltd.
* http://www.xiaomi.com/
*/
#include <linux/fs.h>
#include <linux/f2fs_fs.h>
#include "f2fs.h"
#include "node.h"
#include <trace/events/f2fs.h>
bool sanity_check_extent_cache(struct inode *inode, struct page *ipage)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_extent *i_ext = &F2FS_INODE(ipage)->i_ext;
struct extent_info ei;
int devi;
get_read_extent_info(&ei, i_ext);
if (!ei.len)
return true;
if (!f2fs_is_valid_blkaddr(sbi, ei.blk, DATA_GENERIC_ENHANCE) ||
!f2fs_is_valid_blkaddr(sbi, ei.blk + ei.len - 1,
DATA_GENERIC_ENHANCE)) {
f2fs_warn(sbi, "%s: inode (ino=%lx) extent info [%u, %u, %u] is incorrect, run fsck to fix",
__func__, inode->i_ino,
ei.blk, ei.fofs, ei.len);
return false;
}
if (!IS_DEVICE_ALIASING(inode))
return true;
for (devi = 0; devi < sbi->s_ndevs; devi++) {
if (FDEV(devi).start_blk != ei.blk ||
FDEV(devi).end_blk != ei.blk + ei.len - 1)
continue;
if (devi == 0) {
f2fs_warn(sbi,
"%s: inode (ino=%lx) is an alias of meta device",
__func__, inode->i_ino);
return false;
}
if (bdev_is_zoned(FDEV(devi).bdev)) {
f2fs_warn(sbi,
"%s: device alias inode (ino=%lx)'s extent info "
"[%u, %u, %u] maps to zoned block device",
__func__, inode->i_ino, ei.blk, ei.fofs, ei.len);
return false;
}
return true;
}
f2fs_warn(sbi, "%s: device alias inode (ino=%lx)'s extent info "
"[%u, %u, %u] is inconsistent w/ any devices",
__func__, inode->i_ino, ei.blk, ei.fofs, ei.len);
return false;
}
static void __set_extent_info(struct extent_info *ei,
unsigned int fofs, unsigned int len,
block_t blk, bool keep_clen,
unsigned long age, unsigned long last_blocks,
enum extent_type type)
{
ei->fofs = fofs;
ei->len = len;
if (type == EX_READ) {
ei->blk = blk;
if (keep_clen)
return;
#ifdef CONFIG_F2FS_FS_COMPRESSION
ei->c_len = 0;
#endif
} else if (type == EX_BLOCK_AGE) {
ei->age = age;
ei->last_blocks = last_blocks;
}
}
static bool __init_may_extent_tree(struct inode *inode, enum extent_type type)
{
if (type == EX_READ)
return test_opt(F2FS_I_SB(inode), READ_EXTENT_CACHE) &&
S_ISREG(inode->i_mode);
if (type == EX_BLOCK_AGE)
return test_opt(F2FS_I_SB(inode), AGE_EXTENT_CACHE) &&
(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode));
return false;
}
static bool __may_extent_tree(struct inode *inode, enum extent_type type)
{
if