Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/xe: Add helpers to send TLB invalidations

Break out the GuC specific code into helpers as part of the process to
decouple frontback TLB invalidation code from the backend.

Signed-off-by: Stuart Summers <stuart.summers@intel.com>
Reviewed-by: Stuart Summers <stuart.summers@intel.com>
Signed-off-by: Matthew Brost <matthew.brost@intel.com>
Link: https://lore.kernel.org/r/20250826182911.392550-9-stuart.summers@intel.com

+117 -117
+117 -117
drivers/gpu/drm/xe/xe_tlb_inval.c
··· 221 221 return seqno_recv >= seqno; 222 222 } 223 223 224 - static int send_tlb_inval(struct xe_guc *guc, struct xe_tlb_inval_fence *fence, 225 - u32 *action, int len) 224 + static int send_tlb_inval(struct xe_guc *guc, const u32 *action, int len) 226 225 { 227 226 struct xe_gt *gt = guc_to_gt(guc); 228 227 229 - xe_gt_assert(gt, fence); 228 + xe_gt_assert(gt, action[1]); /* Seqno */ 230 229 231 230 /* 232 231 * XXX: The seqno algorithm relies on TLB invalidation being processed ··· 234 235 */ 235 236 236 237 xe_gt_stats_incr(gt, XE_GT_STATS_ID_TLB_INVAL, 1); 237 - action[1] = fence->seqno; 238 238 239 239 return xe_guc_ct_send(&guc->ct, action, len, 240 240 G2H_LEN_DW_TLB_INVALIDATE, 1); ··· 268 270 XE_GUC_TLB_INVAL_MODE_HEAVY << XE_GUC_TLB_INVAL_MODE_SHIFT | \ 269 271 XE_GUC_TLB_INVAL_FLUSH_CACHE) 270 272 271 - /** 272 - * xe_tlb_inval_guc - Issue a TLB invalidation on this GT for the GuC 273 - * @gt: GT structure 274 - * @fence: invalidation fence which will be signal on TLB invalidation 275 - * completion 276 - * 277 - * Issue a TLB invalidation for the GuC. Completion of TLB is asynchronous and 278 - * caller can use the invalidation fence to wait for completion. 279 - * 280 - * Return: 0 on success, negative error code on error 281 - */ 282 - static int xe_tlb_inval_guc(struct xe_gt *gt, 283 - struct xe_tlb_inval_fence *fence) 273 + static int send_tlb_inval_ggtt(struct xe_gt *gt, int seqno) 284 274 { 285 275 u32 action[] = { 286 276 XE_GUC_ACTION_TLB_INVALIDATION, 287 - 0, /* seqno, replaced in send_tlb_inval */ 277 + seqno, 288 278 MAKE_INVAL_OP(XE_GUC_TLB_INVAL_GUC), 289 279 }; 290 - int ret; 291 280 292 - mutex_lock(&gt->tlb_inval.seqno_lock); 293 - xe_tlb_inval_fence_prep(fence); 294 - 295 - ret = send_tlb_inval(&gt->uc.guc, fence, action, ARRAY_SIZE(action)); 296 - if (ret < 0) 297 - inval_fence_signal_unlocked(gt_to_xe(gt), fence); 298 - mutex_unlock(&gt->tlb_inval.seqno_lock); 299 - 300 - /* 301 - * -ECANCELED indicates the CT is stopped for a GT reset. TLB caches 302 - * should be nuked on a GT reset so this error can be ignored. 303 - */ 304 - if (ret == -ECANCELED) 305 - return 0; 306 - 307 - return ret; 308 - } 309 - 310 - /** 311 - * xe_tlb_inval_ggtt - Issue a TLB invalidation on this GT for the GGTT 312 - * @tlb_inval: TLB invalidation client 313 - * 314 - * Issue a TLB invalidation for the GGTT. Completion of TLB invalidation is 315 - * synchronous. 316 - * 317 - * Return: 0 on success, negative error code on error 318 - */ 319 - int xe_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval) 320 - { 321 - struct xe_gt *gt = tlb_inval->private; 322 - struct xe_device *xe = gt_to_xe(gt); 323 - unsigned int fw_ref; 324 - 325 - if (xe_guc_ct_enabled(&gt->uc.guc.ct) && 326 - gt->uc.guc.submission_state.enabled) { 327 - struct xe_tlb_inval_fence fence; 328 - int ret; 329 - 330 - xe_tlb_inval_fence_init(tlb_inval, &fence, true); 331 - ret = xe_tlb_inval_guc(gt, &fence); 332 - if (ret) 333 - return ret; 334 - 335 - xe_tlb_inval_fence_wait(&fence); 336 - } else if (xe_device_uc_enabled(xe) && !xe_device_wedged(xe)) { 337 - struct xe_mmio *mmio = &gt->mmio; 338 - 339 - if (IS_SRIOV_VF(xe)) 340 - return 0; 341 - 342 - fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); 343 - if (xe->info.platform == XE_PVC || GRAPHICS_VER(xe) >= 20) { 344 - xe_mmio_write32(mmio, PVC_GUC_TLB_INV_DESC1, 345 - PVC_GUC_TLB_INV_DESC1_INVALIDATE); 346 - xe_mmio_write32(mmio, PVC_GUC_TLB_INV_DESC0, 347 - PVC_GUC_TLB_INV_DESC0_VALID); 348 - } else { 349 - xe_mmio_write32(mmio, GUC_TLB_INV_CR, 350 - GUC_TLB_INV_CR_INVALIDATE); 351 - } 352 - xe_force_wake_put(gt_to_fw(gt), fw_ref); 353 - } 354 - 355 - return 0; 281 + return send_tlb_inval(&gt->uc.guc, action, ARRAY_SIZE(action)); 356 282 } 357 283 358 284 static int send_tlb_inval_all(struct xe_tlb_inval *tlb_inval, ··· 291 369 292 370 xe_gt_assert(gt, fence); 293 371 294 - return send_tlb_inval(&gt->uc.guc, fence, action, ARRAY_SIZE(action)); 372 + return send_tlb_inval(&gt->uc.guc, action, ARRAY_SIZE(action)); 295 373 } 296 374 297 375 /** ··· 323 401 */ 324 402 #define MAX_RANGE_TLB_INVALIDATION_LENGTH (rounddown_pow_of_two(ULONG_MAX)) 325 403 326 - /** 327 - * xe_tlb_inval_range - Issue a TLB invalidation on this GT for an address range 328 - * @tlb_inval: TLB invalidation client 329 - * @fence: invalidation fence which will be signal on TLB invalidation 330 - * completion 331 - * @start: start address 332 - * @end: end address 333 - * @asid: address space id 334 - * 335 - * Issue a range based TLB invalidation if supported, if not fallback to a full 336 - * TLB invalidation. Completion of TLB is asynchronous and caller can use 337 - * the invalidation fence to wait for completion. 338 - * 339 - * Return: Negative error code on error, 0 on success 340 - */ 341 - int xe_tlb_inval_range(struct xe_tlb_inval *tlb_inval, 342 - struct xe_tlb_inval_fence *fence, u64 start, u64 end, 343 - u32 asid) 404 + static int send_tlb_inval_ppgtt(struct xe_gt *gt, u64 start, u64 end, 405 + u32 asid, int seqno) 344 406 { 345 - struct xe_gt *gt = tlb_inval->private; 346 - struct xe_device *xe = gt_to_xe(gt); 347 407 #define MAX_TLB_INVALIDATION_LEN 7 348 408 u32 action[MAX_TLB_INVALIDATION_LEN]; 349 409 u64 length = end - start; 350 - int len = 0, ret; 351 - 352 - xe_gt_assert(gt, fence); 353 - 354 - /* Execlists not supported */ 355 - if (gt_to_xe(gt)->info.force_execlist) { 356 - __inval_fence_signal(xe, fence); 357 - return 0; 358 - } 410 + int len = 0; 359 411 360 412 action[len++] = XE_GUC_ACTION_TLB_INVALIDATION; 361 - action[len++] = 0; /* seqno, replaced in send_tlb_inval */ 362 - if (!xe->info.has_range_tlb_inval || 413 + action[len++] = seqno; 414 + if (!gt_to_xe(gt)->info.has_range_tlb_inval || 363 415 length > MAX_RANGE_TLB_INVALIDATION_LENGTH) { 364 416 action[len++] = MAKE_INVAL_OP(XE_GUC_TLB_INVAL_FULL); 365 417 } else { ··· 382 486 383 487 xe_gt_assert(gt, len <= MAX_TLB_INVALIDATION_LEN); 384 488 489 + return send_tlb_inval(&gt->uc.guc, action, len); 490 + } 491 + 492 + static int __xe_tlb_inval_ggtt(struct xe_gt *gt, 493 + struct xe_tlb_inval_fence *fence) 494 + { 495 + int ret; 496 + 385 497 mutex_lock(&gt->tlb_inval.seqno_lock); 386 498 xe_tlb_inval_fence_prep(fence); 387 499 388 - ret = send_tlb_inval(&gt->uc.guc, fence, action, 389 - ARRAY_SIZE(action)); 500 + ret = send_tlb_inval_ggtt(gt, fence->seqno); 501 + if (ret < 0) 502 + inval_fence_signal_unlocked(gt_to_xe(gt), fence); 503 + mutex_unlock(&gt->tlb_inval.seqno_lock); 504 + 505 + /* 506 + * -ECANCELED indicates the CT is stopped for a GT reset. TLB caches 507 + * should be nuked on a GT reset so this error can be ignored. 508 + */ 509 + if (ret == -ECANCELED) 510 + return 0; 511 + 512 + return ret; 513 + } 514 + 515 + /** 516 + * xe_tlb_inval_ggtt - Issue a TLB invalidation on this GT for the GGTT 517 + * @tlb_inval: TLB invalidation client 518 + * 519 + * Issue a TLB invalidation for the GGTT. Completion of TLB invalidation is 520 + * synchronous. 521 + * 522 + * Return: 0 on success, negative error code on error 523 + */ 524 + int xe_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval) 525 + { 526 + struct xe_gt *gt = tlb_inval->private; 527 + struct xe_device *xe = gt_to_xe(gt); 528 + unsigned int fw_ref; 529 + 530 + if (xe_guc_ct_enabled(&gt->uc.guc.ct) && 531 + gt->uc.guc.submission_state.enabled) { 532 + struct xe_tlb_inval_fence fence; 533 + int ret; 534 + 535 + xe_tlb_inval_fence_init(tlb_inval, &fence, true); 536 + ret = __xe_tlb_inval_ggtt(gt, &fence); 537 + if (ret) 538 + return ret; 539 + 540 + xe_tlb_inval_fence_wait(&fence); 541 + } else if (xe_device_uc_enabled(xe) && !xe_device_wedged(xe)) { 542 + struct xe_mmio *mmio = &gt->mmio; 543 + 544 + if (IS_SRIOV_VF(xe)) 545 + return 0; 546 + 547 + fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); 548 + if (xe->info.platform == XE_PVC || GRAPHICS_VER(xe) >= 20) { 549 + xe_mmio_write32(mmio, PVC_GUC_TLB_INV_DESC1, 550 + PVC_GUC_TLB_INV_DESC1_INVALIDATE); 551 + xe_mmio_write32(mmio, PVC_GUC_TLB_INV_DESC0, 552 + PVC_GUC_TLB_INV_DESC0_VALID); 553 + } else { 554 + xe_mmio_write32(mmio, GUC_TLB_INV_CR, 555 + GUC_TLB_INV_CR_INVALIDATE); 556 + } 557 + xe_force_wake_put(gt_to_fw(gt), fw_ref); 558 + } 559 + 560 + return 0; 561 + } 562 + 563 + /** 564 + * xe_tlb_inval_range - Issue a TLB invalidation on this GT for an address range 565 + * @tlb_inval: TLB invalidation client 566 + * @fence: invalidation fence which will be signal on TLB invalidation 567 + * completion 568 + * @start: start address 569 + * @end: end address 570 + * @asid: address space id 571 + * 572 + * Issue a range based TLB invalidation if supported, if not fallback to a full 573 + * TLB invalidation. Completion of TLB is asynchronous and caller can use 574 + * the invalidation fence to wait for completion. 575 + * 576 + * Return: Negative error code on error, 0 on success 577 + */ 578 + int xe_tlb_inval_range(struct xe_tlb_inval *tlb_inval, 579 + struct xe_tlb_inval_fence *fence, u64 start, u64 end, 580 + u32 asid) 581 + { 582 + struct xe_gt *gt = tlb_inval->private; 583 + struct xe_device *xe = gt_to_xe(gt); 584 + int ret; 585 + 586 + xe_gt_assert(gt, fence); 587 + 588 + /* Execlists not supported */ 589 + if (xe->info.force_execlist) { 590 + __inval_fence_signal(xe, fence); 591 + return 0; 592 + } 593 + 594 + mutex_lock(&gt->tlb_inval.seqno_lock); 595 + xe_tlb_inval_fence_prep(fence); 596 + 597 + ret = send_tlb_inval_ppgtt(gt, start, end, asid, fence->seqno); 390 598 if (ret < 0) 391 599 inval_fence_signal_unlocked(xe, fence); 392 600 mutex_unlock(&gt->tlb_inval.seqno_lock);