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

Commit 547808b1 authored by Nikhilesh Reddy's avatar Nikhilesh Reddy Committed by Gerrit - the friendly Code Review server
Browse files

ubi: merge changes from msm-3.18 kernel to msm-4.9 kernel



This commit has the following commits:

'commit e81a5c26e7b5 ("ubi: Add scrub_all support to UBI")'
'commit a9e78113fe84 ("ubi: Fix ubi_io_write_vid_hdr so it
can be used before wl init")'
'commit e2b9f56d5ace ("UBI: Do not re-erase the PEB before
writing the VID header")'
'commit d538879e7328 ("UBI: Re-erase the PEB before writing
the VID header")'

Also fixed few static analysis and checkpatch errors.

Change-Id: Ifafb82fe4ddb7120277dcfbbff79b3e087ca344d
Signed-off-by: default avatarNikhilesh Reddy <reddyn@codeaurora.org>
Signed-off-by: default avatarAnjana <ahari@codeaurora.org>
parent 6286b15f
Loading
Loading
Loading
Loading
+14 −2
Original line number Diff line number Diff line
@@ -834,9 +834,21 @@ 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;
	}
+65 −0
Original line number Diff line number Diff line
@@ -125,6 +125,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 =
@@ -151,6 +154,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.
@@ -343,6 +353,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)
@@ -389,6 +410,15 @@ static ssize_t dev_attribute_show(struct device *dev,
		ret = sprintf(buf, "%d\n", ubi->mtd->index);
	else if (attr == &dev_ro_mode)
		ret = sprintf(buf, "%d\n", ubi->ro_mode);
	else if (attr == &dev_mtd_trigger_scrub)
		ret = snprintf(buf, PAGE_SIZE, "%d\n",
					atomic_read(&ubi->scrub_work_count));
	else if (attr == &dev_mtd_max_scrub_sqnum)
		ret = snprintf(buf, PAGE_SIZE, "%llu\n",
					get_max_sqnum(ubi));
	else if (attr == &dev_mtd_min_scrub_sqnum)
		ret = snprintf(buf, PAGE_SIZE, "%llu\n",
					ubi_wl_scrub_get_min_sqnum(ubi));
	else
		ret = -EINVAL;

@@ -409,10 +439,45 @@ 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 = count;
	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");
			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);
+12 −0
Original line number Diff line number Diff line
@@ -1105,6 +1105,16 @@ 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;
@@ -1123,6 +1133,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 −0
Original line number Diff line number Diff line
@@ -183,6 +183,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
@@ -195,6 +197,8 @@ struct ubi_wl_entry {
	} u;
	int ec;
	int pnum;
	unsigned int tagged_scrub_all:1;
	unsigned long long sqnum;
};

/**
@@ -624,6 +628,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,6 +926,12 @@ int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *used_e,
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);
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,
+262 −3
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@
#include <linux/crc32.h>
#include <linux/freezer.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include "ubi.h"
#include "wl.h"

@@ -487,6 +488,42 @@ static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
	return err;
}

int ubi_wl_re_erase_peb(struct ubi_device *ubi, int pnum)
{
	int err;
	struct ubi_wl_entry *e;
	struct ubi_ec_hdr *ec_hdr;

	spin_lock(&ubi->wl_lock);
	e = ubi->lookuptbl[pnum];
	spin_unlock(&ubi->wl_lock);

	dbg_wl("Re-erase PEB %d,  EC %u", e->pnum, e->ec);

	err = self_check_ec(ubi, e->pnum, e->ec);
	if (err)
		return -EINVAL;

	ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS);
	if (!ec_hdr)
		return -ENOMEM;

	err = ubi_io_sync_erase(ubi, e->pnum, 0);
	if (err < 0)
		goto out_free;

	dbg_wl("re-erased PEB %d, EC %u", e->pnum, e->ec);

	ec_hdr->ec = cpu_to_be64(e->ec);

	err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr);
	if (err)
		goto out_free;
out_free:
	kfree(ec_hdr);
	return err;
}

/**
 * serve_prot_queue - check if it is time to stop protecting PEBs.
 * @ubi: UBI device description object
@@ -862,9 +899,20 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
	}

	/* The PEB has been successfully moved */
	if (scrubbing)
		ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d), data moved to PEB %d",
	if (scrubbing) {
		spin_lock(&ubi->wl_lock);
		if (e1->tagged_scrub_all) {
			WARN_ON(atomic_read(&ubi->scrub_work_count) <= 0);
			atomic_dec(&ubi->scrub_work_count);
			e1->tagged_scrub_all = 0;
			e2->tagged_scrub_all = 0;
		} else {
			ubi_msg(ubi,
				"scrubbed PEB %d (LEB %d:%d),data moved to PEB %d",
				e1->pnum, vol_id, lnum, e2->pnum);
		}
		spin_unlock(&ubi->wl_lock);
	}
	ubi_free_vid_buf(vidb);

	spin_lock(&ubi->wl_lock);
