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/xe_drm.h> 11 12 #include "xe_device.h" 13 #include "xe_gt.h" 14 #include "xe_macros.h" 15 #include "xe_vm.h" 16 17 static int do_compare(u64 addr, u64 value, u64 mask, u16 op) 18 { 19 u64 rvalue; 20 int err; 21 bool passed; 22 23 err = copy_from_user(&rvalue, u64_to_user_ptr(addr), sizeof(rvalue)); 24 if (err) 25 return -EFAULT; 26 27 switch (op) { 28 case DRM_XE_UFENCE_WAIT_EQ: 29 passed = (rvalue & mask) == (value & mask); 30 break; 31 case DRM_XE_UFENCE_WAIT_NEQ: 32 passed = (rvalue & mask) != (value & mask); 33 break; 34 case DRM_XE_UFENCE_WAIT_GT: 35 passed = (rvalue & mask) > (value & mask); 36 break; 37 case DRM_XE_UFENCE_WAIT_GTE: 38 passed = (rvalue & mask) >= (value & mask); 39 break; 40 case DRM_XE_UFENCE_WAIT_LT: 41 passed = (rvalue & mask) < (value & mask); 42 break; 43 case DRM_XE_UFENCE_WAIT_LTE: 44 passed = (rvalue & mask) <= (value & mask); 45 break; 46 default: 47 XE_BUG_ON("Not possible"); 48 } 49 50 return passed ? 0 : 1; 51 } 52 53 static const enum xe_engine_class user_to_xe_engine_class[] = { 54 [DRM_XE_ENGINE_CLASS_RENDER] = XE_ENGINE_CLASS_RENDER, 55 [DRM_XE_ENGINE_CLASS_COPY] = XE_ENGINE_CLASS_COPY, 56 [DRM_XE_ENGINE_CLASS_VIDEO_DECODE] = XE_ENGINE_CLASS_VIDEO_DECODE, 57 [DRM_XE_ENGINE_CLASS_VIDEO_ENHANCE] = XE_ENGINE_CLASS_VIDEO_ENHANCE, 58 [DRM_XE_ENGINE_CLASS_COMPUTE] = XE_ENGINE_CLASS_COMPUTE, 59 }; 60 61 static int check_hw_engines(struct xe_device *xe, 62 struct drm_xe_engine_class_instance *eci, 63 int num_engines) 64 { 65 int i; 66 67 for (i = 0; i < num_engines; ++i) { 68 enum xe_engine_class user_class = 69 user_to_xe_engine_class[eci[i].engine_class]; 70 71 if (eci[i].gt_id >= xe->info.tile_count) 72 return -EINVAL; 73 74 if (!xe_gt_hw_engine(xe_device_get_gt(xe, eci[i].gt_id), 75 user_class, eci[i].engine_instance, true)) 76 return -EINVAL; 77 } 78 79 return 0; 80 } 81 82 #define VALID_FLAGS (DRM_XE_UFENCE_WAIT_SOFT_OP | \ 83 DRM_XE_UFENCE_WAIT_ABSTIME | \ 84 DRM_XE_UFENCE_WAIT_VM_ERROR) 85 #define MAX_OP DRM_XE_UFENCE_WAIT_LTE 86 87 int xe_wait_user_fence_ioctl(struct drm_device *dev, void *data, 88 struct drm_file *file) 89 { 90 struct xe_device *xe = to_xe_device(dev); 91 DEFINE_WAIT_FUNC(w_wait, woken_wake_function); 92 struct drm_xe_wait_user_fence *args = data; 93 struct drm_xe_engine_class_instance eci[XE_HW_ENGINE_MAX_INSTANCE]; 94 struct drm_xe_engine_class_instance __user *user_eci = 95 u64_to_user_ptr(args->instances); 96 struct xe_vm *vm = NULL; 97 u64 addr = args->addr; 98 int err; 99 bool no_engines = args->flags & DRM_XE_UFENCE_WAIT_SOFT_OP || 100 args->flags & DRM_XE_UFENCE_WAIT_VM_ERROR; 101 unsigned long timeout = args->timeout; 102 103 if (XE_IOCTL_ERR(xe, args->extensions) || XE_IOCTL_ERR(xe, args->pad) || 104 XE_IOCTL_ERR(xe, args->reserved[0] || args->reserved[1])) 105 return -EINVAL; 106 107 if (XE_IOCTL_ERR(xe, args->flags & ~VALID_FLAGS)) 108 return -EINVAL; 109 110 if (XE_IOCTL_ERR(xe, args->op > MAX_OP)) 111 return -EINVAL; 112 113 if (XE_IOCTL_ERR(xe, no_engines && 114 (args->num_engines || args->instances))) 115 return -EINVAL; 116 117 if (XE_IOCTL_ERR(xe, !no_engines && !args->num_engines)) 118 return -EINVAL; 119 120 if (XE_IOCTL_ERR(xe, !(args->flags & DRM_XE_UFENCE_WAIT_VM_ERROR) && 121 addr & 0x7)) 122 return -EINVAL; 123 124 if (!no_engines) { 125 err = copy_from_user(eci, user_eci, 126 sizeof(struct drm_xe_engine_class_instance) * 127 args->num_engines); 128 if (XE_IOCTL_ERR(xe, err)) 129 return -EFAULT; 130 131 if (XE_IOCTL_ERR(xe, check_hw_engines(xe, eci, 132 args->num_engines))) 133 return -EINVAL; 134 } 135 136 if (args->flags & DRM_XE_UFENCE_WAIT_VM_ERROR) { 137 if (XE_IOCTL_ERR(xe, args->vm_id >> 32)) 138 return -EINVAL; 139 140 vm = xe_vm_lookup(to_xe_file(file), args->vm_id); 141 if (XE_IOCTL_ERR(xe, !vm)) 142 return -ENOENT; 143 144 if (XE_IOCTL_ERR(xe, !vm->async_ops.error_capture.addr)) { 145 xe_vm_put(vm); 146 return -ENOTSUPP; 147 } 148 149 addr = vm->async_ops.error_capture.addr; 150 } 151 152 if (XE_IOCTL_ERR(xe, timeout > MAX_SCHEDULE_TIMEOUT)) 153 return -EINVAL; 154 155 /* 156 * FIXME: Very simple implementation at the moment, single wait queue 157 * for everything. Could be optimized to have a wait queue for every 158 * hardware engine. Open coding as 'do_compare' can sleep which doesn't 159 * work with the wait_event_* macros. 160 */ 161 if (vm) 162 add_wait_queue(&vm->async_ops.error_capture.wq, &w_wait); 163 else 164 add_wait_queue(&xe->ufence_wq, &w_wait); 165 for (;;) { 166 if (vm && xe_vm_is_closed(vm)) { 167 err = -ENODEV; 168 break; 169 } 170 err = do_compare(addr, args->value, args->mask, args->op); 171 if (err <= 0) 172 break; 173 174 if (signal_pending(current)) { 175 err = -ERESTARTSYS; 176 break; 177 } 178 179 if (!timeout) { 180 err = -ETIME; 181 break; 182 } 183 184 timeout = wait_woken(&w_wait, TASK_INTERRUPTIBLE, timeout); 185 } 186 if (vm) { 187 remove_wait_queue(&vm->async_ops.error_capture.wq, &w_wait); 188 xe_vm_put(vm); 189 } else { 190 remove_wait_queue(&xe->ufence_wq, &w_wait); 191 } 192 if (XE_IOCTL_ERR(xe, err < 0)) 193 return err; 194 else if (XE_IOCTL_ERR(xe, !timeout)) 195 return -ETIME; 196 197 /* 198 * Again very simple, return the time in jiffies that has past, may need 199 * a more precision 200 */ 201 if (args->flags & DRM_XE_UFENCE_WAIT_ABSTIME) 202 args->timeout = args->timeout - timeout; 203 204 return 0; 205 } 206