aboutsummaryrefslogtreecommitdiffstats
path: root/sound/core
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-10-16 07:05:59 -0400
committerTakashi Iwai <tiwai@suse.de>2012-10-30 06:07:10 -0400
commita0830dbd4e42b38aefdf3fb61ba5019a1a99ea85 (patch)
tree4dc74b708a07b56d12ed72a34d0a2e0cb8c8b9d4 /sound/core
parent888ea7d5ac6815ba16b3b3a20f665a92c7af6724 (diff)
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>
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/compress_offload.c9
-rw-r--r--sound/core/control.c3
-rw-r--r--sound/core/hwdep.c5
-rw-r--r--sound/core/init.c50
-rw-r--r--sound/core/oss/mixer_oss.c10
-rw-r--r--sound/core/oss/pcm_oss.c2
-rw-r--r--sound/core/pcm_native.c9
-rw-r--r--sound/core/rawmidi.c6
-rw-r--r--sound/core/sound.c11
-rw-r--r--sound/core/sound_oss.c10
10 files changed, 83 insertions, 32 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index c40ae573346d..ad11dc994792 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)
100 100
101 if (dirn != compr->direction) { 101 if (dirn != compr->direction) {
102 pr_err("this device doesn't support this direction\n"); 102 pr_err("this device doesn't support this direction\n");
103 snd_card_unref(compr->card);
103 return -EINVAL; 104 return -EINVAL;
104 } 105 }
105 106
106 data = kzalloc(sizeof(*data), GFP_KERNEL); 107 data = kzalloc(sizeof(*data), GFP_KERNEL);
107 if (!data) 108 if (!data) {
109 snd_card_unref(compr->card);
108 return -ENOMEM; 110 return -ENOMEM;
111 }
109 data->stream.ops = compr->ops; 112 data->stream.ops = compr->ops;
110 data->stream.direction = dirn; 113 data->stream.direction = dirn;
111 data->stream.private_data = compr->private_data; 114 data->stream.private_data = compr->private_data;
@@ -113,6 +116,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
113 runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 116 runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
114 if (!runtime) { 117 if (!runtime) {
115 kfree(data); 118 kfree(data);
119 snd_card_unref(compr->card);
116 return -ENOMEM; 120 return -ENOMEM;
117 } 121 }
118 runtime->state = SNDRV_PCM_STATE_OPEN; 122 runtime->state = SNDRV_PCM_STATE_OPEN;
@@ -126,7 +130,8 @@ static int snd_compr_open(struct inode *inode, struct file *f)
126 kfree(runtime); 130 kfree(runtime);
127 kfree(data); 131 kfree(data);
128 } 132 }
129 return ret; 133 snd_card_unref(compr->card);
134 return 0;
130} 135}
131 136
132static int snd_compr_free(struct inode *inode, struct file *f) 137static int snd_compr_free(struct inode *inode, struct file *f)
diff --git a/sound/core/control.c b/sound/core/control.c
index 7e86a5b9f3b5..9768a3963c8f 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)
86 write_lock_irqsave(&card->ctl_files_rwlock, flags); 86 write_lock_irqsave(&card->ctl_files_rwlock, flags);
87 list_add_tail(&ctl->list, &card->ctl_files); 87 list_add_tail(&ctl->list, &card->ctl_files);
88 write_unlock_irqrestore(&card->ctl_files_rwlock, flags); 88 write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
89 snd_card_unref(card);
89 return 0; 90 return 0;
90 91
91 __error: 92 __error:
@@ -93,6 +94,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
93 __error2: 94 __error2:
94 snd_card_file_remove(card, file); 95 snd_card_file_remove(card, file);
95 __error1: 96 __error1:
97 if (card)
98 snd_card_unref(card);
96 return err; 99 return err;
97} 100}
98 101
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index 75ea16f35b1a..53a6ba5ad615 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)
100 if (hw == NULL) 100 if (hw == NULL)
101 return -ENODEV; 101 return -ENODEV;
102 102
103 if (!try_module_get(hw->card->module)) 103 if (!try_module_get(hw->card->module)) {
104 snd_card_unref(hw->card);
104 return -EFAULT; 105 return -EFAULT;
106 }
105 107
106 init_waitqueue_entry(&wait, current); 108 init_waitqueue_entry(&wait, current);
107 add_wait_queue(&hw->open_wait, &wait); 109 add_wait_queue(&hw->open_wait, &wait);
@@ -148,6 +150,7 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
148 mutex_unlock(&hw->open_mutex); 150 mutex_unlock(&hw->open_mutex);
149 if (err < 0) 151 if (err < 0)
150 module_put(hw->card->module); 152 module_put(hw->card->module);
153 snd_card_unref(hw->card);
151 return err; 154 return err;
152} 155}
153 156
diff --git a/sound/core/init.c b/sound/core/init.c
index d8ec849af128..7b012d15c2cf 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -213,6 +213,7 @@ int snd_card_create(int idx, const char *xid,
213 spin_lock_init(&card->files_lock); 213 spin_lock_init(&card->files_lock);
214 INIT_LIST_HEAD(&card->files_list); 214 INIT_LIST_HEAD(&card->files_list);
215 init_waitqueue_head(&card->shutdown_sleep); 215 init_waitqueue_head(&card->shutdown_sleep);
216 atomic_set(&card->refcount, 0);
216#ifdef CONFIG_PM 217#ifdef CONFIG_PM
217 mutex_init(&card->power_lock); 218 mutex_init(&card->power_lock);
218 init_waitqueue_head(&card->power_sleep); 219 init_waitqueue_head(&card->power_sleep);
@@ -446,21 +447,36 @@ static int snd_card_do_free(struct snd_card *card)
446 return 0; 447 return 0;
447} 448}
448 449
450/**
451 * snd_card_unref - release the reference counter
452 * @card: the card instance
453 *
454 * Decrements the reference counter. When it reaches to zero, wake up
455 * the sleeper and call the destructor if needed.
456 */
457void snd_card_unref(struct snd_card *card)
458{
459 if (atomic_dec_and_test(&card->refcount)) {
460 wake_up(&card->shutdown_sleep);
461 if (card->free_on_last_close)
462 snd_card_do_free(card);
463 }
464}
465EXPORT_SYMBOL(snd_card_unref);
466
449int snd_card_free_when_closed(struct snd_card *card) 467int snd_card_free_when_closed(struct snd_card *card)
450{ 468{
451 int free_now = 0; 469 int ret;
452 int ret = snd_card_disconnect(card);
453 if (ret)
454 return ret;
455 470
456 spin_lock(&card->files_lock); 471 atomic_inc(&card->refcount);
457 if (list_empty(&card->files_list)) 472 ret = snd_card_disconnect(card);
458 free_now = 1; 473 if (ret) {
459 else 474 atomic_dec(&card->refcount);
460 card->free_on_last_close = 1; 475 return ret;
461 spin_unlock(&card->files_lock); 476 }
462 477
463 if (free_now) 478 card->free_on_last_close = 1;
479 if (atomic_dec_and_test(&card->refcount))
464 snd_card_do_free(card); 480 snd_card_do_free(card);
465 return 0; 481 return 0;
466} 482}
@@ -474,7 +490,7 @@ int snd_card_free(struct snd_card *card)
474 return ret; 490 return ret;
475 491
476 /* wait, until all devices are ready for the free operation */ 492 /* wait, until all devices are ready for the free operation */
477 wait_event(card->shutdown_sleep, list_empty(&card->files_list)); 493 wait_event(card->shutdown_sleep, !atomic_read(&card->refcount));
478 snd_card_do_free(card); 494 snd_card_do_free(card);
479 return 0; 495 return 0;
480} 496}
@@ -886,6 +902,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file)
886 return -ENODEV; 902 return -ENODEV;
887 } 903 }
888 list_add(&mfile->list, &card->files_list); 904 list_add(&mfile->list, &card->files_list);
905 atomic_inc(&card->refcount);
889 spin_unlock(&card->files_lock); 906 spin_unlock(&card->files_lock);
890 return 0; 907 return 0;
891} 908}
@@ -908,7 +925,6 @@ EXPORT_SYMBOL(snd_card_file_add);
908int snd_card_file_remove(struct snd_card *card, struct file *file) 925int snd_card_file_remove(struct snd_card *card, struct file *file)
909{ 926{
910 struct snd_monitor_file *mfile, *found = NULL; 927 struct snd_monitor_file *mfile, *found = NULL;
911 int last_close = 0;
912 928
913 spin_lock(&card->files_lock); 929 spin_lock(&card->files_lock);
914 list_for_each_entry(mfile, &card->files_list, list) { 930 list_for_each_entry(mfile, &card->files_list, list) {