mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-05 18:39:59 +00:00
ubi: Rework Fastmap attach base code
Introduce a new list to the UBI attach information object to be able to deal better with old and corrupted Fastmap eraseblocks. Also move more Fastmap specific code into fastmap.c. Signed-off-by: Richard Weinberger <richard@nod.at>
This commit is contained in:
parent
be8011053f
commit
fdf10ed710
4 changed files with 165 additions and 45 deletions
|
@ -174,6 +174,40 @@ static int add_corrupted(struct ubi_attach_info *ai, int pnum, int ec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* add_fastmap - add a Fastmap related physical eraseblock.
|
||||
* @ai: attaching information
|
||||
* @pnum: physical eraseblock number the VID header came from
|
||||
* @vid_hdr: the volume identifier header
|
||||
* @ec: erase counter of the physical eraseblock
|
||||
*
|
||||
* This function allocates a 'struct ubi_ainf_peb' object for a Fastamp
|
||||
* physical eraseblock @pnum and adds it to the 'fastmap' list.
|
||||
* Such blocks can be Fastmap super and data blocks from both the most
|
||||
* recent Fastmap we're attaching from or from old Fastmaps which will
|
||||
* be erased.
|
||||
*/
|
||||
static int add_fastmap(struct ubi_attach_info *ai, int pnum,
|
||||
struct ubi_vid_hdr *vid_hdr, int ec)
|
||||
{
|
||||
struct ubi_ainf_peb *aeb;
|
||||
|
||||
aeb = kmem_cache_alloc(ai->aeb_slab_cache, GFP_KERNEL);
|
||||
if (!aeb)
|
||||
return -ENOMEM;
|
||||
|
||||
aeb->pnum = pnum;
|
||||
aeb->vol_id = be32_to_cpu(vidh->vol_id);
|
||||
aeb->sqnum = be64_to_cpu(vidh->sqnum);
|
||||
aeb->ec = ec;
|
||||
list_add(&aeb->u.list, &ai->fastmap);
|
||||
|
||||
dbg_bld("add to fastmap list: PEB %d, vol_id %d, sqnum: %llu", pnum,
|
||||
aeb->vol_id, aeb->sqnum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate_vid_hdr - check volume identifier header.
|
||||
* @ubi: UBI device description object
|
||||
|
@ -822,18 +856,15 @@ static bool vol_ignored(int vol_id)
|
|||
* @ubi: UBI device description object
|
||||
* @ai: attaching information
|
||||
* @pnum: the physical eraseblock number
|
||||
* @vid: The volume ID of the found volume will be stored in this pointer
|
||||
* @sqnum: The sqnum of the found volume will be stored in this pointer
|
||||
*
|
||||
* This function reads UBI headers of PEB @pnum, checks them, and adds
|
||||
* information about this PEB to the corresponding list or RB-tree in the
|
||||
* "attaching info" structure. Returns zero if the physical eraseblock was
|
||||
* successfully handled and a negative error code in case of failure.
|
||||
*/
|
||||
static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
||||
int pnum, int *vid, unsigned long long *sqnum)
|
||||
static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum)
|
||||
{
|
||||
long long uninitialized_var(ec);
|
||||
long long ec;
|
||||
int err, bitflips = 0, vol_id = -1, ec_err = 0;
|
||||
|
||||
dbg_bld("scan PEB %d", pnum);
|
||||
|
@ -1005,10 +1036,6 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
|||
}
|
||||
|
||||
vol_id = be32_to_cpu(vidh->vol_id);
|
||||
if (vid)
|
||||
*vid = vol_id;
|
||||
if (sqnum)
|
||||
*sqnum = be64_to_cpu(vidh->sqnum);
|
||||
if (vol_id > UBI_MAX_VOLUMES && !vol_ignored(vol_id)) {
|
||||
int lnum = be32_to_cpu(vidh->lnum);
|
||||
|
||||
|
@ -1049,7 +1076,12 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
|||
if (ec_err)
|
||||
ubi_warn(ubi, "valid VID header but corrupted EC header at PEB %d",
|
||||
pnum);
|
||||
err = ubi_add_to_av(ubi, ai, pnum, ec, vidh, bitflips);
|
||||
|
||||
if (ubi_is_fm_vol(vol_id))
|
||||
err = add_fastmap(ai, pnum, vidh, ec);
|
||||
else
|
||||
err = ubi_add_to_av(ubi, ai, pnum, ec, vidh, bitflips);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -1198,6 +1230,10 @@ static void destroy_ai(struct ubi_attach_info *ai)
|
|||
list_del(&aeb->u.list);
|
||||
kmem_cache_free(ai->aeb_slab_cache, aeb);
|
||||
}
|
||||
list_for_each_entry_safe(aeb, aeb_tmp, &ai->fastmap, u.list) {
|
||||
list_del(&aeb->u.list);
|
||||
kmem_cache_free(ai->aeb_slab_cache, aeb);
|
||||
}
|
||||
|
||||
/* Destroy the volume RB-tree */
|
||||
rb = ai->volumes.rb_node;
|
||||
|
@ -1257,7 +1293,7 @@ static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
|||
cond_resched();
|
||||
|
||||
dbg_gen("process PEB %d", pnum);
|
||||
err = scan_peb(ubi, ai, pnum, NULL, NULL);
|
||||
err = scan_peb(ubi, ai, pnum);
|
||||
if (err < 0)
|
||||
goto out_vidh;
|
||||
}
|
||||
|
@ -1323,6 +1359,7 @@ static struct ubi_attach_info *alloc_ai(void)
|
|||
INIT_LIST_HEAD(&ai->free);
|
||||
INIT_LIST_HEAD(&ai->erase);
|
||||
INIT_LIST_HEAD(&ai->alien);
|
||||
INIT_LIST_HEAD(&ai->fastmap);
|
||||
ai->volumes = RB_ROOT;
|
||||
ai->aeb_slab_cache = kmem_cache_create("ubi_aeb_slab_cache",
|
||||
sizeof(struct ubi_ainf_peb),
|
||||
|
@ -1349,52 +1386,54 @@ static struct ubi_attach_info *alloc_ai(void)
|
|||
*/
|
||||
static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info **ai)
|
||||
{
|
||||
int err, pnum, fm_anchor = -1;
|
||||
unsigned long long max_sqnum = 0;
|
||||
int err, pnum;
|
||||
struct ubi_attach_info *scan_ai;
|
||||
|
||||
err = -ENOMEM;
|
||||
|
||||
scan_ai = alloc_ai();
|
||||
if (!scan_ai)
|
||||
goto out;
|
||||
|
||||
ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
|
||||
if (!ech)
|
||||
goto out;
|
||||
goto out_ai;
|
||||
|
||||
vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
|
||||
if (!vidh)
|
||||
goto out_ech;
|
||||
|
||||
for (pnum = 0; pnum < UBI_FM_MAX_START; pnum++) {
|
||||
int vol_id = -1;
|
||||
unsigned long long sqnum = -1;
|
||||
cond_resched();
|
||||
|
||||
dbg_gen("process PEB %d", pnum);
|
||||
err = scan_peb(ubi, *ai, pnum, &vol_id, &sqnum);
|
||||
err = scan_peb(ubi, scan_ai, pnum);
|
||||
if (err < 0)
|
||||
goto out_vidh;
|
||||
|
||||
if (vol_id == UBI_FM_SB_VOLUME_ID && sqnum > max_sqnum) {
|
||||
max_sqnum = sqnum;
|
||||
fm_anchor = pnum;
|
||||
}
|
||||
}
|
||||
|
||||
ubi_free_vid_hdr(ubi, vidh);
|
||||
kfree(ech);
|
||||
|
||||
if (fm_anchor < 0)
|
||||
return UBI_NO_FASTMAP;
|
||||
err = ubi_scan_fastmap(ubi, *ai, scan_ai);
|
||||
if (err) {
|
||||
/*
|
||||
* Didn't attach via fastmap, do a full scan but reuse what
|
||||
* we've aready scanned.
|
||||
*/
|
||||
destroy_ai(*ai);
|
||||
*ai = scan_ai;
|
||||
} else
|
||||
destroy_ai(scan_ai);
|
||||
|
||||
destroy_ai(*ai);
|
||||
*ai = alloc_ai();
|
||||
if (!*ai)
|
||||
return -ENOMEM;
|
||||
|
||||
return ubi_scan_fastmap(ubi, *ai, fm_anchor);
|
||||
return err;
|
||||
|
||||
out_vidh:
|
||||
ubi_free_vid_hdr(ubi, vidh);
|
||||
out_ech:
|
||||
kfree(ech);
|
||||
out_ai:
|
||||
destroy_ai(scan_ai);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -849,28 +849,58 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* find_fm_anchor - find the most recent Fastmap superblock (anchor)
|
||||
* @ai: UBI attach info to be filled
|
||||
*/
|
||||
static int find_fm_anchor(struct ubi_attach_info *ai)
|
||||
{
|
||||
int ret = -1;
|
||||
struct ubi_ainf_peb *aeb;
|
||||
unsigned long long max_sqnum = 0;
|
||||
|
||||
list_for_each_entry(aeb, &ai->fastmap, u.list) {
|
||||
if (aeb->vol_id == UBI_FM_SB_VOLUME_ID && aeb->sqnum > max_sqnum) {
|
||||
max_sqnum = aeb->sqnum;
|
||||
ret = aeb->pnum;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_scan_fastmap - scan the fastmap.
|
||||
* @ubi: UBI device object
|
||||
* @ai: UBI attach info to be filled
|
||||
* @fm_anchor: The fastmap starts at this PEB
|
||||
* @scan_ai: UBI attach info from the first 64 PEBs,
|
||||
* used to find the most recent Fastmap data structure
|
||||
*
|
||||
* Returns 0 on success, UBI_NO_FASTMAP if no fastmap was found,
|
||||
* UBI_BAD_FASTMAP if one was found but is not usable.
|
||||
* < 0 indicates an internal error.
|
||||
*/
|
||||
int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
||||
int fm_anchor)
|
||||
struct ubi_attach_info *scan_ai)
|
||||
{
|
||||
struct ubi_fm_sb *fmsb, *fmsb2;
|
||||
struct ubi_vid_hdr *vh;
|
||||
struct ubi_ec_hdr *ech;
|
||||
struct ubi_fastmap_layout *fm;
|
||||
int i, used_blocks, pnum, ret = 0;
|
||||
struct ubi_ainf_peb *tmp_aeb, *aeb;
|
||||
int i, used_blocks, pnum, fm_anchor, ret = 0;
|
||||
size_t fm_size;
|
||||
__be32 crc, tmp_crc;
|
||||
unsigned long long sqnum = 0;
|
||||
|
||||
fm_anchor = find_fm_anchor(scan_ai);
|
||||
if (fm_anchor < 0)
|
||||
return UBI_NO_FASTMAP;
|
||||
|
||||
/* Move all (possible) fastmap blocks into our new attach structure. */
|
||||
list_for_each_entry_safe(aeb, tmp_aeb, &scan_ai->fastmap, u.list)
|
||||
list_move_tail(&aeb->u.list, &ai->fastmap);
|
||||
|
||||
down_write(&ubi->fm_protect);
|
||||
memset(ubi->fm_buf, 0, ubi->fm_size);
|
||||
|
||||
|
|
|
@ -703,6 +703,8 @@ struct ubi_ainf_volume {
|
|||
* @erase: list of physical eraseblocks which have to be erased
|
||||
* @alien: list of physical eraseblocks which should not be used by UBI (e.g.,
|
||||
* those belonging to "preserve"-compatible internal volumes)
|
||||
* @fastmap: list of physical eraseblocks which relate to fastmap (e.g.,
|
||||
* eraseblocks of the current and not yet erased old fastmap blocks)
|
||||
* @corr_peb_count: count of PEBs in the @corr list
|
||||
* @empty_peb_count: count of PEBs which are presumably empty (contain only
|
||||
* 0xFF bytes)
|
||||
|
@ -731,6 +733,7 @@ struct ubi_attach_info {
|
|||
struct list_head free;
|
||||
struct list_head erase;
|
||||
struct list_head alien;
|
||||
struct list_head fastmap;
|
||||
int corr_peb_count;
|
||||
int empty_peb_count;
|
||||
int alien_peb_count;
|
||||
|
@ -911,7 +914,7 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
|
|||
size_t ubi_calc_fm_size(struct ubi_device *ubi);
|
||||
int ubi_update_fastmap(struct ubi_device *ubi);
|
||||
int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
||||
int fm_anchor);
|
||||
struct ubi_attach_info *scan_ai);
|
||||
#else
|
||||
static inline int ubi_update_fastmap(struct ubi_device *ubi) { return 0; }
|
||||
#endif
|
||||
|
@ -1120,4 +1123,27 @@ static inline bool ubi_is_fm_vol(int vol_id)
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubi_find_fm_block - check whether a PEB is part of the current Fastmap.
|
||||
* @ubi: UBI device description object
|
||||
* @pnum: physical eraseblock to look for
|
||||
*
|
||||
* This function returns a wear leveling object if @pnum relates to the current
|
||||
* fastmap, @NULL otherwise.
|
||||
*/
|
||||
static inline struct ubi_wl_entry *ubi_find_fm_block(const struct ubi_device *ubi,
|
||||
int pnum)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (ubi->fm) {
|
||||
for (i = 0; i < ubi->fm->used_blocks; i++) {
|
||||
if (ubi->fm->e[i]->pnum == pnum)
|
||||
return ubi->fm->e[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* !__UBI_UBI_H__ */
|
||||
|
|
|
@ -1598,19 +1598,44 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
|
|||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(aeb, &ai->fastmap, u.list) {
|
||||
cond_resched();
|
||||
|
||||
e = ubi_find_fm_block(ubi, aeb->pnum);
|
||||
|
||||
if (e) {
|
||||
ubi_assert(!ubi->lookuptbl[e->pnum]);
|
||||
ubi->lookuptbl[e->pnum] = e;
|
||||
} else {
|
||||
/*
|
||||
* Usually old Fastmap PEBs are scheduled for erasure
|
||||
* and we don't have to care about them but if we face
|
||||
* an power cut before scheduling them we need to
|
||||
* take care of them here.
|
||||
*/
|
||||
if (ubi->lookuptbl[aeb->pnum])
|
||||
continue;
|
||||
|
||||
e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL);
|
||||
if (!e)
|
||||
goto out_free;
|
||||
|
||||
e->pnum = aeb->pnum;
|
||||
e->ec = aeb->ec;
|
||||
ubi_assert(!ubi->lookuptbl[e->pnum]);
|
||||
ubi->lookuptbl[e->pnum] = e;
|
||||
if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) {
|
||||
wl_entry_destroy(ubi, e);
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
found_pebs++;
|
||||
}
|
||||
|
||||
dbg_wl("found %i PEBs", found_pebs);
|
||||
|
||||
if (ubi->fm) {
|
||||
ubi_assert(ubi->good_peb_count ==
|
||||
found_pebs + ubi->fm->used_blocks);
|
||||
|
||||
for (i = 0; i < ubi->fm->used_blocks; i++) {
|
||||
e = ubi->fm->e[i];
|
||||
ubi->lookuptbl[e->pnum] = e;
|
||||
}
|
||||
}
|
||||
else
|
||||
ubi_assert(ubi->good_peb_count == found_pebs);
|
||||
ubi_assert(ubi->good_peb_count == found_pebs);
|
||||
|
||||
reserved_pebs = WL_RESERVED_PEBS;
|
||||
ubi_fastmap_init(ubi, &reserved_pebs);
|
||||
|
|
Loading…
Reference in a new issue