Commit 96f34d18 authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'drm-xe-fixes-2026-05-14' of https://gitlab.freedesktop.org/drm/xe/kernel into drm-fixes



- Madvise fix around purgeability tracking (Arvind)
- Restore engine mask for specific blitter style (Roper)
- Couple UAF fixes (Auld)
- Drop unused ggtt_balloon field (Wajdeczko)

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

From: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://patch.msgid.link/agXWkM3Y98bqt6TG@intel.com
parents 68055b28 8fb70afe
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -897,10 +897,10 @@ void xe_bo_set_purgeable_state(struct xe_bo *bo,
		  new_state == XE_MADV_PURGEABLE_PURGED);

	/* Once purged, always purged - cannot transition out */
	xe_assert(xe, !(bo->madv_purgeable == XE_MADV_PURGEABLE_PURGED &&
	xe_assert(xe, !(bo->purgeable.state == XE_MADV_PURGEABLE_PURGED &&
			new_state != XE_MADV_PURGEABLE_PURGED));

	bo->madv_purgeable = new_state;
	bo->purgeable.state = new_state;
	xe_bo_set_purgeable_shrinker(bo, new_state);
}

@@ -2368,7 +2368,7 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
	INIT_LIST_HEAD(&bo->vram_userfault_link);

	/* Initialize purge advisory state */
	bo->madv_purgeable = XE_MADV_PURGEABLE_WILLNEED;
	bo->purgeable.state = XE_MADV_PURGEABLE_WILLNEED;

	drm_gem_private_object_init(&xe->drm, &bo->ttm.base, size);

+86 −2
Original line number Diff line number Diff line
@@ -251,7 +251,7 @@ static inline bool xe_bo_is_protected(const struct xe_bo *bo)
static inline bool xe_bo_is_purged(struct xe_bo *bo)
{
	xe_bo_assert_held(bo);
	return bo->madv_purgeable == XE_MADV_PURGEABLE_PURGED;
	return bo->purgeable.state == XE_MADV_PURGEABLE_PURGED;
}

/**
@@ -268,11 +268,95 @@ static inline bool xe_bo_is_purged(struct xe_bo *bo)
static inline bool xe_bo_madv_is_dontneed(struct xe_bo *bo)
{
	xe_bo_assert_held(bo);
	return bo->madv_purgeable == XE_MADV_PURGEABLE_DONTNEED;
	return bo->purgeable.state == XE_MADV_PURGEABLE_DONTNEED;
}

void xe_bo_set_purgeable_state(struct xe_bo *bo, enum xe_madv_purgeable_state new_state);

/**
 * xe_bo_willneed_get_locked() - Acquire a WILLNEED holder on a BO
 * @bo: Buffer object
 *
 * Increments willneed_count and, on a 0->1 transition, promotes the BO
 * from DONTNEED to WILLNEED. PURGED is terminal and is never modified.
 *
 * Caller must hold the BO's dma-resv lock.
 */
static inline void xe_bo_willneed_get_locked(struct xe_bo *bo)
{
	xe_bo_assert_held(bo);

	/* Imported BOs are owned externally; do not track purgeability. */
	if (drm_gem_is_imported(&bo->ttm.base))
		return;

	if (bo->purgeable.willneed_count++ == 0 && xe_bo_madv_is_dontneed(bo))
		xe_bo_set_purgeable_state(bo, XE_MADV_PURGEABLE_WILLNEED);
}

/**
 * xe_bo_willneed_put_locked() - Release a WILLNEED holder on a BO
 * @bo: Buffer object
 *
 * Decrements willneed_count and, on a 1->0 transition, marks the BO
 * DONTNEED only if it still has VMAs (implying all active VMAs are
 * DONTNEED). If the last VMA is being removed, preserve the current BO
 * state to match the previous VMA-walk semantics.
 *
 * PURGED is terminal and the BO state is never modified.
 *
 * Caller must hold the BO's dma-resv lock.
 */
static inline void xe_bo_willneed_put_locked(struct xe_bo *bo)
{
	xe_bo_assert_held(bo);

	if (drm_gem_is_imported(&bo->ttm.base))
		return;

	xe_assert(xe_bo_device(bo), bo->purgeable.willneed_count > 0);
	if (--bo->purgeable.willneed_count == 0 && bo->purgeable.vma_count > 0 &&
	    !xe_bo_is_purged(bo))
		xe_bo_set_purgeable_state(bo, XE_MADV_PURGEABLE_DONTNEED);
}

/**
 * xe_bo_vma_count_inc_locked() - Account a new VMA on a BO
 * @bo: Buffer object
 *
 * Increments vma_count.
 *
 * Caller must hold the BO's dma-resv lock.
 */
static inline void xe_bo_vma_count_inc_locked(struct xe_bo *bo)
{
	xe_bo_assert_held(bo);

	if (drm_gem_is_imported(&bo->ttm.base))
		return;

	bo->purgeable.vma_count++;
}

/**
 * xe_bo_vma_count_dec_locked() - Account a VMA removal on a BO
 * @bo: Buffer object
 *
 * Decrements vma_count.
 *
 * Caller must hold the BO's dma-resv lock.
 */
static inline void xe_bo_vma_count_dec_locked(struct xe_bo *bo)
{
	xe_bo_assert_held(bo);

	if (drm_gem_is_imported(&bo->ttm.base))
		return;

	xe_assert(xe_bo_device(bo), bo->purgeable.vma_count > 0);
	bo->purgeable.vma_count--;
}

static inline void xe_bo_unpin_map_no_vm(struct xe_bo *bo)
{
	if (likely(bo)) {
+25 −3
Original line number Diff line number Diff line
@@ -111,10 +111,32 @@ struct xe_bo {
	u64 min_align;

	/**
	 * @madv_purgeable: user space advise on BO purgeability, protected
	 * by BO's dma-resv lock.
	 * @purgeable: Purgeability state and accounting.
	 *
	 * All fields are protected by the BO's dma-resv lock.
	 */
	u32 madv_purgeable;
	struct {
		/**
		 * @purgeable.state: BO purgeability state
		 *                   (WILLNEED/DONTNEED/PURGED).
		 */
		u32 state;

		/**
		 * @purgeable.vma_count: Number of VMAs currently mapping this BO.
		 */
		u32 vma_count;

		/**
		 * @purgeable.willneed_count: Number of active WILLNEED holders.
		 *
		 * Counts WILLNEED VMAs plus active dma-buf exports for
		 * non-imported BOs. The BO flips to DONTNEED on a 1->0
		 * transition only when VMAs still exist; if the last VMA is
		 * removed, the previous BO state is preserved.
		 */
		u32 willneed_count;
	} purgeable;
};

#endif
+38 −42
Original line number Diff line number Diff line
@@ -193,6 +193,18 @@ static int xe_dma_buf_begin_cpu_access(struct dma_buf *dma_buf,
	return 0;
}

static void xe_dma_buf_release(struct dma_buf *dmabuf)
{
	struct drm_gem_object *obj = dmabuf->priv;
	struct xe_bo *bo = gem_to_xe_bo(obj);

	xe_bo_lock(bo, false);
	xe_bo_willneed_put_locked(bo);
	xe_bo_unlock(bo);

	drm_gem_dmabuf_release(dmabuf);
}

static const struct dma_buf_ops xe_dmabuf_ops = {
	.attach = xe_dma_buf_attach,
	.detach = xe_dma_buf_detach,
@@ -200,7 +212,7 @@ static const struct dma_buf_ops xe_dmabuf_ops = {
	.unpin = xe_dma_buf_unpin,
	.map_dma_buf = xe_dma_buf_map,
	.unmap_dma_buf = xe_dma_buf_unmap,
	.release = drm_gem_dmabuf_release,
	.release = xe_dma_buf_release,
	.begin_cpu_access = xe_dma_buf_begin_cpu_access,
	.mmap = drm_gem_dmabuf_mmap,
	.vmap = drm_gem_dmabuf_vmap,
@@ -241,33 +253,33 @@ struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj, int flags)
		ret = -EINVAL;
		goto out_unlock;
	}

	xe_bo_willneed_get_locked(bo);
	xe_bo_unlock(bo);

	ret = ttm_bo_setup_export(&bo->ttm, &ctx);
	if (ret)
		return ERR_PTR(ret);
		goto out_put;

	buf = drm_gem_prime_export(obj, flags);
	if (!IS_ERR(buf))
		buf->ops = &xe_dmabuf_ops;
	if (IS_ERR(buf)) {
		ret = PTR_ERR(buf);
		goto out_put;
	}

	buf->ops = &xe_dmabuf_ops;
	return buf;

out_put:
	xe_bo_lock(bo, false);
	xe_bo_willneed_put_locked(bo);
out_unlock:
	xe_bo_unlock(bo);
	return ERR_PTR(ret);
}

/*
 * Takes ownership of @storage: on success it is transferred to the returned
 * drm_gem_object; on failure it is freed before returning the error.
 * This matches the contract of xe_bo_init_locked() which frees @storage on
 * its error paths, so callers need not (and must not) free @storage after
 * this call.
 */
static struct drm_gem_object *
xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage,
		    struct dma_buf *dma_buf)
xe_dma_buf_create_obj(struct drm_device *dev, struct dma_buf *dma_buf)
{
	struct dma_resv *resv = dma_buf->resv;
	struct xe_device *xe = to_xe_device(dev);
@@ -278,10 +290,8 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage,
	int ret = 0;

	dummy_obj = drm_gpuvm_resv_object_alloc(&xe->drm);
	if (!dummy_obj) {
		xe_bo_free(storage);
	if (!dummy_obj)
		return ERR_PTR(-ENOMEM);
	}

	dummy_obj->resv = resv;
	xe_validation_guard(&ctx, &xe->val, &exec, (struct xe_val_flags) {}, ret) {
@@ -290,8 +300,7 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage,
		if (ret)
			break;

		/* xe_bo_init_locked() frees storage on error */
		bo = xe_bo_init_locked(xe, storage, NULL, resv, NULL, dma_buf->size,
		bo = xe_bo_init_locked(xe, NULL, NULL, resv, NULL, dma_buf->size,
				       0, /* Will require 1way or 2way for vm_bind */
				       ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec);
		drm_exec_retry_on_contention(&exec);
@@ -342,7 +351,6 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev,
	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;
@@ -358,13 +366,15 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev,
	}

	/*
	 * 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.
	 * This needs to happen before the attach, since it will create a new
	 * attachment for this, and add it to the list of attachments, at which
	 * point it is globally visible, and at any point the export side can
	 * call into on invalidate_mappings callback, which require a working
	 * object.
	 */
	bo = xe_bo_alloc();
	if (IS_ERR(bo))
		return ERR_CAST(bo);
	obj = xe_dma_buf_create_obj(dev, dma_buf);
	if (IS_ERR(obj))
		return obj;

	attach_ops = &xe_dma_buf_attach_ops;
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
@@ -372,29 +382,15 @@ struct drm_gem_object *xe_gem_prime_import(struct drm_device *dev,
		attach_ops = test->attach_ops;
#endif

	attach = dma_buf_dynamic_attach(dma_buf, dev->dev, attach_ops, &bo->ttm.base);
	attach = dma_buf_dynamic_attach(dma_buf, dev->dev, attach_ops, obj);
	if (IS_ERR(attach)) {
		obj = ERR_CAST(attach);
		goto out_err;
		xe_bo_put(gem_to_xe_bo(obj));
		return ERR_CAST(attach);
	}

	/*
	 * xe_dma_buf_init_obj() takes ownership of bo on both success
	 * and failure, so we must not touch bo after this call.
	 */
	obj = xe_dma_buf_init_obj(dev, bo, dma_buf);
	if (IS_ERR(obj)) {
		dma_buf_detach(dma_buf, attach);
		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)
+7 −0
Original line number Diff line number Diff line
@@ -144,6 +144,13 @@ struct xe_gt {
		u8 id;
		/** @info.has_indirect_ring_state: GT has indirect ring state support */
		u8 has_indirect_ring_state:1;
		/**
		 * @info.has_xe2_blt_instructions: GT supports Xe2-style MEM_SET
		 * and MEM_COPY blitter functionality.  Note that despite the
		 * name, some Xe1 platforms may also support this "Xe2-style"
		 * feature.
		 */
		u8 has_xe2_blt_instructions:1;
		/**
		 * @info.num_geometry_xecore_fuse_regs: Number of 32b-bit fuse
		 * registers the geometry XeCore mask spans.
Loading