[RAMEN9610-8702][COMMON] diskcipher: support f2fs
[GitHub/LineageOS/android_kernel_motorola_exynos9610.git] / fs / f2fs / data.c
index b42e8d3539e8d719a64a35d4a3a386172aff7fda..513e24024a75b58b5dfad85335c4b5bd2f2cf7ff 100644 (file)
@@ -19,8 +19,6 @@
 #include <linux/bio.h>
 #include <linux/prefetch.h>
 #include <linux/uio.h>
-#include <linux/mm.h>
-#include <linux/memcontrol.h>
 #include <linux/cleancache.h>
 #include <linux/sched/signal.h>
 
 #include <trace/events/f2fs.h>
 #include <trace/events/android_fs.h>
 
+#define NUM_PREALLOC_POST_READ_CTXS    128
+
+static struct kmem_cache *bio_post_read_ctx_cache;
+static mempool_t *bio_post_read_ctx_pool;
+
 static bool __is_cp_guaranteed(struct page *page)
 {
        struct address_space *mapping = page->mapping;
@@ -51,11 +54,126 @@ static bool __is_cp_guaranteed(struct page *page)
        return false;
 }
 
-static void f2fs_read_end_io(struct bio *bio)
+/* postprocessing steps for read bios */
+enum bio_post_read_step {
+       STEP_INITIAL = 0,
+       STEP_DECRYPT,
+};
+
+struct bio_post_read_ctx {
+       struct bio *bio;
+       struct work_struct work;
+       unsigned int cur_step;
+       unsigned int enabled_steps;
+};
+
+/* device unit number for iv sector */
+#define PG_DUN(i,p)                                            \
+       ((((i)->i_ino & 0xffffffff) << 32) | ((p)->index & 0xffffffff))
+
+static inline bool f2fs_may_encrypt_bio(struct inode *inode,
+               struct f2fs_io_info *fio)
 {
-       struct bio_vec *bvec;
+#ifdef CONFIG_CRYPTO_DISKCIPHER
+       if (fio && (fio->type != DATA || fio->encrypted_page))
+               return false;
+
+       return (f2fs_encrypted_file(inode) &&
+                       fscrypt_disk_encrypted(inode));
+#else
+       return false;
+#endif
+}
+
+static inline bool f2fs_bio_disk_encrypted(unsigned int bi_opf)
+{
+       if (bi_opf & REQ_CRYPT)
+               return true;
+       else
+               return false;
+}
+
+static bool f2fs_mergeable_bio(struct bio *bio, u64 dun, void *ci, bool bio_encrypted)
+{
+#ifdef CONFIG_CRYPTO_DISKCIPHER
+       if (!bio)
+               return true;
+
+       /* if both of them are not encrypted, no further check is needed */
+       if (!f2fs_bio_disk_encrypted(bio->bi_opf) && !bio_encrypted)
+               return true;
+
+#ifdef CONFIG_CRYPTO_DISKCIPHER_DUN
+       if (bio->bi_aux_private == ci)
+               return bio_end_dun(bio) == dun;
+       else
+               return false;
+#else
+       return bio->bi_aux_private == ci;
+#endif
+#else
+       return true;
+#endif
+}
+
+static void __read_end_io(struct bio *bio)
+{
+       struct page *page;
+       struct bio_vec *bv;
        int i;
 
+       bio_for_each_segment_all(bv, bio, i) {
+               page = bv->bv_page;
+
+               /* PG_error was set if any post_read step failed */
+               if (bio->bi_status || PageError(page)) {
+                       ClearPageUptodate(page);
+                       SetPageError(page);
+               } else {
+                       SetPageUptodate(page);
+               }
+               unlock_page(page);
+       }
+       if (bio->bi_private)
+               mempool_free(bio->bi_private, bio_post_read_ctx_pool);
+       bio_put(bio);
+}
+
+static void bio_post_read_processing(struct bio_post_read_ctx *ctx);
+
+static void decrypt_work(struct work_struct *work)
+{
+       struct bio_post_read_ctx *ctx =
+               container_of(work, struct bio_post_read_ctx, work);
+
+       fscrypt_decrypt_bio(ctx->bio);
+
+       bio_post_read_processing(ctx);
+}
+
+static void bio_post_read_processing(struct bio_post_read_ctx *ctx)
+{
+       switch (++ctx->cur_step) {
+       case STEP_DECRYPT:
+               if (ctx->enabled_steps & (1 << STEP_DECRYPT)) {
+                       INIT_WORK(&ctx->work, decrypt_work);
+                       fscrypt_enqueue_decrypt_work(&ctx->work);
+                       return;
+               }
+               ctx->cur_step++;
+               /* fall-through */
+       default:
+               __read_end_io(ctx->bio);
+       }
+}
+
+static bool f2fs_bio_post_read_required(struct bio *bio)
+{
+       return bio->bi_private && !bio->bi_status;
+}
+
+static void f2fs_read_end_io(struct bio *bio)
+{
 #ifdef CONFIG_F2FS_FAULT_INJECTION
        if (time_to_inject(F2FS_P_SB(bio->bi_io_vec->bv_page), FAULT_IO)) {
                f2fs_show_injection_info(FAULT_IO);
@@ -63,28 +181,19 @@ static void f2fs_read_end_io(struct bio *bio)
        }
 #endif
 
-       if (f2fs_bio_encrypted(bio)) {
-               if (bio->bi_status) {
-                       fscrypt_release_ctx(bio->bi_private);
-               } else {
-                       fscrypt_decrypt_bio_pages(bio->bi_private, bio);
-                       return;
-               }
-       }
+       if(f2fs_bio_disk_encrypted(bio->bi_opf))
+               goto end_io;
 
-       bio_for_each_segment_all(bvec, bio, i) {
-               struct page *page = bvec->bv_page;
+       if (f2fs_bio_post_read_required(bio)) {
+               struct bio_post_read_ctx *ctx = bio->bi_private;
 
-               if (!bio->bi_status) {
-                       if (!PageUptodate(page))
-                               SetPageUptodate(page);
-               } else {
-                       ClearPageUptodate(page);
-                       SetPageError(page);
-               }
-               unlock_page(page);
+               ctx->cur_step = STEP_INITIAL;
+               bio_post_read_processing(ctx);
+               return;
        }
-       bio_put(bio);
+
+end_io:
+       __read_end_io(bio);
 }
 
 static void f2fs_write_end_io(struct bio *bio)
@@ -251,7 +360,10 @@ static void __submit_merged_bio(struct f2fs_bio_info *io)
        if (!io->bio)
                return;
 
-       bio_set_op_attrs(io->bio, fio->op, fio->op_flags);
+       if (f2fs_bio_disk_encrypted(io->bio->bi_opf))
+               bio_set_op_attrs(io->bio, fio->op, fio->op_flags | REQ_CRYPT);
+       else
+               bio_set_op_attrs(io->bio, fio->op, fio->op_flags);
 
        if (is_read_io(fio->op))
                trace_f2fs_prepare_read_bio(io->sbi->sb, fio->type, io->bio);
@@ -383,6 +495,7 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio)
        struct bio *bio;
        struct page *page = fio->encrypted_page ?
                        fio->encrypted_page : fio->page;
+       struct inode *inode = fio->page->mapping->host;
 
        verify_block_addr(fio, fio->new_blkaddr);
        trace_f2fs_submit_page_bio(page, fio);
@@ -396,8 +509,12 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio)
                bio_put(bio);
                return -EFAULT;
        }
+       fio->op_flags |= fio->encrypted_page ? REQ_NOENCRYPT : 0;
        bio_set_op_attrs(bio, fio->op, fio->op_flags);
 
+       if (f2fs_may_encrypt_bio(inode, fio))
+               fscrypt_set_bio(inode, bio, PG_DUN(inode, fio->page));
+
        __submit_bio(fio->sbi, bio, fio->type);
 
        if (!is_read_io(fio->op))
@@ -412,6 +529,9 @@ int f2fs_submit_page_write(struct f2fs_io_info *fio)
        struct f2fs_bio_info *io = sbi->write_io[btype] + fio->temp;
        struct page *bio_page;
        int err = 0;
+       struct inode *inode;
+       bool bio_encrypted;
+       u64 dun;
 
        f2fs_bug_on(sbi, is_read_io(fio->op));
 
@@ -434,6 +554,10 @@ next:
        verify_block_addr(fio, fio->new_blkaddr);
 
        bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page;
+       inode = fio->page->mapping->host;
+       dun = PG_DUN(inode, fio->page);
+       bio_encrypted = f2fs_may_encrypt_bio(inode, fio);
+       fio->op_flags |= fio->encrypted_page ? REQ_NOENCRYPT : 0;
 
        /* set submitted = true as a return value */
        fio->submitted = true;
@@ -444,6 +568,10 @@ next:
            (io->fio.op != fio->op || io->fio.op_flags != fio->op_flags) ||
                        !__same_bdev(sbi, fio->new_blkaddr, io->bio)))
                __submit_merged_bio(io);
