Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ec242d21 authored by Pradeep P V K's avatar Pradeep P V K Committed by Gerrit - the friendly Code Review server
Browse files

ubi: Add scrub_all support to UBI



The data on NAND parts degrades overtime. This is known as data decay.
This decay is accelarated by extreme temperatures.

When data on nand is read over and over again this can also cause decay
of data. This is known as read disturb and can degrade the data in
the page/block that is read and the adjacent cells as well.

This data degrade can be corrected to a certain degree by the nand
driver/controller using the ECC but this is not sufficient specially
in products designed to last years.

The only way to combat the decay is to "refresh" the data by moving it
to a new location.

Add scrub_all support to UBI to facilitate the data refresh.

Change-Id: Ifafb82fe4ddb7120277dcfbbff79b3e087ca344d
Signed-off-by: default avatarNikhilesh Reddy <reddyn@codeaurora.org>
Signed-off-by: default avatarPradeep P V K <quic_pragalla@quicinc.com>
parent 9ac53309
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
@@ -821,9 +821,22 @@ struct ubi_ainf_peb *ubi_early_get_peb(struct ubi_device *ubi,
	int err = 0;
	struct ubi_ainf_peb *aeb, *tmp_aeb;

	if (!list_empty(&ai->free)) {
		aeb = list_entry(ai->free.next, struct ubi_ainf_peb, u.list);
	list_for_each_entry_safe(aeb, tmp_aeb, &ai->free, u.list) {

		list_del(&aeb->u.list);
		if (aeb->ec == UBI_UNKNOWN) {
			ubi_err(ubi, "PEB %d in freelist has unknown EC",
					aeb->pnum);
			aeb->ec = ai->mean_ec;
		}
		err = early_erase_peb(ubi, ai, aeb->pnum, aeb->ec+1);
		if (err) {
			ubi_err(ubi, "Erase failed for PEB %d in freelist",
					aeb->pnum);
			list_add(&aeb->u.list, &ai->erase);
			continue;
		}
		aeb->ec += 1;
		dbg_bld("return free PEB %d, EC %d", aeb->pnum, aeb->ec);
		return aeb;
	}
+5 −4
Original line number Diff line number Diff line
@@ -121,7 +121,7 @@ static int __init ubiblock_set_param(const char *val,
		return -EINVAL;
	}

	strcpy(buf, val);
	strlcpy(buf, val, sizeof(buf));

	/* Get rid of the final newline */
	if (buf[len - 1] == '\n')
@@ -141,12 +141,12 @@ static int __init ubiblock_set_param(const char *val,
		ret = kstrtoint(tokens[1], 10, &param->vol_id);
		if (ret < 0) {
			param->vol_id = -1;
			strcpy(param->name, tokens[1]);
			strlcpy(param->name, tokens[1], UBIBLOCK_PARAM_LEN+1);
		}

	} else {
		/* One parameter: must be device path */
		strcpy(param->name, tokens[0]);
		strlcpy(param->name, tokens[0], UBIBLOCK_PARAM_LEN+1);
		param->ubi_num = -1;
		param->vol_id = -1;
	}
@@ -412,7 +412,8 @@ int ubiblock_create(struct ubi_volume_info *vi)
		goto out_put_disk;
	}
	gd->private_data = dev;
	sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id);
	scnprintf(gd->disk_name, DISK_NAME_LEN, "ubiblock%d_%d",
					dev->ubi_num, dev->vol_id);
	set_capacity(gd, disk_capacity);
	dev->gd = gd;

+95 −16
Original line number Diff line number Diff line
@@ -95,7 +95,7 @@ static DEFINE_SPINLOCK(ubi_devices_lock);
static ssize_t version_show(struct class *class, struct class_attribute *attr,
			    char *buf)
{
	return sprintf(buf, "%d\n", UBI_VERSION);
	return scnprintf(buf, sizeof(int), "%d\n", UBI_VERSION);
}
static CLASS_ATTR_RO(version);

@@ -114,6 +114,9 @@ struct class ubi_class = {

static ssize_t dev_attribute_show(struct device *dev,
				  struct device_attribute *attr, char *buf);
static ssize_t dev_attribute_store(struct device *dev,
				   struct device_attribute *attr,
				   const char *buf, size_t count);

/* UBI device attributes (correspond to files in '/<sysfs>/class/ubi/ubiX') */
static struct device_attribute dev_eraseblock_size =
@@ -140,6 +143,13 @@ static struct device_attribute dev_mtd_num =
	__ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL);
static struct device_attribute dev_ro_mode =
	__ATTR(ro_mode, S_IRUGO, dev_attribute_show, NULL);
static struct device_attribute dev_mtd_trigger_scrub =
	__ATTR(scrub_all, 0644,
		dev_attribute_show, dev_attribute_store);
