1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2024 Intel Corporation 4 */ 5 6 #include <drm/drm_managed.h> 7 8 #include "xe_assert.h" 9 #include "xe_device_types.h" 10 #include "xe_exec_queue.h" 11 #include "xe_gt.h" 12 #include "xe_gt_stats.h" 13 #include "xe_hw_engine_group.h" 14 #include "xe_sync.h" 15 #include "xe_vm.h" 16 17 static void 18 hw_engine_group_resume_lr_jobs_func(struct work_struct *w) 19 { 20 struct xe_exec_queue *q; 21 struct xe_hw_engine_group *group = container_of(w, struct xe_hw_engine_group, resume_work); 22 int err; 23 enum xe_hw_engine_group_execution_mode previous_mode; 24 25 err = xe_hw_engine_group_get_mode(group, EXEC_MODE_LR, &previous_mode, 26 NULL, 0); 27 if (err) 28 return; 29 30 if (previous_mode == EXEC_MODE_LR) 31 goto put; 32 33 list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) { 34 if (!xe_vm_in_fault_mode(q->vm)) 35 continue; 36 37 /* 38 * Only resume queues that were actually suspended. A queue whose 39 * suspend() failed (e.g. killed/banned/wedged) was never 40 * suspended, so it must not be resumed. 41 */ 42 if (!READ_ONCE(q->lr.suspended)) 43 continue; 44 45 WRITE_ONCE(q->lr.suspended, false); 46 q->ops->resume(q); 47 } 48 49 put: 50 xe_hw_engine_group_put(group); 51 } 52 53 static struct xe_hw_engine_group * 54 hw_engine_group_alloc(struct xe_device *xe) 55 { 56 struct xe_hw_engine_group *group; 57 int err; 58 59 group = drmm_kzalloc(&xe->drm, sizeof(*group), GFP_KERNEL); 60 if (!group) 61 return ERR_PTR(-ENOMEM); 62 63 group->resume_wq = alloc_workqueue("xe-resume-lr-jobs-wq", WQ_PERCPU, 64 0); 65 if (!group->resume_wq) 66 return ERR_PTR(-ENOMEM); 67 68 err = drmm_add_action_or_reset(&xe->drm, __drmm_workqueue_release, group->resume_wq); 69 if (err) 70 return ERR_PTR(err); 71 72 init_rwsem(&group->mode_sem); 73 INIT_WORK(&group->resume_work, hw_engine_group_resume_lr_jobs_func); 74 INIT_LIST_HEAD(&group->exec_queue_list); 75 76 return group; 77 } 78 79 /** 80 * xe_hw_engine_setup_groups() - Setup the hw engine groups for the gt 81 * @gt: The gt for which groups are setup 82 * 83 * Return: 0 on success, negative error code on error. 84 */ 85 int xe_hw_engine_setup_groups(struct xe_gt *gt) 86 { 87 struct xe_hw_engine *hwe; 88 enum xe_hw_engine_id id; 89 struct xe_hw_engine_group *group_rcs_ccs, *group_bcs, *group_vcs_vecs; 90 struct xe_device *xe = gt_to_xe(gt); 91 92 group_rcs_ccs = hw_engine_group_alloc(xe); 93 if (IS_ERR(group_rcs_ccs)) 94 return PTR_ERR(group_rcs_ccs); 95 96 group_bcs = hw_engine_group_alloc(xe); 97 if (IS_ERR(group_bcs)) 98 return PTR_ERR(group_bcs); 99 100 group_vcs_vecs = hw_engine_group_alloc(xe); 101 if (IS_ERR(group_vcs_vecs)) 102 return PTR_ERR(group_vcs_vecs); 103 104 for_each_hw_engine(hwe, gt, id) { 105 switch (hwe->class) { 106 case XE_ENGINE_CLASS_COPY: 107 hwe->hw_engine_group = group_bcs; 108 break; 109 case XE_ENGINE_CLASS_RENDER: 110 case XE_ENGINE_CLASS_COMPUTE: 111 hwe->hw_engine_group = group_rcs_ccs; 112 break; 113 case XE_ENGINE_CLASS_VIDEO_DECODE: 114 case XE_ENGINE_CLASS_VIDEO_ENHANCE: 115 hwe->hw_engine_group = group_vcs_vecs; 116 break; 117 case XE_ENGINE_CLASS_OTHER: 118 break; 119 case XE_ENGINE_CLASS_MAX: 120 xe_gt_assert(gt, false); 121 } 122 } 123 124 return 0; 125 } 126 127 /** 128 * xe_hw_engine_group_add_exec_queue() - Add an exec queue to a hw engine group 129 * @group: The hw engine group 130 * @q: The exec_queue 131 * 132 * Return: 0 on success, 133 * -EINTR if the lock could not be acquired 134 */ 135 int xe_hw_engine_group_add_exec_queue(struct xe_hw_engine_group *group, struct xe_exec_queue *q) 136 { 137 int err; 138 struct xe_device *xe = gt_to_xe(q->gt); 139 140 xe_assert(xe, group); 141 xe_assert(xe, !(q->flags & EXEC_QUEUE_FLAG_VM)); 142 xe_assert(xe, q->vm); 143 144 if (xe_vm_in_preempt_fence_mode(q->vm)) 145 return 0; 146 147 err = down_write_killable(&group->mode_sem); 148 if (err) 149 return err; 150 151 if (xe_vm_in_fault_mode(q->vm) && group->cur_mode == EXEC_MODE_DMA_FENCE) { 152 /* 153 * suspend() can fail (e.g. killed/banned/wedged), leaving the 154 * queue un-suspended. Propagate the failure so the queue is not 155 * added; on failure nothing was suspended, so there is nothing to 156 * undo. Only record the queue as suspended (and later resume it) 157 * once suspend() has succeeded. 158 */ 159 err = q->ops->suspend(q); 160 if (err) 161 goto err_suspend; 162 163 WRITE_ONCE(q->lr.suspended, true); 164 err = q->ops->suspend_wait(q); 165 if (err) 166 goto err_suspend; 167 168 xe_hw_engine_group_resume_faulting_lr_jobs(group); 169 } 170 171 list_add(&q->hw_engine_group_link, &group->exec_queue_list); 172 up_write(&group->mode_sem); 173 174 return 0; 175 176 err_suspend: 177 up_write(&group->mode_sem); 178 return err; 179 } 180 ALLOW_ERROR_INJECTION(xe_hw_engine_group_add_exec_queue, ERRNO); 181 182 /** 183 * xe_hw_engine_group_del_exec_queue() - Delete an exec queue from a hw engine group 184 * @group: The hw engine group 185 * @q: The exec_queue 186 */ 187 void xe_hw_engine_group_del_exec_queue(struct xe_hw_engine_group *group, struct xe_exec_queue *q) 188 { 189 struct xe_device *xe = gt_to_xe(q->gt); 190 191 xe_assert(xe, group); 192 xe_assert(xe, q->vm); 193 194 down_write(&group->mode_sem); 195 196 if (!list_empty(&q->hw_engine_group_link)) 197 list_del(&q->hw_engine_group_link); 198 199 up_write(&group->mode_sem); 200 } 201 202 /** 203 * xe_hw_engine_group_resume_faulting_lr_jobs() - Asynchronously resume the hw engine group's 204 * faulting LR jobs 205 * @group: The hw engine group 206 */ 207 void xe_hw_engine_group_resume_faulting_lr_jobs(struct xe_hw_engine_group *group) 208 { 209 queue_work(group->resume_wq, &group->resume_work); 210 } 211 212 /** 213 * xe_hw_engine_group_suspend_faulting_lr_jobs() - Suspend the faulting LR jobs of this group 214 * @group: The hw engine group 215 * @has_deps: dma-fence job triggering suspend has dependencies 216 * 217 * Return: 0 on success, negative error code on error. 218 */ 219 static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group *group, 220 bool has_deps) 221 { 222 int err; 223 struct xe_exec_queue *q; 224 struct xe_gt *gt = NULL; 225 bool need_resume = false; 226 ktime_t start = xe_gt_stats_ktime_get(); 227 228 lockdep_assert_held_write(&group->mode_sem); 229 230 list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) { 231 232 if (!xe_vm_in_fault_mode(q->vm)) 233 continue; 234 235 if (has_deps) 236 return -EAGAIN; 237 238 xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT, 1); 239 /* 240 * suspend() only fails when the queue is killed/banned/wedged. 241 * Such a queue is being torn down (its removal from HW is handled 242 * by the kill/ban teardown), so it is not a live fault-mode 243 * context the mode switch must preempt. Skip it rather than 244 * failing the switch, otherwise one dying sibling would block a 245 * dma-fence submission on the healthy queues in the group. Only 246 * queues recorded as suspended below are later waited on and 247 * resumed. 248 */ 249 err = q->ops->suspend(q); 250 if (err) 251 continue; 252 253 WRITE_ONCE(q->lr.suspended, true); 254 need_resume = true; 255 gt = q->gt; 256 } 257 258 list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) { 259 if (!xe_vm_in_fault_mode(q->vm)) 260 continue; 261 262 /* Only wait on queues that were actually suspended above. */ 263 if (!READ_ONCE(q->lr.suspended)) 264 continue; 265 266 err = q->ops->suspend_wait(q); 267 if (err) 268 goto err_resume; 269 } 270 271 if (gt) { 272 xe_gt_stats_incr(gt, 273 XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_US, 274 xe_gt_stats_ktime_us_delta(start)); 275 } 276 277 if (need_resume) 278 xe_hw_engine_group_resume_faulting_lr_jobs(group); 279 280 return 0; 281 282 err_resume: 283 /* 284 * A suspend_wait() failed partway through the mode switch. Resume the 285 * sibling queues that were already suspended in this call so they are 286 * not left suspended forever. 287 * 288 * resume() requires the suspend to have completed (suspend_pending 289 * cleared) or it trips the !suspend_pending assert. So skip the resume 290 * when either: 291 * - suspend_wait_blocking() fails: on a GuC timeout it bans the queue 292 * and triggers cleanup, so the queue is being torn down; or 293 * - reset_status() is true: the queue was reset/killed/banned/wedged. 294 * suspend_wait() can return success in this case via its killed/ 295 * stopped wait condition while suspend_pending is still set, and the 296 * queue is being torn down anyway, so its state is resolved by 297 * teardown rather than by a resume here. 298 * In either case leave the queue marked suspended. 299 * 300 * Use the *blocking* (uninterruptible) wait here: the queues resumed on 301 * this path may belong to a different process than the one that 302 * triggered the mode switch. An interruptible suspend_wait() would 303 * return -ERESTARTSYS if the triggering task is signalled, skip the 304 * resume, and leave the other process's queue suspended forever 305 * (cross-process DoS). 306 */ 307 list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) { 308 if (!xe_vm_in_fault_mode(q->vm)) 309 continue; 310 311 if (!READ_ONCE(q->lr.suspended)) 312 continue; 313 314 if (q->ops->suspend_wait_blocking(q) || q->ops->reset_status(q)) 315 continue; 316 317 WRITE_ONCE(q->lr.suspended, false); 318 q->ops->resume(q); 319 } 320 321 return err; 322 } 323 324 /** 325 * xe_hw_engine_group_wait_for_dma_fence_jobs() - Wait for dma fence jobs to complete 326 * @group: The hw engine group 327 * 328 * This function is not meant to be called directly from a user IOCTL as dma_fence_wait() 329 * is not interruptible. 330 * 331 * Return: 0 on success, 332 * -ETIME if waiting for one job failed 333 */ 334 static int xe_hw_engine_group_wait_for_dma_fence_jobs(struct xe_hw_engine_group *group) 335 { 336 long timeout; 337 struct xe_exec_queue *q; 338 struct xe_gt *gt = NULL; 339 struct dma_fence *fence; 340 ktime_t start = xe_gt_stats_ktime_get(); 341 342 lockdep_assert_held_write(&group->mode_sem); 343 344 list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) { 345 if (xe_vm_in_lr_mode(q->vm)) 346 continue; 347 348 xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_COUNT, 1); 349 fence = xe_exec_queue_last_fence_get_for_resume(q, q->vm); 350 timeout = dma_fence_wait(fence, false); 351 dma_fence_put(fence); 352 gt = q->gt; 353 354 if (timeout < 0) 355 return -ETIME; 356 } 357 358 if (gt) { 359 xe_gt_stats_incr(gt, 360 XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_US, 361 xe_gt_stats_ktime_us_delta(start)); 362 } 363 364 return 0; 365 } 366 367 static int switch_mode(struct xe_hw_engine_group *group, bool has_deps) 368 { 369 int err = 0; 370 enum xe_hw_engine_group_execution_mode new_mode; 371 372 lockdep_assert_held_write(&group->mode_sem); 373 374 switch (group->cur_mode) { 375 case EXEC_MODE_LR: 376 new_mode = EXEC_MODE_DMA_FENCE; 377 err = xe_hw_engine_group_suspend_faulting_lr_jobs(group, 378 has_deps); 379 break; 380 case EXEC_MODE_DMA_FENCE: 381 new_mode = EXEC_MODE_LR; 382 err = xe_hw_engine_group_wait_for_dma_fence_jobs(group); 383 break; 384 } 385 386 if (err) 387 return err; 388 389 group->cur_mode = new_mode; 390 391 return 0; 392 } 393 394 static int wait_syncs(struct xe_sync_entry *syncs, int num_syncs) 395 { 396 int err, i; 397 398 for (i = 0; i < num_syncs; ++i) { 399 err = xe_sync_entry_wait(syncs + i); 400 if (err) 401 return err; 402 } 403 404 return 0; 405 } 406 407 /** 408 * xe_hw_engine_group_get_mode() - Get the group to execute in the new mode 409 * @group: The hw engine group 410 * @new_mode: The new execution mode 411 * @previous_mode: Pointer to the previous mode provided for use by caller 412 * @syncs: Syncs from exec IOCTL 413 * @num_syncs: Number of syncs from exec IOCTL 414 * 415 * Return: 0 if successful, -EINTR if locking failed. 416 */ 417 int xe_hw_engine_group_get_mode(struct xe_hw_engine_group *group, 418 enum xe_hw_engine_group_execution_mode new_mode, 419 enum xe_hw_engine_group_execution_mode *previous_mode, 420 struct xe_sync_entry *syncs, int num_syncs) 421 __acquires(&group->mode_sem) 422 { 423 bool has_deps = !!num_syncs; 424 int err = down_read_interruptible(&group->mode_sem); 425 426 if (err) 427 return err; 428 429 *previous_mode = group->cur_mode; 430 431 if (new_mode != group->cur_mode) { 432 up_read(&group->mode_sem); 433 retry: 434 err = down_write_killable(&group->mode_sem); 435 if (err) 436 return err; 437 438 if (new_mode != group->cur_mode) { 439 err = switch_mode(group, has_deps); 440 if (err) { 441 up_write(&group->mode_sem); 442 443 if (err != -EAGAIN) 444 return err; 445 446 err = wait_syncs(syncs, num_syncs); 447 if (err) 448 return err; 449 450 has_deps = false; 451 goto retry; 452 } 453 } 454 downgrade_write(&group->mode_sem); 455 } 456 457 return err; 458 } 459 460 /** 461 * xe_hw_engine_group_put() - Put the group 462 * @group: The hw engine group 463 */ 464 void xe_hw_engine_group_put(struct xe_hw_engine_group *group) 465 __releases(&group->mode_sem) 466 { 467 up_read(&group->mode_sem); 468 } 469 470 /** 471 * xe_hw_engine_group_find_exec_mode() - Find the execution mode for this exec queue 472 * @q: The exec_queue 473 */ 474 enum xe_hw_engine_group_execution_mode 475 xe_hw_engine_group_find_exec_mode(struct xe_exec_queue *q) 476 { 477 if (xe_vm_in_fault_mode(q->vm)) 478 return EXEC_MODE_LR; 479 else 480 return EXEC_MODE_DMA_FENCE; 481 } 482