test/test-kdbus-activator
test/test-kdbus-chat
test/test-kdbus-timeout
+test/test-kdbus-prio
/**
* 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
*/
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;
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);
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);
goto exit_unref;
}
- INIT_LIST_HEAD(&reply->entry);
reply->conn = kdbus_conn_ref(conn_dst);
reply->cookie = msg->cookie;
*
* 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)) {
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.
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);
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
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) {
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:
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);
* @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
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;
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,
}
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;
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;
* 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_*)
struct kdbus_msg {
__u64 size;
__u64 flags;
+ __s64 priority;
__u64 dst_id;
__u64 src_id;
__u64 payload_type;
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
* @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;
* 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.
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),
* @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.
test-kdbus-activator \
test-kdbus-monitor \
test-kdbus-chat \
- test-kdbus-timeout
+ test-kdbus-timeout \
+ test-kdbus-prio
all: $(TESTS)
uint64_t cookie,
uint64_t flags,
uint64_t timeout,
+ int64_t priority,
uint64_t dst_id)
{
struct kdbus_msg *msg;
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;
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) {
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;
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);
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);
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;
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) {
}
}
- 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;
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;
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|
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;
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;
}
}
- 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;
--- /dev/null
+#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();
+}
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;
/* 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;
}
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 */
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 */
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));
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);
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;
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);
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;
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);
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;
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);
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;
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);
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 */
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);