Created
October 21, 2014 12:03
-
-
Save dvdhrm/d876eabd734ef3346f2b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/domain.c b/domain.c | |
| index 90840e7..757e41f 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,86 @@ 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; | |
| + goto exit_minor; | |
| } | |
| - 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_minor; | |
| } | |
| - 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_minor: | |
| + kdbus_minor_free(d->dev.devt); | |
| exit_put: | |
| put_device(&d->dev); | |
| return ret; | |
| diff --git a/domain.h b/domain.h | |
| index 542d2c8..f51cdb5 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 3e879d8..e01f64d 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,58 @@ 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_minor; | |
| } | |
| - e->id = ++bus->ep_seq_last; | |
| + | |
| + if (kdbus_ep_find(bus, name)) { | |
| + mutex_unlock(&bus->lock); | |
| + ret = -EEXIST; | |
| + goto exit_minor; | |
| + } | |
| + | |
| 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_minor: | |
| + kdbus_minor_free(e->dev.devt); | |
| exit_put: | |
| put_device(&e->dev); | |
| return ret; | |
| diff --git a/endpoint.h b/endpoint.h | |
| index 7ad25d3..19cb2d3 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 8ebf0df..762046c 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 4f85497..0e8e9a5 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 78c906a..caa4aab 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 9881801..89dfd4a 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)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment