match: add flags to kdbus_cmd_match (ABI break!)
authorDaniel Mack <daniel@zonque.org>
Thu, 2 Oct 2014 15:09:09 +0000 (17:09 +0200)
committerDaniel Mack <daniel@zonque.org>
Thu, 2 Oct 2014 15:10:55 +0000 (17:10 +0200)
Add a 64-bit flags field to struct kdbus_cmd_match, and define a flag
that allows for a race-free way of replacing existing matches.

Signed-off-by: Daniel Mack <daniel@zonque.org>
handle.c
kdbus.h
kdbus.txt
match.c

index e9173d3ea5778fbba5774b4c613d2fedeeaf5bd3..af20cdd7912fa36d956bfa442d521bc578a8177b 100644 (file)
--- a/handle.c
+++ b/handle.c
@@ -738,7 +738,7 @@ static long kdbus_handle_ioctl_ep_connected(struct file *file, unsigned int cmd,
                if (ret < 0)
                        break;
 
-               ret = kdbus_match_db_add(conn, p);
+               ret = kdbus_match_db_add(conn, cmd_match);
                break;
        }
 
diff --git a/kdbus.h b/kdbus.h
index c97994974b7882246c9d04fe689870f3d1fc8175..801125946bee639db2deab361138732bec7ed8e6 100644 (file)
--- a/kdbus.h
+++ b/kdbus.h
@@ -713,6 +713,16 @@ struct kdbus_cmd_update {
        struct kdbus_item items[0];
 } __attribute__((aligned(8)));
 
+/**
+ * enum kdbus_cmd_match_flags - flags to control the KDBUS_CMD_MATCH_ADD ioctl
+ * @KDBUS_MATCH_REPLACE:       If entries with the supplied cookie already
+ *                             exists, remove them before installing the new
+ *                             matches.
+ */
+enum kdbus_cmd_match_flags {
+       KDBUS_MATCH_REPLACE     = 1ULL <<  0,
+};
+
 /**
  * struct kdbus_cmd_match - struct to add or remove matches
  * @size:              The total size of the struct
@@ -726,6 +736,7 @@ struct kdbus_cmd_update {
 struct kdbus_cmd_match {
        __u64 size;
        __u64 cookie;
+       __u64 flags;
        struct kdbus_item items[0];
 } __attribute__((aligned(8)));
 
index cfcfe9a24bed1eaf05af30124522631a54ac0c35..12de6e059d9d1ab900892769f13090bf2ecb71c5 100644 (file)
--- a/kdbus.txt
+++ b/kdbus.txt
@@ -1127,6 +1127,13 @@ struct kdbus_cmd_match {
     A cookie which identifies the match, so it can be referred to at removal
     time.
 
+  __u64 flags;
+    Flags to control the behavior of the ioctl.
+
+    KDBUS_MATCH_REPLACE:
+      Remove all entries with the given cookie before installing the new one.
+      This allows for race-free replacement of matches.
+
   struct kdbus_item items[0];
     Items to define the actual match. The following item types are expected.
 
@@ -1228,6 +1235,9 @@ struct kdbus_cmd_match {
     The cookie of the match, as it was passed when the match was added.
     All matches that have this cookie will be removed.
 
+  __u64 flags;
+    Unused for this use case,
+
   struct kdbus_item items[0];
     Unused for this use case.
 };
diff --git a/match.c b/match.c
index 60738d425625ac65f38082a03511f0777f146579..689c0e236fee22880939f29222a62b6f5044ef4b 100644 (file)
--- a/match.c
+++ b/match.c
@@ -308,10 +308,28 @@ bool kdbus_match_db_match_kmsg(struct kdbus_match_db *db,
        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
+ * @replace:           If an entry with the given cookie already exists,
+ *                     replace it with the new one.
  *
  * This function is used in the context of the KDBUS_CMD_MATCH_ADD ioctl
  * interface.
@@ -455,6 +473,15 @@ int kdbus_match_db_add(struct kdbus_conn *conn,
        }
 
        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;
@@ -483,19 +510,13 @@ int kdbus_match_db_remove(struct kdbus_conn *conn,
                          struct kdbus_cmd_match *cmd)
 {
        struct kdbus_match_db *db = conn->match_db;
-       struct kdbus_match_entry *entry, *tmp;
-       bool found = false;
+       int ret;
 
        lockdep_assert_held(conn);
 
        mutex_lock(&db->entries_lock);
-       list_for_each_entry_safe(entry, tmp, &db->entries_list, list_entry)
-               if (entry->cookie == cmd->cookie) {
-                       kdbus_match_entry_free(entry);
-                       --db->entries;
-                       found = true;
-               }
+       ret = __kdbus_match_db_remove_unlocked(db, cmd->cookie);
        mutex_unlock(&db->entries_lock);
 
-       return found ? 0 : -ENOENT;
+       return ret;
 }