static struct device_attribute dev_mtd_max_scrub_sqnum =
	__ATTR(scrub_max_sqnum, 0444, dev_attribute_show, NULL);
static struct device_attribute dev_mtd_min_scrub_sqnum =
	__ATTR(scrub_min_sqnum, 0444, dev_attribute_show, NULL);

/**
 * ubi_volume_notify - send a volume change notification.
@@ -332,6 +342,17 @@ int ubi_major2num(int major)
	return ubi_num;
}

static unsigned long long get_max_sqnum(struct ubi_device *ubi)
{
	unsigned long long max_sqnum;

	spin_lock(&ubi->ltree_lock);
	max_sqnum = ubi->global_sqnum - 1;
	spin_unlock(&ubi->ltree_lock);

	return max_sqnum;
}

/* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */
static ssize_t dev_attribute_show(struct device *dev,
				  struct device_attribute *attr, char *buf)
@@ -355,29 +376,50 @@ static ssize_t dev_attribute_show(struct device *dev,
		return -ENODEV;

	if (attr == &dev_eraseblock_size)
		ret = sprintf(buf, "%d\n", ubi->leb_size);
		ret = scnprintf(buf, sizeof(int), "%d\n",
				ubi->leb_size);
	else if (attr == &dev_avail_eraseblocks)
		ret = sprintf(buf, "%d\n", ubi->avail_pebs);
		ret = scnprintf(buf, sizeof(int), "%d\n",
				ubi->avail_pebs);
	else if (attr == &dev_total_eraseblocks)
		ret = sprintf(buf, "%d\n", ubi->good_peb_count);
		ret = scnprintf(buf, sizeof(int), "%d\n",
				ubi->good_peb_count);
	else if (attr == &dev_volumes_count)
		ret = sprintf(buf, "%d\n", ubi->vol_count - UBI_INT_VOL_COUNT);
		ret = scnprintf(buf, sizeof(int), "%d\n",
			ubi->vol_count - UBI_INT_VOL_COUNT);
	else if (attr == &dev_max_ec)
		ret = sprintf(buf, "%d\n", ubi->max_ec);
		ret = scnprintf(buf, sizeof(int), "%d\n",
				ubi->max_ec);
	else if (attr == &dev_reserved_for_bad)
		ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs);
		ret = scnprintf(buf, sizeof(int), "%d\n",
				ubi->beb_rsvd_pebs);
	else if (attr == &dev_bad_peb_count)
		ret = sprintf(buf, "%d\n", ubi->bad_peb_count);
		ret = scnprintf(buf, sizeof(int), "%d\n",
				ubi->bad_peb_count);
	else if (attr == &dev_max_vol_count)
		ret = sprintf(buf, "%d\n", ubi->vtbl_slots);
		ret = scnprintf(buf, sizeof(int), "%d\n",
				ubi->vtbl_slots);
	else if (attr == &dev_min_io_size)
		ret = sprintf(buf, "%d\n", ubi->min_io_size);
		ret = scnprintf(buf, sizeof(int), "%d\n",
				ubi->min_io_size);
	else if (attr == &dev_bgt_enabled)
		ret = sprintf(buf, "%d\n", ubi->thread_enabled);
		ret = scnprintf(buf, sizeof(int), "%d\n",
				ubi->thread_enabled);
	else if (attr == &dev_mtd_num)
		ret = sprintf(buf, "%d\n", ubi->mtd->index);
		ret = scnprintf(buf, sizeof(int), "%d\n",
				ubi->mtd->index);
	else if (attr == &dev_ro_mode)
		ret = sprintf(buf, "%d\n", ubi->ro_mode);
		ret = scnprintf(buf, sizeof(int), "%d\n",
				ubi->ro_mode);
	else if (attr == &dev_mtd_trigger_scrub)
		ret = scnprintf(buf, sizeof(int), "%d\n",
				atomic_read(&ubi->scrub_work_count));
	else if (attr == &dev_mtd_max_scrub_sqnum)
		ret = scnprintf(buf, sizeof(unsigned long long), "%llu\n",
				get_max_sqnum(ubi));
	else if (attr == &dev_mtd_min_scrub_sqnum)
		ret = scnprintf(buf, sizeof(unsigned long long), "%llu\n",
				ubi_wl_scrub_get_min_sqnum(ubi));
	else
		ret = -EINVAL;

@@ -398,10 +440,46 @@ static struct attribute *ubi_dev_attrs[] = {
	&dev_bgt_enabled.attr,
	&dev_mtd_num.attr,
	&dev_ro_mode.attr,
	&dev_mtd_trigger_scrub.attr,
	&dev_mtd_max_scrub_sqnum.attr,
	&dev_mtd_min_scrub_sqnum.attr,
	NULL
};
ATTRIBUTE_GROUPS(ubi_dev);

