| From 54ad00465dc99de209df11682bc8f5cf852b6638 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 16 Oct 2020 12:20:56 -0500 |
| Subject: mailbox: avoid timer start from callback |
| |
| From: Jassi Brar <jaswinder.singh@linaro.org> |
| |
| [ Upstream commit c7dacf5b0f32957b24ef29df1207dc2cd8307743 ] |
| |
| If the txdone is done by polling, it is possible for msg_submit() to start |
| the timer while txdone_hrtimer() callback is running. If the timer needs |
| recheduling, it could already be enqueued by the time hrtimer_forward_now() |
| is called, leading hrtimer to loudly complain. |
| |
| WARNING: CPU: 3 PID: 74 at kernel/time/hrtimer.c:932 hrtimer_forward+0xc4/0x110 |
| CPU: 3 PID: 74 Comm: kworker/u8:1 Not tainted 5.9.0-rc2-00236-gd3520067d01c-dirty #5 |
| Hardware name: Libre Computer AML-S805X-AC (DT) |
| Workqueue: events_freezable_power_ thermal_zone_device_check |
| pstate: 20000085 (nzCv daIf -PAN -UAO BTYPE=--) |
| pc : hrtimer_forward+0xc4/0x110 |
| lr : txdone_hrtimer+0xf8/0x118 |
| [...] |
| |
| This can be fixed by not starting the timer from the callback path. Which |
| requires the timer reloading as long as any message is queued on the |
| channel, and not just when current tx is not done yet. |
| |
| Fixes: 0cc67945ea59 ("mailbox: switch to hrtimer for tx_complete polling") |
| Reported-by: Da Xue <da@libre.computer> |
| Reviewed-by: Sudeep Holla <sudeep.holla@arm.com> |
| Tested-by: Sudeep Holla <sudeep.holla@arm.com> |
| Acked-by: Jerome Brunet <jbrunet@baylibre.com> |
| Tested-by: Jerome Brunet <jbrunet@baylibre.com> |
| Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/mailbox/mailbox.c | 12 +++++++----- |
| 1 file changed, 7 insertions(+), 5 deletions(-) |
| |
| diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c |
| index 0b821a5b2db84..3e7d4b20ab34f 100644 |
| --- a/drivers/mailbox/mailbox.c |
| +++ b/drivers/mailbox/mailbox.c |
| @@ -82,9 +82,12 @@ static void msg_submit(struct mbox_chan *chan) |
| exit: |
| spin_unlock_irqrestore(&chan->lock, flags); |
| |
| - if (!err && (chan->txdone_method & TXDONE_BY_POLL)) |
| - /* kick start the timer immediately to avoid delays */ |
| - hrtimer_start(&chan->mbox->poll_hrt, 0, HRTIMER_MODE_REL); |
| + /* kick start the timer immediately to avoid delays */ |
| + if (!err && (chan->txdone_method & TXDONE_BY_POLL)) { |
| + /* but only if not already active */ |
| + if (!hrtimer_active(&chan->mbox->poll_hrt)) |
| + hrtimer_start(&chan->mbox->poll_hrt, 0, HRTIMER_MODE_REL); |
| + } |
| } |
| |
| static void tx_tick(struct mbox_chan *chan, int r) |
| @@ -122,11 +125,10 @@ static enum hrtimer_restart txdone_hrtimer(struct hrtimer *hrtimer) |
| struct mbox_chan *chan = &mbox->chans[i]; |
| |
| if (chan->active_req && chan->cl) { |
| + resched = true; |
| txdone = chan->mbox->ops->last_tx_done(chan); |
| if (txdone) |
| tx_tick(chan, 0); |
| - else |
| - resched = true; |
| } |
| } |
| |
| -- |
| 2.25.1 |
| |