Skip to content

Commit 978520b

Browse files
committed
ALSA: usb-audio: Fix races at disconnection
Close some races at disconnection of a USB audio device by adding the chip->shutdown_mutex and chip->shutdown check at appropriate places. The spots to put bandaids are: - PCM prepare, hw_params and hw_free - where the usb device is accessed for communication or get speed, in mixer.c and others; the device speed is now cached in subs->speed instead of accessing to chip->dev The accesses in PCM open and close don't need the mutex protection because these are already handled in the core PCM disconnection code. The autosuspend/autoresume codes are still uncovered by this patch because of possible mutex deadlocks. They'll be covered by the upcoming change to rwsem. Also the mixer codes are untouched, too. These will be fixed in another patch, too. Reported-by: Matthieu CASTET <[email protected]> Cc: <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent 9b0573c commit 978520b

File tree

5 files changed

+79
-41
lines changed

5 files changed

+79
-41
lines changed

sound/usb/card.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ struct snd_usb_substream {
126126
struct snd_usb_endpoint *sync_endpoint;
127127
unsigned long flags;
128128
bool need_setup_ep; /* (re)configure EP at prepare? */
129+
unsigned int speed; /* USB_SPEED_XXX */
129130

130131
u64 formats; /* format bitmasks (all or'ed) */
131132
unsigned int num_formats; /* number of supported audio formats (list) */

sound/usb/mixer.c

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -287,33 +287,40 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int v
287287
unsigned char buf[2];
288288
int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
289289
int timeout = 10;
290-
int err;
290+
int idx = 0, err;
291291

292292
err = snd_usb_autoresume(cval->mixer->chip);
293293
if (err < 0)
294294
return -EIO;
295+
mutex_lock(&chip->shutdown_mutex);
295296
while (timeout-- > 0) {
297+
if (chip->shutdown)
298+
break;
299+
idx = snd_usb_ctrl_intf(chip) | (cval->id << 8);
296300
if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request,
297301
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
298-
validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
299-
buf, val_len) >= val_len) {
302+
validx, idx, buf, val_len) >= val_len) {
300303
*value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len));
301-
snd_usb_autosuspend(cval->mixer->chip);
302-
return 0;
304+
err = 0;
305+
goto out;
303306
}
304307
}
305-
snd_usb_autosuspend(cval->mixer->chip);
306308
snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
307-
request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type);
308-
return -EINVAL;
309+
request, validx, idx, cval->val_type);
310+
err = -EINVAL;
311+
312+
out:
313+
mutex_unlock(&chip->shutdown_mutex);
314+
snd_usb_autosuspend(cval->mixer->chip);
315+
return err;
309316
}
310317

311318
static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
312319
{
313320
struct snd_usb_audio *chip = cval->mixer->chip;
314321
unsigned char buf[2 + 3*sizeof(__u16)]; /* enough space for one range */
315322
unsigned char *val;
316-
int ret, size;
323+
int idx = 0, ret, size;
317324
__u8 bRequest;
318325

319326
if (request == UAC_GET_CUR) {
@@ -330,16 +337,22 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int v
330337
if (ret)
331338
goto error;
332339

333-
ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
340+
mutex_lock(&chip->shutdown_mutex);
341+
if (chip->shutdown)
342+
ret = -ENODEV;
343+
else {
344+
idx = snd_usb_ctrl_intf(chip) | (cval->id << 8);
345+
ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
334346
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
335-
validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
336-
buf, size);
347+
validx, idx, buf, size);
348+
}
349+
mutex_unlock(&chip->shutdown_mutex);
337350
snd_usb_autosuspend(chip);
338351

339352
if (ret < 0) {
340353
error:
341354
snd_printk(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
342-
request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type);
355+
request, validx, idx, cval->val_type);
343356
return ret;
344357
}
345358

@@ -417,7 +430,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
417430
{
418431
struct snd_usb_audio *chip = cval->mixer->chip;
419432
unsigned char buf[2];
420-
int val_len, err, timeout = 10;
433+
int idx = 0, val_len, err, timeout = 10;
421434

422435
if (cval->mixer->protocol == UAC_VERSION_1) {
423436
val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
@@ -440,19 +453,27 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
440453
err = snd_usb_autoresume(chip);
441454
if (err < 0)
442455
return -EIO;
443-
while (timeout-- > 0)
456+
mutex_lock(&chip->shutdown_mutex);
457+
while (timeout-- > 0) {
458+
if (chip->shutdown)
459+
break;
460+
idx = snd_usb_ctrl_intf(chip) | (cval->id << 8);
444461
if (snd_usb_ctl_msg(chip->dev,
445462
usb_sndctrlpipe(chip->dev, 0), request,
446463
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
447-
validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
448-
buf, val_len) >= 0) {
449-
snd_usb_autosuspend(chip);
450-
return 0;
464+
validx, idx, buf, val_len) >= 0) {
465+
err = 0;
466+
goto out;
451467
}
452-
snd_usb_autosuspend(chip);
468+
}
453469
snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n",
454-
request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type, buf[0], buf[1]);
455-
return -EINVAL;
470+
request, validx, idx, cval->val_type, buf[0], buf[1]);
471+
err = -EINVAL;
472+
473+
out:
474+
mutex_unlock(&chip->shutdown_mutex);
475+
snd_usb_autosuspend(chip);
476+
return err;
456477
}
457478

458479
static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int value)

sound/usb/pcm.c

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream
7171
unsigned int hwptr_done;
7272

7373
subs = (struct snd_usb_substream *)substream->runtime->private_data;
74+
if (subs->stream->chip->shutdown)
75+
return SNDRV_PCM_POS_XRUN;
7476
spin_lock(&subs->lock);
7577
hwptr_done = subs->hwptr_done;
7678
substream->runtime->delay = snd_usb_pcm_delay(subs,
@@ -444,7 +446,6 @@ static int configure_endpoint(struct snd_usb_substream *subs)
444446
{
445447
int ret;
446448

447-
mutex_lock(&subs->stream->chip->shutdown_mutex);
448449
/* format changed */
449450
stop_endpoints(subs, 0, 0, 0);
450451
ret = snd_usb_endpoint_set_params(subs->data_endpoint,
@@ -455,7 +456,7 @@ static int configure_endpoint(struct snd_usb_substream *subs)
455456
subs->cur_audiofmt,
456457
subs->sync_endpoint);
457458
if (ret < 0)
458-
goto unlock;
459+
return ret;
459460

460461
if (subs->sync_endpoint)
461462
ret = snd_usb_endpoint_set_params(subs->data_endpoint,
@@ -465,9 +466,6 @@ static int configure_endpoint(struct snd_usb_substream *subs)
465466
subs->cur_rate,
466467
subs->cur_audiofmt,
467468
NULL);
468-
469-
unlock:
470-
mutex_unlock(&subs->stream->chip->shutdown_mutex);
471469
return ret;
472470
}
473471

@@ -505,7 +503,13 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
505503
return -EINVAL;
506504
}
507505

508-
if ((ret = set_format(subs, fmt)) < 0)
506+
mutex_lock(&subs->stream->chip->shutdown_mutex);
507+
if (subs->stream->chip->shutdown)
508+
ret = -ENODEV;
509+
else
510+
ret = set_format(subs, fmt);
511+
mutex_unlock(&subs->stream->chip->shutdown_mutex);
512+
if (ret < 0)
509513
return ret;
510514

511515
subs->interface = fmt->iface;
@@ -528,8 +532,10 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
528532
subs->cur_rate = 0;
529533
subs->period_bytes = 0;
530534
mutex_lock(&subs->stream->chip->shutdown_mutex);
531-
stop_endpoints(subs, 0, 1, 1);
532-
deactivate_endpoints(subs);
535+
if (!subs->stream->chip->shutdown) {
536+
stop_endpoints(subs, 0, 1, 1);
537+
deactivate_endpoints(subs);
538+
}
533539
mutex_unlock(&subs->stream->chip->shutdown_mutex);
534540
return snd_pcm_lib_free_vmalloc_buffer(substream);
535541
}
@@ -552,12 +558,19 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
552558
return -ENXIO;
553559
}
554560

555-
if (snd_BUG_ON(!subs->data_endpoint))
556-
return -EIO;
561+
mutex_lock(&subs->stream->chip->shutdown_mutex);
562+
if (subs->stream->chip->shutdown) {
563+
ret = -ENODEV;
564+
goto unlock;
565+
}
566+
if (snd_BUG_ON(!subs->data_endpoint)) {
567+
ret = -EIO;
568+
goto unlock;
569+
}
557570

558571
ret = set_format(subs, subs->cur_audiofmt);
559572
if (ret < 0)
560-
return ret;
573+
goto unlock;
561574

562575
iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
563576
alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
@@ -567,12 +580,12 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
567580
subs->cur_audiofmt,
568581
subs->cur_rate);
569582
if (ret < 0)
570-
return ret;
583+
goto unlock;
571584

572585
if (subs->need_setup_ep) {
573586
ret = configure_endpoint(subs);
574587
if (ret < 0)
575-
return ret;
588+
goto unlock;
576589
subs->need_setup_ep = false;
577590
}
578591

@@ -592,9 +605,11 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
592605
/* for playback, submit the URBs now; otherwise, the first hwptr_done
593606
* updates for all URBs would happen at the same time when starting */
594607
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
595-
return start_endpoints(subs, 1);
608+
ret = start_endpoints(subs, 1);
596609

597-
return 0;
610+
unlock:
611+
mutex_unlock(&subs->stream->chip->shutdown_mutex);
612+
return ret;
598613
}
599614

600615
static struct snd_pcm_hardware snd_usb_hardware =
@@ -647,7 +662,7 @@ static int hw_check_valid_format(struct snd_usb_substream *subs,
647662
return 0;
648663
}
649664
/* check whether the period time is >= the data packet interval */
650-
if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL) {
665+
if (subs->speed != USB_SPEED_FULL) {
651666
ptime = 125 * (1 << fp->datainterval);
652667
if (ptime > pt->max || (ptime == pt->max && pt->openmax)) {
653668
hwc_debug(" > check: ptime %u > max %u\n", ptime, pt->max);
@@ -925,7 +940,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
925940
return err;
926941

927942
param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
928-
if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
943+
if (subs->speed == USB_SPEED_FULL)
929944
/* full speed devices have fixed data packet interval */
930945
ptmin = 1000;
931946
if (ptmin == 1000)

sound/usb/proc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
108108
}
109109
snd_iprintf(buffer, "\n");
110110
}
111-
if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL)
111+
if (subs->speed != USB_SPEED_FULL)
112112
snd_iprintf(buffer, " Data packet interval: %d us\n",
113113
125 * (1 << fp->datainterval));
114114
// snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize);
@@ -124,7 +124,7 @@ static void proc_dump_ep_status(struct snd_usb_substream *subs,
124124
return;
125125
snd_iprintf(buffer, " Packet Size = %d\n", ep->curpacksize);
126126
snd_iprintf(buffer, " Momentary freq = %u Hz (%#x.%04x)\n",
127-
snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
127+
subs->speed == USB_SPEED_FULL
128128
? get_full_speed_hz(ep->freqm)
129129
: get_high_speed_hz(ep->freqm),
130130
ep->freqm >> 16, ep->freqm & 0xffff);

sound/usb/stream.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
9090
subs->direction = stream;
9191
subs->dev = as->chip->dev;
9292
subs->txfr_quirk = as->chip->txfr_quirk;
93+
subs->speed = snd_usb_get_speed(subs->dev);
9394

9495
snd_usb_set_pcm_ops(as->pcm, stream);
9596

0 commit comments

Comments
 (0)