summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKay Sievers <kay@vrfy.org>2013-05-28 10:02:01 +0200
committerKay Sievers <kay@vrfy.org>2013-05-30 05:26:31 +0200
commitf971c4d6c0f148f3ab1a0acbc89c964796fc41c5 (patch)
tree3ff544a76be131e0da8995fc0e31328284fb5483
parent789cd310f26cfb8fb62673e49058f0c93df8ea42 (diff)
downloadkdbus-bus-f971c4d6c0f148f3ab1a0acbc89c964796fc41c5.tar.gz
kdbus-bus-f971c4d6c0f148f3ab1a0acbc89c964796fc41c5.tar.bz2
kdbus-bus-f971c4d6c0f148f3ab1a0acbc89c964796fc41c5.zip
pool: use shmem file as backing store
-rw-r--r--TODO8
-rw-r--r--bus.c2
-rw-r--r--connection.c361
-rw-r--r--connection.h9
-rw-r--r--internal.h3
-rw-r--r--kdbus.h20
-rw-r--r--kdbus.txt2
-rw-r--r--memfd.c2
-rw-r--r--message.c35
-rw-r--r--message.h9
-rw-r--r--notify.c16
-rw-r--r--pool.c315
-rw-r--r--pool.h69
-rw-r--r--test/kdbus-enum.c1
-rw-r--r--test/kdbus-util.c96
-rw-r--r--test/kdbus-util.h4
-rw-r--r--test/test-kdbus.c1
17 files changed, 474 insertions, 479 deletions
diff --git a/TODO b/TODO
index 617ff877dfa..b0b26e58fe8 100644
--- a/TODO
+++ b/TODO
@@ -1,17 +1,13 @@
External API:
- _IOWR() vs. _IOR()/_IOW()?
+ - rules for: which unknown items to ignore in userspace?
Internal:
- - if we expect larger amounts of memory to copy around from the sender
- to the receiver's pool, we should chunk get_user_pages(), replace vmap()
- update/rethink kdbus_conn_accounting_sub_size() use, we don't allocate
kernel memory anymore
- - BUG: the sender needs to ref the receiver when looking it up to protect
- against the receiver going away while its ressources are accessed
- memfd's file pos is shared, which can be confusing, document pread/pwrite
- prefix all functions with kdbus_
- - "signals" miss all the process metadata, we attach only creds
- - set size in shmem file for receive buffer
+ - conn->ep->bus ==> conn->bus
Kernel Core:
- remove our own task_cgroup_path_from_hierarchy() as soon as it's available:
diff --git a/bus.c b/bus.c
index 10b5d5d85ac..5465e1c15ca 100644
--- a/bus.c
+++ b/bus.c
@@ -41,7 +41,7 @@ bool kdbus_bus_uid_is_privileged(const struct kdbus_bus *bus)
}
/**
- * kdbus_bus_unref() - increase the reference counter of a kdbus_bus
+ * kdbus_bus_ref() - increase the reference counter of a kdbus_bus
* @bus: The bus to unref
*
* Every user of a bus, except for its creator, must add a reference to the
diff --git a/connection.c b/connection.c
index 4ed74da2bb7..f585e0ea1ea 100644
--- a/connection.c
+++ b/connection.c
@@ -29,6 +29,7 @@
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/syscalls.h>
+#include <linux/uio.h>
#include <uapi/linux/major.h>
#include "connection.h"
@@ -42,19 +43,22 @@
#include "names.h"
#include "policy.h"
+struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn);
+void kdbus_conn_unref(struct kdbus_conn *conn);
+
struct kdbus_conn_queue {
struct list_head entry;
- /* pointer to message placed in the receiver's buffer */
- struct __user kdbus_msg *msg;
+ /* offset to the message placed in the receiver's buffer */
+ size_t off;
/* passed KDBUS_MSG_PAYLOAD_MEMFD */
- int __user **memfds;
+ size_t *memfds;
struct file **memfds_fp;
unsigned int memfds_count;
/* passed KDBUS_MSG_FDS */
- int __user *fds;
+ size_t fds;
struct file **fds_fp;
unsigned int fds_count;
@@ -169,10 +173,10 @@ exit_unref:
return ret;
}
-static int kdbus_conn_payload_add(struct kdbus_conn_queue *queue,
+static int kdbus_conn_payload_add(struct kdbus_conn *conn,
+ struct kdbus_conn_queue *queue,
const struct kdbus_kmsg *kmsg,
- struct kdbus_slice *slice,
- size_t items, size_t vec_data)
+ size_t off, size_t items, size_t vec_data)
{
const struct kdbus_item *item;
int ret;
@@ -180,7 +184,7 @@ static int kdbus_conn_payload_add(struct kdbus_conn_queue *queue,
if (kmsg->memfds_count > 0) {
size_t size;
- size = kmsg->memfds_count * sizeof(int *);
+ size = kmsg->memfds_count * sizeof(size_t);
queue->memfds = kmalloc(size, GFP_KERNEL);
if (!queue->memfds)
return -ENOMEM;
@@ -194,50 +198,54 @@ static int kdbus_conn_payload_add(struct kdbus_conn_queue *queue,
KDBUS_PART_FOREACH(item, &kmsg->msg, items) {
switch (item->type) {
case KDBUS_MSG_PAYLOAD_VEC: {
- size_t size = KDBUS_PART_HEADER_SIZE +
- sizeof(struct kdbus_vec);
+ const size_t size = KDBUS_PART_HEADER_SIZE +
+ sizeof(struct kdbus_vec);
char tmp[size];
struct kdbus_item *it = (struct kdbus_item *)tmp;
- void *addr = NULL;
/* add item */
- it->type = KDBUS_MSG_PAYLOAD_VEC;
+ it->type = KDBUS_MSG_PAYLOAD_OFF;
it->size = size;
- /* A NULL address is a "padding vec" for alignement */
+ /* a NULL address is a "padding vec" for alignement */
if (KDBUS_PTR(item->vec.address))
- addr = slice->buf + vec_data;
- it->vec.address = KDBUS_ADDR(addr);
+ it->vec.offset = off + vec_data;
+ else
+ it->vec.offset = ~0ULL;
it->vec.size = item->vec.size;
- ret = kdbus_pool_slice_copy(slice, items, it, size);
+ ret = kdbus_pool_write(conn->pool, off + items, it, size);
if (ret < 0)
return ret;
- /* copy kdbus_vec data directly from sender */
- ret = kdbus_pool_slice_copy_user(slice, vec_data,
+ items += KDBUS_ALIGN8((it)->size);
+
+ if (!KDBUS_PTR(item->vec.address))
+ break;
+
+ /* copy kdbus_vec data from sender to receiver */
+ ret = kdbus_pool_write_user(conn->pool, off + vec_data,
KDBUS_PTR(item->vec.address), item->vec.size);
if (ret < 0)
return ret;
- items += KDBUS_ALIGN8((it)->size);
vec_data += item->vec.size;
break;
}
case KDBUS_MSG_PAYLOAD_MEMFD: {
- size_t size = KDBUS_PART_HEADER_SIZE +
- sizeof(struct kdbus_memfd);
+ const size_t size = KDBUS_PART_HEADER_SIZE +
+ sizeof(struct kdbus_memfd);
char tmp[size];
struct kdbus_item *it = (struct kdbus_item *)tmp;
struct file *fp;
- int __user *memfd;
+ size_t memfd;
/* add item */
it->type = KDBUS_MSG_PAYLOAD_MEMFD;
it->size = size;
it->memfd.size = item->memfd.size;
it->memfd.fd = -1;
- ret = kdbus_pool_slice_copy(slice, items, it, size);
+ ret = kdbus_pool_write(conn->pool, off + items, it, size);
if (ret < 0)
return ret;
@@ -248,8 +256,7 @@ static int kdbus_conn_payload_add(struct kdbus_conn_queue *queue,
/* remember the file and the location of the fd number
* which will be updated at RECV time */
- memfd = slice->buf + items +
- offsetof(struct kdbus_item, memfd.fd);
+ memfd = items + offsetof(struct kdbus_item, memfd.fd);
queue->memfds[queue->memfds_count] = memfd;
queue->memfds_fp[queue->memfds_count] = fp;
queue->memfds_count++;
@@ -273,19 +280,19 @@ void kdbus_conn_queue_cleanup(struct kdbus_conn_queue *queue)
kfree(queue);
}
-/* enqueue a message into the receiver's connection */
+/* enqueue a message into the receiver's pool */
int kdbus_conn_queue_insert(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg,
u64 deadline_ns)
{
struct kdbus_conn_queue *queue;
- void __user *buf;
- struct kdbus_slice *slice = NULL;
u64 msg_size;
+ size_t size;
size_t payloads = 0;
size_t fds = 0;
size_t meta = 0;
size_t vec_data;
- size_t size;
+ size_t want, have;
+ size_t off;
int ret = 0;
if (!conn->type == KDBUS_CONN_EP_CONNECTED)
@@ -307,11 +314,17 @@ int kdbus_conn_queue_insert(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg,
if (kmsg->msg.flags & KDBUS_MSG_FLAGS_EXPECT_REPLY)
queue->expect_reply = true;
- /* space for message header */
- msg_size = KDBUS_MSG_HEADER_SIZE;
+ /* we accept items from kernel-created messages */
+ if (kmsg->msg.src_id == KDBUS_SRC_ID_KERNEL)
+ size = kmsg->msg.size;
+ else
+ size = KDBUS_MSG_HEADER_SIZE;
+
+ /* the header */
+ msg_size = size;
/* space for PAYLOAD items */
- if (kmsg->vecs_count + kmsg->memfds_count > 0) {
+ if ((kmsg->vecs_count + kmsg->memfds_count) > 0) {
payloads = msg_size;
msg_size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)) *
kmsg->vecs_count;
@@ -336,56 +349,52 @@ int kdbus_conn_queue_insert(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg,
/* allocate the needed space in the pool of the receiver */
mutex_lock(&conn->lock);
- if (conn->msg_count > KDBUS_CONN_MAX_MSGS) {
+ if (!capable(CAP_IPC_OWNER) &&
+ conn->msg_count > KDBUS_CONN_MAX_MSGS) {
ret = -ENOBUFS;
goto exit_unlock;
}
/* do not give out more than half of the remaining space */
- size = vec_data + kmsg->vecs_size;
- if (size > (conn->pool.size - conn->pool.busy) / 2) {
+ want = vec_data + kmsg->vecs_size;
+ have = kdbus_pool_remain(conn->pool);
+ if (want < have && want > have / 2) {
ret = -EXFULL;
goto exit_unlock;
}
- buf = kdbus_pool_alloc(&conn->pool, size, &slice);
- if (!buf) {
- ret = -ENOBUFS;
+ ret = kdbus_pool_alloc(conn->pool, want, &off);
+ if (ret < 0)
goto exit_unlock;
- }
mutex_unlock(&conn->lock);
- ret = kdbus_pool_slice_map(slice, conn->task);
- if (ret < 0)
- goto exit;
-
- /* update and copy the message header */
- ret = kdbus_pool_slice_copy(slice, 0, &kmsg->msg, KDBUS_MSG_HEADER_SIZE);
+ /* copy the message header */
+ ret = kdbus_pool_write(conn->pool, off, &kmsg->msg, size);
if (ret < 0)
goto exit;
/* update the size */
- ret = kdbus_pool_slice_copy(slice, 0, &msg_size, sizeof(kmsg->msg.size));
+ ret = kdbus_pool_write(conn->pool, off, &msg_size, sizeof(kmsg->msg.size));
if (ret < 0)
goto exit;
/* add PAYLOAD items */
if (kmsg->vecs_count + kmsg->memfds_count > 0) {
- ret = kdbus_conn_payload_add(queue, kmsg,
- slice, payloads, vec_data);
+ ret = kdbus_conn_payload_add(conn, queue, kmsg,
+ off, payloads, vec_data);
if (ret < 0)
goto exit;
}
/* add a FDS item; the array content will be updated at RECV time */
if (kmsg->fds_count > 0) {
- size_t size = KDBUS_PART_HEADER_SIZE;
+ const size_t size = KDBUS_PART_HEADER_SIZE;
char tmp[size];
struct kdbus_item *it = (struct kdbus_item *)tmp;
it->type = KDBUS_MSG_FDS;
it->size = size + (kmsg->fds_count * sizeof(int));
- ret = kdbus_pool_slice_copy(slice, fds, it, size);
+ ret = kdbus_pool_write(conn->pool, off + fds, it, size);
if (ret < 0)
goto exit;
@@ -394,21 +403,20 @@ int kdbus_conn_queue_insert(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg,
goto exit;
/* remember the array to update at RECV */
- queue->fds = buf + fds + offsetof(struct kdbus_item, fds);
+ queue->fds = fds + offsetof(struct kdbus_item, fds);
queue->fds_count = kmsg->fds_count;
}
/* append message metadata/credential items */
if (kmsg->meta_size > 0) {
- ret = kdbus_pool_slice_copy(slice, meta, kmsg->meta, kmsg->meta_size);
+ ret = kdbus_pool_write(conn->pool, off + meta,
+ kmsg->meta, kmsg->meta_size);
if (ret < 0)
goto exit;
}
- kdbus_pool_slice_unmap(slice);
-
- /* remember the pointer to the message */
- queue->msg = buf;
+ /* remember the offset to the message */
+ queue->off = off;
/* link the message into the receiver's queue */
mutex_lock(&conn->lock);
@@ -424,8 +432,7 @@ exit_unlock:
mutex_unlock(&conn->lock);
exit:
kdbus_conn_queue_cleanup(queue);
- kdbus_pool_slice_unmap(slice);
- kdbus_pool_free(&conn->pool, buf);
+ kdbus_pool_free(conn->pool, off);
return ret;
}
@@ -448,7 +455,7 @@ static void kdbus_conn_scan_timeout(struct kdbus_conn *conn)
if (queue->expect_reply)
kdbus_notify_reply_timeout(conn->ep,
queue->src_id, queue->cookie);
- kdbus_pool_free(&conn->pool, queue->msg);
+ kdbus_pool_free(conn->pool, queue->off);
list_del(&queue->entry);
kdbus_conn_queue_cleanup(queue);
} else if (queue->deadline_ns < deadline) {
@@ -481,13 +488,56 @@ static void kdbus_conn_timer_func(unsigned long val)
kdbus_conn_timeout_schedule_scan(conn);
}
+/* find and pin destination connection */
+static int kdbus_conn_get_conn_dst(struct kdbus_bus *bus,
+ const struct kdbus_kmsg *kmsg,
+ struct kdbus_conn **conn)
+{
+ const struct kdbus_msg *msg = &kmsg->msg;
+ struct kdbus_conn *c;
+ int ret = 0;
+
+ mutex_lock(&bus->lock);
+
+ if (msg->dst_id == KDBUS_DST_ID_WELL_KNOWN_NAME) {
+ const struct kdbus_name_entry *name_entry;
+
+ name_entry = kdbus_name_lookup(bus->name_registry,
+ kmsg->dst_name);
+ if (!name_entry) {
+ ret = -ESRCH;
+ goto exit_unlock;
+ }
+
+ c = name_entry->conn;
+ if ((msg->flags & KDBUS_MSG_FLAGS_NO_AUTO_START) &&
+ (c->flags & KDBUS_HELLO_STARTER)) {
+ ret = -EADDRNOTAVAIL;
+ goto exit_unlock;
+ }
+ } else {
+ c = kdbus_bus_find_conn_by_id(bus, msg->dst_id);
+ if (!c) {
+ ret = -ENXIO;
+ goto exit_unlock;
+ }
+ }
+
+ kdbus_conn_ref(c);
+ *conn = c;
+
+exit_unlock:
+ mutex_unlock(&bus->lock);
+ return ret;
+}
+
int kdbus_conn_kmsg_send(struct kdbus_ep *ep,
struct kdbus_conn *conn_src,
struct kdbus_kmsg *kmsg)
{
+ const struct kdbus_msg *msg = &kmsg->msg;
struct kdbus_conn *conn_dst = NULL;
struct kdbus_conn *conn;
- const struct kdbus_msg *msg;
u64 now_ns = 0;
u64 deadline_ns = 0;
int ret;
@@ -507,8 +557,6 @@ int kdbus_conn_kmsg_send(struct kdbus_ep *ep,
return ret;
}
- msg = &kmsg->msg;
-
/* broadcast message */
if (msg->dst_id == KDBUS_DST_ID_BROADCAST) {
unsigned int i;
@@ -526,7 +574,11 @@ int kdbus_conn_kmsg_send(struct kdbus_ep *ep,
kmsg))
continue;
- //FIXME: call kdbus_kmsg_append_for_dst()?
+ /* The first receiver which requests additional
+ * metadata causes the message to carry it; all
+ * receivers after that will see all of the added
+ * data, even when they did not ask for it. */
+ kdbus_kmsg_append_meta(kmsg, conn_src, conn_dst);
kdbus_conn_queue_insert(conn_dst, kmsg, 0);
}
@@ -536,24 +588,9 @@ int kdbus_conn_kmsg_send(struct kdbus_ep *ep,
}
/* direct message */
- if (msg->dst_id == KDBUS_DST_ID_WELL_KNOWN_NAME) {
- const struct kdbus_name_entry *name_entry;
-
- name_entry = kdbus_name_lookup(ep->bus->name_registry,
- kmsg->dst_name);
- if (!name_entry)
- return -ESRCH;
- conn_dst = name_entry->conn;
-
- if ((msg->flags & KDBUS_MSG_FLAGS_NO_AUTO_START) &&
- (conn_dst->flags & KDBUS_HELLO_STARTER))
- return -EADDRNOTAVAIL;
-
- } else {
- conn_dst = kdbus_bus_find_conn_by_id(ep->bus, msg->dst_id);
- if (!conn_dst)
- return -ENXIO;
- }
+ ret = kdbus_conn_get_conn_dst(ep->bus, kmsg, &conn_dst);
+ if (ret < 0)
+ return ret;
if (msg->timeout_ns)
deadline_ns = now_ns + msg->timeout_ns;
@@ -564,12 +601,12 @@ int kdbus_conn_kmsg_send(struct kdbus_ep *ep,
conn_dst,
deadline_ns);
if (ret < 0)
- return ret;
+ goto exit;
}
- ret = kdbus_kmsg_append_for_dst(kmsg, conn_src, conn_dst);
+ ret = kdbus_kmsg_append_meta(kmsg, conn_src, conn_dst);
if (ret < 0)
- return ret;
+ goto exit;
/* monitor connections get all messages */
mutex_lock(&ep->bus->lock);
@@ -584,19 +621,24 @@ int kdbus_conn_kmsg_send(struct kdbus_ep *ep,
mutex_unlock(&ep->bus->lock);
ret = kdbus_conn_queue_insert(conn_dst, kmsg, deadline_ns);
+ if (ret < 0)
+ goto exit;
if (msg->timeout_ns)
kdbus_conn_timeout_schedule_scan(conn_dst);
+exit:
+ kdbus_conn_unref(conn_dst);
return ret;
}
-static int kdbus_conn_fds_install(struct kdbus_conn_queue *queue)
+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 = 0;
+ int ret;
/* get array of file descriptors */
size = queue->fds_count * sizeof(int);
@@ -614,10 +656,9 @@ static int kdbus_conn_fds_install(struct kdbus_conn_queue *queue)
}
/* copy the array into the message item */
- if (copy_to_user(queue->fds, fds, size)) {
- ret = -EFAULT;
+ 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++)
@@ -638,7 +679,9 @@ remove_unused:
return ret;
}
-static int kdbus_conn_memfds_install(struct kdbus_conn_queue *queue, int **memfds)
+static int kdbus_conn_memfds_install(struct kdbus_conn *conn,
+ struct kdbus_conn_queue *queue,
+ int **memfds)
{
size_t size;
int *fds;
@@ -662,10 +705,11 @@ static int kdbus_conn_memfds_install(struct kdbus_conn_queue *queue, int **memfd
/* 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++) {
- if (put_user(fds[i], queue->memfds[i])) {
- ret = -EFAULT;
+ 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 */
@@ -689,17 +733,14 @@ remove_unused:
}
static int
-kdbus_conn_recv_msg(struct kdbus_conn *conn, __u64 __user *address)
+kdbus_conn_recv_msg(struct kdbus_conn *conn, __u64 __user *buf)
{
struct kdbus_conn_queue *queue;
- u64 addr;
+ u64 off;
int *memfds = NULL;
unsigned int i;
int ret;
- if (!KDBUS_IS_ALIGNED8((unsigned long)address))
- return -EFAULT;
-
mutex_lock(&conn->lock);
if (conn->msg_count == 0) {
ret = -EAGAIN;
@@ -709,23 +750,23 @@ kdbus_conn_recv_msg(struct kdbus_conn *conn, __u64 __user *address)
/* return the address of the next message in the pool */
queue = list_first_entry(&conn->msg_list,
struct kdbus_conn_queue, entry);
- addr = KDBUS_ADDR(queue->msg);
- if (copy_to_user(address, &addr, sizeof(__u64))) {
+ off = queue->off;
+ if (copy_to_user(buf, &off, sizeof(__u64))) {
ret = -EFAULT;
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) {
- ret = kdbus_conn_memfds_install(queue, &memfds);
+ 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) {
- ret = kdbus_conn_fds_install(queue);
+ if (queue->fds_count > 0) {
+ ret = kdbus_conn_fds_install(conn, queue);
if (ret < 0)
goto exit_rewind;
}
@@ -818,6 +859,8 @@ static int kdbus_conn_open(struct inode *inode, struct file *file)
if (!conn)
return -ENOMEM;
+ kref_init(&conn->kref);
+
/* find and reference namespace */
ns = kdbus_ns_find_by_major(MAJOR(inode->i_rdev));
if (!ns) {
@@ -866,6 +909,7 @@ static void kdbus_conn_cleanup(struct kdbus_conn *conn)
mutex_lock(&conn->ep->bus->lock);
hash_del(&conn->hentry);
list_del(&conn->monitor_entry);
+ conn->type = KDBUS_CONN_EP_DISCONNECTED;
mutex_unlock(&conn->ep->bus->lock);
/* clean up any messages still left on this endpoint */
@@ -879,7 +923,7 @@ static void kdbus_conn_cleanup(struct kdbus_conn *conn)
if (queue->src_id != conn->id && queue->expect_reply) {
list_add_tail(&queue->entry, &list);
} else {
- kdbus_pool_free(&conn->pool, queue->msg);
+ kdbus_pool_free(conn->pool, queue->off);
kdbus_conn_queue_cleanup(queue);
}
}
@@ -889,7 +933,7 @@ static void kdbus_conn_cleanup(struct kdbus_conn *conn)
kdbus_notify_reply_dead(conn->ep, queue->src_id,
queue->cookie);
mutex_lock(&conn->lock);
- kdbus_pool_free(&conn->pool, queue->msg);
+ kdbus_pool_free(conn->pool, queue->off);
mutex_unlock(&conn->lock);
kdbus_conn_queue_cleanup(queue);
}
@@ -905,8 +949,25 @@ static void kdbus_conn_cleanup(struct kdbus_conn *conn)
kdbus_match_db_unref(conn->match_db);
kdbus_ep_unref(conn->ep);
- kdbus_pool_cleanup(&conn->pool);
- put_task_struct(current);
+ kdbus_pool_cleanup(conn->pool);
+}
+
+static void __kdbus_conn_free(struct kref *kref)
+{
+ struct kdbus_conn *conn = container_of(kref, struct kdbus_conn, kref);
+
+ kfree(conn);
+}
+
+struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn)
+{
+ kref_get(&conn->kref);
+ return conn;
+}
+
+void kdbus_conn_unref(struct kdbus_conn *conn)
+{
+ kref_put(&conn->kref, __kdbus_conn_free);
}
static int kdbus_conn_release(struct inode *inode, struct file *file)
@@ -927,21 +988,18 @@ static int kdbus_conn_release(struct inode *inode, struct file *file)
//FIXME:
break;
- case KDBUS_CONN_EP_CONNECTED: {
- kdbus_conn_cleanup(conn);
-
- /* notify about the closed connection */
+ case KDBUS_CONN_EP_CONNECTED:
kdbus_notify_id_change(conn->ep, KDBUS_MSG_ID_REMOVE,
conn->id, conn->flags);
+ kdbus_conn_cleanup(conn);
break;
- }
default:
break;
}
kdbus_ns_unref(conn->ns);
- kfree(conn);
+ kdbus_conn_unref(conn);
return 0;
}
@@ -1096,7 +1154,6 @@ static long kdbus_conn_ioctl_ep(struct file *file, unsigned int cmd,
case KDBUS_CMD_HELLO: {
/* turn this fd into a connection. */
- const struct kdbus_item *item;
size_t size;
void *v;
@@ -1110,7 +1167,8 @@ static long kdbus_conn_ioctl_ep(struct file *file, unsigned int cmd,
break;
}
- if (size < sizeof(struct kdbus_cmd_hello) || size > KDBUS_HELLO_MAX_SIZE) {
+ if (size < sizeof(struct kdbus_cmd_hello) ||
+ size > KDBUS_HELLO_MAX_SIZE) {
ret = -EMSGSIZE;
break;
}
@@ -1127,48 +1185,15 @@ static long kdbus_conn_ioctl_ep(struct file *file, unsigned int cmd,
break;
}
- KDBUS_PART_FOREACH(item, hello, items) {
- if (!KDBUS_PART_VALID(item, hello)) {
- ret = -EINVAL;
- break;
- }
-
- switch (item->type) {
- case KDBUS_HELLO_POOL: {
- void *p;
-
- if (!KDBUS_PTR(item->vec.address) ||
- item->vec.size == 0) {
- ret = -EINVAL;
- break;
- }
-
- /* enforce page alignment */
- if (!IS_ALIGNED(item->vec.address, PAGE_SIZE) ||
- !IS_ALIGNED(item->vec.size, PAGE_SIZE)) {
- ret = -EFAULT;
- break;
- }
-
- p = KDBUS_PTR(item->vec.address);
- if (!kdbus_pool_is_anon_map(current->mm, p,
- item->vec.size)) {
- ret = -EFAULT;
- break;
- }
-
- ret = kdbus_pool_init(&conn->pool,
- p, item->vec.size);
- break;
- }
-
- default:
- ret = -ENOTSUPP;
- }
+ if (hello->pool_size == 0 ||
+ !IS_ALIGNED(hello->pool_size, PAGE_SIZE)) {
+ ret = -EFAULT;
+ break;
}
- if (!KDBUS_PART_END(item, hello))
- return -EINVAL;
+ ret = kdbus_pool_init(&conn->pool, hello->pool_size);
+ if (ret < 0)
+ break;
mutex_init(&conn->lock);
mutex_init(&conn->names_lock);
@@ -1199,10 +1224,6 @@ static long kdbus_conn_ioctl_ep(struct file *file, unsigned int cmd,
kdbus_conn_set_audit(conn);
kdbus_conn_set_seclabel(conn);
- /* pin and store the task, so a sender can copy to the receiver */
- get_task_struct(current);
- conn->task = current;
-
/* link into bus; get new id for this connection */
mutex_lock(&conn->ep->bus->lock);
conn->id = conn->ep->bus->conn_id_next++;
@@ -1387,7 +1408,7 @@ static long kdbus_conn_ioctl_ep_connected(struct file *file, unsigned int cmd,
break;
}
- case KDBUS_CMD_MSG_RECV:
+ case KDBUS_CMD_MSG_RECV: {
/* receive a pointer to a queued message */
if (!KDBUS_IS_ALIGNED8((uintptr_t)buf)) {
ret = -EFAULT;
@@ -1396,16 +1417,24 @@ static long kdbus_conn_ioctl_ep_connected(struct file *file, unsigned int cmd,
ret = kdbus_conn_recv_msg(conn, buf);
break;
+ }
case KDBUS_CMD_MSG_RELEASE: {
+ u64 off;
+
/* free the memory used in the receiver's pool */
if (!KDBUS_IS_ALIGNED8((uintptr_t)buf)) {
ret = -EFAULT;
break;
}
+ if (copy_from_user(&off, buf, sizeof(__u64))) {
+ ret = -EFAULT;
+ break;
+ }
+
mutex_lock(&conn->lock);
- kdbus_pool_free(&conn->pool, buf);
+ ret = kdbus_pool_free(conn->pool, off);
mutex_unlock(&conn->lock);
break;
}
@@ -1472,6 +1501,13 @@ static unsigned int kdbus_conn_poll(struct file *file,
return mask;
}
+static int kdbus_conn_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct kdbus_conn *conn = file->private_data;
+
+ return kdbus_pool_mmap(conn->pool, vma);
+}
+
const struct file_operations kdbus_device_ops = {
.owner = THIS_MODULE,
.open = kdbus_conn_open,
@@ -1479,6 +1515,7 @@ const struct file_operations kdbus_device_ops = {
.poll = kdbus_conn_poll,
.llseek = noop_llseek,
.unlocked_ioctl = kdbus_conn_ioctl,
+ .mmap = kdbus_conn_mmap,
#ifdef CONFIG_COMPAT
.compat_ioctl = kdbus_conn_ioctl,
#endif
diff --git a/connection.h b/connection.h
index dbc86bac5f6..98651ebf199 100644
--- a/connection.h
+++ b/connection.h
@@ -27,10 +27,12 @@ enum kdbus_conn_type {
KDBUS_CONN_CONTROL_BUS_OWNER, /* fd to hold a bus */
KDBUS_CONN_EP, /* new fd of a bus node */
KDBUS_CONN_EP_CONNECTED, /* connection after HELLO */
+ KDBUS_CONN_EP_DISCONNECTED, /* closed connection */
KDBUS_CONN_EP_OWNER, /* fd to hold an endpoint */
};
struct kdbus_conn {
+ struct kref kref;
enum kdbus_conn_type type;
struct kdbus_ns *ns;
union {
@@ -66,15 +68,12 @@ struct kdbus_conn {
u32 sec_label_len;
#endif
- /* reference to the taks owning the connection */
- struct task_struct *task;
-
/* connection accounting */
unsigned int msg_count;
size_t allocated_size;
- /* userspace-supplied buffer to fill with message data */
- struct kdbus_pool pool;
+ /* buffer to fill with message data */
+ struct kdbus_pool *pool;
};
struct kdbus_kmsg;
diff --git a/internal.h b/internal.h
index 32313e8e619..e46df08703e 100644
--- a/internal.h
+++ b/internal.h
@@ -18,7 +18,7 @@
#define KDBUS_MSG_MAX_SIZE SZ_8K /* maximum size of message header and items */
#define KDBUS_MSG_MAX_ITEMS 128 /* maximum number of message items */
#define KDBUS_MSG_MAX_FDS 256 /* maximum number of passed file descriptors */
-#define KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE SZ_2M /* maximum message payload size */
+#define KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE SZ_8M /* maximum message payload size */
#define KDBUS_NAME_MAX_LEN 255 /* maximum length of well-known bus name */
@@ -36,7 +36,6 @@
/* exported addresses are 64bit */
#define KDBUS_PTR(addr) ((void *)(uintptr_t)(addr))
-#define KDBUS_ADDR(ptr) ((u64)(ptr))
/* exported sizes are 64bit and data aligned to 64 bit */
#define KDBUS_ALIGN8(s) ALIGN((s), 8)
diff --git a/kdbus.h b/kdbus.h
index 13f1ddc9a30..3b7783e1b16 100644
--- a/kdbus.h
+++ b/kdbus.h
@@ -73,8 +73,11 @@ struct kdbus_timestamp {
};
struct kdbus_vec {
- __u64 address;
__u64 size;
+ union {
+ __u64 address;
+ __u64 offset;
+ };
};
struct kdbus_memfd {
@@ -88,6 +91,7 @@ enum {
/* Filled in by userspace */
KDBUS_MSG_PAYLOAD_VEC, /* .data_vec, reference to memory area */
+ KDBUS_MSG_PAYLOAD_OFF, /* .data_vec, reference to memory area */
KDBUS_MSG_PAYLOAD_MEMFD, /* file descriptor of a special data file */
KDBUS_MSG_FDS, /* .data_fds of file descriptors */
KDBUS_MSG_BLOOM, /* for broadcasts, carries bloom filter blob in .data */
@@ -243,13 +247,6 @@ enum {
KDBUS_HELLO_ATTACH_AUDIT = 1 << 16,
};
-/* Items to append to struct kdbus_cmd_hello */
-enum {
- _KDBUS_HELLO_NULL,
- KDBUS_HELLO_POOL, /* kdbus_vec, userspace supplied pool to
- * place received messages */
-};
-
struct kdbus_cmd_hello {
__u64 size;
@@ -270,6 +267,7 @@ struct kdbus_cmd_hello {
__u64 id; /* id assigned to this connection */
__u64 bloom_size; /* The bloom filter size chosen by the
* bus owner */
+ __u64 pool_size; /* maximum size of pool buffer */
struct kdbus_item items[0];
};
@@ -285,7 +283,8 @@ enum {
_KDBUS_MAKE_NULL,
KDBUS_MAKE_NAME,
KDBUS_MAKE_CGROUP, /* the cgroup hierarchy ID for which to attach
- * cgroup membership paths * to messages. */
+ * cgroup membership paths to messages.
+ * FIXME: remove, use *the* hierarchy */
KDBUS_MAKE_CRED, /* allow translator services which connect
* to the bus on behalf of somebody else,
* allow specifiying the credentials of the
@@ -305,7 +304,6 @@ struct kdbus_cmd_bus_make {
* KDBUS_CMD_HELLO, later */
__u64 bloom_size; /* size of the bloom filter for this bus */
struct kdbus_item items[0];
-
};
struct kdbus_cmd_ep_make {
@@ -415,7 +413,7 @@ enum {
/* kdbus ep node commands: require connected state */
KDBUS_CMD_MSG_SEND = _IOWR(KDBUS_IOC_MAGIC, 0x40, struct kdbus_msg),
KDBUS_CMD_MSG_RECV = _IOWR(KDBUS_IOC_MAGIC, 0x41, __u64 *),
- KDBUS_CMD_MSG_RELEASE = _IOWR(KDBUS_IOC_MAGIC, 0x42, struct kdbus_msg),
+ KDBUS_CMD_MSG_RELEASE = _IOWR(KDBUS_IOC_MAGIC, 0x42, __u64 *),
KDBUS_CMD_NAME_ACQUIRE = _IOWR(KDBUS_IOC_MAGIC, 0x50, struct kdbus_cmd_name),
KDBUS_CMD_NAME_RELEASE = _IOWR(KDBUS_IOC_MAGIC, 0x51, struct kdbus_cmd_name),
diff --git a/kdbus.txt b/kdbus.txt
index 016e371b8cd..9b80cc0508a 100644
--- a/kdbus.txt
+++ b/kdbus.txt
@@ -352,7 +352,7 @@ Ioctl API
Send a message and pass data from userspace to the kernel.
KDBUS_CMD_MSG_RECV
- Receive a message from the kernel which is placed in the receiver-supplied
+ Receive a message from the kernel which is placed in the receiver's
pool.
KDBUS_CMD_MSG_RELEASE
diff --git a/memfd.c b/memfd.c
index 97c99dbd547..8d38361e377 100644
--- a/memfd.c
+++ b/memfd.c
@@ -80,7 +80,7 @@ int kdbus_memfd_new(int *fd)
mutex_init(&mf->lock);
/* allocate a new unlinked shmem file */
- shmemfp = shmem_file_setup("kdbus", 0, 0);
+ shmemfp = shmem_file_setup("kdbus-memfd", 0, 0);
if (IS_ERR(shmemfp)) {
ret = PTR_ERR(shmemfp);
goto exit;
diff --git a/message.c b/message.c
index e09c8860e42..dcbcf9d52ed 100644
--- a/message.c
+++ b/message.c
@@ -81,9 +81,10 @@ void kdbus_kmsg_free(struct kdbus_kmsg *kmsg)
int kdbus_kmsg_new(size_t extra_size, struct kdbus_kmsg **m)
{
- size_t size = sizeof(struct kdbus_kmsg) + KDBUS_ITEM_SIZE(extra_size);
+ size_t size;
struct kdbus_kmsg *kmsg;
+ size = sizeof(struct kdbus_kmsg) + KDBUS_ITEM_SIZE(extra_size);
kmsg = kzalloc(size, GFP_KERNEL);
if (!kmsg)
return -ENOMEM;
@@ -122,7 +123,8 @@ static int kdbus_msg_scan_items(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg
return -EINVAL;
kmsg->vecs_size += item->vec.size;
- if (kmsg->vecs_size > KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE)
+ if (!capable(CAP_IPC_OWNER) &&
+ kmsg->vecs_size > KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE)
return -EMSGSIZE;
kmsg->vecs_count++;
@@ -370,7 +372,7 @@ static int kdbus_kmsg_append_data(struct kdbus_kmsg *kmsg, u64 type,
item->type = type;
item->size = KDBUS_PART_HEADER_SIZE + len;
- memcpy(item->str, buf, len);
+ memcpy(item->data, buf, len);
return 0;
}
@@ -478,13 +480,20 @@ int task_cgroup_path_from_hierarchy(struct task_struct *task, int hierarchy_id,
return ret;
}
-int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg,
- struct kdbus_conn *conn_src,
- struct kdbus_conn *conn_dst)
+int kdbus_kmsg_append_meta(struct kdbus_kmsg *kmsg,
+ struct kdbus_conn *conn_src,
+ struct kdbus_conn *conn_dst)
{
struct kdbus_bus *bus = conn_dst->ep->bus;
int ret = 0;
+ if (!conn_src)
+ return 0;
+
+ /* all metadata already added */
+ if ((conn_dst->flags & kmsg->meta_attached) == conn_dst->flags)
+ return 0;
+
if (conn_dst->flags & KDBUS_HELLO_ATTACH_COMM) {
char comm[TASK_COMM_LEN];
@@ -497,6 +506,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg,
ret = kdbus_kmsg_append_str(kmsg, KDBUS_MSG_SRC_PID_COMM, comm);
if (ret < 0)
return ret;
+
+ kmsg->meta_attached |= KDBUS_HELLO_ATTACH_COMM;
}
if (conn_dst->flags & KDBUS_HELLO_ATTACH_EXE) {
@@ -537,6 +548,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg,
if (ret < 0)
return ret;
}
+
+ kmsg->meta_attached |= KDBUS_HELLO_ATTACH_EXE;
}
if (conn_dst->flags & KDBUS_HELLO_ATTACH_CMDLINE) {
@@ -563,6 +576,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg,
if (ret < 0)
return ret;
+
+ kmsg->meta_attached |= KDBUS_HELLO_ATTACH_CMDLINE;
}
/* we always return a 4 elements, the element size is 1/4 */
@@ -592,6 +607,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg,
cap, sizeof(cap));
if (ret < 0)
return ret;
+
+ kmsg->meta_attached |= KDBUS_HELLO_ATTACH_CAPS;
}
#ifdef CONFIG_CGROUPS
@@ -611,6 +628,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg,
if (ret < 0)
return ret;
+
+ kmsg->meta_attached |= KDBUS_HELLO_ATTACH_CGROUP;
}
#endif
@@ -621,6 +640,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg,
sizeof(conn_src->audit_ids));
if (ret < 0)
return ret;
+
+ kmsg->meta_attached |= KDBUS_HELLO_ATTACH_AUDIT;
}
#endif
@@ -634,6 +655,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg,
if (ret < 0)
return ret;
}
+
+ kmsg->meta_attached |= KDBUS_HELLO_ATTACH_SECLABEL;
}
#endif
diff --git a/message.h b/message.h
index 96a1355ae84..8f1ab7589c3 100644
--- a/message.h
+++ b/message.h
@@ -35,6 +35,9 @@ struct kdbus_kmsg {
unsigned int vecs_count;
unsigned int memfds_count;
+ /* added metadata flags KDBUS_HELLO_ATTACH_* */
+ u64 meta_attached;
+
struct kdbus_msg msg;
};
@@ -50,7 +53,7 @@ int kdbus_kmsg_append_src_names(struct kdbus_kmsg *kmsg,
struct kdbus_conn *conn);
int kdbus_kmsg_append_cred(struct kdbus_kmsg *kmsg,
const struct kdbus_creds *creds);
-int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg,
- struct kdbus_conn *conn_src,
- struct kdbus_conn *conn_dst);
+int kdbus_kmsg_append_meta(struct kdbus_kmsg *kmsg,
+ struct kdbus_conn *conn_src,
+ struct kdbus_conn *conn_dst);
#endif
diff --git a/notify.c b/notify.c
index c58f944969b..5292c4daab7 100644
--- a/notify.c
+++ b/notify.c
@@ -81,24 +81,25 @@ int kdbus_notify_name_change(struct kdbus_ep *ep, u64 type,
{
struct kdbus_manager_msg_name_change *name_change;
struct kdbus_kmsg *kmsg = NULL;
- struct kdbus_item *data;
+ struct kdbus_item *item;
struct kdbus_msg *msg;
- u64 extra_size = sizeof(*name_change) + strlen(name);
+ size_t extra_size;
int ret;
+ extra_size = sizeof(*name_change) + strlen(name);
ret = kdbus_kmsg_new(extra_size, &kmsg);
if (ret < 0)
return ret;
msg = &kmsg->msg;
- data = msg->items;
- name_change = (struct kdbus_manager_msg_name_change *) data->data;
+ item = msg->items;
+ name_change = (struct kdbus_manager_msg_name_change *)item->data;
/* FIXME */
msg->dst_id = KDBUS_DST_ID_BROADCAST;
msg->src_id = KDBUS_SRC_ID_KERNEL;
- data->type = type;
+ item->type = type;
name_change->old_id = old_id;
name_change->new_id = new_id;
@@ -118,16 +119,15 @@ int kdbus_notify_id_change(struct kdbus_ep *ep, u64 type,
struct kdbus_kmsg *kmsg = NULL;
struct kdbus_item *item;
struct kdbus_msg *msg;
- u64 extra_size = sizeof(*id_change);
int ret;
- ret = kdbus_kmsg_new(extra_size, &kmsg);
+ ret = kdbus_kmsg_new(sizeof(*id_change), &kmsg);
if (ret < 0)
return ret;
msg = &kmsg->msg;
item = msg->items;
- id_change = (struct kdbus_manager_msg_id_change *) item->data;
+ id_change = (struct kdbus_manager_msg_id_change *)item->data;
msg->dst_id = KDBUS_DST_ID_BROADCAST;
msg->src_id = KDBUS_SRC_ID_KERNEL;
diff --git a/pool.c b/pool.c
index 06e6f32b725..78641aa8bc9 100644
--- a/pool.c
+++ b/pool.c
@@ -21,26 +21,70 @@
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/rbtree.h>
+#include <linux/file.h>
+#include <linux/shmem_fs.h>
+#include <linux/aio.h>
#include "pool.h"
#include "message.h"
+/*
+ * Messages sent with KDBUS_CMD_MSG_SEND are copied direcly by the
+ * sending process into the receiver's pool.
+ *
+ * Messages received with KDBUS_CMD_MSG_RECV just return the offset
+ * to the data placed in the pool.
+ *
+ * The internally allocated memory needs to be returned by the receiver
+ * with KDBUS_CMD_MSG_RELEASE.
+ */
+
+/* The receiver's buffer, managed as a pool of allocated and free
+ * slices containing the queued messages. */
+struct kdbus_pool {
+ struct file *f; /* shmem file */
+ size_t size; /* size of file */
+ size_t busy; /* currently allocated size */
+
+ struct list_head slices; /* all slices sorted by address */
+ struct rb_root slices_busy; /* tree of allocated slices */
+ struct rb_root slices_free; /* tree of free slices */
+};
+
+/* The pool has one or more slices, always spanning the entire size of the
+ * pool.
+ *
+ * Every slice is an element in a list sorted by the buffer address, to
+ * provide access to the next neighbor slice.
+ *
+ * Every slice is member in either the busy or the free tree. The free
+ * tree is organized by slice size, the busy tree organized by buffer
+ * offset. */
+struct kdbus_slice {
+ size_t off; /* offset of slice */
+ size_t size; /* size of slice */
+
+ struct list_head entry;
+ struct rb_node rb_node;
+ bool free;
+};
+
static void __maybe_unused kdbus_pool_slices_dump(struct kdbus_pool *pool,
const char *str)
{
struct kdbus_slice *s;
- pr_info("=== dump start '%s' pool=%p buf=%p size=%zu ===\n",
- str, pool, pool->buf, pool->size);
+ pr_info("=== dump start '%s' pool=%p size=%zu ===\n",
+ str, pool, pool->size);
list_for_each_entry(s, &pool->slices, entry)
- pr_info(" slice=%p free=%u, buf=%p size=%zu\n",
- s, s->free, s->buf, s->size);
+ pr_info(" slice=%p free=%u, off=%zu size=%zu\n",
+ s, s->free, s->off, s->size);
pr_info("=== dump end '%s' pool=%p ===\n", str, pool);
}
-static struct kdbus_slice *kdbus_pool_slice_new(void *__user *buf, size_t size)
+static struct kdbus_slice *kdbus_pool_slice_new(size_t off, size_t size)
{
struct kdbus_slice *slice;
@@ -48,7 +92,7 @@ static struct kdbus_slice *kdbus_pool_slice_new(void *__user *buf, size_t size)
if (!slice)
return NULL;
- slice->buf = buf;
+ slice->off = off;
slice->size = size;
slice->free = true;
return slice;
@@ -90,9 +134,9 @@ static void kdbus_pool_add_busy_slice(struct kdbus_pool *pool,
pn = *n;
pslice = rb_entry(pn, struct kdbus_slice, rb_node);
- if (slice->buf < pslice->buf)
+ if (slice->off < pslice->off)
n = &pn->rb_left;
- else if (slice->buf > pslice->buf)
+ else if (slice->off > pslice->off)
n = &pn->rb_right;
}
@@ -100,9 +144,9 @@ static void kdbus_pool_add_busy_slice(struct kdbus_pool *pool,
rb_insert_color(&slice->rb_node, &pool->slices_busy);
}
-/* find a slice by its pool buffer address */
+/* find a slice by its pool offset */
static struct kdbus_slice *kdbus_pool_find_slice(struct kdbus_pool *pool,
- void __user *buf)
+ size_t off)
{
struct rb_node *n;
@@ -111,9 +155,9 @@ static struct kdbus_slice *kdbus_pool_find_slice(struct kdbus_pool *pool,
struct kdbus_slice *s;
s = rb_entry(n, struct kdbus_slice, rb_node);
- if (buf < s->buf)
+ if (off < s->off)
n = n->rb_left;
- else if (buf > s->buf)
+ else if (off > s->off)
n = n->rb_right;
else
return s;
@@ -123,22 +167,22 @@ static struct kdbus_slice *kdbus_pool_find_slice(struct kdbus_pool *pool,
}
/* allocate a slice from the pool with the given size */
-static struct kdbus_slice *kdbus_pool_alloc_slice(struct kdbus_pool *pool,
- size_t size)
+static int kdbus_pool_alloc_slice(struct kdbus_pool *pool,
+ size_t size, struct kdbus_slice **slice)
{
size_t slice_size = KDBUS_ALIGN8(size);
struct rb_node *n;
- struct kdbus_slice *slice;
+ struct kdbus_slice *s;
struct rb_node *found = NULL;
/* search a free slice with the closest matching size */
n = pool->slices_free.rb_node;
while (n) {
- slice = rb_entry(n, struct kdbus_slice, rb_node);
- if (slice_size < slice->size) {
+ s = rb_entry(n, struct kdbus_slice, rb_node);
+ if (slice_size < s->size) {
found = n;
n = n->rb_left;
- } else if (slice_size > slice->size)
+ } else if (slice_size > s->size)
n = n->rb_right;
else {
found = n;
@@ -148,36 +192,37 @@ static struct kdbus_slice *kdbus_pool_alloc_slice(struct kdbus_pool *pool,
/* no slice with the minimum size found in the pool */
if (!found)
- return NULL;
+ return -ENOBUFS;
/* no exact match, use the closest one */
if (!n)
- slice = rb_entry(found, struct kdbus_slice, rb_node);
+ s = rb_entry(found, struct kdbus_slice, rb_node);
/* move slice from free to the busy tree */
rb_erase(found, &pool->slices_free);
- kdbus_pool_add_busy_slice(pool, slice);
+ kdbus_pool_add_busy_slice(pool, s);
/* we got a slice larger than what we asked for? */
- if (slice->size > slice_size) {
- struct kdbus_slice *s;
+ if (s->size > slice_size) {
+ struct kdbus_slice *s_new;
/* split-off the remainder of the size to its own slice */
- s = kdbus_pool_slice_new(slice->buf + slice_size,
- slice->size - slice_size);
- if (!s)
- return NULL;
+ s_new = kdbus_pool_slice_new(s->off + slice_size,
+ s->size - slice_size);
+ if (!s_new)
+ return -ENOMEM;
- list_add(&s->entry, &slice->entry);
- kdbus_pool_add_free_slice(pool, s);
+ list_add(&s_new->entry, &s->entry);
+ kdbus_pool_add_free_slice(pool, s_new);
/* adjust our size now that we split-off another slice */
- slice->size = slice_size;
+ s->size = slice_size;
}
- slice->free = false;
- pool->busy += slice->size;
- return slice;
+ s->free = false;
+ pool->busy += s->size;
+ *slice = s;
+ return 0;
}
/* return an allocated slice back to the pool */
@@ -218,25 +263,38 @@ static void kdbus_pool_free_slice(struct kdbus_pool *pool,
kdbus_pool_add_free_slice(pool, slice);
}
-int kdbus_pool_init(struct kdbus_pool *pool, void __user *buf, size_t size)
+int kdbus_pool_init(struct kdbus_pool **pool, size_t size)
{
+ struct kdbus_pool *p;
+ struct file *f;
struct kdbus_slice *s;
- pool->buf = buf;
- pool->size = size;
- pool->busy = 0;
- pool->slices_free = RB_ROOT;
- pool->slices_busy = RB_ROOT;
+ p = kzalloc(sizeof(struct kdbus_pool), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ f = shmem_file_setup("kdbus-pool", size, 0);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
/* allocate first slice spanning the entire pool */
- s = kdbus_pool_slice_new(buf, size);
- if (!s)
+ s = kdbus_pool_slice_new(0, size);
+ if (!s) {
+ fput(f);
return -ENOMEM;
+ }
+
+ p->f = f;
+ p->size = size;
+ p->busy = 0;
+ p->slices_free = RB_ROOT;
+ p->slices_busy = RB_ROOT;
- INIT_LIST_HEAD(&pool->slices);
- list_add(&s->entry, &pool->slices);
+ INIT_LIST_HEAD(&p->slices);
+ list_add(&s->entry, &p->slices);
- kdbus_pool_add_free_slice(pool, s);
+ kdbus_pool_add_free_slice(p, s);
+ *pool = p;
return 0;
}
@@ -244,38 +302,49 @@ void kdbus_pool_cleanup(struct kdbus_pool *pool)
{
struct kdbus_slice *s, *tmp;
- if (!pool->buf)
+ if (!pool)
return;
list_for_each_entry_safe(s, tmp, &pool->slices, entry) {
list_del(&s->entry);
kfree(s);
}
+
+ fput(pool->f);
+ kfree(pool);
+}
+
+size_t kdbus_pool_remain(const struct kdbus_pool *pool)
+{
+ return pool->size - pool->busy;
}
/* allocate a message of the given size in the receiver's pool */
-void __user *kdbus_pool_alloc(struct kdbus_pool *pool, size_t size,
- struct kdbus_slice **slice)
+int kdbus_pool_alloc(struct kdbus_pool *pool, size_t size, size_t *off)
{
struct kdbus_slice *s;
+ int ret;
- s = kdbus_pool_alloc_slice(pool, size);
- if (!s)
- return NULL;
+ ret = kdbus_pool_alloc_slice(pool, size, &s);
+ if (ret < 0)
+ return ret;
- *slice = s;
- return s->buf;
+ *off = s->off;
+ return 0;
}
/* free the allocated message */
-int kdbus_pool_free(struct kdbus_pool *pool, void __user *buf)
+int kdbus_pool_free(struct kdbus_pool *pool, size_t off)
{
struct kdbus_slice *slice;
- if (!buf)
+ if (!pool)
return 0;
- slice = kdbus_pool_find_slice(pool, buf);
+ if (off >= pool->size)
+ return -EINVAL;
+
+ slice = kdbus_pool_find_slice(pool, off);
if (!slice)
return -ENXIO;
@@ -283,126 +352,48 @@ int kdbus_pool_free(struct kdbus_pool *pool, void __user *buf)
return 0;
}
-/* unpin the receiver's pages */
-void kdbus_pool_slice_unmap(struct kdbus_slice *slice)
+/* write to the receiver's shmem file */
+ssize_t kdbus_pool_write_user(const struct kdbus_pool *pool, size_t off,
+ void *data, size_t len)
{
- unsigned int i;
-
- if (!slice)
- return;
-
- vunmap(slice->pg_buf);
- slice->pg_buf = NULL;
+ loff_t o = off;
- for (i = 0; i < slice->pg_n; i++)
- put_page(slice->pg[i]);
- kfree(slice->pg);
-
- slice->pg_n = 0;
- slice->pg = NULL;
+ return pool->f->f_op->write(pool->f, data, len, &o);
}
-bool kdbus_pool_is_anon_map(struct mm_struct *mm,
- void __user *buf, size_t size)
+ssize_t kdbus_pool_write(const struct kdbus_pool *pool, size_t off,
+ void *data, size_t len)
{
- unsigned long addr = (unsigned long) buf;
- struct vm_area_struct *vma;
-
- vma = find_vma(mm, addr);
- if (!vma)
- return false;
+ loff_t o = off;
+ mm_segment_t old_fs;
+ void __user *p;
+ ssize_t ret;
- if (vma->vm_file)
- return false;
+ old_fs = get_fs();
+ set_fs(get_ds());
- if (addr + size > vma->vm_end)
- return false;
+ p = (void __force __user *)data;
+ ret = pool->f->f_op->write(pool->f, p, len, &o);
- return true;
+ set_fs(old_fs);
+ return ret;
}
-/* pin the receiver's memory range/pages */
-int kdbus_pool_slice_map(struct kdbus_slice *slice, struct task_struct *task)
+/* map the shmem file for the receiver */
+int kdbus_pool_mmap(const struct kdbus_pool *pool, struct vm_area_struct *vma)
{
- unsigned int n;
- int have;
- unsigned long base;
- unsigned long addr;
- struct mm_struct *mm;
-
- /* calculate the number of pages involved in the range */
- addr = (unsigned long)slice->buf;
- n = (addr + slice->size - 1) / PAGE_SIZE - addr / PAGE_SIZE + 1;
-
- slice->pg = kmalloc(n * sizeof(struct page *), GFP_KERNEL);
- if (!slice->pg)
- return -ENOMEM;
-
- /* start address in our first page */
- base = addr & PAGE_MASK;
- slice->pg_off = addr - base;
-
- /* pin the receiver's pool page(s) we will write to; the task
- * is pinned as long as the connection is open */
- mm = get_task_mm(task);
- if (!mm) {
- kdbus_pool_slice_unmap(slice);
- return -ESHUTDOWN;
- }
-
- down_read(&mm->mmap_sem);
- if (kdbus_pool_is_anon_map(mm, slice->buf, slice->size))
- have = get_user_pages(task, mm, base, n, true, false,
- slice->pg, NULL);
- else
- have = -EFAULT;
- up_read(&mm->mmap_sem);
-
- mmput(mm);
-
- if (have < 0) {
- kdbus_pool_slice_unmap(slice);
- return have;
- }
-
- slice->pg_n = have;
-
- /* fewer pages than requested */
- if (slice->pg_n < n) {
- kdbus_pool_slice_unmap(slice);
- return -EFAULT;
- }
+ /* deny write access to the pool */
+ if (vma->vm_flags & VM_WRITE)
+ return -EPERM;
- /* map the slice so we can access it */
- slice->pg_buf = vmap(slice->pg, slice->pg_n, 0, PAGE_KERNEL);
- if (!slice->pg_buf) {
- kdbus_pool_slice_unmap(slice);
+ /* do not allow to map more than the size of the file */
+ if ((vma->vm_end - vma->vm_start) > pool->size)
return -EFAULT;
- }
-
- return 0;
-}
-
-/* copy a memory range to a slice in the receiver's pool */
-int kdbus_pool_slice_copy(struct kdbus_slice *slice, size_t off,
- void *buf, size_t size)
-{
- memcpy(slice->pg_buf + slice->pg_off + off, buf, size);
- return 0;
-}
-
-/* copy a user memory range to a slice in the receiver's pool */
-int kdbus_pool_slice_copy_user(struct kdbus_slice *slice, size_t off,
- void __user *buf, size_t size)
-{
- /* a NULL from address just adds padding bytes for alignement */
- if (!buf) {
- memset(slice->pg_buf + slice->pg_off + off, 0, size);
- return 0;
- }
- if (copy_from_user(slice->pg_buf + slice->pg_off + off, buf, size))
- return -EFAULT;
+ /* replace the connection file with our shmem file */
+ if (vma->vm_file)
+ fput(vma->vm_file);
+ vma->vm_file = get_file(pool->f);
- return 0;
+ return pool->f->f_op->mmap(pool->f, vma);
}
diff --git a/pool.h b/pool.h
index 8b52ede0c0c..dc7fc1f9102 100644
--- a/pool.h
+++ b/pool.h
@@ -13,67 +13,18 @@
#ifndef __KDBUS_POOL_H
#define __KDBUS_POOL_H
-/*
- * Messages sent with KDBUS_CMD_MSG_SEND are copied direcly by the
- * sending process into the receiver's pool. The receiver has provided
- * the memory and registered it with KDBUS_HELLO_POOL.
- *
- * Messages received with KDBUS_CMD_MSG_RECV just return a pointer
- * into the pool.
- *
- * The internally allocated memory needs to be returned by the receiver
- * with * KDBUS_CMD_MSG_RELEASE.
- */
-
-/* The receiver-provided buffer managed as a pool of allocated and free
- * slices containing the queued messages. */
-struct kdbus_pool {
- void __user *buf; /* receiver-supplied buffer */
- size_t size; /* size of buffer */
- size_t busy; /* allocated size */
-
- struct list_head slices; /* all slices sorted by address */
- struct rb_root slices_busy; /* tree of allocated slices */
- struct rb_root slices_free; /* tree of free slices */
-};
+struct kdbus_pool;
-/* The pool has one or more slices, always spanning the entire size of the
- * pool.
- *
- * Every slice is an element in a list sorted by the buffer address, to
- * provide access to the next neighbor slice.
- *
- * Every slice is member in either the busy or the free tree. The free
- * tree is organized by slice size, the busy tree organized by buffer
- * address. */
-struct kdbus_slice {
- void __user *buf; /* address of slice */
- size_t size; /* size of slice */
-
- struct list_head entry;
- struct rb_node rb_node;
- bool free;
-
- struct page **pg; /* pages mapped by the slice */
- unsigned int pg_n; /* number of pages */
- size_t pg_off; /* offset into the first page */
- void *pg_buf; /* kernel address of mapped pages */
-};
-
-int kdbus_pool_init(struct kdbus_pool *pool, void __user *buf, size_t size);
+int kdbus_pool_init(struct kdbus_pool **pool, size_t size);
void kdbus_pool_cleanup(struct kdbus_pool *pool);
-bool kdbus_pool_is_anon_map(struct mm_struct *mm,
- void __user *buf, size_t size);
-
-void __user *kdbus_pool_alloc(struct kdbus_pool *pool, size_t size,
- struct kdbus_slice **slice);
-int kdbus_pool_free(struct kdbus_pool *pool, void __user *buf);
-int kdbus_pool_slice_map(struct kdbus_slice *slice, struct task_struct *task);
-void kdbus_pool_slice_unmap(struct kdbus_slice *slice);
+int kdbus_pool_alloc(struct kdbus_pool *pool, size_t size, size_t *off);
+int kdbus_pool_free(struct kdbus_pool *pool, size_t off);
+size_t kdbus_pool_remain(const struct kdbus_pool *pool);
-int kdbus_pool_slice_copy(struct kdbus_slice *slice, size_t off,
- void *buf, size_t size);
-int kdbus_pool_slice_copy_user(struct kdbus_slice *slice, size_t off,
- void __user *buf, size_t size);
+ssize_t kdbus_pool_write(const struct kdbus_pool *pool, size_t off,
+ void *data, size_t len);
+ssize_t kdbus_pool_write_user(const struct kdbus_pool *pool, size_t off,
+ void __user *data, size_t len);
+int kdbus_pool_mmap(const struct kdbus_pool *pool, struct vm_area_struct *vma);
#endif
diff --git a/test/kdbus-enum.c b/test/kdbus-enum.c
index 4439787e469..0a20c81469e 100644
--- a/test/kdbus-enum.c
+++ b/test/kdbus-enum.c
@@ -56,6 +56,7 @@ LOOKUP(CMD);
TABLE(MSG) = {
ENUM(_KDBUS_MSG_NULL),
ENUM(KDBUS_MSG_PAYLOAD_VEC),
+ ENUM(KDBUS_MSG_PAYLOAD_OFF),
ENUM(KDBUS_MSG_PAYLOAD_MEMFD),
ENUM(KDBUS_MSG_FDS),
ENUM(KDBUS_MSG_BLOOM),
diff --git a/test/kdbus-util.c b/test/kdbus-util.c
index 3cc69cf21ee..7440f941ecd 100644
--- a/test/kdbus-util.c
+++ b/test/kdbus-util.c
@@ -30,19 +30,14 @@
#include "kdbus-util.h"
#include "kdbus-enum.h"
+#define POOL_SIZE (16 * 1014LU * 1024LU)
struct conn *connect_to_bus(const char *path)
{
int fd, ret;
- void *buf;
- struct {
- struct kdbus_cmd_hello hello;
- uint64_t v_size;
- uint64_t v_type;
- struct kdbus_vec vec;
- } h;
+ struct kdbus_cmd_hello hello;
struct conn *conn;
- memset(&h, 0, sizeof(h));
+ memset(&hello, 0, sizeof(hello));
printf("-- opening bus connection %s\n", path);
fd = open(path, O_RDWR|O_CLOEXEC);
@@ -51,34 +46,23 @@ struct conn *connect_to_bus(const char *path)
return NULL;
}
- buf = mmap(NULL, 16 * 1024 * 1024, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
- if (buf == MAP_FAILED) {
- fprintf(stderr, "--- error mmap (%m)\n");
- return NULL;
- }
- h.v_type = KDBUS_HELLO_POOL;
- h.v_size = KDBUS_PART_HEADER_SIZE + sizeof(struct kdbus_vec);
- h.vec.address = (uint64_t)buf;
- h.vec.size = 16 * 1024 * 1024;
-
- h.hello.conn_flags = KDBUS_HELLO_ACCEPT_FD |
- KDBUS_HELLO_ATTACH_COMM |
- KDBUS_HELLO_ATTACH_EXE |
- KDBUS_HELLO_ATTACH_CMDLINE |
- KDBUS_HELLO_ATTACH_CAPS |
- KDBUS_HELLO_ATTACH_CGROUP |
- KDBUS_HELLO_ATTACH_SECLABEL |
- KDBUS_HELLO_ATTACH_AUDIT;
-
- h.hello.size = sizeof(struct kdbus_cmd_hello) +
- KDBUS_PART_HEADER_SIZE + sizeof(struct kdbus_vec);
-
- ret = ioctl(fd, KDBUS_CMD_HELLO, &h.hello);
+ hello.conn_flags = KDBUS_HELLO_ACCEPT_FD |
+ KDBUS_HELLO_ATTACH_COMM |
+ KDBUS_HELLO_ATTACH_EXE |
+ KDBUS_HELLO_ATTACH_CMDLINE |
+ KDBUS_HELLO_ATTACH_CAPS |
+ KDBUS_HELLO_ATTACH_CGROUP |
+ KDBUS_HELLO_ATTACH_SECLABEL |
+ KDBUS_HELLO_ATTACH_AUDIT;
+ hello.size = sizeof(struct kdbus_cmd_hello);
+ hello.pool_size = POOL_SIZE;
+
+ ret = ioctl(fd, KDBUS_CMD_HELLO, &hello);
if (ret) {
fprintf(stderr, "--- error when saying hello: %d (%m)\n", ret);
return NULL;
}
- printf("-- Our peer ID for %s: %llu\n", path, (unsigned long long)h.hello.id);
+ printf("-- Our peer ID for %s: %llu\n", path, (unsigned long long)hello.id);
conn = malloc(sizeof(*conn));
if (!conn) {
@@ -86,8 +70,15 @@ struct conn *connect_to_bus(const char *path)
return NULL;
}
+ conn->buf = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, fd, 0);
+ if (conn->buf == MAP_FAILED) {
+ free(conn);
+ fprintf(stderr, "--- error mmap (%m)\n");
+ return NULL;
+ }
+
conn->fd = fd;
- conn->id = h.hello.id;
+ conn->id = hello.id;
return conn;
}
@@ -101,7 +92,7 @@ int msg_send(const struct conn *conn,
const char ref2[] = "0123456789_1";
struct kdbus_item *item;
uint64_t size;
- int memfd;
+ int memfd = -1;
int ret;
size = sizeof(struct kdbus_msg);
@@ -179,14 +170,13 @@ int msg_send(const struct conn *conn,
if (dst_id == KDBUS_DST_ID_BROADCAST) {
item->type = KDBUS_MSG_BLOOM;
item->size = KDBUS_PART_HEADER_SIZE + 64;
- item = KDBUS_PART_NEXT(item);
} else {
item->type = KDBUS_MSG_PAYLOAD_MEMFD;
item->size = KDBUS_PART_HEADER_SIZE + sizeof(struct kdbus_memfd);
item->memfd.size = 16;
item->memfd.fd = memfd;
- item = KDBUS_PART_NEXT(item);
}
+ item = KDBUS_PART_NEXT(item);
ret = ioctl(conn->fd, KDBUS_CMD_MSG_SEND, msg);
if (ret) {
@@ -194,7 +184,8 @@ int msg_send(const struct conn *conn,
return EXIT_FAILURE;
}
- close(memfd);
+ if (memfd >= 0)
+ close(memfd);
free(msg);
return 0;
@@ -210,9 +201,9 @@ char *msg_id(uint64_t id, char *buf)
return buf;
}
-void msg_dump(struct kdbus_msg *msg)
+void msg_dump(const struct conn *conn, const struct kdbus_msg *msg)
{
- struct kdbus_item *item = msg->items;
+ const struct kdbus_item *item = msg->items;
char buf[32];
printf("MESSAGE: %s (%llu bytes) flags=0x%llx, %s → %s, cookie=%llu, timeout=%llu\n",
@@ -228,14 +219,17 @@ void msg_dump(struct kdbus_msg *msg)
}
switch (item->type) {
- case KDBUS_MSG_PAYLOAD_VEC: {
- char *s = (char *)KDBUS_PTR(item->vec.address);
+ case KDBUS_MSG_PAYLOAD_OFF: {
+ char *s;
- if (!s)
+ if (item->vec.offset == ~0ULL)
s = "[padding bytes]";
+ else
+ s = (char *)conn->buf + item->vec.offset;
- printf(" +%s (%llu bytes) addr=%p size=%llu '%s'\n",
- enum_MSG(item->type), item->size, KDBUS_PTR(item->vec.address),
+ printf(" +%s (%llu bytes) off=%llu size=%llu '%s'\n",
+ enum_MSG(item->type), item->size,
+ (unsigned long long)item->vec.offset,
(unsigned long long)item->vec.size, s);
break;
}
@@ -282,7 +276,7 @@ void msg_dump(struct kdbus_msg *msg)
case KDBUS_MSG_SRC_CMDLINE:
case KDBUS_MSG_SRC_NAMES: {
size_t size = item->size - KDBUS_PART_HEADER_SIZE;
- char *str = item->str;
+ const char *str = item->str;
int count = 0;
printf(" +%s (%llu bytes) ", enum_MSG(item->type), item->size);
@@ -306,7 +300,7 @@ void msg_dump(struct kdbus_msg *msg)
case KDBUS_MSG_SRC_CAPS: {
int n;
- uint32_t *cap;
+ const uint32_t *cap;
int i;
printf(" +%s (%llu bytes) len=%llu bytes)\n",
@@ -378,20 +372,20 @@ void msg_dump(struct kdbus_msg *msg)
int msg_recv(struct conn *conn)
{
- uint64_t addr;
+ uint64_t off;
struct kdbus_msg *msg;
int ret;
- ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &addr);
+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &off);
if (ret < 0) {
fprintf(stderr, "error receiving message: %d (%m)\n", ret);
return EXIT_FAILURE;
}
- msg = KDBUS_PTR(addr);
- msg_dump(msg);
+ msg = (struct kdbus_msg *)(conn->buf + off);
+ msg_dump(conn, msg);
- ret = ioctl(conn->fd, KDBUS_CMD_MSG_RELEASE, msg);
+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_RELEASE, &off);
if (ret < 0) {
fprintf(stderr, "error free message: %d (%m)\n", ret);
return EXIT_FAILURE;
diff --git a/test/kdbus-util.h b/test/kdbus-util.h
index 3f65154b5c9..ead5b2895d6 100644
--- a/test/kdbus-util.h
+++ b/test/kdbus-util.h
@@ -30,13 +30,15 @@
struct conn {
int fd;
uint64_t id;
+ void *buf;
+ size_t size;
};
int name_list(struct conn *conn);
int name_release(struct conn *conn, const char *name);
int name_acquire(struct conn *conn, const char *name, uint64_t flags);
int msg_recv(struct conn *conn);
-void msg_dump(struct kdbus_msg *msg);
+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 dst_id);
struct conn *connect_to_bus(const char *path);
diff --git a/test/test-kdbus.c b/test/test-kdbus.c
index e1055a5f1d8..7485d4cec02 100644
--- a/test/test-kdbus.c
+++ b/test/test-kdbus.c
@@ -126,6 +126,7 @@ int main(int argc, char *argv[])
msg_recv(conn_a);
msg_send(conn_a, NULL, 0xc0000000 | cookie++, conn_b->id);
}
+
if (fds[1].revents & POLLIN) {
msg_recv(conn_b);
msg_send(conn_b, NULL, 0xc0000000 | cookie++, conn_a->id);