UAPI Changes:

- Disallow bind-queue sharing across multiple VMs (Matt Auld)
 
 Core Changes:
 - Fix xe userptr in the absence of CONFIG_DEVICE_PRIVATE (Thomas)
 
 Driver Changes:
 - Fix a missed page count update (Matt Brost)
 - Fix a confused argument to alloc_workqueue() (Marco Crivellari)
 - Kernel-doc fixes (Jani)
 - Disable a workaround on VFs (Matt Brost)
 - Fix a job lock assert (Matt Auld)
 - Update wedged.mode only after successful reset policy change (Lukasz)
 - Select CONFIG_DEVICE_PRIVATE when DRM_XE_GPUSVM is selected (Thomas)
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRskUM7w1oG5rx2IZO4FpNVCsYGvwUCaXIbUgAKCRC4FpNVCsYG
 v+HJAP0fU6LmLJtJ1iVkde0lapI2Yt24HZO2aKyGpfyCG11kggD/TsrGzalpH3MB
 5+HG8rkdhJl6slSq3Bc4Ql+0FnXSiwY=
 =Y5u3
 -----END PGP SIGNATURE-----

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

UAPI Changes:
- Disallow bind-queue sharing across multiple VMs (Matt Auld)

Core Changes:
- Fix xe userptr in the absence of CONFIG_DEVICE_PRIVATE (Thomas)

Driver Changes:
- Fix a missed page count update (Matt Brost)
- Fix a confused argument to alloc_workqueue() (Marco Crivellari)
- Kernel-doc fixes (Jani)
- Disable a workaround on VFs (Matt Brost)
- Fix a job lock assert (Matt Auld)
- Update wedged.mode only after successful reset policy change (Lukasz)
- Select CONFIG_DEVICE_PRIVATE when DRM_XE_GPUSVM is selected (Thomas)

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

From: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
Link: https://patch.msgid.link/aXIdiXaY-RxoaviV@fedora
This commit is contained in:
Dave Airlie 2026-01-23 08:03:43 +10:00
commit e63b9229c3
20 changed files with 175 additions and 40 deletions

View File

@ -210,7 +210,7 @@ config DRM_GPUVM
config DRM_GPUSVM
tristate
depends on DRM && DEVICE_PRIVATE
depends on DRM
select HMM_MIRROR
select MMU_NOTIFIER
help

View File

@ -108,8 +108,10 @@ obj-$(CONFIG_DRM_EXEC) += drm_exec.o
obj-$(CONFIG_DRM_GPUVM) += drm_gpuvm.o
drm_gpusvm_helper-y := \
drm_gpusvm.o\
drm_gpusvm.o
drm_gpusvm_helper-$(CONFIG_ZONE_DEVICE) += \
drm_pagemap.o
obj-$(CONFIG_DRM_GPUSVM) += drm_gpusvm_helper.o
obj-$(CONFIG_DRM_BUDDY) += drm_buddy.o

View File

@ -39,7 +39,7 @@ config DRM_XE
select DRM_TTM
select DRM_TTM_HELPER
select DRM_EXEC
select DRM_GPUSVM if !UML && DEVICE_PRIVATE
select DRM_GPUSVM if !UML
select DRM_GPUVM
select DRM_SCHED
select MMU_NOTIFIER
@ -80,8 +80,9 @@ config DRM_XE_GPUSVM
bool "Enable CPU to GPU address mirroring"
depends on DRM_XE
depends on !UML
depends on DEVICE_PRIVATE
depends on ZONE_DEVICE
default y
select DEVICE_PRIVATE
select DRM_GPUSVM
help
Enable this option if you want support for CPU to GPU address

View File

