diff options
| -rw-r--r-- | drivers/mtd/ubi/eba.c | 55 | ||||
| -rw-r--r-- | drivers/mtd/ubi/kapi.c | 95 | ||||
| -rw-r--r-- | drivers/mtd/ubi/ubi.h | 3 | ||||
| -rw-r--r-- | include/linux/mtd/ubi.h | 48 |
4 files changed, 186 insertions, 15 deletions
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 | |||
| @@ -480,6 +480,61 @@ out_unlock: | |||
| 480 | } | 480 | } |
| 481 | 481 | ||
| 482 | /** | 482 | /** |
| 483 | * ubi_eba_read_leb_sg - read data into a scatter gather list. | ||
| 484 | * @ubi: UBI device description object | ||
| 485 | * @vol: volume description object | ||
| 486 | * @lnum: logical eraseblock number | ||
| 487 | * @sgl: UBI scatter gather list to store the read data | ||
| 488 | * @offset: offset from where to read | ||
| 489 | * @len: how many bytes to read | ||
| 490 | * @check: data CRC check flag | ||
| 491 | * | ||
| 492 | * This function works exactly like ubi_eba_read_leb(). But instead of | ||
| 493 | * storing the read data into a buffer it writes to an UBI scatter gather | ||
| 494 | * list. | ||
| 495 | */ | ||
| 496 | int ubi_eba_read_leb_sg(struct ubi_device *ubi, struct ubi_volume *vol, | ||
| 497 | struct ubi_sgl *sgl, int lnum, int offset, int len, | ||
| 498 | int check) | ||
| 499 | { | ||
| 500 | int to_read; | ||
| 501 | int ret; | ||
| 502 | struct scatterlist *sg; | ||
| 503 | |||
| 504 | for (;;) { | ||
| 505 | ubi_assert(sgl->list_pos < UBI_MAX_SG_COUNT); | ||
| 506 | sg = &sgl->sg[sgl->list_pos]; | ||
| 507 | if (len < sg->length - sgl->page_pos) | ||
| 508 | to_read = len; | ||
| 509 | else | ||
| 510 | to_read = sg->length - sgl->page_pos; | ||
| 511 | |||
| 512 | ret = ubi_eba_read_leb(ubi, vol, lnum, | ||
| 513 | sg_virt(sg) + sgl->page_pos, offset, | ||
| 514 | to_read, check); | ||
| 515 | if (ret < 0) | ||
| 516 | return ret; | ||
| 517 | |||
| 518 | offset += to_read; | ||
| 519 | len -= to_read; | ||
| 520 | if (!len) { | ||
| 521 | sgl->page_pos += to_read; | ||
| 522 | if (sgl->page_pos == sg->length) { | ||
| 523 | sgl->list_pos++; | ||
| 524 | sgl->page_pos = 0; | ||
| 525 | } | ||
| 526 | |||
| 527 | break; | ||
| 528 | } | ||
| 529 | |||
| 530 | sgl->list_pos++; | ||
| 531 | sgl->page_pos = 0; | ||
| 532 | } | ||
| 533 | |||
| 534 | return ret; | ||
| 535 | } | ||
| 536 | |||
| 537 | /** | ||
| 483 | * recover_peb - recover from write failure. | 538 | * recover_peb - recover from write failure. |
| 484 | * @ubi: UBI device description object | 539 | * @ubi: UBI device description object |
| 485 | * @pnum: the physical eraseblock to recover | 540 | * @pnum: the physical eraseblock to recover |
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 | |||
| @@ -366,6 +366,43 @@ void ubi_close_volume(struct ubi_volume_desc *desc) | |||
| 366 | EXPORT_SYMBOL_GPL(ubi_close_volume); | 366 | EXPORT_SYMBOL_GPL(ubi_close_volume); |
| 367 | 367 | ||
| 368 | /** | 368 | /** |
| 369 | * leb_read_sanity_check - does sanity checks on read requests. | ||
| 370 | * @desc: volume descriptor | ||
| 371 | * @lnum: logical eraseblock number to read from | ||
| 372 | * @offset: offset within the logical eraseblock to read from | ||
| 373 | * @len: how many bytes to read | ||
| 374 | * | ||
| 375 | * This function is used by ubi_leb_read() and ubi_leb_read_sg() | ||
| 376 | * to perform sanity checks. | ||
| 377 | */ | ||
| 378 | static int leb_read_sanity_check(struct ubi_volume_desc *desc, int lnum, | ||
| 379 | int offset, int len) | ||
| 380 | { | ||
| 381 | struct ubi_volume *vol = desc->vol; | ||
| 382 | struct ubi_device *ubi = vol->ubi; | ||
| 383 | int vol_id = vol->vol_id; | ||
| 384 | |||
| 385 | if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 || | ||
| 386 | lnum >= vol->used_ebs || offset < 0 || len < 0 || | ||
| 387 | offset + len > vol->usable_leb_size) | ||
| 388 | return -EINVAL; | ||
| 389 | |||
| 390 | if (vol->vol_type == UBI_STATIC_VOLUME) { | ||
| 391 | if (vol->used_ebs == 0) | ||
| 392 | /* Empty static UBI volume */ | ||
| 393 | return 0; | ||
| 394 | if (lnum == vol->used_ebs - 1 && | ||
| 395 | offset + len > vol->last_eb_bytes) | ||
| 396 | return -EINVAL; | ||
| 397 | } | ||
| 398 | |||
| 399 | if (vol->upd_marker) | ||
| 400 | return -EBADF; | ||
| 401 | |||
| 402 | return 0; | ||
| 403 | } | ||
| 404 | |||
| 405 | /** | ||
| 369 | * ubi_leb_read - read data. | 406 | * ubi_leb_read - read data. |
| 370 | * @desc: volume descriptor | 407 | * @desc: volume descriptor |
| 371 | * @lnum: logical eraseblock number to read from | 408 | * @lnum: logical eraseblock number to read from |
| @@ -401,22 +438,10 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, | |||
| 401 | 438 | ||
| 402 | dbg_gen("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset); | 439 | dbg_gen("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset); |
| 403 | 440 | ||
| 404 | if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 || | 441 | err = leb_read_sanity_check(desc, lnum, offset, len); |
| 405 | lnum >= vol->used_ebs || offset < 0 || len < 0 || | 442 | if (err < 0) |
| 406 | offset + len > vol->usable_leb_size) | 443 | return err; |
| 407 | return -EINVAL; | ||
| 408 | |||
| 409 | if (vol->vol_type == UBI_STATIC_VOLUME) { | ||
| 410 | if (vol->used_ebs == 0) | ||
| 411 | /* Empty static UBI volume */ | ||
| 412 | return 0; | ||
| 413 | if (lnum == vol->used_ebs - 1 && | ||
| 414 | offset + len > vol->last_eb_bytes) | ||
| 415 | return -EINVAL; | ||
| 416 | } | ||
| 417 | 444 | ||
| 418 | if (vol->upd_marker) | ||
| 419 | return -EBADF; | ||
| 420 | if (len == 0) | 445 | if (len == 0) |
| 421 | return 0; | 446 | return 0; |
| 422 | 447 | ||
| @@ -430,6 +455,46 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, | |||
| 430 | } | 455 | } |
| 431 | EXPORT_SYMBOL_GPL(ubi_leb_read); | 456 | EXPORT_SYMBOL_GPL(ubi_leb_read); |
| 432 | 457 | ||
| 458 | |||
| 459 | /** | ||
| 460 | * ubi_leb_read_sg - read data into a scatter gather list. | ||
| 461 | * @desc: volume descriptor | ||
| 462 | * @lnum: logical eraseblock number to read from | ||
| 463 | * @buf: buffer where to store the read data | ||
| 464 | * @offset: offset within the logical eraseblock to read from | ||
| 465 | * @len: how many bytes to read | ||
| 466 | * @check: whether UBI has to check the read data's CRC or not. | ||
| 467 | * | ||
| 468 | * This function works exactly like ubi_leb_read_sg(). But instead of | ||
| 469 | * storing the read data into a buffer it writes to an UBI scatter gather | ||
| 470 | * list. | ||
| 471 | */ | ||
| 472 | int ubi_leb_read_sg(struct ubi_volume_desc *desc, int lnum, struct ubi_sgl *sgl, | ||
| 473 | int offset, int len, int check) | ||
| 474 | { | ||
| 475 | struct ubi_volume *vol = desc->vol; | ||
| 476 | struct ubi_device *ubi = vol->ubi; | ||
| 477 | int err, vol_id = vol->vol_id; | ||
| 478 | |||
| 479 | dbg_gen("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset); | ||
| 480 | |||
| 481 | err = leb_read_sanity_check(desc, lnum, offset, len); | ||
| 482 | if (err < 0) | ||
| 483 | return err; | ||
| 484 | |||
| 485 | if (len == 0) | ||
| 486 | return 0; | ||
| 487 | |||
| 488 | err = ubi_eba_read_leb_sg(ubi, vol, sgl, lnum, offset, len, check); | ||
| 489 | if (err && mtd_is_eccerr(err) && vol->vol_type == UBI_STATIC_VOLUME) { | ||
| 490 | ubi_warn(ubi, "mark volume %d as corrupted", vol_id); | ||
| 491 | vol->corrupted = 1; | ||
| 492 | } | ||
| 493 | |||
| 494 | return err; | ||
| 495 | } | ||
| 496 | EXPORT_SYMBOL_GPL(ubi_leb_read_sg); | ||
| 497 | |||
| 433 | /** | 498 | /** |
| 434 | * ubi_leb_write - write data. | 499 | * ubi_leb_write - write data. |
| 435 | * @desc: volume descriptor | 500 | * @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, | |||
| 795 | int lnum); | 795 | int lnum); |
| 796 | int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, | 796 | int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, |
| 797 | void *buf, int offset, int len, int check); | 797 | void *buf, int offset, int len, int check); |
| 798 | int ubi_eba_read_leb_sg(struct ubi_device *ubi, struct ubi_volume *vol, | ||
| 799 | struct ubi_sgl *sgl, int lnum, int offset, int len, | ||
| 800 | int check); | ||
| 798 | int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, | 801 | int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, |
| 799 | const void *buf, int offset, int len); | 802 | const void *buf, int offset, int len); |
| 800 | int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, | 803 | 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,12 +23,19 @@ | |||
| 23 | 23 | ||
| 24 | #include <linux/ioctl.h> | 24 | #include <linux/ioctl.h> |
| 25 | #include <linux/types.h> | 25 | #include <linux/types.h> |
| 26 | #include <linux/scatterlist.h> | ||
| 26 | #include <mtd/ubi-user.h> | 27 | #include <mtd/ubi-user.h> |
| 27 | 28 | ||
| 28 | /* All voumes/LEBs */ | 29 | /* All voumes/LEBs */ |
| 29 | #define UBI_ALL -1 | 30 | #define UBI_ALL -1 |
| 30 | 31 | ||
| 31 | /* | 32 | /* |
| 33 | * Maximum number of scatter gather list entries, | ||
| 34 | * we use only 64 to have a lower memory foot print. | ||
| 35 | */ | ||
| 36 | #define UBI_MAX_SG_COUNT 64 | ||
| 37 | |||
| 38 | /* | ||
| 32 | * enum ubi_open_mode - UBI volume open mode constants. | 39 | * enum ubi_open_mode - UBI volume open mode constants. |
| 33 | * | 40 | * |
| 34 | * UBI_READONLY: read-only mode | 41 | * UBI_READONLY: read-only mode |
| @@ -119,6 +126,35 @@ struct ubi_volume_info { | |||
| 119 | }; | 126 | }; |
| 120 | 127 | ||
| 121 | /** | 128 | /** |
| 129 | * struct ubi_sgl - UBI scatter gather list data structure. | ||
| 130 | * @list_pos: current position in @sg[] | ||
| 131 | * @page_pos: current position in @sg[@list_pos] | ||
| 132 | * @sg: the scatter gather list itself | ||
| 133 | * | ||
| 134 | * ubi_sgl is a wrapper around a scatter list which keeps track of the | ||
| 135 | * current position in the list and the current list item such that | ||
| 136 | * it can be used across multiple ubi_leb_read_sg() calls. | ||
| 137 | */ | ||
| 138 | struct ubi_sgl { | ||
| 139 | int list_pos; | ||
| 140 | int page_pos; | ||
| 141 | struct scatterlist sg[UBI_MAX_SG_COUNT]; | ||
| 142 | }; | ||
| 143 | |||
| 144 | /** | ||
| 145 | * ubi_sgl_init - initialize an UBI scatter gather list data structure. | ||
| 146 | * @usgl: the UBI scatter gather struct itself | ||
| 147 | * | ||
| 148 | * Please note that you still have to use sg_init_table() or any adequate | ||
| 149 | * function to initialize the unterlaying struct scatterlist. | ||
| 150 | */ | ||
| 151 | static inline void ubi_sgl_init(struct ubi_sgl *usgl) | ||
| 152 | { | ||
| 153 | usgl->list_pos = 0; | ||
| 154 | usgl->page_pos = 0; | ||
| 155 | } | ||
| 156 | |||
| 157 | /** | ||
| 122 | * struct ubi_device_info - UBI device description data structure. | 158 | * struct ubi_device_info - UBI device description data structure. |
| 123 | * @ubi_num: ubi device number | 159 | * @ubi_num: ubi device number |
| 124 | * @leb_size: logical eraseblock size on this UBI device | 160 | * @leb_size: logical eraseblock size on this UBI device |
| @@ -213,6 +249,8 @@ int ubi_unregister_volume_notifier(struct notifier_block *nb); | |||
| 213 | void ubi_close_volume(struct ubi_volume_desc *desc); | 249 | void ubi_close_volume(struct ubi_volume_desc *desc); |
| 214 | int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, | 250 | int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, |
| 215 | int len, int check); | 251 | int len, int check); |
| 252 | int ubi_leb_read_sg(struct ubi_volume_desc *desc, int lnum, struct ubi_sgl *sgl, | ||
| 253 | int offset, int len, int check); | ||
| 216 | int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, | 254 | int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, |
| 217 | int offset, int len); | 255 | int offset, int len); |
| 218 | int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, | 256 | 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, | |||
| 233 | { | 271 | { |
| 234 | return ubi_leb_read(desc, lnum, buf, offset, len, 0); | 272 | return ubi_leb_read(desc, lnum, buf, offset, len, 0); |
| 235 | } | 273 | } |
| 274 | |||
| 275 | /* | ||
| 276 | * This function is the same as the 'ubi_leb_read_sg()' function, but it does | ||
| 277 | * not provide the checking capability. | ||
| 278 | */ | ||
| 279 | static inline int ubi_read_sg(struct ubi_volume_desc *desc, int lnum, | ||
| 280 | struct ubi_sgl *sgl, int offset, int len) | ||
| 281 | { | ||
| 282 | return ubi_leb_read_sg(desc, lnum, sgl, offset, len, 0); | ||
| 283 | } | ||
| 236 | #endif /* !__LINUX_UBI_H__ */ | 284 | #endif /* !__LINUX_UBI_H__ */ |
