aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-10-12 09:12:55 -0400
committerTakashi Iwai <tiwai@suse.de>2012-10-30 06:06:54 -0400
commit978520b75f0a1ce82b17e1e8186417250de6d545 (patch)
tree2b0d8619df66706779d9520789e27bf6ca385383
parent9b0573c07f278e9888c352aa9724035c75784ea0 (diff)
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 <matthieu.castet@parrot.com> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/usb/card.h1
-rw-r--r--sound/usb/mixer.c65
-rw-r--r--sound/usb/pcm.c49
-rw-r--r--sound/usb/proc.c4
-rw-r--r--sound/usb/stream.c1
5 files changed, 79 insertions, 41 deletions
diff --git a/sound/usb/card.h b/sound/usb/card.h
index afa4f9e9b27a..814cb357ff88 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -126,6 +126,7 @@ struct snd_usb_substream {
126 struct snd_usb_endpoint *sync_endpoint; 126 struct snd_usb_endpoint *sync_endpoint;
127 unsigned long flags; 127 unsigned long flags;
128 bool need_setup_ep; /* (re)configure EP at prepare? */ 128 bool need_setup_ep; /* (re)configure EP at prepare? */
129 unsigned int speed; /* USB_SPEED_XXX */
129 130
130 u64 formats; /* format bitmasks (all or'ed) */ 131 u64 formats; /* format bitmasks (all or'ed) */
131 unsigned int num_formats; /* number of supported audio formats (list) */ 132 unsigned int num_formats; /* number of supported audio formats (list) */
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index fe56c9da38e9..c2ef11ccd66a 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -287,25 +287,32 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int v
287 unsigned char buf[2]; 287 unsigned char buf[2];
288 int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; 288 int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
289 int timeout = 10; 289 int timeout = 10;
290 int err; 290 int idx = 0, err;
291 291
292 err = snd_usb_autoresume(cval->mixer->chip); 292 err = snd_usb_autoresume(cval->mixer->chip);
293 if (err < 0) 293 if (err < 0)
294 return -EIO; 294 return -EIO;
295 mutex_lock(&chip->shutdown_mutex);
295 while (timeout-- > 0) { 296 while (timeout-- > 0) {
297 if (chip->shutdown)
298 break;
299 idx = snd_usb_ctrl_intf(chip) | (cval->id << 8);
296 if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request, 300 if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request,
297 USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, 301 USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
298 validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), 302 validx, idx, buf, val_len) >= val_len) {
299 buf, val_len) >= val_len) {
300 *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len)); 303 *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len));
301 snd_usb_autosuspend(cval->mixer->chip); 304 err = 0;
302 return 0; 305 goto out;
303 } 306 }
304 } 307 }
305 snd_usb_autosuspend(cval->mixer->chip);
306 snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", 308 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); 309 request, validx, idx, cval->val_type);
308 return -EINVAL; 310 err = -EINVAL;
311
312 out:
313 mutex_unlock(&chip->shutdown_mutex);
314 snd_usb_autosuspend(cval->mixer->chip);
315 return err;
309} 316}
310 317
311static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) 318static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
@@ -313,7 +320,7 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int v
313 struct snd_usb_audio *chip = cval->mixer->chip; 320 struct snd_usb_audio *chip = cval->mixer->chip;
314 unsigned char buf[2 + 3*sizeof(__u16)]; /* enough space for one range */ 321 unsigned char buf[2 + 3*sizeof(__u16)]; /* enough space for one range */
315 unsigned char *val; 322 unsigned char *val;
316 int ret, size; 323 int idx = 0, ret, size;
317 __u8 bRequest; 324 __u8 bRequest;
318 325
319 if (request == UAC_GET_CUR) { 326 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
330 if (ret) 337 if (ret)
331 goto error; 338 goto error;
332 339
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,
334 USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, 346 USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
335 validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), 347 validx, idx, buf, size);
336 buf, size); 348 }
349 mutex_unlock(&chip->shutdown_mutex);
337 snd_usb_autosuspend(chip); 350 snd_usb_autosuspend(chip);
338 351
339 if (ret < 0) { 352 if (ret < 0) {
340error: 353error:
341 snd_printk(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", 354 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);
343 return ret; 356 return ret;
344 } 357 }
345 358
@@ -417,7 +430,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
417{ 430{
418 struct snd_usb_audio *chip = cval->mixer->chip; 431 struct snd_usb_audio *chip = cval->mixer->chip;
419 unsigned char buf[2]; 432 unsigned char buf[2];
420 int val_len, err, timeout = 10; 433 int idx = 0, val_len, err, timeout = 10;
421 434
422 if (cval->mixer->protocol == UAC_VERSION_1) { 435 if (cval->mixer->protocol == UAC_VERSION_1) {
423 val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; 436 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,
440 err = snd_usb_autoresume(chip); 453 err = snd_usb_autoresume(chip);
441 if (err < 0) 454 if (err < 0)
442 return -EIO; 455 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);
444 if (snd_usb_ctl_msg(chip->dev, 461 if (snd_usb_ctl_msg(chip->dev,
445 usb_sndctrlpipe(chip->dev, 0), request, 462 usb_sndctrlpipe(chip->dev, 0), request,
446 USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, 463 USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
447 validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), 464 validx, idx, buf, val_len) >= 0) {
448 buf, val_len) >= 0) { 465 err = 0;
449 snd_usb_autosuspend(chip); 466 goto out;
450 return 0;
451 } 467 }
452 snd_usb_autosuspend(chip); 468 }
453 snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n", 469 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]); 470 request, validx, idx, cval->val_type, buf[0], buf[1]);
455 return -EINVAL; 471 err = -EINVAL;
472
473 out:
474 mutex_unlock(&chip->shutdown_mutex);
475 snd_usb_autosuspend(chip);
476 return err;
456} 477}
457 478
458static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int value) 479static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int value)
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 55e19e1b80ec..55e741c5f231 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -71,6 +71,8 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream
71 unsigned int hwptr_done; 71 unsigned int hwptr_done;
72 72
73 subs = (struct snd_usb_substream *)substream->runtime->private_data; 73 subs = (struct snd_usb_substream *)substream->runtime->private_data;
74 if (subs->stream->chip->shutdown)
75 return SNDRV_PCM_POS_XRUN;
74 spin_lock(&subs->lock); 76 spin_lock(&subs->lock);
75 hwptr_done = subs->hwptr_done; 77 hwptr_done = subs->hwptr_done;
76 substream->runtime->delay = snd_usb_pcm_delay(subs, 78 substream->runtime->delay = snd_usb_pcm_delay(subs,
@@ -444,7 +446,6 @@ static int configure_endpoint(struct snd_usb_substream *subs)
444{ 446{
445 int ret; 447 int ret;
446 448
447 mutex_lock(&subs->stream->chip->shutdown_mutex);
448 /* format changed */ 449 /* format changed */
449 stop_endpoints(subs, 0, 0, 0); 450 stop_endpoints(subs, 0, 0, 0);
450 ret = snd_usb_endpoint_set_params(subs->data_endpoint, 451 ret = snd_usb_endpoint_set_params(subs->data_endpoint,
@@ -455,7 +456,7 @@ static int configure_endpoint(struct snd_usb_substream *subs)
455 subs->cur_audiofmt, 456 subs->cur_audiofmt,
456 subs->sync_endpoint); 457 subs->sync_endpoint);
457 if (ret < 0) 458 if (ret < 0)
458 goto unlock; 459 return ret;
459 460
460 if (subs->sync_endpoint) 461 if (subs->sync_endpoint)
461 ret = snd_usb_endpoint_set_params(subs->data_endpoint, 462 ret = snd_usb_endpoint_set_params(subs->data_endpoint,
@@ -465,9 +466,6 @@ static int configure_endpoint(struct snd_usb_substream *subs)
465 subs->cur_rate, 466 subs->cur_rate,
466 subs->cur_audiofmt, 467 subs->cur_audiofmt,
467 NULL); 468 NULL);
468
469unlock:
470 mutex_unlock(&subs->stream->chip->shutdown_mutex);
471 return ret; 469 return ret;
472} 470}
473 471
@@ -505,7 +503,13 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
505 return -EINVAL; 503 return -EINVAL;
506 } 504 }
507 505
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)
509 return ret; 513 return ret;
510 514
511 subs->interface = fmt->iface; 515 subs->interface = fmt->iface;
@@ -528,8 +532,10 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
528 subs->cur_rate = 0; 532 subs->cur_rate = 0;
529 subs->period_bytes = 0; 533 subs->period_bytes = 0;
530 mutex_lock(&subs->stream->chip->shutdown_mutex); 534 mutex_lock(&subs->stream->chip->shutdown_mutex);
531 stop_endpoints(subs, 0, 1, 1); 535 if (!subs->stream->chip->shutdown) {
532 deactivate_endpoints(subs); 536 stop_endpoints(subs, 0, 1, 1);
537 deactivate_endpoints(subs);
538 }
533 mutex_unlock(&subs->stream->chip->shutdown_mutex); 539 mutex_unlock(&subs->stream->chip->shutdown_mutex);
534 return snd_pcm_lib_free_vmalloc_buffer(substream); 540 return snd_pcm_lib_free_vmalloc_buffer(substream);
535} 541}
@@ -552,12 +558,19 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
552 return -ENXIO; 558 return -ENXIO;
553 } 559 }
554 560
555 if (snd_BUG_ON(!subs->data_endpoint)) 561 mutex_lock(&subs->stream->chip->shutdown_mutex);
556 return -EIO; 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 }
557 570
558 ret = set_format(subs, subs->cur_audiofmt); 571 ret = set_format(subs, subs->cur_audiofmt);
559 if (ret < 0) 572 if (ret < 0)
560 return ret; 573 goto unlock;
561 574
562 iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface); 575 iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
563 alts = &iface->altsetting[subs->cur_audiofmt->altset_idx]; 576 alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
@@ -567,12 +580,12 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
567 subs->cur_audiofmt, 580 subs->cur_audiofmt,
568 subs->cur_rate); 581 subs->cur_rate);
569 if (ret < 0) 582 if (ret < 0)
570 return ret; 583 goto unlock;
571 584
572 if (subs->need_setup_ep) { 585 if (subs->need_setup_ep) {
573 ret = configure_endpoint(subs); 586 ret = configure_endpoint(subs);
574 if (ret < 0) 587 if (ret < 0)
575 return ret; 588 goto unlock;
576 subs->need_setup_ep = false; 589 subs->need_setup_ep = false;
577 } 590 }
578 591
@@ -592,9 +605,11 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
592 /* for playback, submit the URBs now; otherwise, the first hwptr_done 605 /* for playback, submit the URBs now; otherwise, the first hwptr_done
593 * updates for all URBs would happen at the same time when starting */ 606 * updates for all URBs would happen at the same time when starting */
594 if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) 607 if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
595 return start_endpoints(subs, 1); 608 ret = start_endpoints(subs, 1);
596 609
597 return 0; 610 unlock:
611 mutex_unlock(&subs->stream->chip->shutdown_mutex);
612 return ret;
598} 613}
599 614
600static struct snd_pcm_hardware snd_usb_hardware = 615static struct snd_pcm_hardware snd_usb_hardware =
@@ -647,7 +662,7 @@ static int hw_check_valid_format(struct snd_usb_substream *subs,
647 return 0; 662 return 0;
648 } 663 }
649 /* check whether the period time is >= the data packet interval */ 664 /* 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) {
651 ptime = 125 * (1 << fp->datainterval); 666 ptime = 125 * (1 << fp->datainterval);
652 if (ptime > pt->max || (ptime == pt->max && pt->openmax)) { 667 if (ptime > pt->max || (ptime == pt->max && pt->openmax)) {
653 hwc_debug(" > check: ptime %u > max %u\n", ptime, pt->max); 668 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
925 return err; 940 return err;
926 941
927 param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME; 942 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)
929 /* full speed devices have fixed data packet interval */ 944 /* full speed devices have fixed data packet interval */
930 ptmin = 1000; 945 ptmin = 1000;
931 if (ptmin == 1000) 946 if (ptmin == 1000)
diff --git a/sound/usb/proc.c b/sound/usb/proc.c
index ebc1a5b5b3f1..d218f763501f 100644
--- a/sound/usb/proc.c
+++ b/sound/usb/proc.c
@@ -108,7 +108,7 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
108 } 108 }
109 snd_iprintf(buffer, "\n"); 109 snd_iprintf(buffer, "\n");
110 } 110 }
111 if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL) 111 if (subs->speed != USB_SPEED_FULL)
112 snd_iprintf(buffer, " Data packet interval: %d us\n", 112 snd_iprintf(buffer, " Data packet interval: %d us\n",
113 125 * (1 << fp->datainterval)); 113 125 * (1 << fp->datainterval));
114 // snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize); 114 // 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,
124 return; 124 return;
125 snd_iprintf(buffer, " Packet Size = %d\n", ep->curpacksize); 125 snd_iprintf(buffer, " Packet Size = %d\n", ep->curpacksize);
126 snd_iprintf(buffer, " Momentary freq = %u Hz (%#x.%04x)\n", 126 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
128 ? get_full_speed_hz(ep->freqm) 128 ? get_full_speed_hz(ep->freqm)
129 : get_high_speed_hz(ep->freqm), 129 : get_high_speed_hz(ep->freqm),
130 ep->freqm >> 16, ep->freqm & 0xffff); 130 ep->freqm >> 16, ep->freqm & 0xffff);
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 083ed81160e5..1de0c8c002a8 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -90,6 +90,7 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
90 subs->direction = stream; 90 subs->direction = stream;
91 subs->dev = as->chip->dev; 91 subs->dev = as->chip->dev;
92 subs->txfr_quirk = as->chip->txfr_quirk; 92 subs->txfr_quirk = as->chip->txfr_quirk;
93 subs->speed = snd_usb_get_speed(subs->dev);
93 94
94 snd_usb_set_pcm_ops(as->pcm, stream); 95 snd_usb_set_pcm_ops(as->pcm, stream);
95 96