mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08:00
Merge remote-tracking branch 'aosp/upstream-f2fs-stable-linux-4.14.y' into android-4.14-stable
* aosp/upstream-f2fs-stable-linux-4.14.y: f2fs: attach IO flags to the missing cases f2fs: add node_io_flag for bio flags likewise data_io_flag f2fs: remove unused parameter of f2fs_put_rpages_mapping() f2fs: handle readonly filesystem in f2fs_ioc_shutdown() f2fs: avoid utf8_strncasecmp() with unstable name f2fs: don't return vmalloc() memory from f2fs_kmalloc() f2fs: fix retry logic in f2fs_write_cache_pages() f2fs: fix wrong discard space f2fs: compress: don't compress any datas after cp stop f2fs: remove unneeded return value of __insert_discard_tree() f2fs: fix wrong value of tracepoint parameter f2fs: protect new segment allocation in expand_inode_data f2fs: code cleanup by removing ifdef macro surrounding writeback: Avoid skipping inode writeback f2fs: avoid inifinite loop to wait for flushing node pages at cp_error f2fs: compress: fix zstd data corruption f2fs: add compressed/gc data read IO stat f2fs: fix potential use-after-free issue f2fs: compress: don't handle non-compressed data in workqueue f2fs: remove redundant assignment to variable err f2fs: refactor resize_fs to avoid meta updates in progress f2fs: use round_up to enhance calculation f2fs: introduce F2FS_IOC_RESERVE_COMPRESS_BLOCKS f2fs: Avoid double lock for cp_rwsem during checkpoint f2fs: report delalloc reserve as non-free in statfs for project quota f2fs: Fix wrong stub helper update_sit_info f2fs: compress: let lz4 compressor handle output buffer budget properly f2fs: remove blk_plugging in block_operations f2fs: introduce F2FS_IOC_RELEASE_COMPRESS_BLOCKS f2fs: shrink spinlock coverage f2fs: correctly fix the parent inode number during fsync() f2fs: introduce mempool for {,de}compress intermediate page allocation f2fs: introduce f2fs_bmap_compress() f2fs: support fiemap on compressed inode f2fs: support partial truncation on compressed inode f2fs: remove redundant compress inode check f2fs: use strcmp() in parse_options() f2fs: Use the correct style for SPDX License Identifier Conflicts: fs/f2fs/data.c fs/f2fs/dir.c Bug: 154167995 Change-Id: I04ec97a9cafef2d7b8736f36a2a8d244965cae9a Signed-off-by: Jaegeuk Kim <jaegeuk@google.com>
This commit is contained in:
commit
db50a8301e
@ -333,6 +333,15 @@ Description: Give a way to attach REQ_META|FUA to data writes
|
||||
* 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
* Cold | Warm | Hot | Cold | Warm | Hot |
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/node_io_flag
|
||||
Date: June 2020
|
||||
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
|
||||
Description: Give a way to attach REQ_META|FUA to node writes
|
||||
given temperature-based bits. Now the bits indicate:
|
||||
* REQ_META | REQ_FUA |
|
||||
* 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
* Cold | Warm | Hot | Cold | Warm | Hot |
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/iostat_period_ms
|
||||
Date: April 2020
|
||||
Contact: "Daeho Jeong" <daehojeong@google.com>
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* fs/f2fs/acl.h
|
||||
*
|
||||
|
@ -895,8 +895,8 @@ int f2fs_get_valid_checkpoint(struct f2fs_sb_info *sbi)
|
||||
int i;
|
||||
int err;
|
||||
|
||||
sbi->ckpt = f2fs_kzalloc(sbi, array_size(blk_size, cp_blks),
|
||||
GFP_KERNEL);
|
||||
sbi->ckpt = f2fs_kvzalloc(sbi, array_size(blk_size, cp_blks),
|
||||
GFP_KERNEL);
|
||||
if (!sbi->ckpt)
|
||||
return -ENOMEM;
|
||||
/*
|
||||
@ -1166,10 +1166,12 @@ static int block_operations(struct f2fs_sb_info *sbi)
|
||||
.nr_to_write = LONG_MAX,
|
||||
.for_reclaim = 0,
|
||||
};
|
||||
struct blk_plug plug;
|
||||
int err = 0, cnt = 0;
|
||||
|
||||
blk_start_plug(&plug);
|
||||
/*
|
||||
* Let's flush inline_data in dirty node pages.
|
||||
*/
|
||||
f2fs_flush_inline_data(sbi);
|
||||
|
||||
retry_flush_quotas:
|
||||
f2fs_lock_all(sbi);
|
||||
@ -1198,7 +1200,7 @@ retry_flush_dents:
|
||||
f2fs_unlock_all(sbi);
|
||||
err = f2fs_sync_dirty_inodes(sbi, DIR_INODE);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
cond_resched();
|
||||
goto retry_flush_quotas;
|
||||
}
|
||||
@ -1214,7 +1216,7 @@ retry_flush_dents:
|
||||
f2fs_unlock_all(sbi);
|
||||
err = f2fs_sync_inode_meta(sbi);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
cond_resched();
|
||||
goto retry_flush_quotas;
|
||||
}
|
||||
@ -1230,7 +1232,7 @@ retry_flush_nodes:
|
||||
if (err) {
|
||||
up_write(&sbi->node_change);
|
||||
f2fs_unlock_all(sbi);
|
||||
goto out;
|
||||
return err;
|
||||
}
|
||||
cond_resched();
|
||||
goto retry_flush_nodes;
|
||||
@ -1242,8 +1244,6 @@ retry_flush_nodes:
|
||||
*/
|
||||
__prepare_cp_block(sbi);
|
||||
up_write(&sbi->node_change);
|
||||
out:
|
||||
blk_finish_plug(&plug);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1562,7 +1562,8 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
return 0;
|
||||
f2fs_warn(sbi, "Start checkpoint disabled!");
|
||||
}
|
||||
mutex_lock(&sbi->cp_mutex);
|
||||
if (cpc->reason != CP_RESIZE)
|
||||
mutex_lock(&sbi->cp_mutex);
|
||||
|
||||
if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) &&
|
||||
((cpc->reason & CP_FASTBOOT) || (cpc->reason & CP_SYNC) ||
|
||||
@ -1631,7 +1632,8 @@ stop:
|
||||
f2fs_update_time(sbi, CP_TIME);
|
||||
trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish checkpoint");
|
||||
out:
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
if (cpc->reason != CP_RESIZE)
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/lzo.h>
|
||||
#include <linux/lz4.h>
|
||||
#include <linux/zstd.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
@ -70,15 +71,6 @@ static void f2fs_set_compressed_page(struct page *page,
|
||||
page->mapping = inode->i_mapping;
|
||||
}
|
||||
|
||||
static void f2fs_put_compressed_page(struct page *page)
|
||||
{
|
||||
set_page_private(page, (unsigned long)NULL);
|
||||
ClearPagePrivate(page);
|
||||
page->mapping = NULL;
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
static void f2fs_drop_rpages(struct compress_ctx *cc, int len, bool unlock)
|
||||
{
|
||||
int i;
|
||||
@ -103,8 +95,7 @@ static void f2fs_unlock_rpages(struct compress_ctx *cc, int len)
|
||||
f2fs_drop_rpages(cc, len, true);
|
||||
}
|
||||
|
||||
static void f2fs_put_rpages_mapping(struct compress_ctx *cc,
|
||||
struct address_space *mapping,
|
||||
static void f2fs_put_rpages_mapping(struct address_space *mapping,
|
||||
pgoff_t start, int len)
|
||||
{
|
||||
int i;
|
||||
@ -241,7 +232,12 @@ static int lz4_init_compress_ctx(struct compress_ctx *cc)
|
||||
if (!cc->private)
|
||||
return -ENOMEM;
|
||||
|
||||
cc->clen = LZ4_compressBound(PAGE_SIZE << cc->log_cluster_size);
|
||||
/*
|
||||
* we do not change cc->clen to LZ4_compressBound(inputsize) to
|
||||
* adapt worst compress case, because lz4 compressor can handle
|
||||
* output budget properly.
|
||||
*/
|
||||
cc->clen = cc->rlen - PAGE_SIZE - COMPRESS_HEADER_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -257,11 +253,9 @@ static int lz4_compress_pages(struct compress_ctx *cc)
|
||||
|
||||
len = LZ4_compress_default(cc->rbuf, cc->cbuf->cdata, cc->rlen,
|
||||
cc->clen, cc->private);
|
||||
if (!len) {
|
||||
printk_ratelimited("%sF2FS-fs (%s): lz4 compress failed\n",
|
||||
KERN_ERR, F2FS_I_SB(cc->inode)->sb->s_id);
|
||||
return -EIO;
|
||||
}
|
||||
if (!len)
|
||||
return -EAGAIN;
|
||||
|
||||
cc->clen = len;
|
||||
return 0;
|
||||
}
|
||||
@ -371,6 +365,13 @@ static int zstd_compress_pages(struct compress_ctx *cc)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* there is compressed data remained in intermediate buffer due to
|
||||
* no more space in cbuf.cdata
|
||||
*/
|
||||
if (ret)
|
||||
return -EAGAIN;
|
||||
|
||||
cc->clen = outbuf.pos;
|
||||
return 0;
|
||||
}
|
||||
@ -481,17 +482,47 @@ bool f2fs_is_compress_backend_ready(struct inode *inode)
|
||||
return f2fs_cops[F2FS_I(inode)->i_compress_algorithm];
|
||||
}
|
||||
|
||||
static struct page *f2fs_grab_page(void)
|
||||
static mempool_t *compress_page_pool = NULL;
|
||||
static int num_compress_pages = 512;
|
||||
module_param(num_compress_pages, uint, 0444);
|
||||
MODULE_PARM_DESC(num_compress_pages,
|
||||
"Number of intermediate compress pages to preallocate");
|
||||
|
||||
int f2fs_init_compress_mempool(void)
|
||||
{
|
||||
compress_page_pool = mempool_create_page_pool(num_compress_pages, 0);
|
||||
if (!compress_page_pool)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void f2fs_destroy_compress_mempool(void)
|
||||
{
|
||||
mempool_destroy(compress_page_pool);
|
||||
}
|
||||
|
||||
static struct page *f2fs_compress_alloc_page(void)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
page = alloc_page(GFP_NOFS);
|
||||
if (!page)
|
||||
return NULL;
|
||||
page = mempool_alloc(compress_page_pool, GFP_NOFS);
|
||||
lock_page(page);
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
static void f2fs_compress_free_page(struct page *page)
|
||||
{
|
||||
if (!page)
|
||||
return;
|
||||
set_page_private(page, (unsigned long)NULL);
|
||||
ClearPagePrivate(page);
|
||||
page->mapping = NULL;
|
||||
unlock_page(page);
|
||||
mempool_free(page, compress_page_pool);
|
||||
}
|
||||
|
||||
static int f2fs_compress_pages(struct compress_ctx *cc)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
|
||||
@ -521,7 +552,7 @@ static int f2fs_compress_pages(struct compress_ctx *cc)
|
||||
}
|
||||
|
||||
for (i = 0; i < cc->nr_cpages; i++) {
|
||||
cc->cpages[i] = f2fs_grab_page();
|
||||
cc->cpages[i] = f2fs_compress_alloc_page();
|
||||
if (!cc->cpages[i]) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_cpages;
|
||||
@ -566,7 +597,7 @@ static int f2fs_compress_pages(struct compress_ctx *cc)
|
||||
vunmap(cc->rbuf);
|
||||
|
||||
for (i = nr_cpages; i < cc->nr_cpages; i++) {
|
||||
f2fs_put_compressed_page(cc->cpages[i]);
|
||||
f2fs_compress_free_page(cc->cpages[i]);
|
||||
cc->cpages[i] = NULL;
|
||||
}
|
||||
|
||||
@ -586,7 +617,7 @@ out_vunmap_rbuf:
|
||||
out_free_cpages:
|
||||
for (i = 0; i < cc->nr_cpages; i++) {
|
||||
if (cc->cpages[i])
|
||||
f2fs_put_compressed_page(cc->cpages[i]);
|
||||
f2fs_compress_free_page(cc->cpages[i]);
|
||||
}
|
||||
kfree(cc->cpages);
|
||||
cc->cpages = NULL;
|
||||
@ -793,6 +824,8 @@ static bool cluster_may_compress(struct compress_ctx *cc)
|
||||
return false;
|
||||
if (!f2fs_cluster_is_full(cc))
|
||||
return false;
|
||||
if (unlikely(f2fs_cp_error(F2FS_I_SB(cc->inode))))
|
||||
return false;
|
||||
return __cluster_may_compress(cc);
|
||||
}
|
||||
|
||||
@ -884,7 +917,7 @@ retry:
|
||||
|
||||
if (!PageUptodate(page)) {
|
||||
f2fs_unlock_rpages(cc, i + 1);
|
||||
f2fs_put_rpages_mapping(cc, mapping, start_idx,
|
||||
f2fs_put_rpages_mapping(mapping, start_idx,
|
||||
cc->cluster_size);
|
||||
f2fs_destroy_compress_ctx(cc);
|
||||
goto retry;
|
||||
@ -919,7 +952,7 @@ retry:
|
||||
unlock_pages:
|
||||
f2fs_unlock_rpages(cc, i);
|
||||
release_pages:
|
||||
f2fs_put_rpages_mapping(cc, mapping, start_idx, i);
|
||||
f2fs_put_rpages_mapping(mapping, start_idx, i);
|
||||
f2fs_destroy_compress_ctx(cc);
|
||||
return ret;
|
||||
}
|
||||
@ -959,6 +992,55 @@ bool f2fs_compress_write_end(struct inode *inode, void *fsdata,
|
||||
return first_index;
|
||||
}
|
||||
|
||||
int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock)
|
||||
{
|
||||
void *fsdata = NULL;
|
||||
struct page *pagep;
|
||||
int log_cluster_size = F2FS_I(inode)->i_log_cluster_size;
|
||||
pgoff_t start_idx = from >> (PAGE_SHIFT + log_cluster_size) <<
|
||||
log_cluster_size;
|
||||
int err;
|
||||
|
||||
err = f2fs_is_compressed_cluster(inode, start_idx);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* truncate normal cluster */
|
||||
if (!err)
|
||||
return f2fs_do_truncate_blocks(inode, from, lock);
|
||||
|
||||
/* truncate compressed cluster */
|
||||
err = f2fs_prepare_compress_overwrite(inode, &pagep,
|
||||
start_idx, &fsdata);
|
||||
|
||||
/* should not be a normal cluster */
|
||||
f2fs_bug_on(F2FS_I_SB(inode), err == 0);
|
||||
|
||||
if (err <= 0)
|
||||
return err;
|
||||
|
||||
if (err > 0) {
|
||||
struct page **rpages = fsdata;
|
||||
int cluster_size = F2FS_I(inode)->i_cluster_size;
|
||||
int i;
|
||||
|
||||
for (i = cluster_size - 1; i >= 0; i--) {
|
||||
loff_t start = rpages[i]->index << PAGE_SHIFT;
|
||||
|
||||
if (from <= start) {
|
||||
zero_user_segment(rpages[i], 0, PAGE_SIZE);
|
||||
} else {
|
||||
zero_user_segment(rpages[i], from - start,
|
||||
PAGE_SIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
f2fs_compress_write_end(inode, fsdata, start_idx, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int f2fs_write_compressed_pages(struct compress_ctx *cc,
|
||||
int *submitted,
|
||||
struct writeback_control *wbc,
|
||||
@ -1140,7 +1222,7 @@ void f2fs_compress_write_end_io(struct bio *bio, struct page *page)
|
||||
if (unlikely(bio->bi_status))
|
||||
mapping_set_error(cic->inode->i_mapping, -EIO);
|
||||
|
||||
f2fs_put_compressed_page(page);
|
||||
f2fs_compress_free_page(page);
|
||||
|
||||
dec_page_count(sbi, F2FS_WB_DATA);
|
||||
|
||||
@ -1301,7 +1383,7 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
|
||||
for (i = 0; i < dic->nr_cpages; i++) {
|
||||
struct page *page;
|
||||
|
||||
page = f2fs_grab_page();
|
||||
page = f2fs_compress_alloc_page();
|
||||
if (!page)
|
||||
goto out_free;
|
||||
|
||||
@ -1321,7 +1403,7 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
|
||||
continue;
|
||||
}
|
||||
|
||||
dic->tpages[i] = f2fs_grab_page();
|
||||
dic->tpages[i] = f2fs_compress_alloc_page();
|
||||
if (!dic->tpages[i])
|
||||
goto out_free;
|
||||
}
|
||||
@ -1343,8 +1425,7 @@ void f2fs_free_dic(struct decompress_io_ctx *dic)
|
||||
continue;
|
||||
if (!dic->tpages[i])
|
||||
continue;
|
||||
unlock_page(dic->tpages[i]);
|
||||
put_page(dic->tpages[i]);
|
||||
f2fs_compress_free_page(dic->tpages[i]);
|
||||
}
|
||||
kfree(dic->tpages);
|
||||
}
|
||||
@ -1353,7 +1434,7 @@ void f2fs_free_dic(struct decompress_io_ctx *dic)
|
||||
for (i = 0; i < dic->nr_cpages; i++) {
|
||||
if (!dic->cpages[i])
|
||||
continue;
|
||||
f2fs_put_compressed_page(dic->cpages[i]);
|
||||
f2fs_compress_free_page(dic->cpages[i]);
|
||||
}
|
||||
kfree(dic->cpages);
|
||||
}
|
||||
|
148
fs/f2fs/data.c
148
fs/f2fs/data.c
@ -116,7 +116,8 @@ static enum count_type __read_io_type(struct page *page)
|
||||
/* postprocessing steps for read bios */
|
||||
enum bio_post_read_step {
|
||||
STEP_DECRYPT,
|
||||
STEP_DECOMPRESS,
|
||||
STEP_DECOMPRESS_NOWQ, /* handle normal cluster data inplace */
|
||||
STEP_DECOMPRESS, /* handle compressed cluster data in workqueue */
|
||||
STEP_VERITY,
|
||||
};
|
||||
|
||||
@ -580,22 +581,28 @@ void f2fs_submit_bio(struct f2fs_sb_info *sbi,
|
||||
__submit_bio(sbi, bio, type);
|
||||
}
|
||||
|
||||
static void __attach_data_io_flag(struct f2fs_io_info *fio)
|
||||
static void __attach_io_flag(struct f2fs_io_info *fio)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = fio->sbi;
|
||||
unsigned int temp_mask = (1 << NR_TEMP_TYPE) - 1;
|
||||
unsigned int fua_flag = sbi->data_io_flag & temp_mask;
|
||||
unsigned int meta_flag = (sbi->data_io_flag >> NR_TEMP_TYPE) &
|
||||
temp_mask;
|
||||
unsigned int io_flag, fua_flag, meta_flag;
|
||||
|
||||
if (fio->type == DATA)
|
||||
io_flag = sbi->data_io_flag;
|
||||
else if (fio->type == NODE)
|
||||
io_flag = sbi->node_io_flag;
|
||||
else
|
||||
return;
|
||||
|
||||
fua_flag = io_flag & temp_mask;
|
||||
meta_flag = (io_flag >> NR_TEMP_TYPE) & temp_mask;
|
||||
|
||||
/*
|
||||
* data io flag bits per temp:
|
||||
* data/node io flag bits per temp:
|
||||
* REQ_META | REQ_FUA |
|
||||
* 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
* Cold | Warm | Hot | Cold | Warm | Hot |
|
||||
*/
|
||||
if (fio->type != DATA)
|
||||
return;
|
||||
|
||||
if ((1 << fio->temp) & meta_flag)
|
||||
fio->op_flags |= REQ_META;
|
||||
if ((1 << fio->temp) & fua_flag)
|
||||
@ -609,7 +616,7 @@ static void __submit_merged_bio(struct f2fs_bio_info *io)
|
||||
if (!io->bio)
|
||||
return;
|
||||
|
||||
__attach_data_io_flag(fio);
|
||||
__attach_io_flag(fio);
|
||||
bio_set_op_attrs(io->bio, fio->op, fio->op_flags);
|
||||
|
||||
if (is_read_io(fio->op))
|
||||
@ -754,6 +761,7 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio)
|
||||
if (fio->io_wbc && !is_read_io(fio->op))
|
||||
wbc_account_io(fio->io_wbc, page, PAGE_SIZE);
|
||||
|
||||
__attach_io_flag(fio);
|
||||
bio_set_op_attrs(bio, fio->op, fio->op_flags);
|
||||
|
||||
inc_page_count(fio->sbi, is_read_io(fio->op) ?
|
||||
@ -948,6 +956,7 @@ alloc_new:
|
||||
f2fs_set_bio_crypt_ctx(bio, fio->page->mapping->host,
|
||||
fio->page->index, fio,
|
||||
GFP_NOIO);
|
||||
__attach_io_flag(fio);
|
||||
bio_set_op_attrs(bio, fio->op, fio->op_flags);
|
||||
|
||||
add_bio_entry(fio->sbi, bio, page, fio->temp);
|
||||
@ -1077,7 +1086,7 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
|
||||
if (fscrypt_inode_uses_fs_layer_crypto(inode))
|
||||
post_read_steps |= 1 << STEP_DECRYPT;
|
||||
if (f2fs_compressed_file(inode))
|
||||
post_read_steps |= 1 << STEP_DECOMPRESS;
|
||||
post_read_steps |= 1 << STEP_DECOMPRESS_NOWQ;
|
||||
if (f2fs_need_verity(inode, first_idx))
|
||||
post_read_steps |= 1 << STEP_VERITY;
|
||||
|
||||
@ -1919,6 +1928,25 @@ static int f2fs_xattr_fiemap(struct inode *inode,
|
||||
return (err < 0 ? err : 0);
|
||||
}
|
||||
|
||||
static loff_t max_inode_blocks(struct inode *inode)
|
||||
{
|
||||
loff_t result = ADDRS_PER_INODE(inode);
|
||||
loff_t leaf_count = ADDRS_PER_BLOCK(inode);
|
||||
|
||||
/* two direct node blocks */
|
||||
result += (leaf_count * 2);
|
||||
|
||||
/* two indirect node blocks */
|
||||
leaf_count *= NIDS_PER_BLOCK;
|
||||
result += (leaf_count * 2);
|
||||
|
||||
/* one double indirect node block */
|
||||
leaf_count *= NIDS_PER_BLOCK;
|
||||
result += leaf_count;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
u64 start, u64 len)
|
||||
{
|
||||
@ -1928,6 +1956,8 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
u64 logical = 0, phys = 0, size = 0;
|
||||
u32 flags = 0;
|
||||
int ret = 0;
|
||||
bool compr_cluster = false;
|
||||
unsigned int cluster_size = F2FS_I(inode)->i_cluster_size;
|
||||
|
||||
if (fieinfo->fi_flags & FIEMAP_FLAG_CACHE) {
|
||||
ret = f2fs_precache_extents(inode);
|
||||
@ -1962,6 +1992,9 @@ next:
|
||||
memset(&map_bh, 0, sizeof(struct buffer_head));
|
||||
map_bh.b_size = len;
|
||||
|
||||
if (compr_cluster)
|
||||
map_bh.b_size = blk_to_logical(inode, cluster_size - 1);
|
||||
|
||||
ret = get_data_block(inode, start_blk, &map_bh, 0,
|
||||
F2FS_GET_BLOCK_FIEMAP, &next_pgofs);
|
||||
if (ret)
|
||||
@ -1972,7 +2005,7 @@ next:
|
||||
start_blk = next_pgofs;
|
||||
|
||||
if (blk_to_logical(inode, start_blk) < blk_to_logical(inode,
|
||||
F2FS_I_SB(inode)->max_file_blocks))
|
||||
max_inode_blocks(inode)))
|
||||
goto prep_next;
|
||||
|
||||
flags |= FIEMAP_EXTENT_LAST;
|
||||
@ -1984,11 +2017,38 @@ next:
|
||||
|
||||
ret = fiemap_fill_next_extent(fieinfo, logical,
|
||||
phys, size, flags);
|
||||
if (ret)
|
||||
goto out;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
if (start_blk > last_blk || ret)
|
||||
if (start_blk > last_blk)
|
||||
goto out;
|
||||
|
||||
if (compr_cluster) {
|
||||
compr_cluster = false;
|
||||
|
||||
|
||||
logical = blk_to_logical(inode, start_blk - 1);
|
||||
phys = blk_to_logical(inode, map_bh.b_blocknr);
|
||||
size = blk_to_logical(inode, cluster_size);
|
||||
|
||||
flags |= FIEMAP_EXTENT_ENCODED;
|
||||
|
||||
start_blk += cluster_size - 1;
|
||||
|
||||
if (start_blk > last_blk)
|
||||
goto out;
|
||||
|
||||
goto prep_next;
|
||||
}
|
||||
|
||||
if (map_bh.b_blocknr == COMPRESS_ADDR) {
|
||||
compr_cluster = true;
|
||||
start_blk++;
|
||||
goto prep_next;
|
||||
}
|
||||
|
||||
logical = blk_to_logical(inode, start_blk);
|
||||
phys = blk_to_logical(inode, map_bh.b_blocknr);
|
||||
size = map_bh.b_size;
|
||||
@ -2226,6 +2286,7 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
|
||||
for (i = 0; i < dic->nr_cpages; i++) {
|
||||
struct page *page = dic->cpages[i];
|
||||
block_t blkaddr;
|
||||
struct bio_post_read_ctx *ctx;
|
||||
|
||||
blkaddr = data_blkaddr(dn.inode, dn.node_page,
|
||||
dn.ofs_in_node + i + 1);
|
||||
@ -2244,16 +2305,16 @@ submit_and_realloc:
|
||||
page->index, for_write);
|
||||
if (IS_ERR(bio)) {
|
||||
ret = PTR_ERR(bio);
|
||||
bio = NULL;
|
||||
dic->failed = true;
|
||||
if (refcount_sub_and_test(dic->nr_cpages - i,
|
||||
&dic->ref))
|
||||
&dic->ref)) {
|
||||
f2fs_decompress_end_io(dic->rpages,
|
||||
cc->cluster_size, true,
|
||||
false);
|
||||
f2fs_free_dic(dic);
|
||||
f2fs_free_dic(dic);
|
||||
}
|
||||
f2fs_put_dnode(&dn);
|
||||
*bio_ret = bio;
|
||||
*bio_ret = NULL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@ -2263,8 +2324,14 @@ submit_and_realloc:
|
||||
if (bio_add_page(bio, page, blocksize, 0) < blocksize)
|
||||
goto submit_and_realloc;
|
||||
|
||||
/* tag STEP_DECOMPRESS to handle IO in wq */
|
||||
ctx = bio->bi_private;
|
||||
if (!(ctx->enabled_steps & (1 << STEP_DECOMPRESS)))
|
||||
ctx->enabled_steps |= 1 << STEP_DECOMPRESS;
|
||||
|
||||
inc_page_count(sbi, F2FS_RD_DATA);
|
||||
f2fs_update_iostat(sbi, FS_DATA_READ_IO, F2FS_BLKSIZE);
|
||||
f2fs_update_iostat(sbi, FS_CDATA_READ_IO, F2FS_BLKSIZE);
|
||||
ClearPageError(page);
|
||||
*last_block_in_bio = blkaddr;
|
||||
}
|
||||
@ -2894,7 +2961,6 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
|
||||
pgoff_t index;
|
||||
pgoff_t end; /* Inclusive */
|
||||
pgoff_t done_index;
|
||||
int cycled;
|
||||
int range_whole = 0;
|
||||
int tag;
|
||||
int nwritten = 0;
|
||||
@ -2912,17 +2978,12 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
|
||||
if (wbc->range_cyclic) {
|
||||
writeback_index = mapping->writeback_index; /* prev offset */
|
||||
index = writeback_index;
|
||||
if (index == 0)
|
||||
cycled = 1;
|
||||
else
|
||||
cycled = 0;
|
||||
end = -1;
|
||||
} else {
|
||||
index = wbc->range_start >> PAGE_SHIFT;
|
||||
end = wbc->range_end >> PAGE_SHIFT;
|
||||
if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX)
|
||||
range_whole = 1;
|
||||
cycled = 1; /* ignore range_cyclic tests */
|
||||
}
|
||||
if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages)
|
||||
tag = PAGECACHE_TAG_TOWRITE;
|
||||
@ -3087,12 +3148,13 @@ next:
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if ((!cycled && !done) || retry) {
|
||||
cycled = 1;
|
||||
if (retry) {
|
||||
index = 0;
|
||||
end = writeback_index - 1;
|
||||
end = -1;
|
||||
goto retry;
|
||||
}
|
||||
if (wbc->range_cyclic && !done)
|
||||
done_index = 0;
|
||||
if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0))
|
||||
mapping->writeback_index = done_index;
|
||||
|
||||
@ -3746,6 +3808,37 @@ static int f2fs_set_data_page_dirty(struct page *page)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static sector_t f2fs_bmap_compress(struct inode *inode, sector_t block)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
struct dnode_of_data dn;
|
||||
sector_t start_idx, blknr = 0;
|
||||
int ret;
|
||||
|
||||
start_idx = round_down(block, F2FS_I(inode)->i_cluster_size);
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
ret = f2fs_get_dnode_of_data(&dn, start_idx, LOOKUP_NODE);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
if (dn.data_blkaddr != COMPRESS_ADDR) {
|
||||
dn.ofs_in_node += block - start_idx;
|
||||
blknr = f2fs_data_blkaddr(&dn);
|
||||
if (!__is_valid_data_blkaddr(blknr))
|
||||
blknr = 0;
|
||||
}
|
||||
|
||||
f2fs_put_dnode(&dn);
|
||||
|
||||
return blknr;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static sector_t f2fs_bmap(struct address_space *mapping, sector_t block)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
@ -3757,6 +3850,9 @@ static sector_t f2fs_bmap(struct address_space *mapping, sector_t block)
|
||||
if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY))
|
||||
filemap_write_and_wait(mapping);
|
||||
|
||||
if (f2fs_compressed_file(inode))
|
||||
return f2fs_bmap_compress(inode, block);
|
||||
|
||||
return generic_block_bmap(mapping, block, get_data_block_bmap);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* fs/f2fs/f2fs.h
|
||||
*
|
||||
@ -198,6 +198,7 @@ enum {
|
||||
#define CP_DISCARD 0x00000010
|
||||
#define CP_TRIMMED 0x00000020
|
||||
#define CP_PAUSE 0x00000040
|
||||
#define CP_RESIZE 0x00000080
|
||||
|
||||
#define MAX_DISCARD_BLOCKS(sbi) BLKS_PER_SEC(sbi)
|
||||
#define DEF_MAX_DISCARD_REQUEST 8 /* issue 8 discards per round */
|
||||
@ -432,6 +433,10 @@ static inline bool __has_cursum_space(struct f2fs_journal *journal,
|
||||
#define F2FS_IOC_PRECACHE_EXTENTS _IO(F2FS_IOCTL_MAGIC, 15)
|
||||
#define F2FS_IOC_RESIZE_FS _IOW(F2FS_IOCTL_MAGIC, 16, __u64)
|
||||
#define F2FS_IOC_GET_COMPRESS_BLOCKS _IOR(F2FS_IOCTL_MAGIC, 17, __u64)
|
||||
#define F2FS_IOC_RELEASE_COMPRESS_BLOCKS \
|
||||
_IOR(F2FS_IOCTL_MAGIC, 18, __u64)
|
||||
#define F2FS_IOC_RESERVE_COMPRESS_BLOCKS \
|
||||
_IOR(F2FS_IOCTL_MAGIC, 19, __u64)
|
||||
|
||||
#define F2FS_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY
|
||||
#define F2FS_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY
|
||||
@ -1147,6 +1152,8 @@ enum iostat_type {
|
||||
APP_READ_IO, /* app read IOs */
|
||||
APP_MAPPED_READ_IO, /* app mapped read IOs */
|
||||
FS_DATA_READ_IO, /* data read IOs */
|
||||
FS_GDATA_READ_IO, /* data read IOs from background gc */
|
||||
FS_CDATA_READ_IO, /* compressed data read IOs */
|
||||
FS_NODE_READ_IO, /* node read IOs */
|
||||
FS_META_READ_IO, /* meta read IOs */
|
||||
|
||||
@ -1465,7 +1472,6 @@ struct f2fs_sb_info {
|
||||
unsigned int segs_per_sec; /* segments per section */
|
||||
unsigned int secs_per_zone; /* sections per zone */
|
||||
unsigned int total_sections; /* total section count */
|
||||
struct mutex resize_mutex; /* for resize exclusion */
|
||||
unsigned int total_node_count; /* total node block count */
|
||||
unsigned int total_valid_node_count; /* valid node block count */
|
||||
loff_t max_file_blocks; /* max block index of file */
|
||||
@ -1559,6 +1565,7 @@ struct f2fs_sb_info {
|
||||
|
||||
/* to attach REQ_META|REQ_FUA flags */
|
||||
unsigned int data_io_flag;
|
||||
unsigned int node_io_flag;
|
||||
|
||||
/* For sysfs suppport */
|
||||
struct kobject s_kobj;
|
||||
@ -2989,18 +2996,12 @@ static inline bool f2fs_may_extent_tree(struct inode *inode)
|
||||
static inline void *f2fs_kmalloc(struct f2fs_sb_info *sbi,
|
||||
size_t size, gfp_t flags)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
if (time_to_inject(sbi, FAULT_KMALLOC)) {
|
||||
f2fs_show_injection_info(sbi, FAULT_KMALLOC);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = kmalloc(size, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return kvmalloc(size, flags);
|
||||
return kmalloc(size, flags);
|
||||
}
|
||||
|
||||
static inline void *f2fs_kzalloc(struct f2fs_sb_info *sbi,
|
||||
@ -3141,6 +3142,7 @@ static inline void f2fs_clear_page_private(struct page *page)
|
||||
*/
|
||||
int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync);
|
||||
void f2fs_truncate_data_blocks(struct dnode_of_data *dn);
|
||||
int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock);
|
||||
int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock);
|
||||
int f2fs_truncate(struct inode *inode);
|
||||
int f2fs_getattr(const struct path *path, struct kstat *stat,
|
||||
@ -3278,6 +3280,7 @@ void f2fs_ra_node_page(struct f2fs_sb_info *sbi, nid_t nid);
|
||||
struct page *f2fs_get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid);
|
||||
struct page *f2fs_get_node_page_ra(struct page *parent, int start);
|
||||
int f2fs_move_node_page(struct page *node_page, int gc_type);
|
||||
int f2fs_flush_inline_data(struct f2fs_sb_info *sbi);
|
||||
int f2fs_fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
struct writeback_control *wbc, bool atomic,
|
||||
unsigned int *seq_id);
|
||||
@ -3722,7 +3725,7 @@ static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; }
|
||||
static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { }
|
||||
static inline void __init f2fs_create_root_stats(void) { }
|
||||
static inline void f2fs_destroy_root_stats(void) { }
|
||||
static inline void update_sit_info(struct f2fs_sb_info *sbi) {}
|
||||
static inline void f2fs_update_sit_info(struct f2fs_sb_info *sbi) {}
|
||||
#endif
|
||||
|
||||
extern const struct file_operations f2fs_dir_operations;
|
||||
@ -3855,8 +3858,11 @@ int f2fs_prepare_compress_overwrite(struct inode *inode,
|
||||
struct page **pagep, pgoff_t index, void **fsdata);
|
||||
bool f2fs_compress_write_end(struct inode *inode, void *fsdata,
|
||||
pgoff_t index, unsigned copied);
|
||||
int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock);
|
||||
void f2fs_compress_write_end_io(struct bio *bio, struct page *page);
|
||||
bool f2fs_is_compress_backend_ready(struct inode *inode);
|
||||
int f2fs_init_compress_mempool(void);
|
||||
void f2fs_destroy_compress_mempool(void);
|
||||
void f2fs_decompress_pages(struct bio *bio, struct page *page, bool verity);
|
||||
bool f2fs_cluster_is_empty(struct compress_ctx *cc);
|
||||
bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index);
|
||||
@ -3890,6 +3896,8 @@ static inline struct page *f2fs_compress_control_page(struct page *page)
|
||||
WARN_ON_ONCE(1);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
static inline int f2fs_init_compress_mempool(void) { return 0; }
|
||||
static inline void f2fs_destroy_compress_mempool(void) { }
|
||||
#endif
|
||||
|
||||
static inline void set_compress_context(struct inode *inode)
|
||||
@ -4036,6 +4044,10 @@ static inline void f2fs_i_compr_blocks_update(struct inode *inode,
|
||||
{
|
||||
int diff = F2FS_I(inode)->i_cluster_size - blocks;
|
||||
|
||||
/* don't update i_compr_blocks if saved blocks were released */
|
||||
if (!add && !F2FS_I(inode)->i_compr_blocks)
|
||||
return;
|
||||
|
||||
if (add) {
|
||||
F2FS_I(inode)->i_compr_blocks += diff;
|
||||
stat_add_compr_blocks(inode, diff);
|
||||
@ -4079,8 +4091,6 @@ static inline bool f2fs_force_buffered_io(struct inode *inode,
|
||||
return true;
|
||||
if (f2fs_is_multi_device(sbi))
|
||||
return true;
|
||||
if (f2fs_compressed_file(inode))
|
||||
return true;
|
||||
/*
|
||||
* for blkzoned device, fallback direct IO to buffered IO, so
|
||||
* all IOs can be serialized by log-structured write.
|
||||
|
389
fs/f2fs/file.c
389
fs/f2fs/file.c
@ -169,9 +169,11 @@ static int get_parent_ino(struct inode *inode, nid_t *pino)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
|
||||
inode = igrab(inode);
|
||||
dentry = d_find_any_alias(inode);
|
||||
iput(inode);
|
||||
/*
|
||||
* Make sure to get the non-deleted alias. The alias associated with
|
||||
* the open file descriptor being fsync()'ed may be deleted already.
|
||||
*/
|
||||
dentry = d_find_alias(inode);
|
||||
if (!dentry)
|
||||
return 0;
|
||||
|
||||
@ -572,6 +574,7 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
|
||||
bool compressed_cluster = false;
|
||||
int cluster_index = 0, valid_blocks = 0;
|
||||
int cluster_size = F2FS_I(dn->inode)->i_cluster_size;
|
||||
bool released = !F2FS_I(dn->inode)->i_compr_blocks;
|
||||
|
||||
if (IS_INODE(dn->node_page) && f2fs_has_extra_attr(dn->inode))
|
||||
base = get_extra_isize(dn->inode);
|
||||
@ -610,7 +613,9 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
|
||||
clear_inode_flag(dn->inode, FI_FIRST_BLOCK_WRITTEN);
|
||||
|
||||
f2fs_invalidate_blocks(sbi, blkaddr);
|
||||
nr_free++;
|
||||
|
||||
if (!released || blkaddr != COMPRESS_ADDR)
|
||||
nr_free++;
|
||||
}
|
||||
|
||||
if (compressed_cluster)
|
||||
@ -658,9 +663,6 @@ static int truncate_partial_data_page(struct inode *inode, u64 from,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (f2fs_compressed_file(inode))
|
||||
return 0;
|
||||
|
||||
page = f2fs_get_lock_data_page(inode, index, true);
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page) == -ENOENT ? 0 : PTR_ERR(page);
|
||||
@ -676,7 +678,7 @@ truncate_out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_truncate_blocks(struct inode *inode, u64 from, bool lock)
|
||||
int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct dnode_of_data dn;
|
||||
@ -744,23 +746,28 @@ free_partial:
|
||||
int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock)
|
||||
{
|
||||
u64 free_from = from;
|
||||
int err;
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
/*
|
||||
* for compressed file, only support cluster size
|
||||
* aligned truncation.
|
||||
*/
|
||||
if (f2fs_compressed_file(inode)) {
|
||||
size_t cluster_shift = PAGE_SHIFT +
|
||||
F2FS_I(inode)->i_log_cluster_size;
|
||||
size_t cluster_mask = (1 << cluster_shift) - 1;
|
||||
if (f2fs_compressed_file(inode))
|
||||
free_from = round_up(from,
|
||||
F2FS_I(inode)->i_cluster_size << PAGE_SHIFT);
|
||||
#endif
|
||||
|
||||
free_from = from >> cluster_shift;
|
||||
if (from & cluster_mask)
|
||||
free_from++;
|
||||
free_from <<= cluster_shift;
|
||||
}
|
||||
err = f2fs_do_truncate_blocks(inode, free_from, lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return do_truncate_blocks(inode, free_from, lock);
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
if (from != free_from)
|
||||
err = f2fs_truncate_partial_cluster(inode, from, lock);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int f2fs_truncate(struct inode *inode)
|
||||
@ -986,9 +993,7 @@ const struct inode_operations f2fs_file_inode_operations = {
|
||||
.setattr = f2fs_setattr,
|
||||
.get_acl = f2fs_get_acl,
|
||||
.set_acl = f2fs_set_acl,
|
||||
#ifdef CONFIG_F2FS_FS_XATTR
|
||||
.listxattr = f2fs_listxattr,
|
||||
#endif
|
||||
.fiemap = f2fs_fiemap,
|
||||
};
|
||||
|
||||
@ -1667,7 +1672,11 @@ next_alloc:
|
||||
|
||||
down_write(&sbi->pin_sem);
|
||||
map.m_seg_type = CURSEG_COLD_DATA_PINNED;
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
f2fs_allocate_new_segments(sbi, CURSEG_COLD_DATA);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
err = f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_DIO);
|
||||
up_write(&sbi->pin_sem);
|
||||
|
||||
@ -2237,8 +2246,15 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg)
|
||||
|
||||
if (in != F2FS_GOING_DOWN_FULLSYNC) {
|
||||
ret = mnt_want_write_file(filp);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
if (ret == -EROFS) {
|
||||
ret = 0;
|
||||
f2fs_stop_checkpoint(sbi, false);
|
||||
set_sbi_flag(sbi, SBI_IS_SHUTDOWN);
|
||||
trace_f2fs_shutdown(sbi, in, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
switch (in) {
|
||||
@ -3319,7 +3335,6 @@ static int f2fs_ioc_resize_fs(struct file *filp, unsigned long arg)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(file_inode(filp));
|
||||
__u64 block_count;
|
||||
int ret;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
@ -3331,9 +3346,7 @@ static int f2fs_ioc_resize_fs(struct file *filp, unsigned long arg)
|
||||
sizeof(block_count)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = f2fs_resize_fs(sbi, block_count);
|
||||
|
||||
return ret;
|
||||
return f2fs_resize_fs(sbi, block_count);
|
||||
}
|
||||
|
||||
static int f2fs_ioc_enable_verity(struct file *filp, unsigned long arg)
|
||||
@ -3375,6 +3388,326 @@ static int f2fs_get_compress_blocks(struct file *filp, unsigned long arg)
|
||||
return put_user(blocks, (u64 __user *)arg);
|
||||
}
|
||||
|
||||
static int release_compress_blocks(struct dnode_of_data *dn, pgoff_t count)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
|
||||
unsigned int released_blocks = 0;
|
||||
int cluster_size = F2FS_I(dn->inode)->i_cluster_size;
|
||||
block_t blkaddr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
blkaddr = data_blkaddr(dn->inode, dn->node_page,
|
||||
dn->ofs_in_node + i);
|
||||
|
||||
if (!__is_valid_data_blkaddr(blkaddr))
|
||||
continue;
|
||||
if (unlikely(!f2fs_is_valid_blkaddr(sbi, blkaddr,
|
||||
DATA_GENERIC_ENHANCE)))
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
while (count) {
|
||||
int compr_blocks = 0;
|
||||
|
||||
for (i = 0; i < cluster_size; i++, dn->ofs_in_node++) {
|
||||
blkaddr = f2fs_data_blkaddr(dn);
|
||||
|
||||
if (i == 0) {
|
||||
if (blkaddr == COMPRESS_ADDR)
|
||||
continue;
|
||||
dn->ofs_in_node += cluster_size;
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (__is_valid_data_blkaddr(blkaddr))
|
||||
compr_blocks++;
|
||||
|
||||
if (blkaddr != NEW_ADDR)
|
||||
continue;
|
||||
|
||||
dn->data_blkaddr = NULL_ADDR;
|
||||
f2fs_set_data_blkaddr(dn);
|
||||
}
|
||||
|
||||
f2fs_i_compr_blocks_update(dn->inode, compr_blocks, false);
|
||||
dec_valid_block_count(sbi, dn->inode,
|
||||
cluster_size - compr_blocks);
|
||||
|
||||
released_blocks += cluster_size - compr_blocks;
|
||||
next:
|
||||
count -= cluster_size;
|
||||
}
|
||||
|
||||
return released_blocks;
|
||||
}
|
||||
|
||||
static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
pgoff_t page_idx = 0, last_idx;
|
||||
unsigned int released_blocks = 0;
|
||||
int ret;
|
||||
int writecount;
|
||||
|
||||
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!f2fs_compressed_file(inode))
|
||||
return -EINVAL;
|
||||
|
||||
if (f2fs_readonly(sbi->sb))
|
||||
return -EROFS;
|
||||
|
||||
ret = mnt_want_write_file(filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
f2fs_balance_fs(F2FS_I_SB(inode), true);
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
writecount = atomic_read(&inode->i_writecount);
|
||||
if ((filp->f_mode & FMODE_WRITE && writecount != 1) || writecount) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (IS_IMMUTABLE(inode)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!F2FS_I(inode)->i_compr_blocks)
|
||||
goto out;
|
||||
|
||||
F2FS_I(inode)->i_flags |= F2FS_IMMUTABLE_FL;
|
||||
f2fs_set_inode_flags(inode);
|
||||
inode->i_ctime = current_time(inode);
|
||||
f2fs_mark_inode_dirty_sync(inode, true);
|
||||
|
||||
down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
|
||||
down_write(&F2FS_I(inode)->i_mmap_sem);
|
||||
|
||||
last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
|
||||
|
||||
while (page_idx < last_idx) {
|
||||
struct dnode_of_data dn;
|
||||
pgoff_t end_offset, count;
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
ret = f2fs_get_dnode_of_data(&dn, page_idx, LOOKUP_NODE);
|
||||
if (ret) {
|
||||
if (ret == -ENOENT) {
|
||||
page_idx = f2fs_get_next_page_offset(&dn,
|
||||
page_idx);
|
||||
ret = 0;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
|
||||
count = min(end_offset - dn.ofs_in_node, last_idx - page_idx);
|
||||
count = round_up(count, F2FS_I(inode)->i_cluster_size);
|
||||
|
||||
ret = release_compress_blocks(&dn, count);
|
||||
|
||||
f2fs_put_dnode(&dn);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
page_idx += count;
|
||||
released_blocks += ret;
|
||||
}
|
||||
|
||||
up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
|
||||
up_write(&F2FS_I(inode)->i_mmap_sem);
|
||||
out:
|
||||
inode_unlock(inode);
|
||||
|
||||
mnt_drop_write_file(filp);
|
||||
|
||||
if (ret >= 0) {
|
||||
ret = put_user(released_blocks, (u64 __user *)arg);
|
||||
} else if (released_blocks && F2FS_I(inode)->i_compr_blocks) {
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
f2fs_warn(sbi, "%s: partial blocks were released i_ino=%lx "
|
||||
"iblocks=%llu, released=%u, compr_blocks=%llu, "
|
||||
"run fsck to fix.",
|
||||
__func__, inode->i_ino, (u64)inode->i_blocks,
|
||||
released_blocks,
|
||||
F2FS_I(inode)->i_compr_blocks);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int reserve_compress_blocks(struct dnode_of_data *dn, pgoff_t count)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
|
||||
unsigned int reserved_blocks = 0;
|
||||
int cluster_size = F2FS_I(dn->inode)->i_cluster_size;
|
||||
block_t blkaddr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
blkaddr = data_blkaddr(dn->inode, dn->node_page,
|
||||
dn->ofs_in_node + i);
|
||||
|
||||
if (!__is_valid_data_blkaddr(blkaddr))
|
||||
continue;
|
||||
if (unlikely(!f2fs_is_valid_blkaddr(sbi, blkaddr,
|
||||
DATA_GENERIC_ENHANCE)))
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
while (count) {
|
||||
int compr_blocks = 0;
|
||||
blkcnt_t reserved;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < cluster_size; i++, dn->ofs_in_node++) {
|
||||
blkaddr = f2fs_data_blkaddr(dn);
|
||||
|
||||
if (i == 0) {
|
||||
if (blkaddr == COMPRESS_ADDR)
|
||||
continue;
|
||||
dn->ofs_in_node += cluster_size;
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (__is_valid_data_blkaddr(blkaddr)) {
|
||||
compr_blocks++;
|
||||
continue;
|
||||
}
|
||||
|
||||
dn->data_blkaddr = NEW_ADDR;
|
||||
f2fs_set_data_blkaddr(dn);
|
||||
}
|
||||
|
||||
reserved = cluster_size - compr_blocks;
|
||||
ret = inc_valid_block_count(sbi, dn->inode, &reserved);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (reserved != cluster_size - compr_blocks)
|
||||
return -ENOSPC;
|
||||
|
||||
f2fs_i_compr_blocks_update(dn->inode, compr_blocks, true);
|
||||
|
||||
reserved_blocks += reserved;
|
||||
next:
|
||||
count -= cluster_size;
|
||||
}
|
||||
|
||||
return reserved_blocks;
|
||||
}
|
||||
|
||||
static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
pgoff_t page_idx = 0, last_idx;
|
||||
unsigned int reserved_blocks = 0;
|
||||
int ret;
|
||||
|
||||
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!f2fs_compressed_file(inode))
|
||||
return -EINVAL;
|
||||
|
||||
if (f2fs_readonly(sbi->sb))
|
||||
return -EROFS;
|
||||
|
||||
ret = mnt_want_write_file(filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (F2FS_I(inode)->i_compr_blocks)
|
||||
goto out;
|
||||
|
||||
f2fs_balance_fs(F2FS_I_SB(inode), true);
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
if (!IS_IMMUTABLE(inode)) {
|
||||
ret = -EINVAL;
|
||||
goto unlock_inode;
|
||||
}
|
||||
|
||||
down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
|
||||
down_write(&F2FS_I(inode)->i_mmap_sem);
|
||||
|
||||
last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
|
||||
|
||||
while (page_idx < last_idx) {
|
||||
struct dnode_of_data dn;
|
||||
pgoff_t end_offset, count;
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
ret = f2fs_get_dnode_of_data(&dn, page_idx, LOOKUP_NODE);
|
||||
if (ret) {
|
||||
if (ret == -ENOENT) {
|
||||
page_idx = f2fs_get_next_page_offset(&dn,
|
||||
page_idx);
|
||||
ret = 0;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
end_offset = ADDRS_PER_PAGE(dn.node_page, inode);
|
||||
count = min(end_offset - dn.ofs_in_node, last_idx - page_idx);
|
||||
count = round_up(count, F2FS_I(inode)->i_cluster_size);
|
||||
|
||||
ret = reserve_compress_blocks(&dn, count);
|
||||
|
||||
f2fs_put_dnode(&dn);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
page_idx += count;
|
||||
reserved_blocks += ret;
|
||||
}
|
||||
|
||||
up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
|
||||
up_write(&F2FS_I(inode)->i_mmap_sem);
|
||||
|
||||
if (ret >= 0) {
|
||||
F2FS_I(inode)->i_flags &= ~F2FS_IMMUTABLE_FL;
|
||||
f2fs_set_inode_flags(inode);
|
||||
inode->i_ctime = current_time(inode);
|
||||
f2fs_mark_inode_dirty_sync(inode, true);
|
||||
}
|
||||
unlock_inode:
|
||||
inode_unlock(inode);
|
||||
out:
|
||||
mnt_drop_write_file(filp);
|
||||
|
||||
if (ret >= 0) {
|
||||
ret = put_user(reserved_blocks, (u64 __user *)arg);
|
||||
} else if (reserved_blocks && F2FS_I(inode)->i_compr_blocks) {
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
f2fs_warn(sbi, "%s: partial blocks were released i_ino=%lx "
|
||||
"iblocks=%llu, reserved=%u, compr_blocks=%llu, "
|
||||
"run fsck to fix.",
|
||||
__func__, inode->i_ino, (u64)inode->i_blocks,
|
||||
reserved_blocks,
|
||||
F2FS_I(inode)->i_compr_blocks);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
if (unlikely(f2fs_cp_error(F2FS_I_SB(file_inode(filp)))))
|
||||
@ -3453,6 +3786,10 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
return f2fs_ioc_measure_verity(filp, arg);
|
||||
case F2FS_IOC_GET_COMPRESS_BLOCKS:
|
||||
return f2fs_get_compress_blocks(filp, arg);
|
||||
case F2FS_IOC_RELEASE_COMPRESS_BLOCKS:
|
||||
return f2fs_release_compress_blocks(filp, arg);
|
||||
case F2FS_IOC_RESERVE_COMPRESS_BLOCKS:
|
||||
return f2fs_reserve_compress_blocks(filp, arg);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
@ -3617,6 +3954,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
case FS_IOC_ENABLE_VERITY:
|
||||
case FS_IOC_MEASURE_VERITY:
|
||||
case F2FS_IOC_GET_COMPRESS_BLOCKS:
|
||||
case F2FS_IOC_RELEASE_COMPRESS_BLOCKS:
|
||||
case F2FS_IOC_RESERVE_COMPRESS_BLOCKS:
|
||||
break;
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
|
121
fs/f2fs/gc.c
121
fs/f2fs/gc.c
@ -13,6 +13,7 @@
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/sched/signal.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
@ -739,6 +740,7 @@ got_it:
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
f2fs_update_iostat(sbi, FS_DATA_READ_IO, F2FS_BLKSIZE);
|
||||
f2fs_update_iostat(sbi, FS_GDATA_READ_IO, F2FS_BLKSIZE);
|
||||
|
||||
return 0;
|
||||
put_encrypted_page:
|
||||
@ -845,6 +847,7 @@ static int move_data_block(struct inode *inode, block_t bidx,
|
||||
}
|
||||
|
||||
f2fs_update_iostat(fio.sbi, FS_DATA_READ_IO, F2FS_BLKSIZE);
|
||||
f2fs_update_iostat(fio.sbi, FS_GDATA_READ_IO, F2FS_BLKSIZE);
|
||||
|
||||
lock_page(mpage);
|
||||
if (unlikely(mpage->mapping != META_MAPPING(fio.sbi) ||
|
||||
@ -1405,12 +1408,29 @@ void f2fs_build_gc_manager(struct f2fs_sb_info *sbi)
|
||||
GET_SEGNO(sbi, FDEV(0).end_blk) + 1;
|
||||
}
|
||||
|
||||
static int free_segment_range(struct f2fs_sb_info *sbi, unsigned int start,
|
||||
unsigned int end)
|
||||
static int free_segment_range(struct f2fs_sb_info *sbi,
|
||||
unsigned int secs, bool gc_only)
|
||||
{
|
||||
int type;
|
||||
unsigned int segno, next_inuse;
|
||||
unsigned int segno, next_inuse, start, end;
|
||||
struct cp_control cpc = { CP_RESIZE, 0, 0, 0 };
|
||||
int gc_mode, gc_type;
|
||||
int err = 0;
|
||||
int type;
|
||||
|
||||
/* Force block allocation for GC */
|
||||
MAIN_SECS(sbi) -= secs;
|
||||
start = MAIN_SECS(sbi) * sbi->segs_per_sec;
|
||||
end = MAIN_SEGS(sbi) - 1;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
/* Move out cursegs from the target range */
|
||||
for (type = CURSEG_HOT_DATA; type < NR_CURSEG_TYPE; type++)
|
||||
@ -1423,18 +1443,24 @@ static int free_segment_range(struct f2fs_sb_info *sbi, unsigned int start,
|
||||
.iroot = RADIX_TREE_INIT(GFP_NOFS),
|
||||
};
|
||||
|
||||
down_write(&sbi->gc_lock);
|
||||
do_garbage_collect(sbi, segno, &gc_list, FG_GC);
|
||||
up_write(&sbi->gc_lock);
|
||||
put_gc_inode(&gc_list);
|
||||
|
||||
if (get_valid_blocks(sbi, segno, true))
|
||||
return -EAGAIN;
|
||||
if (!gc_only && get_valid_blocks(sbi, segno, true)) {
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
if (fatal_signal_pending(current)) {
|
||||
err = -ERESTARTSYS;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (gc_only)
|
||||
goto out;
|
||||
|
||||
err = f2fs_sync_fs(sbi->sb, 1);
|
||||
err = f2fs_write_checkpoint(sbi, &cpc);
|
||||
if (err)
|
||||
return err;
|
||||
goto out;
|
||||
|
||||
next_inuse = find_next_inuse(FREE_I(sbi), end + 1, start);
|
||||
if (next_inuse <= end) {
|
||||
@ -1442,6 +1468,8 @@ static int free_segment_range(struct f2fs_sb_info *sbi, unsigned int start,
|
||||
next_inuse);
|
||||
f2fs_bug_on(sbi, 1);
|
||||
}
|
||||
out:
|
||||
MAIN_SECS(sbi) += secs;
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1487,6 +1515,7 @@ static void update_fs_metadata(struct f2fs_sb_info *sbi, int secs)
|
||||
|
||||
SM_I(sbi)->segment_count = (int)SM_I(sbi)->segment_count + segs;
|
||||
MAIN_SEGS(sbi) = (int)MAIN_SEGS(sbi) + segs;
|
||||
MAIN_SECS(sbi) += secs;
|
||||
FREE_I(sbi)->free_sections = (int)FREE_I(sbi)->free_sections + secs;
|
||||
FREE_I(sbi)->free_segments = (int)FREE_I(sbi)->free_segments + segs;
|
||||
F2FS_CKPT(sbi)->user_block_count = cpu_to_le64(user_block_count + blks);
|
||||
@ -1508,8 +1537,8 @@ static void update_fs_metadata(struct f2fs_sb_info *sbi, int secs)
|
||||
int f2fs_resize_fs(struct f2fs_sb_info *sbi, __u64 block_count)
|
||||
{
|
||||
__u64 old_block_count, shrunk_blocks;
|
||||
struct cp_control cpc = { CP_RESIZE, 0, 0, 0 };
|
||||
unsigned int secs;
|
||||
int gc_mode, gc_type;
|
||||
int err = 0;
|
||||
__u32 rem;
|
||||
|
||||
@ -1544,10 +1573,27 @@ int f2fs_resize_fs(struct f2fs_sb_info *sbi, __u64 block_count)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
freeze_bdev(sbi->sb->s_bdev);
|
||||
|
||||
shrunk_blocks = old_block_count - block_count;
|
||||
secs = div_u64(shrunk_blocks, BLKS_PER_SEC(sbi));
|
||||
|
||||
/* stop other GC */
|
||||
if (!down_write_trylock(&sbi->gc_lock))
|
||||
return -EAGAIN;
|
||||
|
||||
/* stop CP to protect MAIN_SEC in free_segment_range */
|
||||
f2fs_lock_op(sbi);
|
||||
err = free_segment_range(sbi, secs, true);
|
||||
f2fs_unlock_op(sbi);
|
||||
up_write(&sbi->gc_lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
set_sbi_flag(sbi, SBI_IS_RESIZEFS);
|
||||
|
||||
freeze_super(sbi->sb);
|
||||
down_write(&sbi->gc_lock);
|
||||
mutex_lock(&sbi->cp_mutex);
|
||||
|
||||
spin_lock(&sbi->stat_lock);
|
||||
if (shrunk_blocks + valid_user_blocks(sbi) +
|
||||
sbi->current_reserved_blocks + sbi->unusable_block_count +
|
||||
@ -1556,69 +1602,44 @@ int f2fs_resize_fs(struct f2fs_sb_info *sbi, __u64 block_count)
|
||||
else
|
||||
sbi->user_block_count -= shrunk_blocks;
|
||||
spin_unlock(&sbi->stat_lock);
|
||||
if (err) {
|
||||
thaw_bdev(sbi->sb->s_bdev, sbi->sb);
|
||||
return err;
|
||||
}
|
||||
|
||||
mutex_lock(&sbi->resize_mutex);
|
||||
set_sbi_flag(sbi, SBI_IS_RESIZEFS);
|
||||
|
||||
mutex_lock(&DIRTY_I(sbi)->seglist_lock);
|
||||
|
||||
MAIN_SECS(sbi) -= secs;
|
||||
|
||||
for (gc_mode = 0; gc_mode < MAX_GC_POLICY; gc_mode++)
|
||||
if (SIT_I(sbi)->last_victim[gc_mode] >=
|
||||
MAIN_SECS(sbi) * sbi->segs_per_sec)
|
||||
SIT_I(sbi)->last_victim[gc_mode] = 0;
|
||||
|
||||
for (gc_type = BG_GC; gc_type <= FG_GC; gc_type++)
|
||||
if (sbi->next_victim_seg[gc_type] >=
|
||||
MAIN_SECS(sbi) * sbi->segs_per_sec)
|
||||
sbi->next_victim_seg[gc_type] = NULL_SEGNO;
|
||||
|
||||
mutex_unlock(&DIRTY_I(sbi)->seglist_lock);
|
||||
|
||||
err = free_segment_range(sbi, MAIN_SECS(sbi) * sbi->segs_per_sec,
|
||||
MAIN_SEGS(sbi) - 1);
|
||||
if (err)
|
||||
goto out;
|
||||
goto out_err;
|
||||
|
||||
err = free_segment_range(sbi, secs, false);
|
||||
if (err)
|
||||
goto recover_out;
|
||||
|
||||
update_sb_metadata(sbi, -secs);
|
||||
|
||||
err = f2fs_commit_super(sbi, false);
|
||||
if (err) {
|
||||
update_sb_metadata(sbi, secs);
|
||||
goto out;
|
||||
goto recover_out;
|
||||
}
|
||||
|
||||
mutex_lock(&sbi->cp_mutex);
|
||||
update_fs_metadata(sbi, -secs);
|
||||
clear_sbi_flag(sbi, SBI_IS_RESIZEFS);
|
||||
set_sbi_flag(sbi, SBI_IS_DIRTY);
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
|
||||
err = f2fs_sync_fs(sbi->sb, 1);
|
||||
err = f2fs_write_checkpoint(sbi, &cpc);
|
||||
if (err) {
|
||||
mutex_lock(&sbi->cp_mutex);
|
||||
update_fs_metadata(sbi, secs);
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
update_sb_metadata(sbi, secs);
|
||||
f2fs_commit_super(sbi, false);
|
||||
}
|
||||
out:
|
||||
recover_out:
|
||||
if (err) {
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
f2fs_err(sbi, "resize_fs failed, should run fsck to repair!");
|
||||
|
||||
MAIN_SECS(sbi) += secs;
|
||||
spin_lock(&sbi->stat_lock);
|
||||
sbi->user_block_count += shrunk_blocks;
|
||||
spin_unlock(&sbi->stat_lock);
|
||||
}
|
||||
out_err:
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
up_write(&sbi->gc_lock);
|
||||
thaw_super(sbi->sb);
|
||||
clear_sbi_flag(sbi, SBI_IS_RESIZEFS);
|
||||
mutex_unlock(&sbi->resize_mutex);
|
||||
thaw_bdev(sbi->sb->s_bdev, sbi->sb);
|
||||
return err;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* fs/f2fs/gc.h
|
||||
*
|
||||
|
@ -505,6 +505,7 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
err = PTR_ERR(page);
|
||||
goto out;
|
||||
}
|
||||
err = -ENOENT;
|
||||
goto out_splice;
|
||||
}
|
||||
|
||||
@ -550,7 +551,7 @@ out_splice:
|
||||
#endif
|
||||
new = d_splice_alias(inode, dentry);
|
||||
err = PTR_ERR_OR_ZERO(new);
|
||||
trace_f2fs_lookup_end(dir, dentry, ino, err);
|
||||
trace_f2fs_lookup_end(dir, dentry, ino, !new ? -ENOENT : err);
|
||||
return new;
|
||||
out_iput:
|
||||
iput(inode);
|
||||
@ -565,7 +566,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct f2fs_dir_entry *de;
|
||||
struct page *page;
|
||||
int err = -ENOENT;
|
||||
int err;
|
||||
|
||||
trace_f2fs_unlink_enter(dir, dentry);
|
||||
|
||||
@ -1288,9 +1289,7 @@ const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
|
||||
.get_link = f2fs_encrypted_get_link,
|
||||
.getattr = f2fs_getattr,
|
||||
.setattr = f2fs_setattr,
|
||||
#ifdef CONFIG_F2FS_FS_XATTR
|
||||
.listxattr = f2fs_listxattr,
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct inode_operations f2fs_dir_inode_operations = {
|
||||
@ -1308,9 +1307,7 @@ const struct inode_operations f2fs_dir_inode_operations = {
|
||||
.setattr = f2fs_setattr,
|
||||
.get_acl = f2fs_get_acl,
|
||||
.set_acl = f2fs_set_acl,
|
||||
#ifdef CONFIG_F2FS_FS_XATTR
|
||||
.listxattr = f2fs_listxattr,
|
||||
#endif
|
||||
.fiemap = f2fs_fiemap,
|
||||
};
|
||||
|
||||
@ -1318,9 +1315,7 @@ const struct inode_operations f2fs_symlink_inode_operations = {
|
||||
.get_link = f2fs_get_link,
|
||||
.getattr = f2fs_getattr,
|
||||
.setattr = f2fs_setattr,
|
||||
#ifdef CONFIG_F2FS_FS_XATTR
|
||||
.listxattr = f2fs_listxattr,
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct inode_operations f2fs_special_inode_operations = {
|
||||
@ -1328,7 +1323,5 @@ const struct inode_operations f2fs_special_inode_operations = {
|
||||
.setattr = f2fs_setattr,
|
||||
.get_acl = f2fs_get_acl,
|
||||
.set_acl = f2fs_set_acl,
|
||||
#ifdef CONFIG_F2FS_FS_XATTR
|
||||
.listxattr = f2fs_listxattr,
|
||||
#endif
|
||||
};
|
||||
|
@ -1520,8 +1520,15 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
|
||||
|
||||
trace_f2fs_writepage(page, NODE);
|
||||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
if (is_sbi_flag_set(sbi, SBI_IS_CLOSE)) {
|
||||
ClearPageUptodate(page);
|
||||
dec_page_count(sbi, F2FS_DIRTY_NODES);
|
||||
unlock_page(page);
|
||||
return 0;
|
||||
}
|
||||
goto redirty_out;
|
||||
}
|
||||
|
||||
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
|
||||
goto redirty_out;
|
||||
@ -1807,6 +1814,53 @@ static bool flush_dirty_inode(struct page *page)
|
||||
return true;
|
||||
}
|
||||
|
||||
int f2fs_flush_inline_data(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
pgoff_t index = 0;
|
||||
struct pagevec pvec;
|
||||
int nr_pages;
|
||||
int ret = 0;
|
||||
|
||||
pagevec_init(&pvec, 0);
|
||||
|
||||
while ((nr_pages = pagevec_lookup_tag(&pvec,
|
||||
NODE_MAPPING(sbi), &index, PAGECACHE_TAG_DIRTY))) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
struct page *page = pvec.pages[i];
|
||||
|
||||
if (!IS_DNODE(page))
|
||||
continue;
|
||||
|
||||
lock_page(page);
|
||||
|
||||
if (unlikely(page->mapping != NODE_MAPPING(sbi))) {
|
||||
continue_unlock:
|
||||
unlock_page(page);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!PageDirty(page)) {
|
||||
/* someone wrote it for us */
|
||||
goto continue_unlock;
|
||||
}
|
||||
|
||||
/* flush inline_data, if it's async context. */
|
||||
if (is_inline_node(page)) {
|
||||
clear_inline_node(page);
|
||||
unlock_page(page);
|
||||
flush_inline_data(sbi, ino_of_node(page));
|
||||
continue;
|
||||
}
|
||||
unlock_page(page);
|
||||
}
|
||||
pagevec_release(&pvec);
|
||||
cond_resched();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int f2fs_sync_node_pages(struct f2fs_sb_info *sbi,
|
||||
struct writeback_control *wbc,
|
||||
bool do_balance, enum iostat_type io_type)
|
||||
@ -1870,8 +1924,8 @@ continue_unlock:
|
||||
goto continue_unlock;
|
||||
}
|
||||
|
||||
/* flush inline_data */
|
||||
if (is_inline_node(page)) {
|
||||
/* flush inline_data, if it's async context. */
|
||||
if (do_balance && is_inline_node(page)) {
|
||||
clear_inline_node(page);
|
||||
unlock_page(page);
|
||||
flush_inline_data(sbi, ino_of_node(page));
|
||||
@ -2488,7 +2542,6 @@ void f2fs_alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid)
|
||||
int f2fs_try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct free_nid *i, *next;
|
||||
int nr = nr_shrink;
|
||||
|
||||
if (nm_i->nid_cnt[FREE_NID] <= MAX_FREE_NIDS)
|
||||
@ -2497,17 +2550,23 @@ int f2fs_try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink)
|
||||
if (!mutex_trylock(&nm_i->build_lock))
|
||||
return 0;
|
||||
|
||||
spin_lock(&nm_i->nid_list_lock);
|
||||
list_for_each_entry_safe(i, next, &nm_i->free_nid_list, list) {
|
||||
if (nr_shrink <= 0 ||
|
||||
nm_i->nid_cnt[FREE_NID] <= MAX_FREE_NIDS)
|
||||
break;
|
||||
while (nr_shrink && nm_i->nid_cnt[FREE_NID] > MAX_FREE_NIDS) {
|
||||
struct free_nid *i, *next;
|
||||
unsigned int batch = SHRINK_NID_BATCH_SIZE;
|
||||
|
||||
__remove_free_nid(sbi, i, FREE_NID);
|
||||
kmem_cache_free(free_nid_slab, i);
|
||||
nr_shrink--;
|
||||
spin_lock(&nm_i->nid_list_lock);
|
||||
list_for_each_entry_safe(i, next, &nm_i->free_nid_list, list) {
|
||||
if (!nr_shrink || !batch ||
|
||||
nm_i->nid_cnt[FREE_NID] <= MAX_FREE_NIDS)
|
||||
break;
|
||||
__remove_free_nid(sbi, i, FREE_NID);
|
||||
kmem_cache_free(free_nid_slab, i);
|
||||
nr_shrink--;
|
||||
batch--;
|
||||
}
|
||||
spin_unlock(&nm_i->nid_list_lock);
|
||||
}
|
||||
spin_unlock(&nm_i->nid_list_lock);
|
||||
|
||||
mutex_unlock(&nm_i->build_lock);
|
||||
|
||||
return nr - nr_shrink;
|
||||
@ -2934,7 +2993,7 @@ static int __get_nat_bitmaps(struct f2fs_sb_info *sbi)
|
||||
return 0;
|
||||
|
||||
nm_i->nat_bits_blocks = F2FS_BLK_ALIGN((nat_bits_bytes << 1) + 8);
|
||||
nm_i->nat_bits = f2fs_kzalloc(sbi,
|
||||
nm_i->nat_bits = f2fs_kvzalloc(sbi,
|
||||
nm_i->nat_bits_blocks << F2FS_BLKSIZE_BITS, GFP_KERNEL);
|
||||
if (!nm_i->nat_bits)
|
||||
return -ENOMEM;
|
||||
@ -3067,9 +3126,9 @@ static int init_free_nid_cache(struct f2fs_sb_info *sbi)
|
||||
int i;
|
||||
|
||||
nm_i->free_nid_bitmap =
|
||||
f2fs_kzalloc(sbi, array_size(sizeof(unsigned char *),
|
||||
nm_i->nat_blocks),
|
||||
GFP_KERNEL);
|
||||
f2fs_kvzalloc(sbi, array_size(sizeof(unsigned char *),
|
||||
nm_i->nat_blocks),
|
||||
GFP_KERNEL);
|
||||
if (!nm_i->free_nid_bitmap)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* fs/f2fs/node.h
|
||||
*
|
||||
@ -15,6 +15,9 @@
|
||||
#define FREE_NID_PAGES 8
|
||||
#define MAX_FREE_NIDS (NAT_ENTRY_PER_BLOCK * FREE_NID_PAGES)
|
||||
|
||||
/* size of free nid batch when shrinking */
|
||||
#define SHRINK_NID_BATCH_SIZE 8
|
||||
|
||||
#define DEF_RA_NID_PAGES 0 /* # of nid pages to be readaheaded */
|
||||
|
||||
/* maximum readahead size for node during getting data blocks */
|
||||
|
@ -1221,7 +1221,7 @@ submit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,
|
||||
static void __insert_discard_tree(struct f2fs_sb_info *sbi,
|
||||
struct block_device *bdev, block_t lstart,
|
||||
block_t start, block_t len,
|
||||
struct rb_node **insert_p,
|
||||
@ -1230,7 +1230,6 @@ static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,
|
||||
struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
|
||||
struct rb_node **p;
|
||||
struct rb_node *parent = NULL;
|
||||
struct discard_cmd *dc = NULL;
|
||||
bool leftmost = true;
|
||||
|
||||
if (insert_p && insert_parent) {
|
||||
@ -1242,12 +1241,8 @@ static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,
|
||||
p = f2fs_lookup_rb_tree_for_insert(sbi, &dcc->root, &parent,
|
||||
lstart, &leftmost);
|
||||
do_insert:
|
||||
dc = __attach_discard_cmd(sbi, bdev, lstart, start, len, parent,
|
||||
__attach_discard_cmd(sbi, bdev, lstart, start, len, parent,
|
||||
p, leftmost);
|
||||
if (!dc)
|
||||
return NULL;
|
||||
|
||||
return dc;
|
||||
}
|
||||
|
||||
static void __relocate_discard_cmd(struct discard_cmd_control *dcc,
|
||||
@ -3111,6 +3106,14 @@ void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
|
||||
type = CURSEG_COLD_DATA;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to wait for node_write to avoid block allocation during
|
||||
* checkpoint. This can only happen to quota writes which can cause
|
||||
* the below discard race condition.
|
||||
*/
|
||||
if (IS_DATASEG(type))
|
||||
down_write(&sbi->node_write);
|
||||
|
||||
down_read(&SM_I(sbi)->curseg_lock);
|
||||
|
||||
mutex_lock(&curseg->curseg_mutex);
|
||||
@ -3176,6 +3179,9 @@ void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
|
||||
|
||||
up_read(&SM_I(sbi)->curseg_lock);
|
||||
|
||||
if (IS_DATASEG(type))
|
||||
up_write(&sbi->node_write);
|
||||
|
||||
if (put_pin_sem)
|
||||
up_read(&sbi->pin_sem);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* fs/f2fs/segment.h
|
||||
*
|
||||
|
@ -444,11 +444,11 @@ static int parse_options(struct super_block *sb, char *options)
|
||||
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
if (strlen(name) == 2 && !strncmp(name, "on", 2)) {
|
||||
if (!strcmp(name, "on")) {
|
||||
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
|
||||
} else if (strlen(name) == 3 && !strncmp(name, "off", 3)) {
|
||||
} else if (!strcmp(name, "off")) {
|
||||
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_OFF;
|
||||
} else if (strlen(name) == 4 && !strncmp(name, "sync", 4)) {
|
||||
} else if (!strcmp(name, "sync")) {
|
||||
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_SYNC;
|
||||
} else {
|
||||
kvfree(name);
|
||||
@ -608,16 +608,14 @@ static int parse_options(struct super_block *sb, char *options)
|
||||
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
if (strlen(name) == 8 &&
|
||||
!strncmp(name, "adaptive", 8)) {
|
||||
if (!strcmp(name, "adaptive")) {
|
||||
if (f2fs_sb_has_blkzoned(sbi)) {
|
||||
f2fs_warn(sbi, "adaptive mode is not allowed with zoned block device feature");
|
||||
kvfree(name);
|
||||
return -EINVAL;
|
||||
}
|
||||
F2FS_OPTION(sbi).fs_mode = FS_MODE_ADAPTIVE;
|
||||
} else if (strlen(name) == 3 &&
|
||||
!strncmp(name, "lfs", 3)) {
|
||||
} else if (!strcmp(name, "lfs")) {
|
||||
F2FS_OPTION(sbi).fs_mode = FS_MODE_LFS;
|
||||
} else {
|
||||
kvfree(name);
|
||||
@ -742,14 +740,11 @@ static int parse_options(struct super_block *sb, char *options)
|
||||
name = match_strdup(&args[0]);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
if (strlen(name) == 10 &&
|
||||
!strncmp(name, "user-based", 10)) {
|
||||
if (!strcmp(name, "user-based")) {
|
||||
F2FS_OPTION(sbi).whint_mode = WHINT_MODE_USER;
|
||||
} else if (strlen(name) == 3 &&
|
||||
!strncmp(name, "off", 3)) {
|
||||
} else if (!strcmp(name, "off")) {
|
||||
F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF;
|
||||
} else if (strlen(name) == 8 &&
|
||||
!strncmp(name, "fs-based", 8)) {
|
||||
} else if (!strcmp(name, "fs-based")) {
|
||||
F2FS_OPTION(sbi).whint_mode = WHINT_MODE_FS;
|
||||
} else {
|
||||
kvfree(name);
|
||||
@ -762,11 +757,9 @@ static int parse_options(struct super_block *sb, char *options)
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
if (strlen(name) == 7 &&
|
||||
!strncmp(name, "default", 7)) {
|
||||
if (!strcmp(name, "default")) {
|
||||
F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT;
|
||||
} else if (strlen(name) == 5 &&
|
||||
!strncmp(name, "reuse", 5)) {
|
||||
} else if (!strcmp(name, "reuse")) {
|
||||
F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_REUSE;
|
||||
} else {
|
||||
kvfree(name);
|
||||
@ -778,14 +771,11 @@ static int parse_options(struct super_block *sb, char *options)
|
||||
name = match_strdup(&args[0]);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
if (strlen(name) == 5 &&
|
||||
!strncmp(name, "posix", 5)) {
|
||||
if (!strcmp(name, "posix")) {
|
||||
F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_POSIX;
|
||||
} else if (strlen(name) == 6 &&
|
||||
!strncmp(name, "strict", 6)) {
|
||||
} else if (!strcmp(name, "strict")) {
|
||||
F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_STRICT;
|
||||
} else if (strlen(name) == 9 &&
|
||||
!strncmp(name, "nobarrier", 9)) {
|
||||
} else if (!strcmp(name, "nobarrier")) {
|
||||
F2FS_OPTION(sbi).fsync_mode =
|
||||
FSYNC_MODE_NOBARRIER;
|
||||
} else {
|
||||
@ -842,15 +832,13 @@ static int parse_options(struct super_block *sb, char *options)
|
||||
name = match_strdup(&args[0]);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
if (strlen(name) == 3 && !strcmp(name, "lzo")) {
|
||||
if (!strcmp(name, "lzo")) {
|
||||
F2FS_OPTION(sbi).compress_algorithm =
|
||||
COMPRESS_LZO;
|
||||
} else if (strlen(name) == 3 &&
|
||||
!strcmp(name, "lz4")) {
|
||||
} else if (!strcmp(name, "lz4")) {
|
||||
F2FS_OPTION(sbi).compress_algorithm =
|
||||
COMPRESS_LZ4;
|
||||
} else if (strlen(name) == 4 &&
|
||||
!strcmp(name, "zstd")) {
|
||||
} else if (!strcmp(name, "zstd")) {
|
||||
F2FS_OPTION(sbi).compress_algorithm =
|
||||
COMPRESS_ZSTD;
|
||||
} else {
|
||||
@ -1319,7 +1307,8 @@ static int f2fs_statfs_project(struct super_block *sb,
|
||||
limit >>= sb->s_blocksize_bits;
|
||||
|
||||
if (limit && buf->f_blocks > limit) {
|
||||
curblock = dquot->dq_dqb.dqb_curspace >> sb->s_blocksize_bits;
|
||||
curblock = (dquot->dq_dqb.dqb_curspace +
|
||||
dquot->dq_dqb.dqb_rsvspace) >> sb->s_blocksize_bits;
|
||||
buf->f_blocks = limit;
|
||||
buf->f_bfree = buf->f_bavail =
|
||||
(buf->f_blocks > curblock) ?
|
||||
@ -3080,7 +3069,7 @@ 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_seq = f2fs_kzalloc(sbi,
|
||||
FDEV(devi).blkz_seq = f2fs_kvzalloc(sbi,
|
||||
BITS_TO_LONGS(FDEV(devi).nr_blkz)
|
||||
* sizeof(unsigned long),
|
||||
GFP_KERNEL);
|
||||
@ -3487,7 +3476,6 @@ try_onemore:
|
||||
init_rwsem(&sbi->gc_lock);
|
||||
mutex_init(&sbi->writepages);
|
||||
mutex_init(&sbi->cp_mutex);
|
||||
mutex_init(&sbi->resize_mutex);
|
||||
init_rwsem(&sbi->node_write);
|
||||
init_rwsem(&sbi->node_change);
|
||||
|
||||
@ -3954,7 +3942,12 @@ static int __init init_f2fs_fs(void)
|
||||
err = f2fs_init_bioset();
|
||||
if (err)
|
||||
goto free_bio_enrty_cache;
|
||||
err = f2fs_init_compress_mempool();
|
||||
if (err)
|
||||
goto free_bioset;
|
||||
return 0;
|
||||
free_bioset:
|
||||
f2fs_destroy_bioset();
|
||||
free_bio_enrty_cache:
|
||||
f2fs_destroy_bio_entry_cache();
|
||||
free_post_read:
|
||||
@ -3982,6 +3975,7 @@ fail:
|
||||
|
||||
static void __exit exit_f2fs_fs(void)
|
||||
{
|
||||
f2fs_destroy_compress_mempool();
|
||||
f2fs_destroy_bioset();
|
||||
f2fs_destroy_bio_entry_cache();
|
||||
f2fs_destroy_post_read_processing();
|
||||
|
@ -556,6 +556,7 @@ F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
|
||||
F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
|
||||
#endif
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, data_io_flag, data_io_flag);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, node_io_flag, node_io_flag);
|
||||
F2FS_GENERAL_RO_ATTR(dirty_segments);
|
||||
F2FS_GENERAL_RO_ATTR(free_segments);
|
||||
F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes);
|
||||
@ -637,6 +638,7 @@ static struct attribute *f2fs_attrs[] = {
|
||||
ATTR_LIST(inject_type),
|
||||
#endif
|
||||
ATTR_LIST(data_io_flag),
|
||||
ATTR_LIST(node_io_flag),
|
||||
ATTR_LIST(dirty_segments),
|
||||
ATTR_LIST(free_segments),
|
||||
ATTR_LIST(unusable),
|
||||
@ -803,6 +805,7 @@ static int __maybe_unused iostat_info_seq_show(struct seq_file *seq,
|
||||
seq_printf(seq, "time: %-16llu\n", now);
|
||||
|
||||
/* print app write IOs */
|
||||
seq_puts(seq, "[WRITE]\n");
|
||||
seq_printf(seq, "app buffered: %-16llu\n",
|
||||
sbi->rw_iostat[APP_BUFFERED_IO]);
|
||||
seq_printf(seq, "app direct: %-16llu\n",
|
||||
@ -829,6 +832,7 @@ static int __maybe_unused iostat_info_seq_show(struct seq_file *seq,
|
||||
sbi->rw_iostat[FS_CP_META_IO]);
|
||||
|
||||
/* print app read IOs */
|
||||
seq_puts(seq, "[READ]\n");
|
||||
seq_printf(seq, "app buffered: %-16llu\n",
|
||||
sbi->rw_iostat[APP_BUFFERED_READ_IO]);
|
||||
seq_printf(seq, "app direct: %-16llu\n",
|
||||
@ -839,12 +843,17 @@ static int __maybe_unused iostat_info_seq_show(struct seq_file *seq,
|
||||
/* print fs read IOs */
|
||||
seq_printf(seq, "fs data: %-16llu\n",
|
||||
sbi->rw_iostat[FS_DATA_READ_IO]);
|
||||
seq_printf(seq, "fs gc data: %-16llu\n",
|
||||
sbi->rw_iostat[FS_GDATA_READ_IO]);
|
||||
seq_printf(seq, "fs compr_data: %-16llu\n",
|
||||
sbi->rw_iostat[FS_CDATA_READ_IO]);
|
||||
seq_printf(seq, "fs node: %-16llu\n",
|
||||
sbi->rw_iostat[FS_NODE_READ_IO]);
|
||||
seq_printf(seq, "fs meta: %-16llu\n",
|
||||
sbi->rw_iostat[FS_META_READ_IO]);
|
||||
|
||||
/* print other IOs */
|
||||
seq_puts(seq, "[OTHER]\n");
|
||||
seq_printf(seq, "fs discard: %-16llu\n",
|
||||
sbi->rw_iostat[FS_DISCARD]);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* f2fs IO tracer
|
||||
*
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* fs/f2fs/xattr.h
|
||||
*
|
||||
@ -136,6 +136,7 @@ extern void f2fs_destroy_xattr_caches(struct f2fs_sb_info *);
|
||||
#else
|
||||
|
||||
#define f2fs_xattr_handlers NULL
|
||||
#define f2fs_listxattr NULL
|
||||
static inline int f2fs_setxattr(struct inode *inode, int index,
|
||||
const char *name, const void *value, size_t size,
|
||||
struct page *page, int flags)
|
||||
@ -148,11 +149,6 @@ static inline int f2fs_getxattr(struct inode *inode, int index,
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer,
|
||||
size_t buffer_size)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline int f2fs_init_xattr_caches(struct f2fs_sb_info *sbi) { return 0; }
|
||||
static inline void f2fs_destroy_xattr_caches(struct f2fs_sb_info *sbi) { }
|
||||
#endif
|
||||
|
@ -160,7 +160,9 @@ static void inode_io_list_del_locked(struct inode *inode,
|
||||
struct bdi_writeback *wb)
|
||||
{
|
||||
assert_spin_locked(&wb->list_lock);
|
||||
assert_spin_locked(&inode->i_lock);
|
||||
|
||||
inode->i_state &= ~I_SYNC_QUEUED;
|
||||
list_del_init(&inode->i_io_list);
|
||||
wb_io_lists_depopulated(wb);
|
||||
}
|
||||
@ -1038,7 +1040,9 @@ void inode_io_list_del(struct inode *inode)
|
||||
struct bdi_writeback *wb;
|
||||
|
||||
wb = inode_to_wb_and_lock_list(inode);
|
||||
spin_lock(&inode->i_lock);
|
||||
inode_io_list_del_locked(inode, wb);
|
||||
spin_unlock(&inode->i_lock);
|
||||
spin_unlock(&wb->list_lock);
|
||||
}
|
||||
|
||||
@ -1087,8 +1091,9 @@ void sb_clear_inode_writeback(struct inode *inode)
|
||||
* the case then the inode must have been redirtied while it was being written
|
||||
* out and we don't reset its dirtied_when.
|
||||
*/
|
||||
static void redirty_tail(struct inode *inode, struct bdi_writeback *wb)
|
||||
static void __redirty_tail(struct inode *inode, struct bdi_writeback *wb)
|
||||
{
|
||||
assert_spin_locked(&inode->i_lock);
|
||||
if (!list_empty(&wb->b_dirty)) {
|
||||
struct inode *tail;
|
||||
|
||||
@ -1097,6 +1102,14 @@ static void redirty_tail(struct inode *inode, struct bdi_writeback *wb)
|
||||
inode->dirtied_when = jiffies;
|
||||
}
|
||||
inode_io_list_move_locked(inode, wb, &wb->b_dirty);
|
||||
inode->i_state &= ~I_SYNC_QUEUED;
|
||||
}
|
||||
|
||||
static void redirty_tail(struct inode *inode, struct bdi_writeback *wb)
|
||||
{
|
||||
spin_lock(&inode->i_lock);
|
||||
__redirty_tail(inode, wb);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1165,8 +1178,11 @@ static int move_expired_inodes(struct list_head *delaying_queue,
|
||||
break;
|
||||
list_move(&inode->i_io_list, &tmp);
|
||||
moved++;
|
||||
spin_lock(&inode->i_lock);
|
||||
if (flags & EXPIRE_DIRTY_ATIME)
|
||||
set_bit(__I_DIRTY_TIME_EXPIRED, &inode->i_state);
|
||||
inode->i_state |= I_DIRTY_TIME_EXPIRED;
|
||||
inode->i_state |= I_SYNC_QUEUED;
|
||||
spin_unlock(&inode->i_lock);
|
||||
if (sb_is_blkdev_sb(inode->i_sb))
|
||||
continue;
|
||||
if (sb && sb != inode->i_sb)
|
||||
@ -1309,7 +1325,7 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb,
|
||||
* writeback is not making progress due to locked
|
||||
* buffers. Skip this inode for now.
|
||||
*/
|
||||
redirty_tail(inode, wb);
|
||||
__redirty_tail(inode, wb);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1329,7 +1345,7 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb,
|
||||
* retrying writeback of the dirty page/inode
|
||||
* that cannot be performed immediately.
|
||||
*/
|
||||
redirty_tail(inode, wb);
|
||||
__redirty_tail(inode, wb);
|
||||
}
|
||||
} else if (inode->i_state & I_DIRTY) {
|
||||
/*
|
||||
@ -1337,10 +1353,11 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb,
|
||||
* such as delayed allocation during submission or metadata
|
||||
* updates after data IO completion.
|
||||
*/
|
||||
redirty_tail(inode, wb);
|
||||
__redirty_tail(inode, wb);
|
||||
} else if (inode->i_state & I_DIRTY_TIME) {
|
||||
inode->dirtied_when = jiffies;
|
||||
inode_io_list_move_locked(inode, wb, &wb->b_dirty_time);
|
||||
inode->i_state &= ~I_SYNC_QUEUED;
|
||||
} else {
|
||||
/* The inode is clean. Remove from writeback lists. */
|
||||
inode_io_list_del_locked(inode, wb);
|
||||
@ -1584,8 +1601,9 @@ static long writeback_sb_inodes(struct super_block *sb,
|
||||
*/
|
||||
spin_lock(&inode->i_lock);
|
||||
if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE)) {
|
||||
inode->i_state &= ~I_SYNC_QUEUED;
|
||||
__redirty_tail(inode, wb);
|
||||
spin_unlock(&inode->i_lock);
|
||||
redirty_tail(inode, wb);
|
||||
continue;
|
||||
}
|
||||
if ((inode->i_state & I_SYNC) && wbc.sync_mode != WB_SYNC_ALL) {
|
||||
@ -2175,11 +2193,12 @@ void __mark_inode_dirty(struct inode *inode, int flags)
|
||||
inode->i_state |= flags;
|
||||
|
||||
/*
|
||||
* If the inode is being synced, just update its dirty state.
|
||||
* The unlocker will place the inode on the appropriate
|
||||
* superblock list, based upon its state.
|
||||
* If the inode is queued for writeback by flush worker, just
|
||||
* update its dirty state. Once the flush worker is done with
|
||||
* the inode it will place it on the appropriate superblock
|
||||
* list, based upon its state.
|
||||
*/
|
||||
if (inode->i_state & I_SYNC)
|
||||
if (inode->i_state & I_SYNC_QUEUED)
|
||||
goto out_unlock_inode;
|
||||
|
||||
/*
|
||||
|
16
fs/libfs.c
16
fs/libfs.c
@ -1240,11 +1240,27 @@ int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
|
||||
const struct super_block *sb = dentry->d_sb;
|
||||
const struct unicode_map *um = sb->s_encoding;
|
||||
struct qstr entry = QSTR_INIT(str, len);
|
||||
char strbuf[DNAME_INLINE_LEN];
|
||||
int ret;
|
||||
|
||||
if (!inode || !needs_casefold(inode))
|
||||
goto fallback;
|
||||
|
||||
/*
|
||||
* If the dentry name is stored in-line, then it may be concurrently
|
||||
* modified by a rename. If this happens, the VFS will eventually retry
|
||||
* the lookup, so it doesn't matter what ->d_compare() returns.
|
||||
* However, it's unsafe to call utf8_strncasecmp() with an unstable
|
||||
* string. Therefore, we have to copy the name into a temporary buffer.
|
||||
*/
|
||||
if (len <= DNAME_INLINE_LEN - 1) {
|
||||
memcpy(strbuf, str, len);
|
||||
strbuf[len] = 0;
|
||||
entry.name = strbuf;
|
||||
/* prevent compiler from optimizing out the temporary buffer */
|
||||
barrier();
|
||||
}
|
||||
|
||||
ret = utf8_strncasecmp(um, name, &entry);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
@ -2030,6 +2030,10 @@ static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp)
|
||||
* I_OVL_INUSE Used by overlayfs to get exclusive ownership on upper
|
||||
* and work dirs among overlayfs mounts.
|
||||
*
|
||||
* I_SYNC_QUEUED Inode is queued in b_io or b_more_io writeback lists.
|
||||
* Used to detect that mark_inode_dirty() should not move
|
||||
* inode between dirty lists.
|
||||
*
|
||||
* Q: What is the difference between I_WILL_FREE and I_FREEING?
|
||||
*/
|
||||
#define I_DIRTY_SYNC (1 << 0)
|
||||
@ -2050,7 +2054,8 @@ static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp)
|
||||
#define __I_DIRTY_TIME_EXPIRED 12
|
||||
#define I_DIRTY_TIME_EXPIRED (1 << __I_DIRTY_TIME_EXPIRED)
|
||||
#define I_WB_SWITCH (1 << 13)
|
||||
#define I_OVL_INUSE (1 << 14)
|
||||
#define I_OVL_INUSE (1 << 14)
|
||||
#define I_SYNC_QUEUED (1 << 16)
|
||||
|
||||
#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
|
||||
#define I_DIRTY_ALL (I_DIRTY | I_DIRTY_TIME)
|
||||
|
@ -50,6 +50,7 @@ TRACE_DEFINE_ENUM(CP_RECOVERY);
|
||||
TRACE_DEFINE_ENUM(CP_DISCARD);
|
||||
TRACE_DEFINE_ENUM(CP_TRIMMED);
|
||||
TRACE_DEFINE_ENUM(CP_PAUSE);
|
||||
TRACE_DEFINE_ENUM(CP_RESIZE);
|
||||
|
||||
#define show_block_type(type) \
|
||||
__print_symbolic(type, \
|
||||
@ -136,7 +137,8 @@ TRACE_DEFINE_ENUM(CP_PAUSE);
|
||||
{ CP_RECOVERY, "Recovery" }, \
|
||||
{ CP_DISCARD, "Discard" }, \
|
||||
{ CP_PAUSE, "Pause" }, \
|
||||
{ CP_TRIMMED, "Trimmed" })
|
||||
{ CP_TRIMMED, "Trimmed" }, \
|
||||
{ CP_RESIZE, "Resize" })
|
||||
|
||||
#define show_fsync_cpreason(type) \
|
||||
__print_symbolic(type, \
|
||||
@ -1847,6 +1849,8 @@ TRACE_EVENT(f2fs_iostat,
|
||||
__field(unsigned long long, app_rio)
|
||||
__field(unsigned long long, app_mrio)
|
||||
__field(unsigned long long, fs_drio)
|
||||
__field(unsigned long long, fs_gdrio)
|
||||
__field(unsigned long long, fs_cdrio)
|
||||
__field(unsigned long long, fs_nrio)
|
||||
__field(unsigned long long, fs_mrio)
|
||||
__field(unsigned long long, fs_discard)
|
||||
@ -1871,6 +1875,8 @@ TRACE_EVENT(f2fs_iostat,
|
||||
__entry->app_rio = iostat[APP_READ_IO];
|
||||
__entry->app_mrio = iostat[APP_MAPPED_READ_IO];
|
||||
__entry->fs_drio = iostat[FS_DATA_READ_IO];
|
||||
__entry->fs_gdrio = iostat[FS_GDATA_READ_IO];
|
||||
__entry->fs_cdrio = iostat[FS_CDATA_READ_IO];
|
||||
__entry->fs_nrio = iostat[FS_NODE_READ_IO];
|
||||
__entry->fs_mrio = iostat[FS_META_READ_IO];
|
||||
__entry->fs_discard = iostat[FS_DISCARD];
|
||||
@ -1882,15 +1888,16 @@ TRACE_EVENT(f2fs_iostat,
|
||||
"gc [data=%llu, node=%llu], "
|
||||
"cp [data=%llu, node=%llu, meta=%llu], "
|
||||
"app [read=%llu (direct=%llu, buffered=%llu), mapped=%llu], "
|
||||
"fs [data=%llu, node=%llu, meta=%llu]",
|
||||
"fs [data=%llu, (gc_data=%llu, compr_data=%llu), "
|
||||
"node=%llu, meta=%llu]",
|
||||
show_dev(__entry->dev), __entry->app_wio, __entry->app_dio,
|
||||
__entry->app_bio, __entry->app_mio, __entry->fs_dio,
|
||||
__entry->fs_nio, __entry->fs_mio, __entry->fs_discard,
|
||||
__entry->fs_gc_dio, __entry->fs_gc_nio, __entry->fs_cp_dio,
|
||||
__entry->fs_cp_nio, __entry->fs_cp_mio,
|
||||
__entry->app_rio, __entry->app_drio, __entry->app_brio,
|
||||
__entry->app_mrio, __entry->fs_drio, __entry->fs_nrio,
|
||||
__entry->fs_mrio)
|
||||
__entry->app_mrio, __entry->fs_drio, __entry->fs_gdrio,
|
||||
__entry->fs_cdrio, __entry->fs_nrio, __entry->fs_mrio)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_F2FS_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user