From patchwork Mon Apr 12 08:36:03 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: PATCH 2/7] ubi: logging feature for ubi Date: Sun, 11 Apr 2010 22:36:03 -0000 From: Brijesh Singh X-Patchwork-Id: 49952 Message-Id: To: Artem.Bityutskiy@nokia.com Cc: linux-mtd@lists.infradead.org, rohitvdongre@gmail.com, David Woodhouse , rohit.dongre@samsung.com, brijesh.s.singh@gmail.com Note: super block for logging feature Signed-off-by: brijesh singh --- +} +#endif --- 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 +#include +#include +#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;