From: Shida Zhang <zhangshida@kylinos.cn>
Andreas point out that multiple completions can race setting
bi_status.
The check (parent->bi_status) and the subsequent write are not an
atomic operation. The value of parent->bi_status could have changed
between the time you read it for the if check and the time you write
to it. So we use cmpxchg to fix the race, as suggested by Christoph.
Suggested-by: Andreas Gruenbacher <agruenba@redhat.com>
Suggested-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Shida Zhang <zhangshida@kylinos.cn>
---
block/bio.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/block/bio.c b/block/bio.c
index 55c2c1a0020..aa43435c15f 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -313,9 +313,12 @@ EXPORT_SYMBOL(bio_reset);
static struct bio *__bio_chain_endio(struct bio *bio)
{
struct bio *parent = bio->bi_private;
+ blk_status_t *status = &parent->bi_status;
+ blk_status_t new_status = bio->bi_status;
+
+ if (new_status != BLK_STS_OK)
+ cmpxchg(status, BLK_STS_OK, new_status);
- if (bio->bi_status && !parent->bi_status)
- parent->bi_status = bio->bi_status;
bio_put(bio);
return parent;
}
--
2.34.1
On Fri, Nov 28, 2025 at 12:34 AM zhangshida <starzhangzsd@gmail.com> wrote:
>
> From: Shida Zhang <zhangshida@kylinos.cn>
>
> Andreas point out that multiple completions can race setting
> bi_status.
>
> The check (parent->bi_status) and the subsequent write are not an
> atomic operation. The value of parent->bi_status could have changed
> between the time you read it for the if check and the time you write
> to it. So we use cmpxchg to fix the race, as suggested by Christoph.
>
> Suggested-by: Andreas Gruenbacher <agruenba@redhat.com>
> Suggested-by: Christoph Hellwig <hch@infradead.org>
> Signed-off-by: Shida Zhang <zhangshida@kylinos.cn>
> ---
> block/bio.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/block/bio.c b/block/bio.c
> index 55c2c1a0020..aa43435c15f 100644
> --- a/block/bio.c
> +++ b/block/bio.c
> @@ -313,9 +313,12 @@ EXPORT_SYMBOL(bio_reset);
> static struct bio *__bio_chain_endio(struct bio *bio)
> {
> struct bio *parent = bio->bi_private;
> + blk_status_t *status = &parent->bi_status;
nit: this variable seems unnecessary, just use &parent->bi_status
directly in the one place it's needed?
Best,
Caleb
> + blk_status_t new_status = bio->bi_status;
> +
> + if (new_status != BLK_STS_OK)
> + cmpxchg(status, BLK_STS_OK, new_status);
>
> - if (bio->bi_status && !parent->bi_status)
> - parent->bi_status = bio->bi_status;
> bio_put(bio);
> return parent;
> }
> --
> 2.34.1
>
>
Caleb Sander Mateos <csander@purestorage.com> 于2025年11月29日周六 03:44写道:
>
> On Fri, Nov 28, 2025 at 12:34 AM zhangshida <starzhangzsd@gmail.com> wrote:
> >
> > From: Shida Zhang <zhangshida@kylinos.cn>
> >
> > Andreas point out that multiple completions can race setting
> > bi_status.
> >
> > The check (parent->bi_status) and the subsequent write are not an
> > atomic operation. The value of parent->bi_status could have changed
> > between the time you read it for the if check and the time you write
> > to it. So we use cmpxchg to fix the race, as suggested by Christoph.
> >
> > Suggested-by: Andreas Gruenbacher <agruenba@redhat.com>
> > Suggested-by: Christoph Hellwig <hch@infradead.org>
> > Signed-off-by: Shida Zhang <zhangshida@kylinos.cn>
> > ---
> > block/bio.c | 7 +++++--
> > 1 file changed, 5 insertions(+), 2 deletions(-)
> >
> > diff --git a/block/bio.c b/block/bio.c
> > index 55c2c1a0020..aa43435c15f 100644
> > --- a/block/bio.c
> > +++ b/block/bio.c
> > @@ -313,9 +313,12 @@ EXPORT_SYMBOL(bio_reset);
> > static struct bio *__bio_chain_endio(struct bio *bio)
> > {
> > struct bio *parent = bio->bi_private;
> > + blk_status_t *status = &parent->bi_status;
>
> nit: this variable seems unnecessary, just use &parent->bi_status
> directly in the one place it's needed?
>
Thanks, Caleb and Andreas. I will integrate your suggestions to:
if (!bio->bi_status)
cmpxchg(&parent->bi_status, 0, bio->bi_status);
Thanks,
Shida
> Best,
> Caleb
>
> > + blk_status_t new_status = bio->bi_status;
> > +
> > + if (new_status != BLK_STS_OK)
> > + cmpxchg(status, BLK_STS_OK, new_status);
> >
> > - if (bio->bi_status && !parent->bi_status)
> > - parent->bi_status = bio->bi_status;
> > bio_put(bio);
> > return parent;
> > }
> > --
> > 2.34.1
> >
> >
Stephen Zhang <starzhangzsd@gmail.com> 于2025年11月29日周六 09:47写道:
>
> Caleb Sander Mateos <csander@purestorage.com> 于2025年11月29日周六 03:44写道:
> >
> > On Fri, Nov 28, 2025 at 12:34 AM zhangshida <starzhangzsd@gmail.com> wrote:
> > >
> > > From: Shida Zhang <zhangshida@kylinos.cn>
> > >
> > > Andreas point out that multiple completions can race setting
> > > bi_status.
> > >
> > > The check (parent->bi_status) and the subsequent write are not an
> > > atomic operation. The value of parent->bi_status could have changed
> > > between the time you read it for the if check and the time you write
> > > to it. So we use cmpxchg to fix the race, as suggested by Christoph.
> > >
> > > Suggested-by: Andreas Gruenbacher <agruenba@redhat.com>
> > > Suggested-by: Christoph Hellwig <hch@infradead.org>
> > > Signed-off-by: Shida Zhang <zhangshida@kylinos.cn>
> > > ---
> > > block/bio.c | 7 +++++--
> > > 1 file changed, 5 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/block/bio.c b/block/bio.c
> > > index 55c2c1a0020..aa43435c15f 100644
> > > --- a/block/bio.c
> > > +++ b/block/bio.c
> > > @@ -313,9 +313,12 @@ EXPORT_SYMBOL(bio_reset);
> > > static struct bio *__bio_chain_endio(struct bio *bio)
> > > {
> > > struct bio *parent = bio->bi_private;
> > > + blk_status_t *status = &parent->bi_status;
> >
> > nit: this variable seems unnecessary, just use &parent->bi_status
> > directly in the one place it's needed?
> >
>
> Thanks, Caleb and Andreas. I will integrate your suggestions to:
>
> if (!bio->bi_status)
> cmpxchg(&parent->bi_status, 0, bio->bi_status);
>
Sorry, it should be:
if (bio->bi_status)
cmpxchg(&parent->bi_status, 0, bio->bi_status);
Thanks,
Shida
> Thanks,
> Shida
>
> > Best,
> > Caleb
> >
> > > + blk_status_t new_status = bio->bi_status;
> > > +
> > > + if (new_status != BLK_STS_OK)
> > > + cmpxchg(status, BLK_STS_OK, new_status);
> > >
> > > - if (bio->bi_status && !parent->bi_status)
> > > - parent->bi_status = bio->bi_status;
> > > bio_put(bio);
> > > return parent;
> > > }
> > > --
> > > 2.34.1
> > >
> > >
On Fri, Nov 28, 2025 at 9:32 AM zhangshida <starzhangzsd@gmail.com> wrote:
> From: Shida Zhang <zhangshida@kylinos.cn>
>
> Andreas point out that multiple completions can race setting
> bi_status.
>
> The check (parent->bi_status) and the subsequent write are not an
> atomic operation. The value of parent->bi_status could have changed
> between the time you read it for the if check and the time you write
> to it. So we use cmpxchg to fix the race, as suggested by Christoph.
>
> Suggested-by: Andreas Gruenbacher <agruenba@redhat.com>
> Suggested-by: Christoph Hellwig <hch@infradead.org>
> Signed-off-by: Shida Zhang <zhangshida@kylinos.cn>
> ---
> block/bio.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/block/bio.c b/block/bio.c
> index 55c2c1a0020..aa43435c15f 100644
> --- a/block/bio.c
> +++ b/block/bio.c
> @@ -313,9 +313,12 @@ EXPORT_SYMBOL(bio_reset);
> static struct bio *__bio_chain_endio(struct bio *bio)
> {
> struct bio *parent = bio->bi_private;
> + blk_status_t *status = &parent->bi_status;
> + blk_status_t new_status = bio->bi_status;
> +
> + if (new_status != BLK_STS_OK)
> + cmpxchg(status, BLK_STS_OK, new_status);
This isn't wrong, but bi_status is explicitly set to 0 and compared
with 0 all over the place, so putting in BLK_STS_OK here doesn't
really help IMHO.
>
> - if (bio->bi_status && !parent->bi_status)
> - parent->bi_status = bio->bi_status;
> bio_put(bio);
> return parent;
> }
> --
> 2.34.1
>
Thanks,
Andreas
On Fri, Nov 28, 2025 at 9:32 AM zhangshida <starzhangzsd@gmail.com> wrote:>
> From: Shida Zhang <zhangshida@kylinos.cn>
>
> Andreas point out that multiple completions can race setting
> bi_status.
>
> The check (parent->bi_status) and the subsequent write are not an
> atomic operation. The value of parent->bi_status could have changed
> between the time you read it for the if check and the time you write
> to it. So we use cmpxchg to fix the race, as suggested by Christoph.
>
> Suggested-by: Andreas Gruenbacher <agruenba@redhat.com>
> Suggested-by: Christoph Hellwig <hch@infradead.org>
> Signed-off-by: Shida Zhang <zhangshida@kylinos.cn>
> ---
> block/bio.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/block/bio.c b/block/bio.c
> index 55c2c1a0020..aa43435c15f 100644
> --- a/block/bio.c
> +++ b/block/bio.c
> @@ -313,9 +313,12 @@ EXPORT_SYMBOL(bio_reset);
> static struct bio *__bio_chain_endio(struct bio *bio)
> {
> struct bio *parent = bio->bi_private;
> + blk_status_t *status = &parent->bi_status;
> + blk_status_t new_status = bio->bi_status;
> +
> + if (new_status != BLK_STS_OK)
> + cmpxchg(status, BLK_STS_OK, new_status);
This isn't wrong, but bi_status is explicitly set to 0 and compared
with 0 all over the place, so putting in BLK_STS_OK here doesn't
really help IMHO.
>
> - if (bio->bi_status && !parent->bi_status)
> - parent->bi_status = bio->bi_status;
> bio_put(bio);
> return parent;
> }
> --
> 2.34.1
>
Thanks,
Andreas
On Fri, Nov 28, 2025 at 9:32 AM zhangshida <starzhangzsd@gmail.com> wrote:
> From: Shida Zhang <zhangshida@kylinos.cn>
>
> Andreas point out that multiple completions can race setting
> bi_status.
>
> The check (parent->bi_status) and the subsequent write are not an
> atomic operation. The value of parent->bi_status could have changed
> between the time you read it for the if check and the time you write
> to it. So we use cmpxchg to fix the race, as suggested by Christoph.
>
> Suggested-by: Andreas Gruenbacher <agruenba@redhat.com>
> Suggested-by: Christoph Hellwig <hch@infradead.org>
> Signed-off-by: Shida Zhang <zhangshida@kylinos.cn>
> ---
> block/bio.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/block/bio.c b/block/bio.c
> index 55c2c1a0020..aa43435c15f 100644
> --- a/block/bio.c
> +++ b/block/bio.c
> @@ -313,9 +313,12 @@ EXPORT_SYMBOL(bio_reset);
> static struct bio *__bio_chain_endio(struct bio *bio)
> {
> struct bio *parent = bio->bi_private;
> + blk_status_t *status = &parent->bi_status;
> + blk_status_t new_status = bio->bi_status;
> +
> + if (new_status != BLK_STS_OK)
> + cmpxchg(status, BLK_STS_OK, new_status);
This isn't wrong, but bi_status is explicitly set to 0 and compared
with 0 all over the place, so putting in BLK_STS_OK here doesn't
really help IMHO.
> - if (bio->bi_status && !parent->bi_status)
> - parent->bi_status = bio->bi_status;
> bio_put(bio);
> return parent;
> }
> --
> 2.34.1
>
Thanks,
Andreas
© 2016 - 2026 Red Hat, Inc.