+
+       if (!f2fs_mergeable_bio(io->bio, dun, fscrypt_get_diskcipher(inode), bio_encrypted))
+               __submit_merged_bio(io);
+
 alloc_new:
        if (io->bio == NULL) {
                if ((fio->type == DATA || fio->type == NODE) &&
@@ -455,6 +583,9 @@ alloc_new:
                io->bio = __bio_alloc(sbi, fio->new_blkaddr, fio->io_wbc,
                                                BIO_MAX_PAGES, false,
                                                fio->type, fio->temp);
+               if (bio_encrypted)
+                       fscrypt_set_bio(inode, io->bio, dun);
+
                io->fio = *fio;
        }
 
@@ -482,29 +613,37 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
                                                         unsigned nr_pages)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
-       struct fscrypt_ctx *ctx = NULL;
        struct bio *bio;
-
-       if (f2fs_encrypted_file(inode)) {
-               ctx = fscrypt_get_ctx(inode, GFP_NOFS);
-               if (IS_ERR(ctx))
-                       return ERR_CAST(ctx);
-
-               /* wait the page to be moved by cleaning */
-               f2fs_wait_on_block_writeback(sbi, blkaddr);
-       }
+       struct bio_post_read_ctx *ctx;
+       unsigned int post_read_steps = 0;
 
        bio = f2fs_bio_alloc(sbi, min_t(int, nr_pages, BIO_MAX_PAGES), false);
-       if (!bio) {
-               if (ctx)
-                       fscrypt_release_ctx(ctx);
+       if (!bio)
                return ERR_PTR(-ENOMEM);
-       }
        f2fs_target_device(sbi, blkaddr, bio);
        bio->bi_end_io = f2fs_read_end_io;
-       bio->bi_private = ctx;
        bio_set_op_attrs(bio, REQ_OP_READ, 0);
 
+       if (f2fs_encrypted_file(inode) && !fscrypt_disk_encrypted(inode))
+               post_read_steps |= 1 << STEP_DECRYPT;
+       if (post_read_steps) {
+               ctx = mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS);
+               if (!ctx) {
+                       bio_put(bio);
+                       return ERR_PTR(-ENOMEM);
+               }
+               ctx->bio = bio;
+               ctx->enabled_steps = post_read_steps;
+               bio->bi_private = ctx;
+               bio_set_op_attrs(bio, REQ_OP_READ,
+                       (f2fs_encrypted_inode(inode) ?
+                        REQ_NOENCRYPT :
+                        0));
+
+               /* wait the page to be moved by cleaning */
+               f2fs_wait_on_block_writeback(sbi, blkaddr);
+       }
+
        return bio;
 }
 
@@ -521,6 +660,10 @@ static int f2fs_submit_page_read(struct inode *inode, struct page *page,
                bio_put(bio);
                return -EFAULT;
        }
+
+       if (f2fs_may_encrypt_bio(inode, NULL))
+               fscrypt_set_bio(inode, bio, PG_DUN(inode, page));
+
        __submit_bio(F2FS_I_SB(inode), bio, DATA);
        return 0;
 }
@@ -1376,6 +1519,8 @@ static int f2fs_mpage_readpages(struct address_space *mapping,
        sector_t last_block_in_file;
        sector_t block_nr;
        struct f2fs_map_blocks map;
+       bool bio_encrypted;
+       u64 dun;
 
        map.m_pblk = 0;
        map.m_lblk = 0;
@@ -1453,12 +1598,22 @@ submit_and_realloc:
                        __submit_bio(F2FS_I_SB(inode), bio, DATA);
                        bio = NULL;
                }
+
+               dun = PG_DUN(inode, page);
+               bio_encrypted = f2fs_may_encrypt_bio(inode, NULL);
+               if (!f2fs_mergeable_bio(bio, dun, fscrypt_get_diskcipher(inode), bio_encrypted)) {
+                       __submit_bio(F2FS_I_SB(inode), bio, DATA);
+                       bio = NULL;
+               }
+
                if (bio == NULL) {
                        bio = f2fs_grab_read_bio(inode, block_nr, nr_pages);
                        if (IS_ERR(bio)) {
                                bio = NULL;
                                goto set_error_page;
                        }
+                       if (f2fs_may_encrypt_bio(inode, NULL))
+                               fscrypt_set_bio(inode, bio, dun);
                }
 
                if (bio_add_page(bio, page, blocksize, 0) < blocksize)
@@ -1526,10 +1681,13 @@ static int encrypt_one_page(struct f2fs_io_info *fio)
        if (!f2fs_encrypted_file(inode))
                return 0;
 
-       /* wait for GCed encrypted page writeback */
+       /* wait for GCed page writeback via META_MAPPING */
        f2fs_wait_on_block_writeback(fio->sbi, fio->old_blkaddr);
 
 retry_encrypt:
