mtd: ubi: Fix worker handling

Fixes a bug found on thuban boards, which were for 2 years in
a long-term test with varying temperatures. They showed
problems in u-boot when attaching the ubi partition:

U-Boot# run flash_self_test
Booting from nand
set A...
UBI: attaching mtd1 to ubi0
UBI: scanning is finished
data abort
pc : [<87f97c3c>]          lr : [<87f97c28>]
reloc pc : [<8012cc3c>]    lr : [<8012cc28>]
sp : 85f686e8  ip : 00000020     fp : 000001f7
r10: 8605ce40  r9 : 85f68ef8     r8 : 0001f000
r7 : 00000001  r6 : 00000006     r5 : 0001f000  r4 : 85f6ecc0
r3 : 00000000  r2 : 44e35000     r1 : 87fcbcd4  r0 : 87fc755b
Flags: nZCv  IRQs off  FIQs on  Mode SVC_32
Resetting CPU ...

Reason is, that accidentially the U-Boot implementation
from __schedule_ubi_work() did not check the flag
ubi->thread_enabled and started with wearleveling work,
but ubi did not have setup all structures at this point
and crashes.

Solve this problem by splitting work scheduling and processing.

Signed-off-by: Richard Weinberger <richard@nod.at>
Signed-off-by: Heiko Schocher <hs@denx.de>
This commit is contained in:
Richard Weinberger 2018-02-08 07:29:52 +01:00 committed by Heiko Schocher
parent 02b0895c21
commit f82290afc8
3 changed files with 40 additions and 25 deletions

View file

@ -1060,15 +1060,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
#ifndef __UBOOT__
wake_up_process(ubi->bgt_thread);
#else
/*
* U-Boot special: We have no bgt_thread in U-Boot!
* So just call do_work() here directly.
*/
err = do_work(ubi);
if (err) {
ubi_err(ubi, "%s: work failed with error code %d",
ubi->bgt_name, err);
}
ubi_do_worker(ubi);
#endif
spin_unlock(&ubi->wl_lock);

View file

@ -1119,6 +1119,6 @@ static inline int idx2vol_id(const struct ubi_device *ubi, int idx)
}
#ifdef __UBOOT__
int do_work(struct ubi_device *ubi);
void ubi_do_worker(struct ubi_device *ubi);
#endif
#endif /* !__UBI_UBI_H__ */

View file

@ -191,11 +191,7 @@ static void wl_entry_destroy(struct ubi_device *ubi, struct ubi_wl_entry *e)
* This function returns zero in case of success and a negative error code in
* case of failure.
*/
#ifndef __UBOOT__
static int do_work(struct ubi_device *ubi)
#else
int do_work(struct ubi_device *ubi)
#endif
{
int err;
struct ubi_work *wrk;
@ -528,6 +524,33 @@ repeat:
spin_unlock(&ubi->wl_lock);
}
#ifdef __UBOOT__
void ubi_do_worker(struct ubi_device *ubi)
{
int err;
if (list_empty(&ubi->works) || ubi->ro_mode ||
!ubi->thread_enabled || ubi_dbg_is_bgt_disabled(ubi))
return;
spin_lock(&ubi->wl_lock);
while (!list_empty(&ubi->works)) {
/*
* call do_work, which executes exactly one work form the queue,
* including removeing it from the work queue.
*/
spin_unlock(&ubi->wl_lock);
err = do_work(ubi);
spin_lock(&ubi->wl_lock);
if (err) {
ubi_err(ubi, "%s: work failed with error code %d",
ubi->bgt_name, err);
}
}
spin_unlock(&ubi->wl_lock);
}
#endif
/**
* __schedule_ubi_work - schedule a work.
* @ubi: UBI device description object
@ -545,17 +568,6 @@ static void __schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)
#ifndef __UBOOT__
if (ubi->thread_enabled && !ubi_dbg_is_bgt_disabled(ubi))
wake_up_process(ubi->bgt_thread);
#else
int err;
/*
* U-Boot special: We have no bgt_thread in U-Boot!
* So just call do_work() here directly.
*/
err = do_work(ubi);
if (err) {
ubi_err(ubi, "%s: work failed with error code %d",
ubi->bgt_name, err);
}
#endif
spin_unlock(&ubi->wl_lock);
}
@ -610,6 +622,10 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
wl_wrk->torture = torture;
schedule_ubi_work(ubi, wl_wrk);
#ifdef __UBOOT__
ubi_do_worker(ubi);
#endif
return 0;
}
@ -1011,8 +1027,15 @@ static int ensure_wear_leveling(struct ubi_device *ubi, int nested)
wrk->func = &wear_leveling_worker;
if (nested)
__schedule_ubi_work(ubi, wrk);
#ifndef __UBOOT__
else
schedule_ubi_work(ubi, wrk);
#else
else {
schedule_ubi_work(ubi, wrk);
ubi_do_worker(ubi);
}
#endif
return err;
out_cancel: