RE: [PATCH v5 3/4] drm/xe/forcewake: flush delayed release at power boundaries

"Yao, Jia" <[email protected]>
Newsgroups org.freedesktop.lists.intel-xe
Message-ID <PH8PR11MB804051F88CE2CEA4242D8B26F4A42@PH8PR11MB8040.namprd11.prod.outlook.com>
Reviewed-by: Jia Yao <[email protected]>

> -----Original Message-----
> From: Bai, Zongyao <[email protected]>
> Sent: Wednesday, August 12, 2026 5:07 PM
> To: [email protected]
> Cc: Yao, Jia <[email protected]>; Brost, Matthew
> <[email protected]>; Bai, Zongyao <[email protected]>
> Subject: [PATCH v5 3/4] drm/xe/forcewake: flush delayed release at power
> boundaries
> 
> Add a synchronous forcewake flush that cancels per-domain timers and settles
> pending sleep acknowledgments. Provide a scoped cleanup class so the final
> put is always followed by the flush before the caller continues toward power-
> off.
> 
> Use it for system suspend, runtime suspend, and shutdown. After DRM
> unplug has quiesced engine-cycle readers, flush every GT during remove and
> post-registration probe unwind so delayed callbacks cannot cross teardown or
> a later device FLR.
> 
> Suggested-by: Matthew Brost <[email protected]>
> Assisted-by: GitHub-Copilot:gpt-5.6-sol
> Signed-off-by: Zongyao Bai <[email protected]>
> ---
>  drivers/gpu/drm/xe/xe_device.c           | 11 ++++++
>  drivers/gpu/drm/xe/xe_force_wake.c       | 49 ++++++++++++++++++++++++
>  drivers/gpu/drm/xe/xe_force_wake.h       | 23 +++++++++++
>  drivers/gpu/drm/xe/xe_force_wake_types.h | 42 ++++++++++++++++++++
>  drivers/gpu/drm/xe/xe_gt.c               |  7 ++--
>  5 files changed, 129 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
> index 21d02809c3a4..d69fd8236b0f 100644
> --- a/drivers/gpu/drm/xe/xe_device.c
> +++ b/drivers/gpu/drm/xe/xe_device.c
> @@ -711,6 +711,15 @@ static void xe_device_sanitize(void *arg)
>  		xe_gt_sanitize(gt);
>  }
> 
> +static void xe_device_force_wake_flush(struct xe_device *xe) {
> +	struct xe_gt *gt;
> +	u8 id;
> +
> +	for_each_gt(gt, xe, id)
> +		xe_force_wake_flush(gt_to_fw(gt));
> +}
> +
>  static int xe_set_dma_info(struct xe_device *xe)  {
>  	unsigned int mask_size = xe->info.dma_mask_size; @@ -1169,6
> +1178,7 @@ int xe_device_probe(struct xe_device *xe)
>  err_unregister_display:
>  	xe_display_unregister(xe);
>  	drm_dev_unplug(&xe->drm);
> +	xe_device_force_wake_flush(xe);
> 
>  	return err;
>  }
> @@ -1178,6 +1188,7 @@ void xe_device_remove(struct xe_device *xe)
>  	xe_display_unregister(xe);
> 
>  	drm_dev_unplug(&xe->drm);
> +	xe_device_force_wake_flush(xe);
> 
>  	xe_bo_pci_dev_remove_all(xe);
>  }
> diff --git a/drivers/gpu/drm/xe/xe_force_wake.c
> b/drivers/gpu/drm/xe/xe_force_wake.c
> index 3f5013c7424c..ef43899e1f7d 100644
> --- a/drivers/gpu/drm/xe/xe_force_wake.c
> +++ b/drivers/gpu/drm/xe/xe_force_wake.c
> @@ -391,6 +391,55 @@ void xe_force_wake_put_delay(struct
> xe_force_wake *fw, unsigned int fw_ref)
>  	__xe_force_wake_put(fw, fw_ref, true);  }
> 
> +/**
> + * xe_force_wake_flush - Flush delayed forcewake releases
> + * @fw: Pointer to the force wake structure
> + *
> + * Synchronously cancels delayed-release timers and completes pending
> +sleep
> + * requests and ACK waits. This must be called after the final
> +forcewake put
> + * and before power-off because the timer callback accesses MMIO.
> + *
> + * The caller must ensure no forcewake reference outlives this call and
> + * serialize against new delayed releases or final puts that consume a
> + * recorded delayed-release request. Timers are canceled before taking
> + * fw->lock because their callbacks acquire the same lock.
> + */
> +void xe_force_wake_flush(struct xe_force_wake *fw) {
> +	struct xe_gt *gt = fw->gt;
> +	struct xe_force_wake_domain *domain;
> +	unsigned int tmp, sleep, ack_fail = 0;
> +	unsigned long flags;
> +
> +	for_each_fw_domain(domain, fw, tmp)
> +		hrtimer_cancel(&domain->sleep_timer);
> +
> +	spin_lock_irqsave(&fw->lock, flags);
> +	sleep = fw->sleep_ack_pending_domains;
> +	for_each_fw_domain(domain, fw, tmp) {
> +		assert_domain_state(fw, domain);
> +		domain->delayed_release_requested = false;
> +		if ((fw->delayed_release_domains & BIT(domain->id)) &&
> +		    !domain->ref) {
> +			fw->delayed_release_domains &= ~BIT(domain->id);
> +			sleep |= BIT(domain->id);
> +			domain_sleep(gt, domain);
> +		}
> +		assert_domain_state(fw, domain);
> +	}
> +	for_each_fw_domain_masked(domain, sleep, fw, tmp) {
> +		fw->sleep_ack_pending_domains &= ~BIT(domain->id);
> +		if (domain_sleep_wait(gt, domain) == 0)
> +			fw->awake_domains &= ~BIT(domain->id);
> +		else
> +			ack_fail |= BIT(domain->id);
> +	}
> +	spin_unlock_irqrestore(&fw->lock, flags);
> +
> +	xe_gt_WARN(gt, ack_fail, "Forcewake domain%s %#x failed to
> acknowledge sleep request\n",
> +		   str_plural(hweight_long(ack_fail)), ack_fail); }
> +
>  const char *xe_force_wake_domain_to_str(enum xe_force_wake_domain_id
> id)  {
>  	switch (id) {
> diff --git a/drivers/gpu/drm/xe/xe_force_wake.h
> b/drivers/gpu/drm/xe/xe_force_wake.h
> index 8da675d8db0b..ac37a0177d7b 100644
> --- a/drivers/gpu/drm/xe/xe_force_wake.h
> +++ b/drivers/gpu/drm/xe/xe_force_wake.h
> @@ -19,6 +19,23 @@ unsigned int __must_check xe_force_wake_get(struct
> xe_force_wake *fw,
>  					    enum xe_force_wake_domains
> domains);  void xe_force_wake_put(struct xe_force_wake *fw, unsigned int
> fw_ref);  void xe_force_wake_put_delay(struct xe_force_wake *fw, unsigned
> int fw_ref);
> +void xe_force_wake_flush(struct xe_force_wake *fw);
> +
> +/**
> + * xe_force_wake_put_and_flush - Release forcewake and flush deferred
> +sleep
> + * @fw: Pointer to the force wake structure
> + * @fw_ref: return of xe_force_wake_get()
> + *
> + * Convenience helper for paths that must complete or cancel
> +deferred-release
> + * work before powering off the GT. Sleep acknowledgment failures are
> +reported
> + * by xe_force_wake_flush().
> + */
> +static inline void
> +xe_force_wake_put_and_flush(struct xe_force_wake *fw, unsigned int
> +fw_ref) {
> +	xe_force_wake_put(fw, fw_ref);
> +	xe_force_wake_flush(fw);
> +}
> 
>  const char *xe_force_wake_domain_to_str(enum xe_force_wake_domain_id
> id);
> 
> @@ -93,6 +110,12 @@ DEFINE_CLASS(xe_force_wake, struct
> xe_force_wake_ref,
>  	     xe_force_wake_constructor(fw, domains),
>  	     struct xe_force_wake *fw, unsigned int domains);
> 
> +/* Release forcewake and flush delayed-release work on scope exit. */
> +DEFINE_CLASS(xe_force_wake_flush, struct xe_force_wake_ref,
> +	     xe_force_wake_put_and_flush(_T.fw, _T.domains),
> +	     xe_force_wake_constructor(fw, domains),
> +	     struct xe_force_wake *fw, unsigned int domains);
> +
>  /*
>   * Scoped helper for the forcewake class, using the same trick as
> scoped_guard()
>   * to bind the lifetime to the next statement/block.
> diff --git a/drivers/gpu/drm/xe/xe_force_wake_types.h
> b/drivers/gpu/drm/xe/xe_force_wake_types.h
> index 28c6f6dc87a3..3f20f91b3a70 100644
> --- a/drivers/gpu/drm/xe/xe_force_wake_types.h
> +++ b/drivers/gpu/drm/xe/xe_force_wake_types.h
> @@ -107,6 +107,48 @@ struct xe_force_wake_domain {
>   * Currently only used for GT power domains (where the term "forcewake" is
> used
>   * in the hardware documentation), although the interface could be extended
> to
>   * power wells in other parts of the hardware in the future.
> + *
> + * Delayed-release state flow, protected by @lock:
> + *
> + *                         final delayed put
> + *        +------------+ ----------------------> +---------------+
> + *        | REFERENCED |                         | DELAYED HOLD  |
> + *        | ref > 0    | <---------------------- | ref == 0      |
> + *        +------+-----+   get: cancel and reuse +-------+-------+
> + *               |                                      |
> + *               |                                      | timer expires
> + *      final immediate put                             v
> + *               |                              +---------------+
> + *               |                              | SLEEP SENT    |
> + *               |                              | ref == 0      |
> + *               |                              +-------+-------+
> + *               |                                      |
> + *               v                                      v
> + *        +-------------+ <------------------ flush: wait for ACK
> + *        | ASLEEP      |
> + *        | ref == 0    | <--- flush delayed hold
> + *        +-------------+
> + *
> + * A get from SLEEP SENT first reconciles the sleep ACK, then wakes the
> + * domain and returns it to REFERENCED. A delayed put that is not the
> + final
> + * put sets domain->delayed_release_requested. The final put consumes
> + that
> + * request and transitions the domain to DELAYED HOLD.
> + *
> + * Successful-path state representation at stable boundaries:
> + *
> + * REFERENCED:
> + *   domain->ref > 0; domain->delayed_release_requested may be set.
> + *
> + * DELAYED HOLD:
> + *   domain->ref == 0; @delayed_release_domains contains the domain.
> + *
> + * SLEEP SENT:
> + *   domain->ref == 0; @sleep_ack_pending_domains contains the domain
> and
> + *   @awake_domains does not.
> + *
> + * ASLEEP:
> + *   domain->ref == 0; neither delayed-release state mask nor
> @awake_domains
> + *   contains the domain.
>   */
>  struct xe_force_wake {
>  	/** @gt: back pointers to GT */
> diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c index
> efe425dbcc85..8e7c5f82d362 100644
> --- a/drivers/gpu/drm/xe/xe_gt.c
> +++ b/drivers/gpu/drm/xe/xe_gt.c
> @@ -1013,7 +1013,7 @@ int xe_gt_suspend(struct xe_gt *gt)
>  	xe_gt_dbg(gt, "suspending\n");
>  	xe_gt_sanitize(gt);
> 
> -	CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
> +	CLASS(xe_force_wake_flush, fw_ref)(gt_to_fw(gt),
> XE_FORCEWAKE_ALL);
>  	if (!xe_force_wake_ref_has_domain(fw_ref.domains,
> XE_FORCEWAKE_ALL)) {
>  		xe_gt_err(gt, "suspend failed (%pe)\n", ERR_PTR(-
> ETIMEDOUT));
>  		return -ETIMEDOUT;
> @@ -1036,7 +1036,8 @@ int xe_gt_suspend(struct xe_gt *gt)
> 
>  void xe_gt_shutdown(struct xe_gt *gt)
>  {
> -	CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
> +	CLASS(xe_force_wake_flush, fw_ref)(gt_to_fw(gt),
> XE_FORCEWAKE_ALL);
> +
>  	do_gt_reset(gt);
>  }
> 
> @@ -1092,7 +1093,7 @@ int xe_gt_runtime_suspend(struct xe_gt *gt)  {
>  	xe_gt_dbg(gt, "runtime suspending\n");
> 
> -	CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
> +	CLASS(xe_force_wake_flush, fw_ref)(gt_to_fw(gt),
> XE_FORCEWAKE_ALL);
>  	if (!xe_force_wake_ref_has_domain(fw_ref.domains,
> XE_FORCEWAKE_ALL)) {
>  		xe_gt_err(gt, "runtime suspend failed (%pe)\n", ERR_PTR(-
> ETIMEDOUT));
>  		return -ETIMEDOUT;
> --
> 2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.