[PATCH v3 01/10] ubi: Expose mean erase counter in sysfs

Rickard Andersson
2024-10-11T13:01:20Z

Currently, only "max_ec" can be read from sysfs, which provides a
limited view of the flash device’s wear. In certain cases, such as
bugs in the wear-leveling algorithm, specific blocks can be worn down
more than others, resulting in uneven wear distribution. Providing a
mean erase counter value gives a better understanding of the overall
flash wear.
There exists more detailed info in debugfs, but this information is
only available for debug builds.

This patch calculates the mean value only when it is requested. This
calculation typically takes 2.7 ms for an UBI device with 4091 blocks
running on a single core Cortex-A9 at 792 MHz.

Reviewed-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Rickard Andersson <rickard.andersson@axis.com>
---
drivers/mtd/ubi/build.c | 46 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)

diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index f5140a78a4a8..231ca288f397 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -131,6 +131,8 @@ static struct device_attribute dev_volumes_count =
__ATTR(volumes_count, S_IRUGO, dev_attribute_show, NULL);
static struct device_attribute dev_max_ec =
__ATTR(max_ec, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_mean_ec =
+ __ATTR(mean_ec, S_IRUGO, dev_attribute_show, NULL);
static struct device_attribute dev_reserved_for_bad =
__ATTR(reserved_for_bad, S_IRUGO, dev_attribute_show, NULL);
static struct device_attribute dev_bad_peb_count =
@@ -337,6 +339,47 @@ int ubi_major2num(int major)
return ubi_num;
}

+/**
+ * ubi_calc_mean_ec - calculate mean erase counter value.
+ * @ubi: UBI device description object
+ * @start_peb: First PEB in the range
+ * @end_peb: End PEB in the half-open range
+ *
+ * Returns the mean value of all non-bad blocks of the device that are
+ * in the range. Range is half-open i.e end_peb is not actually included.
+ */
+static int ubi_calc_mean_ec(struct ubi_device *ubi, int start_peb, int end_peb)
+{
+ struct ubi_wl_entry *wl;
+ int peb;
+ int ec_count = 0;
+ int mean_ec = 0;
+ uint64_t ec_sum = 0;
+
+ for (peb = start_peb; peb < end_peb; peb++) {
+ int err;
+
+ err = ubi_io_is_bad(ubi, peb);
+ if (err)
+ continue;
+
+ spin_lock(&ubi->wl_lock);
+
+ wl = ubi->lookuptbl[peb];
+ if (wl) {
+ ec_sum += wl->ec;
+ ec_count++;
+ }
+
+ spin_unlock(&ubi->wl_lock);
+ }
+
+ if (ec_count > 0)
+ mean_ec = div_u64(ec_sum, ec_count);
+
+ return mean_ec;
+}
+
/* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */
static ssize_t dev_attribute_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -366,6 +409,8 @@ static ssize_t dev_attribute_show(struct device *dev,
ret = sprintf(buf, "%d\n", ubi->vol_count - UBI_INT_VOL_COUNT);
else if (attr == &dev_max_ec)
ret = sprintf(buf, "%d\n", ubi->max_ec);
+ else if (attr == &dev_mean_ec)
+ ret = sprintf(buf, "%d\n", ubi_calc_mean_ec(ubi, 0, ubi->peb_count));
else if (attr == &dev_reserved_for_bad)
ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs);
else if (attr == &dev_bad_peb_count)
@@ -392,6 +437,7 @@ static struct attribute *ubi_dev_attrs[] = {
&dev_total_eraseblocks.attr,
&dev_volumes_count.attr,
&dev_max_ec.attr,
+ &dev_mean_ec.attr,
&dev_reserved_for_bad.attr,
&dev_bad_peb_count.attr,
&dev_max_vol_count.attr,