UBI: implement atomic LEB change
Subsystems (2)
PATCH 1/1 UBI: implement atomic LEB change
Quoted 1 line
From
1abfdcbb9426d9c47709bb70e3a4a8974535be2cMon Sep 17 00:00:00 2001
From: Artem Bityutskiy <[email protected]>
Date: Thu, 1 Feb 2007 19:25:31 +0200
Subject: [PATCH] [MTD] UBI: implement atomic LEB change
This patch implements the atomic LEB change feature which allows to change the contents of a logical eraseblock atomically.
This patch makes UBI reserve one more PEB which is needed to implement this feature.
Signed-off-by Artem Bityutskiy <[email protected]>
--- drivers/mtd/ubi/eba.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++- drivers/mtd/ubi/eba.h | 47 +++++++++-- drivers/mtd/ubi/uif.c | 62 +++++++++++++++ include/linux/mtd/ubi.h | 54 +++++++++++++- 4 files changed, 342 insertions(+), 13 deletions(-)
drivers/mtd/ubi/eba.c +190 -2
@@ -22,7 +22,7 @@#include <linux/sched.h> #include <linux/crc32.h> #include <linux/spinlock.h> -#include <linux/rwsem.h> +#include <linux/mutex.h> #include <linux/err.h> #include <linux/types.h> #include <mtd/ubi-header.h>
@@ -41,6 +41,12 @@#include "debug.h" /* +* The EBA unit reserves on PEB to implement the atomic eraseblock movement +* feature. +*/ +#define EBA_RESERVED_PEBS 1 + +/* * The highest bit in logical-to-physical eraseblock mappings is used to * indicate that the logical eraseblock is not mapped. */
@@ -164,6 +170,34 @@ static inline void leb_unmap(const struc }/** +* leb_remap - re-map a logical eraseblock to another physical eraseblock. +* +* @ubi: the UBI device description object +* @vol_id: the volume ID +* @lnum: the logical eraseblock number +* @new_pnum: new physical eraseblock +* +* This function re-maps a logical eraseblock from one physical eraseblock to +* another physical eraseblock. +* The logical eraseblock has to be locked. +*/ +static inline void leb_remap (const struct ubi_info *ubi,int vol_id,int lnum, +int new_pnum) +{ +int idx; +struct ubi_eba_info *eba = ubi->eba; + + idx =vol_id2idx (ubi, vol_id); + +spin_lock (&eba->eba_tbl_lock); +ubi_assert (eba->eba_tbl[idx].recs); +ubi_assert (eba->eba_tbl[idx].recs[lnum].pnum >=0 ); + eba->eba_tbl[idx].recs[lnum].pnum = new_pnum; + eba->eba_tbl[idx].recs[lnum].leb_ver +=1 ; +spin_unlock (&eba->eba_tbl_lock); +} + +/** * leb2peb - get physical eraseblock number the logical eraseblock is mapped * to. *
@@ -413,6 +447,7 @@ int ubi_eba_read_leb(const struct ubi_in *read = len;return 0 ; } +dbg_eba ("read %zd bytes from offset %d of LEB %d:%d, PEB %d" , len, offset, vol_id, lnum, pnum);
@@ -576,8 +611,10 @@ retry: pnum =ubi_wl_get_peb (ubi, dtype);if (unlikely (pnum <0 )) { err = pnum; +dbg_err ("cannot get free PEB - error %d" , err); goto out_vid_hdr; } +dbg_eba ("write VID hdr and %zd bytes at offset %d of LEB %d:%d, PEB %d" , len, offset, vol_id, lnum, pnum);
@@ -725,8 +762,10 @@ retry: pnum =ubi_wl_get_peb (ubi, dtype);if (unlikely (pnum <0 )) { err = pnum; +dbg_err ("cannot get free PEB - error %d" , err); goto out_vid_hdr; } +dbg_eba ("write VID hdr and %zd bytes at of LEB %d:%d, PEB %d" , len, vol_id, lnum, pnum);
@@ -757,7 +796,6 @@ out_unlock:/* Write failure */ write_error:ubi_free_vid_hdr (ubi, vid_hdr); -ubi_free_vid_hdr (ubi, vid_hdr);if (err != -EIO || !io->bad_allowed) goto no_bad_eraseblocks;
@@ -783,6 +821,151 @@ no_bad_eraseblocks:return err; } +int ubi_eba_atomic_leb_change (const struct ubi_info *ubi,int vol_id,int lnum, +const void *buf,size_t len, +enum ubi_data_type dtype) +{ +int err, err1, old_pnum, new_pnum, tries =0 ; +uint32_t leb_ver; +struct ubi_vid_hdr *vid_hdr; +const struct ubi_vtbl_vtr *vtr; +struct ubi_eba_info *eba = ubi->eba; +const struct ubi_io_info *io = ubi->io; + +retry: +/* Input arguments sanity check */ +ubi_assert (vol_id >=0 ); +ubi_assert (vol_id < ubi->acc->max_volumes ||ubi_is_ivol (vol_id)); +ubi_assert (lnum >=0 ); +ubi_assert (len >=0 ); +ubi_assert (dtype ==UBI_DATA_LONGTERM || dtype ==UBI_DATA_SHORTTERM || + dtype ==UBI_DATA_UNKNOWN ); + + vtr =ubi_vtbl_get_vtr (ubi, vol_id); +ubi_assert (!IS_ERR (vtr)); +ubi_assert (len <= io->leb_size - vtr->data_pad); +ubi_assert (lnum < ubi->eba->eba_tbl[vol_id2idx (ubi, vol_id)].leb_count); +ubi_assert (len % io->min_io_size ==0 ); +ubi_assert (vtr->vol_type ==UBI_DYNAMIC_VOLUME ); + +if (unlikely (ubi->io->ro_mode)) { +dbg_err ("read-only mode" ); +return -EROFS ; + } + +mutex_lock (&eba->change_mutex); + + new_pnum =ubi_wl_get_peb (ubi, dtype); +if (unlikely (new_pnum <0 )) { + err = new_pnum; +dbg_err ("cannot get free PEB - error %d" , err); + goto out_unlock_mutex; + } + + err =ubi_eba_leb_write_lock (ubi, vol_id, lnum); +if (unlikely (err)) + goto out_put_unlock_mutex; + + old_pnum =leb2peb (ubi, vol_id, lnum); + leb_ver =leb_get_ver (ubi, vol_id, lnum); + +dbg_eba ("change LEB %d:%d with %zd bytes of data, old PEB %d, " +"new PEB is %d" , vol_id, lnum, len, old_pnum, new_pnum); + +/* +* We are ready to write new data to the new physical eraseblock. Write +* the VID header first, then data. +*/ + vid_hdr =ubi_zalloc_vid_hdr (ubi); +if (unlikely (!vid_hdr)) { + err = -ENOMEM ; + goto out_unlock_leb_put; + } + + leb_ver +=1 ;/* we have to increase the version */ + vid_hdr->leb_ver =cpu_to_ubi32 (leb_ver); + vid_hdr->vol_type =UBI_VID_DYNAMIC ; + vid_hdr->vol_id =cpu_to_ubi32 (vol_id); + vid_hdr->lnum =cpu_to_ubi32 (lnum); + vid_hdr->compat =ubi_vtbl_get_compat (ubi, vol_id); + vid_hdr->data_pad =cpu_to_ubi32 (vtr->data_pad); + + err =ubi_io_write_vid_hdr (ubi, new_pnum, vid_hdr); +if (unlikely (err)) + goto write_error; + +if (len !=0 ) { +size_t written; + + err =ubi_io_write_data (ubi, buf, new_pnum,0 , len, &written); +if (unlikely (err)) + goto write_error; + } + +/* +* The data is there, we need to re-map our LEB from @old_pnum to +* @new_pnum. +*/ +leb_remap (ubi, vol_id, lnum, new_pnum); + +/* And drop the old physical eraseblock */ + err =ubi_wl_put_peb (ubi, old_pnum,0 ); +if (unlikely (err)) +/* +* No idea what is this. But the best we can do is to switch to +* R/O mode. +*/ +ubi_eba_ro_mode (ubi); + + +ubi_free_vid_hdr (ubi, vid_hdr); +ubi_eba_leb_write_unlock (ubi, vol_id, lnum); +mutex_unlock (&eba->change_mutex); +return err; + +out_unlock_leb_put: +ubi_eba_leb_write_unlock (ubi, vol_id, lnum); +out_put_unlock_mutex: + err1 =ubi_wl_put_peb (ubi, new_pnum,0 ); +if (err1) +/* +* Something really bad is going on, switch to R/O mode just in +* case. +*/ +ubi_eba_ro_mode (ubi); +out_unlock_mutex: +mutex_unlock (&eba->change_mutex); +return err; + +write_error: +ubi_free_vid_hdr (ubi, vid_hdr); +if (err != -EIO || !io->bad_allowed) + goto no_bad_eraseblocks; + +/* +* We assume that if this physical eraseblock went bad - the erase code +* will handle that. +*/ +ubi_msg ("try to recover form the error" ); + err =ubi_wl_put_peb (ubi, new_pnum,1 ); +ubi_eba_leb_write_unlock (ubi, vol_id, lnum); +mutex_unlock (&eba->change_mutex); +if (err || ++tries >5 ) +return err; + goto retry; + +/* +* This flash device does not admit of bad eraseblocks or something +* nasty and unexpected happened. Switch to read-only mode just in +* case. +*/ +no_bad_eraseblocks: +ubi_eba_ro_mode (ubi); +ubi_eba_leb_write_unlock (ubi, vol_id, lnum); +mutex_unlock (&eba->change_mutex); +return err; +} +int ubi_eba_leb_is_mapped (const struct ubi_info *ubi,int vol_id,int lnum) {dbg_eba ("check LEB %d:%d PEBs" , vol_id, lnum);
@@ -942,6 +1125,7 @@ int ubi_eba_init_scan(struct ubi_info *uspin_lock_init (&eba->eba_tbl_lock);spin_lock_init (&eba->ltree_lock); +mutex_init (&eba->change_mutex); eba->ltree =RB_ROOT ; eba->num_volumes = acc->max_volumes + acc->ivol_count;
@@ -952,6 +1136,10 @@ int ubi_eba_init_scan(struct ubi_info *u goto out; } + err =ubi_acc_reserve (ubi,EBA_RESERVED_PEBS ); +if (err) + goto out; + err =build_eba_tbl (ubi, si);if (err) goto out;
drivers/mtd/ubi/eba.h +38 -9
101 lines not on this page · raw message
drivers/mtd/ubi/uif.c +62 -0
68 lines not on this page · raw message
include/linux/mtd/ubi.h +52 -2
72 lines not on this page · raw message