From: Hsin-Te Yuan yuanhsinte@chromium.org
[ Upstream commit 1a4aabc27e95674837f2e25f4ef340c0469e6203 ]
According to POSIX spec, EAGAIN returned by read with O_NONBLOCK set means the read would block. Hence, the common implementation in nonblocking model will poll the file when the nonblocking read returns EAGAIN. However, when the target file is thermal zone, this mechanism will totally malfunction because thermal zone doesn't implement sysfs notification and thus the poll will never return.
For example, the read in Golang implemnts such method and sometimes hangs at reading some thermal zones via sysfs.
Change to return -ENODATA instead of -EAGAIN to userspace.
Signed-off-by: Hsin-Te Yuan yuanhsinte@chromium.org Link: https://patch.msgid.link/20250620-temp-v3-1-6becc6aeb66c@chromium.org Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
Based on my analysis of the commit, here is my determination:
**Backport Status: YES**
## Extensive Explanation:
This commit should be backported to stable kernel trees for the following reasons:
### 1. **Bug Fix for Real User-Facing Issue** The commit fixes a bug where userspace applications can hang when reading thermal zone temperature files via sysfs with O_NONBLOCK set. The commit message clearly states that "the read in Golang implements such method and sometimes hangs at reading some thermal zones via sysfs." This is a real-world problem affecting actual applications.
### 2. **POSIX Compliance Issue** The current behavior violates POSIX semantics. According to POSIX specifications, EAGAIN from a non-blocking read means "the read would block" - implying that the caller should poll/select and wait for data to become available. However, thermal zone sysfs files don't implement poll notifications, so applications following standard POSIX patterns will poll forever, causing hangs.
### 3. **Small and Contained Change** The code change is minimal and localized: ```c - if (ret) - return ret; + if (!ret) + return sprintf(buf, "%d\n", temperature);
- return sprintf(buf, "%d\n", temperature); + if (ret == -EAGAIN) + return -ENODATA; + + return ret; ``` It only affects the `temp_show()` function in `thermal_sysfs.c` by translating -EAGAIN to -ENODATA specifically for sysfs reads.
### 4. **Low Risk of Regression** - The change only affects error handling paths - It doesn't modify the normal success case - It only translates one specific error code (-EAGAIN) to another (-ENODATA) - ENODATA is a more appropriate error for "no data available" in a sysfs context
### 5. **Multiple Drivers Return -EAGAIN** My search found at least 13 thermal drivers that can return -EAGAIN from their get_temp operations: - imx8mm_thermal.c - imx_thermal.c - tegra-bpmp-thermal.c - qoriq_thermal.c - lvts_thermal.c - rockchip_thermal.c - exynos_tmu.c - sun8i_thermal.c - stm_thermal.c - intel_powerclamp.c
This indicates the issue affects multiple platforms and thermal drivers.
### 6. **Follows Stable Kernel Rules** - Fixes a real bug that bothers people (application hangs) - Fix is already in Linus's tree (based on the Signed-off-by from Rafael J. Wysocki) - Small change (< 100 lines) - Obviously correct and tested - Doesn't change APIs or break existing functionality
### 7. **No Architectural Changes** The commit doesn't introduce new features or change the thermal subsystem architecture. It's purely a bug fix that makes the sysfs interface behave correctly with non-blocking reads.
The fix is appropriate because sysfs files are not meant to be pollable in the traditional sense - they provide instantaneous data snapshots. Returning ENODATA instead of EAGAIN properly communicates "no data currently available" without implying that polling would help.
drivers/thermal/thermal_sysfs.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index eef40d4f3063..0dea605faadb 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -39,10 +39,13 @@ temp_show(struct device *dev, struct device_attribute *attr, char *buf)
ret = thermal_zone_get_temp(tz, &temperature);
- if (ret) - return ret; + if (!ret) + return sprintf(buf, "%d\n", temperature);
- return sprintf(buf, "%d\n", temperature); + if (ret == -EAGAIN) + return -ENODATA; + + return ret; }
static ssize_t