1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2021 Intel Corporation 4 */ 5 6 #include "xe_sync.h" 7 8 #include <linux/kthread.h> 9 #include <linux/sched/mm.h> 10 #include <linux/uaccess.h> 11 12 #include <drm/drm_print.h> 13 #include <drm/drm_syncobj.h> 14 #include <drm/xe_drm.h> 15 16 #include "xe_device_types.h" 17 #include "xe_macros.h" 18 #include "xe_sched_job_types.h" 19 20 #define SYNC_FLAGS_TYPE_MASK 0x3 21 22 struct user_fence { 23 struct xe_device *xe; 24 struct kref refcount; 25 struct dma_fence_cb cb; 26 struct work_struct worker; 27 struct mm_struct *mm; 28 u64 __user *addr; 29 u64 value; 30 }; 31 32 static void user_fence_destroy(struct kref *kref) 33 { 34 struct user_fence *ufence = container_of(kref, struct user_fence, 35 refcount); 36 37 mmdrop(ufence->mm); 38 kfree(ufence); 39 } 40 41 static void user_fence_get(struct user_fence *ufence) 42 { 43 kref_get(&ufence->refcount); 44 } 45 46 static void user_fence_put(struct user_fence *ufence) 47 { 48 kref_put(&ufence->refcount, user_fence_destroy); 49 } 50 51 static struct user_fence *user_fence_create(struct xe_device *xe, u64 addr, 52 u64 value) 53 { 54 struct user_fence *ufence; 55 56 ufence = kmalloc(sizeof(*ufence), GFP_KERNEL); 57 if (!ufence) 58 return NULL; 59 60 ufence->xe = xe; 61 kref_init(&ufence->refcount); 62 ufence->addr = u64_to_user_ptr(addr); 63 ufence->value = value; 64 ufence->mm = current->mm; 65 mmgrab(ufence->mm); 66 67 return ufence; 68 } 69 70 static void user_fence_worker(struct work_struct *w) 71 { 72 struct user_fence *ufence = container_of(w, struct user_fence, worker); 73 74 if (mmget_not_zero(ufence->mm)) { 75 kthread_use_mm(ufence->mm); 76 if (copy_to_user(ufence->addr, &ufence->value, sizeof(ufence->value))) 77 XE_WARN_ON("Copy to user failed"); 78 kthread_unuse_mm(ufence->mm); 79 mmput(ufence->mm); 80 } 81 82 wake_up_all(&ufence->xe->ufence_wq); 83 user_fence_put(ufence); 84 } 85 86 static void kick_ufence(struct user_fence *ufence, struct dma_fence *fence) 87 { 88 INIT_WORK(&ufence->worker, user_fence_worker); 89 queue_work(ufence->xe->ordered_wq, &ufence->worker); 90 dma_fence_put(fence); 91 } 92 93 static void user_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb) 94 { 95 struct user_fence *ufence = container_of(cb, struct user_fence, cb); 96 97 kick_ufence(ufence, fence); 98 } 99 100 int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef, 101 struct xe_sync_entry *sync, 102 struct drm_xe_sync __user *sync_user, 103 bool exec, bool in_lr_mode) 104 { 105 struct drm_xe_sync sync_in; 106 int err; 107 bool signal; 108 109 if (copy_from_user(&sync_in, sync_user, sizeof(*sync_user))) 110 return -EFAULT; 111 112 if (XE_IOCTL_DBG(xe, sync_in.flags & 113 ~(SYNC_FLAGS_TYPE_MASK | DRM_XE_SYNC_FLAG_SIGNAL)) || 114 XE_IOCTL_DBG(xe, sync_in.pad) || 115 XE_IOCTL_DBG(xe, sync_in.reserved[0] || sync_in.reserved[1])) 116 return -EINVAL; 117 118 signal = sync_in.flags & DRM_XE_SYNC_FLAG_SIGNAL; 119 switch (sync_in.flags & SYNC_FLAGS_TYPE_MASK) { 120 case DRM_XE_SYNC_FLAG_SYNCOBJ: 121 if (XE_IOCTL_DBG(xe, in_lr_mode && signal)) 122 return -EOPNOTSUPP; 123 124 if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr))) 125 return -EINVAL; 126 127 sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle); 128 if (XE_IOCTL_DBG(xe, !sync->syncobj)) 129 return -ENOENT; 130 131 if (!signal) { 132 sync->fence = drm_syncobj_fence_get(sync->syncobj); 133 if (XE_IOCTL_DBG(xe, !sync->fence)) 134 return -EINVAL; 135 } 136 break; 137 138 case DRM_XE_SYNC_FLAG_TIMELINE_SYNCOBJ: 139 if (XE_IOCTL_DBG(xe, in_lr_mode && signal)) 140 return -EOPNOTSUPP; 141 142 if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr))) 143 return -EINVAL; 144 145 if (XE_IOCTL_DBG(xe, sync_in.timeline_value == 0)) 146 return -EINVAL; 147 148 sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle); 149 if (XE_IOCTL_DBG(xe, !sync->syncobj)) 150 return -ENOENT; 151 152 if (signal) { 153 sync->chain_fence = dma_fence_chain_alloc(); 154 if (!sync->chain_fence) 155 return -ENOMEM; 156 } else { 157 sync->fence = drm_syncobj_fence_get(sync->syncobj); 158 if (XE_IOCTL_DBG(xe, !sync->fence)) 159 return -EINVAL; 160 161 err = dma_fence_chain_find_seqno(&sync->fence, 162 sync_in.timeline_value); 163 if (err) 164 return err; 165 } 166 break; 167 168 case DRM_XE_SYNC_FLAG_DMA_BUF: 169 if (XE_IOCTL_DBG(xe, "TODO")) 170 return -EINVAL; 171 break; 172 173 case DRM_XE_SYNC_FLAG_USER_FENCE: 174 if (XE_IOCTL_DBG(xe, !signal)) 175 return -EOPNOTSUPP; 176 177 if (XE_IOCTL_DBG(xe, sync_in.addr & 0x7)) 178 return -EINVAL; 179 180 if (exec) { 181 sync->addr = sync_in.addr; 182 } else { 183 sync->ufence = user_fence_create(xe, sync_in.addr, 184 sync_in.timeline_value); 185 if (XE_IOCTL_DBG(xe, !sync->ufence)) 186 return -ENOMEM; 187 } 188 189 break; 190 191 default: 192 return -EINVAL; 193 } 194 195 sync->flags = sync_in.flags; 196 sync->timeline_value = sync_in.timeline_value; 197 198 return 0; 199 } 200 201 int xe_sync_entry_wait(struct xe_sync_entry *sync) 202 { 203 if (sync->fence) 204 dma_fence_wait(sync->fence, true); 205 206 return 0; 207 } 208 209 int xe_sync_entry_add_deps(struct xe_sync_entry *sync, struct xe_sched_job *job) 210 { 211 int err; 212 213 if (sync->fence) { 214 err = drm_sched_job_add_dependency(&job->drm, 215 dma_fence_get(sync->fence)); 216 if (err) { 217 dma_fence_put(sync->fence); 218 return err; 219 } 220 } 221 222 return 0; 223 } 224 225 void xe_sync_entry_signal(struct xe_sync_entry *sync, struct xe_sched_job *job, 226 struct dma_fence *fence) 227 { 228 if (!(sync->flags & DRM_XE_SYNC_FLAG_SIGNAL)) 229 return; 230 231 if (sync->chain_fence) { 232 drm_syncobj_add_point(sync->syncobj, sync->chain_fence, 233 fence, sync->timeline_value); 234 /* 235 * The chain's ownership is transferred to the 236 * timeline. 237 */ 238 sync->chain_fence = NULL; 239 } else if (sync->syncobj) { 240 drm_syncobj_replace_fence(sync->syncobj, fence); 241 } else if (sync->ufence) { 242 int err; 243 244 dma_fence_get(fence); 245 user_fence_get(sync->ufence); 246 err = dma_fence_add_callback(fence, &sync->ufence->cb, 247 user_fence_cb); 248 if (err == -ENOENT) { 249 kick_ufence(sync->ufence, fence); 250 } else if (err) { 251 XE_WARN_ON("failed to add user fence"); 252 user_fence_put(sync->ufence); 253 dma_fence_put(fence); 254 } 255 } else if ((sync->flags & SYNC_FLAGS_TYPE_MASK) == 256 DRM_XE_SYNC_FLAG_USER_FENCE) { 257 job->user_fence.used = true; 258 job->user_fence.addr = sync->addr; 259 job->user_fence.value = sync->timeline_value; 260 } 261 } 262 263 void xe_sync_entry_cleanup(struct xe_sync_entry *sync) 264 { 265 if (sync->syncobj) 266 drm_syncobj_put(sync->syncobj); 267 if (sync->fence) 268 dma_fence_put(sync->fence); 269 if (sync->chain_fence) 270 dma_fence_put(&sync->chain_fence->base); 271 if (sync->ufence) 272 user_fence_put(sync->ufence); 273 } 274