From: Guixin Liu <kanie@linux.alibaba.com>
To: Keith Busch <kbusch@kernel.org>, Jens Axboe <axboe@kernel.dk>,
Christoph Hellwig <hch@lst.de>, Sagi Grimberg <sagi@grimberg.me>,
Nilay Shroff <nilay@linux.ibm.com>,
Daniel Wagner <dwagner@suse.de>, Hannes Reinecke <hare@suse.de>
Cc: linux-nvme@lists.infradead.org
Subject: [PATCH v2 1/1] nvme-hwmon: notify hwmon alarms on the SMART / Health Status AEN
Date: Tue, 10 Nov 2026 19:07:37 +0800 [thread overview]
Message-ID: <20261110110737.3965373-2-kanie@linux.alibaba.com> (raw)
In-Reply-To: <20261110110737.3965373-1-kanie@linux.alibaba.com>
The NVMe hwmon support exposes the temperature alarm as a plain sysfs
attribute, so monitoring tools have to poll temp1_alarm to learn about
a critical temperature condition. The device does send a SMART / Health
Status asynchronous event when a critical warning is raised, but the
host never enables it in the Asynchronous Event Configuration, and the
completion path silently drops the event.
The temperature warning is bit 1 of the SMART / Health Critical
Warnings field of the Asynchronous Event Configuration (feature 0x0b,
command dword 11 bits 7:0), which mirrors the critical_warning byte of
the SMART log. Unlike the notice events, it has no OAES capability bit,
so the subscription does not go through ctrl->oaes.
The event stays latched until the SMART log is read with RAE cleared,
so the deferred work re-reads it before the completion path re-arms
the Asynchronous Event Request, and then calls hwmon_notify_event()
on temp1_alarm. hwmon_notify_event() also feeds the thermal zone
registered with HWMON_C_REGISTER_TZ and emits the standard hwmon
uevent, which a plain sysfs_notify() would skip.
The nvme-pci transport completes asynchronous events from interrupt
context, so the completion path only queues the work.
Signed-off-by: Guixin Liu <kanie@linux.alibaba.com>
---
drivers/nvme/host/core.c | 17 ++++++++++++++++-
drivers/nvme/host/hwmon.c | 36 ++++++++++++++++++++++++++++++++++++
drivers/nvme/host/nvme.h | 15 +++++++++++++++
3 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index cd0df9b038063..e932e1a7e479c 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -1792,6 +1792,17 @@ static void nvme_enable_aen(struct nvme_ctrl *ctrl)
u32 result, supported_aens = ctrl->oaes & NVME_AEN_SUPPORTED;
int status;
+ /*
+ * Unlike the notice events above, SMART / Health Status events have
+ * no OAES capability bit: AEC dw11 bits 7:0 are the SMART / Health
+ * Critical Warnings (SHCW) mask, which mirrors the critical_warning
+ * byte of the SMART log. Subscribe to the temperature warning
+ * whenever the hwmon support is present, as that is the only
+ * consumer.
+ */
+ if (nvme_hwmon_present(ctrl))
+ supported_aens |= NVME_SMART_CRIT_TEMPERATURE;
+
if (!supported_aens)
return;
@@ -5009,8 +5020,12 @@ void nvme_complete_async_event(struct nvme_ctrl *ctrl, __le16 status,
nvme_handle_aer_persistent_error(ctrl);
return;
}
- fallthrough;
+ ctrl->aen_result = result;
+ break;
case NVME_AER_SMART:
+ nvme_hwmon_smart_event(ctrl);
+ ctrl->aen_result = result;
+ break;
case NVME_AER_CSS:
case NVME_AER_VS:
ctrl->aen_result = result;
diff --git a/drivers/nvme/host/hwmon.c b/drivers/nvme/host/hwmon.c
index 406203d8b9adb..172ef63f1180c 100644
--- a/drivers/nvme/host/hwmon.c
+++ b/drivers/nvme/host/hwmon.c
@@ -14,6 +14,7 @@ struct nvme_hwmon_data {
struct nvme_ctrl *ctrl;
struct nvme_smart_log *log;
struct mutex read_lock;
+ struct work_struct smart_work;
};
static int nvme_get_temp_thresh(struct nvme_ctrl *ctrl, int sensor, bool under,
@@ -223,6 +224,39 @@ static const struct hwmon_chip_info nvme_hwmon_chip_info = {
.info = nvme_hwmon_info,
};
+static void nvme_hwmon_smart_notify(struct work_struct *work)
+{
+ struct nvme_hwmon_data *data =
+ container_of(work, struct nvme_hwmon_data, smart_work);
+ struct nvme_ctrl *ctrl = data->ctrl;
+ int ret;
+
+ /*
+ * The event stays latched on the controller until the SMART log
+ * is read with RAE cleared, so re-read it here before the
+ * completion path re-arms the Asynchronous Event Request.
+ */
+ mutex_lock(&data->read_lock);
+ ret = nvme_hwmon_get_smart_log(data);
+ mutex_unlock(&data->read_lock);
+ if (ret)
+ return;
+
+ hwmon_notify_event(ctrl->hwmon_device, hwmon_temp,
+ hwmon_temp_alarm, 0);
+}
+
+void nvme_hwmon_smart_event(struct nvme_ctrl *ctrl)
+{
+ struct nvme_hwmon_data *data;
+
+ if (!ctrl->hwmon_device)
+ return;
+
+ data = dev_get_drvdata(ctrl->hwmon_device);
+ queue_work(nvme_wq, &data->smart_work);
+}
+
int nvme_hwmon_init(struct nvme_ctrl *ctrl)
{
struct device *dev = ctrl->device;
@@ -242,6 +276,7 @@ int nvme_hwmon_init(struct nvme_ctrl *ctrl)
data->ctrl = ctrl;
mutex_init(&data->read_lock);
+ INIT_WORK(&data->smart_work, nvme_hwmon_smart_notify);
err = nvme_hwmon_get_smart_log(data);
if (err) {
@@ -273,6 +308,7 @@ void nvme_hwmon_exit(struct nvme_ctrl *ctrl)
struct nvme_hwmon_data *data =
dev_get_drvdata(ctrl->hwmon_device);
+ cancel_work_sync(&data->smart_work);
hwmon_device_unregister(ctrl->hwmon_device);
ctrl->hwmon_device = NULL;
kfree(data->log);
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 2543e52d6358a..fe43ab17c3967 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -1276,6 +1276,12 @@ static inline struct nvme_ns *nvme_get_ns_from_dev(struct device *dev)
#ifdef CONFIG_NVME_HWMON
int nvme_hwmon_init(struct nvme_ctrl *ctrl);
void nvme_hwmon_exit(struct nvme_ctrl *ctrl);
+void nvme_hwmon_smart_event(struct nvme_ctrl *ctrl);
+
+static inline bool nvme_hwmon_present(struct nvme_ctrl *ctrl)
+{
+ return ctrl->hwmon_device != NULL;
+}
#else
static inline int nvme_hwmon_init(struct nvme_ctrl *ctrl)
{
@@ -1285,6 +1291,15 @@ static inline int nvme_hwmon_init(struct nvme_ctrl *ctrl)
static inline void nvme_hwmon_exit(struct nvme_ctrl *ctrl)
{
}
+
+static inline void nvme_hwmon_smart_event(struct nvme_ctrl *ctrl)
+{
+}
+
+static inline bool nvme_hwmon_present(struct nvme_ctrl *ctrl)
+{
+ return false;
+}
#endif
static inline void nvme_start_request(struct request *rq)
--
2.43.7
next prev parent reply other threads:[~2026-11-10 11:07 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 11:07 [PATCH v2 0/1] nvme: make SMART / Health Status AENs reach hwmon alarms Guixin Liu
2026-11-10 11:07 ` Guixin Liu [this message]
2026-11-10 11:32 ` [PATCH v2 1/1] nvme-hwmon: notify hwmon alarms on the SMART / Health Status AEN sashiko-bot
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=20261110110737.3965373-2-kanie@linux.alibaba.com \
--to=kanie@linux.alibaba.com \
--cc=axboe@kernel.dk \
--cc=dwagner@suse.de \
--cc=hare@suse.de \
--cc=hch@lst.de \
--cc=kbusch@kernel.org \
--cc=linux-nvme@lists.infradead.org \
--cc=nilay@linux.ibm.com \
--cc=sagi@grimberg.me \
/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