connection: split receive logic
authorDaniel Mack <zonque@gmail.com>
Fri, 17 Jan 2014 20:27:21 +0000 (21:27 +0100)
committerDaniel Mack <zonque@gmail.com>
Fri, 17 Jan 2014 20:27:24 +0000 (21:27 +0100)
This allows us to call the receive logic internally which is needed to
automatically receive a message in synchronous operation.

connection.c
connection.h
handle.c

index 4015fa51d9ac243c08be6c4132cb5cb4778fc108..f136eacf41b6d202cc6ad5506dc3448936af031c 100644 (file)
@@ -737,6 +737,230 @@ exit_unref:
        return ret;
 }
 
+static int kdbus_conn_fds_install(struct kdbus_conn *conn,
+                                 struct kdbus_conn_queue *queue)
+{
+       size_t size;
+       unsigned int i;
+       int *fds;
+       int ret;
+
+       /* get array of file descriptors */
+       size = queue->fds_count * sizeof(int);
+       fds = kmalloc(size, GFP_KERNEL);
+       if (!fds)
+               return -ENOMEM;
+
+       /* allocate new file descriptors in the receiver's process */
+       for (i = 0; i < queue->fds_count; i++) {
+               fds[i] = get_unused_fd();
+               if (fds[i] < 0) {
+                       ret = fds[i];
+                       goto remove_unused;
+               }
+       }
+
+       /* copy the array into the message item */
+       ret = kdbus_pool_write(conn->pool, queue->off + queue->fds, fds, size);
+       if (ret < 0)
+               goto remove_unused;
+
+       /* install files in the receiver's process */
+       for (i = 0; i < queue->fds_count; i++)
+               fd_install(fds[i], get_file(queue->fds_fp[i]));
+
+       kfree(fds);
+       return 0;
+
+remove_unused:
+       for (i = 0; i < queue->fds_count; i++) {
+               if (fds[i] < 0)
+                       break;
+
+               put_unused_fd(fds[i]);
+       }
+
+       kfree(fds);
+       return ret;
+}
+
+static int kdbus_conn_memfds_install(struct kdbus_conn *conn,
+                                    struct kdbus_conn_queue *queue,
+                                    int **memfds)
+{
+       size_t size;
+       int *fds;
+       unsigned int i;
+       int ret = 0;
+
+       size = queue->memfds_count * sizeof(int);
+       fds = kmalloc(size, GFP_KERNEL);
+       if (!fds)
+               return -ENOMEM;
+
+       /* allocate new file descriptors in the receiver's process */
+       for (i = 0; i < queue->memfds_count; i++) {
+               fds[i] = get_unused_fd();
+               if (fds[i] < 0) {
+                       ret = fds[i];
+                       goto remove_unused;
+               }
+       }
+
+       /*
+        * Update the file descriptor number in the items. We remembered
+        * the locations of the values in the buffer.
+        */
+       for (i = 0; i < queue->memfds_count; i++) {
+               ret = kdbus_pool_write(conn->pool,
+                                      queue->off + queue->memfds[i],
+                                      &fds[i], sizeof(int));
+               if (ret < 0)
+                       goto remove_unused;
+       }
+
+       /* install files in the receiver's process */
+       for (i = 0; i < queue->memfds_count; i++)
+               fd_install(fds[i], get_file(queue->memfds_fp[i]));
+
+       *memfds = fds;
+       return 0;
+
+remove_unused:
+       for (i = 0; i < queue->memfds_count; i++) {
+               if (fds[i] < 0)
+                       break;
+
+               put_unused_fd(fds[i]);
+       }
+
+       kfree(fds);
+       *memfds = NULL;
+       return ret;
+}
+
+static int kdbus_conn_recv_msg(struct kdbus_conn *conn,
+                              struct kdbus_cmd_recv *recv)
+{
+       struct kdbus_conn_queue *queue = NULL;
+       int *memfds = NULL;
+       unsigned int i;
+       int ret = 0;
+
+       if (recv->flags & KDBUS_RECV_USE_PRIORITY) {
+               /* get next message with highest priority */
+               queue = rb_entry(conn->msg_prio_highest,
+                                struct kdbus_conn_queue, prio_node);
+
+               /* no entry with the requested priority */
+               if (queue->priority > recv->priority)
+                       return -ENOMSG;
+       } else {
+               /* ignore the priority, return the next entry in the queue */
+               queue = list_first_entry(&conn->msg_list,
+                                        struct kdbus_conn_queue, entry);
+       }
+
+       BUG_ON(!queue);
+
+       /* just drop the message */
+       if (recv->flags & KDBUS_RECV_DROP) {
+               kdbus_conn_queue_remove(conn, queue);
+               kdbus_pool_free_range(conn->pool, queue->off);
+               kdbus_conn_queue_cleanup(queue);
+               return 0;
+       }
+
+       /* Give the offset back to the caller. */
+       recv->offset = queue->off;
+
+       /*
+        * Just return the location of the next message. Do not install
+        * file descriptors or anything else. This is usually used to
+        * determine the sender of the next queued message.
+        */
+       if (recv->flags & KDBUS_RECV_PEEK)
+               return 0;
+
+       /*
+        * Install KDBUS_MSG_PAYLOAD_MEMFDs file descriptors, we return
+        * the list of file descriptors to be able to cleanup on error.
+        */
+       if (queue->memfds_count > 0) {
+               ret = kdbus_conn_memfds_install(conn, queue, &memfds);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* install KDBUS_MSG_FDS file descriptors */
+       if (queue->fds_count > 0) {
+               ret = kdbus_conn_fds_install(conn, queue);
+               if (ret < 0)
+                       goto exit_rewind;
+       }
+
+       kfree(memfds);
+       kdbus_conn_queue_remove(conn, queue);
+       kdbus_pool_flush_dcache(conn->pool, queue->off, queue->size);
+       kdbus_conn_queue_cleanup(queue);
+
+       return 0;
+
+exit_rewind:
+       for (i = 0; i < queue->memfds_count; i++)
+               sys_close(memfds[i]);
+       kfree(memfds);
+
+       return ret;
+}
+
+/**
+ * kdbus_conn_recv_msg_user - receive a message from the queue
+ * @conn:              Connection to work on
+ * @recv_user:         A struct kdbus_cmd_recv containing the command details
+ *
+ * Returns: 0 on success, negative errno on failure.
+ */
+int kdbus_conn_recv_msg_user(struct kdbus_conn *conn,
+                            struct kdbus_cmd_recv __user *recv_buf)
+{
+       struct kdbus_cmd_recv recv;
+       int ret;
+
+       mutex_lock(&conn->lock);
+       if (unlikely(conn->ep->disconnected)) {
+               ret = -ECONNRESET;
+               goto exit_unlock;
+       }
+
+       if (conn->msg_count == 0) {
+               ret = -EAGAIN;
+               goto exit_unlock;
+       }
+
+       if (copy_from_user(&recv, recv_buf, sizeof(struct kdbus_cmd_recv))) {
+               ret = -EFAULT;
+               goto exit_unlock;
+       }
+
+       if (recv.offset > 0) {
+               ret = -EINVAL;
+               goto exit_unlock;
+       }
+
+       ret = kdbus_conn_recv_msg(conn, &recv);
+       if (ret < 0)
+               goto exit_unlock;
+
+       /* return the address of the next message in the pool */
+       if (copy_to_user(&recv_buf->offset, &recv.offset, sizeof(__u64)))
+               ret = -EFAULT;
+
+exit_unlock:
+       mutex_unlock(&conn->lock);
+       return ret;
+}
+
 /**
  * kdbus_conn_kmsg_send() - send a message
  * @ep:                        Endpoint to send from
@@ -942,223 +1166,6 @@ int kdbus_conn_kmsg_list_send(struct kdbus_ep *ep,
        return ret;
 }
 
-static int kdbus_conn_fds_install(struct kdbus_conn *conn,
-                                 struct kdbus_conn_queue *queue)
-{
-       size_t size;
-       unsigned int i;
-       int *fds;
-       int ret;
-
-       /* get array of file descriptors */
-       size = queue->fds_count * sizeof(int);
-       fds = kmalloc(size, GFP_KERNEL);
-       if (!fds)
-               return -ENOMEM;
-
-       /* allocate new file descriptors in the receiver's process */
-       for (i = 0; i < queue->fds_count; i++) {
-               fds[i] = get_unused_fd();
-               if (fds[i] < 0) {
-                       ret = fds[i];
-                       goto remove_unused;
-               }
-       }
-
-       /* copy the array into the message item */
-       ret = kdbus_pool_write(conn->pool, queue->off + queue->fds, fds, size);
-       if (ret < 0)
-               goto remove_unused;
-
-       /* install files in the receiver's process */
-       for (i = 0; i < queue->fds_count; i++)
-               fd_install(fds[i], get_file(queue->fds_fp[i]));
-
-       kfree(fds);
-       return 0;
-
-remove_unused:
-       for (i = 0; i < queue->fds_count; i++) {
-               if (fds[i] < 0)
-                       break;
-
-               put_unused_fd(fds[i]);
-       }
-
-       kfree(fds);
-       return ret;
-}
-
-static int kdbus_conn_memfds_install(struct kdbus_conn *conn,
-                                    struct kdbus_conn_queue *queue,
-                                    int **memfds)
-{
-       size_t size;
-       int *fds;
-       unsigned int i;
-       int ret = 0;
-
-       size = queue->memfds_count * sizeof(int);
-       fds = kmalloc(size, GFP_KERNEL);
-       if (!fds)
-               return -ENOMEM;
-
-       /* allocate new file descriptors in the receiver's process */
-       for (i = 0; i < queue->memfds_count; i++) {
-               fds[i] = get_unused_fd();
-               if (fds[i] < 0) {
-                       ret = fds[i];
-                       goto remove_unused;
-               }
-       }
-
-       /*
-        * Update the file descriptor number in the items. We remembered
-        * the locations of the values in the buffer.
-        */
-       for (i = 0; i < queue->memfds_count; i++) {
-               ret = kdbus_pool_write(conn->pool,
-                                      queue->off + queue->memfds[i],
-                                      &fds[i], sizeof(int));
-               if (ret < 0)
-                       goto remove_unused;
-       }
-
-       /* install files in the receiver's process */
-       for (i = 0; i < queue->memfds_count; i++)
-               fd_install(fds[i], get_file(queue->memfds_fp[i]));
-
-       *memfds = fds;
-       return 0;
-
-remove_unused:
-       for (i = 0; i < queue->memfds_count; i++) {
-               if (fds[i] < 0)
-                       break;
-
-               put_unused_fd(fds[i]);
-       }
-
-       kfree(fds);
-       *memfds = NULL;
-       return ret;
-}
-
-/**
- * kdbus_conn_recv_msg - receive a message from the queue
- * @conn:              Connection to work on
- * @recv_user:         The returned offset to the message in the pool
- *
- * Returns: 0 on success, negative errno on failure.
- */
-int kdbus_conn_recv_msg(struct kdbus_conn *conn,
-                       struct kdbus_cmd_recv __user *recv_user)
-{
-       struct kdbus_cmd_recv recv;
-       struct kdbus_conn_queue *queue = NULL;
-       int *memfds = NULL;
-       unsigned int i;
-       int ret = 0;
-
-       mutex_lock(&conn->lock);
-       if (unlikely(conn->ep->disconnected)) {
-               ret = -ECONNRESET;
-               goto exit_unlock;
-       }
-
-       if (conn->msg_count == 0) {
-               ret = -EAGAIN;
-               goto exit_unlock;
-       }
-
-       if (copy_from_user(&recv, recv_user,
-                          sizeof(struct kdbus_cmd_recv))) {
-               ret = -EFAULT;
-               goto exit_unlock;
-       }
-
-       if (recv.offset > 0) {
-               ret = -EINVAL;
-               goto exit_unlock;
-       }
-
-       if (recv.flags & KDBUS_RECV_USE_PRIORITY) {
-               /* get next message with highest priority */
-               queue = rb_entry(conn->msg_prio_highest,
-                                struct kdbus_conn_queue, prio_node);
-
-               /* no entry with the requested priority */
-               if (queue->priority > recv.priority) {
-                       ret = -ENOMSG;
-                       goto exit_unlock;
-               }
-
-       } else {
-               /* ignore the priority, return the next entry in the queue */
-               queue = list_first_entry(&conn->msg_list,
-                                        struct kdbus_conn_queue, entry);
-       }
-
-       BUG_ON(!queue);
-
-       /* just drop the message */
-       if (recv.flags & KDBUS_RECV_DROP) {
-               kdbus_conn_queue_remove(conn, queue);
-               kdbus_pool_free_range(conn->pool, queue->off);
-               kdbus_conn_queue_cleanup(queue);
-               goto exit_unlock;
-       }
-
-       /* return the address of the next message in the pool */
-       if (copy_to_user(&recv_user->offset, &queue->off, sizeof(__u64))) {
-               ret = -EFAULT;
-               goto exit_unlock;
-       }
-
-       /*
-        * Just return the location of the next message. Do not install
-        * file descriptors or anything else. This is usually used to
-        * determine the sender of the next queued message.
-        */
-       if (recv.flags & KDBUS_RECV_PEEK)
-               goto exit_unlock;
-
-       /*
-        * Install KDBUS_MSG_PAYLOAD_MEMFDs file descriptors, we return
-        * the list of file descriptors to be able to cleanup on error.
-        */
-       if (queue->memfds_count > 0) {
-               ret = kdbus_conn_memfds_install(conn, queue, &memfds);
-               if (ret < 0)
-                       goto exit_unlock;
-       }
-
-       /* install KDBUS_MSG_FDS file descriptors */
-       if (queue->fds_count > 0) {
-               ret = kdbus_conn_fds_install(conn, queue);
-               if (ret < 0)
-                       goto exit_rewind;
-       }
-
-       kfree(memfds);
-
-       kdbus_conn_queue_remove(conn, queue);
-       mutex_unlock(&conn->lock);
-
-       kdbus_pool_flush_dcache(conn->pool, queue->off, queue->size);
-       kdbus_conn_queue_cleanup(queue);
-       return 0;
-
-exit_rewind:
-       for (i = 0; i < queue->memfds_count; i++)
-               sys_close(memfds[i]);
-       kfree(memfds);
-
-exit_unlock:
-       mutex_unlock(&conn->lock);
-       return ret;
-}
-
 /**
  * kdbus_conn_disconnect() - disconnect a connection
  * @conn:                      The connection to disconnect
index c5c2d0ed786db2efb212bf151aa9b10fa5e02ecf..1bb6bb731721660d4b6ebaf824f6050ed43ff2d1 100644 (file)
@@ -90,8 +90,8 @@ struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn);
 struct kdbus_conn *kdbus_conn_unref(struct kdbus_conn *conn);
 int kdbus_conn_disconnect(struct kdbus_conn *conn, bool ensure_msg_list_empty);
 
-int kdbus_conn_recv_msg(struct kdbus_conn *conn,
-                       struct kdbus_cmd_recv __user *recv_user);
+int kdbus_conn_recv_msg_user(struct kdbus_conn *conn,
+                            struct kdbus_cmd_recv __user *recv);
 int kdbus_cmd_conn_info(struct kdbus_conn *conn,
                        void __user *buf);
 int kdbus_conn_kmsg_send(struct kdbus_ep *ep,
index 68a153d741867b8fc57a1ebe8412e450c6ed4c17..fb092a2bfefe5210a832d65342351069ed925178 100644 (file)
--- a/handle.c
+++ b/handle.c
@@ -620,7 +620,7 @@ static long kdbus_handle_ioctl_ep_connected(struct file *file, unsigned int cmd,
                        break;
                }
 
-               ret = kdbus_conn_recv_msg(conn, buf);
+               ret = kdbus_conn_recv_msg_user(conn, buf);
                break;
 
        case KDBUS_CMD_FREE: {