buffer: hoist GFP flags from grow_dev_page() to __getblk_gfp()
authorMatthew Wilcox (Oracle) <willy@infradead.org>
Thu, 14 Sep 2023 15:00:05 +0000 (16:00 +0100)
committerAndrew Morton <akpm@linux-foundation.org>
Wed, 4 Oct 2023 17:32:28 +0000 (10:32 -0700)
grow_dev_page() is only called by grow_buffers().  grow_buffers() is only
called by __getblk_slow() and __getblk_slow() is only called from
__getblk_gfp(), so it is safe to move the GFP flags setting all the way
up.  With that done, add a new bdev_getblk() entry point that leaves the
GFP flags the way the caller specified them.

[willy@infradead.org: fix grow_dev_page() error handling]
Link: https://lkml.kernel.org/r/ZRREEIwqiy5DijKB@casper.infradead.org
Link: https://lkml.kernel.org/r/20230914150011.843330-3-willy@infradead.org
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Hui Zhu <teawater@antgroup.com>
Cc: Dan Carpenter <dan.carpenter@linaro.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
fs/buffer.c
include/linux/buffer_head.h

index 32338b7..80e96c1 100644 (file)
@@ -1043,20 +1043,11 @@ grow_dev_page(struct block_device *bdev, sector_t block,
        struct buffer_head *bh;
        sector_t end_block;
        int ret = 0;
-       gfp_t gfp_mask;
-
-       gfp_mask = mapping_gfp_constraint(inode->i_mapping, ~__GFP_FS) | gfp;
-
-       /*
-        * XXX: __getblk_slow() can not really deal with failure and
-        * will endlessly loop on improvised global reclaim.  Prefer
-        * looping in the allocator rather than here, at least that
-        * code knows what it's doing.
-        */
-       gfp_mask |= __GFP_NOFAIL;
 
        folio = __filemap_get_folio(inode->i_mapping, index,
-                       FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp_mask);
+                       FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
+       if (IS_ERR(folio))
+               return PTR_ERR(folio);
 
        bh = folio_buffers(folio);
        if (bh) {
@@ -1069,7 +1060,10 @@ grow_dev_page(struct block_device *bdev, sector_t block,
                        goto failed;
        }
 
-       bh = folio_alloc_buffers(folio, size, gfp_mask | __GFP_ACCOUNT);
+       ret = -ENOMEM;
+       bh = folio_alloc_buffers(folio, size, gfp | __GFP_ACCOUNT);
+       if (!bh)
+               goto failed;
 
        /*
         * Link the folio to the buffers and initialise them.  Take the
@@ -1420,24 +1414,49 @@ __find_get_block(struct block_device *bdev, sector_t block, unsigned size)
 }
 EXPORT_SYMBOL(__find_get_block);
 
+/**
+ * bdev_getblk - Get a buffer_head in a block device's buffer cache.
+ * @bdev: The block device.
+ * @block: The block number.
+ * @size: The size of buffer_heads for this @bdev.
+ * @gfp: The memory allocation flags to use.
+ *
+ * In contrast to __getblk_gfp(), the @gfp flags must be all of the flags;
+ * they are not augmented with the mapping's GFP flags.
+ *
+ * Return: The buffer head, or NULL if memory could not be allocated.
+ */
+struct buffer_head *bdev_getblk(struct block_device *bdev, sector_t block,
+               unsigned size, gfp_t gfp)
+{
+       struct buffer_head *bh = __find_get_block(bdev, block, size);
+
+       might_alloc(gfp);
+       if (bh)
+               return bh;
+
+       return __getblk_slow(bdev, block, size, gfp);
+}
+EXPORT_SYMBOL(bdev_getblk);
+
 /*
  * __getblk_gfp() will locate (and, if necessary, create) the buffer_head
  * which corresponds to the passed block_device, block and size. The
  * returned buffer has its reference count incremented.
- *
- * __getblk_gfp() will lock up the machine if grow_dev_page's
- * try_to_free_buffers() attempt is failing.  FIXME, perhaps?
  */
 struct buffer_head *
 __getblk_gfp(struct block_device *bdev, sector_t block,
             unsigned size, gfp_t gfp)
 {
-       struct buffer_head *bh = __find_get_block(bdev, block, size);
+       gfp |= mapping_gfp_constraint(bdev->bd_inode->i_mapping, ~__GFP_FS);
 
-       might_sleep();
-       if (bh == NULL)
-               bh = __getblk_slow(bdev, block, size, gfp);
-       return bh;
+       /*
+        * Prefer looping in the allocator rather than here, at least that
+        * code knows what it's doing.
+        */
+       gfp |= __GFP_NOFAIL;
+
+       return bdev_getblk(bdev, block, size, gfp);
 }
 EXPORT_SYMBOL(__getblk_gfp);
 
index 67d94d2..7825bb3 100644 (file)
@@ -227,6 +227,8 @@ void __wait_on_buffer(struct buffer_head *);
 wait_queue_head_t *bh_waitq_head(struct buffer_head *bh);
 struct buffer_head *__find_get_block(struct block_device *bdev, sector_t block,
                        unsigned size);
+struct buffer_head *bdev_getblk(struct block_device *bdev, sector_t block,
+               unsigned size, gfp_t gfp);
 struct buffer_head *__getblk_gfp(struct block_device *bdev, sector_t block,
                                  unsigned size, gfp_t gfp);
 void __brelse(struct buffer_head *);