Commit c0792e00bc2dd1202d48b838b1cf59d13dd2c74a

Authored by Takashi Iwai
Committed by Linus Torvalds
1 parent 0ee46c9dad

[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>

Showing 1 changed file with 18 additions and 1 deletions Side-by-side Diff

... ... @@ -104,12 +104,14 @@
104 104 struct usb_protocol_ops* usb_protocol_ops;
105 105 struct list_head list;
106 106 struct timer_list error_timer;
  107 + spinlock_t disc_lock;
107 108  
108 109 struct snd_usb_midi_endpoint {
109 110 struct snd_usb_midi_out_endpoint *out;
110 111 struct snd_usb_midi_in_endpoint *in;
111 112 } endpoints[MIDI_MAX_ENDPOINTS];
112 113 unsigned long input_triggered;
  114 + unsigned char disconnected;
113 115 };
114 116  
115 117 struct snd_usb_midi_out_endpoint {
... ... @@ -306,6 +308,11 @@
306 308 struct snd_usb_midi *umidi = (struct snd_usb_midi *)data;
307 309 int i;
308 310  
  311 + spin_lock(&umidi->disc_lock);
  312 + if (umidi->disconnected) {
  313 + spin_unlock(&umidi->disc_lock);
  314 + return;
  315 + }
309 316 for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
310 317 struct snd_usb_midi_in_endpoint *in = umidi->endpoints[i].in;
311 318 if (in && in->error_resubmit) {
... ... @@ -316,6 +323,7 @@
316 323 if (umidi->endpoints[i].out)
317 324 snd_usbmidi_do_output(umidi->endpoints[i].out);
318 325 }
  326 + spin_unlock(&umidi->disc_lock);
319 327 }
320 328  
321 329 /* helper function to send static data that may not DMA-able */
... ... @@ -1049,7 +1057,14 @@
1049 1057 int i;
1050 1058  
1051 1059 umidi = list_entry(p, struct snd_usb_midi, list);
1052   - del_timer_sync(&umidi->error_timer);
  1060 + /*
  1061 + * an URB's completion handler may start the timer and
  1062 + * a timer may submit an URB. To reliably break the cycle
  1063 + * a flag under lock must be used
  1064 + */
  1065 + spin_lock_irq(&umidi->disc_lock);
  1066 + umidi->disconnected = 1;
  1067 + spin_unlock_irq(&umidi->disc_lock);
1053 1068 for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
1054 1069 struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
1055 1070 if (ep->out)
... ... @@ -1062,6 +1077,7 @@
1062 1077 if (ep->in)
1063 1078 usb_kill_urb(ep->in->urb);
1064 1079 }
  1080 + del_timer_sync(&umidi->error_timer);
1065 1081 }
1066 1082  
1067 1083 static void snd_usbmidi_rawmidi_free(struct snd_rawmidi *rmidi)
... ... @@ -1685,6 +1701,7 @@
1685 1701 umidi->quirk = quirk;
1686 1702 umidi->usb_protocol_ops = &snd_usbmidi_standard_ops;
1687 1703 init_timer(&umidi->error_timer);
  1704 + spin_lock_init(&umidi->disc_lock);
1688 1705 umidi->error_timer.function = snd_usbmidi_error_timer;
1689 1706 umidi->error_timer.data = (unsigned long)umidi;
1690 1707