From 3964019cf7c5dfad86f53e1b9cfd7c63151faf7e Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 26 Mar 2016 16:04:00 -0400 Subject: [PATCH] add patch crypto-dont-let-data-integrity-writebacks-fail-with-ENOMEM --- ...-let-data-integrity-writebacks-fail-with-ENOMEM | 208 +++++++++++++++++++++ series | 1 + 2 files changed, 209 insertions(+) create mode 100644 crypto-dont-let-data-integrity-writebacks-fail-with-ENOMEM diff --git a/crypto-dont-let-data-integrity-writebacks-fail-with-ENOMEM b/crypto-dont-let-data-integrity-writebacks-fail-with-ENOMEM new file mode 100644 index 00000000..8af58b19 --- /dev/null +++ b/crypto-dont-let-data-integrity-writebacks-fail-with-ENOMEM @@ -0,0 +1,208 @@ +ext4 crypto: don't let data integrity writebacks fail with ENOMEM + +We don't want the writeback triggered from the journal commit (in +data=writeback mode) to cause the journal to abort due to +generic_writepages() returning an ENOMEM error. In addition, if +fsync() fails with ENOMEM, most applications will probably not do the +right thing. + +So if we are doing a data integrity sync, and ext4_encrypt() returns +ENOMEM, we will submit any queued I/O to date, and then retry the +allocation using GFP_NOFAIL. + +Signed-off-by: Theodore Ts'o +--- + fs/ext4/crypto.c | 35 ++++++++++++++++++++--------------- + fs/ext4/ext4.h | 6 ++++-- + fs/ext4/page-io.c | 14 +++++++++++++- + fs/ext4/readpage.c | 2 +- + 4 files changed, 38 insertions(+), 19 deletions(-) + +diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c +index ecb5439..3dc8b76 100644 +--- a/fs/ext4/crypto.c ++++ b/fs/ext4/crypto.c +@@ -93,7 +93,8 @@ void ext4_release_crypto_ctx(struct ext4_crypto_ctx *ctx) + * Return: An allocated and initialized encryption context on success; error + * value or NULL otherwise. + */ +-struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode) ++struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode, ++ gfp_t gfp_flags) + { + struct ext4_crypto_ctx *ctx = NULL; + int res = 0; +@@ -120,7 +121,7 @@ struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode) + list_del(&ctx->free_list); + spin_unlock_irqrestore(&ext4_crypto_ctx_lock, flags); + if (!ctx) { +- ctx = kmem_cache_zalloc(ext4_crypto_ctx_cachep, GFP_NOFS); ++ ctx = kmem_cache_zalloc(ext4_crypto_ctx_cachep, gfp_flags); + if (!ctx) { + res = -ENOMEM; + goto out; +@@ -257,7 +258,8 @@ static int ext4_page_crypto(struct inode *inode, + ext4_direction_t rw, + pgoff_t index, + struct page *src_page, +- struct page *dest_page) ++ struct page *dest_page, ++ gfp_t gfp_flags) + + { + u8 xts_tweak[EXT4_XTS_TWEAK_SIZE]; +@@ -268,7 +270,7 @@ static int ext4_page_crypto(struct inode *inode, + struct crypto_ablkcipher *tfm = ci->ci_ctfm; + int res = 0; + +- req = ablkcipher_request_alloc(tfm, GFP_NOFS); ++ req = ablkcipher_request_alloc(tfm, gfp_flags); + if (!req) { + printk_ratelimited(KERN_ERR + "%s: crypto_request_alloc() failed\n", +@@ -309,9 +311,10 @@ static int ext4_page_crypto(struct inode *inode, + return 0; + } + +-static struct page *alloc_bounce_page(struct ext4_crypto_ctx *ctx) ++static struct page *alloc_bounce_page(struct ext4_crypto_ctx *ctx, ++ gfp_t gfp_flags) + { +- ctx->w.bounce_page = mempool_alloc(ext4_bounce_page_pool, GFP_NOWAIT); ++ ctx->w.bounce_page = mempool_alloc(ext4_bounce_page_pool, gfp_flags); + if (ctx->w.bounce_page == NULL) + return ERR_PTR(-ENOMEM); + ctx->flags |= EXT4_WRITE_PATH_FL; +@@ -334,7 +337,8 @@ static struct page *alloc_bounce_page(struct ext4_crypto_ctx *ctx) + * error value or NULL. + */ + struct page *ext4_encrypt(struct inode *inode, +- struct page *plaintext_page) ++ struct page *plaintext_page, ++ gfp_t gfp_flags) + { + struct ext4_crypto_ctx *ctx; + struct page *ciphertext_page = NULL; +@@ -342,17 +346,17 @@ struct page *ext4_encrypt(struct inode *inode, + + BUG_ON(!PageLocked(plaintext_page)); + +- ctx = ext4_get_crypto_ctx(inode); ++ ctx = ext4_get_crypto_ctx(inode, gfp_flags); + if (IS_ERR(ctx)) + return (struct page *) ctx; + + /* The encryption operation will require a bounce page. */ +- ciphertext_page = alloc_bounce_page(ctx); ++ ciphertext_page = alloc_bounce_page(ctx, gfp_flags); + if (IS_ERR(ciphertext_page)) + goto errout; + ctx->w.control_page = plaintext_page; + err = ext4_page_crypto(inode, EXT4_ENCRYPT, plaintext_page->index, +- plaintext_page, ciphertext_page); ++ plaintext_page, ciphertext_page, gfp_flags); + if (err) { + ciphertext_page = ERR_PTR(err); + errout: +@@ -380,8 +384,8 @@ int ext4_decrypt(struct page *page) + { + BUG_ON(!PageLocked(page)); + +- return ext4_page_crypto(page->mapping->host, +- EXT4_DECRYPT, page->index, page, page); ++ return ext4_page_crypto(page->mapping->host, EXT4_DECRYPT, ++ page->index, page, page, GFP_NOFS); + } + + int ext4_encrypted_zeroout(struct inode *inode, ext4_lblk_t lblk, +@@ -400,11 +404,11 @@ int ext4_encrypted_zeroout(struct inode *inode, ext4_lblk_t lblk, + + BUG_ON(inode->i_sb->s_blocksize != PAGE_CACHE_SIZE); + +- ctx = ext4_get_crypto_ctx(inode); ++ ctx = ext4_get_crypto_ctx(inode, GFP_NOFS); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + +- ciphertext_page = alloc_bounce_page(ctx); ++ ciphertext_page = alloc_bounce_page(ctx, GFP_NOWAIT); + if (IS_ERR(ciphertext_page)) { + err = PTR_ERR(ciphertext_page); + goto errout; +@@ -412,7 +416,8 @@ int ext4_encrypted_zeroout(struct inode *inode, ext4_lblk_t lblk, + + while (len--) { + err = ext4_page_crypto(inode, EXT4_ENCRYPT, lblk, +- ZERO_PAGE(0), ciphertext_page); ++ ZERO_PAGE(0), ciphertext_page, ++ GFP_NOFS); + if (err) + goto errout; + +diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h +index 393689d..1bd7f77 100644 +--- a/fs/ext4/ext4.h ++++ b/fs/ext4/ext4.h +@@ -2291,11 +2291,13 @@ extern struct kmem_cache *ext4_crypt_info_cachep; + bool ext4_valid_contents_enc_mode(uint32_t mode); + uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size); + extern struct workqueue_struct *ext4_read_workqueue; +-struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode); ++struct ext4_crypto_ctx *ext4_get_crypto_ctx(struct inode *inode, ++ gfp_t gfp_flags); + void ext4_release_crypto_ctx(struct ext4_crypto_ctx *ctx); + void ext4_restore_control_page(struct page *data_page); + struct page *ext4_encrypt(struct inode *inode, +- struct page *plaintext_page); ++ struct page *plaintext_page, ++ gfp_t gfp_flags); + int ext4_decrypt(struct page *page); + int ext4_encrypted_zeroout(struct inode *inode, ext4_lblk_t lblk, + ext4_fsblk_t pblk, ext4_lblk_t len); +diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c +index 349d7aa..614bb13 100644 +--- a/fs/ext4/page-io.c ++++ b/fs/ext4/page-io.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + #include "ext4_jbd2.h" + #include "xattr.h" +@@ -480,9 +481,20 @@ int ext4_bio_write_page(struct ext4_io_submit *io, + + if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode) && + nr_to_submit) { +- data_page = ext4_encrypt(inode, page); ++ gfp_t gfp_flags = GFP_NOFS; ++ ++ retry_encrypt: ++ data_page = ext4_encrypt(inode, page, gfp_flags); + if (IS_ERR(data_page)) { + ret = PTR_ERR(data_page); ++ if (ret == ENOMEM && wbc->sync_mode == WB_SYNC_ALL) { ++ if (io->io_bio) { ++ ext4_io_submit(io); ++ congestion_wait(BLK_RW_ASYNC, HZ/50); ++ } ++ gfp_flags |= __GFP_NOFAIL; ++ goto retry_encrypt; ++ } + data_page = NULL; + goto out; + } +diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c +index 5dc5e95..bc7642f 100644 +--- a/fs/ext4/readpage.c ++++ b/fs/ext4/readpage.c +@@ -279,7 +279,7 @@ int ext4_mpage_readpages(struct address_space *mapping, + + if (ext4_encrypted_inode(inode) && + S_ISREG(inode->i_mode)) { +- ctx = ext4_get_crypto_ctx(inode); ++ ctx = ext4_get_crypto_ctx(inode, GFP_NOFS); + if (IS_ERR(ctx)) + goto set_error_page; + } diff --git a/series b/series index ecf92413..a12c9c0d 100644 --- a/series +++ b/series @@ -6,6 +6,7 @@ use-dget_parent-in-ext4_file_open use-file_dentry use-dget_parent-in-ext4_d_revalidate check-if-in-inode-xattr-is-corrupted-in-ext4_expand_extra_isize_ea +crypto-dont-let-data-integrity-writebacks-fail-with-ENOMEM ########################################## # unstable patches -- 2.11.4.GIT