[media] cx88: Fix unsafe locking in suspend-resume
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>
This commit is contained in:
parent
cdcfe40a5f
commit
5ddfbbb9ca
2 changed files with 12 additions and 8 deletions
|
@ -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 cx8802_dev *dev = pci_get_drvdata(pci_dev);
|
||||||
struct cx88_core *core = dev->core;
|
struct cx88_core *core = dev->core;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
/* stop mpeg dma */
|
/* stop mpeg dma */
|
||||||
spin_lock(&dev->slock);
|
spin_lock_irqsave(&dev->slock, flags);
|
||||||
if (!list_empty(&dev->mpegq.active)) {
|
if (!list_empty(&dev->mpegq.active)) {
|
||||||
dprintk( 2, "suspend\n" );
|
dprintk( 2, "suspend\n" );
|
||||||
printk("%s: suspend mpeg\n", core->name);
|
printk("%s: suspend mpeg\n", core->name);
|
||||||
cx8802_stop_dma(dev);
|
cx8802_stop_dma(dev);
|
||||||
del_timer(&dev->mpegq.timeout);
|
del_timer(&dev->mpegq.timeout);
|
||||||
}
|
}
|
||||||
spin_unlock(&dev->slock);
|
spin_unlock_irqrestore(&dev->slock, flags);
|
||||||
|
|
||||||
/* FIXME -- shutdown device */
|
/* FIXME -- shutdown device */
|
||||||
cx88_shutdown(dev->core);
|
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 cx8802_dev *dev = pci_get_drvdata(pci_dev);
|
||||||
struct cx88_core *core = dev->core;
|
struct cx88_core *core = dev->core;
|
||||||
|
unsigned long flags;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (dev->state.disabled) {
|
if (dev->state.disabled) {
|
||||||
|
@ -584,12 +586,12 @@ static int cx8802_resume_common(struct pci_dev *pci_dev)
|
||||||
cx88_reset(dev->core);
|
cx88_reset(dev->core);
|
||||||
|
|
||||||
/* restart video+vbi capture */
|
/* restart video+vbi capture */
|
||||||
spin_lock(&dev->slock);
|
spin_lock_irqsave(&dev->slock, flags);
|
||||||
if (!list_empty(&dev->mpegq.active)) {
|
if (!list_empty(&dev->mpegq.active)) {
|
||||||
printk("%s: resume mpeg\n", core->name);
|
printk("%s: resume mpeg\n", core->name);
|
||||||
cx8802_restart_queue(dev,&dev->mpegq);
|
cx8802_restart_queue(dev,&dev->mpegq);
|
||||||
}
|
}
|
||||||
spin_unlock(&dev->slock);
|
spin_unlock_irqrestore(&dev->slock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 cx8800_dev *dev = pci_get_drvdata(pci_dev);
|
||||||
struct cx88_core *core = dev->core;
|
struct cx88_core *core = dev->core;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
/* stop video+vbi capture */
|
/* stop video+vbi capture */
|
||||||
spin_lock(&dev->slock);
|
spin_lock_irqsave(&dev->slock, flags);
|
||||||
if (!list_empty(&dev->vidq.active)) {
|
if (!list_empty(&dev->vidq.active)) {
|
||||||
printk("%s/0: suspend video\n", core->name);
|
printk("%s/0: suspend video\n", core->name);
|
||||||
stop_video_dma(dev);
|
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);
|
cx8800_stop_vbi_dma(dev);
|
||||||
del_timer(&dev->vbiq.timeout);
|
del_timer(&dev->vbiq.timeout);
|
||||||
}
|
}
|
||||||
spin_unlock(&dev->slock);
|
spin_unlock_irqrestore(&dev->slock, flags);
|
||||||
|
|
||||||
if (core->ir)
|
if (core->ir)
|
||||||
cx88_ir_stop(core);
|
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 cx8800_dev *dev = pci_get_drvdata(pci_dev);
|
||||||
struct cx88_core *core = dev->core;
|
struct cx88_core *core = dev->core;
|
||||||
|
unsigned long flags;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (dev->state.disabled) {
|
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);
|
cx_set(MO_PCI_INTMSK, core->pci_irqmask);
|
||||||
|
|
||||||
/* restart video+vbi capture */
|
/* restart video+vbi capture */
|
||||||
spin_lock(&dev->slock);
|
spin_lock_irqsave(&dev->slock, flags);
|
||||||
if (!list_empty(&dev->vidq.active)) {
|
if (!list_empty(&dev->vidq.active)) {
|
||||||
printk("%s/0: resume video\n", core->name);
|
printk("%s/0: resume video\n", core->name);
|
||||||
restart_video_queue(dev,&dev->vidq);
|
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);
|
printk("%s/0: resume vbi\n", core->name);
|
||||||
cx8800_restart_vbi_queue(dev,&dev->vbiq);
|
cx8800_restart_vbi_queue(dev,&dev->vbiq);
|
||||||
}
|
}
|
||||||
spin_unlock(&dev->slock);
|
spin_unlock_irqrestore(&dev->slock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue