From 07a7480a2b24e052aef0f9e0096937a99150c73c Mon Sep 17 00:00:00 2001 From: Avri Altman Date: Tue, 11 Feb 2025 08:58:13 +0200 Subject: [PATCH] BACKPORT: scsi: ufs: core: Critical health condition Martin hi, The UFS4.1 standard, released on January 8, 2025, added a new exception event: HEALTH_CRITICAL, which notifies the host of a device's critical health condition. This notification implies that the device is approaching the end of its lifetime based on the amount of performed program/erase cycles. Once an EOL (End-of-Life) exception event is received, we increment a designated member, which is exposed via a sysfs entry. This new entry, will report the number of times a critical health event has been reported by a UFS device. To handle this new sysfs entry, userspace applications can use select(), poll(), or epoll() to monitor changes in the critical_health attribute. The kernel will call sysfs_notify() to signal changes, allowing the userspace application to detect and respond to these changes efficiently. The host can gain further insight into the specific issue by reading one of the following attributes: bPreEOLInfo, bDeviceLifeTimeEstA, bDeviceLifeTimeEstB, bWriteBoosterBufferLifeTimeEst, and bRPMBLifeTimeEst. All those are available for reading via the driver's sysfs entries or through an applicable utility. It is up to userspace to read these attributes if needed. Signed-off-by: Avri Altman Link: https://lore.kernel.org/r/20250211065813.58091-1-avri.altman@wdc.com Reviewed-by: Bart Van Assche Signed-off-by: Martin K. Petersen Bug: 409247664 Change-Id: I9beaffb4ca9eeaf8e8d39ab4628c2d1bf40f1c39 (cherry picked from commit edfaf868f3ae65099b41ec28724cb5241eeb9edf) (Fixed minor conflict to adapt to kernel 6.12, no code logic change) Signed-off-by: Ziqi Chen --- Documentation/ABI/testing/sysfs-driver-ufs | 12 ++++++++++++ drivers/ufs/core/ufs-sysfs.c | 10 ++++++++++ drivers/ufs/core/ufshcd.c | 10 ++++++++++ include/ufs/ufs.h | 1 + include/ufs/ufshcd.h | 3 +++ 5 files changed, 36 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index da8d1437d3f4..ae0191295d29 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -1560,6 +1560,18 @@ Description: The Manufacturer ID is defined by JEDEC in JEDEC-JEP106. The file is read only. +What: /sys/bus/platform/drivers/ufshcd/*/critical_health +What: /sys/bus/platform/devices/*.ufs/critical_health +Date: February 2025 +Contact: Avri Altman +Description: Report the number of times a critical health event has been + reported by a UFS device. Further insight into the specific + issue can be gained by reading one of: bPreEOLInfo, + bDeviceLifeTimeEstA, bDeviceLifeTimeEstB, + bWriteBoosterBufferLifeTimeEst, and bRPMBLifeTimeEst. + + The file is read only. + What: /sys/bus/platform/drivers/ufshcd/*/clkscale_enable What: /sys/bus/platform/devices/*.ufs/clkscale_enable Date: January 2025 diff --git a/drivers/ufs/core/ufs-sysfs.c b/drivers/ufs/core/ufs-sysfs.c index 796e37a1d859..762116204fd4 100644 --- a/drivers/ufs/core/ufs-sysfs.c +++ b/drivers/ufs/core/ufs-sysfs.c @@ -458,6 +458,14 @@ static ssize_t pm_qos_enable_store(struct device *dev, return count; } +static ssize_t critical_health_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", hba->critical_health_count); +} + static DEVICE_ATTR_RW(rpm_lvl); static DEVICE_ATTR_RO(rpm_target_dev_state); static DEVICE_ATTR_RO(rpm_target_link_state); @@ -470,6 +478,7 @@ static DEVICE_ATTR_RW(enable_wb_buf_flush); static DEVICE_ATTR_RW(wb_flush_threshold); static DEVICE_ATTR_RW(rtc_update_ms); static DEVICE_ATTR_RW(pm_qos_enable); +static DEVICE_ATTR_RO(critical_health); static struct attribute *ufs_sysfs_ufshcd_attrs[] = { &dev_attr_rpm_lvl.attr, @@ -484,6 +493,7 @@ static struct attribute *ufs_sysfs_ufshcd_attrs[] = { &dev_attr_wb_flush_threshold.attr, &dev_attr_rtc_update_ms.attr, &dev_attr_pm_qos_enable.attr, + &dev_attr_critical_health.attr, NULL }; diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 6bfb01a56931..7d6b37e52697 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -6332,6 +6332,11 @@ static void ufshcd_exception_event_handler(struct work_struct *work) if (status & hba->ee_drv_mask & MASK_EE_URGENT_TEMP) ufshcd_temp_exception_event_handler(hba, status); + if (status & hba->ee_drv_mask & MASK_EE_HEALTH_CRITICAL) { + hba->critical_health_count++; + sysfs_notify(&hba->dev->kobj, NULL, "critical_health"); + } + ufs_debugfs_exception_event(hba, status); out: ufshcd_scsi_unblock_requests(hba); @@ -8448,6 +8453,11 @@ static int ufs_get_device_desc(struct ufs_hba *hba) ufshcd_temp_notif_probe(hba, desc_buf); + if (dev_info->wspecversion >= 0x410) { + hba->critical_health_count = 0; + ufshcd_enable_ee(hba, MASK_EE_HEALTH_CRITICAL); + } + ufs_init_rtc(hba, desc_buf); if (hba->ext_iid_sup) diff --git a/include/ufs/ufs.h b/include/ufs/ufs.h index 59f4557caf49..c3c56adc172b 100644 --- a/include/ufs/ufs.h +++ b/include/ufs/ufs.h @@ -422,6 +422,7 @@ enum { MASK_EE_TOO_LOW_TEMP = BIT(4), MASK_EE_WRITEBOOSTER_EVENT = BIT(5), MASK_EE_PERFORMANCE_THROTTLING = BIT(6), + MASK_EE_HEALTH_CRITICAL = BIT(9), }; #define MASK_EE_URGENT_TEMP (MASK_EE_TOO_HIGH_TEMP | MASK_EE_TOO_LOW_TEMP) diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h index a406e96edf04..69b44d601d0b 100644 --- a/include/ufs/ufshcd.h +++ b/include/ufs/ufshcd.h @@ -968,6 +968,7 @@ enum ufshcd_mcq_opr { * @ufs_rtc_update_work: A work for UFS RTC periodic update * @pm_qos_req: PM QoS request handle * @pm_qos_enabled: flag to check if pm qos is enabled + * @critical_health_count: count of critical health exceptions */ struct ufs_hba { void __iomem *mmio_base; @@ -1139,6 +1140,8 @@ struct ufs_hba { struct pm_qos_request pm_qos_req; bool pm_qos_enabled; + int critical_health_count; + ANDROID_OEM_DATA(1); };