xref: /linux/drivers/gpu/drm/xe/xe_wait_user_fence.c (revision dd08ebf6c3525a7ea2186e636df064ea47281987)
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