On Fri, Oct 16, 2026 at 03:31:02AM +0000, Jia Yao wrote:
> Wa_22013059131 sets FORCE_1_SUB_MESSAGE_PER_FRAGMENT in LSC_CHICKEN_BIT_0
> at engine init, but this is known to cause GPU hangs in certain workloads.
> Add I915_CONTEXT_PARAM_WA_22013059131 so userspace that handles the
> workaround itself (e.g. by limiting SLM size) can set it to 1 to let the
> kernel know bit 15 programming is not needed. The register is not
> context-saved by hardware, so the value is programmed on context switch
> via the indirect context batchbuffer, and the old unconditional write in
> intel_workarounds.c is removed.
> 
> LSC_CHICKEN_BIT_0 is shared by the RCS and CCS engines, not per-context
> state, and GuC only runs RCS and CCS concurrently when they share an
> address space, i.e. belong to the same DRM client. Tracking the opt-out
> per client is therefore both necessary and sufficient to avoid the race.
> The value is latched once, by whichever comes first between an explicit
> CONTEXT_CREATE_EXT_SETPARAM and the first context of the client being
> submitted, and a later request for the opposite value gets -EINVAL; this
> covers the default context (id 0) and needs no new ioctl. The client is
> the unit of consistency, not the register's true isolation boundary:
> unrelated clients can still override each other on RCS and CCS, which is
> unchanged from current behaviour.
> 
> v13:
> - Reject any value other than 0 or 1 with -EINVAL, instead of silently
>   treating any non-zero value the same as 1, so other values remain
>   available to give explicit meaning to in the future (Matt)
> - Drop the v11 ctx_setparam() support for
>   I915_CONTEXT_PARAM_WA_22013059131 added by Sashiko for context 0
>   coverage. The parameter is creation-time only and current
>   compute-runtime usage relies solely on CONTEXT_CREATE_EXT_SETPARAM.
>   Retain getparam() support. (Matt)
> - Rename I915_WA_22013059131_{UNSET,KERNEL,USERSPACE} to
>   I915_WA_22013059131_CLIENT_{UNSET,KERNEL,USERSPACE} to distinguish them
>   from the CONTEXT_WA_22013059131_APPLY context flag (Matt)
> 
> v12:
> - Don't dereference ce->gem_context / ctx->client from lrc_update_regs(),
>   which also runs from reset paths (guc_reset_state(), lrc_reset()) where
>   the ctx lifetime is not guaranteed by the caller. Latch the decision
>   once into CONTEXT_WA_22013059131_APPLY from
>   intel_context_alloc_state(), where the pinner holds a ctx reference,
>   and only sample that bit afterwards (Sashiko)
> 
> v11:
> - Add the missing I915_CONTEXT_PARAM_WA_22013059131 cases to
>   ctx_setparam() and i915_gem_context_getparam_ioctl(), so the
>   parameter can also be set on an already-finalized context (e.g. the
>   default context, id 0) and queried back, not just set at proto-context
>   creation time (Sashiko)
> 
> v10:
> - Move the opt-out from intel_context to i915_drm_client, latched once
>   per client (by explicit setparam or first LRC init) instead of tracked
>   per intel_context, so all contexts of a client - including the
>   default context - always agree and conflicting requests get -EINVAL
>   (Joonas, Sashiko, Matt)
> - Note that GuC's requirement for RCS/CCS contexts to share an address
>   space before running concurrently is what makes client-wide latching
>   sufficient to prevent the race (Matt)
> - Document that this requirement applies to the whole fd, so userspace
>   sharing an fd across an interop boundary must coordinate ordering
>   itself
> 
> v9:
> - Restrict Wa_22013059131 to compute engine only in
>   gen12_emit_indirect_ctx_xcs() (Sashiko)
> 
> v8:
> - Clarify in the uAPI comment that setting this parameter only opts out
>   of LSC_CHICKEN_BIT_0 bit 15 (FORCE_1_SUB_MESSAGE_PER_FRAGMENT);
>   LSC_CHICKEN_BIT_0_UDW MAXREQS_PER_BANK remains unconditionally
>   programmed by the kernel as the other part of Wa_22013059131
> 
> v7:
> - Reject ioctl with -ENODEV on non-DG2-G11 platforms
> 
> v6:
> - Remove excessive blank lines
> 
> v5:
> - Remove fix and stable
> 
> v4:
> - Add a link of the userspace using this API
> 
> v3:
> - Kernel-internal context will not change workaround settings
> 
> Bspec: 54833
> Link: https://github.com/intel/compute-runtime/pull/919
> Cc: [email protected]
> Cc: Shuicheng Lin <[email protected]>
> Cc: Matt Roper <[email protected]>
> Cc: Joonas Lahtinen <[email protected]>
> Cc: Rodrigo Vivi <[email protected]>
> Cc: Maciej Plewka <[email protected]>
> Cc: Andi Shyti <[email protected]>
> Signed-off-by: Jia Yao <[email protected]>

