From: Greg Kroah-Hartman Date: Wed, 7 Nov 2012 15:04:32 +0000 (+0100) Subject: 3.4-stable patches X-Git-Tag: v3.0.52~17 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1c89d0ff677f671f0d0a0ab505220457b07c61e0;p=thirdparty%2Fkernel%2Fstable-queue.git 3.4-stable patches added patches: alsa-add-a-reference-counter-to-card-instance.patch alsa-avoid-endless-sleep-after-disconnect.patch alsa-usb-audio-fix-races-at-disconnection-in-mixer_quirks.c.patch alsa-usb-audio-fix-races-at-disconnection.patch alsa-usb-audio-use-rwsem-for-disconnect-protection.patch --- diff --git a/queue-3.4/alsa-add-a-reference-counter-to-card-instance.patch b/queue-3.4/alsa-add-a-reference-counter-to-card-instance.patch new file mode 100644 index 00000000000..aeac55583f6 --- /dev/null +++ b/queue-3.4/alsa-add-a-reference-counter-to-card-instance.patch @@ -0,0 +1,416 @@ +From tiwai@suse.de Wed Nov 7 16:02:13 2012 +From: tiwai@suse.de +Date: Wed, 7 Nov 2012 12:42:47 +0100 +Subject: ALSA: Add a reference counter to card instance +To: stable@vger.kernel.org + +From: Takashi Iwai + +commit a0830dbd4e42b38aefdf3fb61ba5019a1a99ea85 upstream. + +For more strict protection for wild disconnections, a refcount is +introduced to the card instance, and let it up/down when an object is +referred via snd_lookup_*() in the open ops. + +The free-after-last-close check is also changed to check this refcount +instead of the empty list, too. + +Reported-by: Matthieu CASTET +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + include/sound/core.h | 3 ++ + sound/core/compress_offload.c | 9 +++++-- + sound/core/control.c | 3 ++ + sound/core/hwdep.c | 5 +++- + sound/core/init.c | 50 +++++++++++++++++++++++++----------------- + sound/core/oss/mixer_oss.c | 10 ++++++-- + sound/core/oss/pcm_oss.c | 2 + + sound/core/pcm_native.c | 9 +++++-- + sound/core/rawmidi.c | 6 ++++- + sound/core/sound.c | 11 +++++++-- + sound/core/sound_oss.c | 10 ++++++-- + 11 files changed, 86 insertions(+), 32 deletions(-) + +--- a/include/sound/core.h ++++ b/include/sound/core.h +@@ -132,6 +132,7 @@ struct snd_card { + int shutdown; /* this card is going down */ + int free_on_last_close; /* free in context of file_release */ + wait_queue_head_t shutdown_sleep; ++ atomic_t refcount; /* refcount for disconnection */ + struct device *dev; /* device assigned to this card */ + struct device *card_dev; /* cardX object for sysfs */ + +@@ -189,6 +190,7 @@ struct snd_minor { + const struct file_operations *f_ops; /* file operations */ + void *private_data; /* private data for f_ops->open */ + struct device *dev; /* device for sysfs */ ++ struct snd_card *card_ptr; /* assigned card instance */ + }; + + /* return a device pointer linked to each sound device as a parent */ +@@ -295,6 +297,7 @@ int snd_card_info_done(void); + int snd_component_add(struct snd_card *card, const char *component); + int snd_card_file_add(struct snd_card *card, struct file *file); + int snd_card_file_remove(struct snd_card *card, struct file *file); ++void snd_card_unref(struct snd_card *card); + + #define snd_card_set_dev(card, devptr) ((card)->dev = (devptr)) + +--- a/sound/core/compress_offload.c ++++ b/sound/core/compress_offload.c +@@ -102,12 +102,15 @@ static int snd_compr_open(struct inode * + + if (dirn != compr->direction) { + pr_err("this device doesn't support this direction\n"); ++ snd_card_unref(compr->card); + return -EINVAL; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); +- if (!data) ++ if (!data) { ++ snd_card_unref(compr->card); + return -ENOMEM; ++ } + data->stream.ops = compr->ops; + data->stream.direction = dirn; + data->stream.private_data = compr->private_data; +@@ -115,6 +118,7 @@ static int snd_compr_open(struct inode * + runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); + if (!runtime) { + kfree(data); ++ snd_card_unref(compr->card); + return -ENOMEM; + } + runtime->state = SNDRV_PCM_STATE_OPEN; +@@ -128,7 +132,8 @@ static int snd_compr_open(struct inode * + kfree(runtime); + kfree(data); + } +- return ret; ++ snd_card_unref(compr->card); ++ return 0; + } + + static int snd_compr_free(struct inode *inode, struct file *f) +--- a/sound/core/control.c ++++ b/sound/core/control.c +@@ -86,6 +86,7 @@ static int snd_ctl_open(struct inode *in + write_lock_irqsave(&card->ctl_files_rwlock, flags); + list_add_tail(&ctl->list, &card->ctl_files); + write_unlock_irqrestore(&card->ctl_files_rwlock, flags); ++ snd_card_unref(card); + return 0; + + __error: +@@ -93,6 +94,8 @@ static int snd_ctl_open(struct inode *in + __error2: + snd_card_file_remove(card, file); + __error1: ++ if (card) ++ snd_card_unref(card); + return err; + } + +--- a/sound/core/hwdep.c ++++ b/sound/core/hwdep.c +@@ -100,8 +100,10 @@ static int snd_hwdep_open(struct inode * + if (hw == NULL) + return -ENODEV; + +- if (!try_module_get(hw->card->module)) ++ if (!try_module_get(hw->card->module)) { ++ snd_card_unref(hw->card); + return -EFAULT; ++ } + + init_waitqueue_entry(&wait, current); + add_wait_queue(&hw->open_wait, &wait); +@@ -148,6 +150,7 @@ static int snd_hwdep_open(struct inode * + mutex_unlock(&hw->open_mutex); + if (err < 0) + module_put(hw->card->module); ++ snd_card_unref(hw->card); + return err; + } + +--- a/sound/core/init.c ++++ b/sound/core/init.c +@@ -213,6 +213,7 @@ int snd_card_create(int idx, const char + spin_lock_init(&card->files_lock); + INIT_LIST_HEAD(&card->files_list); + init_waitqueue_head(&card->shutdown_sleep); ++ atomic_set(&card->refcount, 0); + #ifdef CONFIG_PM + mutex_init(&card->power_lock); + init_waitqueue_head(&card->power_sleep); +@@ -446,21 +447,36 @@ static int snd_card_do_free(struct snd_c + return 0; + } + ++/** ++ * snd_card_unref - release the reference counter ++ * @card: the card instance ++ * ++ * Decrements the reference counter. When it reaches to zero, wake up ++ * the sleeper and call the destructor if needed. ++ */ ++void snd_card_unref(struct snd_card *card) ++{ ++ if (atomic_dec_and_test(&card->refcount)) { ++ wake_up(&card->shutdown_sleep); ++ if (card->free_on_last_close) ++ snd_card_do_free(card); ++ } ++} ++EXPORT_SYMBOL(snd_card_unref); ++ + int snd_card_free_when_closed(struct snd_card *card) + { +- int free_now = 0; +- int ret = snd_card_disconnect(card); +- if (ret) +- return ret; ++ int ret; + +- spin_lock(&card->files_lock); +- if (list_empty(&card->files_list)) +- free_now = 1; +- else +- card->free_on_last_close = 1; +- spin_unlock(&card->files_lock); ++ atomic_inc(&card->refcount); ++ ret = snd_card_disconnect(card); ++ if (ret) { ++ atomic_dec(&card->refcount); ++ return ret; ++ } + +- if (free_now) ++ card->free_on_last_close = 1; ++ if (atomic_dec_and_test(&card->refcount)) + snd_card_do_free(card); + return 0; + } +@@ -474,7 +490,7 @@ int snd_card_free(struct snd_card *card) + return ret; + + /* wait, until all devices are ready for the free operation */ +- wait_event(card->shutdown_sleep, list_empty(&card->files_list)); ++ wait_event(card->shutdown_sleep, !atomic_read(&card->refcount)); + snd_card_do_free(card); + return 0; + } +@@ -886,6 +902,7 @@ int snd_card_file_add(struct snd_card *c + return -ENODEV; + } + list_add(&mfile->list, &card->files_list); ++ atomic_inc(&card->refcount); + spin_unlock(&card->files_lock); + return 0; + } +@@ -908,7 +925,6 @@ EXPORT_SYMBOL(snd_card_file_add); + int snd_card_file_remove(struct snd_card *card, struct file *file) + { + struct snd_monitor_file *mfile, *found = NULL; +- int last_close = 0; + + spin_lock(&card->files_lock); + list_for_each_entry(mfile, &card->files_list, list) { +@@ -923,19 +939,13 @@ int snd_card_file_remove(struct snd_card + break; + } + } +- if (list_empty(&card->files_list)) +- last_close = 1; + spin_unlock(&card->files_lock); +- if (last_close) { +- wake_up(&card->shutdown_sleep); +- if (card->free_on_last_close) +- snd_card_do_free(card); +- } + if (!found) { + snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file); + return -ENOENT; + } + kfree(found); ++ snd_card_unref(card); + return 0; + } + +--- a/sound/core/oss/mixer_oss.c ++++ b/sound/core/oss/mixer_oss.c +@@ -52,14 +52,19 @@ static int snd_mixer_oss_open(struct ino + SNDRV_OSS_DEVICE_TYPE_MIXER); + if (card == NULL) + return -ENODEV; +- if (card->mixer_oss == NULL) ++ if (card->mixer_oss == NULL) { ++ snd_card_unref(card); + return -ENODEV; ++ } + err = snd_card_file_add(card, file); +- if (err < 0) ++ if (err < 0) { ++ snd_card_unref(card); + return err; ++ } + fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL); + if (fmixer == NULL) { + snd_card_file_remove(card, file); ++ snd_card_unref(card); + return -ENOMEM; + } + fmixer->card = card; +@@ -68,6 +73,7 @@ static int snd_mixer_oss_open(struct ino + if (!try_module_get(card->module)) { + kfree(fmixer); + snd_card_file_remove(card, file); ++ snd_card_unref(card); + return -EFAULT; + } + return 0; +--- a/sound/core/oss/pcm_oss.c ++++ b/sound/core/oss/pcm_oss.c +@@ -2457,6 +2457,8 @@ static int snd_pcm_oss_open(struct inode + __error2: + snd_card_file_remove(pcm->card, file); + __error1: ++ if (pcm) ++ snd_card_unref(pcm->card); + return err; + } + +--- a/sound/core/pcm_native.c ++++ b/sound/core/pcm_native.c +@@ -1631,6 +1631,7 @@ static int snd_pcm_link(struct snd_pcm_s + write_unlock_irq(&snd_pcm_link_rwlock); + up_write(&snd_pcm_link_rwsem); + _nolock: ++ snd_card_unref(substream1->pcm->card); + fput(file); + if (res < 0) + kfree(group); +@@ -2105,7 +2106,9 @@ static int snd_pcm_playback_open(struct + return err; + pcm = snd_lookup_minor_data(iminor(inode), + SNDRV_DEVICE_TYPE_PCM_PLAYBACK); +- return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK); ++ err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK); ++ snd_card_unref(pcm->card); ++ return err; + } + + static int snd_pcm_capture_open(struct inode *inode, struct file *file) +@@ -2116,7 +2119,9 @@ static int snd_pcm_capture_open(struct i + return err; + pcm = snd_lookup_minor_data(iminor(inode), + SNDRV_DEVICE_TYPE_PCM_CAPTURE); +- return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE); ++ err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE); ++ snd_card_unref(pcm->card); ++ return err; + } + + static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream) +--- a/sound/core/rawmidi.c ++++ b/sound/core/rawmidi.c +@@ -379,8 +379,10 @@ static int snd_rawmidi_open(struct inode + if (rmidi == NULL) + return -ENODEV; + +- if (!try_module_get(rmidi->card->module)) ++ if (!try_module_get(rmidi->card->module)) { ++ snd_card_unref(rmidi->card); + return -ENXIO; ++ } + + mutex_lock(&rmidi->open_mutex); + card = rmidi->card; +@@ -440,6 +442,7 @@ static int snd_rawmidi_open(struct inode + #endif + file->private_data = rawmidi_file; + mutex_unlock(&rmidi->open_mutex); ++ snd_card_unref(rmidi->card); + return 0; + + __error: +@@ -447,6 +450,7 @@ static int snd_rawmidi_open(struct inode + __error_card: + mutex_unlock(&rmidi->open_mutex); + module_put(rmidi->card->module); ++ snd_card_unref(rmidi->card); + return err; + } + +--- a/sound/core/sound.c ++++ b/sound/core/sound.c +@@ -99,6 +99,10 @@ static void snd_request_other(int minor) + * + * Checks that a minor device with the specified type is registered, and returns + * its user data pointer. ++ * ++ * This function increments the reference counter of the card instance ++ * if an associated instance with the given minor number and type is found. ++ * The caller must call snd_card_unref() appropriately later. + */ + void *snd_lookup_minor_data(unsigned int minor, int type) + { +@@ -109,9 +113,11 @@ void *snd_lookup_minor_data(unsigned int + return NULL; + mutex_lock(&sound_mutex); + mreg = snd_minors[minor]; +- if (mreg && mreg->type == type) ++ if (mreg && mreg->type == type) { + private_data = mreg->private_data; +- else ++ if (mreg->card_ptr) ++ atomic_inc(&mreg->card_ptr->refcount); ++ } else + private_data = NULL; + mutex_unlock(&sound_mutex); + return private_data; +@@ -276,6 +282,7 @@ int snd_register_device_for_dev(int type + preg->device = dev; + preg->f_ops = f_ops; + preg->private_data = private_data; ++ preg->card_ptr = card; + mutex_lock(&sound_mutex); + #ifdef CONFIG_SND_DYNAMIC_MINORS + minor = snd_find_free_minor(type); +--- a/sound/core/sound_oss.c ++++ b/sound/core/sound_oss.c +@@ -40,6 +40,9 @@ + static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS]; + static DEFINE_MUTEX(sound_oss_mutex); + ++/* NOTE: This function increments the refcount of the associated card like ++ * snd_lookup_minor_data(); the caller must call snd_card_unref() appropriately ++ */ + void *snd_lookup_oss_minor_data(unsigned int minor, int type) + { + struct snd_minor *mreg; +@@ -49,9 +52,11 @@ void *snd_lookup_oss_minor_data(unsigned + return NULL; + mutex_lock(&sound_oss_mutex); + mreg = snd_oss_minors[minor]; +- if (mreg && mreg->type == type) ++ if (mreg && mreg->type == type) { + private_data = mreg->private_data; +- else ++ if (mreg->card_ptr) ++ atomic_inc(&mreg->card_ptr->refcount); ++ } else + private_data = NULL; + mutex_unlock(&sound_oss_mutex); + return private_data; +@@ -123,6 +128,7 @@ int snd_register_oss_device(int type, st + preg->device = dev; + preg->f_ops = f_ops; + preg->private_data = private_data; ++ preg->card_ptr = card; + mutex_lock(&sound_oss_mutex); + snd_oss_minors[minor] = preg; + minor_unit = SNDRV_MINOR_OSS_DEVICE(minor); diff --git a/queue-3.4/alsa-avoid-endless-sleep-after-disconnect.patch b/queue-3.4/alsa-avoid-endless-sleep-after-disconnect.patch new file mode 100644 index 00000000000..c5345ee2a35 --- /dev/null +++ b/queue-3.4/alsa-avoid-endless-sleep-after-disconnect.patch @@ -0,0 +1,182 @@ +From tiwai@suse.de Wed Nov 7 16:02:34 2012 +From: tiwai@suse.de +Date: Wed, 7 Nov 2012 12:42:48 +0100 +Subject: ALSA: Avoid endless sleep after disconnect +To: stable@vger.kernel.org + +From: Takashi Iwai + +commit 0914f7961babbf28aaa2f19b453951fb4841c03f upstream. + +When disconnect callback is called, each component should wake up +sleepers and check card->shutdown flag for avoiding the endless sleep +blocking the proper resource release. + +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/core/control.c | 2 ++ + sound/core/hwdep.c | 7 +++++++ + sound/core/oss/pcm_oss.c | 4 ++++ + sound/core/pcm.c | 6 +++++- + sound/core/pcm_native.c | 8 ++++++++ + sound/core/rawmidi.c | 20 ++++++++++++++++++++ + 6 files changed, 46 insertions(+), 1 deletion(-) + +--- a/sound/core/control.c ++++ b/sound/core/control.c +@@ -1436,6 +1436,8 @@ static ssize_t snd_ctl_read(struct file + spin_unlock_irq(&ctl->read_lock); + schedule(); + remove_wait_queue(&ctl->change_sleep, &wait); ++ if (ctl->card->shutdown) ++ return -ENODEV; + if (signal_pending(current)) + return -ERESTARTSYS; + spin_lock_irq(&ctl->read_lock); +--- a/sound/core/hwdep.c ++++ b/sound/core/hwdep.c +@@ -131,6 +131,10 @@ static int snd_hwdep_open(struct inode * + mutex_unlock(&hw->open_mutex); + schedule(); + mutex_lock(&hw->open_mutex); ++ if (hw->card->shutdown) { ++ err = -ENODEV; ++ break; ++ } + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; +@@ -462,12 +466,15 @@ static int snd_hwdep_dev_disconnect(stru + mutex_unlock(®ister_mutex); + return -EINVAL; + } ++ mutex_lock(&hwdep->open_mutex); ++ wake_up(&hwdep->open_wait); + #ifdef CONFIG_SND_OSSEMUL + if (hwdep->ossreg) + snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device); + #endif + snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device); + list_del_init(&hwdep->list); ++ mutex_unlock(&hwdep->open_mutex); + mutex_unlock(®ister_mutex); + return 0; + } +--- a/sound/core/oss/pcm_oss.c ++++ b/sound/core/oss/pcm_oss.c +@@ -2441,6 +2441,10 @@ static int snd_pcm_oss_open(struct inode + mutex_unlock(&pcm->open_mutex); + schedule(); + mutex_lock(&pcm->open_mutex); ++ if (pcm->card->shutdown) { ++ err = -ENODEV; ++ break; ++ } + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; +--- a/sound/core/pcm.c ++++ b/sound/core/pcm.c +@@ -1087,12 +1087,16 @@ static int snd_pcm_dev_disconnect(struct + goto unlock; + + mutex_lock(&pcm->open_mutex); ++ wake_up(&pcm->open_wait); + list_del_init(&pcm->list); + for (cidx = 0; cidx < 2; cidx++) + for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { + snd_pcm_stream_lock_irq(substream); +- if (substream->runtime) ++ if (substream->runtime) { + substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; ++ wake_up(&substream->runtime->sleep); ++ wake_up(&substream->runtime->tsleep); ++ } + snd_pcm_stream_unlock_irq(substream); + } + list_for_each_entry(notify, &snd_pcm_notify_list, list) { +--- a/sound/core/pcm_native.c ++++ b/sound/core/pcm_native.c +@@ -1508,6 +1508,10 @@ static int snd_pcm_drain(struct snd_pcm_ + down_read(&snd_pcm_link_rwsem); + snd_pcm_stream_lock_irq(substream); + remove_wait_queue(&to_check->sleep, &wait); ++ if (card->shutdown) { ++ result = -ENODEV; ++ break; ++ } + if (tout == 0) { + if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) + result = -ESTRPIPE; +@@ -2158,6 +2162,10 @@ static int snd_pcm_open(struct file *fil + mutex_unlock(&pcm->open_mutex); + schedule(); + mutex_lock(&pcm->open_mutex); ++ if (pcm->card->shutdown) { ++ err = -ENODEV; ++ break; ++ } + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; +--- a/sound/core/rawmidi.c ++++ b/sound/core/rawmidi.c +@@ -424,6 +424,10 @@ static int snd_rawmidi_open(struct inode + mutex_unlock(&rmidi->open_mutex); + schedule(); + mutex_lock(&rmidi->open_mutex); ++ if (rmidi->card->shutdown) { ++ err = -ENODEV; ++ break; ++ } + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; +@@ -995,6 +999,8 @@ static ssize_t snd_rawmidi_read(struct f + spin_unlock_irq(&runtime->lock); + schedule(); + remove_wait_queue(&runtime->sleep, &wait); ++ if (rfile->rmidi->card->shutdown) ++ return -ENODEV; + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + if (!runtime->avail) +@@ -1238,6 +1244,8 @@ static ssize_t snd_rawmidi_write(struct + spin_unlock_irq(&runtime->lock); + timeout = schedule_timeout(30 * HZ); + remove_wait_queue(&runtime->sleep, &wait); ++ if (rfile->rmidi->card->shutdown) ++ return -ENODEV; + if (signal_pending(current)) + return result > 0 ? result : -ERESTARTSYS; + if (!runtime->avail && !timeout) +@@ -1613,9 +1621,20 @@ static int snd_rawmidi_dev_register(stru + static int snd_rawmidi_dev_disconnect(struct snd_device *device) + { + struct snd_rawmidi *rmidi = device->device_data; ++ int dir; + + mutex_lock(®ister_mutex); ++ mutex_lock(&rmidi->open_mutex); ++ wake_up(&rmidi->open_wait); + list_del_init(&rmidi->list); ++ for (dir = 0; dir < 2; dir++) { ++ struct snd_rawmidi_substream *s; ++ list_for_each_entry(s, &rmidi->streams[dir].substreams, list) { ++ if (s->runtime) ++ wake_up(&s->runtime->sleep); ++ } ++ } ++ + #ifdef CONFIG_SND_OSSEMUL + if (rmidi->ossreg) { + if ((int)rmidi->device == midi_map[rmidi->card->number]) { +@@ -1630,6 +1649,7 @@ static int snd_rawmidi_dev_disconnect(st + } + #endif /* CONFIG_SND_OSSEMUL */ + snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device); ++ mutex_unlock(&rmidi->open_mutex); + mutex_unlock(®ister_mutex); + return 0; + } diff --git a/queue-3.4/alsa-usb-audio-fix-races-at-disconnection-in-mixer_quirks.c.patch b/queue-3.4/alsa-usb-audio-fix-races-at-disconnection-in-mixer_quirks.c.patch new file mode 100644 index 00000000000..2e86874ced0 --- /dev/null +++ b/queue-3.4/alsa-usb-audio-fix-races-at-disconnection-in-mixer_quirks.c.patch @@ -0,0 +1,117 @@ +From tiwai@suse.de Wed Nov 7 16:01:52 2012 +From: tiwai@suse.de +Date: Wed, 7 Nov 2012 12:42:46 +0100 +Subject: ALSA: usb-audio: Fix races at disconnection in mixer_quirks.c +To: stable@vger.kernel.org + + +From: Takashi Iwai + +commit 888ea7d5ac6815ba16b3b3a20f665a92c7af6724 upstream. + +Similar like the previous commit, cover with chip->shutdown_rwsem +and chip->shutdown checks. + +Reported-by: Matthieu CASTET +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/mixer_quirks.c | 37 +++++++++++++++++++++++++++++++++---- + 1 file changed, 33 insertions(+), 4 deletions(-) + +--- a/sound/usb/mixer_quirks.c ++++ b/sound/usb/mixer_quirks.c +@@ -186,6 +186,11 @@ static int snd_audigy2nx_led_put(struct + if (value > 1) + return -EINVAL; + changed = value != mixer->audigy2nx_leds[index]; ++ down_read(&mixer->chip->shutdown_rwsem); ++ if (mixer->chip->shutdown) { ++ err = -ENODEV; ++ goto out; ++ } + if (mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, +@@ -202,6 +207,8 @@ static int snd_audigy2nx_led_put(struct + usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + value, index + 2, NULL, 0); ++ out: ++ up_read(&mixer->chip->shutdown_rwsem); + if (err < 0) + return err; + mixer->audigy2nx_leds[index] = value; +@@ -295,11 +302,16 @@ static void snd_audigy2nx_proc_read(stru + + for (i = 0; jacks[i].name; ++i) { + snd_iprintf(buffer, "%s: ", jacks[i].name); +- err = snd_usb_ctl_msg(mixer->chip->dev, ++ down_read(&mixer->chip->shutdown_rwsem); ++ if (mixer->chip->shutdown) ++ err = 0; ++ else ++ err = snd_usb_ctl_msg(mixer->chip->dev, + usb_rcvctrlpipe(mixer->chip->dev, 0), + UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, 0, + jacks[i].unitid << 8, buf, 3); ++ up_read(&mixer->chip->shutdown_rwsem); + if (err == 3 && (buf[0] == 3 || buf[0] == 6)) + snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]); + else +@@ -329,10 +341,15 @@ static int snd_xonar_u1_switch_put(struc + else + new_status = old_status & ~0x02; + changed = new_status != old_status; +- err = snd_usb_ctl_msg(mixer->chip->dev, ++ down_read(&mixer->chip->shutdown_rwsem); ++ if (mixer->chip->shutdown) ++ err = -ENODEV; ++ else ++ err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + 50, 0, &new_status, 1); ++ up_read(&mixer->chip->shutdown_rwsem); + if (err < 0) + return err; + mixer->xonar_u1_status = new_status; +@@ -371,11 +388,17 @@ static int snd_nativeinstruments_control + u8 bRequest = (kcontrol->private_value >> 16) & 0xff; + u16 wIndex = kcontrol->private_value & 0xffff; + u8 tmp; ++ int ret; + +- int ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest, ++ down_read(&mixer->chip->shutdown_rwsem); ++ if (mixer->chip->shutdown) ++ ret = -ENODEV; ++ else ++ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0, cpu_to_le16(wIndex), + &tmp, sizeof(tmp), 1000); ++ up_read(&mixer->chip->shutdown_rwsem); + + if (ret < 0) { + snd_printk(KERN_ERR +@@ -396,11 +419,17 @@ static int snd_nativeinstruments_control + u8 bRequest = (kcontrol->private_value >> 16) & 0xff; + u16 wIndex = kcontrol->private_value & 0xffff; + u16 wValue = ucontrol->value.integer.value[0]; ++ int ret; + +- int ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), bRequest, ++ down_read(&mixer->chip->shutdown_rwsem); ++ if (mixer->chip->shutdown) ++ ret = -ENODEV; ++ else ++ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), bRequest, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + cpu_to_le16(wValue), cpu_to_le16(wIndex), + NULL, 0, 1000); ++ up_read(&mixer->chip->shutdown_rwsem); + + if (ret < 0) { + snd_printk(KERN_ERR diff --git a/queue-3.4/alsa-usb-audio-fix-races-at-disconnection.patch b/queue-3.4/alsa-usb-audio-fix-races-at-disconnection.patch new file mode 100644 index 00000000000..4f7a77d409c --- /dev/null +++ b/queue-3.4/alsa-usb-audio-fix-races-at-disconnection.patch @@ -0,0 +1,321 @@ +From tiwai@suse.de Wed Nov 7 16:01:05 2012 +From: tiwai@suse.de +Date: Wed, 7 Nov 2012 12:42:44 +0100 +Subject: ALSA: usb-audio: Fix races at disconnection +To: stable@vger.kernel.org + +From: Takashi Iwai + +commit 978520b75f0a1ce82b17e1e8186417250de6d545 upstream. + +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 +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/card.h | 1 + sound/usb/endpoint.c | 9 ++++--- + sound/usb/mixer.c | 65 +++++++++++++++++++++++++++++++++------------------ + sound/usb/pcm.c | 31 ++++++++++++++++++------ + sound/usb/proc.c | 4 +-- + 5 files changed, 76 insertions(+), 34 deletions(-) + +--- a/sound/usb/card.h ++++ b/sound/usb/card.h +@@ -87,6 +87,7 @@ struct snd_usb_substream { + struct snd_urb_ctx syncurb[SYNC_URBS]; /* sync urb table */ + char *syncbuf; /* sync buffer for all sync URBs */ + dma_addr_t sync_dma; /* DMA address of syncbuf */ ++ unsigned int speed; /* USB_SPEED_XXX */ + + u64 formats; /* format bitmasks (all or'ed) */ + unsigned int num_formats; /* number of supported audio formats (list) */ +--- a/sound/usb/endpoint.c ++++ b/sound/usb/endpoint.c +@@ -148,8 +148,10 @@ void snd_usb_release_substream_urbs(stru + int i; + + /* stop urbs (to be sure) */ +- deactivate_urbs(subs, force, 1); +- wait_clear_urbs(subs); ++ if (!subs->stream->chip->shutdown) { ++ deactivate_urbs(subs, force, 1); ++ wait_clear_urbs(subs); ++ } + + for (i = 0; i < MAX_URBS; i++) + release_urb_ctx(&subs->dataurb[i]); +@@ -895,7 +897,8 @@ void snd_usb_init_substream(struct snd_u + subs->dev = as->chip->dev; + subs->txfr_quirk = as->chip->txfr_quirk; + subs->ops = audio_urb_ops[stream]; +- if (snd_usb_get_speed(subs->dev) >= USB_SPEED_HIGH) ++ subs->speed = snd_usb_get_speed(subs->dev); ++ if (subs->speed >= USB_SPEED_HIGH) + subs->ops.prepare_sync = prepare_capture_sync_urb_hs; + + snd_usb_set_pcm_ops(as->pcm, stream); +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -287,25 +287,32 @@ static int get_ctl_value_v1(struct usb_m + unsigned char buf[2]; + int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; + int timeout = 10; +- int err; ++ int idx = 0, err; + + err = snd_usb_autoresume(cval->mixer->chip); + if (err < 0) + return -EIO; ++ mutex_lock(&chip->shutdown_mutex); + while (timeout-- > 0) { ++ if (chip->shutdown) ++ break; ++ idx = snd_usb_ctrl_intf(chip) | (cval->id << 8); + if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, +- validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), +- buf, val_len) >= val_len) { ++ validx, idx, buf, val_len) >= val_len) { + *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len)); +- snd_usb_autosuspend(cval->mixer->chip); +- return 0; ++ err = 0; ++ goto out; + } + } +- snd_usb_autosuspend(cval->mixer->chip); + snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", +- request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type); +- return -EINVAL; ++ request, validx, idx, cval->val_type); ++ err = -EINVAL; ++ ++ out: ++ mutex_unlock(&chip->shutdown_mutex); ++ snd_usb_autosuspend(cval->mixer->chip); ++ return err; + } + + static 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_m + struct snd_usb_audio *chip = cval->mixer->chip; + unsigned char buf[2 + 3*sizeof(__u16)]; /* enough space for one range */ + unsigned char *val; +- int ret, size; ++ int idx = 0, ret, size; + __u8 bRequest; + + if (request == UAC_GET_CUR) { +@@ -330,16 +337,22 @@ static int get_ctl_value_v2(struct usb_m + if (ret) + goto error; + +- ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest, ++ mutex_lock(&chip->shutdown_mutex); ++ if (chip->shutdown) ++ ret = -ENODEV; ++ else { ++ idx = snd_usb_ctrl_intf(chip) | (cval->id << 8); ++ ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, +- validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), +- buf, size); ++ validx, idx, buf, size); ++ } ++ mutex_unlock(&chip->shutdown_mutex); + snd_usb_autosuspend(chip); + + if (ret < 0) { + error: + snd_printk(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", +- request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type); ++ request, validx, idx, cval->val_type); + return ret; + } + +@@ -417,7 +430,7 @@ int snd_usb_mixer_set_ctl_value(struct u + { + struct snd_usb_audio *chip = cval->mixer->chip; + unsigned char buf[2]; +- int val_len, err, timeout = 10; ++ int idx = 0, val_len, err, timeout = 10; + + if (cval->mixer->protocol == UAC_VERSION_1) { + val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; +@@ -440,19 +453,27 @@ int snd_usb_mixer_set_ctl_value(struct u + err = snd_usb_autoresume(chip); + if (err < 0) + return -EIO; +- while (timeout-- > 0) ++ mutex_lock(&chip->shutdown_mutex); ++ while (timeout-- > 0) { ++ if (chip->shutdown) ++ break; ++ idx = snd_usb_ctrl_intf(chip) | (cval->id << 8); + if (snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), request, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, +- validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), +- buf, val_len) >= 0) { +- snd_usb_autosuspend(chip); +- return 0; ++ validx, idx, buf, val_len) >= 0) { ++ err = 0; ++ goto out; + } +- snd_usb_autosuspend(chip); ++ } + snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n", +- request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type, buf[0], buf[1]); +- return -EINVAL; ++ request, validx, idx, cval->val_type, buf[0], buf[1]); ++ err = -EINVAL; ++ ++ out: ++ mutex_unlock(&chip->shutdown_mutex); ++ snd_usb_autosuspend(chip); ++ return err; + } + + static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int value) +--- a/sound/usb/pcm.c ++++ b/sound/usb/pcm.c +@@ -67,6 +67,8 @@ static snd_pcm_uframes_t snd_usb_pcm_poi + unsigned int hwptr_done; + + subs = (struct snd_usb_substream *)substream->runtime->private_data; ++ if (subs->stream->chip->shutdown) ++ return SNDRV_PCM_POS_XRUN; + spin_lock(&subs->lock); + hwptr_done = subs->hwptr_done; + substream->runtime->delay = snd_usb_pcm_delay(subs, +@@ -373,8 +375,14 @@ static int snd_usb_hw_params(struct snd_ + changed = subs->cur_audiofmt != fmt || + subs->period_bytes != params_period_bytes(hw_params) || + subs->cur_rate != rate; ++ ++ mutex_lock(&subs->stream->chip->shutdown_mutex); ++ if (subs->stream->chip->shutdown) { ++ ret = -ENODEV; ++ goto unlock; ++ } + if ((ret = set_format(subs, fmt)) < 0) +- return ret; ++ goto unlock; + + if (subs->cur_rate != rate) { + struct usb_host_interface *alts; +@@ -383,12 +391,11 @@ static int snd_usb_hw_params(struct snd_ + alts = &iface->altsetting[fmt->altset_idx]; + ret = snd_usb_init_sample_rate(subs->stream->chip, subs->interface, alts, fmt, rate); + if (ret < 0) +- return ret; ++ goto unlock; + subs->cur_rate = rate; + } + + if (changed) { +- mutex_lock(&subs->stream->chip->shutdown_mutex); + /* format changed */ + snd_usb_release_substream_urbs(subs, 0); + /* influenced: period_bytes, channels, rate, format, */ +@@ -396,9 +403,10 @@ static int snd_usb_hw_params(struct snd_ + params_rate(hw_params), + snd_pcm_format_physical_width(params_format(hw_params)) * + params_channels(hw_params)); +- mutex_unlock(&subs->stream->chip->shutdown_mutex); + } + ++unlock: ++ mutex_unlock(&subs->stream->chip->shutdown_mutex); + return ret; + } + +@@ -429,12 +437,18 @@ static int snd_usb_pcm_prepare(struct sn + { + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_usb_substream *subs = runtime->private_data; ++ int ret = 0; + + if (! subs->cur_audiofmt) { + snd_printk(KERN_ERR "usbaudio: no format is specified!\n"); + return -ENXIO; + } + ++ mutex_lock(&subs->stream->chip->shutdown_mutex); ++ if (subs->stream->chip->shutdown) { ++ ret = -ENODEV; ++ goto unlock; ++ } + /* some unit conversions in runtime */ + subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize); + subs->curframesize = bytes_to_frames(runtime, subs->curpacksize); +@@ -447,7 +461,10 @@ static int snd_usb_pcm_prepare(struct sn + subs->last_frame_number = 0; + runtime->delay = 0; + +- return snd_usb_substream_prepare(subs, runtime); ++ ret = snd_usb_substream_prepare(subs, runtime); ++ unlock: ++ mutex_unlock(&subs->stream->chip->shutdown_mutex); ++ return ret; + } + + static struct snd_pcm_hardware snd_usb_hardware = +@@ -500,7 +517,7 @@ static int hw_check_valid_format(struct + return 0; + } + /* check whether the period time is >= the data packet interval */ +- if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL) { ++ if (subs->speed != USB_SPEED_FULL) { + ptime = 125 * (1 << fp->datainterval); + if (ptime > pt->max || (ptime == pt->max && pt->openmax)) { + hwc_debug(" > check: ptime %u > max %u\n", ptime, pt->max); +@@ -778,7 +795,7 @@ static int setup_hw_info(struct snd_pcm_ + return err; + + param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME; +- if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) ++ if (subs->speed == USB_SPEED_FULL) + /* full speed devices have fixed data packet interval */ + ptmin = 1000; + if (ptmin == 1000) +--- a/sound/usb/proc.c ++++ b/sound/usb/proc.c +@@ -107,7 +107,7 @@ static void proc_dump_substream_formats( + } + snd_iprintf(buffer, "\n"); + } +- if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL) ++ if (subs->speed != USB_SPEED_FULL) + snd_iprintf(buffer, " Data packet interval: %d us\n", + 125 * (1 << fp->datainterval)); + // snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize); +@@ -128,7 +128,7 @@ static void proc_dump_substream_status(s + snd_iprintf(buffer, "]\n"); + snd_iprintf(buffer, " Packet Size = %d\n", subs->curpacksize); + snd_iprintf(buffer, " Momentary freq = %u Hz (%#x.%04x)\n", +- snd_usb_get_speed(subs->dev) == USB_SPEED_FULL ++ subs->speed == USB_SPEED_FULL + ? get_full_speed_hz(subs->freqm) + : get_high_speed_hz(subs->freqm), + subs->freqm >> 16, subs->freqm & 0xffff); diff --git a/queue-3.4/alsa-usb-audio-use-rwsem-for-disconnect-protection.patch b/queue-3.4/alsa-usb-audio-use-rwsem-for-disconnect-protection.patch new file mode 100644 index 00000000000..33a61dc8cf4 --- /dev/null +++ b/queue-3.4/alsa-usb-audio-use-rwsem-for-disconnect-protection.patch @@ -0,0 +1,198 @@ +From tiwai@suse.de Wed Nov 7 16:01:36 2012 +From: tiwai@suse.de +Date: Wed, 7 Nov 2012 12:42:45 +0100 +Subject: ALSA: usb-audio: Use rwsem for disconnect protection +To: stable@vger.kernel.org + +From: Takashi Iwai + +commit 34f3c89fda4fba9fe689db22253ca8db2f5e6386 upstream. + +Replace mutex with rwsem for codec->shutdown protection so that +concurrent accesses are allowed. + +Also add the protection to snd_usb_autosuspend() and +snd_usb_autoresume(), too. + +Reported-by: Matthieu CASTET +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman +--- + sound/usb/card.c | 12 ++++++++---- + sound/usb/mixer.c | 12 ++++++------ + sound/usb/pcm.c | 12 ++++++------ + sound/usb/usbaudio.h | 2 +- + 4 files changed, 21 insertions(+), 17 deletions(-) + +--- a/sound/usb/card.c ++++ b/sound/usb/card.c +@@ -336,7 +336,7 @@ static int snd_usb_audio_create(struct u + return -ENOMEM; + } + +- mutex_init(&chip->shutdown_mutex); ++ init_rwsem(&chip->shutdown_rwsem); + chip->index = idx; + chip->dev = dev; + chip->card = card; +@@ -556,7 +556,7 @@ static void snd_usb_audio_disconnect(str + + card = chip->card; + mutex_lock(®ister_mutex); +- mutex_lock(&chip->shutdown_mutex); ++ down_write(&chip->shutdown_rwsem); + chip->shutdown = 1; + chip->num_interfaces--; + if (chip->num_interfaces <= 0) { +@@ -574,11 +574,11 @@ static void snd_usb_audio_disconnect(str + snd_usb_mixer_disconnect(p); + } + usb_chip[chip->index] = NULL; +- mutex_unlock(&chip->shutdown_mutex); ++ up_write(&chip->shutdown_rwsem); + mutex_unlock(®ister_mutex); + snd_card_free_when_closed(card); + } else { +- mutex_unlock(&chip->shutdown_mutex); ++ up_write(&chip->shutdown_rwsem); + mutex_unlock(®ister_mutex); + } + } +@@ -610,16 +610,20 @@ int snd_usb_autoresume(struct snd_usb_au + { + int err = -ENODEV; + ++ down_read(&chip->shutdown_rwsem); + if (!chip->shutdown && !chip->probing) + err = usb_autopm_get_interface(chip->pm_intf); ++ up_read(&chip->shutdown_rwsem); + + return err; + } + + void snd_usb_autosuspend(struct snd_usb_audio *chip) + { ++ down_read(&chip->shutdown_rwsem); + if (!chip->shutdown && !chip->probing) + usb_autopm_put_interface(chip->pm_intf); ++ up_read(&chip->shutdown_rwsem); + } + + static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -292,7 +292,7 @@ static int get_ctl_value_v1(struct usb_m + err = snd_usb_autoresume(cval->mixer->chip); + if (err < 0) + return -EIO; +- mutex_lock(&chip->shutdown_mutex); ++ down_read(&chip->shutdown_rwsem); + while (timeout-- > 0) { + if (chip->shutdown) + break; +@@ -310,7 +310,7 @@ static int get_ctl_value_v1(struct usb_m + err = -EINVAL; + + out: +- mutex_unlock(&chip->shutdown_mutex); ++ up_read(&chip->shutdown_rwsem); + snd_usb_autosuspend(cval->mixer->chip); + return err; + } +@@ -337,7 +337,7 @@ static int get_ctl_value_v2(struct usb_m + if (ret) + goto error; + +- mutex_lock(&chip->shutdown_mutex); ++ down_read(&chip->shutdown_rwsem); + if (chip->shutdown) + ret = -ENODEV; + else { +@@ -346,7 +346,7 @@ static int get_ctl_value_v2(struct usb_m + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + validx, idx, buf, size); + } +- mutex_unlock(&chip->shutdown_mutex); ++ up_read(&chip->shutdown_rwsem); + snd_usb_autosuspend(chip); + + if (ret < 0) { +@@ -453,7 +453,7 @@ int snd_usb_mixer_set_ctl_value(struct u + err = snd_usb_autoresume(chip); + if (err < 0) + return -EIO; +- mutex_lock(&chip->shutdown_mutex); ++ down_read(&chip->shutdown_rwsem); + while (timeout-- > 0) { + if (chip->shutdown) + break; +@@ -471,7 +471,7 @@ int snd_usb_mixer_set_ctl_value(struct u + err = -EINVAL; + + out: +- mutex_unlock(&chip->shutdown_mutex); ++ up_read(&chip->shutdown_rwsem); + snd_usb_autosuspend(chip); + return err; + } +--- a/sound/usb/pcm.c ++++ b/sound/usb/pcm.c +@@ -376,7 +376,7 @@ static int snd_usb_hw_params(struct snd_ + subs->period_bytes != params_period_bytes(hw_params) || + subs->cur_rate != rate; + +- mutex_lock(&subs->stream->chip->shutdown_mutex); ++ down_read(&subs->stream->chip->shutdown_rwsem); + if (subs->stream->chip->shutdown) { + ret = -ENODEV; + goto unlock; +@@ -406,7 +406,7 @@ static int snd_usb_hw_params(struct snd_ + } + + unlock: +- mutex_unlock(&subs->stream->chip->shutdown_mutex); ++ up_read(&subs->stream->chip->shutdown_rwsem); + return ret; + } + +@@ -422,9 +422,9 @@ static int snd_usb_hw_free(struct snd_pc + subs->cur_audiofmt = NULL; + subs->cur_rate = 0; + subs->period_bytes = 0; +- mutex_lock(&subs->stream->chip->shutdown_mutex); ++ down_read(&subs->stream->chip->shutdown_rwsem); + snd_usb_release_substream_urbs(subs, 0); +- mutex_unlock(&subs->stream->chip->shutdown_mutex); ++ up_read(&subs->stream->chip->shutdown_rwsem); + return snd_pcm_lib_free_vmalloc_buffer(substream); + } + +@@ -444,7 +444,7 @@ static int snd_usb_pcm_prepare(struct sn + return -ENXIO; + } + +- mutex_lock(&subs->stream->chip->shutdown_mutex); ++ down_read(&subs->stream->chip->shutdown_rwsem); + if (subs->stream->chip->shutdown) { + ret = -ENODEV; + goto unlock; +@@ -463,7 +463,7 @@ static int snd_usb_pcm_prepare(struct sn + + ret = snd_usb_substream_prepare(subs, runtime); + unlock: +- mutex_unlock(&subs->stream->chip->shutdown_mutex); ++ up_read(&subs->stream->chip->shutdown_rwsem); + return ret; + } + +--- a/sound/usb/usbaudio.h ++++ b/sound/usb/usbaudio.h +@@ -36,7 +36,7 @@ struct snd_usb_audio { + struct snd_card *card; + struct usb_interface *pm_intf; + u32 usb_id; +- struct mutex shutdown_mutex; ++ struct rw_semaphore shutdown_rwsem; + unsigned int shutdown:1; + unsigned int probing:1; + unsigned int autosuspended:1; diff --git a/queue-3.4/series b/queue-3.4/series index feccc7a9477..4813da5c5cc 100644 --- a/queue-3.4/series +++ b/queue-3.4/series @@ -29,3 +29,8 @@ nfs-fix-oopses-in-nfs_lookup_revalidate-and-nfs4_lookup_revalidate.patch drm-restore-open_count-if-drm_setup-fails.patch hwmon-w83627ehf-force-initial-bank-selection.patch alsa-pcm-fix-some-races-at-disconnection.patch +alsa-usb-audio-fix-races-at-disconnection.patch +alsa-usb-audio-use-rwsem-for-disconnect-protection.patch +alsa-usb-audio-fix-races-at-disconnection-in-mixer_quirks.c.patch +alsa-add-a-reference-counter-to-card-instance.patch +alsa-avoid-endless-sleep-after-disconnect.patch