diff options
Diffstat (limited to 'sound/core')
| -rw-r--r-- | sound/core/compress_offload.c | 9 | ||||
| -rw-r--r-- | sound/core/control.c | 5 | ||||
| -rw-r--r-- | sound/core/hwdep.c | 12 | ||||
| -rw-r--r-- | sound/core/init.c | 50 | ||||
| -rw-r--r-- | sound/core/oss/mixer_oss.c | 10 | ||||
| -rw-r--r-- | sound/core/oss/pcm_oss.c | 6 | ||||
| -rw-r--r-- | sound/core/pcm.c | 13 | ||||
| -rw-r--r-- | sound/core/pcm_native.c | 33 | ||||
| -rw-r--r-- | sound/core/rawmidi.c | 26 | ||||
| -rw-r--r-- | sound/core/sound.c | 11 | ||||
| -rw-r--r-- | sound/core/sound_oss.c | 10 | 
11 files changed, 147 insertions, 38 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index c40ae573346..ad11dc99479 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -100,12 +100,15 @@ 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; @@ -113,6 +116,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)  	runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);  	if (!runtime) {  		kfree(data); +		snd_card_unref(compr->card);  		return -ENOMEM;  	}  	runtime->state = SNDRV_PCM_STATE_OPEN; @@ -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) diff --git a/sound/core/control.c b/sound/core/control.c index 7e86a5b9f3b..8c7c2c9bba6 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -86,6 +86,7 @@ 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: @@ -93,6 +94,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file)        __error2:  	snd_card_file_remove(card, file);        __error1: +	if (card) +		snd_card_unref(card);        	return err;  } @@ -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); diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 75ea16f35b1..3f7f6628cf7 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -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); @@ -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; @@ -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;  } @@ -459,12 +466,15 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)  		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;  } diff --git a/sound/core/init.c b/sound/core/init.c index d8ec849af12..7b012d15c2c 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -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); @@ -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;  } @@ -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 *card, struct file *file)  		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 *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;  } diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 29f6ded0255..a9a2e63c022 100644 --- 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 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; @@ -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; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 08fde0060fd..f337b66a020 100644 --- 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 *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; @@ -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;  } diff --git a/sound/core/pcm.c b/sound/core/pcm.c index f2991940b27..030102caeee 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -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);  	} @@ -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(®ister_mutex);  	return 0; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 5e12e5bacbb..6e8872de5ba 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -369,6 +369,14 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime)  	return usecs;  } +static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state) +{ +	snd_pcm_stream_lock_irq(substream); +	if (substream->runtime->status->state != SNDRV_PCM_STATE_DISCONNECTED) +		substream->runtime->status->state = state; +	snd_pcm_stream_unlock_irq(substream); +} +  static int snd_pcm_hw_params(struct snd_pcm_substream *substream,  			     struct snd_pcm_hw_params *params)  { @@ -452,7 +460,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,  		runtime->boundary *= 2;  	snd_pcm_timer_resolution_change(substream); -	runtime->status->state = SNDRV_PCM_STATE_SETUP; +	snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP);  	if (pm_qos_request_active(&substream->latency_pm_qos_req))  		pm_qos_remove_request(&substream->latency_pm_qos_req); @@ -464,7 +472,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,  	/* hardware might be unusable from this time,  	   so we force application to retry to set  	   the correct hardware parameter settings */ -	runtime->status->state = SNDRV_PCM_STATE_OPEN; +	snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);  	if (substream->ops->hw_free != NULL)  		substream->ops->hw_free(substream);  	return err; @@ -512,7 +520,7 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)  		return -EBADFD;  	if (substream->ops->hw_free)  		result = substream->ops->hw_free(substream); -	runtime->status->state = SNDRV_PCM_STATE_OPEN; +	snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);  	pm_qos_remove_request(&substream->latency_pm_qos_req);  	return result;  } @@ -1320,7 +1328,7 @@ static void snd_pcm_post_prepare(struct snd_pcm_substream *substream, int state)  {  	struct snd_pcm_runtime *runtime = substream->runtime;  	runtime->control->appl_ptr = runtime->status->hw_ptr; -	runtime->status->state = SNDRV_PCM_STATE_PREPARED; +	snd_pcm_set_state(substream, SNDRV_PCM_STATE_PREPARED);  }  static struct action_ops snd_pcm_action_prepare = { @@ -1510,6 +1518,10 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,  		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; @@ -1634,6 +1646,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)  	write_unlock_irq(&snd_pcm_link_rwlock);  	up_write(&snd_pcm_link_rwsem);   _nolock: +	snd_card_unref(substream1->pcm->card);  	fput_light(file, fput_needed);  	if (res < 0)  		kfree(group); @@ -2108,7 +2121,9 @@ static int snd_pcm_playback_open(struct inode *inode, struct file *file)  		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) @@ -2119,7 +2134,9 @@ static int snd_pcm_capture_open(struct inode *inode, struct file *file)  		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) @@ -2156,6 +2173,10 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)  		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; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index ebf6e49ad3d..1bb95aeea08 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -379,8 +379,10 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)  	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; @@ -422,6 +424,10 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)  		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; @@ -440,6 +446,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)  #endif  	file->private_data = rawmidi_file;  	mutex_unlock(&rmidi->open_mutex); +	snd_card_unref(rmidi->card);  	return 0;   __error: @@ -447,6 +454,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)   __error_card:  	mutex_unlock(&rmidi->open_mutex);  	module_put(rmidi->card->module); +	snd_card_unref(rmidi->card);  	return err;  } @@ -991,6 +999,8 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun  			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) @@ -1234,6 +1244,8 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,  			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) @@ -1609,9 +1621,20 @@ static int snd_rawmidi_dev_register(struct snd_device *device)  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]) { @@ -1626,6 +1649,7 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)  	}  #endif /* CONFIG_SND_OSSEMUL */  	snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device); +	mutex_unlock(&rmidi->open_mutex);  	mutex_unlock(®ister_mutex);  	return 0;  } diff --git a/sound/core/sound.c b/sound/core/sound.c index 643976000ce..89780c323f1 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -98,6 +98,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)  { @@ -108,9 +112,11 @@ void *snd_lookup_minor_data(unsigned int minor, int type)  		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, struct snd_card *card, int dev,  	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); diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index e9528333e36..e1d79ee3590 100644 --- 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 int minor, int type)  		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, struct snd_card *card, int dev,  	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);  |