diff options
author | Boris Brezillon <boris.brezillon@free-electrons.com> | 2016-09-16 10:59:20 -0400 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2016-10-02 16:48:14 -0400 |
commit | f036dfeb859cb412fdbe8e3ee74b632d9ce6387e (patch) | |
tree | 9cfd9243e10f5cb7622b100fd6a73218e26f96da /drivers/mtd/ubi | |
parent | 7b6b749b125a93d673ba12977007dfbd65a61c32 (diff) |
UBI: simplify recover_peb() code
recover_peb() is using a convoluted retry/exit path. Add try_recover_peb()
to simplify the retry logic and make sure we have a single exit path
instead of manually releasing the resource in each error path.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
Diffstat (limited to 'drivers/mtd/ubi')
-rw-r--r-- | drivers/mtd/ubi/eba.c | 130 |
1 files changed, 77 insertions, 53 deletions
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index ebf517271d29..be59cfb81934 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c | |||
@@ -554,39 +554,37 @@ int ubi_eba_read_leb_sg(struct ubi_device *ubi, struct ubi_volume *vol, | |||
554 | } | 554 | } |
555 | 555 | ||
556 | /** | 556 | /** |
557 | * recover_peb - recover from write failure. | 557 | * try_recover_peb - try to recover from write failure. |
558 | * @ubi: UBI device description object | 558 | * @vol: volume description object |
559 | * @pnum: the physical eraseblock to recover | 559 | * @pnum: the physical eraseblock to recover |
560 | * @vol_id: volume ID | ||
561 | * @lnum: logical eraseblock number | 560 | * @lnum: logical eraseblock number |
562 | * @buf: data which was not written because of the write failure | 561 | * @buf: data which was not written because of the write failure |
563 | * @offset: offset of the failed write | 562 | * @offset: offset of the failed write |
564 | * @len: how many bytes should have been written | 563 | * @len: how many bytes should have been written |
564 | * @vid: VID header | ||
565 | * @retry: whether the caller should retry in case of failure | ||
565 | * | 566 | * |
566 | * This function is called in case of a write failure and moves all good data | 567 | * This function is called in case of a write failure and moves all good data |
567 | * from the potentially bad physical eraseblock to a good physical eraseblock. | 568 | * from the potentially bad physical eraseblock to a good physical eraseblock. |
568 | * This function also writes the data which was not written due to the failure. | 569 | * This function also writes the data which was not written due to the failure. |
569 | * Returns new physical eraseblock number in case of success, and a negative | 570 | * Returns 0 in case of success, and a negative error code in case of failure. |
570 | * error code in case of failure. | 571 | * In case of failure, the %retry parameter is set to false if this is a fatal |
572 | * error (retrying won't help), and true otherwise. | ||
571 | */ | 573 | */ |
572 | static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum, | 574 | static int try_recover_peb(struct ubi_volume *vol, int pnum, int lnum, |
573 | const void *buf, int offset, int len) | 575 | const void *buf, int offset, int len, |
576 | struct ubi_vid_hdr *vid_hdr, bool *retry) | ||
574 | { | 577 | { |
575 | int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0; | 578 | struct ubi_device *ubi = vol->ubi; |
576 | struct ubi_volume *vol = ubi->volumes[idx]; | 579 | int new_pnum, err, vol_id = vol->vol_id, data_size; |
577 | struct ubi_vid_hdr *vid_hdr; | ||
578 | uint32_t crc; | 580 | uint32_t crc; |
579 | 581 | ||
580 | vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); | 582 | *retry = false; |
581 | if (!vid_hdr) | ||
582 | return -ENOMEM; | ||
583 | 583 | ||
584 | retry: | ||
585 | new_pnum = ubi_wl_get_peb(ubi); | 584 | new_pnum = ubi_wl_get_peb(ubi); |
586 | if (new_pnum < 0) { | 585 | if (new_pnum < 0) { |
587 | ubi_free_vid_hdr(ubi, vid_hdr); | 586 | err = new_pnum; |
588 | up_read(&ubi->fm_eba_sem); | 587 | goto out_put; |
589 | return new_pnum; | ||
590 | } | 588 | } |
591 | 589 | ||
592 | ubi_msg(ubi, "recover PEB %d, move data to PEB %d", | 590 | ubi_msg(ubi, "recover PEB %d, move data to PEB %d", |
@@ -596,7 +594,6 @@ retry: | |||
596 | if (err && err != UBI_IO_BITFLIPS) { | 594 | if (err && err != UBI_IO_BITFLIPS) { |
597 | if (err > 0) | 595 | if (err > 0) |
598 | err = -EIO; | 596 | err = -EIO; |
599 | up_read(&ubi->fm_eba_sem); | ||
600 | goto out_put; | 597 | goto out_put; |
601 | } | 598 | } |
602 | 599 | ||
@@ -608,12 +605,12 @@ retry: | |||
608 | /* Read everything before the area where the write failure happened */ | 605 | /* Read everything before the area where the write failure happened */ |
609 | if (offset > 0) { | 606 | if (offset > 0) { |
610 | err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, offset); | 607 | err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, offset); |
611 | if (err && err != UBI_IO_BITFLIPS) { | 608 | if (err && err != UBI_IO_BITFLIPS) |
612 | up_read(&ubi->fm_eba_sem); | ||
613 | goto out_unlock; | 609 | goto out_unlock; |
614 | } | ||
615 | } | 610 | } |
616 | 611 | ||
612 | *retry = true; | ||
613 | |||
617 | memcpy(ubi->peb_buf + offset, buf, len); | 614 | memcpy(ubi->peb_buf + offset, buf, len); |
618 | 615 | ||
619 | data_size = offset + len; | 616 | data_size = offset + len; |
@@ -623,49 +620,76 @@ retry: | |||
623 | vid_hdr->data_size = cpu_to_be32(data_size); | 620 | vid_hdr->data_size = cpu_to_be32(data_size); |
624 | vid_hdr->data_crc = cpu_to_be32(crc); | 621 | vid_hdr->data_crc = cpu_to_be32(crc); |
625 | err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr); | 622 | err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr); |
626 | if (err) { | 623 | if (err) |
627 | mutex_unlock(&ubi->buf_mutex); | 624 | goto out_unlock; |
628 | up_read(&ubi->fm_eba_sem); | ||
629 | goto write_error; | ||
630 | } | ||
631 | 625 | ||
632 | err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size); | 626 | err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size); |
633 | if (err) { | ||
634 | mutex_unlock(&ubi->buf_mutex); | ||
635 | up_read(&ubi->fm_eba_sem); | ||
636 | goto write_error; | ||
637 | } | ||
638 | 627 | ||
628 | out_unlock: | ||
639 | mutex_unlock(&ubi->buf_mutex); | 629 | mutex_unlock(&ubi->buf_mutex); |
640 | ubi_free_vid_hdr(ubi, vid_hdr); | ||
641 | 630 | ||
642 | vol->eba_tbl[lnum] = new_pnum; | 631 | if (!err) |
632 | vol->eba_tbl[lnum] = new_pnum; | ||
633 | |||
634 | out_put: | ||
643 | up_read(&ubi->fm_eba_sem); | 635 | up_read(&ubi->fm_eba_sem); |
644 | ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); | ||
645 | 636 | ||
646 | ubi_msg(ubi, "data was successfully recovered"); | 637 | if (!err) { |
647 | return 0; | 638 | ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); |
639 | ubi_msg(ubi, "data was successfully recovered"); | ||
640 | } else if (new_pnum >= 0) { | ||
641 | /* | ||
642 | * Bad luck? This physical eraseblock is bad too? Crud. Let's | ||
643 | * try to get another one. | ||
644 | */ | ||
645 | ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1); | ||
646 | ubi_warn(ubi, "failed to write to PEB %d", new_pnum); | ||
647 | } | ||
648 | 648 | ||
649 | out_unlock: | ||
650 | mutex_unlock(&ubi->buf_mutex); | ||
651 | out_put: | ||
652 | ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1); | ||
653 | ubi_free_vid_hdr(ubi, vid_hdr); | ||
654 | return err; | 649 | return err; |
650 | } | ||
655 | 651 | ||
656 | write_error: | 652 | /** |
657 | /* | 653 | * recover_peb - recover from write failure. |
658 | * Bad luck? This physical eraseblock is bad too? Crud. Let's try to | 654 | * @ubi: UBI device description object |
659 | * get another one. | 655 | * @pnum: the physical eraseblock to recover |
660 | */ | 656 | * @vol_id: volume ID |
661 | ubi_warn(ubi, "failed to write to PEB %d", new_pnum); | 657 | * @lnum: logical eraseblock number |
662 | ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1); | 658 | * @buf: data which was not written because of the write failure |
663 | if (++tries > UBI_IO_RETRIES) { | 659 | * @offset: offset of the failed write |
664 | ubi_free_vid_hdr(ubi, vid_hdr); | 660 | * @len: how many bytes should have been written |
665 | return err; | 661 | * |
662 | * This function is called in case of a write failure and moves all good data | ||
663 | * from the potentially bad physical eraseblock to a good physical eraseblock. | ||
664 | * This function also writes the data which was not written due to the failure. | ||
665 | * Returns 0 in case of success, and a negative error code in case of failure. | ||
666 | * This function tries %UBI_IO_RETRIES before giving up. | ||
667 | */ | ||
668 | static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum, | ||
669 | const void *buf, int offset, int len) | ||
670 | { | ||
671 | int err, idx = vol_id2idx(ubi, vol_id), tries; | ||
672 | struct ubi_volume *vol = ubi->volumes[idx]; | ||
673 | struct ubi_vid_hdr *vid_hdr; | ||
674 | |||
675 | vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); | ||
676 | if (!vid_hdr) | ||
677 | return -ENOMEM; | ||
678 | |||
679 | for (tries = 0; tries <= UBI_IO_RETRIES; tries++) { | ||
680 | bool retry; | ||
681 | |||
682 | err = try_recover_peb(vol, pnum, lnum, buf, offset, len, | ||
683 | vid_hdr, &retry); | ||
684 | if (!err || !retry) | ||
685 | break; | ||
686 | |||
687 | ubi_msg(ubi, "try again"); | ||
666 | } | 688 | } |
667 | ubi_msg(ubi, "try again"); | 689 | |
668 | goto retry; | 690 | ubi_free_vid_hdr(ubi, vid_hdr); |
691 | |||
692 | return err; | ||
669 | } | 693 | } |
670 | 694 | ||
671 | /** | 695 | /** |