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

drm/xe: Fix pagefault and access counter worker functions

When processing G2H messages for pagefault or access counters, we queue a
work item and call queue_work(). This fails if the worker thread is already
queued to run.
The expectation is that the worker function will do more than process a
single item and return. It needs to either process all pending items or
requeue itself if items are pending. But requeuing will add latency and
potential context switch can occur.

We don't want to add unnecessary latency and so the worker should process
as many faults as it can within a reasonable duration of time.
We also do not want to hog the cpu core, so here we execute in a loop
and requeue if still running after more than 20 ms.
This seems reasonable framework and easy to tune this futher if needed.

This resolves issues seen with several igt@xe_exec_fault_mode subtests
where the GPU will hang when KMD ignores a pending pagefault.

v2: requeue the worker instead of having an internal processing loop.
v3: implement hybrid model of v1 and v2
now, run for 20 msec before we will requeue if still running
v4: only requeue in worker if queue is non-empty (Matt B)

Signed-off-by: Brian Welty <brian.welty@intel.com>
Reviewed-by: Matthew Brost <matthew.brost@intel.com>
Reviewed-by: Stuart Summers <stuart.summers@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>

authored by

Brian Welty and committed by
Rodrigo Vivi
e4e4268d 57162274

+50 -36
+50 -36
drivers/gpu/drm/xe/xe_gt_pagefault.c
··· 276 276 277 277 #define PF_MSG_LEN_DW 4 278 278 279 - static int get_pagefault(struct pf_queue *pf_queue, struct pagefault *pf) 279 + static bool get_pagefault(struct pf_queue *pf_queue, struct pagefault *pf) 280 280 { 281 281 const struct xe_guc_pagefault_desc *desc; 282 - int ret = 0; 282 + bool ret = false; 283 283 284 284 spin_lock_irq(&pf_queue->lock); 285 285 if (pf_queue->head != pf_queue->tail) { ··· 303 303 304 304 pf_queue->head = (pf_queue->head + PF_MSG_LEN_DW) % 305 305 PF_QUEUE_NUM_DW; 306 - } else { 307 - ret = -1; 306 + ret = true; 308 307 } 309 308 spin_unlock_irq(&pf_queue->lock); 310 309 ··· 347 348 return full ? -ENOSPC : 0; 348 349 } 349 350 351 + #define USM_QUEUE_MAX_RUNTIME_MS 20 352 + 350 353 static void pf_queue_work_func(struct work_struct *w) 351 354 { 352 355 struct pf_queue *pf_queue = container_of(w, struct pf_queue, worker); ··· 356 355 struct xe_device *xe = gt_to_xe(gt); 357 356 struct xe_guc_pagefault_reply reply = {}; 358 357 struct pagefault pf = {}; 358 + unsigned long threshold; 359 359 int ret; 360 360 361 - ret = get_pagefault(pf_queue, &pf); 362 - if (ret) 363 - return; 361 + threshold = jiffies + msecs_to_jiffies(USM_QUEUE_MAX_RUNTIME_MS); 364 362 365 - ret = handle_pagefault(gt, &pf); 366 - if (unlikely(ret)) { 367 - print_pagefault(xe, &pf); 368 - pf.fault_unsuccessful = 1; 369 - drm_dbg(&xe->drm, "Fault response: Unsuccessful %d\n", ret); 363 + while (get_pagefault(pf_queue, &pf)) { 364 + ret = handle_pagefault(gt, &pf); 365 + if (unlikely(ret)) { 366 + print_pagefault(xe, &pf); 367 + pf.fault_unsuccessful = 1; 368 + drm_dbg(&xe->drm, "Fault response: Unsuccessful %d\n", ret); 369 + } 370 + 371 + reply.dw0 = FIELD_PREP(PFR_VALID, 1) | 372 + FIELD_PREP(PFR_SUCCESS, pf.fault_unsuccessful) | 373 + FIELD_PREP(PFR_REPLY, PFR_ACCESS) | 374 + FIELD_PREP(PFR_DESC_TYPE, FAULT_RESPONSE_DESC) | 375 + FIELD_PREP(PFR_ASID, pf.asid); 376 + 377 + reply.dw1 = FIELD_PREP(PFR_VFID, pf.vfid) | 378 + FIELD_PREP(PFR_ENG_INSTANCE, pf.engine_instance) | 379 + FIELD_PREP(PFR_ENG_CLASS, pf.engine_class) | 380 + FIELD_PREP(PFR_PDATA, pf.pdata); 381 + 382 + send_pagefault_reply(&gt->uc.guc, &reply); 383 + 384 + if (time_after(jiffies, threshold) && 385 + pf_queue->head != pf_queue->tail) { 386 + queue_work(gt->usm.pf_wq, w); 387 + break; 388 + } 370 389 } 371 - 372 - reply.dw0 = FIELD_PREP(PFR_VALID, 1) | 373 - FIELD_PREP(PFR_SUCCESS, pf.fault_unsuccessful) | 374 - FIELD_PREP(PFR_REPLY, PFR_ACCESS) | 375 - FIELD_PREP(PFR_DESC_TYPE, FAULT_RESPONSE_DESC) | 376 - FIELD_PREP(PFR_ASID, pf.asid); 377 - 378 - reply.dw1 = FIELD_PREP(PFR_VFID, pf.vfid) | 379 - FIELD_PREP(PFR_ENG_INSTANCE, pf.engine_instance) | 380 - FIELD_PREP(PFR_ENG_CLASS, pf.engine_class) | 381 - FIELD_PREP(PFR_PDATA, pf.pdata); 382 - 383 - send_pagefault_reply(&gt->uc.guc, &reply); 384 390 } 385 391 386 392 static void acc_queue_work_func(struct work_struct *w); ··· 552 544 553 545 #define ACC_MSG_LEN_DW 4 554 546 555 - static int get_acc(struct acc_queue *acc_queue, struct acc *acc) 547 + static bool get_acc(struct acc_queue *acc_queue, struct acc *acc) 556 548 { 557 549 const struct xe_guc_acc_desc *desc; 558 - int ret = 0; 550 + bool ret = false; 559 551 560 552 spin_lock(&acc_queue->lock); 561 553 if (acc_queue->head != acc_queue->tail) { ··· 575 567 576 568 acc_queue->head = (acc_queue->head + ACC_MSG_LEN_DW) % 577 569 ACC_QUEUE_NUM_DW; 578 - } else { 579 - ret = -1; 570 + ret = true; 580 571 } 581 572 spin_unlock(&acc_queue->lock); 582 573 ··· 588 581 struct xe_gt *gt = acc_queue->gt; 589 582 struct xe_device *xe = gt_to_xe(gt); 590 583 struct acc acc = {}; 584 + unsigned long threshold; 591 585 int ret; 592 586 593 - ret = get_acc(acc_queue, &acc); 594 - if (ret) 595 - return; 587 + threshold = jiffies + msecs_to_jiffies(USM_QUEUE_MAX_RUNTIME_MS); 596 588 597 - ret = handle_acc(gt, &acc); 598 - if (unlikely(ret)) { 599 - print_acc(xe, &acc); 600 - drm_warn(&xe->drm, "ACC: Unsuccessful %d\n", ret); 589 + while (get_acc(acc_queue, &acc)) { 590 + ret = handle_acc(gt, &acc); 591 + if (unlikely(ret)) { 592 + print_acc(xe, &acc); 593 + drm_warn(&xe->drm, "ACC: Unsuccessful %d\n", ret); 594 + } 595 + 596 + if (time_after(jiffies, threshold) && 597 + acc_queue->head != acc_queue->tail) { 598 + queue_work(gt->usm.acc_wq, w); 599 + break; 600 + } 601 601 } 602 602 } 603 603