f2fs-for-5.2-rc1
Another round of various bug fixes came in. Damien improved SMR drive support a bit, and Chao replaced BUG_ON() with reporting errors to user since we've not hit from users but did hit from crafted images. We've found a disk layout bug in large_nat_bits feature which supports very large NAT entries enabled at mkfs. If the feature is enabled, it will give a notice to run fsck to correct the on-disk layout. Enhancement: - reduce memory consumption for SMR drive - better discard handling for multiple partitions - tracepoints for f2fs_file_write_iter/f2fs_filemap_fault - allow to change CP_CHKSUM_OFFSET - detect wrong layout of large_nat_bitmap feature - enhance checking valid data indices Bug fix: - Multiple partition support for SMR drive - deadlock problem in f2fs_balance_fs_bg - add boundary checks to fix abnormal behaviors on fuzzed images - inline_xattr space calculations - replace f2fs_bug_on with errors In addition, this series contains various memory boundary check and sanity check of on-disk consistency. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE00UqedjCtOrGVvQiQBSofoJIUNIFAlzZ/ukACgkQQBSofoJI UNKnkQ//U6QsEgNsC5GeNAXPLxodxC406CSo+aRk//3xUdORzkVNia1ThLeYVNfd 3LsLaSy1eLZ0GylrR/MpvHt+eSWH5L5Ferj2v4l1zAYLr29FEhl6bmYPyvc3e4pr IbHwC6W1g1sV2LxeNp7z0chkT7cOAm633d3OVJzUXD5B1k52lx72QnqoXal0Ae1L tt3h/TVBIRJX26nSTiEZTBnIDmpiZYSsJVGgjqLnTCXHWUKPPfuJqALiELluhBNJ M7gDE3/JYJyb+1PrdtvsEesPJxSLovGJJJvcJKcuMhWwij0Jsq2BwiP3shcfj8iA 76PiPlhjS6D5sMo1hJzbKettusfZrxX284UHNacrkgA/TBbHeGGBy3Fbh6B3+/o1 qvCl0atqp3km6Z8vg5r7nDTOMrg0JSjsHz3WA9ZXZ+cSM6mGxk7vYMd7Sn7h2GqY deIfWqlTdB8hOFQJWDswXI2ILyJlvquc4jTKIUmHp4ZEQXdYSWlBUWm7+1XZvoYn rlrAcr/loSQ/gT4U/Z9RQdpMYb2n2n3+YF8QAeTDmVafMeqbrwspCZf6l8Pfoto1 ZVYO9QUIsvRBDEiDHdLmRwb1ckTTiHiHrO9YwtA5zlYRRyH93MamQTsH7BTGt0OC tjCPbpQGlWK6M9p3LNQ4H5lsdLzD7EEP0JXLOQ57rY3aDaZsOsE= =HOz+ -----END PGP SIGNATURE----- Merge tag 'f2fs-for-v5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs Pull f2fs updates from Jaegeuk Kim: "Another round of various bug fixes came in. Damien improved SMR drive support a bit, and Chao replaced BUG_ON() with reporting errors to user since we've not hit from users but did hit from crafted images. We've found a disk layout bug in large_nat_bits feature which supports very large NAT entries enabled at mkfs. If the feature is enabled, it will give a notice to run fsck to correct the on-disk layout. Enhancements: - reduce memory consumption for SMR drive - better discard handling for multiple partitions - tracepoints for f2fs_file_write_iter/f2fs_filemap_fault - allow to change CP_CHKSUM_OFFSET - detect wrong layout of large_nat_bitmap feature - enhance checking valid data indices Bug fixes: - Multiple partition support for SMR drive - deadlock problem in f2fs_balance_fs_bg - add boundary checks to fix abnormal behaviors on fuzzed images - inline_xattr space calculations - replace f2fs_bug_on with errors In addition, this series contains various memory boundary check and sanity check of on-disk consistency" * tag 'f2fs-for-v5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (40 commits) f2fs: fix to avoid accessing xattr across the boundary f2fs: fix to avoid potential race on sbi->unusable_block_count access/update f2fs: add tracepoint for f2fs_filemap_fault() f2fs: introduce DATA_GENERIC_ENHANCE f2fs: fix to handle error in f2fs_disable_checkpoint() f2fs: remove redundant check in f2fs_file_write_iter() f2fs: fix to be aware of readonly device in write_checkpoint() f2fs: fix to skip recovery on readonly device f2fs: fix to consider multiple device for readonly check f2fs: relocate chksum_offset for large_nat_bitmap feature f2fs: allow unfixed f2fs_checkpoint.checksum_offset f2fs: Replace spaces with tab f2fs: insert space before the open parenthesis '(' f2fs: allow address pointer number of dnode aligning to specified size f2fs: introduce f2fs_read_single_page() for cleanup f2fs: mark is_extension_exist() inline f2fs: fix to set FI_UPDATE_WRITE correctly f2fs: fix to avoid panic in f2fs_inplace_write_data() f2fs: fix to do sanity check on valid block count of segment f2fs: fix to do sanity check on valid node/block count ...
This commit is contained in:
commit
0d28544117
|
@ -285,7 +285,7 @@ static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
|
|||
/* assert(atomic_read(acl->a_refcount) == 1); */
|
||||
|
||||
FOREACH_ACL_ENTRY(pa, acl, pe) {
|
||||
switch(pa->e_tag) {
|
||||
switch (pa->e_tag) {
|
||||
case ACL_USER_OBJ:
|
||||
pa->e_perm &= (mode >> 6) | ~S_IRWXO;
|
||||
mode &= (pa->e_perm << 6) | ~S_IRWXU;
|
||||
|
@ -326,7 +326,7 @@ static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
|
|||
}
|
||||
|
||||
*mode_p = (*mode_p & ~S_IRWXUGO) | mode;
|
||||
return not_equiv;
|
||||
return not_equiv;
|
||||
}
|
||||
|
||||
static int f2fs_acl_create(struct inode *dir, umode_t *mode,
|
||||
|
|
|
@ -66,7 +66,7 @@ static struct page *__get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index,
|
|||
.old_blkaddr = index,
|
||||
.new_blkaddr = index,
|
||||
.encrypted_page = NULL,
|
||||
.is_meta = is_meta,
|
||||
.is_por = !is_meta,
|
||||
};
|
||||
int err;
|
||||
|
||||
|
@ -130,6 +130,30 @@ struct page *f2fs_get_tmp_page(struct f2fs_sb_info *sbi, pgoff_t index)
|
|||
return __get_meta_page(sbi, index, false);
|
||||
}
|
||||
|
||||
static bool __is_bitmap_valid(struct f2fs_sb_info *sbi, block_t blkaddr,
|
||||
int type)
|
||||
{
|
||||
struct seg_entry *se;
|
||||
unsigned int segno, offset;
|
||||
bool exist;
|
||||
|
||||
if (type != DATA_GENERIC_ENHANCE && type != DATA_GENERIC_ENHANCE_READ)
|
||||
return true;
|
||||
|
||||
segno = GET_SEGNO(sbi, blkaddr);
|
||||
offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr);
|
||||
se = get_seg_entry(sbi, segno);
|
||||
|
||||
exist = f2fs_test_bit(offset, se->cur_valid_map);
|
||||
if (!exist && type == DATA_GENERIC_ENHANCE) {
|
||||
f2fs_msg(sbi->sb, KERN_ERR, "Inconsistent error "
|
||||
"blkaddr:%u, sit bitmap:%d", blkaddr, exist);
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
WARN_ON(1);
|
||||
}
|
||||
return exist;
|
||||
}
|
||||
|
||||
bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi,
|
||||
block_t blkaddr, int type)
|
||||
{
|
||||
|
@ -151,15 +175,22 @@ bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi,
|
|||
return false;
|
||||
break;
|
||||
case META_POR:
|
||||
case DATA_GENERIC:
|
||||
if (unlikely(blkaddr >= MAX_BLKADDR(sbi) ||
|
||||
blkaddr < MAIN_BLKADDR(sbi))) {
|
||||
if (type == DATA_GENERIC) {
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"access invalid blkaddr:%u", blkaddr);
|
||||
WARN_ON(1);
|
||||
}
|
||||
blkaddr < MAIN_BLKADDR(sbi)))
|
||||
return false;
|
||||
break;
|
||||
case DATA_GENERIC:
|
||||
case DATA_GENERIC_ENHANCE:
|
||||
case DATA_GENERIC_ENHANCE_READ:
|
||||
if (unlikely(blkaddr >= MAX_BLKADDR(sbi) ||
|
||||
blkaddr < MAIN_BLKADDR(sbi))) {
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"access invalid blkaddr:%u", blkaddr);
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
WARN_ON(1);
|
||||
return false;
|
||||
} else {
|
||||
return __is_bitmap_valid(sbi, blkaddr, type);
|
||||
}
|
||||
break;
|
||||
case META_GENERIC:
|
||||
|
@ -189,7 +220,7 @@ int f2fs_ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
|
|||
.op_flags = sync ? (REQ_META | REQ_PRIO) : REQ_RAHEAD,
|
||||
.encrypted_page = NULL,
|
||||
.in_list = false,
|
||||
.is_meta = (type != META_POR),
|
||||
.is_por = (type == META_POR),
|
||||
};
|
||||
struct blk_plug plug;
|
||||
|
||||
|
@ -644,6 +675,12 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
|
|||
if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG))
|
||||
return 0;
|
||||
|
||||
if (bdev_read_only(sbi->sb->s_bdev)) {
|
||||
f2fs_msg(sbi->sb, KERN_INFO, "write access "
|
||||
"unavailable, skipping orphan cleanup");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (s_flags & SB_RDONLY) {
|
||||
f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs");
|
||||
sbi->sb->s_flags &= ~SB_RDONLY;
|
||||
|
@ -758,13 +795,27 @@ static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk)
|
|||
}
|
||||
}
|
||||
|
||||
static __u32 f2fs_checkpoint_chksum(struct f2fs_sb_info *sbi,
|
||||
struct f2fs_checkpoint *ckpt)
|
||||
{
|
||||
unsigned int chksum_ofs = le32_to_cpu(ckpt->checksum_offset);
|
||||
__u32 chksum;
|
||||
|
||||
chksum = f2fs_crc32(sbi, ckpt, chksum_ofs);
|
||||
if (chksum_ofs < CP_CHKSUM_OFFSET) {
|
||||
chksum_ofs += sizeof(chksum);
|
||||
chksum = f2fs_chksum(sbi, chksum, (__u8 *)ckpt + chksum_ofs,
|
||||
F2FS_BLKSIZE - chksum_ofs);
|
||||
}
|
||||
return chksum;
|
||||
}
|
||||
|
||||
static int get_checkpoint_version(struct f2fs_sb_info *sbi, block_t cp_addr,
|
||||
struct f2fs_checkpoint **cp_block, struct page **cp_page,
|
||||
unsigned long long *version)
|
||||
{
|
||||
unsigned long blk_size = sbi->blocksize;
|
||||
size_t crc_offset = 0;
|
||||
__u32 crc = 0;
|
||||
__u32 crc;
|
||||
|
||||
*cp_page = f2fs_get_meta_page(sbi, cp_addr);
|
||||
if (IS_ERR(*cp_page))
|
||||
|
@ -773,15 +824,27 @@ static int get_checkpoint_version(struct f2fs_sb_info *sbi, block_t cp_addr,
|
|||
*cp_block = (struct f2fs_checkpoint *)page_address(*cp_page);
|
||||
|
||||
crc_offset = le32_to_cpu((*cp_block)->checksum_offset);
|
||||
if (crc_offset > (blk_size - sizeof(__le32))) {
|
||||
if (crc_offset < CP_MIN_CHKSUM_OFFSET ||
|
||||
crc_offset > CP_CHKSUM_OFFSET) {
|
||||
f2fs_put_page(*cp_page, 1);
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"invalid crc_offset: %zu", crc_offset);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
crc = cur_cp_crc(*cp_block);
|
||||
if (!f2fs_crc_valid(sbi, crc, *cp_block, crc_offset)) {
|
||||
if (__is_set_ckpt_flags(*cp_block, CP_LARGE_NAT_BITMAP_FLAG)) {
|
||||
if (crc_offset != CP_MIN_CHKSUM_OFFSET) {
|
||||
f2fs_put_page(*cp_page, 1);
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"layout of large_nat_bitmap is deprecated, "
|
||||
"run fsck to repair, chksum_offset: %zu",
|
||||
crc_offset);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
crc = f2fs_checkpoint_chksum(sbi, *cp_block);
|
||||
if (crc != cur_cp_crc(*cp_block)) {
|
||||
f2fs_put_page(*cp_page, 1);
|
||||
f2fs_msg(sbi->sb, KERN_WARNING, "invalid crc value");
|
||||
return -EINVAL;
|
||||
|
@ -1009,13 +1072,11 @@ retry:
|
|||
if (inode) {
|
||||
unsigned long cur_ino = inode->i_ino;
|
||||
|
||||
if (is_dir)
|
||||
F2FS_I(inode)->cp_task = current;
|
||||
F2FS_I(inode)->cp_task = current;
|
||||
|
||||
filemap_fdatawrite(inode->i_mapping);
|
||||
|
||||
if (is_dir)
|
||||
F2FS_I(inode)->cp_task = NULL;
|
||||
F2FS_I(inode)->cp_task = NULL;
|
||||
|
||||
iput(inode);
|
||||
/* We need to give cpu to another writers. */
|
||||
|
@ -1391,7 +1452,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
get_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP));
|
||||
get_nat_bitmap(sbi, __bitmap_ptr(sbi, NAT_BITMAP));
|
||||
|
||||
crc32 = f2fs_crc32(sbi, ckpt, le32_to_cpu(ckpt->checksum_offset));
|
||||
crc32 = f2fs_checkpoint_chksum(sbi, ckpt);
|
||||
*((__le32 *)((unsigned char *)ckpt +
|
||||
le32_to_cpu(ckpt->checksum_offset)))
|
||||
= cpu_to_le32(crc32);
|
||||
|
@ -1475,7 +1536,11 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
clear_sbi_flag(sbi, SBI_IS_DIRTY);
|
||||
clear_sbi_flag(sbi, SBI_NEED_CP);
|
||||
clear_sbi_flag(sbi, SBI_QUOTA_SKIP_FLUSH);
|
||||
|
||||
spin_lock(&sbi->stat_lock);
|
||||
sbi->unusable_block_count = 0;
|
||||
spin_unlock(&sbi->stat_lock);
|
||||
|
||||
__set_cp_next_pack(sbi);
|
||||
|
||||
/*
|
||||
|
@ -1500,6 +1565,9 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
unsigned long long ckpt_ver;
|
||||
int err = 0;
|
||||
|
||||
if (f2fs_readonly(sbi->sb) || f2fs_hw_is_readonly(sbi))
|
||||
return -EROFS;
|
||||
|
||||
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
|
||||
if (cpc->reason != CP_PAUSE)
|
||||
return 0;
|
||||
|
@ -1516,10 +1584,6 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
if (f2fs_readonly(sbi->sb)) {
|
||||
err = -EROFS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "start block_ops");
|
||||
|
||||
|
|
285
fs/f2fs/data.c
285
fs/f2fs/data.c
|
@ -218,12 +218,14 @@ struct block_device *f2fs_target_device(struct f2fs_sb_info *sbi,
|
|||
struct block_device *bdev = sbi->sb->s_bdev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sbi->s_ndevs; i++) {
|
||||
if (FDEV(i).start_blk <= blk_addr &&
|
||||
FDEV(i).end_blk >= blk_addr) {
|
||||
blk_addr -= FDEV(i).start_blk;
|
||||
bdev = FDEV(i).bdev;
|
||||
break;
|
||||
if (f2fs_is_multi_device(sbi)) {
|
||||
for (i = 0; i < sbi->s_ndevs; i++) {
|
||||
if (FDEV(i).start_blk <= blk_addr &&
|
||||
FDEV(i).end_blk >= blk_addr) {
|
||||
blk_addr -= FDEV(i).start_blk;
|
||||
bdev = FDEV(i).bdev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bio) {
|
||||
|
@ -237,6 +239,9 @@ int f2fs_target_device_index(struct f2fs_sb_info *sbi, block_t blkaddr)
|
|||
{
|
||||
int i;
|
||||
|
||||
if (!f2fs_is_multi_device(sbi))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < sbi->s_ndevs; i++)
|
||||
if (FDEV(i).start_blk <= blkaddr && FDEV(i).end_blk >= blkaddr)
|
||||
return i;
|
||||
|
@ -420,7 +425,7 @@ static void __submit_merged_write_cond(struct f2fs_sb_info *sbi,
|
|||
|
||||
void f2fs_submit_merged_write(struct f2fs_sb_info *sbi, enum page_type type)
|
||||
{
|
||||
__submit_merged_write_cond(sbi, NULL, 0, 0, type, true);
|
||||
__submit_merged_write_cond(sbi, NULL, NULL, 0, type, true);
|
||||
}
|
||||
|
||||
void f2fs_submit_merged_write_cond(struct f2fs_sb_info *sbi,
|
||||
|
@ -448,7 +453,8 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio)
|
|||
fio->encrypted_page : fio->page;
|
||||
|
||||
if (!f2fs_is_valid_blkaddr(fio->sbi, fio->new_blkaddr,
|
||||
__is_meta_io(fio) ? META_GENERIC : DATA_GENERIC))
|
||||
fio->is_por ? META_POR : (__is_meta_io(fio) ?
|
||||
META_GENERIC : DATA_GENERIC_ENHANCE)))
|
||||
return -EFAULT;
|
||||
|
||||
trace_f2fs_submit_page_bio(page, fio);
|
||||
|
@ -498,9 +504,7 @@ next:
|
|||
spin_unlock(&io->io_lock);
|
||||
}
|
||||
|
||||
if (__is_valid_data_blkaddr(fio->old_blkaddr))
|
||||
verify_block_addr(fio, fio->old_blkaddr);
|
||||
verify_block_addr(fio, fio->new_blkaddr);
|
||||
verify_fio_blkaddr(fio);
|
||||
|
||||
bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page;
|
||||
|
||||
|
@ -557,9 +561,6 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
|
|||
struct bio_post_read_ctx *ctx;
|
||||
unsigned int post_read_steps = 0;
|
||||
|
||||
if (!f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC))
|
||||
return ERR_PTR(-EFAULT);
|
||||
|
||||
bio = f2fs_bio_alloc(sbi, min_t(int, nr_pages, BIO_MAX_PAGES), false);
|
||||
if (!bio)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
@ -587,8 +588,10 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
|
|||
static int f2fs_submit_page_read(struct inode *inode, struct page *page,
|
||||
block_t blkaddr)
|
||||
{
|
||||
struct bio *bio = f2fs_grab_read_bio(inode, blkaddr, 1, 0);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct bio *bio;
|
||||
|
||||
bio = f2fs_grab_read_bio(inode, blkaddr, 1, 0);
|
||||
if (IS_ERR(bio))
|
||||
return PTR_ERR(bio);
|
||||
|
||||
|
@ -600,8 +603,8 @@ static int f2fs_submit_page_read(struct inode *inode, struct page *page,
|
|||
return -EFAULT;
|
||||
}
|
||||
ClearPageError(page);
|
||||
inc_page_count(F2FS_I_SB(inode), F2FS_RD_DATA);
|
||||
__submit_bio(F2FS_I_SB(inode), bio, DATA);
|
||||
inc_page_count(sbi, F2FS_RD_DATA);
|
||||
__submit_bio(sbi, bio, DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -729,6 +732,11 @@ struct page *f2fs_get_read_data_page(struct inode *inode, pgoff_t index,
|
|||
|
||||
if (f2fs_lookup_extent_cache(inode, index, &ei)) {
|
||||
dn.data_blkaddr = ei.blk + index - ei.fofs;
|
||||
if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), dn.data_blkaddr,
|
||||
DATA_GENERIC_ENHANCE_READ)) {
|
||||
err = -EFAULT;
|
||||
goto put_err;
|
||||
}
|
||||
goto got_it;
|
||||
}
|
||||
|
||||
|
@ -742,6 +750,13 @@ struct page *f2fs_get_read_data_page(struct inode *inode, pgoff_t index,
|
|||
err = -ENOENT;
|
||||
goto put_err;
|
||||
}
|
||||
if (dn.data_blkaddr != NEW_ADDR &&
|
||||
!f2fs_is_valid_blkaddr(F2FS_I_SB(inode),
|
||||
dn.data_blkaddr,
|
||||
DATA_GENERIC_ENHANCE)) {
|
||||
err = -EFAULT;
|
||||
goto put_err;
|
||||
}
|
||||
got_it:
|
||||
if (PageUptodate(page)) {
|
||||
unlock_page(page);
|
||||
|
@ -1084,12 +1099,12 @@ next_block:
|
|||
blkaddr = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node);
|
||||
|
||||
if (__is_valid_data_blkaddr(blkaddr) &&
|
||||
!f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC)) {
|
||||
!f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC_ENHANCE)) {
|
||||
err = -EFAULT;
|
||||
goto sync_out;
|
||||
}
|
||||
|
||||
if (is_valid_data_blkaddr(sbi, blkaddr)) {
|
||||
if (__is_valid_data_blkaddr(blkaddr)) {
|
||||
/* use out-place-update for driect IO under LFS mode */
|
||||
if (test_opt(sbi, LFS) && flag == F2FS_GET_BLOCK_DIO &&
|
||||
map->m_may_create) {
|
||||
|
@ -1499,6 +1514,118 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int f2fs_read_single_page(struct inode *inode, struct page *page,
|
||||
unsigned nr_pages,
|
||||
struct f2fs_map_blocks *map,
|
||||
struct bio **bio_ret,
|
||||
sector_t *last_block_in_bio,
|
||||
bool is_readahead)
|
||||
{
|
||||
struct bio *bio = *bio_ret;
|
||||
const unsigned blkbits = inode->i_blkbits;
|
||||
const unsigned blocksize = 1 << blkbits;
|
||||
sector_t block_in_file;
|
||||
sector_t last_block;
|
||||
sector_t last_block_in_file;
|
||||
sector_t block_nr;
|
||||
int ret = 0;
|
||||
|
||||
block_in_file = (sector_t)page->index;
|
||||
last_block = block_in_file + nr_pages;
|
||||
last_block_in_file = (i_size_read(inode) + blocksize - 1) >>
|
||||
blkbits;
|
||||
if (last_block > last_block_in_file)
|
||||
last_block = last_block_in_file;
|
||||
|
||||
/* just zeroing out page which is beyond EOF */
|
||||
if (block_in_file >= last_block)
|
||||
goto zero_out;
|
||||
/*
|
||||
* Map blocks using the previous result first.
|
||||
*/
|
||||
if ((map->m_flags & F2FS_MAP_MAPPED) &&
|
||||
block_in_file > map->m_lblk &&
|
||||
block_in_file < (map->m_lblk + map->m_len))
|
||||
goto got_it;
|
||||
|
||||
/*
|
||||
* Then do more f2fs_map_blocks() calls until we are
|
||||
* done with this page.
|
||||
*/
|
||||
map->m_lblk = block_in_file;
|
||||
map->m_len = last_block - block_in_file;
|
||||
|
||||
ret = f2fs_map_blocks(inode, map, 0, F2FS_GET_BLOCK_DEFAULT);
|
||||
if (ret)
|
||||
goto out;
|
||||
got_it:
|
||||
if ((map->m_flags & F2FS_MAP_MAPPED)) {
|
||||
block_nr = map->m_pblk + block_in_file - map->m_lblk;
|
||||
SetPageMappedToDisk(page);
|
||||
|
||||
if (!PageUptodate(page) && !cleancache_get_page(page)) {
|
||||
SetPageUptodate(page);
|
||||
goto confused;
|
||||
}
|
||||
|
||||
if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), block_nr,
|
||||
DATA_GENERIC_ENHANCE_READ)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
zero_out:
|
||||
zero_user_segment(page, 0, PAGE_SIZE);
|
||||
if (!PageUptodate(page))
|
||||
SetPageUptodate(page);
|
||||
unlock_page(page);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* This page will go to BIO. Do we need to send this
|
||||
* BIO off first?
|
||||
*/
|
||||
if (bio && (*last_block_in_bio != block_nr - 1 ||
|
||||
!__same_bdev(F2FS_I_SB(inode), block_nr, bio))) {
|
||||
submit_and_realloc:
|
||||
__submit_bio(F2FS_I_SB(inode), bio, DATA);
|
||||
bio = NULL;
|
||||
}
|
||||
if (bio == NULL) {
|
||||
bio = f2fs_grab_read_bio(inode, block_nr, nr_pages,
|
||||
is_readahead ? REQ_RAHEAD : 0);
|
||||
if (IS_ERR(bio)) {
|
||||
ret = PTR_ERR(bio);
|
||||
bio = NULL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the page is under writeback, we need to wait for
|
||||
* its completion to see the correct decrypted data.
|
||||
*/
|
||||
f2fs_wait_on_block_writeback(inode, block_nr);
|
||||
|
||||
if (bio_add_page(bio, page, blocksize, 0) < blocksize)
|
||||
goto submit_and_realloc;
|
||||
|
||||
inc_page_count(F2FS_I_SB(inode), F2FS_RD_DATA);
|
||||
ClearPageError(page);
|
||||
*last_block_in_bio = block_nr;
|
||||
goto out;
|
||||
confused:
|
||||
if (bio) {
|
||||
__submit_bio(F2FS_I_SB(inode), bio, DATA);
|
||||
bio = NULL;
|
||||
}
|
||||
unlock_page(page);
|
||||
out:
|
||||
*bio_ret = bio;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function was originally taken from fs/mpage.c, and customized for f2fs.
|
||||
* Major change was from block_size == page_size in f2fs by default.
|
||||
|
@ -1515,13 +1642,8 @@ static int f2fs_mpage_readpages(struct address_space *mapping,
|
|||
struct bio *bio = NULL;
|
||||
sector_t last_block_in_bio = 0;
|
||||
struct inode *inode = mapping->host;
|
||||
const unsigned blkbits = inode->i_blkbits;
|
||||
const unsigned blocksize = 1 << blkbits;
|
||||
sector_t block_in_file;
|
||||
sector_t last_block;
|
||||
sector_t last_block_in_file;
|
||||
sector_t block_nr;
|
||||
struct f2fs_map_blocks map;
|
||||
int ret = 0;
|
||||
|
||||
map.m_pblk = 0;
|
||||
map.m_lblk = 0;
|
||||
|
@ -1544,98 +1666,13 @@ static int f2fs_mpage_readpages(struct address_space *mapping,
|
|||
goto next_page;
|
||||
}
|
||||
|
||||
block_in_file = (sector_t)page->index;
|
||||
last_block = block_in_file + nr_pages;
|
||||
last_block_in_file = (i_size_read(inode) + blocksize - 1) >>
|
||||
blkbits;
|
||||
if (last_block > last_block_in_file)
|
||||
last_block = last_block_in_file;
|
||||
|
||||
/* just zeroing out page which is beyond EOF */
|
||||
if (block_in_file >= last_block)
|
||||
goto zero_out;
|
||||
/*
|
||||
* Map blocks using the previous result first.
|
||||
*/
|
||||
if ((map.m_flags & F2FS_MAP_MAPPED) &&
|
||||
block_in_file > map.m_lblk &&
|
||||
block_in_file < (map.m_lblk + map.m_len))
|
||||
goto got_it;
|
||||
|
||||
/*
|
||||
* Then do more f2fs_map_blocks() calls until we are
|
||||
* done with this page.
|
||||
*/
|
||||
map.m_lblk = block_in_file;
|
||||
map.m_len = last_block - block_in_file;
|
||||
|
||||
if (f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_DEFAULT))
|
||||
goto set_error_page;
|
||||
got_it:
|
||||
if ((map.m_flags & F2FS_MAP_MAPPED)) {
|
||||
block_nr = map.m_pblk + block_in_file - map.m_lblk;
|
||||
SetPageMappedToDisk(page);
|
||||
|
||||
if (!PageUptodate(page) && !cleancache_get_page(page)) {
|
||||
SetPageUptodate(page);
|
||||
goto confused;
|
||||
}
|
||||
|
||||
if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), block_nr,
|
||||
DATA_GENERIC))
|
||||
goto set_error_page;
|
||||
} else {
|
||||
zero_out:
|
||||
ret = f2fs_read_single_page(inode, page, nr_pages, &map, &bio,
|
||||
&last_block_in_bio, is_readahead);
|
||||
if (ret) {
|
||||
SetPageError(page);
|
||||
zero_user_segment(page, 0, PAGE_SIZE);
|
||||
if (!PageUptodate(page))
|
||||
SetPageUptodate(page);
|
||||
unlock_page(page);
|
||||
goto next_page;
|
||||
}
|
||||
|
||||
/*
|
||||
* This page will go to BIO. Do we need to send this
|
||||
* BIO off first?
|
||||
*/
|
||||
if (bio && (last_block_in_bio != block_nr - 1 ||
|
||||
!__same_bdev(F2FS_I_SB(inode), block_nr, bio))) {
|
||||
submit_and_realloc:
|
||||
__submit_bio(F2FS_I_SB(inode), bio, DATA);
|
||||
bio = NULL;
|
||||
}
|
||||
if (bio == NULL) {
|
||||
bio = f2fs_grab_read_bio(inode, block_nr, nr_pages,
|
||||
is_readahead ? REQ_RAHEAD : 0);
|
||||
if (IS_ERR(bio)) {
|
||||
bio = NULL;
|
||||
goto set_error_page;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the page is under writeback, we need to wait for
|
||||
* its completion to see the correct decrypted data.
|
||||
*/
|
||||
f2fs_wait_on_block_writeback(inode, block_nr);
|
||||
|
||||
if (bio_add_page(bio, page, blocksize, 0) < blocksize)
|
||||
goto submit_and_realloc;
|
||||
|
||||
inc_page_count(F2FS_I_SB(inode), F2FS_RD_DATA);
|
||||
ClearPageError(page);
|
||||
last_block_in_bio = block_nr;
|
||||
goto next_page;
|
||||
set_error_page:
|
||||
SetPageError(page);
|
||||
zero_user_segment(page, 0, PAGE_SIZE);
|
||||
unlock_page(page);
|
||||
goto next_page;
|
||||
confused:
|
||||
if (bio) {
|
||||
__submit_bio(F2FS_I_SB(inode), bio, DATA);
|
||||
bio = NULL;
|
||||
}
|
||||
unlock_page(page);
|
||||
next_page:
|
||||
if (pages)
|
||||
put_page(page);
|
||||
|
@ -1643,7 +1680,7 @@ next_page:
|
|||
BUG_ON(pages && !list_empty(pages));
|
||||
if (bio)
|
||||
__submit_bio(F2FS_I_SB(inode), bio, DATA);
|
||||
return 0;
|
||||
return pages ? 0 : ret;
|
||||
}
|
||||
|
||||
static int f2fs_read_data_page(struct file *file, struct page *page)
|
||||
|
@ -1813,7 +1850,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
|
|||
fio->old_blkaddr = ei.blk + page->index - ei.fofs;
|
||||
|
||||
if (!f2fs_is_valid_blkaddr(fio->sbi, fio->old_blkaddr,
|
||||
DATA_GENERIC))
|
||||
DATA_GENERIC_ENHANCE))
|
||||
return -EFAULT;
|
||||
|
||||
ipu_force = true;
|
||||
|
@ -1840,7 +1877,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
|
|||
got_it:
|
||||
if (__is_valid_data_blkaddr(fio->old_blkaddr) &&
|
||||
!f2fs_is_valid_blkaddr(fio->sbi, fio->old_blkaddr,
|
||||
DATA_GENERIC)) {
|
||||
DATA_GENERIC_ENHANCE)) {
|
||||
err = -EFAULT;
|
||||
goto out_writepage;
|
||||
}
|
||||
|
@ -1848,7 +1885,8 @@ got_it:
|
|||
* If current allocation needs SSR,
|
||||
* it had better in-place writes for updated data.
|
||||
*/
|
||||
if (ipu_force || (is_valid_data_blkaddr(fio->sbi, fio->old_blkaddr) &&
|
||||
if (ipu_force ||
|
||||
(__is_valid_data_blkaddr(fio->old_blkaddr) &&
|
||||
need_inplace_update(fio))) {
|
||||
err = encrypt_one_page(fio);
|
||||
if (err)
|
||||
|
@ -1866,9 +1904,10 @@ got_it:
|
|||
true);
|
||||
if (PageWriteback(page))
|
||||
end_page_writeback(page);
|
||||
} else {
|
||||
set_inode_flag(inode, FI_UPDATE_WRITE);
|
||||
}
|
||||
trace_f2fs_do_write_data_page(fio->page, IPU);
|
||||
set_inode_flag(inode, FI_UPDATE_WRITE);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -2030,7 +2069,8 @@ out:
|
|||
}
|
||||
|
||||
unlock_page(page);
|
||||
if (!S_ISDIR(inode->i_mode) && !IS_NOQUOTA(inode))
|
||||
if (!S_ISDIR(inode->i_mode) && !IS_NOQUOTA(inode) &&
|
||||
!F2FS_I(inode)->cp_task)
|
||||
f2fs_balance_fs(sbi, need_balance_fs);
|
||||
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
|
@ -2491,6 +2531,11 @@ repeat:
|
|||
zero_user_segment(page, 0, PAGE_SIZE);
|
||||
SetPageUptodate(page);
|
||||
} else {
|
||||
if (!f2fs_is_valid_blkaddr(sbi, blkaddr,
|
||||
DATA_GENERIC_ENHANCE_READ)) {
|
||||
err = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
err = f2fs_submit_page_read(inode, page, blkaddr);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
|
127
fs/f2fs/f2fs.h
127
fs/f2fs/f2fs.h
|
@ -210,7 +210,14 @@ enum {
|
|||
META_SSA,
|
||||
META_MAX,
|
||||
META_POR,
|
||||
DATA_GENERIC,
|
||||
DATA_GENERIC, /* check range only */
|
||||
DATA_GENERIC_ENHANCE, /* strong check on range and segment bitmap */
|
||||
DATA_GENERIC_ENHANCE_READ, /*
|
||||
* strong check on range and segment
|
||||
* bitmap but no warning due to race
|
||||
* condition of read on truncated area
|
||||
* by extent_cache
|
||||
*/
|
||||
META_GENERIC,
|
||||
};
|
||||
|
||||
|
@ -1041,7 +1048,7 @@ struct f2fs_io_info {
|
|||
bool submitted; /* indicate IO submission */
|
||||
int need_lock; /* indicate we need to lock cp_rwsem */
|
||||
bool in_list; /* indicate fio is in io_list */
|
||||
bool is_meta; /* indicate borrow meta inode mapping or not */
|
||||
bool is_por; /* indicate IO is from recovery or not */
|
||||
bool retry; /* need to reallocate block address */
|
||||
enum iostat_type io_type; /* io type */
|
||||
struct writeback_control *io_wbc; /* writeback control */
|
||||
|
@ -1068,8 +1075,8 @@ struct f2fs_dev_info {
|
|||
block_t start_blk;
|
||||
block_t end_blk;
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
unsigned int nr_blkz; /* Total number of zones */
|
||||
u8 *blkz_type; /* Array of zones type */
|
||||
unsigned int nr_blkz; /* Total number of zones */
|
||||
unsigned long *blkz_seq; /* Bitmap indicating sequential zones */
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -1366,6 +1373,17 @@ static inline bool time_to_inject(struct f2fs_sb_info *sbi, int type)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Test if the mounted volume is a multi-device volume.
|
||||
* - For a single regular disk volume, sbi->s_ndevs is 0.
|
||||
* - For a single zoned disk volume, sbi->s_ndevs is 1.
|
||||
* - For a multi-device volume, sbi->s_ndevs is always 2 or more.
|
||||
*/
|
||||
static inline bool f2fs_is_multi_device(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return sbi->s_ndevs > 1;
|
||||
}
|
||||
|
||||
/* For write statistics. Suppose sector size is 512 bytes,
|
||||
* and the return value is in kbytes. s is of struct f2fs_sb_info.
|
||||
*/
|
||||
|
@ -1777,6 +1795,7 @@ enospc:
|
|||
return -ENOSPC;
|
||||
}
|
||||
|
||||
void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...);
|
||||
static inline void dec_valid_block_count(struct f2fs_sb_info *sbi,
|
||||
struct inode *inode,
|
||||
block_t count)
|
||||
|
@ -1785,13 +1804,21 @@ static inline void dec_valid_block_count(struct f2fs_sb_info *sbi,
|
|||
|
||||
spin_lock(&sbi->stat_lock);
|
||||
f2fs_bug_on(sbi, sbi->total_valid_block_count < (block_t) count);
|
||||
f2fs_bug_on(sbi, inode->i_blocks < sectors);
|
||||
sbi->total_valid_block_count -= (block_t)count;
|
||||
if (sbi->reserved_blocks &&
|
||||
sbi->current_reserved_blocks < sbi->reserved_blocks)
|
||||
sbi->current_reserved_blocks = min(sbi->reserved_blocks,
|
||||
sbi->current_reserved_blocks + count);
|
||||
spin_unlock(&sbi->stat_lock);
|
||||
if (unlikely(inode->i_blocks < sectors)) {
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"Inconsistent i_blocks, ino:%lu, iblocks:%llu, sectors:%llu",
|
||||
inode->i_ino,
|
||||
(unsigned long long)inode->i_blocks,
|
||||
(unsigned long long)sectors);
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
return;
|
||||
}
|
||||
f2fs_i_blocks_write(inode, count, false, true);
|
||||
}
|
||||
|
||||
|
@ -1889,7 +1916,11 @@ static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag)
|
|||
if (is_set_ckpt_flags(sbi, CP_LARGE_NAT_BITMAP_FLAG)) {
|
||||
offset = (flag == SIT_BITMAP) ?
|
||||
le32_to_cpu(ckpt->nat_ver_bitmap_bytesize) : 0;
|
||||
return &ckpt->sit_nat_version_bitmap + offset;
|
||||
/*
|
||||
* if large_nat_bitmap feature is enabled, leave checksum
|
||||
* protection for all nat/sit bitmaps.
|
||||
*/
|
||||
return &ckpt->sit_nat_version_bitmap + offset + sizeof(__le32);
|
||||
}
|
||||
|
||||
if (__cp_payload(sbi) > 0) {
|
||||
|
@ -2008,7 +2039,6 @@ static inline void dec_valid_node_count(struct f2fs_sb_info *sbi,
|
|||
|
||||
f2fs_bug_on(sbi, !sbi->total_valid_block_count);
|
||||
f2fs_bug_on(sbi, !sbi->total_valid_node_count);
|
||||
f2fs_bug_on(sbi, !is_inode && !inode->i_blocks);
|
||||
|
||||
sbi->total_valid_node_count--;
|
||||
sbi->total_valid_block_count--;
|
||||
|
@ -2018,10 +2048,19 @@ static inline void dec_valid_node_count(struct f2fs_sb_info *sbi,
|
|||
|
||||
spin_unlock(&sbi->stat_lock);
|
||||
|
||||
if (is_inode)
|
||||
if (is_inode) {
|
||||
dquot_free_inode(inode);
|
||||
else
|
||||
} else {
|
||||
if (unlikely(inode->i_blocks == 0)) {
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"Inconsistent i_blocks, ino:%lu, iblocks:%llu",
|
||||
inode->i_ino,
|
||||
(unsigned long long)inode->i_blocks);
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
return;
|
||||
}
|
||||
f2fs_i_blocks_write(inode, 1, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned int valid_node_count(struct f2fs_sb_info *sbi)
|
||||
|
@ -2545,7 +2584,14 @@ static inline int f2fs_has_inline_xattr(struct inode *inode)
|
|||
|
||||
static inline unsigned int addrs_per_inode(struct inode *inode)
|
||||
{
|
||||
return CUR_ADDRS_PER_INODE(inode) - get_inline_xattr_addrs(inode);
|
||||
unsigned int addrs = CUR_ADDRS_PER_INODE(inode) -
|
||||
get_inline_xattr_addrs(inode);
|
||||
return ALIGN_DOWN(addrs, 1);
|
||||
}
|
||||
|
||||
static inline unsigned int addrs_per_block(struct inode *inode)
|
||||
{
|
||||
return ALIGN_DOWN(DEF_ADDRS_PER_BLOCK, 1);
|
||||
}
|
||||
|
||||
static inline void *inline_xattr_addr(struct inode *inode, struct page *page)
|
||||
|
@ -2558,7 +2604,9 @@ static inline void *inline_xattr_addr(struct inode *inode, struct page *page)
|
|||
|
||||
static inline int inline_xattr_size(struct inode *inode)
|
||||
{
|
||||
return get_inline_xattr_addrs(inode) * sizeof(__le32);
|
||||
if (f2fs_has_inline_xattr(inode))
|
||||
return get_inline_xattr_addrs(inode) * sizeof(__le32);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int f2fs_has_inline_data(struct inode *inode)
|
||||
|
@ -2800,12 +2848,10 @@ static inline void f2fs_update_iostat(struct f2fs_sb_info *sbi,
|
|||
|
||||
#define __is_large_section(sbi) ((sbi)->segs_per_sec > 1)
|
||||
|
||||
#define __is_meta_io(fio) (PAGE_TYPE_OF_BIO((fio)->type) == META && \
|
||||
(!is_read_io((fio)->op) || (fio)->is_meta))
|
||||
#define __is_meta_io(fio) (PAGE_TYPE_OF_BIO((fio)->type) == META)
|
||||
|
||||
bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi,
|
||||
block_t blkaddr, int type);
|
||||
void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...);
|
||||
static inline void verify_blkaddr(struct f2fs_sb_info *sbi,
|
||||
block_t blkaddr, int type)
|
||||
{
|
||||
|
@ -2824,15 +2870,6 @@ static inline bool __is_valid_data_blkaddr(block_t blkaddr)
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline bool is_valid_data_blkaddr(struct f2fs_sb_info *sbi,
|
||||
block_t blkaddr)
|
||||
{
|
||||
if (!__is_valid_data_blkaddr(blkaddr))
|
||||
return false;
|
||||
verify_blkaddr(sbi, blkaddr, DATA_GENERIC);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void f2fs_set_page_private(struct page *page,
|
||||
unsigned long data)
|
||||
{
|
||||
|
@ -3530,16 +3567,12 @@ F2FS_FEATURE_FUNCS(lost_found, LOST_FOUND);
|
|||
F2FS_FEATURE_FUNCS(sb_chksum, SB_CHKSUM);
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
static inline int get_blkz_type(struct f2fs_sb_info *sbi,
|
||||
struct block_device *bdev, block_t blkaddr)
|
||||
static inline bool f2fs_blkz_is_seq(struct f2fs_sb_info *sbi, int devi,
|
||||
block_t blkaddr)
|
||||
{
|
||||
unsigned int zno = blkaddr >> sbi->log_blocks_per_blkz;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sbi->s_ndevs; i++)
|
||||
if (FDEV(i).bdev == bdev)
|
||||
return FDEV(i).blkz_type[zno];
|
||||
return -EINVAL;
|
||||
return test_bit(zno, FDEV(devi).blkz_seq);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -3548,9 +3581,23 @@ static inline bool f2fs_hw_should_discard(struct f2fs_sb_info *sbi)
|
|||
return f2fs_sb_has_blkzoned(sbi);
|
||||
}
|
||||
|
||||
static inline bool f2fs_bdev_support_discard(struct block_device *bdev)
|
||||
{
|
||||
return blk_queue_discard(bdev_get_queue(bdev)) ||
|
||||
bdev_is_zoned(bdev);
|
||||
}
|
||||
|
||||
static inline bool f2fs_hw_support_discard(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return blk_queue_discard(bdev_get_queue(sbi->sb->s_bdev));
|
||||
int i;
|
||||
|
||||
if (!f2fs_is_multi_device(sbi))
|
||||
return f2fs_bdev_support_discard(sbi->sb->s_bdev);
|
||||
|
||||
for (i = 0; i < sbi->s_ndevs; i++)
|
||||
if (f2fs_bdev_support_discard(FDEV(i).bdev))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool f2fs_realtime_discard_enable(struct f2fs_sb_info *sbi)
|
||||
|
@ -3559,6 +3606,20 @@ static inline bool f2fs_realtime_discard_enable(struct f2fs_sb_info *sbi)
|
|||
f2fs_hw_should_discard(sbi);
|
||||
}
|
||||
|
||||
static inline bool f2fs_hw_is_readonly(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!f2fs_is_multi_device(sbi))
|
||||
return bdev_read_only(sbi->sb->s_bdev);
|
||||
|
||||
for (i = 0; i < sbi->s_ndevs; i++)
|
||||
if (bdev_read_only(FDEV(i).bdev))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static inline void set_opt_mode(struct f2fs_sb_info *sbi, unsigned int mt)
|
||||
{
|
||||
clear_opt(sbi, ADAPTIVE);
|
||||
|
@ -3614,7 +3675,7 @@ static inline bool f2fs_force_buffered_io(struct inode *inode,
|
|||
|
||||
if (f2fs_post_read_required(inode))
|
||||
return true;
|
||||
if (sbi->s_ndevs)
|
||||
if (f2fs_is_multi_device(sbi))
|
||||
return true;
|
||||
/*
|
||||
* for blkzoned device, fallback direct IO to buffered IO, so
|
||||
|
@ -3651,4 +3712,4 @@ static inline bool is_journalled_quota(struct f2fs_sb_info *sbi)
|
|||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* _LINUX_F2FS_H */
|
||||
|
|
|
@ -39,6 +39,8 @@ static vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf)
|
|||
ret = filemap_fault(vmf);
|
||||
up_read(&F2FS_I(inode)->i_mmap_sem);
|
||||
|
||||
trace_f2fs_filemap_fault(inode, vmf->pgoff, (unsigned long)ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -356,7 +358,7 @@ static bool __found_offset(struct f2fs_sb_info *sbi, block_t blkaddr,
|
|||
switch (whence) {
|
||||
case SEEK_DATA:
|
||||
if ((blkaddr == NEW_ADDR && dirty == pgofs) ||
|
||||
is_valid_data_blkaddr(sbi, blkaddr))
|
||||
__is_valid_data_blkaddr(blkaddr))
|
||||
return true;
|
||||
break;
|
||||
case SEEK_HOLE:
|
||||
|
@ -422,7 +424,7 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence)
|
|||
|
||||
if (__is_valid_data_blkaddr(blkaddr) &&
|
||||
!f2fs_is_valid_blkaddr(F2FS_I_SB(inode),
|
||||
blkaddr, DATA_GENERIC)) {
|
||||
blkaddr, DATA_GENERIC_ENHANCE)) {
|
||||
f2fs_put_dnode(&dn);
|
||||
goto fail;
|
||||
}
|
||||
|
@ -523,7 +525,8 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
|
|||
f2fs_set_data_blkaddr(dn);
|
||||
|
||||
if (__is_valid_data_blkaddr(blkaddr) &&
|
||||
!f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC))
|
||||
!f2fs_is_valid_blkaddr(sbi, blkaddr,
|
||||
DATA_GENERIC_ENHANCE))
|
||||
continue;
|
||||
|
||||
f2fs_invalidate_blocks(sbi, blkaddr);
|
||||
|
@ -552,7 +555,7 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
|
|||
|
||||
void f2fs_truncate_data_blocks(struct dnode_of_data *dn)
|
||||
{
|
||||
f2fs_truncate_data_blocks_range(dn, ADDRS_PER_BLOCK);
|
||||
f2fs_truncate_data_blocks_range(dn, ADDRS_PER_BLOCK(dn->inode));
|
||||
}
|
||||
|
||||
static int truncate_partial_data_page(struct inode *inode, u64 from,
|
||||
|
@ -1006,7 +1009,8 @@ next_dnode:
|
|||
} else if (ret == -ENOENT) {
|
||||
if (dn.max_level == 0)
|
||||
return -ENOENT;
|
||||
done = min((pgoff_t)ADDRS_PER_BLOCK - dn.ofs_in_node, len);
|
||||
done = min((pgoff_t)ADDRS_PER_BLOCK(inode) - dn.ofs_in_node,
|
||||
len);
|
||||
blkaddr += done;
|
||||
do_replace += done;
|
||||
goto next;
|
||||
|
@ -1017,6 +1021,14 @@ next_dnode:
|
|||
for (i = 0; i < done; i++, blkaddr++, do_replace++, dn.ofs_in_node++) {
|
||||
*blkaddr = datablock_addr(dn.inode,
|
||||
dn.node_page, dn.ofs_in_node);
|
||||
|
||||
if (__is_valid_data_blkaddr(*blkaddr) &&
|
||||
!f2fs_is_valid_blkaddr(sbi, *blkaddr,
|
||||
DATA_GENERIC_ENHANCE)) {
|
||||
f2fs_put_dnode(&dn);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (!f2fs_is_checkpointed_data(sbi, *blkaddr)) {
|
||||
|
||||
if (test_opt(sbi, LFS)) {
|
||||
|
@ -1157,7 +1169,7 @@ static int __exchange_data_block(struct inode *src_inode,
|
|||
int ret;
|
||||
|
||||
while (len) {
|
||||
olen = min((pgoff_t)4 * ADDRS_PER_BLOCK, len);
|
||||
olen = min((pgoff_t)4 * ADDRS_PER_BLOCK(src_inode), len);
|
||||
|
||||
src_blkaddr = f2fs_kvzalloc(F2FS_I_SB(src_inode),
|
||||
array_size(olen, sizeof(block_t)),
|
||||
|
@ -2573,10 +2585,10 @@ static int f2fs_ioc_flush_device(struct file *filp, unsigned long arg)
|
|||
sizeof(range)))
|
||||
return -EFAULT;
|
||||
|
||||
if (sbi->s_ndevs <= 1 || sbi->s_ndevs - 1 <= range.dev_num ||
|
||||
if (!f2fs_is_multi_device(sbi) || sbi->s_ndevs - 1 <= range.dev_num ||
|
||||
__is_large_section(sbi)) {
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"Can't flush %u in %d for segs_per_sec %u != 1\n",
|
||||
"Can't flush %u in %d for segs_per_sec %u != 1",
|
||||
range.dev_num, sbi->s_ndevs,
|
||||
sbi->segs_per_sec);
|
||||
return -EINVAL;
|
||||
|
@ -2858,7 +2870,7 @@ int f2fs_pin_file_control(struct inode *inode, bool inc)
|
|||
|
||||
if (fi->i_gc_failures[GC_FAILURE_PIN] > sbi->gc_pin_file_threshold) {
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"%s: Enable GC = ino %lx after %x GC trials\n",
|
||||
"%s: Enable GC = ino %lx after %x GC trials",
|
||||
__func__, inode->i_ino,
|
||||
fi->i_gc_failures[GC_FAILURE_PIN]);
|
||||
clear_inode_flag(inode, FI_PIN_FILE);
|
||||
|
@ -3035,15 +3047,21 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||
struct inode *inode = file_inode(file);
|
||||
ssize_t ret;
|
||||
|
||||
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
|
||||
return -EIO;
|
||||
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT))
|
||||
return -EINVAL;
|
||||
if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!inode_trylock(inode)) {
|
||||
if (iocb->ki_flags & IOCB_NOWAIT)
|
||||
return -EAGAIN;
|
||||
if (iocb->ki_flags & IOCB_NOWAIT) {
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
inode_lock(inode);
|
||||
}
|
||||
|
||||
|
@ -3056,19 +3074,16 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||
if (iov_iter_fault_in_readable(from, iov_iter_count(from)))
|
||||
set_inode_flag(inode, FI_NO_PREALLOC);
|
||||
|
||||
if ((iocb->ki_flags & IOCB_NOWAIT) &&
|
||||
(iocb->ki_flags & IOCB_DIRECT)) {
|
||||
if (!f2fs_overwrite_io(inode, iocb->ki_pos,
|
||||
if ((iocb->ki_flags & IOCB_NOWAIT)) {
|
||||
if (!f2fs_overwrite_io(inode, iocb->ki_pos,
|
||||
iov_iter_count(from)) ||
|
||||
f2fs_has_inline_data(inode) ||
|
||||
f2fs_force_buffered_io(inode,
|
||||
iocb, from)) {
|
||||
clear_inode_flag(inode,
|
||||
FI_NO_PREALLOC);
|
||||
inode_unlock(inode);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
f2fs_has_inline_data(inode) ||
|
||||
f2fs_force_buffered_io(inode, iocb, from)) {
|
||||
clear_inode_flag(inode, FI_NO_PREALLOC);
|
||||
inode_unlock(inode);
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
preallocated = true;
|
||||
target_size = iocb->ki_pos + iov_iter_count(from);
|
||||
|
@ -3077,7 +3092,8 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||
if (err) {
|
||||
clear_inode_flag(inode, FI_NO_PREALLOC);
|
||||
inode_unlock(inode);
|
||||
return err;
|
||||
ret = err;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
ret = __generic_file_write_iter(iocb, from);
|
||||
|
@ -3091,7 +3107,9 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||
f2fs_update_iostat(F2FS_I_SB(inode), APP_WRITE_IO, ret);
|
||||
}
|
||||
inode_unlock(inode);
|
||||
|
||||
out:
|
||||
trace_f2fs_file_write_iter(inode, iocb->ki_pos,
|
||||
iov_iter_count(from), ret);
|
||||
if (ret > 0)
|
||||
ret = generic_write_sync(iocb, ret);
|
||||
return ret;
|
||||
|
|
16
fs/f2fs/gc.c
16
fs/f2fs/gc.c
|
@ -591,7 +591,7 @@ block_t f2fs_start_bidx_of_node(unsigned int node_ofs, struct inode *inode)
|
|||
int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
|
||||
bidx = node_ofs - 5 - dec;
|
||||
}
|
||||
return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(inode);
|
||||
return bidx * ADDRS_PER_BLOCK(inode) + ADDRS_PER_INODE(inode);
|
||||
}
|
||||
|
||||
static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
|
@ -656,6 +656,11 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
|
|||
|
||||
if (f2fs_lookup_extent_cache(inode, index, &ei)) {
|
||||
dn.data_blkaddr = ei.blk + index - ei.fofs;
|
||||
if (unlikely(!f2fs_is_valid_blkaddr(sbi, dn.data_blkaddr,
|
||||
DATA_GENERIC_ENHANCE_READ))) {
|
||||
err = -EFAULT;
|
||||
goto put_page;
|
||||
}
|
||||
goto got_it;
|
||||
}
|
||||
|
||||
|
@ -665,8 +670,12 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
|
|||
goto put_page;
|
||||
f2fs_put_dnode(&dn);
|
||||
|
||||
if (!__is_valid_data_blkaddr(dn.data_blkaddr)) {
|
||||
err = -ENOENT;
|
||||
goto put_page;
|
||||
}
|
||||
if (unlikely(!f2fs_is_valid_blkaddr(sbi, dn.data_blkaddr,
|
||||
DATA_GENERIC))) {
|
||||
DATA_GENERIC_ENHANCE))) {
|
||||
err = -EFAULT;
|
||||
goto put_page;
|
||||
}
|
||||
|
@ -1175,6 +1184,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
|||
"type [%d, %d] in SSA and SIT",
|
||||
segno, type, GET_SUM_TYPE((&sum->footer)));
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
f2fs_stop_checkpoint(sbi, false);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
|
@ -1346,7 +1356,7 @@ void f2fs_build_gc_manager(struct f2fs_sb_info *sbi)
|
|||
sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
|
||||
|
||||
/* give warm/cold data area from slower device */
|
||||
if (sbi->s_ndevs && !__is_large_section(sbi))
|
||||
if (f2fs_is_multi_device(sbi) && !__is_large_section(sbi))
|
||||
SIT_I(sbi)->last_victim[ALLOC_NEXT] =
|
||||
GET_SEGNO(sbi, FDEV(0).end_blk) + 1;
|
||||
}
|
||||
|
|
|
@ -420,6 +420,14 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct page *ipage,
|
|||
stat_dec_inline_dir(dir);
|
||||
clear_inode_flag(dir, FI_INLINE_DENTRY);
|
||||
|
||||
/*
|
||||
* should retrieve reserved space which was used to keep
|
||||
* inline_dentry's structure for backward compatibility.
|
||||
*/
|
||||
if (!f2fs_sb_has_flexible_inline_xattr(F2FS_I_SB(dir)) &&
|
||||
!f2fs_has_inline_xattr(dir))
|
||||
F2FS_I(dir)->i_inline_xattr_size = 0;
|
||||
|
||||
f2fs_i_depth_write(dir, 1);
|
||||
if (i_size_read(dir) < PAGE_SIZE)
|
||||
f2fs_i_size_write(dir, PAGE_SIZE);
|
||||
|
@ -501,6 +509,15 @@ static int f2fs_move_rehashed_dirents(struct inode *dir, struct page *ipage,
|
|||
|
||||
stat_dec_inline_dir(dir);
|
||||
clear_inode_flag(dir, FI_INLINE_DENTRY);
|
||||
|
||||
/*
|
||||
* should retrieve reserved space which was used to keep
|
||||
* inline_dentry's structure for backward compatibility.
|
||||
*/
|
||||
if (!f2fs_sb_has_flexible_inline_xattr(F2FS_I_SB(dir)) &&
|
||||
!f2fs_has_inline_xattr(dir))
|
||||
F2FS_I(dir)->i_inline_xattr_size = 0;
|
||||
|
||||
kvfree(backup_dentry);
|
||||
return 0;
|
||||
recover:
|
||||
|
|
|
@ -73,7 +73,7 @@ static int __written_first_block(struct f2fs_sb_info *sbi,
|
|||
|
||||
if (!__is_valid_data_blkaddr(addr))
|
||||
return 1;
|
||||
if (!f2fs_is_valid_blkaddr(sbi, addr, DATA_GENERIC))
|
||||
if (!f2fs_is_valid_blkaddr(sbi, addr, DATA_GENERIC_ENHANCE))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
@ -177,8 +177,8 @@ bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page)
|
|||
|
||||
if (provided != calculated)
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"checksum invalid, ino = %x, %x vs. %x",
|
||||
ino_of_node(page), provided, calculated);
|
||||
"checksum invalid, nid = %lu, ino_of_node = %x, %x vs. %x",
|
||||
page->index, ino_of_node(page), provided, calculated);
|
||||
|
||||
return provided == calculated;
|
||||
}
|
||||
|
@ -267,9 +267,10 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page)
|
|||
struct extent_info *ei = &F2FS_I(inode)->extent_tree->largest;
|
||||
|
||||
if (ei->len &&
|
||||
(!f2fs_is_valid_blkaddr(sbi, ei->blk, DATA_GENERIC) ||
|
||||
(!f2fs_is_valid_blkaddr(sbi, ei->blk,
|
||||
DATA_GENERIC_ENHANCE) ||
|
||||
!f2fs_is_valid_blkaddr(sbi, ei->blk + ei->len - 1,
|
||||
DATA_GENERIC))) {
|
||||
DATA_GENERIC_ENHANCE))) {
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"%s: inode (ino=%lx) extent info [%u, %u, %u] "
|
||||
|
@ -488,6 +489,7 @@ make_now:
|
|||
return inode;
|
||||
|
||||
bad_inode:
|
||||
f2fs_inode_synced(inode);
|
||||
iget_failed(inode);
|
||||
trace_f2fs_iget_exit(inode, ret);
|
||||
return ERR_PTR(ret);
|
||||
|
|
|
@ -143,7 +143,7 @@ fail_drop:
|
|||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int is_extension_exist(const unsigned char *s, const char *sub)
|
||||
static inline int is_extension_exist(const unsigned char *s, const char *sub)
|
||||
{
|
||||
size_t slen = strlen(s);
|
||||
size_t sublen = strlen(sub);
|
||||
|
|
|
@ -454,7 +454,7 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
|
|||
new_blkaddr == NULL_ADDR);
|
||||
f2fs_bug_on(sbi, nat_get_blkaddr(e) == NEW_ADDR &&
|
||||
new_blkaddr == NEW_ADDR);
|
||||
f2fs_bug_on(sbi, is_valid_data_blkaddr(sbi, nat_get_blkaddr(e)) &&
|
||||
f2fs_bug_on(sbi, __is_valid_data_blkaddr(nat_get_blkaddr(e)) &&
|
||||
new_blkaddr == NEW_ADDR);
|
||||
|
||||
/* increment version no as node is removed */
|
||||
|
@ -465,7 +465,7 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
|
|||
|
||||
/* change address */
|
||||
nat_set_blkaddr(e, new_blkaddr);
|
||||
if (!is_valid_data_blkaddr(sbi, new_blkaddr))
|
||||
if (!__is_valid_data_blkaddr(new_blkaddr))
|
||||
set_nat_flag(e, IS_CHECKPOINTED, false);
|
||||
__set_nat_cache_dirty(nm_i, e);
|
||||
|
||||
|
@ -526,6 +526,7 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid,
|
|||
struct f2fs_nat_entry ne;
|
||||
struct nat_entry *e;
|
||||
pgoff_t index;
|
||||
block_t blkaddr;
|
||||
int i;
|
||||
|
||||
ni->nid = nid;
|
||||
|
@ -569,6 +570,11 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid,
|
|||
node_info_from_raw_nat(ni, &ne);
|
||||
f2fs_put_page(page, 1);
|
||||
cache:
|
||||
blkaddr = le32_to_cpu(ne.block_addr);
|
||||
if (__is_valid_data_blkaddr(blkaddr) &&
|
||||
!f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC_ENHANCE))
|
||||
return -EFAULT;
|
||||
|
||||
/* cache nat entry */
|
||||
cache_nat_entry(sbi, nid, &ne);
|
||||
return 0;
|
||||
|
@ -600,9 +606,9 @@ static void f2fs_ra_node_pages(struct page *parent, int start, int n)
|
|||
pgoff_t f2fs_get_next_page_offset(struct dnode_of_data *dn, pgoff_t pgofs)
|
||||
{
|
||||
const long direct_index = ADDRS_PER_INODE(dn->inode);
|
||||
const long direct_blks = ADDRS_PER_BLOCK;
|
||||
const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
|
||||
unsigned int skipped_unit = ADDRS_PER_BLOCK;
|
||||
const long direct_blks = ADDRS_PER_BLOCK(dn->inode);
|
||||
const long indirect_blks = ADDRS_PER_BLOCK(dn->inode) * NIDS_PER_BLOCK;
|
||||
unsigned int skipped_unit = ADDRS_PER_BLOCK(dn->inode);
|
||||
int cur_level = dn->cur_level;
|
||||
int max_level = dn->max_level;
|
||||
pgoff_t base = 0;
|
||||
|
@ -638,9 +644,9 @@ static int get_node_path(struct inode *inode, long block,
|
|||
int offset[4], unsigned int noffset[4])
|
||||
{
|
||||
const long direct_index = ADDRS_PER_INODE(inode);
|
||||
const long direct_blks = ADDRS_PER_BLOCK;
|
||||
const long direct_blks = ADDRS_PER_BLOCK(inode);
|
||||
const long dptrs_per_blk = NIDS_PER_BLOCK;
|
||||
const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
|
||||
const long indirect_blks = ADDRS_PER_BLOCK(inode) * NIDS_PER_BLOCK;
|
||||
const long dindirect_blks = indirect_blks * NIDS_PER_BLOCK;
|
||||
int n = 0;
|
||||
int level = 0;
|
||||
|
@ -1181,8 +1187,14 @@ int f2fs_remove_inode_page(struct inode *inode)
|
|||
f2fs_put_dnode(&dn);
|
||||
return -EIO;
|
||||
}
|
||||
f2fs_bug_on(F2FS_I_SB(inode),
|
||||
inode->i_blocks != 0 && inode->i_blocks != 8);
|
||||
|
||||
if (unlikely(inode->i_blocks != 0 && inode->i_blocks != 8)) {
|
||||
f2fs_msg(F2FS_I_SB(inode)->sb, KERN_WARNING,
|
||||
"Inconsistent i_blocks, ino:%lu, iblocks:%llu",
|
||||
inode->i_ino,
|
||||
(unsigned long long)inode->i_blocks);
|
||||
set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
|
||||
}
|
||||
|
||||
/* will put inode & node pages */
|
||||
err = truncate_node(&dn);
|
||||
|
@ -1277,9 +1289,10 @@ static int read_node_page(struct page *page, int op_flags)
|
|||
int err;
|
||||
|
||||
if (PageUptodate(page)) {
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
f2fs_bug_on(sbi, !f2fs_inode_chksum_verify(sbi, page));
|
||||
#endif
|
||||
if (!f2fs_inode_chksum_verify(sbi, page)) {
|
||||
ClearPageUptodate(page);
|
||||
return -EBADMSG;
|
||||
}
|
||||
return LOCKED_PAGE;
|
||||
}
|
||||
|
||||
|
@ -1543,7 +1556,8 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
|
|||
}
|
||||
|
||||
if (__is_valid_data_blkaddr(ni.blk_addr) &&
|
||||
!f2fs_is_valid_blkaddr(sbi, ni.blk_addr, DATA_GENERIC)) {
|
||||
!f2fs_is_valid_blkaddr(sbi, ni.blk_addr,
|
||||
DATA_GENERIC_ENHANCE)) {
|
||||
up_read(&sbi->node_write);
|
||||
goto redirty_out;
|
||||
}
|
||||
|
@ -2078,6 +2092,9 @@ static bool add_free_nid(struct f2fs_sb_info *sbi,
|
|||
if (unlikely(nid == 0))
|
||||
return false;
|
||||
|
||||
if (unlikely(f2fs_check_nid_range(sbi, nid)))
|
||||
return false;
|
||||
|
||||
i = f2fs_kmem_cache_alloc(free_nid_slab, GFP_NOFS);
|
||||
i->nid = nid;
|
||||
i->state = FREE_NID;
|
||||
|
|
|
@ -325,8 +325,10 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
|
|||
break;
|
||||
}
|
||||
|
||||
if (!is_recoverable_dnode(page))
|
||||
if (!is_recoverable_dnode(page)) {
|
||||
f2fs_put_page(page, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_fsync_dnode(page))
|
||||
goto next;
|
||||
|
@ -338,8 +340,10 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
|
|||
if (!check_only &&
|
||||
IS_INODE(page) && is_dent_dnode(page)) {
|
||||
err = f2fs_recover_inode_page(sbi, page);
|
||||
if (err)
|
||||
if (err) {
|
||||
f2fs_put_page(page, 1);
|
||||
break;
|
||||
}
|
||||
quota_inode = true;
|
||||
}
|
||||
|
||||
|
@ -355,6 +359,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
|
|||
err = 0;
|
||||
goto next;
|
||||
}
|
||||
f2fs_put_page(page, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -370,6 +375,7 @@ next:
|
|||
"%s: detect looped node chain, "
|
||||
"blkaddr:%u, next:%u",
|
||||
__func__, blkaddr, next_blkaddr_of_node(page));
|
||||
f2fs_put_page(page, 1);
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
@ -380,7 +386,6 @@ next:
|
|||
|
||||
f2fs_ra_meta_pages_cond(sbi, blkaddr);
|
||||
}
|
||||
f2fs_put_page(page, 1);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -546,7 +551,15 @@ retry_dn:
|
|||
goto err;
|
||||
|
||||
f2fs_bug_on(sbi, ni.ino != ino_of_node(page));
|
||||
f2fs_bug_on(sbi, ofs_of_node(dn.node_page) != ofs_of_node(page));
|
||||
|
||||
if (ofs_of_node(dn.node_page) != ofs_of_node(page)) {
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"Inconsistent ofs_of_node, ino:%lu, ofs:%u, %u",
|
||||
inode->i_ino, ofs_of_node(dn.node_page),
|
||||
ofs_of_node(page));
|
||||
err = -EFAULT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (; start < end; start++, dn.ofs_in_node++) {
|
||||
block_t src, dest;
|
||||
|
@ -554,6 +567,18 @@ retry_dn:
|
|||
src = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node);
|
||||
dest = datablock_addr(dn.inode, page, dn.ofs_in_node);
|
||||
|
||||
if (__is_valid_data_blkaddr(src) &&
|
||||
!f2fs_is_valid_blkaddr(sbi, src, META_POR)) {
|
||||
err = -EFAULT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (__is_valid_data_blkaddr(dest) &&
|
||||
!f2fs_is_valid_blkaddr(sbi, dest, META_POR)) {
|
||||
err = -EFAULT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* skip recovering if dest is the same as src */
|
||||
if (src == dest)
|
||||
continue;
|
||||
|
@ -666,8 +691,10 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list,
|
|||
*/
|
||||
if (IS_INODE(page)) {
|
||||
err = recover_inode(entry->inode, page);
|
||||
if (err)
|
||||
if (err) {
|
||||
f2fs_put_page(page, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (entry->last_dentry == blkaddr) {
|
||||
err = recover_dentry(entry->inode, page, dir_list);
|
||||
|
|
|
@ -580,7 +580,7 @@ static int submit_flush_wait(struct f2fs_sb_info *sbi, nid_t ino)
|
|||
int ret = 0;
|
||||
int i;
|
||||
|
||||
if (!sbi->s_ndevs)
|
||||
if (!f2fs_is_multi_device(sbi))
|
||||
return __submit_flush_wait(sbi, sbi->sb->s_bdev);
|
||||
|
||||
for (i = 0; i < sbi->s_ndevs; i++) {
|
||||
|
@ -648,7 +648,8 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (atomic_inc_return(&fcc->queued_flush) == 1 || sbi->s_ndevs > 1) {
|
||||
if (atomic_inc_return(&fcc->queued_flush) == 1 ||
|
||||
f2fs_is_multi_device(sbi)) {
|
||||
ret = submit_flush_wait(sbi, ino);
|
||||
atomic_dec(&fcc->queued_flush);
|
||||
|
||||
|
@ -754,7 +755,7 @@ int f2fs_flush_device_cache(struct f2fs_sb_info *sbi)
|
|||
{
|
||||
int ret = 0, i;
|
||||
|
||||
if (!sbi->s_ndevs)
|
||||
if (!f2fs_is_multi_device(sbi))
|
||||
return 0;
|
||||
|
||||
for (i = 1; i < sbi->s_ndevs; i++) {
|
||||
|
@ -1367,9 +1368,12 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi,
|
|||
{
|
||||
block_t lblkstart = blkstart;
|
||||
|
||||
if (!f2fs_bdev_support_discard(bdev))
|
||||
return 0;
|
||||
|
||||
trace_f2fs_queue_discard(bdev, blkstart, blklen);
|
||||
|
||||
if (sbi->s_ndevs) {
|
||||
if (f2fs_is_multi_device(sbi)) {
|
||||
int devi = f2fs_target_device_index(sbi, blkstart);
|
||||
|
||||
blkstart -= FDEV(devi).start_blk;
|
||||
|
@ -1732,42 +1736,36 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi,
|
|||
block_t lblkstart = blkstart;
|
||||
int devi = 0;
|
||||
|
||||
if (sbi->s_ndevs) {
|
||||
if (f2fs_is_multi_device(sbi)) {
|
||||
devi = f2fs_target_device_index(sbi, blkstart);
|
||||
if (blkstart < FDEV(devi).start_blk ||
|
||||
blkstart > FDEV(devi).end_blk) {
|
||||
f2fs_msg(sbi->sb, KERN_ERR, "Invalid block %x",
|
||||
blkstart);
|
||||
return -EIO;
|
||||
}
|
||||
blkstart -= FDEV(devi).start_blk;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to know the type of the zone: for conventional zones,
|
||||
* use regular discard if the drive supports it. For sequential
|
||||
* zones, reset the zone write pointer.
|
||||
*/
|
||||
switch (get_blkz_type(sbi, bdev, blkstart)) {
|
||||
|
||||
case BLK_ZONE_TYPE_CONVENTIONAL:
|
||||
if (!blk_queue_discard(bdev_get_queue(bdev)))
|
||||
return 0;
|
||||
return __queue_discard_cmd(sbi, bdev, lblkstart, blklen);
|
||||
case BLK_ZONE_TYPE_SEQWRITE_REQ:
|
||||
case BLK_ZONE_TYPE_SEQWRITE_PREF:
|
||||
/* For sequential zones, reset the zone write pointer */
|
||||
if (f2fs_blkz_is_seq(sbi, devi, blkstart)) {
|
||||
sector = SECTOR_FROM_BLOCK(blkstart);
|
||||
nr_sects = SECTOR_FROM_BLOCK(blklen);
|
||||
|
||||
if (sector & (bdev_zone_sectors(bdev) - 1) ||
|
||||
nr_sects != bdev_zone_sectors(bdev)) {
|
||||
f2fs_msg(sbi->sb, KERN_INFO,
|
||||
"(%d) %s: Unaligned discard attempted (block %x + %x)",
|
||||
f2fs_msg(sbi->sb, KERN_ERR,
|
||||
"(%d) %s: Unaligned zone reset attempted (block %x + %x)",
|
||||
devi, sbi->s_ndevs ? FDEV(devi).path: "",
|
||||
blkstart, blklen);
|
||||
return -EIO;
|
||||
}
|
||||
trace_f2fs_issue_reset_zone(bdev, blkstart);
|
||||
return blkdev_reset_zones(bdev, sector,
|
||||
nr_sects, GFP_NOFS);
|
||||
default:
|
||||
/* Unknown zone type: broken device ? */
|
||||
return -EIO;
|
||||
return blkdev_reset_zones(bdev, sector, nr_sects, GFP_NOFS);
|
||||
}
|
||||
|
||||
/* For conventional zones, use regular discard if supported */
|
||||
return __queue_discard_cmd(sbi, bdev, lblkstart, blklen);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1775,8 +1773,7 @@ static int __issue_discard_async(struct f2fs_sb_info *sbi,
|
|||
struct block_device *bdev, block_t blkstart, block_t blklen)
|
||||
{
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
if (f2fs_sb_has_blkzoned(sbi) &&
|
||||
bdev_zoned_model(bdev) != BLK_ZONED_NONE)
|
||||
if (f2fs_sb_has_blkzoned(sbi) && bdev_is_zoned(bdev))
|
||||
return __f2fs_issue_discard_zone(sbi, bdev, blkstart, blklen);
|
||||
#endif
|
||||
return __queue_discard_cmd(sbi, bdev, blkstart, blklen);
|
||||
|
@ -2172,8 +2169,11 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
|
|||
* before, we must track that to know how much space we
|
||||
* really have.
|
||||
*/
|
||||
if (f2fs_test_bit(offset, se->ckpt_valid_map))
|
||||
if (f2fs_test_bit(offset, se->ckpt_valid_map)) {
|
||||
spin_lock(&sbi->stat_lock);
|
||||
sbi->unusable_block_count++;
|
||||
spin_unlock(&sbi->stat_lock);
|
||||
}
|
||||
}
|
||||
|
||||
if (f2fs_test_and_clear_bit(offset, se->discard_map))
|
||||
|
@ -2220,7 +2220,7 @@ bool f2fs_is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr)
|
|||
struct seg_entry *se;
|
||||
bool is_cp = false;
|
||||
|
||||
if (!is_valid_data_blkaddr(sbi, blkaddr))
|
||||
if (!__is_valid_data_blkaddr(blkaddr))
|
||||
return true;
|
||||
|
||||
down_read(&sit_i->sentry_lock);
|
||||
|
@ -3089,7 +3089,7 @@ static void update_device_state(struct f2fs_io_info *fio)
|
|||
struct f2fs_sb_info *sbi = fio->sbi;
|
||||
unsigned int devidx;
|
||||
|
||||
if (!sbi->s_ndevs)
|
||||
if (!f2fs_is_multi_device(sbi))
|
||||
return;
|
||||
|
||||
devidx = f2fs_target_device_index(sbi, fio->new_blkaddr);
|
||||
|
@ -3187,13 +3187,18 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio)
|
|||
{
|
||||
int err;
|
||||
struct f2fs_sb_info *sbi = fio->sbi;
|
||||
unsigned int segno;
|
||||
|
||||
fio->new_blkaddr = fio->old_blkaddr;
|
||||
/* i/o temperature is needed for passing down write hints */
|
||||
__get_segment_type(fio);
|
||||
|
||||
f2fs_bug_on(sbi, !IS_DATASEG(get_seg_entry(sbi,
|
||||
GET_SEGNO(sbi, fio->new_blkaddr))->type));
|
||||
segno = GET_SEGNO(sbi, fio->new_blkaddr);
|
||||
|
||||
if (!IS_DATASEG(get_seg_entry(sbi, segno)->type)) {
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
stat_inc_inplace_blocks(fio->sbi);
|
||||
|
||||
|
@ -3336,7 +3341,7 @@ void f2fs_wait_on_block_writeback(struct inode *inode, block_t blkaddr)
|
|||
if (!f2fs_post_read_required(inode))
|
||||
return;
|
||||
|
||||
if (!is_valid_data_blkaddr(sbi, blkaddr))
|
||||
if (!__is_valid_data_blkaddr(blkaddr))
|
||||
return;
|
||||
|
||||
cpage = find_lock_page(META_MAPPING(sbi), blkaddr);
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
(GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & ((sbi)->blocks_per_seg - 1))
|
||||
|
||||
#define GET_SEGNO(sbi, blk_addr) \
|
||||
((!is_valid_data_blkaddr(sbi, blk_addr)) ? \
|
||||
((!__is_valid_data_blkaddr(blk_addr)) ? \
|
||||
NULL_SEGNO : GET_L2R_SEGNO(FREE_I(sbi), \
|
||||
GET_SEGNO_FROM_SEG0(sbi, blk_addr)))
|
||||
#define BLKS_PER_SEC(sbi) \
|
||||
|
@ -656,14 +656,15 @@ static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno)
|
|||
f2fs_bug_on(sbi, segno > TOTAL_SEGS(sbi) - 1);
|
||||
}
|
||||
|
||||
static inline void verify_block_addr(struct f2fs_io_info *fio, block_t blk_addr)
|
||||
static inline void verify_fio_blkaddr(struct f2fs_io_info *fio)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = fio->sbi;
|
||||
|
||||
if (__is_meta_io(fio))
|
||||
verify_blkaddr(sbi, blk_addr, META_GENERIC);
|
||||
else
|
||||
verify_blkaddr(sbi, blk_addr, DATA_GENERIC);
|
||||
if (__is_valid_data_blkaddr(fio->old_blkaddr))
|
||||
verify_blkaddr(sbi, fio->old_blkaddr, __is_meta_io(fio) ?
|
||||
META_GENERIC : DATA_GENERIC);
|
||||
verify_blkaddr(sbi, fio->new_blkaddr, __is_meta_io(fio) ?
|
||||
META_GENERIC : DATA_GENERIC_ENHANCE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -672,7 +673,6 @@ static inline void verify_block_addr(struct f2fs_io_info *fio, block_t blk_addr)
|
|||
static inline int check_block_count(struct f2fs_sb_info *sbi,
|
||||
int segno, struct f2fs_sit_entry *raw_sit)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
bool is_valid = test_bit_le(0, raw_sit->valid_map) ? true : false;
|
||||
int valid_blocks = 0;
|
||||
int cur_pos = 0, next_pos;
|
||||
|
@ -699,7 +699,7 @@ static inline int check_block_count(struct f2fs_sb_info *sbi,
|
|||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* check segment usage, and check boundary of a given segment number */
|
||||
if (unlikely(GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg
|
||||
|| segno > TOTAL_SEGS(sbi) - 1)) {
|
||||
|
|
|
@ -1019,7 +1019,7 @@ static void destroy_device_list(struct f2fs_sb_info *sbi)
|
|||
for (i = 0; i < sbi->s_ndevs; i++) {
|
||||
blkdev_put(FDEV(i).bdev, FMODE_EXCL);
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
kvfree(FDEV(i).blkz_type);
|
||||
kvfree(FDEV(i).blkz_seq);
|
||||
#endif
|
||||
}
|
||||
kvfree(sbi->devs);
|
||||
|
@ -1221,10 +1221,13 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|||
buf->f_blocks = total_count - start_count;
|
||||
buf->f_bfree = user_block_count - valid_user_blocks(sbi) -
|
||||
sbi->current_reserved_blocks;
|
||||
|
||||
spin_lock(&sbi->stat_lock);
|
||||
if (unlikely(buf->f_bfree <= sbi->unusable_block_count))
|
||||
buf->f_bfree = 0;
|
||||
else
|
||||
buf->f_bfree -= sbi->unusable_block_count;
|
||||
spin_unlock(&sbi->stat_lock);
|
||||
|
||||
if (buf->f_bfree > F2FS_OPTION(sbi).root_reserved_blocks)
|
||||
buf->f_bavail = buf->f_bfree -
|
||||
|
@ -1499,9 +1502,15 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
|
|||
mutex_lock(&sbi->gc_mutex);
|
||||
cpc.reason = CP_PAUSE;
|
||||
set_sbi_flag(sbi, SBI_CP_DISABLED);
|
||||
f2fs_write_checkpoint(sbi, &cpc);
|
||||
err = f2fs_write_checkpoint(sbi, &cpc);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
spin_lock(&sbi->stat_lock);
|
||||
sbi->unusable_block_count = 0;
|
||||
spin_unlock(&sbi->stat_lock);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&sbi->gc_mutex);
|
||||
restore_flag:
|
||||
sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
|
||||
|
@ -2271,7 +2280,7 @@ static const struct export_operations f2fs_export_ops = {
|
|||
static loff_t max_file_blocks(void)
|
||||
{
|
||||
loff_t result = 0;
|
||||
loff_t leaf_count = ADDRS_PER_BLOCK;
|
||||
loff_t leaf_count = DEF_ADDRS_PER_BLOCK;
|
||||
|
||||
/*
|
||||
* note: previously, result is equal to (DEF_ADDRS_PER_INODE -
|
||||
|
@ -2449,7 +2458,7 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi,
|
|||
/* Currently, support only 4KB page cache size */
|
||||
if (F2FS_BLKSIZE != PAGE_SIZE) {
|
||||
f2fs_msg(sb, KERN_INFO,
|
||||
"Invalid page_cache_size (%lu), supports only 4KB\n",
|
||||
"Invalid page_cache_size (%lu), supports only 4KB",
|
||||
PAGE_SIZE);
|
||||
return 1;
|
||||
}
|
||||
|
@ -2458,7 +2467,7 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi,
|
|||
blocksize = 1 << le32_to_cpu(raw_super->log_blocksize);
|
||||
if (blocksize != F2FS_BLKSIZE) {
|
||||
f2fs_msg(sb, KERN_INFO,
|
||||
"Invalid blocksize (%u), supports only 4KB\n",
|
||||
"Invalid blocksize (%u), supports only 4KB",
|
||||
blocksize);
|
||||
return 1;
|
||||
}
|
||||
|
@ -2466,7 +2475,7 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi,
|
|||
/* check log blocks per segment */
|
||||
if (le32_to_cpu(raw_super->log_blocks_per_seg) != 9) {
|
||||
f2fs_msg(sb, KERN_INFO,
|
||||
"Invalid log blocks per segment (%u)\n",
|
||||
"Invalid log blocks per segment (%u)",
|
||||
le32_to_cpu(raw_super->log_blocks_per_seg));
|
||||
return 1;
|
||||
}
|
||||
|
@ -2587,7 +2596,8 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi)
|
|||
unsigned int log_blocks_per_seg;
|
||||
unsigned int segment_count_main;
|
||||
unsigned int cp_pack_start_sum, cp_payload;
|
||||
block_t user_block_count;
|
||||
block_t user_block_count, valid_user_blocks;
|
||||
block_t avail_node_count, valid_node_count;
|
||||
int i, j;
|
||||
|
||||
total = le32_to_cpu(raw_super->segment_count);
|
||||
|
@ -2622,6 +2632,24 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi)
|
|||
return 1;
|
||||
}
|
||||
|
||||
valid_user_blocks = le64_to_cpu(ckpt->valid_block_count);
|
||||
if (valid_user_blocks > user_block_count) {
|
||||
f2fs_msg(sbi->sb, KERN_ERR,
|
||||
"Wrong valid_user_blocks: %u, user_block_count: %u",
|
||||
valid_user_blocks, user_block_count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
valid_node_count = le32_to_cpu(ckpt->valid_node_count);
|
||||
avail_node_count = sbi->total_node_count - sbi->nquota_files -
|
||||
F2FS_RESERVED_NODE_NUM;
|
||||
if (valid_node_count > avail_node_count) {
|
||||
f2fs_msg(sbi->sb, KERN_ERR,
|
||||
"Wrong valid_node_count: %u, avail_node_count: %u",
|
||||
valid_node_count, avail_node_count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
main_segs = le32_to_cpu(raw_super->segment_count_main);
|
||||
blocks_per_seg = sbi->blocks_per_seg;
|
||||
|
||||
|
@ -2793,9 +2821,11 @@ static int init_blkz_info(struct f2fs_sb_info *sbi, int devi)
|
|||
if (nr_sectors & (bdev_zone_sectors(bdev) - 1))
|
||||
FDEV(devi).nr_blkz++;
|
||||
|
||||
FDEV(devi).blkz_type = f2fs_kmalloc(sbi, FDEV(devi).nr_blkz,
|
||||
GFP_KERNEL);
|
||||
if (!FDEV(devi).blkz_type)
|
||||
FDEV(devi).blkz_seq = f2fs_kzalloc(sbi,
|
||||
BITS_TO_LONGS(FDEV(devi).nr_blkz)
|
||||
* sizeof(unsigned long),
|
||||
GFP_KERNEL);
|
||||
if (!FDEV(devi).blkz_seq)
|
||||
return -ENOMEM;
|
||||
|
||||
#define F2FS_REPORT_NR_ZONES 4096
|
||||
|
@ -2822,7 +2852,8 @@ static int init_blkz_info(struct f2fs_sb_info *sbi, int devi)
|
|||
}
|
||||
|
||||
for (i = 0; i < nr_zones; i++) {
|
||||
FDEV(devi).blkz_type[n] = zones[i].type;
|
||||
if (zones[i].type != BLK_ZONE_TYPE_CONVENTIONAL)
|
||||
set_bit(n, FDEV(devi).blkz_seq);
|
||||
sector += zones[i].len;
|
||||
n++;
|
||||
}
|
||||
|
@ -3105,7 +3136,7 @@ try_onemore:
|
|||
#ifndef CONFIG_BLK_DEV_ZONED
|
||||
if (f2fs_sb_has_blkzoned(sbi)) {
|
||||
f2fs_msg(sb, KERN_ERR,
|
||||
"Zoned block device support is not enabled\n");
|
||||
"Zoned block device support is not enabled");
|
||||
err = -EOPNOTSUPP;
|
||||
goto free_sb_buf;
|
||||
}
|
||||
|
@ -3350,10 +3381,17 @@ try_onemore:
|
|||
* mount should be failed, when device has readonly mode, and
|
||||
* previous checkpoint was not done by clean system shutdown.
|
||||
*/
|
||||
if (bdev_read_only(sb->s_bdev) &&
|
||||
!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
|
||||
err = -EROFS;
|
||||
goto free_meta;
|
||||
if (f2fs_hw_is_readonly(sbi)) {
|
||||
if (!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
|
||||
err = -EROFS;
|
||||
f2fs_msg(sb, KERN_ERR,
|
||||
"Need to recover fsync data, but "
|
||||
"write access unavailable");
|
||||
goto free_meta;
|
||||
}
|
||||
f2fs_msg(sbi->sb, KERN_INFO, "write access "
|
||||
"unavailable, skipping recovery");
|
||||
goto reset_checkpoint;
|
||||
}
|
||||
|
||||
if (need_fsck)
|
||||
|
|
|
@ -202,12 +202,17 @@ static inline const struct xattr_handler *f2fs_xattr_handler(int index)
|
|||
return handler;
|
||||
}
|
||||
|
||||
static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index,
|
||||
size_t len, const char *name)
|
||||
static struct f2fs_xattr_entry *__find_xattr(void *base_addr,
|
||||
void *last_base_addr, int index,
|
||||
size_t len, const char *name)
|
||||
{
|
||||
struct f2fs_xattr_entry *entry;
|
||||
|
||||
list_for_each_xattr(entry, base_addr) {
|
||||
if ((void *)(entry) + sizeof(__u32) > last_base_addr ||
|
||||
(void *)XATTR_NEXT_ENTRY(entry) > last_base_addr)
|
||||
return NULL;
|
||||
|
||||
if (entry->e_name_index != index)
|
||||
continue;
|
||||
if (entry->e_name_len != len)
|
||||
|
@ -297,20 +302,22 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage,
|
|||
const char *name, struct f2fs_xattr_entry **xe,
|
||||
void **base_addr, int *base_size)
|
||||
{
|
||||
void *cur_addr, *txattr_addr, *last_addr = NULL;
|
||||
void *cur_addr, *txattr_addr, *last_txattr_addr;
|
||||
void *last_addr = NULL;
|
||||
nid_t xnid = F2FS_I(inode)->i_xattr_nid;
|
||||
unsigned int size = xnid ? VALID_XATTR_BLOCK_SIZE : 0;
|
||||
unsigned int inline_size = inline_xattr_size(inode);
|
||||
int err = 0;
|
||||
|
||||
if (!size && !inline_size)
|
||||
if (!xnid && !inline_size)
|
||||
return -ENODATA;
|
||||
|
||||
*base_size = inline_size + size + XATTR_PADDING_SIZE;
|
||||
*base_size = XATTR_SIZE(xnid, inode) + XATTR_PADDING_SIZE;
|
||||
txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode), *base_size, GFP_NOFS);
|
||||
if (!txattr_addr)
|
||||
return -ENOMEM;
|
||||
|
||||
last_txattr_addr = (void *)txattr_addr + XATTR_SIZE(xnid, inode);
|
||||
|
||||
/* read from inline xattr */
|
||||
if (inline_size) {
|
||||
err = read_inline_xattr(inode, ipage, txattr_addr);
|
||||
|
@ -337,7 +344,11 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage,
|
|||
else
|
||||
cur_addr = txattr_addr;
|
||||
|
||||
*xe = __find_xattr(cur_addr, index, len, name);
|
||||
*xe = __find_xattr(cur_addr, last_txattr_addr, index, len, name);
|
||||
if (!*xe) {
|
||||
err = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
check:
|
||||
if (IS_XATTR_LAST_ENTRY(*xe)) {
|
||||
err = -ENODATA;
|
||||
|
@ -581,7 +592,8 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
|||
struct page *ipage, int flags)
|
||||
{
|
||||
struct f2fs_xattr_entry *here, *last;
|
||||
void *base_addr;
|
||||
void *base_addr, *last_base_addr;
|
||||
nid_t xnid = F2FS_I(inode)->i_xattr_nid;
|
||||
int found, newsize;
|
||||
size_t len;
|
||||
__u32 new_hsize;
|
||||
|
@ -605,8 +617,14 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
last_base_addr = (void *)base_addr + XATTR_SIZE(xnid, inode);
|
||||
|
||||
/* find entry with wanted name. */
|
||||
here = __find_xattr(base_addr, index, len, name);
|
||||
here = __find_xattr(base_addr, last_base_addr, index, len, name);
|
||||
if (!here) {
|
||||
error = -EFAULT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1;
|
||||
|
||||
|
|
|
@ -71,6 +71,8 @@ struct f2fs_xattr_entry {
|
|||
entry = XATTR_NEXT_ENTRY(entry))
|
||||
#define VALID_XATTR_BLOCK_SIZE (PAGE_SIZE - sizeof(struct node_footer))
|
||||
#define XATTR_PADDING_SIZE (sizeof(__u32))
|
||||
#define XATTR_SIZE(x,i) (((x) ? VALID_XATTR_BLOCK_SIZE : 0) + \
|
||||
(inline_xattr_size(i)))
|
||||
#define MIN_OFFSET(i) XATTR_ALIGN(inline_xattr_size(i) + \
|
||||
VALID_XATTR_BLOCK_SIZE)
|
||||
|
||||
|
|
|
@ -164,6 +164,10 @@ struct f2fs_checkpoint {
|
|||
unsigned char sit_nat_version_bitmap[1];
|
||||
} __packed;
|
||||
|
||||
#define CP_CHKSUM_OFFSET 4092 /* default chksum offset in checkpoint */
|
||||
#define CP_MIN_CHKSUM_OFFSET \
|
||||
(offsetof(struct f2fs_checkpoint, sit_nat_version_bitmap))
|
||||
|
||||
/*
|
||||
* For orphan inode management
|
||||
*/
|
||||
|
@ -198,11 +202,12 @@ struct f2fs_extent {
|
|||
get_extra_isize(inode))
|
||||
#define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */
|
||||
#define ADDRS_PER_INODE(inode) addrs_per_inode(inode)
|
||||
#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */
|
||||
#define DEF_ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */
|
||||
#define ADDRS_PER_BLOCK(inode) addrs_per_block(inode)
|
||||
#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */
|
||||
|
||||
#define ADDRS_PER_PAGE(page, inode) \
|
||||
(IS_INODE(page) ? ADDRS_PER_INODE(inode) : ADDRS_PER_BLOCK)
|
||||
(IS_INODE(page) ? ADDRS_PER_INODE(inode) : ADDRS_PER_BLOCK(inode))
|
||||
|
||||
#define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1)
|
||||
#define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2)
|
||||
|
@ -267,7 +272,7 @@ struct f2fs_inode {
|
|||
} __packed;
|
||||
|
||||
struct direct_node {
|
||||
__le32 addr[ADDRS_PER_BLOCK]; /* array of data block address */
|
||||
__le32 addr[DEF_ADDRS_PER_BLOCK]; /* array of data block address */
|
||||
} __packed;
|
||||
|
||||
struct indirect_node {
|
||||
|
|
|
@ -533,6 +533,37 @@ TRACE_EVENT(f2fs_truncate_partial_nodes,
|
|||
__entry->err)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_file_write_iter,
|
||||
|
||||
TP_PROTO(struct inode *inode, unsigned long offset,
|
||||
unsigned long length, int ret),
|
||||
|
||||
TP_ARGS(inode, offset, length, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(unsigned long, offset)
|
||||
__field(unsigned long, length)
|
||||
__field(int, ret)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->offset = offset;
|
||||
__entry->length = length;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, "
|
||||
"offset = %lu, length = %lu, written(err) = %d",
|
||||
show_dev_ino(__entry),
|
||||
__entry->offset,
|
||||
__entry->length,
|
||||
__entry->ret)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_map_blocks,
|
||||
TP_PROTO(struct inode *inode, struct f2fs_map_blocks *map, int ret),
|
||||
|
||||
|
@ -1253,6 +1284,32 @@ DEFINE_EVENT(f2fs__page, f2fs_commit_inmem_page,
|
|||
TP_ARGS(page, type)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_filemap_fault,
|
||||
|
||||
TP_PROTO(struct inode *inode, pgoff_t index, unsigned long ret),
|
||||
|
||||
TP_ARGS(inode, index, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(pgoff_t, index)
|
||||
__field(unsigned long, ret)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->index = index;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, index = %lu, ret = %lx",
|
||||
show_dev_ino(__entry),
|
||||
(unsigned long)__entry->index,
|
||||
__entry->ret)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_writepages,
|
||||
|
||||
TP_PROTO(struct inode *inode, struct writeback_control *wbc, int type),
|
||||
|
|
Loading…
Reference in a new issue