@ -1055,6 +1055,7 @@ static long xe_bo_shrink_purge(struct ttm_operation_ctx *ctx,
unsigned long *scanned)
{
struct xe_device *xe = ttm_to_xe_device(bo->bdev);
struct ttm_tt *tt = bo->ttm;
long lret;
/* Fake move to system, without copying data. */
@ -1079,8 +1080,10 @@ static long xe_bo_shrink_purge(struct ttm_operation_ctx *ctx,
.writeback = false,
.allow_move = false});
if (lret > 0)
if (lret > 0) {
xe_ttm_tt_account_subtract(xe, bo->ttm);
update_global_total_pages(bo->bdev, -(long)tt->num_pages);
}
return lret;
}
@ -1166,8 +1169,10 @@ long xe_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo,
if (needs_rpm)
xe_pm_runtime_put(xe);
if (lret > 0)
if (lret > 0) {
xe_ttm_tt_account_subtract(xe, tt);
update_global_total_pages(bo->bdev, -(long)tt->num_pages);
}
out_unref:
xe_bo_put(xe_bo);

View File

@ -256,14 +256,64 @@ static ssize_t wedged_mode_show(struct file *f, char __user *ubuf,
return simple_read_from_buffer(ubuf, size, pos, buf, len);
}
static int __wedged_mode_set_reset_policy(struct xe_gt *gt, enum xe_wedged_mode mode)
{
bool enable_engine_reset;
int ret;
enable_engine_reset = (mode != XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET);
ret = xe_guc_ads_scheduler_policy_toggle_reset(&gt->uc.guc.ads,
enable_engine_reset);
if (ret)
xe_gt_err(gt, "Failed to update GuC ADS scheduler policy (%pe)\n", ERR_PTR(ret));
return ret;
}
static int wedged_mode_set_reset_policy(struct xe_device *xe, enum xe_wedged_mode mode)
{
struct xe_gt *gt;
int ret;
u8 id;
guard(xe_pm_runtime)(xe);
for_each_gt(gt, xe, id) {
ret = __wedged_mode_set_reset_policy(gt, mode);
if (ret) {
if (id > 0) {
xe->wedged.inconsistent_reset = true;
drm_err(&xe->drm, "Inconsistent reset policy state between GTs\n");
}
return ret;
}
}
xe->wedged.inconsistent_reset = false;
return 0;
}
static bool wedged_mode_needs_policy_update(struct xe_device *xe, enum xe_wedged_mode mode)
{
if (xe->wedged.inconsistent_reset)
return true;
if (xe->wedged.mode == mode)
return false;
if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET ||
mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)
return true;
return false;
}
static ssize_t wedged_mode_set(struct file *f, const char __user *ubuf,
size_t size, loff_t *pos)
{
struct xe_device *xe = file_inode(f)->i_private;
struct xe_gt *gt;
u32 wedged_mode;
ssize_t ret;
u8 id;
ret = kstrtouint_from_user(ubuf, size, 0, &wedged_mode);
if (ret)
@ -272,22 +322,14 @@ static ssize_t wedged_mode_set(struct file *f, const char __user *ubuf,
if (wedged_mode > 2)
return -EINVAL;
if (xe->wedged.mode == wedged_mode)
return size;
if (wedged_mode_needs_policy_update(xe, wedged_mode)) {
ret = wedged_mode_set_reset_policy(xe, wedged_mode);
if (ret)
return ret;
}
xe->wedged.mode = wedged_mode;
xe_pm_runtime_get(xe);
for_each_gt(gt, xe, id) {
ret = xe_guc_ads_scheduler_policy_toggle_reset(&gt->uc.guc.ads);
if (ret) {
xe_gt_err(gt, "Failed to update GuC ADS scheduler policy. GuC may still cause engine reset even with wedged_mode=2\n");
xe_pm_runtime_put(xe);
return -EIO;
}
}
xe_pm_runtime_put(xe);
return size;
}

View File

@ -44,6 +44,22 @@ struct xe_pat_ops;
struct xe_pxp;
struct xe_vram_region;
/**
* enum xe_wedged_mode - possible wedged modes
* @XE_WEDGED_MODE_NEVER: Device will never be declared wedged.
* @XE_WEDGED_MODE_UPON_CRITICAL_ERROR: Device will be declared wedged only
* when critical error occurs like GT reset failure or firmware failure.
* This is the default mode.
* @XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET: Device will be declared wedged on
* any hang. In this mode, engine resets are disabled to avoid automatic
* recovery attempts. This mode is primarily intended for debugging hangs.
*/
enum xe_wedged_mode {
XE_WEDGED_MODE_NEVER = 0,
XE_WEDGED_MODE_UPON_CRITICAL_ERROR = 1,
XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET = 2,
};
#define XE_BO_INVALID_OFFSET LONG_MAX
#define GRAPHICS_VER(xe) ((xe)->info.graphics_verx100 / 100)
@ -587,6 +603,8 @@ struct xe_device {
int mode;
/** @wedged.method: Recovery method to be sent in the drm device wedged uevent */
unsigned long method;
/** @wedged.inconsistent_reset: Inconsistent reset policy state between GTs */
bool inconsistent_reset;
} wedged;
/** @bo_device: Struct to control async free of BOs */

View File

@ -328,6 +328,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe
* @xe: Xe device.
* @tile: tile which bind exec queue belongs to.
* @flags: exec queue creation flags
* @user_vm: The user VM which this exec queue belongs to
* @extensions: exec queue creation extensions
*
* Normalize bind exec queue creation. Bind exec queue is tied to migration VM
@ -341,6 +342,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe
*/
struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
struct xe_tile *tile,
struct xe_vm *user_vm,
u32 flags, u64 extensions)
{
struct xe_gt *gt = tile->primary_gt;
@ -377,6 +379,9 @@ struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
xe_exec_queue_put(q);
return ERR_PTR(err);
}
if (user_vm)
q->user_vm = xe_vm_get(user_vm);
}
return q;
@ -407,6 +412,11 @@ void xe_exec_queue_destroy(struct kref *ref)
xe_exec_queue_put(eq);
}
if (q->user_vm) {
xe_vm_put(q->user_vm);
q->user_vm = NULL;
}
q->ops->destroy(q);
}
@ -742,6 +752,22 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
XE_IOCTL_DBG(xe, eci[0].engine_instance != 0))
return -EINVAL;
vm = xe_vm_lookup(xef, args->vm_id);
if (XE_IOCTL_DBG(xe, !vm))
return -ENOENT;
err = down_read_interruptible(&vm->lock);
if (err) {
xe_vm_put(vm);
return err;
}
if (XE_IOCTL_DBG(xe, xe_vm_is_closed_or_banned(vm))) {
up_read(&vm->lock);
xe_vm_put(vm);
return -ENOENT;
}
for_each_tile(tile, xe, id) {
struct xe_exec_queue *new;
@ -749,9 +775,11 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
if (id)
flags |= EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD;
new = xe_exec_queue_create_bind(xe, tile, flags,
new = xe_exec_queue_create_bind(xe, tile, vm, flags,
args->extensions);
if (IS_ERR(new)) {
up_read(&vm->lock);
xe_vm_put(vm);
err = PTR_ERR(new);
if (q)
goto put_exec_queue;
@ -763,6 +791,8 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
list_add_tail(&new->multi_gt_list,
&q->multi_gt_link);
}
up_read(&vm->lock);
xe_vm_put(vm);
} else {
logical_mask = calc_validate_logical_mask(xe, eci,
args->width,

View File

@ -28,6 +28,7 @@ struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe
u32 flags, u64 extensions);
struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
struct xe_tile *tile,
struct xe_vm *user_vm,
u32 flags, u64 extensions);
void xe_exec_queue_fini(struct xe_exec_queue *q);

View File

@ -54,6 +54,12 @@ struct xe_exec_queue {
struct kref refcount;
/** @vm: VM (address space) for this exec queue */
struct xe_vm *vm;
/**
* @user_vm: User VM (address space) for this exec queue (bind queues
* only)
*/
struct xe_vm *user_vm;
/** @class: class of this exec queue */
enum xe_engine_class class;
/**

View File

@ -322,7 +322,7 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt)
else
ggtt->pt_ops = &xelp_pt_ops;
ggtt->wq = alloc_workqueue("xe-ggtt-wq", 0, WQ_MEM_RECLAIM);
ggtt->wq = alloc_workqueue("xe-ggtt-wq", WQ_MEM_RECLAIM, 0);
if (!ggtt->wq)
return -ENOMEM;

View File

@ -41,10 +41,10 @@ struct xe_gt_sriov_vf_runtime {
};
/**
* xe_gt_sriov_vf_migration - VF migration data.
* struct xe_gt_sriov_vf_migration - VF migration data.
*/
struct xe_gt_sriov_vf_migration {
/** @migration: VF migration recovery worker */
/** @worker: VF migration recovery worker */
struct work_struct worker;
/** @lock: Protects recovery_queued, teardown */
spinlock_t lock;

View File

@ -983,16 +983,17 @@ static int guc_ads_action_update_policies(struct xe_guc_ads *ads, u32 policy_off
/**
* xe_guc_ads_scheduler_policy_toggle_reset - Toggle reset policy
* @ads: Additional data structures object
* @enable_engine_reset: true to enable engine resets, false otherwise
*
* This function update the GuC's engine reset policy based on wedged.mode.
* This function update the GuC's engine reset policy.
*
* Return: 0 on success, and negative error code otherwise.
*/
int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads)
int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads,
bool enable_engine_reset)
{
struct guc_policies *policies;
struct xe_guc *guc = ads_to_guc(ads);
struct xe_device *xe = ads_to_xe(ads);
CLASS(xe_guc_buf, buf)(&guc->buf, sizeof(*policies));
if (!xe_guc_buf_is_valid(buf))
@ -1004,10 +1005,11 @@ int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads)
policies->dpc_promote_time = ads_blob_read(ads, policies.dpc_promote_time);
policies->max_num_work_items = ads_blob_read(ads, policies.max_num_work_items);
policies->is_valid = 1;
if (xe->wedged.mode == 2)
policies->global_flags |= GLOBAL_POLICY_DISABLE_ENGINE_RESET;
else
if (enable_engine_reset)
policies->global_flags &= ~GLOBAL_POLICY_DISABLE_ENGINE_RESET;
else
policies->global_flags |= GLOBAL_POLICY_DISABLE_ENGINE_RESET;
return guc_ads_action_update_policies(ads, xe_guc_buf_flush(buf));
}

View File

@ -6,6 +6,8 @@
#ifndef _XE_GUC_ADS_H_
#define _XE_GUC_ADS_H_
#include <linux/types.h>
struct xe_guc_ads;
int xe_guc_ads_init(struct xe_guc_ads *ads);
@ -13,6 +15,7 @@ int xe_guc_ads_init_post_hwconfig(struct xe_guc_ads *ads);
void xe_guc_ads_populate(struct xe_guc_ads *ads);
void xe_guc_ads_populate_minimal(struct xe_guc_ads *ads);
void xe_guc_ads_populate_post_load(struct xe_guc_ads *ads);
int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads);
int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads,
bool enable_engine_reset);
#endif

View File

@ -15,10 +15,12 @@
#define XE_LB_MAX_PAYLOAD_SIZE SZ_4K
/**
* xe_late_bind_fw_id - enum to determine late binding fw index
* enum xe_late_bind_fw_id - enum to determine late binding fw index
*/
enum xe_late_bind_fw_id {
/** @XE_LB_FW_FAN_CONTROL: Fan control */
XE_LB_FW_FAN_CONTROL = 0,
/** @XE_LB_FW_MAX_ID: Number of IDs */
XE_LB_FW_MAX_ID
};

View File

@ -1050,6 +1050,9 @@ static ssize_t setup_utilization_wa(struct xe_lrc *lrc,
{
u32 *cmd = batch;
if (IS_SRIOV_VF(gt_to_xe(lrc->gt)))
return 0;
if (xe_gt_WARN_ON(lrc->gt, max_len < 12))
return -ENOSPC;

View File

@ -2445,7 +2445,7 @@ void xe_migrate_job_lock(struct xe_migrate *m, struct xe_exec_queue *q)
if (is_migrate)
mutex_lock(&m->job_mutex);
else
xe_vm_assert_held(q->vm); /* User queues VM's should be locked */
xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */
}
/**
@ -2463,7 +2463,7 @@ void xe_migrate_job_unlock(struct xe_migrate *m, struct xe_exec_queue *q)
if (is_migrate)
mutex_unlock(&m->job_mutex);
else
xe_vm_assert_held(q->vm); /* User queues VM's should be locked */
xe_vm_assert_held(q->user_vm); /* User queues VM's should be locked */
}
#if IS_ENABLED(CONFIG_PROVE_LOCKING)