@@ -1226,6 +1274,7 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum,
retry:
	spin_lock(&ubi->wl_lock);
	e = ubi->lookuptbl[pnum];
	e->sqnum = UBI_UNKNOWN;
	if (e == ubi->move_from) {
		/*
		 * User is putting the physical eraseblock which was selected to
@@ -1262,6 +1311,20 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum,
		} else if (in_wl_tree(e, &ubi->scrub)) {
			self_check_in_wl_tree(ubi, e, &ubi->scrub);
			rb_erase(&e->u.rb, &ubi->scrub);

			/*
			 * Since this PEB has been put we dont need to worry
			 * about it anymore
			 */
			if (e->tagged_scrub_all) {
				int wrk_count;

				wrk_count = atomic_read(&ubi->scrub_work_count);
				WARN_ON(wrk_count <= 0);

				atomic_dec(&ubi->scrub_work_count);
				e->tagged_scrub_all = 0;
			}
		} else if (in_wl_tree(e, &ubi->erroneous)) {
			self_check_in_wl_tree(ubi, e, &ubi->erroneous);
			rb_erase(&e->u.rb, &ubi->erroneous);
@@ -1293,6 +1356,197 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum,
	return err;
}

/**
 * ubi_wl_scrub_get_min_sqnum - Return the minimum sqnum of the used/pq/scrub.
 * @ubi: UBI device description object
 *
 * This function returns the minimum sqnum of the PEB that are currently in use.
 *
 * Return the min sqnum if there are any used PEB's otherwise return ~(0)
 *
 */
unsigned long long ubi_wl_scrub_get_min_sqnum(struct ubi_device *ubi)
{
	int i;
	struct ubi_wl_entry *e, *tmp;
	struct rb_node *node;
	unsigned long long min_sqnum = ~0;

	spin_lock(&ubi->wl_lock);

	/* Go through the pq list */
	for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) {
		list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) {
			if (e->sqnum < min_sqnum)
				min_sqnum = e->sqnum;
		}
	}

	/* Go through used PEB tree */
	for (node = rb_first(&ubi->used); node; node = rb_next(node)) {
		e = rb_entry(node, struct ubi_wl_entry, u.rb);
		self_check_in_wl_tree(ubi, e, &ubi->used);
		if (e->sqnum < min_sqnum)
			min_sqnum = e->sqnum;
	}
	/* Go through scrub PEB tree */
	for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) {
		e = rb_entry(node, struct ubi_wl_entry, u.rb);
		self_check_in_wl_tree(ubi, e, &ubi->scrub);
		if (e->sqnum < min_sqnum)
			min_sqnum = e->sqnum;
	}
	spin_unlock(&ubi->wl_lock);
	return min_sqnum;
}

/**
 * ubi_wl_update_peb_sqnum - Update the vol hdr sqnum of the PEB.
 * @pnum: The PEB number.
 * @vid_hdr: The vol hdr being written to the PEB.
 *
 */
void ubi_wl_update_peb_sqnum(struct ubi_device *ubi, int pnum,
				struct ubi_vid_hdr *vid_hdr)
{
	struct ubi_wl_entry *e;

	spin_lock(&ubi->wl_lock);
	e = ubi->lookuptbl[pnum];
	e->sqnum = be64_to_cpu(vid_hdr->sqnum);
	e->tagged_scrub_all = 0;
	spin_unlock(&ubi->wl_lock);
}

static int is_ubi_readonly(struct ubi_device *ubi)
{
	int is_readonly = 0;

	spin_lock(&ubi->wl_lock);
	if (ubi->ro_mode || !ubi->thread_enabled ||
	    ubi_dbg_is_bgt_disabled(ubi))
		is_readonly = 1;
	spin_unlock(&ubi->wl_lock);

	return is_readonly;
}

/**
 * ubi_wl_scan_all - Scan all PEB's
 * @ubi: UBI device description object
 * @scrub_sqnum: The max seqnum of the PEB to scrub from the used/pq lists
 *
 * This function schedules all device PEBs for scrubbing if the sqnum of the
 * vol hdr is less than the sqnum in the trigger.
 *
 * Return 0 in case of success, (negative) error code otherwise
 *
 */