static ssize_t dev_attribute_store(struct device *dev,
			   struct device_attribute *attr,
			   const char *buf, size_t count)
{
	int ret = 0;
	struct ubi_device *ubi;
	unsigned long long scrub_sqnum;

	ubi = container_of(dev, struct ubi_device, dev);
	ubi = ubi_get_device(ubi->ubi_num);
	if (!ubi)
		return -ENODEV;

	if (attr == &dev_mtd_trigger_scrub) {
		if (kstrtoull(buf, 10, &scrub_sqnum)) {
			ret = -EINVAL;
			goto out;
		}
		if (!ubi->lookuptbl) {
			pr_err("lookuptbl is null\n");
			ret = -ENOENT;
			goto out;
		}
		ret = ubi_wl_scrub_all(ubi, scrub_sqnum);
		if (ret == 0)
			ret = count;
	}

out:
	ubi_put_device(ubi);
	return ret;
}

static void dev_release(struct device *dev)
{
	struct ubi_device *ubi = container_of(dev, struct ubi_device, dev);
@@ -438,7 +516,8 @@ static int uif_init(struct ubi_device *ubi)
	int i, err;
	dev_t dev;

	sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
	scnprintf(ubi->ubi_name, sizeof(UBI_NAME_STR) + 5,
			UBI_NAME_STR "%d", ubi->ubi_num);

	/*
	 * Major numbers for the UBI character devices are allocated
@@ -1376,7 +1455,7 @@ static int ubi_mtd_param_parse(const char *val, const struct kernel_param *kp)
		return 0;
	}

	strcpy(buf, val);
	strlcpy(buf, val, sizeof(buf));

	/* Get rid of the final newline */
	if (buf[len - 1] == '\n')
@@ -1391,7 +1470,7 @@ static int ubi_mtd_param_parse(const char *val, const struct kernel_param *kp)
	}

	p = &mtd_dev_param[mtd_devs];
	strcpy(&p->name[0], tokens[0]);
	strlcpy(&p->name[0], tokens[0], MTD_PARAM_LEN_MAX);

	token = tokens[1];
	if (token) {
+11 −0
Original line number Diff line number Diff line
@@ -1056,6 +1056,15 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
	dbg_io("write VID header to PEB %d", pnum);
	ubi_assert(pnum >= 0 &&  pnum < ubi->peb_count);

	/*
	 * Re-erase the PEB before using it. This should minimize any issues
	 * from decay of charge in this block.
	 */
	if (ubi->wl_is_inited) {
		err = ubi_wl_re_erase_peb(ubi, pnum);
		if (err)
			return err;
	}
	err = self_check_peb_ec_hdr(ubi, pnum);
	if (err)
		return err;
@@ -1074,6 +1083,8 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,

	err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset,
			   ubi->vid_hdr_alsize);
	if (!err && ubi->wl_is_inited)
		ubi_wl_update_peb_sqnum(ubi, pnum, vid_hdr);
	return err;
}

+13 −1
Original line number Diff line number Diff line
@@ -170,6 +170,8 @@ struct ubi_vid_io_buf {
 * @u.list: link in the protection queue
 * @ec: erase counter
 * @pnum: physical eraseblock number
 * @tagged_scrub_all: if the entry is tagged for scrub all
 * @sqnum: The sequence number of the vol header.
 *
 * This data structure is used in the WL sub-system. Each physical eraseblock
 * has a corresponding &struct wl_entry object which may be kept in different
@@ -182,6 +184,8 @@ struct ubi_wl_entry {
	} u;
	int ec;
	int pnum;
	unsigned int tagged_scrub_all:1;
	unsigned long long sqnum;
};

/**
@@ -625,6 +629,9 @@ struct ubi_device {
	struct task_struct *bgt_thread;
	int thread_enabled;
	char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2];
	bool scrub_in_progress;
	atomic_t scrub_work_count;
	int wl_is_inited;

	/* I/O sub-system's stuff */
	long long flash_size;
@@ -919,7 +926,12 @@ int ubi_is_erase_work(struct ubi_work *wrk);
void ubi_refill_pools(struct ubi_device *ubi);
int ubi_ensure_anchor_pebs(struct ubi_device *ubi);
int ubi_bitflip_check(struct ubi_device *ubi, int pnum, int force_scrub);

ssize_t ubi_wl_scrub_all(struct ubi_device *ubi,
			 unsigned long long scrub_sqnum);
void ubi_wl_update_peb_sqnum(struct ubi_device *ubi, int pnum,
				struct ubi_vid_hdr *vid_hdr);
unsigned long long ubi_wl_scrub_get_min_sqnum(struct ubi_device *ubi);
int ubi_wl_re_erase_peb(struct ubi_device *ubi, int pnum);
/* io.c */
int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
		int len);
Loading