Skip to content

Commit 9b7e520

Browse files
committed
ALSA: usb-audio: Fix race against the error recovery URB submission
USB MIDI driver has an error recovery mechanism to resubmit the URB in the delayed timer handler, and this may race with the standard start / stop operations. Although both start and stop operations themselves don't race with each other due to the umidi->mutex protection, but this isn't applied to the timer handler. For fixing this potential race, the following changes are applied: - Since the timer handler can't use the mutex, we apply the umidi->disc_lock protection at each input stream URB submission; this also needs to change the GFP flag to GFP_ATOMIC - Add a check of the URB refcount and skip if already submitted - Move the timer cancel call at disconnection to the beginning of the procedure; this assures the in-flight timer handler is gone properly before killing all pending URBs Reported-by: [email protected] Reported-by: [email protected] Cc: <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent 68359a1 commit 9b7e520

File tree

1 file changed

+12
-5
lines changed

1 file changed

+12
-5
lines changed

sound/usb/midi.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,8 @@ void snd_usbmidi_disconnect(struct list_head *p)
14991499
spin_unlock_irq(&umidi->disc_lock);
15001500
up_write(&umidi->disc_rwsem);
15011501

1502+
del_timer_sync(&umidi->error_timer);
1503+
15021504
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
15031505
struct snd_usb_midi_endpoint *ep = &umidi->endpoints[i];
15041506
if (ep->out)
@@ -1525,7 +1527,6 @@ void snd_usbmidi_disconnect(struct list_head *p)
15251527
ep->in = NULL;
15261528
}
15271529
}
1528-
del_timer_sync(&umidi->error_timer);
15291530
}
15301531
EXPORT_SYMBOL(snd_usbmidi_disconnect);
15311532

@@ -2301,16 +2302,22 @@ void snd_usbmidi_input_stop(struct list_head *p)
23012302
}
23022303
EXPORT_SYMBOL(snd_usbmidi_input_stop);
23032304

2304-
static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint *ep)
2305+
static void snd_usbmidi_input_start_ep(struct snd_usb_midi *umidi,
2306+
struct snd_usb_midi_in_endpoint *ep)
23052307
{
23062308
unsigned int i;
2309+
unsigned long flags;
23072310

23082311
if (!ep)
23092312
return;
23102313
for (i = 0; i < INPUT_URBS; ++i) {
23112314
struct urb *urb = ep->urbs[i];
2312-
urb->dev = ep->umidi->dev;
2313-
snd_usbmidi_submit_urb(urb, GFP_KERNEL);
2315+
spin_lock_irqsave(&umidi->disc_lock, flags);
2316+
if (!atomic_read(&urb->use_count)) {
2317+
urb->dev = ep->umidi->dev;
2318+
snd_usbmidi_submit_urb(urb, GFP_ATOMIC);
2319+
}
2320+
spin_unlock_irqrestore(&umidi->disc_lock, flags);
23142321
}
23152322
}
23162323

@@ -2326,7 +2333,7 @@ void snd_usbmidi_input_start(struct list_head *p)
23262333
if (umidi->input_running || !umidi->opened[1])
23272334
return;
23282335
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
2329-
snd_usbmidi_input_start_ep(umidi->endpoints[i].in);
2336+
snd_usbmidi_input_start_ep(umidi, umidi->endpoints[i].in);
23302337
umidi->input_running = 1;
23312338
}
23322339
EXPORT_SYMBOL(snd_usbmidi_input_start);

0 commit comments

Comments
 (0)