Message ID | 20180309191144.1817-2-christian.koenig@amd.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Fri, Mar 09, 2018 at 08:11:41PM +0100, Christian K??nig wrote: > Each importer can now provide an invalidate_mappings callback. > > This allows the exporter to provide the mappings without the need to pin > the backing store. > > Signed-off-by: Christian K??nig <christian.koenig@amd.com> > --- > drivers/dma-buf/dma-buf.c | 25 +++++++++++++++++++++++++ > include/linux/dma-buf.h | 36 ++++++++++++++++++++++++++++++++++++ > 2 files changed, 61 insertions(+) > > diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c > index d78d5fc173dc..ed8d5844ae74 100644 > --- a/drivers/dma-buf/dma-buf.c > +++ b/drivers/dma-buf/dma-buf.c > @@ -629,6 +629,9 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, > > might_sleep(); > > + if (attach->invalidate_mappings) > + reservation_object_assert_held(attach->dmabuf->resv); > + > if (WARN_ON(!attach || !attach->dmabuf)) > return ERR_PTR(-EINVAL); > > @@ -656,6 +659,9 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, > { > might_sleep(); > > + if (attach->invalidate_mappings) > + reservation_object_assert_held(attach->dmabuf->resv); > + > if (WARN_ON(!attach || !attach->dmabuf || !sg_table)) > return; > > @@ -664,6 +670,25 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, > } > EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment); > > +/** > + * dma_buf_invalidate_mappings - invalidate all mappings of this dma_buf > + * > + * @dmabuf: [in] buffer which mappings should be invalidated > + * > + * Informs all attachmenst that they need to destroy and recreated all their > + * mappings. > + */ > +void dma_buf_invalidate_mappings(struct dma_buf *dmabuf) > +{ > + struct dma_buf_attachment *attach; > + > + reservation_object_assert_held(dmabuf->resv); > + > + list_for_each_entry(attach, &dmabuf->attachments, node) > + attach->invalidate_mappings(attach); To make the locking work I think we also need to require importers to hold the reservation object while attaching/detaching. Otherwise the list walk above could go boom. We could use the existing dma-buf lock, but I think that'll just result in deadlocks. > +} > +EXPORT_SYMBOL_GPL(dma_buf_invalidate_mappings); > + > /** > * DOC: cpu access > * > diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h > index 085db2fee2d7..c1e2f7d93509 100644 > --- a/include/linux/dma-buf.h > +++ b/include/linux/dma-buf.h > @@ -91,6 +91,18 @@ struct dma_buf_ops { > */ > void (*detach)(struct dma_buf *, struct dma_buf_attachment *); > > + /** > + * @supports_mapping_invalidation: > + * > + * True for exporters which supports unpinned DMA-buf operation using > + * the reservation lock. > + * > + * When attachment->invalidate_mappings is set the @map_dma_buf and > + * @unmap_dma_buf callbacks can be called with the reservation lock > + * held. > + */ > + bool supports_mapping_invalidation; Why do we need this? Importer could simply always register with the invalidate_mapping hook registered, and exporters could use it when they see fit. That gives us more lockdep coverage to make sure importers use their attachment callbacks correctly (aka they hold the reservation object). > + > /** > * @map_dma_buf: > * > @@ -326,6 +338,29 @@ struct dma_buf_attachment { > struct device *dev; > struct list_head node; > void *priv; > + > + /** > + * @invalidate_mappings: > + * > + * Optional callback provided by the importer of the attachment which > + * must be set before mappings are created. This doesn't work, it must be set before the attachment is created, otherwise you race with your invalidate callback. I think the simplest option would be to add a new dma_buf_attach_dynamic (well except a less crappy name). > + * > + * If provided the exporter can avoid pinning the backing store while > + * mappings exists. > + * > + * The function is called with the lock of the reservation object > + * associated with the dma_buf held and the mapping function must be > + * called with this lock held as well. This makes sure that no mapping > + * is created concurrently with an ongoing invalidation. > + * > + * After the callback all existing mappings are still valid until all > + * fences in the dma_bufs reservation object are signaled, but should be > + * destroyed by the importer as soon as possible. Do we guarantee that the importer will attach a fence, after which the mapping will be gone? What about re-trying? Or just best effort (i.e. only useful for evicting to try to make room). I think a helper which both unmaps _and_ waits for all the fences to clear would be best, with some guarantees that it'll either fail or all the mappings _will_ be gone. The locking for that one will be hilarious, since we need to figure out dmabuf->lock vs. the reservation. I kinda prefer we throw away the dmabuf->lock and superseed it entirely by the reservation lock. > + * > + * New mappings can be created immediately, but can't be used before the > + * exclusive fence in the dma_bufs reservation object is signaled. > + */ > + void (*invalidate_mappings)(struct dma_buf_attachment *attach); Bunch of questions about exact semantics, but I very much like this. And I think besides those technical details, the overall approach seems sound. -Daniel > }; > > /** > @@ -391,6 +426,7 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *, > enum dma_data_direction); > void dma_buf_unmap_attachment(struct dma_buf_attachment *, struct sg_table *, > enum dma_data_direction); > +void dma_buf_invalidate_mappings(struct dma_buf *dma_buf); > int dma_buf_begin_cpu_access(struct dma_buf *dma_buf, > enum dma_data_direction dir); > int dma_buf_end_cpu_access(struct dma_buf *dma_buf, > -- > 2.14.1 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel
Am 12.03.2018 um 18:07 schrieb Daniel Vetter: > On Fri, Mar 09, 2018 at 08:11:41PM +0100, Christian K??nig wrote: >> [SNIP] >> >> +/** >> + * dma_buf_invalidate_mappings - invalidate all mappings of this dma_buf >> + * >> + * @dmabuf: [in] buffer which mappings should be invalidated >> + * >> + * Informs all attachmenst that they need to destroy and recreated all their >> + * mappings. >> + */ >> +void dma_buf_invalidate_mappings(struct dma_buf *dmabuf) >> +{ >> + struct dma_buf_attachment *attach; >> + >> + reservation_object_assert_held(dmabuf->resv); >> + >> + list_for_each_entry(attach, &dmabuf->attachments, node) >> + attach->invalidate_mappings(attach); > To make the locking work I think we also need to require importers to hold > the reservation object while attaching/detaching. Otherwise the list walk > above could go boom. Oh, good point. Going, to fix this. > [SNIP] >> + /** >> + * @supports_mapping_invalidation: >> + * >> + * True for exporters which supports unpinned DMA-buf operation using >> + * the reservation lock. >> + * >> + * When attachment->invalidate_mappings is set the @map_dma_buf and >> + * @unmap_dma_buf callbacks can be called with the reservation lock >> + * held. >> + */ >> + bool supports_mapping_invalidation; > Why do we need this? Importer could simply always register with the > invalidate_mapping hook registered, and exporters could use it when they > see fit. That gives us more lockdep coverage to make sure importers use > their attachment callbacks correctly (aka they hold the reservation > object). One sole reason: Backward compability. I didn't wanted to audit all those different drivers if they can handle being called with the reservation lock held. > >> + >> /** >> * @map_dma_buf: >> * >> @@ -326,6 +338,29 @@ struct dma_buf_attachment { >> struct device *dev; >> struct list_head node; >> void *priv; >> + >> + /** >> + * @invalidate_mappings: >> + * >> + * Optional callback provided by the importer of the attachment which >> + * must be set before mappings are created. > This doesn't work, it must be set before the attachment is created, > otherwise you race with your invalidate callback. Another good point. > > I think the simplest option would be to add a new dma_buf_attach_dynamic > (well except a less crappy name). Well how about adding an optional invalidate_mappings parameter to the existing dma_buf_attach? > >> + * >> + * If provided the exporter can avoid pinning the backing store while >> + * mappings exists. >> + * >> + * The function is called with the lock of the reservation object >> + * associated with the dma_buf held and the mapping function must be >> + * called with this lock held as well. This makes sure that no mapping >> + * is created concurrently with an ongoing invalidation. >> + * >> + * After the callback all existing mappings are still valid until all >> + * fences in the dma_bufs reservation object are signaled, but should be >> + * destroyed by the importer as soon as possible. > Do we guarantee that the importer will attach a fence, after which the > mapping will be gone? What about re-trying? Or just best effort (i.e. only > useful for evicting to try to make room). The importer should attach fences for all it's operations with the DMA-buf. > I think a helper which both unmaps _and_ waits for all the fences to clear > would be best, with some guarantees that it'll either fail or all the > mappings _will_ be gone. The locking for that one will be hilarious, since > we need to figure out dmabuf->lock vs. the reservation. I kinda prefer we > throw away the dmabuf->lock and superseed it entirely by the reservation > lock. Big NAK on that. The whole API is asynchronously, e.g. we never block for any operation to finish. Otherwise you run into big trouble with cross device GPU resets and stuff like that. >> + * >> + * New mappings can be created immediately, but can't be used before the >> + * exclusive fence in the dma_bufs reservation object is signaled. >> + */ >> + void (*invalidate_mappings)(struct dma_buf_attachment *attach); > Bunch of questions about exact semantics, but I very much like this. And I > think besides those technical details, the overall approach seems sound. Yeah this initial implementation was buggy like hell. Just wanted to confirm that the idea is going in the right direction. Thanks for the comments, Christian. > -Daniel >
On Mon, Mar 12, 2018 at 08:13:15PM +0100, Christian K??nig wrote: > Am 12.03.2018 um 18:07 schrieb Daniel Vetter: > > On Fri, Mar 09, 2018 at 08:11:41PM +0100, Christian K??nig wrote: > > > [SNIP] > > > +/** > > > + * dma_buf_invalidate_mappings - invalidate all mappings of this dma_buf > > > + * > > > + * @dmabuf: [in] buffer which mappings should be invalidated > > > + * > > > + * Informs all attachmenst that they need to destroy and recreated all their > > > + * mappings. > > > + */ > > > +void dma_buf_invalidate_mappings(struct dma_buf *dmabuf) > > > +{ > > > + struct dma_buf_attachment *attach; > > > + > > > + reservation_object_assert_held(dmabuf->resv); > > > + > > > + list_for_each_entry(attach, &dmabuf->attachments, node) > > > + attach->invalidate_mappings(attach); > > To make the locking work I think we also need to require importers to hold > > the reservation object while attaching/detaching. Otherwise the list walk > > above could go boom. > > Oh, good point. Going, to fix this. > > > [SNIP] > > > + /** > > > + * @supports_mapping_invalidation: > > > + * > > > + * True for exporters which supports unpinned DMA-buf operation using > > > + * the reservation lock. > > > + * > > > + * When attachment->invalidate_mappings is set the @map_dma_buf and > > > + * @unmap_dma_buf callbacks can be called with the reservation lock > > > + * held. > > > + */ > > > + bool supports_mapping_invalidation; > > Why do we need this? Importer could simply always register with the > > invalidate_mapping hook registered, and exporters could use it when they > > see fit. That gives us more lockdep coverage to make sure importers use > > their attachment callbacks correctly (aka they hold the reservation > > object). > > One sole reason: Backward compability. > > I didn't wanted to audit all those different drivers if they can handle > being called with the reservation lock held. > > > > > > + > > > /** > > > * @map_dma_buf: > > > * > > > @@ -326,6 +338,29 @@ struct dma_buf_attachment { > > > struct device *dev; > > > struct list_head node; > > > void *priv; > > > + > > > + /** > > > + * @invalidate_mappings: > > > + * > > > + * Optional callback provided by the importer of the attachment which > > > + * must be set before mappings are created. > > This doesn't work, it must be set before the attachment is created, > > otherwise you race with your invalidate callback. > > Another good point. > > > > > I think the simplest option would be to add a new dma_buf_attach_dynamic > > (well except a less crappy name). > > Well how about adding an optional invalidate_mappings parameter to the > existing dma_buf_attach? Not sure that's best, it might confuse dumb importers and you need to change all the callers. But up to you. > > > + * > > > + * If provided the exporter can avoid pinning the backing store while > > > + * mappings exists. > > > + * > > > + * The function is called with the lock of the reservation object > > > + * associated with the dma_buf held and the mapping function must be > > > + * called with this lock held as well. This makes sure that no mapping > > > + * is created concurrently with an ongoing invalidation. > > > + * > > > + * After the callback all existing mappings are still valid until all > > > + * fences in the dma_bufs reservation object are signaled, but should be > > > + * destroyed by the importer as soon as possible. > > Do we guarantee that the importer will attach a fence, after which the > > mapping will be gone? What about re-trying? Or just best effort (i.e. only > > useful for evicting to try to make room). > > The importer should attach fences for all it's operations with the DMA-buf. > > > I think a helper which both unmaps _and_ waits for all the fences to clear > > would be best, with some guarantees that it'll either fail or all the > > mappings _will_ be gone. The locking for that one will be hilarious, since > > we need to figure out dmabuf->lock vs. the reservation. I kinda prefer we > > throw away the dmabuf->lock and superseed it entirely by the reservation > > lock. > > Big NAK on that. The whole API is asynchronously, e.g. we never block for > any operation to finish. > > Otherwise you run into big trouble with cross device GPU resets and stuff > like that. But how will the unmapping work then? You can't throw the sg list away before the dma stopped. The dma only stops once the fence is signalled. The importer can't call dma_buf_detach because the reservation lock is hogged already by the exporter trying to unmap everything. How is this supposed to work? Re GPU might cause a deadlock: Isn't that already a problem if you hold reservations of buffers used on other gpus, which want those reservations to complete the gpu reset, but that gpu reset blocks some fence that the reservation holder is waiting for? We have tons of fun with deadlocks against GPU resets, and loooooots of testcases, and I kinda get the impression amdgpu is throwing a lot of issues under the rug through trylock tricks that shut up lockdep, but don't fix much really. btw adding cross-release lockdep annotations for fences will probably turn up _lots_ more bugs in this area. > > > + * > > > + * New mappings can be created immediately, but can't be used before the > > > + * exclusive fence in the dma_bufs reservation object is signaled. > > > + */ > > > + void (*invalidate_mappings)(struct dma_buf_attachment *attach); > > Bunch of questions about exact semantics, but I very much like this. And I > > think besides those technical details, the overall approach seems sound. > > Yeah this initial implementation was buggy like hell. Just wanted to confirm > that the idea is going in the right direction. I wanted this 7 years ago, idea very much acked :-) Cheers, Daniel
Am 13.03.2018 um 16:17 schrieb Daniel Vetter: > [SNIP] >>> I think a helper which both unmaps _and_ waits for all the fences to clear >>> would be best, with some guarantees that it'll either fail or all the >>> mappings _will_ be gone. The locking for that one will be hilarious, since >>> we need to figure out dmabuf->lock vs. the reservation. I kinda prefer we >>> throw away the dmabuf->lock and superseed it entirely by the reservation >>> lock. >> Big NAK on that. The whole API is asynchronously, e.g. we never block for >> any operation to finish. >> >> Otherwise you run into big trouble with cross device GPU resets and stuff >> like that. > But how will the unmapping work then? You can't throw the sg list away > before the dma stopped. The dma only stops once the fence is signalled. > The importer can't call dma_buf_detach because the reservation lock is > hogged already by the exporter trying to unmap everything. > > How is this supposed to work? Even after invalidation the sg list stays alive until it is explicitly destroyed by the importer using dma_buf_unmap_attachment() which in turn is only allowed after all fences have signaled. The implementation is in ttm_bo_pipeline_gutting(), basically we use the same functionality as for pipelined moves/evictions which hangs the old backing store on a dummy object and destroys it after all fences signaled. While the old sg list is still about to be destroyed the importer can request a new sg list for the new location of the DMA-buf using dma_buf_map_attachment(). This new location becomes valid after the move fence in the reservation object is signaled. So from the CPU point of view multiple sg list could exists at the same time which allows us to have a seamless transition from the old to the new location from the GPU point of view. > Re GPU might cause a deadlock: Isn't that already a problem if you hold > reservations of buffers used on other gpus, which want those reservations > to complete the gpu reset, but that gpu reset blocks some fence that the > reservation holder is waiting for? Correct, that's why amdgpu and TTM tries quite hard to never wait for a fence while a reservation object is locked. The only use case I haven't fixed so far is reaping deleted object during eviction, but that is only a matter of my free time to fix it. > We have tons of fun with deadlocks against GPU resets, and loooooots of > testcases, and I kinda get the impression amdgpu is throwing a lot of > issues under the rug through trylock tricks that shut up lockdep, but > don't fix much really. Hui? Why do you think that? The only trylock I'm aware of is during eviction and there it isn't a problem. > btw adding cross-release lockdep annotations for fences will probably turn > up _lots_ more bugs in this area. At least for amdgpu that should be handled by now. >>>> + * >>>> + * New mappings can be created immediately, but can't be used before the >>>> + * exclusive fence in the dma_bufs reservation object is signaled. >>>> + */ >>>> + void (*invalidate_mappings)(struct dma_buf_attachment *attach); >>> Bunch of questions about exact semantics, but I very much like this. And I >>> think besides those technical details, the overall approach seems sound. >> Yeah this initial implementation was buggy like hell. Just wanted to confirm >> that the idea is going in the right direction. > I wanted this 7 years ago, idea very much acked :-) > Ok, thanks. Good to know. Christian.
On Tue, Mar 13, 2018 at 04:52:02PM +0100, Christian König wrote: > Am 13.03.2018 um 16:17 schrieb Daniel Vetter: > > [SNIP] > > > > I think a helper which both unmaps _and_ waits for all the fences to clear > > > > would be best, with some guarantees that it'll either fail or all the > > > > mappings _will_ be gone. The locking for that one will be hilarious, since > > > > we need to figure out dmabuf->lock vs. the reservation. I kinda prefer we > > > > throw away the dmabuf->lock and superseed it entirely by the reservation > > > > lock. > > > Big NAK on that. The whole API is asynchronously, e.g. we never block for > > > any operation to finish. > > > > > > Otherwise you run into big trouble with cross device GPU resets and stuff > > > like that. > > But how will the unmapping work then? You can't throw the sg list away > > before the dma stopped. The dma only stops once the fence is signalled. > > The importer can't call dma_buf_detach because the reservation lock is > > hogged already by the exporter trying to unmap everything. > > > > How is this supposed to work? > > Even after invalidation the sg list stays alive until it is explicitly > destroyed by the importer using dma_buf_unmap_attachment() which in turn is > only allowed after all fences have signaled. > > The implementation is in ttm_bo_pipeline_gutting(), basically we use the > same functionality as for pipelined moves/evictions which hangs the old > backing store on a dummy object and destroys it after all fences signaled. > > While the old sg list is still about to be destroyed the importer can > request a new sg list for the new location of the DMA-buf using > dma_buf_map_attachment(). This new location becomes valid after the move > fence in the reservation object is signaled. > > So from the CPU point of view multiple sg list could exists at the same time > which allows us to have a seamless transition from the old to the new > location from the GPU point of view. Ok, so plan is to support fully pipeline moves and everything, with the old sg tables lazily cleaned up. I was thinking more about evicting stuff and throwing it out, where there's not going to be any new sg list but the object is going to be swapped out. I think some state flow charts (we can do SVG or DOT) in the kerneldoc would be sweet. > > Re GPU might cause a deadlock: Isn't that already a problem if you hold > > reservations of buffers used on other gpus, which want those reservations > > to complete the gpu reset, but that gpu reset blocks some fence that the > > reservation holder is waiting for? > > Correct, that's why amdgpu and TTM tries quite hard to never wait for a > fence while a reservation object is locked. We might have a fairly huge mismatch of expectations here :-/ > The only use case I haven't fixed so far is reaping deleted object during > eviction, but that is only a matter of my free time to fix it. Yeah, this is the hard one. In general the assumption is that dma_fence will get signalled no matter what you're doing, assuming the only thing you need is to not block interrupts. The i915 gpu reset logic to make that work is a bit a work of art ... If we expect amdgpu and i915 to cooperate with shared buffers I guess one has to give in. No idea how to do that best. > > We have tons of fun with deadlocks against GPU resets, and loooooots of > > testcases, and I kinda get the impression amdgpu is throwing a lot of > > issues under the rug through trylock tricks that shut up lockdep, but > > don't fix much really. > > Hui? Why do you think that? The only trylock I'm aware of is during eviction > and there it isn't a problem. mmap fault handler had one too last time I looked, and it smelled fishy. > > btw adding cross-release lockdep annotations for fences will probably turn > > up _lots_ more bugs in this area. > > At least for amdgpu that should be handled by now. You're sure? :-) Trouble is that cross-release wasn't even ever enabled, much less anyone typed the dma_fence annotations. And just cross-release alone turned up _lost_ of deadlocks in i915 between fences, async workers (userptr, gpu reset) and core mm stuff. I'd be seriously surprised if it wouldn't find an entire rats nest of issues around dma_fence once we enable it. -Daniel > > > > > + * > > > > > + * New mappings can be created immediately, but can't be used before the > > > > > + * exclusive fence in the dma_bufs reservation object is signaled. > > > > > + */ > > > > > + void (*invalidate_mappings)(struct dma_buf_attachment *attach); > > > > Bunch of questions about exact semantics, but I very much like this. And I > > > > think besides those technical details, the overall approach seems sound. > > > Yeah this initial implementation was buggy like hell. Just wanted to confirm > > > that the idea is going in the right direction. > > I wanted this 7 years ago, idea very much acked :-) > > > Ok, thanks. Good to know. > > Christian.
Am 13.03.2018 um 17:00 schrieb Daniel Vetter: > On Tue, Mar 13, 2018 at 04:52:02PM +0100, Christian König wrote: >> Am 13.03.2018 um 16:17 schrieb Daniel Vetter: >> [SNIP] > Ok, so plan is to support fully pipeline moves and everything, with the > old sg tables lazily cleaned up. I was thinking more about evicting stuff > and throwing it out, where there's not going to be any new sg list but the > object is going to be swapped out. Yes, exactly. Well my example was the unlikely case when the object is swapped out and immediately swapped in again because somebody needs it. > > I think some state flow charts (we can do SVG or DOT) in the kerneldoc > would be sweet.Yeah, probably a good idea. Sounds good and I find it great that you're volunteering for that :D Ok seriously, my drawing capabilities are a bit underdeveloped. So I would prefer if somebody could at least help with that. >>> Re GPU might cause a deadlock: Isn't that already a problem if you hold >>> reservations of buffers used on other gpus, which want those reservations >>> to complete the gpu reset, but that gpu reset blocks some fence that the >>> reservation holder is waiting for? >> Correct, that's why amdgpu and TTM tries quite hard to never wait for a >> fence while a reservation object is locked. > We might have a fairly huge mismatch of expectations here :-/ What do you mean with that? >> The only use case I haven't fixed so far is reaping deleted object during >> eviction, but that is only a matter of my free time to fix it. > Yeah, this is the hard one. Actually it isn't so hard, it's just that I didn't had time so far to clean it up and we never hit that issue so far during our reset testing. The main point missing just a bit of functionality in the reservation object and Chris and I already had a good idea how to implement that. > In general the assumption is that dma_fence will get signalled no matter > what you're doing, assuming the only thing you need is to not block > interrupts. The i915 gpu reset logic to make that work is a bit a work of > art ... Correct, but I don't understand why that is so hard on i915? Our GPU scheduler makes all of that rather trivial, e.g. fences either signal correctly or are aborted and set as erroneous after a timeout. > If we expect amdgpu and i915 to cooperate with shared buffers I guess one > has to give in. No idea how to do that best. Again at least from amdgpu side I don't see much of an issue with that. So what exactly do you have in mind here? >>> We have tons of fun with deadlocks against GPU resets, and loooooots of >>> testcases, and I kinda get the impression amdgpu is throwing a lot of >>> issues under the rug through trylock tricks that shut up lockdep, but >>> don't fix much really. >> Hui? Why do you think that? The only trylock I'm aware of is during eviction >> and there it isn't a problem. > mmap fault handler had one too last time I looked, and it smelled fishy. Good point, never wrapped my head fully around that one either. >>> btw adding cross-release lockdep annotations for fences will probably turn >>> up _lots_ more bugs in this area. >> At least for amdgpu that should be handled by now. > You're sure? :-) Yes, except for fallback paths and bootup self tests we simply never wait for fences while holding locks. > Trouble is that cross-release wasn't even ever enabled, much less anyone > typed the dma_fence annotations. And just cross-release alone turned up > _lost_ of deadlocks in i915 between fences, async workers (userptr, gpu > reset) and core mm stuff. Yeah, we had lots of fun with the mm locks as well but as far as I know Felix and I already fixed all of them. Christian. > I'd be seriously surprised if it wouldn't find an entire rats nest of > issues around dma_fence once we enable it. > -Daniel > >>>>>> + * >>>>>> + * New mappings can be created immediately, but can't be used before the >>>>>> + * exclusive fence in the dma_bufs reservation object is signaled. >>>>>> + */ >>>>>> + void (*invalidate_mappings)(struct dma_buf_attachment *attach); >>>>> Bunch of questions about exact semantics, but I very much like this. And I >>>>> think besides those technical details, the overall approach seems sound. >>>> Yeah this initial implementation was buggy like hell. Just wanted to confirm >>>> that the idea is going in the right direction. >>> I wanted this 7 years ago, idea very much acked :-) >>> >> Ok, thanks. Good to know. >> >> Christian.
On Tue, Mar 13, 2018 at 06:20:07PM +0100, Christian König wrote: > Am 13.03.2018 um 17:00 schrieb Daniel Vetter: > > On Tue, Mar 13, 2018 at 04:52:02PM +0100, Christian König wrote: > > > Am 13.03.2018 um 16:17 schrieb Daniel Vetter: > > > [SNIP] > > Ok, so plan is to support fully pipeline moves and everything, with the > > old sg tables lazily cleaned up. I was thinking more about evicting stuff > > and throwing it out, where there's not going to be any new sg list but the > > object is going to be swapped out. > > Yes, exactly. Well my example was the unlikely case when the object is > swapped out and immediately swapped in again because somebody needs it. > > > > > I think some state flow charts (we can do SVG or DOT) in the kerneldoc > > would be sweet.Yeah, probably a good idea. > > Sounds good and I find it great that you're volunteering for that :D > > Ok seriously, my drawing capabilities are a bit underdeveloped. So I would > prefer if somebody could at least help with that. Take a look at the DOT graphs for atomic I've done a while ago. I think we could make a formidable competition for who's doing the worst diagrams :-) > > > > Re GPU might cause a deadlock: Isn't that already a problem if you hold > > > > reservations of buffers used on other gpus, which want those reservations > > > > to complete the gpu reset, but that gpu reset blocks some fence that the > > > > reservation holder is waiting for? > > > Correct, that's why amdgpu and TTM tries quite hard to never wait for a > > > fence while a reservation object is locked. > > We might have a fairly huge mismatch of expectations here :-/ > > What do you mean with that? i915 expects that other drivers don't have this requirement. Our gpu reset can proceed even if it's all locked down. > > > The only use case I haven't fixed so far is reaping deleted object during > > > eviction, but that is only a matter of my free time to fix it. > > Yeah, this is the hard one. > > Actually it isn't so hard, it's just that I didn't had time so far to clean > it up and we never hit that issue so far during our reset testing. > > The main point missing just a bit of functionality in the reservation object > and Chris and I already had a good idea how to implement that. > > > In general the assumption is that dma_fence will get signalled no matter > > what you're doing, assuming the only thing you need is to not block > > interrupts. The i915 gpu reset logic to make that work is a bit a work of > > art ... > > Correct, but I don't understand why that is so hard on i915? Our GPU > scheduler makes all of that rather trivial, e.g. fences either signal > correctly or are aborted and set as erroneous after a timeout. Yes, i915 does the same. It's the locking requirement we disagree on, i915 can reset while holding locks. I think right now we don't reset while holding reservation locks, but only while holding our own locks. I think cross-release would help model us this and uncover all the funny dependency loops we have. The issue I'm seeing: amdgpu: Expects that you never hold any of the heavywheight locks while waiting for a fence (since gpu resets will need them). i915: Happily blocks on fences while holding all kinds of locks, expects gpu reset to be able to recover even in this case. Both drivers either complete the fence (with or without setting the error status to EIO or something like that), that's not the difference. The work of art I referenced is how we managed to complete gpu reset (including resubmitting) while holding plenty of locks. > > If we expect amdgpu and i915 to cooperate with shared buffers I guess one > > has to give in. No idea how to do that best. > > Again at least from amdgpu side I don't see much of an issue with that. So > what exactly do you have in mind here? > > > > > We have tons of fun with deadlocks against GPU resets, and loooooots of > > > > testcases, and I kinda get the impression amdgpu is throwing a lot of > > > > issues under the rug through trylock tricks that shut up lockdep, but > > > > don't fix much really. > > > Hui? Why do you think that? The only trylock I'm aware of is during eviction > > > and there it isn't a problem. > > mmap fault handler had one too last time I looked, and it smelled fishy. > > Good point, never wrapped my head fully around that one either. > > > > > btw adding cross-release lockdep annotations for fences will probably turn > > > > up _lots_ more bugs in this area. > > > At least for amdgpu that should be handled by now. > > You're sure? :-) > > Yes, except for fallback paths and bootup self tests we simply never wait > for fences while holding locks. That's not what I meant with "are you sure". Did you enable the cross-release stuff (after patching the bunch of leftover core kernel issues still present), annotate dma_fence with the cross-release stuff, run a bunch of multi-driver (amdgpu vs i915) dma-buf sharing tests and weep? I didn't do the full thing yet, but just within i915 we've found tons of small little deadlocks we never really considered thanks to cross release, and that wasn't even including the dma_fence annotation. Luckily nothing that needed a full-on driver redesign. I guess I need to ping core kernel maintainers about cross-release again. I'd much prefer if we could validate ->invalidate_mapping and the locking/fence dependency issues using that, instead of me having to read and understand all the drivers. > > Trouble is that cross-release wasn't even ever enabled, much less anyone > > typed the dma_fence annotations. And just cross-release alone turned up > > _lost_ of deadlocks in i915 between fences, async workers (userptr, gpu > > reset) and core mm stuff. > > Yeah, we had lots of fun with the mm locks as well but as far as I know > Felix and I already fixed all of them. Are you sure you mean cross-release fun, and not just normal lockdep fun? The cross-release is orders of magnitude more nasty imo. And we had a few discussions with core folks where they told us "no way we're going to break this depency on our side", involving a chain of cpu hotplug (suspend/resume does that to shut down non-boot cpus), worker threads, userptr, gem locking and core mm. All components required to actually close the loop. I fear that with the ->invalidate_mapping callback (which inverts the control flow between importer and exporter) and tying dma_fences into all this it will be a _lot_ worse. And I'm definitely too stupid to understand all the dependency chains without the aid of lockdep and a full test suite (we have a bunch of amdgpu/i915 dma-buf tests in igt btw). -Daniel > > Christian. > > > I'd be seriously surprised if it wouldn't find an entire rats nest of > > issues around dma_fence once we enable it. > > -Daniel > > > > > > > > > + * > > > > > > > + * New mappings can be created immediately, but can't be used before the > > > > > > > + * exclusive fence in the dma_bufs reservation object is signaled. > > > > > > > + */ > > > > > > > + void (*invalidate_mappings)(struct dma_buf_attachment *attach); > > > > > > Bunch of questions about exact semantics, but I very much like this. And I > > > > > > think besides those technical details, the overall approach seems sound. > > > > > Yeah this initial implementation was buggy like hell. Just wanted to confirm > > > > > that the idea is going in the right direction. > > > > I wanted this 7 years ago, idea very much acked :-) > > > > > > > Ok, thanks. Good to know. > > > > > > Christian. >
Am 15.03.2018 um 10:20 schrieb Daniel Vetter: > On Tue, Mar 13, 2018 at 06:20:07PM +0100, Christian König wrote: > [SNIP] > Take a look at the DOT graphs for atomic I've done a while ago. I think we > could make a formidable competition for who's doing the worst diagrams :-) Thanks, going to give that a try. > [SNIP] > amdgpu: Expects that you never hold any of the heavywheight locks while > waiting for a fence (since gpu resets will need them). > > i915: Happily blocks on fences while holding all kinds of locks, expects > gpu reset to be able to recover even in this case. In this case I can comfort you, the looks amdgpu needs to grab during GPU reset are the reservation lock of the VM page tables. I have strong doubt that i915 will ever hold those. Could be that we run into problems because Thread A hold lock 1 tries to take lock 2, then i915 holds 2 and our reset path needs 1. > [SNIP] >> Yes, except for fallback paths and bootup self tests we simply never wait >> for fences while holding locks. > That's not what I meant with "are you sure". Did you enable the > cross-release stuff (after patching the bunch of leftover core kernel > issues still present), annotate dma_fence with the cross-release stuff, > run a bunch of multi-driver (amdgpu vs i915) dma-buf sharing tests and > weep? Ok, what exactly do you mean with cross-release checking? > I didn't do the full thing yet, but just within i915 we've found tons of > small little deadlocks we never really considered thanks to cross release, > and that wasn't even including the dma_fence annotation. Luckily nothing > that needed a full-on driver redesign. > > I guess I need to ping core kernel maintainers about cross-release again. > I'd much prefer if we could validate ->invalidate_mapping and the > locking/fence dependency issues using that, instead of me having to read > and understand all the drivers. [SNIP] > I fear that with the ->invalidate_mapping callback (which inverts the > control flow between importer and exporter) and tying dma_fences into all > this it will be a _lot_ worse. And I'm definitely too stupid to understand > all the dependency chains without the aid of lockdep and a full test suite > (we have a bunch of amdgpu/i915 dma-buf tests in igt btw). Yes, that is also something I worry about. Regards, Christian.
On Thu, Mar 15, 2018 at 10:56 AM, Christian König <ckoenig.leichtzumerken@gmail.com> wrote: > Am 15.03.2018 um 10:20 schrieb Daniel Vetter: >> >> On Tue, Mar 13, 2018 at 06:20:07PM +0100, Christian König wrote: >> [SNIP] >> Take a look at the DOT graphs for atomic I've done a while ago. I think we >> could make a formidable competition for who's doing the worst diagrams :-) > > > Thanks, going to give that a try. > >> [SNIP] >> amdgpu: Expects that you never hold any of the heavywheight locks while >> waiting for a fence (since gpu resets will need them). >> >> i915: Happily blocks on fences while holding all kinds of locks, expects >> gpu reset to be able to recover even in this case. > > > In this case I can comfort you, the looks amdgpu needs to grab during GPU > reset are the reservation lock of the VM page tables. I have strong doubt > that i915 will ever hold those. Ah good, means that very likely there's at least no huge fundamental design issue that we run into. > Could be that we run into problems because Thread A hold lock 1 tries to > take lock 2, then i915 holds 2 and our reset path needs 1. Yeah that might happen, but lockdep will catch those, and generally those cases can be fixed with slight reordering or re-annotating of the code to avoid upsetting lockdep. As long as we don't have a full-on functional dependency (which is what I've feared). >> [SNIP] >>> >>> Yes, except for fallback paths and bootup self tests we simply never wait >>> for fences while holding locks. >> >> That's not what I meant with "are you sure". Did you enable the >> cross-release stuff (after patching the bunch of leftover core kernel >> issues still present), annotate dma_fence with the cross-release stuff, >> run a bunch of multi-driver (amdgpu vs i915) dma-buf sharing tests and >> weep? > > > Ok, what exactly do you mean with cross-release checking? Current lockdep doesn't spot deadlocks like the below: thread A: holds mutex, waiting for completion. thread B: acquires mutex before it will ever signal the completion A is waiting for ->deadlock cross-release lockdep support can catch these through new fancy annotations. Similar waiter/signaller annotations exists for waiting on workers and anything else, and it would be a perfect fit for waiter/signaller code around dma_fence. lwn has you covered a usual: https://lwn.net/Articles/709849/ Cheers, Daniel >> I didn't do the full thing yet, but just within i915 we've found tons of >> small little deadlocks we never really considered thanks to cross release, >> and that wasn't even including the dma_fence annotation. Luckily nothing >> that needed a full-on driver redesign. >> >> I guess I need to ping core kernel maintainers about cross-release again. >> I'd much prefer if we could validate ->invalidate_mapping and the >> locking/fence dependency issues using that, instead of me having to read >> and understand all the drivers. > > [SNIP] >> >> I fear that with the ->invalidate_mapping callback (which inverts the >> control flow between importer and exporter) and tying dma_fences into all >> this it will be a _lot_ worse. And I'm definitely too stupid to understand >> all the dependency chains without the aid of lockdep and a full test suite >> (we have a bunch of amdgpu/i915 dma-buf tests in igt btw). > > > Yes, that is also something I worry about. > > Regards, > Christian.
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index d78d5fc173dc..ed8d5844ae74 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -629,6 +629,9 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, might_sleep(); + if (attach->invalidate_mappings) + reservation_object_assert_held(attach->dmabuf->resv); + if (WARN_ON(!attach || !attach->dmabuf)) return ERR_PTR(-EINVAL); @@ -656,6 +659,9 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, { might_sleep(); + if (attach->invalidate_mappings) + reservation_object_assert_held(attach->dmabuf->resv); + if (WARN_ON(!attach || !attach->dmabuf || !sg_table)) return; @@ -664,6 +670,25 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, } EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment); +/** + * dma_buf_invalidate_mappings - invalidate all mappings of this dma_buf + * + * @dmabuf: [in] buffer which mappings should be invalidated + * + * Informs all attachmenst that they need to destroy and recreated all their + * mappings. + */ +void dma_buf_invalidate_mappings(struct dma_buf *dmabuf) +{ + struct dma_buf_attachment *attach; + + reservation_object_assert_held(dmabuf->resv); + + list_for_each_entry(attach, &dmabuf->attachments, node) + attach->invalidate_mappings(attach); +} +EXPORT_SYMBOL_GPL(dma_buf_invalidate_mappings); + /** * DOC: cpu access * diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 085db2fee2d7..c1e2f7d93509 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -91,6 +91,18 @@ struct dma_buf_ops { */ void (*detach)(struct dma_buf *, struct dma_buf_attachment *); + /** + * @supports_mapping_invalidation: + * + * True for exporters which supports unpinned DMA-buf operation using + * the reservation lock. + * + * When attachment->invalidate_mappings is set the @map_dma_buf and + * @unmap_dma_buf callbacks can be called with the reservation lock + * held. + */ + bool supports_mapping_invalidation; + /** * @map_dma_buf: * @@ -326,6 +338,29 @@ struct dma_buf_attachment { struct device *dev; struct list_head node; void *priv; + + /** + * @invalidate_mappings: + * + * Optional callback provided by the importer of the attachment which + * must be set before mappings are created. + * + * If provided the exporter can avoid pinning the backing store while + * mappings exists. + * + * The function is called with the lock of the reservation object + * associated with the dma_buf held and the mapping function must be + * called with this lock held as well. This makes sure that no mapping + * is created concurrently with an ongoing invalidation. + * + * After the callback all existing mappings are still valid until all + * fences in the dma_bufs reservation object are signaled, but should be + * destroyed by the importer as soon as possible. + * + * New mappings can be created immediately, but can't be used before the + * exclusive fence in the dma_bufs reservation object is signaled. + */ + void (*invalidate_mappings)(struct dma_buf_attachment *attach); }; /** @@ -391,6 +426,7 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *, enum dma_data_direction); void dma_buf_unmap_attachment(struct dma_buf_attachment *, struct sg_table *, enum dma_data_direction); +void dma_buf_invalidate_mappings(struct dma_buf *dma_buf); int dma_buf_begin_cpu_access(struct dma_buf *dma_buf, enum dma_data_direction dir); int dma_buf_end_cpu_access(struct dma_buf *dma_buf,
Each importer can now provide an invalidate_mappings callback. This allows the exporter to provide the mappings without the need to pin the backing store. Signed-off-by: Christian König <christian.koenig@amd.com> --- drivers/dma-buf/dma-buf.c | 25 +++++++++++++++++++++++++ include/linux/dma-buf.h | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+)