Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: opl3: circular locking in the snd_opl3_note_on() and snd_opl3_note_off()

Fix following circular locking in the opl3 driver.

=======================================================
[ INFO: possible circular locking dependency detected ]
2.6.32-rc3 #87
-------------------------------------------------------
swapper/0 is trying to acquire lock:
(&opl3->voice_lock){..-...}, at: [<cca748fe>] snd_opl3_note_off+0x1e/0xe0 [snd_opl3_synth]

but task is already holding lock:
(&opl3->sys_timer_lock){..-...}, at: [<cca75169>] snd_opl3_timer_func+0x19/0xc0 [snd_opl3_synth]

which lock already depends on the new lock.

the existing dependency chain (in reverse order) is:

-> #1 (&opl3->sys_timer_lock){..-...}:
[<c02461d5>] validate_chain+0xa25/0x1040
[<c0246aca>] __lock_acquire+0x2da/0xab0
[<c024731a>] lock_acquire+0x7a/0xa0
[<c044c300>] _spin_lock_irqsave+0x40/0x60
[<cca75046>] snd_opl3_note_on+0x686/0x790 [snd_opl3_synth]
[<cca68912>] snd_midi_process_event+0x322/0x590 [snd_seq_midi_emul]
[<cca74245>] snd_opl3_synth_event_input+0x15/0x20 [snd_opl3_synth]
[<cca4dcc0>] snd_seq_deliver_single_event+0x100/0x200 [snd_seq]
[<cca4de07>] snd_seq_deliver_event+0x47/0x1f0 [snd_seq]
[<cca4e50b>] snd_seq_dispatch_event+0x3b/0x140 [snd_seq]
[<cca5008c>] snd_seq_check_queue+0x10c/0x120 [snd_seq]
[<cca5037b>] snd_seq_enqueue_event+0x6b/0xe0 [snd_seq]
[<cca4e0fd>] snd_seq_client_enqueue_event+0xdd/0x100 [snd_seq]
[<cca4eb7a>] snd_seq_write+0xea/0x190 [snd_seq]
[<c02827b6>] vfs_write+0x96/0x160
[<c0282c9d>] sys_write+0x3d/0x70
[<c0202c45>] syscall_call+0x7/0xb

-> #0 (&opl3->voice_lock){..-...}:
[<c02467e6>] validate_chain+0x1036/0x1040
[<c0246aca>] __lock_acquire+0x2da/0xab0
[<c024731a>] lock_acquire+0x7a/0xa0
[<c044c300>] _spin_lock_irqsave+0x40/0x60
[<cca748fe>] snd_opl3_note_off+0x1e/0xe0 [snd_opl3_synth]
[<cca751f0>] snd_opl3_timer_func+0xa0/0xc0 [snd_opl3_synth]
[<c022ac46>] run_timer_softirq+0x166/0x1e0
[<c02269e8>] __do_softirq+0x78/0x110
[<c0226ac6>] do_softirq+0x46/0x50
[<c0226e26>] irq_exit+0x36/0x40
[<c0204bd2>] do_IRQ+0x42/0xb0
[<c020328e>] common_interrupt+0x2e/0x40
[<c021092f>] apm_cpu_idle+0x10f/0x290
[<c0201b11>] cpu_idle+0x21/0x40
[<c04443cd>] rest_init+0x4d/0x60
[<c055c835>] start_kernel+0x235/0x280
[<c055c066>] i386_start_kernel+0x66/0x70

other info that might help us debug this:

2 locks held by swapper/0:
#0: (&opl3->tlist){+.-...}, at: [<c022abd0>] run_timer_softirq+0xf0/0x1e0
#1: (&opl3->sys_timer_lock){..-...}, at: [<cca75169>] snd_opl3_timer_func+0x19/0xc0 [snd_opl3_synth]

