Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932331AbaJ2WGI (ORCPT ); Wed, 29 Oct 2014 18:06:08 -0400 Received: from mail.linuxfoundation.org ([140.211.169.12]:53174 "EHLO mail.linuxfoundation.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751917AbaJ2WDc (ORCPT ); Wed, 29 Oct 2014 18:03:32 -0400 From: Greg Kroah-Hartman To: linux-api@vger.kernel.org, linux-kernel@vger.kernel.org Cc: john.stultz@linaro.org, arnd@arndb.de, tj@kernel.org, marcel@holtmann.org, desrt@desrt.ca, hadess@hadess.net, dh.herrmann@gmail.com, tixxdz@opendz.org, gregkh@linuxfoundation.org, simon.mcvittie@collabora.co.uk, daniel@zonque.org, alban.crequy@collabora.co.uk, javier.martinez@collabora.co.uk, teg@jklm.no Subject: kdbus: add code for notifications and matches Date: Wed, 29 Oct 2014 15:00:51 -0700 Message-Id: <1414620056-6675-8-git-send-email-gregkh@linuxfoundation.org> X-Mailer: git-send-email 2.1.2 In-Reply-To: <1414620056-6675-1-git-send-email-gregkh@linuxfoundation.org> References: <1414620056-6675-1-git-send-email-gregkh@linuxfoundation.org> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Daniel Mack 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 Signed-off-by: Greg Kroah-Hartman --- drivers/misc/kdbus/match.c | 521 ++++++++++++++++++++++++++++++++++++++++++++ drivers/misc/kdbus/match.h | 30 +++ drivers/misc/kdbus/notify.c | 235 ++++++++++++++++++++ drivers/misc/kdbus/notify.h | 28 +++ 4 files changed, 814 insertions(+) create mode 100644 drivers/misc/kdbus/match.c create mode 100644 drivers/misc/kdbus/match.h create mode 100644 drivers/misc/kdbus/notify.c create mode 100644 drivers/misc/kdbus/notify.h diff --git a/drivers/misc/kdbus/match.c b/drivers/misc/kdbus/match.c new file mode 100644 index 000000000000..86458a642d07 --- /dev/null +++ b/drivers/misc/kdbus/match.c @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2013-2014 Kay Sievers + * Copyright (C) 2013-2014 Greg Kroah-Hartman + * Copyright (C) 2013-2014 Daniel Mack + * Copyright (C) 2013-2014 David Herrmann + * Copyright (C) 2013-2014 Linux Foundation + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bus.h" +#include "connection.h" +#include "endpoint.h" +#include "item.h" +#include "match.h" +#include "message.h" + +/** + * struct kdbus_match_db - message filters + * @entries_list: List of matches + * @entries_lock: Match data lock + * @entries: Number of entries in database + */ +struct kdbus_match_db { + struct list_head entries_list; + struct mutex entries_lock; + unsigned int entries; +}; + +/** + * 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) +{ + 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; + + 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 + * @db: The match database + */ +void kdbus_match_db_free(struct kdbus_match_db *db) +{ + struct kdbus_match_entry *entry, *tmp; + + mutex_lock(&db->entries_lock); + list_for_each_entry_safe(entry, tmp, &db->entries_list, list_entry) + kdbus_match_entry_free(entry); + mutex_unlock(&db->entries_lock); + + kfree(db); +} + +/** + * kdbus_match_db_new() - create a new match database + * @db: Pointer location for the returned database + * + * Return: 0 on success, negative errno on failure. + */ +int kdbus_match_db_new(struct kdbus_match_db **db) +{ + struct kdbus_match_db *d; + + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) + return -ENOMEM; + + mutex_init(&d->entries_lock); + INIT_LIST_HEAD(&d->entries_list); + + *db = d; + return 0; +} + +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->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; + + /* + * 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 == NULL) { + /* 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; + } + } else { + /* 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; + } + } + } + + return true; +} + +/** + * kdbus_match_db_match_kmsg() - match a kmsg object agains the database entries + * @db: 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. + * + * Return: true if there was a matching database entry, false otherwise. + */ +bool kdbus_match_db_match_kmsg(struct kdbus_match_db *db, + struct kdbus_conn *conn_src, + struct kdbus_kmsg *kmsg) +{ + struct kdbus_match_entry *entry; + bool matched = false; + + mutex_lock(&db->entries_lock); + list_for_each_entry(entry, &db->entries_list, list_entry) { + matched = kdbus_match_rules(entry, conn_src, kmsg); + if (matched) + break; + } + mutex_unlock(&db->entries_lock); + + return matched; +} + +static int __kdbus_match_db_remove_unlocked(struct kdbus_match_db *db, + uint64_t cookie) +{ + struct kdbus_match_entry *entry, *tmp; + bool found = false; + + list_for_each_entry_safe(entry, tmp, &db->entries_list, list_entry) + if (entry->cookie == cookie) { + kdbus_match_entry_free(entry); + --db->entries; + found = true; + } + + return found ? 0 : -ENOENT; +} + +/** + * kdbus_match_db_add() - add an entry to the match database + * @conn: The connection that was used in the ioctl call + * @cmd: The command as provided by the ioctl call + * + * This function is used in the context of the KDBUS_CMD_MATCH_ADD ioctl + * interface. + * + * 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 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 errno on failure + */ +int kdbus_match_db_add(struct kdbus_conn *conn, + struct kdbus_cmd_match *cmd) +{ + struct kdbus_match_entry *entry = NULL; + struct kdbus_match_db *db = conn->match_db; + struct kdbus_item *item; + LIST_HEAD(list); + int ret = 0; + + lockdep_assert_held(conn); + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + ret = -ENOMEM; + goto exit_free; + } + + 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; + break; + } + + switch (item->type) { + case KDBUS_ITEM_BLOOM_MASK: { + u64 generations; + u64 remainder; + + generations = div64_u64_rem(size, conn->bus->bloom.size, + &remainder); + if (size < conn->bus->bloom.size || + remainder > 0) { + ret = -EDOM; + break; + } + + rule->bloom_mask.data = kmemdup(item->data, + size, GFP_KERNEL); + if (!rule->bloom_mask.data) { + ret = -ENOMEM; + break; + } + + /* we get an array of n generations of bloom masks */ + rule->bloom_mask.generations = generations; + + break; + } + case KDBUS_ITEM_NAME: + ret = kdbus_item_validate_name(item); + if (ret < 0) + 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; + + default: + kfree(rule); + continue; + } + + if (ret < 0) { + kfree(rule); + break; + } + + rule->type = item->type; + + list_add_tail(&rule->rules_entry, &entry->rules_list); + } + + mutex_lock(&db->entries_lock); + + /* Remove any entry that has the same cookie as the current one. */ + if (cmd->flags & KDBUS_MATCH_REPLACE) + __kdbus_match_db_remove_unlocked(db, entry->cookie); + + /* + * If the above removal caught any entry, there will be room for the + * new one. + */ + if (++db->entries > KDBUS_MATCH_MAX) { + --db->entries; + ret = -EMFILE; + } + if (ret == 0) + list_add_tail(&entry->list_entry, &db->entries_list); + else + kdbus_match_entry_free(entry); + mutex_unlock(&db->entries_lock); + +exit_free: + return ret; +} + +/** + * kdbus_match_db_remove() - remove an entry from the match database + * @conn: The connection that was used in the ioctl call + * @cmd: Pointer to the match data structure + * + * This function is used in the context of the KDBUS_CMD_MATCH_REMOVE + * ioctl interface. + * + * Return: 0 on success, negative errno on failure. + */ +int kdbus_match_db_remove(struct kdbus_conn *conn, + struct kdbus_cmd_match *cmd) +{ + struct kdbus_match_db *db = conn->match_db; + int ret; + + lockdep_assert_held(conn); + + mutex_lock(&db->entries_lock); + ret = __kdbus_match_db_remove_unlocked(db, cmd->cookie); + mutex_unlock(&db->entries_lock); + + return ret; +} diff --git a/drivers/misc/kdbus/match.h b/drivers/misc/kdbus/match.h new file mode 100644 index 000000000000..72888080a8d0 --- /dev/null +++ b/drivers/misc/kdbus/match.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013-2014 Kay Sievers + * Copyright (C) 2013-2014 Greg Kroah-Hartman + * Copyright (C) 2013-2014 Daniel Mack + * Copyright (C) 2013-2014 David Herrmann + * Copyright (C) 2013-2014 Linux Foundation + * + * 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; + +int kdbus_match_db_new(struct kdbus_match_db **db); +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); +#endif diff --git a/drivers/misc/kdbus/notify.c b/drivers/misc/kdbus/notify.c new file mode 100644 index 000000000000..c68add64cbf0 --- /dev/null +++ b/drivers/misc/kdbus/notify.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2013-2014 Kay Sievers + * Copyright (C) 2013-2014 Greg Kroah-Hartman + * Copyright (C) 2013-2014 Daniel Mack + * Copyright (C) 2013-2014 David Herrmann + * Copyright (C) 2013-2014 Linux Foundation + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "bus.h" +#include "connection.h" +#include "endpoint.h" +#include "item.h" +#include "message.h" +#include "notify.h" + +static int kdbus_notify_reply(struct kdbus_bus *bus, u64 id, + u64 cookie, u64 msg_type) +{ + struct kdbus_kmsg *kmsg = NULL; + int ret; + + BUG_ON(id == 0); + + ret = kdbus_kmsg_new(0, &kmsg); + if (ret < 0) + return ret; + + /* + * 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.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; + + spin_lock(&bus->notify_lock); + list_add_tail(&kmsg->queue_entry, &bus->notify_list); + spin_unlock(&bus->notify_lock); + return ret; +} + +/** + * 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; + int ret; + + name_len = strlen(name) + 1; + extra_size = sizeof(struct kdbus_notify_name_change) + name_len; + ret = kdbus_kmsg_new(extra_size, &kmsg); + if (ret < 0) + return ret; + + 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; + + spin_lock(&bus->notify_lock); + list_add_tail(&kmsg->queue_entry, &bus->notify_list); + spin_unlock(&bus->notify_lock); + return ret; +} + +/** + * 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; + int ret; + + ret = kdbus_kmsg_new(sizeof(struct kdbus_notify_id_change), &kmsg); + if (ret < 0) + return ret; + + 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; + + spin_lock(&bus->notify_lock); + list_add_tail(&kmsg->queue_entry, &bus->notify_list); + spin_unlock(&bus->notify_lock); + return ret; +} + +/** + * 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; + struct kdbus_ep *ep = NULL; + + /* bus->ep is only valid as long as the bus is alive */ + mutex_lock(&bus->lock); + if (!bus->disconnected) + ep = kdbus_ep_ref(bus->ep); + mutex_unlock(&bus->lock); + + mutex_lock(&bus->notify_flush_lock); + + 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, queue_entry) { + if (ep) + kdbus_conn_kmsg_send(ep, NULL, kmsg); + list_del(&kmsg->queue_entry); + kdbus_kmsg_free(kmsg); + } + + mutex_unlock(&bus->notify_flush_lock); + + kdbus_ep_unref(ep); +} + +/** + * 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, queue_entry) { + list_del(&kmsg->queue_entry); + kdbus_kmsg_free(kmsg); + } +} diff --git a/drivers/misc/kdbus/notify.h b/drivers/misc/kdbus/notify.h new file mode 100644 index 000000000000..f6ebd56e2dca --- /dev/null +++ b/drivers/misc/kdbus/notify.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2013-2014 Kay Sievers + * Copyright (C) 2013-2014 Greg Kroah-Hartman + * Copyright (C) 2013-2014 Daniel Mack + * Copyright (C) 2013-2014 David Herrmann + * Copyright (C) 2013-2014 Linux Foundation + * + * 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 -- 2.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/