summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@gmail.com>2014-10-21 14:11:36 +0200
committerDavid Herrmann <dh.herrmann@gmail.com>2014-10-21 14:11:36 +0200
commit1fc0e192a9c4b43842be277112e1210405e23f55 (patch)
treef57924d54e25b602b69df4eddfe2518188262815
parent83f791cbcf08c70713078c80020554c21c031e91 (diff)
downloadkdbus-bus-1fc0e192a9c4b43842be277112e1210405e23f55.tar.gz
kdbus-bus-1fc0e192a9c4b43842be277112e1210405e23f55.tar.bz2
kdbus-bus-1fc0e192a9c4b43842be277112e1210405e23f55.zip
handle: use dynamic major/minor allocation (ABI break)
Instead of requiring 1 major per domain, we now allocate major/minor combinations dynamically. So far, only a single major is allocated during module init, but the code can easily be extended to even make those dynamic. However, device-cgroups require us to have a fixed major. User space must be aware that major/minor numbers no longer have any specific meaning. Each major/minor combination might be assigned to any domain and/or endpoint! Apart from this semantics change, the ABI stays the same. Furthermore, this patch reworks the kdbus_domain_new() and kdbus_ep_new() functions to avoid races against UEVENT_ADD. Both objects must be active before we call device_add() and thus produce UEVENT_ADD. Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
-rw-r--r--domain.c150
-rw-r--r--domain.h5
-rw-r--r--endpoint.c105
-rw-r--r--endpoint.h2
-rw-r--r--handle.c231
-rw-r--r--handle.h30
-rw-r--r--main.c15
-rw-r--r--message.c2
8 files changed, 325 insertions, 215 deletions
diff --git a/domain.c b/domain.c
index 90840e72c60..eb2ce720f68 100644
--- a/domain.c
+++ b/domain.c
@@ -28,14 +28,8 @@
#include "limits.h"
#include "util.h"
-/* map of majors to domains */
-static DEFINE_IDR(kdbus_domain_major_idr);
-
/* previous domain id sequence number */
-static u64 kdbus_domain_seq_last;
-
-/* kdbus subsystem lock */
-static DEFINE_MUTEX(kdbus_subsys_lock);
+static atomic64_t kdbus_domain_seq_last;
/* kdbus sysfs subsystem */
struct bus_type kdbus_subsys = {
@@ -99,19 +93,10 @@ void kdbus_domain_disconnect(struct kdbus_domain *domain)
mutex_unlock(&domain->parent->lock);
}
- if (domain->major > 0) {
- mutex_lock(&kdbus_subsys_lock);
-
+ if (device_is_registered(&domain->dev))
device_del(&domain->dev);
- idr_remove(&kdbus_domain_major_idr, domain->major);
- unregister_chrdev(domain->major, KBUILD_MODNAME);
- domain->major = 0;
- if (idr_is_empty(&kdbus_domain_major_idr))
- idr_destroy(&kdbus_domain_major_idr);
-
- mutex_unlock(&kdbus_subsys_lock);
- }
+ kdbus_minor_set(domain->dev.devt, KDBUS_MINOR_CONTROL, NULL);
/* disconnect all sub-domains */
for (;;) {
@@ -166,9 +151,9 @@ static void __kdbus_domain_free(struct device *dev)
BUG_ON(!list_empty(&domain->bus_list));
BUG_ON(!hash_empty(domain->user_hash));
+ kdbus_minor_free(domain->dev.devt);
kdbus_domain_unref(domain->parent);
idr_destroy(&domain->user_idr);
- idr_destroy(&domain->idr);
kfree(domain->name);
kfree(domain->devpath);
kfree(domain);
@@ -203,29 +188,6 @@ static struct kdbus_domain *kdbus_domain_find(struct kdbus_domain *parent,
}
/**
- * kdbus_domain_find_by_major() - lookup a domain by its major device number
- * @major: Major number
- *
- * Looks up a domain by major number. The returned domain
- * is ref'ed, and needs to be unref'ed by the user. Returns NULL if
- * the domain can't be found.
- *
- * Return: the domain, or NULL if not found
- */
-struct kdbus_domain *kdbus_domain_find_by_major(unsigned int major)
-{
- struct kdbus_domain *domain;
-
- mutex_lock(&kdbus_subsys_lock);
- domain = idr_find(&kdbus_domain_major_idr, major);
- if (domain)
- kdbus_domain_ref(domain);
- mutex_unlock(&kdbus_subsys_lock);
-
- return domain;
-}
-
-/**
* kdbus_domain_new() - create a new domain
* @parent: Parent domain, NULL for initial one
* @name: Name of the domain, NULL for the initial one
@@ -253,7 +215,6 @@ int kdbus_domain_new(struct kdbus_domain *parent, const char *name,
INIT_LIST_HEAD(&d->bus_list);
INIT_LIST_HEAD(&d->domain_list);
d->mode = mode;
- idr_init(&d->idr);
mutex_init(&d->lock);
atomic64_set(&d->msg_seq_last, 0);
idr_init(&d->user_idr);
@@ -264,99 +225,84 @@ int kdbus_domain_new(struct kdbus_domain *parent, const char *name,
d->dev.release = __kdbus_domain_free;
/* compose name and path of base directory in /dev */
- if (!parent) {
+ if (parent) {
+ d->devpath = kasprintf(GFP_KERNEL, "%s/domain/%s",
+ parent->devpath, name);
+ if (!d->devpath) {
+ ret = -ENOMEM;
+ goto exit_put;
+ }
+
+ d->name = kstrdup(name, GFP_KERNEL);
+ if (!d->name) {
+ ret = -ENOMEM;
+ goto exit_put;
+ }
+ } else {
/* initial domain */
d->devpath = kstrdup(KBUILD_MODNAME, GFP_KERNEL);
if (!d->devpath) {
ret = -ENOMEM;
goto exit_put;
}
+ }
- mutex_lock(&kdbus_subsys_lock);
+ ret = dev_set_name(&d->dev, "%s/control", d->devpath);
+ if (ret < 0)
+ goto exit_put;
- } else {
- /* lock order: parent domain -> domain -> subsys_lock */
+ ret = kdbus_minor_alloc(KDBUS_MINOR_CONTROL, NULL, &d->dev.devt);
+ if (ret < 0)
+ goto exit_put;
+
+ if (parent) {
+ /* lock order: parent domain -> domain */
mutex_lock(&parent->lock);
+
if (parent->disconnected) {
mutex_unlock(&parent->lock);
ret = -ESHUTDOWN;
goto exit_put;
}
- mutex_lock(&kdbus_subsys_lock);
-
if (kdbus_domain_find(parent, name)) {
+ mutex_unlock(&parent->lock);
ret = -EEXIST;
- goto exit_unlock;
- }
-
- d->devpath = kasprintf(GFP_KERNEL, "%s/domain/%s",
- parent->devpath, name);
- if (!d->devpath) {
- ret = -ENOMEM;
- goto exit_unlock;
+ goto exit_put;
}
- d->name = kstrdup(name, GFP_KERNEL);
- if (!d->name) {
- ret = -ENOMEM;
- goto exit_unlock;
- }
+ d->parent = kdbus_domain_ref(parent);
+ list_add_tail(&d->domain_entry, &parent->domain_list);
}
- /* get dynamic major */
- ret = register_chrdev(0, d->devpath, &kdbus_device_ops);
- if (ret < 0)
- goto exit_unlock;
-
- d->major = ret;
- d->dev.devt = MKDEV(d->major, 0);
-
- ret = dev_set_name(&d->dev, "%s/control", d->devpath);
- if (ret < 0)
- goto exit_chrdev;
+ d->id = atomic64_inc_return(&kdbus_domain_seq_last);
/*
- * kdbus_device_ops' dev_t finds the domain in the major map,
- * and the bus in the minor map of that domain
+ * We have to mark the domain as enabled _before_ running device_add().
+ * Otherwise, there's a race between UEVENT_ADD (generated by
+ * device_add()) and us enabling the minor.
+ * However, this means user-space can open the minor before we called
+ * device_add(). This is fine, as we never require the device to be
+ * registered, anyway.
*/
- ret = idr_alloc(&kdbus_domain_major_idr, d, d->major, 0, GFP_KERNEL);
- if (ret < 0) {
- if (ret == -ENOSPC)
- ret = -EEXIST;
- goto exit_chrdev;
- }
- /* get id for this domain */
- d->id = ++kdbus_domain_seq_last;
+ d->disconnected = false;
+ kdbus_minor_set_control(d->dev.devt, d);
ret = device_add(&d->dev);
- if (ret < 0)
- goto exit_idr;
- /* link into parent domain */
- if (parent) {
- d->parent = kdbus_domain_ref(parent);
- list_add_tail(&d->domain_entry, &parent->domain_list);
- }
-
- d->disconnected = false;
-
- mutex_unlock(&kdbus_subsys_lock);
if (parent)
mutex_unlock(&parent->lock);
+ if (ret < 0) {
+ kdbus_domain_disconnect(d);
+ kdbus_domain_unref(d);
+ return ret;
+ }
+
*domain = d;
return 0;
-exit_idr:
- idr_remove(&kdbus_domain_major_idr, d->major);
-exit_chrdev:
- unregister_chrdev(d->major, d->devpath);
-exit_unlock:
- mutex_unlock(&kdbus_subsys_lock);
- if (parent)
- mutex_unlock(&parent->lock);
exit_put:
put_device(&d->dev);
return ret;
diff --git a/domain.h b/domain.h
index 542d2c85057..f51cdb56e83 100644
--- a/domain.h
+++ b/domain.h
@@ -26,9 +26,7 @@
* @devpath: /dev base directory path
* @parent: Parent domain
* @id: Global id of this domain
- * @major: Device major number for all nodes
* @mode: Device node access mode
- * @idr: Map of endpoint minors to buses
* @lock: Domain data lock
* @bus_seq_last: Last used bus id sequence number
* @msg_seq_last: Last used message id sequence number
@@ -55,9 +53,7 @@ struct kdbus_domain {
const char *devpath;
struct kdbus_domain *parent;
u64 id;
- unsigned int major;
umode_t mode;
- struct idr idr;
struct mutex lock;
u64 bus_seq_last;
atomic64_t msg_seq_last;
@@ -95,7 +91,6 @@ struct kdbus_domain *kdbus_domain_unref(struct kdbus_domain *domain);
void kdbus_domain_disconnect(struct kdbus_domain *domain);
int kdbus_domain_new(struct kdbus_domain *parent, const char *name,
umode_t mode, struct kdbus_domain **domain);
-struct kdbus_domain *kdbus_domain_find_by_major(unsigned int major);
int kdbus_domain_get_user_unlocked(struct kdbus_domain *domain,
kuid_t uid,
diff --git a/endpoint.c b/endpoint.c
index 3e879d888f7..36411ad4f89 100644
--- a/endpoint.c
+++ b/endpoint.c
@@ -25,6 +25,7 @@
#include "connection.h"
#include "domain.h"
#include "endpoint.h"
+#include "handle.h"
#include "item.h"
#include "message.h"
#include "policy.h"
@@ -76,6 +77,16 @@ void kdbus_ep_disconnect(struct kdbus_ep *ep)
ep->disconnected = true;
mutex_unlock(&ep->lock);
+ /* disconnect from bus */
+ mutex_lock(&ep->bus->lock);
+ list_del(&ep->bus_entry);
+ mutex_unlock(&ep->bus->lock);
+
+ if (device_is_registered(&ep->dev))
+ device_del(&ep->dev);
+
+ kdbus_minor_set(ep->dev.devt, KDBUS_MINOR_EP, NULL);
+
/* disconnect all connections to this endpoint */
for (;;) {
struct kdbus_conn *conn;
@@ -96,19 +107,6 @@ void kdbus_ep_disconnect(struct kdbus_ep *ep)
kdbus_conn_disconnect(conn, false);
kdbus_conn_unref(conn);
}
-
- /* disconnect from bus */
- mutex_lock(&ep->bus->lock);
- list_del(&ep->bus_entry);
- mutex_unlock(&ep->bus->lock);
-
- if (ep->minor > 0) {
- device_del(&ep->dev);
- mutex_lock(&ep->bus->domain->lock);
- idr_remove(&ep->bus->domain->idr, ep->minor);
- mutex_unlock(&ep->bus->domain->lock);
- ep->minor = 0;
- }
}
static void __kdbus_ep_free(struct device *dev)
@@ -119,6 +117,7 @@ static void __kdbus_ep_free(struct device *dev)
BUG_ON(!list_empty(&ep->conn_list));
kdbus_policy_db_clear(&ep->policy_db);
+ kdbus_minor_free(ep->dev.devt);
kdbus_bus_unref(ep->bus);
kdbus_domain_user_unref(ep->user);
kfree(ep->name);
@@ -134,19 +133,13 @@ struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep)
static struct kdbus_ep *kdbus_ep_find(struct kdbus_bus *bus, const char *name)
{
- struct kdbus_ep *e, *ep = NULL;
-
- mutex_lock(&bus->lock);
- list_for_each_entry(e, &bus->ep_list, bus_entry) {
- if (strcmp(e->name, name) != 0)
- continue;
+ struct kdbus_ep *e;
- ep = kdbus_ep_ref(e);
- break;
- }
- mutex_unlock(&bus->lock);
+ list_for_each_entry(e, &bus->ep_list, bus_entry)
+ if (!strcmp(e->name, name))
+ return e;
- return ep;
+ return NULL;
}
/**
@@ -171,12 +164,6 @@ int kdbus_ep_new(struct kdbus_bus *bus, const char *name,
struct kdbus_ep *e;
int ret;
- e = kdbus_ep_find(bus, name);
- if (e) {
- kdbus_ep_unref(e);
- return -EEXIST;
- }
-
e = kzalloc(sizeof(*e), GFP_KERNEL);
if (!e)
return -ENOMEM;
@@ -201,52 +188,56 @@ int kdbus_ep_new(struct kdbus_bus *bus, const char *name,
goto exit_put;
}
- mutex_lock(&bus->domain->lock);
- /* register minor in our endpoint map */
- ret = idr_alloc(&bus->domain->idr, e, 1, 0, GFP_KERNEL);
- if (ret < 0) {
- if (ret == -ENOSPC)
- ret = -EEXIST;
- mutex_unlock(&bus->domain->lock);
- goto exit_put;
- }
-
- e->minor = ret;
- e->dev.devt = MKDEV(bus->domain->major, e->minor);
- mutex_unlock(&bus->domain->lock);
-
ret = dev_set_name(&e->dev, "%s/%s/%s",
bus->domain->devpath, bus->name, name);
if (ret < 0)
- goto exit_idr;
+ goto exit_put;
- ret = device_add(&e->dev);
+ ret = kdbus_minor_alloc(KDBUS_MINOR_EP, NULL, &e->dev.devt);
if (ret < 0)
- goto exit_idr;
+ goto exit_put;
- /* link into bus */
mutex_lock(&bus->lock);
+
if (bus->disconnected) {
mutex_unlock(&bus->lock);
ret = -ESHUTDOWN;
- goto exit_dev;
+ goto exit_put;
}
- e->id = ++bus->ep_seq_last;
+
+ if (kdbus_ep_find(bus, name)) {
+ mutex_unlock(&bus->lock);
+ ret = -EEXIST;
+ goto exit_put;
+ }
+
e->bus = kdbus_bus_ref(bus);
- e->disconnected = false;
list_add_tail(&e->bus_entry, &bus->ep_list);
+
+ e->id = ++bus->ep_seq_last;
+
+ /*
+ * Same as with domains, we have to mark it enabled _before_ running
+ * device_add() to avoid messing with state after UEVENT_ADD was sent.
+ */
+
+ e->disconnected = false;
+ kdbus_minor_set_ep(e->dev.devt, e);
+
+ ret = device_add(&e->dev);
+
mutex_unlock(&bus->lock);
+ if (ret < 0) {
+ kdbus_ep_disconnect(e);
+ kdbus_ep_unref(e);
+ return ret;
+ }
+
if (ep)
*ep = e;
return 0;
-exit_dev:
- device_del(&e->dev);
-exit_idr:
- mutex_lock(&bus->domain->lock);
- idr_remove(&bus->domain->idr, e->minor);
- mutex_unlock(&bus->domain->lock);
exit_put:
put_device(&e->dev);
return ret;
diff --git a/endpoint.h b/endpoint.h
index 7ad25d31f58..19cb2d30d09 100644
--- a/endpoint.h
+++ b/endpoint.h
@@ -26,7 +26,6 @@
* @bus: Bus behind this endpoint
* @name: Name of the endpoint
* @id: ID of this endpoint on the bus
- * @minor: Minor of this endpoint in the domain major
* @mode: File mode of this endpoint device node
* @uid: UID owning this endpoint
* @gid: GID owning this endpoint
@@ -47,7 +46,6 @@ struct kdbus_ep {
struct kdbus_bus *bus;
const char *name;
u64 id;
- unsigned int minor;
umode_t mode;
kuid_t uid;
kgid_t gid;
diff --git a/handle.c b/handle.c
index 8ebf0df203f..762046c8ae5 100644
--- a/handle.c
+++ b/handle.c
@@ -91,70 +91,211 @@ struct kdbus_handle {
};
};
+/* kdbus major */
+static unsigned int kdbus_major;
+
+/* map of minors to objects */
+static DEFINE_IDR(kdbus_minor_idr);
+
+/* kdbus minor lock */
+static DEFINE_SPINLOCK(kdbus_minor_lock);
+
+int kdbus_minor_init(void)
+{
+ int ret;
+
+ ret = __register_chrdev(0, 0, 0xfffff, KBUILD_MODNAME,
+ &kdbus_handle_ops);
+ if (ret < 0)
+ return ret;
+
+ kdbus_major = ret;
+ return 0;
+}
+
+void kdbus_minor_exit(void)
+{
+ __unregister_chrdev(kdbus_major, 0, 0xfffff, KBUILD_MODNAME);
+ idr_destroy(&kdbus_minor_idr);
+}
+
+static void *kdbus_minor_pack(enum kdbus_minor_type type, void *ptr)
+{
+ unsigned long p = (unsigned long)ptr;
+
+ BUILD_BUG_ON(KDBUS_MINOR_CNT > 4);
+
+ if (WARN_ON(p & 0x3UL || type >= KDBUS_MINOR_CNT))
+ return NULL;
+
+ return (void*)(p | (unsigned long)type);
+}
+
+static enum kdbus_minor_type kdbus_minor_unpack(void **ptr)
+{
+ unsigned long p = (unsigned long)*ptr;
+
+ *ptr = (void*)(p & ~0x3UL);
+ return p & 0x3UL;
+}
+
+static void kdbus_minor_ref(enum kdbus_minor_type type, void *ptr)
+{
+ if (ptr) {
+ switch (type) {
+ case KDBUS_MINOR_CONTROL:
+ kdbus_domain_ref(ptr);
+ break;
+ case KDBUS_MINOR_EP:
+ kdbus_ep_ref(ptr);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void kdbus_minor_unref(enum kdbus_minor_type type, void *ptr)
+{
+ if (ptr) {
+ switch (type) {
+ case KDBUS_MINOR_CONTROL:
+ kdbus_domain_unref(ptr);
+ break;
+ case KDBUS_MINOR_EP:
+ kdbus_ep_unref(ptr);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+int kdbus_minor_alloc(enum kdbus_minor_type type, void *ptr, dev_t *out)
+{
+ int ret;
+
+ ptr = kdbus_minor_pack(type, ptr);
+
+ idr_preload(GFP_KERNEL);
+ spin_lock(&kdbus_minor_lock);
+ ret = idr_alloc(&kdbus_minor_idr, ptr, 0, 0, GFP_NOWAIT);
+ spin_unlock(&kdbus_minor_lock);
+ idr_preload_end();
+
+ if (ret < 0)
+ return ret;
+
+ *out = MKDEV(kdbus_major, ret);
+ return 0;
+}
+
+void kdbus_minor_free(dev_t devt)
+{
+ unsigned int minor = MINOR(devt);
+
+ if (!devt)
+ return;
+
+ spin_lock(&kdbus_minor_lock);
+ idr_remove(&kdbus_minor_idr, minor);
+ spin_unlock(&kdbus_minor_lock);
+}
+
+void kdbus_minor_set(dev_t devt, enum kdbus_minor_type type, void *ptr)
+{
+ unsigned int minor = MINOR(devt);
+
+ ptr = kdbus_minor_pack(type, ptr);
+
+ spin_lock(&kdbus_minor_lock);
+ ptr = idr_replace(&kdbus_minor_idr, ptr, minor);
+ spin_unlock(&kdbus_minor_lock);
+}
+
+static int kdbus_minor_lookup(dev_t devt, void **out)
+{
+ unsigned int minor = MINOR(devt);
+ enum kdbus_minor_type type;
+ void *ptr;
+
+ spin_lock(&kdbus_minor_lock);
+ ptr = idr_find(&kdbus_minor_idr, minor);
+ type = kdbus_minor_unpack(&ptr);
+ kdbus_minor_ref(type, ptr);
+ spin_unlock(&kdbus_minor_lock);
+
+ if (!ptr)
+ return -ESHUTDOWN;
+
+ *out = ptr;
+ return type;
+}
+
static int kdbus_handle_open(struct inode *inode, struct file *file)
{
+ enum kdbus_minor_type minor_type;
struct kdbus_handle *handle;
- struct kdbus_domain *domain;
- struct kdbus_ep *ep;
+ void *minor_ptr;
int ret;
- /* find and reference domain */
- domain = kdbus_domain_find_by_major(MAJOR(inode->i_rdev));
- if (!domain || domain->disconnected)
- return -ESHUTDOWN;
+ ret = kdbus_minor_lookup(inode->i_rdev, &minor_ptr);
+ if (ret < 0)
+ return ret;
+
+ minor_type = ret;
handle = kzalloc(sizeof(*handle), GFP_KERNEL);
- if (!handle)
+ if (!handle) {
+ kdbus_minor_unref(minor_type, minor_ptr);
return -ENOMEM;
+ }
- handle->domain = domain;
file->private_data = handle;
- /* control device node */
- if (MINOR(inode->i_rdev) == 0) {
+ switch (minor_type) {
+ case KDBUS_MINOR_CONTROL:
handle->type = KDBUS_HANDLE_CONTROL;
- return 0;
- }
+ handle->domain = minor_ptr;
- /* find endpoint for device node */
- mutex_lock(&handle->domain->lock);
- ep = idr_find(&handle->domain->idr, MINOR(inode->i_rdev));
- if (!ep || ep->disconnected) {
- ret = -ESHUTDOWN;
- goto exit_unlock;
- }
+ break;
- /* create endpoint connection */
- handle->type = KDBUS_HANDLE_EP;
- handle->ep = kdbus_ep_ref(ep);
+ case KDBUS_MINOR_EP:
+ handle->type = KDBUS_HANDLE_EP;
+ handle->ep = minor_ptr;
+ handle->domain = kdbus_domain_ref(handle->ep->bus->domain);
- /* cache the metadata/credentials of the creator of the connection */
- ret = kdbus_meta_new(&handle->meta);
- if (ret < 0)
- goto exit_ep_unref;
-
- ret = kdbus_meta_append(handle->meta, NULL, 0,
- KDBUS_ATTACH_CREDS |
- KDBUS_ATTACH_TID_COMM |
- KDBUS_ATTACH_PID_COMM |
- KDBUS_ATTACH_EXE |
- KDBUS_ATTACH_CMDLINE |
- KDBUS_ATTACH_CGROUP |
- KDBUS_ATTACH_CAPS |
- KDBUS_ATTACH_SECLABEL |
- KDBUS_ATTACH_AUDIT);
- if (ret < 0)
- goto exit_meta_free;
+ /* cache the metadata/credentials of the creator */
+ ret = kdbus_meta_new(&handle->meta);
+ if (ret < 0)
+ goto exit_free;
+
+ ret = kdbus_meta_append(handle->meta, NULL, 0,
+ KDBUS_ATTACH_CREDS |
+ KDBUS_ATTACH_TID_COMM |
+ KDBUS_ATTACH_PID_COMM |
+ KDBUS_ATTACH_EXE |
+ KDBUS_ATTACH_CMDLINE |
+ KDBUS_ATTACH_CGROUP |
+ KDBUS_ATTACH_CAPS |
+ KDBUS_ATTACH_SECLABEL |
+ KDBUS_ATTACH_AUDIT);
+ if (ret < 0)
+ goto exit_free;
+
+ break;
+
+ default:
+ kdbus_minor_unref(minor_type, minor_ptr);
+ ret = -EINVAL;
+ goto exit_free;
+ }
- mutex_unlock(&handle->domain->lock);
return 0;
-exit_meta_free:
+exit_free:
kdbus_meta_free(handle->meta);
-exit_ep_unref:
kdbus_ep_unref(handle->ep);
-exit_unlock:
- mutex_unlock(&handle->domain->lock);
kdbus_domain_unref(handle->domain);
kfree(handle);
return ret;
@@ -1055,7 +1196,7 @@ static int kdbus_handle_mmap(struct file *file, struct vm_area_struct *vma)
return kdbus_pool_mmap(handle->conn->pool, vma);
}
-const struct file_operations kdbus_device_ops = {
+const struct file_operations kdbus_handle_ops = {
.owner = THIS_MODULE,
.open = kdbus_handle_open,
.release = kdbus_handle_release,
diff --git a/handle.h b/handle.h
index 4f85497f862..0e8e9a50aeb 100644
--- a/handle.h
+++ b/handle.h
@@ -14,5 +14,33 @@
#ifndef __KDBUS_HANDLE_H
#define __KDBUS_HANDLE_H
-extern const struct file_operations kdbus_device_ops;
+struct kdbus_domain;
+struct kdbus_ep;
+
+extern const struct file_operations kdbus_handle_ops;
+
+enum kdbus_minor_type {
+ KDBUS_MINOR_CONTROL,
+ KDBUS_MINOR_EP,
+ KDBUS_MINOR_CNT
+};
+
+int kdbus_minor_init(void);
+void kdbus_minor_exit(void);
+int kdbus_minor_alloc(enum kdbus_minor_type type, void *ptr, dev_t *out);
+void kdbus_minor_free(dev_t devt);
+void kdbus_minor_set(dev_t devt, enum kdbus_minor_type type, void *ptr);
+
+/* type-safe kdbus_minor_set() */
+static inline void kdbus_minor_set_control(dev_t devt, struct kdbus_domain *d)
+{
+ kdbus_minor_set(devt, KDBUS_MINOR_CONTROL, d);
+}
+
+/* type-safe kdbus_minor_set() */
+static inline void kdbus_minor_set_ep(dev_t devt, struct kdbus_ep *e)
+{
+ kdbus_minor_set(devt, KDBUS_MINOR_EP, e);
+}
+
#endif
diff --git a/main.c b/main.c
index 78c906a6718..caa4aabc1d8 100644
--- a/main.c
+++ b/main.c
@@ -19,6 +19,7 @@
#include "util.h"
#include "domain.h"
+#include "handle.h"
/* kdbus initial domain */
static struct kdbus_domain *kdbus_domain_init;
@@ -31,25 +32,35 @@ static int __init kdbus_init(void)
if (ret < 0)
return ret;
+ ret = kdbus_minor_init();
+ if (ret < 0)
+ goto exit_subsys;
+
/*
* Create the initial domain; it is world-accessible and
* provides the /dev/kdbus/control device node.
*/
ret = kdbus_domain_new(NULL, NULL, 0666, &kdbus_domain_init);
if (ret < 0) {
- bus_unregister(&kdbus_subsys);
pr_err("failed to initialize, error=%i\n", ret);
- return ret;
+ goto exit_minor;
}
pr_info("initialized\n");
return 0;
+
+exit_minor:
+ kdbus_minor_exit();
+exit_subsys:
+ bus_unregister(&kdbus_subsys);
+ return ret;
}
static void __exit kdbus_exit(void)
{
kdbus_domain_disconnect(kdbus_domain_init);
kdbus_domain_unref(kdbus_domain_init);
+ kdbus_minor_exit();
bus_unregister(&kdbus_subsys);
}
diff --git a/message.c b/message.c
index 9881801af74..89dfd4afe0a 100644
--- a/message.c
+++ b/message.c
@@ -90,7 +90,7 @@ static int kdbus_handle_check_file(struct file *file)
* unix domain sockets and kdbus share a generic garbage collector.
*/
- if (file->f_op == &kdbus_device_ops)
+ if (file->f_op == &kdbus_handle_ops)
return -EOPNOTSUPP;
if (!S_ISSOCK(inode->i_mode))