summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorKarsten Wiese <fzu@wemgehoertderstaat.de>2006-10-06 16:08:27 +0200
committerJaroslav Kysela <perex@suse.cz>2006-10-06 20:23:04 +0200
commita9edfc60227a1dc5c741666ff252a6055b73b184 (patch)
tree15993e126624ec9e05f33f957ee4f69bd4787220 /sound
parent4130d59b1ac6e32c130bd59dbce5eb30fede0197 (diff)
downloadlinux-a9edfc60227a1dc5c741666ff252a6055b73b184.tar.gz
linux-a9edfc60227a1dc5c741666ff252a6055b73b184.tar.bz2
linux-a9edfc60227a1dc5c741666ff252a6055b73b184.zip
[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 @@
#include <sound/control.h>
#include <sound/info.h>
-struct snd_shutdown_f_ops {
- struct file_operations f_ops;
- struct snd_shutdown_f_ops *next;
-};
+static DEFINE_SPINLOCK(shutdown_lock);
+static LIST_HEAD(shutdown_files);
+
+static struct file_operations snd_shutdown_f_ops;
static unsigned int snd_cards_lock; /* locked for registering/using */
struct snd_card *snd_cards[SNDRV_CARDS];
@@ -198,6 +198,25 @@ static ssize_t snd_disconnect_write(struct file *file, const char __user *buf,
return -ENODEV;
}
+static int snd_disconnect_release(struct inode *inode, struct file *file)
+{
+ struct snd_monitor_file *df = NULL, *_df;
+
+ spin_lock(&shutdown_lock);
+ list_for_each_entry(_df, &shutdown_files, shutdown_list) {
+ if (_df->file == file) {
+ df = _df;
+ break;
+ }
+ }
+ spin_unlock(&shutdown_lock);
+
+ if (likely(df))
+ return df->disconnected_f_op->release(inode, file);
+
+ panic("%s(%p, %p) failed!", __FUNCTION__, inode, file);
+}
+
static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait)
{
return POLLERR | POLLNVAL;
@@ -219,6 +238,22 @@ static int snd_disconnect_fasync(int fd, struct file *file, int on)
return -ENODEV;
}
+static struct file_operations snd_shutdown_f_ops =
+{
+ .owner = THIS_MODULE,
+ .llseek = snd_disconnect_llseek,
+ .read = snd_disconnect_read,
+ .write = snd_disconnect_write,
+ .release = snd_disconnect_release,
+ .poll = snd_disconnect_poll,
+ .unlocked_ioctl = snd_disconnect_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = snd_disconnect_ioctl,
+#endif
+ .mmap = snd_disconnect_mmap,
+ .fasync = snd_disconnect_fasync
+};
+
/**
* snd_card_disconnect - disconnect all APIs from the file-operations (user space)
* @card: soundcard structure
@@ -234,9 +269,6 @@ int snd_card_disconnect(struct snd_card *card)
{
struct snd_monitor_file *mfile;
struct file *file;
- struct snd_shutdown_f_ops *s_f_ops;
- struct file_operations *f_ops;
- const struct file_operations *old_f_ops;
int err;
spin_lock(&card->files_lock);
@@ -261,34 +293,14 @@ int snd_card_disconnect(struct snd_card *card)
/* it's critical part, use endless loop */
/* we have no room to fail */
- s_f_ops = kmalloc(sizeof(struct snd_shutdown_f_ops), GFP_ATOMIC);
- if (s_f_ops == NULL)
- panic("Atomic allocation failed for snd_shutdown_f_ops!");
-
- f_ops = &s_f_ops->f_ops;
-
- memset(f_ops, 0, sizeof(*f_ops));
- f_ops->owner = file->f_op->owner;
- f_ops->release = file->f_op->release;
- f_ops->llseek = snd_disconnect_llseek;
- f_ops->read = snd_disconnect_read;
- f_ops->write = snd_disconnect_write;
- f_ops->poll = snd_disconnect_poll;
- f_ops->unlocked_ioctl = snd_disconnect_ioctl;
-#ifdef CONFIG_COMPAT
- f_ops->compat_ioctl = snd_disconnect_ioctl;
-#endif
- f_ops->mmap = snd_disconnect_mmap;
- f_ops->fasync = snd_disconnect_fasync;
+ mfile->disconnected_f_op = mfile->file->f_op;
- s_f_ops->next = card->s_f_ops;
- card->s_f_ops = s_f_ops;
-
- f_ops = fops_get(f_ops);
+ spin_lock(&shutdown_lock);
+ list_add(&mfile->shutdown_list, &shutdown_files);
+ spin_unlock(&shutdown_lock);
- old_f_ops = file->f_op;
- file->f_op = f_ops; /* must be atomic */
- fops_put(old_f_ops);
+ fops_get(&snd_shutdown_f_ops);
+ mfile->file->f_op = &snd_shutdown_f_ops;
mfile = mfile->next;
}
@@ -326,8 +338,6 @@ EXPORT_SYMBOL(snd_card_disconnect);
*/
static int snd_card_do_free(struct snd_card *card)
{
- struct snd_shutdown_f_ops *s_f_ops;
-
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
if (snd_mixer_oss_notify_callback)
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)
snd_printk(KERN_WARNING "unable to free card info\n");
/* Not fatal error */
}
- while (card->s_f_ops) {
- s_f_ops = card->s_f_ops;
- card->s_f_ops = s_f_ops->next;
- kfree(s_f_ops);
- }
kfree(card);
return 0;
}
@@ -670,6 +675,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file)
if (mfile == NULL)
return -ENOMEM;
mfile->file = file;
+ mfile->disconnected_f_op = NULL;
mfile->next = NULL;
spin_lock(&card->files_lock);
if (card->shutdown) {
@@ -716,6 +722,12 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
pfile = mfile;
mfile = mfile->next;
}
+ if (mfile && mfile->disconnected_f_op) {
+ fops_put(mfile->disconnected_f_op);
+ spin_lock(&shutdown_lock);
+ list_del(&mfile->shutdown_list);
+ spin_unlock(&shutdown_lock);
+ }
if (card->files == NULL)
last_close = 1;
spin_unlock(&card->files_lock);