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
>
>
next prev 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