Skip to content

Commit 5580cda

Browse files
martenlirichardweinberger
authored andcommitted
ubi: wl: Close down wear-leveling before nand is suspended
If a reboot/shutdown signal with double force (-ff) is triggered when the erase worker or wear-leveling worker function runs we may end up in a race condition since the MTD device gets a reboot notification and suspends the nand flash before the erase or wear-leveling is done. This will reject all accesses to the flash with -EBUSY. Sequence for the erase worker function: systemctl reboot -ff ubi_thread do_work __do_sys_reboot blocking_notifier_call_chain mtd_reboot_notifier nand_shutdown nand_suspend __erase_worker ubi_sync_erase mtd_erase nand_erase_nand # Blocked by suspended chip nand_get_device => EBUSY Similar sequence for the wear-leveling function: systemctl reboot -ff ubi_thread do_work __do_sys_reboot blocking_notifier_call_chain mtd_reboot_notifier nand_shutdown nand_suspend wear_leveling_worker ubi_eba_copy_leb ubi_io_write mtd_write nand_write_oob # Blocked by suspended chip nand_get_device => EBUSY systemd-shutdown[1]: Rebooting. ubi0 error: ubi_io_write: error -16 while writing 2048 bytes to PEB CPU: 1 PID: 82 Comm: ubi_bgt0d Kdump: loaded Tainted: G O (unwind_backtrace) from [<80107b9f>] (show_stack+0xb/0xc) (show_stack) from [<8033641f>] (dump_stack_lvl+0x2b/0x34) (dump_stack_lvl) from [<803b7f3f>] (ubi_io_write+0x3ab/0x4a8) (ubi_io_write) from [<803b817d>] (ubi_io_write_vid_hdr+0x71/0xb4) (ubi_io_write_vid_hdr) from [<803b6971>] (ubi_eba_copy_leb+0x195/0x2f0) (ubi_eba_copy_leb) from [<803b939b>] (wear_leveling_worker+0x2ff/0x738) (wear_leveling_worker) from [<803b86ef>] (do_work+0x5b/0xb0) (do_work) from [<803b9ee1>] (ubi_thread+0xb1/0x11c) (ubi_thread) from [<8012c113>] (kthread+0x11b/0x134) (kthread) from [<80100139>] (ret_from_fork+0x11/0x38) Exception stack(0x80c43fb0 to 0x80c43ff8) ... ubi0 error: ubi_dump_flash: err -16 while reading 2048 bytes from PEB ubi0 error: wear_leveling_worker: error -16 while moving PEB 246 to PEB ubi0 warning: ubi_ro_mode.part.0: switch to read-only mode ... ubi0 error: do_work: work failed with error code -16 ubi0 error: ubi_thread: ubi_bgt0d: work failed with error code -16 ... Kernel panic - not syncing: Software Watchdog Timer expired Add a reboot notification for the ubi/wear-leveling to shutdown any potential flash work actions before the nand is suspended. Signed-off-by: Mårten Lindahl <[email protected]> Signed-off-by: Richard Weinberger <[email protected]>
1 parent cb33ade commit 5580cda

File tree

2 files changed

+23
-0
lines changed

2 files changed

+23
-0
lines changed

drivers/mtd/ubi/ubi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ struct ubi_debug_info {
549549
* @peb_buf: a buffer of PEB size used for different purposes
550550
* @buf_mutex: protects @peb_buf
551551
* @ckvol_mutex: serializes static volume checking when opening
552+
* @wl_reboot_notifier: close all wear-leveling work before reboot
552553
*
553554
* @dbg: debugging information for this UBI device
554555
*/
@@ -651,6 +652,7 @@ struct ubi_device {
651652
void *peb_buf;
652653
struct mutex buf_mutex;
653654
struct mutex ckvol_mutex;
655+
struct notifier_block wl_reboot_notifier;
654656

655657
struct ubi_debug_info dbg;
656658
};

drivers/mtd/ubi/wl.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
#include <linux/crc32.h>
9090
#include <linux/freezer.h>
9191
#include <linux/kthread.h>
92+
#include <linux/reboot.h>
9293
#include "ubi.h"
9394
#include "wl.h"
9495

@@ -127,6 +128,8 @@ static int self_check_in_wl_tree(const struct ubi_device *ubi,
127128
struct ubi_wl_entry *e, struct rb_root *root);
128129
static int self_check_in_pq(const struct ubi_device *ubi,
129130
struct ubi_wl_entry *e);
131+
static int ubi_wl_reboot_notifier(struct notifier_block *n,
132+
unsigned long state, void *cmd);
130133

131134
/**
132135
* wl_tree_add - add a wear-leveling entry to a WL RB-tree.
@@ -1950,6 +1953,13 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai)
19501953
if (!ubi->ro_mode && !ubi->fm_disabled)
19511954
ubi_ensure_anchor_pebs(ubi);
19521955
#endif
1956+
1957+
if (!ubi->wl_reboot_notifier.notifier_call) {
1958+
ubi->wl_reboot_notifier.notifier_call = ubi_wl_reboot_notifier;
1959+
ubi->wl_reboot_notifier.priority = 1; /* Higher than MTD */
1960+
register_reboot_notifier(&ubi->wl_reboot_notifier);
1961+
}
1962+
19531963
return 0;
19541964

19551965
out_free:
@@ -1995,6 +2005,17 @@ void ubi_wl_close(struct ubi_device *ubi)
19952005
kfree(ubi->lookuptbl);
19962006
}
19972007

2008+
static int ubi_wl_reboot_notifier(struct notifier_block *n,
2009+
unsigned long state, void *cmd)
2010+
{
2011+
struct ubi_device *ubi;
2012+
2013+
ubi = container_of(n, struct ubi_device, wl_reboot_notifier);
2014+
ubi_wl_close(ubi);
2015+
2016+
return NOTIFY_DONE;
2017+
}
2018+
19982019
/**
19992020
* self_check_ec - make sure that the erase counter of a PEB is correct.
20002021
* @ubi: UBI device description object

0 commit comments

Comments
 (0)