Some drivers (e.g. AMDXDNA) only need the CPU pages faulted in and tracked by the notifier, no need DMA mapping.
Add a drm_gpusvm_ctx::no_dma_map flag. When set, get_pages() does the shared HMM fault and records notifier_seq, but skips svm_pages->drm validation, the dma_addr allocation and drm_gpusvm_dma_map_pages(). With no mapping state to check, the fault is redone on every call. The default (no_dma_map == 0) is unchanged. Suggested-by: Matthew Brost <[email protected]> Signed-off-by: Honglei Huang <[email protected]> --- drivers/gpu/drm/drm_gpusvm.c | 44 ++++++++++++++++++++++++++---------- include/drm/drm_gpusvm.h | 9 ++++++++ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 810f801a9f7..0156ee82d7c 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -1663,6 +1663,12 @@ static int drm_gpusvm_dma_map_pages(struct drm_gpusvm *gpusvm, * On error the instances mapped before the failing one stay mapped, so the * caller must unmap and free every instance regardless of the return value. * + * With &drm_gpusvm_ctx.no_dma_map no mapping state is recorded, so + * drm_gpusvm_pages_valid() never returns true and success is only a snapshot: + * the caller must recheck mmu_interval_read_retry() against the recorded + * &drm_gpusvm_pages.notifier_seq under the notifier lock, and hold it until + * its work is visible to invalidation. + * * Return: 0 on success, negative error code on failure. */ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, @@ -1689,11 +1695,18 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, int err = 0; enum dma_data_direction dma_dir = ctx->read_only ? DMA_TO_DEVICE : DMA_BIDIRECTIONAL; + const bool map_dma = !ctx->no_dma_map; unsigned int p; - for (p = 0; p < num_pages; ++p) - if (!svm_pages[p].drm) - return -EINVAL; + if (ctx->no_dma_map && ctx->devmem_only) + return -EINVAL; + + if (map_dma) { + for (p = 0; p < num_pages; ++p) { + if (!svm_pages[p].drm) + return -EINVAL; + } + } retry: remaining = timeout - jiffies; @@ -1703,7 +1716,8 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, hmm_range.notifier_seq = mmu_interval_read_begin(notifier); - if (drm_gpusvm_pages_valid_unlocked(gpusvm, svm_pages, num_pages)) + if (map_dma && + drm_gpusvm_pages_valid_unlocked(gpusvm, svm_pages, num_pages)) goto set_seqno; pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL); @@ -1721,14 +1735,16 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, if (err) goto err_free; - for (p = 0; p < num_pages; ++p) { - if (svm_pages[p].dma_addr) - continue; - svm_pages[p].dma_addr = - kvzalloc_objs(*svm_pages[p].dma_addr, npages); - if (!svm_pages[p].dma_addr) { - err = -ENOMEM; - goto err_free; + if (map_dma) { + for (p = 0; p < num_pages; ++p) { + if (svm_pages[p].dma_addr) + continue; + svm_pages[p].dma_addr = + kvzalloc_objs(*svm_pages[p].dma_addr, npages); + if (!svm_pages[p].dma_addr) { + err = -ENOMEM; + goto err_free; + } } } @@ -1753,6 +1769,9 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, goto retry; } + if (!map_dma) + goto done_mapping; + for (p = 0; p < num_pages; ++p) { if (drm_gpusvm_pages_valid(gpusvm, &svm_pages[p])) continue; @@ -1771,6 +1790,7 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, } } +done_mapping: drm_gpusvm_notifier_unlock(gpusvm); kvfree(pfns); set_seqno: diff --git a/include/drm/drm_gpusvm.h b/include/drm/drm_gpusvm.h index d2b6f3d2b84..ec7b81957b1 100644 --- a/include/drm/drm_gpusvm.h +++ b/include/drm/drm_gpusvm.h @@ -254,6 +254,14 @@ struct drm_gpusvm { * @allow_mixed: Allow mixed mappings in get pages. Mixing between system and * single dpagemap is supported, mixing between multiple dpagemap * is unsupported. + * @no_dma_map: Only fault the CPU pages for the range; skip the device DMA + * mapping step. Used by drivers that consume the faulted pages + * without needing a DMA mapping. In this mode @drm on the + * drm_gpusvm_pages is not required, no mapping state is recorded + * and drm_gpusvm_pages_valid() therefore never reports these + * pages as valid; the caller revalidates the snapshot itself, see + * drm_gpusvm_get_pages(). @devmem_only is rejected and no page + * type check is performed, so @allow_mixed has no effect. * * Context that is DRM GPUSVM is operating in (i.e. user arguments). */ @@ -266,6 +274,7 @@ struct drm_gpusvm_ctx { unsigned int devmem_possible :1; unsigned int devmem_only :1; unsigned int allow_mixed :1; + unsigned int no_dma_map :1; }; int drm_gpusvm_init(struct drm_gpusvm *gpusvm, -- 2.34.1
