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