aboutsummaryrefslogtreecommitdiffstats
path: root/0001-kdbus-interprocess-message-router.patch
diff options
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-01-09 15:55:36 -0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-01-09 15:55:36 -0800
commit4b9ed384417dfbeafbb16b37725fa640352677cb (patch)
treecc44943f0777104a6f8dc41800cc5030b7a27f7c /0001-kdbus-interprocess-message-router.patch
parentfa6afa80467bc6b4ff1b1a9cb8e644000b63a0e8 (diff)
downloadpatches-4b9ed384417dfbeafbb16b37725fa640352677cb.tar.gz
updates
Diffstat (limited to '0001-kdbus-interprocess-message-router.patch')
-rw-r--r--0001-kdbus-interprocess-message-router.patch1096
1 files changed, 1096 insertions, 0 deletions
diff --git a/0001-kdbus-interprocess-message-router.patch b/0001-kdbus-interprocess-message-router.patch
new file mode 100644
index 00000000000000..e671e082b6ab48
--- /dev/null
+++ b/0001-kdbus-interprocess-message-router.patch
@@ -0,0 +1,1096 @@
+From 8c41f3b1fb98a30664c73d61745b346d4013ced5 Mon Sep 17 00:00:00 2001
+From: Kay Sievers <kay@vrfy.org>
+Date: Sat, 22 Dec 2012 18:36:55 +0100
+Subject: [PATCH] kdbus: interprocess message router
+
+---
+ drivers/Kconfig | 2
+ drivers/Makefile | 1
+ drivers/kdbus/Kconfig | 5
+ drivers/kdbus/Makefile | 2
+ drivers/kdbus/kdbus.c | 916 +++++++++++++++++++++++++++++++++++++++++++++
+ include/uapi/kdbus/kdbus.h | 46 ++
+ include/uapi/linux/major.h | 2
+ kdbus.c | 64 +++
+ 8 files changed, 1038 insertions(+)
+ create mode 100644 drivers/kdbus/Kconfig
+ create mode 100644 drivers/kdbus/Makefile
+ create mode 100644 drivers/kdbus/kdbus.c
+ create mode 100644 include/uapi/kdbus/kdbus.h
+ create mode 100644 kdbus.c
+
+--- a/drivers/Kconfig
++++ b/drivers/Kconfig
+@@ -158,4 +158,6 @@ source "drivers/irqchip/Kconfig"
+
+ source "drivers/ipack/Kconfig"
+
++source "drivers/kdbus/Kconfig"
++
+ endmenu
+--- a/drivers/Makefile
++++ b/drivers/Makefile
+@@ -146,3 +146,4 @@ obj-$(CONFIG_MEMORY) += memory/
+ obj-$(CONFIG_IIO) += iio/
+ obj-$(CONFIG_VME_BUS) += vme/
+ obj-$(CONFIG_IPACK_BUS) += ipack/
++obj-$(CONFIG_KDBUS) += kdbus/
+--- /dev/null
++++ b/drivers/kdbus/Kconfig
+@@ -0,0 +1,5 @@
++config KDBUS
++ tristate "kdbus interprocess message router"
++ help
++ kdbus provides efficient kernel-aided message exchange and routing
++ between processes. It is used as the low-level transport of D-Bus.
+--- /dev/null
++++ b/drivers/kdbus/Makefile
+@@ -0,0 +1,2 @@
++obj-$(CONFIG_KDBUS) += kdbus.o
++
+--- /dev/null
++++ b/drivers/kdbus/kdbus.c
+@@ -0,0 +1,916 @@
++/*
++ * kdbus - interprocess message routing
++ *
++ * Copyright (C) 2013
++ *
++ * kdbus is free software; you can redistribute it and/or modify it under
++ * the terms of the GNU Lesser General Public License as published by the
++ * Free Software Foundation; either version 2.1 of the License, or (at
++ * your option) any later version.
++ *
++ */
++
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/idr.h>
++#include <linux/fs.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++#include <linux/cred.h>
++#include <linux/security.h>
++#include <asm/uaccess.h>
++#include <uapi/linux/major.h>
++#include <uapi/kdbus/kdbus.h>
++
++/*
++ * TODO:
++ * - set parent for driver-core /sys/devices/kdbus!... devices to virtual/kdbus/,
++ * the bus subsys misses the "no parent" logic the class subsys has
++ *
++ * - switch to a 64bit idr for connection id <--> kdbus_conn
++ */
++
++/*
++ * Example of device nodes in /dev. For any future changes, keep in mind,
++ * that the layout should support a possible /dev/kdbus/ filesystem for the
++ * init namspace and one for each sub-namespace.
++ *
++ * /dev/kdbus/
++ * |-- control
++ * |-- system
++ * | |-- bus
++ * | |-- ep-epiphany
++ * | `-- ep-firefox
++ * |-- 2702-user
++ * | `-- bus
++ * |-- 1000-user
++ * | `-- bus
++ * `-- ns
++ * |-- myfedoracontainer
++ * | |-- control
++ * | |-- system
++ * | | `-- bus
++ * | `-- 1000-user
++ * | `-- bus
++ * `-- mydebiancontainer
++ * |-- control
++ * |-- system
++ * `-- bus
++ */
++
++/*
++ * kdbus namespace
++ * - provides a "control" node
++ * - owns a major number
++ * - owns all created buses
++ * - the initial namespace is unnamed and stays around for forver
++ * - new namespaces are created by opening the control node and
++ * issuing KDBUS_NS_CREATE
++ * - closing the connection destroys the created namespace
++ */
++struct kdbus_ns {
++ unsigned int ref; /* reference count */
++ bool disconnected; /* invalidated data */
++ struct kdbus_ns *parent;/* parent namespace */
++ __u64 id; /* global id of this namespace */
++ const char *devpath; /* /dev base directory path */
++ int major; /* device major number for all nodes */
++ struct idr idr; /* map of endpoint minors to buses */
++ struct device *dev; /* control device node, minor == 0 */
++ struct mutex lock; /* ns data lock */
++ __u64 bus_id_next; /* next bus id sequence number */
++};
++
++/*
++ * kdbus bus
++ * - provides a "bus" endpoint
++ * - owns additional endpoints
++ * - own all bus connections
++ * - new buses are created by opening the control node and
++ * issuing KDBUS_BUS_CREATE
++ * - closing the connection destroys the created bus
++ */
++struct kdbus_bus {
++ unsigned int ref; /* reference count */
++ bool disconnected; /* invalidated data */
++ struct kdbus_ns *ns; /* namespace of this bus */
++ const char *name; /* bus name */
++ __u64 id; /* id of this bus in the namespace */
++ struct mutex lock; /* bus data lock */
++ __u64 ep_id_next; /* next endpoint id sequence number */
++ __u64 conn_id_next; /* next connection id sequence number */
++ __u64 msg_id_next; /* next message id sequence number */
++ struct idr conn_idr; /* map of connection ids */
++ struct kdbus_ep *ep; /* "bus" default endpoint */
++ struct list_head ep_list; /* endpoints assigned to this bus */
++};
++
++/*
++ * kdbus endpoint
++ * - offers access to a bus, the default device node name is "bus"
++ * - additional endpoints can carry a specific policy/filters
++ */
++struct kdbus_ep {
++ unsigned int ref; /* reference count */
++ bool disconnected; /* invalidated data */
++ struct kdbus_bus *bus; /* bus behind this endpoint */
++ const char *name; /* name, prefixed with uid */
++ __u64 id; /* id of this endpoint on the bus */
++ unsigned int minor; /* minor of this endpoint in the namespace major */
++ struct device *dev; /* device node of this endpoint */
++ umode_t mode; /* file mode of this endpoint device node */
++ uid_t uid; /* uid owning this endpoint */
++ gid_t gid; /* gid owning this endpoint */
++};
++
++/*
++ * kdbus connection
++ * - connection to a control node or an endpoint
++ */
++enum kdbus_conn_type {
++ KDBUS_CONN_UNDEFINED,
++ KDBUS_CONN_CONTROL,
++ KDBUS_CONN_NS_OWNER,
++ KDBUS_CONN_BUS_OWNER,
++ KDBUS_CONN_EP,
++};
++
++struct kdbus_conn {
++ enum kdbus_conn_type type;
++ struct kdbus_ns *ns;
++ union {
++ struct kdbus_ns *ns_owner;
++ struct kdbus_bus *bus_owner;
++ struct kdbus_ep *ep;
++ };
++ __u64 id; /* id of the connection on the bus */
++};
++
++/* kdbus message */
++enum kdbus_msg_data_type {
++ KDBUS_MSG_DATA_UNDEFINED,
++ KDBUS_MSG_DATA_MEM,
++};
++
++struct kdbus_msg_data {
++ u64 data;
++ u64 size;
++ u32 type;
++ u32 flags;
++};
++
++struct kdbus_msg {
++ u64 src_id;
++ u64 dst_id;
++ u64 flags;
++ uid_t src_uid;
++ gid_t src_gid;
++ pid_t src_pid;
++ pid_t src_tid;
++ u64 ts_nsec;
++ u64 id;
++ u64 reserved[8];
++ u32 data_count;
++ struct kdbus_msg_data *data;
++};
++
++static void kdbus_release(struct device *dev)
++{
++ kfree(dev);
++}
++
++/* kdbus sysfs subsystem */
++static struct bus_type kdbus_subsys = {
++ .name = "kdbus",
++};
++
++/* control nodes are world accessible */
++static char *kdbus_devnode_control(struct device *dev,
++ umode_t *mode, uid_t *uid, gid_t *gid)
++{
++ if (mode)
++ *mode = 0666;
++ return NULL;
++}
++
++static struct device_type kdbus_devtype_control = {
++ .name = "control",
++ .release = kdbus_release,
++ .devnode = kdbus_devnode_control,
++};
++
++/* endpoints are by default owned by the bus owner */
++static char *kdbus_devnode_ep(struct device *dev,
++ umode_t *mode, uid_t *uid, gid_t *gid)
++{
++ struct kdbus_ep *ep = dev_get_drvdata(dev);
++
++ if (mode)
++ *mode = ep->mode;
++ if (uid)
++ *uid = ep->uid;
++ if (gid)
++ *gid = ep->gid;
++ return NULL;
++}
++
++static struct device_type kdbus_devtype_ep = {
++ .name = "ep",
++ .release = kdbus_release,
++ .devnode = kdbus_devnode_ep,
++};
++
++/* kdbus initial namespace */
++static struct kdbus_ns *kdbus_ns_init;
++
++/* map of majors to namespaces */
++static DEFINE_IDR(kdbus_ns_major_idr);
++
++/* namespace list lock */
++static DEFINE_MUTEX(kdbus_subsys_lock);
++
++/* next namespace id sequence number */
++static __u64 kdbus_ns_id_next;
++
++extern const struct file_operations kdbus_device_ops;
++static void kdbus_ep_disconnect(struct kdbus_ep *ep);
++static struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep);
++static int kdbus_ep_new(struct kdbus_bus *bus, const char *name,
++ umode_t mode, uid_t uid, gid_t gid,
++ struct kdbus_ep **ep);
++static int kdbus_msg_new(struct kdbus_conn *conn, struct kdbus_msg __user *umsg,
++ struct kdbus_msg **msg);
++static int kdbus_msg_send(struct kdbus_conn *conn, struct kdbus_msg *msg);
++
++static struct kdbus_ep *endpoint_find(struct kdbus_bus *bus, const char *name);
++static int endpoint_remove(struct kdbus_ep *ep);
++
++/* kdbus namespace */
++static struct kdbus_ns *kdbus_ns_ref(struct kdbus_ns *ns)
++{
++ if (!ns)
++ return NULL;
++ ns->ref++;
++ return ns;
++}
++
++static void kdbus_ns_disconnect(struct kdbus_ns *ns)
++{
++ if (ns->disconnected)
++ return;
++ ns->disconnected = true;
++
++ if (ns->dev) {
++ device_unregister(ns->dev);
++ ns->dev = NULL;
++ }
++ if (ns->major > 0) {
++ idr_remove(&kdbus_ns_major_idr, ns->major);
++ unregister_chrdev(ns->major, "kdbus");
++ ns->major = 0;
++ }
++ pr_info("closing namespace %s\n", ns->devpath);
++}
++
++static struct kdbus_ns *kdbus_ns_unref(struct kdbus_ns *ns)
++{
++ if (!ns)
++ return NULL;
++ ns->ref--;
++ if (ns->ref > 0)
++ return ns;
++
++ kdbus_ns_disconnect(ns);
++ pr_info("clean up namespace %s\n", ns->devpath);
++ kfree(ns->devpath);
++ kfree(ns);
++ return NULL;
++}
++
++static int kdbus_ns_new(struct kdbus_ns *parent, const char *name,
++ struct kdbus_ns **ns)
++{
++ struct kdbus_ns *n;
++ int i;
++ int err;
++
++ if ((parent && !name) || (!parent && name))
++ return -EINVAL;
++
++ n = kzalloc(sizeof(struct kdbus_ns), GFP_KERNEL);
++ if (!n)
++ return -ENOMEM;
++
++ n->ref = 1;
++ idr_init(&n->idr);
++ mutex_init(&n->lock);
++
++ /* compose name and path of base directory in /dev */
++ if (!parent) {
++ /* initial namespace */
++ n->devpath = kstrdup("kdbus", GFP_KERNEL);
++ if (!n->devpath) {
++ err = -ENOMEM;
++ goto err;
++ }
++
++ /* register static major to support module auto-loading */
++ err = register_chrdev(KDBUS_CHAR_MAJOR, "kdbus", &kdbus_device_ops);
++ if (err)
++ goto err;
++ n->major = KDBUS_CHAR_MAJOR;
++ } else {
++ n->parent = parent;
++ n->devpath = kasprintf(GFP_KERNEL, "kdbus/ns/%s/%s", parent->devpath, name);
++ if (!n->devpath) {
++ err = -ENOMEM;
++ goto err;
++ }
++
++ /* get dynamic major */
++ n->major = register_chrdev(0, "kdbus", &kdbus_device_ops);
++ if (n->major < 0) {
++ err = n->major;
++ goto err;
++ }
++ }
++
++ /* register major in our namespace map */
++ mutex_lock(&kdbus_subsys_lock);
++ if (!idr_pre_get(&kdbus_ns_major_idr, GFP_KERNEL)) {
++ err = -ENOMEM;
++ goto err_unlock;
++ }
++ err = idr_get_new_above(&kdbus_ns_major_idr, n, n->major, &i);
++ if (err >= 0 && n->major != i) {
++ idr_remove(&kdbus_ns_major_idr, i);
++ err = -EEXIST;
++ goto err_unlock;
++ }
++
++ /* get id for this namespace */
++ n->id = kdbus_ns_id_next++;
++
++ /* register control device for this namespace */
++ n->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
++ if (!n->dev)
++ goto err_unlock;
++ dev_set_name(n->dev, "%s/%s", n->devpath, "control");
++ n->dev->bus = &kdbus_subsys;
++ n->dev->type = &kdbus_devtype_control;
++ n->dev->devt = MKDEV(n->major, 0);
++ dev_set_drvdata(n->dev, n);
++ err = device_register(n->dev);
++ if (err < 0) {
++ put_device(n->dev);
++ n->dev = NULL;
++ goto err_unlock;
++ }
++ mutex_unlock(&kdbus_subsys_lock);
++
++ *ns = n;
++ pr_info("created namespace %llu '%s/'\n",
++ (unsigned long long)n->id, n->devpath);
++ return 0;
++
++err_unlock:
++ mutex_unlock(&kdbus_subsys_lock);
++err:
++ kdbus_ns_unref(n);
++ return err;
++}
++
++/* kdbus bus */
++static struct kdbus_bus *kdbus_bus_ref(struct kdbus_bus *bus)
++{
++ if (!bus)
++ return NULL;
++ bus->ref++;
++ return bus;
++}
++
++static void kdbus_bus_disconnect(struct kdbus_bus *bus)
++{
++ if (bus->disconnected)
++ return;
++ bus->disconnected = true;
++
++ /* remove default endpoint */
++ kdbus_ep_disconnect(bus->ep);
++ kdbus_ep_unref(bus->ep);
++
++ pr_info("closing bus %s/%s\n",
++ bus->ns->devpath, bus->name);
++}
++
++static struct kdbus_bus *kdbus_bus_unref(struct kdbus_bus *bus)
++{
++ if (!bus)
++ return NULL;
++ bus->ref--;
++ if (bus->ref > 0)
++ return bus;
++
++ kdbus_bus_disconnect(bus);
++ pr_info("clean up bus %s/%s\n",
++ bus->ns->devpath, bus->name);
++
++ kfree(bus->name);
++ kfree(bus);
++ return NULL;
++}
++
++static int kdbus_bus_new(struct kdbus_ns *ns, const char *name,
++ umode_t mode, uid_t uid, gid_t gid,
++ struct kdbus_bus **bus)
++{
++ struct kdbus_bus *b;
++ int err;
++
++ b = kzalloc(sizeof(struct kdbus_bus), GFP_KERNEL);
++ if (!b)
++ return -ENOMEM;
++
++ b->ref = 1;
++ b->ns = ns;
++ /* connection 0 == kernel/multi-cast */
++ b->conn_id_next = 1;
++ mutex_init(&b->lock);
++ idr_init(&b->conn_idr);
++ INIT_LIST_HEAD(&b->ep_list);
++
++ if (uid > 0)
++ b->name = kasprintf(GFP_KERNEL, "%u-%s", uid, name);
++ else
++ b->name = kstrdup(name, GFP_KERNEL);
++ if (!b->name) {
++ err = -ENOMEM;
++ goto err;
++ }
++
++ err = kdbus_ep_new(b, "bus", mode, uid, gid, &b->ep);
++ if (err < 0)
++ goto err;
++
++ mutex_lock(&ns->lock);
++ b->id = ns->bus_id_next++;
++ mutex_unlock(&ns->lock);
++
++ *bus = b;
++ pr_info("created bus %llu '%s/%s'\n",
++ (unsigned long long)b->id, ns->devpath, b->name);
++ return 0;
++err:
++ kdbus_bus_unref(b);
++ return err;
++}
++
++/* kdbus endpoint */
++static struct kdbus_ep *kdbus_ep_ref(struct kdbus_ep *ep)
++{
++ if (!ep)
++ return NULL;
++ ep->ref++;
++ return ep;
++}
++
++static void kdbus_ep_disconnect(struct kdbus_ep *ep)
++{
++ if (ep->disconnected)
++ return;
++ ep->disconnected = true;
++
++ if (ep->dev) {
++ device_unregister(ep->dev);
++ ep->dev = NULL;
++ }
++ if (ep->minor > 0) {
++ idr_remove(&ep->bus->ns->idr, ep->minor);
++ ep->minor = 0;
++ }
++ pr_info("closing endpoint %s/%s/%s\n",
++ ep->bus->ns->devpath, ep->bus->name, ep->name);
++}
++
++static struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep)
++{
++ if (!ep)
++ return NULL;
++ ep->ref--;
++ if (ep->ref > 0)
++ return ep;
++
++ mutex_lock(&ep->bus->lock);
++ kdbus_ep_disconnect(ep);
++ pr_info("clean up endpoint %s/%s/%s\n",
++ ep->bus->ns->devpath, ep->bus->name, ep->name);
++ kdbus_bus_unref(ep->bus);
++ mutex_unlock(&ep->bus->lock);
++
++ kfree(ep->name);
++ kfree(ep);
++ return NULL;
++}
++
++/* Find the endpoint for a specific bus */
++static struct kdbus_ep *endpoint_find(struct kdbus_bus *bus, const char *name)
++{
++ struct kdbus_ep *ep = NULL;
++
++ mutex_lock(&bus->lock);
++ list_for_each_entry(ep, &bus->ep_list, bus);
++
++ mutex_unlock(&bus->lock);
++
++ return NULL;
++}
++
++static int kdbus_ep_new(struct kdbus_bus *bus, const char *name,
++ umode_t mode, uid_t uid, gid_t gid,
++ struct kdbus_ep **ep)
++{
++ struct kdbus_ep *e;
++ int err;
++ int i;
++
++ e = kzalloc(sizeof(struct kdbus_ep), GFP_KERNEL);
++ if (!e)
++ return -ENOMEM;
++
++ mutex_lock(&bus->ns->lock);
++ e->ref = 1;
++ e->mode = mode;
++ e->uid = uid;
++ e->gid = gid;
++ e->bus = kdbus_bus_ref(bus);
++
++ e->name = kstrdup(name, GFP_KERNEL);
++ if (!e->name) {
++ err = -ENOMEM;
++ goto err;
++ }
++
++ /* register minor in our endpoint map */
++ if (!idr_pre_get(&bus->ns->idr, GFP_KERNEL)) {
++ err = -ENOMEM;
++ goto err_unlock;
++ }
++ err = idr_get_new_above(&bus->ns->idr, e, 1, &i);
++ if (err < 0)
++ goto err_unlock;
++ e->minor = i;
++
++ /* get id for this endpoint from bus */
++ mutex_lock(&bus->lock);
++ e->id = bus->ep_id_next++;
++ mutex_unlock(&bus->lock);
++
++ /* register bus endpoint device */
++ e->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
++ if (!e->dev)
++ goto err;
++ dev_set_name(e->dev, "%s/%s/%s", bus->ns->devpath, bus->name, name);
++ e->dev->bus = &kdbus_subsys;
++ e->dev->type = &kdbus_devtype_ep;
++ e->dev->devt = MKDEV(bus->ns->major, e->minor);
++ dev_set_drvdata(e->dev, e);
++ err = device_register(e->dev);
++ if (err < 0) {
++ put_device(e->dev);
++ e->dev = NULL;
++ }
++ mutex_unlock(&bus->ns->lock);
++
++ if (ep)
++ *ep = e;
++ pr_info("created endpoint %llu for bus '%s/%s/%s'\n",
++ (unsigned long long)e->id, bus->ns->devpath, bus->name, name);
++ return 0;
++
++err_unlock:
++ mutex_unlock(&bus->ns->lock);
++err:
++ kdbus_ep_unref(e);
++ return err;
++}
++
++static int endpoint_remove(struct kdbus_ep *ep)
++{
++ return 0;
++}
++
++/* kdbus file operations */
++static int kdbus_conn_open(struct inode *inode, struct file *file)
++{
++ struct kdbus_conn *conn;
++ struct kdbus_ns *ns;
++ struct kdbus_ep *ep;
++ int i;
++ int err;
++
++ conn = kzalloc(sizeof(struct kdbus_conn), GFP_KERNEL);
++ if (!conn)
++ return -ENOMEM;
++
++ /* find and reference namespace */
++ mutex_lock(&kdbus_subsys_lock);
++ ns = idr_find(&kdbus_ns_major_idr, MAJOR(inode->i_rdev));
++ if (!ns || ns->disconnected) {
++ kfree(conn);
++ mutex_unlock(&kdbus_subsys_lock);
++ return -ENOENT;
++ }
++ conn->ns = kdbus_ns_ref(ns);
++ file->private_data = conn;
++ mutex_unlock(&kdbus_subsys_lock);
++
++ /* control device node */
++ if (MINOR(inode->i_rdev) == 0) {
++ conn->type = KDBUS_CONN_CONTROL;
++ file->private_data = conn;
++ pr_info("opened control device '%s/control'\n",
++ conn->ns->devpath);
++ return 0;
++ }
++
++ /* find endpoint for device node */
++ mutex_lock(&conn->ns->lock);
++ ep = idr_find(&conn->ns->idr, MINOR(inode->i_rdev));
++ if (!ep || ep->disconnected) {
++ err = -ENOENT;
++ goto err_unlock;
++ }
++
++ /* create endpoint connection */
++ conn->type = KDBUS_CONN_EP;
++ conn->ep = kdbus_ep_ref(ep);
++
++ /* get and register new id for this connection */
++ conn->id = conn->ep->bus->conn_id_next++;
++ if (!idr_pre_get(&conn->ep->bus->conn_idr, GFP_KERNEL)) {
++ err = -ENOMEM;
++ goto err_unlock;
++ }
++ /* FIXME: get 64 bit working, this will fail for the 2^31th connection */
++ err = idr_get_new_above(&conn->ep->bus->conn_idr, conn, conn->id, &i);
++ if (err >= 0 && conn->id != i) {
++ idr_remove(&conn->ep->bus->conn_idr, i);
++ err = -EEXIST;
++ goto err_unlock;
++ }
++
++ file->private_data = conn;
++ mutex_unlock(&conn->ns->lock);
++
++ pr_info("created endpoint bus connection %llu '%s/%s'\n",
++ (unsigned long long)conn->id, conn->ns->devpath,
++ conn->ep->bus->name);
++ return 0;
++
++err_unlock:
++ mutex_unlock(&conn->ns->lock);
++ kfree(conn);
++ return err;
++}
++
++static int kdbus_conn_release(struct inode *inode, struct file *file)
++{
++ struct kdbus_conn *conn = file->private_data;
++
++ switch (conn->type) {
++ case KDBUS_CONN_NS_OWNER:
++ break;
++
++ case KDBUS_CONN_BUS_OWNER:
++ kdbus_bus_disconnect(conn->bus_owner);
++ kdbus_bus_unref(conn->bus_owner);
++ break;
++
++ case KDBUS_CONN_EP:
++ kdbus_ep_unref(conn->ep);
++ break;
++
++ default:
++ break;
++ }
++
++ mutex_lock(&conn->ns->lock);
++ kdbus_ns_unref(conn->ns);
++ mutex_unlock(&conn->ns->lock);
++ kfree(conn);
++ return 0;
++}
++
++/* kdbus control device commands */
++static long kdbus_conn_ioctl_control(struct file *file,
++ unsigned int cmd, void __user *argp)
++{
++ struct kdbus_conn *conn = file->private_data;
++ struct kdbus_cmd_name name;
++ int err;
++
++ switch (cmd) {
++ case KDBUS_CMD_BUS_CREATE: {
++ struct kdbus_bus *bus = NULL;
++
++ if (copy_from_user(&name, argp, sizeof(struct kdbus_cmd_name)))
++ return -EFAULT;
++
++ err = kdbus_bus_new(conn->ns, name.name,
++ 0660, current_fsuid(), current_fsgid(),
++ &bus);
++ if (err < 0)
++ return err;
++
++ /* turn the control fd into a new bus owner device */
++ conn->type = KDBUS_CONN_BUS_OWNER;
++ conn->bus_owner = bus;
++ return 0;
++ }
++
++ case KDBUS_CMD_NS_CREATE:
++ return -ENOSYS;
++
++ default:
++ return -ENOTTY;
++ }
++}
++
++/* kdbus bus endpoint commands */
++static long kdbus_conn_ioctl_ep(struct file *file, unsigned int cmd,
++ void __user *argp)
++{
++ struct kdbus_conn *conn = file->private_data;
++ struct kdbus_cmd_name name;
++ struct kdbus_msg *msg;
++ struct kdbus_ep *ep;
++ long err;
++
++ /* We need a connection before we can do anything with an ioctl */
++ if (!conn)
++ return -EINVAL;
++
++ switch (cmd) {
++ case KDBUS_CMD_EP_CREATE:
++ /* create a new endpoint for this bus */
++ if (copy_from_user(&name, argp, sizeof(struct kdbus_cmd_name)))
++ return -EFAULT;
++ return kdbus_ep_new(conn->ep->bus, name.name,
++ 0660, current_fsuid(), current_fsgid(),
++ NULL);
++
++ case KDBUS_CMD_EP_REMOVE:
++ /* remove an endpoint from this bus */
++ if (copy_from_user(&name, argp, sizeof(struct kdbus_cmd_name)))
++ return -EFAULT;
++ ep = endpoint_find(conn->bus_owner, name.name);
++ if (!ep)
++ return -EINVAL;
++
++ return endpoint_remove(ep);
++
++ case KDBUS_CMD_EP_POLICY_SET:
++ /* upload a policy for this bus */
++ return -ENOSYS;
++
++ case KDBUS_CMD_NAME_ACQUIRE:
++ /* acquire a well-known name */
++ return -ENOSYS;
++
++ case KDBUS_CMD_NAME_RELEASE:
++ /* release a well-known name */
++ return -ENOSYS;
++
++ case KDBUS_CMD_NAME_LIST:
++ /* return all current well-known names */
++ return -ENOSYS;
++
++ case KDBUS_CMD_MATCH_ADD:
++ /* subscribe to/filter for broadcast messages */
++ return -ENOSYS;
++
++ case KDBUS_CMD_MATCH_REMOVE:
++ /* unsubscribe from broadcast messages */
++ return -ENOSYS;
++
++ case KDBUS_CMD_MSG_SEND:
++ /* send a message */
++ err = kdbus_msg_new(conn, argp, &msg);
++ if (err < 0)
++ return err;
++ return kdbus_msg_send(conn, msg);
++
++ case KDBUS_CMD_MSG_RECV:
++ /* receive a message, needs to be freed */
++ return -ENOSYS;
++
++ default:
++ return -ENOTTY;
++ }
++}
++
++static long kdbus_conn_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ struct kdbus_conn *conn = file->private_data;
++ void __user *argp = (void __user *)arg;
++
++ switch (conn->type) {
++ case KDBUS_CONN_CONTROL:
++ return kdbus_conn_ioctl_control(file, cmd, argp);
++
++ case KDBUS_CONN_EP:
++ return kdbus_conn_ioctl_ep(file, cmd, argp);
++
++ default:
++ return -EINVAL;
++ }
++}
++
++const struct file_operations kdbus_device_ops = {
++ .owner = THIS_MODULE,
++ .open = kdbus_conn_open,
++ .release = kdbus_conn_release,
++ .unlocked_ioctl = kdbus_conn_ioctl,
++ .compat_ioctl = kdbus_conn_ioctl,
++ .llseek = noop_llseek,
++};
++
++static void kdbus_msg_free(struct kdbus_msg *msg)
++{
++ kfree(msg);
++}
++
++static int kdbus_msg_new(struct kdbus_conn *conn, struct kdbus_msg __user *umsg,
++ struct kdbus_msg **msg)
++{
++ struct kdbus_msg *m;
++ int err;
++
++ m = kmalloc(GFP_KERNEL, sizeof(struct kdbus_msg));
++ if (!m)
++ return -ENOMEM;
++ if (copy_from_user(m, umsg, sizeof(struct kdbus_msg))) {
++ err = -EFAULT;
++ goto out_err;
++ }
++
++ m->src_id = conn->id;
++ m->id = conn->ep->bus->msg_id_next++;
++ *msg = m;
++ return 0;
++out_err:
++ kdbus_msg_free(m);
++ return err;
++}
++
++static int kdbus_msg_send(struct kdbus_conn *conn, struct kdbus_msg *msg)
++{
++ struct kdbus_conn *conn_dst;
++
++ conn_dst = idr_find(&conn->ep->bus->conn_idr, msg->dst_id);
++ if (!conn_dst)
++ return -ENOENT;
++
++ pr_info("sending message %llu from %llu to %llu\n",
++ (unsigned long long)msg->id, (unsigned long long)msg->src_id,
++ (unsigned long long)msg->dst_id);
++
++ kdbus_msg_free(msg);
++ return 0;
++}
++
++int __init kdbus_init(void)
++{
++ int err;
++
++ err = bus_register(&kdbus_subsys);
++ if (err < 0)
++ return err;
++
++ err = kdbus_ns_new(NULL, NULL, &kdbus_ns_init);
++ if (err < 0) {
++ bus_unregister(&kdbus_subsys);
++ pr_err("kdbus: failed to initialize err=%i\n", err);
++ return err;
++ }
++
++ pr_info("kdbus: initialized\n");
++ return 0;
++}
++
++void __exit kdbus_exit(void)
++{
++ kdbus_ns_unref(kdbus_ns_init);
++ bus_unregister(&kdbus_subsys);
++ pr_info("kdbus: unloaded\n");
++}
++
++module_init(kdbus_init);
++module_exit(kdbus_exit);
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("kdbus interprocess message router");
++MODULE_ALIAS_CHARDEV(KDBUS_CHAR_MAJOR, 0);
++MODULE_ALIAS("devname:kdbus/control");
+--- /dev/null
++++ b/include/uapi/kdbus/kdbus.h
+@@ -0,0 +1,46 @@
++/*
++ * kdbus - interprocess message routing
++ *
++ * Copyright (C) 2013
++ *
++ * kdbus is free software; you can redistribute it and/or modify it under
++ * the terms of the GNU Lesser General Public License as published by the
++ * Free Software Foundation; either version 2.1 of the License, or (at
++ * your option) any later version.
++ *
++ */
++
++#ifndef _KDBUS_H_
++#define _KDBUS_H_
++
++#define KDBUS_IOC_MAGIC 0x95
++
++/* kdbus control device commands */
++struct kdbus_cmd_name {
++ uint64_t capabilities;
++ char name[256];
++ char reserved[256];
++};
++
++enum kdbus_cmd {
++ /* kdbus control commands */
++ KDBUS_CMD_BUS_CREATE = _IOW(KDBUS_IOC_MAGIC, 0x00, struct kdbus_cmd_name),
++ KDBUS_CMD_NS_CREATE = _IOW(KDBUS_IOC_MAGIC, 0x10, struct kdbus_cmd_name),
++
++ /* kdbus endpoint commands */
++ KDBUS_CMD_EP_CREATE = _IOWR(KDBUS_IOC_MAGIC, 0x30, struct kdbus_cmd_name),
++ KDBUS_CMD_EP_REMOVE = _IOWR(KDBUS_IOC_MAGIC, 0x31, int),
++ KDBUS_CMD_EP_POLICY_SET = _IOWR(KDBUS_IOC_MAGIC, 0x32, int),
++
++ KDBUS_CMD_NAME_ACQUIRE = _IOWR(KDBUS_IOC_MAGIC, 0x50, int),
++ KDBUS_CMD_NAME_RELEASE = _IOWR(KDBUS_IOC_MAGIC, 0x51, int),
++ KDBUS_CMD_NAME_LIST = _IOWR(KDBUS_IOC_MAGIC, 0x52, int),
++
++ KDBUS_CMD_MATCH_ADD = _IOWR(KDBUS_IOC_MAGIC, 0x60, int),
++ KDBUS_CMD_MATCH_REMOVE = _IOWR(KDBUS_IOC_MAGIC, 0x61, int),
++
++ KDBUS_CMD_MSG_SEND = _IOWR(KDBUS_IOC_MAGIC, 0x80, int),
++ KDBUS_CMD_MSG_RECV = _IOWR(KDBUS_IOC_MAGIC, 0x81, int),
++};
++
++#endif
+--- a/include/uapi/linux/major.h
++++ b/include/uapi/linux/major.h
+@@ -166,6 +166,8 @@
+
+ #define OSST_MAJOR 206 /* OnStream-SCx0 SCSI tape */
+
++#define KDBUS_CHAR_MAJOR 222
++
+ #define IBM_TTY3270_MAJOR 227
+ #define IBM_FS3270_MAJOR 228
+
+--- /dev/null
++++ b/kdbus.c
+@@ -0,0 +1,64 @@
++#define _GNU_SOURCE
++#include <stdio.h>
++#include <string.h>
++#include <time.h>
++#include <fcntl.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <stdint.h>
++#include <errno.h>
++#include <assert.h>
++#include <sys/ioctl.h>
++
++#include "include/uapi/kdbus/kdbus.h"
++
++int main(int argc, char *argv[])
++{
++ int fdc;
++ int fdb;
++ struct kdbus_cmd_name name;
++ char *busname;
++ char *bus;
++ char *ep;
++ uid_t uid;
++
++ uid = getuid();
++ if (argv[1])
++ busname = argv[1];
++ else if (uid > 0)
++ busname = "system";
++ else
++ busname = "user";
++ strcpy(name.name, busname);
++
++ printf("-- opening /dev/kdbus/control\n");
++ fdc = open("/dev/kdbus/control", O_RDWR|O_CLOEXEC);
++ if (fdc < 0)
++ return EXIT_FAILURE;
++
++ printf("-- creating bus '%s'\n", name.name);
++ ioctl(fdc, KDBUS_CMD_BUS_CREATE, &name);
++
++ if (uid > 0)
++ asprintf(&bus, "/dev/kdbus/%u-%s/bus", uid, busname);
++ else
++ asprintf(&bus, "/dev/kdbus/%s/bus", busname);
++ printf("-- opening bus connection %s\n", bus);
++ fdb = open(bus, O_RDWR|O_CLOEXEC);
++
++
++ asprintf(&ep, "ep-42");
++ strcpy(name.name, ep);
++ printf("-- creating endpoint for bus %s called %s\n", bus, ep);
++ ioctl(fdb, KDBUS_CMD_EP_CREATE, &name);
++
++ printf("-- sleeping 10s\n");
++ sleep(10);
++
++ printf("-- closing bus connection\n");
++ close(fdb);
++
++ printf("-- closing bus master\n");
++ close(fdc);
++ return EXIT_SUCCESS;
++}