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