From: Takashi Iwai Date: Fri, 22 Feb 2008 17:34:44 +0000 (+0100) Subject: [ALSA] race between disconnect and error handling in usbmidi X-Git-Tag: v2.6.25-rc3~87 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c0792e00bc2dd1202d48b838b1cf59d13dd2c74a;p=linux-2.6 [ALSA] race between disconnect and error handling in usbmidi The driver resubmits URBs from an error handler and schedules the error handler from the URBs' completion handlers. To reliably kill the cycle a flag must be used. Signed-off-by: Oliver Neukum Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai Signed-off-by: Linus Torvalds --- diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c index 750e929d58..6676a177c9 100644 --- a/sound/usb/usbmidi.c +++ b/sound/usb/usbmidi.c @@ -104,12 +104,14 @@ struct snd_usb_midi { struct usb_protocol_ops* usb_protocol_ops; struct list_head list; struct timer_list error_timer; + spinlock_t disc_lock; struct snd_usb_midi_endpoint { struct snd_usb_midi_out_endpoint *out; struct snd_usb_midi_in_endpoint *in; } endpoints[MIDI_MAX_ENDPOINTS]; unsigned long input_triggered; + unsigned char disconnected; }; struct snd_usb_midi_out_endpoint { @@ -306,6 +308,11 @@ static void snd_usbmidi_error_timer(unsigned long data) struct snd_usb_midi *umidi = (struct snd_usb_midi *)data; int i; + spin_lock(&umidi->disc_lock); + if (umidi->disconnected) { + spin_unlock(&umidi->disc_lock); + return; + } for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { struct snd_usb_midi_in_endpoint *in = umidi->endpoints[i].in; if (in && in->error_resubmit) { @@ -316,6 +323,7 @@ static void snd_usbmidi_error_timer(unsigned long data) if (umidi->endpoints[i].out) snd_usbmidi_do_output(umidi->endpoints[i].out); } + spin_unlock(&umidi->disc_lock); } /* helper function to send static data that may not DMA-able */ @@ -1049,7 +1057,14 @@ void snd_usbmidi_disconnect(struct list_head* p) int i; umidi = list_entry(p, struct snd_usb_midi, list); - del_timer_sync(&umidi->error_timer); + /* + * an URB's completion handler may start the timer and + * a timer may submit an URB. To reliably break the cycle + * a flag under lock must be used + */ + spin_lock_irq(&umidi->disc_lock); + umidi->disconnected = 1; + spin_unlock_irq(&umidi->disc_lock); for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; if (ep->out) @@ -1062,6 +1077,7 @@ void snd_usbmidi_disconnect(struct list_head* p) if (ep->in) usb_kill_urb(ep->in->urb); } + del_timer_sync(&umidi->error_timer); } static void snd_usbmidi_rawmidi_free(struct snd_rawmidi *rmidi) @@ -1685,6 +1701,7 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip, umidi->quirk = quirk; umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; init_timer(&umidi->error_timer); + spin_lock_init(&umidi->disc_lock); umidi->error_timer.function = snd_usbmidi_error_timer; umidi->error_timer.data = (unsigned long)umidi;