+       if (fscrypt_disk_encrypted(inode))
+               return 0;
+
        fio->encrypted_page = fscrypt_encrypt_page(inode, fio->page,
                        PAGE_SIZE, 0, fio->page->index, gfp_flags);
        if (!IS_ERR(fio->encrypted_page))
@@ -1676,6 +1834,7 @@ got_it:
                        goto out_writepage;
 
                set_page_writeback(page);
+               ClearPageError(page);
                f2fs_put_dnode(&dn);
                if (fio->need_lock == LOCK_REQ)
                        f2fs_unlock_op(fio->sbi);
@@ -1698,6 +1857,7 @@ got_it:
                goto out_writepage;
 
        set_page_writeback(page);
+       ClearPageError(page);
 
        /* LFS mode write path */
        write_data_page(&dn, fio);
@@ -1843,7 +2003,13 @@ out:
 
 redirty_out:
        redirty_page_for_writepage(wbc, page);
-       if (!err)
+       /*
+        * pageout() in MM traslates EAGAIN, so calls handle_write_error()
+        * -> mapping_set_error() -> set_bit(AS_EIO, ...).
+        * file_write_and_wait_range() will see EIO error, which is critical
+        * to return value of fsync() followed by atomic_write failure to user.
+        */
+       if (!err || wbc->for_reclaim)
                return AOP_WRITEPAGE_ACTIVATE;
        unlock_page(page);
        return err;
@@ -2238,8 +2404,8 @@ repeat:
 
        f2fs_wait_on_page_writeback(page, DATA, false);
 
-       /* wait for GCed encrypted page writeback */
-       if (f2fs_encrypted_file(inode))
+       /* wait for GCed page writeback via META_MAPPING */
+       if (f2fs_post_read_required(inode))
                f2fs_wait_on_block_writeback(sbi, blkaddr);
 
        if (len == PAGE_SIZE || PageUptodate(page))
@@ -2452,35 +2618,6 @@ int f2fs_release_page(struct page *page, gfp_t wait)
        return 1;
 }
 
-/*
- * This was copied from __set_page_dirty_buffers which gives higher performance
- * in very high speed storages. (e.g., pmem)
- */
-void f2fs_set_page_dirty_nobuffers(struct page *page)
-{
-       struct address_space *mapping = page->mapping;
-       unsigned long flags;
-
-       if (unlikely(!mapping))
-               return;
-
-       spin_lock(&mapping->private_lock);
-       lock_page_memcg(page);
-       SetPageDirty(page);
-       spin_unlock(&mapping->private_lock);
-
-       spin_lock_irqsave(&mapping->tree_lock, flags);
-       WARN_ON_ONCE(!PageUptodate(page));
-       account_page_dirtied(page, mapping);
-       radix_tree_tag_set(&mapping->page_tree,
-                       page_index(page), PAGECACHE_TAG_DIRTY);
-       spin_unlock_irqrestore(&mapping->tree_lock, flags);
-       unlock_page_memcg(page);
-
-       __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
-       return;
-}
-
 static int f2fs_set_data_page_dirty(struct page *page)
 {
        struct address_space *mapping = page->mapping;
@@ -2504,7 +2641,7 @@ static int f2fs_set_data_page_dirty(struct page *page)
        }
 
        if (!PageDirty(page)) {
-               f2fs_set_page_dirty_nobuffers(page);
+               __set_page_dirty_nobuffers(page);
                update_dirty_page(inode, page);
                return 1;
        }
@@ -2600,3 +2737,27 @@ const struct address_space_operations f2fs_dblock_aops = {
        .migratepage    = f2fs_migrate_page,
 #endif
 };
+
+int __init f2fs_init_post_read_processing(void)
+{
+       bio_post_read_ctx_cache = KMEM_CACHE(bio_post_read_ctx, 0);
+       if (!bio_post_read_ctx_cache)
+               goto fail;
+       bio_post_read_ctx_pool =
+               mempool_create_slab_pool(NUM_PREALLOC_POST_READ_CTXS,
+                                        bio_post_read_ctx_cache);
+       if (!bio_post_read_ctx_pool)
+               goto fail_free_cache;
+       return 0;
+
+fail_free_cache:
+       kmem_cache_destroy(bio_post_read_ctx_cache);
+fail:
+       return -ENOMEM;
+}
+
+void __exit f2fs_destroy_post_read_processing(void)
+{
+       mempool_destroy(bio_post_read_ctx_pool);
+       kmem_cache_destroy(bio_post_read_ctx_cache);
+}