Intel-XE Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Rodrigo Vivi <rodrigo.vivi@intel.com>
To: <batarolandkrisztian@gmail.com>
Cc: <intel-xe@lists.freedesktop.org>, <karthik.poosa@intel.com>,
	<matthew.brost@intel.com>, <raag.jadav@intel.com>,
	<thomas.hellstrom@linux.intel.com>
Subject: Re: [PATCH RFC] drm/xe/hwmon: refresh B70 temperature inputs after GT idle
Date: Sun, 27 Sep 2026 20:06:23 -0400	[thread overview]
Message-ID: <armvfxjttsjTVWk-@intel.com> (raw)
In-Reply-To: <179041811564.526373.13204522833274380927.b70-hwmon-rfc@gmail.com>

On Sat, Sep 26, 2026 at 12:21:55PM +0200, Bata Roland Krisztián via B4 Relay wrote:
> From: Bata Roland Krisztián <batarolandkrisztian@gmail.com>
> 
> Two Arc Pro B70 devices (8086:e223) retain their package temperature
> after compute workloads despite both GTs returning to C6. A device
> runtime PM reference does not refresh the sample while power/control
> is already on.
> 
> With GuC 70.72.1 on Fedora kernel 7.1.10, a 60-second OpenCL workload
> followed by 45 seconds of idle left package temperature at 55 C on both
> cards. Acquiring all GT forcewake references still returned 55 C in
> the first read, about 57 us after open completed. At about 1053 us the
> reported value became 34 C on both cards. A brief GT-only pulse from
> reading cur_freq did not refresh the package value.
> 
> As an RFC, acquire forcewake references for the temperature input read,
> allow time for a new sample, then release the references on both the
> success and partial-acquisition failure paths. Limit the experiment to
> the measured PCI ID and leave labels, limits and other hwmon types on
> their existing paths.
> 
> The 2 ms interval and use of all domains are empirical, not a hardware
> specification guarantee. This needs review of the sensor conversion
> time, minimum wake domains and alternatives such as a PCODE refresh or
> readiness indication before it can become a production fix.
> 
> Compile-tested xe_hwmon.o on current drm-tip with W=1 and Xe display both
> disabled and enabled. The wake/wait/read sequence was measured through
> debugfs on two B70s; the modified kernel has not been boot-tested.
> 
> Link: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7805
> Assisted-by: OpenAI:gpt-6-astra
> Signed-off-by: Bata Roland Krisztián <batarolandkrisztian@gmail.com>
> ---
> RFC: proposed direction for issue 7805, not a claim of a production-ready fix.

The fundamental question here is, do we want to burn power in order to check
for the power?
If so, is grabbing the debugfs/forcewake_all an option before reading this?

But if we decide that this is what we want, we need a generic solution,
not only for this part...

> 
> Hi Xe maintainers,
> 
> I am following up on my B70 stale-temperature report with a smaller
> reproduction, timing measurements, and a prototype read-side change.
> 
> Questions before turning this into v1:
> 
> 1. Is there a PCODE operation or freshness/conversion-ready indication that
>    should be used instead of a fixed settling interval? The first MMIO read
>    after successful forcewake still returned the old temperature.
> 2. Which wake domains are actually required? A GT0 hw_engines read, which
>    holds all domains for that GT while dumping registers, also refreshed
>    package/mctrl/PCIe readings. The conservative prototype mirrors the
>    device forcewake_all sequence used for the controlled timing experiment.
> 3. Should a refresh cover/cache several temperature channels together? The
>    prototype wakes per input read. The power measurements below are for one
>    batch per device every two seconds, not for reading every hwmon channel
>    at high frequency and not measurements of this compiled patch.
> 4. Is this a known firmware issue with a preferred fix? Should the eventual
>    driver handling apply to other Battlemage IDs? This RFC only opts in the
>    tested 8086:e223 devices.
> 
> Hardware and reproduction
> -------------------------
> Two Intel Arc Pro B70s, PCI ID 8086:e223, on an ASRock X870E Taichi / Ryzen
> 5 9600X. Fedora CoreOS 44.20260829.3.1, kernel 7.1.10-200.fc44.x86_64,
> GuC 70.72.1, HuC 8.2.10. power/control=on was kept throughout; it is an
> existing workaround for noisy PCI runtime suspend/resume cycling.
> 
> A bounded 60-second OpenCL workload used a private 1 MiB output buffer per
> device, followed by 45 seconds without GPU submissions. Both GTs reached
> C6, actual frequency was zero, and idle residency continued advancing.
> Package readings stayed at 55 C on both cards. A short GT0 cur_freq read
> (itself a GT-only forcewake pulse) did not correct the package reading.
> 
> Opening forcewake_all, then reading temperatures while holding the fd:
> 
>                     B70 A                  B70 B
> before              55 C                   55 C
> first read          55 C at 57.769 us       55 C at 56.119 us
> first correction    34 C at 1053.529 us     34 C at 1053.039 us
> 
> Times are measured from completion of open, not the initial wake request.
> The 2, 5, 10 ms and later samples were also refreshed. These two observations
> are not a guaranteed maximum sensor-conversion latency.
> 
> In a separate 30-second observation / short-pulse / observation sequence,
> 16 two-millisecond holds at two-second intervals returned approximately
> 24 C and 22-23 C. The complete open/wait/read/close scope was about 3.06 ms
> and 3.07 ms median. GT idle residency during the pulse phase exceeded 99.7%.
> 
> Energy-counter-derived card power, W:
>                     before       pulses       after
> B70 A               5.30         5.51         4.87
> B70 B               4.31         4.48         4.39
> 
> These are short observations with normal background system activity; they
> are not a general power-regression benchmark. Holding all domains for two
> seconds during the timing experiment cost about 81 W per card at the
> retained 2800 MHz request and caused self-heating. A permanent forcewake is
> therefore unsuitable. References were released and both GTs returned to C6
> after every completed experiment. No reset, rebind or PM-policy change was
> used for these measurements.
> 
> Source/build checks
> -------------------
> The BMG temperature read path is unchanged between v7.1.10, v7.2.5 and the
> current drm-tip version examined here. The newer Fedora 20260910 firmware
> collection contains byte-identical BMG GuC/HuC files to 20260810.
> 
> Base: drm-tip 666d2f09d9045fc8f72cc1f71528a04acbaf5229
>       2026-09-26 04:44:20 UTC integration manifest
> Checked: checkpatch --strict, zero errors/warnings/checks for the patch;
>          xe_hwmon.o compile with W=1, Xe display disabled and enabled.
> Not yet tested: booted patched kernel, IGT/CI, suspend/resume, hardware
>                acquisition-error paths, other GPU variants, high-rate
>                multi-channel reads.
> 
> The attached change is deliberately an RFC. I would appreciate guidance on
> the correct hardware handshake and domain scope, and can test a revised
> approach on these two cards.
> 
> A later smoke test of the standalone reproducer below also changed a
> previously low reading upward (26 to 44 C on one card). I therefore do not
> use an unchanged/high-value heuristic to declare a value stale, or replace
> readings with an inferred idle temperature. The observations concern the
> reported values; independent physical thermometry was not performed.
> 
> Standalone reproduction aid
> ---------------------------
> After a GPU workload and idle interval, save this as repro.py and run:
>   sudo python3 repro.py 0000:03:00.0 0000:08:00.0
> Use your cards' PCI addresses. This script itself starts no workload.
> 
> #!/usr/bin/env python3
> # SPDX-License-Identifier: MIT
> """Measure B70 temperature refresh after forcewake; run after workload/idle.
> 
> No workload, PM-policy/frequency write, reset or driver rebind is performed.
> Only the requested 8086:e223 Xe devices are read. Each wake lasts about 10 ms;
> readings are printed after the descriptor has been closed.
> """
> import argparse
> import json
> import os
> from pathlib import Path
> import re
> import signal
> import time
> 
> 
> def text(path):
>     return path.read_text().strip()
> 
> 
> def card(bdf, debugfs):
>     if not re.fullmatch(r"[0-9a-f]{4}:[0-9a-f]{2}:[0-9a-f]{2}\.[0-7]", bdf):
>         raise ValueError("Expected a full lowercase PCI address")
>     pci = Path("/sys/bus/pci/devices") / bdf
>     if (text(pci / "vendor"), text(pci / "device")) != ("0x8086", "0xe223"):
>         raise ValueError(f"Not the tested B70 PCI ID: {bdf}")
>     if (pci / "driver").resolve() != Path("/sys/bus/pci/drivers/xe"):
>         raise ValueError(f"Not bound to xe: {bdf}")
>     hw = list((pci / "hwmon").glob("hwmon*"))
>     if len(hw) != 1 or text(hw[0] / "name") != "xe":
>         raise ValueError(f"Ambiguous hwmon: {bdf}")
>     labels = {text(p): p.with_name(p.name.replace("_label", "_input"))
>               for p in hw[0].glob("temp*_label")}
>     if "pkg" not in labels:
>         raise ValueError(f"Missing package sensor: {bdf}")
>     paths = {label: labels[label] for label in ("pkg", "vram", "mctrl", "pcie")
>              if label in labels}
>     return bdf, pci, paths, debugfs / bdf / "forcewake_all"
> 
> 
> def snapshot(pci, paths):
>     start = time.monotonic_ns()
>     values = {label: int(text(path)) for label, path in paths.items()}
>     return {"temp_read_start_ns": start, "temp_read_end_ns": time.monotonic_ns(),
>             "millidegrees": values,
>             "gt_idle": [text(pci / "tile0" / f"gt{n}" / "gtidle/idle_status")
>                         for n in (0, 1)]}
> 
> 
> def interrupted(signum, _frame):
>     raise SystemExit(f"Interrupted by signal {signum}; descriptors will close")
> 
> 
> def main():
>     parser = argparse.ArgumentParser(description=__doc__)
>     parser.add_argument("--debugfs-root", type=Path, default=Path("/sys/kernel/debug/dri"))
>     parser.add_argument("devices", nargs="+")
>     args = parser.parse_args()
>     if os.geteuid() != 0:
>         parser.error("Run with sudo for debugfs access")
>     if len(args.devices) > 8 or len(set(args.devices)) != len(args.devices):
>         parser.error("Select at most eight distinct devices")
>     cards = [card(bdf, args.debugfs_root) for bdf in args.devices]
>     signal.signal(signal.SIGTERM, interrupted)
>     signal.signal(signal.SIGALRM, interrupted)
>     signal.alarm(30)
>     for bdf, pci, paths, wake in cards:
>         before = snapshot(pci, paths)
>         if before["gt_idle"] != ["gt-c6", "gt-c6"]:
>             print(json.dumps({"bdf": bdf, "skipped": "GT is active", "before": before}))
>             continue
>         samples = []
>         opened_at = time.monotonic_ns()
>         with wake.open("rb"):
>             open_returned_at = time.monotonic_ns()
>             for target_us in (0, 1000, 2000, 5000, 10000):
>                 remaining_ns = open_returned_at + target_us * 1000 - time.monotonic_ns()
>                 if remaining_ns > 0:
>                     time.sleep(remaining_ns / 1e9)
>                 value = snapshot(pci, paths)
>                 samples.append({"target_us": target_us,
>                                 "since_open_us": (value["temp_read_start_ns"] - open_returned_at) / 1000,
>                                 **value})
>         closed_at = time.monotonic_ns()
>         time.sleep(1)
>         print(json.dumps({"bdf": bdf, "kernel": os.uname().release,
>                           "power_control": text(pci / "power/control"),
>                           "before": before, "samples": samples,
>                           "open_us": (open_returned_at - opened_at) / 1000,
>                           "total_scope_us": (closed_at - opened_at) / 1000,
>                           "after_close": snapshot(pci, paths)}), flush=True)
> 
> 
> if __name__ == "__main__":
>     main()
> 
>  drivers/gpu/drm/xe/xe_hwmon.c | 38 +++++++++++++++++++++++++++++++++++
>  1 file changed, 38 insertions(+)
> 
> diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
> index 5edeac96..2bb5bca2 100644
> --- a/drivers/gpu/drm/xe/xe_hwmon.c
> +++ b/drivers/gpu/drm/xe/xe_hwmon.c
> @@ -3,6 +3,7 @@
>   * Copyright © 2023 Intel Corporation
>   */
>  
> +#include <linux/delay.h>
>  #include <linux/hwmon-sysfs.h>
>  #include <linux/hwmon.h>
>  #include <linux/jiffies.h>
> @@ -14,6 +15,8 @@
>  #include "regs/xe_mchbar_regs.h"
>  #include "regs/xe_pcode_regs.h"
>  #include "xe_device.h"
> +#include "xe_force_wake.h"
> +#include "xe_gt.h"
>  #include "xe_hwmon.h"
>  #include "xe_mmio.h"
>  #include "xe_pcode.h"
> @@ -1096,6 +1099,35 @@ xe_hwmon_temp_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
>  	}
>  }
>  
> +static int xe_hwmon_b70_temp_read(struct xe_hwmon *hwmon, u32 attr,
> +				  int channel, long *val)
> +{
> +	unsigned int refs[XE_MAX_TILES_PER_DEVICE * XE_MAX_GT_PER_TILE] = {};
> +	struct xe_device *xe = hwmon->xe;
> +	struct xe_gt *gt;
> +	int ret = -ETIMEDOUT;
> +	u8 id;
> +
> +	for_each_gt(gt, xe, id) {
> +		refs[id] = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
> +		if (!xe_force_wake_ref_has_domain(refs[id], XE_FORCEWAKE_ALL))
> +			goto out;
> +	}
> +
> +	/*
> +	 * A forcewake acknowledgment does not imply a fresh thermal sample.
> +	 * RFC: this interval is empirical on 8086:e223; the conversion time
> +	 * and minimum required wake domains need hardware-spec confirmation.
> +	 */
> +	usleep_range(2000, 2500);
> +	ret = xe_hwmon_temp_read(hwmon, attr, channel, val);
> +out:
> +	for_each_gt(gt, xe, id)
> +		xe_force_wake_put(gt_to_fw(gt), refs[id]);
> +
> +	return ret;
> +}
> +
>  static umode_t
>  xe_hwmon_power_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
>  {
> @@ -1408,6 +1440,12 @@ xe_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
>  
>  	switch (type) {
>  	case hwmon_temp:
> +		/* Limit the RFC workaround to the PCI ID measured in issue 7805. */
> +		if (attr == hwmon_temp_input &&
> +		    hwmon->xe->info.platform == XE_BATTLEMAGE &&
> +		    hwmon->xe->info.devid == 0xe223)
> +			return xe_hwmon_b70_temp_read(hwmon, attr, channel, val);
> +
>  		return xe_hwmon_temp_read(hwmon, attr, channel, val);
>  	case hwmon_power:
>  		return xe_hwmon_power_read(hwmon, attr, channel, val);
> 
> base-commit: 666d2f09d9045fc8f72cc1f71528a04acbaf5229
> -- 
> 2.55.0
> 
> 

  parent reply	other threads:[~2026-09-28  0:06 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-26 10:21 [PATCH RFC] drm/xe/hwmon: refresh B70 temperature inputs after GT idle Bata Roland Krisztián via B4 Relay
2026-09-26 15:19 ` ✓ CI.KUnit: success for " Patchwork
2026-09-26 16:00 ` ✓ Xe.CI.BAT: " Patchwork
2026-09-26 17:47 ` ✓ Xe.CI.FULL: " Patchwork
2026-09-28  0:06 ` Rodrigo Vivi [this message]
2026-09-29 11:59   ` [PATCH RFC] " Roland Bata

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=armvfxjttsjTVWk-@intel.com \
    --to=rodrigo.vivi@intel.com \
    --cc=batarolandkrisztian@gmail.com \
    --cc=intel-xe@lists.freedesktop.org \
    --cc=karthik.poosa@intel.com \
    --cc=matthew.brost@intel.com \
    --cc=raag.jadav@intel.com \
    --cc=thomas.hellstrom@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox