linux/drivers/gpu/drm/xe/xe_dma_buf.c
Dave Airlie d2b9e2f8a1 Core Changes:
Fix drm_gpusvm kernel-doc (Lucas)
 
 Driver Changes:
 - Release guc ids before cancelling work (Tejas)
 - Remove a duplicated pc_start_call (Rodrigo)
 - Fix an incorrect assert in previous userptr fixes (Thomas)
 - Remove gen11 assertions and prefixes (Lucas)
 - Drop sentinels from arg to xe_rtp_process_to_src (Lucas)
 - Temporarily disable D3Cold on BMG (Rodrigo)
 - Fix MOCS debugfs LNCF readout (Tvrtko)
 - Some ring flush cleanups (Tvrtko)
 - Use unsigned int for alignment in fb pinning code (Tvrtko)
 - Retry and wait longer for GuC PC start (Rodrigo)
 - Recognize 3DSTATE_COARSE_PIXEL in LRC dumps (Matt Roper)
 - Remove reduntant check in xe_vm_create_ioctl() (Xin)
 - A bunch of SRIOV updates (Michal)
 - Add stats for SVM page-faults (Francois)
 - Fix an UAF (Harish)
 - Expose fan speed (Raag)
 - Fix exporting xe buffer objects multiple times (Tomasz)
 - Apply a workaround (Vinay)
 - Simplify pinned bo iteration (Thomas)
 - Remove an incorrect "static" keywork (Lucas)
 - Add support for separate firmware files on each GT (Lucas)
 - Survivability handling fixes (Lucas)
 - Allow to inject error in early probe (Lucas)
 - Fix unmet direct dependencies warning (Yue Haibing)
 - More error injection during probe (Francois)
 - Coding style fix (Maarten)
 - Additional stats support (Riana)
 - Add fault injection for xe_oa_alloc_regs (Nakshrtra)
 - Add a BMG PCI ID (Matt Roper)
 - Some SVM fixes and preliminary SVM multi-device work (Thomas)
 - Switch the migrate code from drm managed to dev managed (Aradhya)
 - Fix an out-of-bounds shift when invalidating TLB (Thomas)
 - Ensure fixed_slice_mode gets set after ccs_mode change (Niranjana)
 - Use local fence in error path of xe_migrate_clear (Matthew Brost)
 - More Workarounds (Julia)
 - Define sysfs_ops on all directories (Tejas)
 - Set power state to D3Cold during s2idle/s3 (Badal)
 - Devcoredump output fix (John)
 - Avoid plain 64-bit division (Arnd Bergmann)
 - Reword a debug message (John)
 - Don't print a hwconfig error message when forcing execlists (Stuart)
 - Restore an error code to avoid a smatch warning (Rodrigo)
 - Invalidate L3 read-only cachelines for geometry streams too (Kenneth)
 - Make PPHWSP size explicit in xe_gt_lrc_size() (Gustavo)
 - Add GT frequency events (Vinay)
 - Fix xe_pt_stage_bind_walk kerneldoc (Thomas)
 - Add a workaround (Aradhya)
 - Rework pinned save/restore (Matthew Auld, Matthew Brost)
 - Allow non-contig VRAM kernel BO (Matthew Auld)
 - Support non-contig VRAM provisioning for SRIOV (Matthew Auld)
 - Allow scratch-pages for unmapped parts of page-faulting VMs. (Oak)
 - Ensure XE_BO_FLAG_CPU_ADDR_MIRROR had a unique value (Matt Roper)
 - Fix taking an invalid lock on wedge (Lucas)
 - Configs and documentation for survivability mode (Riana)
 - Remove an unused macro (Shuicheng)
 - Work around a page-fault full error (Matt Brost)
 - Enable a SRIOV workaround (John)
 - Bump the recommended GuC version (John)
 - Allow to drop VRAM resizing (Lucas)
 - Don't expose privileged debugfs files if VF (Michal)
 - Don't show GGTT/LMEM debugfs files under media GT (Michal)
 - Adjust ring-buffer emission for maximum possible size (Tvrtko)
 - Fix notifier vs folio lock deadlock (Matthew Auld)
 - Stop relying on placement for dma-buf unmap Matthew Auld)
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRskUM7w1oG5rx2IZO4FpNVCsYGvwUCaADVdgAKCRC4FpNVCsYG
 vwhhAP95latf9GQChxq3th3z42uVe6YH0GxdorbgHvbrPkW9NwD+OjKNCe9BjADT
 3T1uC+Lf0MkN4kJwIHp9tS0imyh0AgI=
 =U5BC
 -----END PGP SIGNATURE-----

Merge tag 'drm-xe-next-2025-04-17' of https://gitlab.freedesktop.org/drm/xe/kernel into drm-next

Core Changes:
Fix drm_gpusvm kernel-doc (Lucas)

Driver Changes:
- Release guc ids before cancelling work (Tejas)
- Remove a duplicated pc_start_call (Rodrigo)
- Fix an incorrect assert in previous userptr fixes (Thomas)
- Remove gen11 assertions and prefixes (Lucas)
- Drop sentinels from arg to xe_rtp_process_to_src (Lucas)
- Temporarily disable D3Cold on BMG (Rodrigo)
- Fix MOCS debugfs LNCF readout (Tvrtko)
- Some ring flush cleanups (Tvrtko)
- Use unsigned int for alignment in fb pinning code (Tvrtko)
- Retry and wait longer for GuC PC start (Rodrigo)
- Recognize 3DSTATE_COARSE_PIXEL in LRC dumps (Matt Roper)
- Remove reduntant check in xe_vm_create_ioctl() (Xin)
- A bunch of SRIOV updates (Michal)
- Add stats for SVM page-faults (Francois)
- Fix an UAF (Harish)
- Expose fan speed (Raag)
- Fix exporting xe buffer objects multiple times (Tomasz)
- Apply a workaround (Vinay)
- Simplify pinned bo iteration (Thomas)
- Remove an incorrect "static" keywork (Lucas)
- Add support for separate firmware files on each GT (Lucas)
- Survivability handling fixes (Lucas)
- Allow to inject error in early probe (Lucas)
- Fix unmet direct dependencies warning (Yue Haibing)
- More error injection during probe (Francois)
- Coding style fix (Maarten)
- Additional stats support (Riana)
- Add fault injection for xe_oa_alloc_regs (Nakshrtra)
- Add a BMG PCI ID (Matt Roper)
- Some SVM fixes and preliminary SVM multi-device work (Thomas)
- Switch the migrate code from drm managed to dev managed (Aradhya)
- Fix an out-of-bounds shift when invalidating TLB (Thomas)
- Ensure fixed_slice_mode gets set after ccs_mode change (Niranjana)
- Use local fence in error path of xe_migrate_clear (Matthew Brost)
- More Workarounds (Julia)
- Define sysfs_ops on all directories (Tejas)
- Set power state to D3Cold during s2idle/s3 (Badal)
- Devcoredump output fix (John)
- Avoid plain 64-bit division (Arnd Bergmann)
- Reword a debug message (John)
- Don't print a hwconfig error message when forcing execlists (Stuart)
- Restore an error code to avoid a smatch warning (Rodrigo)
- Invalidate L3 read-only cachelines for geometry streams too (Kenneth)
- Make PPHWSP size explicit in xe_gt_lrc_size() (Gustavo)
- Add GT frequency events (Vinay)
- Fix xe_pt_stage_bind_walk kerneldoc (Thomas)
- Add a workaround (Aradhya)
- Rework pinned save/restore (Matthew Auld, Matthew Brost)
- Allow non-contig VRAM kernel BO (Matthew Auld)
- Support non-contig VRAM provisioning for SRIOV (Matthew Auld)
- Allow scratch-pages for unmapped parts of page-faulting VMs. (Oak)
- Ensure XE_BO_FLAG_CPU_ADDR_MIRROR had a unique value (Matt Roper)
- Fix taking an invalid lock on wedge (Lucas)
- Configs and documentation for survivability mode (Riana)
- Remove an unused macro (Shuicheng)
- Work around a page-fault full error (Matt Brost)
- Enable a SRIOV workaround (John)
- Bump the recommended GuC version (John)
- Allow to drop VRAM resizing (Lucas)
- Don't expose privileged debugfs files if VF (Michal)
- Don't show GGTT/LMEM debugfs files under media GT (Michal)
- Adjust ring-buffer emission for maximum possible size (Tvrtko)
- Fix notifier vs folio lock deadlock (Matthew Auld)
- Stop relying on placement for dma-buf unmap Matthew Auld)

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
Link: https://lore.kernel.org/r/aADWaEFKVmxSnDLo@fedora
2025-04-26 08:06:14 +10:00

321 lines
7.4 KiB
C

