ubi: Expose the bitrot interface
Using UBI_IOCRPEB and UBI_IOCSPEB userspace can force reading and scrubbing of PEBs. In case of bitflips UBI will automatically take action and move data to a different PEB. This interface allows a daemon to foster your NAND. Signed-off-by: Richard Weinberger <richard@nod.at>
此提交包含在:
@@ -1440,6 +1440,150 @@ int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum)
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool scrub_possible(struct ubi_device *ubi, struct ubi_wl_entry *e)
|
||||
{
|
||||
if (in_wl_tree(e, &ubi->scrub))
|
||||
return false;
|
||||
else if (in_wl_tree(e, &ubi->erroneous))
|
||||
return false;
|
||||
else if (ubi->move_from == e)
|
||||
return false;
|
||||
else if (ubi->move_to == e)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_bitflip_check - Check an eraseblock for bitflips and scrub it if needed.
|
||||
* @ubi: UBI device description object
|
||||
* @pnum: the physical eraseblock to schedule
|
||||
* @force: dont't read the block, assume bitflips happened and take action.
|
||||
*
|
||||
* This function reads the given eraseblock and checks if bitflips occured.
|
||||
* In case of bitflips, the eraseblock is scheduled for scrubbing.
|
||||
* If scrubbing is forced with @force, the eraseblock is not read,
|
||||
* but scheduled for scrubbing right away.
|
||||
*
|
||||
* Returns:
|
||||
* %EINVAL, PEB is out of range
|
||||
* %ENOENT, PEB is no longer used by UBI
|
||||
* %EBUSY, PEB cannot be checked now or a check is currently running on it
|
||||
* %EAGAIN, bit flips happened but scrubbing is currently not possible
|
||||
* %EUCLEAN, bit flips happened and PEB is scheduled for scrubbing
|
||||
* %0, no bit flips detected
|
||||
*/
|
||||
int ubi_bitflip_check(struct ubi_device *ubi, int pnum, int force)
|
||||
{
|
||||
int err;
|
||||
struct ubi_wl_entry *e;
|
||||
|
||||
if (pnum < 0 || pnum >= ubi->peb_count) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pause all parallel work, otherwise it can happen that the
|
||||
* erase worker frees a wl entry under us.
|
||||
*/
|
||||
down_write(&ubi->work_sem);
|
||||
|
||||
/*
|
||||
* Make sure that the wl entry does not change state while
|
||||
* inspecting it.
|
||||
*/
|
||||
spin_lock(&ubi->wl_lock);
|
||||
e = ubi->lookuptbl[pnum];
|
||||
if (!e) {
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
err = -ENOENT;
|
||||
goto out_resume;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does it make sense to check this PEB?
|
||||
*/
|
||||
if (!scrub_possible(ubi, e)) {
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
err = -EBUSY;
|
||||
goto out_resume;
|
||||
}
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
if (!force) {
|
||||
mutex_lock(&ubi->buf_mutex);
|
||||
err = ubi_io_read(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size);
|
||||
mutex_unlock(&ubi->buf_mutex);
|
||||
}
|
||||
|
||||
if (err == UBI_IO_BITFLIPS || force) {
|
||||
/*
|
||||
* Okay, bit flip happened, let's figure out what we can do.
|
||||
*/
|
||||
spin_lock(&ubi->wl_lock);
|
||||
|
||||
/*
|
||||
* Recheck. We released wl_lock, UBI might have killed the
|
||||
* wl entry under us.
|
||||
*/
|
||||
e = ubi->lookuptbl[pnum];
|
||||
if (!e) {
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
err = -ENOENT;
|
||||
goto out_resume;
|
||||
}
|
||||
|
||||
/*
|
||||
* Need to re-check state
|
||||
*/
|
||||
if (!scrub_possible(ubi, e)) {
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
err = -EBUSY;
|
||||
goto out_resume;
|
||||
}
|
||||
|
||||
if (in_pq(ubi, e)) {
|
||||
prot_queue_del(ubi, e->pnum);
|
||||
wl_tree_add(e, &ubi->scrub);
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
err = ensure_wear_leveling(ubi, 1);
|
||||
} else if (in_wl_tree(e, &ubi->used)) {
|
||||
rb_erase(&e->u.rb, &ubi->used);
|
||||
wl_tree_add(e, &ubi->scrub);
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
err = ensure_wear_leveling(ubi, 1);
|
||||
} else if (in_wl_tree(e, &ubi->free)) {
|
||||
rb_erase(&e->u.rb, &ubi->free);
|
||||
ubi->free_count--;
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
/*
|
||||
* This PEB is empty we can schedule it for
|
||||
* erasure right away. No wear leveling needed.
|
||||
*/
|
||||
err = schedule_erase(ubi, e, UBI_UNKNOWN, UBI_UNKNOWN,
|
||||
force ? 0 : 1, true);
|
||||
} else {
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
err = -EAGAIN;
|
||||
}
|
||||
|
||||
if (!err && !force)
|
||||
err = -EUCLEAN;
|
||||
} else {
|
||||
err = 0;
|
||||
}
|
||||
|
||||
out_resume:
|
||||
up_write(&ubi->work_sem);
|
||||
out:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* tree_destroy - destroy an RB-tree.
|
||||
* @ubi: UBI device description object
|
||||
|
新增問題並參考
封鎖使用者