diff options
author | NeilBrown <neilb@suse.com> | 2018-02-15 20:00:15 +1100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-02-22 15:43:51 +0100 |
commit | e6701adbbabbb078bf332bf99bd05eae7a4c64ac (patch) | |
tree | 1985efde091e234e157a53a966ee5309e832a58e | |
parent | b2a6141782cd9934e8810e1b73b13e5252811e0c (diff) |
dm: correctly handle chained bios in dec_pending()
commit 8dd601fa8317243be887458c49f6c29c2f3d719f upstream.
dec_pending() is given an error status (possibly 0) to be recorded
against a bio. It can be called several times on the one 'struct
dm_io', and it is careful to only assign a non-zero error to
io->status. However when it then assigned io->status to bio->bi_status,
it is not careful and could overwrite a genuine error status with 0.
This can happen when chained bios are in use. If a bio is chained
beneath the bio that this dm_io is handling, the child bio might
complete and set bio->bi_status before the dm_io completes.
This has been possible since chained bios were introduced in 3.14, and
has become a lot easier to trigger with commit 18a25da84354 ("dm: ensure
bio submission follows a depth-first tree walk") as that commit caused
dm to start using chained bios itself.
A particular failure mode is that if a bio spans an 'error' target and a
working target, the 'error' fragment will complete instantly and set the
->bi_status, and the other fragment will normally complete a little
later, and will clear ->bi_status.
The fix is simply to only assign io_error to bio->bi_status when
io_error is not zero.
Reported-and-tested-by: Milan Broz <gmazyland@gmail.com>
Cc: stable@vger.kernel.org (v3.14+)
Signed-off-by: NeilBrown <neilb@suse.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/md/dm.c | 3 |
1 files changed, 2 insertions, 1 deletions
diff --git a/drivers/md/dm.c b/drivers/md/dm.c index c5522551122f..2ffe7db75acb 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -809,7 +809,8 @@ static void dec_pending(struct dm_io *io, int error) } else { /* done with normal IO or empty flush */ trace_block_bio_complete(md->queue, bio, io_error); - bio->bi_error = io_error; + if (io_error) + bio->bi_error = io_error; bio_endio(bio); } } |