History

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.

Takashi Iwai committed 14 years ago

Commit a0830db1 parent 888ea7d11 files · +86 −32Browse files

Unified · Split

11 files
  1. include/sound/core.h +3 −0
  2. sound/core/compress_offload.c +7 −2
  3. sound/core/control.c +3 −0
  4. sound/core/hwdep.c +4 −1
  5. sound/core/init.c +30 −20
  6. sound/core/oss/mixer_oss.c +8 −2
  7. sound/core/oss/pcm_oss.c +2 −0
  8. sound/core/pcm_native.c +7 −2
  9. sound/core/rawmidi.c +5 −1
  10. sound/core/sound.c +9 −2
  11. sound/core/sound_oss.c +8 −2
include/sound/core.h+3−0
@@ -132,6 +132,7 @@ struct snd_card { · 131 unmodified lines
132132 int shutdown; /* this card is going down */
133133 int free_on_last_close; /* free in context of file_release */
134134 wait_queue_head_t shutdown_sleep;
135+ atomic_t refcount; /* refcount for disconnection */
135136 struct device *dev; /* device assigned to this card */
136137 struct device *card_dev; /* cardX object for sysfs */
137138
@@ -189,6 +190,7 @@ struct snd_minor { · 51 unmodified lines
189190 const struct file_operations *f_ops; /* file operations */
190191 void *private_data; /* private data for f_ops->open */
191192 struct device *dev; /* device for sysfs */
193+ struct snd_card *card_ptr; /* assigned card instance */
192194 };
193195
194196 /* return a device pointer linked to each sound device as a parent */
@@ -295,6 +297,7 @@ int snd_card_info_done(void); · 100 unmodified lines
295297 int snd_component_add(struct snd_card *card, const char *component);
296298 int snd_card_file_add(struct snd_card *card, struct file *file);
297299 int snd_card_file_remove(struct snd_card *card, struct file *file);
300+void snd_card_unref(struct snd_card *card);
298301
299302 #define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
300303
sound/core/compress_offload.c+7−2
@@ -100,12 +100,15 @@ static int snd_compr_open(struct inode *inode, struct file *f) · 99 unmodified lines
100100
101101 if (dirn != compr->direction) {
102102 pr_err("this device doesn't support this direction\n");
103+ snd_card_unref(compr->card);
103104 return -EINVAL;
104105 }
105106
106107 data = kzalloc(sizeof(*data), GFP_KERNEL);
107− if (!data)
108+ if (!data) {
109+ snd_card_unref(compr->card);
108110 return -ENOMEM;
111+ }
109112 data->stream.ops = compr->ops;
110113 data->stream.direction = dirn;
111114 data->stream.private_data = compr->private_data;
@@ -113,6 +116,7 @@ static int snd_compr_open(struct inode *inode, struct file *f) · 1 unmodified line
113116 runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
114117 if (!runtime) {
115118 kfree(data);
119+ snd_card_unref(compr->card);
116120 return -ENOMEM;
117121 }
118122 runtime->state = SNDRV_PCM_STATE_OPEN;
@@ -126,7 +130,8 @@ static int snd_compr_open(struct inode *inode, struct file *f) · 7 unmodified lines
126130 kfree(runtime);
127131 kfree(data);
128132 }
129− return ret;
133+ snd_card_unref(compr->card);
134+ return 0;
130135 }
131136
132137 static int snd_compr_free(struct inode *inode, struct file *f)
sound/core/control.c+3−0
@@ -86,6 +86,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file) · 85 unmodified lines
8686 write_lock_irqsave(&card->ctl_files_rwlock, flags);
8787 list_add_tail(&ctl->list, &card->ctl_files);
8888 write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
89+ snd_card_unref(card);
8990 return 0;
9091
9192 __error:
@@ -93,6 +94,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file) · 1 unmodified line
9394 __error2:
9495 snd_card_file_remove(card, file);
9596 __error1:
97+ if (card)
98+ snd_card_unref(card);
9699 return err;
97100 }
98101
sound/core/hwdep.c+4−1
@@ -100,8 +100,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file) · 99 unmodified lines
100100 if (hw == NULL)
101101 return -ENODEV;
102102
103− if (!try_module_get(hw->card->module))
103+ if (!try_module_get(hw->card->module)) {
104+ snd_card_unref(hw->card);
104105 return -EFAULT;
106+ }
105107
106108 init_waitqueue_entry(&wait, current);
107109 add_wait_queue(&hw->open_wait, &wait);
@@ -148,6 +150,7 @@ static int snd_hwdep_open(struct inode *inode, struct file * file) · 40 unmodified lines
148150 mutex_unlock(&hw->open_mutex);
149151 if (err < 0)
150152 module_put(hw->card->module);
153+ snd_card_unref(hw->card);
151154 return err;
152155 }
153156
sound/core/init.c+30−20
@@ -213,6 +213,7 @@ int snd_card_create(int idx, const char *xid, · 212 unmodified lines
213213 spin_lock_init(&card->files_lock);
214214 INIT_LIST_HEAD(&card->files_list);
215215 init_waitqueue_head(&card->shutdown_sleep);
216+ atomic_set(&card->refcount, 0);
216217 #ifdef CONFIG_PM
217218 mutex_init(&card->power_lock);
218219 init_waitqueue_head(&card->power_sleep);
@@ -446,21 +447,36 @@ static int snd_card_do_free(struct snd_card *card) · 227 unmodified lines
446447 return 0;
447448 }
448449
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+ */
457+void 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+}
465+EXPORT_SYMBOL(snd_card_unref);
466+
449467 int snd_card_free_when_closed(struct snd_card *card)
450468 {
451− int free_now = 0;
452− int ret = snd_card_disconnect(card);
453− if (ret)
454− return ret;
469+ int ret;
455470
456− spin_lock(&card->files_lock);
457− if (list_empty(&card->files_list))
458− free_now = 1;
459− else
460− card->free_on_last_close = 1;
461− spin_unlock(&card->files_lock);
471+ atomic_inc(&card->refcount);
472+ ret = snd_card_disconnect(card);
473+ if (ret) {
474+ atomic_dec(&card->refcount);
475+ return ret;
476+ }
462477
463− if (free_now)
478+ card->free_on_last_close = 1;
479+ if (atomic_dec_and_test(&card->refcount))
464480 snd_card_do_free(card);
465481 return 0;
466482 }
@@ -474,7 +490,7 @@ int snd_card_free(struct snd_card *card) · 7 unmodified lines
474490 return ret;
475491
476492 /* 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));
478494 snd_card_do_free(card);
479495 return 0;
480496 }
@@ -886,6 +902,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file) · 405 unmodified lines
886902 return -ENODEV;
887903 }
888904 list_add(&mfile->list, &card->files_list);
905+ atomic_inc(&card->refcount);
889906 spin_unlock(&card->files_lock);
890907 return 0;
891908 }
@@ -908,7 +925,6 @@ EXPORT_SYMBOL(snd_card_file_add); · 16 unmodified lines
908925 int snd_card_file_remove(struct snd_card *card, struct file *file)
909926 {
910927 struct snd_monitor_file *mfile, *found = NULL;
911− int last_close = 0;
912928
913929 spin_lock(&card->files_lock);
914930 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) · 8 unmodified lines
923939 break;
924940 }
925941 }
926− if (list_empty(&card->files_list))
927− last_close = 1;
928942 spin_unlock(&card->files_lock);
929− if (last_close) {
930− wake_up(&card->shutdown_sleep);
931− if (card->free_on_last_close)
932− snd_card_do_free(card);
933− }
934943 if (!found) {
935944 snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
936945 return -ENOENT;
937946 }
938947 kfree(found);
948+ snd_card_unref(card);
939949 return 0;
940950 }
941951
sound/core/oss/mixer_oss.c+8−2
@@ -52,14 +52,19 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file) · 51 unmodified lines
5252 SNDRV_OSS_DEVICE_TYPE_MIXER);
5353 if (card == NULL)
5454 return -ENODEV;
55− if (card->mixer_oss == NULL)
55+ if (card->mixer_oss == NULL) {
56+ snd_card_unref(card);
5657 return -ENODEV;
58+ }
5759 err = snd_card_file_add(card, file);
58− if (err < 0)
60+ if (err < 0) {
61+ snd_card_unref(card);
5962 return err;
63+ }
6064 fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL);
6165 if (fmixer == NULL) {
6266 snd_card_file_remove(card, file);
67+ snd_card_unref(card);
6368 return -ENOMEM;
6469 }
6570 fmixer->card = card;
@@ -68,6 +73,7 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file) · 2 unmodified lines
6873 if (!try_module_get(card->module)) {
6974 kfree(fmixer);
7075 snd_card_file_remove(card, file);
76+ snd_card_unref(card);
7177 return -EFAULT;
7278 }
7379 return 0;
sound/core/oss/pcm_oss.c+2−0
@@ -2457,6 +2457,8 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file) · 2456 unmodified lines
24572457 __error2:
24582458 snd_card_file_remove(pcm->card, file);
24592459 __error1:
2460+ if (pcm)
2461+ snd_card_unref(pcm->card);
24602462 return err;
24612463 }
24622464
sound/core/pcm_native.c+7−2
@@ -1642,6 +1642,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) · 1641 unmodified lines
16421642 write_unlock_irq(&snd_pcm_link_rwlock);
16431643 up_write(&snd_pcm_link_rwsem);
16441644 _nolock:
1645+ snd_card_unref(substream1->pcm->card);
16451646 fput_light(file, fput_needed);
16461647 if (res < 0)
16471648 kfree(group);
@@ -2116,7 +2117,9 @@ static int snd_pcm_playback_open(struct inode *inode, struct file *file) · 468 unmodified lines
21162117 return err;
21172118 pcm = snd_lookup_minor_data(iminor(inode),
21182119 SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
2119− return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
2120+ err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
2121+ snd_card_unref(pcm->card);
2122+ return err;
21202123 }
21212124
21222125 static int snd_pcm_capture_open(struct inode *inode, struct file *file)
@@ -2127,7 +2130,9 @@ static int snd_pcm_capture_open(struct inode *inode, struct file *file) · 4 unmodified lines
21272130 return err;
21282131 pcm = snd_lookup_minor_data(iminor(inode),
21292132 SNDRV_DEVICE_TYPE_PCM_CAPTURE);
2130− return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
2133+ err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
2134+ snd_card_unref(pcm->card);
2135+ return err;
21312136 }
21322137
21332138 static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
sound/core/rawmidi.c+5−1
@@ -379,8 +379,10 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) · 378 unmodified lines
379379 if (rmidi == NULL)
380380 return -ENODEV;
381381
382− if (!try_module_get(rmidi->card->module))
382+ if (!try_module_get(rmidi->card->module)) {
383+ snd_card_unref(rmidi->card);
383384 return -ENXIO;
385+ }
384386
385387 mutex_lock(&rmidi->open_mutex);
386388 card = rmidi->card;
@@ -440,6 +442,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) · 53 unmodified lines
440442 #endif
441443 file->private_data = rawmidi_file;
442444 mutex_unlock(&rmidi->open_mutex);
445+ snd_card_unref(rmidi->card);
443446 return 0;
444447
445448 __error:
@@ -447,6 +450,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) · 1 unmodified line
447450 __error_card:
448451 mutex_unlock(&rmidi->open_mutex);
449452 module_put(rmidi->card->module);
453+ snd_card_unref(rmidi->card);
450454 return err;
451455 }
452456
sound/core/sound.c+9−2
@@ -98,6 +98,10 @@ static void snd_request_other(int minor) · 97 unmodified lines
9898 *
9999 * Checks that a minor device with the specified type is registered, and returns
100100 * its user data pointer.
101+ *
102+ * This function increments the reference counter of the card instance
103+ * if an associated instance with the given minor number and type is found.
104+ * The caller must call snd_card_unref() appropriately later.
101105 */
102106 void *snd_lookup_minor_data(unsigned int minor, int type)
103107 {
@@ -108,9 +112,11 @@ void *snd_lookup_minor_data(unsigned int minor, int type) · 4 unmodified lines
108112 return NULL;
109113 mutex_lock(&sound_mutex);
110114 mreg = snd_minors[minor];
111− if (mreg && mreg->type == type)
115+ if (mreg && mreg->type == type) {
112116 private_data = mreg->private_data;
113− else
117+ if (mreg->card_ptr)
118+ atomic_inc(&mreg->card_ptr->refcount);
119+ } else
114120 private_data = NULL;
115121 mutex_unlock(&sound_mutex);
116122 return private_data;
@@ -275,6 +281,7 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev, · 158 unmodified lines
275281 preg->device = dev;
276282 preg->f_ops = f_ops;
277283 preg->private_data = private_data;
284+ preg->card_ptr = card;
278285 mutex_lock(&sound_mutex);
279286 #ifdef CONFIG_SND_DYNAMIC_MINORS
280287 minor = snd_find_free_minor(type);
sound/core/sound_oss.c+8−2
@@ -40,6 +40,9 @@ · 39 unmodified lines
4040 static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];
4141 static DEFINE_MUTEX(sound_oss_mutex);
4242
43+/* NOTE: This function increments the refcount of the associated card like
44+ * snd_lookup_minor_data(); the caller must call snd_card_unref() appropriately
45+ */
4346 void *snd_lookup_oss_minor_data(unsigned int minor, int type)
4447 {
4548 struct snd_minor *mreg;
@@ -49,9 +52,11 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type) · 3 unmodified lines
4952 return NULL;
5053 mutex_lock(&sound_oss_mutex);
5154 mreg = snd_oss_minors[minor];
52− if (mreg && mreg->type == type)
55+ if (mreg && mreg->type == type) {
5356 private_data = mreg->private_data;
54− else
57+ if (mreg->card_ptr)
58+ atomic_inc(&mreg->card_ptr->refcount);
59+ } else
5560 private_data = NULL;
5661 mutex_unlock(&sound_oss_mutex);
5762 return private_data;
@@ -123,6 +128,7 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev, · 65 unmodified lines
123128 preg->device = dev;
124129 preg->f_ops = f_ops;
125130 preg->private_data = private_data;
131+ preg->card_ptr = card;
126132 mutex_lock(&sound_oss_mutex);
127133 snd_oss_minors[minor] = preg;
128134 minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);