aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorKarsten Wiese <fzu@wemgehoertderstaat.de>2006-10-06 10:08:27 -0400
committerJaroslav Kysela <perex@suse.cz>2006-10-06 14:23:04 -0400
commita9edfc60227a1dc5c741666ff252a6055b73b184 (patch)
tree15993e126624ec9e05f33f957ee4f69bd4787220 /sound
parent4130d59b1ac6e32c130bd59dbce5eb30fede0197 (diff)
[ALSA] Handle file operations during snd_card disconnects using static file->f_op
Alsa used to kmalloc one file->f_op per file per disconnecting snd_card. This led to oopses sometimes when file->f_op was freed before __fput() finished. Patch adds a virtual device for disconnect: VDD. VDD consists of: LIST_HEAD(shutdown_files) protected by DEFINE_SPINLOCK(shutdown_mutex) static struct file_operations snd_shutdown_f_ops and functions assigned to it Additions to struct snd_monitor_file to specify if instance is hidden by VDD or not. A VDD's instance is created in snd_card_disconnect() under the card->files_lock. cleaned up in snd_card_file_remove() under the card->files_lock. Signed-off-by: Karsten Wiese <fzu@wemgehoertderstaat.de> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound')
-rw-r--r--sound/core/init.c92
1 files changed, 52 insertions, 40 deletions
diff --git a/sound/core/init.c b/sound/core/init.c
index d7607a25acdf..3058d626a90a 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -33,10 +33,10 @@
33#include <sound/control.h> 33#include <sound/control.h>
34#include <sound/info.h> 34#include <sound/info.h>
35 35
36struct snd_shutdown_f_ops { 36static DEFINE_SPINLOCK(shutdown_lock);
37 struct file_operations f_ops; 37static LIST_HEAD(shutdown_files);
38 struct snd_shutdown_f_ops *next; 38
39}; 39static struct file_operations snd_shutdown_f_ops;
40 40
41static unsigned int snd_cards_lock; /* locked for registering/using */ 41static unsigned int snd_cards_lock; /* locked for registering/using */
42struct snd_card *snd_cards[SNDRV_CARDS]; 42struct snd_card *snd_cards[SNDRV_CARDS];
@@ -198,6 +198,25 @@ static ssize_t snd_disconnect_write(struct file *file, const char __user *buf,
198 return -ENODEV; 198 return -ENODEV;
199} 199}
200 200
201static int snd_disconnect_release(struct inode *inode, struct file *file)
202{
203 struct snd_monitor_file *df = NULL, *_df;
204
205 spin_lock(&shutdown_lock);
206 list_for_each_entry(_df, &shutdown_files, shutdown_list) {
207 if (_df->file == file) {
208 df = _df;
209 break;
210 }
211 }
212 spin_unlock(&shutdown_lock);
213
214 if (likely(df))
215 return df->disconnected_f_op->release(inode, file);
216
217 panic("%s(%p, %p) failed!", __FUNCTION__, inode, file);
218}
219
201static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait) 220static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait)
202{ 221{
203 return POLLERR | POLLNVAL; 222 return POLLERR | POLLNVAL;
@@ -219,6 +238,22 @@ static int snd_disconnect_fasync(int fd, struct file *file, int on)
219 return -ENODEV; 238 return -ENODEV;
220} 239}
221 240
241static struct file_operations snd_shutdown_f_ops =
242{
243 .owner = THIS_MODULE,
244 .llseek = snd_disconnect_llseek,
245 .read = snd_disconnect_read,
246 .write = snd_disconnect_write,
247 .release = snd_disconnect_release,
248 .poll = snd_disconnect_poll,
249 .unlocked_ioctl = snd_disconnect_ioctl,
250#ifdef CONFIG_COMPAT
251 .compat_ioctl = snd_disconnect_ioctl,
252#endif
253 .mmap = snd_disconnect_mmap,
254 .fasync = snd_disconnect_fasync
255};
256
222/** 257/**
223 * snd_card_disconnect - disconnect all APIs from the file-operations (user space) 258 * snd_card_disconnect - disconnect all APIs from the file-operations (user space)
224 * @card: soundcard structure 259 * @card: soundcard structure
@@ -234,9 +269,6 @@ int snd_card_disconnect(struct snd_card *card)
234{ 269{
235 struct snd_monitor_file *mfile; 270 struct snd_monitor_file *mfile;
236 struct file *file; 271 struct file *file;
237 struct snd_shutdown_f_ops *s_f_ops;
238 struct file_operations *f_ops;
239 const struct file_operations *old_f_ops;
240 int err; 272 int err;
241 273
242 spin_lock(&card->files_lock); 274 spin_lock(&card->files_lock);
@@ -261,34 +293,14 @@ int snd_card_disconnect(struct snd_card *card)
261 293
262 /* it's critical part, use endless loop */ 294 /* it's critical part, use endless loop */
263 /* we have no room to fail */ 295 /* we have no room to fail */
264 s_f_ops = kmalloc(sizeof(struct snd_shutdown_f_ops), GFP_ATOMIC); 296 mfile->disconnected_f_op = mfile->file->f_op;
265 if (s_f_ops == NULL)
266 panic("Atomic allocation failed for snd_shutdown_f_ops!");
267
268 f_ops = &s_f_ops->f_ops;
269
270 memset(f_ops, 0, sizeof(*f_ops));
271 f_ops->owner = file->f_op->owner;
272 f_ops->release = file->f_op->release;
273 f_ops->llseek = snd_disconnect_llseek;
274 f_ops->read = snd_disconnect_read;
275 f_ops->write = snd_disconnect_write;
276 f_ops->poll = snd_disconnect_poll;
277 f_ops->unlocked_ioctl = snd_disconnect_ioctl;
278#ifdef CONFIG_COMPAT
279 f_ops->compat_ioctl = snd_disconnect_ioctl;
280#endif
281 f_ops->mmap = snd_disconnect_mmap;
282 f_ops->fasync = snd_disconnect_fasync;
283 297
284 s_f_ops->next = card->s_f_ops; 298 spin_lock(&shutdown_lock);
285 card->s_f_ops = s_f_ops; 299 list_add(&mfile->shutdown_list, &shutdown_files);
286 300 spin_unlock(&shutdown_lock);
287 f_ops = fops_get(f_ops);
288 301
289 old_f_ops = file->f_op; 302 fops_get(&snd_shutdown_f_ops);
290 file->f_op = f_ops; /* must be atomic */ 303 mfile->file->f_op = &snd_shutdown_f_ops;
291 fops_put(old_f_ops);
292 304
293 mfile = mfile->next; 305 mfile = mfile->next;
294 } 306 }
@@ -326,8 +338,6 @@ EXPORT_SYMBOL(snd_card_disconnect);
326 */ 338 */
327static int snd_card_do_free(struct snd_card *card) 339static int snd_card_do_free(struct snd_card *card)
328{ 340{
329 struct snd_shutdown_f_ops *s_f_ops;
330
331#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) 341#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
332 if (snd_mixer_oss_notify_callback) 342 if (snd_mixer_oss_notify_callback)
333 snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); 343 snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
@@ -351,11 +361,6 @@ static int snd_card_do_free(struct snd_card *card)
351 snd_printk(KERN_WARNING "unable to free card info\n"); 361 snd_printk(KERN_WARNING "unable to free card info\n");
352 /* Not fatal error */ 362 /* Not fatal error */
353 } 363 }
354 while (card->s_f_ops) {
355 s_f_ops = card->s_f_ops;
356 card->s_f_ops = s_f_ops->next;
357 kfree(s_f_ops);
358 }
359 kfree(card); 364 kfree(card);
360 return 0; 365 return 0;
361} 366}
@@ -670,6 +675,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file)
670 if (mfile == NULL) 675 if (mfile == NULL)
671 return -ENOMEM; 676 return -ENOMEM;
672 mfile->file = file; 677 mfile->file = file;
678 mfile->disconnected_f_op = NULL;
673 mfile->next = NULL; 679 mfile->next = NULL;
674 spin_lock(&card->files_lock); 680 spin_lock(&card->files_lock);
675 if (card->shutdown) { 681 if (card->shutdown) {
@@ -716,6 +722,12 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
716 pfile = mfile; 722 pfile = mfile;
717 mfile = mfile->next; 723 mfile = mfile->next;
718 } 724 }
725 if (mfile && mfile->disconnected_f_op) {
726 fops_put(mfile->disconnected_f_op);
727 spin_lock(&shutdown_lock);
728 list_del(&mfile->shutdown_list);
729 spin_unlock(&shutdown_lock);
730 }
719 if (card->files == NULL) 731 if (card->files == NULL)
720 last_close = 1; 732 last_close = 1;
721 spin_unlock(&card->files_lock); 733 spin_unlock(&card->files_lock);