From patchwork Mon Jun 18 16:18:58 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Weinberger X-Patchwork-Id: 165517 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from merlin.infradead.org (merlin.infradead.org [IPv6:2001:4978:20e::2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 635C7B705E for ; Tue, 19 Jun 2012 02:22:55 +1000 (EST) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1Sgeho-0001Wz-83; Mon, 18 Jun 2012 16:21:33 +0000 Received: from a.ns.miles-group.at ([95.130.255.143] helo=radon.swed.at) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1Sgeg0-0000ff-0e for linux-mtd@lists.infradead.org; Mon, 18 Jun 2012 16:19:44 +0000 Received: (qmail 14432 invoked by uid 89); 18 Jun 2012 16:19:38 -0000 Received: by simscan 1.3.1 ppid: 14119, pid: 14429, t: 0.1751s scanners: attach: 1.3.1 clamav: 0.96.5/m:53 Received: from unknown (HELO localhost.localdomain) (richard@nod.at@212.62.202.73) by radon.swed.at with ESMTPA; 18 Jun 2012 16:19:38 -0000 From: Richard Weinberger To: linux-mtd@lists.infradead.org Subject: [PATCH 15/22] UBI: Fastmap: Kill old fastmap in case of a failure Date: Mon, 18 Jun 2012 18:18:58 +0200 Message-Id: <1340036345-96726-16-git-send-email-richard@nod.at> X-Mailer: git-send-email 1.7.6.5 In-Reply-To: <1340036345-96726-1-git-send-email-richard@nod.at> References: <1340036345-96726-1-git-send-email-richard@nod.at> X-Spam-Note: CRM114 invocation failed X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-1.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: artem.bityutskiy@linux.intel.com, linux-kernel@vger.kernel.org, adrian.hunter@intel.com, Heinz.Egger@linutronix.de, thomas.wucher@linutronix.de, shmulik.ladkani@gmail.com, Richard Weinberger , tglx@linutronix.de, Marius.Mazarel@ugal.ro, tim.bird@am.sony.com X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-mtd-bounces@lists.infradead.org Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org We have to make sure that the old (and from this point on invalid) fastmap is killed if we fail to create a new one. Signed-off-by: Richard Weinberger --- drivers/mtd/ubi/fastmap.c | 177 +++++++++++++++++++++++++-------------------- 1 files changed, 100 insertions(+), 77 deletions(-) diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c index f9e5dbb..7d36034 100644 --- a/drivers/mtd/ubi/fastmap.c +++ b/drivers/mtd/ubi/fastmap.c @@ -1266,6 +1266,59 @@ out: return ret; } +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; +} + +static int invalidate_fastmap(struct ubi_device *ubi, + struct ubi_fastmap_layout *fm) +{ + int ret, i; + + ret = erase_block(ubi, fm->e[0]->pnum); + + for (i = 0; i < fm->used_blocks; i++) + ubi_wl_put_fm_peb(ubi, fm->e[i], fm->to_be_tortured[i]); + + return ret; +} + /** * ubi_update_fastmap - will be called by UBI if a volume changes or * a fastmap pool becomes full. @@ -1307,10 +1360,35 @@ int ubi_update_fastmap(struct ubi_device *ubi) } mutex_lock(&ubi->fm_mutex); - old_fm = ubi->fm; ubi->fm = NULL; + if (new_fm->used_blocks > UBI_FM_MAX_BLOCKS) { + ubi_err("fastmap too large"); + 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, -1); + spin_unlock(&ubi->wl_lock); + + if (!tmp_e) { + int j; + ubi_err("could not get any free erase block"); + + for (j = 1; j < i; j++) + ubi_wl_put_fm_peb(ubi, new_fm->e[j], 0); + + ret = -ENOSPC; + goto err; + } + + new_fm->e[i]->pnum = tmp_e->pnum; + new_fm->e[i]->ec = tmp_e->ec; + } + spin_lock(&ubi->wl_lock); tmp_e = ubi_wl_get_fm_peb(ubi, UBI_FM_MAX_START); spin_unlock(&ubi->wl_lock); @@ -1318,53 +1396,14 @@ int ubi_update_fastmap(struct ubi_device *ubi) if (old_fm) { /* no fresh early PEB was found, reuse the old one */ if (!tmp_e) { - struct ubi_ec_hdr *ec_hdr; - long long ec; - - ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); - if (!ec_hdr) { - ret = -ENOMEM; - goto err; - } - - /* we have to erase the block by hand */ - ret = ubi_io_read_ec_hdr(ubi, old_fm->e[0]->pnum, - ec_hdr, 0); - if (ret && ret != UBI_IO_BITFLIPS) { - ubi_err("unable to read EC header"); - kfree(ec_hdr); - goto err; - } - - ret = ubi_io_sync_erase(ubi, old_fm->e[0]->pnum, - 0); + ret = erase_block(ubi, old_fm->e[0]->pnum); if (ret < 0) { - ubi_err("unable to erase old SB"); - kfree(ec_hdr); - goto err; - } - - ec = be64_to_cpu(ec_hdr->ec); - ec += ret; - old_fm->e[0]->ec = ec; - if (ec > UBI_MAX_ERASECOUNTER) { - ubi_err("erase counter overflow!"); - kfree(ec_hdr); - ret = -EINVAL; - goto err; - } - - ec_hdr->ec = cpu_to_be64(ec); - ret = ubi_io_write_ec_hdr(ubi, old_fm->e[0]->pnum, - ec_hdr); - kfree(ec_hdr); - if (ret) { - ubi_err("unable to write new EC header"); + ubi_err("could not erase old early PEB"); goto err; } new_fm->e[0]->pnum = old_fm->e[0]->pnum; - new_fm->e[0]->ec = old_fm->e[0]->ec; + new_fm->e[0]->ec = ret; } else { /* we've got a new early PEB, return the old one */ ubi_wl_put_fm_peb(ubi, old_fm->e[0], @@ -1389,48 +1428,32 @@ int ubi_update_fastmap(struct ubi_device *ubi) new_fm->e[0]->ec = tmp_e->ec; } - if (new_fm->used_blocks > UBI_FM_MAX_BLOCKS) { - ubi_err("fastmap too large"); - ret = -ENOSPC; - goto err; - } - - /* give the wl subsystem a chance to produce some free blocks */ - cond_resched(); - - for (i = 1; i < new_fm->used_blocks; i++) { - spin_lock(&ubi->wl_lock); - tmp_e = ubi_wl_get_fm_peb(ubi, -1); - spin_unlock(&ubi->wl_lock); - - if (!tmp_e) { - ubi_err("could not get any free erase block"); - - while (i--) { - ubi_wl_put_fm_peb(ubi, new_fm->e[i], 0); - kfree(new_fm->e[i]); - } - ret = -ENOSPC; - goto err; - } - - new_fm->e[i]->pnum = tmp_e->pnum; - new_fm->e[i]->ec = tmp_e->ec; - } - - kfree(old_fm); - - down_write(&ubi->fm_sem); down_write(&ubi->work_sem); + down_write(&ubi->fm_sem); ret = ubi_write_fastmap(ubi, new_fm); - up_write(&ubi->work_sem); up_write(&ubi->fm_sem); + up_write(&ubi->work_sem); + + if (ret) + goto err; + out_unlock: mutex_unlock(&ubi->fm_mutex); - + kfree(old_fm); return ret; err: kfree(new_fm); + + ubi_warn("Unable to write new fastmap, err=%i", ret); + + ret = 0; + if (old_fm) { + ret = invalidate_fastmap(ubi, old_fm); + if (ret < 0) + ubi_err("Unable to invalidiate current fastmap!"); + else if (ret) + ret = 0; + } goto out_unlock; }