1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2022 Intel Corporation 4 */ 5 6 #include "xe_wait_user_fence.h" 7 8 #include <drm/drm_device.h> 9 #include <drm/drm_file.h> 10 #include <drm/drm_utils.h> 11 #include <drm/xe_drm.h> 12 13 #include "xe_device.h" 14 #include "xe_gt.h" 15 #include "xe_macros.h" 16 #include "xe_exec_queue.h" 17 18 static int do_compare(u64 addr, u64 value, u64 mask, u16 op) 19 { 20 u64 rvalue; 21 int err; 22 bool passed; 23 24 err = copy_from_user(&rvalue, u64_to_user_ptr(addr), sizeof(rvalue)); 25 if (err) 26 return -EFAULT; 27 28 switch (op) { 29 case DRM_XE_UFENCE_WAIT_OP_EQ: 30 passed = (rvalue & mask) == (value & mask); 31 break; 32 case DRM_XE_UFENCE_WAIT_OP_NEQ: 33 passed = (rvalue & mask) != (value & mask); 34 break; 35 case DRM_XE_UFENCE_WAIT_OP_GT: 36 passed = (rvalue & mask) > (value & mask); 37 break; 38 case DRM_XE_UFENCE_WAIT_OP_GTE: 39 passed = (rvalue & mask) >= (value & mask); 40 break; 41 case DRM_XE_UFENCE_WAIT_OP_LT: 42 passed = (rvalue & mask) < (value & mask); 43 break; 44 case DRM_XE_UFENCE_WAIT_OP_LTE: 45 passed = (rvalue & mask) <= (value & mask); 46 break; 47 default: 48 XE_WARN_ON("Not possible"); 49 return -EINVAL; 50 } 51 52 return passed ? 0 : 1; 53 } 54 55 #define VALID_FLAGS DRM_XE_UFENCE_WAIT_FLAG_ABSTIME 56 #define MAX_OP DRM_XE_UFENCE_WAIT_OP_LTE 57 58 static long to_jiffies_timeout(struct xe_device *xe, 59 struct drm_xe_wait_user_fence *args) 60 { 61 unsigned long long t; 62 long timeout; 63 64 /* 65 * For negative timeout we want to wait "forever" by setting 66 * MAX_SCHEDULE_TIMEOUT. But we have to assign this value also 67 * to args->timeout to avoid being zeroed on the signal delivery 68 * (see arithmetics after wait). 69 */ 70 if (args->timeout < 0) { 71 args->timeout = MAX_SCHEDULE_TIMEOUT; 72 return MAX_SCHEDULE_TIMEOUT; 73 } 74 75 if (args->timeout == 0) 76 return 0; 77 78 /* 79 * Save the timeout to an u64 variable because nsecs_to_jiffies 80 * might return a value that overflows s32 variable. 81 */ 82 if (args->flags & DRM_XE_UFENCE_WAIT_FLAG_ABSTIME) 83 t = drm_timeout_abs_to_jiffies(args->timeout); 84 else 85 t = nsecs_to_jiffies(args->timeout); 86 87 /* 88 * Anything greater then MAX_SCHEDULE_TIMEOUT is meaningless, 89 * also we don't want to cap it at MAX_SCHEDULE_TIMEOUT because 90 * apparently user doesn't mean to wait forever, otherwise the 91 * args->timeout should have been set to a negative value. 92 */ 93 if (t > MAX_SCHEDULE_TIMEOUT) 94 timeout = MAX_SCHEDULE_TIMEOUT - 1; 95 else 96 timeout = t; 97 98 return timeout ?: 1; 99 } 100 101 int xe_wait_user_fence_ioctl(struct drm_device *dev, void *data, 102 struct drm_file *file) 103 { 104 struct xe_device *xe = to_xe_device(dev); 105 struct xe_file *xef = to_xe_file(file); 106 DEFINE_WAIT_FUNC(w_wait, woken_wake_function); 107 struct drm_xe_wait_user_fence *args = data; 108 struct xe_exec_queue *q = NULL; 109 u64 addr = args->addr; 110 int err = 0; 111 long timeout; 112 ktime_t start; 113 114 if (XE_IOCTL_DBG(xe, args->extensions) || XE_IOCTL_DBG(xe, args->pad) || 115 XE_IOCTL_DBG(xe, args->pad2) || 116 XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1])) 117 return -EINVAL; 118 119 if (XE_IOCTL_DBG(xe, args->flags & ~VALID_FLAGS)) 120 return -EINVAL; 121 122 if (XE_IOCTL_DBG(xe, args->op > MAX_OP)) 123 return -EINVAL; 124 125 if (XE_IOCTL_DBG(xe, addr & 0x7)) 126 return -EINVAL; 127 128 if (args->exec_queue_id) { 129 q = xe_exec_queue_lookup(xef, args->exec_queue_id); 130 if (XE_IOCTL_DBG(xe, !q)) 131 return -ENOENT; 132 } 133 134 timeout = to_jiffies_timeout(xe, args); 135 136 start = ktime_get(); 137 138 add_wait_queue(&xe->ufence_wq, &w_wait); 139 for (;;) { 140 err = do_compare(addr, args->value, args->mask, args->op); 141 if (err <= 0) 142 break; 143 144 if (signal_pending(current)) { 145 err = -ERESTARTSYS; 146 break; 147 } 148 149 if (q) { 150 if (q->ops->reset_status(q)) { 151 drm_info(&xe->drm, "exec queue reset detected\n"); 152 err = -EIO; 153 break; 154 } 155 } 156 157 if (!timeout) { 158 err = -ETIME; 159 break; 160 } 161 162 timeout = wait_woken(&w_wait, TASK_INTERRUPTIBLE, timeout); 163 } 164 remove_wait_queue(&xe->ufence_wq, &w_wait); 165 166 if (!(args->flags & DRM_XE_UFENCE_WAIT_FLAG_ABSTIME)) { 167 args->timeout -= ktime_to_ns(ktime_sub(ktime_get(), start)); 168 if (args->timeout < 0) 169 args->timeout = 0; 170 } 171 172 if (!timeout && !(err < 0)) 173 err = -ETIME; 174 175 if (q) 176 xe_exec_queue_put(q); 177 178 return err; 179 } 180