// SPDX-License-Identifier: MIT
/*
* Copyright © 2022 Intel Corporation
*/
#include "xe_dma_buf.h"
#include <kunit/test.h>
#include <linux/dma-buf.h>
#include <linux/pci-p2pdma.h>
#include <drm/drm_device.h>
#include <drm/drm_prime.h>
#include <drm/ttm/ttm_tt.h>
#include "tests/xe_test.h"
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_pm.h"
#include "xe_ttm_vram_mgr.h"
#include "xe_vm.h"
MODULE_IMPORT_NS("DMA_BUF");
static int xe_dma_buf_attach(struct dma_buf *dmabuf,
struct dma_buf_attachment *attach)
{
struct drm_gem_object *obj = attach->dmabuf->priv;
if (attach->peer2peer &&
pci_p2pdma_distance(to_pci_dev(obj->dev->dev), attach->dev, false) < 0)
attach->peer2peer = false;
if (!attach->peer2peer && !xe_bo_can_migrate(gem_to_xe_bo(obj), XE_PL_TT))
return -EOPNOTSUPP;
xe_pm_runtime_get(to_xe_device(obj->dev));
return 0;
}
static void xe_dma_buf_detach(struct dma_buf *dmabuf,
struct dma_buf_attachment *attach)
{
struct drm_gem_object *obj = attach->dmabuf->priv;
xe_pm_runtime_put(to_xe_device(obj->dev));
}
static int xe_dma_buf_pin(struct dma_buf_attachment *attach)
{
struct drm_gem_object *obj = attach->dmabuf->priv;
struct xe_bo *bo = gem_to_xe_bo(obj);
struct xe_device *xe = xe_bo_device(bo);
int ret;
/*
* For now only support pinning in TT memory, for two reasons:
* 1) Avoid pinning in a placement not accessible to some importers.
* 2) Pinning in VRAM requires PIN accounting which is a to-do.
*/
if (xe_bo_is_pinned(bo) && !xe_bo_is_mem_type(bo, XE_PL_TT)) {
drm_dbg(&xe->drm, "Can't migrate pinned bo for dma-buf pin.\n");
return -EINVAL;
}
ret = xe_bo_migrate(bo, XE_PL_TT);
if (ret) {
if (ret != -EINTR && ret != -ERESTARTSYS)
drm_dbg(&xe->drm,
"Failed migrating dma-buf to TT memory: %pe\n",
ERR_PTR(ret));
return ret;
}
ret = xe_bo_pin_external(bo);
xe_assert(xe, !ret);
return 0;
}
static void xe_dma_buf_unpin(struct dma_buf_attachment *attach)
{
struct drm_gem_object *obj = attach->dmabuf->priv;
struct xe_bo *bo = gem_to_xe_bo(obj);
xe_bo_unpin_external(bo);
}
static struct sg_table *xe_dma_buf_map(struct dma_buf_attachment *attach,
enum dma_data_direction dir)
{
struct dma_buf *dma_buf = attach->dmabuf;
struct drm_gem_object *obj = dma_buf->priv;
struct xe_bo *bo = gem_to_xe_bo(obj);
struct sg_table *sgt;
int r = 0;
if (!attach->peer2peer && !xe_bo_can_migrate(bo, XE_PL_TT))
return ERR_PTR(-EOPNOTSUPP);
if (!xe_bo_is_pinned(bo)) {
if (!attach->peer2peer)
r = xe_bo_migrate(bo, XE_PL_TT);
else
r = xe_bo_validate(bo, NULL, false);
if (r)
return ERR_PTR(r);
}
switch (bo->ttm.resource->mem_type) {
case XE_PL_TT:
sgt = drm_prime_pages_to_sg(obj->dev,
bo->ttm.ttm->pages,
bo->ttm.ttm->num_pages);
if (IS_ERR(sgt))
return sgt;
if (dma_map_sgtable(attach->dev, sgt, dir,
DMA_ATTR_SKIP_CPU_SYNC))
goto error_free;
break;
case XE_PL_VRAM0:
case XE_PL_VRAM1:
r = xe_ttm_vram_mgr_alloc_sgt(xe_bo_device(bo),
bo->ttm.resource, 0,
bo->ttm.base.size, attach->dev,
dir, &sgt);
if (r)
return ERR_PTR(r);
break;
default:
return ERR_PTR(-EINVAL);
}
return sgt;
error_free:
sg_free_table(sgt);
kfree(sgt);
return ERR_PTR(-EBUSY);
}
static void xe_dma_buf_unmap(struct dma_buf_attachment *attach,
struct sg_table *sgt,
enum dma_data_direction dir)
{
if (sg_page(sgt->sgl)) {
dma_unmap_sgtable(attach->dev, sgt, dir, 0);
sg_free_table(sgt);
kfree(sgt);
} else {
xe_ttm_vram_mgr_free_sgt(attach->dev, dir, sgt);
}
}
static int xe_dma_buf_begin_cpu_access(struct dma_buf *dma_buf,
enum dma_data_direction direction)
{
struct drm_gem_object *obj = dma_buf->priv;
struct xe_bo *bo = gem_to_xe_bo(obj);
bool reads = (direction == DMA_BIDIRECTIONAL ||
direction == DMA_FROM_DEVICE);
if (!reads)
return 0;
/* Can we do interruptible lock here? */
xe_bo_lock(bo, false);
(void)xe_bo_migrate(bo, XE_PL_TT);
xe_bo_unlock(bo);
return 0;
}
static const struct dma_buf_ops xe_dmabuf_ops = {
.attach = xe_dma_buf_attach,
.detach = xe_dma_buf_detach,
.pin = xe_dma_buf_pin,
.unpin = xe_dma_buf_unpin,
.map_dma_buf = xe_dma_buf_map,
.unmap_dma_buf = xe_dma_buf_unmap,
.release = drm_gem_dmabuf_release,
.begin_cpu_access = xe_dma_buf_begin_cpu_access,
.mmap = drm_gem_dmabuf_mmap,
.vmap = drm_gem_dmabuf_vmap,
.vunmap = drm_gem_dmabuf_vunmap,
};
struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj, int flags)
{
struct xe_bo *bo = gem_to_xe_bo(obj);
struct dma_buf *buf;
if (bo->vm)
return ERR_PTR(-EPERM);
buf = drm_gem_prime_export(obj, flags);
if (!IS_ERR(buf))
buf->ops = &xe_dmabuf_ops;
return buf;
}
static struct drm_gem_object *
xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage,
struct dma_buf *dma_buf)
{
struct dma_resv *resv = dma_buf->resv;
struct xe_device *xe = to_xe_device(dev);
struct xe_bo *bo;
int ret;
dma_resv_lock(resv, NULL);
bo = ___xe_bo_create_locked(xe, storage, NULL, resv, NULL, dma_buf->size,
0, /* Will require 1way or 2way for vm_bind */
ttm_bo_type_sg, XE_BO_FLAG_SYSTEM);
if (IS_ERR(bo)) {
ret = PTR_ERR(bo);
goto error;
}
dma_resv_unlock(resv);
return &bo->ttm.base;
error:
dma_resv_unlock(resv);
return ERR_PTR(ret);
}
static void xe_dma_buf_move_notify(struct dma_buf_attachment *attach)
{
struct drm_gem_object *obj = attach->importer_priv;
struct xe_bo *bo = gem_to_xe_bo(obj);
XE_WARN_ON(xe_bo_evict(bo));
}
static const struct dma_buf_attach_ops xe_dma_buf_attach_ops = {
.allow_peer2peer = true,
.move_notify = xe_dma_buf_move_notify
};
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
struct dma_buf_test_params {
struct xe_test_priv base;
const struct dma_buf_attach_ops *attach_ops;
bool force_different_devices;
u32 mem_mask;
};
#define to_dma_buf_test_params(_priv) \
container_of(_priv, struct dma_buf_test_params, base)
#endif
struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev,
struct dma_buf *dma_buf)
{
XE_TEST_DECLARE(struct dma_buf_test_params *test =
to_dma_buf_test_params
(xe_cur_kunit_priv(XE_TEST_LIVE_DMA_BUF));)
const struct dma_buf_attach_ops *attach_ops;
struct dma_buf_attachment *attach;
struct drm_gem_object *obj;
struct xe_bo *bo;
if (dma_buf->ops == &xe_dmabuf_ops) {
obj = dma_buf->priv;
if (obj->dev == dev &&
!XE_TEST_ONLY(test && test->force_different_devices)) {
/*
* Importing dmabuf exported from out own gem increases
* refcount on gem itself instead of f_count of dmabuf.
*/
drm_gem_object_get(obj);
return obj;
}
}
/*
* Don't publish the bo until we have a valid attachment, and a
* valid attachment needs the bo address. So pre-create a bo before
* creating the attachment and publish.
*/
bo = xe_bo_alloc();
if (IS_ERR(bo))
return ERR_CAST(bo);
attach_ops = &xe_dma_buf_attach_ops;
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
if (test)
attach_ops = test->attach_ops;
#endif
attach = dma_buf_dynamic_attach(dma_buf, dev->dev, attach_ops, &bo->ttm.base);
if (IS_ERR(attach)) {
obj = ERR_CAST(attach);
goto out_err;
}
/* Errors here will take care of freeing the bo. */
obj = xe_dma_buf_init_obj(dev, bo, dma_buf);
if (IS_ERR(obj))
return obj;
get_dma_buf(dma_buf);
obj->import_attach = attach;
return obj;
out_err:
xe_bo_free(bo);
return obj;
}
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
#include "tests/xe_dma_buf.c"
#endif