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