Skip to content

Commit

Permalink
Merge tag 'sound-3.7' of git://git.kernel.org/pub/scm/linux/kernel/gi…
Browse files Browse the repository at this point in the history
…t/tiwai/sound

Pull sound fixes from Takashi Iwai:
 "This contains unexpectedly many changes in a wide range due to the
  fixes for races at disconnection of USB audio devices.  In the end, we
  end up covering fairly core parts of sound subsystem.

  Other than that, just a few usual small fixes."

* tag 'sound-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound:
  ALSA: ice1724: Fix rate setup after resume
  ALSA: Avoid endless sleep after disconnect
  ALSA: Add a reference counter to card instance
  ALSA: usb-audio: Fix races at disconnection in mixer_quirks.c
  ALSA: usb-audio: Use rwsem for disconnect protection
  ALSA: usb-audio: Fix races at disconnection
  ALSA: PCM: Fix some races at disconnection
  ASoC: omap-dmic: Correct functional clock name
  ASoC: zoom2: Fix compile error by including correct header files
  ALSA: hda - Fix mute-LED setup for HP dv5 laptop
  • Loading branch information
Linus Torvalds committed Oct 31, 2012
2 parents 08f05c4 + 16c2e1f commit 3304695
Show file tree
Hide file tree
Showing 24 changed files with 303 additions and 99 deletions.
3 changes: 3 additions & 0 deletions include/sound/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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))

Expand Down
9 changes: 7 additions & 2 deletions sound/core/compress_offload.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,23 @@ static int snd_compr_open(struct inode *inode, struct file *f)

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;
data->stream.device = compr;
runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
if (!runtime) {
kfree(data);
snd_card_unref(compr->card);
return -ENOMEM;
}
runtime->state = SNDRV_PCM_STATE_OPEN;
Expand All @@ -126,7 +130,8 @@ static int snd_compr_open(struct inode *inode, struct file *f)
kfree(runtime);
kfree(data);
}
return ret;
snd_card_unref(compr->card);
return 0;
}

static int snd_compr_free(struct inode *inode, struct file *f)
Expand Down
5 changes: 5 additions & 0 deletions sound/core/control.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,16 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
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:
module_put(card->module);
__error2:
snd_card_file_remove(card, file);
__error1:
if (card)
snd_card_unref(card);
return err;
}

Expand Down Expand Up @@ -1434,6 +1437,8 @@ static ssize_t snd_ctl_read(struct file *file, char __user *buffer,
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);
Expand Down
12 changes: 11 additions & 1 deletion sound/core/hwdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
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);
Expand Down Expand Up @@ -129,6 +131,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
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;
Expand All @@ -148,6 +154,7 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
mutex_unlock(&hw->open_mutex);
if (err < 0)
module_put(hw->card->module);
snd_card_unref(hw->card);
return err;
}

Expand Down Expand Up @@ -459,12 +466,15 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)
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;
}
Expand Down
50 changes: 30 additions & 20 deletions sound/core/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ int snd_card_create(int idx, const char *xid,
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);
Expand Down Expand Up @@ -446,21 +447,36 @@ static int snd_card_do_free(struct snd_card *card)
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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -886,6 +902,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file)
return -ENODEV;
}
list_add(&mfile->list, &card->files_list);
atomic_inc(&card->refcount);
spin_unlock(&card->files_lock);
return 0;
}
Expand All @@ -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) {
Expand All @@ -923,19 +939,13 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
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;
}

Expand Down
10 changes: 8 additions & 2 deletions sound/core/oss/mixer_oss.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,19 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file)
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;
Expand All @@ -68,6 +73,7 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file)
if (!try_module_get(card->module)) {
kfree(fmixer);
snd_card_file_remove(card, file);
snd_card_unref(card);
return -EFAULT;
}
return 0;
Expand Down
6 changes: 6 additions & 0 deletions sound/core/oss/pcm_oss.c
Original file line number Diff line number Diff line change
Expand Up @@ -2441,6 +2441,10 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
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;
Expand All @@ -2457,6 +2461,8 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
__error2:
snd_card_file_remove(pcm->card, file);
__error1:
if (pcm)
snd_card_unref(pcm->card);
return err;
}

Expand Down
13 changes: 11 additions & 2 deletions sound/core/pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1086,11 +1086,19 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
if (list_empty(&pcm->list))
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)
if (substream->runtime)
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) {
snd_pcm_stream_lock_irq(substream);
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) {
notify->n_disconnect(pcm);
}
Expand All @@ -1110,6 +1118,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
pcm->streams[cidx].chmap_kctl = NULL;
}
}
mutex_unlock(&pcm->open_mutex);
unlock:
mutex_unlock(&register_mutex);
return 0;
Expand Down
Loading

0 comments on commit 3304695

Please sign in to comment.