Patchwork PATCH 2/7] ubi: logging feature for ubi

login
register
mail settings
Submitter Brijesh Singh
Date April 12, 2010, 8:36 a.m.
Message ID <k2p6b5362aa1004120136z63438118i56547305b28c3f70@mail.gmail.com>
Download mbox | patch
Permalink /patch/49952/
State New
Headers show

Comments

Brijesh Singh - April 12, 2010, 8:36 a.m.
Note: super block for logging feature

Signed-off-by: brijesh singh <brij.singh@samsung.com>
---
+}
+#endif

Patch

--- ubi_old/drivers/mtd/ubi/sb.c	1970-01-01 05:30:00.000000000 +0530
+++ ubi_new/drivers/mtd/ubi/sb.c	2010-04-09 21:54:02.645580870 +0530
@@ -0,0 +1,813 @@ 
+/*
+ * Copyright © 2009 Samsung Electronics
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author:	 Brijesh Singh
+ *		 Rohit Dongre
+ */
+
+/*
+ * The UBI Super Block handling.
+ * Super block or anchor blocks tracks ubi commit, el, which are moving.
+ * Super block is written on first and last peb.
+ */
+
+#include <linux/slab.h>
+#include <linux/crc32.h>
+#include <linux/err.h>
+#include "ubi.h"
+
+#ifdef CONFIG_MTD_UBI_LOGGED
+
+/**
+ * next_sqnum - get next sequence number.
+ * @ubi: UBI device description object
+ *
+ * This function returns next sequence number to use, which is just the current
+ * sb sequence counter value. It also increases the global sequence counter.
+ */
+static unsigned long long next_sqnum(struct ubi_device *ubi)
+{
+	unsigned long long sqnum;
+
+	spin_lock(&ubi->sb_lock);
+	sqnum = ubi->sb_sqnum++;
+	spin_unlock(&ubi->sb_lock);
+
+	return sqnum;
+}
+
+/**
+ * update_sqnum - update sequence number counter.
+ * @ubi: UBI device description object.
+ * @sqnum: sqnum to be updated from.
+ *
+ * Update sb sqnum to sqnum if value  is greater than present counter.
+ */
+
+static void update_sqnum(struct ubi_device *ubi, unsigned long long sqnum)
+{
+	spin_lock(&ubi->sb_lock);
+	if (ubi->sb_sqnum < sqnum)
+		ubi->sb_sqnum = sqnum;
+	spin_unlock(&ubi->sb_lock);
+}
+
+/**
+ * find_first_sb - find first sb physical eraseblock
+ * @ubi: UBI device description object.
+ *
+ * This function finds first good physical eraseblock.
+ * on first good eraseblock sb is stored.
+ * @returns pnum on success, negative value otherwise.
+ */
+static inline int find_first_sb(struct ubi_device *ubi)
+{
+	int pnum;
+	for (pnum = 0; pnum < ubi->peb_count; pnum++) {
+		if (ubi_io_is_bad(ubi, pnum)) {
+			ubi_warn("bad block at %d", pnum);
+			ubi->peb_lookup[pnum].status = UBIL_PEB_BAD;
+			continue;
+		}
+		return pnum;
+	}
+	return -pnum;
+}
+
+/**
+ * find_last_sb - find last sb physical eraseblock
+ * @ubi: UBI device description object.
+ *
+ * This function finds last good physical eraseblock.
+ * on last good eraseblock sb is stored.
+ * @returns pnum on success, negative value otherwise.
+ */
+static inline int find_last_sb(struct ubi_device *ubi)
+{
+	int pnum;
+	for (pnum = ubi->peb_count - 1; pnum > 0; pnum--) {
+		if (ubi_io_is_bad(ubi, pnum)) {
+			ubi_warn("bad block at %d", pnum);
+			ubi->peb_lookup[pnum].status = UBIL_PEB_BAD;
+			continue;
+		}
+		return pnum;
+	}
+	return -pnum;
+}
+
+/**
+ * recover_sb - recover corrupt sb
+ * @ubi: UBI device description object.
+ * @returns 0 on success, negative value otherwise.
+ *
+ * This function checks which bud is corrupt.A corrupt bud can have the latest
+ * copy of sb. In this case, it copies the latest sb in other bud and
then erase
+ * the corrupt bud. If not, erase the corrupt bud and make it usable.
+ * This function also erases the other bud to maintain sb syncronization.
+ * Finally the offset is set to the next place in log.
+ */
+static int recover_sb(struct ubi_device *ubi)
+{
+	struct ubi_sb *sb = ubi->sb_node;
+	int err = 0, data_size, crc;
+	int torture = 0;
+	int next_active_bud, next_offset;
+
+	if (ubi->sb_needs_rcvry == 3) {
+		ubi_warn("both sb blocks need recovery");
+		ubi_ro_mode(ubi);
+		return -EROFS;
+	}
+
+	sb->lh.node_type = cpu_to_be32(UBIL_SB_NODE_T);
+	data_size = sizeof(struct ubi_sb);
+
+	crc = crc32(UBI_CRC32_INIT, sb->lh.data, data_size);
+	sb->lh.data_size = cpu_to_be32(data_size);
+	sb->lh.data_crc = cpu_to_be32(crc);
+
+	if (ubi->sb_needs_rcvry == ubi->sb_active_bud + 1) {
+		/*
+		 *  bud needing recovery holds  the sb copy.
+		 *  erase and write to other one first.
+		 */
+		next_active_bud = (ubi->sb_active_bud == 0 ? 1 : 0);
+		next_offset = ubi->bud_start_offset;
+		torture = 0;
+	} else {
+		torture = 1;
+		next_active_bud = ubi->sb_needs_rcvry - 1;
+		next_offset = ubi->bud_start_offset;
+	}
+
+erase_sync_sb:
+	err = ubi_io_sync_erase(ubi, ubi->sb_buds[next_active_bud], torture);
+	if (err < 0) {
+		ubi_err("error erasing sb block");
+		goto out_err;
+	}
+
+	err = ubi_write_node(ubi, node2lnode(sb), ubi->sb_buds[next_active_bud],
+			     next_offset, ubi->node_size);
+	if (err) {
+		ubi_err("could not write sb");
+		goto out_err;
+	}
+	/* write bud hdr to recognize this sb bud */
+	err = ubi_sb_create_dflt(ubi, next_active_bud,
+				 ubi->sb_buds[next_active_bud]);
+	if (err < 0) {
+		ubi_err("error writing sb block");
+		goto out_err;
+	}
+
+	if (ubi->sb_needs_rcvry == 0) {
+		ubi->sb_active_bud = 1;
+		ubi->sb_offset = ubi->bud_start_offset;
+		return 0;
+	}
+
+	if (ubi->sb_needs_rcvry == ubi->sb_active_bud + 1) {
+		/* "updated sb" is available in other bud.
+		 *  we have to torture and write curropt bud */
+		torture = 1;
+		next_active_bud = ubi->sb_needs_rcvry - 1;
+		next_offset = ubi->bud_start_offset;
+		ubi->sb_needs_rcvry = 0;
+		goto erase_sync_sb;
+	} else {
+		ubi->sb_needs_rcvry = 0;
+		next_active_bud = (ubi->sb_active_bud == 0 ? 1 : 0);
+		next_offset = ubi->bud_start_offset;
+		torture = 0;
+		goto erase_sync_sb;
+
+	}
+	/* scrub the bud to be recovered */
+
+out_err:
+	ubi_err("sb recovery failed");
+	ubi_ro_mode(ubi);
+	return err;
+}
+
+/**
+ * validate_bud_hdr - validate sb bud header
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock ti read
+ * @bud_hdr: buffer to read bud header
+ *
+ * This function reads sb bud header to @bud_hdr from physical  eraseblock
+ * @pnum. Then it validates the bud header.
+ * @returns 0 on success, error code other wise.
+ */
+static int validate_bud_hdr(struct ubi_device *ubi, int pnum,
+			    struct sb_bud_hdr *bud_hdr)
+{
+	int err = 0;
+
+	dbg_sb("processing bud header at pnum %d", pnum);
+
+	err = ubi_io_read(ubi, node2lnode(bud_hdr), pnum, UBIL_BUD_HDR_OFFSET,
+			  ubi->node_size);
+	if (err && (err != -EBADMSG || err != UBI_IO_BITFLIPS)) {
+		ubi_err("could not read header for sb block %d", pnum);
+		return err;
+	}
+
+	err = verify_node(ubi, node2lnode(bud_hdr), UBIL_SB_BUD_HDR_T);
+	if (err)
+		return err;
+
+	if (be32_to_cpu(bud_hdr->MAGIC) != UBI_MAGIC) {
+		ubi_err("UBI Magic Mismatch at pnum %d", pnum);
+		return -EINVAL;
+	}
+	if (be32_to_cpu(bud_hdr->ubi_version) != UBI_VERSION) {
+		ubi_err("UBI Version Mismatch at pnum %d", pnum);
+		return -EINVAL;
+	}
+	if (be32_to_cpu(bud_hdr->el_version) != UBIL_EL_VERSION) {
+		ubi_err("UBI Log Version Mismatch at pnum %d", pnum);
+		return -EINVAL;
+	}
+	if (be32_to_cpu(bud_hdr->cmt_version) != UBIL_CMT_VERSION) {
+		ubi_err("UBI Commit Mismatch at pnum %d", pnum);
+		return -EINVAL;
+	}
+	if (be32_to_cpu(bud_hdr->lh.node_type) != UBIL_SB_BUD_HDR_T) {
+		ubi_err("UBI Commit Mismatch at pnum %d", pnum);
+		return -EINVAL;
+	}
+
+	if (ubi->flash_size != be64_to_cpu(bud_hdr->flash_size))
+		goto out_err;
+	if (ubi->peb_count != be32_to_cpu(bud_hdr->peb_count))
+		goto out_err;
+	if (ubi->peb_size != be32_to_cpu(bud_hdr->peb_size))
+		goto out_err;
+	if (ubi->min_io_size != be32_to_cpu(bud_hdr->min_io_size))
+		goto out_err;
+	if (ubi->hdrs_min_io_size != be32_to_cpu(bud_hdr->hdrs_min_io_size))
+		goto out_err;
+	if (ubi->bad_allowed != be32_to_cpu(bud_hdr->bad_allowed))
+		goto out_err;
+
+	update_sqnum(ubi, be64_to_cpu(bud_hdr->sqnum));
+	return 0;
+
+out_err:
+	ubi_err("bad values in sb");
+	return -EBADMSG;
+}
+
+/**
+ * validate_sb_node - validate sb node
+ * @ubi: UBI device description object
+ * @sb: sb node to be validated
+ *
+ * This function validates data stored in sb node @sb.
+ * @return 0 on success, error code otherwise.
+ */
+static inline int validate_sb_node(struct ubi_device *ubi, struct ubi_sb *sb)
+{
+
+	int el_resrvd_buds = 0, cmt_next_buds = 0;
+	int node_type;
+
+	if (be32_to_cpu(sb->version) != UBIL_SB_VERSION)
+		return -EBADMSG;
+
+	/* check node type */
+	node_type = be32_to_cpu(sb->lh.node_type);
+	if (node_type != UBIL_SB_NODE_T) {
+		ubi_err("bad node type");
+		return -EBADMSG;
+	}
+
+	el_resrvd_buds = be32_to_cpu(sb->el_resrvd_buds);
+	if (el_resrvd_buds <= 0 || el_resrvd_buds != ubi->el_reservd_buds)
+		return -EBADMSG;
+
+	/**
+	 * vtbl copies can be added to sb. This will help in finding vtbl table
+	 * without scanning.
+	 * if (be32_to_cpu(sb->vtbl_peb[0]) <= 0
+	 *	        || be32_to_cpu(sb->vtbl_peb[1]) <= 0)
+	 *	        return -EBADMSG;
+	 */
+
+	if (be32_to_cpu(sb->cmt_status) != UBIL_CMT_INVALID &&
+	    be32_to_cpu(sb->cmt_status) != UBIL_CMT_WRITE_SUCCESS)
+		return -EBADMSG;
+
+	if (be32_to_cpu(sb->cmt_resrvd_buds) <= 0)
+		return -EBADMSG;
+
+	cmt_next_buds = be32_to_cpu(sb->cmt_next_buds);
+	if (cmt_next_buds < 0)
+		return -EBADMSG;
+
+	return 0;
+}
+
+/**
+ * process_bud - process sb bud
+ * @ubi: UBI device description object
+ * @buf: sb bud content to process
+ *
+ * This function process sb bud @buf, to find last valid
+ * entry from sb. Each entry in sb bud is verified until
+ * empty entry is found or usable bud length is exceeded.
+ * If empty entry is found in sb bud, then ubi->sb_offset
+ * is initialized to previous valid entry. Validation of
+ * valid entry is done.
+ *
+ * In case of corrupt entry in sb bud, function return one
+ * for doing sb recovery. if tail in bud is invalid then -EBADMSG
+ * is returned.
+ */
+
+static int process_bud(struct ubi_device *ubi, void *buf)
+{
+	int err = 0, valid = 1, recovery_needed = 0;
+	struct ubi_sb *node, *valid_node = NULL;
+	int offset = 0;
+
+	while (offset < ubi->bud_usable_len) {
+		node = (struct ubi_sb *)(buf + offset);
+		/* verify if the data content and crc are matching */
+		err = verify_node(ubi, node2lnode(node), UBIL_SB_NODE_T);
+		if (err == UBIL_NODE_EMPTY)
+			break;
+		else if (err) {
+			/*
+			 * seems like one of the entries is corrupt.
+			 * doesn't matter, if we can still get the tail sb,
+			 * super block will be valid.
+			 */
+			ubi_warn("sb node corrupt at offset %d err %d",
+				 offset + ubi->bud_start_offset, err);
+			valid = 1;
+			recovery_needed = 1;
+			offset += ubi->node_size;
+			continue;
+		}
+		valid_node = node;
+		valid = 0;
+		offset += ubi->node_size;
+	}
+
+	/*
+	 *  If tail is invalid, the block does not contain last valid entry
+	 */
+	if (valid == 1)
+		return -EBADMSG;
+
+	/* Offset starts at node start offset */
+	ubi->sb_offset = ubi->bud_start_offset;
+	/* we exited the loop at free node. */
+	ubi->sb_offset += offset - ubi->node_size;
+
+	/* validate contents */
+	err = validate_sb_node(ubi, valid_node);
+	if (err)
+		return err;
+
+	return recovery_needed;
+}
+
+/**
+ * initialize_sb - initialize sb
+ * @ubi: UBI device description object
+ * @node: valid sb node
+ *
+ * This function initializes in ram sb with valid sb node found on flash
+ */
+static int initialize_sb(struct ubi_device *ubi, struct ubi_sb *node)
+{
+	memcpy(ubi->sb_node, node, ubi->node_size);
+	return 0;
+}
+
+/**
+ * ubi_sb_get_node - get sb node
+ * @ubi: UBI device description object
+ *
+ * This function takes sb lock and returns ubi->sb_node. For any change in
+ * sb_node, caller must call this function.
+ */
+struct ubi_sb *ubi_sb_get_node(struct ubi_device *ubi)
+{
+	mutex_lock(&ubi->sb_mutex);
+	return ubi->sb_node;
+}
+
+/**
+ * ubi_sb_put_node - release sb lock
+ * @ubi: UBI device description object
+ *
+ * This function releases sb lock
+ */
+
+void ubi_sb_put_node(struct ubi_device *ubi)
+{
+	mutex_unlock(&ubi->sb_mutex);
+}
+
+/**
+ * ubi_sb_sync_node - write sb node
+ * @ubi: UBI device description object
+ *
+ * This function writes sb node on flash. During write operation if sb full
+ * condition is reached then, sb is erases and updated sb is written
atomically.
+ * @return: 0 on success, error code otherwise.
+ * Note: This function expects caller to take lock using ubi_sb_get_node.
+ */
+int ubi_sb_sync_node(struct ubi_device *ubi)
+{
+	struct ubi_sb *sb = ubi->sb_node;
+	int err, data_size, crc;
+	int next_active_bud, next_offset;
+
+	sb->lh.node_type	= cpu_to_be32(UBIL_SB_NODE_T);
+	sb->version		= cpu_to_be32(UBIL_SB_VERSION);
+	data_size		= sizeof(struct ubi_sb);
+
+	crc = crc32(UBI_CRC32_INIT, sb->lh.data, data_size);
+
+	sb->lh.data_size 	= cpu_to_be32(data_size);
+	sb->lh.data_crc 	= cpu_to_be32(crc);
+
+	if (ubi->sb_active_bud == 1) {
+		next_active_bud = 0;
+		next_offset = ubi->sb_offset + ubi->node_size;
+		if (next_offset >= ubi->peb_size) {
+			err = ubi_io_sync_erase(ubi,
+						ubi->sb_buds[next_active_bud],
+						0);
+			if (err < 0) {
+				ubi_err("error erasing sb block");
+				goto out_err;
+			}
+			err = ubi_sb_create_dflt(ubi, next_active_bud,
+						 ubi->sb_buds[next_active_bud]);
+			if (err < 0) {
+				ubi_err("error writing sb block");
+				goto out_err;
+			}
+			next_offset = ubi->bud_start_offset;
+		}
+	} else if (ubi->sb_active_bud == 0) {
+		next_active_bud = 1;
+		next_offset = ubi->sb_offset;
+		if (next_offset == ubi->bud_start_offset) {
+			err = ubi_io_sync_erase(ubi,
+						ubi->sb_buds[next_active_bud],
+						0);
+			if (err < 0) {
+				ubi_err("error erasing sb block");
+				goto out_err;
+			}
+			err = ubi_sb_create_dflt(ubi, next_active_bud,
+						 ubi->sb_buds[next_active_bud]);
+			if (err < 0) {
+				ubi_err("error writing sb block");
+				goto out_err;
+			}
+		}
+	} else {
+		/* Something went wrong */
+		ubi_err("active sb beyond limitation");
+		BUG();
+	}
+
+	err = ubi_write_node(ubi, node2lnode(sb), ubi->sb_buds[next_active_bud],
+			     next_offset, ubi->node_size);
+	if (err) {
+		ubi_err("could not write sb");
+		goto out_err;
+
+	}
+
+	ubi->sb_active_bud = next_active_bud;
+	ubi->sb_offset = next_offset;
+	return 0;
+out_err:
+	ubi_ro_mode(ubi);
+	return err;
+}
+
+/**
+ * ubi_get_sb - read valid sb node
+ * @ubi: UBI device description object
+ *
+ * This function reads sb bud and find valid entry from sb bud.
+ * If valid entry is found then ubi->sb_node is initialized
+ * and return zero. In case of failure negative error code
+ * is returned.
+ */
+
+int ubi_get_sb(struct ubi_device *ubi)
+{
+	int err = 0, bud, pnum = 0;
+	int rcvr_sb = 0;
+	int bud_is_bad[2] = { 1, 1 };
+	struct sb_bud_hdr *bud_hdr;
+
+	struct ubi_sb *sb_node, *valid_node;
+
+	dbg_sb("ubi trying to find and intialize super blocks");
+
+	ubi->sb_needs_rcvry = 0;
+
+	/* Find first and last block for super blocks */
+	ubi->sb_buds[0] = find_first_sb(ubi);
+	if (ubi->sb_buds[0] < 0) {
+		ubi_err("not enough good blocks");
+		return -EINVAL;
+	}
+
+	ubi->sb_buds[1] = find_last_sb(ubi);
+	if (ubi->sb_buds[1] < 0 || ubi->sb_buds[1] == ubi->sb_buds[0]) {
+		ubi_err("not enough good blocks");
+		return -EINVAL;
+	}
+
+	dbg_sb("sb buds found at pnum %d %d", ubi->sb_buds[0], ubi->sb_buds[1]);
+
+	/* Now validate these pebs for sb bud headers */
+	bud_hdr = alloc_nodes(ubi, UBIL_SB_BUDS_RESRVD);
+	if (!bud_hdr) {
+		ubi_err("error allocating memory");
+		return -ENOMEM;
+	}
+
+	for (bud = 0; bud < 2; bud++)
+		bud_is_bad[bud] = validate_bud_hdr(ubi, ubi->sb_buds[bud],
+						   &bud_hdr[bud]);
+
+	/*
+	 * Check for active bud. If bud 0 is bad, bud 1 is valide
+	 * If both pebs have bad header, we should not process them
+	 */
+
+	if (!bud_is_bad[0])
+		ubi->sb_active_bud = 0;
+	else if (!bud_is_bad[1])
+		ubi->sb_active_bud = 1;
+	else {
+		ubi_err("both super blocks are bad");
+		free_nodes(bud_hdr);
+		return -EBADMSG;
+	}
+
+	/* read the entire block and find updates sb copy. */
+	mutex_lock(&ubi->buf_mutex);
+	memset(ubi->peb_buf1, 0xFF, ubi->peb_size);
+
+	sb_node = alloc_nodes(ubi, 1);
+	if (!sb_node) {
+		ubi_err("error allocating memory");
+		mutex_unlock(&ubi->buf_mutex);
+		free_nodes(bud_hdr);
+		return -ENOMEM;
+	}
+
+retry:
+	/* Read the active sb pnum and process it to find updated sb copy */
+	pnum = ubi->sb_buds[ubi->sb_active_bud];
+	err = ubi_io_read(ubi, ubi->peb_buf1, pnum, ubi->bud_start_offset,
+			  ubi->bud_usable_len);
+	if (err == UBI_IO_BITFLIPS || err == -EBADMSG) {
+		/*
+		 * Scrub the PEB later. Note, -EBADMSG indicates an
+		 * uncorrectable ECC error, but we have our own CRC and
+		 * the data will be checked later.
+		 * One or both copies of sb will need to be recovered later
+		 * This flag is set to 1.
+		 */
+		rcvr_sb = 1;
+	} else if (err) {
+		/*
+		 * something went wrong. We can not process this bud.
+		 * Try other
+		 */
+		goto out_retry;
+	}
+
+	err = process_bud(ubi, ubi->peb_buf1);
+	if (err < 0) {
+		/* Try other bud */
+		goto out_retry;
+	} else if (err)
+		rcvr_sb = 1;
+	/*
+	 * If sb_needs_rcvry = 0, no recovery needed.
+	 * If sb_needs_rcvry = 1, first bud needs scrubing.
+	 * If sb_needs_rcvry = 2, second bud needs scrubing.
+	 * If sb_needs_rcvry = 3, both buds need scrubing.
+	 *
+	 */
+	if (rcvr_sb)
+		ubi->sb_needs_rcvry += ubi->sb_active_bud + 1;
+
+	/* reset recovery flag for retry */
+	rcvr_sb = 0;
+
+	/* get valid node offset just processed by process_bud */
+	valid_node = (struct ubi_sb *)(ubi->peb_buf1 + ubi->sb_offset
+				       - ubi->bud_start_offset);
+	/*
+	 * one of the good blocks holding active super block is processed.
+	 */
+
+	if (ubi->sb_active_bud == 0 && bud_is_bad[1] == 0) {
+		if (be64_to_cpu(bud_hdr[1].sqnum) <
+		    be64_to_cpu(bud_hdr[0].sqnum)) {
+			/*
+			 * This is older copy. Skip it, next sb_sync will
+			 * erase and reuse it.
+			 */
+			ubi_msg("sb #1 is old");
+			goto out;
+		}
+
+		err = ubi_read_node(ubi, node2lnode(sb_node), ubi->sb_buds[1],
+				    ubi->sb_offset, ubi->node_size);
+		if (err) {
+			if (err == UBIL_NODE_EMPTY)
+				goto out;
+
+			ubi_err("could not read super block #1");
+			/* Second copy needs recovery */
+			ubi->sb_needs_rcvry += 2;
+			goto out;
+		}
+
+		/* verify if the data content and crc are matching */
+		err = verify_node(ubi, node2lnode(sb_node), UBIL_SB_NODE_T);
+		if (err == UBIL_NODE_EMPTY)
+			goto out;
+		else if (err) {
+			ubi_warn("sb #1 is corrupt");
+			/* Second copy needs recovery */
+			ubi->sb_needs_rcvry += 2;
+			goto out;
+		}
+
+		/* validate contents */
+		err = validate_sb_node(ubi, sb_node);
+		if (err) {
+			/* Second copy needs recovery */
+			ubi->sb_needs_rcvry += 2;
+			goto out;
+		}
+
+		ubi->sb_active_bud = 1;
+		valid_node = sb_node;
+	}
+
+out:
+	initialize_sb(ubi, valid_node);
+
+	if (ubi->sb_needs_rcvry)
+		recover_sb(ubi);
+
+	err = 0;
+
+	mutex_unlock(&ubi->buf_mutex);
+	kfree(sb_node);
+	free_nodes(bud_hdr);
+	return err;
+
+out_retry:
+	ubi_warn("could not read sb bud #%d err %d", ubi->sb_active_bud, err);
+	/* Set active bud as bad */
+	bud_is_bad[ubi->sb_active_bud] = 1;
+	/*
+	 * If we were processing first bud retry for second bud.
+	 * Do not proceed if second bud is already bad.
+	 * If we were processing second bud, throw error as we are out of buds
+	 */
+	if (ubi->sb_active_bud == 0 && !bud_is_bad[1]) {
+		ubi->sb_active_bud = 1;
+		goto retry;
+	}
+	mutex_unlock(&ubi->buf_mutex);
+	kfree(sb_node);
+	free_nodes(bud_hdr);
+	return err;
+}
+
+/**
+ * ubi_sb_init - initialize super block
+ * @ubi: UBI device description object
+ *
+ * This function initializes super block.
+ */
+int ubi_sb_init(struct ubi_device *ubi)
+{
+
+	dbg_sb("initializing sb parameters");
+
+	ubi->sb_node = kmalloc(ubi->node_size, GFP_KERNEL);
+	if (!ubi->sb_node) {
+		ubi_err("could not allocate memory");
+		return -ENOMEM;
+	}
+	memset(ubi->sb_node, 0xff, ubi->node_size);
+
+	ubi->sb_needs_rcvry = 0;
+	ubi->sb_offset = 0;
+	ubi->sb_active_bud = 1;
+
+	mutex_init(&ubi->sb_mutex);
+	spin_lock_init(&ubi->sb_lock);
+	ubi->sb_sqnum = 1;
+	return 0;
+}
+
+/**
+ * ubi_sb_close - release super block
+ * @ubi: UBI device description object
+ *
+ * Free allocated memory.
+ */
+
+int ubi_sb_close(struct ubi_device *ubi)
+{
+	kfree(ubi->sb_node);
+	return 0;
+}
+
+/**
+ * ubi_sb_create_dflt - create default sb header
+ * @ubi: UBI device description object
+ * @bud: sb bud
+ * @pnum: physical eraseblock to create sb
+ *
+ * This function writes sb header for sb bud @bud on
+ * physiacl eraseblock @pnum. If header is successfully
+ * written this function return zero. In case of failure
+ * function return negative error code.
+ */
+
+int ubi_sb_create_dflt(struct ubi_device *ubi, int bud, int pnum)
+{
+	int err;
+	struct sb_bud_hdr *bud_hdr;
+	int data_size, crc;
+
+	dbg_sb("create sb called on pnum %d bud_id %d", pnum, bud);
+
+	bud_hdr = kmalloc(ubi->node_size, GFP_KERNEL);
+	memset(bud_hdr, 0xff, ubi->node_size);
+
+	bud_hdr->MAGIC			= cpu_to_be32(UBI_MAGIC);
+	bud_hdr->ubi_version		= cpu_to_be32(UBI_VERSION);
+	bud_hdr->el_version		= cpu_to_be32(UBIL_EL_VERSION);
+	bud_hdr->cmt_version		= cpu_to_be32(UBIL_CMT_VERSION);
+
+	bud_hdr->rsvd_pebs		= cpu_to_be32(ubi->rsvd_pebs);
+	bud_hdr->beb_rsvd_pebs		= cpu_to_be32(ubi->beb_rsvd_pebs);
+	bud_hdr->beb_rsvd_level		= cpu_to_be32(ubi->beb_rsvd_level);
+	bud_hdr->flash_size		= cpu_to_be64(ubi->flash_size);
+	bud_hdr->peb_count		= cpu_to_be32(ubi->peb_count);
+	bud_hdr->peb_size		= cpu_to_be32(ubi->peb_size);
+	bud_hdr->min_io_size		= cpu_to_be32(ubi->min_io_size);
+	bud_hdr->hdrs_min_io_size	= cpu_to_be32(ubi->hdrs_min_io_size);
+	bud_hdr->bad_allowed		= cpu_to_be32(ubi->bad_allowed);
+	bud_hdr->sqnum			= cpu_to_be64(next_sqnum(ubi));
+
+	data_size = sizeof(struct sb_bud_hdr);
+	crc = crc32(UBI_CRC32_INIT, bud_hdr->lh.data, data_size);
+
+	bud_hdr->lh.data_size		= cpu_to_be32(data_size);
+	bud_hdr->lh.data_crc		= cpu_to_be32(crc);
+	bud_hdr->lh.node_type		= cpu_to_be32(UBIL_SB_BUD_HDR_T);
+	bud_hdr->lh.node_id		= cpu_to_be32(bud);
+
+	err = ubi_write_node(ubi, node2lnode(bud_hdr),
+				pnum, UBIL_BUD_HDR_OFFSET, ubi->node_size);
+	kfree(bud_hdr);
+	if (!err)
+		ubi->sb_buds[bud] = pnum;
+	return err;