]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.0-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 7 Nov 2012 15:11:29 +0000 (16:11 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 7 Nov 2012 15:11:29 +0000 (16:11 +0100)
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

queue-3.0/alsa-add-a-reference-counter-to-card-instance.patch [new file with mode: 0644]
queue-3.0/alsa-avoid-endless-sleep-after-disconnect.patch [new file with mode: 0644]
queue-3.0/alsa-usb-audio-fix-races-at-disconnection-in-mixer_quirks.c.patch [new file with mode: 0644]
queue-3.0/alsa-usb-audio-fix-races-at-disconnection.patch [new file with mode: 0644]
queue-3.0/alsa-usb-audio-use-rwsem-for-disconnect-protection.patch [new file with mode: 0644]
queue-3.0/series

diff --git a/queue-3.0/alsa-add-a-reference-counter-to-card-instance.patch b/queue-3.0/alsa-add-a-reference-counter-to-card-instance.patch
new file mode 100644 (file)
index 0000000..c249898
--- /dev/null
@@ -0,0 +1,378 @@
+From tiwai@suse.de  Wed Nov  7 16:10:10 2012
+From: tiwai@suse.de
+Date: Wed,  7 Nov 2012 12:44:12 +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/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 +++++++--
+ 10 files changed, 79 insertions(+), 30 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/control.c
++++ b/sound/core/control.c
+@@ -85,6 +85,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:
+@@ -92,6 +93,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
+@@ -99,8 +99,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);
+@@ -147,6 +149,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
+@@ -211,6 +211,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);
+@@ -444,21 +445,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;
+ }
+@@ -472,7 +488,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;
+ }
+@@ -853,6 +869,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;
+ }
+@@ -875,7 +892,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) {
+@@ -890,19 +906,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
+@@ -51,14 +51,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;
+@@ -67,6 +72,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
+@@ -1627,6 +1627,7 @@ static int snd_pcm_link(struct snd_pcm_s
+  _end:
+       write_unlock_irq(&snd_pcm_link_rwlock);
+       up_write(&snd_pcm_link_rwsem);
++      snd_card_unref(substream1->pcm->card);
+       fput(file);
+       return res;
+ }
+@@ -2104,7 +2105,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)
+@@ -2115,7 +2118,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
+@@ -394,8 +394,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;
+@@ -455,6 +457,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:
+@@ -462,6 +465,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;
+@@ -275,6 +281,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
+@@ -39,6 +39,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;
+@@ -48,9 +51,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;
+@@ -122,6 +127,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.0/alsa-avoid-endless-sleep-after-disconnect.patch b/queue-3.0/alsa-avoid-endless-sleep-after-disconnect.patch
new file mode 100644 (file)
index 0000000..bee3c04
--- /dev/null
@@ -0,0 +1,182 @@
+From tiwai@suse.de  Wed Nov  7 16:10:30 2012
+From: tiwai@suse.de
+Date: Wed,  7 Nov 2012 12:44:13 +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
+@@ -1363,6 +1363,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
+@@ -130,6 +130,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;
+@@ -454,12 +458,15 @@ static int snd_hwdep_dev_disconnect(stru
+               mutex_unlock(&register_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(&register_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
+@@ -1046,12 +1046,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
+@@ -1507,6 +1507,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
+@@ -439,6 +439,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;
+@@ -1010,6 +1014,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)
+@@ -1253,6 +1259,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)
+@@ -1628,9 +1636,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(&register_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]) {
+@@ -1645,6 +1664,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(&register_mutex);
+       return 0;
+ }
diff --git a/queue-3.0/alsa-usb-audio-fix-races-at-disconnection-in-mixer_quirks.c.patch b/queue-3.0/alsa-usb-audio-fix-races-at-disconnection-in-mixer_quirks.c.patch
new file mode 100644 (file)
index 0000000..4c2c90b
--- /dev/null
@@ -0,0 +1,117 @@
+From tiwai@suse.de  Wed Nov  7 16:09:03 2012
+From: tiwai@suse.de
+Date: Wed,  7 Nov 2012 12:44:11 +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, 100);
++ 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, 100);
++              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, 100);
++      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.0/alsa-usb-audio-fix-races-at-disconnection.patch b/queue-3.0/alsa-usb-audio-fix-races-at-disconnection.patch
new file mode 100644 (file)
index 0000000..6cf5053
--- /dev/null
@@ -0,0 +1,321 @@
+From tiwai@suse.de  Wed Nov  7 16:06:25 2012
+From: tiwai@suse.de
+Date: Wed,  7 Nov 2012 12:44:09 +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/mixer.c |   65 +++++++++++++++++++++++++++++++++++-------------------
+ sound/usb/pcm.c   |   31 +++++++++++++++++++------
+ sound/usb/proc.c  |    4 +--
+ sound/usb/urb.c   |    9 ++++---
+ 5 files changed, 76 insertions(+), 34 deletions(-)
+
+--- a/sound/usb/card.h
++++ b/sound/usb/card.h
+@@ -86,6 +86,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/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, 100) >= val_len) {
++                                  validx, idx, buf, val_len, 100) >= 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, 1000);
++                            validx, idx, buf, size, 1000);
++      }
++      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, 100) >= 0) {
+-                      snd_usb_autosuspend(chip);
+-                      return 0;
++                                  validx, idx, buf, val_len, 100) >= 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
+@@ -43,6 +43,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;
+       spin_unlock(&subs->lock);
+@@ -347,8 +349,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;
+@@ -357,12 +365,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, */
+@@ -370,9 +377,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;
+ }
+@@ -403,12 +411,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);
+@@ -419,7 +433,10 @@ static int snd_usb_pcm_prepare(struct sn
+       subs->phase = 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 =
+@@ -472,7 +489,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);
+@@ -748,7 +765,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);
+--- a/sound/usb/urb.c
++++ b/sound/usb/urb.c
+@@ -147,8 +147,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]);
+@@ -870,7 +872,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);
diff --git a/queue-3.0/alsa-usb-audio-use-rwsem-for-disconnect-protection.patch b/queue-3.0/alsa-usb-audio-use-rwsem-for-disconnect-protection.patch
new file mode 100644 (file)
index 0000000..d47a43e
--- /dev/null
@@ -0,0 +1,198 @@
+From tiwai@suse.de  Wed Nov  7 16:08:48 2012
+From: tiwai@suse.de
+Date: Wed,  7 Nov 2012 12:44:10 +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
+@@ -335,7 +335,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;
+@@ -555,7 +555,7 @@ static void snd_usb_audio_disconnect(str
+       chip = ptr;
+       card = chip->card;
+       mutex_lock(&register_mutex);
+-      mutex_lock(&chip->shutdown_mutex);
++      down_write(&chip->shutdown_rwsem);
+       chip->shutdown = 1;
+       chip->num_interfaces--;
+       if (chip->num_interfaces <= 0) {
+@@ -573,11 +573,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(&register_mutex);
+               snd_card_free_when_closed(card);
+       } else {
+-              mutex_unlock(&chip->shutdown_mutex);
++              up_write(&chip->shutdown_rwsem);
+               mutex_unlock(&register_mutex);
+       }
+ }
+@@ -609,16 +609,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, 1000);
+       }
+-      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
+@@ -350,7 +350,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;
+@@ -380,7 +380,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;
+ }
+@@ -396,9 +396,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);
+ }
+@@ -418,7 +418,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;
+@@ -435,7 +435,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;   
index 84cba1089d1b949d8000bad5fca13a09cbffd62b..0af4ff5010125bf3f7f308f32f9153eb5fac5ed7 100644 (file)
@@ -16,3 +16,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