Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / sound / core / seq / seq_clientmgr.c
index d449dde..08d5003 100644 (file)
@@ -236,6 +236,7 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
        rwlock_init(&client->ports_lock);
        mutex_init(&client->ports_mutex);
        INIT_LIST_HEAD(&client->ports_list_head);
+       mutex_init(&client->ioctl_mutex);
 
        /* find free slot in the client table */
        spin_lock_irqsave(&clients_lock, flags);
@@ -269,12 +270,12 @@ static int seq_free_client1(struct snd_seq_client *client)
 
        if (!client)
                return 0;
-       snd_seq_delete_all_ports(client);
-       snd_seq_queue_client_leave(client->number);
        spin_lock_irqsave(&clients_lock, flags);
        clienttablock[client->number] = 1;
        clienttab[client->number] = NULL;
        spin_unlock_irqrestore(&clients_lock, flags);
+       snd_seq_delete_all_ports(client);
+       snd_seq_queue_client_leave(client->number);
        snd_use_lock_sync(&client->use_lock);
        snd_seq_queue_client_termination(client->number);
        if (client->pool)
@@ -676,7 +677,7 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
        if (atomic)
                read_lock(&grp->list_lock);
        else
-               down_read(&grp->list_mutex);
+               down_read_nested(&grp->list_mutex, hop);
        list_for_each_entry(subs, &grp->list_head, src_list) {
                /* both ports ready? */
                if (atomic_read(&subs->ref_count) != 2)
@@ -906,7 +907,8 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
 static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
                                        struct snd_seq_event *event,
                                        struct file *file, int blocking,
-                                       int atomic, int hop)
+                                       int atomic, int hop,
+                                       struct mutex *mutexp)
 {
        struct snd_seq_event_cell *cell;
        int err;
@@ -944,7 +946,8 @@ static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
                return -ENXIO; /* queue is not allocated */
 
        /* allocate an event cell */
-       err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic, file);
+       err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic,
+                               file, mutexp);
        if (err < 0)
                return err;
 
@@ -999,7 +1002,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
 {
        struct snd_seq_client *client = file->private_data;
        int written = 0, len;
-       int err = -EINVAL;
+       int err;
        struct snd_seq_event event;
 
        if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
@@ -1013,12 +1016,15 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
                return -ENXIO;
 
        /* allocate the pool now if the pool is not allocated yet */ 
+       mutex_lock(&client->ioctl_mutex);
        if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) {
-               if (snd_seq_pool_init(client->pool) < 0)
-                       return -ENOMEM;
+               err = snd_seq_pool_init(client->pool);
+               if (err < 0)
+                       goto out;
        }
 
        /* only process whole events */
+       err = -EINVAL;
        while (count >= sizeof(struct snd_seq_event)) {
                /* Read in the event header from the user */
                len = sizeof(event);
@@ -1065,7 +1071,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
                /* ok, enqueue it */
                err = snd_seq_client_enqueue_event(client, &event, file,
                                                   !(file->f_flags & O_NONBLOCK),
-                                                  0, 0);
+                                                  0, 0, &client->ioctl_mutex);
                if (err < 0)
                        break;
 
@@ -1076,6 +1082,8 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
                written += len;
        }
 
+ out:
+       mutex_unlock(&client->ioctl_mutex);
        return written ? written : err;
 }
 
@@ -1248,6 +1256,7 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client,
        struct snd_seq_client_port *port;
        struct snd_seq_port_info info;
        struct snd_seq_port_callback *callback;
+       int port_idx;
 
        if (copy_from_user(&info, arg, sizeof(info)))
                return -EFAULT;
@@ -1261,7 +1270,9 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client,
                return -ENOMEM;
 
        if (client->type == USER_CLIENT && info.kernel) {
-               snd_seq_delete_port(client, port->addr.port);
+               port_idx = port->addr.port;
+               snd_seq_port_unlock(port);
+               snd_seq_delete_port(client, port_idx);
                return -EINVAL;
        }
        if (client->type == KERNEL_CLIENT) {
@@ -1283,6 +1294,7 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client,
 
        snd_seq_set_port_info(port, &info);
        snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
+       snd_seq_port_unlock(port);
 
        if (copy_to_user(arg, &info, sizeof(info)))
                return -EFAULT;
@@ -1908,6 +1920,9 @@ static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client,
            (! snd_seq_write_pool_allocated(client) ||
             info.output_pool != client->pool->size)) {
                if (snd_seq_write_pool_allocated(client)) {
+                       /* is the pool in use? */
+                       if (atomic_read(&client->pool->counter))
+                               return -EBUSY;
                        /* remove all existing cells */
                        snd_seq_pool_mark_closing(client->pool);
                        snd_seq_queue_client_leave_cells(client->number);
@@ -2209,11 +2224,15 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
 static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct snd_seq_client *client = file->private_data;
+       long ret;
 
        if (snd_BUG_ON(!client))
                return -ENXIO;
                
-       return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
+       mutex_lock(&client->ioctl_mutex);
+       ret = snd_seq_do_ioctl(client, cmd, (void __user *) arg);
+       mutex_unlock(&client->ioctl_mutex);
+       return ret;
 }
 
 #ifdef CONFIG_COMPAT
@@ -2327,7 +2346,8 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
        if (! cptr->accept_output)
                result = -EPERM;
        else /* send it */
-               result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop);
+               result = snd_seq_client_enqueue_event(cptr, ev, file, blocking,
+                                                     atomic, hop, NULL);
 
        snd_seq_client_unlock(cptr);
        return result;