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