pmem_submit_bio() passes the parent bio to nvdimm_flush() for
REQ_FUA. For virtio-pmem this makes async_pmem_flush() allocate
and submit a child PREFLUSH bio chained to the parent.

That child allocation is in the block submit path. Making it
blocking with GFP_NOIO can consume the same global bio mempool that
submit_bio() uses, while making it GFP_ATOMIC can fail under
pressure. A forced failure of the child allocation produced:

virtio_pmem: forcing child bio allocation failure for test
Buffer I/O error on dev pmem0, logical block 0, lost sync page write
EXT4-fs (pmem0): I/O error while writing superblock
EXT4-fs (pmem0): mount failed

Avoid the child bio completely. Flush FUA synchronously, like
REQ_PREFLUSH, then complete the parent after the flush. Since no
child bio can be created, async_pmem_flush() now only issues the
virtio flush and preserves negative errno values.

Signed-off-by: Li Chen <[email protected]>
---
Changes in v6:
- Replace the child bio allocation fix with synchronous FUA flushing.

 drivers/nvdimm/nd_virtio.c | 22 ++++------------------
 drivers/nvdimm/pmem.c      |  2 +-
 2 files changed, 5 insertions(+), 19 deletions(-)

diff --git a/drivers/nvdimm/nd_virtio.c b/drivers/nvdimm/nd_virtio.c
index 4176046627beb..4b2e9c47af0f5 100644
--- a/drivers/nvdimm/nd_virtio.c
+++ b/drivers/nvdimm/nd_virtio.c
@@ -110,27 +110,13 @@ static int virtio_pmem_flush(struct nd_region *nd_region)
 /* The asynchronous flush callback function */
 int async_pmem_flush(struct nd_region *nd_region, struct bio *bio)
 {
-       /*
-        * Create child bio for asynchronous flush and chain with
-        * parent bio. Otherwise directly call nd_region flush.
-        */
-       if (bio && bio->bi_iter.bi_sector != -1) {
-               struct bio *child = bio_alloc(bio->bi_bdev, 0,
-                                             REQ_OP_WRITE | REQ_PREFLUSH,
-                                             GFP_ATOMIC);
+       int err;
 
-               if (!child)
-                       return -ENOMEM;
-               bio_clone_blkg_association(child, bio);
-               child->bi_iter.bi_sector = -1;
-               bio_chain(child, bio);
-               submit_bio(child);
-               return 0;
-       }
-       if (virtio_pmem_flush(nd_region))
+       err = virtio_pmem_flush(nd_region);
+       if (err > 0)
                return -EIO;
 
-       return 0;
+       return err;
 };
 EXPORT_SYMBOL_GPL(async_pmem_flush);
 MODULE_DESCRIPTION("Virtio Persistent Memory Driver");
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 82ee1ddb3a445..058d2739c95a1 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -242,7 +242,7 @@ static void pmem_submit_bio(struct bio *bio)
        }
 
        if ((bio->bi_opf & REQ_FUA) && !bio->bi_status)
-               ret = nvdimm_flush(nd_region, bio);
+               ret = nvdimm_flush(nd_region, NULL);
 
        if (ret)
                bio->bi_status = errno_to_blk_status(ret);
-- 
2.52.0

Reply via email to