2012-09-26 22:51:49 +07:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2012 Linutronix GmbH
|
2014-09-22 20:36:40 +07:00
|
|
|
* Copyright (c) 2014 sigma star gmbh
|
2012-09-26 22:51:49 +07:00
|
|
|
* Author: Richard Weinberger <richard@nod.at>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; version 2.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
|
|
|
* the GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/crc32.h>
|
|
|
|
#include "ubi.h"
|
|
|
|
|
2014-09-22 20:36:40 +07:00
|
|
|
/**
|
|
|
|
* init_seen - allocate memory for used for debugging.
|
|
|
|
* @ubi: UBI device description object
|
|
|
|
*/
|
|
|
|
static inline int *init_seen(struct ubi_device *ubi)
|
|
|
|
{
|
|
|
|
int *ret;
|
|
|
|
|
|
|
|
if (!ubi_dbg_chk_fastmap(ubi))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ret = kcalloc(ubi->peb_count, sizeof(int), GFP_KERNEL);
|
|
|
|
if (!ret)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* free_seen - free the seen logic integer array.
|
|
|
|
* @seen: integer array of @ubi->peb_count size
|
|
|
|
*/
|
|
|
|
static inline void free_seen(int *seen)
|
|
|
|
{
|
|
|
|
kfree(seen);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* set_seen - mark a PEB as seen.
|
|
|
|
* @ubi: UBI device description object
|
|
|
|
* @pnum: The PEB to be makred as seen
|
|
|
|
* @seen: integer array of @ubi->peb_count size
|
|
|
|
*/
|
|
|
|
static inline void set_seen(struct ubi_device *ubi, int pnum, int *seen)
|
|
|
|
{
|
|
|
|
if (!ubi_dbg_chk_fastmap(ubi) || !seen)
|
|
|
|
return;
|
|
|
|
|
|
|
|
seen[pnum] = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* self_check_seen - check whether all PEB have been seen by fastmap.
|
|
|
|
* @ubi: UBI device description object
|
|
|
|
* @seen: integer array of @ubi->peb_count size
|
|
|
|
*/
|
|
|
|
static int self_check_seen(struct ubi_device *ubi, int *seen)
|
|
|
|
{
|
|
|
|
int pnum, ret = 0;
|
|
|
|
|
|
|
|
if (!ubi_dbg_chk_fastmap(ubi) || !seen)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (pnum = 0; pnum < ubi->peb_count; pnum++) {
|
|
|
|
if (!seen[pnum] && ubi->lookuptbl[pnum]) {
|
|
|
|
ubi_err(ubi, "self-check failed for PEB %d, fastmap didn't see it", pnum);
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-09-26 22:51:49 +07:00
|
|
|
/**
|
|
|
|
* ubi_calc_fm_size - calculates the fastmap size in bytes for an UBI device.
|
|
|
|
* @ubi: UBI device description object
|
|
|
|
*/
|
|
|
|
size_t ubi_calc_fm_size(struct ubi_device *ubi)
|
|
|
|
{
|
|
|
|
size_t size;
|
|
|
|
|
2015-05-26 17:07:06 +07:00
|
|
|
size = sizeof(struct ubi_fm_sb) +
|
|
|
|
sizeof(struct ubi_fm_hdr) +
|
|
|
|
sizeof(struct ubi_fm_scan_pool) +
|
|
|
|
sizeof(struct ubi_fm_scan_pool) +
|
|
|
|
(ubi->peb_count * sizeof(struct ubi_fm_ec)) +
|
|
|
|
(sizeof(struct ubi_fm_eba) +
|
|
|
|
(ubi->peb_count * sizeof(__be32))) +
|
2012-09-26 22:51:49 +07:00
|
|
|
sizeof(struct ubi_fm_volhdr) * UBI_MAX_VOLUMES;
|
|
|
|
return roundup(size, ubi->leb_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* new_fm_vhdr - allocate a new volume header for fastmap usage.
|
|
|
|
* @ubi: UBI device description object
|
|
|
|
* @vol_id: the VID of the new header
|
|
|
|
*
|
|
|
|
* Returns a new struct ubi_vid_hdr on success.
|
|
|
|
* NULL indicates out of memory.
|
|
|
|
*/
|
|
|
|
static struct ubi_vid_hdr *new_fm_vhdr(struct ubi_device *ubi, int vol_id)
|
|
|
|
{
|
|
|
|
struct ubi_vid_hdr *new;
|
|
|
|
|
|
|
|
new = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
|
|
|
|
if (!new)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
new->vol_type = UBI_VID_DYNAMIC;
|
|
|
|
new->vol_id = cpu_to_be32(vol_id);
|
|
|
|
|
|
|
|
/* UBI implementations without fastmap support have to delete the
|
|
|
|
* fastmap.
|
|
|
|
*/
|
|
|
|
new->compat = UBI_COMPAT_DELETE;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* add_aeb - create and add a attach erase block to a given list.
|
|
|
|
* @ai: UBI attach info object
|
|
|
|
* @list: the target list
|
|
|
|
* @pnum: PEB number of the new attach erase block
|
|
|
|
* @ec: erease counter of the new LEB
|
|
|
|
* @scrub: scrub this PEB after attaching
|
|
|
|
*
|
|
|
|
* Returns 0 on success, < 0 indicates an internal error.
|
|
|
|
*/
|
|
|
|
static int add_aeb(struct ubi_attach_info *ai, struct list_head *list,
|
|
|
|
int pnum, int ec, int scrub)
|
|
|
|
{
|
|
|
|
struct ubi_ainf_peb *aeb;
|
|
|
|
|
|
|
|
aeb = kmem_cache_alloc(ai->aeb_slab_cache, GFP_KERNEL);
|
|
|
|
if (!aeb)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
aeb->pnum = pnum;
|
|
|
|
aeb->ec = ec;
|
|
|
|
aeb->lnum = -1;
|
|
|
|
aeb->scrub = scrub;
|
|
|
|
aeb->copy_flag = aeb->sqnum = 0;
|
|
|
|
|
|
|
|
ai->ec_sum += aeb->ec;
|
|
|
|
ai->ec_count++;
|
|
|
|
|
|
|
|
if (ai->max_ec < aeb->ec)
|
|
|
|
ai->max_ec = aeb->ec;
|
|
|
|
|
|
|
|
if (ai->min_ec > aeb->ec)
|
|
|
|
ai->min_ec = aeb->ec;
|
|
|
|
|
|
|
|
list_add_tail(&aeb->u.list, list);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* add_vol - create and add a new volume to ubi_attach_info.
|
|
|
|
* @ai: ubi_attach_info object
|
|
|
|
* @vol_id: VID of the new volume
|
|
|
|
* @used_ebs: number of used EBS
|
|
|
|
* @data_pad: data padding value of the new volume
|
|
|
|
* @vol_type: volume type
|
|
|
|
* @last_eb_bytes: number of bytes in the last LEB
|
|
|
|
*
|
|
|
|
* Returns the new struct ubi_ainf_volume on success.
|
|
|
|
* NULL indicates an error.
|
|
|
|
*/
|
|
|
|
static struct ubi_ainf_volume *add_vol(struct ubi_attach_info *ai, int vol_id,
|
|
|
|
int used_ebs, int data_pad, u8 vol_type,
|
|
|
|
int last_eb_bytes)
|
|
|
|
{
|
|
|
|
struct ubi_ainf_volume *av;
|
|
|
|
struct rb_node **p = &ai->volumes.rb_node, *parent = NULL;
|
|
|
|
|
|
|
|
while (*p) {
|
|
|
|
parent = *p;
|
|
|
|
av = rb_entry(parent, struct ubi_ainf_volume, rb);
|
|
|
|
|
2014-06-24 14:25:18 +07:00
|
|
|
if (vol_id > av->vol_id)
|
2012-09-26 22:51:49 +07:00
|
|
|
p = &(*p)->rb_left;
|
2015-05-26 17:07:09 +07:00
|
|
|
else if (vol_id < av->vol_id)
|
2012-09-26 22:51:49 +07:00
|
|
|
p = &(*p)->rb_right;
|
2015-05-26 17:07:09 +07:00
|
|
|
else
|
|
|
|
return ERR_PTR(-EINVAL);
|
2012-09-26 22:51:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
av = kmalloc(sizeof(struct ubi_ainf_volume), GFP_KERNEL);
|
|
|
|
if (!av)
|
|
|
|
goto out;
|
|
|
|
|
2014-10-25 18:26:49 +07:00
|
|
|
av->highest_lnum = av->leb_count = av->used_ebs = 0;
|
2012-09-26 22:51:49 +07:00
|
|
|
av->vol_id = vol_id;
|
|
|
|
av->data_pad = data_pad;
|
|
|
|
av->last_data_size = last_eb_bytes;
|
|
|
|
av->compat = 0;
|
|
|
|
av->vol_type = vol_type;
|
|
|
|
av->root = RB_ROOT;
|
2014-10-25 18:26:49 +07:00
|
|
|
if (av->vol_type == UBI_STATIC_VOLUME)
|
|
|
|
av->used_ebs = used_ebs;
|
2012-09-26 22:51:49 +07:00
|
|
|
|
|
|
|
dbg_bld("found volume (ID %i)", vol_id);
|
|
|
|
|
|
|
|
rb_link_node(&av->rb, parent, p);
|
|
|
|
rb_insert_color(&av->rb, &ai->volumes);
|
|
|
|
|
|
|
|
out:
|
|
|
|
return av;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* assign_aeb_to_av - assigns a SEB to a given ainf_volume and removes it
|
|
|
|
* from it's original list.
|
|
|
|
* @ai: ubi_attach_info object
|
|
|
|
* @aeb: the to be assigned SEB
|
|
|
|
* @av: target scan volume
|
|
|
|
*/
|
|
|
|
static void assign_aeb_to_av(struct ubi_attach_info *ai,
|
|
|
|
struct ubi_ainf_peb *aeb,
|
|
|
|
struct ubi_ainf_volume *av)
|
|
|
|
{
|
|
|
|
struct ubi_ainf_peb *tmp_aeb;
|
|
|
|
struct rb_node **p = &ai->volumes.rb_node, *parent = NULL;
|
|
|
|
|
|
|
|
p = &av->root.rb_node;
|
|
|
|
while (*p) {
|
|
|
|
parent = *p;
|
|
|
|
|
|
|
|
tmp_aeb = rb_entry(parent, struct ubi_ainf_peb, u.rb);
|
|
|
|
if (aeb->lnum != tmp_aeb->lnum) {
|
|
|
|
if (aeb->lnum < tmp_aeb->lnum)
|
|
|
|
p = &(*p)->rb_left;
|
|
|
|
else
|
|
|
|
p = &(*p)->rb_right;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_del(&aeb->u.list);
|
|
|
|
av->leb_count++;
|
|
|
|
|
|
|
|
rb_link_node(&aeb->u.rb, parent, p);
|
|
|
|
rb_insert_color(&aeb->u.rb, &av->root);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* update_vol - inserts or updates a LEB which was found a pool.
|
|
|
|
* @ubi: the UBI device object
|
|
|
|
* @ai: attach info object
|
|
|
|
* @av: the volume this LEB belongs to
|
|
|
|
* @new_vh: the volume header derived from new_aeb
|
|
|
|
* @new_aeb: the AEB to be examined
|
|
|
|
*
|
|
|
|
* Returns 0 on success, < 0 indicates an internal error.
|
|
|
|
*/
|
|
|
|
static int update_vol(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
|
|
|
struct ubi_ainf_volume *av, struct ubi_vid_hdr *new_vh,
|
|
|
|
struct ubi_ainf_peb *new_aeb)
|
|
|
|
{
|
|
|
|
struct rb_node **p = &av->root.rb_node, *parent = NULL;
|
|
|
|
struct ubi_ainf_peb *aeb, *victim;
|
|
|
|
int cmp_res;
|
|
|
|
|
|
|
|
while (*p) {
|
|
|
|
parent = *p;
|
|
|
|
aeb = rb_entry(parent, struct ubi_ainf_peb, u.rb);
|
|
|
|
|
|
|
|
if (be32_to_cpu(new_vh->lnum) != aeb->lnum) {
|
|
|
|
if (be32_to_cpu(new_vh->lnum) < aeb->lnum)
|
|
|
|
p = &(*p)->rb_left;
|
|
|
|
else
|
|
|
|
p = &(*p)->rb_right;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This case can happen if the fastmap gets written
|
|
|
|
* because of a volume change (creation, deletion, ..).
|
|
|
|
* Then a PEB can be within the persistent EBA and the pool.
|
|
|
|
*/
|
|
|
|
if (aeb->pnum == new_aeb->pnum) {
|
|
|
|
ubi_assert(aeb->lnum == new_aeb->lnum);
|
|
|
|
kmem_cache_free(ai->aeb_slab_cache, new_aeb);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmp_res = ubi_compare_lebs(ubi, aeb, new_aeb->pnum, new_vh);
|
|
|
|
if (cmp_res < 0)
|
|
|
|
return cmp_res;
|
|
|
|
|
|
|
|
/* new_aeb is newer */
|
|
|
|
if (cmp_res & 1) {
|
|
|
|
victim = kmem_cache_alloc(ai->aeb_slab_cache,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!victim)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
victim->ec = aeb->ec;
|
|
|
|
victim->pnum = aeb->pnum;
|
|
|
|
list_add_tail(&victim->u.list, &ai->erase);
|
|
|
|
|
|
|
|
if (av->highest_lnum == be32_to_cpu(new_vh->lnum))
|
2015-06-03 08:37:38 +07:00
|
|
|
av->last_data_size =
|
2012-09-26 22:51:49 +07:00
|
|
|
be32_to_cpu(new_vh->data_size);
|
|
|
|
|
|
|
|
dbg_bld("vol %i: AEB %i's PEB %i is the newer",
|
|
|
|
av->vol_id, aeb->lnum, new_aeb->pnum);
|
|
|
|
|
|
|
|
aeb->ec = new_aeb->ec;
|
|
|
|
aeb->pnum = new_aeb->pnum;
|
|
|
|
aeb->copy_flag = new_vh->copy_flag;
|
|
|
|
aeb->scrub = new_aeb->scrub;
|
|
|
|
kmem_cache_free(ai->aeb_slab_cache, new_aeb);
|
|
|
|
|
|
|
|
/* new_aeb is older */
|
|
|
|
} else {
|
|
|
|
dbg_bld("vol %i: AEB %i's PEB %i is old, dropping it",
|
|
|
|
av->vol_id, aeb->lnum, new_aeb->pnum);
|
|
|
|
list_add_tail(&new_aeb->u.list, &ai->erase);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* This LEB is new, let's add it to the volume */
|
|
|
|
|
|
|
|
if (av->highest_lnum <= be32_to_cpu(new_vh->lnum)) {
|
|
|
|
av->highest_lnum = be32_to_cpu(new_vh->lnum);
|
|
|
|
av->last_data_size = be32_to_cpu(new_vh->data_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (av->vol_type == UBI_STATIC_VOLUME)
|
|
|
|
av->used_ebs = be32_to_cpu(new_vh->used_ebs);
|
|
|
|
|
|
|
|
av->leb_count++;
|
|
|
|
|
|
|
|
rb_link_node(&new_aeb->u.rb, parent, p);
|
|
|
|
rb_insert_color(&new_aeb->u.rb, &av->root);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* process_pool_aeb - we found a non-empty PEB in a pool.
|
|
|
|
* @ubi: UBI device object
|
|
|
|
* @ai: attach info object
|
|
|
|
* @new_vh: the volume header derived from new_aeb
|
|
|
|
* @new_aeb: the AEB to be examined
|
|
|
|
*
|
|
|
|
* Returns 0 on success, < 0 indicates an internal error.
|
|
|
|
*/
|
|
|
|
static int process_pool_aeb(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
|
|
|
struct ubi_vid_hdr *new_vh,
|
|
|
|
struct ubi_ainf_peb *new_aeb)
|
|
|
|
{
|
|
|
|
struct ubi_ainf_volume *av, *tmp_av = NULL;
|
|
|
|
struct rb_node **p = &ai->volumes.rb_node, *parent = NULL;
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
if (be32_to_cpu(new_vh->vol_id) == UBI_FM_SB_VOLUME_ID ||
|
|
|
|
be32_to_cpu(new_vh->vol_id) == UBI_FM_DATA_VOLUME_ID) {
|
|
|
|
kmem_cache_free(ai->aeb_slab_cache, new_aeb);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the volume this SEB belongs to */
|
|
|
|
while (*p) {
|
|
|
|
parent = *p;
|
|
|
|
tmp_av = rb_entry(parent, struct ubi_ainf_volume, rb);
|
|
|
|
|
|
|
|
if (be32_to_cpu(new_vh->vol_id) > tmp_av->vol_id)
|
|
|
|
p = &(*p)->rb_left;
|
|
|
|
else if (be32_to_cpu(new_vh->vol_id) < tmp_av->vol_id)
|
|
|
|
p = &(*p)->rb_right;
|
|
|
|
else {
|
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
av = tmp_av;
|
|
|
|
else {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "orphaned volume in fastmap pool!");
|
2014-09-09 19:25:01 +07:00
|
|
|
kmem_cache_free(ai->aeb_slab_cache, new_aeb);
|
2012-09-26 22:51:49 +07:00
|
|
|
return UBI_BAD_FASTMAP;
|
|
|
|
}
|
|
|
|
|
|
|
|
ubi_assert(be32_to_cpu(new_vh->vol_id) == av->vol_id);
|
|
|
|
|
|
|
|
return update_vol(ubi, ai, av, new_vh, new_aeb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* unmap_peb - unmap a PEB.
|
|
|
|
* If fastmap detects a free PEB in the pool it has to check whether
|
|
|
|
* this PEB has been unmapped after writing the fastmap.
|
|
|
|
*
|
|
|
|
* @ai: UBI attach info object
|
|
|
|
* @pnum: The PEB to be unmapped
|
|
|
|
*/
|
|
|
|
static void unmap_peb(struct ubi_attach_info *ai, int pnum)
|
|
|
|
{
|
|
|
|
struct ubi_ainf_volume *av;
|
|
|
|
struct rb_node *node, *node2;
|
|
|
|
struct ubi_ainf_peb *aeb;
|
|
|
|
|
|
|
|
for (node = rb_first(&ai->volumes); node; node = rb_next(node)) {
|
|
|
|
av = rb_entry(node, struct ubi_ainf_volume, rb);
|
|
|
|
|
|
|
|
for (node2 = rb_first(&av->root); node2;
|
|
|
|
node2 = rb_next(node2)) {
|
|
|
|
aeb = rb_entry(node2, struct ubi_ainf_peb, u.rb);
|
|
|
|
if (aeb->pnum == pnum) {
|
|
|
|
rb_erase(&aeb->u.rb, &av->root);
|
2014-10-24 20:22:05 +07:00
|
|
|
av->leb_count--;
|
2012-09-26 22:51:49 +07:00
|
|
|
kmem_cache_free(ai->aeb_slab_cache, aeb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* scan_pool - scans a pool for changed (no longer empty PEBs).
|
|
|
|
* @ubi: UBI device object
|
|
|
|
* @ai: attach info object
|
|
|
|
* @pebs: an array of all PEB numbers in the to be scanned pool
|
|
|
|
* @pool_size: size of the pool (number of entries in @pebs)
|
|
|
|
* @max_sqnum: pointer to the maximal sequence number
|
|
|
|
* @free: list of PEBs which are most likely free (and go into @ai->free)
|
|
|
|
*
|
|
|
|
* Returns 0 on success, if the pool is unusable UBI_BAD_FASTMAP is returned.
|
|
|
|
* < 0 indicates an internal error.
|
|
|
|
*/
|
|
|
|
static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
2015-10-18 00:55:55 +07:00
|
|
|
__be32 *pebs, int pool_size, unsigned long long *max_sqnum,
|
2014-10-08 02:39:20 +07:00
|
|
|
struct list_head *free)
|
2012-09-26 22:51:49 +07:00
|
|
|
{
|
|
|
|
struct ubi_vid_hdr *vh;
|
|
|
|
struct ubi_ec_hdr *ech;
|
2014-10-08 02:39:20 +07:00
|
|
|
struct ubi_ainf_peb *new_aeb;
|
|
|
|
int i, pnum, err, ret = 0;
|
2012-09-26 22:51:49 +07:00
|
|
|
|
|
|
|
ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
|
|
|
|
if (!ech)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
|
|
|
|
if (!vh) {
|
|
|
|
kfree(ech);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbg_bld("scanning fastmap pool: size = %i", pool_size);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now scan all PEBs in the pool to find changes which have been made
|
|
|
|
* after the creation of the fastmap
|
|
|
|
*/
|
|
|
|
for (i = 0; i < pool_size; i++) {
|
|
|
|
int scrub = 0;
|
2013-09-28 20:55:13 +07:00
|
|
|
int image_seq;
|
2012-09-26 22:51:49 +07:00
|
|
|
|
|
|
|
pnum = be32_to_cpu(pebs[i]);
|
|
|
|
|
|
|
|
if (ubi_io_is_bad(ubi, pnum)) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad PEB in fastmap pool!");
|
2012-09-26 22:51:49 +07:00
|
|
|
ret = UBI_BAD_FASTMAP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
|
|
|
|
if (err && err != UBI_IO_BITFLIPS) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "unable to read EC header! PEB:%i err:%i",
|
2012-09-26 22:51:49 +07:00
|
|
|
pnum, err);
|
|
|
|
ret = err > 0 ? UBI_BAD_FASTMAP : err;
|
|
|
|
goto out;
|
2014-05-21 12:35:38 +07:00
|
|
|
} else if (err == UBI_IO_BITFLIPS)
|
2012-09-26 22:51:49 +07:00
|
|
|
scrub = 1;
|
|
|
|
|
2013-09-28 20:55:13 +07:00
|
|
|
/*
|
|
|
|
* Older UBI implementations have image_seq set to zero, so
|
|
|
|
* we shouldn't fail if image_seq == 0.
|
|
|
|
*/
|
|
|
|
image_seq = be32_to_cpu(ech->image_seq);
|
|
|
|
|
|
|
|
if (image_seq && (image_seq != ubi->image_seq)) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad image seq: 0x%x, expected: 0x%x",
|
2012-09-26 22:51:49 +07:00
|
|
|
be32_to_cpu(ech->image_seq), ubi->image_seq);
|
2013-09-28 20:55:11 +07:00
|
|
|
ret = UBI_BAD_FASTMAP;
|
2012-09-26 22:51:49 +07:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = ubi_io_read_vid_hdr(ubi, pnum, vh, 0);
|
|
|
|
if (err == UBI_IO_FF || err == UBI_IO_FF_BITFLIPS) {
|
|
|
|
unsigned long long ec = be64_to_cpu(ech->ec);
|
|
|
|
unmap_peb(ai, pnum);
|
|
|
|
dbg_bld("Adding PEB to free: %i", pnum);
|
|
|
|
if (err == UBI_IO_FF_BITFLIPS)
|
|
|
|
add_aeb(ai, free, pnum, ec, 1);
|
|
|
|
else
|
|
|
|
add_aeb(ai, free, pnum, ec, 0);
|
|
|
|
continue;
|
|
|
|
} else if (err == 0 || err == UBI_IO_BITFLIPS) {
|
|
|
|
dbg_bld("Found non empty PEB:%i in pool", pnum);
|
|
|
|
|
|
|
|
if (err == UBI_IO_BITFLIPS)
|
|
|
|
scrub = 1;
|
|
|
|
|
|
|
|
new_aeb = kmem_cache_alloc(ai->aeb_slab_cache,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!new_aeb) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_aeb->ec = be64_to_cpu(ech->ec);
|
|
|
|
new_aeb->pnum = pnum;
|
|
|
|
new_aeb->lnum = be32_to_cpu(vh->lnum);
|
|
|
|
new_aeb->sqnum = be64_to_cpu(vh->sqnum);
|
|
|
|
new_aeb->copy_flag = vh->copy_flag;
|
|
|
|
new_aeb->scrub = scrub;
|
|
|
|
|
|
|
|
if (*max_sqnum < new_aeb->sqnum)
|
|
|
|
*max_sqnum = new_aeb->sqnum;
|
|
|
|
|
|
|
|
err = process_pool_aeb(ubi, ai, vh, new_aeb);
|
|
|
|
if (err) {
|
|
|
|
ret = err > 0 ? UBI_BAD_FASTMAP : err;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* We are paranoid and fall back to scanning mode */
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "fastmap pool PEBs contains damaged PEBs!");
|
2012-09-26 22:51:49 +07:00
|
|
|
ret = err > 0 ? UBI_BAD_FASTMAP : err;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
ubi_free_vid_hdr(ubi, vh);
|
|
|
|
kfree(ech);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* count_fastmap_pebs - Counts the PEBs found by fastmap.
|
|
|
|
* @ai: The UBI attach info object
|
|
|
|
*/
|
|
|
|
static int count_fastmap_pebs(struct ubi_attach_info *ai)
|
|
|
|
{
|
|
|
|
struct ubi_ainf_peb *aeb;
|
|
|
|
struct ubi_ainf_volume *av;
|
|
|
|
struct rb_node *rb1, *rb2;
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
list_for_each_entry(aeb, &ai->erase, u.list)
|
|
|
|
n++;
|
|
|
|
|
|
|
|
list_for_each_entry(aeb, &ai->free, u.list)
|
|
|
|
n++;
|
|
|
|
|
|
|
|
ubi_rb_for_each_entry(rb1, av, &ai->volumes, rb)
|
|
|
|
ubi_rb_for_each_entry(rb2, aeb, &av->root, u.rb)
|
|
|
|
n++;
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ubi_attach_fastmap - creates ubi_attach_info from a fastmap.
|
|
|
|
* @ubi: UBI device object
|
|
|
|
* @ai: UBI attach info object
|
|
|
|
* @fm: the fastmap to be attached
|
|
|
|
*
|
|
|
|
* Returns 0 on success, UBI_BAD_FASTMAP if the found fastmap was unusable.
|
|
|
|
* < 0 indicates an internal error.
|
|
|
|
*/
|
|
|
|
static int ubi_attach_fastmap(struct ubi_device *ubi,
|
|
|
|
struct ubi_attach_info *ai,
|
|
|
|
struct ubi_fastmap_layout *fm)
|
|
|
|
{
|
2014-10-08 02:39:20 +07:00
|
|
|
struct list_head used, free;
|
2012-09-26 22:51:49 +07:00
|
|
|
struct ubi_ainf_volume *av;
|
|
|
|
struct ubi_ainf_peb *aeb, *tmp_aeb, *_tmp_aeb;
|
|
|
|
struct ubi_fm_sb *fmsb;
|
|
|
|
struct ubi_fm_hdr *fmhdr;
|
2015-05-26 17:07:07 +07:00
|
|
|
struct ubi_fm_scan_pool *fmpl, *fmpl_wl;
|
2012-09-26 22:51:49 +07:00
|
|
|
struct ubi_fm_ec *fmec;
|
|
|
|
struct ubi_fm_volhdr *fmvhdr;
|
|
|
|
struct ubi_fm_eba *fm_eba;
|
|
|
|
int ret, i, j, pool_size, wl_pool_size;
|
|
|
|
size_t fm_pos = 0, fm_size = ubi->fm_size;
|
|
|
|
unsigned long long max_sqnum = 0;
|
|
|
|
void *fm_raw = ubi->fm_buf;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&used);
|
|
|
|
INIT_LIST_HEAD(&free);
|
|
|
|
ai->min_ec = UBI_MAX_ERASECOUNTER;
|
|
|
|
|
|
|
|
fmsb = (struct ubi_fm_sb *)(fm_raw);
|
|
|
|
ai->max_sqnum = fmsb->sqnum;
|
|
|
|
fm_pos += sizeof(struct ubi_fm_sb);
|
|
|
|
if (fm_pos >= fm_size)
|
|
|
|
goto fail_bad;
|
|
|
|
|
|
|
|
fmhdr = (struct ubi_fm_hdr *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*fmhdr);
|
|
|
|
if (fm_pos >= fm_size)
|
|
|
|
goto fail_bad;
|
|
|
|
|
|
|
|
if (be32_to_cpu(fmhdr->magic) != UBI_FM_HDR_MAGIC) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad fastmap header magic: 0x%x, expected: 0x%x",
|
2012-09-26 22:51:49 +07:00
|
|
|
be32_to_cpu(fmhdr->magic), UBI_FM_HDR_MAGIC);
|
|
|
|
goto fail_bad;
|
|
|
|
}
|
|
|
|
|
2015-05-26 17:07:07 +07:00
|
|
|
fmpl = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*fmpl);
|
2012-09-26 22:51:49 +07:00
|
|
|
if (fm_pos >= fm_size)
|
|
|
|
goto fail_bad;
|
2015-05-26 17:07:07 +07:00
|
|
|
if (be32_to_cpu(fmpl->magic) != UBI_FM_POOL_MAGIC) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad fastmap pool magic: 0x%x, expected: 0x%x",
|
2015-05-26 17:07:07 +07:00
|
|
|
be32_to_cpu(fmpl->magic), UBI_FM_POOL_MAGIC);
|
2012-09-26 22:51:49 +07:00
|
|
|
goto fail_bad;
|
|
|
|
}
|
|
|
|
|
2015-05-26 17:07:07 +07:00
|
|
|
fmpl_wl = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*fmpl_wl);
|
2012-09-26 22:51:49 +07:00
|
|
|
if (fm_pos >= fm_size)
|
|
|
|
goto fail_bad;
|
2015-05-26 17:07:07 +07:00
|
|
|
if (be32_to_cpu(fmpl_wl->magic) != UBI_FM_POOL_MAGIC) {
|
|
|
|
ubi_err(ubi, "bad fastmap WL pool magic: 0x%x, expected: 0x%x",
|
|
|
|
be32_to_cpu(fmpl_wl->magic), UBI_FM_POOL_MAGIC);
|
2012-09-26 22:51:49 +07:00
|
|
|
goto fail_bad;
|
|
|
|
}
|
|
|
|
|
2015-05-26 17:07:07 +07:00
|
|
|
pool_size = be16_to_cpu(fmpl->size);
|
|
|
|
wl_pool_size = be16_to_cpu(fmpl_wl->size);
|
|
|
|
fm->max_pool_size = be16_to_cpu(fmpl->max_size);
|
|
|
|
fm->max_wl_pool_size = be16_to_cpu(fmpl_wl->max_size);
|
2012-09-26 22:51:49 +07:00
|
|
|
|
|
|
|
if (pool_size > UBI_FM_MAX_POOL_SIZE || pool_size < 0) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad pool size: %i", pool_size);
|
2012-09-26 22:51:49 +07:00
|
|
|
goto fail_bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wl_pool_size > UBI_FM_MAX_POOL_SIZE || wl_pool_size < 0) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad WL pool size: %i", wl_pool_size);
|
2012-09-26 22:51:49 +07:00
|
|
|
goto fail_bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (fm->max_pool_size > UBI_FM_MAX_POOL_SIZE ||
|
|
|
|
fm->max_pool_size < 0) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad maximal pool size: %i", fm->max_pool_size);
|
2012-09-26 22:51:49 +07:00
|
|
|
goto fail_bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fm->max_wl_pool_size > UBI_FM_MAX_POOL_SIZE ||
|
|
|
|
fm->max_wl_pool_size < 0) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad maximal WL pool size: %i",
|
|
|
|
fm->max_wl_pool_size);
|
2012-09-26 22:51:49 +07:00
|
|
|
goto fail_bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read EC values from free list */
|
|
|
|
for (i = 0; i < be32_to_cpu(fmhdr->free_peb_count); i++) {
|
|
|
|
fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*fmec);
|
|
|
|
if (fm_pos >= fm_size)
|
|
|
|
goto fail_bad;
|
|
|
|
|
|
|
|
add_aeb(ai, &ai->free, be32_to_cpu(fmec->pnum),
|
|
|
|
be32_to_cpu(fmec->ec), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read EC values from used list */
|
|
|
|
for (i = 0; i < be32_to_cpu(fmhdr->used_peb_count); i++) {
|
|
|
|
fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*fmec);
|
|
|
|
if (fm_pos >= fm_size)
|
|
|
|
goto fail_bad;
|
|
|
|
|
|
|
|
add_aeb(ai, &used, be32_to_cpu(fmec->pnum),
|
|
|
|
be32_to_cpu(fmec->ec), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read EC values from scrub list */
|
|
|
|
for (i = 0; i < be32_to_cpu(fmhdr->scrub_peb_count); i++) {
|
|
|
|
fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*fmec);
|
|
|
|
if (fm_pos >= fm_size)
|
|
|
|
goto fail_bad;
|
|
|
|
|
|
|
|
add_aeb(ai, &used, be32_to_cpu(fmec->pnum),
|
|
|
|
be32_to_cpu(fmec->ec), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read EC values from erase list */
|
|
|
|
for (i = 0; i < be32_to_cpu(fmhdr->erase_peb_count); i++) {
|
|
|
|
fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*fmec);
|
|
|
|
if (fm_pos >= fm_size)
|
|
|
|
goto fail_bad;
|
|
|
|
|
|
|
|
add_aeb(ai, &ai->erase, be32_to_cpu(fmec->pnum),
|
|
|
|
be32_to_cpu(fmec->ec), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
ai->mean_ec = div_u64(ai->ec_sum, ai->ec_count);
|
|
|
|
ai->bad_peb_count = be32_to_cpu(fmhdr->bad_peb_count);
|
|
|
|
|
|
|
|
/* Iterate over all volumes and read their EBA table */
|
|
|
|
for (i = 0; i < be32_to_cpu(fmhdr->vol_count); i++) {
|
|
|
|
fmvhdr = (struct ubi_fm_volhdr *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*fmvhdr);
|
|
|
|
if (fm_pos >= fm_size)
|
|
|
|
goto fail_bad;
|
|
|
|
|
|
|
|
if (be32_to_cpu(fmvhdr->magic) != UBI_FM_VHDR_MAGIC) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad fastmap vol header magic: 0x%x, expected: 0x%x",
|
2012-09-26 22:51:49 +07:00
|
|
|
be32_to_cpu(fmvhdr->magic), UBI_FM_VHDR_MAGIC);
|
|
|
|
goto fail_bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
av = add_vol(ai, be32_to_cpu(fmvhdr->vol_id),
|
|
|
|
be32_to_cpu(fmvhdr->used_ebs),
|
|
|
|
be32_to_cpu(fmvhdr->data_pad),
|
|
|
|
fmvhdr->vol_type,
|
|
|
|
be32_to_cpu(fmvhdr->last_eb_bytes));
|
|
|
|
|
|
|
|
if (!av)
|
|
|
|
goto fail_bad;
|
2015-05-26 17:07:09 +07:00
|
|
|
if (PTR_ERR(av) == -EINVAL) {
|
|
|
|
ubi_err(ubi, "volume (ID %i) already exists",
|
|
|
|
fmvhdr->vol_id);
|
|
|
|
goto fail_bad;
|
|
|
|
}
|
2012-09-26 22:51:49 +07:00
|
|
|
|
|
|
|
ai->vols_found++;
|
|
|
|
if (ai->highest_vol_id < be32_to_cpu(fmvhdr->vol_id))
|
|
|
|
ai->highest_vol_id = be32_to_cpu(fmvhdr->vol_id);
|
|
|
|
|
|
|
|
fm_eba = (struct ubi_fm_eba *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*fm_eba);
|
|
|
|
fm_pos += (sizeof(__be32) * be32_to_cpu(fm_eba->reserved_pebs));
|
|
|
|
if (fm_pos >= fm_size)
|
|
|
|
goto fail_bad;
|
|
|
|
|
|
|
|
if (be32_to_cpu(fm_eba->magic) != UBI_FM_EBA_MAGIC) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad fastmap EBA header magic: 0x%x, expected: 0x%x",
|
2012-09-26 22:51:49 +07:00
|
|
|
be32_to_cpu(fm_eba->magic), UBI_FM_EBA_MAGIC);
|
|
|
|
goto fail_bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < be32_to_cpu(fm_eba->reserved_pebs); j++) {
|
|
|
|
int pnum = be32_to_cpu(fm_eba->pnum[j]);
|
|
|
|
|
2015-07-03 15:36:14 +07:00
|
|
|
if (pnum < 0)
|
2012-09-26 22:51:49 +07:00
|
|
|
continue;
|
|
|
|
|
|
|
|
aeb = NULL;
|
|
|
|
list_for_each_entry(tmp_aeb, &used, u.list) {
|
2013-05-02 07:10:44 +07:00
|
|
|
if (tmp_aeb->pnum == pnum) {
|
2012-09-26 22:51:49 +07:00
|
|
|
aeb = tmp_aeb;
|
2013-05-02 07:10:44 +07:00
|
|
|
break;
|
|
|
|
}
|
2012-09-26 22:51:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!aeb) {
|
2014-10-08 02:39:20 +07:00
|
|
|
ubi_err(ubi, "PEB %i is in EBA but not in used list", pnum);
|
|
|
|
goto fail_bad;
|
2012-09-26 22:51:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
aeb->lnum = j;
|
|
|
|
|
|
|
|
if (av->highest_lnum <= aeb->lnum)
|
|
|
|
av->highest_lnum = aeb->lnum;
|
|
|
|
|
|
|
|
assign_aeb_to_av(ai, aeb, av);
|
|
|
|
|
|
|
|
dbg_bld("inserting PEB:%i (LEB %i) to vol %i",
|
|
|
|
aeb->pnum, aeb->lnum, av->vol_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-26 17:07:07 +07:00
|
|
|
ret = scan_pool(ubi, ai, fmpl->pebs, pool_size, &max_sqnum, &free);
|
2012-09-26 22:51:49 +07:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
|
2015-05-26 17:07:07 +07:00
|
|
|
ret = scan_pool(ubi, ai, fmpl_wl->pebs, wl_pool_size, &max_sqnum, &free);
|
2012-09-26 22:51:49 +07:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
if (max_sqnum > ai->max_sqnum)
|
|
|
|
ai->max_sqnum = max_sqnum;
|
|
|
|
|
2012-10-09 13:14:21 +07:00
|
|
|
list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &free, u.list)
|
|
|
|
list_move_tail(&tmp_aeb->u.list, &ai->free);
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2014-10-07 23:51:07 +07:00
|
|
|
list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &used, u.list)
|
|
|
|
list_move_tail(&tmp_aeb->u.list, &ai->erase);
|
|
|
|
|
2013-09-28 20:55:16 +07:00
|
|
|
ubi_assert(list_empty(&free));
|
|
|
|
|
2012-09-26 22:51:49 +07:00
|
|
|
/*
|
|
|
|
* If fastmap is leaking PEBs (must not happen), raise a
|
|
|
|
* fat warning and fall back to scanning mode.
|
|
|
|
* We do this here because in ubi_wl_init() it's too late
|
|
|
|
* and we cannot fall back to scanning.
|
|
|
|
*/
|
|
|
|
if (WARN_ON(count_fastmap_pebs(ai) != ubi->peb_count -
|
|
|
|
ai->bad_peb_count - fm->used_blocks))
|
|
|
|
goto fail_bad;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail_bad:
|
|
|
|
ret = UBI_BAD_FASTMAP;
|
|
|
|
fail:
|
2013-09-28 20:55:15 +07:00
|
|
|
list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &used, u.list) {
|
|
|
|
list_del(&tmp_aeb->u.list);
|
2014-01-29 20:17:57 +07:00
|
|
|
kmem_cache_free(ai->aeb_slab_cache, tmp_aeb);
|
2013-09-28 20:55:15 +07:00
|
|
|
}
|
|
|
|
list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &free, u.list) {
|
|
|
|
list_del(&tmp_aeb->u.list);
|
2014-01-29 20:17:57 +07:00
|
|
|
kmem_cache_free(ai->aeb_slab_cache, tmp_aeb);
|
2013-09-28 20:55:15 +07:00
|
|
|
}
|
|
|
|
|
2012-09-26 22:51:49 +07:00
|
|
|
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
|
|
|
|
*
|
|
|
|
* 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_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;
|
|
|
|
size_t fm_size;
|
|
|
|
__be32 crc, tmp_crc;
|
|
|
|
unsigned long long sqnum = 0;
|
|
|
|
|
2014-11-10 22:28:08 +07:00
|
|
|
down_write(&ubi->fm_protect);
|
2012-09-26 22:51:49 +07:00
|
|
|
memset(ubi->fm_buf, 0, ubi->fm_size);
|
|
|
|
|
|
|
|
fmsb = kmalloc(sizeof(*fmsb), GFP_KERNEL);
|
|
|
|
if (!fmsb) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
fm = kzalloc(sizeof(*fm), GFP_KERNEL);
|
|
|
|
if (!fm) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
kfree(fmsb);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ubi_io_read(ubi, fmsb, fm_anchor, ubi->leb_start, sizeof(*fmsb));
|
|
|
|
if (ret && ret != UBI_IO_BITFLIPS)
|
|
|
|
goto free_fm_sb;
|
|
|
|
else if (ret == UBI_IO_BITFLIPS)
|
|
|
|
fm->to_be_tortured[0] = 1;
|
|
|
|
|
|
|
|
if (be32_to_cpu(fmsb->magic) != UBI_FM_SB_MAGIC) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad super block magic: 0x%x, expected: 0x%x",
|
2012-09-26 22:51:49 +07:00
|
|
|
be32_to_cpu(fmsb->magic), UBI_FM_SB_MAGIC);
|
|
|
|
ret = UBI_BAD_FASTMAP;
|
|
|
|
goto free_fm_sb;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fmsb->version != UBI_FM_FMT_VERSION) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad fastmap version: %i, expected: %i",
|
2012-09-26 22:51:49 +07:00
|
|
|
fmsb->version, UBI_FM_FMT_VERSION);
|
|
|
|
ret = UBI_BAD_FASTMAP;
|
|
|
|
goto free_fm_sb;
|
|
|
|
}
|
|
|
|
|
|
|
|
used_blocks = be32_to_cpu(fmsb->used_blocks);
|
|
|
|
if (used_blocks > UBI_FM_MAX_BLOCKS || used_blocks < 1) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "number of fastmap blocks is invalid: %i",
|
|
|
|
used_blocks);
|
2012-09-26 22:51:49 +07:00
|
|
|
ret = UBI_BAD_FASTMAP;
|
|
|
|
goto free_fm_sb;
|
|
|
|
}
|
|
|
|
|
|
|
|
fm_size = ubi->leb_size * used_blocks;
|
|
|
|
if (fm_size != ubi->fm_size) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad fastmap size: %zi, expected: %zi",
|
|
|
|
fm_size, ubi->fm_size);
|
2012-09-26 22:51:49 +07:00
|
|
|
ret = UBI_BAD_FASTMAP;
|
|
|
|
goto free_fm_sb;
|
|
|
|
}
|
|
|
|
|
|
|
|
ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
|
|
|
|
if (!ech) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto free_fm_sb;
|
|
|
|
}
|
|
|
|
|
|
|
|
vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
|
|
|
|
if (!vh) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < used_blocks; i++) {
|
2013-09-28 20:55:13 +07:00
|
|
|
int image_seq;
|
|
|
|
|
2012-09-26 22:51:49 +07:00
|
|
|
pnum = be32_to_cpu(fmsb->block_loc[i]);
|
|
|
|
|
|
|
|
if (ubi_io_is_bad(ubi, pnum)) {
|
|
|
|
ret = UBI_BAD_FASTMAP;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
|
|
|
|
if (ret && ret != UBI_IO_BITFLIPS) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "unable to read fastmap block# %i EC (PEB: %i)",
|
2012-09-26 22:51:49 +07:00
|
|
|
i, pnum);
|
|
|
|
if (ret > 0)
|
|
|
|
ret = UBI_BAD_FASTMAP;
|
|
|
|
goto free_hdr;
|
|
|
|
} else if (ret == UBI_IO_BITFLIPS)
|
|
|
|
fm->to_be_tortured[i] = 1;
|
|
|
|
|
2013-09-28 20:55:13 +07:00
|
|
|
image_seq = be32_to_cpu(ech->image_seq);
|
2012-09-26 22:51:49 +07:00
|
|
|
if (!ubi->image_seq)
|
2013-09-28 20:55:13 +07:00
|
|
|
ubi->image_seq = image_seq;
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2013-09-28 20:55:13 +07:00
|
|
|
/*
|
|
|
|
* Older UBI implementations have image_seq set to zero, so
|
|
|
|
* we shouldn't fail if image_seq == 0.
|
|
|
|
*/
|
|
|
|
if (image_seq && (image_seq != ubi->image_seq)) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "wrong image seq:%d instead of %d",
|
2013-09-28 20:55:13 +07:00
|
|
|
be32_to_cpu(ech->image_seq), ubi->image_seq);
|
2012-09-26 22:51:49 +07:00
|
|
|
ret = UBI_BAD_FASTMAP;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ubi_io_read_vid_hdr(ubi, pnum, vh, 0);
|
|
|
|
if (ret && ret != UBI_IO_BITFLIPS) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "unable to read fastmap block# %i (PEB: %i)",
|
2012-09-26 22:51:49 +07:00
|
|
|
i, pnum);
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == 0) {
|
|
|
|
if (be32_to_cpu(vh->vol_id) != UBI_FM_SB_VOLUME_ID) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad fastmap anchor vol_id: 0x%x, expected: 0x%x",
|
2012-09-26 22:51:49 +07:00
|
|
|
be32_to_cpu(vh->vol_id),
|
|
|
|
UBI_FM_SB_VOLUME_ID);
|
|
|
|
ret = UBI_BAD_FASTMAP;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (be32_to_cpu(vh->vol_id) != UBI_FM_DATA_VOLUME_ID) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "bad fastmap data vol_id: 0x%x, expected: 0x%x",
|
2012-09-26 22:51:49 +07:00
|
|
|
be32_to_cpu(vh->vol_id),
|
|
|
|
UBI_FM_DATA_VOLUME_ID);
|
|
|
|
ret = UBI_BAD_FASTMAP;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sqnum < be64_to_cpu(vh->sqnum))
|
|
|
|
sqnum = be64_to_cpu(vh->sqnum);
|
|
|
|
|
|
|
|
ret = ubi_io_read(ubi, ubi->fm_buf + (ubi->leb_size * i), pnum,
|
|
|
|
ubi->leb_start, ubi->leb_size);
|
|
|
|
if (ret && ret != UBI_IO_BITFLIPS) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "unable to read fastmap block# %i (PEB: %i, "
|
2012-09-26 22:51:49 +07:00
|
|
|
"err: %i)", i, pnum, ret);
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(fmsb);
|
|
|
|
fmsb = NULL;
|
|
|
|
|
|
|
|
fmsb2 = (struct ubi_fm_sb *)(ubi->fm_buf);
|
|
|
|
tmp_crc = be32_to_cpu(fmsb2->data_crc);
|
|
|
|
fmsb2->data_crc = 0;
|
|
|
|
crc = crc32(UBI_CRC32_INIT, ubi->fm_buf, fm_size);
|
|
|
|
if (crc != tmp_crc) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "fastmap data CRC is invalid");
|
|
|
|
ubi_err(ubi, "CRC should be: 0x%x, calc: 0x%x",
|
|
|
|
tmp_crc, crc);
|
2012-09-26 22:51:49 +07:00
|
|
|
ret = UBI_BAD_FASTMAP;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fmsb2->sqnum = sqnum;
|
|
|
|
|
|
|
|
fm->used_blocks = used_blocks;
|
|
|
|
|
|
|
|
ret = ubi_attach_fastmap(ubi, ai, fm);
|
|
|
|
if (ret) {
|
|
|
|
if (ret > 0)
|
|
|
|
ret = UBI_BAD_FASTMAP;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < used_blocks; i++) {
|
|
|
|
struct ubi_wl_entry *e;
|
|
|
|
|
|
|
|
e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL);
|
|
|
|
if (!e) {
|
|
|
|
while (i--)
|
|
|
|
kfree(fm->e[i]);
|
|
|
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto free_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
e->pnum = be32_to_cpu(fmsb2->block_loc[i]);
|
|
|
|
e->ec = be32_to_cpu(fmsb2->block_ec[i]);
|
|
|
|
fm->e[i] = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
ubi->fm = fm;
|
|
|
|
ubi->fm_pool.max_size = ubi->fm->max_pool_size;
|
|
|
|
ubi->fm_wl_pool.max_size = ubi->fm->max_wl_pool_size;
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_msg(ubi, "attached by fastmap");
|
|
|
|
ubi_msg(ubi, "fastmap pool size: %d", ubi->fm_pool.max_size);
|
|
|
|
ubi_msg(ubi, "fastmap WL pool size: %d",
|
|
|
|
ubi->fm_wl_pool.max_size);
|
2012-09-26 22:51:49 +07:00
|
|
|
ubi->fm_disabled = 0;
|
UBI: Fix static volume checks when Fastmap is used
Ezequiel reported that he's facing UBI going into read-only
mode after power cut. It turned out that this behavior happens
only when updating a static volume is interrupted and Fastmap is
used.
A possible trace can look like:
ubi0 warning: ubi_io_read_vid_hdr [ubi]: no VID header found at PEB 2323, only 0xFF bytes
ubi0 warning: ubi_eba_read_leb [ubi]: switch to read-only mode
CPU: 0 PID: 833 Comm: ubiupdatevol Not tainted 4.6.0-rc2-ARCH #4
Hardware name: SAMSUNG ELECTRONICS CO., LTD. 300E4C/300E5C/300E7C/NP300E5C-AD8AR, BIOS P04RAP 10/15/2012
0000000000000286 00000000eba949bd ffff8800c45a7b38 ffffffff8140d841
ffff8801964be000 ffff88018eaa4800 ffff8800c45a7bb8 ffffffffa003abf6
ffffffff850e2ac0 8000000000000163 ffff8801850e2ac0 ffff8801850e2ac0
Call Trace:
[<ffffffff8140d841>] dump_stack+0x63/0x82
[<ffffffffa003abf6>] ubi_eba_read_leb+0x486/0x4a0 [ubi]
[<ffffffffa00453b3>] ubi_check_volume+0x83/0xf0 [ubi]
[<ffffffffa0039d97>] ubi_open_volume+0x177/0x350 [ubi]
[<ffffffffa00375d8>] vol_cdev_open+0x58/0xb0 [ubi]
[<ffffffff8124b08e>] chrdev_open+0xae/0x1d0
[<ffffffff81243bcf>] do_dentry_open+0x1ff/0x300
[<ffffffff8124afe0>] ? cdev_put+0x30/0x30
[<ffffffff81244d36>] vfs_open+0x56/0x60
[<ffffffff812545f4>] path_openat+0x4f4/0x1190
[<ffffffff81256621>] do_filp_open+0x91/0x100
[<ffffffff81263547>] ? __alloc_fd+0xc7/0x190
[<ffffffff812450df>] do_sys_open+0x13f/0x210
[<ffffffff812451ce>] SyS_open+0x1e/0x20
[<ffffffff81a99e32>] entry_SYSCALL_64_fastpath+0x1a/0xa4
UBI checks static volumes for data consistency and reads the
whole volume upon first open. If the volume is found erroneous
users of UBI cannot read from it, but another volume update is
possible to fix it. The check is performed by running
ubi_eba_read_leb() on every allocated LEB of the volume.
For static volumes ubi_eba_read_leb() computes the checksum of all
data stored in a LEB. To verify the computed checksum it has to read
the LEB's volume header which stores the original checksum.
If the volume header is not found UBI treats this as fatal internal
error and switches to RO mode. If the UBI device was attached via a
full scan the assumption is correct, the volume header has to be
present as it had to be there while scanning to get known as mapped.
If the attach operation happened via Fastmap the assumption is no
longer correct. When attaching via Fastmap UBI learns the mapping
table from Fastmap's snapshot of the system state and not via a full
scan. It can happen that a LEB got unmapped after a Fastmap was
written to the flash. Then UBI can learn the LEB still as mapped and
accessing it returns only 0xFF bytes. As UBI is not a FTL it is
allowed to have mappings to empty PEBs, it assumes that the layer
above takes care of LEB accounting and referencing.
UBIFS does so using the LEB property tree (LPT).
For static volumes UBI blindly assumes that all LEBs are present and
therefore special actions have to be taken.
The described situation can happen when updating a static volume is
interrupted, either by a user or a power cut.
The volume update code first unmaps all LEBs of a volume and then
writes LEB by LEB. If the sequence of operations is interrupted UBI
detects this either by the absence of LEBs, no volume header present
at scan time, or corrupted payload, detected via checksum.
In the Fastmap case the former method won't trigger as no scan
happened and UBI automatically thinks all LEBs are present.
Only by reading data from a LEB it detects that the volume header is
missing and incorrectly treats this as fatal error.
To deal with the situation ubi_eba_read_leb() from now on checks
whether we attached via Fastmap and handles the absence of a
volume header like a data corruption error.
This way interrupted static volume updates will correctly get detected
also when Fastmap is used.
Cc: <stable@vger.kernel.org>
Reported-by: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
Tested-by: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
Signed-off-by: Richard Weinberger <richard@nod.at>
2016-04-26 21:39:48 +07:00
|
|
|
ubi->fast_attach = 1;
|
2012-09-26 22:51:49 +07:00
|
|
|
|
|
|
|
ubi_free_vid_hdr(ubi, vh);
|
|
|
|
kfree(ech);
|
|
|
|
out:
|
2014-11-10 22:28:08 +07:00
|
|
|
up_write(&ubi->fm_protect);
|
2012-09-26 22:51:49 +07:00
|
|
|
if (ret == UBI_BAD_FASTMAP)
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "Attach by fastmap failed, doing a full scan!");
|
2012-09-26 22:51:49 +07:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
free_hdr:
|
|
|
|
ubi_free_vid_hdr(ubi, vh);
|
|
|
|
kfree(ech);
|
|
|
|
free_fm_sb:
|
|
|
|
kfree(fmsb);
|
|
|
|
kfree(fm);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ubi_write_fastmap - writes a fastmap.
|
|
|
|
* @ubi: UBI device object
|
|
|
|
* @new_fm: the to be written fastmap
|
|
|
|
*
|
|
|
|
* Returns 0 on success, < 0 indicates an internal error.
|
|
|
|
*/
|
|
|
|
static int ubi_write_fastmap(struct ubi_device *ubi,
|
|
|
|
struct ubi_fastmap_layout *new_fm)
|
|
|
|
{
|
|
|
|
size_t fm_pos = 0;
|
|
|
|
void *fm_raw;
|
|
|
|
struct ubi_fm_sb *fmsb;
|
|
|
|
struct ubi_fm_hdr *fmh;
|
2015-05-26 17:07:07 +07:00
|
|
|
struct ubi_fm_scan_pool *fmpl, *fmpl_wl;
|
2012-09-26 22:51:49 +07:00
|
|
|
struct ubi_fm_ec *fec;
|
|
|
|
struct ubi_fm_volhdr *fvh;
|
|
|
|
struct ubi_fm_eba *feba;
|
|
|
|
struct ubi_wl_entry *wl_e;
|
|
|
|
struct ubi_volume *vol;
|
|
|
|
struct ubi_vid_hdr *avhdr, *dvhdr;
|
|
|
|
struct ubi_work *ubi_wrk;
|
2014-10-28 22:24:27 +07:00
|
|
|
struct rb_node *tmp_rb;
|
2012-09-26 22:51:49 +07:00
|
|
|
int ret, i, j, free_peb_count, used_peb_count, vol_count;
|
|
|
|
int scrub_peb_count, erase_peb_count;
|
2014-09-22 20:36:40 +07:00
|
|
|
int *seen_pebs = NULL;
|
2012-09-26 22:51:49 +07:00
|
|
|
|
|
|
|
fm_raw = ubi->fm_buf;
|
|
|
|
memset(ubi->fm_buf, 0, ubi->fm_size);
|
|
|
|
|
|
|
|
avhdr = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID);
|
|
|
|
if (!avhdr) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
dvhdr = new_fm_vhdr(ubi, UBI_FM_DATA_VOLUME_ID);
|
|
|
|
if (!dvhdr) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out_kfree;
|
|
|
|
}
|
|
|
|
|
2014-09-22 20:36:40 +07:00
|
|
|
seen_pebs = init_seen(ubi);
|
|
|
|
if (IS_ERR(seen_pebs)) {
|
|
|
|
ret = PTR_ERR(seen_pebs);
|
|
|
|
goto out_kfree;
|
|
|
|
}
|
|
|
|
|
2012-09-26 22:51:49 +07:00
|
|
|
spin_lock(&ubi->volumes_lock);
|
|
|
|
spin_lock(&ubi->wl_lock);
|
|
|
|
|
|
|
|
fmsb = (struct ubi_fm_sb *)fm_raw;
|
|
|
|
fm_pos += sizeof(*fmsb);
|
|
|
|
ubi_assert(fm_pos <= ubi->fm_size);
|
|
|
|
|
|
|
|
fmh = (struct ubi_fm_hdr *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*fmh);
|
|
|
|
ubi_assert(fm_pos <= ubi->fm_size);
|
|
|
|
|
|
|
|
fmsb->magic = cpu_to_be32(UBI_FM_SB_MAGIC);
|
|
|
|
fmsb->version = UBI_FM_FMT_VERSION;
|
|
|
|
fmsb->used_blocks = cpu_to_be32(new_fm->used_blocks);
|
|
|
|
/* the max sqnum will be filled in while *reading* the fastmap */
|
|
|
|
fmsb->sqnum = 0;
|
|
|
|
|
|
|
|
fmh->magic = cpu_to_be32(UBI_FM_HDR_MAGIC);
|
|
|
|
free_peb_count = 0;
|
|
|
|
used_peb_count = 0;
|
|
|
|
scrub_peb_count = 0;
|
|
|
|
erase_peb_count = 0;
|
|
|
|
vol_count = 0;
|
|
|
|
|
2015-05-26 17:07:07 +07:00
|
|
|
fmpl = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*fmpl);
|
|
|
|
fmpl->magic = cpu_to_be32(UBI_FM_POOL_MAGIC);
|
|
|
|
fmpl->size = cpu_to_be16(ubi->fm_pool.size);
|
|
|
|
fmpl->max_size = cpu_to_be16(ubi->fm_pool.max_size);
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2014-09-22 20:36:40 +07:00
|
|
|
for (i = 0; i < ubi->fm_pool.size; i++) {
|
2015-05-26 17:07:07 +07:00
|
|
|
fmpl->pebs[i] = cpu_to_be32(ubi->fm_pool.pebs[i]);
|
2014-09-22 20:36:40 +07:00
|
|
|
set_seen(ubi, ubi->fm_pool.pebs[i], seen_pebs);
|
|
|
|
}
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2015-05-26 17:07:07 +07:00
|
|
|
fmpl_wl = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*fmpl_wl);
|
|
|
|
fmpl_wl->magic = cpu_to_be32(UBI_FM_POOL_MAGIC);
|
|
|
|
fmpl_wl->size = cpu_to_be16(ubi->fm_wl_pool.size);
|
|
|
|
fmpl_wl->max_size = cpu_to_be16(ubi->fm_wl_pool.max_size);
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2014-09-22 20:36:40 +07:00
|
|
|
for (i = 0; i < ubi->fm_wl_pool.size; i++) {
|
2015-05-26 17:07:07 +07:00
|
|
|
fmpl_wl->pebs[i] = cpu_to_be32(ubi->fm_wl_pool.pebs[i]);
|
2014-09-22 20:36:40 +07:00
|
|
|
set_seen(ubi, ubi->fm_wl_pool.pebs[i], seen_pebs);
|
|
|
|
}
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2014-10-28 22:24:27 +07:00
|
|
|
ubi_for_each_free_peb(ubi, wl_e, tmp_rb) {
|
2012-09-26 22:51:49 +07:00
|
|
|
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
|
|
|
|
|
|
|
fec->pnum = cpu_to_be32(wl_e->pnum);
|
2014-09-22 20:36:40 +07:00
|
|
|
set_seen(ubi, wl_e->pnum, seen_pebs);
|
2012-09-26 22:51:49 +07:00
|
|
|
fec->ec = cpu_to_be32(wl_e->ec);
|
|
|
|
|
|
|
|
free_peb_count++;
|
|
|
|
fm_pos += sizeof(*fec);
|
|
|
|
ubi_assert(fm_pos <= ubi->fm_size);
|
|
|
|
}
|
|
|
|
fmh->free_peb_count = cpu_to_be32(free_peb_count);
|
|
|
|
|
2014-10-28 22:24:27 +07:00
|
|
|
ubi_for_each_used_peb(ubi, wl_e, tmp_rb) {
|
2012-09-26 22:51:49 +07:00
|
|
|
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
|
|
|
|
|
|
|
fec->pnum = cpu_to_be32(wl_e->pnum);
|
2014-09-22 20:36:40 +07:00
|
|
|
set_seen(ubi, wl_e->pnum, seen_pebs);
|
2012-09-26 22:51:49 +07:00
|
|
|
fec->ec = cpu_to_be32(wl_e->ec);
|
|
|
|
|
|
|
|
used_peb_count++;
|
|
|
|
fm_pos += sizeof(*fec);
|
|
|
|
ubi_assert(fm_pos <= ubi->fm_size);
|
|
|
|
}
|
2014-11-24 20:20:31 +07:00
|
|
|
|
2014-10-28 22:24:27 +07:00
|
|
|
ubi_for_each_protected_peb(ubi, i, wl_e) {
|
|
|
|
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
2014-11-24 20:20:31 +07:00
|
|
|
|
2014-10-28 22:24:27 +07:00
|
|
|
fec->pnum = cpu_to_be32(wl_e->pnum);
|
|
|
|
set_seen(ubi, wl_e->pnum, seen_pebs);
|
|
|
|
fec->ec = cpu_to_be32(wl_e->ec);
|
2014-11-24 20:20:31 +07:00
|
|
|
|
2014-10-28 22:24:27 +07:00
|
|
|
used_peb_count++;
|
|
|
|
fm_pos += sizeof(*fec);
|
|
|
|
ubi_assert(fm_pos <= ubi->fm_size);
|
2014-11-24 20:20:31 +07:00
|
|
|
}
|
2012-09-26 22:51:49 +07:00
|
|
|
fmh->used_peb_count = cpu_to_be32(used_peb_count);
|
|
|
|
|
2014-10-28 22:24:27 +07:00
|
|
|
ubi_for_each_scrub_peb(ubi, wl_e, tmp_rb) {
|
2012-09-26 22:51:49 +07:00
|
|
|
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
|
|
|
|
|
|
|
fec->pnum = cpu_to_be32(wl_e->pnum);
|
2014-09-22 20:36:40 +07:00
|
|
|
set_seen(ubi, wl_e->pnum, seen_pebs);
|
2012-09-26 22:51:49 +07:00
|
|
|
fec->ec = cpu_to_be32(wl_e->ec);
|
|
|
|
|
|
|
|
scrub_peb_count++;
|
|
|
|
fm_pos += sizeof(*fec);
|
|
|
|
ubi_assert(fm_pos <= ubi->fm_size);
|
|
|
|
}
|
|
|
|
fmh->scrub_peb_count = cpu_to_be32(scrub_peb_count);
|
|
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(ubi_wrk, &ubi->works, list) {
|
|
|
|
if (ubi_is_erase_work(ubi_wrk)) {
|
|
|
|
wl_e = ubi_wrk->e;
|
|
|
|
ubi_assert(wl_e);
|
|
|
|
|
|
|
|
fec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
|
|
|
|
|
|
|
|
fec->pnum = cpu_to_be32(wl_e->pnum);
|
2014-09-22 20:36:40 +07:00
|
|
|
set_seen(ubi, wl_e->pnum, seen_pebs);
|
2012-09-26 22:51:49 +07:00
|
|
|
fec->ec = cpu_to_be32(wl_e->ec);
|
|
|
|
|
|
|
|
erase_peb_count++;
|
|
|
|
fm_pos += sizeof(*fec);
|
|
|
|
ubi_assert(fm_pos <= ubi->fm_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmh->erase_peb_count = cpu_to_be32(erase_peb_count);
|
|
|
|
|
|
|
|
for (i = 0; i < UBI_MAX_VOLUMES + UBI_INT_VOL_COUNT; i++) {
|
|
|
|
vol = ubi->volumes[i];
|
|
|
|
|
|
|
|
if (!vol)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
vol_count++;
|
|
|
|
|
|
|
|
fvh = (struct ubi_fm_volhdr *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*fvh);
|
|
|
|
ubi_assert(fm_pos <= ubi->fm_size);
|
|
|
|
|
|
|
|
fvh->magic = cpu_to_be32(UBI_FM_VHDR_MAGIC);
|
|
|
|
fvh->vol_id = cpu_to_be32(vol->vol_id);
|
|
|
|
fvh->vol_type = vol->vol_type;
|
|
|
|
fvh->used_ebs = cpu_to_be32(vol->used_ebs);
|
|
|
|
fvh->data_pad = cpu_to_be32(vol->data_pad);
|
|
|
|
fvh->last_eb_bytes = cpu_to_be32(vol->last_eb_bytes);
|
|
|
|
|
|
|
|
ubi_assert(vol->vol_type == UBI_DYNAMIC_VOLUME ||
|
|
|
|
vol->vol_type == UBI_STATIC_VOLUME);
|
|
|
|
|
|
|
|
feba = (struct ubi_fm_eba *)(fm_raw + fm_pos);
|
|
|
|
fm_pos += sizeof(*feba) + (sizeof(__be32) * vol->reserved_pebs);
|
|
|
|
ubi_assert(fm_pos <= ubi->fm_size);
|
|
|
|
|
|
|
|
for (j = 0; j < vol->reserved_pebs; j++)
|
|
|
|
feba->pnum[j] = cpu_to_be32(vol->eba_tbl[j]);
|
|
|
|
|
|
|
|
feba->reserved_pebs = cpu_to_be32(j);
|
|
|
|
feba->magic = cpu_to_be32(UBI_FM_EBA_MAGIC);
|
|
|
|
}
|
|
|
|
fmh->vol_count = cpu_to_be32(vol_count);
|
|
|
|
fmh->bad_peb_count = cpu_to_be32(ubi->bad_peb_count);
|
|
|
|
|
|
|
|
avhdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
|
|
|
avhdr->lnum = 0;
|
|
|
|
|
|
|
|
spin_unlock(&ubi->wl_lock);
|
|
|
|
spin_unlock(&ubi->volumes_lock);
|
|
|
|
|
|
|
|
dbg_bld("writing fastmap SB to PEB %i", new_fm->e[0]->pnum);
|
|
|
|
ret = ubi_io_write_vid_hdr(ubi, new_fm->e[0]->pnum, avhdr);
|
|
|
|
if (ret) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "unable to write vid_hdr to fastmap SB!");
|
2012-09-26 22:51:49 +07:00
|
|
|
goto out_kfree;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < new_fm->used_blocks; i++) {
|
|
|
|
fmsb->block_loc[i] = cpu_to_be32(new_fm->e[i]->pnum);
|
2014-09-22 20:36:40 +07:00
|
|
|
set_seen(ubi, new_fm->e[i]->pnum, seen_pebs);
|
2012-09-26 22:51:49 +07:00
|
|
|
fmsb->block_ec[i] = cpu_to_be32(new_fm->e[i]->ec);
|
|
|
|
}
|
|
|
|
|
|
|
|
fmsb->data_crc = 0;
|
|
|
|
fmsb->data_crc = cpu_to_be32(crc32(UBI_CRC32_INIT, fm_raw,
|
|
|
|
ubi->fm_size));
|
|
|
|
|
|
|
|
for (i = 1; i < new_fm->used_blocks; i++) {
|
|
|
|
dvhdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
|
|
|
dvhdr->lnum = cpu_to_be32(i);
|
|
|
|
dbg_bld("writing fastmap data to PEB %i sqnum %llu",
|
|
|
|
new_fm->e[i]->pnum, be64_to_cpu(dvhdr->sqnum));
|
|
|
|
ret = ubi_io_write_vid_hdr(ubi, new_fm->e[i]->pnum, dvhdr);
|
|
|
|
if (ret) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "unable to write vid_hdr to PEB %i!",
|
2012-09-26 22:51:49 +07:00
|
|
|
new_fm->e[i]->pnum);
|
|
|
|
goto out_kfree;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < new_fm->used_blocks; i++) {
|
|
|
|
ret = ubi_io_write(ubi, fm_raw + (i * ubi->leb_size),
|
|
|
|
new_fm->e[i]->pnum, ubi->leb_start, ubi->leb_size);
|
|
|
|
if (ret) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "unable to write fastmap to PEB %i!",
|
2012-09-26 22:51:49 +07:00
|
|
|
new_fm->e[i]->pnum);
|
|
|
|
goto out_kfree;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ubi_assert(new_fm);
|
|
|
|
ubi->fm = new_fm;
|
|
|
|
|
2014-09-22 20:36:40 +07:00
|
|
|
ret = self_check_seen(ubi, seen_pebs);
|
2012-09-26 22:51:49 +07:00
|
|
|
dbg_bld("fastmap written!");
|
|
|
|
|
|
|
|
out_kfree:
|
|
|
|
ubi_free_vid_hdr(ubi, avhdr);
|
|
|
|
ubi_free_vid_hdr(ubi, dvhdr);
|
2014-09-22 20:36:40 +07:00
|
|
|
free_seen(seen_pebs);
|
2012-09-26 22:51:49 +07:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* erase_block - Manually erase a PEB.
|
|
|
|
* @ubi: UBI device object
|
|
|
|
* @pnum: PEB to be erased
|
|
|
|
*
|
|
|
|
* Returns the new EC value on success, < 0 indicates an internal error.
|
|
|
|
*/
|
|
|
|
static int erase_block(struct ubi_device *ubi, int pnum)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct ubi_ec_hdr *ec_hdr;
|
|
|
|
long long ec;
|
|
|
|
|
|
|
|
ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
|
|
|
|
if (!ec_hdr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = ubi_io_read_ec_hdr(ubi, pnum, ec_hdr, 0);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
else if (ret && ret != UBI_IO_BITFLIPS) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ubi_io_sync_erase(ubi, pnum, 0);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ec = be64_to_cpu(ec_hdr->ec);
|
|
|
|
ec += ret;
|
|
|
|
if (ec > UBI_MAX_ERASECOUNTER) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ec_hdr->ec = cpu_to_be64(ec);
|
|
|
|
ret = ubi_io_write_ec_hdr(ubi, pnum, ec_hdr);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = ec;
|
|
|
|
out:
|
|
|
|
kfree(ec_hdr);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* invalidate_fastmap - destroys a fastmap.
|
|
|
|
* @ubi: UBI device object
|
|
|
|
*
|
2014-11-10 22:11:40 +07:00
|
|
|
* This function ensures that upon next UBI attach a full scan
|
|
|
|
* is issued. We need this if UBI is about to write a new fastmap
|
|
|
|
* but is unable to do so. In this case we have two options:
|
|
|
|
* a) Make sure that the current fastmap will not be usued upon
|
|
|
|
* attach time and contine or b) fall back to RO mode to have the
|
|
|
|
* current fastmap in a valid state.
|
2012-09-26 22:51:49 +07:00
|
|
|
* Returns 0 on success, < 0 indicates an internal error.
|
|
|
|
*/
|
2014-11-10 22:11:40 +07:00
|
|
|
static int invalidate_fastmap(struct ubi_device *ubi)
|
2012-09-26 22:51:49 +07:00
|
|
|
{
|
2013-08-20 03:31:49 +07:00
|
|
|
int ret;
|
2014-11-10 22:11:40 +07:00
|
|
|
struct ubi_fastmap_layout *fm;
|
|
|
|
struct ubi_wl_entry *e;
|
|
|
|
struct ubi_vid_hdr *vh = NULL;
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2014-11-10 22:11:40 +07:00
|
|
|
if (!ubi->fm)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ubi->fm = NULL;
|
|
|
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
fm = kzalloc(sizeof(*fm), GFP_KERNEL);
|
|
|
|
if (!fm)
|
|
|
|
goto out;
|
2012-09-26 22:51:49 +07:00
|
|
|
|
|
|
|
vh = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID);
|
|
|
|
if (!vh)
|
2014-11-10 22:11:40 +07:00
|
|
|
goto out_free_fm;
|
|
|
|
|
|
|
|
ret = -ENOSPC;
|
|
|
|
e = ubi_wl_get_fm_peb(ubi, 1);
|
|
|
|
if (!e)
|
|
|
|
goto out_free_fm;
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2014-11-10 22:11:40 +07:00
|
|
|
/*
|
|
|
|
* Create fake fastmap such that UBI will fall back
|
|
|
|
* to scanning mode.
|
|
|
|
*/
|
2012-09-26 22:51:49 +07:00
|
|
|
vh->sqnum = cpu_to_be64(ubi_next_sqnum(ubi));
|
2014-11-10 22:11:40 +07:00
|
|
|
ret = ubi_io_write_vid_hdr(ubi, e->pnum, vh);
|
|
|
|
if (ret < 0) {
|
|
|
|
ubi_wl_put_fm_peb(ubi, e, 0, 0);
|
|
|
|
goto out_free_fm;
|
|
|
|
}
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2014-11-10 22:11:40 +07:00
|
|
|
fm->used_blocks = 1;
|
|
|
|
fm->e[0] = e;
|
|
|
|
|
|
|
|
ubi->fm = fm;
|
|
|
|
|
|
|
|
out:
|
|
|
|
ubi_free_vid_hdr(ubi, vh);
|
2012-09-26 22:51:49 +07:00
|
|
|
return ret;
|
2014-11-10 22:11:40 +07:00
|
|
|
|
|
|
|
out_free_fm:
|
|
|
|
kfree(fm);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* return_fm_pebs - returns all PEBs used by a fastmap back to the
|
|
|
|
* WL sub-system.
|
|
|
|
* @ubi: UBI device object
|
|
|
|
* @fm: fastmap layout object
|
|
|
|
*/
|
|
|
|
static void return_fm_pebs(struct ubi_device *ubi,
|
|
|
|
struct ubi_fastmap_layout *fm)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!fm)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < fm->used_blocks; i++) {
|
|
|
|
if (fm->e[i]) {
|
|
|
|
ubi_wl_put_fm_peb(ubi, fm->e[i], i,
|
|
|
|
fm->to_be_tortured[i]);
|
|
|
|
fm->e[i] = NULL;
|
|
|
|
}
|
|
|
|
}
|
2012-09-26 22:51:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ubi_update_fastmap - will be called by UBI if a volume changes or
|
|
|
|
* a fastmap pool becomes full.
|
|
|
|
* @ubi: UBI device object
|
|
|
|
*
|
|
|
|
* Returns 0 on success, < 0 indicates an internal error.
|
|
|
|
*/
|
|
|
|
int ubi_update_fastmap(struct ubi_device *ubi)
|
|
|
|
{
|
2014-11-10 22:11:40 +07:00
|
|
|
int ret, i, j;
|
2012-09-26 22:51:49 +07:00
|
|
|
struct ubi_fastmap_layout *new_fm, *old_fm;
|
|
|
|
struct ubi_wl_entry *tmp_e;
|
|
|
|
|
2014-11-10 22:28:08 +07:00
|
|
|
down_write(&ubi->fm_protect);
|
2012-09-26 22:51:49 +07:00
|
|
|
|
|
|
|
ubi_refill_pools(ubi);
|
|
|
|
|
|
|
|
if (ubi->ro_mode || ubi->fm_disabled) {
|
2014-11-10 22:28:08 +07:00
|
|
|
up_write(&ubi->fm_protect);
|
2012-09-26 22:51:49 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ubi_ensure_anchor_pebs(ubi);
|
|
|
|
if (ret) {
|
2014-11-10 22:28:08 +07:00
|
|
|
up_write(&ubi->fm_protect);
|
2012-09-26 22:51:49 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_fm = kzalloc(sizeof(*new_fm), GFP_KERNEL);
|
|
|
|
if (!new_fm) {
|
2014-11-10 22:28:08 +07:00
|
|
|
up_write(&ubi->fm_protect);
|
2012-09-26 22:51:49 +07:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_fm->used_blocks = ubi->fm_size / ubi->leb_size;
|
|
|
|
old_fm = ubi->fm;
|
|
|
|
ubi->fm = NULL;
|
|
|
|
|
|
|
|
if (new_fm->used_blocks > UBI_FM_MAX_BLOCKS) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "fastmap too large");
|
2012-09-26 22:51:49 +07:00
|
|
|
ret = -ENOSPC;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 1; i < new_fm->used_blocks; i++) {
|
|
|
|
spin_lock(&ubi->wl_lock);
|
|
|
|
tmp_e = ubi_wl_get_fm_peb(ubi, 0);
|
|
|
|
spin_unlock(&ubi->wl_lock);
|
|
|
|
|
2014-11-10 22:11:40 +07:00
|
|
|
if (!tmp_e) {
|
|
|
|
if (old_fm && old_fm->e[i]) {
|
|
|
|
ret = erase_block(ubi, old_fm->e[i]->pnum);
|
|
|
|
if (ret < 0) {
|
|
|
|
ubi_err(ubi, "could not erase old fastmap PEB");
|
|
|
|
|
|
|
|
for (j = 1; j < i; j++) {
|
|
|
|
ubi_wl_put_fm_peb(ubi, new_fm->e[j],
|
|
|
|
j, 0);
|
|
|
|
new_fm->e[j] = NULL;
|
|
|
|
}
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
new_fm->e[i] = old_fm->e[i];
|
|
|
|
old_fm->e[i] = NULL;
|
|
|
|
} else {
|
|
|
|
ubi_err(ubi, "could not get any free erase block");
|
|
|
|
|
|
|
|
for (j = 1; j < i; j++) {
|
|
|
|
ubi_wl_put_fm_peb(ubi, new_fm->e[j], j, 0);
|
|
|
|
new_fm->e[j] = NULL;
|
|
|
|
}
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2014-11-10 22:11:40 +07:00
|
|
|
ret = -ENOSPC;
|
2012-09-26 22:51:49 +07:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
} else {
|
2014-10-06 19:47:51 +07:00
|
|
|
new_fm->e[i] = tmp_e;
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2014-11-10 22:11:40 +07:00
|
|
|
if (old_fm && old_fm->e[i]) {
|
2012-09-26 22:51:49 +07:00
|
|
|
ubi_wl_put_fm_peb(ubi, old_fm->e[i], i,
|
|
|
|
old_fm->to_be_tortured[i]);
|
2014-11-10 22:11:40 +07:00
|
|
|
old_fm->e[i] = NULL;
|
|
|
|
}
|
2012-09-26 22:51:49 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-29 22:59:32 +07:00
|
|
|
/* Old fastmap is larger than the new one */
|
|
|
|
if (old_fm && new_fm->used_blocks < old_fm->used_blocks) {
|
|
|
|
for (i = new_fm->used_blocks; i < old_fm->used_blocks; i++) {
|
|
|
|
ubi_wl_put_fm_peb(ubi, old_fm->e[i], i,
|
|
|
|
old_fm->to_be_tortured[i]);
|
2014-11-10 22:11:40 +07:00
|
|
|
old_fm->e[i] = NULL;
|
2014-10-29 22:59:32 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-26 22:51:49 +07:00
|
|
|
spin_lock(&ubi->wl_lock);
|
|
|
|
tmp_e = ubi_wl_get_fm_peb(ubi, 1);
|
|
|
|
spin_unlock(&ubi->wl_lock);
|
|
|
|
|
|
|
|
if (old_fm) {
|
|
|
|
/* no fresh anchor PEB was found, reuse the old one */
|
|
|
|
if (!tmp_e) {
|
|
|
|
ret = erase_block(ubi, old_fm->e[0]->pnum);
|
|
|
|
if (ret < 0) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "could not erase old anchor PEB");
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2014-11-10 22:11:40 +07:00
|
|
|
for (i = 1; i < new_fm->used_blocks; i++) {
|
2012-09-26 22:51:49 +07:00
|
|
|
ubi_wl_put_fm_peb(ubi, new_fm->e[i],
|
|
|
|
i, 0);
|
2014-11-10 22:11:40 +07:00
|
|
|
new_fm->e[i] = NULL;
|
|
|
|
}
|
2012-09-26 22:51:49 +07:00
|
|
|
goto err;
|
|
|
|
}
|
2014-10-06 19:47:51 +07:00
|
|
|
new_fm->e[0] = old_fm->e[0];
|
2012-09-26 22:51:49 +07:00
|
|
|
new_fm->e[0]->ec = ret;
|
2014-11-10 22:11:40 +07:00
|
|
|
old_fm->e[0] = NULL;
|
2012-09-26 22:51:49 +07:00
|
|
|
} else {
|
|
|
|
/* we've got a new anchor PEB, return the old one */
|
|
|
|
ubi_wl_put_fm_peb(ubi, old_fm->e[0], 0,
|
|
|
|
old_fm->to_be_tortured[0]);
|
2014-10-06 19:47:51 +07:00
|
|
|
new_fm->e[0] = tmp_e;
|
2014-11-10 22:11:40 +07:00
|
|
|
old_fm->e[0] = NULL;
|
2012-09-26 22:51:49 +07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!tmp_e) {
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_err(ubi, "could not find any anchor PEB");
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2014-11-10 22:11:40 +07:00
|
|
|
for (i = 1; i < new_fm->used_blocks; i++) {
|
2012-09-26 22:51:49 +07:00
|
|
|
ubi_wl_put_fm_peb(ubi, new_fm->e[i], i, 0);
|
2014-11-10 22:11:40 +07:00
|
|
|
new_fm->e[i] = NULL;
|
|
|
|
}
|
2012-09-26 22:51:49 +07:00
|
|
|
|
|
|
|
ret = -ENOSPC;
|
|
|
|
goto err;
|
|
|
|
}
|
2014-10-06 19:47:51 +07:00
|
|
|
new_fm->e[0] = tmp_e;
|
2012-09-26 22:51:49 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
down_write(&ubi->work_sem);
|
2014-11-10 22:28:08 +07:00
|
|
|
down_write(&ubi->fm_eba_sem);
|
2012-09-26 22:51:49 +07:00
|
|
|
ret = ubi_write_fastmap(ubi, new_fm);
|
2014-11-10 22:28:08 +07:00
|
|
|
up_write(&ubi->fm_eba_sem);
|
2012-09-26 22:51:49 +07:00
|
|
|
up_write(&ubi->work_sem);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
out_unlock:
|
2014-11-10 22:28:08 +07:00
|
|
|
up_write(&ubi->fm_protect);
|
2012-09-26 22:51:49 +07:00
|
|
|
kfree(old_fm);
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
err:
|
2014-10-20 23:57:00 +07:00
|
|
|
ubi_warn(ubi, "Unable to write new fastmap, err=%i", ret);
|
2012-09-26 22:51:49 +07:00
|
|
|
|
2014-11-10 22:11:40 +07:00
|
|
|
ret = invalidate_fastmap(ubi);
|
|
|
|
if (ret < 0) {
|
|
|
|
ubi_err(ubi, "Unable to invalidiate current fastmap!");
|
|
|
|
ubi_ro_mode(ubi);
|
|
|
|
} else {
|
|
|
|
return_fm_pebs(ubi, old_fm);
|
|
|
|
return_fm_pebs(ubi, new_fm);
|
|
|
|
ret = 0;
|
2012-09-26 22:51:49 +07:00
|
|
|
}
|
2014-11-10 22:11:40 +07:00
|
|
|
|
|
|
|
kfree(new_fm);
|
2012-09-26 22:51:49 +07:00
|
|
|
goto out_unlock;
|
|
|
|
}
|