[PATCH 2/2] nvme-hwmon: notify hwmon alarms on the SMART / Health Status AEN
Guixin Liu
kanie at linux.alibaba.com
Mon Nov 9 04:02:04 PST 2026
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 or spare-block condition. The device does send
a SMART / Health Status asynchronous event when its health status
changes, but the host never subscribes to it, and the completion path
silently drops the event.
Subscribe to the SMART / Health Status event, and when it arrives,
wake up pollers on temp1_alarm. The alarm read fetches the SMART log
on demand, so no log refresh is needed in the completion path; the
notify is deferred to a work item because nvme-pci completes
asynchronous events from interrupt context, where sysfs_notify()
cannot sleep.
Signed-off-by: Guixin Liu <kanie at linux.alibaba.com>
---
drivers/nvme/host/core.c | 8 ++++++--
drivers/nvme/host/hwmon.c | 30 ++++++++++++++++++++++++++++++
drivers/nvme/host/nvme.h | 5 +++++
3 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index cd0df9b038063..47dc9eb3ae595 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -1784,7 +1784,7 @@ int nvme_set_queue_count(struct nvme_ctrl *ctrl, int *count)
EXPORT_SYMBOL_GPL(nvme_set_queue_count);
#define NVME_AEN_SUPPORTED \
- (NVME_AEN_CFG_NS_ATTR | NVME_AEN_CFG_FW_ACT | \
+ (NVME_AEN_CFG_SMART | NVME_AEN_CFG_NS_ATTR | NVME_AEN_CFG_FW_ACT | \
NVME_AEN_CFG_ANA_CHANGE | NVME_AEN_CFG_DISC_CHANGE)
static void nvme_enable_aen(struct nvme_ctrl *ctrl)
@@ -5009,8 +5009,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..564d5cae8001b 100644
--- a/drivers/nvme/host/hwmon.c
+++ b/drivers/nvme/host/hwmon.c
@@ -5,6 +5,7 @@
*/
#include <linux/hwmon.h>
+#include <linux/sysfs.h>
#include <linux/units.h>
#include <linux/unaligned.h>
@@ -14,6 +15,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 +225,32 @@ static const struct hwmon_chip_info nvme_hwmon_chip_info = {
.info = nvme_hwmon_info,
};
+/*
+ * Called from the admin completion path on a SMART / Health Status AEN.
+ * The completion context can be a hardirq, so only queue the work here;
+ * the notify itself is done in process context. No log refresh is
+ * needed: userspace re-reads temp1_alarm, which fetches the SMART log
+ * on demand.
+ */
+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);
+}
+
+static void nvme_hwmon_smart_notify(struct work_struct *work)
+{
+ struct nvme_hwmon_data *data =
+ container_of(work, struct nvme_hwmon_data, smart_work);
+
+ sysfs_notify(&data->ctrl->hwmon_device->kobj, NULL, "temp1_alarm");
+}
+
int nvme_hwmon_init(struct nvme_ctrl *ctrl)
{
struct device *dev = ctrl->device;
@@ -242,6 +270,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 +302,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..df8a94c907655 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -1276,6 +1276,7 @@ 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);
#else
static inline int nvme_hwmon_init(struct nvme_ctrl *ctrl)
{
@@ -1285,6 +1286,10 @@ 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)
+{
+}
#endif
static inline void nvme_start_request(struct request *rq)
--
2.43.7
More information about the Linux-nvme
mailing list