mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
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) -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRskUM7w1oG5rx2IZO4FpNVCsYGvwUCan4Y0QAKCRC4FpNVCsYG vw4gAQD/56NgUmv0Rx3BsDCQhjCMb99DtTGbnEvhehmb6nJ0gQD/eBPPDmobVJYZ P0pGk6B014FrmH+PDjD9Vo2EbswLFQE= =ez0c -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
b2601e783a
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
pxp_invalidate_queues(pxp);
|
||||
static void pxp_terminate(struct xe_pxp *pxp, bool hw_only)
|
||||
{
|
||||
struct xe_device *xe = pxp->xe;
|
||||
int ret = 0;
|
||||
|
||||
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 @@ int xe_pxp_pm_suspend(struct xe_pxp *pxp)
|
|||
fallthrough;
|
||||
case XE_PXP_ACTIVE:
|
||||
pxp->key_instance++;
|
||||
pxp->needs_termination_on_resume = true;
|
||||
needs_queue_inval = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -924,6 +958,7 @@ int xe_pxp_pm_suspend(struct xe_pxp *pxp)
|
|||
*/
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user