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
struct kdbus_cmd_match {
__u64 size;
__u64 cookie;
+ __u64 flags;
struct kdbus_item items[0];
} __attribute__((aligned(8)));
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.
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.
};
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.
}
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;
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;
}