On 2025. december 18., csütörtök 9:58:45 középső államokbeli zónaidő Alex
Deucher wrote:
> On Thu, Dec 18, 2025 at 12:21 AM Timur Kristóf <[email protected]>
wrote:
> > On 2025. december 15., hétfő 10:07:11 középső államokbeli zónaidő Alex
> > Deucher>
> > wrote:
> > > GFX ring resets work differently on pre-GFX10 hardware since
> > > there is no MQD managed by the scheduler.
> > > For ring reset, you need issue the reset via CP_VMID_RESET
> > > via KIQ or MMIO and submit the following to the gfx ring to
> > > complete the reset:
> > > 1. EOP packet with EXEC bit set
> > > 2. WAIT_REG_MEM to wait for the fence
> > > 3. Clear CP_VMID_RESET to 0
> > > 4. EVENT_WRITE ENABLE_LEGACY_PIPELINE
> > > 5. EOP packet with EXEC bit set
> > > 6. WAIT_REG_MEM to wait for the fence
> > > Once those commands have completed the reset should
> > > be complete and the ring can accept new packets.
> > >
> > > However, because we have a pipeline sync between jobs,
> > > the PFP is waiting on the fence from the bad job to signal so
> > > it can't process any of the packets in the reset sequence
> > > until that pipeline sync clears. To unblock the PFP, we
> > > use the KIQ to signal the fence after we reset the queue.
> > >
> > > Signed-off-by: Alex Deucher <[email protected]>
> > > ---
> > >
> > > drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c | 104 +++++++++++++++++++++++++-
> > > 1 file changed, 101 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
> > > b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index
> > > bb1465a98c7ca..9b7073650315e
> > > 100644
> > > --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
> > > +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
> > > @@ -2410,8 +2410,10 @@ static int gfx_v9_0_sw_init(struct
> > > amdgpu_ip_block
> > > *ip_block) amdgpu_get_soft_full_reset_mask(&adev->gfx.gfx_ring[0]);
> > >
> > > adev->gfx.compute_supported_reset =
> > >
> > > amdgpu_get_soft_full_reset_mask(&adev-
> > >
> > >gfx.compute_ring[0]);
> > >
> > > - if (!amdgpu_sriov_vf(adev) && !adev->debug_disable_gpu_ring_reset)
> > > + if (!amdgpu_sriov_vf(adev) && !adev->debug_disable_gpu_ring_reset)
> >
> > {
> >
> > > adev->gfx.compute_supported_reset |=
> >
> > AMDGPU_RESET_TYPE_PER_QUEUE;
> >
> > > + adev->gfx.gfx_supported_reset |=
> >
> > AMDGPU_RESET_TYPE_PER_QUEUE;
> >
> > > + }
> > >
> > > r = amdgpu_gfx_kiq_init(adev, GFX9_MEC_HPD_SIZE, 0);
> > > if (r) {
> > >
> > > @@ -7163,6 +7165,103 @@ static void gfx_v9_ring_insert_nop(struct
> > > amdgpu_ring *ring, uint32_t num_nop) amdgpu_ring_insert_nop(ring,
> > > num_nop -
> > > 1);
> > >
> > > }
> > >
> > > +static void gfx_v9_0_ring_emit_wreg_me(struct amdgpu_ring *ring,
> > > + uint32_t reg,
> > > + uint32_t val)
> > > +{
> > > + uint32_t cmd = 0;
> > > +
> > > + switch (ring->funcs->type) {
> > > + case AMDGPU_RING_TYPE_KIQ:
> > > + cmd = (1 << 16); /* no inc addr */
> >
> > What do you mean by "inc addr" in this context?
>
> It's part of the packet. bit 16 controls whether the address is
> incremented or not. This function is basically the same as
> gfx_v9_0_ring_emit_wreg(), but uses the ME to do the wait rather than
> the PFP. I could have alternatively added a new parameter to
> gfx_v9_0_ring_emit_wreg() to select between PFP and ME.
Thanks. I was just not familiar with this field of the packet.
>
> > > + break;
> > > + default:
> > > + cmd = WR_CONFIRM;
> > > + break;
> > > + }
> > > + amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
> > > + amdgpu_ring_write(ring, cmd);
> > > + amdgpu_ring_write(ring, reg);
> > > + amdgpu_ring_write(ring, 0);
> > > + amdgpu_ring_write(ring, val);
> > > +}
> > > +
> > > +static void gfx_v9_0_ring_emit_event_write(struct amdgpu_ring *ring,
> > > + uint32_t event_type,
> > > + uint32_t
> >
> > event_index)
> >
> > > +{
> > > + amdgpu_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE, 0));
> > > + amdgpu_ring_write(ring, EVENT_TYPE(event_type) |
> > > + EVENT_INDEX(event_index));
> > > +}
> > > +
> > > +static int gfx_v9_0_reset_kgq(struct amdgpu_ring *ring,
> > > + unsigned int vmid,
> > > + struct amdgpu_fence *timedout_fence)
> > > +{
> > > + struct amdgpu_device *adev = ring->adev;
> > > + struct amdgpu_kiq *kiq = &adev->gfx.kiq[0];
> > > + struct amdgpu_ring *kiq_ring = &kiq->ring;
> > > + unsigned long flags;
> > > + u32 tmp;
> > > + int r;
> > > +
> > > + amdgpu_ring_reset_helper_begin(ring, timedout_fence);
> > > +
> > > + spin_lock_irqsave(&kiq->ring_lock, flags);
> > > +
> > > + if (amdgpu_ring_alloc(kiq_ring, 5 + 5)) {
> > > + spin_unlock_irqrestore(&kiq->ring_lock, flags);
> > > + return -ENOMEM;
> > > + }
> > > +
> > > + /* send the reset - 5 */
> > > + tmp = REG_SET_FIELD(0, CP_VMID_RESET, RESET_REQUEST, 1 << vmid);
> > > + gfx_v9_0_ring_emit_wreg(kiq_ring,
> > > + SOC15_REG_OFFSET(GC, 0,
> >
> > mmCP_VMID_RESET), tmp);
> >
> > > + /* emit the fence to clear the pipeline sync - 5 */
> > > + gfx_v9_0_ring_emit_fence_kiq(kiq_ring, ring->fence_drv.gpu_addr,
> > > + timedout_fence->base.seqno,
> >
> > 0);
> >
> > As far as I see, this isn't going to work when sched_hw_submission > 2 and
> > there are more than two jobs (from various different userspace processes)
> > emitted in the ring.
> >
> > I can think of two possible solutons:
> > - Emit each fence individually, with a short delay in between to give a
> > chance to the GFX ring to catch up with the KIQ.
> > - Change the wait_reg_mem command used for the pipeline sync to allow
> > greater than equal instead of just equal. Then it's enough to signal just
> > the last fence on the KIQ ring.
>
> That won't work. The signalling patch is asynchronous so if we signal
> additional fences other than the bad one, those jobs will end up being
> seen as successfully completed.
Yeah, I already realized that after I sent the email, and sent a follow-up
email with a different suggestion, that is to change the pipeline sync to not
rely on the fence of the previous submission. eg. the pipeline sync could emit
a separate fence, or even simpler, an ACQUIRE_MEM (gfx9+) / SURFACE_SYNC
(gfx6-8). Then this becomes a non-issue.
What do you think about that?
> That said, there is a change coming
> for the firmware to fix this.
That's very nice to hear that AMD is still willing to fix firmware for old
chips
like Vega. However,
- Would be nice to solve the problem for users who are still running old
firmware, as it doesn't seem too difficult to do so.
- AFAIK gfx7-8 also use the same queue reset mechanism so I think they may be
susceptible to the same issue (and I don't think anyone's gonna release new FW
for those).
> I'd suggest we just limit the queue
> depth to 2 until the new firmware is available.
Last I heard of it, the SteamOS kernel set it to 4 due to "CPU bubbles" that
they observed with the default setting of 2 on the Steam Deck. I think on the
long term we should seriously consider increasing the default in upstream as
well.
>
> > > + amdgpu_ring_commit(kiq_ring);
> > > + r = amdgpu_ring_test_ring(kiq_ring);
> > > + spin_unlock_irqrestore(&kiq->ring_lock, flags);
> > > + if (r)
> > > + return r;
> > > +
> > > + if (amdgpu_ring_alloc(ring, 8 + 7 + 5 + 2 + 8 + 7))
> > > + return -ENOMEM;
> > > + /* emit the fence to finish the reset - 8 */
> > > + ring->trail_seq++;
> > > + gfx_v9_0_ring_emit_fence(ring, ring->trail_fence_gpu_addr,
> > > + ring->trail_seq,
> >
> > AMDGPU_FENCE_FLAG_EXEC);
> >
> > > + /* wait for the fence - 7 */
> > > + gfx_v9_0_wait_reg_mem(ring, 0, 1, 0,
> > > + lower_32_bits(ring-
> > >
> > >trail_fence_gpu_addr),
> > >
> > > + upper_32_bits(ring-
> > >
> > >trail_fence_gpu_addr),
> > >
> > > + ring->trail_seq, 0xffffffff, 4);
> > > + /* clear mmCP_VMID_RESET - 5 */
> > > + gfx_v9_0_ring_emit_wreg_me(ring,
> > > + SOC15_REG_OFFSET(GC, 0,
> >
> > mmCP_VMID_RESET), 0);
> >
> > > + /* event write ENABLE_LEGACY_PIPELINE - 2 */
> > > + gfx_v9_0_ring_emit_event_write(ring, ENABLE_LEGACY_PIPELINE, 0);
> > > + /* emit a regular fence - 8 */
> > > + ring->trail_seq++;
> > > + gfx_v9_0_ring_emit_fence(ring, ring->trail_fence_gpu_addr,
> > > + ring->trail_seq,
> >
> > AMDGPU_FENCE_FLAG_EXEC);
> >
> > > + /* wait for the fence - 7 */
> > > + gfx_v9_0_wait_reg_mem(ring, 1, 1, 0,
> > > + lower_32_bits(ring-
> > >
> > >trail_fence_gpu_addr),
> > >
> > > + upper_32_bits(ring-
> > >
> > >trail_fence_gpu_addr),
> > >
> > > + ring->trail_seq, 0xffffffff, 4);
> >
> > Why is it necessary to emit (and wait for) a regular fence here?
> > I'm not against it, just curious why it's needed.
>
> It's part of the recovery sequence to make sure the
> ENABLE_LEGACY_PIPELINE event has completed successfully.
Ah, okay. I didn't realize that we needed fences after EVENT_WRITE.
>
> > > + amdgpu_ring_commit(ring);
> > > + /* wait for the commands to complete */
> > > + r = amdgpu_ring_test_ring(ring);
> > > + if (r)
> > > + return r;
> > > +
> > > + return amdgpu_ring_reset_helper_end(ring, timedout_fence);
> > > +}
> > > +
> > >
> > > static int gfx_v9_0_reset_kcq(struct amdgpu_ring *ring,
> > >
> > > unsigned int vmid,
> > > struct amdgpu_fence *timedout_fence)
> > >
> > > @@ -7441,9 +7540,9 @@ static const struct amdgpu_ring_funcs
> > > gfx_v9_0_ring_funcs_gfx = { .emit_wreg = gfx_v9_0_ring_emit_wreg,
> > >
> > > .emit_reg_wait = gfx_v9_0_ring_emit_reg_wait,
> > > .emit_reg_write_reg_wait = gfx_v9_0_ring_emit_reg_write_reg_wait,
> > >
> > > - .soft_recovery = gfx_v9_0_ring_soft_recovery,
> >
> > Can you please split removing the soft recovery into a separate patch?
> >
> > Can we talk about removing the soft recovery? For the other chips where it
> > has already been removed, it is percieved by users as a regression.
>
> Queue reset is superset of soft recovery. There's no need for soft
> recovery when queue reset is available.
>
I'm not arguing for or against either approach, just relaying how the change
is percieved by users who tell their experience.
From a user's perspective:
- queue reset just kills the entire job and the offending process, ie. the user
sees the game crash
- soft recovery sometimes allows the guilty process to move on intact, ie. the
user would see a "hitch" in the game but then it would keep working
>
> > > .emit_mem_sync = gfx_v9_0_emit_mem_sync,
> > > .emit_cleaner_shader = gfx_v9_0_ring_emit_cleaner_shader,
> > >
> > > + .reset = gfx_v9_0_reset_kgq,
> > >
> > > .begin_use = amdgpu_gfx_enforce_isolation_ring_begin_use,
> > > .end_use = amdgpu_gfx_enforce_isolation_ring_end_use,
> > >
> > > };
> > >
> > > @@ -7542,7 +7641,6 @@ static const struct amdgpu_ring_funcs
> > > gfx_v9_0_ring_funcs_compute = { .emit_wreg = gfx_v9_0_ring_emit_wreg,
> > >
> > > .emit_reg_wait = gfx_v9_0_ring_emit_reg_wait,
> > > .emit_reg_write_reg_wait = gfx_v9_0_ring_emit_reg_write_reg_wait,
> > >
> > > - .soft_recovery = gfx_v9_0_ring_soft_recovery,
> > >
> > > .emit_mem_sync = gfx_v9_0_emit_mem_sync,
> > > .emit_wave_limit = gfx_v9_0_emit_wave_limit,
> > > .reset = gfx_v9_0_reset_kcq,