[media] cx88: Fix unsafe locking in suspend-resume
authorAlexey Khoroshilov <khoroshilov@ispras.ru>
Sat, 13 Apr 2013 21:52:04 +0000 (18:52 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 22 Apr 2013 13:08:30 +0000 (10:08 -0300)
Legacy PCI suspend-resume handlers are called with interrupts enabled.

But cx8800_suspend/cx8800_resume and
cx8802_suspend_common/cx8802_resume_common use spin_lock/spin_unlock
functions to acquire dev->slock, while the same lock is acquired in the
corresponding irq-handlers: cx8800_irq and cx8802_irq.

That means a deadlock is possible if an interrupt happens while suspend
or resume owns the lock. The patch replaces spin_lock/spin_unlock with
spin_lock_irqsave/spin_unlock_irqrestore.

Found by Linux Driver Verification project (linuxtesting.org).

[mchehab@redhat.com: Fix CodingStyle]
Signed-off-by: Alexey Khoroshilov <khoroshilov@ispras.ru>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/pci/cx88/cx88-mpeg.c
drivers/media/pci/cx88/cx88-video.c

index c9d3182f79d57a0a9fd26b321ca27fbf66c0a9ce..2d3507eb48972046a5baa70772fe685df64bfbec 100644 (file)
@@ -532,16 +532,17 @@ static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state)
 {
        struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
        struct cx88_core *core = dev->core;
+       unsigned long flags;
 
        /* stop mpeg dma */
-       spin_lock(&dev->slock);
+       spin_lock_irqsave(&dev->slock, flags);
        if (!list_empty(&dev->mpegq.active)) {
                dprintk( 2, "suspend\n" );
                printk("%s: suspend mpeg\n", core->name);
                cx8802_stop_dma(dev);
                del_timer(&dev->mpegq.timeout);
        }
-       spin_unlock(&dev->slock);
+       spin_unlock_irqrestore(&dev->slock, flags);
 
        /* FIXME -- shutdown device */
        cx88_shutdown(dev->core);
@@ -558,6 +559,7 @@ static int cx8802_resume_common(struct pci_dev *pci_dev)
 {
        struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
        struct cx88_core *core = dev->core;
+       unsigned long flags;
        int err;
 
        if (dev->state.disabled) {
@@ -584,12 +586,12 @@ static int cx8802_resume_common(struct pci_dev *pci_dev)
        cx88_reset(dev->core);
 
        /* restart video+vbi capture */
-       spin_lock(&dev->slock);
+       spin_lock_irqsave(&dev->slock, flags);
        if (!list_empty(&dev->mpegq.active)) {
                printk("%s: resume mpeg\n", core->name);
                cx8802_restart_queue(dev,&dev->mpegq);
        }
-       spin_unlock(&dev->slock);
+       spin_unlock_irqrestore(&dev->slock, flags);
 
        return 0;
 }
index e3f6181d1044846c8aa87e3e23525328a66b26d8..1b00615fd3953a44966a07656180f4351dc7e43c 100644 (file)
@@ -1954,9 +1954,10 @@ static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state)
 {
        struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
        struct cx88_core *core = dev->core;
+       unsigned long flags;
 
        /* stop video+vbi capture */
-       spin_lock(&dev->slock);
+       spin_lock_irqsave(&dev->slock, flags);
        if (!list_empty(&dev->vidq.active)) {
                printk("%s/0: suspend video\n", core->name);
                stop_video_dma(dev);
@@ -1967,7 +1968,7 @@ static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state)
                cx8800_stop_vbi_dma(dev);
                del_timer(&dev->vbiq.timeout);
        }
-       spin_unlock(&dev->slock);
+       spin_unlock_irqrestore(&dev->slock, flags);
 
        if (core->ir)
                cx88_ir_stop(core);
@@ -1986,6 +1987,7 @@ static int cx8800_resume(struct pci_dev *pci_dev)
 {
        struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
        struct cx88_core *core = dev->core;
+       unsigned long flags;
        int err;
 
        if (dev->state.disabled) {
@@ -2016,7 +2018,7 @@ static int cx8800_resume(struct pci_dev *pci_dev)
        cx_set(MO_PCI_INTMSK, core->pci_irqmask);
 
        /* restart video+vbi capture */
-       spin_lock(&dev->slock);
+       spin_lock_irqsave(&dev->slock, flags);
        if (!list_empty(&dev->vidq.active)) {
                printk("%s/0: resume video\n", core->name);
                restart_video_queue(dev,&dev->vidq);
@@ -2025,7 +2027,7 @@ static int cx8800_resume(struct pci_dev *pci_dev)
                printk("%s/0: resume vbi\n", core->name);
                cx8800_restart_vbi_queue(dev,&dev->vbiq);
        }
-       spin_unlock(&dev->slock);
+       spin_unlock_irqrestore(&dev->slock, flags);
 
        return 0;
 }