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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.