From 383d08e045faddd89797959786233d4c0e1ace80 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 7 May 2009 11:25:54 +0300 Subject: UBI: remove redundant mutex The @mult_mutex does not serve any purpose. We already have @volumes_mutex and it is enough. The @volume mutex is pushed down to the 'ubi_rename_volumes()', because we want first to open all volumes in the exclusive mode, and then lock the mutex, just like all other ioctl's (remove, re-size, etc) do. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 1 - drivers/mtd/ubi/cdev.c | 6 ++---- drivers/mtd/ubi/ubi.h | 2 -- 3 files changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 4048db83aef..e0e8f47f167 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -806,7 +806,6 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) mutex_init(&ubi->buf_mutex); mutex_init(&ubi->ckvol_mutex); - mutex_init(&ubi->mult_mutex); mutex_init(&ubi->volumes_mutex); spin_lock_init(&ubi->volumes_lock); diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index f8e0f68f218..8087b046277 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -810,9 +810,7 @@ static int rename_volumes(struct ubi_device *ubi, re->desc->vol->vol_id, re->desc->vol->name); } - mutex_lock(&ubi->volumes_mutex); err = ubi_rename_volumes(ubi, &rename_list); - mutex_unlock(&ubi->volumes_mutex); out_free: list_for_each_entry_safe(re, re1, &rename_list, list) { @@ -952,9 +950,9 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, break; } - mutex_lock(&ubi->mult_mutex); + mutex_lock(&ubi->volumes_mutex); err = rename_volumes(ubi, req); - mutex_unlock(&ubi->mult_mutex); + mutex_unlock(&ubi->volumes_mutex); kfree(req); break; } diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index c055511bb1b..485c73f850c 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -366,7 +366,6 @@ struct ubi_wl_entry; * @peb_buf2: another buffer of PEB size used for different purposes * @buf_mutex: protects @peb_buf1 and @peb_buf2 * @ckvol_mutex: serializes static volume checking when opening - * @mult_mutex: serializes operations on multiple volumes, like re-naming * @dbg_peb_buf: buffer of PEB size used for debugging * @dbg_buf_mutex: protects @dbg_peb_buf */ @@ -444,7 +443,6 @@ struct ubi_device { void *peb_buf2; struct mutex buf_mutex; struct mutex ckvol_mutex; - struct mutex mult_mutex; #ifdef CONFIG_MTD_UBI_DEBUG void *dbg_peb_buf; struct mutex dbg_buf_mutex; -- cgit v1.2.3 From f089c0b28cdba1076aa8335dcaaaacc3dafc7d36 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 7 May 2009 11:46:49 +0300 Subject: UBI: re-name volumes_mutex to device_mutex The mutex essencially protects the entire UBI device, so the old @volumes_mutex name is a little misleading. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 2 +- drivers/mtd/ubi/cdev.c | 22 +++++++++++----------- drivers/mtd/ubi/ubi.h | 8 ++++---- drivers/mtd/ubi/upd.c | 8 ++++---- drivers/mtd/ubi/vmt.c | 6 +++--- 5 files changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index e0e8f47f167..5d8fda1bda7 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -806,7 +806,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) mutex_init(&ubi->buf_mutex); mutex_init(&ubi->ckvol_mutex); - mutex_init(&ubi->volumes_mutex); + mutex_init(&ubi->device_mutex); spin_lock_init(&ubi->volumes_lock); ubi_msg("attaching mtd%d to ubi%d", mtd->index, ubi_num); diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 8087b046277..1024c106c89 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -558,7 +558,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, break; } - /* Set volume property command*/ + /* Set volume property command */ case UBI_IOCSETPROP: { struct ubi_set_prop_req req; @@ -571,9 +571,9 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, } switch (req.property) { case UBI_PROP_DIRECT_WRITE: - mutex_lock(&ubi->volumes_mutex); + mutex_lock(&ubi->device_mutex); desc->vol->direct_writes = !!req.value; - mutex_unlock(&ubi->volumes_mutex); + mutex_unlock(&ubi->device_mutex); break; default: err = -EINVAL; @@ -810,7 +810,9 @@ static int rename_volumes(struct ubi_device *ubi, re->desc->vol->vol_id, re->desc->vol->name); } + mutex_lock(&ubi->device_mutex); err = ubi_rename_volumes(ubi, &rename_list); + mutex_unlock(&ubi->device_mutex); out_free: list_for_each_entry_safe(re, re1, &rename_list, list) { @@ -854,9 +856,9 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, if (err) break; - mutex_lock(&ubi->volumes_mutex); + mutex_lock(&ubi->device_mutex); err = ubi_create_volume(ubi, &req); - mutex_unlock(&ubi->volumes_mutex); + mutex_unlock(&ubi->device_mutex); if (err) break; @@ -885,9 +887,9 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, break; } - mutex_lock(&ubi->volumes_mutex); + mutex_lock(&ubi->device_mutex); err = ubi_remove_volume(desc, 0); - mutex_unlock(&ubi->volumes_mutex); + mutex_unlock(&ubi->device_mutex); /* * The volume is deleted (unless an error occurred), and the @@ -924,9 +926,9 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, pebs = div_u64(req.bytes + desc->vol->usable_leb_size - 1, desc->vol->usable_leb_size); - mutex_lock(&ubi->volumes_mutex); + mutex_lock(&ubi->device_mutex); err = ubi_resize_volume(desc, pebs); - mutex_unlock(&ubi->volumes_mutex); + mutex_unlock(&ubi->device_mutex); ubi_close_volume(desc); break; } @@ -950,9 +952,7 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, break; } - mutex_lock(&ubi->volumes_mutex); err = rename_volumes(ubi, req); - mutex_unlock(&ubi->volumes_mutex); kfree(req); break; } diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 485c73f850c..76ec79b156a 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -305,9 +305,9 @@ struct ubi_wl_entry; * @vtbl_slots: how many slots are available in the volume table * @vtbl_size: size of the volume table in bytes * @vtbl: in-RAM volume table copy - * @volumes_mutex: protects on-flash volume table and serializes volume - * changes, like creation, deletion, update, re-size, - * re-name and set property + * @device_mutex: protects on-flash volume table and serializes volume + * creation, deletion, update, re-size, re-name and set + * property * * @max_ec: current highest erase counter value * @mean_ec: current mean erase counter value @@ -388,7 +388,7 @@ struct ubi_device { int vtbl_slots; int vtbl_size; struct ubi_vtbl_record *vtbl; - struct mutex volumes_mutex; + struct mutex device_mutex; int max_ec; /* Note, mean_ec is not updated run-time - should be fixed */ diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index 6b4d1ae891a..dce1d92d8e9 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -68,9 +68,9 @@ static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol) sizeof(struct ubi_vtbl_record)); vtbl_rec.upd_marker = 1; - mutex_lock(&ubi->volumes_mutex); + mutex_lock(&ubi->device_mutex); err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); - mutex_unlock(&ubi->volumes_mutex); + mutex_unlock(&ubi->device_mutex); vol->upd_marker = 1; return err; } @@ -109,9 +109,9 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, vol->last_eb_bytes = vol->usable_leb_size; } - mutex_lock(&ubi->volumes_mutex); + mutex_lock(&ubi->device_mutex); err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); - mutex_unlock(&ubi->volumes_mutex); + mutex_unlock(&ubi->device_mutex); vol->upd_marker = 0; return err; } diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index df5483562b7..328c1242920 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -198,7 +198,7 @@ static void volume_sysfs_close(struct ubi_volume *vol) * %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume * and saves it in @req->vol_id. Returns zero in case of success and a negative * error code in case of failure. Note, the caller has to have the - * @ubi->volumes_mutex locked. + * @ubi->device_mutex locked. */ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) { @@ -403,7 +403,7 @@ out_unlock: * * This function removes volume described by @desc. The volume has to be opened * in "exclusive" mode. Returns zero in case of success and a negative error - * code in case of failure. The caller has to have the @ubi->volumes_mutex + * code in case of failure. The caller has to have the @ubi->device_mutex * locked. */ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) @@ -485,7 +485,7 @@ out_unlock: * * This function re-sizes the volume and returns zero in case of success, and a * negative error code in case of failure. The caller has to have the - * @ubi->volumes_mutex locked. + * @ubi->device_mutex locked. */ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) { -- cgit v1.2.3 From e1cf7e6dd4ffd4391391e4e08b0fd44681b0e74d Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 7 May 2009 18:24:14 +0300 Subject: UBI: improve debugging messages Various minor improvements to the debugging messages which I found useful while hunting problems. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 6 ++++-- drivers/mtd/ubi/kapi.c | 9 ++++++--- drivers/mtd/ubi/vmt.c | 10 +++++----- 3 files changed, 15 insertions(+), 10 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 1024c106c89..9a2b217941f 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -113,7 +113,8 @@ static int vol_cdev_open(struct inode *inode, struct file *file) else mode = UBI_READONLY; - dbg_gen("open volume %d, mode %d", vol_id, mode); + dbg_gen("open device %d, volume %d, mode %d", + ubi_num, vol_id, mode); desc = ubi_open_volume(ubi_num, vol_id, mode); if (IS_ERR(desc)) @@ -128,7 +129,8 @@ static int vol_cdev_release(struct inode *inode, struct file *file) struct ubi_volume_desc *desc = file->private_data; struct ubi_volume *vol = desc->vol; - dbg_gen("release volume %d, mode %d", vol->vol_id, desc->mode); + dbg_gen("release device %d, volume %d, mode %d", + vol->ubi->ubi_num, vol->vol_id, desc->mode); if (vol->updating) { ubi_warn("update of volume %d not finished, volume is damaged", diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 4abbe573fa4..2675207c5fe 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -106,7 +106,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) struct ubi_device *ubi; struct ubi_volume *vol; - dbg_gen("open device %d volume %d, mode %d", ubi_num, vol_id, mode); + dbg_gen("open device %d, volume %d, mode %d", ubi_num, vol_id, mode); if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) return ERR_PTR(-EINVAL); @@ -196,6 +196,8 @@ out_free: kfree(desc); out_put_ubi: ubi_put_device(ubi); + dbg_err("cannot open device %d, volume %d, error %d", + ubi_num, vol_id, err); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(ubi_open_volume); @@ -215,7 +217,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, struct ubi_device *ubi; struct ubi_volume_desc *ret; - dbg_gen("open volume %s, mode %d", name, mode); + dbg_gen("open device %d, volume %s, mode %d", ubi_num, name, mode); if (!name) return ERR_PTR(-EINVAL); @@ -266,7 +268,8 @@ void ubi_close_volume(struct ubi_volume_desc *desc) struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - dbg_gen("close volume %d, mode %d", vol->vol_id, desc->mode); + dbg_gen("close device %d, volume %d, mode %d", + ubi->ubi_num, vol->vol_id, desc->mode); spin_lock(&ubi->volumes_lock); switch (desc->mode) { diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 328c1242920..419599d62b2 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -232,8 +232,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) req->vol_id = vol_id; } - dbg_gen("volume ID %d, %llu bytes, type %d, name %s", - vol_id, (unsigned long long)req->bytes, + dbg_gen("create device %d, volume %d, %llu bytes, type %d, name %s", + ubi->ubi_num, vol_id, (unsigned long long)req->bytes, (int)req->vol_type, req->name); /* Ensure that this volume does not exist */ @@ -412,7 +412,7 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) struct ubi_device *ubi = vol->ubi; int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs; - dbg_gen("remove UBI volume %d", vol_id); + dbg_gen("remove device %d, volume %d", ubi->ubi_num, vol_id); ubi_assert(desc->mode == UBI_EXCLUSIVE); ubi_assert(vol == ubi->volumes[vol_id]); @@ -498,8 +498,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) if (ubi->ro_mode) return -EROFS; - dbg_gen("re-size volume %d to from %d to %d PEBs", - vol_id, vol->reserved_pebs, reserved_pebs); + dbg_gen("re-size device %d, volume %d to from %d to %d PEBs", + ubi->ubi_num, vol_id, vol->reserved_pebs, reserved_pebs); if (vol->vol_type == UBI_STATIC_VOLUME && reserved_pebs < vol->used_ebs) { -- cgit v1.2.3 From 2cb81e218f336dc5438a960d1ae098188db9ff11 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 12 May 2009 15:10:03 +0300 Subject: UBI: small debugging code optimization The @ubi->dbg_peb_buf is needed only when paranoid checks are enabled, not when debugging in general is enabled. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 6 +++--- drivers/mtd/ubi/ubi.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 5d8fda1bda7..2c3269ea133 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -824,7 +824,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) if (!ubi->peb_buf2) goto out_free; -#ifdef CONFIG_MTD_UBI_DEBUG +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID mutex_init(&ubi->dbg_buf_mutex); ubi->dbg_peb_buf = vmalloc(ubi->peb_size); if (!ubi->dbg_peb_buf) @@ -891,7 +891,7 @@ out_detach: out_free: vfree(ubi->peb_buf1); vfree(ubi->peb_buf2); -#ifdef CONFIG_MTD_UBI_DEBUG +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID vfree(ubi->dbg_peb_buf); #endif kfree(ubi); @@ -960,7 +960,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) put_mtd_device(ubi->mtd); vfree(ubi->peb_buf1); vfree(ubi->peb_buf2); -#ifdef CONFIG_MTD_UBI_DEBUG +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID vfree(ubi->dbg_peb_buf); #endif ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 76ec79b156a..749007e9f1a 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -443,7 +443,7 @@ struct ubi_device { void *peb_buf2; struct mutex buf_mutex; struct mutex ckvol_mutex; -#ifdef CONFIG_MTD_UBI_DEBUG +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID void *dbg_peb_buf; struct mutex dbg_buf_mutex; #endif -- cgit v1.2.3 From ffb6b7e4fdef715061859651fe46cd27afc6acec Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 12 May 2009 15:43:44 +0300 Subject: UBI: fix races in I/O debugging checks When paranoid checs are enabled, the 'io_paral' test from the 'mtd-utils' package fails. The symptoms are: UBI error: paranoid_check_all_ff: flash region at PEB 3973:512, length 15872 does not contain all 0xFF bytes UBI error: paranoid_check_all_ff: paranoid check failed for PEB 3973 UBI: hex dump of the 512-16384 region It turned out to be a bug in the checking function. Suppose there are 2 tasks - A and B. Task A is the wear-levelling working ('wear_leveling_worker()'). It is reading the VID header to find which LEB this PEB belongs to. Say, task A is reading header of PEB X. Suppose PEB X is unmapped, and has no VID header. Task B is trying to write to PEB X. Task A: in 'ubi_io_read_vid_hdr()': reads the VID header from PEB X. The read data contain all 0xFF bytes. Task B: writes VID header and some data to PEB X Task A: assumes PEB X is empty, calls 'paranoid_check_all_ff()', which fails. The solution for this problem is to make 'paranoid_check_all_ff()' re-read the VID header, re-check it, and only if it is not there, check the rest. This now implemented by the 'paranoid_check_empty()' function. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/io.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 7 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index fe81039f2a7..ac6604aeb72 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -100,6 +100,7 @@ static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, const struct ubi_vid_hdr *vid_hdr); static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len); +static int paranoid_check_empty(struct ubi_device *ubi, int pnum); #else #define paranoid_check_not_bad(ubi, pnum) 0 #define paranoid_check_peb_ec_hdr(ubi, pnum) 0 @@ -107,6 +108,7 @@ static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, #define paranoid_check_peb_vid_hdr(ubi, pnum) 0 #define paranoid_check_vid_hdr(ubi, pnum, vid_hdr) 0 #define paranoid_check_all_ff(ubi, pnum, offset, len) 0 +#define paranoid_check_empty(ubi, pnum) 0 #endif /** @@ -670,11 +672,6 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, if (read_err != -EBADMSG && check_pattern(ec_hdr, 0xFF, UBI_EC_HDR_SIZE)) { /* The physical eraseblock is supposedly empty */ - - /* - * The below is just a paranoid check, it has to be - * compiled out if paranoid checks are disabled. - */ err = paranoid_check_all_ff(ubi, pnum, 0, ubi->peb_size); if (err) @@ -955,8 +952,7 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, * The below is just a paranoid check, it has to be * compiled out if paranoid checks are disabled. */ - err = paranoid_check_all_ff(ubi, pnum, ubi->leb_start, - ubi->leb_size); + err = paranoid_check_empty(ubi, pnum); if (err) return err > 0 ? UBI_IO_BAD_VID_HDR : err; @@ -1280,4 +1276,74 @@ error: return err; } +/** + * paranoid_check_empty - whether a PEB is empty. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function makes sure PEB @pnum is empty, which means it contains only + * %0xFF data bytes. Returns zero if the PEB is empty, %1 if not, and a + * negative error code in case of failure. + * + * Empty PEBs have the EC header, and do not have the VID header. The caller of + * this function should have already made sure the PEB does not have the VID + * header. However, this function re-checks that, because it is possible that + * the header and data has already been written to the PEB. + * + * Let's consider a possible scenario. Suppose there are 2 tasks - A and B. + * Task A is in 'wear_leveling_worker()'. It is reading VID header of PEB X to + * find which LEB it corresponds to. PEB X is currently unmapped, and has no + * VID header. Task B is trying to write to PEB X. + * + * Task A: in 'ubi_io_read_vid_hdr()': reads the VID header from PEB X. The + * read data contain all 0xFF bytes; + * Task B: writes VID header and some data to PEB X; + * Task A: assumes PEB X is empty, calls 'paranoid_check_empty()'. And if we + * do not re-read the VID header, and do not cancel the checking if it + * is there, we fail. + */ +static int paranoid_check_empty(struct ubi_device *ubi, int pnum) +{ + int err, offs = ubi->vid_hdr_aloffset, len = ubi->vid_hdr_alsize; + size_t read; + uint32_t magic; + const struct ubi_vid_hdr *vid_hdr; + + mutex_lock(&ubi->dbg_buf_mutex); + err = ubi->mtd->read(ubi->mtd, offs, len, &read, ubi->dbg_peb_buf); + if (err && err != -EUCLEAN) { + ubi_err("error %d while reading %d bytes from PEB %d:%d, " + "read %zd bytes", err, len, pnum, offs, read); + goto error; + } + + vid_hdr = ubi->dbg_peb_buf; + magic = be32_to_cpu(vid_hdr->magic); + if (magic == UBI_VID_HDR_MAGIC) + /* The PEB contains VID header, so it is not empty */ + goto out; + + err = check_pattern(ubi->dbg_peb_buf, 0xFF, len); + if (err == 0) { + ubi_err("flash region at PEB %d:%d, length %d does not " + "contain all 0xFF bytes", pnum, offs, len); + goto fail; + } + +out: + mutex_unlock(&ubi->dbg_buf_mutex); + return 0; + +fail: + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_msg("hex dump of the %d-%d region", offs, offs + len); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, + ubi->dbg_peb_buf, len, 1); + err = 1; +error: + ubi_dbg_dump_stack(); + mutex_unlock(&ubi->dbg_buf_mutex); + return err; +} + #endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ -- cgit v1.2.3 From cfcf0ec84bee53799e1e393a48af5bdcf719a383 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 12 May 2009 20:29:15 +0300 Subject: UBI: add dump_stack in checking code I am experiencing an error in 'paranoid_check_volume()'. Add dump_stack() there to make it easier to identify the reasons of the error. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/vmt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 419599d62b2..32c6ceb1a06 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -868,6 +868,7 @@ fail: if (vol) ubi_dbg_dump_vol_info(vol); ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id); + dump_stack(); spin_unlock(&ubi->volumes_lock); return -EINVAL; } -- cgit v1.2.3 From d38dce5bfb400226bbffc81289c42a6271826a7e Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 13 May 2009 14:05:24 +0300 Subject: UBI: do not panic if volume check fails If a volume paranoid check fails, do not return an error code to the caller, but just print error messages and go forward. The primary reason for this is that it is difficult to recover and cancel the operation at that stage. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/vmt.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 32c6ceb1a06..8e8d6fae7a0 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -358,7 +358,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ubi->vol_count += 1; spin_unlock(&ubi->volumes_lock); - err = paranoid_check_volumes(ubi); + if (paranoid_check_volumes(ubi)) + dbg_err("check failed while creating volume %d", vol_id); return err; out_sysfs: @@ -465,8 +466,9 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) ubi->vol_count -= 1; spin_unlock(&ubi->volumes_lock); - if (!no_vtbl) - err = paranoid_check_volumes(ubi); + if (!no_vtbl && paranoid_check_volumes(ubi)) + dbg_err("check failed while removing volume %d", vol_id); + return err; out_err: @@ -587,7 +589,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) (long long)vol->used_ebs * vol->usable_leb_size; } - err = paranoid_check_volumes(ubi); + if (paranoid_check_volumes(ubi)) + dbg_err("check failed while re-sizing volume %d", vol_id); return err; out_acc: @@ -635,8 +638,8 @@ int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list) } } - if (!err) - err = paranoid_check_volumes(ubi); + if (!err && paranoid_check_volumes(ubi)) + ; return err; } @@ -688,7 +691,8 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) return err; } - err = paranoid_check_volumes(ubi); + if (paranoid_check_volumes(ubi)) + dbg_err("check failed while adding volume %d", vol_id); return err; out_gluebi: -- cgit v1.2.3 From 95c9c1da79e59fd10ec5da3aeba22981383f7040 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 13 May 2009 17:05:11 +0300 Subject: UBI: minor serialization fix The @vol->upd_marker should be protected by the @ubi->device_mutex, otherwise 'paranoid_check_volume()' complains sometimes because vol->upd_marker is 1 while vtbl_rec->upd_marker is 0. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/upd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index dce1d92d8e9..74fdc40c862 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -70,8 +70,8 @@ static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol) mutex_lock(&ubi->device_mutex); err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); - mutex_unlock(&ubi->device_mutex); vol->upd_marker = 1; + mutex_unlock(&ubi->device_mutex); return err; } @@ -111,8 +111,8 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, mutex_lock(&ubi->device_mutex); err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); - mutex_unlock(&ubi->device_mutex); vol->upd_marker = 0; + mutex_unlock(&ubi->device_mutex); return err; } -- cgit v1.2.3 From ddbd3b61708483f73dbcc62a94d16cc7db928cba Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sat, 23 May 2009 13:44:09 +0300 Subject: UBI: fix race condition This patch fixes a minor problem where we may fail to wake upe the UBI background thread. This is not fatal at all, it may just result at sligtly worse performace for a short period of time, just because the thread will be woken up when real I/O on the UBI starts. Anywey, the issue is the race condition between 'ubi_attach_mtd_dev()' and 'ubi_thread()'. If we do not serialize them, the 'wake_up_process()' call may be done before 'ubi_thread()' went seep, but after it checked 'ubi->thread_enabled'. This issue was spotted by Shin Hong Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 2c3269ea133..1405b556c65 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -380,7 +380,7 @@ static void free_user_volumes(struct ubi_device *ubi) * @ubi: UBI device description object * * This function returns zero in case of success and a negative error code in - * case of failure. Note, this function destroys all volumes if it failes. + * case of failure. Note, this function destroys all volumes if it fails. */ static int uif_init(struct ubi_device *ubi) { @@ -871,9 +871,15 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ubi->beb_rsvd_pebs); ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec); + /* + * The below lock makes sure we do not race with 'ubi_thread()' which + * checks @ubi->thread_enabled. Otherwise we may fail to wake it up. + */ + spin_lock(&ubi->wl_lock); if (!DBG_DISABLE_BGT) ubi->thread_enabled = 1; wake_up_process(ubi->bgt_thread); + spin_unlock(&ubi->wl_lock); ubi_devices[ubi_num] = ubi; return ubi_num; -- cgit v1.2.3 From 90bf0265e5b0d561f215a69bb7a46c4071b2c93b Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sat, 23 May 2009 16:04:17 +0300 Subject: UBI: introduce new constants This patch is a clean-up and a preparation for the following patches. It introduece constants for the return values of the 'ubi_eba_copy_leb()' function. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/eba.c | 33 ++++++++++++++------------------- drivers/mtd/ubi/ubi.h | 16 ++++++++++++++++ drivers/mtd/ubi/wl.c | 13 ++++++------- 3 files changed, 36 insertions(+), 26 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 25def348e5b..7ab79e24724 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -950,12 +950,7 @@ write_error: * physical eraseblock @to. The @vid_hdr buffer may be changed by this * function. Returns: * o %0 in case of success; - * o %1 if the operation was canceled because the volume is being deleted - * or because the PEB was put meanwhile; - * o %2 if the operation was canceled because there was a write error to the - * target PEB; - * o %-EAGAIN if the operation was canceled because a bit-flip was detected - * in the target PEB; + * o %MOVE_CANCEL_RACE, %MOVE_TARGET_WR_ERR, or %MOVE_CANCEL_BITFLIPS; * o a negative error code in case of failure. */ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, @@ -986,13 +981,12 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, * be locked in 'ubi_wl_put_peb()' and wait for the WL worker to finish. */ vol = ubi->volumes[idx]; + spin_unlock(&ubi->volumes_lock); if (!vol) { /* No need to do further work, cancel */ dbg_eba("volume %d is being removed, cancel", vol_id); - spin_unlock(&ubi->volumes_lock); - return 1; + return MOVE_CANCEL_RACE; } - spin_unlock(&ubi->volumes_lock); /* * We do not want anybody to write to this logical eraseblock while we @@ -1004,12 +998,13 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, * (@from). This task locks the LEB and goes sleep in the * 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are * holding @ubi->move_mutex and go sleep on the LEB lock. So, if the - * LEB is already locked, we just do not move it and return %1. + * LEB is already locked, we just do not move it and return + * %MOVE_CANCEL_RACE, which means that UBI will re-try, but later. */ err = leb_write_trylock(ubi, vol_id, lnum); if (err) { dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum); - return err; + return MOVE_CANCEL_RACE; } /* @@ -1021,14 +1016,14 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to " "PEB %d, cancel", vol_id, lnum, from, vol->eba_tbl[lnum]); - err = 1; + err = MOVE_CANCEL_RACE; goto out_unlock_leb; } /* * OK, now the LEB is locked and we can safely start moving it. Since - * this function utilizes the @ubi->peb1_buf buffer which is shared - * with some other functions, so lock the buffer by taking the + * this function utilizes the @ubi->peb_buf1 buffer which is shared + * with some other functions - we lock the buffer by taking the * @ubi->buf_mutex. */ mutex_lock(&ubi->buf_mutex); @@ -1059,7 +1054,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, cond_resched(); /* - * It may turn out to me that the whole @from physical eraseblock + * It may turn out to be that the whole @from physical eraseblock * contains only 0xFF bytes. Then we have to only write the VID header * and do not write any data. This also means we should not set * @vid_hdr->copy_flag, @vid_hdr->data_size, and @vid_hdr->data_crc. @@ -1074,7 +1069,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, err = ubi_io_write_vid_hdr(ubi, to, vid_hdr); if (err) { if (err == -EIO) - err = 2; + err = MOVE_TARGET_WR_ERR; goto out_unlock_buf; } @@ -1086,7 +1081,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, if (err != UBI_IO_BITFLIPS) ubi_warn("cannot read VID header back from PEB %d", to); else - err = -EAGAIN; + err = MOVE_CANCEL_BITFLIPS; goto out_unlock_buf; } @@ -1094,7 +1089,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size); if (err) { if (err == -EIO) - err = 2; + err = MOVE_TARGET_WR_ERR; goto out_unlock_buf; } @@ -1111,7 +1106,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, ubi_warn("cannot read data back from PEB %d", to); else - err = -EAGAIN; + err = MOVE_CANCEL_BITFLIPS; goto out_unlock_buf; } diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 749007e9f1a..fd9b20da5b6 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -100,6 +100,22 @@ enum { UBI_IO_BITFLIPS }; +/* + * Return codes of the 'ubi_eba_copy_leb()' function. + * + * MOVE_CANCEL_RACE: canceled because the volume is being deleted, the source + * PEB was put meanwhile, or there is I/O on the source PEB + * MOVE_TARGET_WR_ERR: canceled because there was a write error to the target + * PEB + * MOVE_CANCEL_BITFLIPS: canceled because a bit-flip was detected in the + * target PEB + */ +enum { + MOVE_CANCEL_RACE = 1, + MOVE_TARGET_WR_ERR, + MOVE_CANCEL_BITFLIPS, +}; + /** * struct ubi_wl_entry - wear-leveling entry. * @u.rb: link in the corresponding (free/used) RB-tree diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 891534f8210..ec915c02301 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -756,15 +756,14 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); if (err) { - if (err == -EAGAIN) - goto out_not_moved; - if (err < 0) - goto out_error; - if (err == 2) { - /* Target PEB write error, torture it */ + if (err == MOVE_CANCEL_BITFLIPS || + err == MOVE_TARGET_WR_ERR) { + /* Target PEB bit-flips or write error, torture it */ torture = 1; goto out_not_moved; } + if (err < 0) + goto out_error; /* * The LEB has not been moved because the volume is being @@ -774,7 +773,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, */ dbg_wl("canceled moving PEB %d", e1->pnum); - ubi_assert(err == 1); + ubi_assert(err == MOVE_CANCEL_RACE); ubi_free_vid_hdr(ubi, vid_hdr); vid_hdr = NULL; -- cgit v1.2.3 From 87960c0b12d0c5a0b37e0c79aef77aa1a0b10d44 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 24 May 2009 11:58:58 +0300 Subject: UBI: fix and clean-up error paths in WL worker This patch fixes the error path in the WL worker - in same cases UBI oopses when 'goto out_error' happens and e1 or e2 are NULL. This patch also cleans up the error paths a little. And I have tested nearly all error paths in the WL worker. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/eba.c | 14 +++---- drivers/mtd/ubi/wl.c | 100 +++++++++++++++++++++++--------------------------- 2 files changed, 53 insertions(+), 61 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 7ab79e24724..587b6cb5040 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -963,7 +963,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, vol_id = be32_to_cpu(vid_hdr->vol_id); lnum = be32_to_cpu(vid_hdr->lnum); - dbg_eba("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to); + dbg_wl("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to); if (vid_hdr->vol_type == UBI_VID_STATIC) { data_size = be32_to_cpu(vid_hdr->data_size); @@ -984,7 +984,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, spin_unlock(&ubi->volumes_lock); if (!vol) { /* No need to do further work, cancel */ - dbg_eba("volume %d is being removed, cancel", vol_id); + dbg_wl("volume %d is being removed, cancel", vol_id); return MOVE_CANCEL_RACE; } @@ -1003,7 +1003,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, */ err = leb_write_trylock(ubi, vol_id, lnum); if (err) { - dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum); + dbg_wl("contention on LEB %d:%d, cancel", vol_id, lnum); return MOVE_CANCEL_RACE; } @@ -1013,9 +1013,9 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, * cancel it. */ if (vol->eba_tbl[lnum] != from) { - dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to " - "PEB %d, cancel", vol_id, lnum, from, - vol->eba_tbl[lnum]); + dbg_wl("LEB %d:%d is no longer mapped to PEB %d, mapped to " + "PEB %d, cancel", vol_id, lnum, from, + vol->eba_tbl[lnum]); err = MOVE_CANCEL_RACE; goto out_unlock_leb; } @@ -1027,7 +1027,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, * @ubi->buf_mutex. */ mutex_lock(&ubi->buf_mutex); - dbg_eba("read %d bytes of data", aldata_size); + dbg_wl("read %d bytes of data", aldata_size); err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size); if (err && err != UBI_IO_BITFLIPS) { ubi_warn("error %d while reading data from PEB %d", diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index ec915c02301..793882ba2a6 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -653,7 +653,7 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, int cancel) { - int err, scrubbing = 0, torture = 0; + int err, scrubbing = 0, torture = 0, protect = 0; struct ubi_wl_entry *e1, *e2; struct ubi_vid_hdr *vid_hdr; @@ -738,64 +738,52 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, /* * We are trying to move PEB without a VID header. UBI * always write VID headers shortly after the PEB was - * given, so we have a situation when it did not have - * chance to write it down because it was preempted. - * Just re-schedule the work, so that next time it will - * likely have the VID header in place. + * given, so we have a situation when it has not yet + * had a chance to write it, because it was preempted. + * So add this PEB to the protection queue so far, + * because presubably more data will be written there + * (including the missin VID header), and then we'll + * move it. */ dbg_wl("PEB %d has no VID header", e1->pnum); + protect = 1; goto out_not_moved; } ubi_err("error %d while reading VID header from PEB %d", err, e1->pnum); - if (err > 0) - err = -EIO; goto out_error; } err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); if (err) { + if (err == MOVE_CANCEL_RACE) { + /* + * The LEB has not been moved because the volume is + * being deleted or the PEB has been put meanwhile. We + * should prevent this PEB from being selected for + * wear-leveling movement again, so put it to the + * protection queue. + */ + protect = 1; + goto out_not_moved; + } + if (err == MOVE_CANCEL_BITFLIPS || err == MOVE_TARGET_WR_ERR) { /* Target PEB bit-flips or write error, torture it */ torture = 1; goto out_not_moved; } + if (err < 0) goto out_error; - /* - * The LEB has not been moved because the volume is being - * deleted or the PEB has been put meanwhile. We should prevent - * this PEB from being selected for wear-leveling movement - * again, so put it to the protection queue. - */ - - dbg_wl("canceled moving PEB %d", e1->pnum); - ubi_assert(err == MOVE_CANCEL_RACE); - - ubi_free_vid_hdr(ubi, vid_hdr); - vid_hdr = NULL; - - spin_lock(&ubi->wl_lock); - prot_queue_add(ubi, e1); - ubi_assert(!ubi->move_to_put); - ubi->move_from = ubi->move_to = NULL; - ubi->wl_scheduled = 0; - spin_unlock(&ubi->wl_lock); - - e1 = NULL; - err = schedule_erase(ubi, e2, 0); - if (err) - goto out_error; - mutex_unlock(&ubi->move_mutex); - return 0; + ubi_assert(0); } /* The PEB has been successfully moved */ ubi_free_vid_hdr(ubi, vid_hdr); - vid_hdr = NULL; if (scrubbing) ubi_msg("scrubbed PEB %d, data moved to PEB %d", e1->pnum, e2->pnum); @@ -811,8 +799,9 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, err = schedule_erase(ubi, e1, 0); if (err) { - e1 = NULL; - goto out_error; + kmem_cache_free(ubi_wl_entry_slab, e1); + kmem_cache_free(ubi_wl_entry_slab, e2); + goto out_ro; } if (e2) { @@ -822,8 +811,10 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, */ dbg_wl("PEB %d was put meanwhile, erase", e2->pnum); err = schedule_erase(ubi, e2, 0); - if (err) - goto out_error; + if (err) { + kmem_cache_free(ubi_wl_entry_slab, e2); + goto out_ro; + } } dbg_wl("done"); @@ -836,11 +827,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, * have been changed, schedule it for erasure. */ out_not_moved: - dbg_wl("canceled moving PEB %d", e1->pnum); - ubi_free_vid_hdr(ubi, vid_hdr); - vid_hdr = NULL; + dbg_wl("cancel moving PEB %d to PEB %d (%d)", + e1->pnum, e2->pnum, err); spin_lock(&ubi->wl_lock); - if (scrubbing) + if (protect) + prot_queue_add(ubi, e1); + else if (scrubbing) wl_tree_add(e1, &ubi->scrub); else wl_tree_add(e1, &ubi->used); @@ -849,32 +841,32 @@ out_not_moved: ubi->wl_scheduled = 0; spin_unlock(&ubi->wl_lock); - e1 = NULL; + ubi_free_vid_hdr(ubi, vid_hdr); err = schedule_erase(ubi, e2, torture); - if (err) - goto out_error; - + if (err) { + kmem_cache_free(ubi_wl_entry_slab, e2); + goto out_ro; + } mutex_unlock(&ubi->move_mutex); return 0; out_error: ubi_err("error %d while moving PEB %d to PEB %d", err, e1->pnum, e2->pnum); - - ubi_free_vid_hdr(ubi, vid_hdr); spin_lock(&ubi->wl_lock); ubi->move_from = ubi->move_to = NULL; ubi->move_to_put = ubi->wl_scheduled = 0; spin_unlock(&ubi->wl_lock); - if (e1) - kmem_cache_free(ubi_wl_entry_slab, e1); - if (e2) - kmem_cache_free(ubi_wl_entry_slab, e2); - ubi_ro_mode(ubi); + ubi_free_vid_hdr(ubi, vid_hdr); + kmem_cache_free(ubi_wl_entry_slab, e1); + kmem_cache_free(ubi_wl_entry_slab, e2); +out_ro: + ubi_ro_mode(ubi); mutex_unlock(&ubi->move_mutex); - return err; + ubi_assert(err != 0); + return err < 0 ? err : -EIO; out_cancel: ubi->wl_scheduled = 0; -- cgit v1.2.3 From b86a2c56e512f46d140a4bcb4e35e8a7d4a99a4b Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 24 May 2009 14:13:34 +0300 Subject: UBI: do not switch to R/O mode on read errors This patch improves UBI errors handling. ATM UBI switches to R/O mode when the WL worker fails to read the source PEB. This means that the upper layers (e.g., UBIFS) has no chances to unmap the erroneous PEB and fix the error. This patch changes this behaviour and makes UBI put PEBs like this into a separate RB-tree, thus preventing the WL worker from hitting the same read errors again and again. But there is a 10% limit on a maximum amount of PEBs like this. If there are too much of them, UBI switches to R/O mode. Additionally, this patch teaches UBI not to panic and switch to R/O mode if after a PEB has been copied, the target LEB cannot be read back. Instead, now UBI cancels the operation and schedules the target PEB for torturing. The error paths has been tested by ingecting errors into 'ubi_eba_copy_leb()'. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 9 +++++++++ drivers/mtd/ubi/eba.c | 19 +++++++++++++------ drivers/mtd/ubi/ubi.h | 16 ++++++++++++++-- drivers/mtd/ubi/wl.c | 45 ++++++++++++++++++++++++++++++++++++++------- 4 files changed, 74 insertions(+), 15 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 1405b556c65..d3da6668266 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -632,6 +632,15 @@ static int io_init(struct ubi_device *ubi) return -EINVAL; } + /* + * Set maximum amount of physical erroneous eraseblocks to be 10%. + * Erroneous PEB are those which have read errors. + */ + ubi->max_erroneous = ubi->peb_count / 10; + if (ubi->max_erroneous < 16) + ubi->max_erroneous = 16; + dbg_msg("max_erroneous %d", ubi->max_erroneous); + /* * It may happen that EC and VID headers are situated in one minimal * I/O unit. In this case we can only accept this UBI image in diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 587b6cb5040..632b95f3ff3 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -419,8 +419,9 @@ retry: * not implemented. */ if (err == UBI_IO_BAD_VID_HDR) { - ubi_warn("bad VID header at PEB %d, LEB" - "%d:%d", pnum, vol_id, lnum); + ubi_warn("corrupted VID header at PEB " + "%d, LEB %d:%d", pnum, vol_id, + lnum); err = -EBADMSG; } else ubi_ro_mode(ubi); @@ -1032,6 +1033,8 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, if (err && err != UBI_IO_BITFLIPS) { ubi_warn("error %d while reading data from PEB %d", err, from); + if (err == -EIO) + err = MOVE_SOURCE_RD_ERR; goto out_unlock_buf; } @@ -1078,9 +1081,11 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, /* Read the VID header back and check if it was written correctly */ err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1); if (err) { - if (err != UBI_IO_BITFLIPS) + if (err != UBI_IO_BITFLIPS) { ubi_warn("cannot read VID header back from PEB %d", to); - else + if (err == -EIO) + err = MOVE_TARGET_RD_ERR; + } else err = MOVE_CANCEL_BITFLIPS; goto out_unlock_buf; } @@ -1102,10 +1107,12 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, err = ubi_io_read_data(ubi, ubi->peb_buf2, to, 0, aldata_size); if (err) { - if (err != UBI_IO_BITFLIPS) + if (err != UBI_IO_BITFLIPS) { ubi_warn("cannot read data back from PEB %d", to); - else + if (err == -EIO) + err = MOVE_TARGET_RD_ERR; + } else err = MOVE_CANCEL_BITFLIPS; goto out_unlock_buf; } diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index fd9b20da5b6..6d929329a8d 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -105,6 +105,10 @@ enum { * * MOVE_CANCEL_RACE: canceled because the volume is being deleted, the source * PEB was put meanwhile, or there is I/O on the source PEB + * MOVE_SOURCE_RD_ERR: canceled because there was a read error from the source + * PEB + * MOVE_TARGET_RD_ERR: canceled because there was a read error from the target + * PEB * MOVE_TARGET_WR_ERR: canceled because there was a write error to the target * PEB * MOVE_CANCEL_BITFLIPS: canceled because a bit-flip was detected in the @@ -112,6 +116,8 @@ enum { */ enum { MOVE_CANCEL_RACE = 1, + MOVE_SOURCE_RD_ERR, + MOVE_TARGET_RD_ERR, MOVE_TARGET_WR_ERR, MOVE_CANCEL_BITFLIPS, }; @@ -334,14 +340,15 @@ struct ubi_wl_entry; * @alc_mutex: serializes "atomic LEB change" operations * * @used: RB-tree of used physical eraseblocks + * @erroneous: RB-tree of erroneous used physical eraseblocks * @free: RB-tree of free physical eraseblocks * @scrub: RB-tree of physical eraseblocks which need scrubbing * @pq: protection queue (contain physical eraseblocks which are temporarily * protected from the wear-leveling worker) * @pq_head: protection queue head * @wl_lock: protects the @used, @free, @pq, @pq_head, @lookuptbl, @move_from, - * @move_to, @move_to_put @erase_pending, @wl_scheduled and @works - * fields + * @move_to, @move_to_put @erase_pending, @wl_scheduled, @works and + * @erroneous_peb_count fields * @move_mutex: serializes eraseblock moves * @work_sem: synchronizes the WL worker with use tasks * @wl_scheduled: non-zero if the wear-leveling was scheduled @@ -361,6 +368,8 @@ struct ubi_wl_entry; * @peb_size: physical eraseblock size * @bad_peb_count: count of bad physical eraseblocks * @good_peb_count: count of good physical eraseblocks + * @erroneous_peb_count: count of erroneous physical eraseblocks in @erroneous + * @max_erroneous: maximum allowed amount of erroneous physical eraseblocks * @min_io_size: minimal input/output unit size of the underlying MTD device * @hdrs_min_io_size: minimal I/O unit size used for VID and EC headers * @ro_mode: if the UBI device is in read-only mode @@ -418,6 +427,7 @@ struct ubi_device { /* Wear-leveling sub-system's stuff */ struct rb_root used; + struct rb_root erroneous; struct rb_root free; struct rb_root scrub; struct list_head pq[UBI_PROT_QUEUE_LEN]; @@ -442,6 +452,8 @@ struct ubi_device { int peb_size; int bad_peb_count; int good_peb_count; + int erroneous_peb_count; + int max_erroneous; int min_io_size; int hdrs_min_io_size; int ro_mode; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 793882ba2a6..9d1d3595a24 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -55,8 +55,8 @@ * * As it was said, for the UBI sub-system all physical eraseblocks are either * "free" or "used". Free eraseblock are kept in the @wl->free RB-tree, while - * used eraseblocks are kept in @wl->used or @wl->scrub RB-trees, or - * (temporarily) in the @wl->pq queue. + * used eraseblocks are kept in @wl->used, @wl->erroneous, or @wl->scrub + * RB-trees, as well as (temporarily) in the @wl->pq queue. * * When the WL sub-system returns a physical eraseblock, the physical * eraseblock is protected from being moved for some "time". For this reason, @@ -83,6 +83,8 @@ * used. The former state corresponds to the @wl->free tree. The latter state * is split up on several sub-states: * o the WL movement is allowed (@wl->used tree); + * o the WL movement is disallowed (@wl->erroneous) becouse the PEB is + * erroneous - e.g., there was a read error; * o the WL movement is temporarily prohibited (@wl->pq queue); * o scrubbing is needed (@wl->scrub tree). * @@ -653,7 +655,7 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, int cancel) { - int err, scrubbing = 0, torture = 0, protect = 0; + int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0; struct ubi_wl_entry *e1, *e2; struct ubi_vid_hdr *vid_hdr; @@ -769,13 +771,31 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, goto out_not_moved; } - if (err == MOVE_CANCEL_BITFLIPS || - err == MOVE_TARGET_WR_ERR) { + if (err == MOVE_CANCEL_BITFLIPS || err == MOVE_TARGET_WR_ERR || + err == MOVE_TARGET_RD_ERR) { /* Target PEB bit-flips or write error, torture it */ torture = 1; goto out_not_moved; } + if (err == MOVE_SOURCE_RD_ERR) { + /* + * An error happened while reading the source PEB. Do + * not switch to R/O mode in this case, and give the + * upper layers a possibility to recover from this, + * e.g. by unmapping corresponding LEB. Instead, just + * put thie PEB to the @ubi->erroneus list to prevent + * UBI from trying to move the over and over again. + */ + if (ubi->erroneous_peb_count > ubi->max_erroneous) { + ubi_err("too many erroneous eraseblocks (%d)", + ubi->erroneous_peb_count); + goto out_error; + } + erroneous = 1; + goto out_not_moved; + } + if (err < 0) goto out_error; @@ -832,7 +852,10 @@ out_not_moved: spin_lock(&ubi->wl_lock); if (protect) prot_queue_add(ubi, e1); - else if (scrubbing) + else if (erroneous) { + wl_tree_add(e1, &ubi->erroneous); + ubi->erroneous_peb_count += 1; + } else if (scrubbing) wl_tree_add(e1, &ubi->scrub); else wl_tree_add(e1, &ubi->used); @@ -1116,6 +1139,13 @@ retry: } else if (in_wl_tree(e, &ubi->scrub)) { paranoid_check_in_wl_tree(e, &ubi->scrub); rb_erase(&e->u.rb, &ubi->scrub); + } else if (in_wl_tree(e, &ubi->erroneous)) { + paranoid_check_in_wl_tree(e, &ubi->erroneous); + rb_erase(&e->u.rb, &ubi->erroneous); + ubi->erroneous_peb_count -= 1; + ubi_assert(ubi->erroneous_peb_count >= 0); + /* Erronious PEBs should be tortured */ + torture = 1; } else { err = prot_queue_del(ubi, e->pnum); if (err) { @@ -1364,7 +1394,7 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) struct ubi_scan_leb *seb, *tmp; struct ubi_wl_entry *e; - ubi->used = ubi->free = ubi->scrub = RB_ROOT; + ubi->used = ubi->erroneous = ubi->free = ubi->scrub = RB_ROOT; spin_lock_init(&ubi->wl_lock); mutex_init(&ubi->move_mutex); init_rwsem(&ubi->work_sem); @@ -1502,6 +1532,7 @@ void ubi_wl_close(struct ubi_device *ubi) cancel_pending(ubi); protection_queue_destroy(ubi); tree_destroy(&ubi->used); + tree_destroy(&ubi->erroneous); tree_destroy(&ubi->free); tree_destroy(&ubi->scrub); kfree(ubi->lookuptbl); -- cgit v1.2.3 From 0e0ee1cc33de8f0cc603269b354085dee340afa0 Mon Sep 17 00:00:00 2001 From: Dmitry Pervushin Date: Wed, 29 Apr 2009 19:29:38 +0400 Subject: UBI: add notification API UBI volume notifications are intended to create the API to get clients notified about volume creation/deletion, renaming and re-sizing. A client can subscribe to these notifications using 'ubi_volume_register()' and cancel the subscription using 'ubi_volume_unregister()'. When UBI volumes change, a blocking notifier is called. Clients also can request "added" events on all volumes that existed before client subscribed to the notifications. If we use notifications instead of calling functions like 'ubi_gluebi_xxx()', we can make the MTD emulation layer to be more flexible: build it as a separate module and load/unload it on demand. [Artem: many cleanups, rework locking, add "updated" event, provide device/volume info in notifiers] Signed-off-by: Dmitry Pervushin Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 100 +++++++++++++++++++++++++++++++++++++++++--- drivers/mtd/ubi/cdev.c | 1 + drivers/mtd/ubi/kapi.c | 108 +++++++++++++++++++++++++++++++++++++++++------- drivers/mtd/ubi/ubi.h | 12 ++++++ drivers/mtd/ubi/vmt.c | 4 ++ 5 files changed, 204 insertions(+), 21 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index d3da6668266..964a99d48bc 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -121,6 +121,94 @@ static struct device_attribute dev_bgt_enabled = static struct device_attribute dev_mtd_num = __ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL); +/** + * ubi_volume_notify - send a volume change notification. + * @ubi: UBI device description object + * @vol: volume description object of the changed volume + * @ntype: notification type to send (%UBI_VOLUME_ADDED, etc) + * + * This is a helper function which notifies all subscribers about a volume + * change event (creation, removal, re-sizing, re-naming, updating). Returns + * zero in case of success and a negative error code in case of failure. + */ +int ubi_volume_notify(struct ubi_device *ubi, struct ubi_volume *vol, int ntype) +{ + struct ubi_notification nt; + + ubi_do_get_device_info(ubi, &nt.di); + ubi_do_get_volume_info(ubi, vol, &nt.vi); + return blocking_notifier_call_chain(&ubi_notifiers, ntype, &nt); +} + +/** + * ubi_notify_all - send a notification to all volumes. + * @ubi: UBI device description object + * @ntype: notification type to send (%UBI_VOLUME_ADDED, etc) + * @nb: the notifier to call + * + * This function walks all volumes of UBI device @ubi and sends the @ntype + * notification for each volume. If @nb is %NULL, then all registered notifiers + * are called, otherwise only the @nb notifier is called. Returns the number of + * sent notifications. + */ +int ubi_notify_all(struct ubi_device *ubi, int ntype, struct notifier_block *nb) +{ + struct ubi_notification nt; + int i, count = 0; + + ubi_do_get_device_info(ubi, &nt.di); + + mutex_lock(&ubi->device_mutex); + for (i = 0; i < ubi->vtbl_slots; i++) { + /* + * Since the @ubi->device is locked, and we are not going to + * change @ubi->volumes, we do not have to lock + * @ubi->volumes_lock. + */ + if (!ubi->volumes[i]) + continue; + + ubi_do_get_volume_info(ubi, ubi->volumes[i], &nt.vi); + if (nb) + nb->notifier_call(nb, ntype, &nt); + else + blocking_notifier_call_chain(&ubi_notifiers, ntype, + &nt); + count += 1; + } + mutex_unlock(&ubi->device_mutex); + + return count; +} + +/** + * ubi_enumerate_volumes - send "add" notification for all existing volumes. + * @nb: the notifier to call + * + * This function walks all UBI devices and volumes and sends the + * %UBI_VOLUME_ADDED notification for each volume. If @nb is %NULL, then all + * registered notifiers are called, otherwise only the @nb notifier is called. + * Returns the number of sent notifications. + */ +int ubi_enumerate_volumes(struct notifier_block *nb) +{ + int i, count = 0; + + /* + * Since the @ubi_devices_mutex is locked, and we are not going to + * change @ubi_devices, we do not have to lock @ubi_devices_lock. + */ + for (i = 0; i < UBI_MAX_DEVICES; i++) { + struct ubi_device *ubi = ubi_devices[i]; + + if (!ubi) + continue; + count += ubi_notify_all(ubi, UBI_VOLUME_ADDED, nb); + } + + return count; +} + /** * ubi_get_device - get UBI device. * @ubi_num: UBI device number @@ -891,6 +979,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) spin_unlock(&ubi->wl_lock); ubi_devices[ubi_num] = ubi; + ubi_notify_all(ubi, UBI_VOLUME_ADDED, NULL); return ubi_num; out_uif: @@ -933,13 +1022,13 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) return -EINVAL; - spin_lock(&ubi_devices_lock); - ubi = ubi_devices[ubi_num]; - if (!ubi) { - spin_unlock(&ubi_devices_lock); + ubi = ubi_get_device(ubi_num); + if (!ubi) return -EINVAL; - } + spin_lock(&ubi_devices_lock); + put_device(&ubi->dev); + ubi->ref_count -= 1; if (ubi->ref_count) { if (!anyway) { spin_unlock(&ubi_devices_lock); @@ -953,6 +1042,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) spin_unlock(&ubi_devices_lock); ubi_assert(ubi_num == ubi->ubi_num); + ubi_notify_all(ubi, UBI_VOLUME_REMOVED, NULL); dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num); /* diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 9a2b217941f..631983615f1 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -396,6 +396,7 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, } vol->checked = 1; ubi_gluebi_updated(vol); + ubi_volume_notify(ubi, vol, UBI_VOLUME_UPDATED); revoke_exclusive(desc, UBI_READWRITE); } diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 2675207c5fe..88a72e9c8be 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -25,6 +25,24 @@ #include #include "ubi.h" +/** + * ubi_do_get_device_info - get information about UBI device. + * @ubi: UBI device description object + * @di: the information is stored here + * + * This function is the same as 'ubi_get_device_info()', but it assumes the UBI + * device is locked and cannot disappear. + */ +void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di) +{ + di->ubi_num = ubi->ubi_num; + di->leb_size = ubi->leb_size; + di->min_io_size = ubi->min_io_size; + di->ro_mode = ubi->ro_mode; + di->cdev = ubi->cdev.dev; +} +EXPORT_SYMBOL_GPL(ubi_do_get_device_info); + /** * ubi_get_device_info - get information about UBI device. * @ubi_num: UBI device number @@ -39,33 +57,24 @@ int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) return -EINVAL; - ubi = ubi_get_device(ubi_num); if (!ubi) return -ENODEV; - - di->ubi_num = ubi->ubi_num; - di->leb_size = ubi->leb_size; - di->min_io_size = ubi->min_io_size; - di->ro_mode = ubi->ro_mode; - di->cdev = ubi->cdev.dev; - + ubi_do_get_device_info(ubi, di); ubi_put_device(ubi); return 0; } EXPORT_SYMBOL_GPL(ubi_get_device_info); /** - * ubi_get_volume_info - get information about UBI volume. - * @desc: volume descriptor + * ubi_do_get_volume_info - get information about UBI volume. + * @ubi: UBI device description object + * @vol: volume description object * @vi: the information is stored here */ -void ubi_get_volume_info(struct ubi_volume_desc *desc, - struct ubi_volume_info *vi) +void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol, + struct ubi_volume_info *vi) { - const struct ubi_volume *vol = desc->vol; - const struct ubi_device *ubi = vol->ubi; - vi->vol_id = vol->vol_id; vi->ubi_num = ubi->ubi_num; vi->size = vol->reserved_pebs; @@ -79,6 +88,17 @@ void ubi_get_volume_info(struct ubi_volume_desc *desc, vi->name = vol->name; vi->cdev = vol->cdev.dev; } + +/** + * ubi_get_volume_info - get information about UBI volume. + * @desc: volume descriptor + * @vi: the information is stored here + */ +void ubi_get_volume_info(struct ubi_volume_desc *desc, + struct ubi_volume_info *vi) +{ + ubi_do_get_volume_info(desc->vol->ubi, desc->vol, vi); +} EXPORT_SYMBOL_GPL(ubi_get_volume_info); /** @@ -561,7 +581,7 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) EXPORT_SYMBOL_GPL(ubi_leb_unmap); /** - * ubi_leb_map - map logical erasblock to a physical eraseblock. + * ubi_leb_map - map logical eraseblock to a physical eraseblock. * @desc: volume descriptor * @lnum: logical eraseblock number * @dtype: expected data type @@ -659,3 +679,59 @@ int ubi_sync(int ubi_num) return 0; } EXPORT_SYMBOL_GPL(ubi_sync); + +BLOCKING_NOTIFIER_HEAD(ubi_notifiers); + +/** + * ubi_register_volume_notifier - register a volume notifier. + * @nb: the notifier description object + * @ignore_existing: if non-zero, do not send "added" notification for all + * already existing volumes + * + * This function registers a volume notifier, which means that + * 'nb->notifier_call()' will be invoked when an UBI volume is created, + * removed, re-sized, re-named, or updated. The first argument of the function + * is the notification type. The second argument is pointer to a + * &struct ubi_notification object which describes the notification event. + * Using UBI API from the volume notifier is prohibited. + * + * This function returns zero in case of success and a negative error code + * in case of failure. + */ +int ubi_register_volume_notifier(struct notifier_block *nb, + int ignore_existing) +{ + int err; + + err = blocking_notifier_chain_register(&ubi_notifiers, nb); + if (err != 0) + return err; + if (ignore_existing) + return 0; + + /* + * We are going to walk all UBI devices and all volumes, and + * notify the user about existing volumes by the %UBI_VOLUME_ADDED + * event. We have to lock the @ubi_devices_mutex to make sure UBI + * devices do not disappear. + */ + mutex_lock(&ubi_devices_mutex); + ubi_enumerate_volumes(nb); + mutex_unlock(&ubi_devices_mutex); + + return err; +} +EXPORT_SYMBOL_GPL(ubi_register_volume_notifier); + +/** + * ubi_unregister_volume_notifier - unregister the volume notifier. + * @nb: the notifier description object + * + * This function unregisters volume notifier @nm and returns zero in case of + * success and a negative error code in case of failure. + */ +int ubi_unregister_volume_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&ubi_notifiers, nb); +} +EXPORT_SYMBOL_GPL(ubi_unregister_volume_notifier); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 6d929329a8d..86e1a4e0ab0 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -38,6 +38,7 @@ #include #include #include +#include #include "ubi-media.h" #include "scan.h" @@ -483,6 +484,7 @@ extern const struct file_operations ubi_cdev_operations; extern const struct file_operations ubi_vol_cdev_operations; extern struct class *ubi_class; extern struct mutex ubi_devices_mutex; +extern struct blocking_notifier_head ubi_notifiers; /* vtbl.c */ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, @@ -575,6 +577,16 @@ struct ubi_device *ubi_get_device(int ubi_num); void ubi_put_device(struct ubi_device *ubi); struct ubi_device *ubi_get_by_major(int major); int ubi_major2num(int major); +int ubi_volume_notify(struct ubi_device *ubi, struct ubi_volume *vol, + int ntype); +int ubi_notify_all(struct ubi_device *ubi, int ntype, + struct notifier_block *nb); +int ubi_enumerate_volumes(struct notifier_block *nb); + +/* kapi.c */ +void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di); +void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol, + struct ubi_volume_info *vi); /* * ubi_rb_for_each_entry - walk an RB-tree. diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 8e8d6fae7a0..e151862a3a9 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -358,6 +358,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ubi->vol_count += 1; spin_unlock(&ubi->volumes_lock); + ubi_volume_notify(ubi, vol, UBI_VOLUME_ADDED); if (paranoid_check_volumes(ubi)) dbg_err("check failed while creating volume %d", vol_id); return err; @@ -466,6 +467,7 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) ubi->vol_count -= 1; spin_unlock(&ubi->volumes_lock); + ubi_volume_notify(ubi, vol, UBI_VOLUME_REMOVED); if (!no_vtbl && paranoid_check_volumes(ubi)) dbg_err("check failed while removing volume %d", vol_id); @@ -589,6 +591,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) (long long)vol->used_ebs * vol->usable_leb_size; } + ubi_volume_notify(ubi, vol, UBI_VOLUME_RESIZED); if (paranoid_check_volumes(ubi)) dbg_err("check failed while re-sizing volume %d", vol_id); return err; @@ -635,6 +638,7 @@ int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list) vol->name_len = re->new_name_len; memcpy(vol->name, re->new_name, re->new_name_len + 1); spin_unlock(&ubi->volumes_lock); + ubi_volume_notify(ubi, vol, UBI_VOLUME_RENAMED); } } -- cgit v1.2.3 From 518ceef0c9ca97023e45ae46aedaefa240c690a6 Mon Sep 17 00:00:00 2001 From: Dmitry Pervushin Date: Wed, 29 Apr 2009 19:29:44 +0400 Subject: UBI: remove built-in gluebi Remove built-in gluebi support. This is a preparation for a standalone glubi module support Signed-off-by: Dmitry Pervushin Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 1 - drivers/mtd/ubi/ubi.h | 26 -------------------------- drivers/mtd/ubi/vmt.c | 26 ++------------------------ 3 files changed, 2 insertions(+), 51 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 631983615f1..f237ddbb271 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -395,7 +395,6 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, vol->corrupted = 1; } vol->checked = 1; - ubi_gluebi_updated(vol); ubi_volume_notify(ubi, vol, UBI_VOLUME_UPDATED); revoke_exclusive(desc, UBI_READWRITE); } diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 86e1a4e0ab0..82da62bde41 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -231,10 +231,6 @@ struct ubi_volume_desc; * @changing_leb: %1 if the atomic LEB change ioctl command is in progress * @direct_writes: %1 if direct writes are enabled for this volume * - * @gluebi_desc: gluebi UBI volume descriptor - * @gluebi_refcount: reference count of the gluebi MTD device - * @gluebi_mtd: MTD device description object of the gluebi MTD device - * * The @corrupted field indicates that the volume's contents is corrupted. * Since UBI protects only static volumes, this field is not relevant to * dynamic volumes - it is user's responsibility to assure their data @@ -278,17 +274,6 @@ struct ubi_volume { unsigned int updating:1; unsigned int changing_leb:1; unsigned int direct_writes:1; - -#ifdef CONFIG_MTD_UBI_GLUEBI - /* - * Gluebi-related stuff may be compiled out. - * Note: this should not be built into UBI but should be a separate - * ubimtd driver which works on top of UBI and emulates MTD devices. - */ - struct ubi_volume_desc *gluebi_desc; - int gluebi_refcount; - struct mtd_info gluebi_mtd; -#endif }; /** @@ -517,17 +502,6 @@ int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int ubi_check_volume(struct ubi_device *ubi, int vol_id); void ubi_calculate_reserved(struct ubi_device *ubi); -/* gluebi.c */ -#ifdef CONFIG_MTD_UBI_GLUEBI -int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol); -int ubi_destroy_gluebi(struct ubi_volume *vol); -void ubi_gluebi_updated(struct ubi_volume *vol); -#else -#define ubi_create_gluebi(ubi, vol) 0 -#define ubi_destroy_gluebi(vol) 0 -#define ubi_gluebi_updated(vol) -#endif - /* eba.c */ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum); diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index e151862a3a9..ab64cb56df6 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -317,10 +317,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) goto out_mapping; } - err = ubi_create_gluebi(ubi, vol); - if (err) - goto out_cdev; - vol->dev.release = vol_release; vol->dev.parent = &ubi->dev; vol->dev.devt = dev; @@ -330,7 +326,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) err = device_register(&vol->dev); if (err) { ubi_err("cannot register device"); - goto out_gluebi; + goto out_cdev; } err = volume_sysfs_init(ubi, vol); @@ -375,10 +371,6 @@ out_sysfs: do_free = 0; get_device(&vol->dev); volume_sysfs_close(vol); -out_gluebi: - if (ubi_destroy_gluebi(vol)) - dbg_err("cannot destroy gluebi for volume %d:%d", - ubi->ubi_num, vol_id); out_cdev: cdev_del(&vol->cdev); out_mapping: @@ -433,10 +425,6 @@ int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) ubi->volumes[vol_id] = NULL; spin_unlock(&ubi->volumes_lock); - err = ubi_destroy_gluebi(vol); - if (err) - goto out_err; - if (!no_vtbl) { err = ubi_change_vtbl_record(ubi, vol_id, NULL); if (err) @@ -674,10 +662,6 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) return err; } - err = ubi_create_gluebi(ubi, vol); - if (err) - goto out_cdev; - vol->dev.release = vol_release; vol->dev.parent = &ubi->dev; vol->dev.devt = dev; @@ -685,12 +669,11 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id); err = device_register(&vol->dev); if (err) - goto out_gluebi; + goto out_cdev; err = volume_sysfs_init(ubi, vol); if (err) { cdev_del(&vol->cdev); - err = ubi_destroy_gluebi(vol); volume_sysfs_close(vol); return err; } @@ -699,8 +682,6 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) dbg_err("check failed while adding volume %d", vol_id); return err; -out_gluebi: - err = ubi_destroy_gluebi(vol); out_cdev: cdev_del(&vol->cdev); return err; @@ -716,12 +697,9 @@ out_cdev: */ void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol) { - int err; - dbg_gen("free volume %d", vol->vol_id); ubi->volumes[vol->vol_id] = NULL; - err = ubi_destroy_gluebi(vol); cdev_del(&vol->cdev); volume_sysfs_close(vol); } -- cgit v1.2.3 From 2ba3d76a1e29f2ba64fbc762875cf9fb2d4ba2ba Mon Sep 17 00:00:00 2001 From: Dmitry Pervushin Date: Sun, 31 May 2009 18:32:59 +0400 Subject: UBI: make gluebi a separate module [Artem: re-worked the patch: made it release resources when the module is unloaded, made it do module referencing, made it really independent on UBI, tested it with the UBI test-suite which can be found in ubi-2.6.git/tests/ubi-tests, re-named most of the funcs/variables to get rid of the "ubi" word and make names consistent.] Signed-off-by: Dmitry Pervushin Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/Kconfig | 13 +- drivers/mtd/ubi/Makefile | 2 +- drivers/mtd/ubi/gluebi.c | 378 +++++++++++++++++++++++++++++++++++------------ 3 files changed, 295 insertions(+), 98 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index 3f063108e95..b1cd7a1a219 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -49,15 +49,16 @@ config MTD_UBI_BEB_RESERVE reserved. Leave the default value if unsure. config MTD_UBI_GLUEBI - bool "Emulate MTD devices" + tristate "MTD devices emulation driver (gluebi)" default n depends on MTD_UBI help - This option enables MTD devices emulation on top of UBI volumes: for - each UBI volumes an MTD device is created, and all I/O to this MTD - device is redirected to the UBI volume. This is handy to make - MTD-oriented software (like JFFS2) work on top of UBI. Do not enable - this if no legacy software will be used. + This option enables gluebi - an additional driver which emulates MTD + devices on top of UBI volumes: for each UBI volumes an MTD device is + created, and all I/O to this MTD device is redirected to the UBI + volume. This is handy to make MTD-oriented software (like JFFS2) + work on top of UBI. Do not enable this unless you use legacy + software. source "drivers/mtd/ubi/Kconfig.debug" endmenu diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile index dd834e04151..c9302a5452b 100644 --- a/drivers/mtd/ubi/Makefile +++ b/drivers/mtd/ubi/Makefile @@ -4,4 +4,4 @@ ubi-y += vtbl.o vmt.o upd.o build.o cdev.o kapi.o eba.o io.o wl.o scan.o ubi-y += misc.o ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o -ubi-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o +obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c index 49cd55ade9c..95aaac03f93 100644 --- a/drivers/mtd/ubi/gluebi.c +++ b/drivers/mtd/ubi/gluebi.c @@ -19,17 +19,71 @@ */ /* - * This file includes implementation of fake MTD devices for each UBI volume. - * This sounds strange, but it is in fact quite useful to make MTD-oriented - * software (including all the legacy software) to work on top of UBI. + * This is a small driver which implements fake MTD devices on top of UBI + * volumes. This sounds strange, but it is in fact quite useful to make + * MTD-oriented software (including all the legacy software) work on top of + * UBI. * * Gluebi emulates MTD devices of "MTD_UBIVOLUME" type. Their minimal I/O unit - * size (mtd->writesize) is equivalent to the UBI minimal I/O unit. The + * size (@mtd->writesize) is equivalent to the UBI minimal I/O unit. The * eraseblock size is equivalent to the logical eraseblock size of the volume. */ +#include +#include +#include #include -#include "ubi.h" +#include +#include +#include +#include +#include "ubi-media.h" + +#define err_msg(fmt, ...) \ + printk(KERN_DEBUG "gluebi (pid %d): %s: " fmt "\n", \ + current->pid, __func__, ##__VA_ARGS__) + +/** + * struct gluebi_device - a gluebi device description data structure. + * @mtd: emulated MTD device description object + * @refcnt: gluebi device reference count + * @desc: UBI volume descriptor + * @ubi_num: UBI device number this gluebi device works on + * @vol_id: ID of UBI volume this gluebi device works on + * @list: link in a list of gluebi devices + */ +struct gluebi_device { + struct mtd_info mtd; + int refcnt; + struct ubi_volume_desc *desc; + int ubi_num; + int vol_id; + struct list_head list; +}; + +/* List of all gluebi devices */ +static LIST_HEAD(gluebi_devices); +static DEFINE_MUTEX(devices_mutex); + +/** + * find_gluebi_nolock - find a gluebi device. + * @ubi_num: UBI device number + * @vol_id: volume ID + * + * This function seraches for gluebi device corresponding to UBI device + * @ubi_num and UBI volume @vol_id. Returns the gluebi device description + * object in case of success and %NULL in case of failure. The caller has to + * have the &devices_mutex locked. + */ +static struct gluebi_device *find_gluebi_nolock(int ubi_num, int vol_id) +{ + struct gluebi_device *gluebi; + + list_for_each_entry(gluebi, &gluebi_devices, list) + if (gluebi->ubi_num == ubi_num && gluebi->vol_id == vol_id) + return gluebi; + return NULL; +} /** * gluebi_get_device - get MTD device reference. @@ -41,15 +95,18 @@ */ static int gluebi_get_device(struct mtd_info *mtd) { - struct ubi_volume *vol; + struct gluebi_device *gluebi; + int ubi_mode = UBI_READONLY; - vol = container_of(mtd, struct ubi_volume, gluebi_mtd); + if (!try_module_get(THIS_MODULE)) + return -ENODEV; - /* - * We do not introduce locks for gluebi reference count because the - * get_device()/put_device() calls are already serialized at MTD. - */ - if (vol->gluebi_refcount > 0) { + if (mtd->flags & MTD_WRITEABLE) + ubi_mode = UBI_READWRITE; + + gluebi = container_of(mtd, struct gluebi_device, mtd); + mutex_lock(&devices_mutex); + if (gluebi->refcnt > 0) { /* * The MTD device is already referenced and this is just one * more reference. MTD allows many users to open the same @@ -58,7 +115,8 @@ static int gluebi_get_device(struct mtd_info *mtd) * open the UBI volume again - just increase the reference * counter and return. */ - vol->gluebi_refcount += 1; + gluebi->refcnt += 1; + mutex_unlock(&devices_mutex); return 0; } @@ -66,11 +124,15 @@ static int gluebi_get_device(struct mtd_info *mtd) * This is the first reference to this UBI volume via the MTD device * interface. Open the corresponding volume in read-write mode. */ - vol->gluebi_desc = ubi_open_volume(vol->ubi->ubi_num, vol->vol_id, - UBI_READWRITE); - if (IS_ERR(vol->gluebi_desc)) - return PTR_ERR(vol->gluebi_desc); - vol->gluebi_refcount += 1; + gluebi->desc = ubi_open_volume(gluebi->ubi_num, gluebi->vol_id, + ubi_mode); + if (IS_ERR(gluebi->desc)) { + mutex_unlock(&devices_mutex); + module_put(THIS_MODULE); + return PTR_ERR(gluebi->desc); + } + gluebi->refcnt += 1; + mutex_unlock(&devices_mutex); return 0; } @@ -83,13 +145,15 @@ static int gluebi_get_device(struct mtd_info *mtd) */ static void gluebi_put_device(struct mtd_info *mtd) { - struct ubi_volume *vol; - - vol = container_of(mtd, struct ubi_volume, gluebi_mtd); - vol->gluebi_refcount -= 1; - ubi_assert(vol->gluebi_refcount >= 0); - if (vol->gluebi_refcount == 0) - ubi_close_volume(vol->gluebi_desc); + struct gluebi_device *gluebi; + + gluebi = container_of(mtd, struct gluebi_device, mtd); + mutex_lock(&devices_mutex); + gluebi->refcnt -= 1; + if (gluebi->refcnt == 0) + ubi_close_volume(gluebi->desc); + module_put(THIS_MODULE); + mutex_unlock(&devices_mutex); } /** @@ -107,16 +171,12 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, unsigned char *buf) { int err = 0, lnum, offs, total_read; - struct ubi_volume *vol; - struct ubi_device *ubi; - - dbg_gen("read %zd bytes from offset %lld", len, from); + struct gluebi_device *gluebi; if (len < 0 || from < 0 || from + len > mtd->size) return -EINVAL; - vol = container_of(mtd, struct ubi_volume, gluebi_mtd); - ubi = vol->ubi; + gluebi = container_of(mtd, struct gluebi_device, mtd); lnum = div_u64_rem(from, mtd->erasesize, &offs); total_read = len; @@ -126,7 +186,7 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, if (to_read > total_read) to_read = total_read; - err = ubi_eba_read_leb(ubi, vol, lnum, buf, offs, to_read, 0); + err = ubi_read(gluebi->desc, lnum, buf, offs, to_read); if (err) break; @@ -152,21 +212,17 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, * case of failure. */ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) + size_t *retlen, const u_char *buf) { int err = 0, lnum, offs, total_written; - struct ubi_volume *vol; - struct ubi_device *ubi; - - dbg_gen("write %zd bytes to offset %lld", len, to); + struct gluebi_device *gluebi; if (len < 0 || to < 0 || len + to > mtd->size) return -EINVAL; - vol = container_of(mtd, struct ubi_volume, gluebi_mtd); - ubi = vol->ubi; + gluebi = container_of(mtd, struct gluebi_device, mtd); - if (ubi->ro_mode) + if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; lnum = div_u64_rem(to, mtd->erasesize, &offs); @@ -181,8 +237,7 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, if (to_write > total_written) to_write = total_written; - err = ubi_eba_write_leb(ubi, vol, lnum, buf, offs, to_write, - UBI_UNKNOWN); + err = ubi_write(gluebi->desc, lnum, buf, offs, to_write); if (err) break; @@ -207,41 +262,36 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) { int err, i, lnum, count; - struct ubi_volume *vol; - struct ubi_device *ubi; - - dbg_gen("erase %llu bytes at offset %llu", (unsigned long long)instr->len, - (unsigned long long)instr->addr); + struct gluebi_device *gluebi; if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize) return -EINVAL; - if (instr->len < 0 || instr->addr + instr->len > mtd->size) return -EINVAL; - if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd)) return -EINVAL; lnum = mtd_div_by_eb(instr->addr, mtd); count = mtd_div_by_eb(instr->len, mtd); - vol = container_of(mtd, struct ubi_volume, gluebi_mtd); - ubi = vol->ubi; + gluebi = container_of(mtd, struct gluebi_device, mtd); - if (ubi->ro_mode) + if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - for (i = 0; i < count; i++) { - err = ubi_eba_unmap_leb(ubi, vol, lnum + i); + for (i = 0; i < count - 1; i++) { + err = ubi_leb_unmap(gluebi->desc, lnum + i); if (err) goto out_err; } - /* * MTD erase operations are synchronous, so we have to make sure the * physical eraseblock is wiped out. + * + * Thus, perform leb_erase instead of leb_unmap operation - leb_erase + * will wait for the end of operations */ - err = ubi_wl_flush(ubi); + err = ubi_leb_erase(gluebi->desc, lnum + i); if (err) goto out_err; @@ -256,28 +306,38 @@ out_err: } /** - * ubi_create_gluebi - initialize gluebi for an UBI volume. - * @ubi: UBI device description object - * @vol: volume description object + * gluebi_create - create a gluebi device for an UBI volume. + * @di: UBI device description object + * @vi: UBI volume description object * - * This function is called when an UBI volume is created in order to create + * This function is called when a new UBI volume is created in order to create * corresponding fake MTD device. Returns zero in case of success and a * negative error code in case of failure. */ -int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) +static int gluebi_create(struct ubi_device_info *di, + struct ubi_volume_info *vi) { - struct mtd_info *mtd = &vol->gluebi_mtd; + struct gluebi_device *gluebi, *g; + struct mtd_info *mtd; - mtd->name = kmemdup(vol->name, vol->name_len + 1, GFP_KERNEL); - if (!mtd->name) + gluebi = kzalloc(sizeof(struct gluebi_device), GFP_KERNEL); + if (!gluebi) return -ENOMEM; + mtd = &gluebi->mtd; + mtd->name = kmemdup(vi->name, vi->name_len + 1, GFP_KERNEL); + if (!mtd->name) { + kfree(gluebi); + return -ENOMEM; + } + + gluebi->vol_id = vi->vol_id; mtd->type = MTD_UBIVOLUME; - if (!ubi->ro_mode) + if (!di->ro_mode) mtd->flags = MTD_WRITEABLE; - mtd->writesize = ubi->min_io_size; mtd->owner = THIS_MODULE; - mtd->erasesize = vol->usable_leb_size; + mtd->writesize = di->min_io_size; + mtd->erasesize = vi->usable_leb_size; mtd->read = gluebi_read; mtd->write = gluebi_write; mtd->erase = gluebi_erase; @@ -285,60 +345,196 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) mtd->put_device = gluebi_put_device; /* - * In case of dynamic volume, MTD device size is just volume size. In + * In case of dynamic a volume, MTD device size is just volume size. In * case of a static volume the size is equivalent to the amount of data * bytes. */ - if (vol->vol_type == UBI_DYNAMIC_VOLUME) - mtd->size = (long long)vol->usable_leb_size * vol->reserved_pebs; + if (vi->vol_type == UBI_DYNAMIC_VOLUME) + mtd->size = (unsigned long long)vi->usable_leb_size * vi->size; else - mtd->size = vol->used_bytes; + mtd->size = vi->used_bytes; + + /* Just a sanity check - make sure this gluebi device does not exist */ + mutex_lock(&devices_mutex); + g = find_gluebi_nolock(vi->ubi_num, vi->vol_id); + if (g) + err_msg("gluebi MTD device %d form UBI device %d volume %d " + "already exists", g->mtd.index, vi->ubi_num, + vi->vol_id); + mutex_unlock(&devices_mutex); if (add_mtd_device(mtd)) { - ubi_err("cannot not add MTD device"); + err_msg("cannot add MTD device"); kfree(mtd->name); + kfree(gluebi); return -ENFILE; } - dbg_gen("added mtd%d (\"%s\"), size %llu, EB size %u", - mtd->index, mtd->name, (unsigned long long)mtd->size, mtd->erasesize); + mutex_lock(&devices_mutex); + list_add_tail(&gluebi->list, &gluebi_devices); + mutex_unlock(&devices_mutex); return 0; } /** - * ubi_destroy_gluebi - close gluebi for an UBI volume. - * @vol: volume description object + * gluebi_remove - remove a gluebi device. + * @vi: UBI volume description object * - * This function is called when an UBI volume is removed in order to remove + * This function is called when an UBI volume is removed and it removes * corresponding fake MTD device. Returns zero in case of success and a * negative error code in case of failure. */ -int ubi_destroy_gluebi(struct ubi_volume *vol) +static int gluebi_remove(struct ubi_volume_info *vi) { - int err; - struct mtd_info *mtd = &vol->gluebi_mtd; + int err = 0; + struct mtd_info *mtd; + struct gluebi_device *gluebi; + + mutex_lock(&devices_mutex); + gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); + if (!gluebi) { + err_msg("got remove notification for unknown UBI device %d " + "volume %d", vi->ubi_num, vi->vol_id); + err = -ENOENT; + } else if (gluebi->refcnt) + err = -EBUSY; + else + list_del(&gluebi->list); + mutex_unlock(&devices_mutex); + if (err) + return err; - dbg_gen("remove mtd%d", mtd->index); + mtd = &gluebi->mtd; err = del_mtd_device(mtd); - if (err) + if (err) { + err_msg("cannot remove fake MTD device %d, UBI device %d, " + "volume %d, error %d", mtd->index, gluebi->ubi_num, + gluebi->vol_id, err); + mutex_lock(&devices_mutex); + list_add_tail(&gluebi->list, &gluebi_devices); + mutex_unlock(&devices_mutex); return err; + } + kfree(mtd->name); + kfree(gluebi); return 0; } /** - * ubi_gluebi_updated - UBI volume was updated notifier. - * @vol: volume description object + * gluebi_updated - UBI volume was updated notifier. + * @vi: volume info structure * - * This function is called every time an UBI volume is updated. This function - * does nothing if volume @vol is dynamic, and changes MTD device size if the + * This function is called every time an UBI volume is updated. It does nothing + * if te volume @vol is dynamic, and changes MTD device size if the * volume is static. This is needed because static volumes cannot be read past - * data they contain. + * data they contain. This function returns zero in case of success and a + * negative error code in case of error. + */ +static int gluebi_updated(struct ubi_volume_info *vi) +{ + struct gluebi_device *gluebi; + + mutex_lock(&devices_mutex); + gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); + if (!gluebi) { + mutex_unlock(&devices_mutex); + err_msg("got update notification for unknown UBI device %d " + "volume %d", vi->ubi_num, vi->vol_id); + return -ENOENT; + } + + if (vi->vol_type == UBI_STATIC_VOLUME) + gluebi->mtd.size = vi->used_bytes; + mutex_unlock(&devices_mutex); + return 0; +} + +/** + * gluebi_resized - UBI volume was re-sized notifier. + * @vi: volume info structure + * + * This function is called every time an UBI volume is re-size. It changes the + * corresponding fake MTD device size. This function returns zero in case of + * success and a negative error code in case of error. + */ +static int gluebi_resized(struct ubi_volume_info *vi) +{ + struct gluebi_device *gluebi; + + mutex_lock(&devices_mutex); + gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); + if (!gluebi) { + mutex_unlock(&devices_mutex); + err_msg("got update notification for unknown UBI device %d " + "volume %d", vi->ubi_num, vi->vol_id); + return -ENOENT; + } + gluebi->mtd.size = vi->used_bytes; + mutex_unlock(&devices_mutex); + return 0; +} + +/** + * gluebi_notify - UBI notification handler. + * @nb: registered notifier block + * @l: notification type + * @ptr: pointer to the &struct ubi_notification object */ -void ubi_gluebi_updated(struct ubi_volume *vol) +static int gluebi_notify(struct notifier_block *nb, unsigned long l, + void *ns_ptr) { - struct mtd_info *mtd = &vol->gluebi_mtd; + struct ubi_notification *nt = ns_ptr; + + switch (l) { + case UBI_VOLUME_ADDED: + gluebi_create(&nt->di, &nt->vi); + break; + case UBI_VOLUME_REMOVED: + gluebi_remove(&nt->vi); + break; + case UBI_VOLUME_RESIZED: + gluebi_resized(&nt->vi); + break; + case UBI_VOLUME_UPDATED: + gluebi_updated(&nt->vi); + break; + default: + break; + } + return NOTIFY_OK; +} - if (vol->vol_type == UBI_STATIC_VOLUME) - mtd->size = vol->used_bytes; +static struct notifier_block gluebi_notifier = { + .notifier_call = gluebi_notify, +}; + +static int __init ubi_gluebi_init(void) +{ + return ubi_register_volume_notifier(&gluebi_notifier, 0); } + +static void __exit ubi_gluebi_exit(void) +{ + struct gluebi_device *gluebi, *g; + + list_for_each_entry_safe(gluebi, g, &gluebi_devices, list) { + int err; + struct mtd_info *mtd = &gluebi->mtd; + + err = del_mtd_device(mtd); + if (err) + err_msg("error %d while removing gluebi MTD device %d, " + "UBI device %d, volume %d - ignoring", err, + mtd->index, gluebi->ubi_num, gluebi->vol_id); + kfree(mtd->name); + kfree(gluebi); + } + ubi_unregister_volume_notifier(&gluebi_notifier); +} + +module_init(ubi_gluebi_init); +module_exit(ubi_gluebi_exit); +MODULE_DESCRIPTION("MTD emulation layer over UBI volumes"); +MODULE_AUTHOR("Artem Bityutskiy, Joern Engel"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 9c259a52fa6ab46841a6094434cd0d752e854180 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 8 Jun 2009 12:49:08 +0300 Subject: UBI: improve messages in the WL worker Print not only the PEB number, but also the LEB number and volume id, which is very useful for bug hunting. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/wl.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 9d1d3595a24..784681e4236 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -656,6 +656,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, int cancel) { int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0; + int vol_id = -1, uninitialized_var(lnum); struct ubi_wl_entry *e1, *e2; struct ubi_vid_hdr *vid_hdr; @@ -757,6 +758,9 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, goto out_error; } + vol_id = be32_to_cpu(vid_hdr->vol_id); + lnum = be32_to_cpu(vid_hdr->lnum); + err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); if (err) { if (err == MOVE_CANCEL_RACE) { @@ -773,7 +777,9 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, if (err == MOVE_CANCEL_BITFLIPS || err == MOVE_TARGET_WR_ERR || err == MOVE_TARGET_RD_ERR) { - /* Target PEB bit-flips or write error, torture it */ + /* + * Target PEB had bit-flips or write error - torture it. + */ torture = 1; goto out_not_moved; } @@ -803,10 +809,10 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, } /* The PEB has been successfully moved */ - ubi_free_vid_hdr(ubi, vid_hdr); if (scrubbing) - ubi_msg("scrubbed PEB %d, data moved to PEB %d", - e1->pnum, e2->pnum); + ubi_msg("scrubbed PEB %d (LEB %d:%d), data moved to PEB %d", + e1->pnum, vol_id, lnum, e2->pnum); + ubi_free_vid_hdr(ubi, vid_hdr); spin_lock(&ubi->wl_lock); if (!ubi->move_to_put) { @@ -829,7 +835,8 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, * Well, the target PEB was put meanwhile, schedule it for * erasure. */ - dbg_wl("PEB %d was put meanwhile, erase", e2->pnum); + dbg_wl("PEB %d (LEB %d:%d) was put meanwhile, erase", + e2->pnum, vol_id, lnum); err = schedule_erase(ubi, e2, 0); if (err) { kmem_cache_free(ubi_wl_entry_slab, e2); @@ -847,8 +854,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, * have been changed, schedule it for erasure. */ out_not_moved: - dbg_wl("cancel moving PEB %d to PEB %d (%d)", - e1->pnum, e2->pnum, err); + if (vol_id != -1) + dbg_wl("cancel moving PEB %d (LEB %d:%d) to PEB %d (%d)", + e1->pnum, vol_id, lnum, e2->pnum, err); + else + dbg_wl("cancel moving PEB %d to PEB %d (%d)", + e1->pnum, e2->pnum, err); spin_lock(&ubi->wl_lock); if (protect) prot_queue_add(ubi, e1); @@ -874,8 +885,12 @@ out_not_moved: return 0; out_error: - ubi_err("error %d while moving PEB %d to PEB %d", - err, e1->pnum, e2->pnum); + if (vol_id != -1) + ubi_err("error %d while moving PEB %d to PEB %d", + err, e1->pnum, e2->pnum); + else + ubi_err("error %d while moving PEB %d (LEB %d:%d) to PEB %d", + err, e1->pnum, vol_id, lnum, e2->pnum); spin_lock(&ubi->wl_lock); ubi->move_from = ubi->move_to = NULL; ubi->move_to_put = ubi->wl_scheduled = 0; -- cgit v1.2.3 From 52b605d107de72c1d3385a3df972e79fb5befa4c Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 8 Jun 2009 16:52:48 +0300 Subject: UBI: print amount of reserved PEBs When marking a PEB as bad, print how many PEBs are left reserved. This is very useful information. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/wl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 784681e4236..f25ae2910ad 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -1072,10 +1072,9 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, ubi_err("no reserved physical eraseblocks"); goto out_ro; } - spin_unlock(&ubi->volumes_lock); - ubi_msg("mark PEB %d as bad", pnum); + ubi_msg("mark PEB %d as bad", pnum); err = ubi_io_mark_bad(ubi, pnum); if (err) goto out_ro; @@ -1085,7 +1084,9 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, ubi->bad_peb_count += 1; ubi->good_peb_count -= 1; ubi_calculate_reserved(ubi); - if (ubi->beb_rsvd_pebs == 0) + if (ubi->beb_rsvd_pebs) + ubi_msg("%d PEBs left in the reserve", ubi->beb_rsvd_pebs); + else ubi_warn("last PEB from the reserved pool was used"); spin_unlock(&ubi->volumes_lock); -- cgit v1.2.3 From 21d08bbcb19d9cdef8ab5b584f25b50d842068e9 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 8 Jun 2009 19:28:18 +0300 Subject: UBI: fix kmem_cache_free on error patch 'kmem_cache_free()' oopeses if NULL is passed, and there is one error-path place where UBI may call it with NULL object. This problem was pointed to by Adrian Hunter. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/wl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index f25ae2910ad..acb5520f7f3 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -826,7 +826,8 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, err = schedule_erase(ubi, e1, 0); if (err) { kmem_cache_free(ubi_wl_entry_slab, e1); - kmem_cache_free(ubi_wl_entry_slab, e2); + if (e2) + kmem_cache_free(ubi_wl_entry_slab, e2); goto out_ro; } -- cgit v1.2.3 From 815bc5f8fe516f55291aef90f2142073821e7a9c Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 8 Jun 2009 19:28:18 +0300 Subject: UBI: fix multiple spelling typos Some of the typos were indicated by Adrian Hunter, some by 'aspell'. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/eba.c | 2 +- drivers/mtd/ubi/io.c | 2 +- drivers/mtd/ubi/ubi.h | 4 ++-- drivers/mtd/ubi/wl.c | 14 +++++++------- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 632b95f3ff3..b6565561218 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -951,7 +951,7 @@ write_error: * physical eraseblock @to. The @vid_hdr buffer may be changed by this * function. Returns: * o %0 in case of success; - * o %MOVE_CANCEL_RACE, %MOVE_TARGET_WR_ERR, or %MOVE_CANCEL_BITFLIPS; + * o %MOVE_CANCEL_RACE, %MOVE_TARGET_WR_ERR, %MOVE_CANCEL_BITFLIPS, etc; * o a negative error code in case of failure. */ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index ac6604aeb72..effaff28bab 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -899,7 +899,7 @@ bad: * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected * and corrected by the flash driver; this is harmless but may indicate that * this eraseblock may become bad soon; - * o %UBI_IO_BAD_VID_HRD if the volume identifier header is corrupted (a CRC + * o %UBI_IO_BAD_VID_HDR if the volume identifier header is corrupted (a CRC * error detected); * o %UBI_IO_PEB_FREE if the physical eraseblock is free (i.e., there is no VID * header there); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 82da62bde41..70ce48b95b6 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -333,8 +333,8 @@ struct ubi_wl_entry; * protected from the wear-leveling worker) * @pq_head: protection queue head * @wl_lock: protects the @used, @free, @pq, @pq_head, @lookuptbl, @move_from, - * @move_to, @move_to_put @erase_pending, @wl_scheduled, @works and - * @erroneous_peb_count fields + * @move_to, @move_to_put @erase_pending, @wl_scheduled, @works, + * @erroneous, and @erroneous_peb_count fields * @move_mutex: serializes eraseblock moves * @work_sem: synchronizes the WL worker with use tasks * @wl_scheduled: non-zero if the wear-leveling was scheduled diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index acb5520f7f3..2b247230061 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -83,7 +83,7 @@ * used. The former state corresponds to the @wl->free tree. The latter state * is split up on several sub-states: * o the WL movement is allowed (@wl->used tree); - * o the WL movement is disallowed (@wl->erroneous) becouse the PEB is + * o the WL movement is disallowed (@wl->erroneous) because the PEB is * erroneous - e.g., there was a read error; * o the WL movement is temporarily prohibited (@wl->pq queue); * o scrubbing is needed (@wl->scrub tree). @@ -744,8 +744,8 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, * given, so we have a situation when it has not yet * had a chance to write it, because it was preempted. * So add this PEB to the protection queue so far, - * because presubably more data will be written there - * (including the missin VID header), and then we'll + * because presumably more data will be written there + * (including the missing VID header), and then we'll * move it. */ dbg_wl("PEB %d has no VID header", e1->pnum); @@ -790,8 +790,8 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, * not switch to R/O mode in this case, and give the * upper layers a possibility to recover from this, * e.g. by unmapping corresponding LEB. Instead, just - * put thie PEB to the @ubi->erroneus list to prevent - * UBI from trying to move the over and over again. + * put this PEB to the @ubi->erroneous list to prevent + * UBI from trying to move it over and over again. */ if (ubi->erroneous_peb_count > ubi->max_erroneous) { ubi_err("too many erroneous eraseblocks (%d)", @@ -1045,7 +1045,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, /* * If this is not %-EIO, we have no idea what to do. Scheduling * this physical eraseblock for erasure again would cause - * errors again and again. Well, lets switch to RO mode. + * errors again and again. Well, lets switch to R/O mode. */ goto out_ro; } @@ -1161,7 +1161,7 @@ retry: rb_erase(&e->u.rb, &ubi->erroneous); ubi->erroneous_peb_count -= 1; ubi_assert(ubi->erroneous_peb_count >= 0); - /* Erronious PEBs should be tortured */ + /* Erroneous PEBs should be tortured */ torture = 1; } else { err = prot_queue_del(ubi, e->pnum); -- cgit v1.2.3 From 6b5c94c6b4e1630a8e1ee7d30383d9396603749f Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 10 Jun 2009 11:32:05 +0300 Subject: UBI: handle more error codes The UBIFS WL worker may encounter read errors and there is logic which makes a decision whether we should do one of: 1. cancel the operation and move the PEB with the read errors to the 'erroneous' list; 2. switch to R/O mode. ATM, only -EIO errors trigger 1., other errors trigger 2. The idea is that if we know we encountered an I/O error, do 1. Otherwise, we do not know how to react, and do 2., just in case. E.g., if the underlying driver became crazy because of a bug, we do not want to harm any data, and switch to R/O mode. This patch does 2 things: 1. Makes sure reads from the source PEB always cause 1. This is more consistent with other reads which come from the upper layers and never cause R/O. 2. Teaches UBI to do 1. also on -EBADMSG, UBI_IO_BAD_VID_HDR, -ENOMEM, and -ETIMEOUT. But this is only when reading the target PEB. This preblems were hunted by Adrian Hunter. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/eba.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index b6565561218..0f2034c3ed2 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -940,6 +940,33 @@ write_error: goto retry; } +/** + * is_error_sane - check whether a read error is sane. + * @err: code of the error happened during reading + * + * This is a helper function for 'ubi_eba_copy_leb()' which is called when we + * cannot read data from the target PEB (an error @err happened). If the error + * code is sane, then we treat this error as non-fatal. Otherwise the error is + * fatal and UBI will be switched to R/O mode later. + * + * The idea is that we try not to switch to R/O mode if the read error is + * something which suggests there was a real read problem. E.g., %-EIO. Or a + * memory allocation failed (-%ENOMEM). Otherwise, it is safer to switch to R/O + * mode, simply because we do not know what happened at the MTD level, and we + * cannot handle this. E.g., the underlying driver may have become crazy, and + * it is safer to switch to R/O mode to preserve the data. + * + * And bear in mind, this is about reading from the target PEB, i.e. the PEB + * which we have just written. + */ +static int is_error_sane(int err) +{ + if (err == -EIO || err == -ENOMEM || err == UBI_IO_BAD_VID_HDR || + err == -ETIMEDOUT) + return 0; + return 1; +} + /** * ubi_eba_copy_leb - copy logical eraseblock. * @ubi: UBI device description object @@ -1033,8 +1060,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, if (err && err != UBI_IO_BITFLIPS) { ubi_warn("error %d while reading data from PEB %d", err, from); - if (err == -EIO) - err = MOVE_SOURCE_RD_ERR; + err = MOVE_SOURCE_RD_ERR; goto out_unlock_buf; } @@ -1082,8 +1108,9 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1); if (err) { if (err != UBI_IO_BITFLIPS) { - ubi_warn("cannot read VID header back from PEB %d", to); - if (err == -EIO) + ubi_warn("error %d while reading VID header back from " + "PEB %d", err, to); + if (is_error_sane(err)) err = MOVE_TARGET_RD_ERR; } else err = MOVE_CANCEL_BITFLIPS; @@ -1108,9 +1135,9 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, err = ubi_io_read_data(ubi, ubi->peb_buf2, to, 0, aldata_size); if (err) { if (err != UBI_IO_BITFLIPS) { - ubi_warn("cannot read data back from PEB %d", - to); - if (err == -EIO) + ubi_warn("error %d while reading data back " + "from PEB %d", err, to); + if (is_error_sane(err)) err = MOVE_TARGET_RD_ERR; } else err = MOVE_CANCEL_BITFLIPS; -- cgit v1.2.3 From d9dd0887cc5c6df0dbbe5a307284610607eea7ab Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 9 Jun 2009 10:59:19 -0700 Subject: UBI: add reboot notifier Terminate the UBI background thread prior to restarting the system. [Artem: amended comments a little] Signed-off-by: Kevin Cernekee Tested-by: Artem Bityutskiy Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 35 +++++++++++++++++++++++++++++++++++ drivers/mtd/ubi/ubi.h | 4 +++- 2 files changed, 38 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 964a99d48bc..286ed594e5a 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "ubi.h" /* Maximum length of the 'mtd=' parameter */ @@ -822,6 +823,34 @@ static int autoresize(struct ubi_device *ubi, int vol_id) return 0; } +/** + * ubi_reboot_notifier - halt UBI transactions immediately prior to a reboot. + * @n: reboot notifier object + * @state: SYS_RESTART, SYS_HALT, or SYS_POWER_OFF + * @cmd: pointer to command string for RESTART2 + * + * This function stops the UBI background thread so that the flash device + * remains quiescent when Linux restarts the system. Any queued work will be + * discarded, but this function will block until do_work() finishes if an + * operation is already in progress. + * + * This function solves a real-life problem observed on NOR flashes when an + * PEB erase operation starts, then the system is rebooted before the erase is + * finishes, and the boot loader gets confused and dies. So we prefer to finish + * the ongoing operation before rebooting. + */ +static int ubi_reboot_notifier(struct notifier_block *n, unsigned long state, + void *cmd) +{ + struct ubi_device *ubi; + + ubi = container_of(n, struct ubi_device, reboot_notifier); + if (ubi->bgt_thread) + kthread_stop(ubi->bgt_thread); + ubi_sync(ubi->ubi_num); + return NOTIFY_DONE; +} + /** * ubi_attach_mtd_dev - attach an MTD device. * @mtd: MTD device description object @@ -978,6 +1007,11 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) wake_up_process(ubi->bgt_thread); spin_unlock(&ubi->wl_lock); + /* Flash device priority is 0 - UBI needs to shut down first */ + ubi->reboot_notifier.priority = 1; + ubi->reboot_notifier.notifier_call = ubi_reboot_notifier; + register_reboot_notifier(&ubi->reboot_notifier); + ubi_devices[ubi_num] = ubi; ubi_notify_all(ubi, UBI_VOLUME_ADDED, NULL); return ubi_num; @@ -1049,6 +1083,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) * Before freeing anything, we have to stop the background thread to * prevent it from doing anything on this device while we are freeing. */ + unregister_reboot_notifier(&ubi->reboot_notifier); if (ubi->bgt_thread) kthread_stop(ubi->bgt_thread); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 70ce48b95b6..28acd133c99 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -36,9 +36,9 @@ #include #include #include +#include #include #include -#include #include "ubi-media.h" #include "scan.h" @@ -348,6 +348,7 @@ struct ubi_wl_entry; * @bgt_thread: background thread description object * @thread_enabled: if the background thread is enabled * @bgt_name: background thread name + * @reboot_notifier: notifier to terminate background thread before rebooting * * @flash_size: underlying MTD device size (in bytes) * @peb_count: count of physical eraseblocks on the MTD device @@ -431,6 +432,7 @@ struct ubi_device { struct task_struct *bgt_thread; int thread_enabled; char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; + struct notifier_block reboot_notifier; /* I/O sub-system's stuff */ long long flash_size; -- cgit v1.2.3