diff options
author | Sergei Shtylylov <sshtylyov@ru.mvista.com> | 2005-12-15 15:34:30 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-12-15 17:22:45 -0500 |
commit | 7b6666530e2736f190a2629c8abe34275054449f (patch) | |
tree | 677a4dc1ba95a98c6c6a98949fdcb0bca95ba1cf /sound | |
parent | 2f40fb72a2121da44c35f2588ee9abce1dffa2a9 (diff) |
[PATCH] Au1550 AC'97 OSS driver spinlock fixes
We have found some issues with Au1550 AC'97 OSS driver in 2.6
(sound/oss/au1550_ac97.c), though it also should concern 2.4 driver
(drivers/sound/au1550_psc.c).
start_dac() grabs a spinlock already held by its caller, au1550_write().
This doesn't show up with the standard UP spinlock impelmentation but when
the different one (mutex based) is in use, a lockup happens.
And the interrupt handlers also didn't grab the spinlock -- that's OK in
the usual kernel but not when the IRQ handlers are threaded. So, they're
grabbing the spinlock now (as every correct interrupt handler should do).
Signed-off-by: Konstantin Baidarov <kbaidarov@ru.mvista.com>
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/oss/au1550_ac97.c | 29 |
1 files changed, 18 insertions, 11 deletions
diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c index 6b46a8a4b1cc..b963c550dae6 100644 --- a/sound/oss/au1550_ac97.c +++ b/sound/oss/au1550_ac97.c | |||
@@ -578,17 +578,15 @@ set_recv_slots(int num_channels) | |||
578 | } while ((stat & PSC_AC97STAT_DR) == 0); | 578 | } while ((stat & PSC_AC97STAT_DR) == 0); |
579 | } | 579 | } |
580 | 580 | ||
581 | /* Hold spinlock for both start_dac() and start_adc() calls */ | ||
581 | static void | 582 | static void |
582 | start_dac(struct au1550_state *s) | 583 | start_dac(struct au1550_state *s) |
583 | { | 584 | { |
584 | struct dmabuf *db = &s->dma_dac; | 585 | struct dmabuf *db = &s->dma_dac; |
585 | unsigned long flags; | ||
586 | 586 | ||
587 | if (!db->stopped) | 587 | if (!db->stopped) |
588 | return; | 588 | return; |
589 | 589 | ||
590 | spin_lock_irqsave(&s->lock, flags); | ||
591 | |||
592 | set_xmit_slots(db->num_channels); | 590 | set_xmit_slots(db->num_channels); |
593 | au_writel(PSC_AC97PCR_TC, PSC_AC97PCR); | 591 | au_writel(PSC_AC97PCR_TC, PSC_AC97PCR); |
594 | au_sync(); | 592 | au_sync(); |
@@ -598,8 +596,6 @@ start_dac(struct au1550_state *s) | |||
598 | au1xxx_dbdma_start(db->dmanr); | 596 | au1xxx_dbdma_start(db->dmanr); |
599 | 597 | ||
600 | db->stopped = 0; | 598 | db->stopped = 0; |
601 | |||
602 | spin_unlock_irqrestore(&s->lock, flags); | ||
603 | } | 599 | } |
604 | 600 | ||
605 | static void | 601 | static void |
@@ -718,7 +714,6 @@ prog_dmabuf_dac(struct au1550_state *s) | |||
718 | } | 714 | } |
719 | 715 | ||
720 | 716 | ||
721 | /* hold spinlock for the following */ | ||
722 | static void | 717 | static void |
723 | dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) | 718 | dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) |
724 | { | 719 | { |
@@ -726,6 +721,8 @@ dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
726 | struct dmabuf *db = &s->dma_dac; | 721 | struct dmabuf *db = &s->dma_dac; |
727 | u32 ac97c_stat; | 722 | u32 ac97c_stat; |
728 | 723 | ||
724 | spin_lock(&s->lock); | ||
725 | |||
729 | ac97c_stat = au_readl(PSC_AC97STAT); | 726 | ac97c_stat = au_readl(PSC_AC97STAT); |
730 | if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE)) | 727 | if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE)) |
731 | pr_debug("AC97C status = 0x%08x\n", ac97c_stat); | 728 | pr_debug("AC97C status = 0x%08x\n", ac97c_stat); |
@@ -747,6 +744,8 @@ dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
747 | /* wake up anybody listening */ | 744 | /* wake up anybody listening */ |
748 | if (waitqueue_active(&db->wait)) | 745 | if (waitqueue_active(&db->wait)) |
749 | wake_up(&db->wait); | 746 | wake_up(&db->wait); |
747 | |||
748 | spin_unlock(&s->lock); | ||
750 | } | 749 | } |
751 | 750 | ||
752 | 751 | ||
@@ -758,6 +757,8 @@ adc_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
758 | u32 obytes; | 757 | u32 obytes; |
759 | char *obuf; | 758 | char *obuf; |
760 | 759 | ||
760 | spin_lock(&s->lock); | ||
761 | |||
761 | /* Pull the buffer from the dma queue. | 762 | /* Pull the buffer from the dma queue. |
762 | */ | 763 | */ |
763 | au1xxx_dbdma_get_dest(dp->dmanr, (void *)(&obuf), &obytes); | 764 | au1xxx_dbdma_get_dest(dp->dmanr, (void *)(&obuf), &obytes); |
@@ -765,6 +766,7 @@ adc_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
765 | if ((dp->count + obytes) > dp->dmasize) { | 766 | if ((dp->count + obytes) > dp->dmasize) { |
766 | /* Overrun. Stop ADC and log the error | 767 | /* Overrun. Stop ADC and log the error |
767 | */ | 768 | */ |
769 | spin_unlock(&s->lock); | ||
768 | stop_adc(s); | 770 | stop_adc(s); |
769 | dp->error++; | 771 | dp->error++; |
770 | err("adc overrun"); | 772 | err("adc overrun"); |
@@ -787,6 +789,7 @@ adc_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
787 | if (waitqueue_active(&dp->wait)) | 789 | if (waitqueue_active(&dp->wait)) |
788 | wake_up(&dp->wait); | 790 | wake_up(&dp->wait); |
789 | 791 | ||
792 | spin_unlock(&s->lock); | ||
790 | } | 793 | } |
791 | 794 | ||
792 | static loff_t | 795 | static loff_t |
@@ -1048,9 +1051,9 @@ au1550_read(struct file *file, char *buffer, size_t count, loff_t *ppos) | |||
1048 | /* wait for samples in ADC dma buffer | 1051 | /* wait for samples in ADC dma buffer |
1049 | */ | 1052 | */ |
1050 | do { | 1053 | do { |
1054 | spin_lock_irqsave(&s->lock, flags); | ||
1051 | if (db->stopped) | 1055 | if (db->stopped) |
1052 | start_adc(s); | 1056 | start_adc(s); |
1053 | spin_lock_irqsave(&s->lock, flags); | ||
1054 | avail = db->count; | 1057 | avail = db->count; |
1055 | if (avail <= 0) | 1058 | if (avail <= 0) |
1056 | __set_current_state(TASK_INTERRUPTIBLE); | 1059 | __set_current_state(TASK_INTERRUPTIBLE); |
@@ -1570,15 +1573,19 @@ au1550_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | |||
1570 | if (get_user(val, (int *) arg)) | 1573 | if (get_user(val, (int *) arg)) |
1571 | return -EFAULT; | 1574 | return -EFAULT; |
1572 | if (file->f_mode & FMODE_READ) { | 1575 | if (file->f_mode & FMODE_READ) { |
1573 | if (val & PCM_ENABLE_INPUT) | 1576 | if (val & PCM_ENABLE_INPUT) { |
1577 | spin_lock_irqsave(&s->lock, flags); | ||
1574 | start_adc(s); | 1578 | start_adc(s); |
1575 | else | 1579 | spin_unlock_irqrestore(&s->lock, flags); |
1580 | } else | ||
1576 | stop_adc(s); | 1581 | stop_adc(s); |
1577 | } | 1582 | } |
1578 | if (file->f_mode & FMODE_WRITE) { | 1583 | if (file->f_mode & FMODE_WRITE) { |
1579 | if (val & PCM_ENABLE_OUTPUT) | 1584 | if (val & PCM_ENABLE_OUTPUT) { |
1585 | spin_lock_irqsave(&s->lock, flags); | ||
1580 | start_dac(s); | 1586 | start_dac(s); |
1581 | else | 1587 | spin_unlock_irqrestore(&s->lock, flags); |
1588 | } else | ||
1582 | stop_dac(s); | 1589 | stop_dac(s); |
1583 | } | 1590 | } |
1584 | return 0; | 1591 | return 0; |