It's been a couple years since I worked closely with the i915 code, but
the changes here look okay to me.

Reviewed-by: Matt Roper <[email protected]>


Matt

> ---
>  drivers/gpu/drm/i915/gem/i915_gem_context.c   | 53 +++++++++++++++++
>  drivers/gpu/drm/i915/gt/intel_context.c       |  2 +
>  drivers/gpu/drm/i915/gt/intel_context_types.h |  1 +
>  drivers/gpu/drm/i915/gt/intel_lrc.c           | 59 ++++++++++++++++++-
>  drivers/gpu/drm/i915/gt/intel_lrc.h           |  3 +
>  drivers/gpu/drm/i915/gt/intel_workarounds.c   | 10 ++--
>  drivers/gpu/drm/i915/i915_drm_client.h        | 57 ++++++++++++++++++
>  include/uapi/drm/i915_drm.h                   | 25 ++++++++
>  8 files changed, 204 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c 
> b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> index 6ac0f23570f3..c2d70fbde7d3 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> @@ -874,6 +874,39 @@ static int set_proto_ctx_sseu(struct 
> drm_i915_file_private *fpriv,
>       return 0;
>  }
>  
> +/*
> + * Wa_22013059131:dg2 - Latch who owns LSC_CHICKEN_BIT_0 bit 15 for every
> + * context of @fpriv.  The register is shared by the RCS and CCS engines, so
> + * the first context to express a preference decides for the whole client
> + * and any later context asking for the opposite is rejected, rather than
> + * letting the two race over the register at context-switch time.
> + *
> + * The owner is also latched, to the kernel, the first time a context of
> + * the client is pinned (see lrc_latch_wa_22013059131()), so opting out is
> + * only possible while none of the client's RCS/CCS contexts has run yet.
> + */
> +static int set_client_wa_22013059131(struct drm_i915_file_private *fpriv,
> +                                  u64 value)
> +{
> +     int want;
> +
> +     /*
> +      * Only 0 and 1 are currently defined; reject anything else now so
> +      * that other values remain available to give explicit meaning to in
> +      * the future, rather than silently treating them as 1 today.
> +      */
> +     if (value > 1)
> +             return -EINVAL;
> +
> +     want = value ? I915_WA_22013059131_CLIENT_USERSPACE :
> +                   I915_WA_22013059131_CLIENT_KERNEL;
> +
> +     if (i915_drm_client_latch_wa_22013059131(fpriv->client, want) != want)
> +             return -EINVAL;
> +
> +     return 0;
> +}
> +
>  static int set_proto_ctx_param(struct drm_i915_file_private *fpriv,
>                              struct i915_gem_proto_context *pc,
>                              struct drm_i915_gem_context_param *args)
> @@ -911,6 +944,15 @@ static int set_proto_ctx_param(struct 
> drm_i915_file_private *fpriv,
>                       ret = -EINVAL;
>               break;
>  
> +     case I915_CONTEXT_PARAM_WA_22013059131:
> +             if (args->size)
> +                     ret = -EINVAL;
> +             else if (!IS_DG2_G11(i915))
> +                     ret = -ENODEV;
> +             else
> +                     ret = set_client_wa_22013059131(fpriv, args->value);
> +             break;
> +
>       case I915_CONTEXT_PARAM_RECOVERABLE:
>               if (args->size)
>                       ret = -EINVAL;
> @@ -2249,6 +2291,7 @@ static int ctx_setparam(struct drm_i915_file_private 
> *fpriv,
>       case I915_CONTEXT_PARAM_RINGSIZE:
>       case I915_CONTEXT_PARAM_VM:
>       case I915_CONTEXT_PARAM_ENGINES:
> +     case I915_CONTEXT_PARAM_WA_22013059131: /* creation-time only */
>       default:
>               ret = -EINVAL;
>               break;
> @@ -2583,6 +2626,16 @@ int i915_gem_context_getparam_ioctl(struct drm_device 
> *dev, void *data,
>               ret = get_protected(ctx, args);
>               break;
>  
> +     case I915_CONTEXT_PARAM_WA_22013059131:
> +             if (!IS_DG2_G11(ctx->i915)) {
> +                     ret = -ENODEV;
> +             } else {
> +                     args->size = 0;
> +                     args->value = 
> atomic_read(&file_priv->client->wa_22013059131_owner) ==
> +                                   I915_WA_22013059131_CLIENT_USERSPACE;
> +             }
> +             break;
> +
>       case I915_CONTEXT_PARAM_NO_ZEROMAP:
>       case I915_CONTEXT_PARAM_BAN_PERIOD:
>       case I915_CONTEXT_PARAM_ENGINES:
> diff --git a/drivers/gpu/drm/i915/gt/intel_context.c 
> b/drivers/gpu/drm/i915/gt/intel_context.c
> index b1b8695ba7c9..dc1b69631046 100644
> --- a/drivers/gpu/drm/i915/gt/intel_context.c
> +++ b/drivers/gpu/drm/i915/gt/intel_context.c
> @@ -13,6 +13,7 @@
>  #include "intel_context.h"
>  #include "intel_engine.h"
>  #include "intel_engine_pm.h"
> +#include "intel_lrc.h"
>  #include "intel_ring.h"
>  
>  static struct kmem_cache *slab_ce;
> @@ -80,6 +81,7 @@ int intel_context_alloc_state(struct intel_context *ce)
>                       if (ctx->client)
>                               i915_drm_client_add_context_objects(ctx->client,
>                                                                   ce);
> +                     lrc_latch_wa_22013059131(ce, ctx);
>                       i915_gem_context_put(ctx);
>               }
>       }
> diff --git a/drivers/gpu/drm/i915/gt/intel_context_types.h 
> b/drivers/gpu/drm/i915/gt/intel_context_types.h
> index 10070ee4d74c..928eab48f38d 100644
> --- a/drivers/gpu/drm/i915/gt/intel_context_types.h
> +++ b/drivers/gpu/drm/i915/gt/intel_context_types.h
> @@ -133,6 +133,7 @@ struct intel_context {
>  #define CONTEXT_EXITING                      13
>  #define CONTEXT_LOW_LATENCY          14
>  #define CONTEXT_OWN_STATE            15
> +#define CONTEXT_WA_22013059131_APPLY 16 /* see lrc_latch_wa_22013059131() */
>  
>       struct {
>               u64 timeout_us;
> diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c 
> b/drivers/gpu/drm/i915/gt/intel_lrc.c
> index 147d22907960..c94b7e3d3980 100644
> --- a/drivers/gpu/drm/i915/gt/intel_lrc.c
> +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
> @@ -5,9 +5,11 @@
>  
>  #include <drm/drm_print.h>
>  
> +#include "gem/i915_gem_context.h"
>  #include "gem/i915_gem_lmem.h"
>  
>  #include "gen8_engine_cs.h"
> +#include "i915_drm_client.h"
>  #include "i915_drv.h"
>  #include "i915_perf.h"
>  #include "i915_reg.h"
> @@ -1348,6 +1350,50 @@ gen12_invalidate_state_cache(u32 *cs)
>       return cs;
>  }
>  
> +/* Latched on first pin while the pinner holds a ctx ref; see 
> intel_context_alloc_state(). */
> +void lrc_latch_wa_22013059131(struct intel_context *ce,
> +                           struct i915_gem_context *ctx)
> +{
> +     int owner;
> +
> +     if (!IS_DG2_G11(ce->engine->i915))
> +             return;
> +
> +     /* Only RCS and CCS ever emit this workaround; don't latch for others. 
> */
> +     if (ce->engine->class != RENDER_CLASS && ce->engine->class != 
> COMPUTE_CLASS)
> +             return;
> +
> +     owner = I915_WA_22013059131_CLIENT_KERNEL;
> +     if (ctx->client)
> +             owner = i915_drm_client_latch_wa_22013059131(ctx->client, 
> owner);
> +
> +     if (owner != I915_WA_22013059131_CLIENT_USERSPACE)
> +             set_bit(CONTEXT_WA_22013059131_APPLY, &ce->flags);
> +}
> +
> +static u32 *
> +dg2_g11_emit_wa_22013059131(const struct intel_context *ce, u32 *cs)
> +{
> +     /*
> +      * While re-writing LSC_CHICKEN_BIT_0 for Wa_22013059131, the
> +      * other bits of the register will also get overwritten.  The
> +      * hardware default for all other bits is 0, but any workarounds
> +      * that adjust the other bits in the lower dword of the register
> +      * also need to be re-applied here.  At the moment that's just
> +      * Wa_22014226127, which is always set for DG2-G11 platforms.
> +      */
> +     u32 val = DISABLE_D8_D16_COASLESCE;
> +
> +     if (test_bit(CONTEXT_WA_22013059131_APPLY, &ce->flags))
> +             val |= FORCE_1_SUB_MESSAGE_PER_FRAGMENT;
> +
> +     *cs++ = MI_LOAD_REGISTER_IMM(1);
> +     *cs++ = i915_mmio_reg_offset(LSC_CHICKEN_BIT_0);
> +     *cs++ = val;
> +
> +     return cs;
> +}
> +
>  static u32 *
>  gen12_emit_indirect_ctx_rcs(const struct intel_context *ce, u32 *cs)
>  {
> @@ -1371,6 +1417,10 @@ gen12_emit_indirect_ctx_rcs(const struct intel_context 
> *ce, u32 *cs)
>           IS_DG2(ce->engine->i915))
>               cs = dg2_emit_draw_watermark_setting(cs);
>  
> +     /* Wa_22013059131:dg2 */
> +     if (IS_DG2_G11(ce->engine->i915))
> +             cs = dg2_g11_emit_wa_22013059131(ce, cs);
> +
>       return cs;
>  }
>  
> @@ -1387,7 +1437,14 @@ gen12_emit_indirect_ctx_xcs(const struct intel_context 
> *ce, u32 *cs)
>                                                   
> PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE,
>                                                   0);
>  
> -     return gen12_emit_aux_table_inv(ce->engine, cs);
> +     cs = gen12_emit_aux_table_inv(ce->engine, cs);
> +
> +     /* Wa_22013059131:dg2 */
> +     if (IS_DG2_G11(ce->engine->i915))
> +             if (ce->engine->class == COMPUTE_CLASS)
> +                     cs = dg2_g11_emit_wa_22013059131(ce, cs);
> +
> +     return cs;
>  }
>  
>  static u32 *xehp_emit_fastcolor_blt_wabb(const struct intel_context *ce, u32 
> *cs)
> diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.h 
> b/drivers/gpu/drm/i915/gt/intel_lrc.h
> index 7111bae759f3..cc204ac2d7bf 100644
> --- a/drivers/gpu/drm/i915/gt/intel_lrc.h
> +++ b/drivers/gpu/drm/i915/gt/intel_lrc.h
> @@ -14,6 +14,7 @@
>  #include "intel_context.h"
>  
>  struct drm_i915_gem_object;
> +struct i915_gem_context;
>  struct i915_gem_ww_ctx;
>  struct intel_engine_cs;
>  struct intel_ring;
> @@ -35,6 +36,8 @@ void lrc_fini_wa_ctx(struct intel_engine_cs *engine);
>  
>  int lrc_alloc(struct intel_context *ce,
>             struct intel_engine_cs *engine);
> +void lrc_latch_wa_22013059131(struct intel_context *ce,
> +                           struct i915_gem_context *ctx);
>  void lrc_reset(struct intel_context *ce);
>  void lrc_fini(struct intel_context *ce);
>  void lrc_destroy(struct kref *kref);
> diff --git a/drivers/gpu/drm/i915/gt/intel_workarounds.c 
> b/drivers/gpu/drm/i915/gt/intel_workarounds.c
> index 24ea5d8d529c..ef6eea3ab597 100644
> --- a/drivers/gpu/drm/i915/gt/intel_workarounds.c
> +++ b/drivers/gpu/drm/i915/gt/intel_workarounds.c
> @@ -2840,7 +2840,11 @@ general_render_compute_wa_init(struct intel_engine_cs 
> *engine, struct i915_wa_li
>       if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) ||
>           IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_A0, STEP_B0) ||
>           IS_DG2(i915)) {
> -             /* Wa_22014226127 */
> +             /*
> +              * Wa_22014226127: Note that this workaround also needs to be
> +              * re-applied in intel_lrc.c when LSC_CHICKEN_BIT_0 is
> +              * re-written for Wa_22013059131.
> +              */
>               wa_mcr_write_or(wal, LSC_CHICKEN_BIT_0, 
> DISABLE_D8_D16_COASLESCE);
>       }
>  
> @@ -2867,10 +2871,6 @@ general_render_compute_wa_init(struct intel_engine_cs 
> *engine, struct i915_wa_li
>                                    MAXREQS_PER_BANK,
>                                    REG_FIELD_PREP(MAXREQS_PER_BANK, 2));
>  
> -             /* Wa_22013059131:dg2 */
> -             wa_mcr_write_or(wal, LSC_CHICKEN_BIT_0,
> -                             FORCE_1_SUB_MESSAGE_PER_FRAGMENT);
> -
>               /*
>                * Wa_22012654132
>                *
> diff --git a/drivers/gpu/drm/i915/i915_drm_client.h 
> b/drivers/gpu/drm/i915/i915_drm_client.h
> index 2e7a50d16a88..684a66bcd71f 100644
> --- a/drivers/gpu/drm/i915/i915_drm_client.h
> +++ b/drivers/gpu/drm/i915/i915_drm_client.h
> @@ -45,8 +45,65 @@ struct i915_drm_client {
>        * @past_runtime: Accumulation of pphwsp runtimes from closed contexts.
>        */
>       atomic64_t past_runtime[I915_LAST_UABI_ENGINE_CLASS + 1];
> +
> +     /**
> +      * @wa_22013059131_owner: Who programs Wa_22013059131 for this client.
> +      *
> +      * One of the I915_WA_22013059131_* values below.  Latched once, on
> +      * the first of either I915_CONTEXT_PARAM_WA_22013059131 being set or
> +      * a context of this client being pinned for the first time
> +      * (intel_context_alloc_state(), which holds ce->pin_mutex and a
> +      * reference on the context), and read-only afterwards.  See
> +      * i915_drm_client_latch_wa_22013059131().
> +      */
> +     atomic_t wa_22013059131_owner;
>  };
>  
> +/*
> + * Wa_22013059131:dg2 - who programs LSC_CHICKEN_BIT_0's
> + * FORCE_1_SUB_MESSAGE_PER_FRAGMENT bit for the contexts of a client.
> + *
> + * LSC_CHICKEN_BIT_0 is shared by the RCS and CCS engines rather than being
> + * part of the saved context image, so it cannot be tracked per context.
> + * GuC only lets RCS and CCS run concurrently when their contexts share an
> + * address space, so tracking this per DRM client (which forces every
> + * address space of that client to agree) is sufficient to avoid contexts
> + * that could actually run at the same time racing over the register.
> + *
> + * I915_WA_22013059131_CLIENT_UNSET must be 0 as the client is 
> zero-initialised.
> + */
> +#define I915_WA_22013059131_CLIENT_UNSET     0
> +#define I915_WA_22013059131_CLIENT_KERNEL    1
> +#define I915_WA_22013059131_CLIENT_USERSPACE 2
> +
> +/**
> + * i915_drm_client_latch_wa_22013059131 - Latch Wa_22013059131 ownership
> + * @client: the DRM client
> + * @want: I915_WA_22013059131_CLIENT_KERNEL or 
> I915_WA_22013059131_CLIENT_USERSPACE
> + *
> + * Claim @want as the owner of Wa_22013059131 for @client, unless an owner
> + * has already been latched.  Callers that must not change the owner pass
> + * the value they rely on and compare it against the return value.
> + *
> + * The value is latched with a single cmpxchg so that no extra locking is
> + * needed.  This matters because the callers run with different locks held:
> + * GEM_CONTEXT_CREATE_EXT_SETPARAM does not hold &proto_context_lock, while
> + * GEM_CONTEXT_SETPARAM does, and intel_context_alloc_state() holds
> + * ce->pin_mutex instead.
> + *
> + * Returns: the owner in effect for @client, never 
> I915_WA_22013059131_CLIENT_UNSET.
> + */
> +static inline int
> +i915_drm_client_latch_wa_22013059131(struct i915_drm_client *client, int 
> want)
> +{
> +     int owner;
> +
> +     owner = atomic_cmpxchg(&client->wa_22013059131_owner,
> +                            I915_WA_22013059131_CLIENT_UNSET, want);
> +
> +     return owner == I915_WA_22013059131_CLIENT_UNSET ? want : owner;
> +}
> +
>  static inline struct i915_drm_client *
>  i915_drm_client_get(struct i915_drm_client *client)
>  {
> diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h
> index 535cb68fdb5c..94553daec4a2 100644
> --- a/include/uapi/drm/i915_drm.h
> +++ b/include/uapi/drm/i915_drm.h
> @@ -2172,6 +2172,31 @@ struct drm_i915_gem_context_param {
>   * Note that this is a debug API not available on production kernel builds.
>   */
>  #define I915_CONTEXT_PARAM_CONTEXT_IMAGE     0xf
> +
> +/*
> + * I915_CONTEXT_PARAM_WA_22013059131:
> + *
> + * Default value 0 means the kernel sets LSC_CHICKEN_BIT_0 bit 15
> + * (FORCE_1_SUB_MESSAGE_PER_FRAGMENT) as part of Wa_22013059131.  Set to 1
> + * to inform the kernel that userspace is handling the SLM contention
> + * workaround itself (e.g. by limiting SLM size), so bit 15 programming is
> + * not needed.  Only 0 and 1 are valid; any other value is rejected with
> + * -EINVAL.
> + *
> + * LSC_CHICKEN_BIT_0 is shared by the RCS and CCS engines, so this setting
> + * takes effect for every context of the calling DRM client (the whole file
> + * descriptor), not just the context the ioctl is issued on.  It is latched
> + * once, by whichever comes first: an explicit call to this parameter, or
> + * any context of the client being submitted for the first time.  A later
> + * request for the opposite value fails with -EINVAL.  Userspace that wants
> + * to opt out must therefore do so before submitting any work on this fd,
> + * including via the default context (id 0) created on open().
> + *
> + * Note: LSC_CHICKEN_BIT_0_UDW MAXREQS_PER_BANK (bits 39:37) is the
> + * other part of Wa_22013059131 and remains unconditionally programmed
> + * by the kernel regardless of this setting.  DG2-G11 only.
> + */
> +#define I915_CONTEXT_PARAM_WA_22013059131    0x10
>  /* Must be kept compact -- no holes and well documented */
>  
>       /** @value: Context parameter value to be set or queried */
> -- 
> 2.43.0
> 

-- 
Matt Roper
Graphics Software Engineer
Linux GPU Platform Enablement
Intel Corporation

Reply via email to