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
132 int shutdown; /* this card is going down */132 int shutdown; /* this card is going down */
133 int free_on_last_close; /* free in context of file_release */133 int free_on_last_close; /* free in context of file_release */
134 wait_queue_head_t shutdown_sleep;134 wait_queue_head_t shutdown_sleep;
135 atomic_t refcount; /* refcount for disconnection */
135 struct device *dev; /* device assigned to this card */136 struct device *dev; /* device assigned to this card */
136 struct device *card_dev; /* cardX object for sysfs */137 struct device *card_dev; /* cardX object for sysfs */
137 138
@@ -189,6 +190,7 @@ struct snd_minor { · 51 unmodified lines
189 const struct file_operations *f_ops; /* file operations */190 const struct file_operations *f_ops; /* file operations */
190 void *private_data; /* private data for f_ops->open */191 void *private_data; /* private data for f_ops->open */
191 struct device *dev; /* device for sysfs */192 struct device *dev; /* device for sysfs */
193 struct snd_card *card_ptr; /* assigned card instance */
192};194};
193 195
194/* return a device pointer linked to each sound device as a parent */196/* 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
295int snd_component_add(struct snd_card *card, const char *component);297int snd_component_add(struct snd_card *card, const char *component);
296int snd_card_file_add(struct snd_card *card, struct file *file);298int snd_card_file_add(struct snd_card *card, struct file *file);
297int snd_card_file_remove(struct snd_card *card, struct file *file);299int snd_card_file_remove(struct snd_card *card, struct file *file);
300void snd_card_unref(struct snd_card *card);
298 301
299#define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))302#define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
300 303
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
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) · 1 unmodified line
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) · 7 unmodified lines
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)
sound/core/control.c+3−0
@@ -86,6 +86,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file) · 85 unmodified lines
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) · 1 unmodified line
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
sound/core/hwdep.c+4−1
@@ -100,8 +100,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file) · 99 unmodified lines
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) · 40 unmodified lines
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
sound/core/init.c+30−20
@@ -213,6 +213,7 @@ int snd_card_create(int idx, const char *xid, · 212 unmodified lines
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_PM217#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) · 227 unmodified lines
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 else474 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) · 7 unmodified lines
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) · 405 unmodified lines
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); · 16 unmodified lines
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) {
@@ -923,19 +939,13 @@ int snd_card_file_remove(struct snd_card *card, struct file *file) · 8 unmodified lines
923 break;939 break;
924 }940 }
925 }941 }
926 if (list_empty(&card->files_list))
927 last_close = 1;
928 spin_unlock(&card->files_lock);942 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 }
934 if (!found) {943 if (!found) {
935 snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);944 snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
936 return -ENOENT;945 return -ENOENT;
937 }946 }
938 kfree(found);947 kfree(found);
948 snd_card_unref(card);
939 return 0;949 return 0;
940}950}
941 951
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
52 SNDRV_OSS_DEVICE_TYPE_MIXER);52 SNDRV_OSS_DEVICE_TYPE_MIXER);
53 if (card == NULL)53 if (card == NULL)
54 return -ENODEV;54 return -ENODEV;
55 if (card->mixer_oss == NULL)55 if (card->mixer_oss == NULL) {
56 snd_card_unref(card);
56 return -ENODEV;57 return -ENODEV;
58 }
57 err = snd_card_file_add(card, file);59 err = snd_card_file_add(card, file);
58 if (err < 0)60 if (err < 0) {
61 snd_card_unref(card);
59 return err;62 return err;
63 }
60 fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL);64 fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL);
61 if (fmixer == NULL) {65 if (fmixer == NULL) {
62 snd_card_file_remove(card, file);66 snd_card_file_remove(card, file);
67 snd_card_unref(card);
63 return -ENOMEM;68 return -ENOMEM;
64 }69 }
65 fmixer->card = card;70 fmixer->card = card;
@@ -68,6 +73,7 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file) · 2 unmodified lines
68 if (!try_module_get(card->module)) {73 if (!try_module_get(card->module)) {
69 kfree(fmixer);74 kfree(fmixer);
70 snd_card_file_remove(card, file);75 snd_card_file_remove(card, file);
76 snd_card_unref(card);
71 return -EFAULT;77 return -EFAULT;
72 }78 }
73 return 0;79 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
2457 __error2:2457 __error2:
2458 snd_card_file_remove(pcm->card, file);2458 snd_card_file_remove(pcm->card, file);
2459 __error1:2459 __error1:
2460 if (pcm)
2461 snd_card_unref(pcm->card);
2460 return err;2462 return err;
2461}2463}
2462 2464
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
1642 write_unlock_irq(&snd_pcm_link_rwlock);1642 write_unlock_irq(&snd_pcm_link_rwlock);
1643 up_write(&snd_pcm_link_rwsem);1643 up_write(&snd_pcm_link_rwsem);
1644 _nolock:1644 _nolock:
1645 snd_card_unref(substream1->pcm->card);
1645 fput_light(file, fput_needed);1646 fput_light(file, fput_needed);
1646 if (res < 0)1647 if (res < 0)
1647 kfree(group);1648 kfree(group);
@@ -2116,7 +2117,9 @@ static int snd_pcm_playback_open(struct inode *inode, struct file *file) · 468 unmodified lines
2116 return err;2117 return err;
2117 pcm = snd_lookup_minor_data(iminor(inode),2118 pcm = snd_lookup_minor_data(iminor(inode),
2118 SNDRV_DEVICE_TYPE_PCM_PLAYBACK);2119 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;
2120}2123}
2121 2124
2122static int snd_pcm_capture_open(struct inode *inode, struct file *file)2125static 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
2127 return err;2130 return err;
2128 pcm = snd_lookup_minor_data(iminor(inode),2131 pcm = snd_lookup_minor_data(iminor(inode),
2129 SNDRV_DEVICE_TYPE_PCM_CAPTURE);2132 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;
2131}2136}
2132 2137
2133static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)2138static 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
379 if (rmidi == NULL)379 if (rmidi == NULL)
380 return -ENODEV;380 return -ENODEV;
381 381
382 if (!try_module_get(rmidi->card->module))382 if (!try_module_get(rmidi->card->module)) {
383 snd_card_unref(rmidi->card);
383 return -ENXIO;384 return -ENXIO;
385 }
384 386
385 mutex_lock(&rmidi->open_mutex);387 mutex_lock(&rmidi->open_mutex);
386 card = rmidi->card;388 card = rmidi->card;
@@ -440,6 +442,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) · 53 unmodified lines
440#endif442#endif
441 file->private_data = rawmidi_file;443 file->private_data = rawmidi_file;
442 mutex_unlock(&rmidi->open_mutex);444 mutex_unlock(&rmidi->open_mutex);
445 snd_card_unref(rmidi->card);
443 return 0;446 return 0;
444 447
445 __error:448 __error:
@@ -447,6 +450,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) · 1 unmodified line
447 __error_card:450 __error_card:
448 mutex_unlock(&rmidi->open_mutex);451 mutex_unlock(&rmidi->open_mutex);
449 module_put(rmidi->card->module);452 module_put(rmidi->card->module);
453 snd_card_unref(rmidi->card);
450 return err;454 return err;
451}455}
452 456
sound/core/sound.c+9−2
@@ -98,6 +98,10 @@ static void snd_request_other(int minor) · 97 unmodified lines
98 *98 *
99 * Checks that a minor device with the specified type is registered, and returns99 * Checks that a minor device with the specified type is registered, and returns
100 * its user data pointer.100 * 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.
101 */105 */
102void *snd_lookup_minor_data(unsigned int minor, int type)106void *snd_lookup_minor_data(unsigned int minor, int type)
103{107{
@@ -108,9 +112,11 @@ void *snd_lookup_minor_data(unsigned int minor, int type) · 4 unmodified lines
108 return NULL;112 return NULL;
109 mutex_lock(&sound_mutex);113 mutex_lock(&sound_mutex);
110 mreg = snd_minors[minor];114 mreg = snd_minors[minor];
111 if (mreg && mreg->type == type)115 if (mreg && mreg->type == type) {
112 private_data = mreg->private_data;116 private_data = mreg->private_data;
113 else117 if (mreg->card_ptr)
118 atomic_inc(&mreg->card_ptr->refcount);
119 } else
114 private_data = NULL;120 private_data = NULL;
115 mutex_unlock(&sound_mutex);121 mutex_unlock(&sound_mutex);
116 return private_data;122 return private_data;
@@ -275,6 +281,7 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev, · 158 unmodified lines
275 preg->device = dev;281 preg->device = dev;
276 preg->f_ops = f_ops;282 preg->f_ops = f_ops;
277 preg->private_data = private_data;283 preg->private_data = private_data;
284 preg->card_ptr = card;
278 mutex_lock(&sound_mutex);285 mutex_lock(&sound_mutex);
279#ifdef CONFIG_SND_DYNAMIC_MINORS286#ifdef CONFIG_SND_DYNAMIC_MINORS
280 minor = snd_find_free_minor(type);287 minor = snd_find_free_minor(type);
sound/core/sound_oss.c+8−2
@@ -40,6 +40,9 @@ · 39 unmodified lines
40static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];40static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];
41static DEFINE_MUTEX(sound_oss_mutex);41static DEFINE_MUTEX(sound_oss_mutex);
42 42
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 */
43void *snd_lookup_oss_minor_data(unsigned int minor, int type)46void *snd_lookup_oss_minor_data(unsigned int minor, int type)
44{47{
45 struct snd_minor *mreg;48 struct snd_minor *mreg;
@@ -49,9 +52,11 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type) · 3 unmodified lines
49 return NULL;52 return NULL;
50 mutex_lock(&sound_oss_mutex);53 mutex_lock(&sound_oss_mutex);
51 mreg = snd_oss_minors[minor];54 mreg = snd_oss_minors[minor];
52 if (mreg && mreg->type == type)55 if (mreg && mreg->type == type) {
53 private_data = mreg->private_data;56 private_data = mreg->private_data;
54 else57 if (mreg->card_ptr)
58 atomic_inc(&mreg->card_ptr->refcount);
59 } else
55 private_data = NULL;60 private_data = NULL;
56 mutex_unlock(&sound_oss_mutex);61 mutex_unlock(&sound_oss_mutex);
57 return private_data;62 return private_data;
@@ -123,6 +128,7 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev, · 65 unmodified lines
123 preg->device = dev;128 preg->device = dev;
124 preg->f_ops = f_ops;129 preg->f_ops = f_ops;
125 preg->private_data = private_data;130 preg->private_data = private_data;
131 preg->card_ptr = card;
126 mutex_lock(&sound_oss_mutex);132 mutex_lock(&sound_oss_mutex);
127 snd_oss_minors[minor] = preg;133 snd_oss_minors[minor] = preg;
128 minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);134 minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);