View File

@ -346,7 +346,7 @@ int xe_sriov_vf_ccs_init(struct xe_device *xe)
flags = EXEC_QUEUE_FLAG_KERNEL |
EXEC_QUEUE_FLAG_PERMANENT |
EXEC_QUEUE_FLAG_MIGRATE;
q = xe_exec_queue_create_bind(xe, tile, flags, 0);
q = xe_exec_queue_create_bind(xe, tile, NULL, flags, 0);
if (IS_ERR(q)) {
err = PTR_ERR(q);
goto err_ret;

View File

@ -1617,7 +1617,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef)
if (!vm->pt_root[id])
continue;
q = xe_exec_queue_create_bind(xe, tile, create_flags, 0);
q = xe_exec_queue_create_bind(xe, tile, vm, create_flags, 0);
if (IS_ERR(q)) {
err = PTR_ERR(q);
goto err_close;
@ -3578,6 +3578,11 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
}
}
if (XE_IOCTL_DBG(xe, q && vm != q->user_vm)) {
err = -EINVAL;
goto put_exec_queue;
}
/* Ensure all UNMAPs visible */
xe_svm_flush(vm);

View File

@ -379,7 +379,7 @@ static inline void xe_vm_set_validation_exec(struct xe_vm *vm, struct drm_exec *
}
/**
* xe_vm_set_validation_exec() - Accessor to read the drm_exec object
* xe_vm_validation_exec() - Accessor to read the drm_exec object
* @vm: The vm we want to register a drm_exec object with.
*
* Return: The drm_exec object used to lock the vm's resv. The value

View File

@ -209,6 +209,19 @@ struct drm_pagemap_devmem_ops {
struct dma_fence *pre_migrate_fence);
};
#if IS_ENABLED(CONFIG_ZONE_DEVICE)
struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page);
#else
static inline struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page)
{
return NULL;
}
#endif /* IS_ENABLED(CONFIG_ZONE_DEVICE) */
/**
* struct drm_pagemap_devmem - Structure representing a GPU SVM device memory allocation
*
@ -233,6 +246,8 @@ struct drm_pagemap_devmem {
struct dma_fence *pre_migrate_fence;
};
#if IS_ENABLED(CONFIG_ZONE_DEVICE)
int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
struct mm_struct *mm,
unsigned long start, unsigned long end,
@ -243,8 +258,6 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation);
const struct dev_pagemap_ops *drm_pagemap_pagemap_ops_get(void);
struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page);
void drm_pagemap_devmem_init(struct drm_pagemap_devmem *devmem_allocation,
struct device *dev, struct mm_struct *mm,
const struct drm_pagemap_devmem_ops *ops,
@ -256,4 +269,6 @@ int drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
struct mm_struct *mm,
unsigned long timeslice_ms);
#endif /* IS_ENABLED(CONFIG_ZONE_DEVICE) */
#endif