summaryrefslogtreecommitdiffstats
path: root/kdbus-add-code-for-notifications-and-matches.patch
diff options
context:
space:
mode:
Diffstat (limited to 'kdbus-add-code-for-notifications-and-matches.patch')
-rw-r--r--kdbus-add-code-for-notifications-and-matches.patch929
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, &notify_list);
-+ spin_unlock(&bus->notify_lock);
-+
-+ list_for_each_entry_safe(kmsg, tmp, &notify_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