iomap: rework REQ_FUA selection

The way how iomap_dio_can_use_fua and the caller is structured is
a bit confusing, as the main guarding condition is hidden in the
helper, and the secondary conditions are split between caller and
callee.

Refactor the code, so that iomap_dio_bio_iter itself tracks if a write
might need metadata updates based on the iomap type and flags, and
then have a condition based on that to use the FUA flag.

Note that this also moves the REQ_OP_WRITE assignment to the end of
the branch to improve readability a bit.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Link: https://patch.msgid.link/20251113170633.1453259-4-hch@lst.de
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Christoph Hellwig 2025-11-13 18:06:28 +01:00 committed by Christian Brauner
parent ddb4873286
commit 29086a31b3
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
1 changed files with 49 additions and 31 deletions

View File

@ -287,23 +287,6 @@ static int iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio,
return 0; return 0;
} }
/*
* Use a FUA write if we need datasync semantics and this is a pure data I/O
* that doesn't require any metadata updates (including after I/O completion
* such as unwritten extent conversion) and the underlying device either
* doesn't have a volatile write cache or supports FUA.
* This allows us to avoid cache flushes on I/O completion.
*/
static inline bool iomap_dio_can_use_fua(const struct iomap *iomap,
struct iomap_dio *dio)
{
if (iomap->flags & (IOMAP_F_SHARED | IOMAP_F_DIRTY))
return false;
if (!(dio->flags & IOMAP_DIO_WRITE_THROUGH))
return false;
return !bdev_write_cache(iomap->bdev) || bdev_fua(iomap->bdev);
}
static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio) static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)
{ {
const struct iomap *iomap = &iter->iomap; const struct iomap *iomap = &iter->iomap;
@ -332,7 +315,24 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)
return -EINVAL; return -EINVAL;
if (dio->flags & IOMAP_DIO_WRITE) { if (dio->flags & IOMAP_DIO_WRITE) {
bio_opf |= REQ_OP_WRITE; bool need_completion_work = true;
switch (iomap->type) {
case IOMAP_MAPPED:
/*
* Directly mapped I/O does not inherently need to do
* work at I/O completion time. But there are various
* cases below where this will get set again.
*/
need_completion_work = false;
break;
case IOMAP_UNWRITTEN:
dio->flags |= IOMAP_DIO_UNWRITTEN;
need_zeroout = true;
break;
default:
break;
}
if (iomap->flags & IOMAP_F_ATOMIC_BIO) { if (iomap->flags & IOMAP_F_ATOMIC_BIO) {
/* /*
@ -345,22 +345,40 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)
bio_opf |= REQ_ATOMIC; bio_opf |= REQ_ATOMIC;
} }
if (iomap->type == IOMAP_UNWRITTEN) { if (iomap->flags & IOMAP_F_SHARED) {
dio->flags |= IOMAP_DIO_UNWRITTEN; /*
* Unsharing of needs to update metadata at I/O
* completion time.
*/
need_completion_work = true;
dio->flags |= IOMAP_DIO_COW;
}
if (iomap->flags & IOMAP_F_NEW) {
/*
* Newly allocated blocks might need recording in
* metadata at I/O completion time.
*/
need_completion_work = true;
need_zeroout = true; need_zeroout = true;
} }
if (iomap->flags & IOMAP_F_SHARED) /*
dio->flags |= IOMAP_DIO_COW; * Use a FUA write if we need datasync semantics and this is a
* pure overwrite that doesn't require any metadata updates.
if (iomap->flags & IOMAP_F_NEW) *
need_zeroout = true; * This allows us to avoid cache flushes on I/O completion.
else if (iomap->type == IOMAP_MAPPED && */
iomap_dio_can_use_fua(iomap, dio)) if (dio->flags & IOMAP_DIO_WRITE_THROUGH) {
bio_opf |= REQ_FUA; if (!need_completion_work &&
!(iomap->flags & IOMAP_F_DIRTY) &&
if (!(bio_opf & REQ_FUA)) (!bdev_write_cache(iomap->bdev) ||
dio->flags &= ~IOMAP_DIO_WRITE_THROUGH; bdev_fua(iomap->bdev)))
bio_opf |= REQ_FUA;
else
dio->flags &= ~IOMAP_DIO_WRITE_THROUGH;
}
bio_opf |= REQ_OP_WRITE;
} else { } else {
bio_opf |= REQ_OP_READ; bio_opf |= REQ_OP_READ;
} }