]> Git Repo - linux.git/blob - drivers/gpu/drm/xe/xe_wait_user_fence.c
Merge tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux
[linux.git] / drivers / gpu / drm / xe / xe_wait_user_fence.c
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 <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 gueue 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 (!timeout && !(err < 0))
173                 err = -ETIME;
174
175         if (q)
176                 xe_exec_queue_put(q);
177
178         return err;
179 }
This page took 0.073049 seconds and 4 git commands to generate.