Skip to content

Commit

Permalink
[ALSA] race between disconnect and error handling in usbmidi
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Takashi Iwai authored and Linus Torvalds committed Feb 22, 2008
1 parent 0ee46c9 commit c0792e0
Showing 1 changed file with 18 additions and 1 deletion.
19 changes: 18 additions & 1 deletion sound/usb/usbmidi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand All @@ -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 */
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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;

Expand Down

0 comments on commit c0792e0

Please sign in to comment.