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

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

Comments

Brijesh Singh - April 12, 2010, 8:36 a.m.
Note: el(eba log) for logging feature

Signed-off-by: brijesh singh <brij.singh@samsung.com>
---

Patch

--- 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 <linux/slab.h>
+#include <linux/crc32.h>
+#include <linux/err.h>
+#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