From: David Herrmann <dh.herrmann@gmail.com>
To: linux-kernel@vger.kernel.org
Cc: Andy Lutomirski <luto@amacapital.net>,
Jiri Kosina <jikos@kernel.org>, Greg KH <greg@kroah.com>,
Hannes Reinecke <hare@suse.com>,
Steven Rostedt <rostedt@goodmis.org>,
Arnd Bergmann <arnd@arndb.de>, Tom Gundersen <teg@jklm.no>,
David Herrmann <dh.herrmann@gmail.com>,
Josh Triplett <josh@joshtriplett.org>,
Linus Torvalds <torvalds@linux-foundation.org>,
Andrew Morton <akpm@linux-foundation.org>
Subject: [RFC v1 12/14] bus1: hook up file-operations
Date: Wed, 26 Oct 2016 21:18:08 +0200 [thread overview]
Message-ID: <20161026191810.12275-13-dh.herrmann@gmail.com> (raw)
In-Reply-To: <20161026191810.12275-1-dh.herrmann@gmail.com>
From: Tom Gundersen <teg@jklm.no>
This hooks up all the file-operations on a bus1-file-descriptor. It
implements the ioctls as defined in the UAPI, as well as mmap() and
poll() support.
Signed-off-by: Tom Gundersen <teg@jklm.no>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
ipc/bus1/main.c | 46 +++
ipc/bus1/peer.c | 934 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
ipc/bus1/peer.h | 8 +
3 files changed, 987 insertions(+), 1 deletion(-)
diff --git a/ipc/bus1/main.c b/ipc/bus1/main.c
index 51034f3..d5a726a 100644
--- a/ipc/bus1/main.c
+++ b/ipc/bus1/main.c
@@ -14,10 +14,17 @@
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/seq_file.h>
+#include <linux/uio.h>
+#include <uapi/linux/bus1.h>
#include "main.h"
#include "peer.h"
#include "tests.h"
#include "user.h"
+#include "util/active.h"
+#include "util/pool.h"
+#include "util/queue.h"
static int bus1_fop_open(struct inode *inode, struct file *file)
{
@@ -37,6 +44,41 @@ static int bus1_fop_release(struct inode *inode, struct file *file)
return 0;
}
+static unsigned int bus1_fop_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct bus1_peer *peer = file->private_data;
+ unsigned int mask;
+
+ poll_wait(file, &peer->waitq, wait);
+
+ /* access queue->front unlocked */
+ rcu_read_lock();
+ if (bus1_active_is_deactivated(&peer->active)) {
+ mask = POLLHUP;
+ } else {
+ mask = POLLOUT | POLLWRNORM;
+ if (bus1_queue_is_readable_rcu(&peer->data.queue))
+ mask |= POLLIN | POLLRDNORM;
+ }
+ rcu_read_unlock();
+
+ return mask;
+}
+
+static int bus1_fop_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct bus1_peer *peer = file->private_data;
+ int r;
+
+ if (!bus1_peer_acquire(peer))
+ return -ESHUTDOWN;
+
+ r = bus1_pool_mmap(&peer->data.pool, vma);
+ bus1_peer_release(peer);
+ return r;
+}
+
static void bus1_fop_show_fdinfo(struct seq_file *m, struct file *file)
{
struct bus1_peer *peer = file->private_data;
@@ -48,7 +90,11 @@ const struct file_operations bus1_fops = {
.owner = THIS_MODULE,
.open = bus1_fop_open,
.release = bus1_fop_release,
+ .poll = bus1_fop_poll,
.llseek = noop_llseek,
+ .mmap = bus1_fop_mmap,
+ .unlocked_ioctl = bus1_peer_ioctl,
+ .compat_ioctl = bus1_peer_ioctl,
.show_fdinfo = bus1_fop_show_fdinfo,
};
diff --git a/ipc/bus1/peer.c b/ipc/bus1/peer.c
index 0ff7a98..f0da4a7 100644
--- a/ipc/bus1/peer.c
+++ b/ipc/bus1/peer.c
@@ -23,11 +23,52 @@
#include <linux/uaccess.h>
#include <linux/uio.h>
#include <linux/wait.h>
+#include <uapi/linux/bus1.h>
+#include "handle.h"
#include "main.h"
+#include "message.h"
#include "peer.h"
+#include "tx.h"
#include "user.h"
#include "util.h"
#include "util/active.h"
+#include "util/pool.h"
+#include "util/queue.h"
+
+static struct bus1_queue_node *
+bus1_peer_free_qnode(struct bus1_queue_node *qnode)
+{
+ struct bus1_message *m;
+ struct bus1_handle *h;
+
+ /*
+ * Queue-nodes are generic entities that can only be destroyed by who
+ * created them. That is, they have no embedded release callback.
+ * Instead, we must detect them by type. Since the queue logic is kept
+ * generic, it cannot provide this helper. Instead, we have this small
+ * destructor here, which simply dispatches to the correct handler.
+ */
+
+ if (qnode) {
+ switch (bus1_queue_node_get_type(qnode)) {
+ case BUS1_MSG_DATA:
+ m = container_of(qnode, struct bus1_message, qnode);
+ bus1_message_unref(m);
+ break;
+ case BUS1_MSG_NODE_DESTROY:
+ case BUS1_MSG_NODE_RELEASE:
+ h = container_of(qnode, struct bus1_handle, qnode);
+ bus1_handle_unref(h);
+ break;
+ case BUS1_MSG_NONE:
+ default:
+ WARN(1, "Unknown message type\n");
+ break;
+ }
+ }
+
+ return NULL;
+}
/**
* bus1_peer_new() - allocate new peer
@@ -47,6 +88,7 @@ struct bus1_peer *bus1_peer_new(void)
const struct cred *cred = current_cred();
struct bus1_peer *peer;
struct bus1_user *user;
+ int r;
user = bus1_user_ref_by_uid(cred->uid);
if (IS_ERR(user))
@@ -75,9 +117,14 @@ struct bus1_peer *bus1_peer_new(void)
/* initialize peer-private section */
mutex_init(&peer->local.lock);
+ peer->local.seed = NULL;
peer->local.map_handles = RB_ROOT;
peer->local.handle_ids = 0;
+ r = bus1_pool_init(&peer->data.pool, KBUILD_MODNAME "-peer");
+ if (r < 0)
+ goto error;
+
if (!IS_ERR_OR_NULL(bus1_debugdir)) {
char idstr[22];
@@ -96,6 +143,103 @@ struct bus1_peer *bus1_peer_new(void)
bus1_active_activate(&peer->active);
return peer;
+
+error:
+ bus1_peer_free(peer);
+ return ERR_PTR(r);
+}
+
+static void bus1_peer_flush(struct bus1_peer *peer, u64 flags)
+{
+ struct bus1_queue_node *qlist, *qnode;
+ struct bus1_handle *h, *safe;
+ struct bus1_tx tx;
+ size_t n_slices;
+ u64 ts;
+ int n;
+
+ lockdep_assert_held(&peer->local.lock);
+
+ bus1_tx_init(&tx, peer);
+
+ if (flags & BUS1_PEER_RESET_FLAG_FLUSH) {
+ /* protect handles on the seed */
+ if (!(flags & BUS1_PEER_RESET_FLAG_FLUSH_SEED) &&
+ peer->local.seed) {
+ /*
+ * XXX: When the flush operation does not ask for a
+ * RESET of the seed, we want to protect the nodes
+ * that were instantiated with this seed.
+ * Right now, we do not support this, but rather
+ * treat all nodes as local nodes. If node
+ * injection will be supported one day, we should
+ * make sure to drop n_user of all seed-handles to
+ * 0 here, to make sure they're skipped in the
+ * mass-destruction below.
+ */
+ }
+
+ /* first destroy all live anchors */
+ mutex_lock(&peer->data.lock);
+ rbtree_postorder_for_each_entry_safe(h, safe,
+ &peer->local.map_handles,
+ rb_to_peer) {
+ if (!bus1_handle_is_anchor(h) ||
+ !bus1_handle_is_live(h))
+ continue;
+
+ bus1_handle_destroy_locked(h, &tx);
+ }
+ mutex_unlock(&peer->data.lock);
+
+ /* atomically commit the destruction transaction */
+ ts = bus1_tx_commit(&tx);
+
+ /* now release all user handles */
+ rbtree_postorder_for_each_entry_safe(h, safe,
+ &peer->local.map_handles,
+ rb_to_peer) {
+ n = atomic_xchg(&h->n_user, 0);
+ bus1_handle_forget_keep(h);
+
+ if (bus1_handle_is_anchor(h)) {
+ if (n > 1)
+ bus1_handle_release_n(h, n - 1, true);
+ bus1_handle_release(h, false);
+ } else {
+ bus1_handle_release_n(h, n, true);
+ }
+ }
+ peer->local.map_handles = RB_ROOT;
+
+ /* finally flush the queue and pool */
+ mutex_lock(&peer->data.lock);
+ qlist = bus1_queue_flush(&peer->data.queue, ts);
+ bus1_pool_flush(&peer->data.pool, &n_slices);
+ mutex_unlock(&peer->data.lock);
+
+ while ((qnode = qlist)) {
+ qlist = qnode->next;
+ qnode->next = NULL;
+ bus1_peer_free_qnode(qnode);
+ }
+ }
+
+ /* drop seed if requested */
+ if (flags & BUS1_PEER_RESET_FLAG_FLUSH_SEED)
+ peer->local.seed = bus1_message_unref(peer->local.seed);
+
+ bus1_tx_deinit(&tx);
+}
+
+static void bus1_peer_cleanup(struct bus1_active *a, void *userdata)
+{
+ struct bus1_peer *peer = container_of(a, struct bus1_peer, active);
+
+ mutex_lock(&peer->local.lock);
+ bus1_peer_flush(peer, BUS1_PEER_RESET_FLAG_FLUSH |
+ BUS1_PEER_RESET_FLAG_FLUSH_SEED);
+ mutex_unlock(&peer->local.lock);
}
static int bus1_peer_disconnect(struct bus1_peer *peer)
@@ -104,7 +248,7 @@ static int bus1_peer_disconnect(struct bus1_peer *peer)
bus1_active_drain(&peer->active, &peer->waitq);
if (!bus1_active_cleanup(&peer->active, &peer->waitq,
- NULL, NULL))
+ bus1_peer_cleanup, NULL))
return -ESHUTDOWN;
return 0;
@@ -133,6 +277,7 @@ struct bus1_peer *bus1_peer_free(struct bus1_peer *peer)
/* deinitialize peer-private section */
WARN_ON(!RB_EMPTY_ROOT(&peer->local.map_handles));
+ WARN_ON(peer->local.seed);
mutex_destroy(&peer->local.lock);
/* deinitialize data section */
@@ -150,3 +295,790 @@ struct bus1_peer *bus1_peer_free(struct bus1_peer *peer)
return NULL;
}
+
+static int bus1_peer_ioctl_peer_query(struct bus1_peer *peer,
+ unsigned long arg)
+{
+ struct bus1_cmd_peer_reset __user *uparam = (void __user *)arg;
+ struct bus1_cmd_peer_reset param;
+
+ BUILD_BUG_ON(_IOC_SIZE(BUS1_CMD_PEER_QUERY) != sizeof(param));
+
+ if (copy_from_user(¶m, uparam, sizeof(param)))
+ return -EFAULT;
+ if (unlikely(param.flags))
+ return -EINVAL;
+
+ mutex_lock(&peer->local.lock);
+ param.peer_flags = peer->flags & BUS1_PEER_FLAG_WANT_SECCTX;
+ param.max_slices = -1;
+ param.max_handles = -1;
+ param.max_inflight_bytes = -1;
+ param.max_inflight_fds = -1;
+ mutex_unlock(&peer->local.lock);
+
+ return copy_to_user(uparam, ¶m, sizeof(param)) ? -EFAULT : 0;
+}
+
+static int bus1_peer_ioctl_peer_reset(struct bus1_peer *peer,
+ unsigned long arg)
+{
+ struct bus1_cmd_peer_reset __user *uparam = (void __user *)arg;
+ struct bus1_cmd_peer_reset param;
+
+ BUILD_BUG_ON(_IOC_SIZE(BUS1_CMD_PEER_RESET) != sizeof(param));
+
+ if (copy_from_user(¶m, uparam, sizeof(param)))
+ return -EFAULT;
+ if (unlikely(param.flags & ~(BUS1_PEER_RESET_FLAG_FLUSH |
+ BUS1_PEER_RESET_FLAG_FLUSH_SEED)))
+ return -EINVAL;
+ if (unlikely(param.peer_flags != -1 &&
+ (param.peer_flags & ~BUS1_PEER_FLAG_WANT_SECCTX)))
+ return -EINVAL;
+ if (unlikely(param.max_slices != -1 ||
+ param.max_handles != -1 ||
+ param.max_inflight_bytes != -1 ||
+ param.max_inflight_fds != -1))
+ return -EINVAL;
+
+ mutex_lock(&peer->local.lock);
+
+ if (param.peer_flags != -1)
+ peer->flags = param.peer_flags;
+
+ bus1_peer_flush(peer, param.flags);
+
+ mutex_unlock(&peer->local.lock);
+
+ return 0;
+}
+
+static int bus1_peer_ioctl_handle_release(struct bus1_peer *peer,
+ unsigned long arg)
+{
+ struct bus1_handle *h = NULL;
+ bool is_new, strong = true;
+ u64 id;
+ int r;
+
+ BUILD_BUG_ON(_IOC_SIZE(BUS1_CMD_HANDLE_RELEASE) != sizeof(id));
+
+ if (get_user(id, (const u64 __user *)arg))
+ return -EFAULT;
+
+ mutex_lock(&peer->local.lock);
+
+ h = bus1_handle_import(peer, id, &is_new);
+ if (IS_ERR(h)) {
+ r = PTR_ERR(h);
+ goto exit;
+ }
+
+ if (is_new) {
+ /*
+ * A handle is non-public only if the import lazily created the
+ * node. In that case the node is live and the last reference
+ * cannot be dropped until the node is destroyed. Hence, we
+ * return EBUSY.
+ *
+ * Since we did not modify the node, and the node was lazily
+ * created, there is no point in keeping the node allocated. We
+ * simply pretend we didn't allocate it so the next operation
+ * will just do the lazy allocation again.
+ */
+ bus1_handle_forget(h);
+ r = -EBUSY;
+ goto exit;
+ }
+
+ if (atomic_read(&h->n_user) == 1 && bus1_handle_is_anchor(h)) {
+ if (bus1_handle_is_live(h)) {
+ r = -EBUSY;
+ goto exit;
+ }
+
+ strong = false;
+ }
+
+ WARN_ON(atomic_dec_return(&h->n_user) < 0);
+ bus1_handle_forget(h);
+ bus1_handle_release(h, strong);
+
+ r = 0;
+
+exit:
+ mutex_unlock(&peer->local.lock);
+ bus1_handle_unref(h);
+ return r;
+}
+
+static int bus1_peer_transfer(struct bus1_peer *src,
+ struct bus1_peer *dst,
+ struct bus1_cmd_handle_transfer *param)
+{
+ struct bus1_handle *src_h = NULL, *dst_h = NULL;
+ bool is_new;
+ int r;
+
+ bus1_mutex_lock2(&src->local.lock, &dst->local.lock);
+
+ src_h = bus1_handle_import(src, param->src_handle, &is_new);
+ if (IS_ERR(src_h)) {
+ r = PTR_ERR(src_h);
+ src_h = NULL;
+ goto exit;
+ }
+
+ if (!bus1_handle_is_live(src_h)) {
+ /*
+ * If @src_h has a destruction queued, we cannot guarantee that
+ * we can join the transaction. Hence, we bail out and tell the
+ * caller that the node is already destroyed.
+ *
+ * In case @src_h->anchor is on one of the peers involved, this
+ * is properly synchronized. However, if it is a 3rd party node
+ * then it might not be committed, yet.
+ *
+ * XXX: We really ought to settle on the destruction. This
+ * requires some waitq to settle on, though.
+ */
+ param->dst_handle = BUS1_HANDLE_INVALID;
+ r = 0;
+ goto exit;
+ }
+
+ dst_h = bus1_handle_ref_by_other(dst, src_h);
+ if (!dst_h) {
+ dst_h = bus1_handle_new_remote(dst, src_h);
+ if (IS_ERR(dst_h)) {
+ r = PTR_ERR(dst_h);
+ dst_h = NULL;
+ goto exit;
+ }
+ }
+
+ if (is_new) {
+ WARN_ON(src_h != bus1_handle_acquire(src_h, false));
+ WARN_ON(atomic_inc_return(&src_h->n_user) != 1);
+ }
+
+ dst_h = bus1_handle_acquire(dst_h, true);
+ param->dst_handle = bus1_handle_identify(dst_h);
+ bus1_handle_export(dst_h);
+ WARN_ON(atomic_inc_return(&dst_h->n_user) < 1);
+
+ r = 0;
+
+exit:
+ bus1_handle_forget(src_h);
+ bus1_mutex_unlock2(&src->local.lock, &dst->local.lock);
+ bus1_handle_unref(dst_h);
+ bus1_handle_unref(src_h);
+ return r;
+}
+
+static int bus1_peer_ioctl_handle_transfer(struct bus1_peer *src,
+ unsigned long arg)
+{
+ struct bus1_cmd_handle_transfer __user *uparam = (void __user *)arg;
+ struct bus1_cmd_handle_transfer param;
+ struct bus1_peer *dst = NULL;
+ struct fd dst_f;
+ int r;
+
+ BUILD_BUG_ON(_IOC_SIZE(BUS1_CMD_HANDLE_TRANSFER) != sizeof(param));
+
+ if (copy_from_user(¶m, (void __user *)arg, sizeof(param)))
+ return -EFAULT;
+ if (unlikely(param.flags))
+ return -EINVAL;
+
+ if (param.dst_fd != -1) {
+ dst_f = fdget(param.dst_fd);
+ if (!dst_f.file)
+ return -EBADF;
+ if (dst_f.file->f_op != &bus1_fops) {
+ fdput(dst_f);
+ return -EOPNOTSUPP;
+ }
+
+ dst = bus1_peer_acquire(dst_f.file->private_data);
+ fdput(dst_f);
+ if (!dst)
+ return -ESHUTDOWN;
+ }
+
+ r = bus1_peer_transfer(src, dst ?: src, ¶m);
+ bus1_peer_release(dst);
+ if (r < 0)
+ return r;
+
+ return copy_to_user(uparam, ¶m, sizeof(param)) ? -EFAULT : 0;
+}
+
+static int bus1_peer_ioctl_nodes_destroy(struct bus1_peer *peer,
+ unsigned long arg)
+{
+ struct bus1_cmd_nodes_destroy param;
+ size_t n_charge = 0, n_discharge = 0;
+ struct bus1_handle *h, *list = BUS1_TAIL;
+ const u64 __user *ptr_nodes;
+ struct bus1_tx tx;
+ bool is_new;
+ u64 i, id;
+ int r;
+
+ BUILD_BUG_ON(_IOC_SIZE(BUS1_CMD_NODES_DESTROY) != sizeof(param));
+
+ if (copy_from_user(¶m, (void __user *)arg, sizeof(param)))
+ return -EFAULT;
+ if (unlikely(param.flags & ~BUS1_NODES_DESTROY_FLAG_RELEASE_HANDLES))
+ return -EINVAL;
+ if (unlikely(param.ptr_nodes != (u64)(unsigned long)param.ptr_nodes))
+ return -EFAULT;
+
+ mutex_lock(&peer->local.lock);
+
+ bus1_tx_init(&tx, peer);
+ ptr_nodes = (const u64 __user *)(unsigned long)param.ptr_nodes;
+
+ for (i = 0; i < param.n_nodes; ++i) {
+ if (get_user(id, ptr_nodes + i)) {
+ r = -EFAULT;
+ goto exit;
+ }
+
+ h = bus1_handle_import(peer, id, &is_new);
+ if (IS_ERR(h)) {
+ r = PTR_ERR(h);
+ goto exit;
+ }
+
+ if (h->tlink) {
+ bus1_handle_unref(h);
+ r = -ENOTUNIQ;
+ goto exit;
+ }
+
+ h->tlink = list;
+ list = h;
+
+ if (!bus1_handle_is_anchor(h)) {
+ r = -EREMOTE;
+ goto exit;
+ }
+
+ if (!bus1_handle_is_live(h)) {
+ r = -ESTALE;
+ goto exit;
+ }
+
+ if (is_new)
+ ++n_charge;
+ }
+
+ /* nothing below this point can fail, anymore */
+
+ mutex_lock(&peer->data.lock);
+ for (h = list; h != BUS1_TAIL; h = h->tlink) {
+ if (!bus1_handle_is_public(h)) {
+ WARN_ON(h != bus1_handle_acquire_locked(h, false));
+ WARN_ON(atomic_inc_return(&h->n_user) != 1);
+ }
+
+ bus1_handle_destroy_locked(h, &tx);
+ }
+ mutex_unlock(&peer->data.lock);
+
+ bus1_tx_commit(&tx);
+
+ while (list != BUS1_TAIL) {
+ h = list;
+ list = h->tlink;
+ h->tlink = NULL;
+
+ if (param.flags & BUS1_NODES_DESTROY_FLAG_RELEASE_HANDLES) {
+ ++n_discharge;
+ if (atomic_dec_return(&h->n_user) == 0) {
+ bus1_handle_forget(h);
+ bus1_handle_release(h, false);
+ } else {
+ bus1_handle_release(h, true);
+ }
+ }
+
+ bus1_handle_unref(h);
+ }
+
+ r = 0;
+
+exit:
+ while (list != BUS1_TAIL) {
+ h = list;
+ list = h->tlink;
+ h->tlink = NULL;
+
+ bus1_handle_forget(h);
+ bus1_handle_unref(h);
+ }
+ bus1_tx_deinit(&tx);
+ mutex_unlock(&peer->local.lock);
+ return r;
+}
+
+static int bus1_peer_ioctl_slice_release(struct bus1_peer *peer,
+ unsigned long arg)
+{
+ size_t n_slices = 0;
+ u64 offset;
+ int r;
+
+ BUILD_BUG_ON(_IOC_SIZE(BUS1_CMD_SLICE_RELEASE) != sizeof(offset));
+
+ if (get_user(offset, (const u64 __user *)arg))
+ return -EFAULT;
+
+ mutex_lock(&peer->data.lock);
+ r = bus1_pool_release_user(&peer->data.pool, offset, &n_slices);
+ mutex_unlock(&peer->data.lock);
+ return r;
+}
+
+static struct bus1_message *bus1_peer_new_message(struct bus1_peer *peer,
+ struct bus1_factory *f,
+ u64 id)
+{
+ struct bus1_message *m = NULL;
+ struct bus1_handle *h = NULL;
+ struct bus1_peer *p = NULL;
+ bool is_new;
+ int r;
+
+ h = bus1_handle_import(peer, id, &is_new);
+ if (IS_ERR(h))
+ return ERR_CAST(h);
+
+ if (h->tlink) {
+ r = -ENOTUNIQ;
+ goto error;
+ }
+
+ if (bus1_handle_is_anchor(h))
+ p = bus1_peer_acquire(peer);
+ else
+ p = bus1_handle_acquire_owner(h);
+ if (!p) {
+ r = -ESHUTDOWN;
+ goto error;
+ }
+
+ m = bus1_factory_instantiate(f, h, p);
+ if (IS_ERR(m)) {
+ r = PTR_ERR(m);
+ goto error;
+ }
+
+ /* marker to detect duplicates */
+ h->tlink = BUS1_TAIL;
+
+ /* m->dst pins the handle for us */
+ bus1_handle_unref(h);
+
+ /* merge charge into factory (which shares the lookup with us) */
+ if (is_new)
+ ++f->n_handles_charge;
+
+ return m;
+
+error:
+ bus1_peer_release(p);
+ if (is_new)
+ bus1_handle_forget(h);
+ bus1_handle_unref(h);
+ return ERR_PTR(r);
+}
+
+static int bus1_peer_ioctl_send(struct bus1_peer *peer,
+ unsigned long arg)
+{
+ struct bus1_queue_node *mlist = NULL;
+ struct bus1_factory *factory = NULL;
+ const u64 __user *ptr_destinations;
+ struct bus1_cmd_send param;
+ struct bus1_message *m;
+ struct bus1_peer *p;
+ size_t i, n_charge = 0;
+ struct bus1_tx tx;
+ u8 stack[512];
+ u64 id;
+ int r;
+
+ BUILD_BUG_ON(_IOC_SIZE(BUS1_CMD_SEND) != sizeof(param));
+
+ if (copy_from_user(¶m, (void __user *)arg, sizeof(param)))
+ return -EFAULT;
+ if (unlikely(param.flags & ~(BUS1_SEND_FLAG_CONTINUE |
+ BUS1_SEND_FLAG_SEED)))
+ return -EINVAL;
+
+ /* check basic limits; avoids integer-overflows later on */
+ if (unlikely(param.n_destinations > INT_MAX) ||
+ unlikely(param.n_vecs > UIO_MAXIOV) ||
+ unlikely(param.n_fds > BUS1_FD_MAX))
+ return -EMSGSIZE;
+
+ /* 32bit pointer validity checks */
+ if (unlikely(param.ptr_destinations !=
+ (u64)(unsigned long)param.ptr_destinations) ||
+ unlikely(param.ptr_errors !=
+ (u64)(unsigned long)param.ptr_errors) ||
+ unlikely(param.ptr_vecs !=
+ (u64)(unsigned long)param.ptr_vecs) ||
+ unlikely(param.ptr_handles !=
+ (u64)(unsigned long)param.ptr_handles) ||
+ unlikely(param.ptr_fds !=
+ (u64)(unsigned long)param.ptr_fds))
+ return -EFAULT;
+
+ mutex_lock(&peer->local.lock);
+
+ bus1_tx_init(&tx, peer);
+ ptr_destinations =
+ (const u64 __user *)(unsigned long)param.ptr_destinations;
+
+ factory = bus1_factory_new(peer, ¶m, stack, sizeof(stack));
+ if (IS_ERR(factory)) {
+ r = PTR_ERR(factory);
+ factory = NULL;
+ goto exit;
+ }
+
+ if (param.flags & BUS1_SEND_FLAG_SEED) {
+ if (unlikely((param.flags & BUS1_SEND_FLAG_CONTINUE) ||
+ param.n_destinations)) {
+ r = -EINVAL;
+ goto exit;
+ }
+
+ /* XXX: set seed */
+ r = -ENOTSUPP;
+ goto exit;
+ } else {
+ for (i = 0; i < param.n_destinations; ++i) {
+ if (get_user(id, ptr_destinations + i)) {
+ r = -EFAULT;
+ goto exit;
+ }
+
+ m = bus1_peer_new_message(peer, factory, id);
+ if (IS_ERR(m)) {
+ r = PTR_ERR(m);
+ goto exit;
+ }
+
+ if (!bus1_handle_is_public(m->dst))
+ ++n_charge;
+
+ m->qnode.next = mlist;
+ mlist = &m->qnode;
+ }
+
+ r = bus1_factory_seal(factory);
+ if (r < 0)
+ goto exit;
+
+ /*
+ * Now everything is prepared, charged, and pinned. Iterate
+ * each message, acquire references, and stage the message.
+ * From here on, we must not error out, anymore.
+ */
+
+ while (mlist) {
+ m = container_of(mlist, struct bus1_message, qnode);
+ mlist = m->qnode.next;
+ m->qnode.next = NULL;
+
+ if (!bus1_handle_is_public(m->dst)) {
+ --factory->n_handles_charge;
+ WARN_ON(m->dst != bus1_handle_acquire(m->dst,
+ false));
+ WARN_ON(atomic_inc_return(&m->dst->n_user)
+ != 1);
+ }
+
+ m->dst->tlink = NULL;
+
+ /* this consumes @m and @m->qnode.owner */
+ bus1_message_stage(m, &tx);
+ }
+
+ WARN_ON(factory->n_handles_charge != 0);
+ bus1_tx_commit(&tx);
+ }
+
+ r = 0;
+
+exit:
+ while (mlist) {
+ m = container_of(mlist, struct bus1_message, qnode);
+ mlist = m->qnode.next;
+ m->qnode.next = NULL;
+
+ p = m->qnode.owner;
+ m->dst->tlink = NULL;
+
+ bus1_handle_forget(m->dst);
+ bus1_message_unref(m);
+ bus1_peer_release(p);
+ }
+ bus1_factory_free(factory);
+ bus1_tx_deinit(&tx);
+ mutex_unlock(&peer->local.lock);
+ return r;
+}
+
+static struct bus1_queue_node *bus1_peer_peek(struct bus1_peer *peer,
+ struct bus1_cmd_recv *param,
+ bool *morep)
+{
+ struct bus1_queue_node *qnode;
+ struct bus1_message *m;
+ struct bus1_handle *h;
+ u64 ts;
+
+ lockdep_assert_held(&peer->local.lock);
+
+ if (unlikely(param->flags & BUS1_RECV_FLAG_SEED)) {
+ if (!peer->local.seed)
+ return ERR_PTR(-EAGAIN);
+
+ *morep = false;
+ return &peer->local.seed->qnode;
+ }
+
+ mutex_lock(&peer->data.lock);
+ while ((qnode = bus1_queue_peek(&peer->data.queue, morep))) {
+ switch (bus1_queue_node_get_type(qnode)) {
+ case BUS1_MSG_DATA:
+ m = container_of(qnode, struct bus1_message, qnode);
+ h = m->dst;
+ break;
+ case BUS1_MSG_NODE_DESTROY:
+ case BUS1_MSG_NODE_RELEASE:
+ m = NULL;
+ h = container_of(qnode, struct bus1_handle, qnode);
+ break;
+ case BUS1_MSG_NONE:
+ default:
+ mutex_unlock(&peer->data.lock);
+ WARN(1, "Unknown message type\n");
+ return ERR_PTR(-ENOTRECOVERABLE);
+ }
+
+ ts = bus1_queue_node_get_timestamp(qnode);
+ if (ts <= peer->data.queue.flush ||
+ !bus1_handle_is_public(h) ||
+ !bus1_handle_is_live_at(h, ts)) {
+ bus1_queue_remove(&peer->data.queue, &peer->waitq,
+ qnode);
+ if (m) {
+ mutex_unlock(&peer->data.lock);
+ bus1_message_unref(m);
+ mutex_lock(&peer->data.lock);
+ } else {
+ bus1_handle_unref(h);
+ }
+
+ continue;
+ }
+
+ if (!m && !(param->flags & BUS1_RECV_FLAG_PEEK))
+ bus1_queue_remove(&peer->data.queue, &peer->waitq,
+ qnode);
+
+ break;
+ }
+ mutex_unlock(&peer->data.lock);
+
+ return qnode ?: ERR_PTR(-EAGAIN);
+}
+
+static int bus1_peer_ioctl_recv(struct bus1_peer *peer,
+ unsigned long arg)
+{
+ struct bus1_queue_node *qnode = NULL;
+ struct bus1_cmd_recv param;
+ struct bus1_message *m;
+ struct bus1_handle *h;
+ unsigned int type;
+ bool more = false;
+ int r;
+
+ BUILD_BUG_ON(_IOC_SIZE(BUS1_CMD_RECV) != sizeof(param));
+
+ if (copy_from_user(¶m, (void __user *)arg, sizeof(param)))
+ return -EFAULT;
+ if (unlikely(param.flags & ~(BUS1_RECV_FLAG_PEEK |
+ BUS1_RECV_FLAG_SEED |
+ BUS1_RECV_FLAG_INSTALL_FDS)))
+ return -EINVAL;
+
+ mutex_lock(&peer->local.lock);
+
+ qnode = bus1_peer_peek(peer, ¶m, &more);
+ if (IS_ERR(qnode)) {
+ r = PTR_ERR(qnode);
+ goto exit;
+ }
+
+ type = bus1_queue_node_get_type(qnode);
+ switch (type) {
+ case BUS1_MSG_DATA:
+ m = container_of(qnode, struct bus1_message, qnode);
+ WARN_ON(m->dst->id == BUS1_HANDLE_INVALID);
+
+ if (param.max_offset < m->slice->offset + m->slice->size) {
+ r = -ERANGE;
+ goto exit;
+ }
+
+ r = bus1_message_install(m, ¶m);
+ if (r < 0)
+ goto exit;
+
+ param.msg.type = BUS1_MSG_DATA;
+ param.msg.flags = m->flags;
+ param.msg.destination = m->dst->id;
+ param.msg.uid = m->uid;
+ param.msg.gid = m->gid;
+ param.msg.pid = m->pid;
+ param.msg.tid = m->tid;
+ param.msg.offset = m->slice->offset;
+ param.msg.n_bytes = m->n_bytes;
+ param.msg.n_handles = m->n_handles;
+ param.msg.n_fds = m->n_files;
+ param.msg.n_secctx = m->n_secctx;
+
+ if (likely(!(param.flags & BUS1_RECV_FLAG_PEEK))) {
+ if (unlikely(param.flags & BUS1_RECV_FLAG_SEED)) {
+ peer->local.seed = NULL;
+ } else {
+ mutex_lock(&peer->data.lock);
+ bus1_queue_remove(&peer->data.queue,
+ &peer->waitq, qnode);
+ mutex_unlock(&peer->data.lock);
+ }
+ bus1_message_unref(m);
+ }
+ break;
+ case BUS1_MSG_NODE_DESTROY:
+ case BUS1_MSG_NODE_RELEASE:
+ h = container_of(qnode, struct bus1_handle, qnode);
+ WARN_ON(h->id == BUS1_HANDLE_INVALID);
+
+ param.msg.type = type;
+ param.msg.flags = 0;
+ param.msg.destination = h->id;
+ param.msg.uid = -1;
+ param.msg.gid = -1;
+ param.msg.pid = 0;
+ param.msg.tid = 0;
+ param.msg.offset = BUS1_OFFSET_INVALID;
+ param.msg.n_bytes = 0;
+ param.msg.n_handles = 0;
+ param.msg.n_fds = 0;
+ param.msg.n_secctx = 0;
+
+ if (likely(!(param.flags & BUS1_RECV_FLAG_PEEK)))
+ bus1_handle_unref(h);
+ break;
+ case BUS1_MSG_NONE:
+ default:
+ WARN(1, "Unknown message type\n");
+ r = -ENOTRECOVERABLE;
+ goto exit;
+ }
+
+ if (more)
+ param.msg.flags |= BUS1_MSG_FLAG_CONTINUE;
+
+ if (copy_to_user((void __user *)arg, ¶m, sizeof(param)))
+ r = -EFAULT;
+ else
+ r = 0;
+
+exit:
+ mutex_unlock(&peer->local.lock);
+ return r;
+}
+
+/**
+ * bus1_peer_ioctl() - handle peer ioctls
+ * @file: file the ioctl is called on
+ * @cmd: ioctl command
+ * @arg: ioctl argument
+ *
+ * This handles the given ioctl (cmd+arg) on a peer. This expects the peer to
+ * be stored in the private_data field of @file.
+ *
+ * Multiple ioctls can be called in parallel just fine. No locking is needed.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+long bus1_peer_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct bus1_peer *peer = file->private_data;
+ int r;
+
+ /*
+ * First handle ioctls that do not require an active-reference, then
+ * all the remaining ones wrapped in an active reference.
+ */
+ switch (cmd) {
+ case BUS1_CMD_PEER_DISCONNECT:
+ if (unlikely(arg))
+ return -EINVAL;
+
+ r = bus1_peer_disconnect(peer);
+ break;
+ default:
+ if (!bus1_peer_acquire(peer))
+ return -ESHUTDOWN;
+
+ switch (cmd) {
+ case BUS1_CMD_PEER_QUERY:
+ r = bus1_peer_ioctl_peer_query(peer, arg);
+ break;
+ case BUS1_CMD_PEER_RESET:
+ r = bus1_peer_ioctl_peer_reset(peer, arg);
+ break;
+ case BUS1_CMD_HANDLE_RELEASE:
+ r = bus1_peer_ioctl_handle_release(peer, arg);
+ break;
+ case BUS1_CMD_HANDLE_TRANSFER:
+ r = bus1_peer_ioctl_handle_transfer(peer, arg);
+ break;
+ case BUS1_CMD_NODES_DESTROY:
+ r = bus1_peer_ioctl_nodes_destroy(peer, arg);
+ break;
+ case BUS1_CMD_SLICE_RELEASE:
+ r = bus1_peer_ioctl_slice_release(peer, arg);
+ break;
+ case BUS1_CMD_SEND:
+ r = bus1_peer_ioctl_send(peer, arg);
+ break;
+ case BUS1_CMD_RECV:
+ r = bus1_peer_ioctl_recv(peer, arg);
+ break;
+ default:
+ r = -ENOTTY;
+ break;
+ }
+
+ bus1_peer_release(peer);
+ break;
+ }
+
+ return r;
+}
diff --git a/ipc/bus1/peer.h b/ipc/bus1/peer.h
index 5eb558f..26c051f 100644
--- a/ipc/bus1/peer.h
+++ b/ipc/bus1/peer.h
@@ -52,11 +52,13 @@
#include <linux/rcupdate.h>
#include <linux/rbtree.h>
#include <linux/wait.h>
+#include <uapi/linux/bus1.h>
#include "user.h"
#include "util/active.h"
#include "util/pool.h"
#include "util/queue.h"
+struct bus1_message;
struct cred;
struct dentry;
struct pid_namespace;
@@ -73,8 +75,12 @@ struct pid_namespace;
* @active: active references
* @debugdir: debugfs root of this peer, or NULL/ERR_PTR
* @data.lock: data lock
+ * @data.pool: data pool
* @data.queue: message queue
* @local.lock: local peer runtime lock
+ * @local.seed: pinned seed message
+ * @local.map_handles: map of owned handles (by handle ID)
+ * @local.handle_ids: handle ID allocator
*/
struct bus1_peer {
u64 id;
@@ -95,6 +101,7 @@ struct bus1_peer {
struct {
struct mutex lock;
+ struct bus1_message *seed;
struct rb_root map_handles;
u64 handle_ids;
} local;
@@ -102,6 +109,7 @@ struct bus1_peer {
struct bus1_peer *bus1_peer_new(void);
struct bus1_peer *bus1_peer_free(struct bus1_peer *peer);
+long bus1_peer_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
/**
* bus1_peer_acquire() - acquire active reference to peer
--
2.10.1
next prev parent reply other threads:[~2016-10-26 19:22 UTC|newest]
Thread overview: 55+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-10-26 19:17 [RFC v1 00/14] Bus1 Kernel Message Bus David Herrmann
2016-10-26 19:17 ` [RFC v1 01/14] bus1: add bus1(7) man-page David Herrmann
2016-10-27 23:12 ` Kirill A. Shutemov
2016-10-26 19:17 ` [RFC v1 02/14] bus1: provide stub cdev /dev/bus1 David Herrmann
2016-10-26 23:19 ` Andy Lutomirski
2016-10-26 23:54 ` Tom Gundersen
2016-10-27 9:11 ` Arnd Bergmann
2016-10-27 15:25 ` Tom Gundersen
2016-10-27 16:37 ` Linus Torvalds
2016-10-27 16:39 ` Tom Gundersen
2016-10-29 22:13 ` Arnd Bergmann
2016-10-26 19:17 ` [RFC v1 03/14] bus1: util - active reference utility library David Herrmann
2016-10-26 19:18 ` [RFC v1 04/14] bus1: util - fixed list " David Herrmann
2016-10-27 12:37 ` Peter Zijlstra
2016-10-27 12:48 ` David Herrmann
2016-10-27 12:56 ` Arnd Bergmann
2016-10-27 13:31 ` David Herrmann
2016-10-26 19:18 ` [RFC v1 05/14] bus1: util - pool " David Herrmann
2016-10-27 12:54 ` Peter Zijlstra
2016-10-27 12:59 ` Peter Zijlstra
2016-10-27 15:00 ` Peter Zijlstra
2016-10-27 15:14 ` Peter Zijlstra
2016-10-26 19:18 ` [RFC v1 06/14] bus1: util - queue " David Herrmann
2016-10-27 15:27 ` Peter Zijlstra
2016-10-27 16:43 ` Peter Zijlstra
2016-10-28 11:33 ` Tom Gundersen
2016-10-28 13:33 ` Peter Zijlstra
2016-10-28 13:47 ` Tom Gundersen
2016-10-28 13:58 ` Peter Zijlstra
2016-10-28 14:33 ` Tom Gundersen
2016-10-28 16:49 ` Peter Zijlstra
2016-10-26 19:18 ` [RFC v1 07/14] bus1: tracking user contexts David Herrmann
2016-10-26 19:18 ` [RFC v1 08/14] bus1: implement peer management context David Herrmann
2016-10-28 12:06 ` Richard Weinberger
2016-10-28 13:18 ` Tom Gundersen
2016-10-28 13:21 ` Richard Weinberger
2016-10-28 13:05 ` Richard Weinberger
2016-10-28 13:23 ` Tom Gundersen
2016-10-28 13:54 ` Richard Weinberger
2016-10-26 19:18 ` [RFC v1 09/14] bus1: provide transaction context for multicasts David Herrmann
2016-10-28 14:37 ` Peter Zijlstra
2016-10-26 19:18 ` [RFC v1 10/14] bus1: add handle management David Herrmann
2016-10-26 19:18 ` [RFC v1 11/14] bus1: implement message transmission David Herrmann
2016-10-26 19:18 ` David Herrmann [this message]
2016-10-26 19:18 ` [RFC v1 13/14] bus1: limit and protect resources David Herrmann
2016-10-26 19:18 ` [RFC v1 14/14] bus1: basic user-space kselftests David Herrmann
2016-10-26 19:39 ` [RFC v1 00/14] Bus1 Kernel Message Bus Linus Torvalds
2016-10-26 20:34 ` David Herrmann
2016-10-27 0:45 ` Kirill A. Shutemov
2016-10-29 21:04 ` Josh Triplett
2016-11-02 14:45 ` David Herrmann
2017-01-30 22:11 ` Pavel Machek
2016-10-27 11:10 ` Michael Kerrisk
2016-10-28 13:11 ` Richard Weinberger
2016-10-28 13:37 ` Tom Gundersen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20161026191810.12275-13-dh.herrmann@gmail.com \
--to=dh.herrmann@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=arnd@arndb.de \
--cc=greg@kroah.com \
--cc=hare@suse.com \
--cc=jikos@kernel.org \
--cc=josh@joshtriplett.org \
--cc=linux-kernel@vger.kernel.org \
--cc=luto@amacapital.net \
--cc=rostedt@goodmis.org \
--cc=teg@jklm.no \
--cc=torvalds@linux-foundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.