#include <linux/uaccess.h>
#include "bus.h"
+#include "notify.h"
#include "connection.h"
#include "domain.h"
#include "endpoint.h"
BUG_ON(!hash_empty(bus->conn_hash));
kdbus_bus_disconnect(bus);
+ kdbus_notify_free(bus);
atomic_dec(&bus->user->buses);
kdbus_domain_user_unref(bus->user);
kdbus_name_registry_free(bus->name_registry);
hash_init(b->conn_hash);
INIT_LIST_HEAD(&b->ep_list);
INIT_LIST_HEAD(&b->monitors_list);
+ INIT_LIST_HEAD(&b->notify_list);
+ spin_lock_init(&b->notify_lock);
+ mutex_init(&b->notify_flush_lock);
atomic64_set(&b->conn_seq_last, 0);
b->domain = kdbus_domain_ref(domain);
#define __KDBUS_BUS_H
#include <linux/hashtable.h>
+#include <linux/spinlock.h>
#include <linux/idr.h>
#include "util.h"
* @kref: Reference count
* @disconnected: Invalidated data
* @uid_owner: The uid of the owner of the bus
- * @domain: Domain of this bus
+ * @domain: Domain of this bus
* @name: The bus name
* @id: ID of this bus in the domain
* @lock: Bus data lock
* @id128: Unique random 128 bit ID of this bus
* @user: Owner of the connection
* @policy_db: Policy database for this bus
+ * @notify_list: List of pending kernel-generated messages
+ * @notify_lock: Notification list lock
+ * @notify_flush_lock: Notification flushing lock
*
* A bus provides a "bus" endpoint / device node.
*
u8 id128[16];
struct kdbus_domain_user *user;
struct kdbus_policy_db *policy_db;
+ struct list_head notify_list;
+ spinlock_t notify_lock;
+ struct mutex notify_flush_lock;
};
int kdbus_bus_make_user(const struct kdbus_cmd_make *make,
{
struct kdbus_conn *conn;
struct kdbus_conn_reply *reply, *reply_tmp;
- LIST_HEAD(notify_list);
LIST_HEAD(reply_list);
u64 deadline = ~0ULL;
struct timespec ts;
if (reply->deadline_ns == 0)
continue;
- kdbus_notify_reply_timeout(reply->conn->id, reply->cookie,
- ¬ify_list);
+ kdbus_notify_reply_timeout(conn->bus, reply->conn->id,
+ reply->cookie);
}
/* rearm delayed work with next timeout */
}
mutex_unlock(&conn->lock);
- kdbus_conn_kmsg_list_send(conn->ep, ¬ify_list);
+ kdbus_notify_flush(conn->bus);
list_for_each_entry_safe(reply, reply_tmp, &reply_list, entry)
kdbus_conn_reply_free(reply);
struct kdbus_cmd_recv *recv)
{
struct kdbus_conn_queue *queue = NULL;
- LIST_HEAD(notify_list);
int ret = 0;
mutex_lock(&conn->lock);
reply = queue->reply;
}
- kdbus_notify_reply_dead(queue->src_id,
- queue->cookie, ¬ify_list);
+ kdbus_notify_reply_dead(conn->bus,
+ queue->src_id,
+ queue->cookie);
}
kdbus_conn_queue_remove(conn, queue);
exit_unlock:
mutex_unlock(&conn->lock);
exit:
- kdbus_conn_kmsg_list_send(conn->ep, ¬ify_list);
+ kdbus_notify_flush(conn->bus);
return ret;
}
return ret;
}
-/**
- * kdbus_conn_kmsg_free() - free a list of kmsg objects
- * @kmsg_list: List head of kmsg objects to free.
- */
-void kdbus_conn_kmsg_list_free(struct list_head *kmsg_list)
-{
- struct kdbus_kmsg *kmsg, *tmp;
-
- list_for_each_entry_safe(kmsg, tmp, kmsg_list, queue_entry) {
- list_del(&kmsg->queue_entry);
- kdbus_kmsg_free(kmsg);
- }
-}
-
-/**
- * kdbus_conn_kmsg_list_send() - send a list of previously collected messages
- * @ep: The endpoint to use for sending
- * @kmsg_list: List head of kmsg objects to send.
- *
- * The list is cleared and freed after sending.
- *
- * Return: 0 on success, negative errno on failure
- */
-int kdbus_conn_kmsg_list_send(struct kdbus_ep *ep,
- struct list_head *kmsg_list)
-{
- struct kdbus_kmsg *kmsg;
- int ret = 0;
-
- list_for_each_entry(kmsg, kmsg_list, queue_entry) {
- ret = kdbus_conn_kmsg_send(ep, NULL, kmsg);
- if (ret < 0)
- break;
- }
-
- kdbus_conn_kmsg_list_free(kmsg_list);
-
- return ret;
-}
-
/**
* kdbus_conn_disconnect() - disconnect a connection
* @conn: The connection to disconnect
{
struct kdbus_conn_reply *reply, *reply_tmp;
struct kdbus_conn_queue *queue, *tmp;
- LIST_HEAD(notify_list);
LIST_HEAD(reply_list);
mutex_lock(&conn->lock);
mutex_lock(&conn->lock);
list_for_each_entry_safe(queue, tmp, &conn->msg_list, entry) {
if (queue->reply)
- kdbus_notify_reply_dead(queue->src_id,
- queue->cookie, ¬ify_list);
+ kdbus_notify_reply_dead(conn->bus, queue->src_id,
+ queue->cookie);
kdbus_conn_queue_remove(conn, queue);
kdbus_pool_slice_free(queue->slice);
* dead' notification, mark entry as handled,
* and trigger the timeout handler.
*/
- kdbus_notify_reply_dead(reply->conn->id, reply->cookie,
- ¬ify_list);
+ kdbus_notify_reply_dead(conn->bus, reply->conn->id,
+ reply->cookie);
reply->deadline_ns = 0;
if (kdbus_conn_active(reply->conn))
/* wake up the queue so that users can get a POLLERR */
wake_up_interruptible(&conn->wait);
- kdbus_notify_id_change(KDBUS_ITEM_ID_REMOVE, conn->id, conn->flags,
- ¬ify_list);
+ kdbus_notify_id_change(conn->bus, KDBUS_ITEM_ID_REMOVE, conn->id,
+ conn->flags);
- kdbus_conn_kmsg_list_send(conn->ep, ¬ify_list);
+ kdbus_notify_flush(conn->bus);
return 0;
}
struct kdbus_conn *conn;
struct kdbus_bus *bus;
size_t seclabel_len = 0;
- LIST_HEAD(notify_list);
bool is_policy_holder;
bool is_activator;
bool is_monitor;
conn->attach_flags = hello->attach_flags;
/* notify about the new active connection */
- ret = kdbus_notify_id_change(KDBUS_ITEM_ID_ADD, conn->id, conn->flags,
- ¬ify_list);
+ ret = kdbus_notify_id_change(conn->bus, KDBUS_ITEM_ID_ADD, conn->id,
+ conn->flags);
if (ret < 0)
goto exit_unref_ep;
- kdbus_conn_kmsg_list_send(conn->ep, ¬ify_list);
+ kdbus_notify_flush(conn->bus);
if (is_activator) {
u64 flags = KDBUS_NAME_ACTIVATOR;
int kdbus_conn_kmsg_send(struct kdbus_ep *ep,
struct kdbus_conn *conn_src,
struct kdbus_kmsg *kmsg);
-void kdbus_conn_kmsg_list_free(struct list_head *kmsg_list);
-int kdbus_conn_kmsg_list_send(struct kdbus_ep *ep,
- struct list_head *kmsg_list);
int kdbus_conn_move_messages(struct kdbus_conn *conn_dst,
struct kdbus_conn *conn_src,
u64 name_id);
}
static int kdbus_name_replace_owner(struct kdbus_name_entry *e,
- struct kdbus_conn *conn,
- u64 flags, struct list_head *notify_list)
+ struct kdbus_conn *conn, u64 flags)
{
struct kdbus_conn *conn_old = kdbus_conn_ref(e->conn);
int ret;
goto exit_unlock;
}
- ret = kdbus_notify_name_change(KDBUS_ITEM_NAME_CHANGE,
+ ret = kdbus_notify_name_change(conn->bus, KDBUS_ITEM_NAME_CHANGE,
e->conn->id, conn->id,
- e->flags, flags,
- e->name, notify_list);
+ e->flags, flags, e->name);
if (ret < 0)
goto exit_unlock;
}
static int kdbus_name_entry_release(struct kdbus_name_entry *e,
- struct list_head *notify_list)
+ struct kdbus_bus *bus)
{
struct kdbus_conn *conn;
struct kdbus_name_queue_item,
entry_entry);
- ret = kdbus_name_replace_owner(e, q->conn, q->flags,
- notify_list);
+ ret = kdbus_name_replace_owner(e, q->conn, q->flags);
if (ret < 0)
continue;
if (ret < 0)
goto exit_release;
- return kdbus_name_replace_owner(e, e->activator, flags,
- notify_list);
+ return kdbus_name_replace_owner(e, e->activator, flags);
}
exit_release:
/* release the name */
- kdbus_notify_name_change(KDBUS_ITEM_NAME_REMOVE,
+ kdbus_notify_name_change(e->conn->bus, KDBUS_ITEM_NAME_REMOVE,
e->conn->id, 0,
- e->flags, 0, e->name,
- notify_list);
+ e->flags, 0, e->name);
conn = kdbus_conn_ref(e->conn);
mutex_lock(&conn->lock);
}
static int kdbus_name_release(struct kdbus_name_entry *e,
- struct kdbus_conn *conn,
- struct list_head *notify_list)
+ struct kdbus_conn *conn)
{
struct kdbus_name_queue_item *q_tmp, *q;
/* Is the connection already the real owner of the name? */
if (e->conn == conn)
- return kdbus_name_entry_release(e, notify_list);
+ return kdbus_name_entry_release(e, conn->bus);
/*
* Otherwise, walk the list of queued entries and search for
struct kdbus_conn *activator = NULL;
struct kdbus_name_entry *e_tmp, *e;
LIST_HEAD(names_queue_list);
- LIST_HEAD(notify_list);
LIST_HEAD(names_list);
mutex_lock(&conn->lock);
list_for_each_entry_safe(q, q_tmp, &names_queue_list, conn_entry)
kdbus_name_queue_item_free(q);
list_for_each_entry_safe(e, e_tmp, &names_list, conn_entry)
- kdbus_name_entry_release(e, ¬ify_list);
+ kdbus_name_entry_release(e, conn->bus);
mutex_unlock(®->lock);
mutex_unlock(&conn->bus->lock);
kdbus_conn_unref(activator);
-
- kdbus_conn_kmsg_list_send(conn->ep, ¬ify_list);
+ kdbus_notify_flush(conn->bus);
}
/**
struct kdbus_name_entry **entry)
{
struct kdbus_name_entry *e = NULL;
- LIST_HEAD(notify_list);
u32 hash;
int ret = 0;
if (ret < 0)
goto exit_unlock;
- ret = kdbus_name_replace_owner(e, conn, *flags,
- ¬ify_list);
+ ret = kdbus_name_replace_owner(e, conn, *flags);
goto exit_unlock;
}
goto exit_unlock;
}
- ret = kdbus_name_replace_owner(e, conn, *flags,
- ¬ify_list);
+ ret = kdbus_name_replace_owner(e, conn, *flags);
goto exit_unlock;
}
kdbus_name_entry_set_owner(e, conn);
mutex_unlock(&conn->lock);
- kdbus_notify_name_change(KDBUS_ITEM_NAME_ADD,
+ kdbus_notify_name_change(e->conn->bus, KDBUS_ITEM_NAME_ADD,
0, e->conn->id,
- 0, e->flags, e->name,
- ¬ify_list);
+ 0, e->flags, e->name);
if (entry)
*entry = e;
exit_unlock:
mutex_unlock(®->lock);
mutex_unlock(&conn->bus->lock);
- kdbus_conn_kmsg_list_send(conn->ep, ¬ify_list);
+ kdbus_notify_flush(conn->bus);
return ret;
}
struct kdbus_cmd_name *cmd)
{
struct kdbus_name_entry *e = NULL;
- LIST_HEAD(notify_list);
u64 allowed;
int ret = 0;
ret = kdbus_name_acquire(reg, conn, cmd->name, &cmd->flags, &e);
exit_unref_conn:
- kdbus_conn_kmsg_list_send(conn->ep, ¬ify_list);
+ kdbus_notify_flush(conn->bus);
kdbus_conn_unref(conn);
return ret;
{
struct kdbus_bus *bus = conn->bus;
struct kdbus_name_entry *e;
- LIST_HEAD(notify_list);
u32 hash;
int ret = 0;
kdbus_conn_ref(conn);
}
- ret = kdbus_name_release(e, conn, ¬ify_list);
+ ret = kdbus_name_release(e, conn);
exit_unlock:
mutex_unlock(®->lock);
mutex_unlock(&bus->lock);
if (conn) {
- kdbus_conn_kmsg_list_send(conn->ep, ¬ify_list);
+ kdbus_notify_flush(conn->bus);
kdbus_conn_unref(conn);
}
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include "bus.h"
+#include "connection.h"
#include "message.h"
#include "notify.h"
-static int kdbus_notify_reply(u64 id, u64 cookie, u64 msg_type,
- struct list_head *queue_list)
+static int kdbus_notify_reply(struct kdbus_bus *bus, u64 id,
+ u64 cookie, u64 msg_type)
{
struct kdbus_kmsg *kmsg = NULL;
int ret;
kmsg->msg.cookie_reply = cookie;
kmsg->msg.items[0].type = msg_type;
- list_add_tail(&kmsg->queue_entry, queue_list);
+ 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.
- * @queue_list: A queue list for the newly generated kdbus_kmsg.
- * The caller has to free all items in the list using
- * kdbus_kmsg_free(). Maybe NULL, in which case this
- * function does nothing.
*
* 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(u64 id, u64 cookie, struct list_head *queue_list)
+int kdbus_notify_reply_timeout(struct kdbus_bus *bus, u64 id, u64 cookie)
{
- return kdbus_notify_reply(id, cookie, KDBUS_ITEM_REPLY_TIMEOUT,
- queue_list);
+ 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.
- * @queue_list: A queue list for the newly generated kdbus_kmsg.
- * The caller has to free all items in the list using
- * kdbus_kmsg_free(). Maybe NULL, in which case this
- * function does nothing.
*
* 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(u64 id, u64 cookie, struct list_head *queue_list)
+int kdbus_notify_reply_dead(struct kdbus_bus *bus, u64 id, u64 cookie)
{
- return kdbus_notify_reply(id, cookie, KDBUS_ITEM_REPLY_DEAD,
- queue_list);
+ 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_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
- * @queue_list: A queue list for the newly generated kdbus_kmsg.
- * The caller has to free all items in the list using
- * kdbus_kmsg_free(). Maybe NULL, in which case this
- * function does nothing.
*
* Return: 0 on success, negative errno on failure.
*/
-int kdbus_notify_name_change(u64 type,
+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 list_head *queue_list)
+ const char *name)
{
struct kdbus_kmsg *kmsg = NULL;
size_t name_len, extra_size;
int ret;
- if (!queue_list)
- return 0;
-
name_len = strlen(name) + 1;
extra_size = sizeof(struct kdbus_notify_name_change) + name_len;
ret = kdbus_kmsg_new(extra_size, &kmsg);
memcpy(kmsg->msg.items[0].name_change.name, name, name_len);
kmsg->notify_name = kmsg->msg.items[0].name_change.name;
- list_add_tail(&kmsg->queue_entry, queue_list);
+ 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
- * @queue_list: A queue list for the newly generated kdbus_kmsg.
- * The caller has to free all items in the list using
- * kdbus_kmsg_free(). Maybe NULL, in which case this
- * function does nothing.
*
* Return: 0 on success, negative errno on failure.
*/
-int kdbus_notify_id_change(u64 type, u64 id, u64 flags,
- struct list_head *queue_list)
+int kdbus_notify_id_change(struct kdbus_bus *bus, u64 type, u64 id, u64 flags)
{
struct kdbus_kmsg *kmsg = NULL;
int ret;
kmsg->msg.items[0].id_change.id = id;
kmsg->msg.items[0].id_change.flags = flags;
- list_add_tail(&kmsg->queue_entry, queue_list);
+ 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;
+
+ 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) {
+ kdbus_conn_kmsg_send(bus->ep, NULL, kmsg);
+ list_del(&kmsg->queue_entry);
+ kdbus_kmsg_free(kmsg);
+ }
+
+ 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, queue_entry) {
+ list_del(&kmsg->queue_entry);
+ kdbus_kmsg_free(kmsg);
+ }
+}
#ifndef __KDBUS_NOTIFY_H
#define __KDBUS_NOTIFY_H
-int kdbus_notify_id_change(u64 type, u64 id, u64 flags,
- struct list_head *queue_list);
-int kdbus_notify_reply_timeout(u64 id, u64 cookie,
- struct list_head *queue_list);
-int kdbus_notify_reply_dead(u64 id, u64 cookie,
- struct list_head *queue_list);
-int kdbus_notify_name_change(u64 type,
+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,
- struct list_head *queue_list);
+ const char *name);
+void kdbus_notify_flush(struct kdbus_bus *bus);
+void kdbus_notify_free(struct kdbus_bus *bus);
#endif