handle notify messages in a bus list to preserve bus-global ordering
authorKay Sievers <kay@vrfy.org>
Fri, 11 Apr 2014 18:11:34 +0000 (11:11 -0700)
committerKay Sievers <kay@vrfy.org>
Fri, 11 Apr 2014 18:11:34 +0000 (11:11 -0700)
bus.c
bus.h
connection.c
connection.h
names.c
notify.c
notify.h

diff --git a/bus.c b/bus.c
index 9fea64369db5019d816bbabaf988e0425915d159..7af1a4eebabbfb8293444d6980d99f6ae654de1a 100644 (file)
--- a/bus.c
+++ b/bus.c
@@ -23,6 +23,7 @@
 #include <linux/uaccess.h>
 
 #include "bus.h"
+#include "notify.h"
 #include "connection.h"
 #include "domain.h"
 #include "endpoint.h"
@@ -73,6 +74,7 @@ static void __kdbus_bus_free(struct kref *kref)
        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);
@@ -244,6 +246,9 @@ int kdbus_bus_new(struct kdbus_domain *domain,
        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);
 
diff --git a/bus.h b/bus.h
index 3eda7273dea5f27358ab2ef028f6d595fba1e149..a5832b84c4ff15d682edb14395adf946f48d8155 100644 (file)
--- a/bus.h
+++ b/bus.h
@@ -14,6 +14,7 @@
 #define __KDBUS_BUS_H
 
 #include <linux/hashtable.h>
+#include <linux/spinlock.h>
 #include <linux/idr.h>
 
 #include "util.h"
@@ -23,7 +24,7 @@
  * @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
@@ -41,6 +42,9 @@
  * @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.
  *
@@ -70,6 +74,9 @@ struct kdbus_bus {
        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,
index 7bfec00427163a0fcbd30d6ab66dc21baf9f5f57..4abfc46d567d5a16ca0148d6bcd0d5340a79aeea 100644 (file)
@@ -708,7 +708,6 @@ static void kdbus_conn_work(struct work_struct *work)
 {
        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;
@@ -756,8 +755,8 @@ static void kdbus_conn_work(struct work_struct *work)
                if (reply->deadline_ns == 0)
                        continue;
 
-               kdbus_notify_reply_timeout(reply->conn->id, reply->cookie,
-                                          &notify_list);
+               kdbus_notify_reply_timeout(conn->bus, reply->conn->id,
+                                          reply->cookie);
        }
 
        /* rearm delayed work with next timeout */
@@ -768,7 +767,7 @@ static void kdbus_conn_work(struct work_struct *work)
        }
        mutex_unlock(&conn->lock);
 
-       kdbus_conn_kmsg_list_send(conn->ep, &notify_list);
+       kdbus_notify_flush(conn->bus);
 
        list_for_each_entry_safe(reply, reply_tmp, &reply_list, entry)
                kdbus_conn_reply_free(reply);
@@ -993,7 +992,6 @@ int kdbus_cmd_msg_recv(struct kdbus_conn *conn,
                       struct kdbus_cmd_recv *recv)
 {
        struct kdbus_conn_queue *queue = NULL;
-       LIST_HEAD(notify_list);
        int ret = 0;
 
        mutex_lock(&conn->lock);
@@ -1057,8 +1055,9 @@ int kdbus_cmd_msg_recv(struct kdbus_conn *conn,
                                reply = queue->reply;
                        }
 
-                       kdbus_notify_reply_dead(queue->src_id,
-                                               queue->cookie, &notify_list);
+                       kdbus_notify_reply_dead(conn->bus,
+                                               queue->src_id,
+                                               queue->cookie);
                }
 
                kdbus_conn_queue_remove(conn, queue);
@@ -1097,7 +1096,7 @@ int kdbus_cmd_msg_recv(struct kdbus_conn *conn,
 exit_unlock:
        mutex_unlock(&conn->lock);
 exit:
-       kdbus_conn_kmsg_list_send(conn->ep, &notify_list);
+       kdbus_notify_flush(conn->bus);
        return ret;
 }
 
@@ -1441,46 +1440,6 @@ exit_unref:
        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