stack backtrace:
Pid: 0, comm: swapper Not tainted 2.6.32-rc3 #87
Call Trace:
[<c0245188>] print_circular_bug+0xc8/0xd0
[<c02467e6>] validate_chain+0x1036/0x1040
[<c0247f14>] ? check_usage_forwards+0x54/0xd0
[<c0246aca>] __lock_acquire+0x2da/0xab0
[<c024731a>] lock_acquire+0x7a/0xa0
[<cca748fe>] ? snd_opl3_note_off+0x1e/0xe0 [snd_opl3_synth]
[<c044c300>] _spin_lock_irqsave+0x40/0x60
[<cca748fe>] ? snd_opl3_note_off+0x1e/0xe0 [snd_opl3_synth]
[<cca748fe>] snd_opl3_note_off+0x1e/0xe0 [snd_opl3_synth]
[<c044c307>] ? _spin_lock_irqsave+0x47/0x60
[<cca751f0>] snd_opl3_timer_func+0xa0/0xc0 [snd_opl3_synth]
[<c022ac46>] run_timer_softirq+0x166/0x1e0
[<c022abd0>] ? run_timer_softirq+0xf0/0x1e0
[<cca75150>] ? snd_opl3_timer_func+0x0/0xc0 [snd_opl3_synth]
[<c02269e8>] __do_softirq+0x78/0x110
[<c044c0fd>] ? _spin_unlock+0x1d/0x20
[<c025915f>] ? handle_level_irq+0xaf/0xe0
[<c0226ac6>] do_softirq+0x46/0x50
[<c0226e26>] irq_exit+0x36/0x40
[<c0204bd2>] do_IRQ+0x42/0xb0
[<c024463c>] ? trace_hardirqs_on_caller+0x12c/0x180
[<c020328e>] common_interrupt+0x2e/0x40
[<c0208d88>] ? default_idle+0x38/0x50
[<c021092f>] apm_cpu_idle+0x10f/0x290
[<c0201b11>] cpu_idle+0x21/0x40
[<c04443cd>] rest_init+0x4d/0x60
[<c055c835>] start_kernel+0x235/0x280
[<c055c210>] ? unknown_bootoption+0x0/0x210
[<c055c066>] i386_start_kernel+0x66/0x70

Signed-off-by: Krzysztof Helt <krzysztof.h1@wp.pl>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Krzysztof Helt and committed by
Takashi Iwai
8dce39b8 2bdf6633

+20 -8
+20 -8
sound/drivers/opl3/opl3_midi.c
··· 29 29 30 30 extern int use_internal_drums; 31 31 32 + static void snd_opl3_note_off_unsafe(void *p, int note, int vel, 33 + struct snd_midi_channel *chan); 32 34 /* 33 35 * The next table looks magical, but it certainly is not. Its values have 34 36 * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception ··· 244 242 int again = 0; 245 243 int i; 246 244 247 - spin_lock_irqsave(&opl3->sys_timer_lock, flags); 245 + spin_lock_irqsave(&opl3->voice_lock, flags); 248 246 for (i = 0; i < opl3->max_voices; i++) { 249 247 struct snd_opl3_voice *vp = &opl3->voices[i]; 250 248 if (vp->state > 0 && vp->note_off_check) { 251 249 if (vp->note_off == jiffies) 252 - snd_opl3_note_off(opl3, vp->note, 0, vp->chan); 250 + snd_opl3_note_off_unsafe(opl3, vp->note, 0, 251 + vp->chan); 253 252 else 254 253 again++; 255 254 } 256 255 } 256 + spin_unlock_irqrestore(&opl3->voice_lock, flags); 257 + 258 + spin_lock_irqsave(&opl3->sys_timer_lock, flags); 257 259 if (again) { 258 260 opl3->tlist.expires = jiffies + 1; /* invoke again */ 259 261 add_timer(&opl3->tlist); ··· 664 658 /* 665 659 * Release a note in response to a midi note off. 666 660 */ 667 - void snd_opl3_note_off(void *p, int note, int vel, struct snd_midi_channel *chan) 661 + static void snd_opl3_note_off_unsafe(void *p, int note, int vel, 662 + struct snd_midi_channel *chan) 668 663 { 669 664 struct snd_opl3 *opl3; 670 665 671 666 int voice; 672 667 struct snd_opl3_voice *vp; 673 - 674 - unsigned long flags; 675 668 676 669 opl3 = p; 677 670 ··· 679 674 chan->number, chan->midi_program, note); 680 675 #endif 681 676 682 - spin_lock_irqsave(&opl3->voice_lock, flags); 683 - 684 677 if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { 685 678 if (chan->drum_channel && use_internal_drums) { 686 679 snd_opl3_drum_switch(opl3, note, vel, 0, chan); 687 - spin_unlock_irqrestore(&opl3->voice_lock, flags); 688 680 return; 689 681 } 690 682 /* this loop will hopefully kill all extra voices, because ··· 699 697 snd_opl3_kill_voice(opl3, voice); 700 698 } 701 699 } 700 + } 701 + 702 + void snd_opl3_note_off(void *p, int note, int vel, 703 + struct snd_midi_channel *chan) 704 + { 705 + struct snd_opl3 *opl3 = p; 706 + unsigned long flags; 707 + 708 + spin_lock_irqsave(&opl3->voice_lock, flags); 709 + snd_opl3_note_off_unsafe(p, note, vel, chan); 702 710 spin_unlock_irqrestore(&opl3->voice_lock, flags); 703 711 } 704 712