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

Reply via email to