+++ /dev/null
-From 219e7b6fea8ca67b3a430cdbb5b53a2e98a5bad7 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Tue, 2 Aug 2022 12:24:37 -0700
-Subject: f2fs: handle decompress only post processing in softirq
-
-From: Daeho Jeong <daehojeong@google.com>
-
-[ Upstream commit bff139b49d9f70c1ac5384aac94554846aa834de ]
-
-Now decompression is being handled in workqueue and it makes read I/O
-latency non-deterministic, because of the non-deterministic scheduling
-nature of workqueues. So, I made it handled in softirq context only if
-possible, not in low memory devices, since this modification will
-maintain decompresion related memory a little longer.
-
-Signed-off-by: Daeho Jeong <daehojeong@google.com>
-Reviewed-by: Chao Yu <chao@kernel.org>
-Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
-Stable-dep-of: b0327c84e91a ("f2fs: compress: fix to avoid use-after-free on dic")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- fs/f2fs/compress.c | 203 ++++++++++++++++++++++++++++++---------------
- fs/f2fs/data.c | 52 ++++++++----
- fs/f2fs/f2fs.h | 17 ++--
- 3 files changed, 179 insertions(+), 93 deletions(-)
-
-diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
-index 455fac164fda0..3849252b1b06f 100644
---- a/fs/f2fs/compress.c
-+++ b/fs/f2fs/compress.c
-@@ -738,14 +738,19 @@ static int f2fs_compress_pages(struct compress_ctx *cc)
- return ret;
- }
-
--void f2fs_decompress_cluster(struct decompress_io_ctx *dic)
-+static int f2fs_prepare_decomp_mem(struct decompress_io_ctx *dic,
-+ bool pre_alloc);
-+static void f2fs_release_decomp_mem(struct decompress_io_ctx *dic,
-+ bool bypass_destroy_callback, bool pre_alloc);
-+
-+void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task)
- {
- struct f2fs_sb_info *sbi = F2FS_I_SB(dic->inode);
- struct f2fs_inode_info *fi = F2FS_I(dic->inode);
- const struct f2fs_compress_ops *cops =
- f2fs_cops[fi->i_compress_algorithm];
-+ bool bypass_callback = false;
- int ret;
-- int i;
-
- trace_f2fs_decompress_pages_start(dic->inode, dic->cluster_idx,
- dic->cluster_size, fi->i_compress_algorithm);
-@@ -755,41 +760,10 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic)
- goto out_end_io;
- }
-
-- dic->tpages = page_array_alloc(dic->inode, dic->cluster_size);
-- if (!dic->tpages) {
-- ret = -ENOMEM;
-- goto out_end_io;
-- }
--
-- for (i = 0; i < dic->cluster_size; i++) {
-- if (dic->rpages[i]) {
-- dic->tpages[i] = dic->rpages[i];
-- continue;
-- }
--
-- dic->tpages[i] = f2fs_compress_alloc_page();
-- if (!dic->tpages[i]) {
-- ret = -ENOMEM;
-- goto out_end_io;
-- }
-- }
--
-- if (cops->init_decompress_ctx) {
-- ret = cops->init_decompress_ctx(dic);
-- if (ret)
-- goto out_end_io;
-- }
--
-- dic->rbuf = f2fs_vmap(dic->tpages, dic->cluster_size);
-- if (!dic->rbuf) {
-- ret = -ENOMEM;
-- goto out_destroy_decompress_ctx;
-- }
--
-- dic->cbuf = f2fs_vmap(dic->cpages, dic->nr_cpages);
-- if (!dic->cbuf) {
-- ret = -ENOMEM;
-- goto out_vunmap_rbuf;
-+ ret = f2fs_prepare_decomp_mem(dic, false);
-+ if (ret) {
-+ bypass_callback = true;
-+ goto out_release;
- }
-
- dic->clen = le32_to_cpu(dic->cbuf->clen);
-@@ -797,7 +771,7 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic)
-
- if (dic->clen > PAGE_SIZE * dic->nr_cpages - COMPRESS_HEADER_SIZE) {
- ret = -EFSCORRUPTED;
-- goto out_vunmap_cbuf;
-+ goto out_release;
- }
-
- ret = cops->decompress_pages(dic);
-@@ -818,17 +792,13 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic)
- }
- }
-
--out_vunmap_cbuf:
-- vm_unmap_ram(dic->cbuf, dic->nr_cpages);
--out_vunmap_rbuf:
-- vm_unmap_ram(dic->rbuf, dic->cluster_size);
--out_destroy_decompress_ctx:
-- if (cops->destroy_decompress_ctx)
-- cops->destroy_decompress_ctx(dic);
-+out_release:
-+ f2fs_release_decomp_mem(dic, bypass_callback, false);
-+
- out_end_io:
- trace_f2fs_decompress_pages_end(dic->inode, dic->cluster_idx,
- dic->clen, ret);
-- f2fs_decompress_end_io(dic, ret);
-+ f2fs_decompress_end_io(dic, ret, in_task);
- }
-
- /*
-@@ -838,7 +808,7 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic)
- * (or in the case of a failure, cleans up without actually decompressing).
- */
- void f2fs_end_read_compressed_page(struct page *page, bool failed,
-- block_t blkaddr)
-+ block_t blkaddr, bool in_task)
- {
- struct decompress_io_ctx *dic =
- (struct decompress_io_ctx *)page_private(page);
-@@ -848,12 +818,12 @@ void f2fs_end_read_compressed_page(struct page *page, bool failed,
-
- if (failed)
- WRITE_ONCE(dic->failed, true);
-- else if (blkaddr)
-+ else if (blkaddr && in_task)
- f2fs_cache_compressed_page(sbi, page,
- dic->inode->i_ino, blkaddr);
-
- if (atomic_dec_and_test(&dic->remaining_pages))
-- f2fs_decompress_cluster(dic);
-+ f2fs_decompress_cluster(dic, in_task);
- }
-
- static bool is_page_in_cluster(struct compress_ctx *cc, pgoff_t index)
-@@ -1552,16 +1522,85 @@ int f2fs_write_multi_pages(struct compress_ctx *cc,
- return err;
- }
-
--static void f2fs_free_dic(struct decompress_io_ctx *dic);
-+static inline bool allow_memalloc_for_decomp(struct f2fs_sb_info *sbi,
-+ bool pre_alloc)
-+{
-+ return pre_alloc ^ f2fs_low_mem_mode(sbi);
-+}
-+
-+static int f2fs_prepare_decomp_mem(struct decompress_io_ctx *dic,
-+ bool pre_alloc)
-+{
-+ const struct f2fs_compress_ops *cops =
-+ f2fs_cops[F2FS_I(dic->inode)->i_compress_algorithm];
-+ int i;
-+
-+ if (!allow_memalloc_for_decomp(F2FS_I_SB(dic->inode), pre_alloc))
-+ return 0;
-+
-+ dic->tpages = page_array_alloc(dic->inode, dic->cluster_size);
-+ if (!dic->tpages)
-+ return -ENOMEM;
-+
-+ for (i = 0; i < dic->cluster_size; i++) {
-+ if (dic->rpages[i]) {
-+ dic->tpages[i] = dic->rpages[i];
-+ continue;
-+ }
-+
-+ dic->tpages[i] = f2fs_compress_alloc_page();
-+ if (!dic->tpages[i])
-+ return -ENOMEM;
-+ }
-+
-+ dic->rbuf = f2fs_vmap(dic->tpages, dic->cluster_size);
-+ if (!dic->rbuf)
-+ return -ENOMEM;
-+
-+ dic->cbuf = f2fs_vmap(dic->cpages, dic->nr_cpages);
-+ if (!dic->cbuf)
-+ return -ENOMEM;
-+
-+ if (cops->init_decompress_ctx) {
-+ int ret = cops->init_decompress_ctx(dic);
-+
-+ if (ret)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static void f2fs_release_decomp_mem(struct decompress_io_ctx *dic,
-+ bool bypass_destroy_callback, bool pre_alloc)
-+{
-+ const struct f2fs_compress_ops *cops =
-+ f2fs_cops[F2FS_I(dic->inode)->i_compress_algorithm];
-+
-+ if (!allow_memalloc_for_decomp(F2FS_I_SB(dic->inode), pre_alloc))
-+ return;
-+
-+ if (!bypass_destroy_callback && cops->destroy_decompress_ctx)
-+ cops->destroy_decompress_ctx(dic);
-+
-+ if (dic->cbuf)
-+ vm_unmap_ram(dic->cbuf, dic->nr_cpages);
-+
-+ if (dic->rbuf)
-+ vm_unmap_ram(dic->rbuf, dic->cluster_size);
-+}
-+
-+static void f2fs_free_dic(struct decompress_io_ctx *dic,
-+ bool bypass_destroy_callback);
-
- struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
- {
- struct decompress_io_ctx *dic;
- pgoff_t start_idx = start_idx_of_cluster(cc);
-- int i;
-+ struct f2fs_sb_info *sbi = F2FS_I_SB(cc->inode);
-+ int i, ret;
-
-- dic = f2fs_kmem_cache_alloc(dic_entry_slab, GFP_F2FS_ZERO,
-- false, F2FS_I_SB(cc->inode));
-+ dic = f2fs_kmem_cache_alloc(dic_entry_slab, GFP_F2FS_ZERO, false, sbi);
- if (!dic)
- return ERR_PTR(-ENOMEM);
-
-@@ -1587,32 +1626,43 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc)
- dic->nr_rpages = cc->cluster_size;
-
- dic->cpages = page_array_alloc(dic->inode, dic->nr_cpages);
-- if (!dic->cpages)
-+ if (!dic->cpages) {
-+ ret = -ENOMEM;
- goto out_free;
-+ }
-
- for (i = 0; i < dic->nr_cpages; i++) {
- struct page *page;
-
- page = f2fs_compress_alloc_page();
-- if (!page)
-+ if (!page) {
-+ ret = -ENOMEM;
- goto out_free;
-+ }
-
- f2fs_set_compressed_page(page, cc->inode,
- start_idx + i + 1, dic);
- dic->cpages[i] = page;
- }
-
-+ ret = f2fs_prepare_decomp_mem(dic, true);
-+ if (ret)
-+ goto out_free;
-+
- return dic;
-
- out_free:
-- f2fs_free_dic(dic);
-- return ERR_PTR(-ENOMEM);
-+ f2fs_free_dic(dic, true);
-+ return ERR_PTR(ret);
- }
-
--static void f2fs_free_dic(struct decompress_io_ctx *dic)
-+static void f2fs_free_dic(struct decompress_io_ctx *dic,
-+ bool bypass_destroy_callback)
- {
- int i;
-
-+ f2fs_release_decomp_mem(dic, bypass_destroy_callback, true);
-+
- if (dic->tpages) {
- for (i = 0; i < dic->cluster_size; i++) {
- if (dic->rpages[i])
-@@ -1637,17 +1687,33 @@ static void f2fs_free_dic(struct decompress_io_ctx *dic)
- kmem_cache_free(dic_entry_slab, dic);
- }
-
--static void f2fs_put_dic(struct decompress_io_ctx *dic)
-+static void f2fs_late_free_dic(struct work_struct *work)
-+{
-+ struct decompress_io_ctx *dic =
-+ container_of(work, struct decompress_io_ctx, free_work);
-+
-+ f2fs_free_dic(dic, false);
-+}
-+
-+static void f2fs_put_dic(struct decompress_io_ctx *dic, bool in_task)
- {
-- if (refcount_dec_and_test(&dic->refcnt))
-- f2fs_free_dic(dic);
-+ if (refcount_dec_and_test(&dic->refcnt)) {
-+ if (in_task) {
-+ f2fs_free_dic(dic, false);
-+ } else {
-+ INIT_WORK(&dic->free_work, f2fs_late_free_dic);
-+ queue_work(F2FS_I_SB(dic->inode)->post_read_wq,
-+ &dic->free_work);
-+ }
-+ }
- }
-
- /*
- * Update and unlock the cluster's pagecache pages, and release the reference to
- * the decompress_io_ctx that was being held for I/O completion.
- */
--static void __f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed)
-+static void __f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed,
-+ bool in_task)
- {
- int i;
-
-@@ -1668,7 +1734,7 @@ static void __f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed)
- unlock_page(rpage);
- }
-
-- f2fs_put_dic(dic);
-+ f2fs_put_dic(dic, in_task);
- }
-
- static void f2fs_verify_cluster(struct work_struct *work)
-@@ -1685,14 +1751,15 @@ static void f2fs_verify_cluster(struct work_struct *work)
- SetPageError(rpage);
- }
-
-- __f2fs_decompress_end_io(dic, false);
-+ __f2fs_decompress_end_io(dic, false, true);
- }
-
- /*
- * This is called when a compressed cluster has been decompressed
- * (or failed to be read and/or decompressed).
- */
--void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed)
-+void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed,
-+ bool in_task)
- {
- if (!failed && dic->need_verity) {
- /*
-@@ -1704,7 +1771,7 @@ void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed)
- INIT_WORK(&dic->verity_work, f2fs_verify_cluster);
- fsverity_enqueue_verify_work(&dic->verity_work);
- } else {
-- __f2fs_decompress_end_io(dic, failed);
-+ __f2fs_decompress_end_io(dic, failed, in_task);
- }
- }
-
-@@ -1713,12 +1780,12 @@ void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed)
- *
- * This is called when the page is no longer needed and can be freed.
- */
--void f2fs_put_page_dic(struct page *page)
-+void f2fs_put_page_dic(struct page *page, bool in_task)
- {
- struct decompress_io_ctx *dic =
- (struct decompress_io_ctx *)page_private(page);
-
-- f2fs_put_dic(dic);
-+ f2fs_put_dic(dic, in_task);
- }
-
- /*
-diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
-index 3956852ad1de0..93c14dae4460a 100644
---- a/fs/f2fs/data.c
-+++ b/fs/f2fs/data.c
-@@ -120,7 +120,7 @@ struct bio_post_read_ctx {
- block_t fs_blkaddr;
- };
-
--static void f2fs_finish_read_bio(struct bio *bio)
-+static void f2fs_finish_read_bio(struct bio *bio, bool in_task)
- {
- struct bio_vec *bv;
- struct bvec_iter_all iter_all;
-@@ -134,8 +134,9 @@ static void f2fs_finish_read_bio(struct bio *bio)
-
- if (f2fs_is_compressed_page(page)) {
- if (bio->bi_status)
-- f2fs_end_read_compressed_page(page, true, 0);
-- f2fs_put_page_dic(page);
-+ f2fs_end_read_compressed_page(page, true, 0,
-+ in_task);
-+ f2fs_put_page_dic(page, in_task);
- continue;
- }
-
-@@ -192,7 +193,7 @@ static void f2fs_verify_bio(struct work_struct *work)
- fsverity_verify_bio(bio);
- }
-
-- f2fs_finish_read_bio(bio);
-+ f2fs_finish_read_bio(bio, true);
- }
-
- /*
-@@ -204,7 +205,7 @@ static void f2fs_verify_bio(struct work_struct *work)
- * can involve reading verity metadata pages from the file, and these verity
- * metadata pages may be encrypted and/or compressed.
- */
--static void f2fs_verify_and_finish_bio(struct bio *bio)
-+static void f2fs_verify_and_finish_bio(struct bio *bio, bool in_task)
- {
- struct bio_post_read_ctx *ctx = bio->bi_private;
-
-@@ -212,7 +213,7 @@ static void f2fs_verify_and_finish_bio(struct bio *bio)
- INIT_WORK(&ctx->work, f2fs_verify_bio);
- fsverity_enqueue_verify_work(&ctx->work);
- } else {
-- f2fs_finish_read_bio(bio);
-+ f2fs_finish_read_bio(bio, in_task);
- }
- }
-
-@@ -225,7 +226,8 @@ static void f2fs_verify_and_finish_bio(struct bio *bio)
- * that the bio includes at least one compressed page. The actual decompression
- * is done on a per-cluster basis, not a per-bio basis.
- */
--static void f2fs_handle_step_decompress(struct bio_post_read_ctx *ctx)
-+static void f2fs_handle_step_decompress(struct bio_post_read_ctx *ctx,
-+ bool in_task)
- {
- struct bio_vec *bv;
- struct bvec_iter_all iter_all;
-@@ -238,7 +240,7 @@ static void f2fs_handle_step_decompress(struct bio_post_read_ctx *ctx)
- /* PG_error was set if decryption failed. */
- if (f2fs_is_compressed_page(page))
- f2fs_end_read_compressed_page(page, PageError(page),
-- blkaddr);
-+ blkaddr, in_task);
- else
- all_compressed = false;
-
-@@ -263,15 +265,16 @@ static void f2fs_post_read_work(struct work_struct *work)
- fscrypt_decrypt_bio(ctx->bio);
-
- if (ctx->enabled_steps & STEP_DECOMPRESS)
-- f2fs_handle_step_decompress(ctx);
-+ f2fs_handle_step_decompress(ctx, true);
-
-- f2fs_verify_and_finish_bio(ctx->bio);
-+ f2fs_verify_and_finish_bio(ctx->bio, true);
- }
-
- static void f2fs_read_end_io(struct bio *bio)
- {
- struct f2fs_sb_info *sbi = F2FS_P_SB(bio_first_page_all(bio));
- struct bio_post_read_ctx *ctx;
-+ bool intask = in_task();
-
- iostat_update_and_unbind_ctx(bio, 0);
- ctx = bio->bi_private;
-@@ -282,16 +285,29 @@ static void f2fs_read_end_io(struct bio *bio)
- }
-
- if (bio->bi_status) {
-- f2fs_finish_read_bio(bio);
-+ f2fs_finish_read_bio(bio, intask);
- return;
- }
-
-- if (ctx && (ctx->enabled_steps & (STEP_DECRYPT | STEP_DECOMPRESS))) {
-- INIT_WORK(&ctx->work, f2fs_post_read_work);
-- queue_work(ctx->sbi->post_read_wq, &ctx->work);
-- } else {
-- f2fs_verify_and_finish_bio(bio);
-+ if (ctx) {
-+ unsigned int enabled_steps = ctx->enabled_steps &
-+ (STEP_DECRYPT | STEP_DECOMPRESS);
-+
-+ /*
-+ * If we have only decompression step between decompression and
-+ * decrypt, we don't need post processing for this.
-+ */
-+ if (enabled_steps == STEP_DECOMPRESS &&
-+ !f2fs_low_mem_mode(sbi)) {
-+ f2fs_handle_step_decompress(ctx, intask);
-+ } else if (enabled_steps) {
-+ INIT_WORK(&ctx->work, f2fs_post_read_work);
-+ queue_work(ctx->sbi->post_read_wq, &ctx->work);
-+ return;
-+ }
- }
-+
-+ f2fs_verify_and_finish_bio(bio, intask);
- }
-
- static void f2fs_write_end_io(struct bio *bio)
-@@ -2254,7 +2270,7 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
-
- if (f2fs_load_compressed_page(sbi, page, blkaddr)) {
- if (atomic_dec_and_test(&dic->remaining_pages))
-- f2fs_decompress_cluster(dic);
-+ f2fs_decompress_cluster(dic, true);
- continue;
- }
-
-@@ -2272,7 +2288,7 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
- page->index, for_write);
- if (IS_ERR(bio)) {
- ret = PTR_ERR(bio);
-- f2fs_decompress_end_io(dic, ret);
-+ f2fs_decompress_end_io(dic, ret, true);
- f2fs_put_dnode(&dn);
- *bio_ret = NULL;
- return ret;
-diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
-index fb53a35f31b04..1f6b735edc4d5 100644
---- a/fs/f2fs/f2fs.h
-+++ b/fs/f2fs/f2fs.h
-@@ -1557,6 +1557,7 @@ struct decompress_io_ctx {
- void *private; /* payload buffer for specified decompression algorithm */
- void *private2; /* extra payload buffer */
- struct work_struct verity_work; /* work to verify the decompressed pages */
-+ struct work_struct free_work; /* work for late free this structure itself */
- };
-
- #define NULL_CLUSTER ((unsigned int)(~0))
-@@ -4058,9 +4059,9 @@ 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_cluster(struct decompress_io_ctx *dic);
-+void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task);
- void f2fs_end_read_compressed_page(struct page *page, bool failed,
-- block_t blkaddr);
-+ block_t blkaddr, bool in_task);
- bool f2fs_cluster_is_empty(struct compress_ctx *cc);
- bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index);
- bool f2fs_sanity_check_cluster(struct dnode_of_data *dn);
-@@ -4077,8 +4078,9 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
- unsigned nr_pages, sector_t *last_block_in_bio,
- bool is_readahead, bool for_write);
- struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc);
--void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed);
--void f2fs_put_page_dic(struct page *page);
-+void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed,
-+ bool in_task);
-+void f2fs_put_page_dic(struct page *page, bool in_task);
- unsigned int f2fs_cluster_blocks_are_contiguous(struct dnode_of_data *dn);
- int f2fs_init_compress_ctx(struct compress_ctx *cc);
- void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse);
-@@ -4124,13 +4126,14 @@ static inline struct page *f2fs_compress_control_page(struct page *page)
- }
- static inline int f2fs_init_compress_mempool(void) { return 0; }
- static inline void f2fs_destroy_compress_mempool(void) { }
--static inline void f2fs_decompress_cluster(struct decompress_io_ctx *dic) { }
-+static inline void f2fs_decompress_cluster(struct decompress_io_ctx *dic,
-+ bool in_task) { }
- static inline void f2fs_end_read_compressed_page(struct page *page,
-- bool failed, block_t blkaddr)
-+ bool failed, block_t blkaddr, bool in_task)
- {
- WARN_ON_ONCE(1);
- }
--static inline void f2fs_put_page_dic(struct page *page)
-+static inline void f2fs_put_page_dic(struct page *page, bool in_task)
- {
- WARN_ON_ONCE(1);
- }
---
-2.42.0
-
+++ /dev/null
-From c9b88216941edab0e607dea249f4d4d0df4776c3 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Mon, 20 Jun 2022 10:38:42 -0700
-Subject: f2fs: introduce memory mode
-
-From: Daeho Jeong <daehojeong@google.com>
-
-[ Upstream commit 7a8fc586180d8c57db5cc1e2acb32bb9986f642b ]
-
-Introduce memory mode to supports "normal" and "low" memory modes.
-"low" mode is to support low memory devices. Because of the nature of
-low memory devices, in this mode, f2fs will try to save memory sometimes
-by sacrificing performance. "normal" mode is the default mode and same
-as before.
-
-Signed-off-by: Daeho Jeong <daehojeong@google.com>
-Reviewed-by: Chao Yu <chao@kernel.org>
-Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
-Stable-dep-of: b0327c84e91a ("f2fs: compress: fix to avoid use-after-free on dic")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- Documentation/filesystems/f2fs.rst | 5 +++++
- fs/f2fs/f2fs.h | 13 +++++++++++++
- fs/f2fs/super.c | 24 ++++++++++++++++++++++++
- 3 files changed, 42 insertions(+)
-
-diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst
-index 7fe50b0bccde9..415bba41597c3 100644
---- a/Documentation/filesystems/f2fs.rst
-+++ b/Documentation/filesystems/f2fs.rst
-@@ -323,6 +323,11 @@ discard_unit=%s Control discard unit, the argument can be "block", "segment"
- default, it is helpful for large sized SMR or ZNS devices to
- reduce memory cost by getting rid of fs metadata supports small
- discard.
-+memory=%s Control memory mode. This supports "normal" and "low" modes.
-+ "low" mode is introduced to support low memory devices.
-+ Because of the nature of low memory devices, in this mode, f2fs
-+ will try to save memory sometimes by sacrificing performance.
-+ "normal" mode is the default mode and same as before.
- ======================== ============================================================
-
- Debugfs Entries
-diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
-index 835ef98643bd4..fb53a35f31b04 100644
---- a/fs/f2fs/f2fs.h
-+++ b/fs/f2fs/f2fs.h
-@@ -142,6 +142,7 @@ struct f2fs_mount_info {
- int fsync_mode; /* fsync policy */
- int fs_mode; /* fs mode: LFS or ADAPTIVE */
- int bggc_mode; /* bggc mode: off, on or sync */
-+ int memory_mode; /* memory mode */
- int discard_unit; /*
- * discard command's offset/size should
- * be aligned to this unit: block,
-@@ -1330,6 +1331,13 @@ enum {
- DISCARD_UNIT_SECTION, /* basic discard unit is section */
- };
-
-+enum {
-+ MEMORY_MODE_NORMAL, /* memory mode for normal devices */
-+ MEMORY_MODE_LOW, /* memory mode for low memry devices */
-+};
-+
-+
-+
- static inline int f2fs_test_bit(unsigned int nr, char *addr);
- static inline void f2fs_set_bit(unsigned int nr, char *addr);
- static inline void f2fs_clear_bit(unsigned int nr, char *addr);
-@@ -4294,6 +4302,11 @@ static inline bool f2fs_lfs_mode(struct f2fs_sb_info *sbi)
- return F2FS_OPTION(sbi).fs_mode == FS_MODE_LFS;
- }
-
-+static inline bool f2fs_low_mem_mode(struct f2fs_sb_info *sbi)
-+{
-+ return F2FS_OPTION(sbi).memory_mode == MEMORY_MODE_LOW;
-+}
-+
- static inline bool f2fs_may_compress(struct inode *inode)
- {
- if (IS_SWAPFILE(inode) || f2fs_is_pinned_file(inode) ||
-diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
-index 5c0b2b300aa1b..4b862e4a6a110 100644
---- a/fs/f2fs/super.c
-+++ b/fs/f2fs/super.c
-@@ -159,6 +159,7 @@ enum {
- Opt_gc_merge,
- Opt_nogc_merge,
- Opt_discard_unit,
-+ Opt_memory_mode,
- Opt_err,
- };
-
-@@ -236,6 +237,7 @@ static match_table_t f2fs_tokens = {
- {Opt_gc_merge, "gc_merge"},
- {Opt_nogc_merge, "nogc_merge"},
- {Opt_discard_unit, "discard_unit=%s"},
-+ {Opt_memory_mode, "memory=%s"},
- {Opt_err, NULL},
- };
-
-@@ -1241,6 +1243,22 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
- }
- kfree(name);
- break;
-+ case Opt_memory_mode:
-+ name = match_strdup(&args[0]);
-+ if (!name)
-+ return -ENOMEM;
-+ if (!strcmp(name, "normal")) {
-+ F2FS_OPTION(sbi).memory_mode =
-+ MEMORY_MODE_NORMAL;
-+ } else if (!strcmp(name, "low")) {
-+ F2FS_OPTION(sbi).memory_mode =
-+ MEMORY_MODE_LOW;
-+ } else {
-+ kfree(name);
-+ return -EINVAL;
-+ }
-+ kfree(name);
-+ break;
- default:
- f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
- p);
-@@ -2012,6 +2030,11 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
- else if (F2FS_OPTION(sbi).discard_unit == DISCARD_UNIT_SECTION)
- seq_printf(seq, ",discard_unit=%s", "section");
-
-+ if (F2FS_OPTION(sbi).memory_mode == MEMORY_MODE_NORMAL)
-+ seq_printf(seq, ",memory=%s", "normal");
-+ else if (F2FS_OPTION(sbi).memory_mode == MEMORY_MODE_LOW)
-+ seq_printf(seq, ",memory=%s", "low");
-+
- return 0;
- }
-
-@@ -2034,6 +2057,7 @@ static void default_options(struct f2fs_sb_info *sbi)
- F2FS_OPTION(sbi).compress_ext_cnt = 0;
- F2FS_OPTION(sbi).compress_mode = COMPR_MODE_FS;
- F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
-+ F2FS_OPTION(sbi).memory_mode = MEMORY_MODE_NORMAL;
-
- sbi->sb->s_flags &= ~SB_INLINECRYPT;
-
---
-2.42.0
-