* [PATCH] bcachefs: dio write: Take ref on mm_struct when using asynchronously @ 2024-11-27 21:36 Kent Overstreet 2024-11-27 21:47 ` Jens Axboe 2024-11-27 21:48 ` Jann Horn 0 siblings, 2 replies; 6+ messages in thread From: Kent Overstreet @ 2024-11-27 21:36 UTC (permalink / raw) To: linux-bcachefs; +Cc: Kent Overstreet, Jann Horn, Jens Axboe When invoked from aio, mm_struct is guaranteed to outlive the request since its lifetime is tied to the io_context - but that's not the case for io_uring, it's possible that a process could be killed and mm_struct goes away while a request is in flight. So if we're submitting the rest of the io asynchronously, we may need a ref on mm_struct. Per Jens, this is not actually a bug because we're not yet flipping on FMODE_NOWAIT, meaning io_uring will do the submission from an io_worker kthread - but this patch is necessary for safely flipping on FMODE_NOWAIT for more efficient submissions in the future. Reported-by: Jann Horn <jannh@google.com> Cc: Jens Axboe <axboe@kernel.dk> Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev> --- fs/bcachefs/fs-io-direct.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/fs/bcachefs/fs-io-direct.c b/fs/bcachefs/fs-io-direct.c index 2089c36b5866..cea4dd9a0745 100644 --- a/fs/bcachefs/fs-io-direct.c +++ b/fs/bcachefs/fs-io-direct.c @@ -226,6 +226,7 @@ struct dio_write { struct mm_struct *mm; const struct iovec *iov; unsigned loop:1, + have_mm_ref:1, extending:1, sync:1, flush:1; @@ -390,6 +391,9 @@ static __always_inline long bch2_dio_write_done(struct dio_write *dio) kfree(dio->iov); + if (dio->have_mm_ref) + mmput(dio->mm); + ret = dio->op.error ?: ((long) dio->written << 9); bio_put(&dio->op.wbio.bio); @@ -529,9 +533,24 @@ static __always_inline long bch2_dio_write_loop(struct dio_write *dio) if (unlikely(dio->iter.count) && !dio->sync && - !dio->loop && - bch2_dio_write_copy_iov(dio)) - dio->sync = sync = true; + !dio->loop) { + /* + * Rest of write will be submitted asynchronously - + * unless copying the iov fails: + */ + if (likely(!bch2_dio_write_copy_iov(dio))) { + /* + * aio guarantees that mm_struct outlives the + * request, but io_uring does not + */ + if (dio->mm) { + mmgrab(dio->mm); + dio->have_mm_ref = true; + } + } else { + dio->sync = sync = true; + } + } dio->loop = true; closure_call(&dio->op.cl, bch2_write, NULL, NULL); @@ -559,7 +578,7 @@ static __always_inline long bch2_dio_write_loop(struct dio_write *dio) static noinline __cold void bch2_dio_write_continue(struct dio_write *dio) { - struct mm_struct *mm = dio->mm; + struct mm_struct *mm = dio->have_mm_ref ? dio->mm: NULL; bio_reset(&dio->op.wbio.bio, NULL, REQ_OP_WRITE); -- 2.45.2 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] bcachefs: dio write: Take ref on mm_struct when using asynchronously 2024-11-27 21:36 [PATCH] bcachefs: dio write: Take ref on mm_struct when using asynchronously Kent Overstreet @ 2024-11-27 21:47 ` Jens Axboe 2024-11-27 21:48 ` Jann Horn 1 sibling, 0 replies; 6+ messages in thread From: Jens Axboe @ 2024-11-27 21:47 UTC (permalink / raw) To: Kent Overstreet, linux-bcachefs; +Cc: Jann Horn On 11/27/24 2:36 PM, Kent Overstreet wrote: > When invoked from aio, mm_struct is guaranteed to outlive the request > since its lifetime is tied to the io_context - but that's not the case > for io_uring, it's possible that a process could be killed and mm_struct > goes away while a request is in flight. > > So if we're submitting the rest of the io asynchronously, we may need a > ref on mm_struct. > > Per Jens, this is not actually a bug because we're not yet flipping on > FMODE_NOWAIT, meaning io_uring will do the submission from an io_worker > kthread - but this patch is necessary for safely flipping on > FMODE_NOWAIT for more efficient submissions in the future. This doesn't look right. The mmgrab looks fine, but that should be paired with mmdrop. And you still need an mmget_not_zero() before doing kthread_use_mm(), and then the mmput/mmdrop when after unuse done. -- Jens Axboe ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] bcachefs: dio write: Take ref on mm_struct when using asynchronously 2024-11-27 21:36 [PATCH] bcachefs: dio write: Take ref on mm_struct when using asynchronously Kent Overstreet 2024-11-27 21:47 ` Jens Axboe @ 2024-11-27 21:48 ` Jann Horn 2024-11-27 22:11 ` Kent Overstreet 1 sibling, 1 reply; 6+ messages in thread From: Jann Horn @ 2024-11-27 21:48 UTC (permalink / raw) To: Kent Overstreet; +Cc: linux-bcachefs, Jens Axboe On Wed, Nov 27, 2024 at 10:36 PM Kent Overstreet <kent.overstreet@linux.dev> wrote: > When invoked from aio, mm_struct is guaranteed to outlive the request > since its lifetime is tied to the io_context - but that's not the case > for io_uring, it's possible that a process could be killed and mm_struct > goes away while a request is in flight. > > So if we're submitting the rest of the io asynchronously, we may need a > ref on mm_struct. > > Per Jens, this is not actually a bug because we're not yet flipping on > FMODE_NOWAIT, meaning io_uring will do the submission from an io_worker > kthread - but this patch is necessary for safely flipping on > FMODE_NOWAIT for more efficient submissions in the future. Ah, one thing to look out for is that mm_struct has two types of refcounting, this patch mixes them up - I don't think my explanation of that was very clear: - mmgrab()/mmdrop() prevent the mm_struct itself being freed, but don't properly keep the mappings in it alive - mmget()/mmput() fully keep the MM alive An mmget() reference automatically also holds an mmgrab() reference, basically. For kthread_use_mm(), you need an mmget() reference; so the most straightforward option would be to mmget() when creating the asynchronous work and mmput() when you're done. But that has the disadvantage that the reference keeps the entire mm_struct with all the mappings inside it alive, and it even indirectly holds a reference to all the files mapped in the MM; though I don't know if that's really a problem here. If you want to avoid keeping the MM of an exiting process alive while the I/O is running, the neater pattern is to use mmgrab()/mmdrop() for your long-term reference, and then use mmget_not_zero()/mmput() around kthread_use_mm()/kthread_unuse_mm() (and bail if mmget_not_zero() fails). You can see this pattern in places like vfio_iommu_type1_dma_rw_chunk(). ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] bcachefs: dio write: Take ref on mm_struct when using asynchronously 2024-11-27 21:48 ` Jann Horn @ 2024-11-27 22:11 ` Kent Overstreet 2024-11-27 22:21 ` Jens Axboe 2024-11-27 23:11 ` Jann Horn 0 siblings, 2 replies; 6+ messages in thread From: Kent Overstreet @ 2024-11-27 22:11 UTC (permalink / raw) To: Jann Horn; +Cc: linux-bcachefs, Jens Axboe On Wed, Nov 27, 2024 at 10:48:03PM +0100, Jann Horn wrote: > On Wed, Nov 27, 2024 at 10:36 PM Kent Overstreet > <kent.overstreet@linux.dev> wrote: > > When invoked from aio, mm_struct is guaranteed to outlive the request > > since its lifetime is tied to the io_context - but that's not the case > > for io_uring, it's possible that a process could be killed and mm_struct > > goes away while a request is in flight. > > > > So if we're submitting the rest of the io asynchronously, we may need a > > ref on mm_struct. > > > > Per Jens, this is not actually a bug because we're not yet flipping on > > FMODE_NOWAIT, meaning io_uring will do the submission from an io_worker > > kthread - but this patch is necessary for safely flipping on > > FMODE_NOWAIT for more efficient submissions in the future. > > Ah, one thing to look out for is that mm_struct has two types of > refcounting, this patch mixes them up - I don't think my explanation > of that was very clear: > > - mmgrab()/mmdrop() prevent the mm_struct itself being freed, but > don't properly keep the mappings in it alive > - mmget()/mmput() fully keep the MM alive > > An mmget() reference automatically also holds an mmgrab() reference, basically. > > For kthread_use_mm(), you need an mmget() reference; so the most > straightforward option would be to mmget() when creating the > asynchronous work and mmput() when you're done. But that has the > disadvantage that the reference keeps the entire mm_struct with all > the mappings inside it alive, and it even indirectly holds a reference > to all the files mapped in the MM; though I don't know if that's > really a problem here. > > If you want to avoid keeping the MM of an exiting process alive while > the I/O is running, the neater pattern is to use mmgrab()/mmdrop() for > your long-term reference, and then use mmget_not_zero()/mmput() around > kthread_use_mm()/kthread_unuse_mm() (and bail if mmget_not_zero() > fails). You can see this pattern in places like > vfio_iommu_type1_dma_rw_chunk(). gotcha... From 3ef3067690d838dfc9a12686caa015e85e217f01 Mon Sep 17 00:00:00 2001 From: Kent Overstreet <kent.overstreet@linux.dev> Date: Wed, 27 Nov 2024 16:32:26 -0500 Subject: [PATCH] bcachefs: dio write: Take ref on mm_struct when using asynchronously When invoked from aio, mm_struct is guaranteed to outlive the request since its lifetime is tied to the io_context - but that's not the case for io_uring, it's possible that a process could be killed and mm_struct goes away while a request is in flight. So if we're submitting the rest of the io asynchronously, we may need a ref on mm_struct. Per Jens, this is not actually a bug because we're not yet flipping on FMODE_NOWAIT, meaning io_uring will do the submission from an io_worker kthread - but this patch is necessary for safely flipping on FMODE_NOWAIT for more efficient submissions in the future. Reported-by: Jann Horn <jannh@google.com> Cc: Jens Axboe <axboe@kernel.dk> Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev> diff --git a/fs/bcachefs/fs-io-direct.c b/fs/bcachefs/fs-io-direct.c index 2089c36b5866..8da0577411e8 100644 --- a/fs/bcachefs/fs-io-direct.c +++ b/fs/bcachefs/fs-io-direct.c @@ -226,6 +226,7 @@ struct dio_write { struct mm_struct *mm; const struct iovec *iov; unsigned loop:1, + have_mm_ref:1, extending:1, sync:1, flush:1; @@ -390,6 +391,9 @@ static __always_inline long bch2_dio_write_done(struct dio_write *dio) kfree(dio->iov); + if (dio->have_mm_ref) + mmdrop(dio->mm); + ret = dio->op.error ?: ((long) dio->written << 9); bio_put(&dio->op.wbio.bio); @@ -529,9 +533,24 @@ static __always_inline long bch2_dio_write_loop(struct dio_write *dio) if (unlikely(dio->iter.count) && !dio->sync && - !dio->loop && - bch2_dio_write_copy_iov(dio)) - dio->sync = sync = true; + !dio->loop) { + /* + * Rest of write will be submitted asynchronously - + * unless copying the iov fails: + */ + if (likely(!bch2_dio_write_copy_iov(dio))) { + /* + * aio guarantees that mm_struct outlives the + * request, but io_uring does not + */ + if (dio->mm) { + mmgrab(dio->mm); + dio->have_mm_ref = true; + } + } else { + dio->sync = sync = true; + } + } dio->loop = true; closure_call(&dio->op.cl, bch2_write, NULL, NULL); @@ -559,15 +578,25 @@ static __always_inline long bch2_dio_write_loop(struct dio_write *dio) static noinline __cold void bch2_dio_write_continue(struct dio_write *dio) { - struct mm_struct *mm = dio->mm; + struct mm_struct *mm = dio->have_mm_ref ? dio->mm: NULL; bio_reset(&dio->op.wbio.bio, NULL, REQ_OP_WRITE); - if (mm) + if (mm) { + if (unlikely(!mmget_not_zero(mm))) { + /* process exited */ + dio->op.error = -ESRCH; + bch2_dio_write_done(dio); + return; + } + kthread_use_mm(mm); + } bch2_dio_write_loop(dio); - if (mm) + if (mm) { + mmput(mm); kthread_unuse_mm(mm); + } } static void bch2_dio_write_loop_async(struct bch_write_op *op) ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] bcachefs: dio write: Take ref on mm_struct when using asynchronously 2024-11-27 22:11 ` Kent Overstreet @ 2024-11-27 22:21 ` Jens Axboe 2024-11-27 23:11 ` Jann Horn 1 sibling, 0 replies; 6+ messages in thread From: Jens Axboe @ 2024-11-27 22:21 UTC (permalink / raw) To: Kent Overstreet, Jann Horn; +Cc: linux-bcachefs On 11/27/24 3:11 PM, Kent Overstreet wrote: > diff --git a/fs/bcachefs/fs-io-direct.c b/fs/bcachefs/fs-io-direct.c > index 2089c36b5866..8da0577411e8 100644 > --- a/fs/bcachefs/fs-io-direct.c > +++ b/fs/bcachefs/fs-io-direct.c > @@ -226,6 +226,7 @@ struct dio_write { > struct mm_struct *mm; > const struct iovec *iov; > unsigned loop:1, > + have_mm_ref:1, > extending:1, > sync:1, > flush:1; > @@ -390,6 +391,9 @@ static __always_inline long bch2_dio_write_done(struct dio_write *dio) > > kfree(dio->iov); > > + if (dio->have_mm_ref) > + mmdrop(dio->mm); > + > ret = dio->op.error ?: ((long) dio->written << 9); > bio_put(&dio->op.wbio.bio); > > @@ -529,9 +533,24 @@ static __always_inline long bch2_dio_write_loop(struct dio_write *dio) > > if (unlikely(dio->iter.count) && > !dio->sync && > - !dio->loop && > - bch2_dio_write_copy_iov(dio)) > - dio->sync = sync = true; > + !dio->loop) { > + /* > + * Rest of write will be submitted asynchronously - > + * unless copying the iov fails: > + */ > + if (likely(!bch2_dio_write_copy_iov(dio))) { > + /* > + * aio guarantees that mm_struct outlives the > + * request, but io_uring does not > + */ > + if (dio->mm) { > + mmgrab(dio->mm); > + dio->have_mm_ref = true; > + } > + } else { > + dio->sync = sync = true; > + } > + } > > dio->loop = true; > closure_call(&dio->op.cl, bch2_write, NULL, NULL); > @@ -559,15 +578,25 @@ static __always_inline long bch2_dio_write_loop(struct dio_write *dio) > > static noinline __cold void bch2_dio_write_continue(struct dio_write *dio) > { > - struct mm_struct *mm = dio->mm; > + struct mm_struct *mm = dio->have_mm_ref ? dio->mm: NULL; > > bio_reset(&dio->op.wbio.bio, NULL, REQ_OP_WRITE); > > - if (mm) > + if (mm) { > + if (unlikely(!mmget_not_zero(mm))) { > + /* process exited */ > + dio->op.error = -ESRCH; > + bch2_dio_write_done(dio); > + return; > + } > + > kthread_use_mm(mm); > + } > bch2_dio_write_loop(dio); > - if (mm) > + if (mm) { > + mmput(mm); > kthread_unuse_mm(mm); > + } > } > > static void bch2_dio_write_loop_async(struct bch_write_op *op) Based on a quick look, this one looks good to me. -- Jens Axboe ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] bcachefs: dio write: Take ref on mm_struct when using asynchronously 2024-11-27 22:11 ` Kent Overstreet 2024-11-27 22:21 ` Jens Axboe @ 2024-11-27 23:11 ` Jann Horn 1 sibling, 0 replies; 6+ messages in thread From: Jann Horn @ 2024-11-27 23:11 UTC (permalink / raw) To: Kent Overstreet; +Cc: linux-bcachefs, Jens Axboe On Wed, Nov 27, 2024 at 11:11 PM Kent Overstreet <kent.overstreet@linux.dev> wrote: > On Wed, Nov 27, 2024 at 10:48:03PM +0100, Jann Horn wrote: > > On Wed, Nov 27, 2024 at 10:36 PM Kent Overstreet > > <kent.overstreet@linux.dev> wrote: > > > When invoked from aio, mm_struct is guaranteed to outlive the request > > > since its lifetime is tied to the io_context - but that's not the case > > > for io_uring, it's possible that a process could be killed and mm_struct > > > goes away while a request is in flight. > > > > > > So if we're submitting the rest of the io asynchronously, we may need a > > > ref on mm_struct. > > > > > > Per Jens, this is not actually a bug because we're not yet flipping on > > > FMODE_NOWAIT, meaning io_uring will do the submission from an io_worker > > > kthread - but this patch is necessary for safely flipping on > > > FMODE_NOWAIT for more efficient submissions in the future. > > > > Ah, one thing to look out for is that mm_struct has two types of > > refcounting, this patch mixes them up - I don't think my explanation > > of that was very clear: > > > > - mmgrab()/mmdrop() prevent the mm_struct itself being freed, but > > don't properly keep the mappings in it alive > > - mmget()/mmput() fully keep the MM alive > > > > An mmget() reference automatically also holds an mmgrab() reference, basically. > > > > For kthread_use_mm(), you need an mmget() reference; so the most > > straightforward option would be to mmget() when creating the > > asynchronous work and mmput() when you're done. But that has the > > disadvantage that the reference keeps the entire mm_struct with all > > the mappings inside it alive, and it even indirectly holds a reference > > to all the files mapped in the MM; though I don't know if that's > > really a problem here. > > > > If you want to avoid keeping the MM of an exiting process alive while > > the I/O is running, the neater pattern is to use mmgrab()/mmdrop() for > > your long-term reference, and then use mmget_not_zero()/mmput() around > > kthread_use_mm()/kthread_unuse_mm() (and bail if mmget_not_zero() > > fails). You can see this pattern in places like > > vfio_iommu_type1_dma_rw_chunk(). > > gotcha... > > From 3ef3067690d838dfc9a12686caa015e85e217f01 Mon Sep 17 00:00:00 2001 > From: Kent Overstreet <kent.overstreet@linux.dev> > Date: Wed, 27 Nov 2024 16:32:26 -0500 > Subject: [PATCH] bcachefs: dio write: Take ref on mm_struct when using > asynchronously > > When invoked from aio, mm_struct is guaranteed to outlive the request > since its lifetime is tied to the io_context - but that's not the case > for io_uring, it's possible that a process could be killed and mm_struct > goes away while a request is in flight. > > So if we're submitting the rest of the io asynchronously, we may need a > ref on mm_struct. > > Per Jens, this is not actually a bug because we're not yet flipping on > FMODE_NOWAIT, meaning io_uring will do the submission from an io_worker > kthread - but this patch is necessary for safely flipping on > FMODE_NOWAIT for more efficient submissions in the future. > > Reported-by: Jann Horn <jannh@google.com> > Cc: Jens Axboe <axboe@kernel.dk> > Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev> > > diff --git a/fs/bcachefs/fs-io-direct.c b/fs/bcachefs/fs-io-direct.c > index 2089c36b5866..8da0577411e8 100644 > --- a/fs/bcachefs/fs-io-direct.c > +++ b/fs/bcachefs/fs-io-direct.c > @@ -226,6 +226,7 @@ struct dio_write { > struct mm_struct *mm; > const struct iovec *iov; > unsigned loop:1, > + have_mm_ref:1, > extending:1, > sync:1, > flush:1; > @@ -390,6 +391,9 @@ static __always_inline long bch2_dio_write_done(struct dio_write *dio) > > kfree(dio->iov); > > + if (dio->have_mm_ref) > + mmdrop(dio->mm); > + > ret = dio->op.error ?: ((long) dio->written << 9); > bio_put(&dio->op.wbio.bio); > > @@ -529,9 +533,24 @@ static __always_inline long bch2_dio_write_loop(struct dio_write *dio) > > if (unlikely(dio->iter.count) && > !dio->sync && > - !dio->loop && > - bch2_dio_write_copy_iov(dio)) > - dio->sync = sync = true; > + !dio->loop) { > + /* > + * Rest of write will be submitted asynchronously - > + * unless copying the iov fails: > + */ > + if (likely(!bch2_dio_write_copy_iov(dio))) { > + /* > + * aio guarantees that mm_struct outlives the > + * request, but io_uring does not > + */ > + if (dio->mm) { > + mmgrab(dio->mm); > + dio->have_mm_ref = true; > + } > + } else { > + dio->sync = sync = true; > + } > + } > > dio->loop = true; > closure_call(&dio->op.cl, bch2_write, NULL, NULL); > @@ -559,15 +578,25 @@ static __always_inline long bch2_dio_write_loop(struct dio_write *dio) > > static noinline __cold void bch2_dio_write_continue(struct dio_write *dio) > { > - struct mm_struct *mm = dio->mm; > + struct mm_struct *mm = dio->have_mm_ref ? dio->mm: NULL; > > bio_reset(&dio->op.wbio.bio, NULL, REQ_OP_WRITE); > > - if (mm) > + if (mm) { > + if (unlikely(!mmget_not_zero(mm))) { > + /* process exited */ > + dio->op.error = -ESRCH; > + bch2_dio_write_done(dio); > + return; > + } > + > kthread_use_mm(mm); > + } > bch2_dio_write_loop(dio); > - if (mm) > + if (mm) { > + mmput(mm); > kthread_unuse_mm(mm); > + } I think the mmput() should go after kthread_unuse_mm(), though I don't know if this ordering actually breaks anything. Looks fine apart from that at a glance. > } > > static void bch2_dio_write_loop_async(struct bch_write_op *op) ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2024-11-27 23:12 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-11-27 21:36 [PATCH] bcachefs: dio write: Take ref on mm_struct when using asynchronously Kent Overstreet 2024-11-27 21:47 ` Jens Axboe 2024-11-27 21:48 ` Jann Horn 2024-11-27 22:11 ` Kent Overstreet 2024-11-27 22:21 ` Jens Axboe 2024-11-27 23:11 ` Jann Horn
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox