diff options
-rw-r--r-- | MAINTAINERS | 9 | ||||
-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 | ||||
-rw-r--r-- | fs/ubifs/debug.c | 4 | ||||
-rw-r--r-- | fs/ubifs/dir.c | 16 | ||||
-rw-r--r-- | fs/ubifs/file.c | 4 | ||||
-rw-r--r-- | fs/ubifs/replay.c | 19 | ||||
-rw-r--r-- | fs/ubifs/super.c | 1 | ||||
-rw-r--r-- | fs/ubifs/ubifs.h | 4 | ||||
-rw-r--r-- | fs/ubifs/xattr.c | 112 | ||||
-rw-r--r-- | include/linux/mtd/ubi.h | 53 |
20 files changed, 521 insertions, 193 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 3ac697a64280..1c7e321f77b8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9990,20 +9990,15 @@ F: drivers/scsi/ufs/ UNSORTED BLOCK IMAGES (UBI) M: Artem Bityutskiy <dedekind1@gmail.com> +M: Richard Weinberger <richard@nod.at> W: http://www.linux-mtd.infradead.org/ L: linux-mtd@lists.infradead.org T: git git://git.infradead.org/ubifs-2.6.git -S: Maintained +S: Supported F: drivers/mtd/ubi/ F: include/linux/mtd/ubi.h F: include/uapi/mtd/ubi-user.h -UNSORTED BLOCK IMAGES (UBI) Fastmap -M: Richard Weinberger <richard@nod.at> -L: linux-mtd@lists.infradead.org -S: Maintained -F: drivers/mtd/ubi/fastmap.c - USB ACM DRIVER M: Oliver Neukum <oliver@neukum.org> L: linux-usb@vger.kernel.org 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; diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c index 7ed13e1e216a..4cfb3e82c56f 100644 --- a/fs/ubifs/debug.c +++ b/fs/ubifs/debug.c @@ -2032,6 +2032,8 @@ static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr, long long blk_offs; struct ubifs_data_node *dn = node; + ubifs_assert(zbr->len >= UBIFS_DATA_NODE_SZ); + /* * Search the inode node this data node belongs to and insert * it to the RB-tree of inodes. @@ -2060,6 +2062,8 @@ static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr, struct ubifs_dent_node *dent = node; struct fsck_inode *fscki1; + ubifs_assert(zbr->len >= UBIFS_DENT_NODE_SZ); + err = ubifs_validate_entry(c, dent); if (err) goto out_dump; diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index c49b1981ac95..0fa6c803992e 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -270,6 +270,10 @@ static int ubifs_create(struct inode *dir, struct dentry *dentry, umode_t mode, goto out_budg; } + err = ubifs_init_security(dir, inode, &dentry->d_name); + if (err) + goto out_cancel; + mutex_lock(&dir_ui->ui_mutex); dir->i_size += sz_change; dir_ui->ui_size = dir->i_size; @@ -726,6 +730,10 @@ static int ubifs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) goto out_budg; } + err = ubifs_init_security(dir, inode, &dentry->d_name); + if (err) + goto out_cancel; + mutex_lock(&dir_ui->ui_mutex); insert_inode_hash(inode); inc_nlink(inode); @@ -806,6 +814,10 @@ static int ubifs_mknod(struct inode *dir, struct dentry *dentry, ui->data = dev; ui->data_len = devlen; + err = ubifs_init_security(dir, inode, &dentry->d_name); + if (err) + goto out_cancel; + mutex_lock(&dir_ui->ui_mutex); dir->i_size += sz_change; dir_ui->ui_size = dir->i_size; @@ -882,6 +894,10 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry, ui->data_len = len; inode->i_size = ubifs_inode(inode)->ui_size = len; + err = ubifs_init_security(dir, inode, &dentry->d_name); + if (err) + goto out_cancel; + mutex_lock(&dir_ui->ui_mutex); dir->i_size += sz_change; dir_ui->ui_size = dir->i_size; diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 035e51011444..e627c0acf626 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -1573,6 +1573,10 @@ const struct inode_operations ubifs_symlink_inode_operations = { .follow_link = ubifs_follow_link, .setattr = ubifs_setattr, .getattr = ubifs_getattr, + .setxattr = ubifs_setxattr, + .getxattr = ubifs_getxattr, + .listxattr = ubifs_listxattr, + .removexattr = ubifs_removexattr, }; const struct file_operations ubifs_file_operations = { diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index 3187925e9879..9b40a1c5e160 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c @@ -1028,9 +1028,22 @@ int ubifs_replay_journal(struct ubifs_info *c) do { err = replay_log_leb(c, lnum, 0, c->sbuf); - if (err == 1) - /* We hit the end of the log */ - break; + if (err == 1) { + if (lnum != c->lhead_lnum) + /* We hit the end of the log */ + break; + + /* + * The head of the log must always start with the + * "commit start" node on a properly formatted UBIFS. + * But we found no nodes at all, which means that + * someting went wrong and we cannot proceed mounting + * the file-system. + */ + ubifs_err("no UBIFS nodes found at the log head LEB %d:%d, possibly corrupted", + lnum, 0); + err = -EINVAL; + } if (err) goto out; lnum = ubifs_next_log_lnum(c, lnum); diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 6197154f36ca..93e946561c5c 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -2036,6 +2036,7 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent) if (c->max_inode_sz > MAX_LFS_FILESIZE) sb->s_maxbytes = c->max_inode_sz = MAX_LFS_FILESIZE; sb->s_op = &ubifs_super_operations; + sb->s_xattr = ubifs_xattr_handlers; mutex_lock(&c->umount_mutex); err = mount_ubifs(c); diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index c4fe900c67ab..bc04b9c69891 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -36,6 +36,7 @@ #include <linux/mtd/ubi.h> #include <linux/pagemap.h> #include <linux/backing-dev.h> +#include <linux/security.h> #include "ubifs-media.h" /* Version of this UBIFS implementation */ @@ -1465,6 +1466,7 @@ extern spinlock_t ubifs_infos_lock; extern atomic_long_t ubifs_clean_zn_cnt; extern struct kmem_cache *ubifs_inode_slab; extern const struct super_operations ubifs_super_operations; +extern const struct xattr_handler *ubifs_xattr_handlers[]; extern const struct address_space_operations ubifs_file_address_operations; extern const struct file_operations ubifs_file_operations; extern const struct inode_operations ubifs_file_inode_operations; @@ -1754,6 +1756,8 @@ ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf, size_t size); ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size); int ubifs_removexattr(struct dentry *dentry, const char *name); +int ubifs_init_security(struct inode *dentry, struct inode *inode, + const struct qstr *qstr); /* super.c */ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum); diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c index 5e0a63b1b0d5..a92be244a6fb 100644 --- a/fs/ubifs/xattr.c +++ b/fs/ubifs/xattr.c @@ -100,24 +100,30 @@ static const struct file_operations empty_fops; static int create_xattr(struct ubifs_info *c, struct inode *host, const struct qstr *nm, const void *value, int size) { - int err; + int err, names_len; struct inode *inode; struct ubifs_inode *ui, *host_ui = ubifs_inode(host); struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1, .new_ino_d = ALIGN(size, 8), .dirtied_ino = 1, .dirtied_ino_d = ALIGN(host_ui->data_len, 8) }; - if (host_ui->xattr_cnt >= MAX_XATTRS_PER_INODE) + if (host_ui->xattr_cnt >= MAX_XATTRS_PER_INODE) { + ubifs_err("inode %lu already has too many xattrs (%d), cannot create more", + host->i_ino, host_ui->xattr_cnt); return -ENOSPC; + } /* * Linux limits the maximum size of the extended attribute names list * to %XATTR_LIST_MAX. This means we should not allow creating more * extended attributes if the name list becomes larger. This limitation * is artificial for UBIFS, though. */ - if (host_ui->xattr_names + host_ui->xattr_cnt + - nm->len + 1 > XATTR_LIST_MAX) + names_len = host_ui->xattr_names + host_ui->xattr_cnt + nm->len + 1; + if (names_len > XATTR_LIST_MAX) { + ubifs_err("cannot add one more xattr name to inode %lu, total names length would become %d, max. is %d", + host->i_ino, names_len, XATTR_LIST_MAX); return -ENOSPC; + } err = ubifs_budget_space(c, &req); if (err) @@ -293,18 +299,16 @@ static struct inode *iget_xattr(struct ubifs_info *c, ino_t inum) return ERR_PTR(-EINVAL); } -int ubifs_setxattr(struct dentry *dentry, const char *name, - const void *value, size_t size, int flags) +static int setxattr(struct inode *host, const char *name, const void *value, + size_t size, int flags) { - struct inode *inode, *host = dentry->d_inode; + struct inode *inode; struct ubifs_info *c = host->i_sb->s_fs_info; struct qstr nm = QSTR_INIT(name, strlen(name)); struct ubifs_dent_node *xent; union ubifs_key key; int err, type; - dbg_gen("xattr '%s', host ino %lu ('%pd'), size %zd", name, - host->i_ino, dentry, size); ubifs_assert(mutex_is_locked(&host->i_mutex)); if (size > UBIFS_MAX_INO_DATA) @@ -356,6 +360,15 @@ out_free: return err; } +int ubifs_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + dbg_gen("xattr '%s', host ino %lu ('%pd'), size %zd", + name, dentry->d_inode->i_ino, dentry, size); + + return setxattr(dentry->d_inode, name, value, size, flags); +} + ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf, size_t size) { @@ -568,3 +581,84 @@ out_free: kfree(xent); return err; } + +static size_t security_listxattr(struct dentry *d, char *list, size_t list_size, + const char *name, size_t name_len, int flags) +{ + const int prefix_len = XATTR_SECURITY_PREFIX_LEN; + const size_t total_len = prefix_len + name_len + 1; + + if (list && total_len <= list_size) { + memcpy(list, XATTR_SECURITY_PREFIX, prefix_len); + memcpy(list + prefix_len, name, name_len); + list[prefix_len + name_len] = '\0'; + } + + return total_len; +} + +static int security_getxattr(struct dentry *d, const char *name, void *buffer, + size_t size, int flags) +{ + return ubifs_getxattr(d, name, buffer, size); +} + +static int security_setxattr(struct dentry *d, const char *name, + const void *value, size_t size, int flags, + int handler_flags) +{ + return ubifs_setxattr(d, name, value, size, flags); +} + +static const struct xattr_handler ubifs_xattr_security_handler = { + .prefix = XATTR_SECURITY_PREFIX, + .list = security_listxattr, + .get = security_getxattr, + .set = security_setxattr, +}; + +const struct xattr_handler *ubifs_xattr_handlers[] = { + &ubifs_xattr_security_handler, + NULL, +}; + +static int init_xattrs(struct inode *inode, const struct xattr *xattr_array, + void *fs_info) +{ + const struct xattr *xattr; + char *name; + int err = 0; + + for (xattr = xattr_array; xattr->name != NULL; xattr++) { + name = kmalloc(XATTR_SECURITY_PREFIX_LEN + + strlen(xattr->name) + 1, GFP_NOFS); + if (!name) { + err = -ENOMEM; + break; + } + strcpy(name, XATTR_SECURITY_PREFIX); + strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name); + err = setxattr(inode, name, xattr->value, xattr->value_len, 0); + kfree(name); + if (err < 0) + break; + } + + return err; +} + +int ubifs_init_security(struct inode *dentry, struct inode *inode, + const struct qstr *qstr) +{ + int err; + + mutex_lock(&inode->i_mutex); + err = security_inode_init_security(inode, dentry, qstr, + &init_xattrs, 0); + mutex_unlock(&inode->i_mutex); + + if (err) + ubifs_err("cannot initialize security for inode %lu, error %d", + inode->i_ino, err); + return err; +} diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h index c3918a0684fe..1e271cb559cd 100644 --- a/include/linux/mtd/ubi.h +++ b/include/linux/mtd/ubi.h @@ -23,22 +23,32 @@ #include <linux/ioctl.h> #include <linux/types.h> +#include <linux/scatterlist.h> #include <mtd/ubi-user.h> /* All voumes/LEBs */ #define UBI_ALL -1 /* + * Maximum number of scatter gather list entries, + * we use only 64 to have a lower memory foot print. + */ +#define UBI_MAX_SG_COUNT 64 + +/* * enum ubi_open_mode - UBI volume open mode constants. * * UBI_READONLY: read-only mode * UBI_READWRITE: read-write mode * UBI_EXCLUSIVE: exclusive mode + * UBI_METAONLY: modify only the volume meta-data, + * i.e. the data stored in the volume table, but not in any of volume LEBs. */ enum { UBI_READONLY = 1, UBI_READWRITE, - UBI_EXCLUSIVE + UBI_EXCLUSIVE, + UBI_METAONLY }; /** @@ -116,6 +126,35 @@ struct ubi_volume_info { }; /** + * struct ubi_sgl - UBI scatter gather list data structure. + * @list_pos: current position in @sg[] + * @page_pos: current position in @sg[@list_pos] + * @sg: the scatter gather list itself + * + * ubi_sgl is a wrapper around a scatter list which keeps track of the + * current position in the list and the current list item such that + * it can be used across multiple ubi_leb_read_sg() calls. + */ +struct ubi_sgl { + int list_pos; + int page_pos; + struct scatterlist sg[UBI_MAX_SG_COUNT]; +}; + +/** + * ubi_sgl_init - initialize an UBI scatter gather list data structure. + * @usgl: the UBI scatter gather struct itself + * + * Please note that you still have to use sg_init_table() or any adequate + * function to initialize the unterlaying struct scatterlist. + */ +static inline void ubi_sgl_init(struct ubi_sgl *usgl) +{ + usgl->list_pos = 0; + usgl->page_pos = 0; +} + +/** * struct ubi_device_info - UBI device description data structure. * @ubi_num: ubi device number * @leb_size: logical eraseblock size on this UBI device @@ -210,6 +249,8 @@ int ubi_unregister_volume_notifier(struct notifier_block *nb); void ubi_close_volume(struct ubi_volume_desc *desc); int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, int len, int check); +int ubi_leb_read_sg(struct ubi_volume_desc *desc, int lnum, struct ubi_sgl *sgl, + int offset, int len, int check); int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, int offset, int len); int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, @@ -230,4 +271,14 @@ static inline int ubi_read(struct ubi_volume_desc *desc, int lnum, char *buf, { return ubi_leb_read(desc, lnum, buf, offset, len, 0); } + +/* + * This function is the same as the 'ubi_leb_read_sg()' function, but it does + * not provide the checking capability. + */ +static inline int ubi_read_sg(struct ubi_volume_desc *desc, int lnum, + struct ubi_sgl *sgl, int offset, int len) +{ + return ubi_leb_read_sg(desc, lnum, sgl, offset, len, 0); +} #endif /* !__LINUX_UBI_H__ */ |