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 | 64 ++++++++++++++++++++++++++---------- include/drm/drm_gpusvm.h | 5 +++ 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 42e606b94681..c89ff4a9d081 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -1703,11 +1703,11 @@ 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; - bool all_valid; for (p = 0; p < num_pages; ++p) - if (!svm_pages[p].drm) + if (map_dma && !svm_pages[p].drm) return -EINVAL; retry: @@ -1716,12 +1716,24 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, hmm_range.notifier_seq = mmu_interval_read_begin(notifier); - all_valid = true; - for (p = 0; p < num_pages; ++p) - if (!drm_gpusvm_pages_valid_unlocked(gpusvm, &svm_pages[p])) - all_valid = false; - if (all_valid) - goto set_seqno; + /* + * The HMM fault is shared by all the drm_gpusvm_pages instances (they + * all mirror the same CPU range); only the DMA mapping below is + * per-instance. In no_dma_map mode there is no DMA mapping state to + * validate, so the fault is always redone. Otherwise skip the fault + * entirely if every instance is already valid. + * drm_gpusvm_pages_valid_unlocked() also drops the stale dma_addr array + * of any instance that is no longer valid. + */ + if (map_dma) { + bool all_valid = true; + + for (p = 0; p < num_pages; ++p) + if (!drm_gpusvm_pages_valid_unlocked(gpusvm, &svm_pages[p])) + all_valid = false; + if (all_valid) + goto set_seqno; + } pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL); if (!pfns) @@ -1731,17 +1743,24 @@ 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 = - kvmalloc_objs(*svm_pages[p].dma_addr, npages); - if (!svm_pages[p].dma_addr) { - err = -ENOMEM; - goto err_free; + /* + * Allocate the dma_addr array of each instance outside the notifier + * lock. A still-valid instance keeps its existing dma_addr array and + * is not reallocated. Skipped entirely in no_dma_map mode. + */ + if (map_dma) { + for (p = 0; p < num_pages; ++p) { + if (svm_pages[p].dma_addr) + continue; + svm_pages[p].dma_addr = + kvmalloc_objs(*svm_pages[p].dma_addr, npages); + if (!svm_pages[p].dma_addr) { + err = -ENOMEM; + goto err_free; + } + svm_pages[p].state = (struct dma_iova_state){}; + svm_pages[p].state_offset = 0; } - svm_pages[p].state = (struct dma_iova_state){}; - svm_pages[p].state_offset = 0; } /* @@ -1765,6 +1784,14 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, goto retry; } + /* + * no_dma_map: the caller only needs the HMM fault, not a device DMA + * mapping. The fault has been validated under the notifier lock + * above; skip the per-instance DMA mapping entirely. + */ + if (!map_dma) + goto done_mapping; + for (p = 0; p < num_pages; ++p) { if (drm_gpusvm_pages_valid(gpusvm, &svm_pages[p])) continue; @@ -1784,6 +1811,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 d2b6f3d2b842..84db209cf148 100644 --- a/include/drm/drm_gpusvm.h +++ b/include/drm/drm_gpusvm.h @@ -254,6 +254,10 @@ 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 (e.g. AMDXDNA userptr) that + * consume the faulted pages without needing a DMA mapping. In + * this mode @drm on the drm_gpusvm_pages is not required. * * Context that is DRM GPUSVM is operating in (i.e. user arguments). */ @@ -266,6 +270,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
