diff options
author | Takashi Iwai <tiwai@suse.de> | 2011-02-22 04:21:18 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-02-23 02:15:43 -0500 |
commit | 382225e62bdb8059b7f915b133426425516dd300 (patch) | |
tree | 25f85ecb38145f3ca1547685066b2bf9057e3c72 /sound | |
parent | 306496761745942d8167e9193a738b559a7fb0b3 (diff) |
ALSA: usb-audio: fix oops due to cleanup race when disconnecting
When a USB audio device is disconnected, snd_usb_audio_disconnect()
kills all audio URBs. At the same time, the application, after being
notified of the disconnection, might close the device, in which case
ALSA calls the .hw_free callback, which should free the URBs too.
Commit de1b8b93a0ba "[ALSA] Fix hang-up at disconnection of usb-audio"
prevented snd_usb_hw_free() from freeing the URBs to avoid a hang that
resulted from this race, but this introduced another race because the
URB callbacks could now be executed after snd_usb_hw_free() has
returned, and try to access already freed data.
Fix the first race by introducing a mutex to serialize the disconnect
callback and all PCM callbacks that manage URBs (hw_free and hw_params).
Reported-and-tested-by: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
Cc: <stable@kernel.org>
[CL: also serialize hw_params callback]
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/usb/card.c | 4 | ||||
-rw-r--r-- | sound/usb/pcm.c | 7 | ||||
-rw-r--r-- | sound/usb/usbaudio.h | 1 |
3 files changed, 10 insertions, 2 deletions
diff --git a/sound/usb/card.c b/sound/usb/card.c index 800f7cb4f251..c0f8270bc199 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c | |||
@@ -323,6 +323,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, | |||
323 | return -ENOMEM; | 323 | return -ENOMEM; |
324 | } | 324 | } |
325 | 325 | ||
326 | mutex_init(&chip->shutdown_mutex); | ||
326 | chip->index = idx; | 327 | chip->index = idx; |
327 | chip->dev = dev; | 328 | chip->dev = dev; |
328 | chip->card = card; | 329 | chip->card = card; |
@@ -531,6 +532,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) | |||
531 | chip = ptr; | 532 | chip = ptr; |
532 | card = chip->card; | 533 | card = chip->card; |
533 | mutex_lock(®ister_mutex); | 534 | mutex_lock(®ister_mutex); |
535 | mutex_lock(&chip->shutdown_mutex); | ||
534 | chip->shutdown = 1; | 536 | chip->shutdown = 1; |
535 | chip->num_interfaces--; | 537 | chip->num_interfaces--; |
536 | if (chip->num_interfaces <= 0) { | 538 | if (chip->num_interfaces <= 0) { |
@@ -548,9 +550,11 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) | |||
548 | snd_usb_mixer_disconnect(p); | 550 | snd_usb_mixer_disconnect(p); |
549 | } | 551 | } |
550 | usb_chip[chip->index] = NULL; | 552 | usb_chip[chip->index] = NULL; |
553 | mutex_unlock(&chip->shutdown_mutex); | ||
551 | mutex_unlock(®ister_mutex); | 554 | mutex_unlock(®ister_mutex); |
552 | snd_card_free_when_closed(card); | 555 | snd_card_free_when_closed(card); |
553 | } else { | 556 | } else { |
557 | mutex_unlock(&chip->shutdown_mutex); | ||
554 | mutex_unlock(®ister_mutex); | 558 | mutex_unlock(®ister_mutex); |
555 | } | 559 | } |
556 | } | 560 | } |
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 4132522ac90f..e3f680526cb5 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c | |||
@@ -361,6 +361,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, | |||
361 | } | 361 | } |
362 | 362 | ||
363 | if (changed) { | 363 | if (changed) { |
364 | mutex_lock(&subs->stream->chip->shutdown_mutex); | ||
364 | /* format changed */ | 365 | /* format changed */ |
365 | snd_usb_release_substream_urbs(subs, 0); | 366 | snd_usb_release_substream_urbs(subs, 0); |
366 | /* influenced: period_bytes, channels, rate, format, */ | 367 | /* influenced: period_bytes, channels, rate, format, */ |
@@ -368,6 +369,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, | |||
368 | params_rate(hw_params), | 369 | params_rate(hw_params), |
369 | snd_pcm_format_physical_width(params_format(hw_params)) * | 370 | snd_pcm_format_physical_width(params_format(hw_params)) * |
370 | params_channels(hw_params)); | 371 | params_channels(hw_params)); |
372 | mutex_unlock(&subs->stream->chip->shutdown_mutex); | ||
371 | } | 373 | } |
372 | 374 | ||
373 | return ret; | 375 | return ret; |
@@ -385,8 +387,9 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) | |||
385 | subs->cur_audiofmt = NULL; | 387 | subs->cur_audiofmt = NULL; |
386 | subs->cur_rate = 0; | 388 | subs->cur_rate = 0; |
387 | subs->period_bytes = 0; | 389 | subs->period_bytes = 0; |
388 | if (!subs->stream->chip->shutdown) | 390 | mutex_lock(&subs->stream->chip->shutdown_mutex); |
389 | snd_usb_release_substream_urbs(subs, 0); | 391 | snd_usb_release_substream_urbs(subs, 0); |
392 | mutex_unlock(&subs->stream->chip->shutdown_mutex); | ||
390 | return snd_pcm_lib_free_vmalloc_buffer(substream); | 393 | return snd_pcm_lib_free_vmalloc_buffer(substream); |
391 | } | 394 | } |
392 | 395 | ||
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index db3eb21627ee..6e66fffe87f5 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h | |||
@@ -36,6 +36,7 @@ struct snd_usb_audio { | |||
36 | struct snd_card *card; | 36 | struct snd_card *card; |
37 | u32 usb_id; | 37 | u32 usb_id; |
38 | int shutdown; | 38 | int shutdown; |
39 | struct mutex shutdown_mutex; | ||
39 | unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */ | 40 | unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */ |
40 | int num_interfaces; | 41 | int num_interfaces; |
41 | int num_suspended_intf; | 42 | int num_suspended_intf; |