]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/ext4/readpage.c
Merge tag 'block-5.6-2020-02-05' of git://git.kernel.dk/linux-block
[linux.git] / fs / ext4 / readpage.c
index fef7755300c359ba7c87b283c516241189280d2e..c1769afbf799530b8ce61e5815cb22fa51570e5d 100644 (file)
@@ -57,6 +57,7 @@ enum bio_post_read_step {
        STEP_INITIAL = 0,
        STEP_DECRYPT,
        STEP_VERITY,
+       STEP_MAX,
 };
 
 struct bio_post_read_ctx {
@@ -106,10 +107,22 @@ static void verity_work(struct work_struct *work)
 {
        struct bio_post_read_ctx *ctx =
                container_of(work, struct bio_post_read_ctx, work);
+       struct bio *bio = ctx->bio;
 
-       fsverity_verify_bio(ctx->bio);
+       /*
+        * fsverity_verify_bio() may call readpages() again, and although verity
+        * will be disabled for that, decryption may still be needed, causing
+        * another bio_post_read_ctx to be allocated.  So to guarantee that
+        * mempool_alloc() never deadlocks we must free the current ctx first.
+        * This is safe because verity is the last post-read step.
+        */
+       BUILD_BUG_ON(STEP_VERITY + 1 != STEP_MAX);
+       mempool_free(ctx, bio_post_read_ctx_pool);
+       bio->bi_private = NULL;
 
-       bio_post_read_processing(ctx);
+       fsverity_verify_bio(bio);
+
+       __read_end_io(bio);
 }
 
 static void bio_post_read_processing(struct bio_post_read_ctx *ctx)
@@ -176,12 +189,11 @@ static inline bool ext4_need_verity(const struct inode *inode, pgoff_t idx)
               idx < DIV_ROUND_UP(inode->i_size, PAGE_SIZE);
 }
 
-static struct bio_post_read_ctx *get_bio_post_read_ctx(struct inode *inode,
-                                                      struct bio *bio,
-                                                      pgoff_t first_idx)
+static void ext4_set_bio_post_read_ctx(struct bio *bio,
+                                      const struct inode *inode,
+                                      pgoff_t first_idx)
 {
        unsigned int post_read_steps = 0;
-       struct bio_post_read_ctx *ctx = NULL;
 
        if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode))
                post_read_steps |= 1 << STEP_DECRYPT;
@@ -190,14 +202,14 @@ static struct bio_post_read_ctx *get_bio_post_read_ctx(struct inode *inode,
                post_read_steps |= 1 << STEP_VERITY;
 
        if (post_read_steps) {
-               ctx = mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS);
-               if (!ctx)
-                       return ERR_PTR(-ENOMEM);
+               /* Due to the mempool, this never fails. */
+               struct bio_post_read_ctx *ctx =
+                       mempool_alloc(bio_post_read_ctx_pool, GFP_NOFS);
+
                ctx->bio = bio;
                ctx->enabled_steps = post_read_steps;
                bio->bi_private = ctx;
        }
-       return ctx;
 }
 
 static inline loff_t ext4_readpage_limit(struct inode *inode)
@@ -358,24 +370,16 @@ int ext4_mpage_readpages(struct address_space *mapping,
                        bio = NULL;
                }
                if (bio == NULL) {
-                       struct bio_post_read_ctx *ctx;
-
                        /*
                         * bio_alloc will _always_ be able to allocate a bio if
                         * __GFP_DIRECT_RECLAIM is set, see bio_alloc_bioset().
                         */
                        bio = bio_alloc(GFP_KERNEL,
                                min_t(int, nr_pages, BIO_MAX_PAGES));
-                       ctx = get_bio_post_read_ctx(inode, bio, page->index);
-                       if (IS_ERR(ctx)) {
-                               bio_put(bio);
-                               bio = NULL;
-                               goto set_error_page;
-                       }
+                       ext4_set_bio_post_read_ctx(bio, inode, page->index);
                        bio_set_dev(bio, bdev);
                        bio->bi_iter.bi_sector = blocks[0] << (blkbits - 9);
                        bio->bi_end_io = mpage_end_io;
-                       bio->bi_private = ctx;
                        bio_set_op_attrs(bio, REQ_OP_READ,
                                                is_readahead ? REQ_RAHEAD : 0);
                }