Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9539ec0b authored by Theodore Ts'o's avatar Theodore Ts'o Committed by Eric Biggers
Browse files

UPSTREAM: ext4 crypto: don't let data integrity writebacks fail with ENOMEM



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: default avatarTheodore Ts'o <tytso@mit.edu>
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit 0a76f023e6f2073936cc87ff839b2aaeccc4fb9a)
Change-Id: I8a867d0d7c065c827c8802e6188385c2ab675476
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
parent 59baeb7a
Loading
Loading
Loading
Loading
+21 −16
Original line number Diff line number Diff line
@@ -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;
+4 −2
Original line number Diff line number Diff line
@@ -2084,11 +2084,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;
+13 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ratelimit.h>
#include <linux/backing-dev.h>

#include "ext4_jbd2.h"
#include "xattr.h"
@@ -489,9 +490,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;
		}
+1 −1
Original line number Diff line number Diff line
@@ -318,7 +318,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;
			}