diff options
| author | Dave Airlie <airlied@redhat.com> | 2026-08-14 14:24:11 +1000 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2026-08-14 15:41:40 +1000 |
| commit | b2601e783a2e54f6963d65f0d94f96d96c146a3a (patch) | |
| tree | 470bc9022dd7fc70d2d3662e56f779c634d7e960 | |
| parent | 7581e7c73e8fbea7c885583146fbe09a8462a25d (diff) | |
| parent | ef526d122b62af5afa437f095aa6661a953676c4 (diff) | |
| download | linux-next-b2601e783a2e54f6963d65f0d94f96d96c146a3a.tar.gz linux-next-b2601e783a2e54f6963d65f0d94f96d96c146a3a.zip | |
Merge tag 'drm-xe-fixes-2026-08-13' of https://gitlab.freedesktop.org/drm/xe/kernel into drm-fixes
Driver Changes:
- Fix DPT Allocation paths (Maarten)
- Fixes around UM queue BO (Jia)
- Order ring writes before ring tail updates (Matthew Brost)
- Add termination on resume for PXP (Daniele)
- Document Sentinel and make CTX_TIMESTAMP read TOCTOU-safe (Gajendra)
- Fix sync entry leak on OA config emit failure (Linmao Li)
- Check managed mutex initilization errors (Linmao Li)
- Fix min frequency setting (Vinay)
- Fix xe_device_probe error path (Raag)
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Thomas Hellstrom <thomas.hellstrom@linux.intel.com>
Link: https://patch.msgid.link/an4ZogmPqP2Xtfx3@fedora
| -rw-r--r-- | drivers/gpu/drm/xe/display/xe_fb_pin.c | 33 | ||||
| -rw-r--r-- | drivers/gpu/drm/xe/xe_bo.h | 14 | ||||
| -rw-r--r-- | drivers/gpu/drm/xe/xe_device.c | 6 | ||||
| -rw-r--r-- | drivers/gpu/drm/xe/xe_guc_ads.c | 93 | ||||
| -rw-r--r-- | drivers/gpu/drm/xe/xe_guc_ads_types.h | 5 | ||||
| -rw-r--r-- | drivers/gpu/drm/xe/xe_guc_pc.c | 13 | ||||
| -rw-r--r-- | drivers/gpu/drm/xe/xe_lrc.c | 29 | ||||
| -rw-r--r-- | drivers/gpu/drm/xe/xe_lrc.h | 7 | ||||
| -rw-r--r-- | drivers/gpu/drm/xe/xe_oa.c | 13 | ||||
| -rw-r--r-- | drivers/gpu/drm/xe/xe_pxp.c | 162 | ||||
| -rw-r--r-- | drivers/gpu/drm/xe/xe_pxp_types.h | 27 |
11 files changed, 279 insertions, 123 deletions
diff --git a/drivers/gpu/drm/xe/display/xe_fb_pin.c b/drivers/gpu/drm/xe/display/xe_fb_pin.c index 5f4a0cd8deca..73469ea5f333 100644 --- a/drivers/gpu/drm/xe/display/xe_fb_pin.c +++ b/drivers/gpu/drm/xe/display/xe_fb_pin.c @@ -164,31 +164,14 @@ static int __xe_pin_fb_vma_dpt(struct drm_gem_object *obj, dpt_size = ALIGN(intel_rotation_info_size(&view->rotated) * 8, XE_PAGE_SIZE); - if (IS_DGFX(xe)) - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_VRAM0 | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE, - pin_params->alignment, false); - else - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_STOLEN | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE, - pin_params->alignment, false); - if (IS_ERR(dpt)) - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_SYSTEM | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE | - XE_BO_FLAG_FORCE_WC, - pin_params->alignment, false); + dpt = xe_bo_create_pin_map_at_novm(xe, tile0, + dpt_size, ~0ull, + ttm_bo_type_kernel, + XE_BO_FLAG_VRAM_IF_DGFX(tile0) | + XE_BO_FLAG_GGTT | + XE_BO_FLAG_PAGETABLE | + XE_BO_FLAG_FORCE_WC, + pin_params->alignment, false); if (IS_ERR(dpt)) return PTR_ERR(dpt); diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h index 7ae1d9ac0574..57039cf42ea7 100644 --- a/drivers/gpu/drm/xe/xe_bo.h +++ b/drivers/gpu/drm/xe/xe_bo.h @@ -6,6 +6,7 @@ #ifndef _XE_BO_H_ #define _XE_BO_H_ +#include <drm/drm_prime.h> #include <drm/ttm/ttm_tt.h> #include "xe_bo_types.h" @@ -548,6 +549,19 @@ void xe_bo_dev_fini(struct xe_bo_dev *bo_device); struct sg_table *xe_bo_sg(struct xe_bo *bo); +/** + * xe_bo_sg_is_contiguous() - Check if a BO's DMA address space is contiguous. + * @bo: the BO to check (must have a valid sg table, i.e. !xe_bo_is_vram()) + * @len: required contiguous length in bytes + * + * Returns true if the first @len bytes of the BO are mapped to a contiguous + * DMA address range. + */ +static inline bool xe_bo_sg_is_contiguous(struct xe_bo *bo, size_t len) +{ + return drm_prime_get_contiguous_size(xe_bo_sg(bo)) >= len; +} + /* * xe_sg_segment_size() - Provides upper limit for sg segment size. * @dev: device pointer diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index dcb48caa485d..a2b076b1dc60 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -1102,7 +1102,11 @@ int xe_device_probe(struct xe_device *xe) if (err) goto err_unregister_display; - return devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe); + err = devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe); + if (err) + goto err_unregister_display; + + return 0; err_unregister_display: xe_display_unregister(xe); diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index c98454545a85..886bafd31f45 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -63,10 +63,14 @@ ads_to_map(struct xe_guc_ads *ads) /* * The Additional Data Struct (ADS) has pointers for different buffers used by - * the GuC. One single gem object contains the ADS struct itself (guc_ads) and - * all the extra buffers indirectly linked via the ADS struct's entries. + * the GuC. One gem object (ads->bo) contains the ADS struct itself (guc_ads) + * and most of the extra buffers linked via the ADS struct's entries. The UM + * fault queues (PAGE_FAULT, PAGE_FAULT_RESPONSE, ACCESS_COUNTER rings) are + * kept in a separate BO (ads->um_queue_bo) so that the full memset of ads->bo + * performed on every GT reset does not discard fault descriptors already + * written into the rings by the GPU. * - * Layout of the ADS blob allocated for the GuC: + * Layout of the ADS blob (ads->bo): * * +---------------------------------------+ <== base * | guc_ads | @@ -98,10 +102,6 @@ ads_to_map(struct xe_guc_ads *ads) * +---------------------------------------+ * | padding | * +---------------------------------------+ <== 4K aligned - * | UM queues | - * +---------------------------------------+ - * | padding | - * +---------------------------------------+ <== 4K aligned * | private data | * +---------------------------------------+ * | padding | @@ -155,16 +155,6 @@ static size_t guc_ads_capture_size(struct xe_guc_ads *ads) return PAGE_ALIGN(ads->capture_size); } -static size_t guc_ads_um_queues_size(struct xe_guc_ads *ads) -{ - struct xe_device *xe = ads_to_xe(ads); - - if (!xe->info.has_usm) - return 0; - - return GUC_UM_QUEUE_SIZE * GUC_UM_HW_QUEUE_MAX; -} - static size_t guc_ads_private_data_size(struct xe_guc_ads *ads) { return PAGE_ALIGN(ads_to_guc(ads)->fw.private_data_size); @@ -205,22 +195,12 @@ static size_t guc_ads_capture_offset(struct xe_guc_ads *ads) return PAGE_ALIGN(offset); } -static size_t guc_ads_um_queues_offset(struct xe_guc_ads *ads) -{ - u32 offset; - - offset = guc_ads_capture_offset(ads) + - guc_ads_capture_size(ads); - - return PAGE_ALIGN(offset); -} - static size_t guc_ads_private_data_offset(struct xe_guc_ads *ads) { size_t offset; - offset = guc_ads_um_queues_offset(ads) + - guc_ads_um_queues_size(ads); + offset = guc_ads_capture_offset(ads) + + guc_ads_capture_size(ads); return PAGE_ALIGN(offset); } @@ -409,6 +389,49 @@ int xe_guc_ads_init(struct xe_guc_ads *ads) ads->bo = bo; + if (xe->info.has_usm) { + /* + * Allocate a separate BO for the HW fault ring (UM queues). + * + * Round the size up to the next power of two so that on iGPU + * (system memory, no IOMMU) the TTM pool issues a single + * alloc_pages(order=N) call, maximising the chance of getting + * a physically contiguous block. GuC requires contiguous DPA. + */ + size_t um_size = IS_DGFX(xe) ? + GUC_UM_QUEUE_SIZE * GUC_UM_HW_QUEUE_MAX : + roundup_pow_of_two(GUC_UM_QUEUE_SIZE * + GUC_UM_HW_QUEUE_MAX); + + u32 um_flags = XE_BO_FLAG_VRAM_IF_DGFX(tile) | + XE_BO_FLAG_GGTT | + XE_BO_FLAG_GGTT_INVALIDATE | + XE_BO_FLAG_PINNED_NORESTORE | + XE_BO_FLAG_NEEDS_UC; + + bo = xe_managed_bo_create_pin_map(xe, tile, um_size, um_flags); + if (IS_ERR(bo)) + return PTR_ERR(bo); + + /* + * On pre-Xe3p platforms, GAM (not GuC) accesses the UM queue + * ring via base_dpa, which must be a contiguous DMA address + * range. Verify that the allocated pages are contiguous in + * DMA address space. + */ + if (!xe_bo_is_vram(bo) && + !xe_guc_using_main_gamctrl_queues(ads_to_guc(ads)) && + unlikely(!xe_bo_sg_is_contiguous(bo, + GUC_UM_QUEUE_SIZE * + GUC_UM_HW_QUEUE_MAX))) { + drm_err(&xe->drm, + "UM fault queue memory is not contiguous in DMA address space; GAM requires contiguous DPA\n"); + return -ENOMEM; + } + + ads->um_queue_bo = bo; + } + return 0; } ALLOW_ERROR_INJECTION(xe_guc_ads_init, ERRNO); /* See xe_pci_probe() */ @@ -820,7 +843,7 @@ static void guc_mmio_reg_state_init(struct xe_guc_ads *ads) static void guc_um_init_params(struct xe_guc_ads *ads) { - u32 um_queue_offset = guc_ads_um_queues_offset(ads); + struct xe_bo *um_bo = ads->um_queue_bo; struct xe_guc *guc = ads_to_guc(ads); struct xe_device *xe = ads_to_xe(ads); u64 base_dpa; @@ -830,8 +853,14 @@ static void guc_um_init_params(struct xe_guc_ads *ads) with_dpa = !xe_guc_using_main_gamctrl_queues(guc); - base_ggtt = xe_bo_ggtt_addr(ads->bo) + um_queue_offset; - base_dpa = xe_bo_main_addr(ads->bo, PAGE_SIZE) + um_queue_offset; + if (um_bo) { + /* All USM platforms: UM queues in dedicated um_queue_bo */ + base_ggtt = xe_bo_ggtt_addr(um_bo); + base_dpa = xe_bo_main_addr(um_bo, PAGE_SIZE); + } else { + /* Platform does not support USM: no UM queues, nothing to do */ + return; + } for (i = 0; i < GUC_UM_HW_QUEUE_MAX; ++i) { /* diff --git a/drivers/gpu/drm/xe/xe_guc_ads_types.h b/drivers/gpu/drm/xe/xe_guc_ads_types.h index 48a8e092023f..845c1fbd93a4 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads_types.h +++ b/drivers/gpu/drm/xe/xe_guc_ads_types.h @@ -16,6 +16,11 @@ struct xe_bo; struct xe_guc_ads { /** @bo: Xe BO for GuC ads blob */ struct xe_bo *bo; + /** + * @um_queue_bo: Dedicated BO for the HW fault ring (UM queues). + * NULL if the platform does not support USM. + */ + struct xe_bo *um_queue_bo; /** @golden_lrc_size: golden LRC size */ size_t golden_lrc_size; /** @regset_size: size of register set passed to GuC for save/restore */ diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c index 59f2fa79ad42..7cf8f4858598 100644 --- a/drivers/gpu/drm/xe/xe_guc_pc.c +++ b/drivers/gpu/drm/xe/xe_guc_pc.c @@ -911,6 +911,7 @@ static bool pc_needs_min_freq_change(struct xe_guc_pc *pc) static int pc_adjust_freq_bounds(struct xe_guc_pc *pc) { int ret; + u32 min_freq; lockdep_assert_held(&pc->freq_lock); @@ -933,8 +934,14 @@ static int pc_adjust_freq_bounds(struct xe_guc_pc *pc) * Same thing happens for Server platforms where min is listed as * RPMax */ - if (pc_get_min_freq(pc) > pc->rp0_freq) + min_freq = pc_get_min_freq(pc); + if (min_freq > pc->rp0_freq) { ret = pc_set_min_freq(pc, pc->rp0_freq); + if (ret) + goto out; + + min_freq = pc->rp0_freq; + } /* * Setting GT RP min frequency to 1.2GHz by default for @@ -947,8 +954,8 @@ static int pc_adjust_freq_bounds(struct xe_guc_pc *pc) * we aren't expecting high power output across board * */ - if (pc_needs_min_freq_change(pc)) - ret = pc_set_min_freq(pc, max(BMG_MIN_FREQ, pc_get_min_freq(pc))); + if (pc_needs_min_freq_change(pc) && min_freq < BMG_MIN_FREQ) + ret = pc_set_min_freq(pc, BMG_MIN_FREQ); out: return ret; diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index a4292a11391d..9f8217ff1904 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -1096,7 +1096,7 @@ static void xe_lrc_finish(struct xe_lrc *lrc) * on until it is scheduled, we also read the ENGINE_ID MMIO in the WA BB and * store it in the PPHSWP. */ -#define CONTEXT_ACTIVE 1ULL +#define CONTEXT_ACTIVE XE_LRC_CTX_TIMESTAMP_ACTIVE static ssize_t setup_utilization_wa(struct xe_lrc *lrc, struct xe_hw_engine *hwe, u32 *batch, @@ -1849,6 +1849,13 @@ void xe_lrc_write_ring(struct xe_lrc *lrc, const void *data, size_t size) __xe_lrc_write_ring(lrc, ring, &noop, sizeof(noop)); } + + /* + * The ring and the LRC context image are both WC, so the ring tail + * update which publishes these writes can become visible to the device + * first. Ensure the ring contents are visible before returning. + */ + xe_device_wmb(xe); } u64 xe_lrc_descriptor(struct xe_lrc *lrc) @@ -2720,21 +2727,27 @@ static u64 xe_lrc_update_multi_queue_timestamp(struct xe_lrc *lrc, u64 *old_ts) static u64 xe_lrc_context_timestamp(struct xe_lrc *lrc) { u64 reg_ts, new_ts = lrc->ctx_timestamp; + u64 stored; /* CTX_TIMESTAMP mmio read is invalid on VF, so return the LRC value */ if (IS_SRIOV_VF(lrc_to_xe(lrc))) return xe_lrc_ctx_timestamp(lrc); - if (context_active(lrc) && - !get_ctx_timestamp(lrc, xe_lrc_engine_id(lrc), ®_ts)) + /* Safely read CTX_TIMESTAMP: Avoid TOCTOU on LRC-stored CONTEXT_ACTIVE sentinel */ + stored = xe_lrc_ctx_timestamp(lrc); + if (stored != CONTEXT_ACTIVE) + return stored; + + /* Context is active: read the live timestamp from the engine's MMIO register */ + if (!get_ctx_timestamp(lrc, xe_lrc_engine_id(lrc), ®_ts)) new_ts = reg_ts; - /* - * If context swicthed out while we were here, just return the latest - * LRC CTX TIMESTAMP value. + /* If the context switched out prefer using the value + * from context-save over the stale MMIO read. */ - if (!context_active(lrc)) - return xe_lrc_ctx_timestamp(lrc); + stored = xe_lrc_ctx_timestamp(lrc); + if (stored != CONTEXT_ACTIVE) + return stored; return new_ts; } diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h index 0a3a611391ee..7be5e3da8bc8 100644 --- a/drivers/gpu/drm/xe/xe_lrc.h +++ b/drivers/gpu/drm/xe/xe_lrc.h @@ -9,6 +9,13 @@ #include "xe_lrc_types.h" +/* + * Sentinel value stored in lrc->ctx_timestamp while a context is starting. + * The hardware hasn't yet written the real CTX_TIMESTAMP, so this is not a + * valid elapsed-time sample and must not be used as one. + */ +#define XE_LRC_CTX_TIMESTAMP_ACTIVE 1ULL + struct drm_printer; struct xe_bb; struct xe_device; diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 2dce6a47202c..18d990c5d4ec 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -1594,6 +1594,10 @@ static long xe_oa_config_locked(struct xe_oa_stream *stream, u64 arg) config = xchg(&stream->oa_config, config); drm_dbg(&stream->oa->xe->drm, "changed to oa config uuid=%s\n", stream->oa_config->uuid); + } else { + while (param.num_syncs--) + xe_sync_entry_cleanup(¶m.syncs[param.num_syncs]); + kfree(param.syncs); } err_config_put: @@ -2713,9 +2717,7 @@ static int xe_oa_init_gt(struct xe_gt *gt) __xe_oa_init_oa_units(gt); - drmm_mutex_init(>_to_xe(gt)->drm, >->oa.gt_lock); - - return 0; + return drmm_mutex_init(>_to_xe(gt)->drm, >->oa.gt_lock); } static void xe_oa_print_gt_oa_units(struct xe_gt *gt) @@ -2855,7 +2857,10 @@ int xe_oa_init(struct xe_device *xe) oa->xe = xe; oa->oa_formats = oa_formats; - drmm_mutex_init(&oa->xe->drm, &oa->metrics_lock); + ret = drmm_mutex_init(&oa->xe->drm, &oa->metrics_lock); + if (ret) + goto exit; + idr_init_base(&oa->metrics_idr, 1); ret = xe_oa_init_oa_units(oa); diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index 968b7e70b3f9..d17ab6e59df5 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -8,6 +8,8 @@ #include <drm/drm_managed.h> #include <uapi/drm/xe_drm.h> +#include <linux/device.h> + #include "xe_bo.h" #include "xe_bo_types.h" #include "xe_device_types.h" @@ -164,16 +166,9 @@ static void mark_termination_in_progress(struct xe_pxp *pxp) pxp->status = XE_PXP_TERMINATION_IN_PROGRESS; } -static void pxp_terminate(struct xe_pxp *pxp) +static bool pxp_prep_for_termination(struct xe_pxp *pxp) { - int ret = 0; - struct xe_device *xe = pxp->xe; - - if (!wait_for_completion_timeout(&pxp->activation, - msecs_to_jiffies(PXP_ACTIVATION_TIMEOUT_MS))) - drm_err(&xe->drm, "failed to wait for PXP start before termination\n"); - - mutex_lock(&pxp->mutex); + lockdep_assert_held(&pxp->mutex); if (pxp->status == XE_PXP_ACTIVE) pxp->key_instance++; @@ -182,10 +177,8 @@ static void pxp_terminate(struct xe_pxp *pxp) * we'll mark the status as needing termination on resume, so no need to * emit a termination now. */ - if (pxp->status == XE_PXP_SUSPENDED) { - mutex_unlock(&pxp->mutex); - return; - } + if (pxp->status == XE_PXP_SUSPENDED) + return false; /* * If we have a termination already in progress, we need to wait for @@ -195,15 +188,44 @@ static void pxp_terminate(struct xe_pxp *pxp) */ if (pxp->status == XE_PXP_TERMINATION_IN_PROGRESS) { pxp->status = XE_PXP_NEEDS_ADDITIONAL_TERMINATION; - mutex_unlock(&pxp->mutex); - return; + return false; } mark_termination_in_progress(pxp); - mutex_unlock(&pxp->mutex); + return true; +} + +static void pxp_terminate(struct xe_pxp *pxp, bool hw_only) +{ + struct xe_device *xe = pxp->xe; + int ret = 0; - pxp_invalidate_queues(pxp); + if (!wait_for_completion_timeout(&pxp->activation, + msecs_to_jiffies(PXP_ACTIVATION_TIMEOUT_MS))) + drm_err(&xe->drm, "failed to wait for PXP start before termination\n"); + + if (!hw_only) { + bool prep_ok; + + mutex_lock(&pxp->mutex); + + prep_ok = pxp_prep_for_termination(pxp); + + mutex_unlock(&pxp->mutex); + + if (!prep_ok) + return; + + pxp_invalidate_queues(pxp); + } else { + /* + * The caller of the HW-only termination should have already + * called pxp_prep_for_termination and marked the termination as + * in progress. + */ + xe_assert(xe, !completion_done(&pxp->termination)); + } ret = pxp_terminate_hw(pxp); if (ret) { @@ -249,33 +271,46 @@ static void pxp_terminate_complete(struct xe_pxp *pxp) mutex_unlock(&pxp->mutex); } -static void pxp_irq_work(struct work_struct *work) +static void pxp_events_work(struct work_struct *work) { - struct xe_pxp *pxp = container_of(work, typeof(*pxp), irq.work); + struct xe_pxp *pxp = container_of(work, typeof(*pxp), events.work); struct xe_device *xe = pxp->xe; + bool hw_only = false; u32 events = 0; - spin_lock_irq(&xe->irq.lock); - events = pxp->irq.events; - pxp->irq.events = 0; - spin_unlock_irq(&xe->irq.lock); + events = atomic_xchg(&pxp->events.pending, 0); if (!events) return; /* - * If we're processing a termination irq while suspending then don't - * bother, we're going to re-init everything on resume anyway. + * If the termination request comes from an irq while we're suspending, + * then we can defer it to the resume path instead of waking the device + * up. + * In the case of the termination on resume the pm reference is taken + * in xe_pxp_pm_resume() and released here. + * Note that we do not expect both events to be set at the same time, + * but if it does happen due to a spurious interrupt we want to behave + * as if the only request we got was the one from the resume path; this + * is because the termination prep has already been done in + * xe_pxp_pm_resume() and it is impossible for any PXP operations to + * occur between the prep and the termination completion, so there is no + * need for a new SW prep. */ - if ((events & PXP_TERMINATION_REQUEST) && !xe_pm_runtime_get_if_active(xe)) + if (events & PXP_TERMINATION_REQUEST_ON_RESUME) { + events &= ~PXP_TERMINATION_REQUEST_IRQ; + hw_only = true; + } + + if ((events & PXP_TERMINATION_REQUEST_IRQ) && !xe_pm_runtime_get_if_active(xe)) return; if (events & PXP_TERMINATION_REQUEST) { - events &= ~PXP_TERMINATION_COMPLETE; - pxp_terminate(pxp); + events &= ~PXP_TERMINATION_COMPLETE_IRQ; + pxp_terminate(pxp, hw_only); } - if (events & PXP_TERMINATION_COMPLETE) + if (events & PXP_TERMINATION_COMPLETE_IRQ) pxp_terminate_complete(pxp); if (events & PXP_TERMINATION_REQUEST) @@ -296,20 +331,18 @@ void xe_pxp_irq_handler(struct xe_device *xe, u16 iir) return; } - lockdep_assert_held(&xe->irq.lock); - if (unlikely(!iir)) return; if (iir & (KCR_PXP_STATE_TERMINATED_INTERRUPT | KCR_APP_TERMINATED_PER_FW_REQ_INTERRUPT)) - pxp->irq.events |= PXP_TERMINATION_REQUEST; + atomic_or(PXP_TERMINATION_REQUEST_IRQ, &pxp->events.pending); if (iir & KCR_PXP_STATE_RESET_COMPLETE_INTERRUPT) - pxp->irq.events |= PXP_TERMINATION_COMPLETE; + atomic_or(PXP_TERMINATION_COMPLETE_IRQ, &pxp->events.pending); - if (pxp->irq.events) - queue_work(pxp->irq.wq, &pxp->irq.work); + if (atomic_read(&pxp->events.pending)) + queue_work(pxp->events.wq, &pxp->events.work); } static int kcr_pxp_set_status(const struct xe_pxp *pxp, bool enable) @@ -340,7 +373,7 @@ static void pxp_fini(void *arg) { struct xe_pxp *pxp = arg; - destroy_workqueue(pxp->irq.wq); + destroy_workqueue(pxp->events.wq); xe_pxp_destroy_execution_resources(pxp); /* no need to explicitly disable KCR since we're going to do an FLR */ @@ -402,7 +435,7 @@ int xe_pxp_init(struct xe_device *xe) INIT_LIST_HEAD(&pxp->queues.list); spin_lock_init(&pxp->queues.lock); - INIT_WORK(&pxp->irq.work, pxp_irq_work); + INIT_WORK(&pxp->events.work, pxp_events_work); pxp->xe = xe; pxp->gt = gt; @@ -421,8 +454,8 @@ int xe_pxp_init(struct xe_device *xe) mutex_init(&pxp->mutex); - pxp->irq.wq = alloc_ordered_workqueue("pxp-wq", 0); - if (!pxp->irq.wq) { + pxp->events.wq = alloc_ordered_workqueue("pxp-wq", 0); + if (!pxp->events.wq) { err = -ENOMEM; goto out_free; } @@ -442,7 +475,7 @@ int xe_pxp_init(struct xe_device *xe) out_kcr_disable: kcr_pxp_disable(pxp); out_wq: - destroy_workqueue(pxp->irq.wq); + destroy_workqueue(pxp->events.wq); out_free: drmm_kfree(&xe->drm, pxp); out: @@ -889,6 +922,7 @@ wait_for_activation: fallthrough; case XE_PXP_ACTIVE: pxp->key_instance++; + pxp->needs_termination_on_resume = true; needs_queue_inval = true; break; } @@ -924,6 +958,7 @@ wait_for_activation: */ void xe_pxp_pm_resume(struct xe_pxp *pxp) { + bool has_pm = false; int err; if (!xe_pxp_is_enabled(pxp)) @@ -931,14 +966,57 @@ void xe_pxp_pm_resume(struct xe_pxp *pxp) err = kcr_pxp_enable(pxp); + /* + * We want to avoid the device runtime suspending before we're done with + * the termination queued below, so we need a runtime PM reference; we + * can't call the rpm functions from within the PXP lock, so we take the + * ref here. Note that we don't want the rpm resume code to actually run + * here as that would call back into this function, but as long as we + * don't enable DPM_FLAG_SMART_SUSPEND (which we currently do not) we're + * guaranteed to not be runtime suspended at this point, so we can + * safely use the get_noresume variant. + */ + if (pxp->needs_termination_on_resume) { + has_pm = true; + + xe_assert(pxp->xe, !dev_pm_smart_suspend(pxp->xe->drm.dev)); + xe_pm_runtime_get_noresume(pxp->xe); + } + mutex_lock(&pxp->mutex); xe_assert(pxp->xe, pxp->status == XE_PXP_SUSPENDED); - if (err) + if (err) { pxp->status = XE_PXP_ERROR; - else + } else { pxp->status = XE_PXP_NEEDS_TERMINATION; + if (pxp->needs_termination_on_resume) { + pxp->needs_termination_on_resume = false; + + /* + * We can't call pxp_terminate_hw directly from here + * because we're not allowed to do allocations within + * the rpm resume call, so we defer the termination to + * the worker that we use for the termination irqs. + * However, we do not want any PXP ops to go through + * between the suspend completing and the worker + * starting, so we need to do the termination prep + * immediately, which will mark the termination as in + * progress and stall PXP ops. + */ + if (pxp_prep_for_termination(pxp)) { + has_pm = false; /* move PM ref ownership to worker */ + + atomic_or(PXP_TERMINATION_REQUEST_ON_RESUME, &pxp->events.pending); + queue_work(pxp->events.wq, &pxp->events.work); + } + } + } + mutex_unlock(&pxp->mutex); + + if (has_pm) + xe_pm_runtime_put(pxp->xe); } diff --git a/drivers/gpu/drm/xe/xe_pxp_types.h b/drivers/gpu/drm/xe/xe_pxp_types.h index ec86306e16f4..8132a9750b6e 100644 --- a/drivers/gpu/drm/xe/xe_pxp_types.h +++ b/drivers/gpu/drm/xe/xe_pxp_types.h @@ -85,17 +85,20 @@ struct xe_pxp { /** @gsc_res: kernel-owned objects for PXP submissions to the GSCCS */ struct xe_pxp_gsc_client_resources gsc_res; - /** @irq: wrapper for the worker and queue used for PXP irq support */ + /** @events: wrapper for the worker and queue used for PXP event handling */ struct { - /** @irq.work: worker that manages irq events. */ + /** @events.work: worker that manages termination events. */ struct work_struct work; - /** @irq.wq: workqueue on which to queue the irq work. */ + /** @events.wq: workqueue on which to queue the work. */ struct workqueue_struct *wq; - /** @irq.events: pending events, protected with xe->irq.lock. */ - u32 events; -#define PXP_TERMINATION_REQUEST BIT(0) -#define PXP_TERMINATION_COMPLETE BIT(1) - } irq; + /** @events.pending: pending events */ + atomic_t pending; +#define PXP_TERMINATION_REQUEST_IRQ BIT(0) +#define PXP_TERMINATION_REQUEST_ON_RESUME BIT(1) +#define PXP_TERMINATION_REQUEST (PXP_TERMINATION_REQUEST_IRQ | \ + PXP_TERMINATION_REQUEST_ON_RESUME) +#define PXP_TERMINATION_COMPLETE_IRQ BIT(2) + } events; /** @mutex: protects the pxp status and the queue list */ struct mutex mutex; @@ -130,6 +133,14 @@ struct xe_pxp { * suspend cycles. */ u32 last_suspend_key_instance; + /** + * @needs_termination_on_resume: indicates if PXP termination is needed + * on resume. This is set if PXP was active when we suspend and it is + * cleared when we queue the termination on resume. Since the suspend + * and resume calls cannot execute at the same time, this variable does + * not need to be protected by the PXP lock. + */ + bool needs_termination_on_resume; }; #endif /* _XE_PXP_TYPES_H_ */ |
