aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Hogan <james.hogan@imgtec.com>2014-12-01 07:55:10 -0500
committerMauro Carvalho Chehab <mchehab@osg.samsung.com>2014-12-04 12:39:32 -0500
commitac03086067a5524ae9e020ba5712a208c67b2736 (patch)
tree99962489f95ad6219a5db52a45acc0e168fb4f5d
parentea0de4ec5489da0fe738b274effac4f950e93d76 (diff)
[media] img-ir/hw: Fix potential deadlock stopping timer
The end timer is used for switching back from repeat code timings when no repeat codes have been received for a certain amount of time. When the protocol is changed, the end timer is deleted synchronously with del_timer_sync(), however this takes place while holding the main spin lock, and the timer handler also needs to acquire the spin lock. This opens the possibility of a deadlock on an SMP system if the protocol is changed just as the repeat timer is expiring. One CPU could end up in img_ir_set_decoder() holding the lock and waiting for the end timer to complete, while the other CPU is stuck in the timer handler spinning on the lock held by the first CPU. Lockdep also spots a possible lock inversion in the same code, since img_ir_set_decoder() acquires the img-ir lock before the timer lock, but the timer handler will try and acquire them the other way around: ========================================================= [ INFO: possible irq lock inversion dependency detected ] 3.18.0-rc5+ #957 Not tainted --------------------------------------------------------- swapper/0/0 just changed the state of lock: (((&hw->end_timer))){+.-...}, at: [<4006ae5c>] _call_timer_fn+0x0/0xfc but this lock was taken by another, HARDIRQ-safe lock in the past: (&(&priv->lock)->rlock#2){-.....} and interrupts could create inverse lock ordering between them. other info that might help us debug this: Possible interrupt unsafe locking scenario: CPU0 CPU1 ---- ---- lock(((&hw->end_timer))); local_irq_disable(); lock(&(&priv->lock)->rlock#2); lock(((&hw->end_timer))); <Interrupt> lock(&(&priv->lock)->rlock#2); *** DEADLOCK *** This is fixed by releasing the main spin lock while performing the del_timer_sync() call. The timer is prevented from restarting before the lock is reacquired by a new "stopping" flag which img_ir_handle_data() checks before updating the timer. --------------------------------------------------------- swapper/0/0 just changed the state of lock: (((&hw->end_timer))){+.-...}, at: [<4006ae5c>] _call_timer_fn+0x0/0xfc but this lock was taken by another, HARDIRQ-safe lock in the past: (&(&priv->lock)->rlock#2){-.....} and interrupts could create inverse lock ordering between them. other info that might help us debug this: Possible interrupt unsafe locking scenario: CPU0 CPU1 ---- ---- lock(((&hw->end_timer))); local_irq_disable(); lock(&(&priv->lock)->rlock#2); lock(((&hw->end_timer))); <Interrupt> lock(&(&priv->lock)->rlock#2); *** DEADLOCK *** This is fixed by releasing the main spin lock while performing the del_timer_sync() call. The timer is prevented from restarting before the lock is reacquired by a new "stopping" flag which img_ir_handle_data() checks before updating the timer. Signed-off-by: James Hogan <james.hogan@imgtec.com> Cc: Sifan Naeem <sifan.naeem@imgtec.com> Cc: <stable@vger.kernel.org> # v3.15+ Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
-rw-r--r--drivers/media/rc/img-ir/img-ir-hw.c22
-rw-r--r--drivers/media/rc/img-ir/img-ir-hw.h3
2 files changed, 22 insertions, 3 deletions
diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c
index 9db065344b41..2fd47c9bf5d8 100644
--- a/drivers/media/rc/img-ir/img-ir-hw.c
+++ b/drivers/media/rc/img-ir/img-ir-hw.c
@@ -530,6 +530,22 @@ static void img_ir_set_decoder(struct img_ir_priv *priv,
530 u32 ir_status, irq_en; 530 u32 ir_status, irq_en;
531 spin_lock_irq(&priv->lock); 531 spin_lock_irq(&priv->lock);
532 532
533 /*
534 * First record that the protocol is being stopped so that the end timer
535 * isn't restarted while we're trying to stop it.
536 */
537 hw->stopping = true;
538
539 /*
540 * Release the lock to stop the end timer, since the end timer handler
541 * acquires the lock and we don't want to deadlock waiting for it.
542 */
543 spin_unlock_irq(&priv->lock);
544 del_timer_sync(&hw->end_timer);
545 spin_lock_irq(&priv->lock);
546
547 hw->stopping = false;
548
533 /* switch off and disable interrupts */ 549 /* switch off and disable interrupts */
534 img_ir_write(priv, IMG_IR_CONTROL, 0); 550 img_ir_write(priv, IMG_IR_CONTROL, 0);
535 irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE); 551 irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
@@ -547,8 +563,7 @@ static void img_ir_set_decoder(struct img_ir_priv *priv,
547 img_ir_read(priv, IMG_IR_DATA_LW); 563 img_ir_read(priv, IMG_IR_DATA_LW);
548 img_ir_read(priv, IMG_IR_DATA_UP); 564 img_ir_read(priv, IMG_IR_DATA_UP);
549 565
550 /* stop the end timer and switch back to normal mode */ 566 /* switch back to normal mode */
551 del_timer_sync(&hw->end_timer);
552 hw->mode = IMG_IR_M_NORMAL; 567 hw->mode = IMG_IR_M_NORMAL;
553 568
554 /* clear the wakeup scancode filter */ 569 /* clear the wakeup scancode filter */
@@ -819,7 +834,8 @@ static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw)
819 } 834 }
820 835
821 836
822 if (dec->repeat) { 837 /* we mustn't update the end timer while trying to stop it */
838 if (dec->repeat && !hw->stopping) {
823 unsigned long interval; 839 unsigned long interval;
824 840
825 img_ir_begin_repeat(priv); 841 img_ir_begin_repeat(priv);
diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h
index 8fcc16c32c5b..307ddcd1a99e 100644
--- a/drivers/media/rc/img-ir/img-ir-hw.h
+++ b/drivers/media/rc/img-ir/img-ir-hw.h
@@ -214,6 +214,8 @@ enum img_ir_mode {
214 * @flags: IMG_IR_F_*. 214 * @flags: IMG_IR_F_*.
215 * @filters: HW filters (derived from scancode filters). 215 * @filters: HW filters (derived from scancode filters).
216 * @mode: Current decode mode. 216 * @mode: Current decode mode.
217 * @stopping: Indicates that decoder is being taken down and timers
218 * should not be restarted.
217 * @suspend_irqen: Saved IRQ enable mask over suspend. 219 * @suspend_irqen: Saved IRQ enable mask over suspend.
218 */ 220 */
219struct img_ir_priv_hw { 221struct img_ir_priv_hw {
@@ -229,6 +231,7 @@ struct img_ir_priv_hw {
229 struct img_ir_filter filters[RC_FILTER_MAX]; 231 struct img_ir_filter filters[RC_FILTER_MAX];
230 232
231 enum img_ir_mode mode; 233 enum img_ir_mode mode;
234 bool stopping;
232 u32 suspend_irqen; 235 u32 suspend_irqen;
233}; 236};
234 237