diff options
| author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-01-09 15:55:36 -0800 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-01-09 15:55:36 -0800 |
| commit | 4b9ed384417dfbeafbb16b37725fa640352677cb (patch) | |
| tree | cc44943f0777104a6f8dc41800cc5030b7a27f7c /0001-kdbus-interprocess-message-router.patch | |
| parent | fa6afa80467bc6b4ff1b1a9cb8e644000b63a0e8 (diff) | |
| download | patches-4b9ed384417dfbeafbb16b37725fa640352677cb.tar.gz | |
updates
Diffstat (limited to '0001-kdbus-interprocess-message-router.patch')
| -rw-r--r-- | 0001-kdbus-interprocess-message-router.patch | 1096 |
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; ++} |
