diff options
Diffstat (limited to 'drivers/mtd/ubi/fastmap.c')
| -rw-r--r-- | drivers/mtd/ubi/fastmap.c | 157 |
1 files changed, 52 insertions, 105 deletions
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c index 462526a10537..9a4940874be5 100644 --- a/drivers/mtd/ubi/fastmap.c +++ b/drivers/mtd/ubi/fastmap.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012 Linutronix GmbH * Copyright (c) 2014 sigma star gmbh * 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> @@ -29,8 +20,7 @@ static inline unsigned long *init_seen(struct ubi_device *ubi) if (!ubi_dbg_chk_fastmap(ubi)) return NULL; - ret = kcalloc(BITS_TO_LONGS(ubi->peb_count), sizeof(unsigned long), - GFP_KERNEL); + ret = bitmap_zalloc(ubi->peb_count, GFP_NOFS); if (!ret) return ERR_PTR(-ENOMEM); @@ -43,7 +33,7 @@ static inline unsigned long *init_seen(struct ubi_device *ubi) */ static inline void free_seen(unsigned long *seen) { - kfree(seen); + bitmap_free(seen); } /** @@ -73,7 +63,7 @@ static int self_check_seen(struct ubi_device *ubi, unsigned long *seen) return 0; for (pnum = 0; pnum < ubi->peb_count; pnum++) { - if (test_bit(pnum, seen) && ubi->lookuptbl[pnum]) { + if (!test_bit(pnum, seen) && ubi->lookuptbl[pnum]) { ubi_err(ubi, "self-check failed for PEB %d, fastmap didn't see it", pnum); ret = -EINVAL; } @@ -95,15 +85,16 @@ size_t ubi_calc_fm_size(struct ubi_device *ubi) 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))) + - sizeof(struct ubi_fm_volhdr) * UBI_MAX_VOLUMES; + ((sizeof(struct ubi_fm_eba) + + sizeof(struct ubi_fm_volhdr)) * + (UBI_MAX_VOLUMES + UBI_INT_VOL_COUNT)) + + (ubi->peb_count * sizeof(__be32)); return roundup(size, ubi->leb_size); } /** - * new_fm_vhdr - allocate a new volume header for fastmap usage. + * new_fm_vbuf() - allocate a new volume header for fastmap usage. * @ubi: UBI device description object * @vol_id: the VID of the new header * @@ -115,7 +106,7 @@ static struct ubi_vid_io_buf *new_fm_vbuf(struct ubi_device *ubi, int vol_id) struct ubi_vid_io_buf *new; struct ubi_vid_hdr *vh; - new = ubi_alloc_vid_buf(ubi, GFP_KERNEL); + new = ubi_alloc_vid_buf(ubi, GFP_NOFS); if (!new) goto out; @@ -477,7 +468,9 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai, if (err == UBI_IO_FF_BITFLIPS) scrub = 1; - add_aeb(ai, free, pnum, ec, scrub); + ret = add_aeb(ai, free, pnum, ec, scrub); + if (ret) + goto out; continue; } else if (err == 0 || err == UBI_IO_BITFLIPS) { dbg_bld("Found non empty PEB:%i in pool", pnum); @@ -647,8 +640,10 @@ static int ubi_attach_fastmap(struct ubi_device *ubi, if (fm_pos >= fm_size) goto fail_bad; - add_aeb(ai, &ai->free, be32_to_cpu(fmec->pnum), - be32_to_cpu(fmec->ec), 0); + ret = add_aeb(ai, &ai->free, be32_to_cpu(fmec->pnum), + be32_to_cpu(fmec->ec), 0); + if (ret) + goto fail; } /* read EC values from used list */ @@ -658,8 +653,10 @@ static int ubi_attach_fastmap(struct ubi_device *ubi, if (fm_pos >= fm_size) goto fail_bad; - add_aeb(ai, &used, be32_to_cpu(fmec->pnum), - be32_to_cpu(fmec->ec), 0); + ret = add_aeb(ai, &used, be32_to_cpu(fmec->pnum), + be32_to_cpu(fmec->ec), 0); + if (ret) + goto fail; } /* read EC values from scrub list */ @@ -669,8 +666,10 @@ static int ubi_attach_fastmap(struct ubi_device *ubi, if (fm_pos >= fm_size) goto fail_bad; - add_aeb(ai, &used, be32_to_cpu(fmec->pnum), - be32_to_cpu(fmec->ec), 1); + ret = add_aeb(ai, &used, be32_to_cpu(fmec->pnum), + be32_to_cpu(fmec->ec), 1); + if (ret) + goto fail; } /* read EC values from erase list */ @@ -680,8 +679,10 @@ static int ubi_attach_fastmap(struct ubi_device *ubi, if (fm_pos >= fm_size) goto fail_bad; - add_aeb(ai, &ai->erase, be32_to_cpu(fmec->pnum), - be32_to_cpu(fmec->ec), 1); + ret = add_aeb(ai, &ai->erase, be32_to_cpu(fmec->pnum), + be32_to_cpu(fmec->ec), 1); + if (ret) + goto fail; } ai->mean_ec = div_u64(ai->ec_sum, ai->ec_count); @@ -1107,8 +1108,7 @@ int ubi_fastmap_init_checkmap(struct ubi_volume *vol, int leb_count) if (!ubi->fast_attach) return 0; - vol->checkmap = kcalloc(BITS_TO_LONGS(leb_count), sizeof(unsigned long), - GFP_KERNEL); + vol->checkmap = bitmap_zalloc(leb_count, GFP_KERNEL); if (!vol->checkmap) return -ENOMEM; @@ -1117,7 +1117,7 @@ int ubi_fastmap_init_checkmap(struct ubi_volume *vol, int leb_count) void ubi_fastmap_destroy_checkmap(struct ubi_volume *vol) { - kfree(vol->checkmap); + bitmap_free(vol->checkmap); } /** @@ -1146,7 +1146,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, struct rb_node *tmp_rb; int ret, i, j, free_peb_count, used_peb_count, vol_count; int scrub_peb_count, erase_peb_count; - unsigned long *seen_pebs = NULL; + unsigned long *seen_pebs; fm_raw = ubi->fm_buf; memset(ubi->fm_buf, 0, ubi->fm_size); @@ -1160,7 +1160,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, dvbuf = new_fm_vbuf(ubi, UBI_FM_DATA_VOLUME_ID); if (!dvbuf) { ret = -ENOMEM; - goto out_kfree; + goto out_free_avbuf; } avhdr = ubi_get_vid_hdr(avbuf); @@ -1169,7 +1169,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, seen_pebs = init_seen(ubi); if (IS_ERR(seen_pebs)) { ret = PTR_ERR(seen_pebs); - goto out_kfree; + goto out_free_dvbuf; } spin_lock(&ubi->volumes_lock); @@ -1337,7 +1337,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, ret = ubi_io_write_vid_hdr(ubi, new_fm->e[0]->pnum, avbuf); if (ret) { ubi_err(ubi, "unable to write vid_hdr to fastmap SB!"); - goto out_kfree; + goto out_free_seen; } for (i = 0; i < new_fm->used_blocks; i++) { @@ -1359,7 +1359,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, if (ret) { ubi_err(ubi, "unable to write vid_hdr to PEB %i!", new_fm->e[i]->pnum); - goto out_kfree; + goto out_free_seen; } } @@ -1369,7 +1369,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, if (ret) { ubi_err(ubi, "unable to write fastmap to PEB %i!", new_fm->e[i]->pnum); - goto out_kfree; + goto out_free_seen; } } @@ -1379,58 +1379,14 @@ static int ubi_write_fastmap(struct ubi_device *ubi, ret = self_check_seen(ubi, seen_pebs); dbg_bld("fastmap written!"); -out_kfree: - ubi_free_vid_buf(avbuf); - ubi_free_vid_buf(dvbuf); +out_free_seen: free_seen(seen_pebs); -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; +out_free_dvbuf: + ubi_free_vid_buf(dvbuf); +out_free_avbuf: + ubi_free_vid_buf(avbuf); - ret = ec; out: - kfree(ec_hdr); return ret; } @@ -1460,7 +1416,7 @@ static int invalidate_fastmap(struct ubi_device *ubi) ubi->fm = NULL; ret = -ENOMEM; - fm = kzalloc(sizeof(*fm), GFP_KERNEL); + fm = kzalloc(sizeof(*fm), GFP_NOFS); if (!fm) goto out; @@ -1536,11 +1492,7 @@ int ubi_update_fastmap(struct ubi_device *ubi) struct ubi_fastmap_layout *new_fm, *old_fm; struct ubi_wl_entry *tmp_e; - down_write(&ubi->fm_protect); - down_write(&ubi->work_sem); - down_write(&ubi->fm_eba_sem); - - ubi_refill_pools(ubi); + ubi_refill_pools_and_lock(ubi); if (ubi->ro_mode || ubi->fm_disabled) { up_write(&ubi->fm_eba_sem); @@ -1549,15 +1501,7 @@ int ubi_update_fastmap(struct ubi_device *ubi) return 0; } - ret = ubi_ensure_anchor_pebs(ubi); - if (ret) { - up_write(&ubi->fm_eba_sem); - up_write(&ubi->work_sem); - up_write(&ubi->fm_protect); - return ret; - } - - new_fm = kzalloc(sizeof(*new_fm), GFP_KERNEL); + new_fm = kzalloc(sizeof(*new_fm), GFP_NOFS); if (!new_fm) { up_write(&ubi->fm_eba_sem); up_write(&ubi->work_sem); @@ -1582,7 +1526,7 @@ int ubi_update_fastmap(struct ubi_device *ubi) if (!tmp_e) { if (old_fm && old_fm->e[i]) { - ret = erase_block(ubi, old_fm->e[i]->pnum); + ret = ubi_sync_erase(ubi, old_fm->e[i], 0); if (ret < 0) { ubi_err(ubi, "could not erase old fastmap PEB"); @@ -1627,13 +1571,14 @@ int ubi_update_fastmap(struct ubi_device *ubi) } spin_lock(&ubi->wl_lock); - tmp_e = ubi_wl_get_fm_peb(ubi, 1); + tmp_e = ubi->fm_anchor; + ubi->fm_anchor = NULL; 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); + ret = ubi_sync_erase(ubi, old_fm->e[0], 0); if (ret < 0) { ubi_err(ubi, "could not erase old anchor PEB"); @@ -1645,7 +1590,6 @@ int ubi_update_fastmap(struct ubi_device *ubi) goto err; } new_fm->e[0] = old_fm->e[0]; - new_fm->e[0]->ec = ret; old_fm->e[0] = NULL; } else { /* we've got a new anchor PEB, return the old one */ @@ -1679,6 +1623,9 @@ out_unlock: up_write(&ubi->work_sem); up_write(&ubi->fm_protect); kfree(old_fm); + + ubi_ensure_anchor_pebs(ubi); + return ret; err: |
