handle: use dynamic major/minor allocation (ABI break)
authorDavid Herrmann <dh.herrmann@gmail.com>
Tue, 21 Oct 2014 12:11:36 +0000 (14:11 +0200)
committerDavid Herrmann <dh.herrmann@gmail.com>
Tue, 21 Oct 2014 12:11:36 +0000 (14:11 +0200)
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>
domain.c
domain.h
endpoint.c
endpoint.h
handle.c
handle.h
main.c
message.c

index 90840e72c601fcb333f6b5be3fd29f061097a7f7..eb2ce720f6868b195980488b27721246627fcec8 100644 (file)
--- a/domain.c
+++ b/domain.c
 #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);
@@ -202,29 +187,6 @@ static struct kdbus_domain *kdbus_domain_find(struct kdbus_domain *parent,
        return NULL;
 }
 
-/**
- * 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
@@ -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;
index 542d2c8505706fbaf7f974d8ed40438e1fb60538..f51cdb56e83aa8c11b372cc11ed1dd249256ec55 100644 (file)
--- 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,
index 3e879d888f7e920bb1f9164913336d2023ba995f..36411ad4f899356ff6b5dc3798a441db5908d71e 100644 (file)
@@ -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;
index 7ad25d31f58e7662c4de066635352e04569a065d..19cb2d30d0933f7169151e57fbec45e4f8a536da 100644 (file)
@@ -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;
index 8ebf0df203f403258b44781bdf374bc7ba51fecd..762046c8ae507e9adf40cc1bcdfaed9d28c245e3 100644 (file)
--- 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,
index 4f85497f862c6405930da4e158d6b875eb07aeeb..0e8e9a50aeb152a9156dd2498543b3fb3eb823e6 100644 (file)
--- a/handle.h
+++ b/handle.h
 #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 78c906a6718e64f7216426fae5346a4600cbf98c..caa4aabc1d8d1c47ac065262b4feacc5aad0fcd8 100644 (file)
--- 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);
 }
 
index 9881801af742363717b8bb1b0498b874e4a585a6..89dfd4afe0a759e067b214cbf74a4a77f82253db 100644 (file)
--- 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))