xe_drm_ioctl() holds the device I/O gate across the whole ioctl. Software waits can wait for a fence that userspace has not submitted. Signalling GT fences cannot end these waits, so device I/O draining can hang during reset or wedge isolation.
Release the outer gate before syncobj waits, syncobj transfers and Xe user-fence waits. Protect each Xe user-fence memory read with the gate, but release it before sleeping. Wake Xe user-fence waiters when PCI recovery starts, the device wedges or the device is unplugged, so they can observe the blocked state. Cc: Matthew Brost <[email protected]> Cc: Thomas Hellström <[email protected]> Cc: Himal Prasad Ghimiray <[email protected]> Cc: Rodrigo Vivi <[email protected]> Assisted-by: LLM Signed-off-by: Arvind Yadav <[email protected]> --- drivers/gpu/drm/xe/xe_device.c | 28 +++++++++++++++++++++++++ drivers/gpu/drm/xe/xe_pci_error.c | 1 + drivers/gpu/drm/xe/xe_wait_user_fence.c | 12 ++++++++--- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 807cb0e98314..9112462bda6d 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -232,6 +232,19 @@ static const struct drm_ioctl_desc xe_ioctls[] = { DRM_RENDER_ALLOW), }; +static bool xe_drm_ioctl_is_wait(unsigned int cmd) +{ + switch (DRM_IOCTL_NR(cmd)) { + case DRM_IOCTL_NR(DRM_IOCTL_SYNCOBJ_WAIT): + case DRM_IOCTL_NR(DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT): + case DRM_IOCTL_NR(DRM_IOCTL_SYNCOBJ_TRANSFER): + case DRM_IOCTL_NR(DRM_IOCTL_XE_WAIT_USER_FENCE): + return true; + default: + return false; + } +} + static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct drm_file *file_priv = file->private_data; @@ -243,6 +256,12 @@ static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (ret) return ret; + /* These waits must not hold up device I/O draining. */ + if (xe_drm_ioctl_is_wait(cmd)) { + xe_device_io_put(io_idx); + return drm_ioctl(file, cmd, arg); + } + scoped_cond_guard(xe_pm_runtime_ioctl, ret = ACQUIRE_ERR(xe_pm_runtime_ioctl, &scope), xe) { @@ -265,6 +284,12 @@ static long xe_drm_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo if (ret) return ret; + /* These waits must not hold up device I/O draining. */ + if (xe_drm_ioctl_is_wait(cmd)) { + xe_device_io_put(io_idx); + return drm_compat_ioctl(file, cmd, arg); + } + scoped_cond_guard(xe_pm_runtime_ioctl, ret = ACQUIRE_ERR(xe_pm_runtime_ioctl, &scope), xe) { @@ -1142,6 +1167,8 @@ void xe_device_remove(struct xe_device *xe) drm_dev_unplug(&xe->drm); + wake_up_all(&xe->ufence_wq); + xe_bo_pci_dev_remove_all(xe); } @@ -1441,6 +1468,7 @@ void xe_device_declare_wedged(struct xe_device *xe) if (!atomic_xchg(&xe->wedged.flag, 1)) { xe->needs_flr_on_fini = true; xe_pm_runtime_get_noresume(xe); + wake_up_all(&xe->ufence_wq); xe_log_err_fatal(xe, WEDGED, -EIO, "Device declared wedged!\n"); xe_err_once(xe, "IOCTLs and executions are now blocked!\n" diff --git a/drivers/gpu/drm/xe/xe_pci_error.c b/drivers/gpu/drm/xe/xe_pci_error.c index 3e06f504f1b3..3dfcef03850e 100644 --- a/drivers/gpu/drm/xe/xe_pci_error.c +++ b/drivers/gpu/drm/xe/xe_pci_error.c @@ -21,6 +21,7 @@ static void prepare_device_for_reset(struct pci_dev *pdev) /* Block device access until PCI error recovery completes. */ xe_device_set_in_reset(xe); + wake_up_all(&xe->ufence_wq); for_each_gt(gt, xe, id) xe_gt_declare_wedged(gt); diff --git a/drivers/gpu/drm/xe/xe_wait_user_fence.c b/drivers/gpu/drm/xe/xe_wait_user_fence.c index e241f3ec3547..b5239c002368 100644 --- a/drivers/gpu/drm/xe/xe_wait_user_fence.c +++ b/drivers/gpu/drm/xe/xe_wait_user_fence.c @@ -14,13 +14,19 @@ #include "xe_macros.h" #include "xe_exec_queue.h" -static int do_compare(u64 addr, u64 value, u64 mask, u16 op) +static int do_compare(struct xe_device *xe, u64 addr, u64 value, u64 mask, u16 op) { u64 rvalue; + int io_idx; int err; bool passed; + err = xe_device_io_get(xe, &io_idx); + if (err) + return err; + err = copy_from_user(&rvalue, u64_to_user_ptr(addr), sizeof(rvalue)); + xe_device_io_put(io_idx); if (err) return -EFAULT; @@ -136,7 +142,7 @@ int xe_wait_user_fence_ioctl(struct drm_device *dev, void *data, add_wait_queue(&xe->ufence_wq, &w_wait); for (;;) { - err = do_compare(addr, args->value, args->mask, args->op); + err = do_compare(xe, addr, args->value, args->mask, args->op); if (err <= 0) break; @@ -155,7 +161,7 @@ int xe_wait_user_fence_ioctl(struct drm_device *dev, void *data, if (!timeout) { LNL_FLUSH_WORKQUEUE(xe->ordered_wq); - err = do_compare(addr, args->value, args->mask, + err = do_compare(xe, addr, args->value, args->mask, args->op); if (err <= 0) { drm_dbg(&xe->drm, "LNL_FLUSH_WORKQUEUE resolved ufence timeout\n"); -- 2.43.0
