[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 <oneukum@suse.de>
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Takashi Iwai 2008-02-22 18:34:44 +01:00 committed by Linus Torvalds
parent 0ee46c9dad
commit c0792e00bc

View file

@ -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;