diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/ubi/block.c | 247 | ||||
-rw-r--r-- | drivers/mtd/ubi/build.c | 6 | ||||
-rw-r--r-- | drivers/mtd/ubi/cdev.c | 19 | ||||
-rw-r--r-- | drivers/mtd/ubi/eba.c | 56 | ||||
-rw-r--r-- | drivers/mtd/ubi/fastmap.c | 13 | ||||
-rw-r--r-- | drivers/mtd/ubi/io.c | 3 | ||||
-rw-r--r-- | drivers/mtd/ubi/kapi.c | 110 | ||||
-rw-r--r-- | drivers/mtd/ubi/misc.c | 2 | ||||
-rw-r--r-- | drivers/mtd/ubi/ubi.h | 19 | ||||
-rw-r--r-- | drivers/mtd/ubi/vtbl.c | 7 | ||||
-rw-r--r-- | drivers/mtd/ubi/wl.c | 10 |
11 files changed, 319 insertions, 173 deletions
diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c index 6b6bce28bd63..db2c05b6fe7f 100644 --- a/drivers/mtd/ubi/block.c +++ b/drivers/mtd/ubi/block.c @@ -42,11 +42,12 @@ #include <linux/list.h> #include <linux/mutex.h> #include <linux/slab.h> -#include <linux/vmalloc.h> #include <linux/mtd/ubi.h> #include <linux/workqueue.h> #include <linux/blkdev.h> +#include <linux/blk-mq.h> #include <linux/hdreg.h> +#include <linux/scatterlist.h> #include <asm/div64.h> #include "ubi-media.h" @@ -67,6 +68,11 @@ struct ubiblock_param { char name[UBIBLOCK_PARAM_LEN+1]; }; +struct ubiblock_pdu { + struct work_struct work; + struct ubi_sgl usgl; +}; + /* Numbers of elements set in the @ubiblock_param array */ static int ubiblock_devs __initdata; @@ -84,11 +90,10 @@ struct ubiblock { struct request_queue *rq; struct workqueue_struct *wq; - struct work_struct work; struct mutex dev_mutex; - spinlock_t queue_lock; struct list_head list; + struct blk_mq_tag_set tag_set; }; /* Linked list of all ubiblock instances */ @@ -181,31 +186,20 @@ static struct ubiblock *find_dev_nolock(int ubi_num, int vol_id) return NULL; } -static int ubiblock_read_to_buf(struct ubiblock *dev, char *buffer, - int leb, int offset, int len) +static int ubiblock_read(struct ubiblock_pdu *pdu) { - int ret; - - ret = ubi_read(dev->desc, leb, buffer, offset, len); - if (ret) { - dev_err(disk_to_dev(dev->gd), "%d while reading from LEB %d (offset %d, length %d)", - ret, leb, offset, len); - return ret; - } - return 0; -} + int ret, leb, offset, bytes_left, to_read; + u64 pos; + struct request *req = blk_mq_rq_from_pdu(pdu); + struct ubiblock *dev = req->q->queuedata; -static int ubiblock_read(struct ubiblock *dev, char *buffer, - sector_t sec, int len) -{ - int ret, leb, offset; - int bytes_left = len; - int to_read = len; - u64 pos = sec << 9; + to_read = blk_rq_bytes(req); + pos = blk_rq_pos(req) << 9; /* Get LEB:offset address to read from */ offset = do_div(pos, dev->leb_size); leb = pos; + bytes_left = to_read; while (bytes_left) { /* @@ -215,11 +209,10 @@ static int ubiblock_read(struct ubiblock *dev, char *buffer, if (offset + to_read > dev->leb_size) to_read = dev->leb_size - offset; - ret = ubiblock_read_to_buf(dev, buffer, leb, offset, to_read); - if (ret) + ret = ubi_read_sg(dev->desc, leb, &pdu->usgl, offset, to_read); + if (ret < 0) return ret; - buffer += to_read; bytes_left -= to_read; to_read = bytes_left; leb += 1; @@ -228,79 +221,6 @@ static int ubiblock_read(struct ubiblock *dev, char *buffer, return 0; } -static int do_ubiblock_request(struct ubiblock *dev, struct request *req) -{ - int len, ret; - sector_t sec; - - if (req->cmd_type != REQ_TYPE_FS) - return -EIO; - - if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > - get_capacity(req->rq_disk)) - return -EIO; - - if (rq_data_dir(req) != READ) - return -ENOSYS; /* Write not implemented */ - - sec = blk_rq_pos(req); - len = blk_rq_cur_bytes(req); - - /* - * Let's prevent the device from being removed while we're doing I/O - * work. Notice that this means we serialize all the I/O operations, - * but it's probably of no impact given the NAND core serializes - * flash access anyway. - */ - mutex_lock(&dev->dev_mutex); - ret = ubiblock_read(dev, bio_data(req->bio), sec, len); - mutex_unlock(&dev->dev_mutex); - - return ret; -} - -static void ubiblock_do_work(struct work_struct *work) -{ - struct ubiblock *dev = - container_of(work, struct ubiblock, work); - struct request_queue *rq = dev->rq; - struct request *req; - int res; - - spin_lock_irq(rq->queue_lock); - - req = blk_fetch_request(rq); - while (req) { - - spin_unlock_irq(rq->queue_lock); - res = do_ubiblock_request(dev, req); - spin_lock_irq(rq->queue_lock); - - /* - * If we're done with this request, - * we need to fetch a new one - */ - if (!__blk_end_request_cur(req, res)) - req = blk_fetch_request(rq); - } - - spin_unlock_irq(rq->queue_lock); -} - -static void ubiblock_request(struct request_queue *rq) -{ - struct ubiblock *dev; - struct request *req; - - dev = rq->queuedata; - - if (!dev) - while ((req = blk_fetch_request(rq)) != NULL) - __blk_end_request_all(req, -ENODEV); - else - queue_work(dev->wq, &dev->work); -} - static int ubiblock_open(struct block_device *bdev, fmode_t mode) { struct ubiblock *dev = bdev->bd_disk->private_data; @@ -374,6 +294,63 @@ static const struct block_device_operations ubiblock_ops = { .getgeo = ubiblock_getgeo, }; +static void ubiblock_do_work(struct work_struct *work) +{ + int ret; + struct ubiblock_pdu *pdu = container_of(work, struct ubiblock_pdu, work); + struct request *req = blk_mq_rq_from_pdu(pdu); + + blk_mq_start_request(req); + + /* + * It is safe to ignore the return value of blk_rq_map_sg() because + * the number of sg entries is limited to UBI_MAX_SG_COUNT + * and ubi_read_sg() will check that limit. + */ + blk_rq_map_sg(req->q, req, pdu->usgl.sg); + + ret = ubiblock_read(pdu); + blk_mq_end_request(req, ret); +} + +static int ubiblock_queue_rq(struct blk_mq_hw_ctx *hctx, + const struct blk_mq_queue_data *bd) +{ + struct request *req = bd->rq; + struct ubiblock *dev = hctx->queue->queuedata; + struct ubiblock_pdu *pdu = blk_mq_rq_to_pdu(req); + + if (req->cmd_type != REQ_TYPE_FS) + return BLK_MQ_RQ_QUEUE_ERROR; + + if (rq_data_dir(req) != READ) + return BLK_MQ_RQ_QUEUE_ERROR; /* Write not implemented */ + + ubi_sgl_init(&pdu->usgl); + queue_work(dev->wq, &pdu->work); + + return BLK_MQ_RQ_QUEUE_OK; +} + +static int ubiblock_init_request(void *data, struct request *req, + unsigned int hctx_idx, + unsigned int request_idx, + unsigned int numa_node) +{ + struct ubiblock_pdu *pdu = blk_mq_rq_to_pdu(req); + + sg_init_table(pdu->usgl.sg, UBI_MAX_SG_COUNT); + INIT_WORK(&pdu->work, ubiblock_do_work); + + return 0; +} + +static struct blk_mq_ops ubiblock_mq_ops = { + .queue_rq = ubiblock_queue_rq, + .init_request = ubiblock_init_request, + .map_queue = blk_mq_map_queue, +}; + int ubiblock_create(struct ubi_volume_info *vi) { struct ubiblock *dev; @@ -417,14 +394,28 @@ int ubiblock_create(struct ubi_volume_info *vi) set_capacity(gd, disk_capacity); dev->gd = gd; - spin_lock_init(&dev->queue_lock); - dev->rq = blk_init_queue(ubiblock_request, &dev->queue_lock); - if (!dev->rq) { - dev_err(disk_to_dev(gd), "blk_init_queue failed"); - ret = -ENODEV; + dev->tag_set.ops = &ubiblock_mq_ops; + dev->tag_set.queue_depth = 64; + dev->tag_set.numa_node = NUMA_NO_NODE; + dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; + dev->tag_set.cmd_size = sizeof(struct ubiblock_pdu); + dev->tag_set.driver_data = dev; + dev->tag_set.nr_hw_queues = 1; + + ret = blk_mq_alloc_tag_set(&dev->tag_set); + if (ret) { + dev_err(disk_to_dev(dev->gd), "blk_mq_alloc_tag_set failed"); goto out_put_disk; } + dev->rq = blk_mq_init_queue(&dev->tag_set); + if (IS_ERR(dev->rq)) { + dev_err(disk_to_dev(gd), "blk_mq_init_queue failed"); + ret = PTR_ERR(dev->rq); + goto out_free_tags; + } + blk_queue_max_segments(dev->rq, UBI_MAX_SG_COUNT); + dev->rq->queuedata = dev; dev->gd->queue = dev->rq; @@ -437,7 +428,6 @@ int ubiblock_create(struct ubi_volume_info *vi) ret = -ENOMEM; goto out_free_queue; } - INIT_WORK(&dev->work, ubiblock_do_work); mutex_lock(&devices_mutex); list_add_tail(&dev->list, &ubiblock_devices); @@ -451,6 +441,8 @@ int ubiblock_create(struct ubi_volume_info *vi) out_free_queue: blk_cleanup_queue(dev->rq); +out_free_tags: + blk_mq_free_tag_set(&dev->tag_set); out_put_disk: put_disk(dev->gd); out_free_dev: @@ -461,8 +453,13 @@ out_free_dev: static void ubiblock_cleanup(struct ubiblock *dev) { + /* Stop new requests to arrive */ del_gendisk(dev->gd); + /* Flush pending work */ + destroy_workqueue(dev->wq); + /* Finally destroy the blk queue */ blk_cleanup_queue(dev->rq); + blk_mq_free_tag_set(&dev->tag_set); dev_info(disk_to_dev(dev->gd), "released"); put_disk(dev->gd); } @@ -490,9 +487,6 @@ int ubiblock_remove(struct ubi_volume_info *vi) list_del(&dev->list); mutex_unlock(&devices_mutex); - /* Flush pending work and stop this workqueue */ - destroy_workqueue(dev->wq); - ubiblock_cleanup(dev); mutex_unlock(&dev->dev_mutex); kfree(dev); @@ -583,22 +577,28 @@ open_volume_desc(const char *name, int ubi_num, int vol_id) return ubi_open_volume(ubi_num, vol_id, UBI_READONLY); } -static int __init ubiblock_create_from_param(void) +static void __init ubiblock_create_from_param(void) { - int i, ret; + int i, ret = 0; struct ubiblock_param *p; struct ubi_volume_desc *desc; struct ubi_volume_info vi; + /* + * If there is an error creating one of the ubiblocks, continue on to + * create the following ubiblocks. This helps in a circumstance where + * the kernel command-line specifies multiple block devices and some + * may be broken, but we still want the working ones to come up. + */ for (i = 0; i < ubiblock_devs; i++) { p = &ubiblock_param[i]; desc = open_volume_desc(p->name, p->ubi_num, p->vol_id); if (IS_ERR(desc)) { - pr_err("UBI: block: can't open volume, err=%ld\n", - PTR_ERR(desc)); - ret = PTR_ERR(desc); - break; + pr_err( + "UBI: block: can't open volume on ubi%d_%d, err=%ld", + p->ubi_num, p->vol_id, PTR_ERR(desc)); + continue; } ubi_get_volume_info(desc, &vi); @@ -606,12 +606,12 @@ static int __init ubiblock_create_from_param(void) ret = ubiblock_create(&vi); if (ret) { - pr_err("UBI: block: can't add '%s' volume, err=%d\n", - vi.name, ret); - break; + pr_err( + "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d", + vi.name, p->ubi_num, p->vol_id, ret); + continue; } } - return ret; } static void ubiblock_remove_all(void) @@ -620,8 +620,6 @@ static void ubiblock_remove_all(void) struct ubiblock *dev; list_for_each_entry_safe(dev, next, &ubiblock_devices, list) { - /* Flush pending work and stop workqueue */ - destroy_workqueue(dev->wq); /* The module is being forcefully removed */ WARN_ON(dev->desc); /* Remove from device list */ @@ -639,10 +637,12 @@ int __init ubiblock_init(void) if (ubiblock_major < 0) return ubiblock_major; - /* Attach block devices from 'block=' module param */ - ret = ubiblock_create_from_param(); - if (ret) - goto err_remove; + /* + * Attach block devices from 'block=' module param. + * Even if one block device in the param list fails to come up, + * still allow the module to load and leave any others up. + */ + ubiblock_create_from_param(); /* * Block devices are only created upon user requests, so we ignore @@ -655,7 +655,6 @@ int __init ubiblock_init(void) err_unreg: unregister_blkdev(ubiblock_major, "ubiblock"); -err_remove: ubiblock_remove_all(); return ret; } diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 3405be46ebe9..ba01a8d22d28 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -923,7 +923,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, /* Make sure ubi_num is not busy */ if (ubi_devices[ubi_num]) { - ubi_err(ubi, "ubi%d already exists", ubi_num); + ubi_err(ubi, "already exists"); return -EEXIST; } } @@ -973,7 +973,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, mutex_init(&ubi->fm_mutex); init_rwsem(&ubi->fm_sem); - ubi_msg(ubi, "attaching mtd%d to ubi%d", mtd->index, ubi_num); + ubi_msg(ubi, "attaching mtd%d", mtd->index); err = io_init(ubi, max_beb_per1024); if (err) @@ -1428,7 +1428,7 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) } if (len == 0) { - pr_err("UBI warning: empty 'mtd=' parameter - ignored\n"); + pr_warn("UBI warning: empty 'mtd=' parameter - ignored\n"); return 0; } diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 3410ea8109f8..d647e504f9b1 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -48,26 +48,25 @@ /** * get_exclusive - get exclusive access to an UBI volume. - * @ubi: UBI device description object * @desc: volume descriptor * * This function changes UBI volume open mode to "exclusive". Returns previous * mode value (positive integer) in case of success and a negative error code * in case of failure. */ -static int get_exclusive(struct ubi_device *ubi, struct ubi_volume_desc *desc) +static int get_exclusive(struct ubi_volume_desc *desc) { int users, err; struct ubi_volume *vol = desc->vol; spin_lock(&vol->ubi->volumes_lock); - users = vol->readers + vol->writers + vol->exclusive; + users = vol->readers + vol->writers + vol->exclusive + vol->metaonly; ubi_assert(users > 0); if (users > 1) { - ubi_err(ubi, "%d users for volume %d", users, vol->vol_id); + ubi_err(vol->ubi, "%d users for volume %d", users, vol->vol_id); err = -EBUSY; } else { - vol->readers = vol->writers = 0; + vol->readers = vol->writers = vol->metaonly = 0; vol->exclusive = 1; err = desc->mode; desc->mode = UBI_EXCLUSIVE; @@ -87,13 +86,15 @@ static void revoke_exclusive(struct ubi_volume_desc *desc, int mode) struct ubi_volume *vol = desc->vol; spin_lock(&vol->ubi->volumes_lock); - ubi_assert(vol->readers == 0 && vol->writers == 0); + ubi_assert(vol->readers == 0 && vol->writers == 0 && vol->metaonly == 0); ubi_assert(vol->exclusive == 1 && desc->mode == UBI_EXCLUSIVE); vol->exclusive = 0; if (mode == UBI_READONLY) vol->readers = 1; else if (mode == UBI_READWRITE) vol->writers = 1; + else if (mode == UBI_METAONLY) + vol->metaonly = 1; else vol->exclusive = 1; spin_unlock(&vol->ubi->volumes_lock); @@ -421,7 +422,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, break; } - err = get_exclusive(ubi, desc); + err = get_exclusive(desc); if (err < 0) break; @@ -457,7 +458,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, req.bytes < 0 || req.lnum >= vol->usable_leb_size) break; - err = get_exclusive(ubi, desc); + err = get_exclusive(desc); if (err < 0) break; @@ -734,7 +735,7 @@ static int rename_volumes(struct ubi_device *ubi, goto out_free; } - re->desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_READWRITE); + re->desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_METAONLY); if (IS_ERR(re->desc)) { err = PTR_ERR(re->desc); ubi_err(ubi, "cannot open volume %d, error %d", diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index a40020cf0923..da4c79259f67 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -426,6 +426,7 @@ retry: pnum, vol_id, lnum); err = -EBADMSG; } else + err = -EINVAL; ubi_ro_mode(ubi); } goto out_free; @@ -480,6 +481,61 @@ out_unlock: } /** + * ubi_eba_read_leb_sg - read data into a scatter gather list. + * @ubi: UBI device description object + * @vol: volume description object + * @lnum: logical eraseblock number + * @sgl: UBI scatter gather list to store the read data + * @offset: offset from where to read + * @len: how many bytes to read + * @check: data CRC check flag + * + * This function works exactly like ubi_eba_read_leb(). But instead of + * storing the read data into a buffer it writes to an UBI scatter gather + * list. + */ +int ubi_eba_read_leb_sg(struct ubi_device *ubi, struct ubi_volume *vol, + struct ubi_sgl *sgl, int lnum, int offset, int len, + int check) +{ + int to_read; + int ret; + struct scatterlist *sg; + + for (;;) { + ubi_assert(sgl->list_pos < UBI_MAX_SG_COUNT); + sg = &sgl->sg[sgl->list_pos]; + if (len < sg->length - sgl->page_pos) + to_read = len; + else + to_read = sg->length - sgl->page_pos; + + ret = ubi_eba_read_leb(ubi, vol, lnum, + sg_virt(sg) + sgl->page_pos, offset, + to_read, check); + if (ret < 0) + return ret; + + offset += to_read; + len -= to_read; + if (!len) { + sgl->page_pos += to_read; + if (sgl->page_pos == sg->length) { + sgl->list_pos++; + sgl->page_pos = 0; + } + + break; + } + + sgl->list_pos++; + sgl->page_pos = 0; + } + + return ret; +} + +/** * recover_peb - recover from write failure. * @ubi: UBI device description object * @pnum: the physical eraseblock to recover diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c index b56672bf3294..db3defdfc3c0 100644 --- a/drivers/mtd/ubi/fastmap.c +++ b/drivers/mtd/ubi/fastmap.c @@ -1196,6 +1196,19 @@ static int ubi_write_fastmap(struct ubi_device *ubi, fm_pos += sizeof(*fec); ubi_assert(fm_pos <= ubi->fm_size); } + + for (i = 0; i < UBI_PROT_QUEUE_LEN; i++) { + list_for_each_entry(wl_e, &ubi->pq[i], u.list) { + fec = (struct ubi_fm_ec *)(fm_raw + fm_pos); + + fec->pnum = cpu_to_be32(wl_e->pnum); + fec->ec = cpu_to_be32(wl_e->ec); + + used_peb_count++; + fm_pos += sizeof(*fec); + ubi_assert(fm_pos <= ubi->fm_size); + } + } fmh->used_peb_count = cpu_to_be32(used_peb_count); for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) { diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 396aaa543362..ed0bcb35472f 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -1419,8 +1419,7 @@ int ubi_self_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len) fail: ubi_err(ubi, "self-check failed for PEB %d", pnum); - ubi_msg(ubi, "hex dump of the %d-%d region", - offset, offset + len); + ubi_msg(ubi, "hex dump of the %d-%d region", offset, offset + len); print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, buf, len, 1); err = -EINVAL; error: diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index f3bab669f6bb..478e00cf2d9e 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -137,7 +137,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) return ERR_PTR(-EINVAL); if (mode != UBI_READONLY && mode != UBI_READWRITE && - mode != UBI_EXCLUSIVE) + mode != UBI_EXCLUSIVE && mode != UBI_METAONLY) return ERR_PTR(-EINVAL); /* @@ -182,10 +182,17 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) break; case UBI_EXCLUSIVE: - if (vol->exclusive || vol->writers || vol->readers) + if (vol->exclusive || vol->writers || vol->readers || + vol->metaonly) goto out_unlock; vol->exclusive = 1; break; + + case UBI_METAONLY: + if (vol->metaonly || vol->exclusive) + goto out_unlock; + vol->metaonly = 1; + break; } get_device(&vol->dev); vol->ref_count += 1; @@ -343,6 +350,10 @@ void ubi_close_volume(struct ubi_volume_desc *desc) break; case UBI_EXCLUSIVE: vol->exclusive = 0; + break; + case UBI_METAONLY: + vol->metaonly = 0; + break; } vol->ref_count -= 1; spin_unlock(&ubi->volumes_lock); @@ -355,6 +366,43 @@ void ubi_close_volume(struct ubi_volume_desc *desc) EXPORT_SYMBOL_GPL(ubi_close_volume); /** + * leb_read_sanity_check - does sanity checks on read requests. + * @desc: volume descriptor + * @lnum: logical eraseblock number to read from + * @offset: offset within the logical eraseblock to read from + * @len: how many bytes to read + * + * This function is used by ubi_leb_read() and ubi_leb_read_sg() + * to perform sanity checks. + */ +static int leb_read_sanity_check(struct ubi_volume_desc *desc, int lnum, + int offset, int len) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int vol_id = vol->vol_id; + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 || + lnum >= vol->used_ebs || offset < 0 || len < 0 || + offset + len > vol->usable_leb_size) + return -EINVAL; + + if (vol->vol_type == UBI_STATIC_VOLUME) { + if (vol->used_ebs == 0) + /* Empty static UBI volume */ + return 0; + if (lnum == vol->used_ebs - 1 && + offset + len > vol->last_eb_bytes) + return -EINVAL; + } + + if (vol->upd_marker) + return -EBADF; + + return 0; +} + +/** * ubi_leb_read - read data. * @desc: volume descriptor * @lnum: logical eraseblock number to read from @@ -390,22 +438,10 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, dbg_gen("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset); - if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 || - lnum >= vol->used_ebs || offset < 0 || len < 0 || - offset + len > vol->usable_leb_size) - return -EINVAL; - - if (vol->vol_type == UBI_STATIC_VOLUME) { - if (vol->used_ebs == 0) - /* Empty static UBI volume */ - return 0; - if (lnum == vol->used_ebs - 1 && - offset + len > vol->last_eb_bytes) - return -EINVAL; - } + err = leb_read_sanity_check(desc, lnum, offset, len); + if (err < 0) + return err; - if (vol->upd_marker) - return -EBADF; if (len == 0) return 0; @@ -419,6 +455,46 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, } EXPORT_SYMBOL_GPL(ubi_leb_read); + +/** + * ubi_leb_read_sg - read data into a scatter gather list. + * @desc: volume descriptor + * @lnum: logical eraseblock number to read from + * @buf: buffer where to store the read data + * @offset: offset within the logical eraseblock to read from + * @len: how many bytes to read + * @check: whether UBI has to check the read data's CRC or not. + * + * This function works exactly like ubi_leb_read_sg(). But instead of + * storing the read data into a buffer it writes to an UBI scatter gather + * list. + */ +int ubi_leb_read_sg(struct ubi_volume_desc *desc, int lnum, struct ubi_sgl *sgl, + int offset, int len, int check) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int err, vol_id = vol->vol_id; + + dbg_gen("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset); + + err = leb_read_sanity_check(desc, lnum, offset, len); + if (err < 0) + return err; + + if (len == 0) + return 0; + + err = ubi_eba_read_leb_sg(ubi, vol, sgl, lnum, offset, len, check); + if (err && mtd_is_eccerr(err) && vol->vol_type == UBI_STATIC_VOLUME) { + ubi_warn(ubi, "mark volume %d as corrupted", vol_id); + vol->corrupted = 1; + } + + return err; +} +EXPORT_SYMBOL_GPL(ubi_leb_read_sg); + /** * ubi_leb_write - write data. * @desc: volume descriptor diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c index dbda77e556cb..2a45ac210b16 100644 --- a/drivers/mtd/ubi/misc.c +++ b/drivers/mtd/ubi/misc.c @@ -74,6 +74,8 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id) for (i = 0; i < vol->used_ebs; i++) { int size; + cond_resched(); + if (i == vol->used_ebs - 1) size = vol->last_eb_bytes; else diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index f80ffaba9058..c5be82d9d345 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -50,13 +50,13 @@ #define UBI_NAME_STR "ubi" /* Normal UBI messages */ -#define ubi_msg(ubi, fmt, ...) pr_notice("UBI-%d: %s:" fmt "\n", \ - ubi->ubi_num, __func__, ##__VA_ARGS__) +#define ubi_msg(ubi, fmt, ...) pr_notice(UBI_NAME_STR "%d: " fmt "\n", \ + ubi->ubi_num, ##__VA_ARGS__) /* UBI warning messages */ -#define ubi_warn(ubi, fmt, ...) pr_warn("UBI-%d warning: %s: " fmt "\n", \ +#define ubi_warn(ubi, fmt, ...) pr_warn(UBI_NAME_STR "%d warning: %s: " fmt "\n", \ ubi->ubi_num, __func__, ##__VA_ARGS__) /* UBI error messages */ -#define ubi_err(ubi, fmt, ...) pr_err("UBI-%d error: %s: " fmt "\n", \ +#define ubi_err(ubi, fmt, ...) pr_err(UBI_NAME_STR "%d error: %s: " fmt "\n", \ ubi->ubi_num, __func__, ##__VA_ARGS__) /* Background thread name pattern */ @@ -261,6 +261,7 @@ struct ubi_fm_pool { * @readers: number of users holding this volume in read-only mode * @writers: number of users holding this volume in read-write mode * @exclusive: whether somebody holds this volume in exclusive mode + * @metaonly: whether somebody is altering only meta data of this volume * * @reserved_pebs: how many physical eraseblocks are reserved for this volume * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) @@ -309,6 +310,7 @@ struct ubi_volume { int readers; int writers; int exclusive; + int metaonly; int reserved_pebs; int vol_type; @@ -339,7 +341,8 @@ struct ubi_volume { /** * struct ubi_volume_desc - UBI volume descriptor returned when it is opened. * @vol: reference to the corresponding volume description object - * @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE) + * @mode: open mode (%UBI_READONLY, %UBI_READWRITE, %UBI_EXCLUSIVE + * or %UBI_METAONLY) */ struct ubi_volume_desc { struct ubi_volume *vol; @@ -390,7 +393,8 @@ struct ubi_debug_info { * @volumes_lock: protects @volumes, @rsvd_pebs, @avail_pebs, beb_rsvd_pebs, * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, * @vol->readers, @vol->writers, @vol->exclusive, - * @vol->ref_count, @vol->mapping and @vol->eba_tbl. + * @vol->metaonly, @vol->ref_count, @vol->mapping and + * @vol->eba_tbl. * @ref_count: count of references on the UBI device * @image_seq: image sequence number recorded on EC headers * @@ -791,6 +795,9 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum); int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, void *buf, int offset, int len, int check); +int ubi_eba_read_leb_sg(struct ubi_device *ubi, struct ubi_volume *vol, + struct ubi_sgl *sgl, int lnum, int offset, int len, + int check); int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, const void *buf, int offset, int len); int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index f8fc3081bbb4..68c9c5ea676f 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -655,14 +655,13 @@ static int init_volumes(struct ubi_device *ubi, /** * check_av - check volume attaching information. - * @ubi: UBI device description object * @vol: UBI volume description object * @av: volume attaching information * * This function returns zero if the volume attaching information is consistent * to the data read from the volume tabla, and %-EINVAL if not. */ -static int check_av(const struct ubi_device *ubi, const struct ubi_volume *vol, +static int check_av(const struct ubi_volume *vol, const struct ubi_ainf_volume *av) { int err; @@ -690,7 +689,7 @@ static int check_av(const struct ubi_device *ubi, const struct ubi_volume *vol, return 0; bad: - ubi_err(ubi, "bad attaching information, error %d", err); + ubi_err(vol->ubi, "bad attaching information, error %d", err); ubi_dump_av(av); ubi_dump_vol_info(vol); return -EINVAL; @@ -753,7 +752,7 @@ static int check_attaching_info(const struct ubi_device *ubi, ubi_msg(ubi, "finish volume %d removal", av->vol_id); ubi_remove_av(ai, av); } else if (av) { - err = check_av(ubi, vol, av); + err = check_av(vol, av); if (err) return err; } diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 834f6fe1f5fa..8f7bde6a85d6 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -470,11 +470,8 @@ struct ubi_wl_entry *ubi_wl_get_fm_peb(struct ubi_device *ubi, int anchor) { struct ubi_wl_entry *e = NULL; - if (!ubi->free.rb_node || (ubi->free_count - ubi->beb_rsvd_pebs < 1)) { - ubi_warn(ubi, "Can't get peb for fastmap:anchor=%d, free_cnt=%d, reserved=%d", - anchor, ubi->free_count, ubi->beb_rsvd_pebs); + if (!ubi->free.rb_node || (ubi->free_count - ubi->beb_rsvd_pebs < 1)) goto out; - } if (anchor) e = find_anchor_wl_entry(&ubi->free); @@ -1806,11 +1803,8 @@ int ubi_thread(void *u) for (;;) { int err; - if (kthread_should_stop()) { - ubi_msg(ubi, "background thread \"%s\" should stop, PID %d", - ubi->bgt_name, task_pid_nr(current)); + if (kthread_should_stop()) break; - } if (try_to_freeze()) continue; |