[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