--- /dev/null
+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 <tiwai@suse.de>
+
+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 <matthieu.castet@parrot.com>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);
--- /dev/null
+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 <tiwai@suse.de>
+
+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 <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
+ }
--- /dev/null
+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 <tiwai@suse.de>
+
+commit 888ea7d5ac6815ba16b3b3a20f665a92c7af6724 upstream.
+
+Similar like the previous commit, cover with chip->shutdown_rwsem
+and chip->shutdown checks.
+
+Reported-by: Matthieu CASTET <matthieu.castet@parrot.com>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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
--- /dev/null
+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 <tiwai@suse.de>
+
+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 <matthieu.castet@parrot.com>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);
--- /dev/null
+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 <tiwai@suse.de>
+
+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 <matthieu.castet@parrot.com>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
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