mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-25 16:11:45 +00:00
ALSA: Add a reference counter to card instance
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> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
888ea7d5ac
commit
a0830dbd4e
11 changed files with 86 additions and 32 deletions
|
@ -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)
|
||||
int ret;
|
||||
|
||||
atomic_inc(&card->refcount);
|
||||
ret = snd_card_disconnect(card);
|
||||
if (ret) {
|
||||
atomic_dec(&card->refcount);
|
||||
return 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue