All of lore.kernel.org
 help / color / mirror / Atom feed
* PATCH 4/7] ubi: logging feature for ubi
@ 2010-04-12  8:36 Brijesh Singh
  0 siblings, 0 replies; only message in thread
From: Brijesh Singh @ 2010-04-12  8:36 UTC (permalink / raw)
  To: Artem.Bityutskiy
  Cc: linux-mtd, rohitvdongre, David Woodhouse, rohit.dongre, brijesh.s.singh

Note: el(eba log) for logging feature

Signed-off-by: brijesh singh <brij.singh@samsung.com>
---
--- 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

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2010-04-12  8:36 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-04-12  8:36 PATCH 4/7] ubi: logging feature for ubi Brijesh Singh

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.