From patchwork Mon Apr 12 08:36:18 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: PATCH 4/7] ubi: logging feature for ubi Date: Sun, 11 Apr 2010 22:36:18 -0000 From: Brijesh Singh X-Patchwork-Id: 49956 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: el(eba log) for logging feature Signed-off-by: brijesh singh --- --- ubi_old/drivers/mtd/ubi/el.c 1970-01-01 05:30:00.000000000 +0530 +++ ubi_new/drivers/mtd/ubi/el.c 2010-04-09 21:54:02.624073440 +0530 @@ -0,0 +1,568 @@ +/* + * 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 el blocks are logged blocks. They store the eba mappings. When one + * peb mapping is to be read or written, we group the pebs (utilize the space) + * When el blocks are full, commit is called which relocates the blocks. + */ + +#include +#include +#include +#include "ubi.h" + +#ifdef CONFIG_MTD_UBI_LOGGED + +/** + * verify_bud_hdr - verify el bud header + * @ubi_device: UBI device description object + * @pnum: physical eraseblock + * @bud_hdr: el bud header to be verified + * + * This function verifies the el_bud_hdr for consitency. + * @return: On success return 0, error value other wise. + */ +static inline int verify_bud_hdr(struct ubi_device *ubi, int pnum, + struct el_bud_hdr *bud_hdr) +{ + int err; + + /* verify generic node */ + err = verify_node(ubi, node2lnode(bud_hdr), UBIL_EL_BUD_HDR_T); + if (err) + return err; + + /* check node type */ + if (be32_to_cpu(bud_hdr->lh.node_type) != UBIL_EL_BUD_HDR_T) { + ubi_err("node type mismatch"); + return -EINVAL; + } + + /* check bud magic */ + if (be32_to_cpu(bud_hdr->MAGIC) != UBIL_EL_BUD_HDR_MAGIC) { + ubi_err("magic Mismatch"); + return -EBADMSG; + } + + /* check version for el */ + if (be32_to_cpu(bud_hdr->log_version) != UBIL_EL_VERSION) { + ubi_err("version mismatch"); + return -EINVAL; + } + + return 0; +} + +/** + * process_node - process el node + * @ubi_device: UBI device description object + * @node: el node + * + * This function checks el node @node and based on group to + * which @node belong, initializes peb_lookup table. + * If @node valid and initialization is done successfull, + * @return: On success return 0, error value other wise. + */ +static int process_node(struct ubi_device *ubi, struct el_node *node) +{ + int data_size, group, node_type, pnum; + int err; + + err = verify_node(ubi, node2lnode(node), UBIL_EL_NODE_T); + if (err) + return err; + + /* check node type */ + node_type = be32_to_cpu(node->lh.node_type); + if (node_type != UBIL_EL_NODE_T) { + ubi_err("bad node type"); + return -EBADMSG; + } + + group = be32_to_cpu(node->lh.node_id); + if (group < 0 || group >= ubi->el_no_of_grps) + return -EINVAL; + + data_size = be32_to_cpu(node->lh.data_size); + if (data_size > ubi->node_size) + return -EBADMSG; + + /* copy the information in peb_lookup buffer */ + pnum = group * ubi->el_pebs_in_grp; + memcpy(&ubi->peb_lookup[pnum], node->recs, data_size); + return 0; +} + +/** + * prepare_node - generate el node + * @ubi_device: UBI device description object + * @node: el node where to prepare. + * @group: group number for which node should be prepared. + * + * This function prepares a node for said group value. + * TODO grouping recently used pebs can help in removing peb_lookup buffer. + * @return: On success return 0, error value other wise. + */ +static void prepare_node(struct ubi_device *ubi, + struct el_node *node, int group) +{ + int pnum = group * ubi->el_pebs_in_grp; + int data_size, crc; + /* data size for node */ + data_size = ubi->el_pebs_in_grp * sizeof(struct peb_info); + if (pnum + ubi->el_pebs_in_grp > ubi->peb_count) + data_size = (ubi->peb_count - pnum) * sizeof(struct peb_info); + /* copy group information in node */ + memcpy(node->recs, &ubi->peb_lookup[pnum], data_size); + + node->lh.node_type = cpu_to_be32(UBIL_EL_NODE_T); + node->lh.node_id = cpu_to_be32(group); + node->lh.data_size = cpu_to_be32(data_size); + + /* Calculate the Data CRC First */ + crc = crc32(UBI_CRC32_INIT, node->lh.data, data_size); + node->lh.data_crc = cpu_to_be32(crc); +} + +/** + * process_bud - process el bud + * @ubi_device: UBI device description object + * @buf: buffer containing el bud + * + * This processes nodes in the bud.empty node is the tail of the log.If any + * node is corrupt, the node is skipped. In this case, cmt is scheduled. + * After processing of el bud, ubi->el_offset is set to last valid el node, + * that is to tail of the log. + * @return: On success return 0, error value other wise. + */ +static int process_bud(struct ubi_device *ubi, void *buf) +{ + int err = UBIL_NODE_EMPTY; + struct el_node *node; + int offset = 0; + + for (offset = 0; offset < ubi->bud_usable_len; + offset += ubi->node_size) { + node = (struct el_node *)(buf + offset); + err = process_node(ubi, node); + if (err == UBIL_NODE_EMPTY) + break; + else if (err) { + /* + * Seems like one of the entries is corrupt. + * Doesn't matter, lets continue. + */ + ubi_warn("el bad node offset %d, needs recovery", + offset); + ubi_schedule_cmt(ubi); + continue; + } + } + + ubi->el_offset = offset; + if (offset >= ubi->bud_usable_len) + return UBIL_BUD_FULL; + else + return err; + +} + +/** + * reserve_space -reserve space in log. + * @ubi: ubi device descrition object + * @pnum: pointer to pnum + * @offset: offset in pnum + * + * This function returns 0 on success and negative err on failure. + */ +static inline int reserve_space(struct ubi_device *ubi, int *pnum, int *offset) +{ + int off = ubi->el_offset; + int bud = ubi->el_active_bud; + + off += ubi->node_size; + if (off >= ubi->peb_size) { + /** + * there is no space in bud, go to next bud. + * write log from start + */ + bud += 1; + off = ubi->bud_start_offset; + if (bud >= ubi->el_reservd_buds) { + /* there are no bud left. */ + return -ENOSPC; + } + } + + /* Set el offset and active bud to new once */ + ubi->el_offset = off; + ubi->el_active_bud = bud; + + /* Set return pnum and offset */ + *pnum = ubi->el_buds[ubi->el_active_bud]; + *offset = off; + return 0; +} + +/** + * el_write_node - write el node to log + * @ubi_device: UBI device description object + * @group: group number of el + * + * This function writes el node to the tail of the log. If there is no space, + * cmt is called. This creates a new log.node will be written to the new log + * If cmt operation is in progress, el node should not be written. + * @return: On success return 0, error value other wise. + */ +static int el_write_node(struct ubi_device *ubi, int group) +{ + int err; + struct el_node *node; + int pnum, offset; + + /* + * When cmt is in progress only wl is writing. + * Let it halppen in Ram.This is necessary because sometime + * Wl has to give free blocks to cmt. */ + + if (ubi_cmt_progress(ubi)) + BUG(); + + node = alloc_nodes(ubi, 1); + if (!node) + return -ENOMEM; + +rsrv_space: + err = reserve_space(ubi, &pnum, &offset); + if (err == -ENOSPC) { + err = ubi_cmt(ubi); + if (err) { + ubi_err("ubi commit error %d", err); + goto out_err; + } + goto rsrv_space; + } + + /* TODO there is inconsistency in prepare node. + * But needs better solution. + */ + prepare_node(ubi, node, group); + ubi->c_dirty = C_DIRTY; + + err = ubi_write_node(ubi, node2lnode(node), pnum, + offset, ubi->node_size); + if (err) { + ubi_err("could not write eba to log "); + ubi_ro_mode(ubi); + goto out_err; + + } +out_err: + free_nodes(node); + return err; +} + +/** + * ubi_el_scan - scan the el log. + * @ubi_device: UBI device description object + * + * el_scan scans the entire el buds, and processes them. map of valid entries + * is created in the peb_lookup buffer. + * @return: On success return 0, error value other wise. + */ +int ubi_el_scan(struct ubi_device *ubi) +{ + int err = 0; + int pnum, bud; + struct ubi_sb *sb = ubi->sb_node; + + struct el_bud_hdr *bud_hdr; + + bud_hdr = kmalloc(ubi->node_size, GFP_KERNEL); + if (!bud_hdr) { + ubi_msg("out of memory"); + return -ENOMEM; + } + + for (bud = 0; bud < ubi->el_reservd_buds; bud++) { + pnum = be32_to_cpu(sb->buds[bud]); + err = ubi_read_node(ubi, node2lnode(bud_hdr), pnum, + UBIL_BUD_HDR_OFFSET, ubi->node_size); + if (err) { + ubi_err("error reading el bud %d pum %d", bud + 1, + pnum); + if (err == -EBADMSG || err == UBI_IO_BITFLIPS) { + /* we are getting ecc error from hardware. + * schedule cmt to relocate el. */ + ubi_schedule_cmt(ubi); + } else + goto out_err; + } + + err = verify_bud_hdr(ubi, pnum, bud_hdr); + if (err) + goto out_err; + + ubi->el_buds[bud] = pnum; + } + + mutex_lock(&ubi->buf_mutex); + memset(ubi->peb_buf1, 0xFF, ubi->peb_size); + + for (bud = 0; bud < ubi->el_reservd_buds; bud++) { + pnum = ubi->el_buds[bud]; + ubi->el_active_bud = bud; + err = ubi_io_read(ubi, ubi->peb_buf1, pnum, + ubi->bud_start_offset, ubi->bud_usable_len); + if (err) { + ubi_err("error reading el bud %d pum %d", bud + 1, + pnum); + if (err == -EBADMSG || err == UBI_IO_BITFLIPS) + ubi_schedule_cmt(ubi); + else + goto out_read_err; + } + + err = process_bud(ubi, ubi->peb_buf1); + if (err == UBIL_NODE_EMPTY) + break; + else if (err != UBIL_BUD_FULL) + goto out_read_err; + } + +out_read_err: + mutex_unlock(&ubi->buf_mutex); +out_err: + kfree(bud_hdr); + return (err == UBIL_NODE_EMPTY || err == UBIL_BUD_FULL) ? 0 : err; +} + +/** + * ubi_write_bud_hdr + * @ubi: UBI device description object + * @bud: el bud + * @pnum: physical eraseblock + * + * this function writes default bud hdr to the pnum. + * @return: On success return 0, error value other wise. + */ +int ubi_el_create_dflt(struct ubi_device *ubi, int bud, int pnum) +{ + int err = 0; + struct el_bud_hdr *bud_hdr; + int data_size, crc; + + dbg_el("creating default EL with %d pebs reserved ", pnum); + + bud_hdr = alloc_nodes(ubi, 1); + if (!bud_hdr) { + ubi_msg("Out of memory"); + return -ENOMEM; + } + + bud_hdr->MAGIC = cpu_to_be32(UBIL_EL_BUD_HDR_MAGIC); + bud_hdr->log_version = cpu_to_be32(UBIL_EL_VERSION); + + data_size = sizeof(struct el_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_EL_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); + if (err) + ubi_err("could not write EL Block header in Block %d", pnum); + + free_nodes(bud_hdr); + return err; +} + +/** + * el_init - + * @ubi_device: UBI device description object + * return: On success return 0, error value other wise. + */ + +int ubi_el_init(struct ubi_device *ubi) +{ + + ubi->el_buds = kmalloc(ubi->el_reservd_buds * sizeof(int), GFP_KERNEL); + if (!ubi->el_buds) { + ubi_msg("out of memory"); + return -ENOMEM; + } + + mutex_init(&ubi->el_mutex); + ubi->el_offset = 0; + ubi->el_active_bud = 0; + return 0; +} + +/** + * ubi_el_close - + * @ubi_device: UBI device description object + */ +void ubi_el_close(struct ubi_device *ubi) +{ + kfree(ubi->el_buds); +} + +/** + * ubi_el_read_ec_hdr - read erase count of physical eraseblock + * @ubi_device: UBI device description object + * @pnum: physical eraseblock + * + * This function return erase count for physical eraseblock + * @pnum. + */ + +int ubi_el_read_ec_hdr(struct ubi_device *ubi, int pnum) +{ + return ubi->peb_lookup[pnum].ec; +} + +/** + * ubi_el_write_ec_hdr - update erase count of physical eraseblock + * @ubi_device: UBI device description object + * @pnum: physical eraseblock + * @ec: erase count + * + * This function updates erase count of physical eraseblock @pnum + * int el log. i.e. el. + */ + +int ubi_el_write_ec_hdr(struct ubi_device *ubi, int pnum, int ec) +{ + int group = pnum / ubi->el_pebs_in_grp; + int err = 0; + + dbg_el("writing Ec Header for Pnum %d grp %d", pnum, group); + + mutex_lock(&ubi->el_mutex); + if (paronoid_check_special(ubi, pnum)) { + ubi_err("overwritng ec of sp blk"); + dump_stack(); + } + + ubi->peb_lookup[pnum].ec = ec; + ubi->peb_lookup[pnum].status = UBIL_PEB_FREE; + + err = el_write_node(ubi, group); + + mutex_unlock(&ubi->el_mutex); + return err; +} + +/** + * ubi_elo_write_ec_hdr_no_sync - in memory update erase count of + * physical eraseblock + * @ubi_device: UBI device description object + * @pnum: physical eraseblock + * @ec: erase count + * + * This function update erase count of physical eraseblock @pnum + * to new value @ec. Modification is done in memory, log entry is + * not written. + */ + +int ubi_el_write_ec_hdr_no_sync(struct ubi_device *ubi, int pnum, int ec) +{ + dbg_el("writing Ec Header no sync for pnum %d", pnum); + + ubi->peb_lookup[pnum].ec = ec; + ubi->peb_lookup[pnum].status = UBIL_PEB_FREE; + return 0; +} + +/** + * ubi_el_mark_pending - mark physical eraseblock as erase pending + * @ubi_device: UBI device description object + * @pnum: physical eraseblock + * + * This function mark physical eraseblock @pnum as erase pending. + */ + +int ubi_el_mark_pending(struct ubi_device *ubi, int pnum) +{ + dbg_el("marking Ec pending in ram for pnum %d", pnum); + ubi->peb_lookup[pnum].status = UBIL_PEB_ERASE_PENDING; + return 0; +} + +/** + * ubi_el_read_vid_hdr - read vid header + * @ubi_device: UBI device description object + * @pnum: physical eraseblock + * @vid_hdr: vid header + * @verbose: + * + * This function reads vid header for physical eraseblock @pnum to + * @vid_hdr. + * @return status of physical eraseblock @pnum. + */ + +int ubi_el_read_vid_hdr(struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr, int verbose) +{ + + memcpy(vid_hdr, &ubi->peb_lookup[pnum].v, UBI_VID_HDR_SIZE); + return ubi->peb_lookup[pnum].status; +} + +/** + * ubi_el_write_vid_hdr - write vid header + * @ubi_device: UBI device description object + * @pnum: physical eraseblock + * @vid_hdr: vid header + * @verbose: + * + * This function updates vid hdr in el. + * @returns 0 on success, error code other wise. + */ + +int ubi_el_write_vid_hdr(struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr) +{ + + int group = pnum / ubi->el_pebs_in_grp; + int err; + + dbg_el("writing vid hdr in ram for Pnum %d grp %d", pnum, group); + + mutex_lock(&ubi->el_mutex); + if (paronoid_check_special(ubi, pnum)) { + ubi_err("overwritng ec of sp blk"); + dump_stack(); + } + + memcpy(&ubi->peb_lookup[pnum].v, vid_hdr, UBI_VID_HDR_SIZE); + ubi->peb_lookup[pnum].status = UBIL_PEB_USED; + + err = el_write_node(ubi, group); + mutex_unlock(&ubi->el_mutex); + return err; +} + +#endif