diff options
Diffstat (limited to 'kdbus-add-code-for-notifications-and-matches.patch')
-rw-r--r-- | kdbus-add-code-for-notifications-and-matches.patch | 929 |
1 files changed, 0 insertions, 929 deletions
diff --git a/kdbus-add-code-for-notifications-and-matches.patch b/kdbus-add-code-for-notifications-and-matches.patch deleted file mode 100644 index b9ec77f50..000000000 --- a/kdbus-add-code-for-notifications-and-matches.patch +++ /dev/null @@ -1,929 +0,0 @@ -From: Daniel Mack <daniel@zonque.org> -Date: Thu, 11 Sep 2014 18:59:16 +0200 -Subject: [PATCH] kdbus: add code for notifications and matches - -This patch adds code for matches and notifications. - -Notifications are broadcast messages generated by the kernel, which -notify subscribes when connections are created or destroyed, when -well-known-names have been claimed, released or changed ownership, -or when reply messages have timed out. - -Matches are used to tell the kernel driver which broadcast messages -a connection is interested in. Matches can either be specific on one -of the kernel-generated notification types, or carry a bloom filter -mask to match against a message from userspace. The latter is a way -to pre-filter messages from other connections in order to mitigate -unnecessary wakeups. - -Signed-off-by: Daniel Mack <daniel@zonque.org> -Signed-off-by: David Herrmann <dh.herrmann@gmail.com> -Signed-off-by: Djalal Harouni <tixxdz@opendz.org> -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ---- - ipc/kdbus/match.c | 559 +++++++++++++++++++++++++++++++++++++++++++++++++++++ - ipc/kdbus/match.h | 35 ++++ - ipc/kdbus/notify.c | 248 ++++++++++++++++++++++++ - ipc/kdbus/notify.h | 30 +++ - 4 files changed, 872 insertions(+) - create mode 100644 ipc/kdbus/match.c - create mode 100644 ipc/kdbus/match.h - create mode 100644 ipc/kdbus/notify.c - create mode 100644 ipc/kdbus/notify.h - -diff --git a/ipc/kdbus/match.c b/ipc/kdbus/match.c -new file mode 100644 -index 000000000000..30cec1ca819f ---- /dev/null -+++ b/ipc/kdbus/match.c -@@ -0,0 +1,559 @@ -+/* -+ * Copyright (C) 2013-2015 Kay Sievers -+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2015 Linux Foundation -+ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> -+ * -+ * 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. -+ */ -+ -+#include <linux/fs.h> -+#include <linux/hash.h> -+#include <linux/init.h> -+#include <linux/mutex.h> -+#include <linux/sched.h> -+#include <linux/sizes.h> -+#include <linux/slab.h> -+#include <linux/uaccess.h> -+ -+#include "bus.h" -+#include "connection.h" -+#include "endpoint.h" -+#include "handle.h" -+#include "item.h" -+#include "match.h" -+#include "message.h" -+#include "names.h" -+ -+/** -+ * struct kdbus_match_db - message filters -+ * @entries_list: List of matches -+ * @mdb_rwlock: Match data lock -+ * @entries_count: Number of entries in database -+ */ -+struct kdbus_match_db { -+ struct list_head entries_list; -+ struct rw_semaphore mdb_rwlock; -+ unsigned int entries_count; -+}; -+ -+/** -+ * struct kdbus_match_entry - a match database entry -+ * @cookie: User-supplied cookie to lookup the entry -+ * @list_entry: The list entry element for the db list -+ * @rules_list: The list head for tracking rules of this entry -+ */ -+struct kdbus_match_entry { -+ u64 cookie; -+ struct list_head list_entry; -+ struct list_head rules_list; -+}; -+ -+/** -+ * struct kdbus_bloom_mask - mask to match against filter -+ * @generations: Number of generations carried -+ * @data: Array of bloom bit fields -+ */ -+struct kdbus_bloom_mask { -+ u64 generations; -+ u64 *data; -+}; -+ -+/** -+ * struct kdbus_match_rule - a rule appended to a match entry -+ * @type: An item type to match agains -+ * @bloom_mask: Bloom mask to match a message's filter against, used -+ * with KDBUS_ITEM_BLOOM_MASK -+ * @name: Name to match against, used with KDBUS_ITEM_NAME, -+ * KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE} -+ * @old_id: ID to match against, used with -+ * KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE}, -+ * KDBUS_ITEM_ID_REMOVE -+ * @new_id: ID to match against, used with -+ * KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE}, -+ * KDBUS_ITEM_ID_REMOVE -+ * @src_id: ID to match against, used with KDBUS_ITEM_ID -+ * @rules_entry: Entry in the entry's rules list -+ */ -+struct kdbus_match_rule { -+ u64 type; -+ union { -+ struct kdbus_bloom_mask bloom_mask; -+ struct { -+ char *name; -+ u64 old_id; -+ u64 new_id; -+ }; -+ u64 src_id; -+ }; -+ struct list_head rules_entry; -+}; -+ -+static void kdbus_match_rule_free(struct kdbus_match_rule *rule) -+{ -+ if (!rule) -+ return; -+ -+ switch (rule->type) { -+ case KDBUS_ITEM_BLOOM_MASK: -+ kfree(rule->bloom_mask.data); -+ break; -+ -+ case KDBUS_ITEM_NAME: -+ case KDBUS_ITEM_NAME_ADD: -+ case KDBUS_ITEM_NAME_REMOVE: -+ case KDBUS_ITEM_NAME_CHANGE: -+ kfree(rule->name); -+ break; -+ -+ case KDBUS_ITEM_ID: -+ case KDBUS_ITEM_ID_ADD: -+ case KDBUS_ITEM_ID_REMOVE: -+ break; -+ -+ default: -+ BUG(); -+ } -+ -+ list_del(&rule->rules_entry); -+ kfree(rule); -+} -+ -+static void kdbus_match_entry_free(struct kdbus_match_entry *entry) -+{ -+ struct kdbus_match_rule *r, *tmp; -+ -+ if (!entry) -+ return; -+ -+ list_for_each_entry_safe(r, tmp, &entry->rules_list, rules_entry) -+ kdbus_match_rule_free(r); -+ -+ list_del(&entry->list_entry); -+ kfree(entry); -+} -+ -+/** -+ * kdbus_match_db_free() - free match db resources -+ * @mdb: The match database -+ */ -+void kdbus_match_db_free(struct kdbus_match_db *mdb) -+{ -+ struct kdbus_match_entry *entry, *tmp; -+ -+ if (!mdb) -+ return; -+ -+ list_for_each_entry_safe(entry, tmp, &mdb->entries_list, list_entry) -+ kdbus_match_entry_free(entry); -+ -+ kfree(mdb); -+} -+ -+/** -+ * kdbus_match_db_new() - create a new match database -+ * -+ * Return: a new kdbus_match_db on success, ERR_PTR on failure. -+ */ -+struct kdbus_match_db *kdbus_match_db_new(void) -+{ -+ struct kdbus_match_db *d; -+ -+ d = kzalloc(sizeof(*d), GFP_KERNEL); -+ if (!d) -+ return ERR_PTR(-ENOMEM); -+ -+ init_rwsem(&d->mdb_rwlock); -+ INIT_LIST_HEAD(&d->entries_list); -+ -+ return d; -+} -+ -+static bool kdbus_match_bloom(const struct kdbus_bloom_filter *filter, -+ const struct kdbus_bloom_mask *mask, -+ const struct kdbus_conn *conn) -+{ -+ size_t n = conn->ep->bus->bloom.size / sizeof(u64); -+ const u64 *m; -+ size_t i; -+ -+ /* -+ * The message's filter carries a generation identifier, the -+ * match's mask possibly carries an array of multiple generations -+ * of the mask. Select the mask with the closest match of the -+ * filter's generation. -+ */ -+ m = mask->data + (min(filter->generation, mask->generations - 1) * n); -+ -+ /* -+ * The message's filter contains the messages properties, -+ * the match's mask contains the properties to look for in the -+ * message. Check the mask bit field against the filter bit field, -+ * if the message possibly carries the properties the connection -+ * has subscribed to. -+ */ -+ for (i = 0; i < n; i++) -+ if ((filter->data[i] & m[i]) != m[i]) -+ return false; -+ -+ return true; -+} -+ -+static bool kdbus_match_rules(const struct kdbus_match_entry *entry, -+ struct kdbus_conn *conn_src, -+ struct kdbus_kmsg *kmsg) -+{ -+ struct kdbus_match_rule *r; -+ -+ if (conn_src) -+ lockdep_assert_held(&conn_src->ep->bus->name_registry->rwlock); -+ -+ /* -+ * Walk all the rules and bail out immediately -+ * if any of them is unsatisfied. -+ */ -+ -+ list_for_each_entry(r, &entry->rules_list, rules_entry) { -+ if (conn_src) { -+ /* messages from userspace */ -+ -+ switch (r->type) { -+ case KDBUS_ITEM_BLOOM_MASK: -+ if (!kdbus_match_bloom(kmsg->bloom_filter, -+ &r->bloom_mask, -+ conn_src)) -+ return false; -+ break; -+ -+ case KDBUS_ITEM_ID: -+ if (r->src_id != conn_src->id && -+ r->src_id != KDBUS_MATCH_ID_ANY) -+ return false; -+ -+ break; -+ -+ case KDBUS_ITEM_NAME: -+ if (!kdbus_conn_has_name(conn_src, r->name)) -+ return false; -+ -+ break; -+ -+ default: -+ return false; -+ } -+ } else { -+ /* kernel notifications */ -+ -+ if (kmsg->notify_type != r->type) -+ return false; -+ -+ switch (r->type) { -+ case KDBUS_ITEM_ID_ADD: -+ if (r->new_id != KDBUS_MATCH_ID_ANY && -+ r->new_id != kmsg->notify_new_id) -+ return false; -+ -+ break; -+ -+ case KDBUS_ITEM_ID_REMOVE: -+ if (r->old_id != KDBUS_MATCH_ID_ANY && -+ r->old_id != kmsg->notify_old_id) -+ return false; -+ -+ break; -+ -+ case KDBUS_ITEM_NAME_ADD: -+ case KDBUS_ITEM_NAME_CHANGE: -+ case KDBUS_ITEM_NAME_REMOVE: -+ if ((r->old_id != KDBUS_MATCH_ID_ANY && -+ r->old_id != kmsg->notify_old_id) || -+ (r->new_id != KDBUS_MATCH_ID_ANY && -+ r->new_id != kmsg->notify_new_id) || -+ (r->name && kmsg->notify_name && -+ strcmp(r->name, kmsg->notify_name) != 0)) -+ return false; -+ -+ break; -+ -+ default: -+ return false; -+ } -+ } -+ } -+ -+ return true; -+} -+ -+/** -+ * kdbus_match_db_match_kmsg() - match a kmsg object agains the database entries -+ * @mdb: The match database -+ * @conn_src: The connection object originating the message -+ * @kmsg: The kmsg to perform the match on -+ * -+ * This function will walk through all the database entries previously uploaded -+ * with kdbus_match_db_add(). As soon as any of them has an all-satisfied rule -+ * set, this function will return true. -+ * -+ * The caller must hold the registry lock of conn_src->ep->bus, in case conn_src -+ * is non-NULL. -+ * -+ * Return: true if there was a matching database entry, false otherwise. -+ */ -+bool kdbus_match_db_match_kmsg(struct kdbus_match_db *mdb, -+ struct kdbus_conn *conn_src, -+ struct kdbus_kmsg *kmsg) -+{ -+ struct kdbus_match_entry *entry; -+ bool matched = false; -+ -+ down_read(&mdb->mdb_rwlock); -+ list_for_each_entry(entry, &mdb->entries_list, list_entry) { -+ matched = kdbus_match_rules(entry, conn_src, kmsg); -+ if (matched) -+ break; -+ } -+ up_read(&mdb->mdb_rwlock); -+ -+ return matched; -+} -+ -+static int kdbus_match_db_remove_unlocked(struct kdbus_match_db *mdb, -+ u64 cookie) -+{ -+ struct kdbus_match_entry *entry, *tmp; -+ bool found = false; -+ -+ list_for_each_entry_safe(entry, tmp, &mdb->entries_list, list_entry) -+ if (entry->cookie == cookie) { -+ kdbus_match_entry_free(entry); -+ --mdb->entries_count; -+ found = true; -+ } -+ -+ return found ? 0 : -EBADSLT; -+} -+ -+/** -+ * kdbus_cmd_match_add() - handle KDBUS_CMD_MATCH_ADD -+ * @conn: connection to operate on -+ * @argp: command payload -+ * -+ * One call to this function (or one ioctl(KDBUS_CMD_MATCH_ADD), respectively, -+ * adds one new database entry with n rules attached to it. Each rule is -+ * described with an kdbus_item, and an entry is considered matching if all -+ * its rules are satisfied. -+ * -+ * The items attached to a kdbus_cmd_match struct have the following mapping: -+ * -+ * KDBUS_ITEM_BLOOM_MASK: A bloom mask -+ * KDBUS_ITEM_NAME: A connection's source name -+ * KDBUS_ITEM_ID: A connection ID -+ * KDBUS_ITEM_NAME_ADD: -+ * KDBUS_ITEM_NAME_REMOVE: -+ * KDBUS_ITEM_NAME_CHANGE: Well-known name changes, carry -+ * kdbus_notify_name_change -+ * KDBUS_ITEM_ID_ADD: -+ * KDBUS_ITEM_ID_REMOVE: Connection ID changes, carry -+ * kdbus_notify_id_change -+ * -+ * For kdbus_notify_{id,name}_change structs, only the ID and name fields -+ * are looked at when adding an entry. The flags are unused. -+ * -+ * Also note that KDBUS_ITEM_BLOOM_MASK, KDBUS_ITEM_NAME and KDBUS_ITEM_ID -+ * are used to match messages from userspace, while the others apply to -+ * kernel-generated notifications. -+ * -+ * Return: 0 on success, negative error code on failure. -+ */ -+int kdbus_cmd_match_add(struct kdbus_conn *conn, void __user *argp) -+{ -+ struct kdbus_match_db *mdb = conn->match_db; -+ struct kdbus_match_entry *entry = NULL; -+ struct kdbus_cmd_match *cmd; -+ struct kdbus_item *item; -+ int ret; -+ -+ struct kdbus_arg argv[] = { -+ { .type = KDBUS_ITEM_NEGOTIATE }, -+ { .type = KDBUS_ITEM_BLOOM_MASK, .multiple = true }, -+ { .type = KDBUS_ITEM_NAME, .multiple = true }, -+ { .type = KDBUS_ITEM_ID, .multiple = true }, -+ { .type = KDBUS_ITEM_NAME_ADD, .multiple = true }, -+ { .type = KDBUS_ITEM_NAME_REMOVE, .multiple = true }, -+ { .type = KDBUS_ITEM_NAME_CHANGE, .multiple = true }, -+ { .type = KDBUS_ITEM_ID_ADD, .multiple = true }, -+ { .type = KDBUS_ITEM_ID_REMOVE, .multiple = true }, -+ }; -+ struct kdbus_args args = { -+ .allowed_flags = KDBUS_FLAG_NEGOTIATE | -+ KDBUS_MATCH_REPLACE, -+ .argv = argv, -+ .argc = ARRAY_SIZE(argv), -+ }; -+ -+ if (!kdbus_conn_is_ordinary(conn)) -+ return -EOPNOTSUPP; -+ -+ ret = kdbus_args_parse(&args, argp, &cmd); -+ if (ret != 0) -+ return ret; -+ -+ entry = kzalloc(sizeof(*entry), GFP_KERNEL); -+ if (!entry) { -+ ret = -ENOMEM; -+ goto exit; -+ } -+ -+ entry->cookie = cmd->cookie; -+ INIT_LIST_HEAD(&entry->list_entry); -+ INIT_LIST_HEAD(&entry->rules_list); -+ -+ KDBUS_ITEMS_FOREACH(item, cmd->items, KDBUS_ITEMS_SIZE(cmd, items)) { -+ struct kdbus_match_rule *rule; -+ size_t size = item->size - offsetof(struct kdbus_item, data); -+ -+ rule = kzalloc(sizeof(*rule), GFP_KERNEL); -+ if (!rule) { -+ ret = -ENOMEM; -+ goto exit; -+ } -+ -+ rule->type = item->type; -+ INIT_LIST_HEAD(&rule->rules_entry); -+ -+ switch (item->type) { -+ case KDBUS_ITEM_BLOOM_MASK: { -+ u64 bsize = conn->ep->bus->bloom.size; -+ u64 generations; -+ u64 remainder; -+ -+ generations = div64_u64_rem(size, bsize, &remainder); -+ if (size < bsize || remainder > 0) { -+ ret = -EDOM; -+ break; -+ } -+ -+ rule->bloom_mask.data = kmemdup(item->data, -+ size, GFP_KERNEL); -+ if (!rule->bloom_mask.data) { -+ ret = -ENOMEM; -+ break; -+ } -+ -+ rule->bloom_mask.generations = generations; -+ break; -+ } -+ -+ case KDBUS_ITEM_NAME: -+ if (!kdbus_name_is_valid(item->str, false)) { -+ ret = -EINVAL; -+ break; -+ } -+ -+ rule->name = kstrdup(item->str, GFP_KERNEL); -+ if (!rule->name) -+ ret = -ENOMEM; -+ -+ break; -+ -+ case KDBUS_ITEM_ID: -+ rule->src_id = item->id; -+ break; -+ -+ case KDBUS_ITEM_NAME_ADD: -+ case KDBUS_ITEM_NAME_REMOVE: -+ case KDBUS_ITEM_NAME_CHANGE: -+ rule->old_id = item->name_change.old_id.id; -+ rule->new_id = item->name_change.new_id.id; -+ -+ if (size > sizeof(struct kdbus_notify_name_change)) { -+ rule->name = kstrdup(item->name_change.name, -+ GFP_KERNEL); -+ if (!rule->name) -+ ret = -ENOMEM; -+ } -+ -+ break; -+ -+ case KDBUS_ITEM_ID_ADD: -+ case KDBUS_ITEM_ID_REMOVE: -+ if (item->type == KDBUS_ITEM_ID_ADD) -+ rule->new_id = item->id_change.id; -+ else -+ rule->old_id = item->id_change.id; -+ -+ break; -+ } -+ -+ if (ret < 0) { -+ kdbus_match_rule_free(rule); -+ goto exit; -+ } -+ -+ list_add_tail(&rule->rules_entry, &entry->rules_list); -+ } -+ -+ down_write(&mdb->mdb_rwlock); -+ -+ /* Remove any entry that has the same cookie as the current one. */ -+ if (cmd->flags & KDBUS_MATCH_REPLACE) -+ kdbus_match_db_remove_unlocked(mdb, entry->cookie); -+ -+ /* -+ * If the above removal caught any entry, there will be room for the -+ * new one. -+ */ -+ if (++mdb->entries_count > KDBUS_MATCH_MAX) { -+ --mdb->entries_count; -+ ret = -EMFILE; -+ } else { -+ list_add_tail(&entry->list_entry, &mdb->entries_list); -+ entry = NULL; -+ } -+ -+ up_write(&mdb->mdb_rwlock); -+ -+exit: -+ kdbus_match_entry_free(entry); -+ return kdbus_args_clear(&args, ret); -+} -+ -+/** -+ * kdbus_cmd_match_remove() - handle KDBUS_CMD_MATCH_REMOVE -+ * @conn: connection to operate on -+ * @argp: command payload -+ * -+ * Return: 0 on success, negative error code on failure. -+ */ -+int kdbus_cmd_match_remove(struct kdbus_conn *conn, void __user *argp) -+{ -+ struct kdbus_cmd_match *cmd; -+ int ret; -+ -+ struct kdbus_arg argv[] = { -+ { .type = KDBUS_ITEM_NEGOTIATE }, -+ }; -+ struct kdbus_args args = { -+ .allowed_flags = KDBUS_FLAG_NEGOTIATE, -+ .argv = argv, -+ .argc = ARRAY_SIZE(argv), -+ }; -+ -+ if (!kdbus_conn_is_ordinary(conn)) -+ return -EOPNOTSUPP; -+ -+ ret = kdbus_args_parse(&args, argp, &cmd); -+ if (ret != 0) -+ return ret; -+ -+ down_write(&conn->match_db->mdb_rwlock); -+ ret = kdbus_match_db_remove_unlocked(conn->match_db, cmd->cookie); -+ up_write(&conn->match_db->mdb_rwlock); -+ -+ return kdbus_args_clear(&args, ret); -+} -diff --git a/ipc/kdbus/match.h b/ipc/kdbus/match.h -new file mode 100644 -index 000000000000..ea4292938deb ---- /dev/null -+++ b/ipc/kdbus/match.h -@@ -0,0 +1,35 @@ -+/* -+ * Copyright (C) 2013-2015 Kay Sievers -+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2015 Linux Foundation -+ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> -+ * -+ * 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_MATCH_H -+#define __KDBUS_MATCH_H -+ -+struct kdbus_conn; -+struct kdbus_kmsg; -+struct kdbus_match_db; -+ -+struct kdbus_match_db *kdbus_match_db_new(void); -+void kdbus_match_db_free(struct kdbus_match_db *db); -+int kdbus_match_db_add(struct kdbus_conn *conn, -+ struct kdbus_cmd_match *cmd); -+int kdbus_match_db_remove(struct kdbus_conn *conn, -+ struct kdbus_cmd_match *cmd); -+bool kdbus_match_db_match_kmsg(struct kdbus_match_db *db, -+ struct kdbus_conn *conn_src, -+ struct kdbus_kmsg *kmsg); -+ -+int kdbus_cmd_match_add(struct kdbus_conn *conn, void __user *argp); -+int kdbus_cmd_match_remove(struct kdbus_conn *conn, void __user *argp); -+ -+#endif -diff --git a/ipc/kdbus/notify.c b/ipc/kdbus/notify.c -new file mode 100644 -index 000000000000..e4a454222f09 ---- /dev/null -+++ b/ipc/kdbus/notify.c -@@ -0,0 +1,248 @@ -+/* -+ * Copyright (C) 2013-2015 Kay Sievers -+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2015 Linux Foundation -+ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> -+ * -+ * 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. -+ */ -+ -+#include <linux/fs.h> -+#include <linux/init.h> -+#include <linux/module.h> -+#include <linux/mutex.h> -+#include <linux/spinlock.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+ -+#include "bus.h" -+#include "connection.h" -+#include "domain.h" -+#include "endpoint.h" -+#include "item.h" -+#include "message.h" -+#include "notify.h" -+ -+static inline void kdbus_notify_add_tail(struct kdbus_kmsg *kmsg, -+ struct kdbus_bus *bus) -+{ -+ spin_lock(&bus->notify_lock); -+ list_add_tail(&kmsg->notify_entry, &bus->notify_list); -+ spin_unlock(&bus->notify_lock); -+} -+ -+static int kdbus_notify_reply(struct kdbus_bus *bus, u64 id, -+ u64 cookie, u64 msg_type) -+{ -+ struct kdbus_kmsg *kmsg = NULL; -+ -+ WARN_ON(id == 0); -+ -+ kmsg = kdbus_kmsg_new(bus, 0); -+ if (IS_ERR(kmsg)) -+ return PTR_ERR(kmsg); -+ -+ /* -+ * a kernel-generated notification can only contain one -+ * struct kdbus_item, so make a shortcut here for -+ * faster lookup in the match db. -+ */ -+ kmsg->notify_type = msg_type; -+ kmsg->msg.flags = KDBUS_MSG_SIGNAL; -+ kmsg->msg.dst_id = id; -+ kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL; -+ kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL; -+ kmsg->msg.cookie_reply = cookie; -+ kmsg->msg.items[0].type = msg_type; -+ -+ kdbus_notify_add_tail(kmsg, bus); -+ -+ return 0; -+} -+ -+/** -+ * kdbus_notify_reply_timeout() - queue a timeout reply -+ * @bus: Bus which queues the messages -+ * @id: The destination's connection ID -+ * @cookie: The cookie to set in the reply. -+ * -+ * Queues a message that has a KDBUS_ITEM_REPLY_TIMEOUT item attached. -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_notify_reply_timeout(struct kdbus_bus *bus, u64 id, u64 cookie) -+{ -+ return kdbus_notify_reply(bus, id, cookie, KDBUS_ITEM_REPLY_TIMEOUT); -+} -+ -+/** -+ * kdbus_notify_reply_dead() - queue a 'dead' reply -+ * @bus: Bus which queues the messages -+ * @id: The destination's connection ID -+ * @cookie: The cookie to set in the reply. -+ * -+ * Queues a message that has a KDBUS_ITEM_REPLY_DEAD item attached. -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_notify_reply_dead(struct kdbus_bus *bus, u64 id, u64 cookie) -+{ -+ return kdbus_notify_reply(bus, id, cookie, KDBUS_ITEM_REPLY_DEAD); -+} -+ -+/** -+ * kdbus_notify_name_change() - queue a notification about a name owner change -+ * @bus: Bus which queues the messages -+ * @type: The type if the notification; KDBUS_ITEM_NAME_ADD, -+ * KDBUS_ITEM_NAME_CHANGE or KDBUS_ITEM_NAME_REMOVE -+ * @old_id: The id of the connection that used to own the name -+ * @new_id: The id of the new owner connection -+ * @old_flags: The flags to pass in the KDBUS_ITEM flags field for -+ * the old owner -+ * @new_flags: The flags to pass in the KDBUS_ITEM flags field for -+ * the new owner -+ * @name: The name that was removed or assigned to a new owner -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_notify_name_change(struct kdbus_bus *bus, u64 type, -+ u64 old_id, u64 new_id, -+ u64 old_flags, u64 new_flags, -+ const char *name) -+{ -+ struct kdbus_kmsg *kmsg = NULL; -+ size_t name_len, extra_size; -+ -+ name_len = strlen(name) + 1; -+ extra_size = sizeof(struct kdbus_notify_name_change) + name_len; -+ kmsg = kdbus_kmsg_new(bus, extra_size); -+ if (IS_ERR(kmsg)) -+ return PTR_ERR(kmsg); -+ -+ kmsg->msg.flags = KDBUS_MSG_SIGNAL; -+ kmsg->msg.dst_id = KDBUS_DST_ID_BROADCAST; -+ kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL; -+ kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL; -+ kmsg->notify_type = type; -+ kmsg->notify_old_id = old_id; -+ kmsg->notify_new_id = new_id; -+ kmsg->msg.items[0].type = type; -+ kmsg->msg.items[0].name_change.old_id.id = old_id; -+ kmsg->msg.items[0].name_change.old_id.flags = old_flags; -+ kmsg->msg.items[0].name_change.new_id.id = new_id; -+ kmsg->msg.items[0].name_change.new_id.flags = new_flags; -+ memcpy(kmsg->msg.items[0].name_change.name, name, name_len); -+ kmsg->notify_name = kmsg->msg.items[0].name_change.name; -+ -+ kdbus_notify_add_tail(kmsg, bus); -+ -+ return 0; -+} -+ -+/** -+ * kdbus_notify_id_change() - queue a notification about a unique ID change -+ * @bus: Bus which queues the messages -+ * @type: The type if the notification; KDBUS_ITEM_ID_ADD or -+ * KDBUS_ITEM_ID_REMOVE -+ * @id: The id of the connection that was added or removed -+ * @flags: The flags to pass in the KDBUS_ITEM flags field -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_notify_id_change(struct kdbus_bus *bus, u64 type, u64 id, u64 flags) -+{ -+ struct kdbus_kmsg *kmsg = NULL; -+ -+ kmsg = kdbus_kmsg_new(bus, sizeof(struct kdbus_notify_id_change)); -+ if (IS_ERR(kmsg)) -+ return PTR_ERR(kmsg); -+ -+ kmsg->msg.flags = KDBUS_MSG_SIGNAL; -+ kmsg->msg.dst_id = KDBUS_DST_ID_BROADCAST; -+ kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL; -+ kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL; -+ kmsg->notify_type = type; -+ -+ switch (type) { -+ case KDBUS_ITEM_ID_ADD: -+ kmsg->notify_new_id = id; -+ break; -+ -+ case KDBUS_ITEM_ID_REMOVE: -+ kmsg->notify_old_id = id; -+ break; -+ -+ default: -+ BUG(); -+ } -+ -+ kmsg->msg.items[0].type = type; -+ kmsg->msg.items[0].id_change.id = id; -+ kmsg->msg.items[0].id_change.flags = flags; -+ -+ kdbus_notify_add_tail(kmsg, bus); -+ -+ return 0; -+} -+ -+/** -+ * kdbus_notify_flush() - send a list of collected messages -+ * @bus: Bus which queues the messages -+ * -+ * The list is empty after sending the messages. -+ */ -+void kdbus_notify_flush(struct kdbus_bus *bus) -+{ -+ LIST_HEAD(notify_list); -+ struct kdbus_kmsg *kmsg, *tmp; -+ -+ mutex_lock(&bus->notify_flush_lock); -+ down_read(&bus->name_registry->rwlock); -+ -+ spin_lock(&bus->notify_lock); -+ list_splice_init(&bus->notify_list, ¬ify_list); -+ spin_unlock(&bus->notify_lock); -+ -+ list_for_each_entry_safe(kmsg, tmp, ¬ify_list, notify_entry) { -+ kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, NULL, -+ KDBUS_ATTACH_TIMESTAMP); -+ -+ if (kmsg->msg.dst_id != KDBUS_DST_ID_BROADCAST) { -+ struct kdbus_conn *conn; -+ -+ conn = kdbus_bus_find_conn_by_id(bus, kmsg->msg.dst_id); -+ if (conn) { -+ kdbus_bus_eavesdrop(bus, NULL, kmsg); -+ kdbus_conn_entry_insert(NULL, conn, kmsg, NULL); -+ kdbus_conn_unref(conn); -+ } -+ } else { -+ kdbus_bus_broadcast(bus, NULL, kmsg); -+ } -+ -+ list_del(&kmsg->notify_entry); -+ kdbus_kmsg_free(kmsg); -+ } -+ -+ up_read(&bus->name_registry->rwlock); -+ mutex_unlock(&bus->notify_flush_lock); -+} -+ -+/** -+ * kdbus_notify_free() - free a list of collected messages -+ * @bus: Bus which queues the messages -+ */ -+void kdbus_notify_free(struct kdbus_bus *bus) -+{ -+ struct kdbus_kmsg *kmsg, *tmp; -+ -+ list_for_each_entry_safe(kmsg, tmp, &bus->notify_list, notify_entry) { -+ list_del(&kmsg->notify_entry); -+ kdbus_kmsg_free(kmsg); -+ } -+} -diff --git a/ipc/kdbus/notify.h b/ipc/kdbus/notify.h -new file mode 100644 -index 000000000000..03df464cb735 ---- /dev/null -+++ b/ipc/kdbus/notify.h -@@ -0,0 +1,30 @@ -+/* -+ * Copyright (C) 2013-2015 Kay Sievers -+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2015 Linux Foundation -+ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> -+ * -+ * 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_NOTIFY_H -+#define __KDBUS_NOTIFY_H -+ -+struct kdbus_bus; -+ -+int kdbus_notify_id_change(struct kdbus_bus *bus, u64 type, u64 id, u64 flags); -+int kdbus_notify_reply_timeout(struct kdbus_bus *bus, u64 id, u64 cookie); -+int kdbus_notify_reply_dead(struct kdbus_bus *bus, u64 id, u64 cookie); -+int kdbus_notify_name_change(struct kdbus_bus *bus, u64 type, -+ u64 old_id, u64 new_id, -+ u64 old_flags, u64 new_flags, -+ const char *name); -+void kdbus_notify_flush(struct kdbus_bus *bus); -+void kdbus_notify_free(struct kdbus_bus *bus); -+ -+#endif |