ext4 crypto: don't let data integrity writebacks fail with ENOMEM
authorTheodore Ts'o <tytso@mit.edu>
Sat, 26 Mar 2016 20:14:34 +0000 (16:14 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 May 2017 12:30:11 +0000 (14:30 +0200)
commit c9af28fdd44922a6c10c9f8315718408af98e315 upstream.

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.

Google-Bug-Id: 27641567

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/ext4/crypto.c
fs/ext4/ext4.h
fs/ext4/page-io.c
fs/ext4/readpage.c

index 9d6c2dcf1bd055fa37db1946f8366d8240703e4b..f240cef8b3265a38b218bdf0e8b8306a7d1587f0 100644 (file)
@@ -94,7 +94,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;
@@ -121,7 +122,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;
@@ -258,7 +259,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];
@@ -269,7 +271,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",
@@ -310,9 +312,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;
@@ -335,7 +338,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;
@@ -343,17 +347,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:
@@ -381,8 +385,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, struct ext4_extent *ex)
@@ -403,11 +407,11 @@ int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex)
 
        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;
@@ -415,11 +419,12 @@ int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex)
 
        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;
 
-               bio = bio_alloc(GFP_KERNEL, 1);
+               bio = bio_alloc(GFP_NOWAIT, 1);
                if (!bio) {
                        err = -ENOMEM;
                        goto errout;
index 3de9bb357b4f171996731a349658bd00dbc3fc7d..c8ad14c697c4a72da15f147061a419aefdf09356 100644 (file)
@@ -2261,11 +2261,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, struct ext4_extent *ex);
 extern const struct dentry_operations ext4_encrypted_d_ops;
index 17fbe3882b8eb70e3ecc0445a950d25b42423beb..5c72ae5d62a62f4016d994d9c101db91f9a00a98 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/mm.h>
+#include <linux/backing-dev.h>
 
 #include "ext4_jbd2.h"
 #include "xattr.h"
@@ -485,9 +486,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;
                }
index 5dc5e95063de2a7e42749a94464f00f7c50be4b8..bc7642f57dc82b6e4862d02e07b6a3b1ed67250c 100644 (file)
@@ -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;
                        }