@@ -1497,7 +1456,6 @@ int kdbus_conn_disconnect(struct kdbus_conn *conn, bool ensure_queue_empty)
 {
        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);
@@ -1538,8 +1496,8 @@ int kdbus_conn_disconnect(struct kdbus_conn *conn, bool ensure_queue_empty)
        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, &notify_list);
+                       kdbus_notify_reply_dead(conn->bus, queue->src_id,
+                                               queue->cookie);
 
                kdbus_conn_queue_remove(conn, queue);
                kdbus_pool_slice_free(queue->slice);
@@ -1559,8 +1517,8 @@ int kdbus_conn_disconnect(struct kdbus_conn *conn, bool ensure_queue_empty)
                 * dead' notification, mark entry as handled,
                 * and trigger the timeout handler.
                 */
-               kdbus_notify_reply_dead(reply->conn->id, reply->cookie,
-                                       &notify_list);
+               kdbus_notify_reply_dead(conn->bus, reply->conn->id,
+                                       reply->cookie);
 
                reply->deadline_ns = 0;
                if (kdbus_conn_active(reply->conn))
@@ -1573,10 +1531,10 @@ int kdbus_conn_disconnect(struct kdbus_conn *conn, bool ensure_queue_empty)
        /* 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,
-                              &notify_list);
+       kdbus_notify_id_change(conn->bus, KDBUS_ITEM_ID_REMOVE, conn->id,
+                              conn->flags);
 
-       kdbus_conn_kmsg_list_send(conn->ep, &notify_list);
+       kdbus_notify_flush(conn->bus);
        return 0;
 }
 
@@ -1916,7 +1874,6 @@ int kdbus_conn_new(struct kdbus_ep *ep,
        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;
@@ -2074,11 +2031,11 @@ int kdbus_conn_new(struct kdbus_ep *ep,
        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,
-                                    &notify_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, &notify_list);
+       kdbus_notify_flush(conn->bus);
 
        if (is_activator) {
                u64 flags = KDBUS_NAME_ACTIVATOR;
index a91ea4c5b432c47eef566f243dc6d1f90647d91c..dde1347b02bdc1b0a4bdd674bfab50bf47b96c43 100644 (file)
@@ -121,9 +121,6 @@ int kdbus_cmd_conn_update(struct kdbus_conn *conn,
 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);
diff --git a/names.c b/names.c
index 3b9c010a63ee5780f198b5af754e9833b5ff9d50..cd8984ba21081deb779baf72141c047491e0cc4e 100644 (file)
--- a/names.c
+++ b/names.c
@@ -138,8 +138,7 @@ static void kdbus_name_entry_set_owner(struct kdbus_name_entry *e,
 }
 
 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;
@@ -161,10 +160,9 @@ static int kdbus_name_replace_owner(struct kdbus_name_entry *e,
                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;
 
@@ -182,7 +180,7 @@ 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;
 
@@ -195,8 +193,7 @@ static int kdbus_name_entry_release(struct kdbus_name_entry *e,
                                     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;
 
@@ -220,16 +217,14 @@ static int kdbus_name_entry_release(struct kdbus_name_entry *e,
                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);
@@ -244,14 +239,13 @@ exit_release:
 }
 
 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
@@ -282,7 +276,6 @@ void kdbus_name_remove_by_conn(struct kdbus_name_registry *reg,
        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);
@@ -300,13 +293,12 @@ void kdbus_name_remove_by_conn(struct kdbus_name_registry *reg,
        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, &notify_list);
+               kdbus_name_entry_release(e, conn->bus);
        mutex_unlock(&reg->lock);
        mutex_unlock(&conn->bus->lock);
 
        kdbus_conn_unref(activator);
-
-       kdbus_conn_kmsg_list_send(conn->ep, &notify_list);
+       kdbus_notify_flush(conn->bus);
 }
 
 /**
@@ -446,7 +438,6 @@ int kdbus_name_acquire(struct kdbus_name_registry *reg,
                       struct kdbus_name_entry **entry)
 {
        struct kdbus_name_entry *e = NULL;
-       LIST_HEAD(notify_list);
        u32 hash;
        int ret = 0;
 
@@ -492,8 +483,7 @@ int kdbus_name_acquire(struct kdbus_name_registry *reg,
                        if (ret < 0)
                                goto exit_unlock;
 
-                       ret = kdbus_name_replace_owner(e, conn, *flags,
-                                                      &notify_list);
+                       ret = kdbus_name_replace_owner(e, conn, *flags);
                        goto exit_unlock;
                }
 
@@ -511,8 +501,7 @@ int kdbus_name_acquire(struct kdbus_name_registry *reg,
                                        goto exit_unlock;
                        }
 
-                       ret = kdbus_name_replace_owner(e, conn, *flags,
-                                                      &notify_list);
+                       ret = kdbus_name_replace_owner(e, conn, *flags);
                        goto exit_unlock;
                }
 
@@ -572,10 +561,9 @@ int kdbus_name_acquire(struct kdbus_name_registry *reg,
        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,
-                                &notify_list);
+                                0, e->flags, e->name);
 
        if (entry)
                *entry = e;
@@ -583,7 +571,7 @@ int kdbus_name_acquire(struct kdbus_name_registry *reg,
 exit_unlock:
        mutex_unlock(&reg->lock);
        mutex_unlock(&conn->bus->lock);
-       kdbus_conn_kmsg_list_send(conn->ep, &notify_list);
+       kdbus_notify_flush(conn->bus);
 
        return ret;
 }
@@ -601,7 +589,6 @@ int kdbus_cmd_name_acquire(struct kdbus_name_registry *reg,
                           struct kdbus_cmd_name *cmd)
 {
        struct kdbus_name_entry *e = NULL;
-       LIST_HEAD(notify_list);
        u64 allowed;
        int ret = 0;
 
@@ -657,7 +644,7 @@ int kdbus_cmd_name_acquire(struct kdbus_name_registry *reg,
        ret = kdbus_name_acquire(reg, conn, cmd->name, &cmd->flags, &e);
 
 exit_unref_conn:
-       kdbus_conn_kmsg_list_send(conn->ep, &notify_list);
+       kdbus_notify_flush(conn->bus);
        kdbus_conn_unref(conn);
 
        return ret;
@@ -677,7 +664,6 @@ int kdbus_cmd_name_release(struct kdbus_name_registry *reg,
 {
        struct kdbus_bus *bus = conn->bus;
        struct kdbus_name_entry *e;
-       LIST_HEAD(notify_list);
        u32 hash;
        int ret = 0;
 
@@ -713,14 +699,14 @@ int kdbus_cmd_name_release(struct kdbus_name_registry *reg,
                kdbus_conn_ref(conn);
        }
 
-       ret = kdbus_name_release(e, conn, &notify_list);
+       ret = kdbus_name_release(e, conn);
 
 exit_unlock:
        mutex_unlock(&reg->lock);
        mutex_unlock(&bus->lock);
 
        if (conn) {
-               kdbus_conn_kmsg_list_send(conn->ep, &notify_list);
+               kdbus_notify_flush(conn->bus);
                kdbus_conn_unref(conn);
        }
 
index 4c36b9860cc8d1028252e5fea705e1e36ef8abb0..60106ebdccc5c3d5077e8425c3b7f36be9c4d985 100644 (file)
--- a/notify.c
+++ b/notify.c
 #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;
@@ -45,50 +48,45 @@ static int kdbus_notify_reply(u64 id, u64 cookie, u64 msg_type,
        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
@@ -98,26 +96,18 @@ int kdbus_notify_reply_dead(u64 id, u64 cookie, struct list_head *queue_list)
  * @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);
@@ -137,25 +127,23 @@ int kdbus_notify_name_change(u64 type,
        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;
@@ -185,6 +173,48 @@ int kdbus_notify_id_change(u64 type, u64 id, u64 flags,
        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, &notify_list);
+       spin_unlock(&bus->notify_lock);
+
+       list_for_each_entry_safe(kmsg, tmp, &notify_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);
+       }
+}
index 890e51f73b6df05423e95814da880429e16f21f5..726fac0ae55b3811d7e1499ee2a38e9489e0fa97 100644 (file)
--- a/notify.h
+++ b/notify.h
 #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