update RECV ioctl and implement priority queues (ABI break)
authorKay Sievers <kay@vrfy.org>
Thu, 16 Jan 2014 09:35:43 +0000 (10:35 +0100)
committerKay Sievers <kay@vrfy.org>
Fri, 17 Jan 2014 00:38:05 +0000 (01:38 +0100)
15 files changed:
.gitignore
connection.c
connection.h
handle.c
kdbus.h
test/Makefile
test/kdbus-util.c
test/kdbus-util.h
test/test-kdbus-activator.c
test/test-kdbus-benchmark.c
test/test-kdbus-chat.c
test/test-kdbus-monitor.c
test/test-kdbus-prio.c [new file with mode: 0644]
test/test-kdbus-timeout.c
test/test-kdbus.c

index 0d2e6b5be8ab39bafad0c5ea1ba4b9a0130e3991..18413eaf9549cd1f929acb546dcdadadc0ba3c2a 100644 (file)
@@ -16,3 +16,4 @@ test/test-kdbus-monitor
 test/test-kdbus-activator
 test/test-kdbus-chat
 test/test-kdbus-timeout
+test/test-kdbus-prio
index c9f6f009b0ccc56cb41d09826f1cd84e1d95f3bf..9a2998ae7323f5778c6601e0e99275b251e26b30 100644 (file)
@@ -42,6 +42,8 @@
 /**
  * struct kdbus_conn_queue - messages waiting to be read
  * @entry:             Entry in the connection's list
+ * @prio_node:         Entry in the priority queue tree
+ * @prio_entry:                Queue tree node entry in the list of one priority
  * @off:               Offset into the shmem file in the receiver's pool
  * @size:              The number of bytes used in the pool
  * @memfds:            Arrays of offsets where to update the installed
@@ -58,6 +60,9 @@
  */
 struct kdbus_conn_queue {
        struct list_head entry;
+       struct rb_node prio_node;
+       struct list_head prio_entry;
+       s64 priority;
        size_t off;
        size_t size;
 
@@ -319,6 +324,86 @@ static int kdbus_conn_payload_add(struct kdbus_conn *conn,
        return 0;
 }
 
+/* add queue entry to connection, maintain priority queue */
+static void kdbus_conn_queue_add(struct kdbus_conn *conn,
+                                struct kdbus_conn_queue *queue)
+{
+       struct rb_node **n;
+       struct rb_node *pn = NULL;
+       bool highest = true;
+
+       /* sort into priority queue tree */
+       n = &conn->msg_prio_queue.rb_node;
+       while (*n) {
+               struct kdbus_conn_queue *q;
+
+               pn = *n;
+               q = rb_entry(pn, struct kdbus_conn_queue, prio_node);
+
+               /* existing node for this priority, add to its list */
+               if (likely(queue->priority == q->priority)) {
+                       list_add_tail(&queue->prio_entry, &q->prio_entry);
+                       goto prio_done;
+               }
+
+               if (queue->priority < q->priority) {
+                       n = &pn->rb_left;
+               } else {
+                       n = &pn->rb_right;
+                       highest = false;
+               }
+       }
+
+       /* cache highest-priority entry */
+       if (highest)
+               conn->msg_prio_highest = &queue->prio_node;
+
+       /* new node for this priority */
+       rb_link_node(&queue->prio_node, pn, n);
+       rb_insert_color(&queue->prio_node, &conn->msg_prio_queue);
+       INIT_LIST_HEAD(&queue->prio_entry);
+
+prio_done:
+       /* add to unsorted fifo list */
+       list_add_tail(&queue->entry, &conn->msg_list);
+       conn->msg_count++;
+}
+
+/* remove queue entry from connection, maintain priority queue */
+static void kdbus_conn_queue_remove(struct kdbus_conn *conn,
+                                   struct kdbus_conn_queue *queue)
+{
+       conn->msg_count--;
+       list_del(&queue->entry);
+
+       if (list_empty(&queue->prio_entry)) {
+               /*
+                * Single entry for this priority, update cached
+                * highest-priority entry, remove the tree node.
+                */
+               if (conn->msg_prio_highest == &queue->prio_node)
+                       conn->msg_prio_highest = rb_next(&queue->prio_node);
+
+               rb_erase(&queue->prio_node, &conn->msg_prio_queue);
+       } else {
+               struct kdbus_conn_queue *q;
+
+               /*
+                * Multiple entries for this priority entry, get next one in
+                * the list. Update cached highest-priority entry, store the
+                * new one as the tree node.
+                */
+               q = list_first_entry(&queue->prio_entry,
+                                    struct kdbus_conn_queue, prio_entry);
+               list_del(&queue->prio_entry);
+
+               if (conn->msg_prio_highest == &queue->prio_node)
+                       conn->msg_prio_highest = &q->prio_node;
+
+               rb_replace_node(&queue->prio_node, &q->prio_node, &conn->msg_prio_queue);
+       }
+}
+
 static void kdbus_conn_queue_cleanup(struct kdbus_conn_queue *queue)
 {
        kdbus_conn_memfds_unref(queue);
@@ -479,13 +564,13 @@ static int kdbus_conn_queue_insert(struct kdbus_conn *conn,
                        goto exit_pool_free;
        }
 
-       /* remember the offset to the message */
+       /* copy some properties of the message to the queue entry */
        queue->off = off;
        queue->size = want;
+       queue->priority = kmsg->msg.priority;
 
        /* link the message into the receiver's queue */
-       list_add_tail(&queue->entry, &conn->msg_list);
-       conn->msg_count++;
+       kdbus_conn_queue_add(conn, queue);
 
        mutex_unlock(&conn->lock);
 
@@ -774,7 +859,6 @@ int kdbus_conn_kmsg_send(struct kdbus_ep *ep,
                        goto exit_unref;
                }
 
-               INIT_LIST_HEAD(&reply->entry);
                reply->conn = kdbus_conn_ref(conn_dst);
                reply->cookie = msg->cookie;
 
@@ -966,12 +1050,14 @@ remove_unused:
  *
  * Returns: 0 on success, negative errno on failure.
  */
-int kdbus_conn_recv_msg(struct kdbus_conn *conn, __u64 __user *buf)
+int kdbus_conn_recv_msg(struct kdbus_conn *conn,
+                       struct kdbus_cmd_recv __user *recv_user)
 {
-       struct kdbus_conn_queue *queue;
+       struct kdbus_cmd_recv recv;
+       struct kdbus_conn_queue *queue = NULL;
        int *memfds = NULL;
        unsigned int i;
-       int ret;
+       int ret = 0;
 
        mutex_lock(&conn->lock);
        if (unlikely(conn->ep->disconnected)) {
@@ -984,15 +1070,58 @@ int kdbus_conn_recv_msg(struct kdbus_conn *conn, __u64 __user *buf)
                goto exit_unlock;
        }
 
-       /* return the address of the next message in the pool */
-       queue = list_first_entry(&conn->msg_list,
-                                struct kdbus_conn_queue, entry);
+       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);
 
-       if (copy_to_user(buf, &queue->off, sizeof(__u64))) {
+       /* 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.
@@ -1012,8 +1141,7 @@ int kdbus_conn_recv_msg(struct kdbus_conn *conn, __u64 __user *buf)
 
        kfree(memfds);
 
-       list_del(&queue->entry);
-       conn->msg_count--;
+       kdbus_conn_queue_remove(conn, queue);
        mutex_unlock(&conn->lock);
 
        kdbus_pool_flush_dcache(conn->pool, queue->off, queue->size);
@@ -1030,82 +1158,6 @@ exit_unlock:
        return ret;
 }
 
-/**
- * kdbus_conn_drop_msg - receive a message from the queue
- * @conn:              Connection to work on
- *
- * Returns: 0 on success, negative errno on failure.
- */
-int kdbus_conn_drop_msg(struct kdbus_conn *conn)
-{
-       struct kdbus_conn_queue *queue;
-       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;
-       }
-
-       queue = list_first_entry(&conn->msg_list,
-                                struct kdbus_conn_queue, entry);
-       list_del(&queue->entry);
-       conn->msg_count--;
-
-       kdbus_pool_free_range(conn->pool, queue->off);
-       mutex_unlock(&conn->lock);
-
-       kdbus_conn_queue_cleanup(queue);
-       return 0;
-
-exit_unlock:
-       mutex_unlock(&conn->lock);
-       return ret;
-}
-
-/**
- * kdbus_conn_src_msg - return the sender of a message in the queue
- * @conn:              Connection to work on
- * @buf:               The ID of the sender of the next message in the queue
- *
- * Returns: 0 on success, negative errno on failure.
- */
-int kdbus_conn_src_msg(struct kdbus_conn *conn, __u64 __user *buf)
-{
-       struct kdbus_conn_queue *queue;
-       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;
-       }
-
-       queue = list_first_entry(&conn->msg_list,
-                                struct kdbus_conn_queue, entry);
-       if (copy_to_user(buf, &queue->src_id, sizeof(__u64))) {
-               ret = -EFAULT;
-               goto exit_unlock;
-       }
-
-       mutex_unlock(&conn->lock);
-       return 0;
-
-exit_unlock:
-       mutex_unlock(&conn->lock);
-       return ret;
-}
-
 /**
  * kdbus_conn_disconnect() - disconnect a connection
  * @conn:                      The connection to disconnect
@@ -1269,11 +1321,14 @@ int kdbus_conn_move_messages(struct kdbus_conn *conn_dst,
 
        BUG_ON(conn_src == conn_dst);
 
+       /* remove all messages from the source */
        mutex_lock(&conn_src->lock);
        list_splice_init(&conn_src->msg_list, &msg_list);
+       conn_src->msg_prio_queue = RB_ROOT;
        conn_src->msg_count = 0;
        mutex_unlock(&conn_src->lock);
 
+       /* insert messages into destination */
        mutex_lock(&conn_dst->lock);
        list_for_each_entry_safe(q, tmp, &msg_list, entry) {
 
@@ -1286,8 +1341,7 @@ int kdbus_conn_move_messages(struct kdbus_conn *conn_dst,
                if (ret < 0)
                        goto exit_unlock_dst;
 
-               list_add_tail(&q->entry, &conn_dst->msg_list);
-               conn_dst->msg_count++;
+               kdbus_conn_queue_add(conn_dst, q);
        }
 
 exit_unlock_dst:
@@ -1575,6 +1629,7 @@ int kdbus_conn_new(struct kdbus_ep *ep,
        kref_init(&conn->kref);
        mutex_init(&conn->lock);
        INIT_LIST_HEAD(&conn->msg_list);
+       conn->msg_prio_queue = RB_ROOT;
        INIT_LIST_HEAD(&conn->names_list);
        INIT_LIST_HEAD(&conn->names_queue_list);
        INIT_LIST_HEAD(&conn->reply_list);
index 0a7aaa9cdf0130db8efef4c336d654d8f09f1bcf..70162121f5b847a29db047f363b4b671d531946d 100644 (file)
@@ -29,6 +29,8 @@
  * @attach_flags:      KDBUS_ATTACH_* flags
  * @lock:              Connection data lock
  * @msg_list:          Queue of messages
+ * @msg_prio_queue:    Tree of messages, sorted by priority
+ * @prio_highest:      Cached entry for highest priority (lowest value) node
  * @hentry:            Entry in ID <-> connection map
  * @monitor_entry:     The connection is a monitor
  * @names_list:                List of well-known names
@@ -58,6 +60,8 @@ struct kdbus_conn {
        u64 attach_flags;
        struct mutex lock;
        struct list_head msg_list;
+       struct rb_root msg_prio_queue;
+       struct rb_node *msg_prio_highest;
        struct hlist_node hentry;
        struct list_head monitor_entry;
        struct list_head names_list;
@@ -86,9 +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, __u64 __user *buf);
-int kdbus_conn_drop_msg(struct kdbus_conn *conn);
-int kdbus_conn_src_msg(struct kdbus_conn *conn, __u64 __user *buf);
+int kdbus_conn_recv_msg(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 cbc56287f2d1696ce8294d0449fd545fea0e62b3..68a153d741867b8fc57a1ebe8412e450c6ed4c17 100644 (file)
--- a/handle.c
+++ b/handle.c
@@ -614,7 +614,7 @@ static long kdbus_handle_ioctl_ep_connected(struct file *file, unsigned int cmd,
        }
 
        case KDBUS_CMD_MSG_RECV:
-               /* receive a pointer to a queued message */
+               /* handle a queued message */
                if (!KDBUS_IS_ALIGNED8((uintptr_t)buf)) {
                        ret = -EFAULT;
                        break;
@@ -641,14 +641,6 @@ static long kdbus_handle_ioctl_ep_connected(struct file *file, unsigned int cmd,
                break;
        }
 
-       case KDBUS_CMD_MSG_DROP:
-               ret = kdbus_conn_drop_msg(conn);
-               break;
-
-       case KDBUS_CMD_MSG_SRC:
-               ret = kdbus_conn_src_msg(conn, buf);
-               break;
-
        case KDBUS_CMD_MEMFD_NEW:
                ret = kdbus_handle_memfd(buf);
                break;
diff --git a/kdbus.h b/kdbus.h
index d2e4fd301bf07faf42364e59a840195534c3ab96..a758547b4add641b9544f60b95b54edb3eca4c69 100644 (file)
--- a/kdbus.h
+++ b/kdbus.h
@@ -349,6 +349,7 @@ enum kdbus_payload_type {
  * struct kdbus_msg - the representation of a kdbus message
  * @size:              Total size of the message
  * @flags:             Message flags (KDBUS_MSG_FLAGS_*)
+ * @priority:          Message queue priority value
  * @dst_id:            64-bit ID of the destination connection
  * @src_id:            64-bit ID of the source connection
  * @payload_type:      Payload type (KDBUS_PAYLOAD_*)
@@ -366,6 +367,7 @@ enum kdbus_payload_type {
 struct kdbus_msg {
        __u64 size;
        __u64 flags;
+       __s64 priority;
        __u64 dst_id;
        __u64 src_id;
        __u64 payload_type;
@@ -377,6 +379,42 @@ struct kdbus_msg {
        struct kdbus_item items[0];
 } __attribute__((aligned(8)));
 
+/**
+ * enum kdbus_recv_flags - flags for de-queuing messages
+ * @KDBUS_RECV_PEEK:           Return the next queued message without
+ *                             actually de-queuing it, and without installing
+ *                             any file descriptors or other resources. It is
+ *                             usually used to determine the activating
+ *                             connection of a bus name.
+ * @KDBUS_RECV_DROP:           Drop and free the next queued message and all
+ *                             its ressources without actually receiveing it.
+ * @KDBUS_RECV_USE_PRIORITY:   Only de-queue messages with the specified or
+ *                             higher priority; if not set, the priority field
+ *                             is ignored.
+ */
+enum kdbus_recv_flags {
+       KDBUS_RECV_PEEK         = 1 <<  0,
+       KDBUS_RECV_DROP         = 1 <<  1,
+       KDBUS_RECV_USE_PRIORITY = 1 <<  2,
+};
+
+/**
+ * kdbus_cmd_recv - struct to de-queue a buffered message
+ * @flags:             KDBUS_RECV_* flags
+ * @priority:          Minimum priority of the messages to de-queue. Lowest
+ *                     values have the highest priority.
+ * @offset:            Returned offset in the pool where the message is
+ *                     stored. The user must use KDBUS_CMD_FREE to free
+ *                     the allocated memory.
+ *
+ * This struct is used with the KDBUS_CMD_MSG_RECV ioctl.
+ */
+struct kdbus_cmd_recv {
+       __u64 flags;
+       __s64 priority;
+       __u64 offset;
+} __attribute__((aligned(8)));
+
 /**
  * enum kdbus_policy_access_type - permissions of a policy record
  * @_KDBUS_POLICY_ACCESS_NULL: Uninitialized/invalid
@@ -480,8 +518,7 @@ enum kdbus_attach_flags {
  * @id128:             Unique 128-bit ID of the bus (kernel â†’ userspace)
  * @items:             A list of items
  *
- * This struct is used with the KDBUS_CMD_HELLO ioctl. See the ioctl
- * documentation for more information.
+ * This struct is used with the KDBUS_CMD_HELLO ioctl.
  */
 struct kdbus_cmd_hello {
        __u64 size;
@@ -700,11 +737,6 @@ struct kdbus_cmd_memfd_make {
  *                             placed in the receiver's pool.
  * @KDBUS_CMD_FREE:            Release the allocated memory in the receiver's
  *                             pool.
- * @KDBUS_CMD_DROP:            Drop and free the next queued message and all
- *                             its ressources without actually receiveing it.
- * @KDBUS_CMD_SRC:             Query the sender's connection ID of the next
- *                             queued message, used to determine the activating
- *                             connection of a bus name.
  * @KDBUS_CMD_NAME_ACQUIRE:    Request a well-known bus name to associate with
  *                             the connection. Well-known names are used to
  *                             address a peer on the bus.
@@ -759,10 +791,8 @@ enum kdbus_ioctl_type {
        KDBUS_CMD_BYEBYE =              _IO  (KDBUS_IOC_MAGIC, 0x31),
 
        KDBUS_CMD_MSG_SEND =            _IOW (KDBUS_IOC_MAGIC, 0x40, struct kdbus_msg),
-       KDBUS_CMD_MSG_RECV =            _IOR (KDBUS_IOC_MAGIC, 0x41, __u64 *),
+       KDBUS_CMD_MSG_RECV =            _IOWR(KDBUS_IOC_MAGIC, 0x41, struct kdbus_cmd_recv),
        KDBUS_CMD_FREE =                _IOW (KDBUS_IOC_MAGIC, 0x42, __u64 *),
-       KDBUS_CMD_MSG_DROP =            _IO  (KDBUS_IOC_MAGIC, 0x43),
-       KDBUS_CMD_MSG_SRC =             _IOR (KDBUS_IOC_MAGIC, 0x44, __u64 *),
 
        KDBUS_CMD_NAME_ACQUIRE =        _IOWR(KDBUS_IOC_MAGIC, 0x50, struct kdbus_cmd_name),
        KDBUS_CMD_NAME_RELEASE =        _IOW (KDBUS_IOC_MAGIC, 0x51, struct kdbus_cmd_name),
@@ -827,6 +857,8 @@ enum kdbus_ioctl_type {
  * @ENOENT:            The name to query information about is currently not on
  *                     the bus.
  * @ENOMEM:            Out of memory.
+ * @ENOMSG:            The queue is not empty, but no message with a matching
+ *                     priority is currently queued.
  * @ENOSYS:            The requested functionality is not available.
  * @ENOTSUPP:          The feature negotiation failed, a not supported feature
  *                     was requested, or an unknown item type was received.
index 893dfc6908c746f4d296a138379e1a6d61d29a83..d668b5ccb322f12e776d23d4e00cc46bbd2ecf87 100644 (file)
@@ -17,7 +17,8 @@ TESTS= \
        test-kdbus-activator \
        test-kdbus-monitor \
        test-kdbus-chat \
-       test-kdbus-timeout
+       test-kdbus-timeout \
+       test-kdbus-prio
 
 all: $(TESTS)
 
index c375bb9db5df57abba55f877df3125ee0079ab5b..8979635d0931bf27e06c73c6694db73f4ef07bc9 100644 (file)
@@ -103,6 +103,7 @@ int msg_send(const struct conn *conn,
                    uint64_t cookie,
                    uint64_t flags,
                    uint64_t timeout,
+                   int64_t priority,
                    uint64_t dst_id)
 {
        struct kdbus_msg *msg;
@@ -166,6 +167,7 @@ int msg_send(const struct conn *conn,
        memset(msg, 0, size);
        msg->flags = flags;
        msg->timeout_ns = timeout;
+       msg->priority = priority;
        msg->size = size;
        msg->src_id = conn->id;
        msg->dst_id = name ? 0 : dst_id;
@@ -247,11 +249,12 @@ void msg_dump(const struct conn *conn, const struct kdbus_msg *msg)
        else
                cookie_reply = msg->cookie_reply;
 
-       printf("MESSAGE: %s (%llu bytes) flags=0x%08llx, %s â†’ %s, cookie=%llu, timeout=%llu cookie_reply=%llu\n",
+       printf("MESSAGE: %s (%llu bytes) flags=0x%08llx, %s â†’ %s, cookie=%llu, timeout=%llu cookie_reply=%llu priority=%lli\n",
                enum_PAYLOAD(msg->payload_type), (unsigned long long)msg->size,
                (unsigned long long)msg->flags,
                msg_id(msg->src_id, buf_src), msg_id(msg->dst_id, buf_dst),
-               (unsigned long long)msg->cookie, (unsigned long long)timeout, (unsigned long long)cookie_reply);
+               (unsigned long long)msg->cookie, (unsigned long long)timeout, (unsigned long long)cookie_reply,
+               (long long)msg->priority);
 
        KDBUS_ITEM_FOREACH(item, msg, items) {
                if (item->size < KDBUS_ITEM_HEADER_SIZE) {
@@ -422,23 +425,25 @@ void msg_dump(const struct conn *conn, const struct kdbus_msg *msg)
 
 int msg_recv(struct conn *conn)
 {
-       uint64_t off;
+       struct kdbus_cmd_recv recv = {};
        struct kdbus_msg *msg;
        int ret;
 
-       ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &off);
+       ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv);
        if (ret < 0) {
+               ret = -errno;
                fprintf(stderr, "error receiving message: %d (%m)\n", ret);
-               return EXIT_FAILURE;
+               return ret;
        }
 
-       msg = (struct kdbus_msg *)(conn->buf + off);
+       msg = (struct kdbus_msg *)(conn->buf + recv.offset);
        msg_dump(conn, msg);
 
-       ret = ioctl(conn->fd, KDBUS_CMD_FREE, &off);
+       ret = ioctl(conn->fd, KDBUS_CMD_FREE, &recv.offset);
        if (ret < 0) {
+               ret = -errno;
                fprintf(stderr, "error free message: %d (%m)\n", ret);
-               return EXIT_FAILURE;
+               return ret;
        }
 
        return 0;
index 6765d65e5578b27a2d52ddd3d157dac20404cc2d..205d3680e0b9fbb6329c65e35a2a28f5720e62fa 100644 (file)
@@ -43,7 +43,7 @@ int msg_recv(struct conn *conn);
 void msg_dump(const struct conn *conn, const struct kdbus_msg *msg);
 char *msg_id(uint64_t id, char *buf);
 int msg_send(const struct conn *conn, const char *name, uint64_t cookie,
-            uint64_t flags, uint64_t timeout, uint64_t dst_id);
+            uint64_t flags, uint64_t timeout, int64_t priority, uint64_t dst_id);
 struct conn *connect_to_bus(const char *path, uint64_t hello_flags);
 void append_policy(struct kdbus_cmd_policy *cmd_policy, struct kdbus_item *policy, __u64 max_size);
 struct kdbus_item *make_policy_name(const char *name);
index 4b06e243fad86b5075fd785ecd9a4f11501faa5f..4754a925cdf35f71b17df739b55260eb6a2a4f22 100644 (file)
@@ -42,7 +42,7 @@ static struct conn *make_activator(const char *path, const char *name)
        hello->conn_flags = KDBUS_HELLO_ACTIVATOR;
 
        item = hello->items;
-       item->size = KDBUS_ITEM_SIZE(slen);
+       item->size = KDBUS_ITEM_HEADER_SIZE + slen;
        item->type = KDBUS_ITEM_NAME;
        strcpy(item->str, name);
 
@@ -133,7 +133,7 @@ int main(int argc, char *argv[])
                        KDBUS_NAME_LIST_ACTIVATORS |
                        KDBUS_NAME_LIST_QUEUED);
 
-       msg_send(conn_a, "foo.test.activator", 0xdeafbeef, 0, 0, KDBUS_DST_ID_NAME);
+       msg_send(conn_a, "foo.test.activator", 0xdeafbeef, 0, 0, 0, KDBUS_DST_ID_NAME);
 
        fds[0].fd = activator->fd;
        fds[1].fd = conn_a->fd;
index a7014adbae102bf09c211e125a45d316fdab69c4..1d19fa7fc07415e1df3c95ce2795fb3d0982d9d0 100644 (file)
@@ -159,17 +159,17 @@ static int
 handle_echo_reply(struct conn *conn)
 {
        int ret;
-       uint64_t off;
+       struct kdbus_cmd_recv recv = {};
        struct kdbus_msg *msg;
        const struct kdbus_item *item;
 
-       ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &off);
+       ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv);
        if (ret < 0) {
                fprintf(stderr, "error receiving message: %d (%m)\n", ret);
                return EXIT_FAILURE;
        }
 
-       msg = (struct kdbus_msg *)(conn->buf + off);
+       msg = (struct kdbus_msg *)(conn->buf + recv.offset);
        item = msg->items;
 
        KDBUS_ITEM_FOREACH(item, msg, items) {
@@ -196,7 +196,7 @@ handle_echo_reply(struct conn *conn)
                }
        }
 
-       ret = ioctl(conn->fd, KDBUS_CMD_FREE, &off);
+       ret = ioctl(conn->fd, KDBUS_CMD_FREE, &recv.offset);
        if (ret < 0) {
                fprintf(stderr, "error free message: %d (%m)\n", ret);
                return EXIT_FAILURE;
index 99fbbb355ec0ae5f7d9ee1e53b8b31ff61c0ccb6..df62e2be72eea0b97c39e5de731fd1310eb3e54d 100644 (file)
@@ -116,7 +116,7 @@ int main(int argc, char *argv[])
        add_match_empty(conn_b->fd);
 
        cookie = 0;
-       msg_send(conn_b, NULL, 0xc0000000 | cookie, 0, 0, KDBUS_DST_ID_BROADCAST);
+       msg_send(conn_b, NULL, 0xc0000000 | cookie, 0, 0, 0, KDBUS_DST_ID_BROADCAST);
 
        fds[0].fd = conn_a->fd;
        fds[1].fd = conn_b->fd;
@@ -140,12 +140,12 @@ int main(int argc, char *argv[])
                                name_release(conn_a, "foo.bar.baz");
 
                        msg_recv(conn_a);
-                       msg_send(conn_a, NULL, 0xc0000000 | cookie++, 0, 0, conn_b->id);
+                       msg_send(conn_a, NULL, 0xc0000000 | cookie++, 0, 0, 0, conn_b->id);
                }
 
                if (fds[1].revents & POLLIN) {
                        msg_recv(conn_b);
-                       msg_send(conn_b, NULL, 0xc0000000 | cookie++, 0, 0, conn_a->id);
+                       msg_send(conn_b, NULL, 0xc0000000 | cookie++, 0, 0, 0, conn_a->id);
                }
 
                name_list(conn_b, KDBUS_NAME_LIST_UNIQUE|
index 754fba71fe08637888a7d187ce37d55a31ac2504..b06b898174d5e14a91bb20ed984625a405abd74f 100644 (file)
@@ -46,7 +46,8 @@ static void usage(const char *argv0)
 static int dump_packet(struct conn *conn, int fd)
 {
        int ret;
-       uint64_t off, size;
+       struct kdbus_cmd_recv recv = {};
+       uint64_t size;
        struct kdbus_msg *msg;
        const struct kdbus_item *item;
        struct timeval now;
@@ -58,13 +59,13 @@ static int dump_packet(struct conn *conn, int fd)
        entry.tv_sec = now.tv_sec;
        entry.tv_usec = now.tv_usec;
 
-       ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &off);
+       ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv);
        if (ret < 0) {
                fprintf(stderr, "error receiving message: %d (%m)\n", ret);
                return EXIT_FAILURE;
        }
 
-       msg = (struct kdbus_msg *)(conn->buf + off);
+       msg = (struct kdbus_msg *)(conn->buf + recv.offset);
        item = msg->items;
        size = msg->size;
 
@@ -113,7 +114,7 @@ static int dump_packet(struct conn *conn, int fd)
                }
        }
 
-       ret = ioctl(conn->fd, KDBUS_CMD_FREE, &off);
+       ret = ioctl(conn->fd, KDBUS_CMD_FREE, &recv.offset);
        if (ret < 0) {
                fprintf(stderr, "error free message: %d (%m)\n", ret);
                return EXIT_FAILURE;
diff --git a/test/test-kdbus-prio.c b/test/test-kdbus-prio.c
new file mode 100644 (file)
index 0000000..ef3b6dc
--- /dev/null
@@ -0,0 +1,160 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+static int msg_recv_prio(struct conn *conn, int64_t priority)
+{
+       struct kdbus_cmd_recv recv = {
+               .flags = KDBUS_RECV_USE_PRIORITY,
+               .priority = priority,
+       };
+       struct kdbus_msg *msg;
+       int ret;
+
+       ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv);
+       if (ret < 0) {
+               ret = -errno;
+               fprintf(stderr, "error receiving message: %d (%m)\n", ret);
+               return ret;
+       }
+
+       msg = (struct kdbus_msg *)(conn->buf + recv.offset);
+       msg_dump(conn, msg);
+
+       ret = ioctl(conn->fd, KDBUS_CMD_FREE, &recv.offset);
+       if (ret < 0) {
+               ret = -errno;
+               fprintf(stderr, "error free message: %d (%m)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int run_test(void)
+{
+       struct {
+               struct kdbus_cmd_make head;
+
+               /* bloom size item */
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       uint64_t bloom_size;
+               } bs;
+
+               /* name item */
+               uint64_t n_size;
+               uint64_t n_type;
+               char name[64];
+       } bus_make;
+       char *bus;
+       struct conn *conn_a, *conn_b;
+       uint64_t cookie;
+       int fdc;
+       int ret;
+
+       printf("-- opening /dev/" KBUILD_MODNAME "/control\n");
+       fdc = open("/dev/" KBUILD_MODNAME "/control", O_RDWR|O_CLOEXEC);
+       if (fdc < 0) {
+               fprintf(stderr, "--- error %d (%m)\n", fdc);
+               return EXIT_FAILURE;
+       }
+
+       memset(&bus_make, 0, sizeof(bus_make));
+       bus_make.head.flags = KDBUS_MAKE_POLICY_OPEN;
+       bus_make.bs.size = sizeof(bus_make.bs);
+       bus_make.bs.type = KDBUS_ITEM_BLOOM_SIZE;
+       bus_make.bs.bloom_size = 64;
+
+       snprintf(bus_make.name, sizeof(bus_make.name), "%u-testbus", getuid());
+       bus_make.n_type = KDBUS_ITEM_MAKE_NAME;
+       bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1;
+
+       bus_make.head.size = sizeof(struct kdbus_cmd_make) +
+                            sizeof(bus_make.bs) +
+                            bus_make.n_size;
+
+       printf("-- creating bus '%s'\n", bus_make.name);
+       ret = ioctl(fdc, KDBUS_CMD_BUS_MAKE, &bus_make);
+       if (ret) {
+               fprintf(stderr, "--- error %d (%m)\n", ret);
+               return EXIT_FAILURE;
+       }
+
+       if (asprintf(&bus, "/dev/" KBUILD_MODNAME "/%s/bus", bus_make.name) < 0)
+               return EXIT_FAILURE;
+
+       conn_a = connect_to_bus(bus, 0);
+       conn_b = connect_to_bus(bus, 0);
+       if (!conn_a || !conn_b)
+               return EXIT_FAILURE;
+
+       cookie = 0;
+       msg_send(conn_b, NULL, ++cookie, 0, 0,   25, conn_a->id);
+       msg_send(conn_b, NULL, ++cookie, 0, 0, -600, conn_a->id);
+       msg_send(conn_b, NULL, ++cookie, 0, 0,   10, conn_a->id);
+       msg_send(conn_b, NULL, ++cookie, 0, 0,  -35, conn_a->id);
+       msg_send(conn_b, NULL, ++cookie, 0, 0, -100, conn_a->id);
+       msg_send(conn_b, NULL, ++cookie, 0, 0,   20, conn_a->id);
+       msg_send(conn_b, NULL, ++cookie, 0, 0,  -15, conn_a->id);
+       msg_send(conn_b, NULL, ++cookie, 0, 0, -800, conn_a->id);
+       msg_send(conn_b, NULL, ++cookie, 0, 0, -150, conn_a->id);
+       msg_send(conn_b, NULL, ++cookie, 0, 0, -150, conn_a->id);
+       msg_send(conn_b, NULL, ++cookie, 0, 0,   10, conn_a->id);
+       msg_send(conn_b, NULL, ++cookie, 0, 0, -800, conn_a->id);
+       msg_send(conn_b, NULL, ++cookie, 0, 0,  -10, conn_a->id);
+
+       printf("--- get priority -200\n");
+       for (;;) {
+               if (msg_recv_prio(conn_a, -200) < 0)
+                       break;
+       }
+
+       printf("--- get priority -100\n");
+       for (;;) {
+               if (msg_recv_prio(conn_a, -100) < 0)
+                       break;
+       }
+
+       printf("--- get priority 10\n");
+       for (;;) {
+               if (msg_recv_prio(conn_a, 10) < 0)
+                       break;
+       }
+
+       printf("--- get priority (all)\n");
+       for (;;) {
+               if (msg_recv(conn_a) < 0)
+                       break;
+       }
+
+       close(conn_a->fd);
+       close(conn_b->fd);
+       free(conn_a);
+       free(conn_b);
+       close(fdc);
+       free(bus);
+
+       return EXIT_SUCCESS;
+}
+
+int main(int argc, char *argv[])
+{
+       if (argc > 1)
+               while (run_test() == 0);
+
+       return run_test();
+}
index f25e3e522fa8c10e5c54b95894a8cf4ea51e3136..527b5a9cbf2073964f75a4fe323eb8600781c154 100644 (file)
@@ -18,21 +18,21 @@ static uint64_t expected = 0;
 
 int timeout_msg_recv(struct conn *conn)
 {
-       uint64_t off;
+       struct kdbus_cmd_recv recv = {};
        struct kdbus_msg *msg;
        int ret;
 
-       ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &off);
+       ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv);
        if (ret < 0) {
                fprintf(stderr, "error receiving message: %d (%m)\n", ret);
                return EXIT_FAILURE;
        }
 
-       msg = (struct kdbus_msg *)(conn->buf + off);
+       msg = (struct kdbus_msg *)(conn->buf + recv.offset);
        expected &= ~(1ULL << msg->cookie_reply);
        printf("Got message timeout for cookie %llu\n", msg->cookie_reply);
 
-       ret = ioctl(conn->fd, KDBUS_CMD_FREE, &off);
+       ret = ioctl(conn->fd, KDBUS_CMD_FREE, &recv.offset);
        if (ret < 0) {
                fprintf(stderr, "error free message: %d (%m)\n", ret);
                return EXIT_FAILURE;
@@ -104,7 +104,7 @@ static int run_test(void)
        /* send messages that expect a reply (within 1 sec), but never answer it */
        for (i = 0; i < n_msgs; i++) {
                printf("Sending message with cookie %u ...\n", i);
-               msg_send(conn_b, NULL, i, KDBUS_MSG_FLAGS_EXPECT_REPLY, (i + 1) * 100ULL * 1000ULL, conn_a->id);
+               msg_send(conn_b, NULL, i, KDBUS_MSG_FLAGS_EXPECT_REPLY, (i + 1) * 100ULL * 1000ULL, 0, conn_a->id);
                expected |= 1ULL << i;
        }
 
index 9b8a07391bcc30b70c3f9f085858fb499448e23a..01047015716e93bde9ac936ccdc15f9f2d121556 100644 (file)
@@ -445,7 +445,7 @@ static int check_hello(struct kdbus_check_env *env)
 static int check_byebye(struct kdbus_check_env *env)
 {
        struct kdbus_conn *conn;
-       uint64_t off;
+       struct kdbus_cmd_recv recv = {};
        int ret;
 
        /* create a 2nd connection */
@@ -464,10 +464,10 @@ static int check_byebye(struct kdbus_check_env *env)
        ASSERT_RETURN(ret == -1 && errno == EBUSY);
 
        /* receive the message */
-       ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &off);
+       ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv);
        ASSERT_RETURN(ret == 0);
 
-       ret = ioctl(conn->fd, KDBUS_CMD_FREE, &off);
+       ret = ioctl(conn->fd, KDBUS_CMD_FREE, &recv.offset);
        ASSERT_RETURN(ret == 0);
 
        /* and try again */
@@ -703,7 +703,7 @@ static int check_match_id_add(struct kdbus_check_env *env)
        struct kdbus_conn *conn;
        struct kdbus_item *item;
        struct kdbus_msg *msg;
-       uint64_t off;
+       struct kdbus_cmd_recv recv = {};
        int ret;
 
        memset(&buf, 0, sizeof(buf));
@@ -723,10 +723,10 @@ static int check_match_id_add(struct kdbus_check_env *env)
        ASSERT_RETURN(conn != NULL);
 
        /* 1st connection should have received a notification */
-       ret = ioctl(env->conn->fd, KDBUS_CMD_MSG_RECV, &off);
+       ret = ioctl(env->conn->fd, KDBUS_CMD_MSG_RECV, &recv);
        ASSERT_RETURN(ret == 0);
 
-       msg = (struct kdbus_msg *)(env->conn->buf + off);
+       msg = (struct kdbus_msg *)(env->conn->buf + recv.offset);
        item = &msg->items[0];
        ASSERT_RETURN(item->type == KDBUS_ITEM_ID_ADD);
        ASSERT_RETURN(item->id_change.id == conn->hello.id);
@@ -749,7 +749,7 @@ static int check_match_id_remove(struct kdbus_check_env *env)
        struct kdbus_conn *conn;
        struct kdbus_item *item;
        struct kdbus_msg *msg;
-       uint64_t off;
+       struct kdbus_cmd_recv recv = {};
        size_t id;
        int ret;
 
@@ -773,10 +773,10 @@ static int check_match_id_remove(struct kdbus_check_env *env)
        free_conn(conn);
 
        /* 1st connection should have received a notification */
-       ret = ioctl(env->conn->fd, KDBUS_CMD_MSG_RECV, &off);
+       ret = ioctl(env->conn->fd, KDBUS_CMD_MSG_RECV, &recv);
        ASSERT_RETURN(ret == 0);
 
-       msg = (struct kdbus_msg *)(env->conn->buf + off);
+       msg = (struct kdbus_msg *)(env->conn->buf + recv.offset);
        item = &msg->items[0];
        ASSERT_RETURN(item->type == KDBUS_ITEM_ID_REMOVE);
        ASSERT_RETURN(item->id_change.id == id);
@@ -798,7 +798,8 @@ static int check_match_name_add(struct kdbus_check_env *env)
        struct kdbus_cmd_name *cmd_name;
        struct kdbus_item *item;
        struct kdbus_msg *msg;
-       uint64_t size, off;
+       uint64_t size;
+       struct kdbus_cmd_recv recv = {};
        char *name;
        int ret;
 
@@ -830,10 +831,10 @@ static int check_match_name_add(struct kdbus_check_env *env)
        ASSERT_RETURN(ret == 0);
 
        /* we should have received a notification */
-       ret = ioctl(env->conn->fd, KDBUS_CMD_MSG_RECV, &off);
+       ret = ioctl(env->conn->fd, KDBUS_CMD_MSG_RECV, &recv);
        ASSERT_RETURN(ret == 0);
 
-       msg = (struct kdbus_msg *)(env->conn->buf + off);
+       msg = (struct kdbus_msg *)(env->conn->buf + recv.offset);
        item = &msg->items[0];
        ASSERT_RETURN(item->type == KDBUS_ITEM_NAME_ADD);
        ASSERT_RETURN(item->name_change.old.id == 0);
@@ -857,7 +858,8 @@ static int check_match_name_remove(struct kdbus_check_env *env)
        struct kdbus_cmd_name *cmd_name;
        struct kdbus_item *item;
        struct kdbus_msg *msg;
-       uint64_t size, off;
+       uint64_t size;
+       struct kdbus_cmd_recv recv = {};
        char *name;
        int ret;
 
@@ -893,10 +895,10 @@ static int check_match_name_remove(struct kdbus_check_env *env)
        ASSERT_RETURN(ret == 0);
 
        /* we should have received a notification */
-       ret = ioctl(env->conn->fd, KDBUS_CMD_MSG_RECV, &off);
+       ret = ioctl(env->conn->fd, KDBUS_CMD_MSG_RECV, &recv);
        ASSERT_RETURN(ret == 0);
 
-       msg = (struct kdbus_msg *)(env->conn->buf + off);
+       msg = (struct kdbus_msg *)(env->conn->buf + recv.offset);
        item = &msg->items[0];
        ASSERT_RETURN(item->type == KDBUS_ITEM_NAME_REMOVE);
        ASSERT_RETURN(item->name_change.old.id == env->conn->hello.id);
@@ -921,7 +923,8 @@ static int check_match_name_change(struct kdbus_check_env *env)
        struct kdbus_item *item;
        struct kdbus_conn *conn;
        struct kdbus_msg *msg;
-       uint64_t size, off;
+       uint64_t size;
+       struct kdbus_cmd_recv recv = {};
        char *name;
        int ret;
 
@@ -972,10 +975,10 @@ static int check_match_name_change(struct kdbus_check_env *env)
        ASSERT_RETURN(ret == 0);
 
        /* we should have received a notification */
-       ret = ioctl(env->conn->fd, KDBUS_CMD_MSG_RECV, &off);
+       ret = ioctl(env->conn->fd, KDBUS_CMD_MSG_RECV, &recv);
        ASSERT_RETURN(ret == 0);
 
-       msg = (struct kdbus_msg *)(env->conn->buf + off);
+       msg = (struct kdbus_msg *)(env->conn->buf + recv.offset);
        item = &msg->items[0];
        ASSERT_RETURN(item->type == KDBUS_ITEM_NAME_CHANGE);
        ASSERT_RETURN(item->name_change.old.id == env->conn->hello.id);
@@ -993,7 +996,7 @@ static int check_msg_basic(struct kdbus_check_env *env)
        struct kdbus_msg *msg;
        uint64_t cookie = 0x1234abcd5678eeff;
        struct pollfd fd;
-       uint64_t off;
+       struct kdbus_cmd_recv recv = {};
        int ret;
 
        /* create a 2nd connection */
@@ -1015,13 +1018,13 @@ static int check_msg_basic(struct kdbus_check_env *env)
        ret = poll(&fd, 1, 100);
        ASSERT_RETURN(ret > 0 && (fd.revents & POLLIN));
 
-       ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &off);
+       ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv);
        ASSERT_RETURN(ret == 0);
 
-       msg = (struct kdbus_msg *)(conn->buf + off);
+       msg = (struct kdbus_msg *)(conn->buf + recv.offset);
        ASSERT_RETURN(msg->cookie == cookie);
 
-       ret = ioctl(conn->fd, KDBUS_CMD_FREE, &off);
+       ret = ioctl(conn->fd, KDBUS_CMD_FREE, &recv.offset);
        ASSERT_RETURN(ret == 0);
 
        free_conn(conn);