* RFC: fixup for mtdblock issue with erase warnings @ 2017-04-26 17:46 Ben Dooks 2017-04-26 17:46 ` [PATCH 1/1] mtd: mtdblock: avoid __might_sleep warnings in mtd_erase Ben Dooks 0 siblings, 1 reply; 5+ messages in thread From: Ben Dooks @ 2017-04-26 17:46 UTC (permalink / raw) To: linux-kernel, David Woodhouse, Brian Norris, linux-mtd We have been running into a warning from __might_sleep() in the cfi erase code. We think this is due to the mtdblock driver trying to sleep when an operation is going to complete from the _erase callback. If this patch is acceptable, it should probably go into stable as well as the current release. Note, currently being tested on v4.4.y ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/1] mtd: mtdblock: avoid __might_sleep warnings in mtd_erase 2017-04-26 17:46 RFC: fixup for mtdblock issue with erase warnings Ben Dooks @ 2017-04-26 17:46 ` Ben Dooks 2017-04-26 18:18 ` [Linux-kernel] " Ben Hutchings 0 siblings, 1 reply; 5+ messages in thread From: Ben Dooks @ 2017-04-26 17:46 UTC (permalink / raw) To: linux-kernel, David Woodhouse, Brian Norris, linux-mtd; +Cc: Ben Dooks The mtd_erase() call can hit code that will trigger warnings from __might_sleep(), such as the do_erase_oneblock() function on the cfi_cmdset_0002.c file. This is due to some of the erase functions doing the work in the thread they are called in, which means that the erase_write() should only go into TASK_INTERRUPTIBLE once the mtd_erase call has returned. Note, this still does not check if the erase->state is not set to an error. This should stop the following warning being produced: ] WARNING: CPU: 0 PID: 23 at kernel/sched/core.c:7588 __might_sleep+0x84/0x9c() ] do not call blocking ops when !TASK_RUNNING; state=1 set at [<8029c724>] erase_write+0x0/0x178 ] Modules linked in: ] CPU: 0 PID: 23 Comm: kworker/0:1 Tainted: G W 4.4.62-dev-ic-9Feb2017-47164-g95b1e7c09ff3 #855 ] Hardware name: NVIDIA Tegra SoC (Flattened Device Tree) ] Workqueue: mtdblock9 mtd_blktrans_work ] [<8001eddc>] (unwind_backtrace) from [<8001b2fc>] (show_stack+0x10/0x14) ] [<8001b2fc>] (show_stack) from [<80213948>] (dump_stack+0x80/0x9c) ] [<80213948>] (dump_stack) from [<800287d8>] (warn_slowpath_common+0x80/0xac) ] [<800287d8>] (warn_slowpath_common) from [<80028830>] (warn_slowpath_fmt+0x2c/0x3c) ] [<80028830>] (warn_slowpath_fmt) from [<800448ec>] (__might_sleep+0x84/0x9c) ] [<800448ec>] (__might_sleep) from [<8047a714>] (mutex_lock+0x18/0x3c) ] [<8047a714>] (mutex_lock) from [<802a22dc>] (do_erase_oneblock+0x64/0x3b8) ] [<802a22dc>] (do_erase_oneblock) from [<8029d628>] (cfi_varsize_frob+0x144/0x1dc) ] [<8029d628>] (cfi_varsize_frob) from [<8029e930>] (cfi_amdstd_erase_varsize+0x28/0x50) ] [<8029e930>] (cfi_amdstd_erase_varsize) from [<80299370>] (part_erase+0x2c/0x74) ] [<80299370>] (part_erase) from [<8029c7e0>] (erase_write+0xbc/0x178) ] [<8029c7e0>] (erase_write) from [<8029c8c4>] (write_cached_data+0x28/0x3c) ] [<8029c8c4>] (write_cached_data) from [<8029cc70>] (mtdblock_writesect+0x178/0x1bc) ] [<8029cc70>] (mtdblock_writesect) from [<8029bf68>] (mtd_blktrans_work+0x2ec/0x360) ] [<8029bf68>] (mtd_blktrans_work) from [<8003b314>] (process_one_work+0x19c/0x310) ] [<8003b314>] (process_one_work) from [<8003c174>] (worker_thread+0x2e8/0x3d4) ] [<8003c174>] (worker_thread) from [<800403d0>] (kthread+0xe4/0xf8) ] [<800403d0>] (kthread) from [<80017478>] (ret_from_fork+0x14/0x3c) ] ---[ end trace ca8c94fad04126d2 ]--- [ben.hutchings@codethink.co.uk: suggest use of wait_woken()] Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk> --- drivers/mtd/mtdblock.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index bb4c14f83c75..4b1cd464f919 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -68,6 +68,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, DECLARE_WAITQUEUE(wait, current); wait_queue_head_t wait_q; size_t retlen; + long timeout = 1; int ret; /* @@ -81,12 +82,10 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, erase.len = len; erase.priv = (u_long)&wait_q; - set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&wait_q, &wait); ret = mtd_erase(mtd, &erase); if (ret) { - set_current_state(TASK_RUNNING); remove_wait_queue(&wait_q, &wait); printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " "on \"%s\" failed\n", @@ -94,8 +93,18 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, return ret; } - schedule(); /* Wait for erase to finish. */ + if (erase->state != MTD_ERASE_DONE && + erase->state != MTD_ERASE_FAILED) + timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, + MAX_SCHEDULE_TIMEOUT); + remove_wait_queue(&wait_q, &wait); + if (timeout == 0) { + printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " + "on \"%s\" failed\n", + pos, len, mtd->name); + return -ETIMEDOUT; + } /* * Next, write the data to flash. -- 2.11.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [Linux-kernel] [PATCH 1/1] mtd: mtdblock: avoid __might_sleep warnings in mtd_erase 2017-04-26 17:46 ` [PATCH 1/1] mtd: mtdblock: avoid __might_sleep warnings in mtd_erase Ben Dooks @ 2017-04-26 18:18 ` Ben Hutchings 2017-04-27 8:27 ` Ben Dooks 0 siblings, 1 reply; 5+ messages in thread From: Ben Hutchings @ 2017-04-26 18:18 UTC (permalink / raw) To: Ben Dooks; +Cc: linux-kernel, David Woodhouse, Brian Norris, linux-mtd On Wed, 2017-04-26 at 18:46 +0100, Ben Dooks wrote: > The mtd_erase() call can hit code that will trigger warnings > from __might_sleep(), such as the do_erase_oneblock() function > on the cfi_cmdset_0002.c file. > > This is due to some of the erase functions doing the work in the > thread they are called in, which means that the erase_write() > should only go into TASK_INTERRUPTIBLE once the mtd_erase call > has returned. [...] > diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c > index bb4c14f83c75..4b1cd464f919 100644 > --- a/drivers/mtd/mtdblock.c > +++ b/drivers/mtd/mtdblock.c > @@ -68,6 +68,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, > DECLARE_WAITQUEUE(wait, current); > wait_queue_head_t wait_q; > size_t retlen; > + long timeout = 1; > int ret; > > /* > @@ -81,12 +82,10 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, > erase.len = len; > erase.priv = (u_long)&wait_q; > > - set_current_state(TASK_INTERRUPTIBLE); > add_wait_queue(&wait_q, &wait); > > ret = mtd_erase(mtd, &erase); > if (ret) { > - set_current_state(TASK_RUNNING); > remove_wait_queue(&wait_q, &wait); > printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " > "on \"%s\" failed\n", > @@ -94,8 +93,18 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, > return ret; > } > > - schedule(); /* Wait for erase to finish. */ > + if (erase->state != MTD_ERASE_DONE && > + erase->state != MTD_ERASE_FAILED) > + timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, > + MAX_SCHEDULE_TIMEOUT); If mtd_erase() returns 0 then the wait queue either has been woken or will be woken. Since we're already on the wait queue, it's safe to wait unconditionally. I think that making the wait conditional results in a race condition that could result in returning too early. Also there seems to be another existing problem here: if this is interrupted and we return early then the driver can use-after-free the wait queue and erase structure. mtdchar waits uninterruptibly for exactly this reason. We really ought to have an always-synchronous wrapper for mtd_erase(), because this seems to be hard to get right... Ben. > remove_wait_queue(&wait_q, &wait); > + if (timeout == 0) { > + printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " > + "on \"%s\" failed\n", > + pos, len, mtd->name); > + return -ETIMEDOUT; > + } > > /* > * Next, write the data to flash. -- Ben Hutchings Software Developer, Codethink Ltd. ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [Linux-kernel] [PATCH 1/1] mtd: mtdblock: avoid __might_sleep warnings in mtd_erase 2017-04-26 18:18 ` [Linux-kernel] " Ben Hutchings @ 2017-04-27 8:27 ` Ben Dooks 2017-04-27 12:04 ` Ben Hutchings 0 siblings, 1 reply; 5+ messages in thread From: Ben Dooks @ 2017-04-27 8:27 UTC (permalink / raw) To: Ben Hutchings; +Cc: linux-kernel, David Woodhouse, Brian Norris, linux-mtd On 26/04/17 19:18, Ben Hutchings wrote: > On Wed, 2017-04-26 at 18:46 +0100, Ben Dooks wrote: >> The mtd_erase() call can hit code that will trigger warnings >> from __might_sleep(), such as the do_erase_oneblock() function >> on the cfi_cmdset_0002.c file. >> >> This is due to some of the erase functions doing the work in the >> thread they are called in, which means that the erase_write() >> should only go into TASK_INTERRUPTIBLE once the mtd_erase call >> has returned. > [...] >> diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c >> index bb4c14f83c75..4b1cd464f919 100644 >> --- a/drivers/mtd/mtdblock.c >> +++ b/drivers/mtd/mtdblock.c >> @@ -68,6 +68,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, >> DECLARE_WAITQUEUE(wait, current); >> wait_queue_head_t wait_q; >> size_t retlen; >> + long timeout = 1; >> int ret; >> >> /* >> @@ -81,12 +82,10 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, >> erase.len = len; >> erase.priv = (u_long)&wait_q; >> >> - set_current_state(TASK_INTERRUPTIBLE); >> add_wait_queue(&wait_q, &wait); >> >> ret = mtd_erase(mtd, &erase); >> if (ret) { >> - set_current_state(TASK_RUNNING); >> remove_wait_queue(&wait_q, &wait); >> printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " >> "on \"%s\" failed\n", >> @@ -94,8 +93,18 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, >> return ret; >> } >> >> - schedule(); /* Wait for erase to finish. */ >> + if (erase->state != MTD_ERASE_DONE && >> + erase->state != MTD_ERASE_FAILED) >> + timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, >> + MAX_SCHEDULE_TIMEOUT); > > If mtd_erase() returns 0 then the wait queue either has been woken or > will be woken. Since we're already on the wait queue, it's safe to wait > unconditionally. > > I think that making the wait conditional results in a race condition > that could result in returning too early. > > Also there seems to be another existing problem here: if this is > interrupted and we return early then the driver can use-after-free the > wait queue and erase structure. mtdchar waits uninterruptibly for > exactly this reason. > > We really ought to have an always-synchronous wrapper for mtd_erase(), > because this seems to be hard to get right... > > Ben. Ok, so add something like mtd_erase_sync() which uses mtd_erase and then waits for the completion and then use it for both mtdblock and mtdchar? -- Ben Dooks http://www.codethink.co.uk/ Senior Engineer Codethink - Providing Genius ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [Linux-kernel] [PATCH 1/1] mtd: mtdblock: avoid __might_sleep warnings in mtd_erase 2017-04-27 8:27 ` Ben Dooks @ 2017-04-27 12:04 ` Ben Hutchings 0 siblings, 0 replies; 5+ messages in thread From: Ben Hutchings @ 2017-04-27 12:04 UTC (permalink / raw) To: Ben Dooks; +Cc: linux-kernel, David Woodhouse, Brian Norris, linux-mtd On Thu, 2017-04-27 at 09:27 +0100, Ben Dooks wrote: > On 26/04/17 19:18, Ben Hutchings wrote: > > On Wed, 2017-04-26 at 18:46 +0100, Ben Dooks wrote: > >> The mtd_erase() call can hit code that will trigger warnings > >> from __might_sleep(), such as the do_erase_oneblock() function > >> on the cfi_cmdset_0002.c file. > >> > >> This is due to some of the erase functions doing the work in the > >> thread they are called in, which means that the erase_write() > >> should only go into TASK_INTERRUPTIBLE once the mtd_erase call > >> has returned. > > [...] > >> diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c > >> index bb4c14f83c75..4b1cd464f919 100644 > >> --- a/drivers/mtd/mtdblock.c > >> +++ b/drivers/mtd/mtdblock.c > >> @@ -68,6 +68,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, > >> DECLARE_WAITQUEUE(wait, current); > >> wait_queue_head_t wait_q; > >> size_t retlen; > >> + long timeout = 1; > >> int ret; > >> > >> /* > >> @@ -81,12 +82,10 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, > >> erase.len = len; > >> erase.priv = (u_long)&wait_q; > >> > >> - set_current_state(TASK_INTERRUPTIBLE); > >> add_wait_queue(&wait_q, &wait); > >> > >> ret = mtd_erase(mtd, &erase); > >> if (ret) { > >> - set_current_state(TASK_RUNNING); > >> remove_wait_queue(&wait_q, &wait); > >> printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " > >> "on \"%s\" failed\n", > >> @@ -94,8 +93,18 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, > >> return ret; > >> } > >> > >> - schedule(); /* Wait for erase to finish. */ > >> + if (erase->state != MTD_ERASE_DONE && > >> + erase->state != MTD_ERASE_FAILED) > >> + timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, > >> + MAX_SCHEDULE_TIMEOUT); > > > > If mtd_erase() returns 0 then the wait queue either has been woken or > > will be woken. Since we're already on the wait queue, it's safe to wait > > unconditionally. > > > > I think that making the wait conditional results in a race condition > > that could result in returning too early. > > > > Also there seems to be another existing problem here: if this is > > interrupted and we return early then the driver can use-after-free the > > wait queue and erase structure. mtdchar waits uninterruptibly for > > exactly this reason. > > > > We really ought to have an always-synchronous wrapper for mtd_erase(), > > because this seems to be hard to get right... > > > > Ben. > > Ok, so add something like mtd_erase_sync() which uses mtd_erase and then > waits for the completion and then use it for both mtdblock and mtdchar? ...and any other callers that want synchronous behaviour, especially if they're doing it wrong now. Ben. -- Ben Hutchings Software Developer, Codethink Ltd. ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2017-04-27 12:05 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2017-04-26 17:46 RFC: fixup for mtdblock issue with erase warnings Ben Dooks 2017-04-26 17:46 ` [PATCH 1/1] mtd: mtdblock: avoid __might_sleep warnings in mtd_erase Ben Dooks 2017-04-26 18:18 ` [Linux-kernel] " Ben Hutchings 2017-04-27 8:27 ` Ben Dooks 2017-04-27 12:04 ` Ben Hutchings
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox