From fafdd2bf2638157670f28462b641150d16dbaeca Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Mon, 24 Nov 2014 22:30:09 +0100 Subject: UBI: Implement UBI_METAONLY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UBI_METAONLY is a new open mode for UBI volumes, it indicates that only meta data is being changed. Meta data in terms of UBI volumes means data which is stored in the UBI volume table but not on the volume itself. While it does not interfere with UBI_READONLY and UBI_READWRITE it is not allowed to use UBI_METAONLY together with UBI_EXCLUSIVE. Cc: Ezequiel Garcia Cc: Andrew Murray Signed-off-by: Richard Weinberger Tested-by: Guido Martínez Reviewed-by: Guido Martínez Tested-by: Christoph Fritz Tested-by: Andrew Murray --- include/linux/mtd/ubi.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h index c3918a0684fe..8fa2753316e8 100644 --- a/include/linux/mtd/ubi.h +++ b/include/linux/mtd/ubi.h @@ -34,11 +34,14 @@ * 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 }; /** -- cgit v1.2.3 From 9ff08979e17423f0f691c1d76f35dfec72a5e459 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Sat, 10 Jan 2015 22:52:13 +0100 Subject: UBI: Add initial support for scatter gather Adds a new set of functions to deal with scatter gather. ubi_eba_read_leb_sg() will read from a LEB into a scatter gather list. The new data structure struct ubi_sgl will be used within UBI to hold the scatter gather list itself and metadata to have a cursor within the list. Signed-off-by: Richard Weinberger Tested-by: Ezequiel Garcia Reviewed-by: Ezequiel Garcia --- drivers/mtd/ubi/eba.c | 55 ++++++++++++++++++++++++++++ drivers/mtd/ubi/kapi.c | 95 +++++++++++++++++++++++++++++++++++++++++-------- drivers/mtd/ubi/ubi.h | 3 ++ include/linux/mtd/ubi.h | 48 +++++++++++++++++++++++++ 4 files changed, 186 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index a40020cf0923..f1db73da7975 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -479,6 +479,61 @@ out_unlock: return err; } +/** + * 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 diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 589c423fac2d..478e00cf2d9e 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -365,6 +365,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 @@ -401,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; @@ -430,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/ubi.h b/drivers/mtd/ubi/ubi.h index 20cabc060b61..d94b81fa2306 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -795,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/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h index 8fa2753316e8..1e271cb559cd 100644 --- a/include/linux/mtd/ubi.h +++ b/include/linux/mtd/ubi.h @@ -23,11 +23,18 @@ #include #include +#include #include /* 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. * @@ -118,6 +125,35 @@ struct ubi_volume_info { dev_t cdev; }; +/** + * 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 @@ -213,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, @@ -233,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__ */ -- cgit v1.2.3