ssize_t ubi_wl_scrub_all(struct ubi_device *ubi, unsigned long long scrub_sqnum)
{
	struct rb_node *node;
	struct ubi_wl_entry *e, *tmp;
	int scrub_count = 0;
	int total_scrub_count = 0;
	int err, i;

	if (!ubi->lookuptbl) {
		ubi_err(ubi, "lookuptbl is null");
		return -ENOENT;
	}

	if (is_ubi_readonly(ubi)) {
		ubi_err(ubi, "Cannot *Initiate* scrub:background thread disabled or readonly!");
		return -EROFS;
	}

	/* Wait for all currently running work to be done! */
	down_write(&ubi->work_sem);
	spin_lock(&ubi->wl_lock);
	ubi_msg(ubi, "Scrub triggered sqnum = %llu!", scrub_sqnum);

	if (ubi->scrub_in_progress) {
		ubi_err(ubi, "Scrub already in progress, ignoring the trigger");
		spin_unlock(&ubi->wl_lock);
		up_write(&ubi->work_sem); /* Allow new work to start. */
		return -EBUSY;
	}
	ubi->scrub_in_progress = true;

	/* Go through scrub PEB tree and count pending */
	for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) {
		e = rb_entry(node, struct ubi_wl_entry, u.rb);
		self_check_in_wl_tree(ubi, e, &ubi->scrub);
		e->tagged_scrub_all = 1;
		total_scrub_count++;
	}

	/* Move all used pebs to scrub tree */
	node = rb_first(&ubi->used);
	while (node != NULL) {
		e = rb_entry(node, struct ubi_wl_entry, u.rb);
		self_check_in_wl_tree(ubi, e, &ubi->used);
		node = rb_next(node);

		if (e->sqnum > scrub_sqnum)
			continue;
		rb_erase(&e->u.rb, &ubi->used);
		wl_tree_add(e, &ubi->scrub);
		e->tagged_scrub_all = 1;
		scrub_count++;
		total_scrub_count++;
	}

	/* Move all protected pebs to scrub tree */
	for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) {
		list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) {

			if (e->sqnum > scrub_sqnum)
				continue;

			list_del(&e->u.list);
			wl_tree_add(e, &ubi->scrub);
			e->tagged_scrub_all = 1;
			scrub_count++;
			total_scrub_count++;
		}
	}

	atomic_set(&ubi->scrub_work_count, total_scrub_count);
	spin_unlock(&ubi->wl_lock);
	up_write(&ubi->work_sem); /* Allow new work to start. */

	/*
	 * Technically scrubbing is the same as wear-leveling, so it is done
	 * by the WL worker.
	 */
	err = ensure_wear_leveling(ubi, 0);
	if (err) {
		ubi_err(ubi, "Failed to start the WL worker err =%d", err);
		return err;
	}
	ubi_msg(ubi, "Scheduled %d PEB's for scrubbing!", scrub_count);
	ubi_msg(ubi, "Total PEB's for scrub = %d", total_scrub_count);

	/* Wait for scrub to finish */
	while (atomic_read(&ubi->scrub_work_count) > 0) {
		/* Poll every second to check if the scrub work is done */
		msleep(1000);

		if (is_ubi_readonly(ubi)) {
			ubi_err(ubi, "Cannot *Complete* scrub:background thread disabled or readonly!");
			return -EROFS;
		}
		wake_up_process(ubi->bgt_thread);
	}

	spin_lock(&ubi->wl_lock);
	ubi->scrub_in_progress = false;
	spin_unlock(&ubi->wl_lock);
	ubi_msg(ubi, "Done scrubbing %d PEB's!", scrub_count);
	return 0;
}

/**
 * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing.
 * @ubi: UBI device description object
@@ -1622,6 +1876,8 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)

		e->pnum = aeb->pnum;
		e->ec = aeb->ec;
		e->tagged_scrub_all = 0;
		e->sqnum = aeb->sqnum;
		ubi_assert(e->ec >= 0);

		wl_tree_add(e, &ubi->free);
@@ -1644,6 +1900,8 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)

			e->pnum = aeb->pnum;
			e->ec = aeb->ec;
			e->tagged_scrub_all = 0;
			e->sqnum = aeb->sqnum;
			ubi->lookuptbl[e->pnum] = e;

			if (!aeb->scrub) {
@@ -1724,6 +1982,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
	if (err)
		goto out_free;

	ubi->wl_is_inited = 1;
	return 0;

out_free: