diff options
author | Sebastian Siewior <bigeasy@linutronix.de> | 2015-11-26 15:23:50 -0500 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2016-01-10 06:33:11 -0500 |
commit | 34b89df90374b631692132640c6b3dbef52f808d (patch) | |
tree | 3a1e0fec6d608bf57763f94f6b92e05f0cb54ebb | |
parent | 168309855a7d1e16db751e9c647119fe2d2dc878 (diff) |
mtd: ubi: wl: avoid erasing a PEB which is empty
wear_leveling_worker() currently unconditionally puts a PEB on erase in
the error case even it just been taken from the free_list and never
used.
In case the PEB was never used it can be put back on the free list
saving a precious erase cycle.
v1…v2:
- to_leb_clean -> dst_leb_clean
- use the nested option for ensure_wear_leveling()
- do_sync_erase() can't go -ENOMEM so we can just go into
RO-mode now.
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
-rw-r--r-- | drivers/mtd/ubi/wl.c | 21 |
1 files changed, 18 insertions, 3 deletions
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 56065632a5b8..17ec948ac40e 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c | |||
@@ -628,6 +628,7 @@ static int do_sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, | |||
628 | return __erase_worker(ubi, &wl_wrk); | 628 | return __erase_worker(ubi, &wl_wrk); |
629 | } | 629 | } |
630 | 630 | ||
631 | static int ensure_wear_leveling(struct ubi_device *ubi, int nested); | ||
631 | /** | 632 | /** |
632 | * wear_leveling_worker - wear-leveling worker function. | 633 | * wear_leveling_worker - wear-leveling worker function. |
633 | * @ubi: UBI device description object | 634 | * @ubi: UBI device description object |
@@ -649,6 +650,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, | |||
649 | #endif | 650 | #endif |
650 | struct ubi_wl_entry *e1, *e2; | 651 | struct ubi_wl_entry *e1, *e2; |
651 | struct ubi_vid_hdr *vid_hdr; | 652 | struct ubi_vid_hdr *vid_hdr; |
653 | int dst_leb_clean = 0; | ||
652 | 654 | ||
653 | kfree(wrk); | 655 | kfree(wrk); |
654 | if (shutdown) | 656 | if (shutdown) |
@@ -753,6 +755,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, | |||
753 | 755 | ||
754 | err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0); | 756 | err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0); |
755 | if (err && err != UBI_IO_BITFLIPS) { | 757 | if (err && err != UBI_IO_BITFLIPS) { |
758 | dst_leb_clean = 1; | ||
756 | if (err == UBI_IO_FF) { | 759 | if (err == UBI_IO_FF) { |
757 | /* | 760 | /* |
758 | * We are trying to move PEB without a VID header. UBI | 761 | * We are trying to move PEB without a VID header. UBI |
@@ -798,10 +801,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, | |||
798 | * protection queue. | 801 | * protection queue. |
799 | */ | 802 | */ |
800 | protect = 1; | 803 | protect = 1; |
804 | dst_leb_clean = 1; | ||
801 | goto out_not_moved; | 805 | goto out_not_moved; |
802 | } | 806 | } |
803 | if (err == MOVE_RETRY) { | 807 | if (err == MOVE_RETRY) { |
804 | scrubbing = 1; | 808 | scrubbing = 1; |
809 | dst_leb_clean = 1; | ||
805 | goto out_not_moved; | 810 | goto out_not_moved; |
806 | } | 811 | } |
807 | if (err == MOVE_TARGET_BITFLIPS || err == MOVE_TARGET_WR_ERR || | 812 | if (err == MOVE_TARGET_BITFLIPS || err == MOVE_TARGET_WR_ERR || |
@@ -827,6 +832,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, | |||
827 | ubi->erroneous_peb_count); | 832 | ubi->erroneous_peb_count); |
828 | goto out_error; | 833 | goto out_error; |
829 | } | 834 | } |
835 | dst_leb_clean = 1; | ||
830 | erroneous = 1; | 836 | erroneous = 1; |
831 | goto out_not_moved; | 837 | goto out_not_moved; |
832 | } | 838 | } |
@@ -897,15 +903,24 @@ out_not_moved: | |||
897 | wl_tree_add(e1, &ubi->scrub); | 903 | wl_tree_add(e1, &ubi->scrub); |
898 | else | 904 | else |
899 | wl_tree_add(e1, &ubi->used); | 905 | wl_tree_add(e1, &ubi->used); |
906 | if (dst_leb_clean) { | ||
907 | wl_tree_add(e2, &ubi->free); | ||
908 | ubi->free_count++; | ||
909 | } | ||
910 | |||
900 | ubi_assert(!ubi->move_to_put); | 911 | ubi_assert(!ubi->move_to_put); |
901 | ubi->move_from = ubi->move_to = NULL; | 912 | ubi->move_from = ubi->move_to = NULL; |
902 | ubi->wl_scheduled = 0; | 913 | ubi->wl_scheduled = 0; |
903 | spin_unlock(&ubi->wl_lock); | 914 | spin_unlock(&ubi->wl_lock); |
904 | 915 | ||
905 | ubi_free_vid_hdr(ubi, vid_hdr); | 916 | ubi_free_vid_hdr(ubi, vid_hdr); |
906 | err = do_sync_erase(ubi, e2, vol_id, lnum, torture); | 917 | if (dst_leb_clean) { |
907 | if (err) | 918 | ensure_wear_leveling(ubi, 1); |
908 | goto out_ro; | 919 | } else { |
920 | err = do_sync_erase(ubi, e2, vol_id, lnum, torture); | ||
921 | if (err) | ||
922 | goto out_ro; | ||
923 | } | ||
909 | 924 | ||
910 | mutex_unlock(&ubi->move_mutex); | 925 | mutex_unlock(&ubi->move_mutex); |
911 | return 0; | 926 | return 0; |